MyDeveloperDay created this revision.
MyDeveloperDay added reviewers: HazardyKnusperkeks, curdeius, owenpan.
MyDeveloperDay added a project: clang-format.
Herald added a project: All.
MyDeveloperDay requested review of this revision.
Herald added a project: clang.

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)


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D133589

Files:
  clang/docs/ClangFormatStyleOptions.rst
  clang/include/clang/Format/Format.h
  clang/lib/Format/Format.cpp
  clang/lib/Format/TokenAnnotator.cpp
  clang/unittests/Format/FormatTestJson.cpp

Index: clang/unittests/Format/FormatTestJson.cpp
===================================================================
--- clang/unittests/Format/FormatTestJson.cpp
+++ clang/unittests/Format/FormatTestJson.cpp
@@ -159,6 +159,27 @@
                "]");
 }
 
+TEST_F(FormatTestJson, JsonArrayOneLine) {
+  FormatStyle Style = getLLVMStyle(FormatStyle::LK_Json);
+  Style.JsonMultilineArrays = 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;
Index: clang/lib/Format/TokenAnnotator.cpp
===================================================================
--- clang/lib/Format/TokenAnnotator.cpp
+++ clang/lib/Format/TokenAnnotator.cpp
@@ -4399,18 +4399,48 @@
     // }
     if (Left.is(TT_DictLiteral) && Left.is(tok::l_brace))
       return true;
-    // Always break after a JSON array opener.
+    // Always break after a JSON array opener based on JsonMultilineArrays
     // [
     // ]
     if (Left.is(TT_ArrayInitializerLSquare) && Left.is(tok::l_square) &&
         !Right.is(tok::r_square)) {
-      return true;
+      if (Right.is(tok::l_brace))
+        return true;
+      // scan to the right if an we see an object or an array inside
+      // then break
+      const auto *Tok = &Right;
+      while (Tok) {
+        if (Tok->isOneOf(tok::l_brace, tok::l_square)) {
+          return true;
+        }
+        if (Tok->isOneOf(tok::r_brace, tok::r_square)) {
+          break;
+        }
+        Tok = Tok->Next;
+      }
+      return Style.JsonMultilineArrays;
     }
     // Always break after successive entries.
     // 1,
     // 2
-    if (Left.is(tok::comma))
-      return true;
+    if (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
+      const auto *Tok = &Right;
+      while (Tok) {
+        if (Tok->isOneOf(tok::l_brace, tok::l_square)) {
+          return true;
+        }
+        if (Tok->isOneOf(tok::r_brace, tok::r_square)) {
+          break;
+        }
+        Tok = Tok->Next;
+      }
+      return Style.JsonMultilineArrays;
+    }
   }
 
   // If the last token before a '}', ']', or ')' is a comma or a trailing
Index: clang/lib/Format/Format.cpp
===================================================================
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -756,6 +756,8 @@
     IO.mapOptional("ExperimentalAutoDetectBinPacking",
                    Style.ExperimentalAutoDetectBinPacking);
 
+    IO.mapOptional("JsonMultilineArrays", Style.JsonMultilineArrays);
+
     IO.mapOptional("PackConstructorInitializers",
                    Style.PackConstructorInitializers);
     // For backward compatibility:
@@ -1249,6 +1251,7 @@
   LLVMStyle.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Never;
   LLVMStyle.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_LogicalBlock;
   LLVMStyle.ExperimentalAutoDetectBinPacking = false;
+  LLVMStyle.JsonMultilineArrays = true;
   LLVMStyle.PackConstructorInitializers = FormatStyle::PCIS_BinPack;
   LLVMStyle.FixNamespaceComments = true;
   LLVMStyle.ForEachMacros.push_back("foreach");
Index: clang/include/clang/Format/Format.h
===================================================================
--- clang/include/clang/Format/Format.h
+++ clang/include/clang/Format/Format.h
@@ -2058,6 +2058,21 @@
   /// \version 12
   EmptyLineBeforeAccessModifierStyle EmptyLineBeforeAccessModifier;
 
+  /// 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)
+  /// \code
+  ///    true:                                  false:
+  ///    [                          vs.      [1, 2, 3, 4]
+  ///      1,
+  ///      2,
+  ///      3,
+  ///      4
+  ///    ]
+  /// \endcode
+  /// \version 16
+  bool JsonMultilineArrays;
+
   /// If ``true``, clang-format detects whether function calls and
   /// definitions are formatted with one parameter per line.
   ///
@@ -3897,6 +3912,7 @@
            DisableFormat == R.DisableFormat &&
            EmptyLineAfterAccessModifier == R.EmptyLineAfterAccessModifier &&
            EmptyLineBeforeAccessModifier == R.EmptyLineBeforeAccessModifier &&
+           JsonMultilineArrays == R.JsonMultilineArrays &&
            ExperimentalAutoDetectBinPacking ==
                R.ExperimentalAutoDetectBinPacking &&
            PackConstructorInitializers == R.PackConstructorInitializers &&
Index: clang/docs/ClangFormatStyleOptions.rst
===================================================================
--- clang/docs/ClangFormatStyleOptions.rst
+++ clang/docs/ClangFormatStyleOptions.rst
@@ -3112,6 +3112,21 @@
      false:
      import {VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying,} from "some/module.js"
 
+**JsonMultilineArrays** (``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)
+
+  .. code-block:: c++
+
+     true:                                  false:
+     [                          vs.      [1, 2, 3, 4]
+       1,
+       2,
+       3,
+       4
+     ]
+
 **KeepEmptyLinesAtTheStartOfBlocks** (``Boolean``) :versionbadge:`clang-format 3.7`
   If true, the empty line at the start of blocks is kept.
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to