From f04d2761b1e42e40cd142d18c01d2579be9d501e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 21 Sep 2018 17:58:42 +0200 Subject: [PATCH] add word distance (by line) and rank suggestions by that --- .../editor/contrib/suggest/completionModel.ts | 16 ++++- .../contrib/suggest/suggestController.ts | 4 +- src/vs/editor/contrib/suggest/suggestModel.ts | 20 +++--- src/vs/editor/contrib/suggest/wordDistance.ts | 62 +++++++++++++++++++ 4 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 src/vs/editor/contrib/suggest/wordDistance.ts diff --git a/src/vs/editor/contrib/suggest/completionModel.ts b/src/vs/editor/contrib/suggest/completionModel.ts index 06208d010d0..c4ca4f6ddcf 100644 --- a/src/vs/editor/contrib/suggest/completionModel.ts +++ b/src/vs/editor/contrib/suggest/completionModel.ts @@ -10,11 +10,13 @@ import { isDisposable } from 'vs/base/common/lifecycle'; import { ISuggestResult, ISuggestSupport } from 'vs/editor/common/modes'; import { ISuggestionItem } from './suggest'; import { InternalSuggestOptions, EDITOR_DEFAULTS } from 'vs/editor/common/config/editorOptions'; +import { WordDistance } from 'vs/editor/contrib/suggest/wordDistance'; export interface ICompletionItem extends ISuggestionItem { matches?: number[]; score?: number; idx?: number; + distance?: number; word?: string; } @@ -58,7 +60,13 @@ export class CompletionModel { private _isIncomplete: Set; private _stats: ICompletionStats; - constructor(items: ISuggestionItem[], column: number, lineContext: LineContext, options: InternalSuggestOptions = EDITOR_DEFAULTS.contribInfo.suggest) { + constructor( + items: ISuggestionItem[], + column: number, + lineContext: LineContext, + private readonly _wordDistanceOracle: WordDistance, + options: InternalSuggestOptions = EDITOR_DEFAULTS.contribInfo.suggest + ) { this._items = items; this._column = column; this._options = options; @@ -209,7 +217,7 @@ export class CompletionModel { } item.idx = i; - + item.distance = this._wordDistanceOracle.distance(item.position, suggestion.label); target.push(item); // update stats @@ -229,6 +237,10 @@ export class CompletionModel { return -1; } else if (a.score < b.score) { return 1; + } else if (a.distance < b.distance) { + return -1; + } else if (a.distance > b.distance) { + return 1; } else if (a.idx < b.idx) { return -1; } else if (a.idx > b.idx) { diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index 63386502cdc..04504e8fc60 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -32,6 +32,7 @@ import { State, SuggestModel } from './suggestModel'; import { ISelectedSuggestion, SuggestWidget } from './suggestWidget'; import { WordContextKey } from 'vs/editor/contrib/suggest/wordContextKey'; import { once, anyEvent } from 'vs/base/common/event'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; class AcceptOnCharacterOracle { @@ -97,12 +98,13 @@ export class SuggestController implements IEditorContribution { constructor( private _editor: ICodeEditor, + @IEditorWorkerService editorWorker: IEditorWorkerService, @ICommandService private readonly _commandService: ICommandService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { - this._model = new SuggestModel(this._editor); + this._model = new SuggestModel(this._editor, editorWorker); this._memory = _instantiationService.createInstance(SuggestMemories, this._editor.getConfiguration().contribInfo.suggestSelection); this._alternatives = new SuggestAlternatives(this._editor, item => this._onDidSelectItem(item, false, false), this._contextKeyService); diff --git a/src/vs/editor/contrib/suggest/suggestModel.ts b/src/vs/editor/contrib/suggest/suggestModel.ts index 0881dcd595c..b4c24c8f99c 100644 --- a/src/vs/editor/contrib/suggest/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/suggestModel.ts @@ -20,6 +20,8 @@ import { CompletionModel } from './completionModel'; import { ISuggestionItem, getSuggestionComparator, provideSuggestionItems, getSnippetSuggestSupport } from './suggest'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { WordDistance } from 'vs/editor/contrib/suggest/wordDistance'; export interface ICancelEvent { readonly retrigger: boolean; @@ -91,13 +93,12 @@ export const enum State { export class SuggestModel implements IDisposable { - private _editor: ICodeEditor; private _toDispose: IDisposable[] = []; private _quickSuggestDelay: number; private _triggerCharacterListener: IDisposable; private readonly _triggerQuickSuggest = new TimeoutTimer(); private readonly _triggerRefilter = new TimeoutTimer(); - private _state: State; + private _state: State = State.Idle; private _requestToken: CancellationTokenSource; private _context: LineContext; @@ -112,9 +113,10 @@ export class SuggestModel implements IDisposable { readonly onDidTrigger: Event = this._onDidTrigger.event; readonly onDidSuggest: Event = this._onDidSuggest.event; - constructor(editor: ICodeEditor) { - this._editor = editor; - this._state = State.Idle; + constructor( + private readonly _editor: ICodeEditor, + private readonly _editorWorker: IEditorWorkerService + ) { this._completionModel = null; this._context = null; this._currentSelection = this._editor.getSelection() || new Selection(1, 1, 1, 1); @@ -385,15 +387,18 @@ export class SuggestModel implements IDisposable { this._requestToken = new CancellationTokenSource(); - provideSuggestionItems( + let wordDistance = WordDistance.create(this._editorWorker, this._editor); + + let items = provideSuggestionItems( model, this._editor.getPosition(), this._editor.getConfiguration().contribInfo.suggest.snippets, onlyFrom, suggestCtx, this._requestToken.token + ); - ).then(items => { + Promise.all([items, wordDistance]).then(([items, wordDistance]) => { this._requestToken.dispose(); @@ -416,6 +421,7 @@ export class SuggestModel implements IDisposable { leadingLineContent: ctx.leadingLineContent, characterCountDelta: this._context ? ctx.column - this._context.column : 0 }, + wordDistance, this._editor.getConfiguration().contribInfo.suggest ); this._onNewContext(ctx); diff --git a/src/vs/editor/contrib/suggest/wordDistance.ts b/src/vs/editor/contrib/suggest/wordDistance.ts new file mode 100644 index 00000000000..4abd8fae7bd --- /dev/null +++ b/src/vs/editor/contrib/suggest/wordDistance.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { binarySearch } from 'vs/base/common/arrays'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { IPosition } from 'vs/editor/common/core/position'; + + +export abstract class WordDistance { + + static create(service: IEditorWorkerService, editor: ICodeEditor): Thenable { + + const model = editor.getModel(); + const position = editor.getPosition(); + + return service.getWordRanges(model.uri, position).then(data => { + + return new class extends WordDistance { + distance(anchor: IPosition, word: string) { + if (!data || !position.equals(editor.getPosition())) { + return 0; + } + + let lineNumbers = new Map(); + if (!lineNumbers.has(word)) { + let wordLineNumbers: number[]; + let ranges = data[word]; + if (ranges) { + wordLineNumbers = ranges.map(range => range.startLineNumber); + wordLineNumbers = wordLineNumbers.sort(); + } + lineNumbers.set(word, wordLineNumbers); + delete data[word]; + } + + let offset = lineNumbers.get(word); + if (!offset) { + return Number.MAX_VALUE; + } + + let idx = binarySearch(offset, anchor.lineNumber, (a, b) => a - b); + if (idx >= 0) { + return 0; + } else { + idx = ~idx; + idx %= offset.length; + return Math.abs(offset[idx] - anchor.lineNumber); + } + } + }; + }); + } + + abstract distance(anchor: IPosition, word: string): number; +} + + -- GitLab