Files
swift-mirror/lib/IDE/Formatting.cpp
Keith Smiley 2d91af2077 IDE: Fix formatting of closing square brackets
Previously closing square brackets would be caught in the logic for
elements in collections, indenting them to the same level. Now they are
indented with the normal non-sibling logic, which indents them
correctly.
2018-07-25 20:27:32 -07:00

1010 lines
34 KiB
C++

//===--- Formatting.cpp ---------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/AST/ASTWalker.h"
#include "swift/IDE/SourceEntityWalker.h"
#include "swift/Parse/Parser.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Basic/SourceManager.h"
#include "swift/IDE/Formatting.h"
#include "swift/Subsystems.h"
using namespace swift;
using namespace ide;
namespace {
struct SiblingAlignInfo {
SourceLoc Loc;
bool ExtraIndent;
};
struct TokenInfo {
const Token *StartOfLineTarget;
const Token *StartOfLineBeforeTarget;
TokenInfo(const Token *StartOfLineTarget,
const Token *StartOfLineBeforeTarget) :
StartOfLineTarget(StartOfLineTarget),
StartOfLineBeforeTarget(StartOfLineBeforeTarget) {}
TokenInfo() : TokenInfo(nullptr, nullptr) {}
operator bool() { return StartOfLineTarget && StartOfLineBeforeTarget; }
};
using StringBuilder = llvm::SmallString<64>;
static SourceLoc getVarDeclInitEnd(VarDecl *VD) {
return VD->getBracesRange().isValid()
? VD->getBracesRange().End
: VD->getParentInitializer() &&
VD->getParentInitializer()->getEndLoc().isValid()
? VD->getParentInitializer()->getEndLoc()
: SourceLoc();
}
class FormatContext {
SourceManager &SM;
std::vector<swift::ASTWalker::ParentTy>& Stack;
std::vector<swift::ASTWalker::ParentTy>::reverse_iterator Cursor;
swift::ASTWalker::ParentTy Start;
swift::ASTWalker::ParentTy End;
bool InDocCommentBlock;
bool InCommentLine;
bool InStringLiteral;
SiblingAlignInfo SiblingInfo;
public:
FormatContext(SourceManager &SM,
std::vector<swift::ASTWalker::ParentTy>& Stack,
swift::ASTWalker::ParentTy Start = swift::ASTWalker::ParentTy(),
swift::ASTWalker::ParentTy End = swift::ASTWalker::ParentTy(),
bool InDocCommentBlock = false,
bool InCommentLine = false,
bool InStringLiteral = false,
SiblingAlignInfo SiblingInfo = SiblingAlignInfo())
:SM(SM), Stack(Stack), Cursor(Stack.rbegin()), Start(Start), End(End),
InDocCommentBlock(InDocCommentBlock), InCommentLine(InCommentLine),
InStringLiteral(InStringLiteral),
SiblingInfo(SiblingInfo) { }
FormatContext parent() {
assert(Cursor != Stack.rend());
FormatContext Parent(*this);
++Parent.Cursor;
return Parent;
}
bool IsInDocCommentBlock() {
return InDocCommentBlock;
}
bool IsInCommentLine() {
return InCommentLine;
}
bool IsInStringLiteral() const {
return InStringLiteral;
}
bool isSwitchControlStmt(unsigned LineIndex, StringRef Text) {
if (!isSwitchContext())
return false;
StringRef LineText = swift::ide::getTextForLine(LineIndex, Text, /*Trim*/true);
return LineText.startswith("break") || LineText.startswith("continue") ||
LineText.startswith("return") || LineText.startswith("fallthrough");
}
void padToSiblingColumn(StringBuilder &Builder,
const CodeFormatOptions &FmtOptions) {
assert(SiblingInfo.Loc.isValid() && "No sibling to align with.");
CharSourceRange Range(SM, Lexer::getLocForStartOfLine(SM, SiblingInfo.Loc),
SiblingInfo.Loc);
unsigned SpaceLength = 0;
unsigned TabLength = 0;
// Calculating space length
for (auto C: Range.str()) {
if (C == '\t')
SpaceLength += FmtOptions.TabWidth;
else
SpaceLength += 1;
}
// If we are using tabs, calculating the number of tabs and spaces we need
// to insert.
if (FmtOptions.UseTabs) {
TabLength = SpaceLength / FmtOptions.TabWidth;
SpaceLength = SpaceLength % FmtOptions.TabWidth;
}
Builder.append(TabLength, '\t');
Builder.append(SpaceLength, ' ');
}
bool HasSibling() {
return SiblingInfo.Loc.isValid();
}
bool needExtraIndentationForSibling() {
return SiblingInfo.ExtraIndent;
}
std::pair<unsigned, unsigned> lineAndColumn() {
if (Cursor == Stack.rend())
return std::make_pair(0, 0);
if (Stmt *S = Cursor->getAsStmt()) {
SourceLoc SL = S->getStartLoc();
return SM.getLineAndColumn(SL);
}
if (Decl *D = Cursor->getAsDecl()) {
SourceLoc SL = D->getStartLoc();
// FIXME: put the attributes into forward source order so we don't need
// to iterate through them.
for (auto *Attr : D->getAttrs()) {
SourceLoc AttrLoc = Attr->getRangeWithAt().Start;
if (AttrLoc.isValid() && SM.isBeforeInBuffer(AttrLoc, SL))
SL = AttrLoc;
}
return SM.getLineAndColumn(SL);
}
if (Expr *E = Cursor->getAsExpr()) {
SourceLoc SL = E->getStartLoc();
return SM.getLineAndColumn(SL);
}
return std::make_pair(0, 0);
}
template <class T>
bool isStmtContext() {
if (Cursor == Stack.rend())
return false;
Stmt *ContextStmt = Cursor->getAsStmt();
return ContextStmt && isa<T>(ContextStmt);
}
bool isBraceContext() {
return isStmtContext<BraceStmt>();
}
bool isImplicitBraceContext() {
// If we're directly at the top, it's implicit.
if (Cursor == Stack.rend())
return true;
if (!isBraceContext())
return false;
auto Parent = parent();
// If the parent is directly at the top, it's implicit.
if (Parent.Cursor == Stack.rend())
return true;
// If we're within a case body, it's implicit.
// For example:
// case ...:
// case body is implicitly wrapped in a brace statement
if (Parent.isCaseContext())
return true;
return false;
}
bool isCaseContext() {
return isStmtContext<CaseStmt>();
}
bool isSwitchContext() {
return isStmtContext<SwitchStmt>();
}
std::pair<unsigned, unsigned> indentLineAndColumn() {
if (Cursor == Stack.rend())
return std::make_pair(0, 0);
// Get the line and indent position for this context.
auto LineAndColumn = lineAndColumn();
auto SavedCursor = Cursor;
// Walk up the context stack to find the topmost applicable context.
while (++Cursor != Stack.rend()) {
auto ParentLineAndColumn = lineAndColumn();
if (ParentLineAndColumn.second == 0)
break;
if (ParentLineAndColumn.first != LineAndColumn.first) {
// The start line is not the same, see if this is at the 'else' clause.
if (auto *If = dyn_cast_or_null<IfStmt>(Cursor->getAsStmt())) {
SourceLoc ElseLoc = If->getElseLoc();
// If we're at 'else', take the indent of 'if' and continue.
if (ElseLoc.isValid() &&
LineAndColumn.first == SM.getLineAndColumn(ElseLoc).first) {
LineAndColumn = ParentLineAndColumn;
continue;
}
// If we are at conditions, take the indent of 'if' and continue.
for (auto Cond : If->getCond()) {
if (LineAndColumn.first == SM.getLineNumber(Cond.getEndLoc())) {
LineAndColumn = ParentLineAndColumn;
continue;
}
}
}
// No extra indentation level for getters without explicit names.
// e.g.
// public var someValue: Int {
// return 0; <- No indentation added because of the getter.
// }
if (auto VD = dyn_cast_or_null<VarDecl>(Cursor->getAsDecl())) {
if (auto Getter = VD->getGetter()) {
if (!Getter->isImplicit() &&
Getter->getAccessorKeywordLoc().isInvalid()) {
LineAndColumn = ParentLineAndColumn;
continue;
}
}
}
// Align with Func start instead of with param decls.
if (auto *FD = dyn_cast_or_null<AbstractFunctionDecl>(Cursor->getAsDecl())) {
if (LineAndColumn.first <= SM.getLineNumber(FD->getSignatureSourceRange().End)) {
LineAndColumn = ParentLineAndColumn;
continue;
}
}
// Break out if the line is no longer the same.
break;
}
LineAndColumn.second = ParentLineAndColumn.second;
}
Cursor = SavedCursor;
return LineAndColumn;
}
bool exprEndAtLine(Expr *E, unsigned Line) {
return E->getEndLoc().isValid() && SM.getLineNumber(E->getEndLoc()) == Line;
};
bool shouldAddIndentForLine(unsigned Line, TokenInfo TInfo,
const CodeFormatOptions &FmtOptions) {
if (Cursor == Stack.rend())
return false;
if (TInfo) {
if (TInfo.StartOfLineTarget->getKind() == tok::l_brace &&
isKeywordPossibleDeclStart(*TInfo.StartOfLineBeforeTarget) &&
TInfo.StartOfLineBeforeTarget->isKeyword())
return false;
}
// Handle switch / case, indent unless at a case label.
if (auto *Case = dyn_cast_or_null<CaseStmt>(Cursor->getAsStmt())) {
auto LabelItems = Case->getCaseLabelItems();
SourceLoc Loc;
if (!LabelItems.empty())
Loc = LabelItems.back().getPattern()->getLoc();
if (Loc.isValid())
return Line > SM.getLineAndColumn(Loc).first;
return true;
}
if (isSwitchContext()) {
// If we're at the start of a case label, don't add indent.
// For example:
// switch ... {
// case xyz: <-- No indent here, should be at same level as switch.
Stmt *AtStmtStart = Start.getAsStmt();
if (AtStmtStart && isa<CaseStmt>(AtStmtStart))
return FmtOptions.IndentSwitchCase;
// If we're at the open brace of the switch, don't add an indent.
// For example:
// switch ...
// { <-- No indent here, open brace should be at same level as switch.
auto *S = cast<SwitchStmt>(Cursor->getAsStmt());
if (SM.getLineAndColumn(S->getLBraceLoc()).first == Line)
return false;
if (IsInCommentLine()) {
for (auto Case : S->getCases()) {
// switch ...
// {
// // case comment <-- No indent here.
// case 0:
if (SM.getLineAndColumn(Case->swift::Stmt::getStartLoc()).first == Line + 1)
return FmtOptions.IndentSwitchCase;
}
}
}
// If we're within an implicit brace context, don't add indent.
if (isImplicitBraceContext())
return false;
// If we're at the open brace of a no-name getter, don't add an indent.
// For example:
// public var someValue: Int
// { <- We add no indentation here.
// return 0
// }
if (auto FD = dyn_cast_or_null<AccessorDecl>(Start.getAsDecl())) {
if (FD->isGetter() && FD->getAccessorKeywordLoc().isInvalid()) {
if (SM.getLineNumber(FD->getBody()->getLBraceLoc()) == Line)
return false;
}
}
// If we're at the beginning of a brace on a separate line in the context
// of anything other than BraceStmt, don't add an indent.
// For example:
// func foo()
// { <-- No indent here, open brace should be at same level as func.
Stmt *AtStmtStart = Start.getAsStmt();
if (AtStmtStart && isa<BraceStmt>(AtStmtStart) && !isBraceContext())
return false;
// If we're at the end of a brace on a separate line in the context
// of anything other than BraceStmt, don't add an indent.
// For example:
if (Stmt *AtStmtEnd = End.getAsStmt()) {
if (!isBraceContext()) {
// func foo() {
// } <-- No indent here, close brace should be at same level as func.
if (isa<BraceStmt>(AtStmtEnd))
return false;
// do {
// }
// catch {
// } <-- No indent here, close brace should be at same level as do.
// catch {
// }
if (isa<CatchStmt>(AtStmtEnd))
return false;
}
}
// If we're at the open brace of a NominalTypeDecl or ExtensionDecl,
// don't add an indent.
// For example:
// class Foo
// { <-- No indent here, open brace should be at same level as class.
auto *NTD = dyn_cast_or_null<NominalTypeDecl>(Cursor->getAsDecl());
if (NTD && SM.getLineAndColumn(NTD->getBraces().Start).first == Line)
return false;
auto *ETD = dyn_cast_or_null<ExtensionDecl>(Cursor->getAsDecl());
if (ETD && SM.getLineAndColumn(ETD->getBraces().Start).first == Line)
return false;
// If we are at the start of a trailing closure, do not add indentation.
// For example:
// foo(1)
// { <-- No indent here.
auto *TE = dyn_cast_or_null<TupleExpr>(Cursor->getAsExpr());
if (TE && TE->hasTrailingClosure() &&
SM.getLineNumber(TE->getElements().back()->getStartLoc()) == Line) {
return false;
}
// If we're in an IfStmt and at the 'else', don't add an indent.
IfStmt *If = dyn_cast_or_null<IfStmt>(Cursor->getAsStmt());
if (If && If->getElseLoc().isValid() &&
SM.getLineAndColumn(If->getElseLoc()).first == Line)
return false;
// If we're in a DoCatchStmt and at a 'catch', don't add an indent.
if (auto *DoCatchS = dyn_cast_or_null<DoCatchStmt>(Cursor->getAsStmt())) {
for (CatchStmt *CatchS : DoCatchS->getCatches()) {
SourceLoc Loc = CatchS->getCatchLoc();
if (Loc.isValid() && SM.getLineAndColumn(Loc).first == Line)
return false;
}
}
// If we're at the end of a closure, paren or tuple expr, and the context
// is a paren/tuple expr ending with that sub expression, and it ends on the
// same line, don't add an indent.
// For example:
// foo(x, {
// }) <-- No indent here, the paren expr for the call ends on the same line.
Expr *AtExprEnd = End.getAsExpr();
if (AtExprEnd && (isa<ClosureExpr>(AtExprEnd) ||
isa<ParenExpr>(AtExprEnd) ||
isa<TupleExpr>(AtExprEnd) ||
isa<CaptureListExpr>(AtExprEnd))) {
if (auto *Paren = dyn_cast_or_null<ParenExpr>(Cursor->getAsExpr())) {
auto *SubExpr = Paren->getSubExpr();
if (SubExpr && SubExpr == AtExprEnd &&
SM.getLineAndColumn(Paren->getEndLoc()).first == Line)
return false;
} else if (auto *Tuple = dyn_cast_or_null<TupleExpr>(Cursor->getAsExpr())) {
auto SubExprs = Tuple->getElements();
if (!SubExprs.empty() && SubExprs.back() == AtExprEnd &&
SM.getLineAndColumn(Tuple->getEndLoc()).first == Line) {
return false;
}
} else if (auto *VD = dyn_cast_or_null<VarDecl>(Cursor->getAsDecl())) {
SourceLoc Loc = getVarDeclInitEnd(VD);
if (Loc.isValid() && SM.getLineNumber(Loc) == Line) {
return false;
}
} else if (auto *Seq = dyn_cast_or_null<SequenceExpr>(Cursor->getAsExpr())) {
ArrayRef<Expr*> Elements = Seq->getElements();
if (Elements.size() == 3 &&
isa<AssignExpr>(Elements[1]) &&
SM.getLineAndColumn(Elements[2]->getEndLoc()).first == Line) {
return false;
}
}
}
// let msg = String([65, 108, 105, 103, 110].map { c in
// Character(UnicodeScalar(c))
// }) <--- No indentation here.
auto AtCursorExpr = Cursor->getAsExpr();
if (AtExprEnd && AtCursorExpr && (isa<ParenExpr>(AtCursorExpr) ||
isa<TupleExpr>(AtCursorExpr))) {
if (isa<CallExpr>(AtExprEnd)) {
if (exprEndAtLine(AtExprEnd, Line) &&
exprEndAtLine(AtCursorExpr, Line)) {
return false;
}
}
// foo(A: {
// ...
// }, B: { <--- No indentation here.
// ...
// })
if (auto *TE = dyn_cast<TupleExpr>(AtCursorExpr)) {
if (isa<ClosureExpr>(AtExprEnd) && exprEndAtLine(AtExprEnd, Line)) {
for (auto *ELE : TE->getElements()) {
if (exprEndAtLine(ELE, Line)) {
return false;
}
}
}
}
}
// Indent another level from the outer context by default.
return true;
}
};
class FormatWalker : public SourceEntityWalker {
using TokenIt = ArrayRef<Token>::iterator;
class SiblingCollector {
SourceLoc FoundSibling;
SourceManager &SM;
ArrayRef<Token> Tokens;
SourceLoc &TargetLoc;
TokenIt TI;
bool NeedExtraIndentation;
class SourceLocIterator
: public std::iterator<std::input_iterator_tag, SourceLoc>
{
TokenIt It;
public:
SourceLocIterator(TokenIt It) :It(It) {}
SourceLocIterator(const SourceLocIterator& mit) : It(mit.It) {}
SourceLocIterator& operator++() {++It; return *this;}
SourceLocIterator operator++(int) {
SourceLocIterator tmp(*this);
operator++();
return tmp;
}
bool operator==(const SourceLocIterator& rhs) {return It==rhs.It;}
bool operator!=(const SourceLocIterator& rhs) {return It!=rhs.It;}
SourceLoc operator*() {return It->getLoc();}
const SourceLoc operator*() const { return It->getLoc(); }
};
void adjustTokenIteratorToImmediateAfter(SourceLoc End) {
SourceLocIterator LocBegin(Tokens.begin());
SourceLocIterator LocEnd(Tokens.end());
auto Lower = std::lower_bound(LocBegin, LocEnd, End,
[&](SourceLoc L, SourceLoc R) {
return SM.isBeforeInBuffer(L, R);
});
if (*Lower == End) {
Lower ++;
}
TI = Tokens.begin();
std::advance(TI, std::distance(LocBegin, Lower));
}
bool isImmediateAfterSeparator(SourceLoc End, tok Separator) {
adjustTokenIteratorToImmediateAfter(End);
if (TI == Tokens.end() || TI->getKind() != Separator)
return false;
auto SeparatorLoc = TI->getLoc();
TI ++;
if (TI == Tokens.end())
return false;
auto NextLoc = TI->getLoc();
return SM.isBeforeInBuffer(SeparatorLoc, TargetLoc) &&
!SM.isBeforeInBuffer(NextLoc, TargetLoc);
}
bool isTargetImmediateAfter(SourceLoc Loc) {
adjustTokenIteratorToImmediateAfter(Loc);
// Make sure target loc is after loc
return SM.isBeforeInBuffer(Loc, TargetLoc) &&
// Make sure immediate loc after loc is not before target loc.
!SM.isBeforeInBuffer(TI->getLoc(), TargetLoc);
}
bool sameLineWithTarget(SourceLoc Loc) {
return SM.getLineNumber(Loc) == SM.getLineNumber(TargetLoc);
}
public:
SiblingCollector(SourceManager &SM, ArrayRef<Token> Tokens,
SourceLoc &TargetLoc) : SM(SM), Tokens(Tokens),
TargetLoc(TargetLoc), TI(Tokens.begin()),
NeedExtraIndentation(false) {}
void collect(ASTNode Node) {
if (FoundSibling.isValid())
return;
SourceLoc PrevLoc;
auto FindAlignLoc = [&](SourceLoc Loc) {
if (PrevLoc.isValid() && Loc.isValid() &&
SM.getLineNumber(PrevLoc) == SM.getLineNumber(Loc))
return PrevLoc;
return PrevLoc = Loc;
};
auto addPair = [&](SourceLoc EndLoc, SourceLoc AlignLoc, tok Separator) {
if (isImmediateAfterSeparator(EndLoc, Separator))
FoundSibling = AlignLoc;
};
if (auto AE = dyn_cast_or_null<ApplyExpr>(Node.dyn_cast<Expr *>())) {
collect(AE->getArg());
return;
}
if (auto PE = dyn_cast_or_null<ParenExpr>(Node.dyn_cast<Expr *>())) {
if (auto Sub = PE->getSubExpr()) {
addPair(Sub->getEndLoc(), FindAlignLoc(Sub->getStartLoc()),
tok::comma);
}
}
// Tuple elements are siblings.
if (auto TE = dyn_cast_or_null<TupleExpr>(Node.dyn_cast<Expr *>())) {
// Trailing closures are not considered siblings to other args.
unsigned EndAdjust = TE->hasTrailingClosure() ? 1 : 0;
for (unsigned I = 0, N = TE->getNumElements() - EndAdjust; I < N; I++) {
auto EleStart = TE->getElementNameLoc(I);
if (EleStart.isInvalid()) {
EleStart = TE->getElement(I)->getStartLoc();
}
addPair(TE->getElement(I)->getEndLoc(), FindAlignLoc(EleStart), tok::comma);
}
}
if (auto AFD = dyn_cast_or_null<AbstractFunctionDecl>(Node.dyn_cast<Decl*>())) {
// Function parameters are siblings.
for (auto *param : *AFD->getParameters()) {
addPair(param->getEndLoc(), FindAlignLoc(param->getStartLoc()),
tok::comma);
}
}
// Array/Dictionary elements are siblings to align with each other.
if (auto AE = dyn_cast_or_null<CollectionExpr>(Node.dyn_cast<Expr *>())) {
// The following check ends-up creating too much indentation,
// for example:
// let something = [
// a
// ]
//
// Disabling the check gets us back to the Swift2.2 behavior:
// let something = [
// a
// ]
//
// FIXME: We are going to revisit the behavior and the indentation we
// want for dictionary/array literals.
//
#if 0
SourceLoc LBracketLoc = AE->getLBracketLoc();
if (isTargetImmediateAfter(LBracketLoc) &&
!sameLineWithTarget(LBracketLoc)) {
FoundSibling = LBracketLoc;
NeedExtraIndentation = true;
}
#endif
for (unsigned I = 0, N = AE->getNumElements(); I < N; I++) {
addPair(AE->getElement(I)->getEndLoc(),
FindAlignLoc(AE->getElement(I)->getStartLoc()), tok::comma);
}
}
// Case label items in a case statement are siblings.
if (auto CS = dyn_cast_or_null<CaseStmt>(Node.dyn_cast<Stmt *>())) {
for (const CaseLabelItem& Item : CS->getCaseLabelItems()) {
addPair(Item.getEndLoc(), FindAlignLoc(Item.getStartLoc()), tok::comma);
}
}
};
SiblingAlignInfo getSiblingInfo() {
return {FoundSibling, NeedExtraIndentation};
}
};
SourceFile &SF;
SourceManager &SM;
SourceLoc TargetLocation;
std::vector<swift::ASTWalker::ParentTy> Stack;
swift::ASTWalker::ParentTy AtStart;
swift::ASTWalker::ParentTy AtEnd;
bool InDocCommentBlock = false;
bool InCommentLine = false;
bool InStringLiteral = false;
ArrayRef<Token> Tokens;
LangOptions Options;
TokenIt CurrentTokIt;
unsigned TargetLine;
SiblingCollector SCollector;
/// Sometimes, target is a part of "parent", for instance, "#else" is a part
/// of an IfConfigDecl, so that IfConfigDecl is not really the parent of "#else".
bool isTargetPartOf(swift::ASTWalker::ParentTy Parent) {
if (auto Conf = dyn_cast_or_null<IfConfigDecl>(Parent.getAsDecl())) {
for (auto Clause : Conf->getClauses()) {
if (Clause.Loc == TargetLocation)
return true;
}
} else if (auto Call = dyn_cast_or_null<CallExpr>(Parent.getAsExpr())) {
if (auto Clo = dyn_cast<ClosureExpr>(Call->getFn())) {
if (Clo->getBody()->getLBraceLoc() == TargetLocation ||
Clo->getBody()->getRBraceLoc() == TargetLocation) {
return true;
}
}
}
return false;
}
template <class T>
bool HandlePre(T* Node, SourceLoc Start, SourceLoc End) {
scanForComments(Start);
SCollector.collect(Node);
if (SM.isBeforeInBuffer(TargetLocation, Start))
return false; // Target is before start of Node, skip it.
if (SM.isBeforeInBuffer(End, TargetLocation))
return false; // Target is after end of Node, skip it.
if (TargetLocation == Start) {
// Target is right at the start of Node, mark it.
AtStart = Node;
return false;
}
if (TargetLocation == End) {
// Target is right at the end of Node, mark it.
AtEnd = Node;
return false;
}
// Target is within Node and Node is really the parent of Target, take it.
if (!isTargetPartOf(Node))
Stack.push_back(Node);
return true;
}
void scanForComments(SourceLoc Loc) {
if (InDocCommentBlock || InCommentLine)
return;
for (auto InValid = Loc.isInvalid(); CurrentTokIt != Tokens.end() &&
(InValid || SM.isBeforeInBuffer(CurrentTokIt->getLoc(), Loc));
CurrentTokIt++) {
if (CurrentTokIt->getKind() == tok::comment) {
auto StartLine = SM.getLineNumber(CurrentTokIt->getRange().getStart());
auto EndLine = SM.getLineNumber(CurrentTokIt->getRange().getEnd());
auto TokenStr = CurrentTokIt->getRange().str();
InDocCommentBlock |= TargetLine > StartLine && TargetLine <= EndLine &&
TokenStr.startswith("/*");
InCommentLine |= StartLine == TargetLine && TokenStr.startswith("//");
}
}
}
template <typename T>
bool HandlePost(T* Node) {
if (SM.isBeforeInBuffer(TargetLocation, Node->getStartLoc()))
return false; // Target is before start of Node, terminate walking.
return true;
}
public:
explicit FormatWalker(SourceFile &SF, SourceManager &SM)
:SF(SF), SM(SM),
Tokens(SF.getAllTokens()),
CurrentTokIt(Tokens.begin()),
SCollector(SM, Tokens, TargetLocation) {}
FormatContext walkToLocation(SourceLoc Loc) {
Stack.clear();
TargetLocation = Loc;
TargetLine = SM.getLineNumber(TargetLocation);
AtStart = AtEnd = swift::ASTWalker::ParentTy();
walk(SF);
scanForComments(SourceLoc());
return FormatContext(SM, Stack, AtStart, AtEnd, InDocCommentBlock,
InCommentLine, InStringLiteral,
SCollector.getSiblingInfo());
}
ArrayRef<Token> getTokens() {
return llvm::makeArrayRef(Tokens);
}
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
SourceLoc Start = D->getStartLoc();
SourceLoc End = D->getEndLoc();
if (auto *VD = dyn_cast<VarDecl>(D)) {
// We'll treat properties with accessors as spanning the braces as well.
// This will ensure we can do indentation inside the braces.
auto Loc = getVarDeclInitEnd(VD);
End = Loc.isValid() ? Loc : End;
}
return HandlePre(D, Start, End);
}
bool walkToDeclPost(Decl *D) override {
return HandlePost(D);
}
bool walkToStmtPre(Stmt *S) override {
return HandlePre(S, S->getStartLoc(), S->getEndLoc());
}
bool walkToStmtPost(Stmt *S) override {
return HandlePost(S);
}
bool walkToExprPre(Expr *E) override {
if (E->getKind() == ExprKind::StringLiteral &&
SM.isBeforeInBuffer(E->getStartLoc(), TargetLocation) &&
SM.isBeforeInBuffer(TargetLocation,
Lexer::getLocForEndOfToken(SM, E->getEndLoc()))) {
InStringLiteral = true;
}
return HandlePre(E, E->getStartLoc(), E->getEndLoc());
}
bool walkToExprPost(Expr *E) override {
return HandlePost(E);
}
bool shouldWalkInactiveConfigRegion() override {
return true;
}
};
class CodeFormatter {
CodeFormatOptions &FmtOptions;
public:
CodeFormatter(CodeFormatOptions &Options)
:FmtOptions(Options) { }
std::pair<LineRange, std::string> indent(unsigned LineIndex,
FormatContext &FC,
StringRef Text, TokenInfo ToInfo) {
// If having sibling locs to align with, respect siblings.
auto isClosingSquare =
ToInfo && ToInfo.StartOfLineTarget->getKind() == tok::r_square;
if (!isClosingSquare && FC.HasSibling()) {
StringRef Line = swift::ide::getTextForLine(LineIndex, Text, /*Trim*/true);
StringBuilder Builder;
FC.padToSiblingColumn(Builder, FmtOptions);
if (FC.needExtraIndentationForSibling()) {
if (FmtOptions.UseTabs)
Builder.append(1, '\t');
else
Builder.append(FmtOptions.IndentWidth, ' ');
}
Builder.append(Line);
return std::make_pair(LineRange(LineIndex, 1), Builder.str().str());
}
if (FC.IsInStringLiteral()) {
return std::make_pair(LineRange(LineIndex, 1),
swift::ide::getTextForLine(LineIndex, Text, /*Trim*/false));
}
// Take the current indent position of the outer context, then add another
// indent level if expected.
auto LineAndColumn = FC.indentLineAndColumn();
size_t ExpandedIndent = swift::ide::getExpandedIndentForLine(LineAndColumn.first,
FmtOptions, Text);
auto AddIndentFunc = [&] () {
auto Width = FmtOptions.UseTabs ? FmtOptions.TabWidth
: FmtOptions.IndentWidth;
// We don't need to add additional indentation if Width is zero.
if (!Width)
return;
// Increment indent.
ExpandedIndent += Width;
// Normalize indent to align on proper column indent width.
ExpandedIndent -= ExpandedIndent % Width;
};
if (LineAndColumn.second > 0 &&
FC.shouldAddIndentForLine(LineIndex, ToInfo, FmtOptions))
AddIndentFunc();
// Control statements in switch align with the rest of the block in case.
// For example:
// switch ... {
// case xyz:
// break <-- Extra indent level here.
if (FmtOptions.IndentSwitchCase && FC.isSwitchControlStmt(LineIndex, Text))
AddIndentFunc();
if (FC.IsInDocCommentBlock()) {
// Inside doc comment block, the indent is one space, e.g.
// /**
// * <---Indent to align with the first star.
// */
ExpandedIndent += 1;
}
// Reformat the specified line with the calculated indent.
StringRef Line = swift::ide::getTextForLine(LineIndex, Text, /*Trim*/true);
std::string IndentedLine;
if (FmtOptions.UseTabs)
IndentedLine.assign(ExpandedIndent / FmtOptions.TabWidth, '\t');
else
IndentedLine.assign(ExpandedIndent, ' ');
IndentedLine.append(Line);
// Return affected line range, which can later be more than one line.
LineRange range = LineRange(LineIndex, 1);
return std::make_pair(range, IndentedLine);
}
};
class TokenInfoCollector {
SourceManager &SM;
ArrayRef<Token> Tokens;
unsigned Line;
struct Comparator {
SourceManager &SM;
Comparator(SourceManager &SM) : SM(SM) {}
bool operator()(const Token &T, unsigned Line) const {
return SM.getLineNumber(T.getLoc()) < Line;
}
bool operator()(unsigned Line, const Token &T) const {
return Line < SM.getLineNumber(T.getLoc());
}
};
public:
TokenInfoCollector(SourceManager &SM, ArrayRef<Token> Tokens,
unsigned Line) : SM(SM), Tokens(Tokens), Line(Line) {}
TokenInfo collect() {
if (Line == 0)
return TokenInfo();
Comparator Comp(SM);
auto LineMatch = [this] (const Token* T, unsigned Line) {
return T != Tokens.end() && SM.getLineNumber(T->getLoc()) == Line;
};
auto TargetIt = std::lower_bound(Tokens.begin(), Tokens.end(), Line, Comp);
auto LineBefore = std::lower_bound(Tokens.begin(), TargetIt, Line - 1, Comp);
if (LineMatch(TargetIt, Line) && LineMatch(LineBefore, Line - 1))
return TokenInfo(TargetIt, LineBefore);
return TokenInfo();
}
};
} //anonymous namespace
size_t swift::ide::getOffsetOfLine(unsigned LineIndex, StringRef Text) {
// SourceLoc start = SourceLoc(llvm::SMLoc::getFromPointer(Text.begin()));
// FIXME: We should have a cached line map in EditableTextBuffer, for now
// we just do the slow naive thing here.
size_t LineOffset = 0;
unsigned CurrentLine = 0;
while (LineOffset < Text.size() && ++CurrentLine < LineIndex) {
LineOffset = Text.find_first_of("\r\n", LineOffset);
if (LineOffset != std::string::npos) {
++LineOffset;
if (LineOffset < Text.size() &&
Text[LineOffset - 1] == '\r' && Text[LineOffset] == '\n')
++LineOffset;
}
}
if (LineOffset == std::string::npos)
LineOffset = 0;
return LineOffset;
}
size_t swift::ide::getOffsetOfLine(unsigned LineIndex, StringRef Text, bool Trim) {
size_t LineOffset = swift::ide::getOffsetOfLine(LineIndex, Text);
if (!Trim)
return LineOffset;
// Skip leading whitespace.
size_t FirstNonWSOnLine = Text.find_first_not_of(" \t\v\f", LineOffset);
if (FirstNonWSOnLine != std::string::npos)
LineOffset = FirstNonWSOnLine;
return LineOffset;
}
llvm::StringRef swift::ide::getTextForLine(unsigned LineIndex, StringRef Text,
bool Trim) {
size_t LineOffset = getOffsetOfLine(LineIndex, Text, Trim);
size_t LineEnd = Text.find_first_of("\r\n", LineOffset);
return Text.slice(LineOffset, LineEnd);
}
size_t swift::ide::getExpandedIndentForLine(unsigned LineIndex,
CodeFormatOptions Options,
StringRef Text) {
size_t LineOffset = getOffsetOfLine(LineIndex, Text);
// Tab-expand all leading whitespace
size_t FirstNonWSOnLine = Text.find_first_not_of(" \t\v\f", LineOffset);
size_t Indent = 0;
while (LineOffset < Text.size() && LineOffset < FirstNonWSOnLine) {
if (Text[LineOffset++] == '\t')
Indent += Options.TabWidth;
else
Indent += 1;
}
return Indent;
}
std::pair<LineRange, std::string> swift::ide::reformat(LineRange Range,
CodeFormatOptions Options,
SourceManager &SM,
SourceFile &SF) {
// Sanitize 0-width tab
if (Options.UseTabs && !Options.TabWidth) {
// If IndentWidth is specified, use it as the tab width.
if (Options.IndentWidth)
Options.TabWidth = Options.IndentWidth;
// Otherwise, use the default value,
else
Options.TabWidth = 4;
}
FormatWalker walker(SF, SM);
auto SourceBufferID = SF.getBufferID().getValue();
StringRef Text = SM.getLLVMSourceMgr()
.getMemoryBuffer(SourceBufferID)->getBuffer();
size_t Offset = getOffsetOfLine(Range.startLine(), Text, /*Trim*/true);
SourceLoc Loc = SM.getLocForBufferStart(SourceBufferID)
.getAdvancedLoc(Offset);
FormatContext FC = walker.walkToLocation(Loc);
CodeFormatter CF(Options);
unsigned Line = Range.startLine();
return CF.indent(Line, FC, Text, TokenInfoCollector(SM, walker.getTokens(),
Line).collect());
}