-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Move Quick Info content production to the Features layer and add Razor EA co-hosting APIs for Hover and Completion #75226
Move Quick Info content production to the Features layer and add Razor EA co-hosting APIs for Hover and Completion #75226
Conversation
This is necessary to produce rich VS adornment-based hover tooltips in Roslyn OOP. Currently this logic lives in the EditorFeatures layer.
IntellisenseQuickInfoBuilderContext has four(!) properties that are used to create navigation Actions. I've wrapped this in NavigationActionFactory class.
IntellisenseQuickInfoBuilderContext renamed to QuickInfoContentBuilderContext
EditorFeaturesOnTheFlyDocsElement renamed to QuickInfoOnTheFlyDocsElement
This change is a rewrite of BuildInteractiveTextElements to reduce allocations and prepare it to be moved to the Features layer. In addition, it is no exposed as an extension method on ImmutableArray<TaggedText> named ToInteractiveTextElements(...).
With the logic to produce rich Quick Info content in the Features layer, this service is no longer and its logic can be moved into HoverHandler.
I can review the tomorrow! |
src/Features/Core/Portable/Common/GlyphExtensions.KnownImageIds.cs
Outdated
Show resolved
Hide resolved
src/Features/Core/Portable/QuickInfo/Presentation/QuickInfoContentBuilder.cs
Outdated
Show resolved
Hide resolved
{ | ||
try | ||
{ | ||
using var token = asyncListener.BeginAsyncOperation(nameof(NavigateToTargetAsync)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for the big blocks of code, can you link to the place it got moved from?
navigationTarget, workspace, documentId, threadingContext, operationExecutor, asyncListener, streamingPresenter.Value).Forget(); | ||
} | ||
|
||
private static async Task NavigateToTargetAsync( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 This method was moved from
roslyn/src/EditorFeatures/Core/IntelliSense/Helpers.cs
Lines 190 to 232 in 4da55b7
private static async Task NavigateToQuickInfoTargetAsync( | |
string navigationTarget, | |
Workspace workspace, | |
DocumentId documentId, | |
IThreadingContext threadingContext, | |
IUIThreadOperationExecutor operationExecutor, | |
IAsynchronousOperationListener asyncListener, | |
IStreamingFindUsagesPresenter streamingPresenter) | |
{ | |
try | |
{ | |
using var token = asyncListener.BeginAsyncOperation(nameof(NavigateToQuickInfoTargetAsync)); | |
using var context = operationExecutor.BeginExecute(EditorFeaturesResources.IntelliSense, EditorFeaturesResources.Navigating, allowCancellation: true, showProgress: false); | |
var cancellationToken = context.UserCancellationToken; | |
var solution = workspace.CurrentSolution; | |
SymbolKeyResolution resolvedSymbolKey; | |
try | |
{ | |
var project = solution.GetRequiredProject(documentId.ProjectId); | |
var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); | |
resolvedSymbolKey = SymbolKey.ResolveString(navigationTarget, compilation, cancellationToken: cancellationToken); | |
} | |
catch | |
{ | |
// Ignore symbol resolution failures. It likely is just a badly formed URI. | |
return; | |
} | |
if (resolvedSymbolKey.GetAnySymbol() is { } symbol) | |
{ | |
var location = await GoToDefinitionHelpers.GetDefinitionLocationAsync( | |
symbol, solution, threadingContext, streamingPresenter, cancellationToken).ConfigureAwait(false); | |
await location.TryNavigateToAsync(threadingContext, new NavigationOptions(PreferProvisionalTab: true, ActivateTab: true), cancellationToken).ConfigureAwait(false); | |
} | |
} | |
catch (OperationCanceledException) | |
{ | |
} | |
catch (Exception ex) when (FatalError.ReportAndCatch(ex, ErrorSeverity.Critical)) | |
{ | |
} | |
} |
|
||
namespace Microsoft.CodeAnalysis.QuickInfo.Presentation; | ||
|
||
internal static class TaggedTextExtensions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 This was moved and refactored from https://github.com/dotnet/roslyn/blob/main/src/EditorFeatures/Core/IntelliSense/Helpers.cs.
|
||
namespace Microsoft.CodeAnalysis.QuickInfo.Presentation; | ||
|
||
internal static class QuickInfoContentBuilder |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 The bulk of this was moved and refactored from https://github.com/dotnet/roslyn/blob/main/src/EditorFeatures/Core/IntelliSense/QuickInfo/IntellisenseQuickInfoBuilder.cs.
|
||
namespace Microsoft.CodeAnalysis.QuickInfo.Presentation; | ||
|
||
internal sealed class QuickInfoOnTheFlyDocsElement(Document document, OnTheFlyDocsInfo info) : QuickInfoElement |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 This was moved and adjusted from https://github.com/dotnet/roslyn/blob/main/src/EditorFeatures/Core/IntelliSense/QuickInfo/EditorFeaturesOnTheFlyDocsElement.cs.
|
||
namespace Microsoft.CodeAnalysis.QuickInfo.Presentation; | ||
|
||
internal sealed class QuickInfoContentBuilderContext( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 This was essentially moved from https://github.com/dotnet/roslyn/blob/main/src/EditorFeatures/Core/IntelliSense/QuickInfo/IntellisenseQuickInfoBuilderContext.cs.
using Roslyn.Utilities; | ||
using LSP = Roslyn.LanguageServer.Protocol; | ||
|
||
namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Completion | ||
{ | ||
internal abstract class AbstractLspCompletionResultCreationService : ILspCompletionResultCreationService | ||
internal static class CompletionResultFactory |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 This is essentially a merge of AbstractLspCompletionResultCreationService, DefaultLspCompletionResultCreationService, and EditorLspCompletionResultCreationService
Per code review feedback, this extension shouldn't be defined in the Features layer. Instead, it can move to LanguageServer.Protocol and be leveraged from the EditorFeatures layer.
06451df
to
a60dd72
Compare
src/LanguageServer/Protocol/Handler/Completion/CompletionResolveHandler.cs
Show resolved
Hide resolved
src/LanguageServer/Protocol/Handler/Completion/CompletionResultFactory.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EA bits look great, thanks for the extra work!
Skimmed the rest and the broad strokes made sense, I'll leave Roslyn folks to comment on the Roslyn-isms.
Razor is moving toward an architecture where its LSP handlers are loaded within the Roslyn LSP and are implemented via services that run in Roslyn OOP within VS Service Hub. Of the handlers that Razor needs to implement, it turns out that 'hover' and 'completion' (and likely, 'documentSymbol' and 'references') present a special challenge. In Roslyn, these handler implementations rely on host-provided services to provide the results for a default LSP client or richer results for the VS LSP client. However, the VS-targeted services are implemented in the EditorFeatures layer, which is never loaded in the Roslyn OOP.
This pull requests decouples these services from their VS dependencies and pushes the service logic into the Features, or into the Roslyn language server itself.
While this change is needed to support Razor co-hosting, it would also be needed if Roslyn ever wanted to move its VS language server out-of-process.
cc @dibarbet and @davidwengier