Make TS completion item more efficent to compute

For #95324

- Removes repeated calls to `getWordRangeAtPosition`
- Only use our fuzzy string logic when using TS 3.8 or older
This commit is contained in:
Matt Bierner 2020-04-15 10:00:02 -07:00
parent 6c068456e3
commit 948aa4a214

View file

@ -32,8 +32,16 @@ interface CompletionContext {
readonly isNewIdentifierLocation: boolean; readonly isNewIdentifierLocation: boolean;
readonly isMemberCompletion: boolean; readonly isMemberCompletion: boolean;
readonly isInValidCommitCharacterContext: boolean; readonly isInValidCommitCharacterContext: boolean;
readonly enableCallCompletions: boolean;
readonly dotAccessorContext?: DotAccessorContext; readonly dotAccessorContext?: DotAccessorContext;
readonly enableCallCompletions: boolean;
readonly useCodeSnippetsOnMethodSuggest: boolean,
readonly wordRange: vscode.Range | undefined;
readonly line: string;
readonly useFuzzyWordRangeLogic: boolean,
} }
class MyCompletionItem extends vscode.CompletionItem { class MyCompletionItem extends vscode.CompletionItem {
@ -42,10 +50,8 @@ class MyCompletionItem extends vscode.CompletionItem {
constructor( constructor(
public readonly position: vscode.Position, public readonly position: vscode.Position,
public readonly document: vscode.TextDocument, public readonly document: vscode.TextDocument,
line: string,
public readonly tsEntry: Proto.CompletionEntry, public readonly tsEntry: Proto.CompletionEntry,
useCodeSnippetsOnMethodSuggest: boolean, private readonly completionContext: CompletionContext,
public readonly completionContext: CompletionContext,
public readonly metadata: any | undefined, public readonly metadata: any | undefined,
) { ) {
super(tsEntry.name, MyCompletionItem.convertKind(tsEntry.kind)); super(tsEntry.name, MyCompletionItem.convertKind(tsEntry.kind));
@ -63,13 +69,13 @@ class MyCompletionItem extends vscode.CompletionItem {
} }
this.position = position; this.position = position;
this.useCodeSnippet = useCodeSnippetsOnMethodSuggest && (this.kind === vscode.CompletionItemKind.Function || this.kind === vscode.CompletionItemKind.Method); this.useCodeSnippet = completionContext.useCodeSnippetsOnMethodSuggest && (this.kind === vscode.CompletionItemKind.Function || this.kind === vscode.CompletionItemKind.Method);
if (tsEntry.replacementSpan) { if (tsEntry.replacementSpan) {
let replaceRange = typeConverters.Range.fromTextSpan(tsEntry.replacementSpan); let replaceRange = typeConverters.Range.fromTextSpan(tsEntry.replacementSpan);
// Make sure we only replace a single line at most // Make sure we only replace a single line at most
if (!replaceRange.isSingleLine) { if (!replaceRange.isSingleLine) {
replaceRange = new vscode.Range(replaceRange.start.line, replaceRange.start.character, replaceRange.start.line, line.length); replaceRange = new vscode.Range(replaceRange.start.line, replaceRange.start.character, replaceRange.start.line, completionContext.line.length);
} }
this.range = { this.range = {
inserting: new vscode.Range(replaceRange.start, position), inserting: new vscode.Range(replaceRange.start, position),
@ -78,12 +84,12 @@ class MyCompletionItem extends vscode.CompletionItem {
} }
this.insertText = tsEntry.insertText; this.insertText = tsEntry.insertText;
this.filterText = this.getFilterText(line, tsEntry.insertText); this.filterText = this.getFilterText(completionContext.line, tsEntry.insertText);
if (completionContext.isMemberCompletion && completionContext.dotAccessorContext) { if (completionContext.isMemberCompletion && completionContext.dotAccessorContext) {
this.filterText = completionContext.dotAccessorContext.text + (this.insertText || this.label); this.filterText = completionContext.dotAccessorContext.text + (this.insertText || this.label);
if (!this.range) { if (!this.range) {
const replacementRange = this.getReplaceRange(line); const replacementRange = this.getFuzzyWordRange();
if (replacementRange) { if (replacementRange) {
this.range = { this.range = {
inserting: completionContext.dotAccessorContext.range, inserting: completionContext.dotAccessorContext.range,
@ -126,13 +132,13 @@ class MyCompletionItem extends vscode.CompletionItem {
} }
} }
} }
this.resolveRange(line); this.resolveRange();
} }
private getFilterText(line: string, insertText: string | undefined): string | undefined { private getFilterText(line: string, insertText: string | undefined): string | undefined {
// Handle private field completions // Handle private field completions
if (this.tsEntry.name.startsWith('#')) { if (this.tsEntry.name.startsWith('#')) {
const wordRange = this.document.getWordRangeAtPosition(this.position); const wordRange = this.completionContext.wordRange;
const wordStart = wordRange ? line.charAt(wordRange.start.character) : undefined; const wordStart = wordRange ? line.charAt(wordRange.start.character) : undefined;
if (insertText) { if (insertText) {
if (insertText.startsWith('this.#')) { if (insertText.startsWith('this.#')) {
@ -164,12 +170,12 @@ class MyCompletionItem extends vscode.CompletionItem {
return insertText; return insertText;
} }
private resolveRange(line: string): void { private resolveRange(): void {
if (this.range) { if (this.range) {
return; return;
} }
const replaceRange = this.getReplaceRange(line); const replaceRange = this.getFuzzyWordRange();
if (replaceRange) { if (replaceRange) {
this.range = { this.range = {
inserting: new vscode.Range(replaceRange.start, this.position), inserting: new vscode.Range(replaceRange.start, this.position),
@ -178,23 +184,21 @@ class MyCompletionItem extends vscode.CompletionItem {
} }
} }
private getReplaceRange(line: string) { private getFuzzyWordRange() {
const wordRange = this.document.getWordRangeAtPosition(this.position); if (this.completionContext.useFuzzyWordRangeLogic) {
let replaceRange = wordRange; // Try getting longer, prefix based range for completions that span words
const text = this.completionContext.line.slice(Math.max(0, this.position.character - this.label.length), this.position.character).toLowerCase();
// Try getting longer, prefix based range for completions that span words const entryName = this.label.toLowerCase();
const text = line.slice(Math.max(0, this.position.character - this.label.length), this.position.character).toLowerCase(); for (let i = entryName.length; i >= 0; --i) {
const entryName = this.label.toLowerCase(); if (text.endsWith(entryName.substr(0, i)) && (!this.completionContext.wordRange || this.completionContext.wordRange.start.character > this.position.character - i)) {
for (let i = entryName.length; i >= 0; --i) { return new vscode.Range(
if (text.endsWith(entryName.substr(0, i)) && (!wordRange || wordRange.start.character > this.position.character - i)) { new vscode.Position(this.position.line, Math.max(0, this.position.character - i)),
replaceRange = new vscode.Range( this.position);
new vscode.Position(this.position.line, Math.max(0, this.position.character - i)), }
this.position);
break;
} }
} }
return replaceRange; return this.completionContext.wordRange;
} }
private static convertKind(kind: string): vscode.CompletionItemKind { private static convertKind(kind: string): vscode.CompletionItemKind {
@ -498,15 +502,20 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider
metadata = response.metadata; metadata = response.metadata;
} }
const wordRange = document.getWordRangeAtPosition(position);
const isInValidCommitCharacterContext = this.isInValidCommitCharacterContext(document, position); const isInValidCommitCharacterContext = this.isInValidCommitCharacterContext(document, position);
const items = entries const items = entries
.filter(entry => !shouldExcludeCompletionEntry(entry, completionConfiguration)) .filter(entry => !shouldExcludeCompletionEntry(entry, completionConfiguration))
.map(entry => new MyCompletionItem(position, document, line.text, entry, completionConfiguration.useCodeSnippetsOnMethodSuggest, { .map(entry => new MyCompletionItem(position, document, entry, {
isNewIdentifierLocation, isNewIdentifierLocation,
isMemberCompletion, isMemberCompletion,
dotAccessorContext, dotAccessorContext,
isInValidCommitCharacterContext, isInValidCommitCharacterContext,
enableCallCompletions: !completionConfiguration.useCodeSnippetsOnMethodSuggest enableCallCompletions: !completionConfiguration.useCodeSnippetsOnMethodSuggest,
wordRange,
line: line.text,
useCodeSnippetsOnMethodSuggest: completionConfiguration.useCodeSnippetsOnMethodSuggest,
useFuzzyWordRangeLogic: this.client.apiVersion.lt(API.v390),
}, metadata)); }, metadata));
return new vscode.CompletionList(items, isIncomplete); return new vscode.CompletionList(items, isIncomplete);
} }