Index: docs/ClangFormatStyleOptions.rst =================================================================== --- docs/ClangFormatStyleOptions.rst +++ docs/ClangFormatStyleOptions.rst @@ -994,9 +994,9 @@ .. code-block:: c++ - Constructor() - : initializer1(), - initializer2() + Constructor() + : initializer1(), + initializer2() * ``BCIS_BeforeComma`` (in configuration: ``BeforeComma``) Break constructor initializers before the colon and commas, and align @@ -1004,18 +1004,18 @@ .. code-block:: c++ - Constructor() - : initializer1() - , initializer2() + Constructor() + : initializer1() + , initializer2() * ``BCIS_AfterColon`` (in configuration: ``AfterColon``) Break constructor initializers after the colon and commas. .. code-block:: c++ - Constructor() : - initializer1(), - initializer2() + Constructor() : + initializer1(), + initializer2() @@ -1201,7 +1201,8 @@ * ``IBS_Regroup`` (in configuration: ``Regroup``) Merge multiple ``#include`` blocks together and sort as one. - Then split into groups based on category priority. See ``IncludeCategories``. + Then split into groups based on category priority. See + ``IncludeCategories``. .. code-block:: c++ @@ -1577,24 +1578,38 @@ **RawStringFormats** (``std::vector``) - Raw string delimiters denoting that the raw string contents are - code in a particular language and can be reformatted. + Defines hints for detecting supported languages code blocks in raw + strings. + + A raw string with a matching delimiter or a matching enclosing function + name will be reformatted assuming the specified language based on the + style for that language defined in the .clang-format file. If no style has + been defined in the .clang-format file for the specific language, a + predefined style given by 'BasedOnStyle' is used. If 'BasedOnStyle' is not + found, the formatting is based on llvm style. A matching delimiter takes + precedence over a matching enclosing function name for determining the + language of the raw string contents. - A raw string with a matching delimiter will be reformatted assuming the - specified language based on a predefined style given by 'BasedOnStyle'. - If 'BasedOnStyle' is not found, the formatting is based on llvm style. + There should be at most one specification per language and each delimiter + and enclosing function should not occur in multiple specifications. To configure this in the .clang-format file, use: .. code-block:: yaml RawStringFormats: - - Delimiter: 'pb' - Language: TextProto - BasedOnStyle: llvm - - Delimiter: 'proto' - Language: TextProto - BasedOnStyle: google + - Language: TextProto + Delimiters: + - 'pb' + - 'proto' + EnclosingFunctions: + - 'PARSE_TEXT_PROTO' + BasedOnStyle: google + - Language: Cpp + Delimiters: + - 'cc' + - 'cpp' + BasedOnStyle: llvm **ReflowComments** (``bool``) If ``true``, clang-format will attempt to re-flow comments. Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -1367,12 +1367,15 @@ LanguageKind Language; /// \brief A list of raw string delimiters that match this language. std::vector Delimiters; + /// \brief A list of enclosing function names that match this language. + std::vector EnclosingFunctions; /// \brief The style name on which this raw string format is based on. /// If not specified, the raw string format is based on the style that this /// format is based on. std::string BasedOnStyle; bool operator==(const RawStringFormat &Other) const { return Language == Other.Language && Delimiters == Other.Delimiters && + EnclosingFunctions == Other.EnclosingFunctions && BasedOnStyle == Other.BasedOnStyle; } }; @@ -1380,12 +1383,17 @@ /// \brief Defines hints for detecting supported languages code blocks in raw /// strings. /// - /// A raw string with a matching delimiter will be reformatted assuming the - /// specified language based on the style for that language defined in the - /// .clang-format file. If no style has been defined in the .clang-format file - /// for the specific language, a predefined style given by 'BasedOnStyle' is - /// used. If 'BasedOnStyle' is not found, the formatting is based on llvm - /// style. + /// A raw string with a matching delimiter or a matching enclosing function + /// name will be reformatted assuming the specified language based on the + /// style for that language defined in the .clang-format file. If no style has + /// been defined in the .clang-format file for the specific language, a + /// predefined style given by 'BasedOnStyle' is used. If 'BasedOnStyle' is not + /// found, the formatting is based on llvm style. A matching delimiter takes + /// precedence over a matching enclosing function name for determining the + /// language of the raw string contents. + /// + /// There should be at most one specification per language and each delimiter + /// and enclosing function should not occur in multiple specifications. /// /// To configure this in the .clang-format file, use: /// \code{.yaml} @@ -1394,6 +1402,8 @@ /// Delimiters: /// - 'pb' /// - 'proto' + /// EnclosingFunctions: + /// - 'PARSE_TEXT_PROTO' /// BasedOnStyle: google /// - Language: Cpp /// Delimiters: Index: lib/Format/ContinuationIndenter.h =================================================================== --- lib/Format/ContinuationIndenter.h +++ lib/Format/ContinuationIndenter.h @@ -38,10 +38,14 @@ struct RawStringFormatStyleManager { llvm::StringMap DelimiterStyle; + llvm::StringMap EnclosingFunctionStyle; RawStringFormatStyleManager(const FormatStyle &CodeStyle); - llvm::Optional get(StringRef Delimiter) const; + llvm::Optional getDelimiterStyle(StringRef Delimiter) const; + + llvm::Optional + getEnclosingFunctionStyle(StringRef EnclosingFunction) const; }; class ContinuationIndenter { Index: lib/Format/ContinuationIndenter.cpp =================================================================== --- lib/Format/ContinuationIndenter.cpp +++ lib/Format/ContinuationIndenter.cpp @@ -105,32 +105,44 @@ RawStringFormatStyleManager::RawStringFormatStyleManager( const FormatStyle &CodeStyle) { for (const auto &RawStringFormat : CodeStyle.RawStringFormats) { - for (StringRef Delimiter : RawStringFormat.Delimiters) { - llvm::Optional LanguageStyle = - CodeStyle.GetLanguageStyle(RawStringFormat.Language); - if (!LanguageStyle) { - FormatStyle PredefinedStyle; - if (!getPredefinedStyle(RawStringFormat.BasedOnStyle, - RawStringFormat.Language, &PredefinedStyle)) { - PredefinedStyle = getLLVMStyle(); - PredefinedStyle.Language = RawStringFormat.Language; - } - LanguageStyle = PredefinedStyle; + llvm::Optional LanguageStyle = + CodeStyle.GetLanguageStyle(RawStringFormat.Language); + if (!LanguageStyle) { + FormatStyle PredefinedStyle; + if (!getPredefinedStyle(RawStringFormat.BasedOnStyle, + RawStringFormat.Language, &PredefinedStyle)) { + PredefinedStyle = getLLVMStyle(); + PredefinedStyle.Language = RawStringFormat.Language; } - LanguageStyle->ColumnLimit = CodeStyle.ColumnLimit; + LanguageStyle = PredefinedStyle; + } + LanguageStyle->ColumnLimit = CodeStyle.ColumnLimit; + for (StringRef Delimiter : RawStringFormat.Delimiters) { DelimiterStyle.insert({Delimiter, *LanguageStyle}); } + for (StringRef EnclosingFunction : RawStringFormat.EnclosingFunctions) { + EnclosingFunctionStyle.insert({EnclosingFunction, *LanguageStyle}); + } } } llvm::Optional -RawStringFormatStyleManager::get(StringRef Delimiter) const { +RawStringFormatStyleManager::getDelimiterStyle(StringRef Delimiter) const { auto It = DelimiterStyle.find(Delimiter); if (It == DelimiterStyle.end()) return None; return It->second; } +llvm::Optional +RawStringFormatStyleManager::getEnclosingFunctionStyle( + StringRef EnclosingFunction) const { + auto It = EnclosingFunctionStyle.find(EnclosingFunction); + if (It == EnclosingFunctionStyle.end()) + return None; + return It->second; +} + ContinuationIndenter::ContinuationIndenter(const FormatStyle &Style, const AdditionalKeywords &Keywords, const SourceManager &SourceMgr, @@ -1437,6 +1449,26 @@ return Penalty; } +// Returns the enclosing function name of a token, or the empty string if not +// found. +static StringRef getEnclosingFunctionName(const FormatToken &Current) { + // Look for: 'function(' or 'function(' before Current. + auto Tok = Current.getPreviousNonComment(); + if (!Tok || !Tok->is(tok::l_paren)) + return ""; + Tok = Tok->getPreviousNonComment(); + if (!Tok) + return ""; + if (Tok->is(TT_TemplateCloser)) { + Tok = Tok->MatchingParen; + if (Tok) + Tok = Tok->getPreviousNonComment(); + } + if (!Tok || !Tok->is(tok::identifier)) + return ""; + return Tok->TokenText; +} + llvm::Optional ContinuationIndenter::getRawStringStyle(const FormatToken &Current, const LineState &State) { @@ -1445,7 +1477,10 @@ auto Delimiter = getRawStringDelimiter(Current.TokenText); if (!Delimiter) return None; - auto RawStringStyle = RawStringFormats.get(*Delimiter); + auto RawStringStyle = RawStringFormats.getDelimiterStyle(*Delimiter); + if (!RawStringStyle) + RawStringStyle = RawStringFormats.getEnclosingFunctionStyle( + getEnclosingFunctionName(Current)); if (!RawStringStyle) return None; RawStringStyle->ColumnLimit = getColumnLimit(State); Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -457,6 +457,7 @@ static void mapping(IO &IO, FormatStyle::RawStringFormat &Format) { IO.mapOptional("Language", Format.Language); IO.mapOptional("Delimiters", Format.Delimiters); + IO.mapOptional("EnclosingFunctions", Format.EnclosingFunctions); IO.mapOptional("BasedOnStyle", Format.BasedOnStyle); } }; @@ -705,6 +706,12 @@ "textproto", "TEXTPROTO", }, + /*EnclosingFunctionNames=*/ + { + "EqualsProto", + "PARSE_TEXT_PROTO", + "ParseTextProto", + }, /*BasedOnStyle=*/"google", }}; GoogleStyle.SpacesBeforeTrailingComments = 2; Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -10408,8 +10408,18 @@ Style.RawStringFormats.clear(); std::vector ExpectedRawStringFormats = { - {FormatStyle::LK_TextProto, {"pb", "proto"}, "llvm"}, - {FormatStyle::LK_Cpp, {"cc", "cpp"}, "google"}, + { + FormatStyle::LK_TextProto, + {"pb", "proto"}, + {"PARSE_TEXT_PROTO"}, + "llvm", + }, + { + FormatStyle::LK_Cpp, + {"cc", "cpp"}, + {"C_CODEBLOCK", "CPPEVAL"}, + "", + }, }; CHECK_PARSE("RawStringFormats:\n" @@ -10417,12 +10427,16 @@ " Delimiters:\n" " - 'pb'\n" " - 'proto'\n" + " EnclosingFunctions:\n" + " - 'PARSE_TEXT_PROTO'\n" " BasedOnStyle: llvm\n" " - Language: Cpp\n" " Delimiters:\n" " - 'cc'\n" " - 'cpp'\n" - " BasedOnStyle: google\n", + " EnclosingFunctions:\n" + " - 'C_CODEBLOCK'\n" + " - 'CPPEVAL'\n", RawStringFormats, ExpectedRawStringFormats); } Index: unittests/Format/FormatTestRawStrings.cpp =================================================================== --- unittests/Format/FormatTestRawStrings.cpp +++ unittests/Format/FormatTestRawStrings.cpp @@ -65,23 +65,32 @@ FormatStyle getRawStringPbStyleWithColumns(unsigned ColumnLimit) { FormatStyle Style = getLLVMStyle(); Style.ColumnLimit = ColumnLimit; - Style.RawStringFormats = {{/*Language=*/FormatStyle::LK_TextProto, - /*Delimiters=*/{"pb"}, - /*BasedOnStyle=*/"google"}}; + Style.RawStringFormats = { + {/*Language=*/FormatStyle::LK_TextProto, + /*Delimiters=*/{"pb"}, + /*EnclosingFunctions=*/{}, + /*BasedOnStyle=*/"google"}, + }; return Style; } FormatStyle getRawStringLLVMCppStyleBasedOn(std::string BasedOnStyle) { FormatStyle Style = getLLVMStyle(); - Style.RawStringFormats = {{/*Language=*/FormatStyle::LK_Cpp, - /*Delimiters=*/{"cpp"}, BasedOnStyle}}; + Style.RawStringFormats = { + {/*Language=*/FormatStyle::LK_Cpp, + /*Delimiters=*/{"cpp"}, + /*EnclosingFunctions=*/{}, BasedOnStyle}, + }; return Style; } FormatStyle getRawStringGoogleCppStyleBasedOn(std::string BasedOnStyle) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_Cpp); - Style.RawStringFormats = {{/*Language=*/FormatStyle::LK_Cpp, - /*Delimiters=*/{"cpp"}, BasedOnStyle}}; + Style.RawStringFormats = { + {/*Language=*/FormatStyle::LK_Cpp, + /*Delimiters=*/{"cpp"}, + /*EnclosingFunctions=*/{}, BasedOnStyle}, + }; return Style; } @@ -122,7 +131,7 @@ EXPECT_EQ(0, parseConfiguration("---\n" "Language: Cpp\n" "BasedOnStyle: Google", &Style).value()); - Style.RawStringFormats = {{FormatStyle::LK_Cpp, {"cpp"}, "llvm"}}; + Style.RawStringFormats = {{FormatStyle::LK_Cpp, {"cpp"}, {}, "llvm"}}; expect_eq(R"test(int* i = R"cpp(int* j = 0;)cpp";)test", format(R"test(int * i = R"cpp(int * j = 0;)cpp";)test", Style)); } @@ -720,6 +729,29 @@ getRawStringPbStyleWithColumns(20))); } +TEST_F(FormatTestRawStrings, FormatsRawStringsWithEnclosingFunctionName) { + FormatStyle Style = getRawStringPbStyleWithColumns(40); + Style.RawStringFormats[0].EnclosingFunctions.push_back( + "PARSE_TEXT_PROTO"); + Style.RawStringFormats[0].EnclosingFunctions.push_back("ParseTextProto"); + expect_eq(R"test(a = PARSE_TEXT_PROTO(R"(key: value)");)test", + format(R"test(a = PARSE_TEXT_PROTO(R"(key:value)");)test", Style)); + + expect_eq(R"test( +a = PARSE_TEXT_PROTO /**/ ( + /**/ R"(key: value)");)test", + format(R"test( +a = PARSE_TEXT_PROTO/**/(/**/R"(key:value)");)test", + Style)); + + expect_eq(R"test( +a = ParseTextProto( + R"(key: value)");)test", + format(R"test( +a = ParseTextProto(R"(key:value)");)test", + Style)); +} + } // end namespace } // end namespace format } // end namespace clang