From 95b39471110680f609cf56f5babf031a1c855d64 Mon Sep 17 00:00:00 2001 From: mydeveloperday Date: Mon, 19 Sep 2022 17:48:58 +0100 Subject: [PATCH] [clang-format] JSON formatting add new option for controlling newlines in json arrays Working in a mixed environment of both vscode/vim with a team configured prettier configuration, this can leave clang-format and prettier fighting each other over the formatting of arrays, both simple arrays of elements. This review aims to add some "control knobs" to the Json formatting in clang-format to help align the two tools so they can be used interchangeably. This will allow simply arrays `[1, 2, 3]` to remain on a single line but will break those arrays based on context within that array. Happy to change the name of the option (this is the third name I tried) Reviewed By: HazardyKnusperkeks, owenpan Differential Revision: https://reviews.llvm.org/D133589 --- clang/docs/ClangFormatStyleOptions.rst | 17 +++++++++++++++++ clang/include/clang/Format/Format.h | 18 ++++++++++++++++++ clang/lib/Format/Format.cpp | 2 ++ clang/lib/Format/TokenAnnotator.cpp | 26 +++++++++++++++----------- clang/unittests/Format/FormatTestJson.cpp | 21 +++++++++++++++++++++ 5 files changed, 73 insertions(+), 11 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 01ebbd7..3be2915 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -1698,6 +1698,23 @@ the configuration (without a prefix: ``Auto``). @Mock DataLoad loader; +**BreakArrays** (``Boolean``) :versionbadge:`clang-format 16` + If ``true``, clang-format will always break after a Json array `[` + otherwise it will scan until the closing `]` to determine if it should add + newlines between elements (prettier compatible). + + NOTE: This is currently only for formatting JSON. + + .. code-block:: c++ + + true: false: + [ vs. [1, 2, 3, 4] + 1, + 2, + 3, + 4 + ] + **BreakBeforeBinaryOperators** (``BinaryOperatorStyle``) :versionbadge:`clang-format 3.6` The way to wrap binary operators. diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 818dbe0..c8b22f5 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -872,6 +872,23 @@ struct FormatStyle { /// \version 3.7 bool BinPackParameters; + /// If ``true``, clang-format will always break after a Json array `[` + /// otherwise it will scan until the closing `]` to determine if it should add + /// newlines between elements (prettier compatible). + /// + /// NOTE: This is currently only for formatting JSON. + /// \code + /// true: false: + /// [ vs. [1, 2, 3, 4] + /// 1, + /// 2, + /// 3, + /// 4 + /// ] + /// \endcode + /// \version 16 + bool BreakArrays; + /// The style of wrapping parameters on the same line (bin-packed) or /// on one line each. enum BinPackStyle : int8_t { @@ -3878,6 +3895,7 @@ struct FormatStyle { AttributeMacros == R.AttributeMacros && BinPackArguments == R.BinPackArguments && BinPackParameters == R.BinPackParameters && + BreakArrays == R.BreakArrays && BreakBeforeBinaryOperators == R.BreakBeforeBinaryOperators && BreakBeforeBraces == R.BreakBeforeBraces && BreakBeforeConceptDeclarations == R.BreakBeforeConceptDeclarations && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 0327a7c..58d5fc1 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -728,6 +728,7 @@ template <> struct MappingTraits { IO.mapOptional("BreakAfterJavaFieldAnnotations", Style.BreakAfterJavaFieldAnnotations); + IO.mapOptional("BreakArrays", Style.BreakArrays); IO.mapOptional("BreakStringLiterals", Style.BreakStringLiterals); IO.mapOptional("ColumnLimit", Style.ColumnLimit); IO.mapOptional("CommentPragmas", Style.CommentPragmas); @@ -1249,6 +1250,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Never; LLVMStyle.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_LogicalBlock; LLVMStyle.ExperimentalAutoDetectBinPacking = false; + LLVMStyle.BreakArrays = true; LLVMStyle.PackConstructorInitializers = FormatStyle::PCIS_BinPack; LLVMStyle.FixNamespaceComments = true; LLVMStyle.ForEachMacros.push_back("foreach"); diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index e530120..21c2115 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -4401,18 +4401,22 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, // } if (Left.is(TT_DictLiteral) && Left.is(tok::l_brace)) return true; - // Always break after a JSON array opener. - // [ - // ] - if (Left.is(TT_ArrayInitializerLSquare) && Left.is(tok::l_square) && - !Right.is(tok::r_square)) { - return true; + // Always break after a JSON array opener based on BreakArrays. + if ((Left.is(TT_ArrayInitializerLSquare) && Left.is(tok::l_square) && + Right.isNot(tok::r_square)) || + Left.is(tok::comma)) { + if (Right.is(tok::l_brace)) + return true; + // scan to the right if an we see an object or an array inside + // then break. + for (const auto *Tok = &Right; Tok; Tok = Tok->Next) { + if (Tok->isOneOf(tok::l_brace, tok::l_square)) + return true; + if (Tok->isOneOf(tok::r_brace, tok::r_square)) + break; + } + return Style.BreakArrays; } - // Always break after successive entries. - // 1, - // 2 - if (Left.is(tok::comma)) - return true; } // If the last token before a '}', ']', or ')' is a comma or a trailing diff --git a/clang/unittests/Format/FormatTestJson.cpp b/clang/unittests/Format/FormatTestJson.cpp index e213f6b..17fc3b7 100644 --- a/clang/unittests/Format/FormatTestJson.cpp +++ b/clang/unittests/Format/FormatTestJson.cpp @@ -159,6 +159,27 @@ TEST_F(FormatTestJson, JsonArray) { "]"); } +TEST_F(FormatTestJson, JsonArrayOneLine) { + FormatStyle Style = getLLVMStyle(FormatStyle::LK_Json); + Style.BreakArrays = false; + Style.SpacesInContainerLiterals = false; + verifyFormat("[]", Style); + verifyFormat("[1]", Style); + verifyFormat("[1, 2]", Style); + verifyFormat("[1, 2, 3]", Style); + verifyFormat("[1, 2, 3, 4]", Style); + verifyFormat("[1, 2, 3, 4, 5]", Style); + + verifyFormat("[\n" + " 1,\n" + " 2,\n" + " {\n" + " A: 1\n" + " }\n" + "]", + Style); +} + TEST_F(FormatTestJson, JsonNoStringSplit) { FormatStyle Style = getLLVMStyle(FormatStyle::LK_Json); Style.IndentWidth = 4; -- 2.7.4