diff --git a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs index 08025830a4bca..8ce98a031b551 100644 --- a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs +++ b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs @@ -38,18 +38,24 @@ public InlayHintHandler(IGlobalOptionService optionsService) public TextDocumentIdentifier GetTextDocumentIdentifier(InlayHintParams request) => request.TextDocument; - public async Task HandleRequestAsync(InlayHintParams request, RequestContext context, CancellationToken cancellationToken) + public Task HandleRequestAsync(InlayHintParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.GetRequiredDocument(); + var inlayHintCache = context.GetRequiredLspService(); + var options = _optionsService.GetInlineHintsOptions(document.Project.Language); + + return GetInlayHintsAsync(document, request.TextDocument, request.Range, options, displayAllOverride: false, inlayHintCache, cancellationToken); + } + + internal static async Task 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(); - 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(); // 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. @@ -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; diff --git a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs index 122bdcc25e622..468e908a507e6 100644 --- a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs +++ b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs @@ -2,6 +2,7 @@ // 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; @@ -9,7 +10,6 @@ using Roslyn.Utilities; using StreamJsonRpc; using LSP = Roslyn.LanguageServer.Protocol; -using System.Text.Json; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint { @@ -30,11 +30,16 @@ public InlayHintResolveHandler(InlayHintCache inlayHintCache) public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.InlayHint request) => GetInlayHintResolveData(request).TextDocument; - public async Task HandleRequestAsync(LSP.InlayHint request, RequestContext context, CancellationToken cancellationToken) + public Task HandleRequestAsync(LSP.InlayHint request, RequestContext context, CancellationToken cancellationToken) { var document = context.GetRequiredDocument(); + return ResolveInlayHintAsync(document, request, _inlayHintCache, cancellationToken); + } + + internal static async Task 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; @@ -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]); } diff --git a/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj b/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj index c85748a90fe20..73945f4ec700e 100644 --- a/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj +++ b/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj @@ -37,9 +37,12 @@ - + + + + diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs b/src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs new file mode 100644 index 0000000000000..1c7fe47a2ea68 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs @@ -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 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 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); + } + } +} diff --git a/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj b/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj index 41dd9af02dfb1..6164b50074dd8 100644 --- a/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj +++ b/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj @@ -31,6 +31,7 @@ +