Index: clang/docs/ClangFormatStyleOptions.rst =================================================================== --- clang/docs/ClangFormatStyleOptions.rst +++ clang/docs/ClangFormatStyleOptions.rst @@ -1828,6 +1828,20 @@ {} { } + * ``bool AfterCSharpProperty`` Wrap C# setter/getter properties. + + .. code-block:: c++ + + true: + get + {} + set + {} + + false: + get {} + set {} + .. _BreakAfterAttributes: Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -461,6 +461,8 @@ - Add additional Qualifier Ordering support for special cases such as templates, requires clauses, long qualified names. - Fix all known issues associated with ``LambdaBodyIndentation: OuterScope``. +- Add ``AfterCSharpProperty`` style to option ``BraceWrapping`` to allow + newline before the opening brace of ``set;get;``. libclang -------- Index: clang/include/clang/Format/Format.h =================================================================== --- clang/include/clang/Format/Format.h +++ clang/include/clang/Format/Format.h @@ -1205,6 +1205,19 @@ /// \endcode /// bool SplitEmptyNamespace; + /// Wrap C# setter/getter properties. + /// \code + /// true: + /// get + /// {} + /// set + /// {} + /// + /// false: + /// get {} + /// set {} + /// \endcode + bool AfterCSharpProperty; }; /// Control of individual brace wrapping cases. Index: clang/lib/Format/Format.cpp =================================================================== --- clang/lib/Format/Format.cpp +++ clang/lib/Format/Format.cpp @@ -181,6 +181,7 @@ IO.mapOptional("AfterEnum", Wrapping.AfterEnum); IO.mapOptional("AfterExternBlock", Wrapping.AfterExternBlock); IO.mapOptional("AfterFunction", Wrapping.AfterFunction); + IO.mapOptional("AfterCSharpProperty", Wrapping.AfterCSharpProperty); IO.mapOptional("AfterNamespace", Wrapping.AfterNamespace); IO.mapOptional("AfterObjCDeclaration", Wrapping.AfterObjCDeclaration); IO.mapOptional("AfterStruct", Wrapping.AfterStruct); @@ -1188,7 +1189,8 @@ /*IndentBraces=*/false, /*SplitEmptyFunction=*/true, /*SplitEmptyRecord=*/true, - /*SplitEmptyNamespace=*/true}; + /*SplitEmptyNamespace=*/true, + /*AfterCSharpProperty=*/false}; switch (Expanded.BreakBeforeBraces) { case FormatStyle::BS_Linux: Expanded.BraceWrapping.AfterClass = true; @@ -1354,7 +1356,8 @@ /*IndentBraces=*/false, /*SplitEmptyFunction=*/true, /*SplitEmptyRecord=*/true, - /*SplitEmptyNamespace=*/true}; + /*SplitEmptyNamespace=*/true, + /*AfterCSharpProperty=*/false}; LLVMStyle.BreakAfterAttributes = FormatStyle::ABS_Never; LLVMStyle.BreakAfterJavaFieldAnnotations = false; LLVMStyle.BreakArrays = true; Index: clang/lib/Format/TokenAnnotator.cpp =================================================================== --- clang/lib/Format/TokenAnnotator.cpp +++ clang/lib/Format/TokenAnnotator.cpp @@ -4984,6 +4984,13 @@ return true; } + // Handle `string Foo {{set {} get {}}`. + if (Style.isCSharp() && Style.BraceWrapping.AfterCSharpProperty && + (Line.startsWith(Keywords.kw_get) || + Line.startsWith(Keywords.kw_set))) { + return true; + } + // Don't attempt to interpret struct return types as structs. if (Right.isNot(TT_FunctionLBrace)) { return (Line.startsWith(tok::kw_class) && @@ -4993,16 +5000,28 @@ } } - if (Left.is(TT_ObjCBlockLBrace) && - Style.AllowShortBlocksOnASingleLine == FormatStyle::SBS_Never) { + // Handle `string Foo {set;get }`. + if (Style.isCSharp() && Style.BraceWrapping.AfterCSharpProperty && + (( Left.isOneOf(tok::l_brace,tok::semi) && + Right.isOneOf(Keywords.kw_set,Keywords.kw_get)) || + (Left.is(tok::semi) && Left.Previous && + Left.Previous->is(Keywords.kw_get) && Right.is(tok::r_brace)) || + (Left.is(tok::semi) && Left.Previous && + Left.Previous->is(Keywords.kw_set) && Right.is(tok::r_brace)) + )) { return true; } - // Ensure wrapping after __attribute__((XX)) and @interface etc. - if (Left.is(TT_AttributeParen) && Right.is(TT_ObjCDecl)) +if (Left.is(TT_ObjCBlockLBrace) && + Style.AllowShortBlocksOnASingleLine == FormatStyle::SBS_Never) { return true; +} - if (Left.is(TT_LambdaLBrace)) { +// Ensure wrapping after __attribute__((XX)) and @interface etc. +if (Left.is(TT_AttributeParen) && Right.is(TT_ObjCDecl)) + return true; + +if (Left.is(TT_LambdaLBrace)) { if (IsFunctionArgument(Left) && Style.AllowShortLambdasOnASingleLine == FormatStyle::SLS_Inline) { return false; @@ -5014,54 +5033,54 @@ Style.AllowShortLambdasOnASingleLine == FormatStyle::SLS_Empty)) { return true; } - } +} - if (Style.BraceWrapping.BeforeLambdaBody && Right.is(TT_LambdaLBrace) && - Left.isOneOf(tok::star, tok::amp, tok::ampamp, TT_TemplateCloser)) { +if (Style.BraceWrapping.BeforeLambdaBody && Right.is(TT_LambdaLBrace) && + Left.isOneOf(tok::star, tok::amp, tok::ampamp, TT_TemplateCloser)) { return true; - } +} - // Put multiple Java annotation on a new line. - if ((Style.Language == FormatStyle::LK_Java || Style.isJavaScript()) && - Left.is(TT_LeadingJavaAnnotation) && - Right.isNot(TT_LeadingJavaAnnotation) && Right.isNot(tok::l_paren) && - (Line.Last->is(tok::l_brace) || Style.BreakAfterJavaFieldAnnotations)) { +// Put multiple Java annotation on a new line. +if ((Style.Language == FormatStyle::LK_Java || Style.isJavaScript()) && + Left.is(TT_LeadingJavaAnnotation) && + Right.isNot(TT_LeadingJavaAnnotation) && Right.isNot(tok::l_paren) && + (Line.Last->is(tok::l_brace) || Style.BreakAfterJavaFieldAnnotations)) { return true; - } +} - if (Right.is(TT_ProtoExtensionLSquare)) - return true; +if (Right.is(TT_ProtoExtensionLSquare)) + return true; - // In text proto instances if a submessage contains at least 2 entries and at - // least one of them is a submessage, like A { ... B { ... } ... }, - // put all of the entries of A on separate lines by forcing the selector of - // the submessage B to be put on a newline. - // - // Example: these can stay on one line: - // a { scalar_1: 1 scalar_2: 2 } - // a { b { key: value } } - // - // and these entries need to be on a new line even if putting them all in one - // line is under the column limit: - // a { - // scalar: 1 - // b { key: value } - // } - // - // We enforce this by breaking before a submessage field that has previous - // siblings, *and* breaking before a field that follows a submessage field. - // - // 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) { +// In text proto instances if a submessage contains at least 2 entries and at +// least one of them is a submessage, like A { ... B { ... } ... }, +// put all of the entries of A on separate lines by forcing the selector of +// the submessage B to be put on a newline. +// +// Example: these can stay on one line: +// a { scalar_1: 1 scalar_2: 2 } +// a { b { key: value } } +// +// and these entries need to be on a new line even if putting them all in one +// line is under the column limit: +// a { +// scalar: 1 +// b { key: value } +// } +// +// We enforce this by breaking before a submessage field that has previous +// siblings, *and* breaking before a field that follows a submessage field. +// +// 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 (Left.is(tok::at)) @@ -5115,15 +5134,15 @@ // put on a new line anyways. if (Left.isOneOf(tok::r_brace, tok::greater, tok::r_square)) return true; - } +} - // Deal with lambda arguments in C++ - we want consistent line breaks whether - // they happen to be at arg0, arg1 or argN. The selection is a bit nuanced - // as aggressive line breaks are placed when the lambda is not the last arg. - if ((Style.Language == FormatStyle::LK_Cpp || - Style.Language == FormatStyle::LK_ObjC) && - Left.is(tok::l_paren) && Left.BlockParameterCount > 0 && - !Right.isOneOf(tok::l_paren, TT_LambdaLSquare)) { +// Deal with lambda arguments in C++ - we want consistent line breaks whether +// they happen to be at arg0, arg1 or argN. The selection is a bit nuanced +// as aggressive line breaks are placed when the lambda is not the last arg. +if ((Style.Language == FormatStyle::LK_Cpp || + Style.Language == FormatStyle::LK_ObjC) && + Left.is(tok::l_paren) && Left.BlockParameterCount > 0 && + !Right.isOneOf(tok::l_paren, TT_LambdaLSquare)) { // Multiple lambdas in the same function call force line breaks. if (Left.BlockParameterCount > 1) return true; @@ -5139,9 +5158,9 @@ return false; if (!Next->isOneOf(TT_LambdaLSquare, tok::l_brace, tok::caret)) return true; - } +} - return false; +return false; } bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, Index: clang/unittests/Format/FormatTestCSharp.cpp =================================================================== --- clang/unittests/Format/FormatTestCSharp.cpp +++ clang/unittests/Format/FormatTestCSharp.cpp @@ -1604,5 +1604,81 @@ EXPECT_NE("", format("int where b <")); // reduced from crasher } +TEST_F(FormatTestCSharp, PropertyWrapping) { + FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); + + Style.BraceWrapping.AfterCSharpProperty = false; + verifyFormat("class A\n" + "{\n" + " string Foo { set; get; }\n" + "}\n", + Style); + verifyFormat("class A\n" + "{\n" + " string Foo { get; set; }\n" + "}\n", + Style); + verifyFormat("class A\n" + "{\n" + " string Foo\n" + " {\n" + " set {\n" + " val = value;\n" + " }\n" + " get {\n" + " return value;\n" + " }\n" + " }\n" + "}\n", + Style); + + Style.BraceWrapping.AfterCSharpProperty = true; + + verifyFormat("class A\n" + "{\n" + " string Bar {\n" + " set;\n" + " get;\n" + " }\n" + "}\n", + Style); + verifyFormat("class A\n" + "{\n" + " string Bar {\n" + " get;\n" + " set;\n" + " }\n" + "}\n", + Style); + verifyFormat("class A\n" + "{\n" + " string Bar\n" + " {\n" + " set\n" + " {\n" + " val = value;\n" + " }\n" + " get\n" + " {\n" + " return value;\n" + " }\n" + " }\n" + "}\n", + Style); + /* TODO fix the indentation of get; + verifyFormat("class A\n" + "{\n" + " string Bar {\n" + " get;\n" + " set\n" + " {\n" + " val = value;\n" + " }\n" + " }\n" + "}\n", + Style); + */ +} + } // namespace format } // end namespace clang