File: Interactive\InteractiveCommandHandler.cs
Web Access
Project: src\src\EditorFeatures\Core\Microsoft.CodeAnalysis.EditorFeatures_l5l0octc_wpftmp.csproj (Microsoft.CodeAnalysis.EditorFeatures)
// 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.Diagnostics;
using Microsoft.VisualStudio.InteractiveWindow;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods;
using Microsoft.VisualStudio.Text.Operations;
using Microsoft.VisualStudio.Utilities;
using System.Threading;
using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
using Microsoft.VisualStudio.Commanding;
using Microsoft.VisualStudio.Text.Editor.Commanding;
using Microsoft.CodeAnalysis.Options;
 
namespace Microsoft.CodeAnalysis.Interactive;
 
internal abstract class InteractiveCommandHandler :
    ICommandHandler<ExecuteInInteractiveCommandArgs>,
    ICommandHandler<CopyToInteractiveCommandArgs>
{
    private readonly EditorOptionsService _editorOptionsService;
    private readonly IEditorOperationsFactoryService _editorOperationsFactoryService;
 
    protected InteractiveCommandHandler(
        IContentTypeRegistryService contentTypeRegistryService,
        EditorOptionsService editorOptionsService,
        IEditorOperationsFactoryService editorOperationsFactoryService)
    {
        ContentTypeRegistryService = contentTypeRegistryService;
        _editorOptionsService = editorOptionsService;
        _editorOperationsFactoryService = editorOperationsFactoryService;
    }
 
    protected IContentTypeRegistryService ContentTypeRegistryService { get; }
 
    protected abstract IInteractiveWindow OpenInteractiveWindow(bool focus);
 
    protected abstract ISendToInteractiveSubmissionProvider SendToInteractiveSubmissionProvider { get; }
 
    public string DisplayName => EditorFeaturesResources.Interactive;
 
    private string GetSelectedText(EditorCommandArgs args, CancellationToken cancellationToken)
    {
        var editorOptions = _editorOptionsService.Factory.GetOptions(args.SubjectBuffer);
        return SendToInteractiveSubmissionProvider.GetSelectedText(editorOptions, args, cancellationToken);
    }
 
    CommandState ICommandHandler<ExecuteInInteractiveCommandArgs>.GetCommandState(ExecuteInInteractiveCommandArgs args)
        => CommandState.Available;
 
    bool ICommandHandler<ExecuteInInteractiveCommandArgs>.ExecuteCommand(ExecuteInInteractiveCommandArgs args, CommandExecutionContext context)
    {
        var window = OpenInteractiveWindow(focus: false);
        using (context.OperationContext.AddScope(allowCancellation: true, EditorFeaturesWpfResources.Executing_selection_in_Interactive_Window))
        {
            var submission = GetSelectedText(args, context.OperationContext.UserCancellationToken);
            if (!string.IsNullOrWhiteSpace(submission))
            {
                window.SubmitAsync([submission]);
            }
        }
 
        return true;
    }
 
    CommandState ICommandHandler<CopyToInteractiveCommandArgs>.GetCommandState(CopyToInteractiveCommandArgs args)
        => CommandState.Available;
 
    bool ICommandHandler<CopyToInteractiveCommandArgs>.ExecuteCommand(CopyToInteractiveCommandArgs args, CommandExecutionContext context)
    {
        var window = OpenInteractiveWindow(focus: true);
        var buffer = window.CurrentLanguageBuffer;
 
        if (buffer != null)
        {
            CopyToWindow(window, args, context);
        }
        else
        {
            Action action = null;
            action = new Action(() =>
            {
                window.ReadyForInput -= action;
                CopyToWindow(window, args, context);
            });
 
            window.ReadyForInput += action;
        }
 
        return true;
    }
 
    private void CopyToWindow(IInteractiveWindow window, CopyToInteractiveCommandArgs args, CommandExecutionContext context)
    {
        var buffer = window.CurrentLanguageBuffer;
        Debug.Assert(buffer != null);
 
        using (var edit = buffer.CreateEdit())
        using (var waitScope = context.OperationContext.AddScope(allowCancellation: true,
            EditorFeaturesWpfResources.Copying_selection_to_Interactive_Window))
        {
            var text = GetSelectedText(args, context.OperationContext.UserCancellationToken);
 
            // If the last line isn't empty in the existing submission buffer, we will prepend a
            // newline
            var lastLine = buffer.CurrentSnapshot.GetLineFromLineNumber(buffer.CurrentSnapshot.LineCount - 1);
            if (lastLine.Extent.Length > 0)
            {
                var editorOptions = _editorOptionsService.Factory.GetOptions(args.SubjectBuffer);
                text = editorOptions.GetNewLineCharacter() + text;
            }
 
            edit.Insert(buffer.CurrentSnapshot.Length, text);
            edit.Apply();
        }
 
        // Move the caret to the end
        var editorOperations = _editorOperationsFactoryService.GetEditorOperations(window.TextView);
        var endPoint = new VirtualSnapshotPoint(window.TextView.TextBuffer.CurrentSnapshot, window.TextView.TextBuffer.CurrentSnapshot.Length);
        editorOperations.SelectAndMoveCaret(endPoint, endPoint);
    }
}