From 616de864da6ae41d08c5baf40f35c7307b21fd95 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Sun, 23 Nov 2014 16:46:28 +0000 Subject: [PATCH] clang-format: [JS] Support Closure's module statements. These are like import statements and should not be line-wrapped. Minor restructuring of the handling of other import statements. llvm-svn: 222637 --- clang/lib/Format/ContinuationIndenter.cpp | 5 ++- clang/lib/Format/Format.cpp | 3 +- clang/lib/Format/TokenAnnotator.cpp | 47 ++++++++++++++--------- clang/lib/Format/TokenAnnotator.h | 9 +++-- clang/lib/Format/UnwrappedLineParser.cpp | 2 + clang/unittests/Format/FormatTestJS.cpp | 11 ++++++ 6 files changed, 51 insertions(+), 26 deletions(-) diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp index 825a88ebae0c..f4c4522aa18c 100644 --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -986,8 +986,9 @@ unsigned ContinuationIndenter::breakProtrudingToken(const FormatToken &Current, if (Current.Type != TT_BlockComment && Current.IsMultiline) return addMultilineToken(Current, State); - // Don't break implicit string literals. - if (Current.Type == TT_ImplicitStringLiteral) + // Don't break implicit string literals or import statements. + if (Current.Type == TT_ImplicitStringLiteral || + State.Line->Type == LT_ImportStatement) return 0; if (!Current.isStringLiteral() && !Current.is(tok::comment)) diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 7c062d68376e..2f9f98424bce 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -951,7 +951,8 @@ public: ColumnLimit = getColumnLimit(TheLine.InPPDirective); } - if (TheLine.Last->TotalLength + Indent <= ColumnLimit) { + if (TheLine.Last->TotalLength + Indent <= ColumnLimit || + TheLine.Type == LT_ImportStatement) { LineState State = Indenter->getInitialState(Indent, &TheLine, DryRun); while (State.NextToken) { formatChildren(State, /*Newline=*/false, /*DryRun=*/false, Penalty); diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index c4d443b2ca02..7a71d9361b1f 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -33,8 +33,8 @@ class AnnotatingParser { public: AnnotatingParser(const FormatStyle &Style, AnnotatedLine &Line, const AdditionalKeywords &Keywords) - : Style(Style), Line(Line), CurrentToken(Line.First), - KeywordVirtualFound(false), AutoFound(false), Keywords(Keywords) { + : Style(Style), Line(Line), CurrentToken(Line.First), AutoFound(false), + Keywords(Keywords) { Contexts.push_back(Context(tok::unknown, 1, /*IsExpression=*/false)); resetTokenMetadata(CurrentToken); } @@ -536,14 +536,6 @@ private: CurrentToken->Type = TT_ImplicitStringLiteral; next(); } - } else { - while (CurrentToken) { - if (CurrentToken->isNot(tok::comment)) - // Mark these tokens as "implicit" string literals, so that - // they are not split or line-wrapped. - CurrentToken->Type = TT_ImplicitStringLiteral; - next(); - } } } @@ -570,23 +562,25 @@ private: } } - void parsePreprocessorDirective() { + LineType parsePreprocessorDirective() { + LineType Type = LT_PreprocessorDirective; next(); if (!CurrentToken) - return; + return Type; if (CurrentToken->Tok.is(tok::numeric_constant)) { CurrentToken->SpacesRequiredBefore = 1; - return; + return Type; } // Hashes in the middle of a line can lead to any strange token // sequence. if (!CurrentToken->Tok.getIdentifierInfo()) - return; + return Type; switch (CurrentToken->Tok.getIdentifierInfo()->getPPKeywordID()) { case tok::pp_include: case tok::pp_import: next(); parseIncludeDirective(); + Type = LT_ImportStatement; break; case tok::pp_error: case tok::pp_warning: @@ -605,13 +599,13 @@ private: } while (CurrentToken) next(); + return Type; } public: LineType parseLine() { if (CurrentToken->is(tok::hash)) { - parsePreprocessorDirective(); - return LT_PreprocessorDirective; + return parsePreprocessorDirective(); } // Directly allow to 'import ' to support protocol buffer @@ -622,24 +616,30 @@ public: CurrentToken->Next) { next(); parseIncludeDirective(); - return LT_Other; + return LT_ImportStatement; } // If this line starts and ends in '<' and '>', respectively, it is likely // part of "#define ". if (CurrentToken->is(tok::less) && Line.Last->is(tok::greater)) { parseIncludeDirective(); - return LT_Other; + return LT_ImportStatement; } + bool KeywordVirtualFound = false; + bool ImportStatement = false; while (CurrentToken) { if (CurrentToken->is(tok::kw_virtual)) KeywordVirtualFound = true; + if (IsImportStatement(*CurrentToken)) + ImportStatement = true; if (!consumeToken()) return LT_Invalid; } if (KeywordVirtualFound) return LT_VirtualFunctionDecl; + if (ImportStatement) + return LT_ImportStatement; if (Line.First->Type == TT_ObjCMethodSpecifier) { if (Contexts.back().FirstObjCSelectorName) @@ -652,6 +652,16 @@ public: } private: + bool IsImportStatement(const FormatToken &Tok) { + // FIXME: Closure-library specific stuff should not be hard-coded but be + // configurable. + return Style.Language == FormatStyle::LK_JavaScript && + Tok.TokenText == "goog" && Tok.Next && Tok.Next->is(tok::period) && + Tok.Next->Next && (Tok.Next->Next->TokenText == "module" || + Tok.Next->Next->TokenText == "require" || + Tok.Next->Next->TokenText == "provide"); + } + void resetTokenMetadata(FormatToken *Token) { if (!Token) return; @@ -1066,7 +1076,6 @@ private: const FormatStyle &Style; AnnotatedLine &Line; FormatToken *CurrentToken; - bool KeywordVirtualFound; bool AutoFound; const AdditionalKeywords &Keywords; }; diff --git a/clang/lib/Format/TokenAnnotator.h b/clang/lib/Format/TokenAnnotator.h index fc4d1d66e727..ff8e32a56afc 100644 --- a/clang/lib/Format/TokenAnnotator.h +++ b/clang/lib/Format/TokenAnnotator.h @@ -27,12 +27,13 @@ namespace format { enum LineType { LT_Invalid, - LT_Other, - LT_PreprocessorDirective, - LT_VirtualFunctionDecl, + LT_ImportStatement, LT_ObjCDecl, // An @interface, @implementation, or @protocol line. LT_ObjCMethodDecl, - LT_ObjCProperty // An @property line. + LT_ObjCProperty, // An @property line. + LT_Other, + LT_PreprocessorDirective, + LT_VirtualFunctionDecl }; class AnnotatedLine { diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 2c6a4fb25405..ba492aae0295 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -418,6 +418,8 @@ void UnwrappedLineParser::parseBlock(bool MustBeDeclaration, bool AddLevel, } static bool IsGoogScope(const UnwrappedLine &Line) { + // FIXME: Closure-library specific stuff should not be hard-coded but be + // configurable. if (Line.Tokens.size() < 4) return false; auto I = Line.Tokens.begin(); diff --git a/clang/unittests/Format/FormatTestJS.cpp b/clang/unittests/Format/FormatTestJS.cpp index 6ee150c6624f..b0733bdd25c8 100644 --- a/clang/unittests/Format/FormatTestJS.cpp +++ b/clang/unittests/Format/FormatTestJS.cpp @@ -158,6 +158,17 @@ TEST_F(FormatTestJS, GoogScopes) { "}); // goog.scope"); } +TEST_F(FormatTestJS, GoogModules) { + verifyFormat("goog.module('this.is.really.absurdly.long');", + getGoogleJSStyleWithColumns(40)); + verifyFormat("goog.require('this.is.really.absurdly.long');", + getGoogleJSStyleWithColumns(40)); + verifyFormat("goog.provide('this.is.really.absurdly.long');", + getGoogleJSStyleWithColumns(40)); + verifyFormat("var long = goog.require('this.is.really.absurdly.long');", + getGoogleJSStyleWithColumns(40)); +} + TEST_F(FormatTestJS, FormatsFreestandingFunctions) { verifyFormat("function outer1(a, b) {\n" " function inner1(a, b) { return a; }\n"