Skip to content

Commit

Permalink
rename selectQuickSuggestions to editor.suggest.selectionMode, al…
Browse files Browse the repository at this point in the history
…low to differentiate between auto suggestions trigger by typing (aka quick suggest) and by trigger characters

re #139825 and #169830
  • Loading branch information
jrieken committed Dec 23, 2022
1 parent f4bfb23 commit e0e0b57
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 76 deletions.
21 changes: 14 additions & 7 deletions src/vs/editor/common/config/editorOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4046,7 +4046,7 @@ export interface ISuggestOptions {
/**
* Select suggestions when triggered via quick suggest or trigger characters
*/
selectQuickSuggestions?: boolean;
selectionMode?: 'always' | 'never' | 'whenTriggerCharacter' | 'whenQuickSuggestion';
/**
* Enable or disable icons in suggestions. Defaults to true.
*/
Expand Down Expand Up @@ -4199,7 +4199,7 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, ISuggestOptio
snippetsPreventQuickSuggestions: true,
localityBonus: false,
shareSuggestSelections: false,
selectQuickSuggestions: true,
selectionMode: 'always',
showIcons: true,
showStatusBar: false,
preview: false,
Expand Down Expand Up @@ -4263,10 +4263,17 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, ISuggestOptio
default: defaults.shareSuggestSelections,
markdownDescription: nls.localize('suggest.shareSuggestSelections', "Controls whether remembered suggestion selections are shared between multiple workspaces and windows (needs `#editor.suggestSelection#`).")
},
'editor.suggest.selectQuickSuggestions': {
type: 'boolean',
default: defaults.selectQuickSuggestions,
markdownDescription: nls.localize('suggest.selectQuickSuggestions', "Controls whether the suggest widget becomes active when triggered via quick suggest or trigger characters.")
'editor.suggest.selectionMode': {
type: 'string',
enum: ['always', 'never', 'whenTriggerCharacter', 'whenQuickSuggestion'],
enumDescriptions: [
nls.localize('suggest.insertMode.always', "Always activate the suggest widget when triggering IntelliSense automatically."),
nls.localize('suggest.insertMode.never', "Never activate the suggest when when triggering IntelliSense automatically."),
nls.localize('suggest.insertMode.whenTriggerCharacter', "Activate the suggest widget only when triggering IntelliSense from a trigger character."),
nls.localize('suggest.insertMode.whenQuickSuggestion', "Activate the suggest widget only when triggering IntelliSense as you type."),
],
default: defaults.selectionMode,
markdownDescription: nls.localize('suggest.selectionMode', "Controls whether the suggest widget becomes active when triggered via quick suggest or trigger characters. Note that the widget is always active when explicitly invoked, e.g via `Ctrl+Space`.")
},
'editor.suggest.snippetsPreventQuickSuggestions': {
type: 'boolean',
Expand Down Expand Up @@ -4466,7 +4473,7 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, ISuggestOptio
snippetsPreventQuickSuggestions: boolean(input.snippetsPreventQuickSuggestions, this.defaultValue.filterGraceful),
localityBonus: boolean(input.localityBonus, this.defaultValue.localityBonus),
shareSuggestSelections: boolean(input.shareSuggestSelections, this.defaultValue.shareSuggestSelections),
selectQuickSuggestions: boolean(input.selectQuickSuggestions, this.defaultValue.selectQuickSuggestions),
selectionMode: stringSet(input.selectionMode, this.defaultValue.selectionMode, ['always', 'never', 'whenQuickSuggestion', 'whenTriggerCharacter']),
showIcons: boolean(input.showIcons, this.defaultValue.showIcons),
showStatusBar: boolean(input.showStatusBar, this.defaultValue.showStatusBar),
preview: boolean(input.preview, this.defaultValue.preview),
Expand Down
29 changes: 21 additions & 8 deletions src/vs/editor/contrib/suggest/browser/suggestController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { Range } from 'vs/editor/common/core/range';
import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model';
import { CompletionItemInsertTextRule, CompletionItemProvider } from 'vs/editor/common/languages';
import { CompletionItemInsertTextRule, CompletionItemProvider, CompletionTriggerKind } from 'vs/editor/common/languages';
import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2';
import { SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser';
import { ISuggestMemoryService } from 'vs/editor/contrib/suggest/browser/suggestMemory';
Expand Down Expand Up @@ -230,7 +230,7 @@ export class SuggestController implements IEditorContribution {
this._lineSuffix.value = new LineSuffix(this.editor.getModel()!, e.position);
}));
this._toDispose.add(this.model.onDidSuggest(e => {
if (e.shy) {
if (e.triggerOptions.shy) {
return;
}
let index = -1;
Expand All @@ -245,16 +245,29 @@ export class SuggestController implements IEditorContribution {
}

let noFocus = false;
if (e.auto) {
// don't "focus" item when configure to do so or when in snippet mode (and configured to do so)
if (e.triggerOptions.auto) {
// don't "focus" item when configured to do so or when in snippet mode (and configured to do so)
const options = this.editor.getOption(EditorOption.suggest);
if (!options.selectQuickSuggestions) {
noFocus = true;
} else if (options.snippetsPreventQuickSuggestions && SnippetController2.get(this.editor)?.isInSnippet()) {

if (options.snippetsPreventQuickSuggestions && SnippetController2.get(this.editor)?.isInSnippet()) {
// SPECIAL: in snippet mode, we never focus unless the user wants to
noFocus = true;

} else if (options.selectionMode === 'never' || options.selectionMode === 'always') {
// simple: always or never
noFocus = options.selectionMode === 'never';

} else if (options.selectionMode === 'whenTriggerCharacter') {
// on with trigger character
noFocus = e.triggerOptions.triggerKind !== CompletionTriggerKind.TriggerCharacter;

} else if (options.selectionMode === 'whenQuickSuggestion') {
// without trigger character or when refiltering
noFocus = e.triggerOptions.triggerKind === CompletionTriggerKind.TriggerCharacter && !e.triggerOptions.refilter;
}

}
this.widget.value.showSuggestions(e.completionModel, index, e.isFrozen, e.auto, noFocus);
this.widget.value.showSuggestions(e.completionModel, index, e.isFrozen, e.triggerOptions.auto, noFocus);
}));
this._toDispose.add(this.model.onDidCancel(e => {
if (!e.retrigger) {
Expand Down
68 changes: 36 additions & 32 deletions src/vs/editor/contrib/suggest/browser/suggestModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ export interface ITriggerEvent {
export interface ISuggestEvent {
readonly completionModel: CompletionModel;
readonly isFrozen: boolean;
readonly auto: boolean;
readonly shy: boolean;
readonly triggerOptions: SuggestTriggerOptions;
}

export interface SuggestTriggerOptions {
readonly auto: boolean;
readonly shy?: boolean;
readonly refilter?: boolean;
readonly retrigger?: boolean;
readonly triggerKind?: CompletionTriggerKind;
readonly triggerCharacter?: string;
Expand Down Expand Up @@ -84,16 +84,14 @@ export class LineContext {
readonly column: number;
readonly leadingLineContent: string;
readonly leadingWord: IWordAtPosition;
readonly auto: boolean;
readonly shy: boolean;
readonly triggerOptions: SuggestTriggerOptions;

constructor(model: ITextModel, position: Position, auto: boolean, shy: boolean | undefined) {
constructor(model: ITextModel, position: Position, triggerOptions: SuggestTriggerOptions) {
this.leadingLineContent = model.getLineContent(position.lineNumber).substr(0, position.column - 1);
this.leadingWord = model.getWordUntilPosition(position);
this.lineNumber = position.lineNumber;
this.column = position.column;
this.auto = auto;
this.shy = shy ?? false;
this.triggerOptions = triggerOptions;
}
}

Expand Down Expand Up @@ -146,8 +144,8 @@ export class SuggestModel implements IDisposable {
private readonly _toDispose = new DisposableStore();
private readonly _triggerCharacterListener = new DisposableStore();
private readonly _triggerQuickSuggest = new TimeoutTimer();
private _state: State = State.Idle;

private _triggerState: SuggestTriggerOptions | undefined = undefined;
private _requestToken?: CancellationTokenSource;
private _context?: LineContext;
private _currentSelection: Selection;
Expand Down Expand Up @@ -209,7 +207,7 @@ export class SuggestModel implements IDisposable {
// only filter completions when the editor isn't composing a character
// allow-any-unicode-next-line
// e.g. ¨ + u makes ü but just ¨ cannot be used for filtering
if (!editorIsComposing) {
if (!editorIsComposing && this._triggerState !== undefined) {
this._refilterCompletionItems();
}
}));
Expand Down Expand Up @@ -292,6 +290,7 @@ export class SuggestModel implements IDisposable {

this.trigger({
auto: true,
triggerKind: CompletionTriggerKind.TriggerCharacter,
triggerCharacter: lastChar,
retrigger: Boolean(this._completionModel),
clipboardText: this._completionModel?.clipboardText,
Expand All @@ -307,15 +306,21 @@ export class SuggestModel implements IDisposable {
// --- trigger/retrigger/cancel suggest

get state(): State {
return this._state;
if (!this._triggerState) {
return State.Idle;
} else if (!this._triggerState.auto) {
return State.Manual;
} else {
return State.Auto;
}
}

cancel(retrigger: boolean = false): void {
if (this._state !== State.Idle) {
if (this._triggerState !== undefined) {
this._triggerQuickSuggest.cancel();
this._requestToken?.cancel();
this._requestToken = undefined;
this._state = State.Idle;
this._triggerState = undefined;
this._completionModel = undefined;
this._context = undefined;
this._onDidCancel.fire({ retrigger });
Expand All @@ -327,11 +332,11 @@ export class SuggestModel implements IDisposable {
}

private _updateActiveSuggestSession(): void {
if (this._state !== State.Idle) {
if (this._triggerState !== undefined) {
if (!this._editor.hasModel() || !this._languageFeaturesService.completionProvider.has(this._editor.getModel())) {
this.cancel();
} else {
this.trigger({ auto: this._state === State.Auto, retrigger: true });
this.trigger({ auto: this._triggerState.auto, retrigger: true });
}
}
}
Expand All @@ -356,13 +361,13 @@ export class SuggestModel implements IDisposable {
}


if (this._state === State.Idle && e.reason === CursorChangeReason.NotSet) {
if (this._triggerState === undefined && e.reason === CursorChangeReason.NotSet) {
if (prevSelection.containsRange(this._currentSelection) || prevSelection.getEndPosition().isBeforeOrEqual(this._currentSelection.getPosition())) {
// cursor did move RIGHT due to typing -> trigger quick suggest
this._doTriggerQuickSuggest();
}

} else if (this._state !== State.Idle && e.reason === CursorChangeReason.Explicit) {
} else if (this._triggerState !== undefined && e.reason === CursorChangeReason.Explicit) {
// suggest is active and something like cursor keys are used to move
// the cursor. this means we can refilter at the new position
this._refilterCompletionItems();
Expand All @@ -371,7 +376,7 @@ export class SuggestModel implements IDisposable {

private _onCompositionEnd(): void {
// trigger or refilter when composition ends
if (this._state === State.Idle) {
if (this._triggerState === undefined) {
this._doTriggerQuickSuggest();
} else {
this._refilterCompletionItems();
Expand All @@ -388,7 +393,7 @@ export class SuggestModel implements IDisposable {
this.cancel();

this._triggerQuickSuggest.cancelAndSet(() => {
if (this._state !== State.Idle) {
if (this._triggerState !== undefined) {
return;
}
if (!LineContext.shouldAutoTrigger(this._editor)) {
Expand Down Expand Up @@ -432,10 +437,11 @@ export class SuggestModel implements IDisposable {

private _refilterCompletionItems(): void {
assertType(this._editor.hasModel());
assertType(this._triggerState !== undefined);

const model = this._editor.getModel();
const position = this._editor.getPosition();
const ctx = new LineContext(model, position, this._state === State.Auto, false);
const ctx = new LineContext(model, position, { ...this._triggerState, refilter: true });
this._onNewContext(ctx);
}

Expand All @@ -445,13 +451,12 @@ export class SuggestModel implements IDisposable {
}

const model = this._editor.getModel();
const auto = options.auto;
const ctx = new LineContext(model, this._editor.getPosition(), auto, options.shy);
const ctx = new LineContext(model, this._editor.getPosition(), options);

// Cancel previous requests, change state & update UI
this.cancel(options.retrigger);
this._state = auto ? State.Auto : State.Manual;
this._onDidTrigger.fire({ auto, shy: options.shy ?? false, position: this._editor.getPosition() });
this._triggerState = options;
this._onDidTrigger.fire({ auto: options.auto, shy: options.shy ?? false, position: this._editor.getPosition() });

// Capture context when request was sent
this._context = ctx;
Expand Down Expand Up @@ -509,7 +514,7 @@ export class SuggestModel implements IDisposable {
clipboardText = await this._clipboardService.readText();
}

if (this._state === State.Idle) {
if (this._triggerState === undefined) {
return;
}

Expand All @@ -521,7 +526,7 @@ export class SuggestModel implements IDisposable {
// items = items.concat(existing.items).sort(cmpFn);
// }

const ctx = new LineContext(model, this._editor.getPosition(), auto, options.shy);
const ctx = new LineContext(model, this._editor.getPosition(), options);
const fuzzySearchOptions = {
...FuzzyScoreOptions.default,
firstMatchCanBeWeak: !this._editor.getOption(EditorOption.suggest).matchOnWordStartOnly
Expand Down Expand Up @@ -635,7 +640,7 @@ export class SuggestModel implements IDisposable {
if (ctx.column < this._context.column) {
// typed -> moved cursor LEFT -> retrigger if still on a word
if (ctx.leadingWord.word) {
this.trigger({ auto: this._context.auto, retrigger: true });
this.trigger({ auto: this._context.triggerOptions.auto, retrigger: true });
} else {
this.cancel();
}
Expand All @@ -652,7 +657,7 @@ export class SuggestModel implements IDisposable {
if (LineContext.shouldAutoTrigger(this._editor)) {
const map = this._completionModel.getItemsByProvider();
this.trigger({
auto: this._context.auto,
auto: this._context.triggerOptions.auto,
retrigger: true,
clipboardText: this._completionModel.clipboardText,
completionOptions: { providerItemsToReuse: map }
Expand All @@ -675,7 +680,7 @@ export class SuggestModel implements IDisposable {
}

this.trigger({
auto: this._state === State.Auto,
auto: this._context.triggerOptions.auto,
triggerKind: CompletionTriggerKind.TriggerForIncompleteCompletions,
retrigger: true,
clipboardText: this._completionModel.clipboardText,
Expand Down Expand Up @@ -704,11 +709,11 @@ export class SuggestModel implements IDisposable {

if (shouldAutoTrigger && this._context.leadingWord.endColumn < ctx.leadingWord.startColumn) {
// retrigger when heading into a new word
this.trigger({ auto: this._context.auto, retrigger: true });
this.trigger({ auto: this._context.triggerOptions.auto, retrigger: true });
return;
}

if (!this._context.auto) {
if (!this._context.triggerOptions.auto) {
// freeze when IntelliSense was manually requested
this._completionModel.lineContext = oldLineContext;
isFrozen = this._completionModel.items.length > 0;
Expand All @@ -729,8 +734,7 @@ export class SuggestModel implements IDisposable {

this._onDidSuggest.fire({
completionModel: this._completionModel,
auto: this._context.auto,
shy: this._context.shy,
triggerOptions: ctx.triggerOptions,
isFrozen,
});
}
Expand Down
4 changes: 1 addition & 3 deletions src/vs/editor/contrib/suggest/browser/suggestWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -555,9 +555,7 @@ export class SuggestWidget implements IDisposable {
this._list.splice(0, this._list.length, this._completionModel.items);
this._setState(isFrozen ? State.Frozen : State.Open);
this._list.reveal(selectionIndex, 0);
if (!noFocus) {
this._list.setFocus([selectionIndex]);
}
this._list.setFocus(noFocus ? [] : [selectionIndex]);
} finally {
this._onDidFocus.resume();
this._onDidSelect.resume();
Expand Down
Loading

0 comments on commit e0e0b57

Please sign in to comment.