diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index f3aa1c65ad3050e172ceb8e3589ceec52059fdfc..26b1e1f45b8c41baff3d34ca979060dc209aed9e 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -22,7 +22,7 @@ import * as nls from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Context as SuggestContext, CompletionItem } from './suggest'; import { SuggestAlternatives } from './suggestAlternatives'; import { State, SuggestModel } from './suggestModel'; @@ -34,7 +34,7 @@ import { IdleValue } from 'vs/base/common/async'; import { isObject } from 'vs/base/common/types'; import { CommitCharacterController } from './suggestCommitCharacters'; import { IPosition } from 'vs/editor/common/core/position'; -import { TrackedRangeStickiness, ITextModel } from 'vs/editor/common/model'; +import { TrackedRangeStickiness, ITextModel, IWordAtPosition } from 'vs/editor/common/model'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import * as platform from 'vs/base/common/platform'; @@ -115,10 +115,10 @@ export class SuggestController implements IEditorContribution { const widget = this._instantiationService.createInstance(SuggestWidget, this._editor); this._toDispose.add(widget); - this._toDispose.add(widget.onDidSelect(item => this._insertSuggestion(item, false, true, true), this)); + this._toDispose.add(widget.onDidSelect(item => this._insertSuggestion(item, false, true, true, false), this)); // Wire up logic to accept a suggestion on certain characters - const commitCharacterController = new CommitCharacterController(this._editor, widget, item => this._insertSuggestion(item, false, true, false)); + const commitCharacterController = new CommitCharacterController(this._editor, widget, item => this._insertSuggestion(item, false, true, false, false)); this._toDispose.add(commitCharacterController); this._toDispose.add(this._model.onDidSuggest(e => { if (e.completionModel.items.length === 0) { @@ -223,7 +223,12 @@ export class SuggestController implements IEditorContribution { this._lineSuffix.dispose(); } - protected _insertSuggestion(event: ISelectedSuggestion | undefined, keepAlternativeSuggestions: boolean, undoStopBefore: boolean, undoStopAfter: boolean): void { + protected _insertSuggestion( + event: ISelectedSuggestion | undefined, + keepAlternativeSuggestions: boolean, + undoStopBefore: boolean, undoStopAfter: boolean, + overwriteSuffix: boolean + ): void { if (!event || !event.item) { this._alternatives.getValue().reset(); this._model.cancel(); @@ -237,8 +242,8 @@ export class SuggestController implements IEditorContribution { const model = this._editor.getModel(); const modelVersionNow = model.getAlternativeVersionId(); const { completion: suggestion, position } = event.item; - const editorColumn = this._editor.getPosition().column; - const columnDelta = editorColumn - position.column; + const editorPosition = this._editor.getPosition(); + const columnDelta = editorPosition.column - position.column; // pushing undo stops *before* additional text edits and // *after* the main edit @@ -251,7 +256,7 @@ export class SuggestController implements IEditorContribution { } // keep item in memory - this._memoryService.memorize(model, this._editor.getPosition(), event.item); + this._memoryService.memorize(model, editorPosition, event.item); let { insertText } = suggestion; if (!(suggestion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet)) { @@ -260,7 +265,12 @@ export class SuggestController implements IEditorContribution { const overwriteBefore = position.column - suggestion.range.startColumn; const overwriteAfter = suggestion.range.endColumn - position.column; - const suffixDelta = this._lineSuffix.value ? this._lineSuffix.value.delta(this._editor.getPosition()) : 0; + + let suffixDelta = this._lineSuffix.value ? this._lineSuffix.value.delta(position) : 0; + let word: IWordAtPosition | null; + if (overwriteAfter === 0 && overwriteSuffix && (word = model.getWordAtPosition(position))) { + suffixDelta += word.endColumn - position.column; + } SnippetController2.get(this._editor).insert(insertText, { overwriteBefore: overwriteBefore + columnDelta, @@ -300,7 +310,7 @@ export class SuggestController implements IEditorContribution { if (modelVersionNow !== model.getAlternativeVersionId()) { model.undo(); } - this._insertSuggestion(next, false, false, false); + this._insertSuggestion(next, false, false, false, overwriteSuffix); break; } }); @@ -382,7 +392,7 @@ export class SuggestController implements IEditorContribution { return; } this._editor.pushUndoStop(); - this._insertSuggestion({ index, item, model: completionModel }, true, false, false); + this._insertSuggestion({ index, item, model: completionModel }, true, false, false, false); }, undefined, listener); }); @@ -392,9 +402,9 @@ export class SuggestController implements IEditorContribution { this._editor.focus(); } - acceptSelectedSuggestion(keepAlternativeSuggestions?: boolean): void { + acceptSelectedSuggestion(keepAlternativeSuggestions: boolean, overwriteSuffix: boolean): void { const item = this._widget.getValue().getFocusedItem(); - this._insertSuggestion(item, !!keepAlternativeSuggestions, true, true); + this._insertSuggestion(item, keepAlternativeSuggestions, true, true, overwriteSuffix); } acceptNextSuggestion() { @@ -489,18 +499,37 @@ const SuggestCommand = EditorCommand.bindToContribution(Sugge registerEditorCommand(new SuggestCommand({ id: 'acceptSelectedSuggestion', precondition: SuggestContext.Visible, - handler: x => x.acceptSelectedSuggestion(true), kbOpts: { weight: weight, kbExpr: EditorContextKeys.textInputFocus, primary: KeyCode.Tab + }, + handler(x, args) { + let overwriteSuffix: boolean; + if (typeof args !== 'object') { + overwriteSuffix = false; + } else if (typeof args.overwriteSuffix !== 'boolean') { + overwriteSuffix = false; + } else { + overwriteSuffix = args.overwriteSuffix; + } + x.acceptSelectedSuggestion(true, overwriteSuffix); } })); +KeybindingsRegistry.registerKeybindingRule({ + id: 'acceptSelectedSuggestion', + when: ContextKeyExpr.and(SuggestContext.Visible, EditorContextKeys.textInputFocus), + primary: KeyMod.Shift | KeyCode.Tab, + secondary: [KeyMod.Shift | KeyCode.Enter], + args: { overwriteSuffix: true }, + weight +}); + registerEditorCommand(new SuggestCommand({ id: 'acceptSelectedSuggestionOnEnter', precondition: SuggestContext.Visible, - handler: x => x.acceptSelectedSuggestion(false), + handler: x => x.acceptSelectedSuggestion(false, false), kbOpts: { weight: weight, kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, SuggestContext.AcceptSuggestionsOnEnter, SuggestContext.MakesTextEdit), diff --git a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts index e489938804e3ed21540bf027bb18ec53b552a7d1..88448ea0db71565793555b630d7849ae9dbd8f73 100644 --- a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts @@ -676,7 +676,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { return withOracle(async (sugget, editor) => { class TestCtrl extends SuggestController { _insertSuggestion(item: ISelectedSuggestion) { - super._insertSuggestion(item, false, true, true); + super._insertSuggestion(item, false, true, true, false); } } const ctrl = editor.registerAndInstantiateContribution(TestCtrl); diff --git a/src/vs/platform/keybinding/common/keybindingsRegistry.ts b/src/vs/platform/keybinding/common/keybindingsRegistry.ts index 3c30daf3ab475554fbf4017a77a3fb6a3073d43b..5c3de02794b758e13902ac7bd53bd807da7ee590 100644 --- a/src/vs/platform/keybinding/common/keybindingsRegistry.ts +++ b/src/vs/platform/keybinding/common/keybindingsRegistry.ts @@ -38,6 +38,7 @@ export interface IKeybindings { export interface IKeybindingRule extends IKeybindings { id: string; weight: number; + args?: any; when: ContextKeyExpr | null | undefined; } @@ -132,7 +133,7 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { if (actualKb && actualKb.primary) { const kk = createKeybinding(actualKb.primary, OS); if (kk) { - this._registerDefaultKeybinding(kk, rule.id, undefined, rule.weight, 0, rule.when); + this._registerDefaultKeybinding(kk, rule.id, rule.args, rule.weight, 0, rule.when); } } @@ -141,7 +142,7 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { const k = actualKb.secondary[i]; const kk = createKeybinding(k, OS); if (kk) { - this._registerDefaultKeybinding(kk, rule.id, undefined, rule.weight, -i - 1, rule.when); + this._registerDefaultKeybinding(kk, rule.id, rule.args, rule.weight, -i - 1, rule.when); } } } diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index c4774d5c30b03d02d21cfe2265ea8b1f34234c02..3f37db94e301608e06f55eea073eab5fe40e2627 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -919,7 +919,7 @@ class AcceptReplInputAction extends EditorAction { } run(accessor: ServicesAccessor, editor: ICodeEditor): void | Promise { - SuggestController.get(editor).acceptSelectedSuggestion(); + SuggestController.get(editor).acceptSelectedSuggestion(false, false); accessor.get(IPrivateReplService).acceptReplInput(); } } @@ -941,7 +941,7 @@ class FilterReplAction extends EditorAction { } run(accessor: ServicesAccessor, editor: ICodeEditor): void | Promise { - SuggestController.get(editor).acceptSelectedSuggestion(); + SuggestController.get(editor).acceptSelectedSuggestion(false, false); accessor.get(IPrivateReplService).focusRepl(); } }