未验证 提交 aa66fbf2 编写于 作者: A Alex Dima

Avoid computing undo edits if not necessary

上级 f4f93a67
...@@ -1092,9 +1092,11 @@ export interface ITextModel { ...@@ -1092,9 +1092,11 @@ export interface ITextModel {
* Edit the model without adding the edits to the undo stack. * Edit the model without adding the edits to the undo stack.
* This can have dire consequences on the undo stack! See @pushEditOperations for the preferred way. * This can have dire consequences on the undo stack! See @pushEditOperations for the preferred way.
* @param operations The edit operations. * @param operations The edit operations.
* @return The inverse edit operations, that, when applied, will bring the model back to the previous state. * @return If desired, the inverse edit operations, that, when applied, will bring the model back to the previous state.
*/ */
applyEdits(operations: IIdentifiedSingleEditOperation[]): IValidEditOperation[]; applyEdits(operations: IIdentifiedSingleEditOperation[]): void;
applyEdits(operations: IIdentifiedSingleEditOperation[], computeUndoEdits: false): void;
applyEdits(operations: IIdentifiedSingleEditOperation[], computeUndoEdits: true): IValidEditOperation[];
/** /**
* Change the end of line sequence without recording in the undo stack. * Change the end of line sequence without recording in the undo stack.
...@@ -1299,7 +1301,7 @@ export interface ITextBuffer { ...@@ -1299,7 +1301,7 @@ export interface ITextBuffer {
export class ApplyEditsResult { export class ApplyEditsResult {
constructor( constructor(
public readonly reverseEdits: IValidEditOperation[], public readonly reverseEdits: IValidEditOperation[] | null,
public readonly changes: IInternalModelContentChange[], public readonly changes: IInternalModelContentChange[],
public readonly trimAutoWhitespaceLineNumbers: number[] | null public readonly trimAutoWhitespaceLineNumbers: number[] | null
) { } ) { }
......
...@@ -326,7 +326,7 @@ export class EditStack { ...@@ -326,7 +326,7 @@ export class EditStack {
public pushEditOperation(beforeCursorState: Selection[] | null, editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer | null): Selection[] | null { public pushEditOperation(beforeCursorState: Selection[] | null, editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer | null): Selection[] | null {
const editStackElement = this._getOrCreateEditStackElement(beforeCursorState); const editStackElement = this._getOrCreateEditStackElement(beforeCursorState);
const inverseEditOperations = this._model.applyEdits(editOperations); const inverseEditOperations = this._model.applyEdits(editOperations, true);
const afterCursorState = EditStack._computeCursorState(cursorStateComputer, inverseEditOperations); const afterCursorState = EditStack._computeCursorState(cursorStateComputer, inverseEditOperations);
editStackElement.append(this._model, inverseEditOperations, getModelEOL(this._model), this._model.getAlternativeVersionId(), afterCursorState); editStackElement.append(this._model, inverseEditOperations, getModelEOL(this._model), this._model.getAlternativeVersionId(), afterCursorState);
return afterCursorState; return afterCursorState;
......
...@@ -261,6 +261,20 @@ export class PieceTreeTextBuffer implements ITextBuffer { ...@@ -261,6 +261,20 @@ export class PieceTreeTextBuffer implements ITextBuffer {
// Sort operations ascending // Sort operations ascending
operations.sort(PieceTreeTextBuffer._sortOpsAscending); operations.sort(PieceTreeTextBuffer._sortOpsAscending);
let hasTouchingRanges = false;
for (let i = 0, count = operations.length - 1; i < count; i++) {
let rangeEnd = operations[i].range.getEndPosition();
let nextRangeStart = operations[i + 1].range.getStartPosition();
if (nextRangeStart.isBeforeOrEqual(rangeEnd)) {
if (nextRangeStart.isBefore(rangeEnd)) {
// overlapping ranges
throw new Error('Overlapping ranges are not allowed!');
}
hasTouchingRanges = true;
}
}
if (canReduceOperations) { if (canReduceOperations) {
operations = this._reduceOperations(operations); operations = this._reduceOperations(operations);
} }
...@@ -289,24 +303,11 @@ export class PieceTreeTextBuffer implements ITextBuffer { ...@@ -289,24 +303,11 @@ export class PieceTreeTextBuffer implements ITextBuffer {
} }
} }
let reverseOperations: IReverseSingleEditOperation[] = []; let reverseOperations: IReverseSingleEditOperation[] | null = null;
if (computeUndoEdits) { if (computeUndoEdits) {
let hasTouchingRanges = false;
for (let i = 0, count = operations.length - 1; i < count; i++) {
let rangeEnd = operations[i].range.getEndPosition();
let nextRangeStart = operations[i + 1].range.getStartPosition();
if (nextRangeStart.isBeforeOrEqual(rangeEnd)) {
if (nextRangeStart.isBefore(rangeEnd)) {
// overlapping ranges
throw new Error('Overlapping ranges are not allowed!');
}
hasTouchingRanges = true;
}
}
let reverseRangeDeltaOffset = 0; let reverseRangeDeltaOffset = 0;
reverseOperations = [];
for (let i = 0; i < operations.length; i++) { for (let i = 0; i < operations.length; i++) {
const op = operations[i]; const op = operations[i];
const reverseRange = reverseRanges[i]; const reverseRange = reverseRanges[i];
......
...@@ -1330,18 +1330,22 @@ export class TextModel extends Disposable implements model.ITextModel { ...@@ -1330,18 +1330,22 @@ export class TextModel extends Disposable implements model.ITextModel {
} }
} }
public applyEdits(rawOperations: model.IIdentifiedSingleEditOperation[], computeUndoEdits: boolean = true): model.IValidEditOperation[] { public applyEdits(operations: model.IIdentifiedSingleEditOperation[]): void;
public applyEdits(operations: model.IIdentifiedSingleEditOperation[], computeUndoEdits: false): void;
public applyEdits(operations: model.IIdentifiedSingleEditOperation[], computeUndoEdits: true): model.IValidEditOperation[];
public applyEdits(rawOperations: model.IIdentifiedSingleEditOperation[], computeUndoEdits: boolean = false): void | model.IValidEditOperation[] {
try { try {
this._onDidChangeDecorations.beginDeferredEmit(); this._onDidChangeDecorations.beginDeferredEmit();
this._eventEmitter.beginDeferredEmit(); this._eventEmitter.beginDeferredEmit();
return this._doApplyEdits(this._validateEditOperations(rawOperations), computeUndoEdits); const operations = this._validateEditOperations(rawOperations);
return this._doApplyEdits(operations, computeUndoEdits);
} finally { } finally {
this._eventEmitter.endDeferredEmit(); this._eventEmitter.endDeferredEmit();
this._onDidChangeDecorations.endDeferredEmit(); this._onDidChangeDecorations.endDeferredEmit();
} }
} }
private _doApplyEdits(rawOperations: model.ValidAnnotatedEditOperation[], computeUndoEdits: boolean): model.IValidEditOperation[] { private _doApplyEdits(rawOperations: model.ValidAnnotatedEditOperation[], computeUndoEdits: boolean): void | model.IValidEditOperation[] {
const oldLineCount = this._buffer.getLineCount(); const oldLineCount = this._buffer.getLineCount();
const result = this._buffer.applyEdits(rawOperations, this._options.trimAutoWhitespace, computeUndoEdits); const result = this._buffer.applyEdits(rawOperations, this._options.trimAutoWhitespace, computeUndoEdits);
...@@ -1419,7 +1423,7 @@ export class TextModel extends Disposable implements model.ITextModel { ...@@ -1419,7 +1423,7 @@ export class TextModel extends Disposable implements model.ITextModel {
); );
} }
return result.reverseEdits; return (result.reverseEdits === null ? undefined : result.reverseEdits);
} }
public undo(): void { public undo(): void {
......
...@@ -1104,7 +1104,7 @@ suite('EditorModel - EditableTextModel.applyEdits', () => { ...@@ -1104,7 +1104,7 @@ suite('EditorModel - EditableTextModel.applyEdits', () => {
{ range: new Range(3, 1, 3, 6), text: null, }, { range: new Range(3, 1, 3, 6), text: null, },
{ range: new Range(2, 1, 3, 1), text: null, }, { range: new Range(2, 1, 3, 1), text: null, },
{ range: new Range(3, 6, 3, 6), text: '\nline2' } { range: new Range(3, 6, 3, 6), text: '\nline2' }
]); ], true);
model.applyEdits(undoEdits); model.applyEdits(undoEdits);
......
...@@ -17,7 +17,7 @@ export function testApplyEditsWithSyncedModels(original: string[], edits: IIdent ...@@ -17,7 +17,7 @@ export function testApplyEditsWithSyncedModels(original: string[], edits: IIdent
assertSyncedModels(originalStr, (model, assertMirrorModels) => { assertSyncedModels(originalStr, (model, assertMirrorModels) => {
// Apply edits & collect inverse edits // Apply edits & collect inverse edits
let inverseEdits = model.applyEdits(edits); let inverseEdits = model.applyEdits(edits, true);
// Assert edits produced expected result // Assert edits produced expected result
assert.deepEqual(model.getValue(EndOfLinePreference.LF), expectedStr); assert.deepEqual(model.getValue(EndOfLinePreference.LF), expectedStr);
...@@ -25,7 +25,7 @@ export function testApplyEditsWithSyncedModels(original: string[], edits: IIdent ...@@ -25,7 +25,7 @@ export function testApplyEditsWithSyncedModels(original: string[], edits: IIdent
assertMirrorModels(); assertMirrorModels();
// Apply the inverse edits // Apply the inverse edits
let inverseInverseEdits = model.applyEdits(inverseEdits); let inverseInverseEdits = model.applyEdits(inverseEdits, true);
// Assert the inverse edits brought back model to original state // Assert the inverse edits brought back model to original state
assert.deepEqual(model.getValue(EndOfLinePreference.LF), originalStr); assert.deepEqual(model.getValue(EndOfLinePreference.LF), originalStr);
......
...@@ -330,7 +330,7 @@ suite('Editor Model - Model', () => { ...@@ -330,7 +330,7 @@ suite('Editor Model - Model', () => {
let res = thisModel.applyEdits([ let res = thisModel.applyEdits([
{ range: new Range(2, 1, 2, 1), text: 'a' }, { range: new Range(2, 1, 2, 1), text: 'a' },
{ range: new Range(1, 1, 1, 1), text: 'b' }, { range: new Range(1, 1, 1, 1), text: 'b' },
]); ], true);
assert.deepEqual(res[0].range, new Range(2, 1, 2, 2)); assert.deepEqual(res[0].range, new Range(2, 1, 2, 2));
assert.deepEqual(res[1].range, new Range(1, 1, 1, 2)); assert.deepEqual(res[1].range, new Range(1, 1, 1, 2));
......
...@@ -50,14 +50,14 @@ suite('Editor Model - Model Edit Operation', () => { ...@@ -50,14 +50,14 @@ suite('Editor Model - Model Edit Operation', () => {
function assertSingleEditOp(singleEditOp: IIdentifiedSingleEditOperation, editedLines: string[]) { function assertSingleEditOp(singleEditOp: IIdentifiedSingleEditOperation, editedLines: string[]) {
let editOp = [singleEditOp]; let editOp = [singleEditOp];
let inverseEditOp = model.applyEdits(editOp); let inverseEditOp = model.applyEdits(editOp, true);
assert.equal(model.getLineCount(), editedLines.length); assert.equal(model.getLineCount(), editedLines.length);
for (let i = 0; i < editedLines.length; i++) { for (let i = 0; i < editedLines.length; i++) {
assert.equal(model.getLineContent(i + 1), editedLines[i]); assert.equal(model.getLineContent(i + 1), editedLines[i]);
} }
let originalOp = model.applyEdits(inverseEditOp); let originalOp = model.applyEdits(inverseEditOp, true);
assert.equal(model.getLineCount(), 5); assert.equal(model.getLineCount(), 5);
assert.equal(model.getLineContent(1), LINE1); assert.equal(model.getLineContent(1), LINE1);
......
...@@ -1902,9 +1902,11 @@ declare namespace monaco.editor { ...@@ -1902,9 +1902,11 @@ declare namespace monaco.editor {
* Edit the model without adding the edits to the undo stack. * Edit the model without adding the edits to the undo stack.
* This can have dire consequences on the undo stack! See @pushEditOperations for the preferred way. * This can have dire consequences on the undo stack! See @pushEditOperations for the preferred way.
* @param operations The edit operations. * @param operations The edit operations.
* @return The inverse edit operations, that, when applied, will bring the model back to the previous state. * @return If desired, the inverse edit operations, that, when applied, will bring the model back to the previous state.
*/ */
applyEdits(operations: IIdentifiedSingleEditOperation[]): IValidEditOperation[]; applyEdits(operations: IIdentifiedSingleEditOperation[]): void;
applyEdits(operations: IIdentifiedSingleEditOperation[], computeUndoEdits: false): void;
applyEdits(operations: IIdentifiedSingleEditOperation[], computeUndoEdits: true): IValidEditOperation[];
/** /**
* Change the end of line sequence without recording in the undo stack. * Change the end of line sequence without recording in the undo stack.
* This can have dire consequences on the undo stack! See @pushEOL for the preferred way. * This can have dire consequences on the undo stack! See @pushEOL for the preferred way.
......
...@@ -399,7 +399,7 @@ export class BulkEditPreviewProvider implements ITextModelContentProvider { ...@@ -399,7 +399,7 @@ export class BulkEditPreviewProvider implements ITextModelContentProvider {
} }
// apply new edits and keep (future) undo edits // apply new edits and keep (future) undo edits
const newEdits = this._operations.getFileEdits(uri); const newEdits = this._operations.getFileEdits(uri);
const newUndoEdits = model.applyEdits(newEdits); const newUndoEdits = model.applyEdits(newEdits, true);
this._modelPreviewEdits.set(model.id, newUndoEdits); this._modelPreviewEdits.set(model.id, newUndoEdits);
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册