|
// 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.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Utilities;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Formatting;
internal static class FormattingHelpers
{
// TODO: Need to determine correct way to handle newlines
public const string NewLine = "\r\n";
public static string GetIndent(this SyntaxToken token)
{
var precedingTrivia = token.GetAllPrecedingTriviaToPreviousToken();
// indent is the spaces/tabs between last new line (if there is one) and end of trivia
var indent = precedingTrivia.AsString();
var lastNewLinePos = indent.LastIndexOf(NewLine, StringComparison.Ordinal);
if (lastNewLinePos != -1)
{
var start = lastNewLinePos + NewLine.Length;
indent = indent[start..];
}
return indent;
}
public static string ContentBeforeLastNewLine(this IEnumerable<SyntaxTrivia> trivia)
{
var leading = trivia.AsString();
var lastNewLinePos = leading.LastIndexOf(NewLine, StringComparison.Ordinal);
if (lastNewLinePos == -1)
{
return string.Empty;
}
return leading[..lastNewLinePos];
}
public static (SyntaxToken openBrace, SyntaxToken closeBrace) GetBracePair(this SyntaxNode? node)
=> node.GetBraces();
public static (SyntaxToken openBracket, SyntaxToken closeBracket) GetBracketPair(this SyntaxNode? node)
=> node.GetBrackets();
public static bool IsValidBracketOrBracePair(this (SyntaxToken openBracketOrBrace, SyntaxToken closeBracketOrBrace) bracketOrBracePair)
{
if (bracketOrBracePair.openBracketOrBrace.IsKind(SyntaxKind.None) ||
bracketOrBracePair.openBracketOrBrace.IsMissing ||
bracketOrBracePair.closeBracketOrBrace.IsKind(SyntaxKind.None))
{
return false;
}
if (bracketOrBracePair.openBracketOrBrace.IsKind(SyntaxKind.OpenBraceToken))
{
return bracketOrBracePair.closeBracketOrBrace.IsKind(SyntaxKind.CloseBraceToken);
}
if (bracketOrBracePair.openBracketOrBrace.IsKind(SyntaxKind.OpenBracketToken))
{
return bracketOrBracePair.closeBracketOrBrace.IsKind(SyntaxKind.CloseBracketToken);
}
return false;
}
public static bool IsOpenParenInParameterListOfAConversionOperatorDeclaration(this SyntaxToken token)
=> token.IsOpenParenInParameterList() && token.Parent.IsParentKind(SyntaxKind.ConversionOperatorDeclaration);
public static bool IsOpenParenInParameterListOfAOperationDeclaration(this SyntaxToken token)
=> token.IsOpenParenInParameterList() && token.Parent.IsParentKind(SyntaxKind.OperatorDeclaration);
public static bool IsOpenParenInParameterList(this SyntaxToken token)
=> token.Kind() == SyntaxKind.OpenParenToken && token.Parent.IsKind(SyntaxKind.ParameterList);
public static bool IsCloseParenInParameterList(this SyntaxToken token)
=> token.Kind() == SyntaxKind.CloseParenToken && token.Parent.IsKind(SyntaxKind.ParameterList);
public static bool IsOpenParenInArgumentListOrPositionalPattern(this SyntaxToken token)
{
return token.Kind() == SyntaxKind.OpenParenToken &&
IsTokenInArgumentListOrPositionalPattern(token);
}
public static bool IsCloseParenInArgumentListOrPositionalPattern(this SyntaxToken token)
{
return token.Kind() == SyntaxKind.CloseParenToken &&
IsTokenInArgumentListOrPositionalPattern(token);
}
private static bool IsTokenInArgumentListOrPositionalPattern(SyntaxToken token)
{
// Argument lists
if (token.Parent is (kind: SyntaxKind.ArgumentList or SyntaxKind.AttributeArgumentList))
{
return true;
}
// Positional patterns
if (token.Parent.IsKind(SyntaxKind.PositionalPatternClause) && token.Parent.Parent.IsKind(SyntaxKind.RecursivePattern))
{
// Avoid treating tuple expressions as positional patterns for formatting
return token.Parent.Parent.GetFirstToken() != token;
}
return false;
}
public static bool IsColonInTypeBaseList(this SyntaxToken token)
=> token.Kind() == SyntaxKind.ColonToken && token.Parent.IsKind(SyntaxKind.BaseList);
public static bool IsCommaInArgumentOrParameterList(this SyntaxToken token)
=> token.Kind() == SyntaxKind.CommaToken && (token.Parent.IsAnyArgumentList() || token.Parent?.Kind() is SyntaxKind.ParameterList or SyntaxKind.FunctionPointerParameterList);
public static bool IsOpenParenInParameterListOfParenthesizedLambdaExpression(this SyntaxToken token)
=> token.Kind() == SyntaxKind.OpenParenToken && token.Parent.IsKind(SyntaxKind.ParameterList) && token.Parent.Parent.IsKind(SyntaxKind.ParenthesizedLambdaExpression);
public static bool IsLambdaBodyBlock(this SyntaxNode node)
{
if (node.Kind() != SyntaxKind.Block)
{
return false;
}
return node.Parent?.Kind() is SyntaxKind.SimpleLambdaExpression or SyntaxKind.ParenthesizedLambdaExpression;
}
public static bool IsAnonymousMethodBlock(this SyntaxNode node)
{
if (node.Kind() != SyntaxKind.Block)
{
return false;
}
return node.IsParentKind(SyntaxKind.AnonymousMethodExpression);
}
public static bool IsSemicolonInForStatement(this SyntaxToken token)
{
return
token.Kind() == SyntaxKind.SemicolonToken &&
token.Parent is ForStatementSyntax forStatement &&
(forStatement.FirstSemicolonToken == token || forStatement.SecondSemicolonToken == token);
}
public static bool IsSemicolonOfEmbeddedStatement(this SyntaxToken token)
{
if (token.Kind() != SyntaxKind.SemicolonToken)
{
return false;
}
if (token.Parent is not StatementSyntax statement ||
statement.GetLastToken() != token)
{
return false;
}
return IsEmbeddedStatement(statement);
}
public static bool IsCloseBraceOfExpression(this SyntaxToken token)
{
if (token.Kind() != SyntaxKind.CloseBraceToken)
{
return false;
}
return token.Parent is ExpressionSyntax || token.Parent.IsKind(SyntaxKind.PropertyPatternClause);
}
public static bool IsCloseBraceOfEmbeddedBlock(this SyntaxToken token)
{
if (token.Kind() != SyntaxKind.CloseBraceToken)
{
return false;
}
if (token.Parent is not BlockSyntax block ||
block.CloseBraceToken != token)
{
return false;
}
return IsEmbeddedStatement(block);
}
public static bool IsEmbeddedStatement([NotNullWhen(true)] this SyntaxNode? node)
{
SyntaxNode? statementOrElse = node as StatementSyntax;
statementOrElse ??= node as ElseClauseSyntax;
return statementOrElse != null
&& statementOrElse.Parent != null
&& statementOrElse.Parent.IsEmbeddedStatementOwner();
}
public static bool IsCommaInEnumDeclaration(this SyntaxToken token)
{
return token.Kind() == SyntaxKind.CommaToken &&
token.Parent.IsKind(SyntaxKind.EnumDeclaration);
}
public static bool IsCommaInAnyArgumentsList(this SyntaxToken token)
{
return token.Kind() == SyntaxKind.CommaToken &&
token.Parent.IsAnyArgumentList();
}
public static bool IsParenInParenthesizedExpression(this SyntaxToken token)
{
if (token.Parent is not ParenthesizedExpressionSyntax parenthesizedExpression)
{
return false;
}
return parenthesizedExpression.OpenParenToken.Equals(token) || parenthesizedExpression.CloseParenToken.Equals(token);
}
public static bool IsParenInArgumentList(this SyntaxToken token)
{
var parent = token.Parent ?? throw new ArgumentNullException(nameof(token));
switch (parent.Kind())
{
case SyntaxKind.SizeOfExpression:
var sizeOfExpression = (SizeOfExpressionSyntax)parent;
return sizeOfExpression.OpenParenToken == token || sizeOfExpression.CloseParenToken == token;
case SyntaxKind.TypeOfExpression:
var typeOfExpression = (TypeOfExpressionSyntax)parent;
return typeOfExpression.OpenParenToken == token || typeOfExpression.CloseParenToken == token;
case SyntaxKind.CheckedExpression:
case SyntaxKind.UncheckedExpression:
var checkedOfExpression = (CheckedExpressionSyntax)parent;
return checkedOfExpression.OpenParenToken == token || checkedOfExpression.CloseParenToken == token;
case SyntaxKind.DefaultExpression:
var defaultExpression = (DefaultExpressionSyntax)parent;
return defaultExpression.OpenParenToken == token || defaultExpression.CloseParenToken == token;
case SyntaxKind.MakeRefExpression:
var makeRefExpression = (MakeRefExpressionSyntax)parent;
return makeRefExpression.OpenParenToken == token || makeRefExpression.CloseParenToken == token;
case SyntaxKind.RefTypeExpression:
var refTypeOfExpression = (RefTypeExpressionSyntax)parent;
return refTypeOfExpression.OpenParenToken == token || refTypeOfExpression.CloseParenToken == token;
case SyntaxKind.RefValueExpression:
var refValueExpression = (RefValueExpressionSyntax)parent;
return refValueExpression.OpenParenToken == token || refValueExpression.CloseParenToken == token;
case SyntaxKind.ArgumentList:
var argumentList = (ArgumentListSyntax)parent;
return argumentList.OpenParenToken == token || argumentList.CloseParenToken == token;
case SyntaxKind.AttributeArgumentList:
var attributeArgumentList = (AttributeArgumentListSyntax)parent;
return attributeArgumentList.OpenParenToken == token || attributeArgumentList.CloseParenToken == token;
}
return false;
}
public static bool IsEqualsTokenInAutoPropertyInitializers(this SyntaxToken token)
{
return token.IsKind(SyntaxKind.EqualsToken) &&
token.Parent.IsKind(SyntaxKind.EqualsValueClause) &&
token.Parent.Parent.IsKind(SyntaxKind.PropertyDeclaration);
}
public static bool IsCloseParenInStatement(this SyntaxToken token)
{
if (token.Parent is not StatementSyntax statement)
{
return false;
}
return statement switch
{
IfStatementSyntax ifStatement => ifStatement.CloseParenToken.Equals(token),
SwitchStatementSyntax switchStatement => switchStatement.CloseParenToken.Equals(token),
WhileStatementSyntax whileStatement => whileStatement.CloseParenToken.Equals(token),
DoStatementSyntax doStatement => doStatement.CloseParenToken.Equals(token),
ForStatementSyntax forStatement => forStatement.CloseParenToken.Equals(token),
CommonForEachStatementSyntax foreachStatement => foreachStatement.CloseParenToken.Equals(token),
LockStatementSyntax lockStatement => lockStatement.CloseParenToken.Equals(token),
UsingStatementSyntax usingStatement => usingStatement.CloseParenToken.Equals(token),
FixedStatementSyntax fixedStatement => fixedStatement.CloseParenToken.Equals(token),
_ => false,
};
}
public static bool IsDotInMemberAccessOrQualifiedName(this SyntaxToken token)
=> token.IsDotInMemberAccess() || (token.Kind() == SyntaxKind.DotToken && token.Parent.IsKind(SyntaxKind.QualifiedName));
public static bool IsDotInMemberAccess(this SyntaxToken token)
{
if (token.Parent is not MemberAccessExpressionSyntax memberAccess)
{
return false;
}
return token.Kind() == SyntaxKind.DotToken
&& memberAccess.OperatorToken.Equals(token);
}
public static bool IsGenericGreaterThanToken(this SyntaxToken token)
{
if (token.Kind() == SyntaxKind.GreaterThanToken)
return token.Parent is (kind: SyntaxKind.TypeParameterList or SyntaxKind.TypeArgumentList);
return false;
}
public static bool IsCommaInInitializerExpression(this SyntaxToken token)
{
return token.Kind() == SyntaxKind.CommaToken &&
((token.Parent is InitializerExpressionSyntax) ||
(token.Parent is AnonymousObjectCreationExpressionSyntax));
}
public static bool IsColonInCasePatternSwitchLabel(this SyntaxToken token)
=> token.Kind() == SyntaxKind.ColonToken && token.Parent is CasePatternSwitchLabelSyntax;
public static bool IsColonInSwitchExpressionArm(this SyntaxToken token)
=> token.Kind() == SyntaxKind.ColonToken && token.Parent.IsKind(SyntaxKind.SwitchExpressionArm);
public static bool IsCommaInSwitchExpression(this SyntaxToken token)
=> token.Kind() == SyntaxKind.CommaToken && token.Parent.IsKind(SyntaxKind.SwitchExpression);
public static bool IsCommaInPropertyPatternClause(this SyntaxToken token)
=> token.Kind() == SyntaxKind.CommaToken && token.Parent.IsKind(SyntaxKind.PropertyPatternClause);
public static bool IsIdentifierInLabeledStatement(this SyntaxToken token)
{
return token.Kind() == SyntaxKind.IdentifierToken &&
token.Parent is LabeledStatementSyntax labeledStatement &&
labeledStatement.Identifier == token;
}
public static bool IsColonInSwitchLabel(this SyntaxToken token)
=> FormattingRangeHelper.IsColonInSwitchLabel(token);
public static bool IsColonInLabeledStatement(this SyntaxToken token)
{
return token.Kind() == SyntaxKind.ColonToken &&
token.Parent is LabeledStatementSyntax labeledStatement &&
labeledStatement.ColonToken == token;
}
public static bool IsEmbeddedStatementOwnerWithCloseParen([NotNullWhen(true)] this SyntaxNode? node)
{
return node is IfStatementSyntax or
WhileStatementSyntax or
ForStatementSyntax or
CommonForEachStatementSyntax or
UsingStatementSyntax or
FixedStatementSyntax or
LockStatementSyntax;
}
public static bool IsNestedQueryExpression(this SyntaxToken token)
{
return token.Kind() == SyntaxKind.InKeyword &&
token.Parent is FromClauseSyntax fromClause &&
fromClause.Expression is QueryExpressionSyntax;
}
public static bool IsFirstFromKeywordInExpression(this SyntaxToken token)
{
return token.Kind() == SyntaxKind.FromKeyword &&
token.Parent?.Parent is QueryExpressionSyntax queryExpression &&
queryExpression.GetFirstToken().Equals(token);
}
public static bool IsInitializerForObjectOrAnonymousObjectCreationExpression([NotNullWhen(true)] this SyntaxNode? node)
{
if (node is InitializerExpressionSyntax initializer)
{
var parent = initializer.Parent;
if (parent is AnonymousObjectCreationExpressionSyntax)
{
return true;
}
if (parent is BaseObjectCreationExpressionSyntax)
{
if (initializer.Expressions.Count <= 0)
{
return true;
}
var expression = initializer.Expressions[0];
if (expression.Kind() == SyntaxKind.SimpleAssignmentExpression)
{
return true;
}
}
return false;
}
else if (node is AnonymousObjectMemberDeclaratorSyntax anonymousObjectInitializer)
{
return anonymousObjectInitializer.Parent is AnonymousObjectCreationExpressionSyntax;
}
else
{
return false;
}
}
public static bool IsInitializerForArrayOrCollectionCreationExpression([NotNullWhen(true)] this SyntaxNode? node)
{
if (node is InitializerExpressionSyntax initializer)
{
var parent = initializer.Parent;
if (parent is ArrayCreationExpressionSyntax ||
parent is ImplicitArrayCreationExpressionSyntax ||
parent is StackAllocArrayCreationExpressionSyntax ||
parent is ImplicitStackAllocArrayCreationExpressionSyntax ||
parent is EqualsValueClauseSyntax ||
parent.IsKind(SyntaxKind.SimpleAssignmentExpression))
{
return true;
}
if (parent is BaseObjectCreationExpressionSyntax)
{
return !IsInitializerForObjectOrAnonymousObjectCreationExpression(initializer);
}
return false;
}
else if (node is AnonymousObjectMemberDeclaratorSyntax anonymousObjectInitializer)
{
var parent = anonymousObjectInitializer.Parent;
if (parent is ArrayCreationExpressionSyntax ||
parent is ImplicitArrayCreationExpressionSyntax ||
parent is EqualsValueClauseSyntax ||
parent is BaseObjectCreationExpressionSyntax ||
parent.IsKind(SyntaxKind.SimpleAssignmentExpression))
{
return true;
}
return false;
}
else
{
return false;
}
}
public static bool ParenOrBracketContainsNothing(this SyntaxToken token1, SyntaxToken token2)
{
return (token1.Kind() == SyntaxKind.OpenParenToken && token2.Kind() == SyntaxKind.CloseParenToken) ||
(token1.Kind() == SyntaxKind.OpenBracketToken && token2.Kind() == SyntaxKind.CloseBracketToken);
}
public static bool IsLastTokenInLabelStatement(this SyntaxToken token)
{
if (token.Kind() is not SyntaxKind.SemicolonToken and not SyntaxKind.CloseBraceToken)
{
return false;
}
if (token.Parent == null)
{
return false;
}
return token.Parent.Parent is LabeledStatementSyntax;
}
public static (SyntaxToken firstToken, SyntaxToken lastToken) GetFirstAndLastMemberDeclarationTokensAfterAttributes(this MemberDeclarationSyntax node)
{
Contract.ThrowIfNull(node);
// there are no attributes associated with the node. return back first and last token of the node.
var attributes = node.GetAttributes();
if (attributes.Count == 0)
{
return (node.GetFirstToken(includeZeroWidth: true), node.GetLastToken(includeZeroWidth: true));
}
var lastToken = node.GetLastToken(includeZeroWidth: true);
var lastAttributeToken = attributes.Last().GetLastToken(includeZeroWidth: true);
if (lastAttributeToken.Equals(lastToken))
{
return default;
}
var firstTokenAfterAttribute = lastAttributeToken.GetNextToken(includeZeroWidth: true);
// there are attributes, get first token after the tokens belong to attributes
return (firstTokenAfterAttribute, lastToken);
}
public static bool IsPlusOrMinusExpression(this SyntaxToken token)
{
if (token.Kind() is not SyntaxKind.PlusToken and not SyntaxKind.MinusToken)
{
return false;
}
return token.Parent is PrefixUnaryExpressionSyntax;
}
public static bool IsInterpolation(this SyntaxToken currentToken)
=> currentToken.Parent.IsKind(SyntaxKind.Interpolation);
/// <summary>
/// Checks whether currentToken is the opening paren of a deconstruction-declaration in var form, such as <c>var (x, y) = ...</c>
/// </summary>
public static bool IsOpenParenInVarDeconstructionDeclaration(this SyntaxToken currentToken)
{
return currentToken.Kind() == SyntaxKind.OpenParenToken &&
currentToken.Parent is ParenthesizedVariableDesignationSyntax &&
currentToken.Parent.Parent is DeclarationExpressionSyntax;
}
/// <summary>
/// Check whether the currentToken is a comma and is a delimiter between arguments inside a tuple expression.
/// </summary>
public static bool IsCommaInTupleExpression(this SyntaxToken currentToken)
{
return currentToken.IsKind(SyntaxKind.CommaToken) &&
currentToken.Parent.IsKind(SyntaxKind.TupleExpression);
}
public static bool IsCommaInCollectionExpression(this SyntaxToken token)
=> token.Kind() == SyntaxKind.CommaToken && token.Parent.IsKind(SyntaxKind.CollectionExpression);
}
|