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

Expose inlay hints to Razor cohosting #74548

Merged
merged 2 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,24 @@ public InlayHintHandler(IGlobalOptionService optionsService)
public TextDocumentIdentifier GetTextDocumentIdentifier(InlayHintParams request)
=> request.TextDocument;

public async Task<LSP.InlayHint[]?> HandleRequestAsync(InlayHintParams request, RequestContext context, CancellationToken cancellationToken)
public Task<LSP.InlayHint[]?> HandleRequestAsync(InlayHintParams request, RequestContext context, CancellationToken cancellationToken)
{
var document = context.GetRequiredDocument();
var inlayHintCache = context.GetRequiredLspService<InlayHintCache>();
var options = _optionsService.GetInlineHintsOptions(document.Project.Language);

return GetInlayHintsAsync(document, request.TextDocument, request.Range, options, displayAllOverride: false, inlayHintCache, cancellationToken);
}

internal static async Task<LSP.InlayHint[]?> GetInlayHintsAsync(Document document, TextDocumentIdentifier textDocumentIdentifier, LSP.Range range, InlineHintsOptions options, bool displayAllOverride, InlayHintCache inlayHintCache, CancellationToken cancellationToken)
{
var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false);
var textSpan = ProtocolConversions.RangeToTextSpan(request.Range, text);
var textSpan = ProtocolConversions.RangeToTextSpan(range, text);

var inlineHintService = document.GetRequiredLanguageService<IInlineHintsService>();
var options = _optionsService.GetInlineHintsOptions(document.Project.Language);
var hints = await inlineHintService.GetInlineHintsAsync(document, textSpan, options, displayAllOverride: false, cancellationToken).ConfigureAwait(false);
var hints = await inlineHintService.GetInlineHintsAsync(document, textSpan, options, displayAllOverride, cancellationToken).ConfigureAwait(false);

var syntaxVersion = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false);
var inlayHintCache = context.GetRequiredLspService<InlayHintCache>();

// Store the members in the resolve cache so that when we get a resolve request for a particular
// member we can re-use the inline hint.
Expand Down Expand Up @@ -83,7 +89,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(InlayHintParams request)
ToolTip = null,
PaddingLeft = leftPadding,
PaddingRight = rightPadding,
Data = new InlayHintResolveData(resultId, i, request.TextDocument)
Data = new InlayHintResolveData(resultId, i, textDocumentIdentifier)
};

inlayHints[i] = inlayHint;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.InlineHints;
using Roslyn.LanguageServer.Protocol;
using Roslyn.Utilities;
using StreamJsonRpc;
using LSP = Roslyn.LanguageServer.Protocol;
using System.Text.Json;

namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint
{
Expand All @@ -30,11 +30,16 @@ public InlayHintResolveHandler(InlayHintCache inlayHintCache)
public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.InlayHint request)
=> GetInlayHintResolveData(request).TextDocument;

public async Task<LSP.InlayHint> HandleRequestAsync(LSP.InlayHint request, RequestContext context, CancellationToken cancellationToken)
public Task<LSP.InlayHint> HandleRequestAsync(LSP.InlayHint request, RequestContext context, CancellationToken cancellationToken)
{
var document = context.GetRequiredDocument();
return ResolveInlayHintAsync(document, request, _inlayHintCache, cancellationToken);
}

internal static async Task<LSP.InlayHint> ResolveInlayHintAsync(Document document, LSP.InlayHint request, InlayHintCache inlayHintCache, CancellationToken cancellationToken)
{
var resolveData = GetInlayHintResolveData(request);
var (cacheEntry, inlineHintToResolve) = GetCacheEntry(resolveData);
var (cacheEntry, inlineHintToResolve) = GetCacheEntry(resolveData, inlayHintCache);

var currentSyntaxVersion = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false);
var cachedSyntaxVersion = cacheEntry.SyntaxVersion;
Expand All @@ -53,9 +58,9 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.InlayHint request)
return request;
}

