//===--- BreakableToken.h - Format C++ code -------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief Declares BreakableToken, BreakableStringLiteral, BreakableComment, /// BreakableBlockComment and BreakableLineCommentSection classes, that contain /// token type-specific logic to break long lines in tokens and reflow content /// between tokens. /// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_LIB_FORMAT_BREAKABLETOKEN_H #define LLVM_CLANG_LIB_FORMAT_BREAKABLETOKEN_H #include "Encoding.h" #include "TokenAnnotator.h" #include "WhitespaceManager.h" #include "llvm/Support/Regex.h" #include namespace clang { namespace format { /// \brief Checks if \p Token switches formatting, like /* clang-format off */. /// \p Token must be a comment. bool switchesFormatting(const FormatToken &Token); struct FormatStyle; /// \brief Base class for strategies on how to break tokens. /// /// This is organised around the concept of a \c Split, which is a whitespace /// range that signifies a position of the content of a token where a /// reformatting might be done. Operating with splits is divided into 3 /// operations: /// - getSplit, for finding a split starting at a position, /// - getLineLengthAfterSplit, for calculating the size in columns of the rest /// of the content after a split has been used for breaking, and /// - insertBreak, for executing the split using a whitespace manager. /// /// There is a pair of operations that are used to compress a long whitespace /// range with a single space if that will bring the line lenght under the /// column limit: /// - getLineLengthAfterCompression, for calculating the size in columns of the /// line after a whitespace range has been compressed, and /// - compressWhitespace, for executing the whitespace compression using a /// whitespace manager; note that the compressed whitespace may be in the /// middle of the original line and of the reformatted line. /// /// For tokens where the whitespace before each line needs to be also /// reformatted, for example for tokens supporting reflow, there are analogous /// operations that might be executed before the main line breaking occurs: /// - getSplitBefore, for finding a split such that the content preceding it /// needs to be specially reflown, /// - getLineLengthAfterSplitBefore, for calculating the line length in columns /// of the remainder of the content after the beginning of the content has /// been reformatted, and /// - replaceWhitespaceBefore, for executing the reflow using a whitespace /// manager. /// /// FIXME: The interface seems set in stone, so we might want to just pull the /// strategy into the class, instead of controlling it from the outside. class BreakableToken { public: /// \brief Contains starting character index and length of split. typedef std::pair Split; virtual ~BreakableToken() {} /// \brief Returns the number of lines in this token in the original code. virtual unsigned getLineCount() const = 0; /// \brief Returns the number of columns required to format the piece of line /// at \p LineIndex, from byte offset \p TailOffset with length \p Length. /// /// Note that previous breaks are not taken into account. \p TailOffset is /// always specified from the start of the (original) line. /// \p Length can be set to StringRef::npos, which means "to the end of line". virtual unsigned getLineLengthAfterSplit(unsigned LineIndex, unsigned TailOffset, StringRef::size_type Length) const = 0; /// \brief Returns a range (offset, length) at which to break the line at /// \p LineIndex, if previously broken at \p TailOffset. If possible, do not /// violate \p ColumnLimit. virtual Split getSplit(unsigned LineIndex, unsigned TailOffset, unsigned ColumnLimit, llvm::Regex &CommentPragmasRegex) const = 0; /// \brief Emits the previously retrieved \p Split via \p Whitespaces. virtual void insertBreak(unsigned LineIndex, unsigned TailOffset, Split Split, WhitespaceManager &Whitespaces) = 0; /// \brief Returns the number of columns required to format the piece of line /// at \p LineIndex, from byte offset \p TailOffset after the whitespace range /// \p Split has been compressed into a single space. unsigned getLineLengthAfterCompression(unsigned RemainingTokenColumns, Split Split) const; /// \brief Replaces the whitespace range described by \p Split with a single /// space. virtual void compressWhitespace(unsigned LineIndex, unsigned TailOffset, Split Split, WhitespaceManager &Whitespaces) = 0; /// \brief Returns a whitespace range (offset, length) of the content at /// \p LineIndex such that the content preceding this range needs to be /// reformatted before any breaks are made to this line. /// /// \p PreviousEndColumn is the end column of the previous line after /// formatting. /// /// A result having offset == StringRef::npos means that no piece of the line /// needs to be reformatted before any breaks are made. virtual Split getSplitBefore(unsigned LineIndex, unsigned PreviousEndColumn, unsigned ColumnLimit, llvm::Regex &CommentPragmasRegex) const { return Split(StringRef::npos, 0); } /// \brief Returns the number of columns required to format the piece of line /// at \p LineIndex after the content preceding the whitespace range specified /// \p SplitBefore has been reformatted, but before any breaks are made to /// this line. virtual unsigned getLineLengthAfterSplitBefore(unsigned LineIndex, unsigned TailOffset, unsigned PreviousEndColumn, unsigned ColumnLimit, Split SplitBefore) const { return getLineLengthAfterSplit(LineIndex, TailOffset, StringRef::npos); } /// \brief Replaces the whitespace between \p LineIndex-1 and \p LineIndex. /// Performs a reformatting of the content at \p LineIndex preceding the /// whitespace range \p SplitBefore. virtual void replaceWhitespaceBefore(unsigned LineIndex, unsigned PreviousEndColumn, unsigned ColumnLimit, Split SplitBefore, WhitespaceManager &Whitespaces) {} /// \brief Updates the next token of \p State to the next token after this /// one. This can be used when this token manages a set of underlying tokens /// as a unit and is responsible for the formatting of the them. virtual void updateNextToken(LineState &State) const {} protected: BreakableToken(const FormatToken &Tok, bool InPPDirective, encoding::Encoding Encoding, const FormatStyle &Style) : Tok(Tok), InPPDirective(InPPDirective), Encoding(Encoding), Style(Style) {} const FormatToken &Tok; const bool InPPDirective; const encoding::Encoding Encoding; const FormatStyle &Style; }; /// \brief Base class for single line tokens that can be broken. /// /// \c getSplit() needs to be implemented by child classes. class BreakableSingleLineToken : public BreakableToken { public: unsigned getLineCount() const override; unsigned getLineLengthAfterSplit(unsigned LineIndex, unsigned TailOffset, StringRef::size_type Length) const override; protected: BreakableSingleLineToken(const FormatToken &Tok, unsigned StartColumn, StringRef Prefix, StringRef Postfix, bool InPPDirective, encoding::Encoding Encoding, const FormatStyle &Style); // The column in which the token starts. unsigned StartColumn; // The prefix a line needs after a break in the token. StringRef Prefix; // The postfix a line needs before introducing a break. StringRef Postfix; // The token text excluding the prefix and postfix. StringRef Line; }; class BreakableStringLiteral : public BreakableSingleLineToken { public: /// \brief Creates a breakable token for a single line string literal. /// /// \p StartColumn specifies the column in which the token will start /// after formatting. BreakableStringLiteral(const FormatToken &Tok, unsigned StartColumn, StringRef Prefix, StringRef Postfix, bool InPPDirective, encoding::Encoding Encoding, const FormatStyle &Style); Split getSplit(unsigned LineIndex, unsigned TailOffset, unsigned ColumnLimit, llvm::Regex &CommentPragmasRegex) const override; void insertBreak(unsigned LineIndex, unsigned TailOffset, Split Split, WhitespaceManager &Whitespaces) override; void compressWhitespace(unsigned LineIndex, unsigned TailOffset, Split Split, WhitespaceManager &Whitespaces) override {} }; class BreakableComment : public BreakableToken { protected: /// \brief Creates a breakable token for a comment. /// /// \p StartColumn specifies the column in which the comment will start after /// formatting. BreakableComment(const FormatToken &Token, unsigned StartColumn, bool InPPDirective, encoding::Encoding Encoding, const FormatStyle &Style); public: unsigned getLineCount() const override; Split getSplit(unsigned LineIndex, unsigned TailOffset, unsigned ColumnLimit, llvm::Regex &CommentPragmasRegex) const override; void compressWhitespace(unsigned LineIndex, unsigned TailOffset, Split Split, WhitespaceManager &Whitespaces) override; protected: virtual unsigned getContentStartColumn(unsigned LineIndex, unsigned TailOffset) const = 0; // Returns a split that divides Text into a left and right parts, such that // the left part is suitable for reflowing after PreviousEndColumn. Split getReflowSplit(StringRef Text, StringRef ReflowPrefix, unsigned PreviousEndColumn, unsigned ColumnLimit) const; // Returns the token containing the line at LineIndex. const FormatToken &tokenAt(unsigned LineIndex) const; // Checks if the content of line LineIndex may be reflown with the previous // line. virtual bool mayReflow(unsigned LineIndex, llvm::Regex &CommentPragmasRegex) const = 0; // Contains the original text of the lines of the block comment. // // In case of a block comments, excludes the leading /* in the first line and // trailing */ in the last line. In case of line comments, excludes the // leading // and spaces. SmallVector Lines; // Contains the text of the lines excluding all leading and trailing // whitespace between the lines. Note that the decoration (if present) is also // not considered part of the text. SmallVector Content; // Tokens[i] contains a reference to the token containing Lines[i] if the // whitespace range before that token is managed by this block. // Otherwise, Tokens[i] is a null pointer. SmallVector Tokens; // ContentColumn[i] is the target column at which Content[i] should be. // Note that this excludes a leading "* " or "*" in case of block comments // where all lines have a "*" prefix, or the leading "// " or "//" in case of // line comments. // // In block comments, the first line's target column is always positive. The // remaining lines' target columns are relative to the first line to allow // correct indentation of comments in \c WhitespaceManager. Thus they can be // negative as well (in case the first line needs to be unindented more than // there's actual whitespace in another line). SmallVector ContentColumn; // The intended start column of the first line of text from this section. unsigned StartColumn; // The prefix to use in front a line that has been reflown up. // For example, when reflowing the second line after the first here: // // comment 1 // // comment 2 // we expect: // // comment 1 comment 2 // and not: // // comment 1comment 2 StringRef ReflowPrefix = " "; }; class BreakableBlockComment : public BreakableComment { public: BreakableBlockComment(const FormatToken &Token, unsigned StartColumn, unsigned OriginalStartColumn, bool FirstInLine, bool InPPDirective, encoding::Encoding Encoding, const FormatStyle &Style); unsigned getLineLengthAfterSplit(unsigned LineIndex, unsigned TailOffset, StringRef::size_type Length) const override; void insertBreak(unsigned LineIndex, unsigned TailOffset, Split Split, WhitespaceManager &Whitespaces) override; Split getSplitBefore(unsigned LineIndex, unsigned PreviousEndColumn, unsigned ColumnLimit, llvm::Regex &CommentPragmasRegex) const override; unsigned getLineLengthAfterSplitBefore(unsigned LineIndex, unsigned TailOffset, unsigned PreviousEndColumn, unsigned ColumnLimit, Split SplitBefore) const override; void replaceWhitespaceBefore(unsigned LineIndex, unsigned PreviousEndColumn, unsigned ColumnLimit, Split SplitBefore, WhitespaceManager &Whitespaces) override; bool mayReflow(unsigned LineIndex, llvm::Regex &CommentPragmasRegex) const override; private: // Rearranges the whitespace between Lines[LineIndex-1] and Lines[LineIndex]. // // Updates Content[LineIndex-1] and Content[LineIndex] by stripping off // leading and trailing whitespace. // // Sets ContentColumn to the intended column in which the text at // Lines[LineIndex] starts (note that the decoration, if present, is not // considered part of the text). void adjustWhitespace(unsigned LineIndex, int IndentDelta); // Computes the end column if the full Content from LineIndex gets reflown // after PreviousEndColumn. unsigned getReflownColumn(StringRef Content, unsigned LineIndex, unsigned PreviousEndColumn) const; unsigned getContentStartColumn(unsigned LineIndex, unsigned TailOffset) const override; // The column at which the text of a broken line should start. // Note that an optional decoration would go before that column. // IndentAtLineBreak is a uniform position for all lines in a block comment, // regardless of their relative position. // FIXME: Revisit the decision to do this; the main reason was to support // patterns like // /**************//** // * Comment // We could also support such patterns by special casing the first line // instead. unsigned IndentAtLineBreak; // This is to distinguish between the case when the last line was empty and // the case when it started with a decoration ("*" or "* "). bool LastLineNeedsDecoration; // Either "* " if all lines begin with a "*", or empty. StringRef Decoration; // If this block comment has decorations, this is the column of the start of // the decorations. unsigned DecorationColumn; }; class BreakableLineCommentSection : public BreakableComment { public: BreakableLineCommentSection(const FormatToken &Token, unsigned StartColumn, unsigned OriginalStartColumn, bool FirstInLine, bool InPPDirective, encoding::Encoding Encoding, const FormatStyle &Style); unsigned getLineLengthAfterSplit(unsigned LineIndex, unsigned TailOffset, StringRef::size_type Length) const override; void insertBreak(unsigned LineIndex, unsigned TailOffset, Split Split, WhitespaceManager &Whitespaces) override; Split getSplitBefore(unsigned LineIndex, unsigned PreviousEndColumn, unsigned ColumnLimit, llvm::Regex &CommentPragmasRegex) const override; unsigned getLineLengthAfterSplitBefore(unsigned LineIndex, unsigned TailOffset, unsigned PreviousEndColumn, unsigned ColumnLimit, Split SplitBefore) const override; void replaceWhitespaceBefore(unsigned LineIndex, unsigned PreviousEndColumn, unsigned ColumnLimit, Split SplitBefore, WhitespaceManager &Whitespaces) override; void updateNextToken(LineState &State) const override; bool mayReflow(unsigned LineIndex, llvm::Regex &CommentPragmasRegex) const override; private: unsigned getContentStartColumn(unsigned LineIndex, unsigned TailOffset) const override; // OriginalPrefix[i] contains the original prefix of line i, including // trailing whitespace before the start of the content. The indentation // preceding the prefix is not included. // For example, if the line is: // // content // then the original prefix is "// ". SmallVector OriginalPrefix; // Prefix[i] contains the intended leading "//" with trailing spaces to // account for the indentation of content within the comment at line i after // formatting. It can be different than the original prefix when the original // line starts like this: // //content // Then the original prefix is "//", but the prefix is "// ". SmallVector Prefix; SmallVector OriginalContentColumn; /// \brief The token to which the last line of this breakable token belongs /// to; nullptr if that token is the initial token. /// /// The distinction is because if the token of the last line of this breakable /// token is distinct from the initial token, this breakable token owns the /// whitespace before the token of the last line, and the whitespace manager /// must be able to modify it. FormatToken *LastLineTok = nullptr; }; } // namespace format } // namespace clang #endif