diff --git a/src/vs/editor/common/commonCodeEditor.ts b/src/vs/editor/common/commonCodeEditor.ts index d698e6a2f0ce83ab819f8450ba669c24d128a968..2fef9136e64c924fde2b69cc2112dcb0b4508c84 100644 --- a/src/vs/editor/common/commonCodeEditor.ts +++ b/src/vs/editor/common/commonCodeEditor.ts @@ -698,16 +698,8 @@ export abstract class CommonCodeEditor extends Disposable implements editorCommo return this.cursor; } - public executeCommand(source: string, command: editorCommon.ICommand): void { - if (!this.cursor) { - return; - } - this.cursor.trigger(source, editorCommon.Handler.ExecuteCommand, command); - } - public pushUndoStop(): boolean { - if (!this.cursor) { - // no view, no cursor + if (!this.model) { return false; } if (this._configuration.editor.readOnly) { @@ -739,6 +731,13 @@ export abstract class CommonCodeEditor extends Disposable implements editorCommo return true; } + public executeCommand(source: string, command: editorCommon.ICommand): void { + if (!this.cursor) { + return; + } + this.cursor.trigger(source, editorCommon.Handler.ExecuteCommand, command); + } + public executeCommands(source: string, commands: editorCommon.ICommand[]): void { if (!this.cursor) { return; diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index 3c35af418f36800341bcfb35fbb8c3e1b03f5699..aec7779027468832051bba23a06403e53d4d3e54 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -59,10 +59,9 @@ export class Cursor extends Disposable implements ICursors { private readonly _configuration: editorCommon.IConfiguration; private readonly _model: editorCommon.IModel; private readonly _viewModelHelper: IViewModelHelper; - public context: CursorContext; - private _cursors: CursorCollection; + private _isHandling: boolean; private _isDoingComposition: boolean; private _columnSelectData: IColumnSelectData; @@ -77,31 +76,16 @@ export class Cursor extends Disposable implements ICursors { this._configuration = configuration; this._model = model; this._viewModelHelper = viewModelHelper; - - const createCursorContext = () => { - const config = new CursorConfiguration( - this._model.getLanguageIdentifier(), - this._model.getOneIndent(), - this._model.getOptions(), - this._configuration - ); - this.context = new CursorContext( - this._model, - this._viewModelHelper, - config - ); - if (this._cursors) { - this._cursors.updateContext(this.context); - } - }; - createCursorContext(); - + this.context = new CursorContext(this._configuration, this._model, this._viewModelHelper); this._cursors = new CursorCollection(this.context); this._isHandling = false; this._isDoingComposition = false; this._columnSelectData = null; + this._handlers = {}; + this._registerHandlers(); + this._register(this._model.addBulkListener((events) => { if (this._isHandling) { return; @@ -115,14 +99,8 @@ export class Cursor extends Disposable implements ICursors { if (eventType === TextModelEventType.ModelRawContentChanged2) { hadContentChange = true; - const changeEvent = event.data; - - for (let j = 0, lenJ = changeEvent.changes.length; j < lenJ; j++) { - const change = changeEvent.changes[j]; - if (change.changeType === RawContentChangedType.Flush) { - hadFlushEvent = true; - } - } + const rawChangeEvent = event.data; + hadFlushEvent = hadFlushEvent || rawChangeEvent.containsEvent(RawContentChangedType.Flush); } } @@ -133,29 +111,29 @@ export class Cursor extends Disposable implements ICursors { this._onModelContentChanged(hadFlushEvent); })); + const updateCursorContext = () => { + this.context = new CursorContext(this._configuration, this._model, this._viewModelHelper); + this._cursors.updateContext(this.context); + }; this._register(this._model.onDidChangeLanguage((e) => { - createCursorContext(); + updateCursorContext(); })); this._register(LanguageConfigurationRegistry.onDidChange(() => { // TODO@Alex: react only if certain supports changed? (and if my model's mode changed) - createCursorContext(); + updateCursorContext(); })); this._register(model.onDidChangeOptions(() => { - createCursorContext(); + updateCursorContext(); })); this._register(this._configuration.onDidChange((e) => { if (CursorConfiguration.shouldRecreate(e)) { - createCursorContext(); + updateCursorContext(); } })); - - this._handlers = {}; - this._registerHandlers(); } public dispose(): void { this._cursors.dispose(); - this._cursors = null; super.dispose(); } @@ -203,8 +181,8 @@ export class Cursor extends Disposable implements ICursors { } if (somethingChanged) { - this.emitCursorPositionChanged(source, reason); - this.emitCursorSelectionChanged(source, reason); + this._emitCursorPositionChanged(source, reason); + this._emitCursorSelectionChanged(source, reason); } } @@ -295,16 +273,13 @@ export class Cursor extends Disposable implements ICursors { if (hadFlushEvent) { // a model.setValue() was called this._cursors.dispose(); - this._cursors = new CursorCollection(this.context); - this.emitCursorPositionChanged('model', CursorChangeReason.ContentFlush); - this.emitCursorSelectionChanged('model', CursorChangeReason.ContentFlush); + this._emitCursorPositionChanged('model', CursorChangeReason.ContentFlush); + this._emitCursorSelectionChanged('model', CursorChangeReason.ContentFlush); } else { - if (!this._isHandling) { - const selectionsFromMarkers = this._cursors.readSelectionFromMarkers(); - this.setStates('modelChange', CursorChangeReason.RecoverFromMarkers, CursorState.fromModelSelections(selectionsFromMarkers)); - } + const selectionsFromMarkers = this._cursors.readSelectionFromMarkers(); + this.setStates('modelChange', CursorChangeReason.RecoverFromMarkers, CursorState.fromModelSelections(selectionsFromMarkers)); } } @@ -392,9 +367,9 @@ export class Cursor extends Disposable implements ICursors { } if (somethingChanged) { - this.emitCursorPositionChanged(args.eventSource, cursorPositionChangeReason); + this._emitCursorPositionChanged(args.eventSource, cursorPositionChangeReason); this._revealRange(RevealTarget.Primary, VerticalRevealType.Simple, true); - this.emitCursorSelectionChanged(args.eventSource, cursorPositionChangeReason); + this._emitCursorSelectionChanged(args.eventSource, cursorPositionChangeReason); } } catch (err) { @@ -415,7 +390,7 @@ export class Cursor extends Disposable implements ICursors { // ----------------------------------------------------------------------------------------------------------- // ----- emitting events - private emitCursorPositionChanged(source: string, reason: CursorChangeReason): void { + private _emitCursorPositionChanged(source: string, reason: CursorChangeReason): void { const positions = this._cursors.getPositions(); const primaryPosition = positions[0]; const secondaryPositions = positions.slice(1); @@ -443,7 +418,7 @@ export class Cursor extends Disposable implements ICursors { this._eventEmitter.emit(CursorEventType.CursorPositionChanged, e); } - private emitCursorSelectionChanged(source: string, reason: CursorChangeReason): void { + private _emitCursorSelectionChanged(source: string, reason: CursorChangeReason): void { const selections = this._cursors.getSelections(); const primarySelection = selections[0]; const secondarySelections = selections.slice(1); @@ -729,8 +704,8 @@ export class Cursor extends Disposable implements ICursors { this._cursors.killSecondaryCursors(); return new EditOperationResult([command], { - shouldPushStackElementBefore: true, - shouldPushStackElementAfter: true + shouldPushStackElementBefore: false, + shouldPushStackElementAfter: false }); } @@ -738,8 +713,8 @@ export class Cursor extends Disposable implements ICursors { const commands = args.eventData; return new EditOperationResult(commands, { - shouldPushStackElementBefore: true, - shouldPushStackElementAfter: true + shouldPushStackElementBefore: false, + shouldPushStackElementAfter: false }); } } diff --git a/src/vs/editor/common/controller/cursorCommon.ts b/src/vs/editor/common/controller/cursorCommon.ts index 8c37d25a581132f3ab0213757abc8bb5f2e124c0..8684392bd95dc61d0940b785b53e60e25c6f6fae 100644 --- a/src/vs/editor/common/controller/cursorCommon.ts +++ b/src/vs/editor/common/controller/cursorCommon.ts @@ -284,10 +284,15 @@ export class CursorContext { private readonly _viewModelHelper: IViewModelHelper; private readonly _coordinatesConverter: ICoordinatesConverter; - constructor(model: IModel, viewModelHelper: IViewModelHelper, config: CursorConfiguration) { + constructor(configuration: IConfiguration, model: IModel, viewModelHelper: IViewModelHelper) { this.model = model; this.viewModel = viewModelHelper.viewModel; - this.config = config; + this.config = new CursorConfiguration( + this.model.getLanguageIdentifier(), + this.model.getOneIndent(), + this.model.getOptions(), + configuration + );; this._viewModelHelper = viewModelHelper; this._coordinatesConverter = viewModelHelper.coordinatesConverter; } diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index b5c1bba3a0a3eca0949a00c69d802ab3b9269ff9..268c48f5d1188f1529e94019e6fc452f9e593d1b 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -1886,6 +1886,7 @@ export interface ICommonCodeEditor extends IEditor { /** * Execute a command on the editor. + * The edits will land on the undo-redo stack, but no "undo stop" will be pushed. * @param source The source of the call. * @param command The command to execute */ @@ -1898,6 +1899,7 @@ export interface ICommonCodeEditor extends IEditor { /** * Execute edits on the editor. + * The edits will land on the undo-redo stack, but no "undo stop" will be pushed. * @param source The source of the call. * @param edits The edits to execute. * @param endCursoState Cursor state after the edits were applied. diff --git a/src/vs/editor/common/model/textModelEvents.ts b/src/vs/editor/common/model/textModelEvents.ts index 53103be6e27dddff3e21fa43a827f609b6661c48..890dd25df46c5d0a3bf16d242043c1666eb3dbdc 100644 --- a/src/vs/editor/common/model/textModelEvents.ts +++ b/src/vs/editor/common/model/textModelEvents.ts @@ -243,4 +243,14 @@ export class ModelRawContentChangedEvent { this.isUndoing = isUndoing; this.isRedoing = isRedoing; } + + public containsEvent(type: RawContentChangedType): boolean { + for (let i = 0, len = this.changes.length; i < len; i++) { + const change = this.changes[i]; + if (change.changeType === type) { + return true; + } + } + return false; + } } diff --git a/src/vs/editor/contrib/caretOperations/common/caretOperations.ts b/src/vs/editor/contrib/caretOperations/common/caretOperations.ts index aec00037cb5c026b07d0f14073628d9db5a08e9e..96e935bd69d7856d9c58e18157fe6e38307e5257 100644 --- a/src/vs/editor/contrib/caretOperations/common/caretOperations.ts +++ b/src/vs/editor/contrib/caretOperations/common/caretOperations.ts @@ -29,7 +29,9 @@ class MoveCaretAction extends EditorAction { commands.push(new MoveCaretCommand(selections[i], this.left)); } + editor.pushUndoStop(); editor.executeCommands(this.id, commands); + editor.pushUndoStop(); } } diff --git a/src/vs/editor/contrib/caretOperations/common/transpose.ts b/src/vs/editor/contrib/caretOperations/common/transpose.ts index fbb878058565510fe7128f41518f75497f6de7e7..5ca66ce287d6a7551c43dc39861f72e0b708232f 100644 --- a/src/vs/editor/contrib/caretOperations/common/transpose.ts +++ b/src/vs/editor/contrib/caretOperations/common/transpose.ts @@ -63,7 +63,9 @@ class TransposeLettersAction extends EditorAction { } if (commands.length > 0) { + editor.pushUndoStop(); editor.executeCommands(this.id, commands); + editor.pushUndoStop(); } } } diff --git a/src/vs/editor/contrib/comment/common/comment.ts b/src/vs/editor/contrib/comment/common/comment.ts index 570d96b7cbb2e47d271ebaa5f00bea968383c59a..898e1aa89d0b949326f00e719b22d711db1ef915 100644 --- a/src/vs/editor/contrib/comment/common/comment.ts +++ b/src/vs/editor/contrib/comment/common/comment.ts @@ -35,7 +35,9 @@ abstract class CommentLineAction extends EditorAction { commands.push(new LineCommentCommand(selections[i], opts.tabSize, this._type)); } + editor.pushUndoStop(); editor.executeCommands(this.id, commands); + editor.pushUndoStop(); } } @@ -113,6 +115,8 @@ class BlockCommentAction extends EditorAction { commands.push(new BlockCommentCommand(selections[i])); } + editor.pushUndoStop(); editor.executeCommands(this.id, commands); + editor.pushUndoStop(); } } diff --git a/src/vs/editor/contrib/dnd/browser/dnd.ts b/src/vs/editor/contrib/dnd/browser/dnd.ts index cca6986ce3304d5a352548b34f38aed3c33c55b8..49a944af722d9983801f3628b22b174fe421fdb6 100644 --- a/src/vs/editor/contrib/dnd/browser/dnd.ts +++ b/src/vs/editor/contrib/dnd/browser/dnd.ts @@ -146,7 +146,9 @@ export class DragAndDropController implements editorCommon.IEditorContribution { this._dragSelection.getEndPosition().equals(newCursorPosition) || this._dragSelection.getStartPosition().equals(newCursorPosition) ) // we allow users to paste content beside the selection )) { + this._editor.pushUndoStop(); this._editor.executeCommand(DragAndDropController.ID, new DragAndDropCommand(this._dragSelection, newCursorPosition, mouseEvent.event[DragAndDropController.TRIGGER_MODIFIER] || this._modiferPressed)); + this._editor.pushUndoStop(); } } diff --git a/src/vs/editor/contrib/find/common/findModel.ts b/src/vs/editor/contrib/find/common/findModel.ts index faa2f4ff9241991c5e3c81e8907201bae01fc217..02ccc8f4ea5dce3f01d4eee8d263ef985b99c20d 100644 --- a/src/vs/editor/contrib/find/common/findModel.ts +++ b/src/vs/editor/contrib/find/common/findModel.ts @@ -468,7 +468,9 @@ export class FindModelBoundToEditorModel { private _executeEditorCommand(source: string, command: editorCommon.ICommand): void { try { this._ignoreModelContentChanged = true; + this._editor.pushUndoStop(); this._editor.executeCommand(source, command); + this._editor.pushUndoStop(); } finally { this._ignoreModelContentChanged = false; } diff --git a/src/vs/editor/contrib/format/common/formatCommand.ts b/src/vs/editor/contrib/format/common/formatCommand.ts index dcf612c8d33d15e1732eae0a0c5d458ac3c23c68..8a77bff13e3287a8acd39a4586a417198c50c2d2 100644 --- a/src/vs/editor/contrib/format/common/formatCommand.ts +++ b/src/vs/editor/contrib/format/common/formatCommand.ts @@ -17,7 +17,9 @@ export class EditOperationsCommand implements editorCommon.ICommand { if (typeof cmd._newEol === 'number') { editor.getModel().setEOL(cmd._newEol); } + editor.pushUndoStop(); editor.executeCommand('formatEditsCommand', cmd); + editor.pushUndoStop(); } private _edits: TextEdit[]; diff --git a/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.ts b/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.ts index 8f8548bbc00bfb9593720b719987eaf510fd1ed3..773b69e156e6449d09a1b80772b084ab61daf9a0 100644 --- a/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.ts +++ b/src/vs/editor/contrib/inPlaceReplace/common/inPlaceReplace.ts @@ -112,7 +112,10 @@ class InPlaceReplaceController implements IEditorContribution { // Insert new text var command = new InPlaceReplaceCommand(editRange, selection, result.value); + + this.editor.pushUndoStop(); this.editor.executeCommand(source, command); + this.editor.pushUndoStop(); // add decoration this.decorationIds = this.editor.deltaDecorations(this.decorationIds, [{ diff --git a/src/vs/editor/contrib/indentation/common/indentation.ts b/src/vs/editor/contrib/indentation/common/indentation.ts index 44d1a1bdac36f1f399c3c147811765b6df947589..7fb494c770d071f34458b388db323ddb2cf0e705 100644 --- a/src/vs/editor/contrib/indentation/common/indentation.ts +++ b/src/vs/editor/contrib/indentation/common/indentation.ts @@ -164,7 +164,11 @@ export class IndentationToSpacesAction extends EditorAction { } let modelOpts = model.getOptions(); const command = new IndentationToSpacesCommand(editor.getSelection(), modelOpts.tabSize); + + editor.pushUndoStop(); editor.executeCommands(this.id, [command]); + editor.pushUndoStop(); + model.updateOptions({ insertSpaces: true }); @@ -191,7 +195,11 @@ export class IndentationToTabsAction extends EditorAction { } let modelOpts = model.getOptions(); const command = new IndentationToTabsCommand(editor.getSelection(), modelOpts.tabSize); + + editor.pushUndoStop(); editor.executeCommands(this.id, [command]); + editor.pushUndoStop(); + model.updateOptions({ insertSpaces: false }); diff --git a/src/vs/editor/contrib/linesOperations/common/linesOperations.ts b/src/vs/editor/contrib/linesOperations/common/linesOperations.ts index 54255479f3055edb50c14d898c3ee4d4d5952672..59acf7fcd23795d8e3a6bafca56ddb1c13bfb8ff 100644 --- a/src/vs/editor/contrib/linesOperations/common/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/common/linesOperations.ts @@ -39,7 +39,9 @@ abstract class AbstractCopyLinesAction extends EditorAction { commands.push(new CopyLinesCommand(selections[i], this.down)); } + editor.pushUndoStop(); editor.executeCommands(this.id, commands); + editor.pushUndoStop(); } } @@ -97,7 +99,9 @@ abstract class AbstractMoveLinesAction extends EditorAction { commands.push(new MoveLinesCommand(selections[i], this.down)); } + editor.pushUndoStop(); editor.executeCommands(this.id, commands); + editor.pushUndoStop(); } } @@ -151,7 +155,9 @@ abstract class AbstractSortLinesAction extends EditorAction { var command = new SortLinesCommand(editor.getSelection(), this.descending); + editor.pushUndoStop(); editor.executeCommands(this.id, [command]); + editor.pushUndoStop(); } } @@ -201,7 +207,9 @@ export class TrimTrailingWhitespaceAction extends EditorAction { var command = new TrimTrailingWhitespaceCommand(editor.getSelection()); + editor.pushUndoStop(); editor.executeCommands(this.id, [command]); + editor.pushUndoStop(); } } @@ -280,7 +288,9 @@ class DeleteLinesAction extends AbstractRemoveLinesAction { return new DeleteLinesCommand(op.startLineNumber, op.endLineNumber, op.positionColumn); }); + editor.pushUndoStop(); editor.executeCommands(this.id, commands); + editor.pushUndoStop(); } } @@ -694,7 +704,9 @@ export class TransposeAction extends EditorAction { } } + editor.pushUndoStop(); editor.executeCommands(this.id, commands); + editor.pushUndoStop(); } } @@ -725,7 +737,9 @@ export abstract class AbstractCaseAction extends EditorAction { } } + editor.pushUndoStop(); editor.executeCommands(this.id, commands); + editor.pushUndoStop(); } protected abstract _modifyText(text: string): string; diff --git a/src/vs/editor/contrib/wordOperations/common/wordOperations.ts b/src/vs/editor/contrib/wordOperations/common/wordOperations.ts index 0b1d96ee992fa418a0931d5c6d1b3f985664e2e2..b4b6a410f9d19b1ce5ee9c5c60df18897689e758 100644 --- a/src/vs/editor/contrib/wordOperations/common/wordOperations.ts +++ b/src/vs/editor/contrib/wordOperations/common/wordOperations.ts @@ -275,7 +275,9 @@ export abstract class DeleteWordCommand extends EditorCommand { return new ReplaceCommand(deleteRange, ''); }); + editor.pushUndoStop(); editor.executeCommands(this.id, commands); + editor.pushUndoStop(); } protected abstract _delete(wordSeparators: WordCharacterClassifier, model: IModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 140e5d7e7ae12a7dfc6827c28b9a5ac17fa08765..334b54b64b1d3711a8c2dde09c4602f5835ae130 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2294,6 +2294,7 @@ declare module monaco.editor { getAction(id: string): IEditorAction; /** * Execute a command on the editor. + * The edits will land on the undo-redo stack, but no "undo stop" will be pushed. * @param source The source of the call. * @param command The command to execute */ @@ -2304,6 +2305,7 @@ declare module monaco.editor { pushUndoStop(): boolean; /** * Execute edits on the editor. + * The edits will land on the undo-redo stack, but no "undo stop" will be pushed. * @param source The source of the call. * @param edits The edits to execute. * @param endCursoState Cursor state after the edits were applied.