From 69593b34c37d3bb5ecc02790118e543522a92b00 Mon Sep 17 00:00:00 2001 From: Krasimir Georgiev Date: Mon, 25 Jun 2018 12:43:12 +0000 Subject: [PATCH] [clang-format] Keep @message together in text protos Summary: 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. Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D48543 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@335459 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/ContinuationIndenter.cpp | 3 ++- lib/Format/TokenAnnotator.cpp | 21 +++++++++++++++++++-- unittests/Format/FormatTestTextProto.cpp | 18 ++++++++++++++++++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index 294c58d621..1a5a6deb04 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -379,7 +379,8 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { 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; diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index 3280f25fef..68ea02296b 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -2949,15 +2949,32 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, // // 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 { ... } diff --git a/unittests/Format/FormatTestTextProto.cpp b/unittests/Format/FormatTestTextProto.cpp index 9c4e1a8520..0875bd8925 100644 --- a/unittests/Format/FormatTestTextProto.cpp +++ b/unittests/Format/FormatTestTextProto.cpp @@ -717,5 +717,23 @@ TEST_F(FormatTestTextProto, FormatsCommentsAtEndOfFile) { "# 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 -- 2.40.0