File: RawStringLiteral\RawStringLiteralCommandHandler_TypeChar.cs
Web Access
Project: src\src\EditorFeatures\CSharp\Microsoft.CodeAnalysis.CSharp.EditorFeatures.csproj (Microsoft.CodeAnalysis.CSharp.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.
 
using System;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.RawStringLiteral;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Commanding;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.RawStringLiteral;
 
internal partial class RawStringLiteralCommandHandler : IChainedCommandHandler<TypeCharCommandArgs>
{
    public CommandState GetCommandState(TypeCharCommandArgs args, Func<CommandState> nextCommandHandler)
        => nextCommandHandler();
 
    public void ExecuteCommand(TypeCharCommandArgs args, Action nextCommandHandler, CommandExecutionContext context)
    {
        if (!ExecuteCommandWorker(args, nextCommandHandler))
            nextCommandHandler();
    }
 
    private bool ExecuteCommandWorker(TypeCharCommandArgs args, Action nextCommandHandler)
    {
        if (args.TypedChar != '"')
            return false;
 
        var textView = args.TextView;
        var subjectBuffer = args.SubjectBuffer;
        var spans = textView.Selection.GetSnapshotSpansOnBuffer(subjectBuffer);
 
        if (spans.Count != 1)
            return false;
 
        var span = spans.First();
        if (span.Length != 0)
            return false;
 
        var caret = textView.GetCaretPoint(subjectBuffer);
        if (caret == null)
            return false;
 
        var document = subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
        if (document == null)
            return false;
 
        // This command handler only runs for C#, which should always provide this service.
        var service = document.Project.GetRequiredLanguageService<IRawStringLiteralAutoInsertService>();
 
        var cancellationToken = CancellationToken.None;
        var text = document.GetTextSynchronously(cancellationToken);
        var textChangeOpt = service.GetTextChangeForQuote(document, text, caret.Value.Position, cancellationToken);
 
        if (textChangeOpt is not TextChange textChange)
            return false;
 
        // Looks good.  First, let the quote get added by the normal type char handlers.  Then make our text change.
        // We do this in two steps so that undo can work properly.
        nextCommandHandler();
 
        using var transaction = CaretPreservingEditTransaction.TryCreate(
            CSharpEditorResources.Grow_raw_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService);
 
        var edit = subjectBuffer.CreateEdit();
        edit.Insert(textChange.Span.Start, textChange.NewText);
        edit.Apply();
 
        // ensure the caret is placed after where the original quote got added.
        textView.Caret.MoveTo(new SnapshotPoint(subjectBuffer.CurrentSnapshot, caret.Value.Position + 1));
 
        transaction?.Complete();
        return true;
    }
}