Skip to content

Commit 5704f0d

Browse files
committed
Allow touching edits in editableTextModel (microsoft#3980)
1 parent 2e9f012 commit 5704f0d

4 files changed

Lines changed: 296 additions & 97 deletions

File tree

src/vs/editor/common/core/position.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,29 @@ export class Position implements IEditorPosition {
2121
}
2222

2323
public isBefore(other:IPosition): boolean {
24-
if (this.lineNumber < other.lineNumber) {
24+
return Position.isBefore(this, other);
25+
}
26+
public static isBefore(a:IPosition, b:IPosition): boolean {
27+
if (a.lineNumber < b.lineNumber) {
2528
return true;
2629
}
27-
if (other.lineNumber < this.lineNumber) {
30+
if (b.lineNumber < a.lineNumber) {
2831
return false;
2932
}
30-
return this.column < other.column;
33+
return a.column < b.column;
3134
}
3235

3336
public isBeforeOrEqual(other:IPosition): boolean {
34-
if (this.lineNumber < other.lineNumber) {
37+
return Position.isBeforeOrEqual(this, other);
38+
}
39+
public static isBeforeOrEqual(a:IPosition, b:IPosition): boolean {
40+
if (a.lineNumber < b.lineNumber) {
3541
return true;
3642
}
37-
if (other.lineNumber < this.lineNumber) {
43+
if (b.lineNumber < a.lineNumber) {
3844
return false;
3945
}
40-
return this.column <= other.column;
46+
return a.column <= b.column;
4147
}
4248

4349
public clone(): Position {

src/vs/editor/common/model/editableTextModel.ts

Lines changed: 66 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,8 @@ import {ILineEdit, ILineMarker, ModelLine} from 'vs/editor/common/model/modelLin
1212
import {DeferredEventsBuilder, TextModelWithDecorations} from 'vs/editor/common/model/textModelWithDecorations';
1313
import {IMode} from 'vs/editor/common/modes';
1414

15-
export interface IDeltaSingleEditOperation {
16-
original: IValidatedEditOperation;
17-
deltaStartLineNumber: number;
18-
deltaStartColumn: number;
19-
deltaEndLineNumber: number;
20-
deltaEndColumn: number;
21-
}
22-
2315
export interface IValidatedEditOperation {
16+
sortIndex: number;
2417
identifier: editorCommon.ISingleEditOperationIdentifier;
2518
range: editorCommon.IEditorRange;
2619
rangeLength: number;
@@ -155,6 +148,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
155148
}
156149

157150
return {
151+
sortIndex: 0,
158152
identifier: operations[0].identifier,
159153
range: entireEditRange,
160154
rangeLength: this.getValueLengthInRange(entireEditRange),
@@ -163,6 +157,22 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
163157
};
164158
}
165159

160+
private static _sortOpsAscending(a:IValidatedEditOperation, b:IValidatedEditOperation): number {
161+
let r = Range.compareRangesUsingEnds(a.range, b.range);
162+
if (r === 0) {
163+
return a.sortIndex - b.sortIndex;
164+
}
165+
return r;
166+
}
167+
168+
private static _sortOpsDescending(a:IValidatedEditOperation, b:IValidatedEditOperation): number {
169+
let r = Range.compareRangesUsingEnds(a.range, b.range);
170+
if (r === 0) {
171+
return b.sortIndex - a.sortIndex;
172+
}
173+
return -r;
174+
}
175+
166176
public applyEdits(rawOperations:editorCommon.IIdentifiedSingleEditOperation[]): editorCommon.IIdentifiedSingleEditOperation[] {
167177
if (rawOperations.length === 0) {
168178
return [];
@@ -173,6 +183,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
173183
let op = rawOperations[i];
174184
let validatedRange = this.validateRange(op.range);
175185
operations[i] = {
186+
sortIndex: i,
176187
identifier: op.identifier,
177188
range: validatedRange,
178189
rangeLength: this.getValueLengthInRange(validatedRange),
@@ -181,20 +192,19 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
181192
};
182193
}
183194

184-
// Sort operations
185-
operations.sort((a, b) => {
186-
return Range.compareRangesUsingEnds(a.range, b.range);
187-
});
195+
// Sort operations ascending
196+
operations.sort(EditableTextModel._sortOpsAscending);
197+
198+
for (let i = 0, count = operations.length - 1; i < count; i++) {
199+
let rangeEnd = operations[i].range.getEndPosition();
200+
let nextRangeStart = operations[i + 1].range.getStartPosition();
188201

189-
// Operations can not overlap!
190-
for (let i = operations.length - 2; i >= 0; i--) {
191-
if (operations[i+1].range.getStartPosition().isBeforeOrEqual(operations[i].range.getEndPosition())) {
202+
if (nextRangeStart.isBefore(rangeEnd)) {
203+
// overlapping ranges
192204
throw new Error('Overlapping ranges are not allowed!');
193205
}
194206
}
195207

196-
// console.log(JSON.stringify(operations, null, '\t'));
197-
198208
operations = this._reduceOperations(operations);
199209

200210
let editableRange = this.getEditableRange();
@@ -208,8 +218,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
208218
}
209219

210220
// Delta encode operations
211-
let deltaOperations = EditableTextModel._toDeltaOperations(operations);
212-
let reverseRanges = EditableTextModel._getInverseEditRanges(deltaOperations);
221+
let reverseRanges = EditableTextModel._getInverseEditRanges(operations);
213222
let reverseOperations: editorCommon.IIdentifiedSingleEditOperation[] = [];
214223
for (let i = 0; i < operations.length; i++) {
215224
reverseOperations[i] = {
@@ -225,63 +234,69 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
225234
return reverseOperations;
226235
}
227236

228-
private static _toDeltaOperation(base: IValidatedEditOperation, operation:IValidatedEditOperation): IDeltaSingleEditOperation {
229-
let deltaStartLineNumber = operation.range.startLineNumber - (base ? base.range.endLineNumber : 0);
230-
let deltaStartColumn = operation.range.startColumn - (deltaStartLineNumber === 0 ? base.range.endColumn : 0);
231-
let deltaEndLineNumber = operation.range.endLineNumber - (base ? base.range.endLineNumber : 0);
232-
let deltaEndColumn = operation.range.endColumn - (deltaEndLineNumber === 0 ? base.range.endColumn : 0);
233-
234-
return {
235-
original: operation,
236-
deltaStartLineNumber: deltaStartLineNumber,
237-
deltaStartColumn: deltaStartColumn,
238-
deltaEndLineNumber: deltaEndLineNumber,
239-
deltaEndColumn: deltaEndColumn
240-
};
241-
}
242-
243237
/**
244238
* Assumes `operations` are validated and sorted ascending
245239
*/
246-
public static _getInverseEditRanges(operations:IDeltaSingleEditOperation[]): editorCommon.IEditorRange[] {
247-
let lineNumber = 0,
248-
column = 0,
249-
result:editorCommon.IEditorRange[] = [];
240+
public static _getInverseEditRanges(operations:IValidatedEditOperation[]): editorCommon.IEditorRange[] {
241+
let result:editorCommon.IEditorRange[] = [];
250242

243+
let prevOpEndLineNumber: number;
244+
let prevOpEndColumn: number;
245+
let prevOp:IValidatedEditOperation = null;
251246
for (let i = 0, len = operations.length; i < len; i++) {
252247
let op = operations[i];
253248

254-
let startLineNumber = op.deltaStartLineNumber + lineNumber;
255-
let startColumn = op.deltaStartColumn + (op.deltaStartLineNumber === 0 ? column : 0);
249+
let startLineNumber: number;
250+
let startColumn: number;
251+
252+
if (prevOp) {
253+
if (prevOp.range.endLineNumber === op.range.startLineNumber) {
254+
startLineNumber = prevOpEndLineNumber;
255+
startColumn = prevOpEndColumn + (op.range.startColumn - prevOp.range.endColumn);
256+
} else {
257+
startLineNumber = prevOpEndLineNumber + (op.range.startLineNumber - prevOp.range.endLineNumber);
258+
startColumn = op.range.startColumn;
259+
}
260+
} else {
261+
startLineNumber = op.range.startLineNumber;
262+
startColumn = op.range.startColumn;
263+
}
264+
256265
let resultRange: editorCommon.IEditorRange;
257266

258-
if (op.original.lines && op.original.lines.length > 0) {
259-
// There is something to insert
260-
if (op.original.lines.length === 1) {
261-
// Single line insert
262-
resultRange = new Range(startLineNumber, startColumn, startLineNumber, startColumn + op.original.lines[0].length);
267+
if (op.lines && op.lines.length > 0) {
268+
// the operation inserts something
269+
let lineCount = op.lines.length;
270+
let firstLine = op.lines[0];
271+
let lastLine = op.lines[lineCount - 1];
272+
273+
if (lineCount === 1) {
274+
// single line insert
275+
resultRange = new Range(startLineNumber, startColumn, startLineNumber, startColumn + firstLine.length);
263276
} else {
264-
// Multi line insert
265-
resultRange = new Range(startLineNumber, startColumn, startLineNumber + op.original.lines.length - 1, op.original.lines[op.original.lines.length - 1].length + 1);
277+
// multi line insert
278+
resultRange = new Range(startLineNumber, startColumn, startLineNumber + lineCount - 1, lastLine.length + 1);
266279
}
267280
} else {
268281
// There is nothing to insert
269282
resultRange = new Range(startLineNumber, startColumn, startLineNumber, startColumn);
270283
}
271284

272-
lineNumber = resultRange.endLineNumber;
273-
column = resultRange.endColumn;
285+
prevOpEndLineNumber = resultRange.endLineNumber;
286+
prevOpEndColumn = resultRange.endColumn;
274287

275288
result.push(resultRange);
289+
prevOp = op;
276290
}
277291

278292
return result;
279293
}
280294

281295
private _applyEdits(operations:IValidatedEditOperation[]): void {
282296

283-
// Note the minus!
284-
operations = operations.sort((a, b) => -Range.compareRangesUsingEnds(a.range, b.range));
297+
// Sort operations descending
298+
operations.sort(EditableTextModel._sortOpsDescending);
299+
285300

286301
this._withDeferredEvents((deferredEventsBuilder:DeferredEventsBuilder) => {
287302
let contentChangedEvents: editorCommon.IModelContentChangedEvent[] = [];
@@ -516,14 +531,6 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
516531
}
517532
}
518533

519-
public static _toDeltaOperations(operations:IValidatedEditOperation[]): IDeltaSingleEditOperation[] {
520-
let result: IDeltaSingleEditOperation[] = [];
521-
for (let i = 0; i < operations.length; i++) {
522-
result[i] = EditableTextModel._toDeltaOperation(i > 0 ? operations[i-1] : null, operations[i]);
523-
}
524-
return result;
525-
}
526-
527534
public undo(): editorCommon.IEditorSelection[] {
528535
if (this._isDisposed) {
529536
throw new Error('EditableTextModel.undo: Model is disposed');

0 commit comments

Comments
 (0)