Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use CompletionService in IntellisenseService #840

Merged
merged 10 commits into from
May 3, 2017

Conversation

filipw
Copy link
Member

@filipw filipw commented Apr 30, 2017

At the moment, the CompletionService is only used for keywords, everything else is coming from Recommender.

This PR introduces relying on CompletionService for all completions.
The symbols are still processed the same way as before, so none of the existing features are broken, but extra features available via CompletionService also show up in intellisense now (as we process all of its providers).
This will close a lot of open bugs via a relatively risk-free change.

I will push some tests into this soon but here are some of the examples of new features that are introduced via this PR:

Attribute completion without Attribute

Cref completion

Object initializer members completion

Named parameters completion

Override support

Note: because of this Regex in omnisharp-vscode, where brackets are removed, it appears a little broken. This change would need to be done in omnisharp-vscode.

Copy link
Contributor

@DustinCampbell DustinCampbell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Exciting change!

I never opened up the floodgates with CompletionService here because it's not really good enough for completions with multiple text edits (like override completion), or completions that need the commit character in order to complete properly. Personally, I'd like us to figure out override completion a little better before we merge this (to get closer to user expectations), but this should go along ways toward relieving some pain.

// as the display text.
var response = new AutoCompleteResponse()
{
CompletionText = item.DisplayText,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably isn't good enough for override completion. Completions on override will just be wrong until we can properly support CompletionService because the completion is multi-line, moves the caret, and may add usings above.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Completion on partial will also be broken.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI that this is handled by calling when before an item is committed. http://source.roslyn.io/#Microsoft.CodeAnalysis.Features/Completion/CompletionService.cs,bfe3e3a9f66443fb.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To support completion properly we need a new api similar to what LSP provides with additionalTextEdits?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but we'll want to be more of a "resolve" API, so we don't have to go computing this for every completion item.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually overrides and partials won't even reach that specific line because they'd branch out here since these providers encode symbols. So the formatting/presentation of i.e. symbol information will be consistent with the rest of the experience, but yeah ultimately the DisplayText will be used for completion.

From what I experimented, even the simple DisplayText at the moment isn't that bad. It actually, in case of overrides, has the following format https://github.com/dotnet/roslyn/blob/614299ff83da9959fa07131c6d0ffbc58873b6ae/src/Features/Core/Portable/Completion/Providers/AbstractOverrideCompletionProvider.ItemGetter.cs#L21. So you get to see the full signature, and that's what can be inserted into the code even using the current object/endpoint model we have (of course things like missing usings would have to be resolved manually separately by the user)

It's definitely sub par experience compared to VS but at the same time maybe it's better than not having anything?
But of course, we could also just disable the complex providers like override/partial from participating in the old intellisense endpoint and only add them once we have a new one


public static async Task<IEnumerable<ISymbol>> GetCompletionSymbols(this CompletionItem completionItem, IEnumerable<ISymbol> recommendedSymbols, Document document)
{
// for SymbolCompletionProvider, use the logic of extracting information from recommended symbols
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? Isn't call GetSymbols(...) good enough below for all cases?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kind of hate that we pre-compute recommendedSymbols (which is essentially like calling the CompletionService twice) just to handle this special case.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unfortunately since this change dotnet/roslyn#15688, the SymbolCompletionProvider doesn't store encoded symbols in the properties of the completion item anymore, so just calling GetSymbols() results in an empty array. Instead, the only thing I could think of was that they are fetched using Recommender. I am sure you'd know more background behind this change and if there is a better way to do it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, I'd forgotten about that. Computing all symbol IDs was simply too expensive.

public static async Task<IEnumerable<ISymbol>> GetCompletionSymbols(this CompletionItem completionItem, IEnumerable<ISymbol> recommendedSymbols, Document document)
{
// for SymbolCompletionProvider, use the logic of extracting information from recommended symbols
if (completionItem.Properties.ContainsKey("Provider") && completionItem.Properties["Provider"] == "Microsoft.CodeAnalysis.CSharp.Completion.Providers.SymbolCompletionProvider")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be good to throw an exception if the type Microsoft.CodeAnalysis.CSharp.Completion.Providers.SymbolCompletionProvider doesn't exist. That way, we aren't searching around for the reason why completion starts failing if this internal type's name changes.

static CompletionItemExtensions()
{
var symbolCompletionItemType = typeof(CompletionItem).GetTypeInfo().Assembly.GetType("Microsoft.CodeAnalysis.Completion.Providers.SymbolCompletionItem");
_getSymbolsAsync = symbolCompletionItemType.GetMethod("GetSymbolsAsync", BindingFlags.Public | BindingFlags.Static);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've got some extension methods for this sort of thing (used by MetadataHelper) that throw exceptions nicely when they fail. I'd recommend using those to ensure we can easily spot failures if internal API names change.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes good point 👍

@filipw
Copy link
Member Author

filipw commented Apr 30, 2017

thanks, I agree that in order to properly benefit from all the power of CompletionService we'll need a brand new endpoint.
What I tried to do here was to improve the current behavior as much as possible, with relatively small change to the current way the server operates, to solve a few annoyances 😃

Copy link
Member

@david-driscoll david-driscoll left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there are complications with partials and overrides, perhaps we can hide those behind a flag that must be turned on in omnisharp.json?

It's a little more work to remove them from the output, however that lets us keep a nice default for user expectations, but also offer something a little more bleeding edge.

// as the display text.
var response = new AutoCompleteResponse()
{
CompletionText = item.DisplayText,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To support completion properly we need a new api similar to what LSP provides with additionalTextEdits?

@DustinCampbell
Copy link
Contributor

The failures on *Nix look real.

@filipw filipw force-pushed the feature/completion-service branch from a6b9794 to 70c6c11 Compare May 2, 2017 06:37
@filipw filipw closed this May 2, 2017
@filipw filipw reopened this May 2, 2017
@filipw filipw force-pushed the feature/completion-service branch from a1507e6 to 5c8d067 Compare May 2, 2017 12:08
@filipw
Copy link
Member Author

filipw commented May 2, 2017

The failures on *Nix look real.

Yes it was actually caused by this https://github.com/dotnet/corefx/issues/15825#issuecomment-277722247 "bug" ("behavior"?) of CoreFX - default US culture-based sort behavior is different on Windows and *nix. It should be fine now.

@filipw filipw force-pushed the feature/completion-service branch 2 times, most recently from 96b3033 to 5643a01 Compare May 2, 2017 13:23
if (completionItem.Properties.ContainsKey("Provider") && completionItem.Properties["Provider"] == "Microsoft.CodeAnalysis.CSharp.Completion.Providers.SymbolCompletionProvider")
{
var symbols = recommendedSymbols.Where(x => x.Name == completionItem.Properties["SymbolName"] && (int)x.Kind == int.Parse(completionItem.Properties["SymbolKind"])).Distinct();
return symbols ?? Enumerable.Empty<ISymbol>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think Where(...) or Distinct(...) will ever return null.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are right 😊

@DustinCampbell
Copy link
Contributor

Note: because of this Regex in omnisharp-vscode, where brackets are removed, it appears a little broken. This change would need to be done in omnisharp-vscode.

FWIW, I've never noticed that regex. It existed before I was on the project, so it's presence is a mystery.

Copy link
Contributor

@DustinCampbell DustinCampbell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do this. We might want to revisit the experience around completion items that insert methods (e.g. override, partial), but this is definitely an improvement!

Copy link
Member

@david-driscoll david-driscoll left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@DustinCampbell
Copy link
Contributor

All right. Merging... 😄

@DustinCampbell DustinCampbell merged commit 9a811e7 into OmniSharp:dev May 3, 2017
@filipw
Copy link
Member Author

filipw commented May 3, 2017

awesome! 🐣☀️

@filipw filipw deleted the feature/completion-service branch May 3, 2017 21:14
@lukepuplett
Copy link

Hello - sorry to bother but a cluster of issues all point to this. I don't get intellisense for C# object initializers in VSCode and it seems this has been a feature for years, right? I'm fully up to date, I think; I don't know how to check the version of OmniSharp.

My question is: where do I go for help on this?

@darkguy2008
Copy link

I have to +1 @lukepuplett as it seems I'm experiencing the same thing. Trying to get intellisense for class properties in its initializer is sending me straight to the Process namespace (halfway through the full .NET namespace list) instead of sending me to the top of the list (where the properties are).

I opened a bug report in VSCode here but maybe it's related to this instead? microsoft/vscode#128963

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants