From 4f3a760b36cadca18b0de8392da7caeb9830cffa Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Mon, 20 May 2024 17:17:39 -0700 Subject: [PATCH 1/6] Return the SignatureHelp items nearest to the cursor --- .../SignatureHelp/SignatureHelpHandler.cs | 87 ++++++++++++------- .../SignatureHelp/SignatureHelpTests.cs | 23 +++++ 2 files changed, 79 insertions(+), 31 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs b/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs index 058779150580c..1f417cbaf4c95 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs @@ -10,9 +10,9 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.SignatureHelp; -using Microsoft.CodeAnalysis.Options; using Roslyn.Text.Adornments; using LSP = Roslyn.LanguageServer.Protocol; @@ -53,48 +53,73 @@ public SignatureHelpHandler( var triggerInfo = new SignatureHelpTriggerInfo(SignatureHelpTriggerReason.InvokeSignatureHelpCommand); var options = _globalOptions.GetSignatureHelpOptions(document.Project.Language); + var sigHelpItems = new ArrayBuilder(); + foreach (var provider in providers) { var items = await provider.Value.GetItemsAsync(document, position, triggerInfo, options, cancellationToken).ConfigureAwait(false); - if (items != null) + if (items is null) { - var sigInfos = new ArrayBuilder(); + continue; + } - foreach (var item in items.Items) - { - LSP.SignatureInformation sigInfo; - if (clientCapabilities.HasVisualStudioLspCapability() == true) - { - sigInfo = new LSP.VSInternalSignatureInformation - { - ColorizedLabel = GetSignatureClassifiedText(item) - }; - } - else - { - sigInfo = new LSP.SignatureInformation(); - } + sigHelpItems.Add(items); + } + + // Multiple providers could be offering items at the current location. As + // there might be an ObjectCreationExpression within a MethodInvocation. + // We want to identify the items nearest to the cursor. + + SignatureHelpItems? helpItems = null; + var minDistance = int.MaxValue; + foreach (var items in sigHelpItems.ToArrayAndFree()) + { + var distanceFromCursor = position - items.ApplicableSpan.Start; + if (distanceFromCursor < minDistance) + { + minDistance = distanceFromCursor; + helpItems = items; + } + } + + if (helpItems is not null) + { + var sigInfos = new ArrayBuilder(); - sigInfo.Label = GetSignatureText(item); - sigInfo.Documentation = new LSP.MarkupContent { Kind = LSP.MarkupKind.PlainText, Value = item.DocumentationFactory(cancellationToken).GetFullText() }; - sigInfo.Parameters = item.Parameters.Select(p => new LSP.ParameterInformation + foreach (var item in helpItems.Items) + { + LSP.SignatureInformation sigInfo; + if (clientCapabilities.HasVisualStudioLspCapability() == true) + { + sigInfo = new LSP.VSInternalSignatureInformation { - Label = p.Name, - Documentation = new LSP.MarkupContent { Kind = LSP.MarkupKind.PlainText, Value = p.DocumentationFactory(cancellationToken).GetFullText() } - }).ToArray(); - sigInfos.Add(sigInfo); + ColorizedLabel = GetSignatureClassifiedText(item) + }; } - - var sigHelp = new LSP.SignatureHelp + else { - ActiveSignature = GetActiveSignature(items), - ActiveParameter = items.ArgumentIndex, - Signatures = sigInfos.ToArrayAndFree() - }; + sigInfo = new LSP.SignatureInformation(); + } - return sigHelp; + sigInfo.Label = GetSignatureText(item); + sigInfo.Documentation = new LSP.MarkupContent { Kind = LSP.MarkupKind.PlainText, Value = item.DocumentationFactory(cancellationToken).GetFullText() }; + sigInfo.Parameters = item.Parameters.Select(p => new LSP.ParameterInformation + { + Label = p.Name, + Documentation = new LSP.MarkupContent { Kind = LSP.MarkupKind.PlainText, Value = p.DocumentationFactory(cancellationToken).GetFullText() } + }).ToArray(); + sigInfos.Add(sigInfo); } + + var sigHelp = new LSP.SignatureHelp + { + ActiveSignature = GetActiveSignature(helpItems), + ActiveParameter = helpItems.ArgumentIndex, + Signatures = sigInfos.ToArrayAndFree() + }; + + return sigHelp; } return null; diff --git a/src/Features/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs index d654f0ff66553..4e2cac33b0b1a 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs @@ -49,6 +49,29 @@ int M2(string a) AssertJsonEquals(expected, results); } + [Theory, CombinatorialData] + public async Task TestGetNestedSignatureHelpAsync(bool mutatingLspWorkspace) + { + var markup = +@"class Foo { + public Foo(int showMe) {} + + public static void Do(Foo foo) { + Do(new Foo({|caret:|} + } +}"; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + var expected = new LSP.SignatureHelp() + { + ActiveParameter = 0, + ActiveSignature = 0, + Signatures = [CreateSignatureInformation("Foo(int showMe)", "", "showMe", "")] + }; + + var results = await RunGetSignatureHelpAsync(testLspServer, testLspServer.GetLocations("caret").Single()); + AssertJsonEquals(expected, results); + } + private static async Task RunGetSignatureHelpAsync(TestLspServer testLspServer, LSP.Location caret) { return await testLspServer.ExecuteRequestAsync( From 593792b9bd321dd4f564e25ed64302b5b1fb3be0 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Tue, 21 May 2024 21:19:17 -0700 Subject: [PATCH 2/6] Add a SignatureHelpService that provides help items for the specified position. --- .../Controller.Session_ComputeModel.cs | 73 ++----------- .../CSharpSignatureHelpService.cs | 32 ++++++ .../SignatureHelp/SignatureHelpService.cs | 103 ++++++++++++++++++ .../SignatureHelpServiceWithProviders.cs | 65 +++++++++++ .../SignatureHelp/SignatureHelpHandler.cs | 97 ++++++----------- .../VisualBasicSignatureHelpService.vb | 33 ++++++ 6 files changed, 273 insertions(+), 130 deletions(-) create mode 100644 src/Features/CSharp/Portable/SignatureHelp/CSharpSignatureHelpService.cs create mode 100644 src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs create mode 100644 src/Features/Core/Portable/SignatureHelp/SignatureHelpServiceWithProviders.cs create mode 100644 src/Features/VisualBasic/Portable/SignatureHelp/VisualBasicSignatureHelpService.vb diff --git a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs index e08b50aed307f..23074ae1bebed 100644 --- a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs +++ b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs @@ -13,10 +13,8 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SignatureHelp; -using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; using Roslyn.Utilities; @@ -87,9 +85,14 @@ private async Task ComputeModelInBackgroundAsync( var options = Controller.GlobalOptions.GetSignatureHelpOptions(document.Project.Language); // first try to query the providers that can trigger on the specified character - var (provider, items) = await ComputeItemsAsync( - providers, caretPosition, triggerInfo, - options, document, cancellationToken).ConfigureAwait(false); + var service = document.GetRequiredLanguageService(); + var (provider, items) = await service.GetSignatureHelpAsync( + providers, + document, + caretPosition, + triggerInfo, + options, + cancellationToken).ConfigureAwait(false); if (provider == null) { @@ -174,66 +177,6 @@ private static bool DisplayPartsMatch(SignatureHelpItem i1, SignatureHelpItem i2 private static bool CompareParts(TaggedText p1, TaggedText p2) => p1.ToString() == p2.ToString(); - - private static async Task<(ISignatureHelpProvider provider, SignatureHelpItems items)> ComputeItemsAsync( - ImmutableArray providers, - SnapshotPoint caretPosition, - SignatureHelpTriggerInfo triggerInfo, - SignatureHelpOptions options, - Document document, - CancellationToken cancellationToken) - { - try - { - ISignatureHelpProvider bestProvider = null; - SignatureHelpItems bestItems = null; - - // TODO(cyrusn): We're calling into extensions, we need to make ourselves resilient - // to the extension crashing. - foreach (var provider in providers) - { - cancellationToken.ThrowIfCancellationRequested(); - - var currentItems = await provider.GetItemsAsync(document, caretPosition, triggerInfo, options, cancellationToken).ConfigureAwait(false); - if (currentItems != null && currentItems.ApplicableSpan.IntersectsWith(caretPosition.Position)) - { - // If another provider provides sig help items, then only take them if they - // start after the last batch of items. i.e. we want the set of items that - // conceptually are closer to where the caret position is. This way if you have: - // - // Goo(new Bar($$ - // - // Then invoking sig help will only show the items for "new Bar(" and not also - // the items for "Goo(..." - if (IsBetter(bestItems, currentItems.ApplicableSpan)) - { - bestItems = currentItems; - bestProvider = provider; - } - } - } - - return (bestProvider, bestItems); - } - catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) - { - return (null, null); - } - } - - private static bool IsBetter(SignatureHelpItems bestItems, TextSpan? currentTextSpan) - { - // If we have no best text span, then this span is definitely better. - if (bestItems == null) - { - return true; - } - - // Otherwise we want the one that is conceptually the innermost signature. So it's - // only better if the distance from it to the caret position is less than the best - // one so far. - return currentTextSpan.Value.Start > bestItems.ApplicableSpan.Start; - } } } } diff --git a/src/Features/CSharp/Portable/SignatureHelp/CSharpSignatureHelpService.cs b/src/Features/CSharp/Portable/SignatureHelp/CSharpSignatureHelpService.cs new file mode 100644 index 0000000000000..dbf3756a3c787 --- /dev/null +++ b/src/Features/CSharp/Portable/SignatureHelp/CSharpSignatureHelpService.cs @@ -0,0 +1,32 @@ +// 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; +using System.Composition; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.SignatureHelp; + +namespace Microsoft.CodeAnalysis.CSharp.SignatureHelp; + +[ExportLanguageServiceFactory(typeof(SignatureHelpService), LanguageNames.CSharp), Shared] +internal class CSharpSignatureHelpServiceFactory : ILanguageServiceFactory +{ + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public CSharpSignatureHelpServiceFactory() + { + } + + public ILanguageService CreateLanguageService(HostLanguageServices languageServices) + => new CSharpSignatureHelpService(languageServices.LanguageServices); +} + +internal class CSharpSignatureHelpService : SignatureHelpServiceWithProviders +{ + internal CSharpSignatureHelpService(LanguageServices services) + : base(services) + { + } +} diff --git a/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs b/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs new file mode 100644 index 0000000000000..207634bd8efad --- /dev/null +++ b/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs @@ -0,0 +1,103 @@ +// 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.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.SignatureHelp; + +/// +/// A service that is used to determine the appropriate signature help for a position in a document. +/// +internal abstract class SignatureHelpService : ILanguageService +{ + /// + /// Gets the appropriate for the specified document. + /// + public static SignatureHelpService? GetService(Document? document) + => document?.GetLanguageService(); + + /// + /// Gets the and associated with + /// the position in the document. + /// + public abstract Task<(ISignatureHelpProvider? provider, SignatureHelpItems? bestItems)> GetSignatureHelpAsync( + Document document, + int position, + SignatureHelpTriggerInfo triggerInfo, + SignatureHelpOptions options, + CancellationToken cancellationToken = default); + + /// + /// Gets the and associated with + /// the position in the document. + /// + public async Task<(ISignatureHelpProvider? provider, SignatureHelpItems? bestItems)> GetSignatureHelpAsync( + ImmutableArray providers, + Document document, + int position, + SignatureHelpTriggerInfo triggerInfo, + SignatureHelpOptions options, + CancellationToken cancellationToken) + { + ISignatureHelpProvider? bestProvider = null; + SignatureHelpItems? bestItems = null; + + // returns the first non-empty quick info found (based on provider order) + foreach (var provider in providers) + { + var items = await TryGetItemsAsync(document, position, triggerInfo, options, provider, cancellationToken).ConfigureAwait(false); + if (items is null) + { + continue; + } + + // If another provider provides sig help items, then only take them if they + // start after the last batch of items. i.e. we want the set of items that + // conceptually are closer to where the caret position is. This way if you have: + // + // Goo(new Bar($$ + // + // Then invoking sig help will only show the items for "new Bar(" and not also + // the items for "Goo(..." + if (bestItems is not null && items.ApplicableSpan.Start < bestItems.ApplicableSpan.Start) + { + continue; + } + + bestProvider = provider; + bestItems = items; + } + + return (bestProvider, bestItems); + } + + private static async Task TryGetItemsAsync(Document document, int position, SignatureHelpTriggerInfo triggerInfo, SignatureHelpOptions options, ISignatureHelpProvider provider, CancellationToken cancellationToken) + { + // We're calling into extensions, we need to make ourselves resilient + // to the extension crashing. + try + { + var items = await provider.GetItemsAsync(document, position, triggerInfo, options, cancellationToken).ConfigureAwait(false); + if (items is null) + { + return null; + } + + if (!items.ApplicableSpan.IntersectsWith(position)) + { + return null; + } + + return items; + } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) + { + return null; + } + } +} diff --git a/src/Features/Core/Portable/SignatureHelp/SignatureHelpServiceWithProviders.cs b/src/Features/Core/Portable/SignatureHelp/SignatureHelpServiceWithProviders.cs new file mode 100644 index 0000000000000..d08d4df22b76c --- /dev/null +++ b/src/Features/Core/Portable/SignatureHelp/SignatureHelpServiceWithProviders.cs @@ -0,0 +1,65 @@ +// 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; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.Utilities; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.SignatureHelp; + +/// +/// Base class for 's that delegate to 's. +/// +internal abstract class SignatureHelpServiceWithProviders : SignatureHelpService +{ + private readonly LanguageServices _services; + private ImmutableArray _providers; + + protected SignatureHelpServiceWithProviders(LanguageServices services) + { + _services = services; + } + + private ImmutableArray GetProviders() + { + if (_providers.IsDefault) + { + var mefExporter = _services.SolutionServices.ExportProvider; + + var providers = ExtensionOrderer + .Order(mefExporter.GetExports() + .Where(lz => lz.Metadata.Language == _services.Language)) + .Select(lz => lz.Value) + .ToImmutableArray(); + + ImmutableInterlocked.InterlockedCompareExchange(ref _providers, providers, default); + } + + return _providers; + } + + public override Task<(ISignatureHelpProvider? provider, SignatureHelpItems? bestItems)> GetSignatureHelpAsync( + Document document, + int position, + SignatureHelpTriggerInfo triggerInfo, + SignatureHelpOptions options, + CancellationToken cancellationToken) + { + return GetSignatureHelpAsync( + GetProviders(), + document, + position, + triggerInfo, + options, + cancellationToken); + } +} + diff --git a/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs b/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs index 1f417cbaf4c95..f0d50e53e1f7d 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Composition; using System.Linq; using System.Text; @@ -12,6 +11,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SignatureHelp; using Roslyn.Text.Adornments; using LSP = Roslyn.LanguageServer.Protocol; @@ -22,16 +22,12 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler [Method(LSP.Methods.TextDocumentSignatureHelpName)] internal class SignatureHelpHandler : ILspServiceDocumentRequestHandler { - private readonly IEnumerable> _allProviders; private readonly IGlobalOptionService _globalOptions; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public SignatureHelpHandler( - [ImportMany] IEnumerable> allProviders, - IGlobalOptionService globalOptions) + public SignatureHelpHandler(IGlobalOptionService globalOptions) { - _allProviders = allProviders; _globalOptions = globalOptions; } @@ -47,82 +43,53 @@ public SignatureHelpHandler( if (document == null) return null; - var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); + var service = document.GetRequiredLanguageService(); - var providers = _allProviders.Where(p => p.Metadata.Language == document.Project.Language); + var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var triggerInfo = new SignatureHelpTriggerInfo(SignatureHelpTriggerReason.InvokeSignatureHelpCommand); var options = _globalOptions.GetSignatureHelpOptions(document.Project.Language); - var sigHelpItems = new ArrayBuilder(); - - foreach (var provider in providers) + var (_, sigItems) = await service.GetSignatureHelpAsync(document, position, triggerInfo, options, cancellationToken).ConfigureAwait(false); + if (sigItems is null) { - var items = await provider.Value.GetItemsAsync(document, position, triggerInfo, options, cancellationToken).ConfigureAwait(false); - - if (items is null) - { - continue; - } - - sigHelpItems.Add(items); + return null; } - // Multiple providers could be offering items at the current location. As - // there might be an ObjectCreationExpression within a MethodInvocation. - // We want to identify the items nearest to the cursor. + using var _ = ArrayBuilder.GetInstance(out var sigInfos); - SignatureHelpItems? helpItems = null; - var minDistance = int.MaxValue; - foreach (var items in sigHelpItems.ToArrayAndFree()) + foreach (var item in sigItems.Items) { - var distanceFromCursor = position - items.ApplicableSpan.Start; - if (distanceFromCursor < minDistance) + LSP.SignatureInformation sigInfo; + if (clientCapabilities.HasVisualStudioLspCapability() == true) { - minDistance = distanceFromCursor; - helpItems = items; + sigInfo = new LSP.VSInternalSignatureInformation + { + ColorizedLabel = GetSignatureClassifiedText(item) + }; } - } - - if (helpItems is not null) - { - var sigInfos = new ArrayBuilder(); - - foreach (var item in helpItems.Items) + else { - LSP.SignatureInformation sigInfo; - if (clientCapabilities.HasVisualStudioLspCapability() == true) - { - sigInfo = new LSP.VSInternalSignatureInformation - { - ColorizedLabel = GetSignatureClassifiedText(item) - }; - } - else - { - sigInfo = new LSP.SignatureInformation(); - } - - sigInfo.Label = GetSignatureText(item); - sigInfo.Documentation = new LSP.MarkupContent { Kind = LSP.MarkupKind.PlainText, Value = item.DocumentationFactory(cancellationToken).GetFullText() }; - sigInfo.Parameters = item.Parameters.Select(p => new LSP.ParameterInformation - { - Label = p.Name, - Documentation = new LSP.MarkupContent { Kind = LSP.MarkupKind.PlainText, Value = p.DocumentationFactory(cancellationToken).GetFullText() } - }).ToArray(); - sigInfos.Add(sigInfo); + sigInfo = new LSP.SignatureInformation(); } - var sigHelp = new LSP.SignatureHelp + sigInfo.Label = GetSignatureText(item); + sigInfo.Documentation = new LSP.MarkupContent { Kind = LSP.MarkupKind.PlainText, Value = item.DocumentationFactory(cancellationToken).GetFullText() }; + sigInfo.Parameters = item.Parameters.Select(p => new LSP.ParameterInformation { - ActiveSignature = GetActiveSignature(helpItems), - ActiveParameter = helpItems.ArgumentIndex, - Signatures = sigInfos.ToArrayAndFree() - }; - - return sigHelp; + Label = p.Name, + Documentation = new LSP.MarkupContent { Kind = LSP.MarkupKind.PlainText, Value = p.DocumentationFactory(cancellationToken).GetFullText() } + }).ToArray(); + sigInfos.Add(sigInfo); } - return null; + var sigHelp = new LSP.SignatureHelp + { + ActiveSignature = GetActiveSignature(sigItems), + ActiveParameter = sigItems.ArgumentIndex, + Signatures = sigInfos.ToArray() + }; + + return sigHelp; } private static int GetActiveSignature(SignatureHelpItems items) diff --git a/src/Features/VisualBasic/Portable/SignatureHelp/VisualBasicSignatureHelpService.vb b/src/Features/VisualBasic/Portable/SignatureHelp/VisualBasicSignatureHelpService.vb new file mode 100644 index 0000000000000..99a944e1e3afd --- /dev/null +++ b/src/Features/VisualBasic/Portable/SignatureHelp/VisualBasicSignatureHelpService.vb @@ -0,0 +1,33 @@ +' 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. + +Imports System.Composition +Imports Microsoft.CodeAnalysis.Host +Imports Microsoft.CodeAnalysis.Host.Mef +Imports Microsoft.CodeAnalysis.SignatureHelp + +Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp + + Friend Class VisualBasicSignatureHelpServiceFactory + Implements ILanguageServiceFactory + + + + Public Sub New() + End Sub + + Public Function CreateLanguageService(languageServices As HostLanguageServices) As ILanguageService Implements ILanguageServiceFactory.CreateLanguageService + Return New VisualBasicSignatureHelpService(languageServices.LanguageServices) + End Function + + End Class + + Friend Class VisualBasicSignatureHelpService + Inherits SignatureHelpServiceWithProviders + + Public Sub New(services As Host.LanguageServices) + MyBase.New(services) + End Sub + End Class +End Namespace From 6da3b0f0df012870c4f85579d95347e06fc9fe33 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Wed, 22 May 2024 15:39:09 -0700 Subject: [PATCH 3/6] Move away from LanguageService and simplify. --- .../Controller.Session_ComputeModel.cs | 3 +- .../Core.Wpf/SignatureHelp/Controller.cs | 5 ++ .../SignatureHelpControllerProvider.cs | 5 +- .../SignatureHelpControllerTests.vb | 5 +- .../CSharpSignatureHelpService.cs | 32 --------- .../SignatureHelp/ISignatureHelpService.cs | 38 +++++++++++ .../SignatureHelp/SignatureHelpService.cs | 43 +++++++++--- .../SignatureHelpServiceWithProviders.cs | 65 ------------------- .../SignatureHelp/SignatureHelpHandler.cs | 11 ++-- .../VisualBasicSignatureHelpService.vb | 33 ---------- 10 files changed, 90 insertions(+), 150 deletions(-) delete mode 100644 src/Features/CSharp/Portable/SignatureHelp/CSharpSignatureHelpService.cs create mode 100644 src/Features/Core/Portable/SignatureHelp/ISignatureHelpService.cs delete mode 100644 src/Features/Core/Portable/SignatureHelp/SignatureHelpServiceWithProviders.cs delete mode 100644 src/Features/VisualBasic/Portable/SignatureHelp/VisualBasicSignatureHelpService.vb diff --git a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs index 23074ae1bebed..4115506cf2bf6 100644 --- a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs +++ b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs @@ -85,8 +85,7 @@ private async Task ComputeModelInBackgroundAsync( var options = Controller.GlobalOptions.GetSignatureHelpOptions(document.Project.Language); // first try to query the providers that can trigger on the specified character - var service = document.GetRequiredLanguageService(); - var (provider, items) = await service.GetSignatureHelpAsync( + var (provider, items) = await Controller.SignatureHelpService.GetSignatureHelpAsync( providers, document, caretPosition, diff --git a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.cs b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.cs index c47daf7d94ede..cbcb810869fd4 100644 --- a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.cs +++ b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.cs @@ -35,6 +35,7 @@ internal partial class Controller : private ImmutableArray _providers; private IContentType _lastSeenContentType; + public ISignatureHelpService SignatureHelpService { get; } public string DisplayName => EditorFeaturesResources.Signature_Help; public Controller( @@ -45,11 +46,13 @@ public Controller( IIntelliSensePresenter presenter, IAsynchronousOperationListener asyncListener, IDocumentProvider documentProvider, + ISignatureHelpService signatureHelpService, IList> allProviders, IAsyncCompletionBroker completionBroker) : base(globalOptions, threadingContext, textView, subjectBuffer, presenter, asyncListener, documentProvider, "SignatureHelp") { _completionBroker = completionBroker; + SignatureHelpService = signatureHelpService; _allProviders = allProviders; } @@ -62,10 +65,12 @@ internal Controller( IIntelliSensePresenter presenter, IAsynchronousOperationListener asyncListener, IDocumentProvider documentProvider, + ISignatureHelpService signatureHelpService, IList providers, IAsyncCompletionBroker completionBroker) : base(globalOptions, threadingContext, textView, subjectBuffer, presenter, asyncListener, documentProvider, "SignatureHelp") { + SignatureHelpService = signatureHelpService; _providers = providers.ToImmutableArray(); _completionBroker = completionBroker; } diff --git a/src/EditorFeatures/Core.Wpf/SignatureHelp/SignatureHelpControllerProvider.cs b/src/EditorFeatures/Core.Wpf/SignatureHelp/SignatureHelpControllerProvider.cs index 3023a34d66205..3b8af939117a4 100644 --- a/src/EditorFeatures/Core.Wpf/SignatureHelp/SignatureHelpControllerProvider.cs +++ b/src/EditorFeatures/Core.Wpf/SignatureHelp/SignatureHelpControllerProvider.cs @@ -8,8 +8,8 @@ using System.Linq; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.SignatureHelp; @@ -26,6 +26,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHel internal sealed class SignatureHelpControllerProvider( IGlobalOptionService globalOptions, IThreadingContext threadingContext, + ISignatureHelpService signatureHelpService, [ImportMany] IEnumerable> signatureHelpProviders, [ImportMany] IEnumerable, OrderableMetadata>> signatureHelpPresenters, IAsyncCompletionBroker completionBroker, @@ -38,6 +39,7 @@ internal sealed class SignatureHelpControllerProvider( private readonly IIntelliSensePresenter _signatureHelpPresenter = ExtensionOrderer.Order(signatureHelpPresenters).Select(lazy => lazy.Value).FirstOrDefault(); private readonly IAsynchronousOperationListener _listener = listenerProvider.GetListener(FeatureAttribute.SignatureHelp); private readonly IAsyncCompletionBroker _completionBroker = completionBroker; + private readonly ISignatureHelpService _signatureHelpService = signatureHelpService; private readonly IList> _signatureHelpProviders = ExtensionOrderer.Order(signatureHelpProviders); public Controller? GetController(ITextView textView, ITextBuffer subjectBuffer) @@ -71,6 +73,7 @@ private Controller GetControllerSlow(ITextView textView, ITextBuffer subjectBuff _signatureHelpPresenter, _listener, new DocumentProvider(_threadingContext), + _signatureHelpService, _signatureHelpProviders, _completionBroker)); } diff --git a/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb b/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb index 7f0bf0c1ead54..cea225f2557cd 100644 --- a/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb @@ -8,7 +8,6 @@ Imports Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense Imports Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities Imports Microsoft.CodeAnalysis.Editor.UnitTests.Utilities -Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Shared.TestHooks Imports Microsoft.CodeAnalysis.SignatureHelp Imports Microsoft.CodeAnalysis.Text @@ -113,7 +112,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Dim mre = New ManualResetEvent(False) Dim controller = CreateController(CreateWorkspace(), items:=CreateItems(2), waitForPresentation:=False) Dim slowProvider = New Mock(Of ISignatureHelpProvider)(MockBehavior.Strict) - slowProvider.Setup(Function(p) p.GetItemsAsync(It.IsAny(Of Document), It.IsAny(Of Integer), It.IsAny(Of SignatureHelpTriggerInfo), Options, It.IsAny(Of CancellationToken))) _ + slowProvider.Setup(Function(p) p.GetItemsAsync(It.IsAny(Of Document), It.IsAny(Of Integer), It.IsAny(Of SignatureHelpTriggerInfo), options, It.IsAny(Of CancellationToken))) _ .Returns(Function() mre.WaitOne() Return Task.FromResult(New SignatureHelpItems(CreateItems(2), TextSpan.FromBounds(0, 0), selectedItem:=0, argumentIndex:=0, argumentCount:=0, argumentName:=Nothing)) @@ -293,6 +292,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense documentProvider.Setup(Function(p) p.GetDocument(It.IsAny(Of ITextSnapshot), It.IsAny(Of CancellationToken))).Returns(document) End If + Dim signatureHelpService = workspace.GetService(Of ISignatureHelpService) If provider Is Nothing Then items = If(items, CreateItems(1)) provider = New MockSignatureHelpProvider(items) @@ -315,6 +315,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense presenter.Object, asyncListener, documentProvider.Object, + signatureHelpService, {provider}, mockCompletionBroker.Object) diff --git a/src/Features/CSharp/Portable/SignatureHelp/CSharpSignatureHelpService.cs b/src/Features/CSharp/Portable/SignatureHelp/CSharpSignatureHelpService.cs deleted file mode 100644 index dbf3756a3c787..0000000000000 --- a/src/Features/CSharp/Portable/SignatureHelp/CSharpSignatureHelpService.cs +++ /dev/null @@ -1,32 +0,0 @@ -// 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; -using System.Composition; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.SignatureHelp; - -namespace Microsoft.CodeAnalysis.CSharp.SignatureHelp; - -[ExportLanguageServiceFactory(typeof(SignatureHelpService), LanguageNames.CSharp), Shared] -internal class CSharpSignatureHelpServiceFactory : ILanguageServiceFactory -{ - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpSignatureHelpServiceFactory() - { - } - - public ILanguageService CreateLanguageService(HostLanguageServices languageServices) - => new CSharpSignatureHelpService(languageServices.LanguageServices); -} - -internal class CSharpSignatureHelpService : SignatureHelpServiceWithProviders -{ - internal CSharpSignatureHelpService(LanguageServices services) - : base(services) - { - } -} diff --git a/src/Features/Core/Portable/SignatureHelp/ISignatureHelpService.cs b/src/Features/Core/Portable/SignatureHelp/ISignatureHelpService.cs new file mode 100644 index 0000000000000..d36f4facc8fee --- /dev/null +++ b/src/Features/Core/Portable/SignatureHelp/ISignatureHelpService.cs @@ -0,0 +1,38 @@ +// 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.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.SignatureHelp; + +/// +/// A service that is used to determine the appropriate signature help for a position in a document. +/// +internal interface ISignatureHelpService +{ + /// + /// Gets the and associated with + /// the position in the document. + /// + public Task<(ISignatureHelpProvider? provider, SignatureHelpItems? bestItems)> GetSignatureHelpAsync( + Document document, + int position, + SignatureHelpTriggerInfo triggerInfo, + SignatureHelpOptions options, + CancellationToken cancellationToken = default); + + /// + /// Gets the and associated with + /// the position in the document. + /// + public Task<(ISignatureHelpProvider? provider, SignatureHelpItems? bestItems)> GetSignatureHelpAsync( + ImmutableArray providers, + Document document, + int position, + SignatureHelpTriggerInfo triggerInfo, + SignatureHelpOptions options, + CancellationToken cancellationToken); +} diff --git a/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs b/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs index 207634bd8efad..65a98dfbe90d5 100644 --- a/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs +++ b/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs @@ -2,35 +2,58 @@ // 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; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Collections.Immutable; +using System.Composition; +using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Host.Mef; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.SignatureHelp; /// /// A service that is used to determine the appropriate signature help for a position in a document. /// -internal abstract class SignatureHelpService : ILanguageService +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +[Export(typeof(ISignatureHelpService)), Shared] +internal sealed class SignatureHelpService([ImportMany] IEnumerable> allProviders) + : ISignatureHelpService { - /// - /// Gets the appropriate for the specified document. - /// - public static SignatureHelpService? GetService(Document? document) - => document?.GetLanguageService(); + private readonly ConcurrentDictionary> _providersByLanguage = []; + private readonly IEnumerable> _allProviders = allProviders; + + private ImmutableArray GetProviders(string language) + { + return _providersByLanguage.GetOrAdd(language, language + => _allProviders.Where(p => p.Metadata.Language == language) + .SelectAsArray(p => p.Value)); + } /// /// Gets the and associated with /// the position in the document. /// - public abstract Task<(ISignatureHelpProvider? provider, SignatureHelpItems? bestItems)> GetSignatureHelpAsync( + public Task<(ISignatureHelpProvider? provider, SignatureHelpItems? bestItems)> GetSignatureHelpAsync( Document document, int position, SignatureHelpTriggerInfo triggerInfo, SignatureHelpOptions options, - CancellationToken cancellationToken = default); + CancellationToken cancellationToken = default) + { + return GetSignatureHelpAsync( + GetProviders(document.Project.Language), + document, + position, + triggerInfo, + options, + cancellationToken); + } /// /// Gets the and associated with diff --git a/src/Features/Core/Portable/SignatureHelp/SignatureHelpServiceWithProviders.cs b/src/Features/Core/Portable/SignatureHelp/SignatureHelpServiceWithProviders.cs deleted file mode 100644 index d08d4df22b76c..0000000000000 --- a/src/Features/Core/Portable/SignatureHelp/SignatureHelpServiceWithProviders.cs +++ /dev/null @@ -1,65 +0,0 @@ -// 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; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Shared.Utilities; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.SignatureHelp; - -/// -/// Base class for 's that delegate to 's. -/// -internal abstract class SignatureHelpServiceWithProviders : SignatureHelpService -{ - private readonly LanguageServices _services; - private ImmutableArray _providers; - - protected SignatureHelpServiceWithProviders(LanguageServices services) - { - _services = services; - } - - private ImmutableArray GetProviders() - { - if (_providers.IsDefault) - { - var mefExporter = _services.SolutionServices.ExportProvider; - - var providers = ExtensionOrderer - .Order(mefExporter.GetExports() - .Where(lz => lz.Metadata.Language == _services.Language)) - .Select(lz => lz.Value) - .ToImmutableArray(); - - ImmutableInterlocked.InterlockedCompareExchange(ref _providers, providers, default); - } - - return _providers; - } - - public override Task<(ISignatureHelpProvider? provider, SignatureHelpItems? bestItems)> GetSignatureHelpAsync( - Document document, - int position, - SignatureHelpTriggerInfo triggerInfo, - SignatureHelpOptions options, - CancellationToken cancellationToken) - { - return GetSignatureHelpAsync( - GetProviders(), - document, - position, - triggerInfo, - options, - cancellationToken); - } -} - diff --git a/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs b/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs index f0d50e53e1f7d..55c8fedd6ba21 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SignatureHelp; using Roslyn.Text.Adornments; using LSP = Roslyn.LanguageServer.Protocol; @@ -22,12 +21,16 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler [Method(LSP.Methods.TextDocumentSignatureHelpName)] internal class SignatureHelpHandler : ILspServiceDocumentRequestHandler { + private readonly ISignatureHelpService _signatureHelpService; private readonly IGlobalOptionService _globalOptions; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public SignatureHelpHandler(IGlobalOptionService globalOptions) + public SignatureHelpHandler( + ISignatureHelpService signatureHelpService, + IGlobalOptionService globalOptions) { + _signatureHelpService = signatureHelpService; _globalOptions = globalOptions; } @@ -43,13 +46,11 @@ public SignatureHelpHandler(IGlobalOptionService globalOptions) if (document == null) return null; - var service = document.GetRequiredLanguageService(); - var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var triggerInfo = new SignatureHelpTriggerInfo(SignatureHelpTriggerReason.InvokeSignatureHelpCommand); var options = _globalOptions.GetSignatureHelpOptions(document.Project.Language); - var (_, sigItems) = await service.GetSignatureHelpAsync(document, position, triggerInfo, options, cancellationToken).ConfigureAwait(false); + var (_, sigItems) = await _signatureHelpService.GetSignatureHelpAsync(document, position, triggerInfo, options, cancellationToken).ConfigureAwait(false); if (sigItems is null) { return null; diff --git a/src/Features/VisualBasic/Portable/SignatureHelp/VisualBasicSignatureHelpService.vb b/src/Features/VisualBasic/Portable/SignatureHelp/VisualBasicSignatureHelpService.vb deleted file mode 100644 index 99a944e1e3afd..0000000000000 --- a/src/Features/VisualBasic/Portable/SignatureHelp/VisualBasicSignatureHelpService.vb +++ /dev/null @@ -1,33 +0,0 @@ -' 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. - -Imports System.Composition -Imports Microsoft.CodeAnalysis.Host -Imports Microsoft.CodeAnalysis.Host.Mef -Imports Microsoft.CodeAnalysis.SignatureHelp - -Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp - - Friend Class VisualBasicSignatureHelpServiceFactory - Implements ILanguageServiceFactory - - - - Public Sub New() - End Sub - - Public Function CreateLanguageService(languageServices As HostLanguageServices) As ILanguageService Implements ILanguageServiceFactory.CreateLanguageService - Return New VisualBasicSignatureHelpService(languageServices.LanguageServices) - End Function - - End Class - - Friend Class VisualBasicSignatureHelpService - Inherits SignatureHelpServiceWithProviders - - Public Sub New(services As Host.LanguageServices) - MyBase.New(services) - End Sub - End Class -End Namespace From 5940e8cfa78b1a64866b635bae78e9fe7eaf750a Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Thu, 23 May 2024 14:04:04 -0700 Subject: [PATCH 4/6] Use extension manager when running sig help providers --- .../SignatureHelp/SignatureHelpService.cs | 38 +++++-------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs b/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs index 65a98dfbe90d5..56920347c243e 100644 --- a/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs +++ b/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs @@ -10,7 +10,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.Host.Mef; using Roslyn.Utilities; @@ -67,14 +67,21 @@ private ImmutableArray GetProviders(string language) SignatureHelpOptions options, CancellationToken cancellationToken) { + var extensionManager = document.Project.Solution.Services.GetRequiredService(); + ISignatureHelpProvider? bestProvider = null; SignatureHelpItems? bestItems = null; // returns the first non-empty quick info found (based on provider order) foreach (var provider in providers) { - var items = await TryGetItemsAsync(document, position, triggerInfo, options, provider, cancellationToken).ConfigureAwait(false); - if (items is null) + var items = await extensionManager.PerformFunctionAsync( + provider, + cancellationToken => provider.GetItemsAsync(document, position, triggerInfo, options, cancellationToken), + defaultValue: null, + cancellationToken).ConfigureAwait(false); + + if (items is null || !items.ApplicableSpan.IntersectsWith(position)) { continue; } @@ -98,29 +105,4 @@ private ImmutableArray GetProviders(string language) return (bestProvider, bestItems); } - - private static async Task TryGetItemsAsync(Document document, int position, SignatureHelpTriggerInfo triggerInfo, SignatureHelpOptions options, ISignatureHelpProvider provider, CancellationToken cancellationToken) - { - // We're calling into extensions, we need to make ourselves resilient - // to the extension crashing. - try - { - var items = await provider.GetItemsAsync(document, position, triggerInfo, options, cancellationToken).ConfigureAwait(false); - if (items is null) - { - return null; - } - - if (!items.ApplicableSpan.IntersectsWith(position)) - { - return null; - } - - return items; - } - catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) - { - return null; - } - } } From 819706afd1bc29e48e11519db0085969b236bb68 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Thu, 23 May 2024 16:08:17 -0700 Subject: [PATCH 5/6] Remove default cancellationToken --- .../Core/Portable/SignatureHelp/ISignatureHelpService.cs | 2 +- .../Core/Portable/SignatureHelp/SignatureHelpService.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Features/Core/Portable/SignatureHelp/ISignatureHelpService.cs b/src/Features/Core/Portable/SignatureHelp/ISignatureHelpService.cs index d36f4facc8fee..765f0e5bdc183 100644 --- a/src/Features/Core/Portable/SignatureHelp/ISignatureHelpService.cs +++ b/src/Features/Core/Portable/SignatureHelp/ISignatureHelpService.cs @@ -22,7 +22,7 @@ internal interface ISignatureHelpService int position, SignatureHelpTriggerInfo triggerInfo, SignatureHelpOptions options, - CancellationToken cancellationToken = default); + CancellationToken cancellationToken); /// /// Gets the and associated with diff --git a/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs b/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs index 56920347c243e..0a431cf31e550 100644 --- a/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs +++ b/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs @@ -44,7 +44,7 @@ private ImmutableArray GetProviders(string language) int position, SignatureHelpTriggerInfo triggerInfo, SignatureHelpOptions options, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken) { return GetSignatureHelpAsync( GetProviders(document.Project.Language), From 5df81cf191504aa871fe2dd8285f4a7dad23b086 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Fri, 24 May 2024 13:19:53 -0700 Subject: [PATCH 6/6] Remove ISignatureHelpService interface --- .../Controller.Session_ComputeModel.cs | 2 +- .../Core.Wpf/SignatureHelp/Controller.cs | 5 --- .../SignatureHelpControllerProvider.cs | 3 -- .../SignatureHelpControllerTests.vb | 2 - .../SignatureHelp/ISignatureHelpService.cs | 38 ------------------- .../SignatureHelp/SignatureHelpService.cs | 10 ++--- .../SignatureHelp/SignatureHelpHandler.cs | 4 +- 7 files changed, 8 insertions(+), 56 deletions(-) delete mode 100644 src/Features/Core/Portable/SignatureHelp/ISignatureHelpService.cs diff --git a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs index 4115506cf2bf6..13933d4173af1 100644 --- a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs +++ b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs @@ -85,7 +85,7 @@ private async Task ComputeModelInBackgroundAsync( var options = Controller.GlobalOptions.GetSignatureHelpOptions(document.Project.Language); // first try to query the providers that can trigger on the specified character - var (provider, items) = await Controller.SignatureHelpService.GetSignatureHelpAsync( + var (provider, items) = await SignatureHelpService.GetSignatureHelpAsync( providers, document, caretPosition, diff --git a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.cs b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.cs index cbcb810869fd4..c47daf7d94ede 100644 --- a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.cs +++ b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.cs @@ -35,7 +35,6 @@ internal partial class Controller : private ImmutableArray _providers; private IContentType _lastSeenContentType; - public ISignatureHelpService SignatureHelpService { get; } public string DisplayName => EditorFeaturesResources.Signature_Help; public Controller( @@ -46,13 +45,11 @@ public Controller( IIntelliSensePresenter presenter, IAsynchronousOperationListener asyncListener, IDocumentProvider documentProvider, - ISignatureHelpService signatureHelpService, IList> allProviders, IAsyncCompletionBroker completionBroker) : base(globalOptions, threadingContext, textView, subjectBuffer, presenter, asyncListener, documentProvider, "SignatureHelp") { _completionBroker = completionBroker; - SignatureHelpService = signatureHelpService; _allProviders = allProviders; } @@ -65,12 +62,10 @@ internal Controller( IIntelliSensePresenter presenter, IAsynchronousOperationListener asyncListener, IDocumentProvider documentProvider, - ISignatureHelpService signatureHelpService, IList providers, IAsyncCompletionBroker completionBroker) : base(globalOptions, threadingContext, textView, subjectBuffer, presenter, asyncListener, documentProvider, "SignatureHelp") { - SignatureHelpService = signatureHelpService; _providers = providers.ToImmutableArray(); _completionBroker = completionBroker; } diff --git a/src/EditorFeatures/Core.Wpf/SignatureHelp/SignatureHelpControllerProvider.cs b/src/EditorFeatures/Core.Wpf/SignatureHelp/SignatureHelpControllerProvider.cs index 3b8af939117a4..4563fc4a5ed7b 100644 --- a/src/EditorFeatures/Core.Wpf/SignatureHelp/SignatureHelpControllerProvider.cs +++ b/src/EditorFeatures/Core.Wpf/SignatureHelp/SignatureHelpControllerProvider.cs @@ -26,7 +26,6 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHel internal sealed class SignatureHelpControllerProvider( IGlobalOptionService globalOptions, IThreadingContext threadingContext, - ISignatureHelpService signatureHelpService, [ImportMany] IEnumerable> signatureHelpProviders, [ImportMany] IEnumerable, OrderableMetadata>> signatureHelpPresenters, IAsyncCompletionBroker completionBroker, @@ -39,7 +38,6 @@ internal sealed class SignatureHelpControllerProvider( private readonly IIntelliSensePresenter _signatureHelpPresenter = ExtensionOrderer.Order(signatureHelpPresenters).Select(lazy => lazy.Value).FirstOrDefault(); private readonly IAsynchronousOperationListener _listener = listenerProvider.GetListener(FeatureAttribute.SignatureHelp); private readonly IAsyncCompletionBroker _completionBroker = completionBroker; - private readonly ISignatureHelpService _signatureHelpService = signatureHelpService; private readonly IList> _signatureHelpProviders = ExtensionOrderer.Order(signatureHelpProviders); public Controller? GetController(ITextView textView, ITextBuffer subjectBuffer) @@ -73,7 +71,6 @@ private Controller GetControllerSlow(ITextView textView, ITextBuffer subjectBuff _signatureHelpPresenter, _listener, new DocumentProvider(_threadingContext), - _signatureHelpService, _signatureHelpProviders, _completionBroker)); } diff --git a/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb b/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb index cea225f2557cd..cd890271bb414 100644 --- a/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb @@ -292,7 +292,6 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense documentProvider.Setup(Function(p) p.GetDocument(It.IsAny(Of ITextSnapshot), It.IsAny(Of CancellationToken))).Returns(document) End If - Dim signatureHelpService = workspace.GetService(Of ISignatureHelpService) If provider Is Nothing Then items = If(items, CreateItems(1)) provider = New MockSignatureHelpProvider(items) @@ -315,7 +314,6 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense presenter.Object, asyncListener, documentProvider.Object, - signatureHelpService, {provider}, mockCompletionBroker.Object) diff --git a/src/Features/Core/Portable/SignatureHelp/ISignatureHelpService.cs b/src/Features/Core/Portable/SignatureHelp/ISignatureHelpService.cs deleted file mode 100644 index 765f0e5bdc183..0000000000000 --- a/src/Features/Core/Portable/SignatureHelp/ISignatureHelpService.cs +++ /dev/null @@ -1,38 +0,0 @@ -// 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.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.CodeAnalysis.SignatureHelp; - -/// -/// A service that is used to determine the appropriate signature help for a position in a document. -/// -internal interface ISignatureHelpService -{ - /// - /// Gets the and associated with - /// the position in the document. - /// - public Task<(ISignatureHelpProvider? provider, SignatureHelpItems? bestItems)> GetSignatureHelpAsync( - Document document, - int position, - SignatureHelpTriggerInfo triggerInfo, - SignatureHelpOptions options, - CancellationToken cancellationToken); - - /// - /// Gets the and associated with - /// the position in the document. - /// - public Task<(ISignatureHelpProvider? provider, SignatureHelpItems? bestItems)> GetSignatureHelpAsync( - ImmutableArray providers, - Document document, - int position, - SignatureHelpTriggerInfo triggerInfo, - SignatureHelpOptions options, - CancellationToken cancellationToken); -} diff --git a/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs b/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs index 0a431cf31e550..186fd418b8d7d 100644 --- a/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs +++ b/src/Features/Core/Portable/SignatureHelp/SignatureHelpService.cs @@ -21,17 +21,17 @@ namespace Microsoft.CodeAnalysis.SignatureHelp; /// [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -[Export(typeof(ISignatureHelpService)), Shared] +[Export(typeof(SignatureHelpService)), Shared] internal sealed class SignatureHelpService([ImportMany] IEnumerable> allProviders) - : ISignatureHelpService { private readonly ConcurrentDictionary> _providersByLanguage = []; private readonly IEnumerable> _allProviders = allProviders; private ImmutableArray GetProviders(string language) { - return _providersByLanguage.GetOrAdd(language, language - => _allProviders.Where(p => p.Metadata.Language == language) + return _providersByLanguage.GetOrAdd(language, language => + _allProviders + .Where(p => p.Metadata.Language == language) .SelectAsArray(p => p.Value)); } @@ -59,7 +59,7 @@ private ImmutableArray GetProviders(string language) /// Gets the and associated with /// the position in the document. /// - public async Task<(ISignatureHelpProvider? provider, SignatureHelpItems? bestItems)> GetSignatureHelpAsync( + public static async Task<(ISignatureHelpProvider? provider, SignatureHelpItems? bestItems)> GetSignatureHelpAsync( ImmutableArray providers, Document document, int position, diff --git a/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs b/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs index 55c8fedd6ba21..fc9128f924d92 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs @@ -21,13 +21,13 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler [Method(LSP.Methods.TextDocumentSignatureHelpName)] internal class SignatureHelpHandler : ILspServiceDocumentRequestHandler { - private readonly ISignatureHelpService _signatureHelpService; + private readonly SignatureHelpService _signatureHelpService; private readonly IGlobalOptionService _globalOptions; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public SignatureHelpHandler( - ISignatureHelpService signatureHelpService, + SignatureHelpService signatureHelpService, IGlobalOptionService globalOptions) { _signatureHelpService = signatureHelpService;