diff --git a/src/vs/editor/contrib/suggest/browser/suggestController.ts b/src/vs/editor/contrib/suggest/browser/suggestController.ts index d33ce936fe5..1ead2fb39ee 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestController.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestController.ts @@ -353,7 +353,17 @@ export class SuggestController implements IEditorContribution { const scrollState = StableEditorScrollState.capture(this.editor); this.editor.executeEdits( 'suggestController.additionalTextEdits.sync', - item.completion.additionalTextEdits.map(edit => EditOperation.replaceMove(Range.lift(edit.range), edit.text)) + item.completion.additionalTextEdits.map(edit => { + let range = Range.lift(edit.range); + if (range.startLineNumber === item.position.lineNumber && range.startColumn > item.position.column) { + // shift additional edit when it is "after" the completion insertion position + const columnDelta = this.editor.getPosition()!.column - item.position.column; + const startColumnDelta = columnDelta; + const endColumnDelta = Range.spansMultipleLines(range) ? 0 : columnDelta; + range = new Range(range.startLineNumber, range.startColumn + startColumnDelta, range.endLineNumber, range.endColumn + endColumnDelta); + } + return EditOperation.replaceMove(range, edit.text); + }) ); scrollState.restoreRelativeVerticalPositionOfCursor(this.editor); diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts index f24030cb93e..381e2663d3e 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts @@ -616,4 +616,56 @@ suite('SuggestController', function () { await p2; }); + + test('Ranges where additionalTextEdits are applied are not appropriate when characters are typed #177591', async function () { + disposables.add(languageFeaturesService.completionProvider.register({ scheme: 'test-ctrl' }, { + _debugDisplayName: 'test', + provideCompletionItems(doc, pos) { + return { + suggestions: [{ + kind: CompletionItemKind.Snippet, + label: 'aaa', + insertText: 'aaa', + range: Range.fromPositions(pos), + additionalTextEdits: [{ + range: Range.fromPositions(pos.delta(0, 10)), + text: 'aaa' + }] + }] + }; + } + })); + + { // PART1 - no typing + editor.setValue(`123456789123456789`); + editor.setSelection(new Selection(1, 1, 1, 1)); + const p1 = Event.toPromise(controller.model.onDidSuggest); + controller.triggerSuggest(); + + const e = await p1; + assert.strictEqual(e.completionModel.items.length, 1); + assert.strictEqual(e.completionModel.items[0].textLabel, 'aaa'); + + controller.acceptSelectedSuggestion(false, false); + + assert.strictEqual(editor.getValue(), 'aaa1234567891aaa23456789'); + } + + { // PART2 - typing + editor.setValue(`123456789123456789`); + editor.setSelection(new Selection(1, 1, 1, 1)); + const p1 = Event.toPromise(controller.model.onDidSuggest); + controller.triggerSuggest(); + + const e = await p1; + assert.strictEqual(e.completionModel.items.length, 1); + assert.strictEqual(e.completionModel.items[0].textLabel, 'aaa'); + + editor.trigger('keyboard', 'type', { text: 'aa' }); + + controller.acceptSelectedSuggestion(false, false); + + assert.strictEqual(editor.getValue(), 'aaa1234567891aaa23456789'); + } + }); });