Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -431,6 +431,23 @@ /// type. bool IndentWrappedFunctionNames; + /// \brief Quotation styles for JavaScript strings. Does not affect template + /// strings. + enum JavaScriptQuoteStyle { + /// Leave string quotes as they are. + JSQS_Leave, + /// Always use single quotes. + JSQS_Single, + /// Always use double quotes. + JSQS_Double + }; + + /// \brief The JavaScriptQuoteStyle to use for JavaScript strings. + JavaScriptQuoteStyle JavaScriptQuotes; + + /// \brief Whether to wrap JavaScript import/export statements. + bool JavaScriptWrapImports; + /// \brief If true, empty lines at the start of blocks are kept. bool KeepEmptyLinesAtTheStartOfBlocks; @@ -613,20 +630,6 @@ /// \brief The way to use tab characters in the resulting file. UseTabStyle UseTab; - /// \brief Quotation styles for JavaScript strings. Does not affect template - /// strings. - enum JavaScriptQuoteStyle { - /// Leave string quotes as they are. - JSQS_Leave, - /// Always use single quotes. - JSQS_Single, - /// Always use double quotes. - JSQS_Double - }; - - /// \brief The JavaScriptQuoteStyle to use for JavaScript strings. - JavaScriptQuoteStyle JavaScriptQuotes; - bool operator==(const FormatStyle &R) const { return AccessModifierOffset == R.AccessModifierOffset && AlignAfterOpenBracket == R.AlignAfterOpenBracket && @@ -675,6 +678,8 @@ IndentCaseLabels == R.IndentCaseLabels && IndentWidth == R.IndentWidth && Language == R.Language && IndentWrappedFunctionNames == R.IndentWrappedFunctionNames && + JavaScriptQuotes == R.JavaScriptQuotes && + JavaScriptWrapImports == R.JavaScriptWrapImports && KeepEmptyLinesAtTheStartOfBlocks == R.KeepEmptyLinesAtTheStartOfBlocks && MacroBlockBegin == R.MacroBlockBegin && @@ -703,8 +708,7 @@ SpacesInParentheses == R.SpacesInParentheses && SpacesInSquareBrackets == R.SpacesInSquareBrackets && Standard == R.Standard && TabWidth == R.TabWidth && - UseTab == R.UseTab && - JavaScriptQuotes == R.JavaScriptQuotes; + UseTab == R.UseTab; } }; Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -314,6 +314,8 @@ IO.mapOptional("IndentWidth", Style.IndentWidth); IO.mapOptional("IndentWrappedFunctionNames", Style.IndentWrappedFunctionNames); + IO.mapOptional("JavaScriptQuotes", Style.JavaScriptQuotes); + IO.mapOptional("JavaScriptWrapImports", Style.JavaScriptWrapImports); IO.mapOptional("KeepEmptyLinesAtTheStartOfBlocks", Style.KeepEmptyLinesAtTheStartOfBlocks); IO.mapOptional("MacroBlockBegin", Style.MacroBlockBegin); @@ -353,7 +355,6 @@ IO.mapOptional("Standard", Style.Standard); IO.mapOptional("TabWidth", Style.TabWidth); IO.mapOptional("UseTab", Style.UseTab); - IO.mapOptional("JavaScriptQuotes", Style.JavaScriptQuotes); } }; @@ -613,6 +614,7 @@ GoogleStyle.MaxEmptyLinesToKeep = 3; GoogleStyle.SpacesInContainerLiterals = false; GoogleStyle.JavaScriptQuotes = FormatStyle::JSQS_Single; + GoogleStyle.JavaScriptWrapImports = false; } else if (Language == FormatStyle::LK_Proto) { GoogleStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; GoogleStyle.SpacesInContainerLiterals = false; Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -786,7 +786,7 @@ // import {...} from '...'; if (Style.Language == FormatStyle::LK_JavaScript && - CurrentToken->is(Keywords.kw_import)) + CurrentToken->is(Keywords.kw_import) && !Style.JavaScriptWrapImports) return LT_ImportStatement; bool KeywordVirtualFound = false; @@ -804,7 +804,7 @@ if (Line.First->is(tok::kw_export) && CurrentToken->is(Keywords.kw_from) && CurrentToken->Next && CurrentToken->Next->isStringLiteral()) - ImportStatement = true; + ImportStatement = !Style.JavaScriptWrapImports; if (isClosureImportStatement(*CurrentToken)) ImportStatement = true; } @@ -2126,6 +2126,11 @@ // locations that should have whitespace following are identified by the // above set of follower tokens. return false; + // Postfix non-null assertion operator, as in `foo!.bar()`. + if (Right.is(tok::exclaim) && (Left.isOneOf(tok::identifier, tok::r_paren, + tok::r_square, tok::r_brace) || + Left.Tok.isLiteral())) + return false; } else if (Style.Language == FormatStyle::LK_Java) { if (Left.is(tok::r_square) && Right.is(tok::l_brace)) return true; Index: unittests/Format/FormatTestJS.cpp =================================================================== --- unittests/Format/FormatTestJS.cpp +++ unittests/Format/FormatTestJS.cpp @@ -1078,6 +1078,27 @@ "}"); } +TEST_F(FormatTestJS, ImportWrapping) { + FormatStyle Style = getGoogleJSStyleWithColumns(80); + Style.JavaScriptWrapImports = true; + verifyFormat("import {\n" + " VeryLongImportsAreAnnoying,\n" + " VeryLongImportsAreAnnoying,\n" + " VeryLongImportsAreAnnoying,\n" + "} from 'some/module.js';", + Style); + verifyFormat("import {\n" + " A,\n" + " A,\n" + "} from 'some/module.js';", + Style); + verifyFormat("export {\n" + " A,\n" + " A,\n" + "} from 'some/module.js';", + Style); +} + TEST_F(FormatTestJS, TemplateStrings) { // Keeps any whitespace/indentation within the template string. verifyFormat("var x = `hello\n" @@ -1293,5 +1314,14 @@ "var x = hello();"); } +TEST_F(FormatTestJS, NonNullAssertionOperator) { + verifyFormat("let x = foo!.bar();\n"); + verifyFormat("let x = foo ? bar! : baz;\n"); + verifyFormat("let x = !foo;\n"); + verifyFormat("let x = foo[0]!;\n"); + verifyFormat("let x = (foo)!;\n"); + verifyFormat("let x = {foo: 1}!;\n"); +} + } // end namespace tooling } // end namespace clang