Index: clang/docs/ClangFormatStyleOptions.rst =================================================================== --- clang/docs/ClangFormatStyleOptions.rst +++ clang/docs/ClangFormatStyleOptions.rst @@ -2820,19 +2820,54 @@ **JavaScriptWrapImports** (``Boolean``) :versionbadge:`clang-format 3.9` - Whether to wrap JavaScript import/export statements. + Whether to wrap JavaScript import/export statements. If + ``false``, then every import statement will be unwrapped to a + single line. + + If ``JavaScriptWrapImports`` is ``true``, then the behavior + depends on the value of ``ColumnWidth``: + + * If ``ColumnWidth`` is 0 (no limit on the number of columns), + then import statements will keep the number of lines they + start with. + + * If ``ColumnWidth`` is non-zero (there is a limit on the + number of columns), then import statements will be wrapped + to an appropriate number of lines, or unwrapped into a + single line if there is room. + .. code-block:: js - true: + // Before running clang-format + import {VeryLongImport, AnotherLongImport, LongImportsAreAnnoying} from 'some/module.js'; + import { + Foo, + Bar, + Baz + } from 'another/module.js'; + + JavaScriptWrapImports: false + import {VeryLongImport, AnotherLongImport, LongImportsAreAnnoying} from 'some/module.js'; + import {Foo, Bar, Baz} from 'another/module.js'; + + JavaScriptWrapImports: true + ColumnWidth: 0 + import {VeryLongImport, AnotherLongImport, LongImportsAreAnnoying} from 'some/module.js'; import { - VeryLongImportsAreAnnoying, - VeryLongImportsAreAnnoying, - VeryLongImportsAreAnnoying, - } from 'some/module.js' + Foo, + Bar, + Baz + } from 'another/module.js'; - false: - import {VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying,} from "some/module.js" + JavaScriptWrapImports: true + ColumnWidth: 80 + import { + VeryLongImport, + AnotherLongImport, + LongImportsAreAnnoying + } from 'some/module.js'; + import {Foo, Bar, Baz} from 'another/module.js'; **KeepEmptyLinesAtTheStartOfBlocks** (``Boolean``) :versionbadge:`clang-format 3.7` If true, the empty line at the start of blocks is kept. Index: clang/include/clang/Format/Format.h =================================================================== --- clang/include/clang/Format/Format.h +++ clang/include/clang/Format/Format.h @@ -2610,17 +2610,52 @@ JavaScriptQuoteStyle JavaScriptQuotes; // clang-format off - /// Whether to wrap JavaScript import/export statements. + /// Whether to wrap JavaScript import/export statements. If + /// ``false``, then every import statement will be unwrapped to a + /// single line. + /// + /// If ``JavaScriptWrapImports`` is ``true``, then the behavior + /// depends on the value of ``ColumnWidth``: + /// + /// * If ``ColumnWidth`` is 0 (no limit on the number of columns), + /// then import statements will keep the number of lines they + /// start with. + /// + /// * If ``ColumnWidth`` is non-zero (there is a limit on the + /// number of columns), then import statements will be wrapped + /// to an appropriate number of lines, or unwrapped into a + /// single line if there is room. + /// /// \code{.js} - /// true: + /// // Before running clang-format + /// import {VeryLongImport, AnotherLongImport, LongImportsAreAnnoying} from 'some/module.js'; + /// import { + /// Foo, + /// Bar, + /// Baz + /// } from 'another/module.js'; + /// + /// JavaScriptWrapImports: false + /// import {VeryLongImport, AnotherLongImport, LongImportsAreAnnoying} from 'some/module.js'; + /// import {Foo, Bar, Baz} from 'another/module.js'; + /// + /// JavaScriptWrapImports: true + /// ColumnWidth: 0 + /// import {VeryLongImport, AnotherLongImport, LongImportsAreAnnoying} from 'some/module.js'; /// import { - /// VeryLongImportsAreAnnoying, - /// VeryLongImportsAreAnnoying, - /// VeryLongImportsAreAnnoying, - /// } from 'some/module.js' + /// Foo, + /// Bar, + /// Baz + /// } from 'another/module.js'; /// - /// false: - /// import {VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying,} from "some/module.js" + /// JavaScriptWrapImports: true + /// ColumnWidth: 80 + /// import { + /// VeryLongImport, + /// AnotherLongImport, + /// LongImportsAreAnnoying + /// } from 'some/module.js'; + /// import {Foo, Bar, Baz} from 'another/module.js'; /// \endcode /// \version 3.9 bool JavaScriptWrapImports; Index: clang/lib/Format/ContinuationIndenter.cpp =================================================================== --- clang/lib/Format/ContinuationIndenter.cpp +++ clang/lib/Format/ContinuationIndenter.cpp @@ -325,12 +325,19 @@ if (Previous.is(tok::l_square) && Previous.is(TT_ObjCMethodExpr)) return false; + if (Style.isJavaScript() && State.Line->Type == LT_ImportStatement && + !Style.JavaScriptWrapImports) + return false; + return !State.Stack.back().NoLineBreak; } bool ContinuationIndenter::mustBreak(const LineState &State) { const FormatToken &Current = *State.NextToken; const FormatToken &Previous = *Current.Previous; + if (Style.isJavaScript() && State.Line->Type == LT_ImportStatement && + Style.ColumnLimit == 0) + return false; if (Style.BraceWrapping.BeforeLambdaBody && Current.CanBreakBefore && Current.is(TT_LambdaLBrace) && Previous.isNot(TT_LineComment)) { auto LambdaBodyLength = getLengthToMatchingParen(Current, State.Stack); Index: clang/unittests/Format/FormatTestJS.cpp =================================================================== --- clang/unittests/Format/FormatTestJS.cpp +++ clang/unittests/Format/FormatTestJS.cpp @@ -1946,6 +1946,7 @@ verifyFormat("import {VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying," " VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying" "} from 'some/module.js';"); + FormatStyle Style = getGoogleJSStyleWithColumns(80); Style.JavaScriptWrapImports = true; verifyFormat("import {\n" @@ -1954,18 +1955,76 @@ " VeryLongImportsAreAnnoying,\n" "} from 'some/module.js';", Style); + verifyFormat("import {A, B} from 'some/module.js';", Style); + Style.JavaScriptWrapImports = false; + verifyFormat("import {VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying," + " VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying" + "} from 'some/module.js';", + Style); + verifyFormat("import {A, B} from 'some/module.js';", Style); + + // For ColumnLimit: 0 tests, we use the code/expected_output form of + // verifyFormat because the output format depends on the input format, so we + // can't let test::messUp modify the input indiscriminately. + Style.ColumnLimit = 0; + Style.JavaScriptWrapImports = true; + verifyFormat("import {\n" + " VeryLongImportsAreAnnoying,\n" + " VeryLongImportsAreAnnoying,\n" + " VeryLongImportsAreAnnoying,\n" + " VeryLongImportsAreAnnoying,\n" + "} from 'some/module.js';", + "import {\n" + " VeryLongImportsAreAnnoying,\n" + " VeryLongImportsAreAnnoying,\n" + " VeryLongImportsAreAnnoying,\n" + " VeryLongImportsAreAnnoying,\n" + "} from 'some/module.js';", + Style); + verifyFormat("import {VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying," + " VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying" + "} from 'some/module.js';", + "import {VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying," + " VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying" + "} from 'some/module.js';", + Style); verifyFormat("import {\n" " A,\n" + " B\n" + "} from 'some/module.js';", + "import {\n" " A,\n" + " B\n" "} from 'some/module.js';", Style); - verifyFormat("export {\n" - " A,\n" + verifyFormat("import {A, B} from 'some/module.js';", + "import {A, B} from 'some/module.js';", + Style); + Style.JavaScriptWrapImports = false; + verifyFormat("import {VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying," + " VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying" + "} from 'some/module.js';", + "import {\n" + " VeryLongImportsAreAnnoying,\n" + " VeryLongImportsAreAnnoying,\n" + " VeryLongImportsAreAnnoying,\n" + " VeryLongImportsAreAnnoying\n" + "} from 'some/module.js';", + Style); + verifyFormat("import {A, B} from 'some/module.js';", + "import {\n" " A,\n" + " B\n" "} from 'some/module.js';", Style); + verifyFormat("import {A, B} from 'some/module.js';", + "import {A, B} from 'some/module.js';", + Style); + + // Here we use the code/expected_output form of verifyFormat because + // test::messUp would mess up the case being tested. Style.ColumnLimit = 40; - // Using this version of verifyFormat because test::messUp hides the issue. + Style.JavaScriptWrapImports = true; verifyFormat("import {\n" " A,\n" "} from\n"