move more logic into suggest model

This commit is contained in:
Johannes Rieken 2016-08-17 12:36:31 +02:00
parent 7db3469c87
commit d7771265a8
2 changed files with 95 additions and 88 deletions

View file

@ -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();

View file

@ -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();
}
} }