Skip to content

Commit

Permalink
Use the new completion and completion/resolve services being added to…
Browse files Browse the repository at this point in the history
… the roslyn backend for a better overall completion experience.
  • Loading branch information
333fred committed Aug 16, 2020
1 parent 7e5c79b commit 45fa03d
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 171 deletions.
137 changes: 0 additions & 137 deletions src/features/completionItemProvider.ts

This file was deleted.

90 changes: 90 additions & 0 deletions src/features/completionProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { CompletionItemProvider, TextDocument, Position, CompletionContext, CompletionList, CompletionItem, MarkdownString, TextEdit, Range, SnippetString } from "vscode";
import AbstractProvider from "./abstractProvider";
import * as protocol from "../omnisharp/protocol";
import * as serverUtils from '../omnisharp/utils';
import { CancellationToken, CompletionTriggerKind as LspCompletionTriggerKind, InsertTextFormat } from "vscode-languageserver-protocol";
import { createRequest } from "../omnisharp/typeConversion";

export default class OmnisharpCompletionProvider extends AbstractProvider implements CompletionItemProvider {

#lastCompletions?: Map<CompletionItem, protocol.OmnisharpCompletionItem>;

public async provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext): Promise<CompletionList> {
let request = createRequest<protocol.CompletionRequest>(document, position);
request.CompletionTrigger = (context.triggerKind + 1) as LspCompletionTriggerKind;
request.TriggerCharacter = context.triggerCharacter;

try {
const response = await serverUtils.getCompletion(this._server, request, token);
const mappedItems = response.Items.map(this._convertToVscodeCompletionItem);

this.#lastCompletions = new Map();

for (let i = 0; i < mappedItems.length; i++) {
this.#lastCompletions.set(mappedItems[i], response.Items[i]);
}

return { items: mappedItems };
}
catch (error) {
return;
}
}

public async resolveCompletionItem(item: CompletionItem, token: CancellationToken): Promise<CompletionItem> {
if (!this.#lastCompletions) {
return item;
}

const lspItem = this.#lastCompletions.get(item);
if (!lspItem) {
return item;
}

const request: protocol.CompletionResolveRequest = { Item: lspItem };
try {
const response = await serverUtils.getCompletionResolve(this._server, request, token);
return this._convertToVscodeCompletionItem(response.Item);
}
catch (error) {
return;
}
}

private _convertToVscodeCompletionItem(omnisharpCompletion: protocol.OmnisharpCompletionItem): CompletionItem {
let docs: MarkdownString | undefined = omnisharpCompletion.Documentation ? new MarkdownString(omnisharpCompletion.Documentation, false) : undefined;

const mapTextEdit = function (edit: protocol.LinePositionSpanTextChange): TextEdit {
const newStart = new Position(edit.StartLine, edit.StartColumn);
const newEnd = new Position(edit.EndLine, edit.EndColumn);
const newRange = new Range(newStart, newEnd);
return new TextEdit(newRange, edit.NewText);
};

const additionalTextEdits = omnisharpCompletion.AdditionalTextEdits?.map(mapTextEdit);

const insertText = omnisharpCompletion.InsertTextFormat === InsertTextFormat.Snippet
? new SnippetString(omnisharpCompletion.InsertText)
: omnisharpCompletion.InsertText;

return {
label: omnisharpCompletion.Label,
kind: omnisharpCompletion.Kind - 1,
detail: omnisharpCompletion.Detail,
documentation: docs,
commitCharacters: omnisharpCompletion.CommitCharacters,
preselect: omnisharpCompletion.Preselect,
filterText: omnisharpCompletion.FilterText,
insertText: insertText,
tags: omnisharpCompletion.Tags,
sortText: omnisharpCompletion.SortText,
additionalTextEdits: additionalTextEdits,
keepWhitespace: true
};
}
}
6 changes: 3 additions & 3 deletions src/omnisharp/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { safeLength, sum } from '../common';
import { CSharpConfigurationProvider } from '../configurationProvider';
import CodeActionProvider from '../features/codeActionProvider';
import CodeLensProvider from '../features/codeLensProvider';
import CompletionItemProvider from '../features/completionItemProvider';
import CompletionProvider from '../features/completionProvider';
import DefinitionMetadataDocumentProvider from '../features/definitionMetadataDocumentProvider';
import DefinitionProvider from '../features/definitionProvider';
import DocumentHighlightProvider from '../features/documentHighlightProvider';
Expand Down Expand Up @@ -89,7 +89,7 @@ export async function activate(context: vscode.ExtensionContext, packageJSON: an
localDisposables.add(vscode.languages.registerDocumentRangeFormattingEditProvider(documentSelector, new FormatProvider(server, languageMiddlewareFeature)));
localDisposables.add(vscode.languages.registerOnTypeFormattingEditProvider(documentSelector, new FormatProvider(server, languageMiddlewareFeature), '}', ';'));
}
localDisposables.add(vscode.languages.registerCompletionItemProvider(documentSelector, new CompletionItemProvider(server, languageMiddlewareFeature), '.', ' '));
localDisposables.add(vscode.languages.registerCompletionItemProvider(documentSelector, new CompletionProvider(server, languageMiddlewareFeature), '.', ' '));
localDisposables.add(vscode.languages.registerWorkspaceSymbolProvider(new WorkspaceSymbolProvider(server, optionProvider, languageMiddlewareFeature)));
localDisposables.add(vscode.languages.registerSignatureHelpProvider(documentSelector, new SignatureHelpProvider(server, languageMiddlewareFeature), '(', ','));
const codeActionProvider = new CodeActionProvider(server, optionProvider, languageMiddlewareFeature);
Expand Down Expand Up @@ -202,4 +202,4 @@ export async function activate(context: vscode.ExtensionContext, packageJSON: an
return new Promise<ActivationResult>(resolve =>
server.onServerStart(e =>
resolve({ server, advisor, testManager })));
}
}
61 changes: 37 additions & 24 deletions src/omnisharp/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import * as path from 'path';
import { CompletionTriggerKind, CompletionItemKind, CompletionItemTag, InsertTextFormat } from 'vscode-languageserver-protocol';

