summaryrefslogtreecommitdiffstats
path: root/lib/Format/WhitespaceManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Format/WhitespaceManager.cpp')
-rw-r--r--lib/Format/WhitespaceManager.cpp413
1 files changed, 261 insertions, 152 deletions
diff --git a/lib/Format/WhitespaceManager.cpp b/lib/Format/WhitespaceManager.cpp
index a75c592..26a8d41e 100644
--- a/lib/Format/WhitespaceManager.cpp
+++ b/lib/Format/WhitespaceManager.cpp
@@ -18,193 +18,302 @@
namespace clang {
namespace format {
-void WhitespaceManager::replaceWhitespace(const AnnotatedToken &Tok,
- unsigned NewLines, unsigned Spaces,
- unsigned WhitespaceStartColumn) {
- if (NewLines > 0)
- alignEscapedNewlines();
-
- // 2+ newlines mean an empty line separating logic scopes.
- if (NewLines >= 2)
- alignComments();
-
- // Align line comments if they are trailing or if they continue other
- // trailing comments.
- if (Tok.isTrailingComment()) {
- SourceLocation TokenEndLoc = Tok.FormatTok.getStartOfNonWhitespace()
- .getLocWithOffset(Tok.FormatTok.TokenLength);
- // Remove the comment's trailing whitespace.
- if (Tok.FormatTok.TrailingWhiteSpaceLength != 0)
- Replaces.insert(tooling::Replacement(
- SourceMgr, TokenEndLoc, Tok.FormatTok.TrailingWhiteSpaceLength, ""));
-
- bool LineExceedsColumnLimit =
- Spaces + WhitespaceStartColumn + Tok.FormatTok.TokenLength >
- Style.ColumnLimit;
- // Align comment with other comments.
- if ((Tok.Parent != NULL || !Comments.empty()) &&
- !LineExceedsColumnLimit) {
- unsigned MinColumn =
- NewLines > 0 ? Spaces : WhitespaceStartColumn + Spaces;
- unsigned MaxColumn = Style.ColumnLimit - Tok.FormatTok.TokenLength;
- Comments.push_back(StoredToken(
- Tok.FormatTok.WhiteSpaceStart, Tok.FormatTok.WhiteSpaceLength,
- MinColumn, MaxColumn, NewLines, Spaces));
- return;
- }
- }
-
- // If this line does not have a trailing comment, align the stored comments.
- if (Tok.Children.empty() && !Tok.isTrailingComment())
- alignComments();
-
- storeReplacement(Tok.FormatTok.WhiteSpaceStart,
- Tok.FormatTok.WhiteSpaceLength,
- getNewLineText(NewLines, Spaces));
-}
-
-void WhitespaceManager::replacePPWhitespace(const AnnotatedToken &Tok,
- unsigned NewLines, unsigned Spaces,
- unsigned WhitespaceStartColumn) {
- if (NewLines == 0) {
- replaceWhitespace(Tok, NewLines, Spaces, WhitespaceStartColumn);
- } else {
- // The earliest position for "\" is 2 after the last token.
- unsigned MinColumn = WhitespaceStartColumn + 2;
- unsigned MaxColumn = Style.ColumnLimit;
- EscapedNewlines.push_back(StoredToken(
- Tok.FormatTok.WhiteSpaceStart, Tok.FormatTok.WhiteSpaceLength,
- MinColumn, MaxColumn, NewLines, Spaces));
- }
+bool
+WhitespaceManager::Change::IsBeforeInFile::operator()(const Change &C1,
+ const Change &C2) const {
+ return SourceMgr.isBeforeInTranslationUnit(
+ C1.OriginalWhitespaceRange.getBegin(),
+ C2.OriginalWhitespaceRange.getBegin());
}
-void WhitespaceManager::breakToken(const FormatToken &Tok, unsigned Offset,
- unsigned ReplaceChars, StringRef Prefix,
- StringRef Postfix, bool InPPDirective,
- unsigned Spaces,
- unsigned WhitespaceStartColumn) {
- SourceLocation Location =
- Tok.getStartOfNonWhitespace().getLocWithOffset(Offset);
- if (InPPDirective) {
- // The earliest position for "\" is 2 after the last token.
- unsigned MinColumn = WhitespaceStartColumn + 2;
- unsigned MaxColumn = Style.ColumnLimit;
- StoredToken StoredTok = StoredToken(Location, ReplaceChars, MinColumn,
- MaxColumn, /*NewLines=*/ 1, Spaces);
- StoredTok.Prefix = Prefix;
- StoredTok.Postfix = Postfix;
- EscapedNewlines.push_back(StoredTok);
- } else {
- std::string ReplacementText =
- (Prefix + getNewLineText(1, Spaces) + Postfix).str();
- Replaces.insert(tooling::Replacement(SourceMgr, Location, ReplaceChars,
- ReplacementText));
- }
+WhitespaceManager::Change::Change(
+ bool CreateReplacement, const SourceRange &OriginalWhitespaceRange,
+ unsigned IndentLevel, unsigned Spaces, unsigned StartOfTokenColumn,
+ unsigned NewlinesBefore, StringRef PreviousLinePostfix,
+ StringRef CurrentLinePrefix, tok::TokenKind Kind, bool ContinuesPPDirective)
+ : CreateReplacement(CreateReplacement),
+ OriginalWhitespaceRange(OriginalWhitespaceRange),
+ StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore),
+ PreviousLinePostfix(PreviousLinePostfix),
+ CurrentLinePrefix(CurrentLinePrefix), Kind(Kind),
+ ContinuesPPDirective(ContinuesPPDirective), IndentLevel(IndentLevel),
+ Spaces(Spaces) {}
+
+void WhitespaceManager::reset() {
+ Changes.clear();
+ Replaces.clear();
}
-const tooling::Replacements &WhitespaceManager::generateReplacements() {
- alignComments();
- alignEscapedNewlines();
- return Replaces;
+void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines,
+ unsigned IndentLevel, unsigned Spaces,
+ unsigned StartOfTokenColumn,
+ bool InPPDirective) {
+ if (Tok.Finalized)
+ return;
+ Tok.Decision = (Newlines > 0) ? FD_Break : FD_Continue;
+ Changes.push_back(Change(true, Tok.WhitespaceRange, IndentLevel, Spaces,
+ StartOfTokenColumn, Newlines, "", "",
+ Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst));
}
-void WhitespaceManager::addReplacement(const SourceLocation &SourceLoc,
- unsigned ReplaceChars, StringRef Text) {
- Replaces.insert(
- tooling::Replacement(SourceMgr, SourceLoc, ReplaceChars, Text));
+void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
+ bool InPPDirective) {
+ if (Tok.Finalized)
+ return;
+ Changes.push_back(Change(false, Tok.WhitespaceRange, /*IndentLevel=*/0,
+ /*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore,
+ "", "", Tok.Tok.getKind(),
+ InPPDirective && !Tok.IsFirst));
}
-void WhitespaceManager::addUntouchableComment(unsigned Column) {
- StoredToken Tok = StoredToken(SourceLocation(), 0, Column, Column, 0, 0);
- Tok.Untouchable = true;
- Comments.push_back(Tok);
+void WhitespaceManager::replaceWhitespaceInToken(
+ const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,
+ StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,
+ unsigned Newlines, unsigned IndentLevel, unsigned Spaces) {
+ if (Tok.Finalized)
+ return;
+ Changes.push_back(Change(
+ true, SourceRange(Tok.getStartOfNonWhitespace().getLocWithOffset(Offset),
+ Tok.getStartOfNonWhitespace().getLocWithOffset(
+ Offset + ReplaceChars)),
+ IndentLevel, Spaces, Spaces, Newlines, PreviousPostfix, CurrentPrefix,
+ // If we don't add a newline this change doesn't start a comment. Thus,
+ // when we align line comments, we don't need to treat this change as one.
+ // FIXME: We still need to take this change in account to properly
+ // calculate the new length of the comment and to calculate the changes
+ // for which to do the alignment when aligning comments.
+ Tok.Type == TT_LineComment && Newlines > 0 ? tok::comment : tok::unknown,
+ InPPDirective && !Tok.IsFirst));
}
-std::string WhitespaceManager::getNewLineText(unsigned NewLines,
- unsigned Spaces) {
- return std::string(NewLines, '\n') + std::string(Spaces, ' ');
+const tooling::Replacements &WhitespaceManager::generateReplacements() {
+ if (Changes.empty())
+ return Replaces;
+
+ std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr));
+ calculateLineBreakInformation();
+ alignTrailingComments();
+ alignEscapedNewlines();
+ generateChanges();
+
+ return Replaces;
}
-std::string WhitespaceManager::getNewLineText(unsigned NewLines,
- unsigned Spaces,
- unsigned WhitespaceStartColumn,
- unsigned EscapedNewlineColumn) {
- std::string NewLineText;
- if (NewLines > 0) {
- unsigned Offset =
- std::min<int>(EscapedNewlineColumn - 1, WhitespaceStartColumn);
- for (unsigned i = 0; i < NewLines; ++i) {
- NewLineText += std::string(EscapedNewlineColumn - Offset - 1, ' ');
- NewLineText += "\\\n";
- Offset = 0;
- }
+void WhitespaceManager::calculateLineBreakInformation() {
+ Changes[0].PreviousEndOfTokenColumn = 0;
+ for (unsigned i = 1, e = Changes.size(); i != e; ++i) {
+ unsigned OriginalWhitespaceStart =
+ SourceMgr.getFileOffset(Changes[i].OriginalWhitespaceRange.getBegin());
+ unsigned PreviousOriginalWhitespaceEnd = SourceMgr.getFileOffset(
+ Changes[i - 1].OriginalWhitespaceRange.getEnd());
+ Changes[i - 1].TokenLength = OriginalWhitespaceStart -
+ PreviousOriginalWhitespaceEnd +
+ Changes[i].PreviousLinePostfix.size() +
+ Changes[i - 1].CurrentLinePrefix.size();
+
+ Changes[i].PreviousEndOfTokenColumn =
+ Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength;
+
+ Changes[i - 1].IsTrailingComment =
+ (Changes[i].NewlinesBefore > 0 || Changes[i].Kind == tok::eof) &&
+ Changes[i - 1].Kind == tok::comment;
}
- return NewLineText + std::string(Spaces, ' ');
+ // FIXME: The last token is currently not always an eof token; in those
+ // cases, setting TokenLength of the last token to 0 is wrong.
+ Changes.back().TokenLength = 0;
+ Changes.back().IsTrailingComment = Changes.back().Kind == tok::comment;
}
-void WhitespaceManager::alignComments() {
+void WhitespaceManager::alignTrailingComments() {
unsigned MinColumn = 0;
unsigned MaxColumn = UINT_MAX;
- token_iterator Start = Comments.begin();
- for (token_iterator I = Start, E = Comments.end(); I != E; ++I) {
- if (I->MinColumn > MaxColumn || I->MaxColumn < MinColumn) {
- alignComments(Start, I, MinColumn);
- MinColumn = I->MinColumn;
- MaxColumn = I->MaxColumn;
- Start = I;
- } else {
- MinColumn = std::max(MinColumn, I->MinColumn);
- MaxColumn = std::min(MaxColumn, I->MaxColumn);
+ unsigned StartOfSequence = 0;
+ bool BreakBeforeNext = false;
+ unsigned Newlines = 0;
+ for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
+ unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
+ // FIXME: Correctly handle ChangeMaxColumn in PP directives.
+ unsigned ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength;
+ Newlines += Changes[i].NewlinesBefore;
+ if (Changes[i].IsTrailingComment) {
+ // If this comment follows an } in column 0, it probably documents the
+ // closing of a namespace and we don't want to align it.
+ bool FollowsRBraceInColumn0 = i > 0 && Changes[i].NewlinesBefore == 0 &&
+ Changes[i - 1].Kind == tok::r_brace &&
+ Changes[i - 1].StartOfTokenColumn == 0;
+ bool WasAlignedWithStartOfNextLine = false;
+ if (Changes[i].NewlinesBefore == 1) { // A comment on its own line.
+ for (unsigned j = i + 1; j != e; ++j) {
+ if (Changes[j].Kind != tok::comment) { // Skip over comments.
+ // The start of the next token was previously aligned with the
+ // start of this comment.
+ WasAlignedWithStartOfNextLine =
+ (SourceMgr.getSpellingColumnNumber(
+ Changes[i].OriginalWhitespaceRange.getEnd()) ==
+ SourceMgr.getSpellingColumnNumber(
+ Changes[j].OriginalWhitespaceRange.getEnd()));
+ break;
+ }
+ }
+ }
+ if (!Style.AlignTrailingComments || FollowsRBraceInColumn0) {
+ alignTrailingComments(StartOfSequence, i, MinColumn);
+ MinColumn = ChangeMinColumn;
+ MaxColumn = ChangeMinColumn;
+ StartOfSequence = i;
+ } else if (BreakBeforeNext || Newlines > 1 ||
+ (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
+ // Break the comment sequence if the previous line did not end
+ // in a trailing comment.
+ (Changes[i].NewlinesBefore == 1 && i > 0 &&
+ !Changes[i - 1].IsTrailingComment) ||
+ WasAlignedWithStartOfNextLine) {
+ alignTrailingComments(StartOfSequence, i, MinColumn);
+ MinColumn = ChangeMinColumn;
+ MaxColumn = ChangeMaxColumn;
+ StartOfSequence = i;
+ } else {
+ MinColumn = std::max(MinColumn, ChangeMinColumn);
+ MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
+ }
+ BreakBeforeNext =
+ (i == 0) || (Changes[i].NewlinesBefore > 1) ||
+ // Never start a sequence with a comment at the beginning of
+ // the line.
+ (Changes[i].NewlinesBefore == 1 && StartOfSequence == i);
+ Newlines = 0;
}
}
- alignComments(Start, Comments.end(), MinColumn);
- Comments.clear();
+ alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
}
-void WhitespaceManager::alignComments(token_iterator I, token_iterator E,
- unsigned Column) {
- while (I != E) {
- if (!I->Untouchable) {
- unsigned Spaces = I->Spaces + Column - I->MinColumn;
- storeReplacement(I->ReplacementLoc, I->ReplacementLength,
- getNewLineText(I->NewLines, Spaces));
+void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,
+ unsigned Column) {
+ for (unsigned i = Start; i != End; ++i) {
+ if (Changes[i].IsTrailingComment) {
+ assert(Column >= Changes[i].StartOfTokenColumn);
+ Changes[i].Spaces += Column - Changes[i].StartOfTokenColumn;
+ Changes[i].StartOfTokenColumn = Column;
}
- ++I;
}
}
void WhitespaceManager::alignEscapedNewlines() {
- unsigned MinColumn;
- if (Style.AlignEscapedNewlinesLeft) {
- MinColumn = 0;
- for (token_iterator I = EscapedNewlines.begin(), E = EscapedNewlines.end();
- I != E; ++I) {
- if (I->MinColumn > MinColumn)
- MinColumn = I->MinColumn;
+ unsigned MaxEndOfLine =
+ Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
+ unsigned StartOfMacro = 0;
+ for (unsigned i = 1, e = Changes.size(); i < e; ++i) {
+ Change &C = Changes[i];
+ if (C.NewlinesBefore > 0) {
+ if (C.ContinuesPPDirective) {
+ MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine);
+ } else {
+ alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);
+ MaxEndOfLine = Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
+ StartOfMacro = i;
+ }
}
- } else {
- MinColumn = Style.ColumnLimit;
}
+ alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);
+}
- for (token_iterator I = EscapedNewlines.begin(), E = EscapedNewlines.end();
- I != E; ++I) {
- // I->MinColumn - 2 is the end of the previous token (i.e. the
- // WhitespaceStartColumn).
- storeReplacement(
- I->ReplacementLoc, I->ReplacementLength,
- I->Prefix + getNewLineText(I->NewLines, I->Spaces, I->MinColumn - 2,
- MinColumn) + I->Postfix);
+void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,
+ unsigned Column) {
+ for (unsigned i = Start; i < End; ++i) {
+ Change &C = Changes[i];
+ if (C.NewlinesBefore > 0) {
+ assert(C.ContinuesPPDirective);
+ if (C.PreviousEndOfTokenColumn + 1 > Column)
+ C.EscapedNewlineColumn = 0;
+ else
+ C.EscapedNewlineColumn = Column;
+ }
+ }
+}
+void WhitespaceManager::generateChanges() {
+ for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
+ const Change &C = Changes[i];
+ if (C.CreateReplacement) {
+ std::string ReplacementText = C.PreviousLinePostfix;
+ if (C.ContinuesPPDirective)
+ appendNewlineText(ReplacementText, C.NewlinesBefore,
+ C.PreviousEndOfTokenColumn, C.EscapedNewlineColumn);
+ else
+ appendNewlineText(ReplacementText, C.NewlinesBefore);
+ appendIndentText(ReplacementText, C.IndentLevel, C.Spaces,
+ C.StartOfTokenColumn - C.Spaces);
+ ReplacementText.append(C.CurrentLinePrefix);
+ storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
+ }
}
- EscapedNewlines.clear();
}
-void WhitespaceManager::storeReplacement(SourceLocation Loc, unsigned Length,
- const std::string Text) {
+void WhitespaceManager::storeReplacement(const SourceRange &Range,
+ StringRef Text) {
+ unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -
+ SourceMgr.getFileOffset(Range.getBegin());
// Don't create a replacement, if it does not change anything.
- if (StringRef(SourceMgr.getCharacterData(Loc), Length) == Text)
+ if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
+ WhitespaceLength) == Text)
return;
- Replaces.insert(tooling::Replacement(SourceMgr, Loc, Length, Text));
+ Replaces.insert(tooling::Replacement(
+ SourceMgr, CharSourceRange::getCharRange(Range), Text));
+}
+
+void WhitespaceManager::appendNewlineText(std::string &Text,
+ unsigned Newlines) {
+ for (unsigned i = 0; i < Newlines; ++i)
+ Text.append(UseCRLF ? "\r\n" : "\n");
+}
+
+void WhitespaceManager::appendNewlineText(std::string &Text, unsigned Newlines,
+ unsigned PreviousEndOfTokenColumn,
+ unsigned EscapedNewlineColumn) {
+ if (Newlines > 0) {
+ unsigned Offset =
+ std::min<int>(EscapedNewlineColumn - 1, PreviousEndOfTokenColumn);
+ for (unsigned i = 0; i < Newlines; ++i) {
+ Text.append(std::string(EscapedNewlineColumn - Offset - 1, ' '));
+ Text.append(UseCRLF ? "\\\r\n" : "\\\n");
+ Offset = 0;
+ }
+ }
+}
+
+void WhitespaceManager::appendIndentText(std::string &Text,
+ unsigned IndentLevel, unsigned Spaces,
+ unsigned WhitespaceStartColumn) {
+ switch (Style.UseTab) {
+ case FormatStyle::UT_Never:
+ Text.append(std::string(Spaces, ' '));
+ break;
+ case FormatStyle::UT_Always: {
+ unsigned FirstTabWidth =
+ Style.TabWidth - WhitespaceStartColumn % Style.TabWidth;
+ // Indent with tabs only when there's at least one full tab.
+ if (FirstTabWidth + Style.TabWidth <= Spaces) {
+ Spaces -= FirstTabWidth;
+ Text.append("\t");
+ }
+ Text.append(std::string(Spaces / Style.TabWidth, '\t'));
+ Text.append(std::string(Spaces % Style.TabWidth, ' '));
+ break;
+ }
+ case FormatStyle::UT_ForIndentation:
+ if (WhitespaceStartColumn == 0) {
+ unsigned Indentation = IndentLevel * Style.IndentWidth;
+ // This happens, e.g. when a line in a block comment is indented less than
+ // the first one.
+ if (Indentation > Spaces)
+ Indentation = Spaces;
+ unsigned Tabs = Indentation / Style.TabWidth;
+ Text.append(std::string(Tabs, '\t'));
+ Spaces -= Tabs * Style.TabWidth;
+ }
+ Text.append(std::string(Spaces, ' '));
+ break;
+ }
}
} // namespace format
OpenPOWER on IntegriCloud