private (InlayHintCache.InlayHintCacheEntry CacheEntry, InlineHint InlineHintToResolve) GetCacheEntry(InlayHintResolveData resolveData)
private static (InlayHintCache.InlayHintCacheEntry CacheEntry, InlineHint InlineHintToResolve) GetCacheEntry(InlayHintResolveData resolveData, InlayHintCache inlayHintCache)
{
var cacheEntry = _inlayHintCache.GetCachedEntry(resolveData.ResultId);
var cacheEntry = inlayHintCache.GetCachedEntry(resolveData.ResultId);
Contract.ThrowIfNull(cacheEntry, "Missing cache entry for inlay hint resolve request");
return (cacheEntry, cacheEntry.InlayHintMembers[resolveData.ListIndex]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,12 @@
<!-- Full IVT is through ExternalAccess for functionality -->
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.ExternalAccess.Razor" />
<!-- Restricted IVT is direct for protocol types only -->
<RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.Remote.Razor" Namespace="Roslyn.LanguageServer.Protocol" Partner="Razor" Key="$(RazorKey)" />
<RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.Razor.Workspaces" Namespace="Roslyn.LanguageServer.Protocol" Partner="Razor" Key="$(RazorKey)" />
<RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.Remote.Razor" Namespace="Roslyn.LanguageServer.Protocol" Partner="Razor" Key="$(RazorKey)" />
<RestrictedInternalsVisibleTo Include="Microsoft.VisualStudio.LanguageServices.Razor" Namespace="Roslyn.LanguageServer.Protocol" Partner="Razor" Key="$(RazorKey)" />
<!-- Restricted IVT for protocol types for Razor tests -->
<RestrictedInternalsVisibleTo Include="Microsoft.VisualStudio.LanguageServices.Razor.Test" Namespace="Roslyn.LanguageServer.Protocol" Partner="Razor" Key="$(RazorKey)" />
<!-- Full IVT is through ExternalAccess for functionality -->
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.ExternalAccess.Xaml" />
<!-- Restricted IVT is direct for protocol types only -->
<RestrictedInternalsVisibleTo Include="Microsoft.VisualStudio.DesignTools.CodeAnalysis" Namespace="Roslyn.LanguageServer.Protocol" Partner="Xaml" Key="$(VisualStudioKey)" />
Expand Down
49 changes: 49 additions & 0 deletions src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.InlineHints;
using Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint;
using Roslyn.LanguageServer.Protocol;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost.Handlers
{
internal static class InlayHints
{
// In the Roslyn LSP server this cache has the same lifetime as the LSP server. For Razor, running OOP, we don't have
// that same lifetime anywhere, everything is just static. This is likely not ideal, but the inlay hint cache has a
// max size of 3 items, so it's not a huge deal.
private static InlayHintCache? s_resolveCache;

public static Task<InlayHint[]?> GetInlayHintsAsync(Document document, TextDocumentIdentifier textDocumentIdentifier, Range range, bool displayAllOverride, CancellationToken cancellationToken)
{
s_resolveCache ??= new();

// Currently Roslyn options don't sync to OOP so trying to get the real options out of IGlobalOptionsService will
// always just result in the defaults, which for inline hints are to not show anything. However, the editor has a
// setting for LSP inlay hints, so we can assume that if we get a request from the client, the user wants hints.
// When overriding however, Roslyn does a nicer job if type hints are off.
var options = InlineHintsOptions.Default;
if (!displayAllOverride)
{
options = options with
{
TypeOptions = options.TypeOptions with { EnabledForTypes = true },
ParameterOptions = options.ParameterOptions with { EnabledForParameters = true },
};
}

return InlayHintHandler.GetInlayHintsAsync(document, textDocumentIdentifier, range, options, displayAllOverride, s_resolveCache, cancellationToken);
}

public static Task<InlayHint> ResolveInlayHintAsync(Document document, InlayHint request, CancellationToken cancellationToken)
{
Contract.ThrowIfNull(s_resolveCache, "Cache should never be null for resolve, since it should have been created by the original request");

return InlayHintResolveHandler.ResolveInlayHintAsync(document, request, s_resolveCache, cancellationToken);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.Razor.Workspaces" Key="$(RazorKey)" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.Razor.Workspaces.Test" Key="$(RazorKey)" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.Remote.Razor" Key="$(RazorKey)" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.Remote.Razor.Test" Key="$(RazorKey)" />
<InternalsVisibleTo Include="Microsoft.VisualStudio.Editor.Razor" Key="$(RazorKey)" />
<InternalsVisibleTo Include="Microsoft.VisualStudio.LanguageServerClient.Razor" Key="$(RazorKey)" />
<InternalsVisibleTo Include="Microsoft.VisualStudio.LanguageServerClient.Razor.Test" Key="$(RazorKey)" />
Expand Down
Loading