export module Requests {
export const AddToProject = '/addtoproject';
Expand All @@ -29,6 +30,8 @@ export module Requests {
export const UpdateBuffer = '/updatebuffer';
export const Metadata = '/metadata';
export const ReAnalyze = '/reanalyze';
export const Completion = '/completion';
export const CompletionResolve = '/completion/resolve';
}

export namespace WireProtocol {
Expand Down Expand Up @@ -258,30 +261,6 @@ export interface SyntaxFeature {
Data: string;
}

export interface AutoCompleteRequest extends Request {
WordToComplete: string;
WantDocumentationForEveryCompletionResult?: boolean;
WantImportableTypes?: boolean;
WantMethodHeader?: boolean;
WantSnippet?: boolean;
WantReturnType?: boolean;
WantKind?: boolean;
TriggerCharacter?: string;
}

export interface AutoCompleteResponse {
CompletionText: string;
Description: string;
DisplayText: string;
RequiredNamespaceImport: string;
MethodHeader: string;
ReturnType: string;
Snippet: string;
Kind: string;
IsSuggestionMode: boolean;
Preselect: boolean;
}

export interface ProjectInformationResponse {
MsBuildProject: MSBuildProject;
}
Expand Down Expand Up @@ -473,6 +452,40 @@ export enum FileChangeType {
DirectoryDelete = "DirectoryDelete"
}

export interface CompletionRequest extends Request {
CompletionTrigger: CompletionTriggerKind;
TriggerCharacter?: string;
}

export interface CompletionResponse {
IsIncomplete: boolean;
Items: OmnisharpCompletionItem[];
}

export interface CompletionResolveRequest {
Item: OmnisharpCompletionItem;
}

export interface CompletionResolveResponse {
Item: OmnisharpCompletionItem;
}

export interface OmnisharpCompletionItem {
Label: string;
Kind: CompletionItemKind;
Tags?: CompletionItemTag[];
Detail?: string;
Documentation?: string;
Preselect: boolean;
SortText?: string;
FilterText?: string;
InsertText?: string;
InsertTextFormat?: InsertTextFormat;
CommitCharacters?: string[];
AdditionalTextEdits?: LinePositionSpanTextChange[];
Data: any;
}

export namespace V2 {

export module Requests {
Expand Down
12 changes: 8 additions & 4 deletions src/omnisharp/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ import * as protocol from './protocol';
import * as vscode from 'vscode';
import { MSBuildProject } from './protocol';

export async function autoComplete(server: OmniSharpServer, request: protocol.AutoCompleteRequest, token: vscode.CancellationToken) {
return server.makeRequest<protocol.AutoCompleteResponse[]>(protocol.Requests.AutoComplete, request, token);
}

export async function codeCheck(server: OmniSharpServer, request: protocol.Request, token: vscode.CancellationToken) {
return server.makeRequest<protocol.QuickFixResponse>(protocol.Requests.CodeCheck, request, token);
}
Expand Down Expand Up @@ -168,6 +164,14 @@ export async function getSemanticHighlights(server: OmniSharpServer, request: pr
return server.makeRequest<protocol.V2.SemanticHighlightResponse>(protocol.V2.Requests.Highlight, request);
}

export async function getCompletion(server: OmniSharpServer, request: protocol.CompletionRequest, context: vscode.CancellationToken) {
return server.makeRequest<protocol.CompletionResponse>(protocol.Requests.Completion, request, context);
}

export async function getCompletionResolve(server: OmniSharpServer, request: protocol.CompletionResolveRequest, context: vscode.CancellationToken) {
return server.makeRequest<protocol.CompletionResolveResponse>(protocol.Requests.CompletionResolve, request, context);
}

export async function isNetCoreProject(project: protocol.MSBuildProject) {
return project.TargetFrameworks.find(tf => tf.ShortName.startsWith('netcoreapp') || tf.ShortName.startsWith('netstandard')) !== undefined;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import OmniSharpCompletionItemProvider from "../../src/features/completionItemProvider";
import OmniSharpCompletionProvider from "../../src/features/completionProvider";
import * as vscode from 'vscode';
import testAssetWorkspace from "./testAssets/testAssetWorkspace";
import * as path from "path";
import { expect } from "chai";
import { activateCSharpExtension, isRazorWorkspace } from "./integrationHelpers";

suite(`${OmniSharpCompletionItemProvider.name}: Returns the completion items`, () => {
suite(`${OmniSharpCompletionProvider.name}: Returns the completion items`, () => {
let fileUri: vscode.Uri;

suiteSetup(async function () {
Expand Down Expand Up @@ -42,4 +42,4 @@ suite(`${OmniSharpCompletionItemProvider.name}: Returns the completion items`, (
let preselectList = completionList.items.filter(item => item.preselect === true);
expect(preselectList).to.not.be.empty;
});
});
});

0 comments on commit 45fa03d

Please sign in to comment.