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
This commit is contained in:
Daniel Jasper 2014-11-23 16:46:28 +00:00
parent 6de72e5b0c
commit 616de864da
6 changed files with 51 additions and 26 deletions

View File

@ -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))

View File

@ -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);

View File

@ -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 <string-literal>' 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 <a/b.h>".
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;
};

View File

@ -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 {

View File

@ -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();

View File

@ -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"