17330f729Sjoerg //===--- WhitespaceManager.h - Format C++ code ------------------*- C++ -*-===// 27330f729Sjoerg // 37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information. 57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 67330f729Sjoerg // 77330f729Sjoerg //===----------------------------------------------------------------------===// 87330f729Sjoerg /// 97330f729Sjoerg /// \file 107330f729Sjoerg /// WhitespaceManager class manages whitespace around tokens and their 117330f729Sjoerg /// replacements. 127330f729Sjoerg /// 137330f729Sjoerg //===----------------------------------------------------------------------===// 147330f729Sjoerg 157330f729Sjoerg #ifndef LLVM_CLANG_LIB_FORMAT_WHITESPACEMANAGER_H 167330f729Sjoerg #define LLVM_CLANG_LIB_FORMAT_WHITESPACEMANAGER_H 177330f729Sjoerg 187330f729Sjoerg #include "TokenAnnotator.h" 197330f729Sjoerg #include "clang/Basic/SourceManager.h" 207330f729Sjoerg #include "clang/Format/Format.h" 217330f729Sjoerg #include <string> 22*e038c9c4Sjoerg #include <tuple> 237330f729Sjoerg 247330f729Sjoerg namespace clang { 257330f729Sjoerg namespace format { 267330f729Sjoerg 277330f729Sjoerg /// Manages the whitespaces around tokens and their replacements. 287330f729Sjoerg /// 297330f729Sjoerg /// This includes special handling for certain constructs, e.g. the alignment of 307330f729Sjoerg /// trailing line comments. 317330f729Sjoerg /// 327330f729Sjoerg /// To guarantee correctness of alignment operations, the \c WhitespaceManager 337330f729Sjoerg /// must be informed about every token in the source file; for each token, there 347330f729Sjoerg /// must be exactly one call to either \c replaceWhitespace or 357330f729Sjoerg /// \c addUntouchableToken. 367330f729Sjoerg /// 377330f729Sjoerg /// There may be multiple calls to \c breakToken for a given token. 387330f729Sjoerg class WhitespaceManager { 397330f729Sjoerg public: WhitespaceManager(const SourceManager & SourceMgr,const FormatStyle & Style,bool UseCRLF)407330f729Sjoerg WhitespaceManager(const SourceManager &SourceMgr, const FormatStyle &Style, 417330f729Sjoerg bool UseCRLF) 427330f729Sjoerg : SourceMgr(SourceMgr), Style(Style), UseCRLF(UseCRLF) {} 437330f729Sjoerg useCRLF()447330f729Sjoerg bool useCRLF() const { return UseCRLF; } 457330f729Sjoerg 467330f729Sjoerg /// Replaces the whitespace in front of \p Tok. Only call once for 477330f729Sjoerg /// each \c AnnotatedToken. 487330f729Sjoerg /// 497330f729Sjoerg /// \p StartOfTokenColumn is the column at which the token will start after 507330f729Sjoerg /// this replacement. It is needed for determining how \p Spaces is turned 517330f729Sjoerg /// into tabs and spaces for some format styles. 527330f729Sjoerg void replaceWhitespace(FormatToken &Tok, unsigned Newlines, unsigned Spaces, 53*e038c9c4Sjoerg unsigned StartOfTokenColumn, bool isAligned = false, 547330f729Sjoerg bool InPPDirective = false); 557330f729Sjoerg 567330f729Sjoerg /// Adds information about an unchangeable token's whitespace. 577330f729Sjoerg /// 587330f729Sjoerg /// Needs to be called for every token for which \c replaceWhitespace 597330f729Sjoerg /// was not called. 607330f729Sjoerg void addUntouchableToken(const FormatToken &Tok, bool InPPDirective); 617330f729Sjoerg 627330f729Sjoerg llvm::Error addReplacement(const tooling::Replacement &Replacement); 637330f729Sjoerg 647330f729Sjoerg /// Inserts or replaces whitespace in the middle of a token. 657330f729Sjoerg /// 667330f729Sjoerg /// Inserts \p PreviousPostfix, \p Newlines, \p Spaces and \p CurrentPrefix 677330f729Sjoerg /// (in this order) at \p Offset inside \p Tok, replacing \p ReplaceChars 687330f729Sjoerg /// characters. 697330f729Sjoerg /// 707330f729Sjoerg /// Note: \p Spaces can be negative to retain information about initial 717330f729Sjoerg /// relative column offset between a line of a block comment and the start of 727330f729Sjoerg /// the comment. This negative offset may be compensated by trailing comment 737330f729Sjoerg /// alignment here. In all other cases negative \p Spaces will be truncated to 747330f729Sjoerg /// 0. 757330f729Sjoerg /// 767330f729Sjoerg /// When \p InPPDirective is true, escaped newlines are inserted. \p Spaces is 777330f729Sjoerg /// used to align backslashes correctly. 787330f729Sjoerg void replaceWhitespaceInToken(const FormatToken &Tok, unsigned Offset, 797330f729Sjoerg unsigned ReplaceChars, 807330f729Sjoerg StringRef PreviousPostfix, 817330f729Sjoerg StringRef CurrentPrefix, bool InPPDirective, 827330f729Sjoerg unsigned Newlines, int Spaces); 837330f729Sjoerg 847330f729Sjoerg /// Returns all the \c Replacements created during formatting. 857330f729Sjoerg const tooling::Replacements &generateReplacements(); 867330f729Sjoerg 877330f729Sjoerg /// Represents a change before a token, a break inside a token, 887330f729Sjoerg /// or the layout of an unchanged token (or whitespace within). 897330f729Sjoerg struct Change { 907330f729Sjoerg /// Functor to sort changes in original source order. 917330f729Sjoerg class IsBeforeInFile { 927330f729Sjoerg public: IsBeforeInFileChange937330f729Sjoerg IsBeforeInFile(const SourceManager &SourceMgr) : SourceMgr(SourceMgr) {} 947330f729Sjoerg bool operator()(const Change &C1, const Change &C2) const; 957330f729Sjoerg 967330f729Sjoerg private: 977330f729Sjoerg const SourceManager &SourceMgr; 987330f729Sjoerg }; 997330f729Sjoerg 1007330f729Sjoerg /// Creates a \c Change. 1017330f729Sjoerg /// 1027330f729Sjoerg /// The generated \c Change will replace the characters at 1037330f729Sjoerg /// \p OriginalWhitespaceRange with a concatenation of 1047330f729Sjoerg /// \p PreviousLinePostfix, \p NewlinesBefore line breaks, \p Spaces spaces 1057330f729Sjoerg /// and \p CurrentLinePrefix. 1067330f729Sjoerg /// 1077330f729Sjoerg /// \p StartOfTokenColumn and \p InPPDirective will be used to lay out 1087330f729Sjoerg /// trailing comments and escaped newlines. 1097330f729Sjoerg Change(const FormatToken &Tok, bool CreateReplacement, 1107330f729Sjoerg SourceRange OriginalWhitespaceRange, int Spaces, 1117330f729Sjoerg unsigned StartOfTokenColumn, unsigned NewlinesBefore, 1127330f729Sjoerg StringRef PreviousLinePostfix, StringRef CurrentLinePrefix, 113*e038c9c4Sjoerg bool IsAligned, bool ContinuesPPDirective, bool IsInsideToken); 1147330f729Sjoerg 1157330f729Sjoerg // The kind of the token whose whitespace this change replaces, or in which 1167330f729Sjoerg // this change inserts whitespace. 1177330f729Sjoerg // FIXME: Currently this is not set correctly for breaks inside comments, as 1187330f729Sjoerg // the \c BreakableToken is still doing its own alignment. 1197330f729Sjoerg const FormatToken *Tok; 1207330f729Sjoerg 1217330f729Sjoerg bool CreateReplacement; 1227330f729Sjoerg // Changes might be in the middle of a token, so we cannot just keep the 1237330f729Sjoerg // FormatToken around to query its information. 1247330f729Sjoerg SourceRange OriginalWhitespaceRange; 1257330f729Sjoerg unsigned StartOfTokenColumn; 1267330f729Sjoerg unsigned NewlinesBefore; 1277330f729Sjoerg std::string PreviousLinePostfix; 1287330f729Sjoerg std::string CurrentLinePrefix; 129*e038c9c4Sjoerg bool IsAligned; 1307330f729Sjoerg bool ContinuesPPDirective; 1317330f729Sjoerg 1327330f729Sjoerg // The number of spaces in front of the token or broken part of the token. 1337330f729Sjoerg // This will be adapted when aligning tokens. 1347330f729Sjoerg // Can be negative to retain information about the initial relative offset 1357330f729Sjoerg // of the lines in a block comment. This is used when aligning trailing 1367330f729Sjoerg // comments. Uncompensated negative offset is truncated to 0. 1377330f729Sjoerg int Spaces; 1387330f729Sjoerg 1397330f729Sjoerg // If this change is inside of a token but not at the start of the token or 1407330f729Sjoerg // directly after a newline. 1417330f729Sjoerg bool IsInsideToken; 1427330f729Sjoerg 1437330f729Sjoerg // \c IsTrailingComment, \c TokenLength, \c PreviousEndOfTokenColumn and 1447330f729Sjoerg // \c EscapedNewlineColumn will be calculated in 1457330f729Sjoerg // \c calculateLineBreakInformation. 1467330f729Sjoerg bool IsTrailingComment; 1477330f729Sjoerg unsigned TokenLength; 1487330f729Sjoerg unsigned PreviousEndOfTokenColumn; 1497330f729Sjoerg unsigned EscapedNewlineColumn; 1507330f729Sjoerg 1517330f729Sjoerg // These fields are used to retain correct relative line indentation in a 1527330f729Sjoerg // block comment when aligning trailing comments. 1537330f729Sjoerg // 1547330f729Sjoerg // If this Change represents a continuation of a block comment, 1557330f729Sjoerg // \c StartOfBlockComment is pointer to the first Change in the block 1567330f729Sjoerg // comment. \c IndentationOffset is a relative column offset to this 1577330f729Sjoerg // change, so that the correct column can be reconstructed at the end of 1587330f729Sjoerg // the alignment process. 1597330f729Sjoerg const Change *StartOfBlockComment; 1607330f729Sjoerg int IndentationOffset; 1617330f729Sjoerg 162*e038c9c4Sjoerg // Depth of conditionals. Computed from tracking fake parenthesis, except 163*e038c9c4Sjoerg // it does not increase the indent for "chained" conditionals. 164*e038c9c4Sjoerg int ConditionalsLevel; 165*e038c9c4Sjoerg 166*e038c9c4Sjoerg // A combination of indent, nesting and conditionals levels, which are used 167*e038c9c4Sjoerg // in tandem to compute lexical scope, for the purposes of deciding 1687330f729Sjoerg // when to stop consecutive alignment runs. indentAndNestingLevelChange169*e038c9c4Sjoerg std::tuple<unsigned, unsigned, unsigned> indentAndNestingLevel() const { 170*e038c9c4Sjoerg return std::make_tuple(Tok->IndentLevel, Tok->NestingLevel, 171*e038c9c4Sjoerg ConditionalsLevel); 1727330f729Sjoerg } 1737330f729Sjoerg }; 1747330f729Sjoerg 1757330f729Sjoerg private: 1767330f729Sjoerg /// Calculate \c IsTrailingComment, \c TokenLength for the last tokens 1777330f729Sjoerg /// or token parts in a line and \c PreviousEndOfTokenColumn and 1787330f729Sjoerg /// \c EscapedNewlineColumn for the first tokens or token parts in a line. 1797330f729Sjoerg void calculateLineBreakInformation(); 1807330f729Sjoerg 1817330f729Sjoerg /// \brief Align consecutive C/C++ preprocessor macros over all \c Changes. 1827330f729Sjoerg void alignConsecutiveMacros(); 1837330f729Sjoerg 1847330f729Sjoerg /// Align consecutive assignments over all \c Changes. 1857330f729Sjoerg void alignConsecutiveAssignments(); 1867330f729Sjoerg 187*e038c9c4Sjoerg /// Align consecutive bitfields over all \c Changes. 188*e038c9c4Sjoerg void alignConsecutiveBitFields(); 189*e038c9c4Sjoerg 1907330f729Sjoerg /// Align consecutive declarations over all \c Changes. 1917330f729Sjoerg void alignConsecutiveDeclarations(); 1927330f729Sjoerg 193*e038c9c4Sjoerg /// Align consecutive declarations over all \c Changes. 194*e038c9c4Sjoerg void alignChainedConditionals(); 195*e038c9c4Sjoerg 1967330f729Sjoerg /// Align trailing comments over all \c Changes. 1977330f729Sjoerg void alignTrailingComments(); 1987330f729Sjoerg 1997330f729Sjoerg /// Align trailing comments from change \p Start to change \p End at 2007330f729Sjoerg /// the specified \p Column. 2017330f729Sjoerg void alignTrailingComments(unsigned Start, unsigned End, unsigned Column); 2027330f729Sjoerg 2037330f729Sjoerg /// Align escaped newlines over all \c Changes. 2047330f729Sjoerg void alignEscapedNewlines(); 2057330f729Sjoerg 2067330f729Sjoerg /// Align escaped newlines from change \p Start to change \p End at 2077330f729Sjoerg /// the specified \p Column. 2087330f729Sjoerg void alignEscapedNewlines(unsigned Start, unsigned End, unsigned Column); 2097330f729Sjoerg 2107330f729Sjoerg /// Fill \c Replaces with the replacements for all effective changes. 2117330f729Sjoerg void generateChanges(); 2127330f729Sjoerg 2137330f729Sjoerg /// Stores \p Text as the replacement for the whitespace in \p Range. 2147330f729Sjoerg void storeReplacement(SourceRange Range, StringRef Text); 2157330f729Sjoerg void appendNewlineText(std::string &Text, unsigned Newlines); 2167330f729Sjoerg void appendEscapedNewlineText(std::string &Text, unsigned Newlines, 2177330f729Sjoerg unsigned PreviousEndOfTokenColumn, 2187330f729Sjoerg unsigned EscapedNewlineColumn); 2197330f729Sjoerg void appendIndentText(std::string &Text, unsigned IndentLevel, 220*e038c9c4Sjoerg unsigned Spaces, unsigned WhitespaceStartColumn, 221*e038c9c4Sjoerg bool IsAligned); 222*e038c9c4Sjoerg unsigned appendTabIndent(std::string &Text, unsigned Spaces, 223*e038c9c4Sjoerg unsigned Indentation); 2247330f729Sjoerg 2257330f729Sjoerg SmallVector<Change, 16> Changes; 2267330f729Sjoerg const SourceManager &SourceMgr; 2277330f729Sjoerg tooling::Replacements Replaces; 2287330f729Sjoerg const FormatStyle &Style; 2297330f729Sjoerg bool UseCRLF; 2307330f729Sjoerg }; 2317330f729Sjoerg 2327330f729Sjoerg } // namespace format 2337330f729Sjoerg } // namespace clang 2347330f729Sjoerg 2357330f729Sjoerg #endif 236