krasimir created this revision.
Herald added a subscriber: cfe-commits.

In C++ code snippets of the form `@field` are common. This makes clang-format
keep them together in text protos, whereas before it would break them.


Repository:
  rC Clang

https://reviews.llvm.org/D48543

Files:
  lib/Format/ContinuationIndenter.cpp
  lib/Format/TokenAnnotator.cpp
  unittests/Format/FormatTestTextProto.cpp


Index: unittests/Format/FormatTestTextProto.cpp
===================================================================
--- unittests/Format/FormatTestTextProto.cpp
+++ unittests/Format/FormatTestTextProto.cpp
@@ -717,5 +717,23 @@
                "# endfile comment");
 }
 
+TEST_F(FormatTestTextProto, KeepsAmpersandsNextToKeys) {
+  verifyFormat("@tmpl { field: 1 }");
+  verifyFormat("@placeholder: 1");
+  verifyFormat("@name <>");
+  verifyFormat("submessage: @base { key: value }");
+  verifyFormat("submessage: @base {\n"
+               "  key: value\n"
+               "  item: {}\n"
+               "}");
+  verifyFormat("submessage: {\n"
+               "  msg: @base {\n"
+               "    yolo: {}\n"
+               "    key: value\n"
+               "  }\n"
+               "  key: value\n"
+               "}");
+}
+
 } // end namespace tooling
 } // end namespace clang
Index: lib/Format/TokenAnnotator.cpp
===================================================================
--- lib/Format/TokenAnnotator.cpp
+++ lib/Format/TokenAnnotator.cpp
@@ -2949,15 +2949,32 @@
   //
   // Be careful to exclude the case  [proto.ext] { ... } since the `]` is
   // the TT_SelectorName there, but we don't want to break inside the brackets.
+  //
+  // Another edge case is @submessage { key: value }, which is a common
+  // substitution placeholder. In this case we want to keep `@` and 
`submessage`
+  // together.
+  //
   // We ensure elsewhere that extensions are always on their own line.
   if ((Style.Language == FormatStyle::LK_Proto ||
        Style.Language == FormatStyle::LK_TextProto) &&
       Right.is(TT_SelectorName) && !Right.is(tok::r_square) && Right.Next) {
+    // Keep `@submessage` together in:
+    // @submessage { key: value }
+    if (Right.Previous && Right.Previous->is(tok::at))
+      return false;
     // Look for the scope opener after selector in cases like:
     // selector { ...
     // selector: { ...
-    FormatToken *LBrace =
-        Right.Next->is(tok::colon) ? Right.Next->Next : Right.Next;
+    // selector: @base { ...
+    FormatToken *LBrace = Right.Next;
+    if (LBrace && LBrace->is(tok::colon)) {
+      LBrace = LBrace->Next;
+      if (LBrace && LBrace->is(tok::at)) {
+        LBrace = LBrace->Next;
+        if (LBrace)
+          LBrace = LBrace->Next;
+      }
+    }
     if (LBrace &&
         // The scope opener is one of {, [, <:
         // selector { ... }
Index: lib/Format/ContinuationIndenter.cpp
===================================================================
--- lib/Format/ContinuationIndenter.cpp
+++ lib/Format/ContinuationIndenter.cpp
@@ -379,7 +379,8 @@
   if (Current.is(TT_ObjCMethodExpr) && !Previous.is(TT_SelectorName) &&
       State.Line->startsWith(TT_ObjCMethodSpecifier))
     return true;
-  if (Current.is(TT_SelectorName) && State.Stack.back().ObjCSelectorNameFound 
&&
+  if (Current.is(TT_SelectorName) && !Previous.is(tok::at) &&
+      State.Stack.back().ObjCSelectorNameFound &&
       State.Stack.back().BreakBeforeParameter)
     return true;
 


Index: unittests/Format/FormatTestTextProto.cpp
===================================================================
--- unittests/Format/FormatTestTextProto.cpp
+++ unittests/Format/FormatTestTextProto.cpp
@@ -717,5 +717,23 @@
                "# endfile comment");
 }
 
+TEST_F(FormatTestTextProto, KeepsAmpersandsNextToKeys) {
+  verifyFormat("@tmpl { field: 1 }");
+  verifyFormat("@placeholder: 1");
+  verifyFormat("@name <>");
+  verifyFormat("submessage: @base { key: value }");
+  verifyFormat("submessage: @base {\n"
+               "  key: value\n"
+               "  item: {}\n"
+               "}");
+  verifyFormat("submessage: {\n"
+               "  msg: @base {\n"
+               "    yolo: {}\n"
+               "    key: value\n"
+               "  }\n"
+               "  key: value\n"
+               "}");
+}
+
 } // end namespace tooling
 } // end namespace clang
Index: lib/Format/TokenAnnotator.cpp
===================================================================
--- lib/Format/TokenAnnotator.cpp
+++ lib/Format/TokenAnnotator.cpp
@@ -2949,15 +2949,32 @@
   //
   // Be careful to exclude the case  [proto.ext] { ... } since the `]` is
   // the TT_SelectorName there, but we don't want to break inside the brackets.
+  //
+  // Another edge case is @submessage { key: value }, which is a common
+  // substitution placeholder. In this case we want to keep `@` and `submessage`
+  // together.
+  //
   // We ensure elsewhere that extensions are always on their own line.
   if ((Style.Language == FormatStyle::LK_Proto ||
        Style.Language == FormatStyle::LK_TextProto) &&
       Right.is(TT_SelectorName) && !Right.is(tok::r_square) && Right.Next) {
+    // Keep `@submessage` together in:
+    // @submessage { key: value }
+    if (Right.Previous && Right.Previous->is(tok::at))
+      return false;
     // Look for the scope opener after selector in cases like:
     // selector { ...
     // selector: { ...
-    FormatToken *LBrace =
-        Right.Next->is(tok::colon) ? Right.Next->Next : Right.Next;
+    // selector: @base { ...
+    FormatToken *LBrace = Right.Next;
+    if (LBrace && LBrace->is(tok::colon)) {
+      LBrace = LBrace->Next;
+      if (LBrace && LBrace->is(tok::at)) {
+        LBrace = LBrace->Next;
+        if (LBrace)
+          LBrace = LBrace->Next;
+      }
+    }
     if (LBrace &&
         // The scope opener is one of {, [, <:
         // selector { ... }
Index: lib/Format/ContinuationIndenter.cpp
===================================================================
--- lib/Format/ContinuationIndenter.cpp
+++ lib/Format/ContinuationIndenter.cpp
@@ -379,7 +379,8 @@
   if (Current.is(TT_ObjCMethodExpr) && !Previous.is(TT_SelectorName) &&
       State.Line->startsWith(TT_ObjCMethodSpecifier))
     return true;
-  if (Current.is(TT_SelectorName) && State.Stack.back().ObjCSelectorNameFound &&
+  if (Current.is(TT_SelectorName) && !Previous.is(tok::at) &&
+      State.Stack.back().ObjCSelectorNameFound &&
       State.Stack.back().BreakBeforeParameter)
     return true;
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D48543: [clang-f... Krasimir Georgiev via Phabricator via cfe-commits

Reply via email to