//===--- 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, and /// BreakableBlockComment classes, that contain token type-specific logic to /// break long lines in tokens. /// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_FORMAT_BREAKABLETOKEN_H #define LLVM_CLANG_FORMAT_BREAKABLETOKEN_H #include "TokenAnnotator.h" #include "WhitespaceManager.h" #include namespace clang { namespace format { class BreakableToken { public: BreakableToken(const SourceManager &SourceMgr, const FormatToken &Tok, unsigned StartColumn) : Tok(Tok), StartColumn(StartColumn), TokenText(SourceMgr.getCharacterData(Tok.getStartOfNonWhitespace()), Tok.TokenLength) {} virtual ~BreakableToken() {} virtual unsigned getLineCount() const = 0; virtual unsigned getLineSize(unsigned Index) const = 0; virtual unsigned getLineLengthAfterSplit(unsigned LineIndex, unsigned TailOffset) const = 0; // Contains starting character index and length of split. typedef std::pair Split; virtual Split getSplit(unsigned LineIndex, unsigned TailOffset, unsigned ColumnLimit) const = 0; virtual void insertBreak(unsigned LineIndex, unsigned TailOffset, Split Split, bool InPPDirective, WhitespaceManager &Whitespaces) = 0; virtual void trimLine(unsigned LineIndex, unsigned TailOffset, unsigned InPPDirective, WhitespaceManager &Whitespaces) {} protected: const FormatToken &Tok; unsigned StartColumn; StringRef TokenText; }; class BreakableStringLiteral : public BreakableToken { public: BreakableStringLiteral(const SourceManager &SourceMgr, const FormatToken &Tok, unsigned StartColumn) : BreakableToken(SourceMgr, Tok, StartColumn) { assert(TokenText.startswith("\"") && TokenText.endswith("\"")); } virtual unsigned getLineCount() const { return 1; } virtual unsigned getLineSize(unsigned Index) const { return Tok.TokenLength - 2; // Should be in sync with getLine } virtual unsigned getLineLengthAfterSplit(unsigned LineIndex, unsigned TailOffset) const { return getDecorationLength() + getLine().size() - TailOffset; } virtual Split getSplit(unsigned LineIndex, unsigned TailOffset, unsigned ColumnLimit) const { StringRef Text = getLine().substr(TailOffset); if (ColumnLimit <= getDecorationLength()) return Split(StringRef::npos, 0); unsigned MaxSplit = ColumnLimit - getDecorationLength(); assert(MaxSplit < Text.size()); StringRef::size_type SpaceOffset = Text.rfind(' ', MaxSplit); if (SpaceOffset != StringRef::npos && SpaceOffset != 0) return Split(SpaceOffset + 1, 0); StringRef::size_type SlashOffset = Text.rfind('/', MaxSplit); if (SlashOffset != StringRef::npos && SlashOffset != 0) return Split(SlashOffset + 1, 0); StringRef::size_type SplitPoint = getStartOfCharacter(Text, MaxSplit); if (SplitPoint != StringRef::npos && SplitPoint > 1) // Do not split at 0. return Split(SplitPoint, 0); return Split(StringRef::npos, 0); } virtual void insertBreak(unsigned LineIndex, unsigned TailOffset, Split Split, bool InPPDirective, WhitespaceManager &Whitespaces) { unsigned WhitespaceStartColumn = StartColumn + Split.first + 2; Whitespaces.breakToken(Tok, 1 + TailOffset + Split.first, Split.second, "\"", "\"", InPPDirective, StartColumn, WhitespaceStartColumn); } private: StringRef getLine() const { // Get string without quotes. // FIXME: Handle string prefixes. return TokenText.substr(1, TokenText.size() - 2); } unsigned getDecorationLength() const { return StartColumn + 2; } static StringRef::size_type getStartOfCharacter(StringRef Text, StringRef::size_type Offset) { StringRef::size_type NextEscape = Text.find('\\'); while (NextEscape != StringRef::npos && NextEscape < Offset) { StringRef::size_type SequenceLength = getEscapeSequenceLength(Text.substr(NextEscape)); if (Offset < NextEscape + SequenceLength) return NextEscape; NextEscape = Text.find('\\', NextEscape + SequenceLength); } return Offset; } static unsigned getEscapeSequenceLength(StringRef Text) { assert(Text[0] == '\\'); if (Text.size() < 2) return 1; switch (Text[1]) { case 'u': return 6; case 'U': return 10; case 'x': return getHexLength(Text); default: if (Text[1] >= '0' && Text[1] <= '7') return getOctalLength(Text); return 2; } } static unsigned getHexLength(StringRef Text) { unsigned I = 2; // Point after '\x'. while (I < Text.size() && ((Text[I] >= '0' && Text[I] <= '9') || (Text[I] >= 'a' && Text[I] <= 'f') || (Text[I] >= 'A' && Text[I] <= 'F'))) { ++I; } return I; } static unsigned getOctalLength(StringRef Text) { unsigned I = 1; while (I < Text.size() && I < 4 && (Text[I] >= '0' && Text[I] <= '7')) { ++I; } return I; } }; class BreakableComment : public BreakableToken { public: virtual unsigned getLineSize(unsigned Index) const { return getLine(Index).size(); } virtual unsigned getLineCount() const { return Lines.size(); } virtual unsigned getLineLengthAfterSplit(unsigned LineIndex, unsigned TailOffset) const { return getContentStartColumn(LineIndex, TailOffset) + getLine(LineIndex).size() - TailOffset; } virtual Split getSplit(unsigned LineIndex, unsigned TailOffset, unsigned ColumnLimit) const; virtual void insertBreak(unsigned LineIndex, unsigned TailOffset, Split Split, bool InPPDirective, WhitespaceManager &Whitespaces); protected: BreakableComment(const SourceManager &SourceMgr, const FormatToken &Tok, unsigned StartColumn) : BreakableToken(SourceMgr, Tok, StartColumn) {} // Get comment lines without /* */, common prefix and trailing whitespace. // Last line is not trimmed, as it is terminated by */, so its trailing // whitespace is not really trailing. StringRef getLine(unsigned Index) const { return Index < Lines.size() - 1 ? Lines[Index].rtrim() : Lines[Index]; } unsigned getContentStartColumn(unsigned LineIndex, unsigned TailOffset) const { return (TailOffset == 0 && LineIndex == 0) ? StartColumn : IndentAtLineBreak + Decoration.size(); } unsigned IndentAtLineBreak; StringRef Decoration; SmallVector Lines; }; class BreakableBlockComment : public BreakableComment { public: BreakableBlockComment(const SourceManager &SourceMgr, const AnnotatedToken &Token, unsigned StartColumn); void alignLines(WhitespaceManager &Whitespaces); virtual unsigned getLineLengthAfterSplit(unsigned LineIndex, unsigned TailOffset) const { return BreakableComment::getLineLengthAfterSplit(LineIndex, TailOffset) + (LineIndex + 1 < Lines.size() ? 0 : 2); } virtual void trimLine(unsigned LineIndex, unsigned TailOffset, unsigned InPPDirective, WhitespaceManager &Whitespaces); private: unsigned OriginalStartColumn; unsigned CommonPrefixLength; }; class BreakableLineComment : public BreakableComment { public: BreakableLineComment(const SourceManager &SourceMgr, const AnnotatedToken &Token, unsigned StartColumn); private: static StringRef getLineCommentPrefix(StringRef Comment); }; } // namespace format } // namespace clang #endif // LLVM_CLANG_FORMAT_BREAKABLETOKEN_H