File: SplitStringLiteral\SimpleStringSplitter.cs
Web Access
Project: src\src\Features\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Features.csproj (Microsoft.CodeAnalysis.CSharp.Features)
// 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.Threading;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Indentation;
using Microsoft.CodeAnalysis.Text;
 
namespace Microsoft.CodeAnalysis.CSharp.SplitStringLiteral;
 
internal abstract partial class StringSplitter
{
    private sealed class SimpleStringSplitter(
        ParsedDocument document,
        int position,
        SyntaxToken token,
        in IndentationOptions indentationOptions,
        CancellationToken cancellationToken) : StringSplitter(document, position, indentationOptions, cancellationToken)
    {
        private const char QuoteCharacter = '"';
        private readonly SyntaxToken _token = token;
 
        // Don't split @"" strings.  They already support directly embedding newlines.
        // Don't split UTF-8 strings if the cursor is after the quote.
        protected override bool CheckToken()
            => !_token.IsVerbatimStringLiteral() && !CursorIsAfterQuotesInUtf8String();
 
        private bool CursorIsAfterQuotesInUtf8String()
        {
            return _token.IsKind(SyntaxKind.Utf8StringLiteralToken) && CursorPosition >= _token.Span.End - "u8".Length;
        }
 
        protected override SyntaxNode GetNodeToReplace() => _token.Parent;
 
        protected override BinaryExpressionSyntax CreateSplitString()
        {
            // TODO(cyrusn): Deal with the positoin being after a \ character
            var prefix = Document.Text.GetSubText(TextSpan.FromBounds(_token.SpanStart, CursorPosition)).ToString();
            var suffix = Document.Text.GetSubText(TextSpan.FromBounds(CursorPosition, _token.Span.End)).ToString();
 
            // If we're spliting a UTF-8 string we need to keep the u8 suffix on the first part. We copy whatever
            // the user had on the second part, for consistency.
            var firstTokenSuffix = _token.Kind() == SyntaxKind.Utf8StringLiteralToken
                ? Document.Text.GetSubText(TextSpan.FromBounds(_token.Span.End - "u8".Length, _token.Span.End)).ToString()
                : "";
 
            var firstToken = SyntaxFactory.Token(
                _token.LeadingTrivia,
                _token.Kind(),
                text: prefix + QuoteCharacter + firstTokenSuffix,
                valueText: "",
                trailing: [SyntaxFactory.ElasticSpace]);
 
            var secondToken = SyntaxFactory.Token(
                default,
                _token.Kind(),
                text: QuoteCharacter + suffix,
                valueText: "",
                trailing: _token.TrailingTrivia);
 
            var leftExpression = SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, firstToken);
            var rightExpression = SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, secondToken);
 
            return SyntaxFactory.BinaryExpression(
                SyntaxKind.AddExpression,
                leftExpression,
                PlusNewLineToken,
                rightExpression.WithAdditionalAnnotations(RightNodeAnnotation));
        }
 
        protected override int StringOpenQuoteLength() => "\"".Length;
    }
}