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