Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -592,7 +592,8 @@ return false; } } - return true; + // There are no top-level unbalanced braces in text protos. + return Style.Language != FormatStyle::LK_TextProto; } void updateParameterCount(FormatToken *Left, FormatToken *Current) { @@ -712,6 +713,11 @@ } else if (Contexts.back().ContextKind == tok::l_paren) { Tok->Type = TT_InlineASMColon; } + // Detects trailing pieces like key: + if ((Style.Language == FormatStyle::LK_Proto || + Style.Language == FormatStyle::LK_TextProto) && + !CurrentToken) + return false; break; case tok::pipe: case tok::amp: @@ -798,6 +804,10 @@ if (Previous && Previous->Type != TT_DictLiteral) Previous->Type = TT_SelectorName; } + } else if (Style.Language == FormatStyle::LK_TextProto || + Style.Language == FormatStyle::LK_Proto) { + // In TT_Proto and TT_TextProto, tok::less cannot be a binary operator. + return false; } else { Tok->Type = TT_BinaryOperator; NonTemplateLess.insert(Tok); @@ -809,13 +819,16 @@ case tok::r_square: return false; case tok::r_brace: - // Lines can start with '}'. - if (Tok->Previous) + // Lines can start with '}' except in text protos. + if (Tok->Previous || Style.Language == FormatStyle::LK_TextProto) return false; break; case tok::greater: - if (Style.Language != FormatStyle::LK_TextProto) - Tok->Type = TT_BinaryOperator; + // In protos and text protos tok::greater cannot be a binary operator. + if (Style.Language == FormatStyle::LK_Proto || + Style.Language == FormatStyle::LK_TextProto) + return false; + Tok->Type = TT_BinaryOperator; break; case tok::kw_operator: if (Style.Language == FormatStyle::LK_TextProto || Index: unittests/Format/FormatTestProto.cpp =================================================================== --- unittests/Format/FormatTestProto.cpp +++ unittests/Format/FormatTestProto.cpp @@ -18,13 +18,21 @@ namespace format { class FormatTestProto : public ::testing::Test { + enum StatusCheck { SC_ExpectComplete, SC_ExpectIncomplete }; + protected: static std::string format(llvm::StringRef Code, unsigned Offset, - unsigned Length, const FormatStyle &Style) { + unsigned Length, const FormatStyle &Style, + StatusCheck CheckComplete = SC_ExpectComplete) { DEBUG(llvm::errs() << "---\n"); DEBUG(llvm::errs() << Code << "\n\n"); std::vector Ranges(1, tooling::Range(Offset, Length)); - tooling::Replacements Replaces = reformat(Style, Code, Ranges); + FormattingAttemptStatus Status; + tooling::Replacements Replaces = + reformat(Style, Code, Ranges, "", &Status); + bool ExpectedCompleteFormat = CheckComplete == SC_ExpectComplete; + EXPECT_EQ(ExpectedCompleteFormat, Status.FormatComplete) + << Code << "\n\n"; auto Result = applyAllReplacements(Code, Replaces); EXPECT_TRUE(static_cast(Result)); DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); @@ -41,6 +49,12 @@ EXPECT_EQ(Code.str(), format(Code)) << "Expected code is not stable"; EXPECT_EQ(Code.str(), format(test::messUp(Code))); } + + static void verifyIncompleteFormat(llvm::StringRef Code) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_Proto); + EXPECT_EQ(Code.str(), + format(Code, 0, Code.size(), Style, SC_ExpectIncomplete)); + } }; TEST_F(FormatTestProto, FormatsMessages) { @@ -492,5 +506,12 @@ "};"); } +TEST_F(FormatTestProto, IncompleteFormat) { + verifyIncompleteFormat("option ("); + verifyIncompleteFormat("option (MyProto.options) = { bbbbbbbbb:"); + verifyIncompleteFormat("option (MyProto.options) = { bbbbbbbbb: <\n"); + verifyIncompleteFormat("option (MyProto.options) = { bbbbbbbbb: [\n"); +} + } // end namespace tooling } // end namespace clang Index: unittests/Format/FormatTestTextProto.cpp =================================================================== --- unittests/Format/FormatTestTextProto.cpp +++ unittests/Format/FormatTestTextProto.cpp @@ -19,12 +19,20 @@ class FormatTestTextProto : public ::testing::Test { protected: + enum StatusCheck { SC_ExpectComplete, SC_ExpectIncomplete }; + static std::string format(llvm::StringRef Code, unsigned Offset, - unsigned Length, const FormatStyle &Style) { + unsigned Length, const FormatStyle &Style, + StatusCheck CheckComplete = SC_ExpectComplete) { DEBUG(llvm::errs() << "---\n"); DEBUG(llvm::errs() << Code << "\n\n"); std::vector Ranges(1, tooling::Range(Offset, Length)); - tooling::Replacements Replaces = reformat(Style, Code, Ranges); + FormattingAttemptStatus Status; + tooling::Replacements Replaces = + reformat(Style, Code, Ranges, "", &Status); + bool ExpectedCompleteFormat = CheckComplete == SC_ExpectComplete; + EXPECT_EQ(ExpectedCompleteFormat, Status.FormatComplete) + << Code << "\n\n"; auto Result = applyAllReplacements(Code, Replaces); EXPECT_TRUE(static_cast(Result)); DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); @@ -45,6 +53,12 @@ Style.ColumnLimit = 60; // To make writing tests easier. verifyFormat(Code, Style); } + + static void verifyIncompleteFormat(llvm::StringRef Code) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_TextProto); + EXPECT_EQ(Code.str(), + format(Code, 0, Code.size(), Style, SC_ExpectIncomplete)); + } }; TEST_F(FormatTestTextProto, KeepsTopLevelEntriesFittingALine) { @@ -495,5 +509,38 @@ "}", Style); } +TEST_F(FormatTestTextProto, IncompleteFormat) { + verifyIncompleteFormat("data {"); + verifyIncompleteFormat("data <"); + verifyIncompleteFormat("data ["); + verifyIncompleteFormat("data: {"); + verifyIncompleteFormat("data: <"); + verifyIncompleteFormat("data: ["); + verifyIncompleteFormat("key:"); + verifyIncompleteFormat("key:}"); + verifyIncompleteFormat("key: ]"); + verifyIncompleteFormat("key: >"); + verifyIncompleteFormat(": value"); + verifyIncompleteFormat(": {}"); + verifyIncompleteFormat(": <>"); + verifyIncompleteFormat(": []"); + verifyIncompleteFormat("}\n" + "key: value"); + verifyIncompleteFormat("]\n" + "key: value"); + verifyIncompleteFormat("> key: value"); + verifyIncompleteFormat("data { key: {"); + verifyIncompleteFormat("data < key: ["); + verifyIncompleteFormat("data [ key: {"); + verifyIncompleteFormat("> key: value {"); + verifyIncompleteFormat("> key: ["); + verifyIncompleteFormat("}\n" + "key: {"); + verifyIncompleteFormat("data { key: 1 id:"); + verifyIncompleteFormat("}\n" + "key {"); + verifyIncompleteFormat("> <"); +} + } // end namespace tooling } // end namespace clang