public static Task <Document> ReplaceWithInterpolatedStringAsync( Document document, LiteralExpressionSyntax literalExpression, int interpolationStartIndex = -1, int interpolationLength = 0, bool addNameOf = false, CancellationToken cancellationToken = default(CancellationToken)) { string s = literalExpression.Token.Text; var sb = new StringBuilder(); sb.Append('$'); sb.Append(StringUtility.DoubleBraces(s.Substring(0, interpolationStartIndex))); sb.Append('{'); if (addNameOf) { sb.Append( NameOfExpression( StringLiteralParser.Parse( s, interpolationStartIndex, interpolationLength, isVerbatim: s.StartsWith("@"), isInterpolatedText: false))); } sb.Append('}'); sb.Append(StringUtility.DoubleBraces(s.Substring(interpolationStartIndex + interpolationLength))); ExpressionSyntax newNode = ParseExpression(sb.ToString()).WithTriviaFrom(literalExpression); return(document.ReplaceNodeAsync(literalExpression, newNode, cancellationToken)); }
/// <summary> /// /// </summary> /// <param name="stream"></param> /// <returns></returns> /// <remarks> /// /// See http://www.dmtf.org/sites/default/files/standards/documents/DSP0221_3.0.0a.pdf /// /// A.17.3 String values /// /// Unless explicitly specified via ABNF rule WS, no whitespace is allowed between the elements of the rules /// in this ABNF section. /// /// stringValue = DOUBLEQUOTE *stringChar DOUBLEQUOTE /// *( *WS DOUBLEQUOTE *stringChar DOUBLEQUOTE ) /// stringChar = stringUCSchar / stringEscapeSequence /// /// </remarks> private static StringLiteralToken ReadStringLiteralToken(ILexerStream stream) { // BUGBUG - no support for *( *WS DOUBLEQUOTE *stringChar DOUBLEQUOTE ) // BUGBUG - incomplete escape sequences // BUGBUG - no support for UCS characters var sourceChars = new List <SourceChar>(); // read the first character sourceChars.Add(stream.ReadChar('"')); // read the remaining characters var parser = new StringLiteralParser(); while (!stream.Eof) { var peek = stream.Peek(); if (StringValidator.IsDoubleQuote(peek.Value) && !parser.IsEscaped) { parser.ConsumeEos(); break; } else { sourceChars.Add(peek); parser.ConsumeChar(stream.Read()); } } // read the last character sourceChars.Add(stream.ReadChar('"')); // process any escape sequences in the string var unescaped = parser.OutputString.ToString(); // return the result var extent = new SourceExtent(sourceChars); return(new StringLiteralToken(extent, unescaped)); }
public static Task <Document> ConvertToInterpolatedStringAsync( Document document, LiteralExpressionSyntax literalExpression, int interpolationStartIndex = -1, int interpolationLength = 0, bool addNameOf = false, CancellationToken cancellationToken = default) { string s = literalExpression.Token.Text; StringBuilder sb = StringBuilderCache.GetInstance(); sb.Append('$'); int length = sb.Length; sb.Append(s, 0, interpolationStartIndex); sb.Replace("{", "{{", length); sb.Replace("}", "}}", length); sb.Append('{'); if (addNameOf) { sb.Append( NameOfExpression( StringLiteralParser.Parse( s, interpolationStartIndex, interpolationLength, isVerbatim: s.StartsWith("@"), isInterpolatedText: false))); } int closeBracePosition = sb.Length; sb.Append('}'); length = sb.Length; int startIndex = interpolationStartIndex + interpolationLength; sb.Append(s, startIndex, s.Length - startIndex); sb.Replace("{", "{{", length); sb.Replace("}", "}}", length); ExpressionSyntax newNode = ParseExpression(StringBuilderCache.GetStringAndFree(sb)); SyntaxToken closeBrace = newNode.FindToken(closeBracePosition); newNode = newNode .ReplaceToken(closeBrace, closeBrace.WithNavigationAnnotation()) .WithTriviaFrom(literalExpression); return(document.ReplaceNodeAsync(literalExpression, newNode, cancellationToken)); }
public void cannot_parse_newlines_within_literal(char delimiter, string newLine) { var result = StringLiteralParser.Parser(new Input(delimiter + "hello" + newLine + "world" + delimiter)); Assert.False(result.WasSuccessful); Assert.Equal("unexpected end of line", result.Message); Assert.Equal(1, result.Expectations.Count()); Assert.Equal("continued string contents or " + delimiter, result.Expectations.ElementAt(0)); }
public void cannot_parse_non_terminated_string(char delimiter, string input) { var result = StringLiteralParser.Parser(new Input(delimiter + input)); Assert.False(result.WasSuccessful); Assert.Equal("unexpected end of input", result.Message); Assert.Equal(1, result.Expectations.Count()); Assert.Equal("continued string contents or " + delimiter, result.Expectations.ElementAt(0)); }
public void cannot_parse_non_delimitered_string(string input) { var result = StringLiteralParser.Parser(new Input(input)); Assert.False(result.WasSuccessful); Assert.Equal("unexpected '" + input[0] + "'", result.Message); Assert.Equal(2, result.Expectations.Count()); Assert.Equal("\"", result.Expectations.ElementAt(0)); Assert.Equal("'", result.Expectations.ElementAt(1)); }
public void cannot_parse_empty_input() { var result = StringLiteralParser.Parser(new Input("")); Assert.False(result.WasSuccessful); Assert.Equal("unexpected end of input", result.Message); Assert.Equal(2, result.Expectations.Count()); Assert.Equal("string delimited by \"", result.Expectations.ElementAt(0)); Assert.Equal("string delimited by '", result.Expectations.ElementAt(1)); }
public static Task <Document> RefactorAsync( Document document, InterpolatedStringExpressionSyntax interpolatedString, TextSpan span, bool addNameOf = false, CancellationToken cancellationToken = default) { string s = interpolatedString.ToString(); int startIndex = span.Start - interpolatedString.SpanStart; var sb = new StringBuilder(); sb.Append(s, 0, startIndex); sb.Append('{'); if (addNameOf) { string identifier = StringLiteralParser.Parse( s, startIndex, span.Length, isVerbatim: interpolatedString.IsVerbatim(), isInterpolatedText: true); sb.Append("nameof("); sb.Append(identifier); sb.Append(")"); } int closeBracePosition = sb.Length; sb.Append('}'); startIndex += span.Length; sb.Append(s, startIndex, s.Length - startIndex); ExpressionSyntax newNode = ParseExpression(sb.ToString()); SyntaxToken closeBrace = newNode.FindToken(closeBracePosition); newNode = newNode .ReplaceToken(closeBrace, closeBrace.WithNavigationAnnotation()) .WithTriviaFrom(interpolatedString); return(document.ReplaceNodeAsync(interpolatedString, newNode, cancellationToken)); }
IEnumerable <Iterator> Parse(string expression) { var builder = new StringBuilder(); using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(expression))) { using (var reader = new StreamReader(stream)) { while (!reader.EndOfStream) { var idx = (char)reader.Read(); if (idx == -1) { break; } if (idx == '/') { yield return(new Iterator(builder.ToString())); builder.Clear(); } else if (idx == '"' && builder.Length == 0) { // String literal iterator. yield return(new Iterator(StringLiteralParser.ReadQuotedString(reader))); } else { builder.Append(idx); } } } } if (builder.Length == 0) { throw new ApplicationException("Syntax error at end of expression"); } yield return(new Iterator(builder.ToString())); }
private static int GetStartIndex(LiteralExpressionSyntax literalExpression, TextSpan span) { int index = span.Start - literalExpression.Span.Start; string text = literalExpression.Token.Text; if (text.StartsWith("@", StringComparison.Ordinal)) { if (index > 1 && StringLiteralParser.CanExtractSpan(text, 2, text.Length - 3, span.Offset(-literalExpression.Span.Start), isVerbatim: true, isInterpolatedText: false)) { return(index); } } else if (index > 0 && StringLiteralParser.CanExtractSpan(text, 1, text.Length - 2, span.Offset(-literalExpression.SpanStart), isVerbatim: false, isInterpolatedText: false)) { return(index); } return(-1); }
public static Task <Document> RefactorAsync( Document document, InterpolatedStringExpressionSyntax interpolatedString, TextSpan span, bool addNameOf = false, CancellationToken cancellationToken = default(CancellationToken)) { string s = interpolatedString.ToString(); int startIndex = span.Start - interpolatedString.SpanStart; var sb = new StringBuilder(); sb.Append(s, 0, startIndex); sb.Append('{'); if (addNameOf) { sb.Append( CSharpFactory.NameOfExpression( StringLiteralParser.Parse( s, startIndex, span.Length, isVerbatim: interpolatedString.IsVerbatim(), isInterpolatedText: true))); } sb.Append('}'); startIndex += span.Length; sb.Append(s, startIndex, s.Length - startIndex); ExpressionSyntax newNode = ParseExpression(sb.ToString()).WithTriviaFrom(interpolatedString); return(document.ReplaceNodeAsync(interpolatedString, newNode, cancellationToken)); }
private static int GetStartIndex(StringLiteralExpressionInfo info, TextSpan span) { int spanStart = info.Expression.SpanStart; int index = span.Start - spanStart; string text = info.Text; if (info.IsVerbatim) { if (index > 1 && StringLiteralParser.CanExtractSpan(text, 2, text.Length - 3, span.Offset(-spanStart), isVerbatim: true, isInterpolatedText: false)) { return(index); } } else if (index > 0 && StringLiteralParser.CanExtractSpan(text, 1, text.Length - 2, span.Offset(-spanStart), isVerbatim: false, isInterpolatedText: false)) { return(index); } return(-1); }
public static async Task ComputeRefactoringsAsync(RefactoringContext context, LiteralExpressionSyntax literalExpression) { if (context.IsRefactoringEnabled(RefactoringIdentifiers.InsertStringInterpolation) && context.SupportsCSharp6 && context.Span.End < literalExpression.Span.End) { int startIndex = GetStartIndex(literalExpression, context.Span); if (startIndex != -1) { context.RegisterRefactoring( "Insert interpolation", cancellationToken => { return(ReplaceWithInterpolatedStringAsync( context.Document, literalExpression, startIndex, context.Span.Length, addNameOf: false, cancellationToken: cancellationToken)); }); if (!context.Span.IsEmpty) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); string name = StringLiteralParser.Parse(literalExpression.Token.Text, startIndex, context.Span.Length, literalExpression.IsVerbatimStringLiteral(), isInterpolatedText: false); foreach (ISymbol symbol in semanticModel.LookupSymbols(literalExpression.SpanStart)) { if (string.Equals(name, symbol.MetadataName, StringComparison.Ordinal)) { context.RegisterRefactoring( "Insert interpolation with nameof", cancellationToken => { return(ReplaceWithInterpolatedStringAsync( context.Document, literalExpression, startIndex, context.Span.Length, addNameOf: true, cancellationToken: cancellationToken)); }); break; } } } } } if (context.Span.IsBetweenSpans(literalExpression)) { string text = literalExpression.GetStringLiteralInnerText(); if (literalExpression.IsVerbatimStringLiteral()) { if (text.Contains("\"\"")) { if (context.IsRefactoringEnabled(RefactoringIdentifiers.ReplaceVerbatimStringLiteralWithRegularStringLiteral)) { context.RegisterRefactoring( "Replace verbatim string literal with regular string literal", cancellationToken => { return(ReplaceWithRegularStringLiteralAsync( context.Document, literalExpression, cancellationToken)); }); } if (context.IsRefactoringEnabled(RefactoringIdentifiers.ReplaceVerbatimStringLiteralWithRegularStringLiterals) && text.Contains("\n")) { context.RegisterRefactoring( "Replace verbatim string literal with regular string literals", cancellationToken => { return(ReplaceWithRegularStringLiteralsAsync( context.Document, literalExpression, cancellationToken)); }); } } } else if (context.IsRefactoringEnabled(RefactoringIdentifiers.ReplaceRegularStringLiteralWithVerbatimStringLiteral) && text.Contains(@"\")) { context.RegisterRefactoring( "Replace regular string literal with verbatim string literal", cancellationToken => { return(ReplaceWithVerbatimStringLiteralAsync( context.Document, literalExpression, cancellationToken)); }); } } if (context.IsRefactoringEnabled(RefactoringIdentifiers.UseStringEmptyInsteadOfEmptyStringLiteral) && CanReplaceWithStringEmpty(literalExpression)) { context.RegisterRefactoring( "Replace \"\" with 'string.Empty'", cancellationToken => { return(ReplaceWithStringEmptyAsync( context.Document, literalExpression, cancellationToken)); }); } }
public static async Task ComputeRefactoringsAsync(RefactoringContext context, InterpolatedStringTextSyntax interpolatedStringText) { if (context.IsRefactoringEnabled(RefactoringIdentifiers.InsertStringInterpolation) && interpolatedStringText.IsParentKind(SyntaxKind.InterpolatedStringExpression)) { TextSpan span = context.Span; var interpolatedString = (InterpolatedStringExpressionSyntax)interpolatedStringText.Parent; string text = interpolatedStringText.TextToken.Text; bool isVerbatim = interpolatedString.IsVerbatim(); if (StringLiteralParser.CanExtractSpan( text, span.Offset(-interpolatedStringText.SpanStart), isVerbatim, isInterpolatedText: true)) { context.RegisterRefactoring( "Insert interpolation", cancellationToken => { return(InsertInterpolationRefactoring.RefactorAsync( context.Document, interpolatedString, span, addNameOf: false, cancellationToken: cancellationToken)); }); if (!span.IsEmpty) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); string name = StringLiteralParser.Parse( text, span.Start - interpolatedStringText.SpanStart, span.Length, isVerbatim: isVerbatim, isInterpolatedText: true); foreach (ISymbol symbol in semanticModel.LookupSymbols(interpolatedStringText.SpanStart)) { if (string.Equals(name, symbol.MetadataName, StringComparison.Ordinal)) { context.RegisterRefactoring( "Insert interpolation with nameof", cancellationToken => { return(InsertInterpolationRefactoring.RefactorAsync( context.Document, interpolatedString, span, addNameOf: true, cancellationToken: cancellationToken)); }); break; } } } } } }
public IEnumerable <string> GetTokens() { var builder = new StringBuilder(); while (!_reader.EndOfStream) { var current = (char)_reader.Peek(); switch (current) { case ':': if (builder.Length > 0) { yield return(builder.ToString()); builder.Clear(); } _reader.Read(); // Discarding ':'. yield return(":"); break; case '@': if (builder.Length > 0) { builder.Append(current); _reader.Read(); } else { _reader.Read(); // Discarding '@'. var next = (char)_reader.Read(); if (next == '"') { yield return(StringLiteralParser.ReadMultiLineString(_reader)); builder.Clear(); } else { builder.Append('@'); builder.Append(next); } } break; case '"': case '\'': if (builder.Length > 0) { builder.Append(current); _reader.Read(); } else { yield return(StringLiteralParser.ReadQuotedString(_reader)); builder.Clear(); } break; case '\r': if (builder.Length > 0) { yield return(builder.ToString()); builder.Clear(); } _reader.Read(); // Discarding '\r'. if (_reader.EndOfStream) { throw new ApplicationException("CR/LF error close to EOF"); } var lf = (char)_reader.Read(); if (lf != '\n') { throw new ApplicationException("CR/LF error in Hyperlambda"); } yield return("\r\n"); break; case '\n': if (builder.Length > 0) { yield return(builder.ToString()); builder.Clear(); } _reader.Read(); // Discarding '\n'. yield return("\r\n"); break; case '/': if (builder.Length > 0) { _reader.Read(); // Discarding '/' character. builder.Append('/'); } else { _reader.Read(); // Discarding current '/'. if (_reader.Peek() == '/') { // Eating the rest of the line. while (!_reader.EndOfStream && (char)_reader.Peek() != '\n') { _reader.Read(); } } else if (_reader.Peek() == '*') { // Eating until "*/". var seenEndOfComment = false; while (!_reader.EndOfStream && !seenEndOfComment) { var idxComment = _reader.Read(); if (idxComment == '*') { // Checking if we're at end of comment. if (_reader.Peek() == '/') { _reader.Read(); seenEndOfComment = true; } } } if (!seenEndOfComment && _reader.EndOfStream) { throw new ApplicationException("Syntax error in comment close to end of Hyperlambda"); } } else { builder.Append(current); // Only a part of the current token. } } break; case ' ': _reader.Read(); // Discarding current ' '. if (builder.Length > 0) { builder.Append(current); } else { builder.Append(' '); while (!_reader.EndOfStream && (char)_reader.Peek() == ' ') { _reader.Read(); builder.Append(' '); } if (!_reader.EndOfStream && builder.Length % 3 != 0) { throw new ApplicationException("Odd number of spaces found in Hyperlambda file"); } yield return(builder.ToString()); builder.Clear(); } break; default: builder.Append(current); _reader.Read(); break; } } // Returning the last token, if any. if (builder.Length > 0) { yield return(builder.ToString()); } }
public static async Task ComputeRefactoringsAsync(RefactoringContext context, LiteralExpressionSyntax literalExpression) { StringLiteralExpressionInfo info = SyntaxInfo.StringLiteralExpressionInfo(literalExpression); Debug.Assert(info.Success); if (!info.Success) { return; } if (context.IsRefactoringEnabled(RefactoringIdentifiers.InsertStringInterpolation) && context.SupportsCSharp6 && context.Span.End < literalExpression.Span.End && !CSharpUtility.IsPartOfExpressionThatMustBeConstant(literalExpression)) { int startIndex = GetStartIndex(info, context.Span); if (startIndex != -1) { context.RegisterRefactoring( "Insert interpolation", cancellationToken => { return(ReplaceWithInterpolatedStringAsync( context.Document, literalExpression, startIndex, context.Span.Length, addNameOf: false, cancellationToken: cancellationToken)); }, RefactoringIdentifiers.InsertStringInterpolation); if (!context.Span.IsEmpty) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); string name = StringLiteralParser.Parse(literalExpression.Token.Text, startIndex, context.Span.Length, info.IsVerbatim, isInterpolatedText: false); foreach (ISymbol symbol in semanticModel.LookupSymbols(literalExpression.SpanStart)) { if (string.Equals(name, symbol.MetadataName, StringComparison.Ordinal)) { context.RegisterRefactoring( "Insert interpolation with nameof", cancellationToken => { return(ReplaceWithInterpolatedStringAsync( context.Document, literalExpression, startIndex, context.Span.Length, addNameOf: true, cancellationToken: cancellationToken)); }, EquivalenceKey.Join(RefactoringIdentifiers.InsertStringInterpolation, "WithNameOf")); break; } } } } } if (context.Span.IsBetweenSpans(literalExpression)) { if (info.IsVerbatim) { if (info.ContainsEscapeSequence) { if (context.IsRefactoringEnabled(RefactoringIdentifiers.ReplaceVerbatimStringLiteralWithRegularStringLiteral)) { context.RegisterRefactoring( "Replace verbatim string literal with regular string literal", ct => ReplaceWithRegularStringLiteralAsync(context.Document, literalExpression, ct), RefactoringIdentifiers.ReplaceVerbatimStringLiteralWithRegularStringLiteral); } if (context.IsRefactoringEnabled(RefactoringIdentifiers.ReplaceVerbatimStringLiteralWithRegularStringLiterals) && info.ContainsLinefeed) { context.RegisterRefactoring( "Replace verbatim string literal with regular string literals", ct => ReplaceWithRegularStringLiteralsAsync(context.Document, literalExpression, ct), RefactoringIdentifiers.ReplaceVerbatimStringLiteralWithRegularStringLiterals); } } } else if (context.IsRefactoringEnabled(RefactoringIdentifiers.ReplaceRegularStringLiteralWithVerbatimStringLiteral) && info.ContainsEscapeSequence) { context.RegisterRefactoring( "Replace regular string literal with verbatim string literal", ct => ReplaceWithVerbatimStringLiteralAsync(context.Document, literalExpression, ct), RefactoringIdentifiers.ReplaceRegularStringLiteralWithVerbatimStringLiteral); } } if (context.IsRefactoringEnabled(RefactoringIdentifiers.UseStringEmptyInsteadOfEmptyStringLiteral) && CanReplaceWithStringEmpty(literalExpression)) { context.RegisterRefactoring( "Replace \"\" with 'string.Empty'", ct => ReplaceWithStringEmptyAsync(context.Document, literalExpression, ct), RefactoringIdentifiers.UseStringEmptyInsteadOfEmptyStringLiteral); } }