File: SignatureHelp\SignatureHelpBeforeCompletionCommandHandler.cs
Web Access
Project: src\src\EditorFeatures\Core.Wpf\Microsoft.CodeAnalysis.EditorFeatures.Wpf_a0rtafw3_wpftmp.csproj (Microsoft.CodeAnalysis.EditorFeatures.Wpf)
// 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.
 
#nullable disable
 
using System;
using System.ComponentModel.Composition;
using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
using Microsoft.VisualStudio.Commanding;
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
using Microsoft.VisualStudio.Text.Editor.Commanding;
using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
using Microsoft.VisualStudio.Utilities;
 
namespace Microsoft.CodeAnalysis.Editor.CommandHandlers;
 
/// <summary>
/// There are two forms of intellisense that may be active at the same time. Completion and SigHelp. Completion
/// precedes SigHelp in the command handler because it wants to make sure it's operating on a buffer *after*
/// Completion has changed it. i.e. if "WriteL(" is typed, sig help wants to allow completion to complete that to
/// "WriteLine(" before it tried to proffer sig help. If we were to reverse things, then we'd get a bogus situation
/// where sig help would see "WriteL(" would have nothing to offer and would return.
/// </summary>
[Export]
[Export(typeof(ICommandHandler))]
[ContentType(ContentTypeNames.RoslynContentType)]
[Name(PredefinedCommandHandlerNames.SignatureHelpBeforeCompletion)]
[Order(Before = PredefinedCompletionNames.CompletionCommandHandler)]
// Ensure roslyn comes after LSP to allow them to provide results.
// https://github.com/dotnet/roslyn/issues/42338
[Order(After = "LSP SignatureHelpCommandHandler")]
internal sealed class SignatureHelpBeforeCompletionCommandHandler :
    AbstractSignatureHelpCommandHandler,
    IChainedCommandHandler<TypeCharCommandArgs>,
    IChainedCommandHandler<InvokeSignatureHelpCommandArgs>
{
    public string DisplayName => EditorFeaturesResources.Signature_Help;
 
    [ImportingConstructor]
    [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
    public SignatureHelpBeforeCompletionCommandHandler(
        IThreadingContext threadingContext,
        SignatureHelpControllerProvider controllerProvider,
        IGlobalOptionService globalOptions)
        : base(threadingContext, controllerProvider, globalOptions)
    {
    }
 
    private bool TryGetControllerCommandHandler<TCommandArgs>(TCommandArgs args, out ICommandHandler commandHandler)
        where TCommandArgs : EditorCommandArgs
    {
        this.ThreadingContext.ThrowIfNotOnUIThread();
        if (!TryGetController(args, out var controller))
        {
            commandHandler = null;
            return false;
        }
 
        commandHandler = controller;
        return true;
    }
 
    private CommandState GetCommandStateWorker<TCommandArgs>(
        TCommandArgs args,
        Func<CommandState> nextHandler)
        where TCommandArgs : EditorCommandArgs
    {
        this.ThreadingContext.ThrowIfNotOnUIThread();
        return TryGetControllerCommandHandler(args, out var commandHandler)
            ? commandHandler.GetCommandState(args, nextHandler)
            : nextHandler();
    }
 
    private void ExecuteCommandWorker<TCommandArgs>(
        TCommandArgs args,
        Action nextHandler,
        CommandExecutionContext context)
        where TCommandArgs : EditorCommandArgs
    {
        this.ThreadingContext.ThrowIfNotOnUIThread();
        if (!TryGetControllerCommandHandler(args, out var commandHandler))
        {
            nextHandler();
        }
        else
        {
            commandHandler.ExecuteCommand(args, nextHandler, context);
        }
    }
 
    CommandState IChainedCommandHandler<TypeCharCommandArgs>.GetCommandState(TypeCharCommandArgs args, Func<CommandState> nextHandler)
    {
        this.ThreadingContext.ThrowIfNotOnUIThread();
        return GetCommandStateWorker(args, nextHandler);
    }
 
    void IChainedCommandHandler<TypeCharCommandArgs>.ExecuteCommand(TypeCharCommandArgs args, Action nextHandler, CommandExecutionContext context)
    {
        this.ThreadingContext.ThrowIfNotOnUIThread();
        ExecuteCommandWorker(args, nextHandler, context);
    }
 
    CommandState IChainedCommandHandler<InvokeSignatureHelpCommandArgs>.GetCommandState(InvokeSignatureHelpCommandArgs args, Func<CommandState> nextHandler)
    {
        this.ThreadingContext.ThrowIfNotOnUIThread();
        return CommandState.Available;
    }
 
    void IChainedCommandHandler<InvokeSignatureHelpCommandArgs>.ExecuteCommand(InvokeSignatureHelpCommandArgs args, Action nextHandler, CommandExecutionContext context)
    {
        this.ThreadingContext.ThrowIfNotOnUIThread();
        ExecuteCommandWorker(args, nextHandler, context);
    }
}