Skip to content

Commit 6608b73

Browse files
333fredJoeRobich
andauthored
New completion service (#3986)
* Use the new completion and completion/resolve services being added to the roslyn backend for a better overall completion experience. * Be more threadsafe. * Add additional tests, including an override completion test. * Correct line/column handling. * Add an option to support the forthcoming unimported type completion from Omnisharp. * Adjust wording of the description. Co-authored-by: Joey Robichaud <[email protected]> Co-authored-by: Joey Robichaud <[email protected]>
1 parent 57d87d4 commit 6608b73

File tree

16 files changed

+237
-219
lines changed

16 files changed

+237
-219
lines changed

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,11 @@
777777
"scope": "machine",
778778
"description": "Enables support for decompiling external references instead of viewing metadata."
779779
},
780+
"omnisharp.enableImportCompletion": {
781+
"type": "boolean",
782+
"default": false,
783+
"description": "Enables support for showing unimported types and unimported extension methods in completion lists. When committed, the appropriate using directive will be added at the top of the current file. This option can have a negative impact on initial completion responsiveness, particularly for the first few completion sessions after opening a solution."
784+
},
780785
"razor.plugin.path": {
781786
"type": [
782787
"string",

src/features/completionItemProvider.ts

Lines changed: 0 additions & 137 deletions
This file was deleted.

src/features/completionProvider.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { CompletionItemProvider, TextDocument, Position, CompletionContext, CompletionList, CompletionItem, MarkdownString, TextEdit, Range, SnippetString } from "vscode";
7+
import AbstractProvider from "./abstractProvider";
8+
import * as protocol from "../omnisharp/protocol";
9+
import * as serverUtils from '../omnisharp/utils';
10+
import { CancellationToken, CompletionTriggerKind as LspCompletionTriggerKind, InsertTextFormat } from "vscode-languageserver-protocol";
11+
import { createRequest } from "../omnisharp/typeConversion";
12+
13+
export default class OmnisharpCompletionProvider extends AbstractProvider implements CompletionItemProvider {
14+
15+
#lastCompletions?: Map<CompletionItem, protocol.OmnisharpCompletionItem>;
16+
17+
public async provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext): Promise<CompletionList> {
18+
let request = createRequest<protocol.CompletionRequest>(document, position);
19+
request.CompletionTrigger = (context.triggerKind + 1) as LspCompletionTriggerKind;
20+
request.TriggerCharacter = context.triggerCharacter;
21+
22+
try {
23+
const response = await serverUtils.getCompletion(this._server, request, token);
24+
const mappedItems = response.Items.map(this._convertToVscodeCompletionItem);
25+
26+
let lastCompletions = new Map();
27+
28+
for (let i = 0; i < mappedItems.length; i++) {
29+
lastCompletions.set(mappedItems[i], response.Items[i]);
30+
}
31+
32+
this.#lastCompletions = lastCompletions;
33+
34+
return { items: mappedItems };
35+
}
36+
catch (error) {
37+
return;
38+
}
39+
}
40+
41+
public async resolveCompletionItem(item: CompletionItem, token: CancellationToken): Promise<CompletionItem> {
42+
const lastCompletions = this.#lastCompletions;
43+
if (!lastCompletions) {
44+
return item;
45+
}
46+
47+
const lspItem = lastCompletions.get(item);
48+
if (!lspItem) {
49+
return item;
50+
}
51+
52+
const request: protocol.CompletionResolveRequest = { Item: lspItem };
53+
try {
54+
const response = await serverUtils.getCompletionResolve(this._server, request, token);
55+
return this._convertToVscodeCompletionItem(response.Item);
56+
}
57+
catch (error) {
58+
return;
59+
}
60+
}
61+
62+
private _convertToVscodeCompletionItem(omnisharpCompletion: protocol.OmnisharpCompletionItem): CompletionItem {
63+
let docs: MarkdownString | undefined = omnisharpCompletion.Documentation ? new MarkdownString(omnisharpCompletion.Documentation, false) : undefined;
64+
65+
const mapTextEdit = function (edit: protocol.LinePositionSpanTextChange): TextEdit {
66+
const newStart = new Position(edit.StartLine - 1, edit.StartColumn - 1);
67+
const newEnd = new Position(edit.EndLine - 1, edit.EndColumn - 1);
68+
const newRange = new Range(newStart, newEnd);
69+
return new TextEdit(newRange, edit.NewText);
70+
};
71+
72+
const additionalTextEdits = omnisharpCompletion.AdditionalTextEdits?.map(mapTextEdit);
73+
74+
const insertText = omnisharpCompletion.InsertTextFormat === InsertTextFormat.Snippet
75+
? new SnippetString(omnisharpCompletion.InsertText)
76+
: omnisharpCompletion.InsertText;
77+
78+
return {
79+
label: omnisharpCompletion.Label,
80+
kind: omnisharpCompletion.Kind - 1,
81+
detail: omnisharpCompletion.Detail,
82+
documentation: docs,
83+
commitCharacters: omnisharpCompletion.CommitCharacters,
84+
preselect: omnisharpCompletion.Preselect,
85+
filterText: omnisharpCompletion.FilterText,
86+
insertText: insertText,
87+
tags: omnisharpCompletion.Tags,
88+
sortText: omnisharpCompletion.SortText,
89+
additionalTextEdits: additionalTextEdits,
90+
keepWhitespace: true
91+
};
92+
}
93+
}

src/observers/OptionChangeObserver.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ const omniSharpOptions: ReadonlyArray<OptionsKey> = [
1919
"waitForDebugger",
2020
"loggingLevel",
2121
"enableEditorConfigSupport",
22-
"enableDecompilationSupport"
22+
"enableDecompilationSupport",
23+
"enableImportCompletion"
2324
];
2425

2526
function OmniSharpOptionChangeObservable(optionObservable: Observable<Options>): Observable<Options> {

src/omnisharp/extension.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { safeLength, sum } from '../common';
1111
import { CSharpConfigurationProvider } from '../configurationProvider';
1212
import CodeActionProvider from '../features/codeActionProvider';
1313
import CodeLensProvider from '../features/codeLensProvider';
14-
import CompletionItemProvider from '../features/completionItemProvider';
14+
import CompletionProvider from '../features/completionProvider';
1515
import DefinitionMetadataDocumentProvider from '../features/definitionMetadataDocumentProvider';
1616
import DefinitionProvider from '../features/definitionProvider';
1717
import DocumentHighlightProvider from '../features/documentHighlightProvider';
@@ -90,7 +90,7 @@ export async function activate(context: vscode.ExtensionContext, packageJSON: an
9090
localDisposables.add(vscode.languages.registerDocumentRangeFormattingEditProvider(documentSelector, new FormatProvider(server, languageMiddlewareFeature)));
9191
localDisposables.add(vscode.languages.registerOnTypeFormattingEditProvider(documentSelector, new FormatProvider(server, languageMiddlewareFeature), '}', ';'));
9292
}
93-
localDisposables.add(vscode.languages.registerCompletionItemProvider(documentSelector, new CompletionItemProvider(server, languageMiddlewareFeature), '.', ' '));
93+
localDisposables.add(vscode.languages.registerCompletionItemProvider(documentSelector, new CompletionProvider(server, languageMiddlewareFeature), '.', ' '));
9494
localDisposables.add(vscode.languages.registerWorkspaceSymbolProvider(new WorkspaceSymbolProvider(server, optionProvider, languageMiddlewareFeature)));
9595
localDisposables.add(vscode.languages.registerSignatureHelpProvider(documentSelector, new SignatureHelpProvider(server, languageMiddlewareFeature), '(', ','));
9696
// Since the CodeActionProvider registers its own commands, we must instantiate it and add it to the localDisposables
@@ -210,4 +210,4 @@ export async function activate(context: vscode.ExtensionContext, packageJSON: an
210210
return new Promise<ActivationResult>(resolve =>
211211
server.onServerStart(e =>
212212
resolve({ server, advisor, testManager })));
213-
}
213+
}

src/omnisharp/options.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export class Options {
2828
public enableRoslynAnalyzers: boolean,
2929
public enableEditorConfigSupport: boolean,
3030
public enableDecompilationSupport: boolean,
31+
public enableImportCompletion: boolean,
3132
public useSemanticHighlighting: boolean,
3233
public razorPluginPath?: string,
3334
public defaultLaunchSolution?: string,
@@ -70,6 +71,7 @@ export class Options {
7071
const enableRoslynAnalyzers = omnisharpConfig.get<boolean>('enableRoslynAnalyzers', false);
7172
const enableEditorConfigSupport = omnisharpConfig.get<boolean>('enableEditorConfigSupport', false);
7273
const enableDecompilationSupport = omnisharpConfig.get<boolean>('enableDecompilationSupport', false);
74+
const enableImportCompletion = omnisharpConfig.get<boolean>('enableImportCompletion', false);
7375

7476
const useFormatting = csharpConfig.get<boolean>('format.enable', true);
7577

@@ -117,6 +119,7 @@ export class Options {
117119
enableRoslynAnalyzers,
118120
enableEditorConfigSupport,
119121
enableDecompilationSupport,
122+
enableImportCompletion,
120123
useSemanticHighlighting,
121124
razorPluginPath,
122125
defaultLaunchSolution,
@@ -190,4 +193,4 @@ export class Options {
190193
return "auto";
191194
}
192195
}
193-
}
196+
}

0 commit comments

Comments
 (0)