move more logic into suggest model
This commit is contained in:
parent
7db3469c87
commit
d7771265a8
|
@ -7,13 +7,10 @@
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||||
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
|
||||||
import { forEach } from 'vs/base/common/collections';
|
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||||
import { ICommonCodeEditor, IEditorContribution, EditorContextKeys, ModeContextKeys } from 'vs/editor/common/editorCommon';
|
import { ICommonCodeEditor, IEditorContribution, EditorContextKeys, ModeContextKeys } from 'vs/editor/common/editorCommon';
|
||||||
import { editorAction, ServicesAccessor, EditorAction, EditorCommand, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
|
import { editorAction, ServicesAccessor, EditorAction, EditorCommand, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
|
||||||
import { ISuggestSupport, SuggestRegistry } from 'vs/editor/common/modes';
|
|
||||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||||
import { EditorBrowserRegistry } from 'vs/editor/browser/editorBrowserExtensions';
|
import { EditorBrowserRegistry } from 'vs/editor/browser/editorBrowserExtensions';
|
||||||
import { getSnippetController, CodeSnippet } from 'vs/editor/contrib/snippet/common/snippet';
|
import { getSnippetController, CodeSnippet } from 'vs/editor/contrib/snippet/common/snippet';
|
||||||
|
@ -31,7 +28,7 @@ export class SuggestController implements IEditorContribution {
|
||||||
|
|
||||||
private model: SuggestModel;
|
private model: SuggestModel;
|
||||||
private widget: SuggestWidget;
|
private widget: SuggestWidget;
|
||||||
private triggerCharacterListeners: IDisposable[];
|
|
||||||
private toDispose: IDisposable[] = [];
|
private toDispose: IDisposable[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -39,22 +36,12 @@ export class SuggestController implements IEditorContribution {
|
||||||
@IInstantiationService instantiationService: IInstantiationService
|
@IInstantiationService instantiationService: IInstantiationService
|
||||||
) {
|
) {
|
||||||
this.model = new SuggestModel(this.editor);
|
this.model = new SuggestModel(this.editor);
|
||||||
this.widget = instantiationService.createInstance(SuggestWidget, this.editor);
|
|
||||||
|
|
||||||
this.toDispose.push(this.model.onDidTrigger(e => this.widget.showTriggered(e)));
|
this.toDispose.push(this.model.onDidTrigger(e => this.widget.showTriggered(e)));
|
||||||
this.toDispose.push(this.model.onDidSuggest(e => this.widget.showSuggestions(e)));
|
this.toDispose.push(this.model.onDidSuggest(e => this.widget.showSuggestions(e)));
|
||||||
this.toDispose.push(this.model.onDidCancel(e => this.widget.showDidCancel(e)));
|
this.toDispose.push(this.model.onDidCancel(e => this.widget.showDidCancel(e)));
|
||||||
|
|
||||||
|
this.widget = instantiationService.createInstance(SuggestWidget, this.editor);
|
||||||
this.toDispose.push(this.widget.onDidSelect(this.onDidSelectItem, this));
|
this.toDispose.push(this.widget.onDidSelect(this.onDidSelectItem, this));
|
||||||
|
|
||||||
this.triggerCharacterListeners = [];
|
|
||||||
|
|
||||||
this.toDispose.push(editor.onDidChangeConfiguration(() => this.update()));
|
|
||||||
this.toDispose.push(editor.onDidChangeModel(() => this.update()));
|
|
||||||
this.toDispose.push(editor.onDidChangeModelMode(() => this.update()));
|
|
||||||
this.toDispose.push(SuggestRegistry.onDidChange(this.update, this));
|
|
||||||
|
|
||||||
this.update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getId(): string {
|
getId(): string {
|
||||||
|
@ -63,8 +50,6 @@ export class SuggestController implements IEditorContribution {
|
||||||
|
|
||||||
dispose(): void {
|
dispose(): void {
|
||||||
this.toDispose = dispose(this.toDispose);
|
this.toDispose = dispose(this.toDispose);
|
||||||
this.triggerCharacterListeners = dispose(this.triggerCharacterListeners);
|
|
||||||
|
|
||||||
if (this.widget) {
|
if (this.widget) {
|
||||||
this.widget.dispose();
|
this.widget.dispose();
|
||||||
this.widget = null;
|
this.widget = null;
|
||||||
|
@ -88,39 +73,6 @@ export class SuggestController implements IEditorContribution {
|
||||||
this.model.cancel();
|
this.model.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private update(): void {
|
|
||||||
|
|
||||||
this.triggerCharacterListeners = dispose(this.triggerCharacterListeners);
|
|
||||||
|
|
||||||
if (this.editor.getConfiguration().readOnly
|
|
||||||
|| !this.editor.getModel()
|
|
||||||
|| !this.editor.getConfiguration().contribInfo.suggestOnTriggerCharacters) {
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const supportsByTriggerCharacter: { [ch: string]: ISuggestSupport[] } = Object.create(null);
|
|
||||||
for (const support of SuggestRegistry.all(this.editor.getModel())) {
|
|
||||||
if (isFalsyOrEmpty(support.triggerCharacters)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (const ch of support.triggerCharacters) {
|
|
||||||
const array = supportsByTriggerCharacter[ch];
|
|
||||||
if (!array) {
|
|
||||||
supportsByTriggerCharacter[ch] = [support];
|
|
||||||
} else {
|
|
||||||
array.push(support);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
forEach(supportsByTriggerCharacter, entry => {
|
|
||||||
this.triggerCharacterListeners.push(this.editor.addTypingListener(entry.key, () => {
|
|
||||||
this.model.trigger(true, false, entry.value);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
triggerSuggest(): void {
|
triggerSuggest(): void {
|
||||||
this.model.trigger(false, false);
|
this.model.trigger(false, false);
|
||||||
this.editor.focus();
|
this.editor.focus();
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import {onUnexpectedError} from 'vs/base/common/errors';
|
import {onUnexpectedError} from 'vs/base/common/errors';
|
||||||
|
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||||
|
import { forEach } from 'vs/base/common/collections';
|
||||||
import Event, { Emitter } from 'vs/base/common/event';
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
|
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
|
||||||
import {startsWith} from 'vs/base/common/strings';
|
import {startsWith} from 'vs/base/common/strings';
|
||||||
|
@ -140,8 +142,9 @@ enum State {
|
||||||
|
|
||||||
export class SuggestModel implements IDisposable {
|
export class SuggestModel implements IDisposable {
|
||||||
|
|
||||||
private toDispose: IDisposable[];
|
private toDispose: IDisposable[] = [];
|
||||||
private autoSuggestDelay: number;
|
private quickSuggestDelay: number;
|
||||||
|
private triggerCharacterListeners: IDisposable[] = [];
|
||||||
|
|
||||||
private triggerAutoSuggestPromise: TPromise<void>;
|
private triggerAutoSuggestPromise: TPromise<void>;
|
||||||
private state: State;
|
private state: State;
|
||||||
|
@ -171,15 +174,83 @@ export class SuggestModel implements IDisposable {
|
||||||
this.incomplete = false;
|
this.incomplete = false;
|
||||||
this.context = null;
|
this.context = null;
|
||||||
|
|
||||||
this.toDispose = [];
|
// wire up various listeners
|
||||||
this.toDispose.push(this._onDidCancel, this._onDidSuggest, this._onDidTrigger);
|
this.toDispose.push(this.editor.onDidChangeModel(() => {
|
||||||
this.toDispose.push(this.editor.onDidChangeConfiguration(() => this.onEditorConfigurationChange()));
|
this.updateTriggerCharacters();
|
||||||
this.toDispose.push(this.editor.onDidChangeCursorSelection(e => this.onCursorChange(e)));
|
this.cancel();
|
||||||
this.toDispose.push(this.editor.onDidChangeModel(() => this.cancel()));
|
}));
|
||||||
this.toDispose.push(SuggestRegistry.onDidChange(this.onSuggestRegistryChange, this));
|
this.toDispose.push(editor.onDidChangeModelMode(() => {
|
||||||
this.onEditorConfigurationChange();
|
this.updateTriggerCharacters();
|
||||||
|
this.cancel();
|
||||||
|
}));
|
||||||
|
this.toDispose.push(this.editor.onDidChangeConfiguration(() => {
|
||||||
|
this.updateTriggerCharacters();
|
||||||
|
this.updateQuickSuggest();
|
||||||
|
}));
|
||||||
|
this.toDispose.push(SuggestRegistry.onDidChange(() => {
|
||||||
|
this.updateTriggerCharacters();
|
||||||
|
this.updateActiveSuggestSession();
|
||||||
|
}));
|
||||||
|
this.toDispose.push(this.editor.onDidChangeCursorSelection(e => {
|
||||||
|
this.onCursorChange(e);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.updateTriggerCharacters();
|
||||||
|
this.updateQuickSuggest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispose(): void {
|
||||||
|
dispose([this._onDidCancel, this._onDidSuggest, this._onDidTrigger]);
|
||||||
|
this.toDispose = dispose(this.toDispose);
|
||||||
|
this.triggerCharacterListeners = dispose(this.triggerCharacterListeners);
|
||||||
|
this.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- handle configuration & precondition changes
|
||||||
|
|
||||||
|
private updateQuickSuggest(): void {
|
||||||
|
this.quickSuggestDelay = this.editor.getConfiguration().contribInfo.quickSuggestionsDelay;
|
||||||
|
|
||||||
|
if (isNaN(this.quickSuggestDelay) || (!this.quickSuggestDelay && this.quickSuggestDelay !== 0) || this.quickSuggestDelay < 0) {
|
||||||
|
this.quickSuggestDelay = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateTriggerCharacters(): void {
|
||||||
|
|
||||||
|
this.triggerCharacterListeners = dispose(this.triggerCharacterListeners);
|
||||||
|
|
||||||
|
if (this.editor.getConfiguration().readOnly
|
||||||
|
|| !this.editor.getModel()
|
||||||
|
|| !this.editor.getConfiguration().contribInfo.suggestOnTriggerCharacters) {
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const supportsByTriggerCharacter: { [ch: string]: ISuggestSupport[] } = Object.create(null);
|
||||||
|
for (const support of SuggestRegistry.all(this.editor.getModel())) {
|
||||||
|
if (isFalsyOrEmpty(support.triggerCharacters)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const ch of support.triggerCharacters) {
|
||||||
|
const array = supportsByTriggerCharacter[ch];
|
||||||
|
if (!array) {
|
||||||
|
supportsByTriggerCharacter[ch] = [support];
|
||||||
|
} else {
|
||||||
|
array.push(support);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
forEach(supportsByTriggerCharacter, entry => {
|
||||||
|
this.triggerCharacterListeners.push(this.editor.addTypingListener(entry.key, () => {
|
||||||
|
this.trigger(true, false, entry.value);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- trigger/retrigger/cancel suggest
|
||||||
|
|
||||||
cancel(retrigger: boolean = false): boolean {
|
cancel(retrigger: boolean = false): boolean {
|
||||||
const actuallyCanceled = this.state !== State.Idle;
|
const actuallyCanceled = this.state !== State.Idle;
|
||||||
|
|
||||||
|
@ -203,8 +274,14 @@ export class SuggestModel implements IDisposable {
|
||||||
return actuallyCanceled;
|
return actuallyCanceled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private isAutoSuggest(): boolean {
|
private updateActiveSuggestSession(): void {
|
||||||
return this.state === State.Auto;
|
if (this.state !== State.Idle) {
|
||||||
|
if (!SuggestRegistry.has(this.editor.getModel())) {
|
||||||
|
this.cancel();
|
||||||
|
} else {
|
||||||
|
this.trigger(this.state === State.Auto, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onCursorChange(e: ICursorSelectionChangedEvent): void {
|
private onCursorChange(e: ICursorSelectionChangedEvent): void {
|
||||||
|
@ -241,7 +318,7 @@ export class SuggestModel implements IDisposable {
|
||||||
this.cancel();
|
this.cancel();
|
||||||
|
|
||||||
if (ctx.shouldAutoTrigger()) {
|
if (ctx.shouldAutoTrigger()) {
|
||||||
this.triggerAutoSuggestPromise = TPromise.timeout(this.autoSuggestDelay);
|
this.triggerAutoSuggestPromise = TPromise.timeout(this.quickSuggestDelay);
|
||||||
this.triggerAutoSuggestPromise.then(() => {
|
this.triggerAutoSuggestPromise.then(() => {
|
||||||
this.triggerAutoSuggestPromise = null;
|
this.triggerAutoSuggestPromise = null;
|
||||||
this.trigger(true);
|
this.trigger(true);
|
||||||
|
@ -255,19 +332,6 @@ export class SuggestModel implements IDisposable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onSuggestRegistryChange(): void {
|
|
||||||
if (this.state === State.Idle) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SuggestRegistry.has(this.editor.getModel())) {
|
|
||||||
this.cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.trigger(this.state === State.Auto, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public trigger(auto: boolean, retrigger: boolean = false, onlyFrom?: ISuggestSupport[]): void {
|
public trigger(auto: boolean, retrigger: boolean = false, onlyFrom?: ISuggestSupport[]): void {
|
||||||
|
|
||||||
const model = this.editor.getModel();
|
const model = this.editor.getModel();
|
||||||
|
@ -311,6 +375,10 @@ export class SuggestModel implements IDisposable {
|
||||||
}).then(null, onUnexpectedError);
|
}).then(null, onUnexpectedError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isAutoSuggest(): boolean {
|
||||||
|
return this.state === State.Auto;
|
||||||
|
}
|
||||||
|
|
||||||
public getTriggerPosition(): IPosition {
|
public getTriggerPosition(): IPosition {
|
||||||
const {lineNumber, column} = this.context;
|
const {lineNumber, column} = this.context;
|
||||||
return { lineNumber, column };
|
return { lineNumber, column };
|
||||||
|
@ -355,17 +423,4 @@ export class SuggestModel implements IDisposable {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onEditorConfigurationChange(): void {
|
|
||||||
this.autoSuggestDelay = this.editor.getConfiguration().contribInfo.quickSuggestionsDelay;
|
|
||||||
|
|
||||||
if (isNaN(this.autoSuggestDelay) || (!this.autoSuggestDelay && this.autoSuggestDelay !== 0) || this.autoSuggestDelay < 0) {
|
|
||||||
this.autoSuggestDelay = 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose(): void {
|
|
||||||
this.toDispose = dispose(this.toDispose);
|
|
||||||
this.cancel();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue