|
// 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.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Indentation;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Commanding;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.CSharp.RawStringLiteral
{
internal partial class RawStringLiteralCommandHandler : ICommandHandler<ReturnKeyCommandArgs>
{
public CommandState GetCommandState(ReturnKeyCommandArgs args)
=> CommandState.Unspecified;
/// <summary>
/// Checks to see if the user is typing <c>return</c> in <c>"""$$"""</c> and then properly indents the end
/// delimiter of the raw string literal.
/// </summary>
public bool ExecuteCommand(ReturnKeyCommandArgs args, CommandExecutionContext context)
{
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 position = caret.Value.Position;
var currentSnapshot = subjectBuffer.CurrentSnapshot;
if (position >= currentSnapshot.Length)
return false;
if (currentSnapshot[position] != '"')
return false;
var quotesBefore = 0;
var quotesAfter = 0;
for (int i = position, n = currentSnapshot.Length; i < n; i++)
{
if (currentSnapshot[i] != '"')
break;
quotesAfter++;
}
for (var i = position - 1; i >= 0; i--)
{
if (currentSnapshot[i] != '"')
break;
quotesBefore++;
}
if (quotesAfter != quotesBefore)
return false;
if (quotesAfter < 3)
return false;
return SplitRawString(textView, subjectBuffer, span.Start.Position, CancellationToken.None);
}
private bool SplitRawString(ITextView textView, ITextBuffer subjectBuffer, int position, CancellationToken cancellationToken)
{
var document = subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
if (document == null)
return false;
var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken);
var token = parsedDocument.Root.FindToken(position);
if (token.Kind() is not (SyntaxKind.SingleLineRawStringLiteralToken or
SyntaxKind.MultiLineRawStringLiteralToken or
SyntaxKind.InterpolatedSingleLineRawStringStartToken or
SyntaxKind.InterpolatedMultiLineRawStringStartToken))
{
return false;
}
var indentationOptions = subjectBuffer.GetIndentationOptions(_editorOptionsService, document.Project.Services, explicitFormat: false);
var indentation = token.GetPreferredIndentation(parsedDocument, indentationOptions, cancellationToken);
var newLine = indentationOptions.FormattingOptions.NewLine;
using var transaction = CaretPreservingEditTransaction.TryCreate(
CSharpEditorResources.Split_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService);
var edit = subjectBuffer.CreateEdit();
// apply the change:
edit.Insert(position, newLine + newLine + indentation);
var snapshot = edit.Apply();
// move caret:
var lineInNewSnapshot = snapshot.GetLineFromPosition(position);
var nextLine = snapshot.GetLineFromLineNumber(lineInNewSnapshot.LineNumber + 1);
textView.Caret.MoveTo(new VirtualSnapshotPoint(nextLine, indentation.Length));
transaction?.Complete();
return true;
}
}
}
|