//===--- FormatContext.h --------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #ifndef SWIFT_FORMATCONTEXT_H #define SWIFT_FORMATCONTEXT_H #include "swift/AST/AST.h" #include "swift/AST/ASTWalker.h" #include "swift/Frontend/Frontend.h" #include "swift/Basic/SourceManager.h" #include "swift/IDE/SourceEntityWalker.h" #include "swift/Subsystems.h" namespace swift { namespace ide { struct CodeFormatOptions { bool UseTabs = false; unsigned IndentWidth = 4; unsigned TabWidth = 4; }; struct SiblingAlignInfo { SourceLoc Loc; bool ExtraIndent; }; typedef llvm::SmallString<64> StringBuilder; 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& Stack; std::vector::reverse_iterator Cursor; swift::ASTWalker::ParentTy Start; swift::ASTWalker::ParentTy End; bool InDocCommentBlock; bool InCommentLine; SiblingAlignInfo SiblingInfo; public: FormatContext(SourceManager &SM, std::vector& Stack, swift::ASTWalker::ParentTy Start = swift::ASTWalker::ParentTy(), swift::ASTWalker::ParentTy End = swift::ASTWalker::ParentTy(), bool InDocCommentBlock = false, bool InCommentLine = false, SiblingAlignInfo SiblingInfo = SiblingAlignInfo()) :SM(SM), Stack(Stack), Cursor(Stack.rbegin()), Start(Start), End(End), InDocCommentBlock(InDocCommentBlock), InCommentLine(InCommentLine), SiblingInfo(SiblingInfo) { } FormatContext parent() { assert(Cursor != Stack.rend()); FormatContext Parent(*this); ++Parent.Cursor; return Parent; } bool IsInDocCommentBlock() { return InDocCommentBlock; } bool IsInCommentLine() { return InCommentLine; } void padToSiblingColumn(StringBuilder &Builder) { assert(SiblingInfo.Loc.isValid() && "No sibling to align with."); CharSourceRange Range(SM, Lexer::getLocForStartOfLine(SM, SiblingInfo.Loc), SiblingInfo.Loc); for (auto C : Range.str()) { Builder.append(1, C == '\t' ? C : ' '); } } bool HasSibling() { return SiblingInfo.Loc.isValid(); } bool needExtraIndentationForSibling() { return SiblingInfo.ExtraIndent; } std::pair 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 bool isStmtContext() { if (Cursor == Stack.rend()) return false; Stmt *ContextStmt = Cursor->getAsStmt(); return ContextStmt && isa(ContextStmt); } bool isBraceContext() { return isStmtContext(); } 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(); } bool isSwitchContext() { return isStmtContext(); } std::pair 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 (IfStmt *If = dyn_cast_or_null(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(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(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 shouldAddIndentForLine(unsigned Line) { if (Cursor == Stack.rend()) return false; // Handle switch / case, indent unless at a case label. if (CaseStmt *Case = dyn_cast_or_null(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(AtStmtStart)) return false; // 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(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 false; } } } // 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(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(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(AtStmtEnd)) return false; // do { // } // catch { // } <-- No indent here, close brace should be at same level as do. // catch { // } if (isa(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(Cursor->getAsDecl()); if (NTD && SM.getLineAndColumn(NTD->getBraces().Start).first == Line) return false; auto *ETD = dyn_cast_or_null(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(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(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(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(AtExprEnd) || isa(AtExprEnd) || isa(AtExprEnd))) { if (auto *Paren = dyn_cast_or_null(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(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(Cursor->getAsDecl())) { SourceLoc Loc = getVarDeclInitEnd(VD); if (Loc.isValid() && SM.getLineNumber(Loc) == Line) { return false; } } } // Indent another level from the outer context by default. return true; } }; class FormatWalker: public ide::SourceEntityWalker { typedef std::vector::iterator TokenIt; class SiblingCollector { SourceLoc FoundSibling; SourceManager &SM; std::vector &Tokens; SourceLoc &TargetLoc; TokenIt TI; bool NeedExtraIndentation; class SourceLocIterator : public std::iterator { 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();} }; 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, std::vector &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() && 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(Node.dyn_cast())) { collect(AE->getArg()); return; } if (auto PE = dyn_cast_or_null(Node.dyn_cast())) { if (auto Sub = PE->getSubExpr()) { addPair(Sub->getEndLoc(), FindAlignLoc(Sub->getStartLoc()), tok::comma); } } // Tuple elements are siblings. if (auto TE = dyn_cast_or_null(Node.dyn_cast())) { // 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(Node.dyn_cast())) { // Generic type params are siblings to align. if (auto GPL = AFD->getGenericParams()) { const auto Params = GPL->getParams(); for (unsigned I = 0, N = Params.size(); I < N; I ++) { addPair(Params[I]->getEndLoc(), FindAlignLoc(Params[I]->getStartLoc()), tok::comma); } } // Function parameters are siblings. for (auto P : AFD->getParameterLists()) { for (ParamDecl* param : *P) { if (!param->isSelfParameter()) 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(Node.dyn_cast())) { SourceLoc LBracketLoc = AE->getLBracketLoc(); if (isTargetImmediateAfter(LBracketLoc) && !sameLineWithTarget(LBracketLoc)) { FoundSibling = LBracketLoc; NeedExtraIndentation = true; } 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(Node.dyn_cast())) { 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 Stack; swift::ASTWalker::ParentTy AtStart; swift::ASTWalker::ParentTy AtEnd; bool InDocCommentBlock = false; bool InCommentLine = false; std::vector Tokens; LangOptions Options; TokenIt CurrentTokIt; unsigned TargetLine; SiblingCollector SCollector; /// Sometimes, target is a part of "parent", for instance, "#else" is a part /// of an ifconfigstmt, so that ifconfigstmt is not really the parent of "#else". bool isTargetPartOf(swift::ASTWalker::ParentTy Parent) { if(auto Conf = dyn_cast_or_null(Parent.getAsStmt())) { for (auto Clause : Conf->getClauses()) { if (Clause.Loc == TargetLocation) return true; } } else if (auto Call = dyn_cast_or_null(Parent.getAsExpr())) { if(auto Clo = dyn_cast(Call->getFn())) { if (Clo->getBody()->getLBraceLoc() == TargetLocation || Clo->getBody()->getRBraceLoc() == TargetLocation) { return true; } } } return false; } template 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 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(tokenize(Options, SM, SF.getBufferID().getValue())), 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, SCollector.getSiblingInfo()); } bool walkToDeclPre(Decl *D, CharSourceRange Range) override { SourceLoc Start = D->getStartLoc(); SourceLoc End = D->getEndLoc(); if (auto *VD = dyn_cast(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 { return HandlePre(E, E->getStartLoc(), E->getEndLoc()); } bool walkToExprPost(Expr *E) override { return HandlePost(E); } bool shouldWalkInactiveConfigRegion() override { return true; } }; // This is basically the opposite of what the SourceManager Can Do. // Given a buffer and an Offset the SourceManager can tell us the line,column // Could we leverage that to get us the line number faster // maybe via a binary search size_t getOffsetOfLine(unsigned LineIndex, StringRef Text); size_t getOffsetOfTrimmedLine(unsigned LineIndex, StringRef Text); } // namespace ide } // namespace swift #endif // LLVM_SWIFT_FORMATCONTEXT_H