blob: a75c592bfeda6b574afc17641c9da452e4d75382 [file] [log] [blame]
//===--- WhitespaceManager.cpp - 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 This file implements WhitespaceManager class.
///
//===----------------------------------------------------------------------===//
#include "WhitespaceManager.h"
#include "llvm/ADT/STLExtras.h"
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));
}
}
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));
}
}
const tooling::Replacements &WhitespaceManager::generateReplacements() {
alignComments();
alignEscapedNewlines();
return Replaces;
}
void WhitespaceManager::addReplacement(const SourceLocation &SourceLoc,
unsigned ReplaceChars, StringRef Text) {
Replaces.insert(
tooling::Replacement(SourceMgr, SourceLoc, ReplaceChars, Text));
}
void WhitespaceManager::addUntouchableComment(unsigned Column) {
StoredToken Tok = StoredToken(SourceLocation(), 0, Column, Column, 0, 0);
Tok.Untouchable = true;
Comments.push_back(Tok);
}
std::string WhitespaceManager::getNewLineText(unsigned NewLines,
unsigned Spaces) {
return std::string(NewLines, '\n') + std::string(Spaces, ' ');
}
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;
}
}
return NewLineText + std::string(Spaces, ' ');
}
void WhitespaceManager::alignComments() {
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);
}
}
alignComments(Start, Comments.end(), MinColumn);
Comments.clear();
}
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));
}
++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;
}
} else {
MinColumn = Style.ColumnLimit;
}
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);
}
EscapedNewlines.clear();
}
void WhitespaceManager::storeReplacement(SourceLocation Loc, unsigned Length,
const std::string Text) {
// Don't create a replacement, if it does not change anything.
if (StringRef(SourceMgr.getCharacterData(Loc), Length) == Text)
return;
Replaces.insert(tooling::Replacement(SourceMgr, Loc, Length, Text));
}
} // namespace format
} // namespace clang