|
// 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.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Extensions;
internal static class SyntaxTriviaExtensions
{
public static void Deconstruct(this SyntaxTrivia trivia, out SyntaxKind kind)
=> kind = trivia.Kind();
public static bool IsSingleOrMultiLineComment(this SyntaxTrivia trivia)
=> trivia.Kind() is SyntaxKind.MultiLineCommentTrivia or SyntaxKind.SingleLineCommentTrivia;
public static bool IsRegularComment(this SyntaxTrivia trivia)
=> trivia.IsSingleOrMultiLineComment() || trivia.IsShebangDirective();
public static bool IsWhitespaceOrSingleOrMultiLineComment(this SyntaxTrivia trivia)
=> trivia.IsWhitespace() || trivia.IsSingleOrMultiLineComment();
public static bool IsRegularOrDocComment(this SyntaxTrivia trivia)
=> trivia.IsRegularComment() || trivia.IsDocComment();
public static bool IsSingleLineComment(this SyntaxTrivia trivia)
=> trivia.Kind() == SyntaxKind.SingleLineCommentTrivia;
public static bool IsMultiLineComment(this SyntaxTrivia trivia)
=> trivia.Kind() == SyntaxKind.MultiLineCommentTrivia;
public static bool IsShebangDirective(this SyntaxTrivia trivia)
=> trivia.Kind() == SyntaxKind.ShebangDirectiveTrivia;
public static bool IsCompleteMultiLineComment(this SyntaxTrivia trivia)
{
if (trivia.Kind() != SyntaxKind.MultiLineCommentTrivia)
return false;
var text = trivia.ToFullString();
return text is [.., _, _, '*', '/'];
}
public static bool IsDocComment(this SyntaxTrivia trivia)
=> trivia.IsSingleLineDocComment() || trivia.IsMultiLineDocComment();
public static bool IsSingleLineDocComment(this SyntaxTrivia trivia)
=> trivia.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia;
public static bool IsMultiLineDocComment(this SyntaxTrivia trivia)
=> trivia.Kind() == SyntaxKind.MultiLineDocumentationCommentTrivia;
public static string GetCommentText(this SyntaxTrivia trivia)
{
var commentText = trivia.ToString();
if (trivia.Kind() == SyntaxKind.SingleLineCommentTrivia)
{
if (commentText.StartsWith("//", StringComparison.Ordinal))
{
commentText = commentText[2..];
}
return commentText.TrimStart(null);
}
else if (trivia.Kind() == SyntaxKind.MultiLineCommentTrivia)
{
var textBuilder = new StringBuilder();
if (commentText.EndsWith("*/", StringComparison.Ordinal))
{
commentText = commentText[..^2];
}
if (commentText.StartsWith("/*", StringComparison.Ordinal))
{
commentText = commentText[2..];
}
commentText = commentText.Trim();
var newLine = Environment.NewLine;
var lines = commentText.Split([newLine], StringSplitOptions.None);
foreach (var line in lines)
{
var trimmedLine = line.Trim();
// Note: we trim leading '*' characters in multi-line comments.
// If the '*' was intentional, sorry, it's gone.
if (trimmedLine.StartsWith("*", StringComparison.Ordinal))
{
trimmedLine = trimmedLine.TrimStart('*');
trimmedLine = trimmedLine.TrimStart(null);
}
textBuilder.AppendLine(trimmedLine);
}
// remove last line break
textBuilder.Remove(textBuilder.Length - newLine.Length, newLine.Length);
return textBuilder.ToString();
}
else
{
throw new InvalidOperationException();
}
}
public static string AsString(this IEnumerable<SyntaxTrivia> trivia)
{
Contract.ThrowIfNull(trivia);
if (trivia.Any())
{
var sb = new StringBuilder();
trivia.Select(t => t.ToFullString()).Do(s => sb.Append(s));
return sb.ToString();
}
else
{
return string.Empty;
}
}
public static int GetFullWidth(this IEnumerable<SyntaxTrivia> trivia)
{
Contract.ThrowIfNull(trivia);
return trivia.Sum(t => t.FullWidth());
}
public static SyntaxTriviaList AsTrivia(this string s)
=> SyntaxFactory.ParseLeadingTrivia(s ?? string.Empty);
public static bool IsWhitespaceOrEndOfLine(this SyntaxTrivia trivia)
=> IsWhitespace(trivia) || IsEndOfLine(trivia);
public static bool IsEndOfLine(this SyntaxTrivia trivia)
=> trivia.Kind() == SyntaxKind.EndOfLineTrivia;
public static bool IsWhitespace(this SyntaxTrivia trivia)
=> trivia.Kind() == SyntaxKind.WhitespaceTrivia;
public static SyntaxTrivia GetPreviousTrivia(
this SyntaxTrivia trivia, SyntaxTree syntaxTree, CancellationToken cancellationToken, bool findInsideTrivia = false)
{
var span = trivia.FullSpan;
if (span.Start == 0)
{
return default;
}
return syntaxTree.GetRoot(cancellationToken).FindTrivia(span.Start - 1, findInsideTrivia);
}
public static IEnumerable<SyntaxTrivia> FilterComments(this IEnumerable<SyntaxTrivia> trivia, bool addElasticMarker)
{
var previousIsSingleLineComment = false;
foreach (var t in trivia)
{
if (previousIsSingleLineComment && t.IsEndOfLine())
{
yield return t;
}
if (t.IsSingleOrMultiLineComment())
{
yield return t;
}
previousIsSingleLineComment = t.IsSingleLineComment();
}
if (addElasticMarker)
{
yield return SyntaxFactory.ElasticMarker;
}
}
#if false
public static int Width(this SyntaxTrivia trivia)
{
return trivia.Span.Length;
}
public static int FullWidth(this SyntaxTrivia trivia)
{
return trivia.FullSpan.Length;
}
#endif
public static bool IsPragmaDirective(this SyntaxTrivia trivia, out bool isDisable, out bool isActive, out SeparatedSyntaxList<SyntaxNode> errorCodes)
{
if (trivia.IsKind(SyntaxKind.PragmaWarningDirectiveTrivia))
{
var pragmaWarning = (PragmaWarningDirectiveTriviaSyntax)trivia.GetStructure();
isDisable = pragmaWarning.DisableOrRestoreKeyword.IsKind(SyntaxKind.DisableKeyword);
isActive = pragmaWarning.IsActive;
errorCodes = pragmaWarning.ErrorCodes;
return true;
}
isDisable = false;
isActive = false;
errorCodes = default;
return false;
}
}
|