Skip to content

Commit

Permalink
clang-format: [JS] Don't indent JavaScript IIFEs.
Browse files Browse the repository at this point in the history
Because IIFEs[1] are often used like an anonymous namespace around large
sections of JavaScript code, it's useful not to indent to them (which
effectively reduces the column limit by the indent amount needlessly).

It's also common for developers to wrap these around entire files or
libraries. When adopting clang-format, changing the indent entire file
can reduce the usefulness of the blame annotations.

Patch by danbeam, thanks!

Differential Revision: https://reviews.llvm.org/D32989

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@302580 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
mprobst committed May 9, 2017
1 parent b6c02e8 commit cf44d2c
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 4 deletions.
27 changes: 23 additions & 4 deletions lib/Format/UnwrappedLineParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,24 @@ static bool isGoogScope(const UnwrappedLine &Line) {
return I->Tok->is(tok::l_paren);
}

static bool isIIFE(const UnwrappedLine &Line,
const AdditionalKeywords &Keywords) {
// Look for the start of an immediately invoked anonymous function.
// https://en.wikipedia.org/wiki/Immediately-invoked_function_expression
// This is commonly done in JavaScript to create a new, anonymous scope.
// Example: (function() { ... })()
if (Line.Tokens.size() < 3)
return false;
auto I = Line.Tokens.begin();
if (I->Tok->isNot(tok::l_paren))
return false;
++I;
if (I->Tok->isNot(Keywords.kw_function))
return false;
++I;
return I->Tok->is(tok::l_paren);
}

static bool ShouldBreakBeforeBrace(const FormatStyle &Style,
const FormatToken &InitialToken) {
if (InitialToken.is(tok::kw_namespace))
Expand All @@ -493,15 +511,16 @@ void UnwrappedLineParser::parseChildBlock() {
FormatTok->BlockKind = BK_Block;
nextToken();
{
bool GoogScope =
Style.Language == FormatStyle::LK_JavaScript && isGoogScope(*Line);
bool SkipIndent =
(Style.Language == FormatStyle::LK_JavaScript &&
(isGoogScope(*Line) || isIIFE(*Line, Keywords)));
ScopedLineState LineState(*this);
ScopedDeclarationState DeclarationState(*Line, DeclarationScopeStack,
/*MustBeDeclaration=*/false);
Line->Level += GoogScope ? 0 : 1;
Line->Level += SkipIndent ? 0 : 1;
parseLevel(/*HasOpeningBrace=*/true);
flushComments(isOnNewLine(*FormatTok));
Line->Level -= GoogScope ? 0 : 1;
Line->Level -= SkipIndent ? 0 : 1;
}
nextToken();
}
Expand Down
19 changes: 19 additions & 0 deletions unittests/Format/FormatTestJS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,25 @@ TEST_F(FormatTestJS, GoogScopes) {
"});");
}

TEST_F(FormatTestJS, IIFEs) {
// Internal calling parens; no semi.
verifyFormat("(function() {\n"
"var a = 1;\n"
"}())");
// External calling parens; no semi.
verifyFormat("(function() {\n"
"var b = 2;\n"
"})()");
// Internal calling parens; with semi.
verifyFormat("(function() {\n"
"var c = 3;\n"
"}());");
// External calling parens; with semi.
verifyFormat("(function() {\n"
"var d = 4;\n"
"})();");
}

TEST_F(FormatTestJS, GoogModules) {
verifyFormat("goog.module('this.is.really.absurdly.long');",
getGoogleJSStyleWithColumns(40));
Expand Down

0 comments on commit cf44d2c

Please sign in to comment.