/// <summary> /// Code must not contain empty statements. /// </summary> /// <param name="node"> /// The node to process. /// </param> private void CodeMustNotContainEmptyStatements(ITreeNode node) { for (ITreeNode currentNode = node; currentNode != null; currentNode = currentNode.NextSibling) { if (currentNode is ITokenNode) { ITokenNode tokenNode = currentNode as ITokenNode; if (tokenNode.GetTokenType() == CSharpTokenType.SEMICOLON && !(tokenNode.Parent is IForStatement)) { ITokenNode nextNonWhitespaceToken = Utils.GetFirstNonWhitespaceTokenToRight(tokenNode); while (nextNonWhitespaceToken.GetTokenType() == CSharpTokenType.SEMICOLON) { using (WriteLockCookie.Create(true)) { if (nextNonWhitespaceToken.GetNextToken().GetTokenType() == CSharpTokenType.WHITE_SPACE) { LowLevelModificationUtil.DeleteChild(nextNonWhitespaceToken.GetNextToken()); } // remove the spare semi colon LowLevelModificationUtil.DeleteChild(nextNonWhitespaceToken); nextNonWhitespaceToken = Utils.GetFirstNonWhitespaceTokenToRight(tokenNode); } } } } if (currentNode.FirstChild != null) { this.CodeMustNotContainEmptyStatements(currentNode.FirstChild); } } }
/// <summary> /// Preprocessor keywords must not be preceded by space. /// </summary> /// <param name="node"> /// The node to use. /// </param> public static void PreprocessorKeywordsMustNotBePrecededBySpace(ITreeNode node) { for (ITreeNode currentNode = node; currentNode != null; currentNode = currentNode.NextSibling) { if (currentNode is IPreprocessorDirective) { IPreprocessorDirective preprocessorDirectiveNode = currentNode as IPreprocessorDirective; TreeOffset directiveTokenNodeOffset = preprocessorDirectiveNode.Directive.GetTreeStartOffset(); TreeOffset numberSignTokenNodeOffset = preprocessorDirectiveNode.NumberSign.GetTreeStartOffset(); if (directiveTokenNodeOffset - 1 != numberSignTokenNodeOffset) { // There is a gap between them ITokenNode tokenNode = preprocessorDirectiveNode.NumberSign; ITokenNode nextToken = tokenNode.GetNextToken(); using (WriteLockCookie.Create(true)) { // remove the whitespace or new line LowLevelModificationUtil.DeleteChild(nextToken); } } } if (currentNode.FirstChild != null) { PreprocessorKeywordsMustNotBePrecededBySpace(currentNode.FirstChild); } } }
/// <summary> /// Negative and positive signs must be spaced correctly. /// </summary> /// <param name="node"> /// The node to use. /// </param> /// <param name="tokenToCheck"> /// The token to check. /// </param> public static void NegativeAndPositiveSignsMustBeSpacedCorrectly(ITreeNode node, TokenNodeType tokenToCheck) { for (ITreeNode currentNode = node; currentNode != null; currentNode = currentNode.NextSibling) { if (currentNode is ITokenNode) { ITokenNode tokenNode = currentNode as ITokenNode; if (tokenNode.GetTokenType() == tokenToCheck) { if (tokenNode.Parent is IOperatorExpression && !(tokenNode.Parent is IAdditiveExpression)) { ITokenNode nextToken = tokenNode.GetNextToken(); if (nextToken.IsWhitespace()) { using (WriteLockCookie.Create(true)) { // remove the whitespace or new line LowLevelModificationUtil.DeleteChild(nextToken); } } } } } if (currentNode.FirstChild != null) { NegativeAndPositiveSignsMustBeSpacedCorrectly(currentNode.FirstChild, tokenToCheck); } } }
/// <summary> /// Element documentation headers must be preceded by blank line. /// </summary> /// <param name="node"> /// The node. /// </param> private void ElementDocumentationHeadersMustBePrecededByBlankLine(ITreeNode node) { // go back to first new line to the left // thisnew line must be immeidately preceded by a new line and if not insert one for (ITreeNode currentNode = node; currentNode != null; currentNode = currentNode.NextSibling) { if (currentNode is IDocCommentNode) { ITokenNode token = currentNode as ITokenNode; ITokenNode firstNewLineToLeft = Utils.GetFirstNewLineTokenToLeft(token); if (firstNewLineToLeft != null) { ITokenNode tokenBeforeNewLine = firstNewLineToLeft.GetPrevToken(); // if we're the start of a code block then don't insert a new line. if (!tokenBeforeNewLine.IsNewLine() && tokenBeforeNewLine.GetTokenType() != CSharpTokenType.LBRACE && tokenBeforeNewLine.GetTokenType() != CSharpTokenType.END_OF_LINE_COMMENT) { firstNewLineToLeft.GetNextToken().InsertNewLineBefore(); } } } if (currentNode.FirstChild != null) { this.ElementDocumentationHeadersMustBePrecededByBlankLine(currentNode.FirstChild); } } }
private void MakeFormat(FormattingRange range, IEnumerable <string> space) { PsiFormatterHelper.ReplaceSpaces(range.First, range.Last, space); // TODO: Move antiglueing logic into CalcSpaces() ITokenNode nextToken; ITokenNode prevToken = range.First.FindLastTokenIn(); if (prevToken != null) { nextToken = prevToken.GetNextToken(); } else { nextToken = range.First.NextSibling.FindFirstTokenIn(); if (nextToken != null) { prevToken = nextToken.GetPrevToken(); } } if (prevToken != null && nextToken != null) { ITokenNode separator = Context.CodeFormatter.GetMinimalSeparator(prevToken, nextToken); if (separator != null) { LowLevelModificationUtil.AddChildAfter(range.First, separator); } } }
/// <summary> /// Commas must be spaced correctly. /// </summary> /// <param name="node"> /// The node to use. /// </param> public void EqualsMustBeSpacedCorrectly(ITreeNode node) { List <TokenNodeType> tokensThatCanBeLeftSideOfEquals = new List <TokenNodeType> { CSharpTokenType.WHITE_SPACE, CSharpTokenType.NE, CSharpTokenType.LT, CSharpTokenType.GT }; const string WhiteSpace = " "; for (ITreeNode currentNode = node; currentNode != null; currentNode = currentNode.NextSibling) { if (currentNode is ITokenNode) { ITokenNode tokenNode = currentNode as ITokenNode; if (tokenNode.GetTokenType() == CSharpTokenType.EQ) { ITokenNode nextToken = tokenNode.GetNextToken(); ITokenNode previousToken = tokenNode.GetPrevToken(); if (!nextToken.IsWhitespace()) { using (WriteLockCookie.Create(true)) { // insert a space LeafElementBase leafElement = TreeElementFactory.CreateLeafElement( CSharpTokenType.WHITE_SPACE, new JetBrains.Text.StringBuffer(WhiteSpace), 0, WhiteSpace.Length); LowLevelModificationUtil.AddChildBefore(nextToken, new ITreeNode[] { leafElement }); } } if (!tokensThatCanBeLeftSideOfEquals.Contains(previousToken.GetTokenType())) { using (WriteLockCookie.Create(true)) { // insert a space LeafElementBase leafElement = TreeElementFactory.CreateLeafElement( CSharpTokenType.WHITE_SPACE, new JetBrains.Text.StringBuffer(WhiteSpace), 0, WhiteSpace.Length); LowLevelModificationUtil.AddChildBefore(tokenNode, new ITreeNode[] { leafElement }); } } } } if (currentNode.FirstChild != null) { this.EqualsMustBeSpacedCorrectly(currentNode.FirstChild); } } }
/// <summary> /// Remove empty comments. /// </summary> /// <param name="node"> /// The node to process. /// </param> public static void RemoveEmptyComments(ITreeNode node) { // we don't remove empty lines from Element Doc Comments in here // the DeclarationHeader types take care of that. for (ITreeNode currentNode = node; currentNode != null; currentNode = currentNode.NextSibling) { if (currentNode is ITokenNode) { ICommentNode commentNode = currentNode as ICommentNode; if (commentNode != null && !(commentNode is IDocCommentNode)) { if (commentNode.CommentText.Trim() == string.Empty) { ITokenNode leftToken = Utils.GetFirstNewLineTokenToLeft((ITokenNode)currentNode); ITokenNode rightToken = Utils.GetFirstNewLineTokenToRight((ITokenNode)currentNode); if (leftToken == null) { leftToken = (ITokenNode)currentNode; } else { leftToken = leftToken.GetNextToken(); } if (rightToken == null) { rightToken = (ITokenNode)currentNode; } else { currentNode = rightToken.GetNextToken(); } using (WriteLockCookie.Create(true)) { LowLevelModificationUtil.DeleteChildRange(leftToken, rightToken); } } } } if (currentNode != null && currentNode.FirstChild != null) { RemoveEmptyComments(currentNode.FirstChild); } } }
/// <summary> /// Delete child range. /// </summary> /// <param name="first"> /// The first token to delete. /// </param> /// <param name="last"> /// The last token to delete. /// </param> private static void DeleteChildRange(ITokenNode first, ITokenNode last) { using (WriteLockCookie.Create(true)) { List <ITokenNode> a = new List <ITokenNode>(); ITokenNode tokenNodeToStopAt = last.GetNextToken(); for (ITokenNode foundToken = first; foundToken != tokenNodeToStopAt; foundToken = foundToken.GetNextToken()) { a.Add(foundToken); } foreach (ITokenNode tokenNode in a) { LowLevelModificationUtil.DeleteChild(tokenNode); } } }
/// <summary> /// Commas must be spaced correctly. /// </summary> /// <param name="node"> /// The node to use. /// </param> public static void CommasMustBeSpacedCorrectly(ITreeNode node) { List <TokenNodeType> tokensThatCanBeRightSideOfComma = new List <TokenNodeType> { CSharpTokenType.NEW_LINE, CSharpTokenType.WHITE_SPACE, CSharpTokenType.RBRACKET, CSharpTokenType.GT, CSharpTokenType.COMMA, CSharpTokenType.RPARENTH }; const string WhiteSpace = " "; for (ITreeNode currentNode = node; currentNode != null; currentNode = currentNode.NextSibling) { if (currentNode is ITokenNode) { ITokenNode tokenNode = currentNode as ITokenNode; if (tokenNode.GetTokenType() == CSharpTokenType.COMMA) { ITokenNode nextToken = tokenNode.GetNextToken(); if (!tokensThatCanBeRightSideOfComma.Contains(nextToken.GetTokenType())) { using (WriteLockCookie.Create(true)) { // insert a space LeafElementBase leafElement = TreeElementFactory.CreateLeafElement( CSharpTokenType.WHITE_SPACE, new JetBrains.Text.StringBuffer(WhiteSpace), 0, WhiteSpace.Length); LowLevelModificationUtil.AddChildBefore(nextToken, new ITreeNode[] { leafElement }); } } } } if (currentNode.FirstChild != null) { CommasMustBeSpacedCorrectly(currentNode.FirstChild); } } }
/// <summary> /// Comments must not be followed by blank line. /// </summary> /// <param name="node"> /// The node. /// </param> private void CommentsMustNotBeFollowedByBlankLine(ITreeNode node) { for (ITreeNode currentNode = node; currentNode != null; currentNode = currentNode.NextSibling) { if (currentNode is ICommentNode && !(currentNode is IDocCommentNode)) { if (Utils.IsFirstNodeOnLine(currentNode)) { ICommentNode tokenNode = currentNode as ICommentNode; ITokenNode nextToken = tokenNode.GetNextToken(); if (nextToken != null && nextToken.IsNewLine()) { ITokenNode nextNextToken = nextToken.GetNextToken(); if (nextNextToken != null) { ITokenNode nextNextNextToken = Utils.GetFirstNonWhitespaceTokenToRight(nextNextToken); if (nextNextToken.IsNewLine() && !(nextNextNextToken is ICommentNode)) { ITreeNode rightNode = currentNode.FindFormattingRangeToRight(); Utils.RemoveNewLineBefore(rightNode); } } } } } if (currentNode.FirstChild != null) { this.CommentsMustNotBeFollowedByBlankLine(currentNode.FirstChild); } } }
private static void SwapFileHeaderNode(ICSharpFile file, string newHeader) { ITreeRange existingHeaderRange = Utils.GetFileHeaderTreeRange(file); using (WriteLockCookie.Create(file.IsPhysical())) { ICommentNode newCommentNode; if (existingHeaderRange.IsEmpty) { // existing header missing so add on a new line for our new header newHeader += Environment.NewLine; IWhitespaceNode node = file.FirstChild as IWhitespaceNode; bool insertNewLine = true; while (node != null) { if (node.IsNewLine) { insertNewLine = false; break; } node = node.NextSibling as IWhitespaceNode; } if (insertNewLine) { newHeader += Environment.NewLine; } newCommentNode = (ICommentNode) CSharpTokenType.END_OF_LINE_COMMENT.Create(new JB::JetBrains.Text.StringBuffer(newHeader), new TreeOffset(0), new TreeOffset(newHeader.Length)); LowLevelModificationUtil.AddChildBefore(file.FirstChild, new ITreeNode[] { newCommentNode }); } else { ITokenNode lastToken = (ITokenNode)existingHeaderRange.Last; ITokenNode nextToken = lastToken.GetNextToken(); if (nextToken != null) { ITokenNode nextNextToken = nextToken.GetNextToken(); if (nextNextToken != null) { ITokenNode nextNextNextToken = nextNextToken.GetNextToken(); if (!nextToken.IsNewLine() || !nextNextToken.IsNewLine()) { newHeader += Environment.NewLine; } if (nextNextNextToken.GetTokenType() == CSharpTokenType.PP_SHARP && nextToken.IsNewLine() && nextNextToken.IsNewLine()) { newHeader += Environment.NewLine; } newCommentNode = (ICommentNode) CSharpTokenType.END_OF_LINE_COMMENT.Create( new JB::JetBrains.Text.StringBuffer(newHeader), new TreeOffset(0), new TreeOffset(newHeader.Length)); LowLevelModificationUtil.ReplaceChildRange(existingHeaderRange.First, existingHeaderRange.Last, new ITreeNode[] { newCommentNode }); } } } } }
/// <summary> /// Delete child range. /// </summary> /// <param name="first"> /// The first token to delete. /// </param> /// <param name="last"> /// The last token to delete. /// </param> private static void DeleteChildRange(ITokenNode first, ITokenNode last) { using (WriteLockCookie.Create(true)) { List<ITokenNode> a = new List<ITokenNode>(); ITokenNode tokenNodeToStopAt = last.GetNextToken(); for (ITokenNode foundToken = first; foundToken != tokenNodeToStopAt; foundToken = foundToken.GetNextToken()) { a.Add(foundToken); } foreach (ITokenNode tokenNode in a) { LowLevelModificationUtil.DeleteChild(tokenNode); } } }
/// <summary> /// Gets the first non-whitespace token that occurs after the token passed in. /// </summary> /// <param name="tokenNode"> /// The TokenNode to start at. /// </param> /// <returns> /// The first non-whitespace token. /// </returns> public static ITokenNode GetFirstNonWhitespaceTokenToRight(ITokenNode tokenNode) { ITokenNode currentToken = tokenNode.GetNextToken(); while (currentToken != null && currentToken.IsWhitespace()) { currentToken = currentToken.GetNextToken(); } return currentToken; }
/// <summary> /// Gets the first non-whitespace token that occurs after the token passed in. /// </summary> /// <param name="tokenNode"> /// The TokenNode to start at. /// </param> /// <returns> /// The first non-whitespace token. /// </returns> public static ITokenNode GetFirstNewLineTokenToRight(ITokenNode tokenNode) { ITokenNode currentToken = tokenNode.GetNextToken(); while (!currentToken.IsNewLine() && currentToken != null) { currentToken = currentToken.GetNextToken(); } return currentToken; }
/// <summary> /// True if the token is followed on the same line with a non-whitespace token. /// </summary> /// <param name="tokenNode"> /// THe token to start at. /// </param> /// <returns> /// True or false. /// </returns> public static bool TokenHasNonWhitespaceTokenToRightOnSameLine(ITokenNode tokenNode) { ITokenNode currentToken = tokenNode.GetNextToken(); if (currentToken == null) { return false; } while (currentToken.IsWhitespace() && !currentToken.IsNewLine() && currentToken != null) { currentToken = currentToken.GetNextToken(); } return currentToken != null && !currentToken.IsNewLine(); }
/// <summary> /// Closing curly bracket must be followed by blank line. /// </summary> /// <param name="node"> /// The node to use. /// </param> public static void ClosingCurlyBracketMustBeFollowedByBlankLine(ITreeNode node) { // Closing curly brackets must be followed by a newline unless they are closing an object initializer or // followed by one of the endtokens defined here. // catch // finally // else // rbrace // dowhile // preprocessor directives List <TokenNodeType> tokensThatFollowClosingCurlyBracketWithoutNewLine = new List <TokenNodeType> { CSharpTokenType.RBRACE, CSharpTokenType.DO_KEYWORD, CSharpTokenType.ELSE_KEYWORD, CSharpTokenType.CATCH_KEYWORD, CSharpTokenType.FINALLY_KEYWORD }; List <TokenNodeType> objectInitializerFollowers = new List <TokenNodeType> { CSharpTokenType.AS_KEYWORD, CSharpTokenType.IS_KEYWORD, CSharpTokenType.COMMA, CSharpTokenType.SEMICOLON, CSharpTokenType.DOT, CSharpTokenType.QUEST, CSharpTokenType.COLON, CSharpTokenType.RPARENTH, CSharpTokenType.EQEQ, CSharpTokenType.GE, CSharpTokenType.GT, CSharpTokenType.LE, CSharpTokenType.LT, CSharpTokenType.NE, CSharpTokenType.MINUS, CSharpTokenType.PLUS, CSharpTokenType.DIV, CSharpTokenType.ASTERISK, CSharpTokenType.PERC, CSharpTokenType.MINUSMINUS, CSharpTokenType.PLUSPLUS }; for (ITreeNode currentNode = node; currentNode != null; currentNode = currentNode.NextSibling) { if (currentNode is ITokenNode) { ITokenNode tokenNode = currentNode as ITokenNode; if (tokenNode.GetTokenType() == CSharpTokenType.RBRACE) { IBlock blockNode = tokenNode.Parent as IBlock; if (blockNode != null) { JB::JetBrains.Util.dataStructures.TypedIntrinsics.Int32 <DocLine> lineNumberForLBrace = Utils.GetLineNumberForElement(blockNode.LBrace); JB::JetBrains.Util.dataStructures.TypedIntrinsics.Int32 <DocLine> lineNumberForRBrace = Utils.GetLineNumberForElement(blockNode.RBrace); if (lineNumberForLBrace != lineNumberForRBrace) { ITokenNode currentToken = tokenNode.GetNextToken(); int newLineCount = 0; while (currentToken != null) { if (currentToken.IsWhitespace()) { if (currentToken.IsNewLine()) { newLineCount++; if (newLineCount == 2) { // if we get 2 new lines we've already got a blank line after the closing curly bracket so jog on. break; } } } else { if ((!tokensThatFollowClosingCurlyBracketWithoutNewLine.Contains(currentToken.GetTokenType()) && !objectInitializerFollowers.Contains(currentToken.GetTokenType())) || (objectInitializerFollowers.Contains(currentToken.GetTokenType()) && newLineCount == 1)) { tokenNode.GetNextToken().InsertNewLineBefore(); } break; } currentToken = currentToken.GetNextToken(); } } } } } if (currentNode.FirstChild != null) { ClosingCurlyBracketMustBeFollowedByBlankLine(currentNode.FirstChild); } } }