From fc26dad6cfab1fea6baa3a4bd9ed4d1e8bf59f77 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 10 Dec 2015 15:29:55 +0100 Subject: [PATCH] Fixes #231: Restore snippet mode if not entering snippet mode --- src/vs/editor/common/model/modelLine.ts | 73 +++++++++++-------- .../editor/contrib/snippet/common/snippet.ts | 32 +++++--- .../test/common/commands/sideEditing.test.ts | 22 ++++++ .../test/common/model/model.line.test.ts | 44 +++++++++++ 4 files changed, 131 insertions(+), 40 deletions(-) diff --git a/src/vs/editor/common/model/modelLine.ts b/src/vs/editor/common/model/modelLine.ts index 5e6626b0b8a..68e0d16aee9 100644 --- a/src/vs/editor/common/model/modelLine.ts +++ b/src/vs/editor/common/model/modelLine.ts @@ -243,18 +243,28 @@ export class ModelLine { return NO_OP_MARKERS_ADJUSTER; } + this._markers.sort(ModelLine._compareMarkers); + var markers = this._markers; var markersLength = markers.length; var markersIndex = 0; var marker = markers[markersIndex]; + // var printMarker = (m:ILineMarker) => { + // if (m.stickToPreviousCharacter) { + // return '|' + m.column; + // } + // return m.column + '|'; + // } // var printMarkers = () => { - // return '[' + markers.map( m => m.column).join(', ') + ']'; + // return '[' + markers.map(printMarker).join(', ') + ']'; // }; - let adjust = (toColumn:number, delta:number, minimumAllowedColumn:number, isReplace:boolean, forceMoveMarkers:boolean) => { + // console.log('------------- INITIAL MARKERS: ' + printMarkers()); + + let adjust = (toColumn:number, delta:number, minimumAllowedColumn:number, forceStickToPrevious:boolean, forceMoveMarkers:boolean) => { // console.log('------------------------------'); - // console.log('adjust called: toColumn: ' + toColumn + ', delta: ' + delta + ', minimumAllowedColumn: ' + minimumAllowedColumn + ', isReplace: ' + isReplace + ', forceMoveMarkers:' + forceMoveMarkers); + // console.log('adjust called: toColumn: ' + toColumn + ', delta: ' + delta + ', minimumAllowedColumn: ' + minimumAllowedColumn + ', forceStickToPrevious: ' + forceStickToPrevious + ', forceMoveMarkers:' + forceMoveMarkers); // console.log('BEFORE::: markersIndex: ' + markersIndex + ' : ' + printMarkers()); while ( markersIndex < markersLength @@ -263,7 +273,7 @@ export class ModelLine { || ( !forceMoveMarkers && marker.column === toColumn - && (isReplace || marker.stickToPreviousCharacter) + && (forceStickToPrevious || marker.stickToPreviousCharacter) ) ) ) { @@ -287,6 +297,8 @@ export class ModelLine { let finish = (delta:number, lineTextLength:number) => { adjust(Number.MAX_VALUE, delta, 1, false, false); + + // console.log('------------- FINAL MARKERS: ' + printMarkers()); }; return { @@ -296,39 +308,46 @@ export class ModelLine { } public applyEdits(changedMarkers: IChangedMarkers, edits:ILineEdit[]): number { - // console.log('--> applyEdits: ' + JSON.stringify(edits)); - var deltaColumn = 0; - var resultText = this.text; + let deltaColumn = 0; + let resultText = this.text; - var tokensAdjuster = this._createTokensAdjuster(); - var markersAdjuster = this._createMarkersAdjuster(changedMarkers); + let tokensAdjuster = this._createTokensAdjuster(); + let markersAdjuster = this._createMarkersAdjuster(changedMarkers); - for (var i = 0, len = edits.length; i < len; i++) { - let _oldStartColumn = edits[i].startColumn; - let _oldEndColumn = edits[i].endColumn; - // console.log('_oldStartColumn: ' + _oldStartColumn + ', _oldEndColumn: ' + _oldEndColumn); - let startColumn = deltaColumn + edits[i].startColumn; - let endColumn = deltaColumn + edits[i].endColumn; - let text = edits[i].text; + for (let i = 0, len = edits.length; i < len; i++) { + let edit = edits[i]; + + // console.log(); + // console.log('============================='); + // console.log('EDIT #' + i + ' [ ' + edit.startColumn + ' -> ' + edit.endColumn + ' ] : <<<' + edit.text + '>>>, forceMoveMarkers: ' + edit.forceMoveMarkers); + // console.log('deltaColumn: ' + deltaColumn); + + let startColumn = deltaColumn + edit.startColumn; + let endColumn = deltaColumn + edit.endColumn; + let deletingCnt = endColumn - startColumn; + let insertingCnt = edit.text.length; // Adjust tokens & markers before this edit - tokensAdjuster.adjust(_oldStartColumn - 1, deltaColumn, 1); - markersAdjuster.adjust(_oldStartColumn - 1 + 1, deltaColumn, 1, startColumn !== endColumn, edits[i].forceMoveMarkers); + // console.log('Adjust tokens & markers before this edit'); + tokensAdjuster.adjust(edit.startColumn - 1, deltaColumn, 1); + markersAdjuster.adjust(edit.startColumn, deltaColumn, 1, deletingCnt > 0, edit.forceMoveMarkers); // Adjust tokens & markers for the common part of this edit - let commonLength = Math.min(endColumn - startColumn, text.length); + let commonLength = Math.min(deletingCnt, insertingCnt); if (commonLength > 0) { - tokensAdjuster.adjust(_oldStartColumn - 1 + commonLength, deltaColumn, startColumn); - markersAdjuster.adjust(_oldStartColumn - 1 + 1 + commonLength, deltaColumn, startColumn, true, edits[i].forceMoveMarkers); + // console.log('Adjust tokens & markers for the common part of this edit'); + tokensAdjuster.adjust(edit.startColumn - 1 + commonLength, deltaColumn, startColumn); + markersAdjuster.adjust(edit.startColumn + commonLength, deltaColumn, startColumn, deletingCnt > insertingCnt, edit.forceMoveMarkers); } // Perform the edit & update `deltaColumn` - resultText = resultText.substring(0, startColumn - 1) + text + resultText.substring(endColumn - 1); - deltaColumn += text.length - (endColumn - startColumn); + resultText = resultText.substring(0, startColumn - 1) + edit.text + resultText.substring(endColumn - 1); + deltaColumn += insertingCnt - deletingCnt; // Adjust tokens & markers inside this edit - tokensAdjuster.adjust(_oldEndColumn, deltaColumn, startColumn); - markersAdjuster.adjust(_oldEndColumn + 1, deltaColumn, startColumn, false, edits[i].forceMoveMarkers); + // console.log('Adjust tokens & markers inside this edit'); + tokensAdjuster.adjust(edit.endColumn, deltaColumn, startColumn); + markersAdjuster.adjust(edit.endColumn, deltaColumn, startColumn, false, edit.forceMoveMarkers); } // Wrap up tokens & markers; adjust remaining if needed @@ -453,8 +472,6 @@ export class ModelLine { } else { this._markers.push(marker); } - - this._markers.sort(ModelLine._compareMarkers); } public addMarkers(markers:ILineMarker[]): void { @@ -474,8 +491,6 @@ export class ModelLine { } else { this._markers = this._markers.concat(markers); } - - this._markers.sort(ModelLine._compareMarkers); } private static _compareMarkers(a:ILineMarker, b:ILineMarker): number { diff --git a/src/vs/editor/contrib/snippet/common/snippet.ts b/src/vs/editor/contrib/snippet/common/snippet.ts index 5684336dc23..8e192f6be07 100644 --- a/src/vs/editor/contrib/snippet/common/snippet.ts +++ b/src/vs/editor/contrib/snippet/common/snippet.ts @@ -12,9 +12,9 @@ import EventEmitter = require('vs/base/common/eventEmitter'); import {CommonEditorRegistry} from 'vs/editor/common/editorCommonExtensions'; import {Range} from 'vs/editor/common/core/range'; import {Selection} from 'vs/editor/common/core/selection'; -import {ReplaceCommand} from 'vs/editor/common/commands/replaceCommand'; import {IKeybindingService, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybindingService'; import {KeyMod, KeyCode} from 'vs/base/common/keyCodes'; +import {EditOperation} from 'vs/editor/common/core/editOperation'; interface IParsedLinePlaceHolderInfo { id: string; @@ -735,7 +735,8 @@ class SnippetController implements ISnippetController { } public run(snippet:CodeSnippet, overwriteBefore:number, overwriteAfter:number): void { - this.dispose(); + let prevController = this._currentController; + this._currentController = null; if (snippet.placeHolders.length === 0) { // No placeholders => execute for all editor selections @@ -743,6 +744,16 @@ class SnippetController implements ISnippetController { } else { this._runForPrimarySelection(snippet, overwriteBefore, overwriteAfter); } + + if (!this._currentController) { + // we didn't end up in snippet mode again => restore previous controller + this._currentController = prevController; + } else { + // we ended up in snippet mode => dispose previous controller if necessary + if (prevController) { + prevController.dispose(); + } + } } private static _getTypeRangeForSelection(model:EditorCommon.IModel, selection:EditorCommon.IEditorSelection, overwriteBefore:number, overwriteAfter:number): EditorCommon.IEditorRange { @@ -764,16 +775,16 @@ class SnippetController implements ISnippetController { return snippet.bind(model.getLineContent(typeRange.startLineNumber), typeRange.startLineNumber - 1, typeRange.startColumn - 1, editor); } - private static _getCommandForSnippet(adaptedSnippet:ICodeSnippet, typeRange:EditorCommon.IEditorRange): EditorCommon.ICommand { + private static _getCommandForSnippet(adaptedSnippet:ICodeSnippet, typeRange:EditorCommon.IEditorRange): EditorCommon.IIdentifiedSingleEditOperation { var insertText = adaptedSnippet.lines.join('\n'); - return new ReplaceCommand(typeRange, insertText); + return EditOperation.replace(typeRange, insertText); } private _runForPrimarySelection(snippet: CodeSnippet, overwriteBefore: number, overwriteAfter: number): void { var initialAlternativeVersionId = this._editor.getModel().getAlternativeVersionId(); var prepared = SnippetController._prepareSnippet(this._editor, this._editor.getSelection(), snippet, overwriteBefore, overwriteAfter); - this._editor.executeCommand('editor.contrib.insertSnippetHelper', SnippetController._getCommandForSnippet(prepared.adaptedSnippet, prepared.typeRange)); + this._editor.executeEdits('editor.contrib.insertSnippetHelper', [SnippetController._getCommandForSnippet(prepared.adaptedSnippet, prepared.typeRange)]); var cursorOnly = SnippetController._getSnippetCursorOnly(prepared.adaptedSnippet); if (cursorOnly) { @@ -787,16 +798,15 @@ class SnippetController implements ISnippetController { } private _runForAllSelections(snippet:CodeSnippet, overwriteBefore:number, overwriteAfter:number): void { - var selections = this._editor.getSelections(), - i:number, - commands:EditorCommon.ICommand[] = []; + let selections = this._editor.getSelections(), + edits:EditorCommon.IIdentifiedSingleEditOperation[] = []; - for (i = 0; i < selections.length; i++) { + for (let i = 0; i < selections.length; i++) { var prepared = SnippetController._prepareSnippet(this._editor, selections[i], snippet, overwriteBefore, overwriteAfter); - commands.push(SnippetController._getCommandForSnippet(prepared.adaptedSnippet, prepared.typeRange)); + edits.push(SnippetController._getCommandForSnippet(prepared.adaptedSnippet, prepared.typeRange)); } - this._editor.executeCommands('editor.contrib.insertSnippetHelper', commands); + this._editor.executeEdits('editor.contrib.insertSnippetHelper', edits); } private static _prepareSnippet(editor:EditorCommon.ICommonCodeEditor, selection:EditorCommon.IEditorSelection, snippet:CodeSnippet, overwriteBefore:number, overwriteAfter:number): { typeRange: EditorCommon.IEditorRange; adaptedSnippet: ICodeSnippet; } { diff --git a/src/vs/editor/test/common/commands/sideEditing.test.ts b/src/vs/editor/test/common/commands/sideEditing.test.ts index 70e1f02b70a..fde849a3baf 100644 --- a/src/vs/editor/test/common/commands/sideEditing.test.ts +++ b/src/vs/editor/test/common/commands/sideEditing.test.ts @@ -77,6 +77,28 @@ suite('Editor Side Editing - collapsed selection', () => { ); }); + test('replace at selection 2', () => { + testCommand( + [ + 'first', + 'second line', + 'third line', + 'fourth' + ], + new Selection(1,1,1,6), + [ + EditOperation.replace(new Selection(1,1,1,6), 'something') + ], + [ + 'something', + 'second line', + 'third line', + 'fourth' + ], + new Selection(1,1,1,10) + ); + }); + test('ModelLine.applyEdits uses `isReplace`', () => { testLineEditMarker('something', 1, true, { startColumn: 1, endColumn: 1, text: 'asd', forceMoveMarkers: false }, 1); testLineEditMarker('something', 1, true, { startColumn: 1, endColumn: 1, text: 'asd', forceMoveMarkers: true }, 4); diff --git a/src/vs/editor/test/common/model/model.line.test.ts b/src/vs/editor/test/common/model/model.line.test.ts index c589a50fb91..df3d189def3 100644 --- a/src/vs/editor/test/common/model/model.line.test.ts +++ b/src/vs/editor/test/common/model/model.line.test.ts @@ -1607,6 +1607,28 @@ suite('Editor Model - ModelLine.applyEdits text & markers', () => { ); }); + test('delete near markers', () => { + testLineEditMarkers( + 'abcd', + [ + marker(1, 3, true), + marker(2, 3, false) + ], + [{ + startColumn: 3, + endColumn: 4, + text: '', + forceMoveMarkers: false + }], + 'abd', + [], + [ + marker(1, 3, true), + marker(2, 3, false) + ] + ); + }); + test('replace: updates markers 2', () => { testLineEditMarkers( 'Hello world, how are you', @@ -1788,6 +1810,28 @@ suite('Editor Model - ModelLine.applyEdits text & markers', () => { ] ); }); + + test('replace selection', () => { + testLineEditMarkers( + 'first', + [ + marker(1, 1, true), + marker(2, 6, false), + ], + [{ + startColumn: 1, + endColumn: 6, + text: 'something', + forceMoveMarkers: false + }], + 'something', + [2], + [ + marker(1, 1, true), + marker(2, 10, false), + ] + ); + }); }); suite('Editor Model - ModelLine.split text & markers', () => {