提交 f5578864 编写于 作者: J Johannes Rieken

store position at which an item was requested, replace CompletionItem with...

store position at which an item was requested, replace CompletionItem with lightweight alternative, #2508
上级 ea6831e4
......@@ -19,7 +19,7 @@ import { CodeSnippet } from 'vs/editor/contrib/snippet/common/snippet';
import { SnippetController } from 'vs/editor/contrib/snippet/common/snippetController';
import { Context as SuggestContext } from 'vs/editor/contrib/suggest/common/suggest';
import { SuggestModel } from '../common/suggestModel';
import { CompletionItem } from '../common/completionModel';
import { ICompletionItem } from '../common/completionModel';
import { SuggestWidget } from './suggestWidget';
export class SuggestController implements IEditorContribution {
......@@ -63,10 +63,10 @@ export class SuggestController implements IEditorContribution {
}
}
private onDidSelectItem(item: CompletionItem): void {
private onDidSelectItem(item: ICompletionItem): void {
if (item) {
const {insertText, overwriteBefore, overwriteAfter, additionalTextEdits, command} = item.suggestion;
const columnDelta = this.editor.getPosition().column - this.model.getTriggerPosition().column;
const columnDelta = this.editor.getPosition().column - item.position.column;
// todo@joh
// * order of stuff command/extraEdit/actual edit
......
......@@ -23,7 +23,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/
import { IConfigurationChangedEvent } from 'vs/editor/common/editorCommon';
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { Context as SuggestContext } from '../common/suggest';
import { CompletionItem, CompletionModel } from '../common/completionModel';
import { ICompletionItem, CompletionModel } from '../common/completionModel';
import { alert } from 'vs/base/browser/ui/aria/aria';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
......@@ -43,7 +43,7 @@ function matchesColor(text: string) {
return text && text.match(colorRegExp) ? text : null;
}
class Renderer implements IRenderer<CompletionItem, ISuggestionTemplateData> {
class Renderer implements IRenderer<ICompletionItem, ISuggestionTemplateData> {
private triggerKeybindingLabel: string;
......@@ -94,9 +94,9 @@ class Renderer implements IRenderer<CompletionItem, ISuggestionTemplateData> {
return data;
}
renderElement(element: CompletionItem, index: number, templateData: ISuggestionTemplateData): void {
renderElement(element: ICompletionItem, index: number, templateData: ISuggestionTemplateData): void {
const data = <ISuggestionTemplateData>templateData;
const suggestion = (<CompletionItem>element).suggestion;
const suggestion = (<ICompletionItem>element).suggestion;
if (suggestion.documentation) {
data.root.setAttribute('aria-label', nls.localize('suggestionWithDetailsAriaLabel', "{0}, suggestion, has details", suggestion.label));
......@@ -115,7 +115,7 @@ class Renderer implements IRenderer<CompletionItem, ISuggestionTemplateData> {
}
}
data.highlightedLabel.set(suggestion.label, (<CompletionItem>element).highlights);
data.highlightedLabel.set(suggestion.label, (<ICompletionItem>element).highlights);
data.typeLabel.textContent = suggestion.detail || '';
data.documentation.textContent = suggestion.documentation || '';
......@@ -146,11 +146,11 @@ class Renderer implements IRenderer<CompletionItem, ISuggestionTemplateData> {
const FocusHeight = 35;
const UnfocusedHeight = 19;
class Delegate implements IDelegate<CompletionItem> {
class Delegate implements IDelegate<ICompletionItem> {
constructor(private listProvider: () => List<CompletionItem>) { }
constructor(private listProvider: () => List<ICompletionItem>) { }
getHeight(element: CompletionItem): number {
getHeight(element: ICompletionItem): number {
const focus = this.listProvider().getFocusedElements()[0];
if (element.suggestion.documentation && element === focus) {
......@@ -160,7 +160,7 @@ class Delegate implements IDelegate<CompletionItem> {
return UnfocusedHeight;
}
getTemplateId(element: CompletionItem): string {
getTemplateId(element: ICompletionItem): string {
return 'suggestion';
}
}
......@@ -223,7 +223,7 @@ class SuggestionDetails {
return this.el;
}
render(item: CompletionItem): void {
render(item: ICompletionItem): void {
if (!item) {
this.title.textContent = '';
this.type.textContent = '';
......@@ -295,21 +295,21 @@ export class SuggestWidget implements IContentWidget, IDisposable {
private isAuto: boolean;
private loadingTimeout: number;
private currentSuggestionDetails: TPromise<void>;
private focusedItem: CompletionItem;
private focusedItem: ICompletionItem;
private completionModel: CompletionModel;
private element: HTMLElement;
private messageElement: HTMLElement;
private listElement: HTMLElement;
private details: SuggestionDetails;
private delegate: IDelegate<CompletionItem>;
private list: List<CompletionItem>;
private delegate: IDelegate<ICompletionItem>;
private list: List<ICompletionItem>;
private suggestWidgetVisible: IContextKey<boolean>;
private suggestWidgetMultipleSuggestions: IContextKey<boolean>;
private suggestionSupportsAutoAccept: IContextKey<boolean>;
private onDidSelectEmitter = new Emitter<CompletionItem>();
private onDidSelectEmitter = new Emitter<ICompletionItem>();
private editorBlurTimeout: TPromise<void>;
private showTimeout: TPromise<void>;
......@@ -337,7 +337,7 @@ export class SuggestWidget implements IContentWidget, IDisposable {
this.listElement = append(this.element, $('.tree'));
this.details = new SuggestionDetails(this.element, this, this.editor);
let renderer: IRenderer<CompletionItem, any> = instantiationService.createInstance(Renderer, this, this.editor);
let renderer: IRenderer<ICompletionItem, any> = instantiationService.createInstance(Renderer, this, this.editor);
this.delegate = new Delegate(() => this.list);
this.list = new List(this.listElement, this.delegate, [renderer], {
......@@ -389,7 +389,7 @@ export class SuggestWidget implements IContentWidget, IDisposable {
});
}
private onListSelection(e: ISelectionChangeEvent<CompletionItem>): void {
private onListSelection(e: ISelectionChangeEvent<ICompletionItem>): void {
if (!e.elements.length) {
return;
}
......@@ -402,7 +402,7 @@ export class SuggestWidget implements IContentWidget, IDisposable {
this.editor.focus();
}
private _getSuggestionAriaAlertLabel(item:CompletionItem): string {
private _getSuggestionAriaAlertLabel(item:ICompletionItem): string {
if (item.suggestion.documentation) {
return nls.localize('ariaCurrentSuggestionWithDetails',"{0}, suggestion, has details", item.suggestion.label);
} else {
......@@ -421,7 +421,7 @@ export class SuggestWidget implements IContentWidget, IDisposable {
}
}
private onListFocus(e: IFocusChangeEvent<CompletionItem>): void {
private onListFocus(e: IFocusChangeEvent<ICompletionItem>): void {
if (!e.elements.length) {
if (this.currentSuggestionDetails) {
this.currentSuggestionDetails.cancel();
......@@ -527,7 +527,7 @@ export class SuggestWidget implements IContentWidget, IDisposable {
}
}
get onDidSelect():Event<CompletionItem> {
get onDidSelect():Event<ICompletionItem> {
return this.onDidSelectEmitter.event;
}
......@@ -646,7 +646,7 @@ export class SuggestWidget implements IContentWidget, IDisposable {
}
}
getFocusedItem(): CompletionItem {
getFocusedItem(): ICompletionItem {
if (this.state !== State.Hidden
&& this.state !== State.Empty
&& this.state !== State.Loading) {
......
......@@ -6,28 +6,15 @@
'use strict';
import {isFalsyOrEmpty} from 'vs/base/common/arrays';
import {TPromise} from 'vs/base/common/winjs.base';
import {IFilter, IMatch, fuzzyContiguousFilter} from 'vs/base/common/filters';
import {ISuggestion, ISuggestSupport} from 'vs/editor/common/modes';
import {IMatch, fuzzyContiguousFilter} from 'vs/base/common/filters';
import {ISuggestSupport} from 'vs/editor/common/modes';
import {ISuggestionItem} from './suggest';
export class CompletionItem {
suggestion: ISuggestion;
highlights: IMatch[];
filter: IFilter;
constructor(private _item: ISuggestionItem) {
this.suggestion = _item.suggestion;
this.filter = _item.support && _item.support.filter || fuzzyContiguousFilter;
}
resolve(): TPromise<this> {
return this._item.resolve().then(() => this);
}
export interface ICompletionItem extends ISuggestionItem {
highlights?: IMatch[];
}
export interface CompletionStats {
export interface ICompletionStats {
suggestionCount: number;
snippetCount: number;
textCount: number;
......@@ -42,28 +29,16 @@ export class LineContext {
export class CompletionModel {
private _lineContext: LineContext;
private _items: CompletionItem[] = [];
private _incomplete: ISuggestSupport[] = [];
private _items: ICompletionItem[];
private _filteredItems: CompletionItem[] = undefined;
private _filteredItems: ICompletionItem[];
private _topScoreIdx: number;
private _stats: CompletionStats;
private _incomplete: ISuggestSupport[];
private _stats: ICompletionStats;
constructor(items: ISuggestionItem[], lineContext: LineContext) {
this._items = items;
this._lineContext = lineContext;
for (const item of items) {
this._items.push(new CompletionItem(item));
if (item.container.incomplete
&& this._incomplete.indexOf(item.support) < 0) {
this._incomplete.push(item.support);
}
}
}
get incomplete(): ISuggestSupport[] {
return this._incomplete;
}
get lineContext(): LineContext {
......@@ -74,49 +49,56 @@ export class CompletionModel {
if (this._lineContext.leadingLineContent !== value.leadingLineContent
|| this._lineContext.characterCountDelta !== value.characterCountDelta) {
this._filteredItems = undefined;
this._lineContext = value;
this._filteredItems = undefined;
}
}
get items(): CompletionItem[] {
if (!this._filteredItems) {
this._filterAndScore();
}
get items(): ICompletionItem[] {
this._ensureCachedState();
return this._filteredItems;
}
get topScoreIdx(): number {
if (!this._filteredItems) {
this._filterAndScore();
}
this._ensureCachedState();
return this._topScoreIdx;
}
get stats(): CompletionStats {
get incomplete(): ISuggestSupport[] {
this._ensureCachedState();
return this._incomplete;
}
get stats(): ICompletionStats {
this._ensureCachedState();
return this._stats;
}
private _ensureCachedState(): void {
if (!this._filteredItems) {
this._filterAndScore();
this._createCachedState();
}
return this._stats;
}
private _filterAndScore(): void {
private _createCachedState(): void {
this._filteredItems = [];
this._incomplete = [];
this._topScoreIdx = -1;
this._stats = { suggestionCount: 0, snippetCount: 0, textCount: 0 };
const {leadingLineContent, characterCountDelta} = this._lineContext;
const {leadingLineContent, characterCountDelta} = this._lineContext;
let word = '';
let topScore = -1;
for (const item of this._items) {
const {filter, suggestion} = item;
const {suggestion, support, container} = item;
const filter = support && support.filter || fuzzyContiguousFilter;
// 'word' is that remainder of the current line that we
// filter and score against. In theory each suggestion uses a
// differnet word, but in practice not - that's why we cache
const wordLen = item.suggestion.overwriteBefore + characterCountDelta;
const wordLen = suggestion.overwriteBefore + characterCountDelta;
if (word.length !== wordLen) {
word = leadingLineContent.slice(-wordLen);
}
......@@ -146,9 +128,15 @@ export class CompletionModel {
this._topScoreIdx = this._filteredItems.length - 1;
}
// collect those supports that signaled having
// an incomplete result
if (container.incomplete && this._incomplete.indexOf(support) < 0) {
this._incomplete.push(support);
}
// update stats
this._stats.suggestionCount++;
switch (item.suggestion.type) {
switch (suggestion.type) {
case 'snippet': this._stats.snippetCount++; break;
case 'text': this._stats.textCount++; break;
}
......
......@@ -10,7 +10,7 @@ import {compare} from 'vs/base/common/strings';
import {assign} from 'vs/base/common/objects';
import {onUnexpectedError} from 'vs/base/common/errors';
import {TPromise} from 'vs/base/common/winjs.base';
import {IReadOnlyModel} from 'vs/editor/common/editorCommon';
import {IReadOnlyModel, IPosition} from 'vs/editor/common/editorCommon';
import {CommonEditorRegistry} from 'vs/editor/common/editorCommonExtensions';
import {ISuggestResult, ISuggestSupport, ISuggestion, SuggestRegistry} from 'vs/editor/common/modes';
import {ISnippetsRegistry, Extensions} from 'vs/editor/common/modes/snippetsRegistry';
......@@ -25,6 +25,7 @@ export const Context = {
};
export interface ISuggestionItem {
position: IPosition;
suggestion: ISuggestion;
container: ISuggestResult;
support: ISuggestSupport;
......@@ -52,6 +53,8 @@ export function provideSuggestionItems(model: IReadOnlyModel, position: Position
const result: ISuggestionItem[] = [];
const acceptSuggestion = createSuggesionFilter(snippetConfig);
position = position.clone();
// get provider groups, always add snippet suggestion provider
const supports = SuggestRegistry.orderedGroups(model);
supports.unshift([snippetSuggestSupport]);
......@@ -83,6 +86,7 @@ export function provideSuggestionItems(model: IReadOnlyModel, position: Position
fixOverwriteBeforeAfter(suggestion, container);
result.push({
position,
container,
suggestion,
support,
......
......@@ -370,11 +370,6 @@ export class SuggestModel implements IDisposable {
}).then(null, onUnexpectedError);
}
public getTriggerPosition(): IPosition {
const {lineNumber, column} = this.context;
return { lineNumber, column };
}
private onNewContext(ctx: Context): void {
if (this.context && this.context.isDifferentContext(ctx)) {
if (this.context.shouldRetrigger(ctx)) {
......
......@@ -16,6 +16,11 @@ suite('CompletionModel', function () {
return new class implements ISuggestionItem {
position = {
lineNumber: 1,
column: 1
};
suggestion: ISuggestion = {
label,
overwriteBefore,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册