danbeam created this revision. Herald added a subscriber: klimek. 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. Before: (function() { // clang-format pushes stuff to here })(); After: (function() { // clang-format pushes stuff to here })(); [1] https://en.wikipedia.org/wiki/Immediately-invoked_function_expression https://reviews.llvm.org/D32989 Files: lib/Format/UnwrappedLineParser.cpp lib/Format/UnwrappedLineParser.h unittests/Format/FormatTestJS.cpp Index: unittests/Format/FormatTestJS.cpp =================================================================== --- unittests/Format/FormatTestJS.cpp +++ unittests/Format/FormatTestJS.cpp @@ -367,6 +367,21 @@ "});"); } +TEST_F(FormatTestJS, IIFE) { + verifyFormat("(function() {\n" + "var a = 1;\n" + "}())"); + verifyFormat("(function() {\n" + "var b = 2;\n" + "})()"); + verifyFormat("(function() {\n" + "var c = 3;\n" + "})();"); + verifyFormat("(function() {\n" + "var d = 4;\n" + "}());"); +} + TEST_F(FormatTestJS, GoogModules) { verifyFormat("goog.module('this.is.really.absurdly.long');", getGoogleJSStyleWithColumns(40)); Index: lib/Format/UnwrappedLineParser.h =================================================================== --- lib/Format/UnwrappedLineParser.h +++ lib/Format/UnwrappedLineParser.h @@ -125,6 +125,7 @@ void nextToken(); const FormatToken *getPreviousToken(); void readToken(); + bool isIIFE() const; // Decides which comment tokens should be added to the current line and which // should be added as comments before the next token. Index: lib/Format/UnwrappedLineParser.cpp =================================================================== --- lib/Format/UnwrappedLineParser.cpp +++ lib/Format/UnwrappedLineParser.cpp @@ -493,15 +493,16 @@ FormatTok->BlockKind = BK_Block; nextToken(); { - bool GoogScope = - Style.Language == FormatStyle::LK_JavaScript && isGoogScope(*Line); + bool SkipIndent = + Style.Language == FormatStyle::LK_JavaScript && + (isGoogScope(*Line) || isIIFE()); 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(); } @@ -2342,5 +2343,30 @@ } } +bool UnwrappedLineParser::isIIFE() const { + // 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() { ... })() + // FIXME: check for the end invocation or alternate ways to start function + // expressions? + if (Line->Tokens.size() < 5) + 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; + if (I->Tok->isNot(tok::l_paren)) + return false; + ++I; + if (I->Tok->isNot(tok::r_paren)) + return false; + ++I; + return I->Tok->is(tok::l_brace); +} + } // end namespace format } // end namespace clang
Index: unittests/Format/FormatTestJS.cpp =================================================================== --- unittests/Format/FormatTestJS.cpp +++ unittests/Format/FormatTestJS.cpp @@ -367,6 +367,21 @@ "});"); } +TEST_F(FormatTestJS, IIFE) { + verifyFormat("(function() {\n" + "var a = 1;\n" + "}())"); + verifyFormat("(function() {\n" + "var b = 2;\n" + "})()"); + verifyFormat("(function() {\n" + "var c = 3;\n" + "})();"); + verifyFormat("(function() {\n" + "var d = 4;\n" + "}());"); +} + TEST_F(FormatTestJS, GoogModules) { verifyFormat("goog.module('this.is.really.absurdly.long');", getGoogleJSStyleWithColumns(40)); Index: lib/Format/UnwrappedLineParser.h =================================================================== --- lib/Format/UnwrappedLineParser.h +++ lib/Format/UnwrappedLineParser.h @@ -125,6 +125,7 @@ void nextToken(); const FormatToken *getPreviousToken(); void readToken(); + bool isIIFE() const; // Decides which comment tokens should be added to the current line and which // should be added as comments before the next token. Index: lib/Format/UnwrappedLineParser.cpp =================================================================== --- lib/Format/UnwrappedLineParser.cpp +++ lib/Format/UnwrappedLineParser.cpp @@ -493,15 +493,16 @@ FormatTok->BlockKind = BK_Block; nextToken(); { - bool GoogScope = - Style.Language == FormatStyle::LK_JavaScript && isGoogScope(*Line); + bool SkipIndent = + Style.Language == FormatStyle::LK_JavaScript && + (isGoogScope(*Line) || isIIFE()); 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(); } @@ -2342,5 +2343,30 @@ } } +bool UnwrappedLineParser::isIIFE() const { + // 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() { ... })() + // FIXME: check for the end invocation or alternate ways to start function + // expressions? + if (Line->Tokens.size() < 5) + 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; + if (I->Tok->isNot(tok::l_paren)) + return false; + ++I; + if (I->Tok->isNot(tok::r_paren)) + return false; + ++I; + return I->Tok->is(tok::l_brace); +} + } // end namespace format } // end namespace clang
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits