mirror of
https://github.com/Microsoft/vscode
synced 2024-11-05 18:29:38 +00:00
Fixes #20757: Detect overlapping ranges in the extension host, no need to call into renderer process to find out the edits are illegal
This commit is contained in:
parent
731aee189b
commit
974f8aca31
2 changed files with 65 additions and 17 deletions
|
@ -178,4 +178,23 @@ suite('editor tests', () => {
|
|||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
test('issue #20757: Overlapping ranges are not allowed!', () => {
|
||||
return withRandomFileEditor('Hello world!\n\tHello world!', (editor, doc) => {
|
||||
return editor.edit((builder) => {
|
||||
// create two edits that overlap (i.e. are illegal)
|
||||
builder.replace(new Range(0, 0, 0, 2), 'He');
|
||||
builder.replace(new Range(0, 1, 0, 3), 'el');
|
||||
}).then(
|
||||
|
||||
(applied) => {
|
||||
assert.ok(false, 'edit with overlapping ranges should fail');
|
||||
},
|
||||
|
||||
(err) => {
|
||||
assert.ok(true, 'edit with overlapping ranges should fail');
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -38,7 +38,7 @@ export class TextEditorDecorationType implements vscode.TextEditorDecorationType
|
|||
}
|
||||
|
||||
export interface ITextEditOperation {
|
||||
range: Range;
|
||||
range: vscode.Range;
|
||||
text: string;
|
||||
forceMoveMarkers: boolean;
|
||||
}
|
||||
|
@ -53,13 +53,15 @@ export interface IEditData {
|
|||
|
||||
export class TextEditorEdit {
|
||||
|
||||
private _documentVersionId: number;
|
||||
private readonly _document: vscode.TextDocument;
|
||||
private readonly _documentVersionId: number;
|
||||
private _collectedEdits: ITextEditOperation[];
|
||||
private _setEndOfLine: EndOfLine;
|
||||
private _undoStopBefore: boolean;
|
||||
private _undoStopAfter: boolean;
|
||||
private readonly _undoStopBefore: boolean;
|
||||
private readonly _undoStopAfter: boolean;
|
||||
|
||||
constructor(document: vscode.TextDocument, options: { undoStopBefore: boolean; undoStopAfter: boolean; }) {
|
||||
this._document = document;
|
||||
this._documentVersionId = document.version;
|
||||
this._collectedEdits = [];
|
||||
this._setEndOfLine = 0;
|
||||
|
@ -88,19 +90,11 @@ export class TextEditorEdit {
|
|||
throw new Error('Unrecognized location');
|
||||
}
|
||||
|
||||
this._collectedEdits.push({
|
||||
range: range,
|
||||
text: value,
|
||||
forceMoveMarkers: false
|
||||
});
|
||||
this._pushEdit(range, value, false);
|
||||
}
|
||||
|
||||
insert(location: Position, value: string): void {
|
||||
this._collectedEdits.push({
|
||||
range: new Range(location, location),
|
||||
text: value,
|
||||
forceMoveMarkers: true
|
||||
});
|
||||
this._pushEdit(new Range(location, location), value, true);
|
||||
}
|
||||
|
||||
delete(location: Range | Selection): void {
|
||||
|
@ -112,10 +106,15 @@ export class TextEditorEdit {
|
|||
throw new Error('Unrecognized location');
|
||||
}
|
||||
|
||||
this._pushEdit(range, null, true);
|
||||
}
|
||||
|
||||
private _pushEdit(range: Range, text: string, forceMoveMarkers: boolean): void {
|
||||
let validRange = this._document.validateRange(range);
|
||||
this._collectedEdits.push({
|
||||
range: range,
|
||||
text: null,
|
||||
forceMoveMarkers: true
|
||||
range: validRange,
|
||||
text: text,
|
||||
forceMoveMarkers: forceMoveMarkers
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -462,6 +461,36 @@ export class ExtHostTextEditor implements vscode.TextEditor {
|
|||
private _applyEdit(editBuilder: TextEditorEdit): TPromise<boolean> {
|
||||
let editData = editBuilder.finalize();
|
||||
|
||||
// check that the edits are not overlapping (i.e. illegal)
|
||||
let editRanges = editData.edits.map(edit => edit.range);
|
||||
|
||||
// sort ascending (by end and then by start)
|
||||
editRanges.sort((a, b) => {
|
||||
if (a.end.line === b.end.line) {
|
||||
if (a.end.character === b.end.character) {
|
||||
if (a.start.line === b.start.line) {
|
||||
return a.start.character - b.start.character;
|
||||
}
|
||||
return a.start.line - b.start.line;
|
||||
}
|
||||
return a.end.character - b.end.character;
|
||||
}
|
||||
return a.end.line - b.end.line;
|
||||
});
|
||||
|
||||
// check that no edits are overlapping
|
||||
for (let i = 0, count = editRanges.length - 1; i < count; i++) {
|
||||
const rangeEnd = editRanges[i].end;
|
||||
const nextRangeStart = editRanges[i + 1].start;
|
||||
|
||||
if (nextRangeStart.isBefore(rangeEnd)) {
|
||||
// overlapping ranges
|
||||
return TPromise.wrapError<boolean>(
|
||||
new Error('Overlapping ranges are not allowed!')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// prepare data for serialization
|
||||
let edits: ISingleEditOperation[] = editData.edits.map((edit) => {
|
||||
return {
|
||||
|
|
Loading…
Reference in a new issue