In Objective-C some style guides use a style where property declarations are
aligned, to help with code readability. I've added an option which enable this
functionality. It is disabled by default.
The option will align code so
```
@property(nonatomic, weak) NSNumber *text;
@property(nonatomic) BOOL trueOrFalse;
```
becomes
```
@property(nonatomic, weak) NSNumber *text;
@property(nonatomic) BOOL trueOrFalse;
```
http://reviews.llvm.org/D9433
Files:
docs/ClangFormatStyleOptions.rst
include/clang/Format/Format.h
lib/Format/Format.cpp
lib/Format/WhitespaceManager.cpp
lib/Format/WhitespaceManager.h
unittests/Format/FormatTest.cpp
EMAIL PREFERENCES
http://reviews.llvm.org/settings/panel/emailpreferences/
Index: docs/ClangFormatStyleOptions.rst
===================================================================
--- docs/ClangFormatStyleOptions.rst
+++ docs/ClangFormatStyleOptions.rst
@@ -398,6 +398,19 @@
Indent in all namespaces.
+**ObjCAlignPropertyDeclaration** (``bool``)
+ If ``true``, aligns Objective-C property declaration to increase
+ readability.
+
+ This will align the Objective-C property declaration that are on
+ consecutive lines. This will result in formattings like
+ \code
+ @property (nonatomic, strong) NSMutableString \*text;
+ @property (nonatomic, strong, readonly) NSString \*readonly;
+ @property (nonatomic, weak) NSNumber \*text;
+ @property (nonatomic) BOOL trueOrFalse;
+ \endcode
+
**ObjCBlockIndentWidth** (``unsigned``)
The number of characters to use for indentation of ObjC blocks.
Index: include/clang/Format/Format.h
===================================================================
--- include/clang/Format/Format.h
+++ include/clang/Format/Format.h
@@ -262,6 +262,19 @@
/// Otherwise puts them into the right-most column.
bool AlignEscapedNewlinesLeft;
+ /// \brief If \c true, aligns Objective-C property declaration to increase
+ /// readability.
+ ///
+ /// This will align the Objective-C property declaration that are on
+ /// consecutive lines. This will result in formattings like
+ /// \code
+ /// @property (nonatomic, strong) NSMutableString *text;
+ /// @property (nonatomic, strong, readonly) NSString *readonly;
+ /// @property (nonatomic, weak) NSNumber *text;
+ /// @property (nonatomic) BOOL trueOrFalse;
+ /// \endcode
+ bool ObjCAlignPropertyDeclaration;
+
/// \brief The number of columns to use for indentation.
unsigned IndentWidth;
Index: lib/Format/Format.cpp
===================================================================
--- lib/Format/Format.cpp
+++ lib/Format/Format.cpp
@@ -175,6 +175,7 @@
IO.mapOptional("AlignOperands", Style.AlignOperands);
IO.mapOptional("AlignTrailingComments", Style.AlignTrailingComments);
IO.mapOptional("AlignConsecutiveAssignments", Style.AlignConsecutiveAssignments);
+ IO.mapOptional("ObjCAlignPropertyDeclaration", Style.ObjCAlignPropertyDeclaration);
IO.mapOptional("AllowAllParametersOfDeclarationOnNextLine",
Style.AllowAllParametersOfDeclarationOnNextLine);
IO.mapOptional("AllowShortBlocksOnASingleLine",
@@ -364,6 +365,7 @@
LLVMStyle.MaxEmptyLinesToKeep = 1;
LLVMStyle.KeepEmptyLinesAtTheStartOfBlocks = true;
LLVMStyle.NamespaceIndentation = FormatStyle::NI_None;
+ LLVMStyle.ObjCAlignPropertyDeclaration = false;
LLVMStyle.ObjCBlockIndentWidth = 2;
LLVMStyle.ObjCSpaceAfterProperty = false;
LLVMStyle.ObjCSpaceBeforeProtocolList = true;
Index: lib/Format/WhitespaceManager.cpp
===================================================================
--- lib/Format/WhitespaceManager.cpp
+++ lib/Format/WhitespaceManager.cpp
@@ -29,12 +29,13 @@
bool CreateReplacement, const SourceRange &OriginalWhitespaceRange,
unsigned IndentLevel, int Spaces, unsigned StartOfTokenColumn,
unsigned NewlinesBefore, StringRef PreviousLinePostfix,
- StringRef CurrentLinePrefix, tok::TokenKind Kind, bool ContinuesPPDirective)
+ StringRef CurrentLinePrefix, tok::TokenKind Kind,
+ tok::ObjCKeywordKind ObjCKind, bool ContinuesPPDirective)
: CreateReplacement(CreateReplacement),
OriginalWhitespaceRange(OriginalWhitespaceRange),
StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore),
PreviousLinePostfix(PreviousLinePostfix),
- CurrentLinePrefix(CurrentLinePrefix), Kind(Kind),
+ CurrentLinePrefix(CurrentLinePrefix), Kind(Kind), ObjCKind(ObjCKind),
ContinuesPPDirective(ContinuesPPDirective), IndentLevel(IndentLevel),
Spaces(Spaces), IsTrailingComment(false), TokenLength(0),
PreviousEndOfTokenColumn(0), EscapedNewlineColumn(0),
@@ -54,7 +55,8 @@
Tok.Decision = (Newlines > 0) ? FD_Break : FD_Continue;
Changes.push_back(Change(true, Tok.WhitespaceRange, IndentLevel, Spaces,
StartOfTokenColumn, Newlines, "", "",
- Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst));
+ Tok.Tok.getKind(), Tok.Tok.getObjCKeywordID(),
+ InPPDirective && !Tok.IsFirst));
}
void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
@@ -64,6 +66,7 @@
Changes.push_back(Change(false, Tok.WhitespaceRange, /*IndentLevel=*/0,
/*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore,
"", "", Tok.Tok.getKind(),
+ Tok.Tok.getObjCKeywordID(),
InPPDirective && !Tok.IsFirst));
}
@@ -84,7 +87,7 @@
// calculate the new length of the comment and to calculate the changes
// for which to do the alignment when aligning comments.
Tok.is(TT_LineComment) && Newlines > 0 ? tok::comment : tok::unknown,
- InPPDirective && !Tok.IsFirst));
+ Tok.Tok.getObjCKeywordID(), InPPDirective && !Tok.IsFirst));
}
const tooling::Replacements &WhitespaceManager::generateReplacements() {
@@ -94,6 +97,7 @@
std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr));
calculateLineBreakInformation();
alignConsecutiveAssignments();
+ alignObjCPropertyDeclarations();
alignTrailingComments();
alignEscapedNewlines();
generateChanges();
@@ -232,6 +236,189 @@
}
}
+// Walk through all of the changed and find the property declarations to align.
+// We do so in two passes. The first will find consecutive lines which begin
+// with a property token and then find the start of the type declaration,
+// the first token after the right parenthesis. These are then aligned. In the
+// second pass we detect the same property tokens but look for the ending
+// semi-colon. We then align on either the variable name (if there is no
+// pointer or the pointer is not right aligned) or the pointer symbol.
+void WhitespaceManager::alignObjCPropertyDeclarations() {
+ if (!Style.ObjCAlignPropertyDeclaration)
+ return;
+
+ unsigned StartOfSequence = 0;
+ unsigned EndOfSequence = 0;
+ bool FoundPropertyOnLine = false;
+ bool FoundRightParenOnLine = false;
+ bool FoundSemiColonOnLine = false;
+ unsigned MinTypeColumn = 0;
+ unsigned MinVariableColumn = 0;
+
+ auto AlignSequence = [&] {
+ if (MinTypeColumn > 0) {
+ alignObjCPropertyTypeDeclarations(StartOfSequence, EndOfSequence,
+ MinTypeColumn);
+ } else if (MinVariableColumn > 0) {
+ alignObjCPropertyVariableDeclarations(StartOfSequence, EndOfSequence,
+ MinVariableColumn);
+ }
+ StartOfSequence = 0;
+ EndOfSequence = 0;
+ MinTypeColumn = 0;
+ MinVariableColumn = 0;
+ };
+
+ auto BeginNewline = [&](unsigned i) {
+ if (StartOfSequence > 0 &&
+ (Changes[i].NewlinesBefore > 1 || !FoundPropertyOnLine ||
+ (FoundPropertyOnLine &&
+ (!FoundRightParenOnLine || !FoundSemiColonOnLine)))) {
+ EndOfSequence = i - 1;
+ AlignSequence();
+ }
+ FoundPropertyOnLine = false;
+ FoundRightParenOnLine = false;
+ FoundSemiColonOnLine = false;
+ };
+
+ for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
+ if (Changes[i].NewlinesBefore != 0)
+ BeginNewline(i);
+
+ if (Changes[i].ObjCKind == tok::objc_property) {
+ FoundPropertyOnLine = true;
+ if (StartOfSequence == 0)
+ StartOfSequence = i;
+ } else if (FoundPropertyOnLine && Changes[i].Kind == tok::r_paren &&
+ !FoundRightParenOnLine) {
+ FoundRightParenOnLine = true;
+ if (i + 1 != Changes.size()) {
+ unsigned ChangeTypeColumn = Changes[i + 1].StartOfTokenColumn;
+ MinTypeColumn = std::max(MinTypeColumn, ChangeTypeColumn);
+ }
+ } else if (Changes[i].Kind == tok::semi && !FoundSemiColonOnLine) {
+ FoundSemiColonOnLine = true;
+ }
+ }
+
+ if (StartOfSequence > 0) {
+ EndOfSequence = Changes.size();
+ AlignSequence();
+ }
+
+ StartOfSequence = 0;
+ EndOfSequence = 0;
+ FoundPropertyOnLine = false;
+ FoundRightParenOnLine = false;
+ FoundSemiColonOnLine = false;
+ MinTypeColumn = 0;
+ MinVariableColumn = 0;
+
+ for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
+ // Check if we started a newline and if a sequence ended. Align it if we
+ // did.
+ if (Changes[i].NewlinesBefore != 0)
+ BeginNewline(i);
+
+ if (Changes[i].ObjCKind == tok::objc_property) {
+ FoundPropertyOnLine = true;
+ if (StartOfSequence == 0)
+ StartOfSequence = i;
+ } else if (FoundPropertyOnLine && Changes[i].Kind == tok::r_paren &&
+ !FoundRightParenOnLine) {
+ FoundRightParenOnLine = true;
+ } else if (FoundRightParenOnLine && Changes[i].Kind == tok::semi &&
+ !FoundSemiColonOnLine) {
+ FoundSemiColonOnLine = true;
+ if (Changes[i - 1].Kind == tok::identifier) {
+ unsigned VariableChangeIndex = i - 1;
+ if (Style.PointerAlignment == FormatStyle::PAS_Right &&
+ Changes[i - 2].Kind == tok::star)
+ VariableChangeIndex = i - 2;
+ unsigned ChangeVariableColumn =
+ Changes[VariableChangeIndex].StartOfTokenColumn;
+ MinVariableColumn = std::max(MinVariableColumn, ChangeVariableColumn);
+ }
+ }
+ }
+
+ if (StartOfSequence > 0) {
+ EndOfSequence = Changes.size();
+ AlignSequence();
+ }
+}
+
+void WhitespaceManager::alignObjCPropertyTypeDeclarations(unsigned Start,
+ unsigned End,
+ unsigned Column) {
+ bool FoundRightParenOnLine = false;
+ unsigned PreviousShift = 0;
+ for (unsigned i = Start; i != End; ++i) {
+ int Shift = 0;
+ if (Changes[i].NewlinesBefore > 0) {
+ FoundRightParenOnLine = false;
+ PreviousShift = 0;
+ }
+
+ if (Changes[i].Kind == tok::r_paren && !FoundRightParenOnLine) {
+ FoundRightParenOnLine = true;
+ if (i + 1 != Changes.size()) {
+ Shift = Column - Changes[i + 1].StartOfTokenColumn;
+ assert(Shift >= 0);
+ Changes[i + 1].Spaces += Shift;
+ if (i + 2 != Changes.size())
+ Changes[i + 2].PreviousEndOfTokenColumn += Shift;
+ Changes[i + 1].StartOfTokenColumn += Shift;
+ PreviousShift = Shift;
+ }
+ }
+
+ if (FoundRightParenOnLine) {
+ Changes[i].StartOfTokenColumn += PreviousShift;
+ if (i + 1 != Changes.size())
+ Changes[i + 1].PreviousEndOfTokenColumn += PreviousShift;
+ }
+ }
+}
+
+void WhitespaceManager::alignObjCPropertyVariableDeclarations(unsigned Start,
+ unsigned End,
+ unsigned Column) {
+ bool FoundSemiColonOnLine = false;
+ unsigned PreviousShift = 0;
+ for (unsigned i = Start; i != End; ++i) {
+ int Shift = 0;
+ if (Changes[i].NewlinesBefore > 0) {
+ FoundSemiColonOnLine = false;
+ PreviousShift = 0;
+ }
+
+ if (Changes[i].Kind == tok::semi && !FoundSemiColonOnLine) {
+ FoundSemiColonOnLine = true;
+ if (Changes[i - 1].Kind == tok::identifier) {
+ unsigned VariableChangeIndex = i - 1;
+ if (Style.PointerAlignment == FormatStyle::PAS_Right &&
+ Changes[i - 2].Kind == tok::star)
+ VariableChangeIndex = i - 2;
+ Shift = Column - Changes[VariableChangeIndex].StartOfTokenColumn;
+ assert(Shift >= 0);
+ Changes[VariableChangeIndex].Spaces += Shift;
+ if (VariableChangeIndex + 1 != Changes.size())
+ Changes[VariableChangeIndex + 1].PreviousEndOfTokenColumn += Shift;
+ Changes[VariableChangeIndex + 1].StartOfTokenColumn += Shift;
+ PreviousShift = Shift;
+ }
+ }
+
+ if (FoundSemiColonOnLine) {
+ Changes[i].StartOfTokenColumn += PreviousShift;
+ if (i + 1 != Changes.size())
+ Changes[i + 1].PreviousEndOfTokenColumn += PreviousShift;
+ }
+ }
+}
+
void WhitespaceManager::alignTrailingComments() {
unsigned MinColumn = 0;
unsigned MaxColumn = UINT_MAX;
Index: lib/Format/WhitespaceManager.h
===================================================================
--- lib/Format/WhitespaceManager.h
+++ lib/Format/WhitespaceManager.h
@@ -110,7 +110,7 @@
unsigned IndentLevel, int Spaces, unsigned StartOfTokenColumn,
unsigned NewlinesBefore, StringRef PreviousLinePostfix,
StringRef CurrentLinePrefix, tok::TokenKind Kind,
- bool ContinuesPPDirective);
+ tok::ObjCKeywordKind ObjCKind, bool ContinuesPPDirective);
bool CreateReplacement;
// Changes might be in the middle of a token, so we cannot just keep the
@@ -125,6 +125,7 @@
// FIXME: Currently this is not set correctly for breaks inside comments, as
// the \c BreakableToken is still doing its own alignment.
tok::TokenKind Kind;
+ tok::ObjCKeywordKind ObjCKind;
bool ContinuesPPDirective;
// The number of nested blocks the token is in. This is used to add tabs
@@ -171,6 +172,18 @@
/// the specified \p Column.
void alignConsecutiveAssignments(unsigned Start, unsigned End, unsigned Column);
+ /// \brief Aligns Objective-C property declarations over all \c Changes.
+ void alignObjCPropertyDeclarations();
+
+ /// \brief Aligns the type declaration part of Objective-C property
+ /// declarations from change \p Start to change \p End at the specified
+ /// \p Column.
+ void alignObjCPropertyTypeDeclarations(unsigned Start, unsigned End, unsigned Column);
+
+ /// \brief Aligns the variable name part of Objective-C property declarations
+ /// from change \p Start to change \p End at the specified \p Column.
+ void alignObjCPropertyVariableDeclarations(unsigned Start, unsigned End, unsigned Column);
+
/// \brief Align trailing comments over all \c Changes.
void alignTrailingComments();
Index: unittests/Format/FormatTest.cpp
===================================================================
--- unittests/Format/FormatTest.cpp
+++ unittests/Format/FormatTest.cpp
@@ -8549,6 +8549,110 @@
Alignment);
}
+TEST_F(FormatTest, ObjCAlignPropertyDeclaration) {
+ FormatStyle PropertyAlignment = getLLVMStyle();
+ PropertyAlignment.ObjCAlignPropertyDeclaration = false;
+ verifyFormat("@property(nonatomic, strong) NSMutableString *text;\n"
+ "@property(nonatomic, strong, readonly) NSString *readonly;\n"
+ "@property(nonatomic, weak) NSNumber *text;\n"
+ "@property(nonatomic) BOOL trueOrFalse;",
+ PropertyAlignment);
+
+ PropertyAlignment.ObjCAlignPropertyDeclaration = true;
+ verifyFormat("@property(nonatomic, strong) NSMutableString *text;\n"
+ "@property(nonatomic, strong, readonly) NSString *readonly;\n"
+ "@property(nonatomic, weak) NSNumber *text;\n"
+ "@property(nonatomic) BOOL trueOrFalse;",
+ PropertyAlignment);
+ verifyFormat("@property(nonatomic, strong) NSMutableString *text;\n"
+ "@property(nonatomic, strong, readonly) NSString *readonly;\n"
+ "// Nothing here\n"
+ "@property(nonatomic, weak) NSNumber *text;\n"
+ "@property(nonatomic) BOOL trueOrFalse;",
+ PropertyAlignment);
+ verifyFormat("@property(nonatomic, strong, readonly) NSString *readonly;\n"
+ "@property(nonatomic, strong) NSMutableString *text;\n"
+ "// Nothing here\n"
+ "@property(nonatomic) BOOL trueOrFalse;\n"
+ "@property(nonatomic, weak) NSNumber *text;\n",
+ PropertyAlignment);
+ verifyFormat("@property(nonatomic, strong, getter=iNeedText) NSMutableString *text;\n"
+ "@property(nonatomic, strong, readonly) NSString *readonly;",
+ PropertyAlignment);
+ verifyFormat("@property(nonatomic, strong) NSArray /* NSString */ *text;\n"
+ "@property(nonatomic, strong, readonly) NSString *readonly;",
+ PropertyAlignment);
+
+ verifyFormat("@property(nonatomic) BOOL trueOrFalse;\n"
+ "@property(nonatomic, strong)\n"
+ " NSLooooooooooooooonnnnnnngggggggTyyyyyppppppeeee *loooooooooonnnnngVariable;\n"
+ "@property(nonatomic, weak) NSNumber *text;\n"
+ "@property(nonatomic, weak, readonly) NSArray *array;",
+ PropertyAlignment);
+ verifyFormat("@property(nonatomic, strong)\n"
+ " NSLooooooooooooooonnnnnnngggggggTyyyyyppppppeeee *loooooooooonnnnngVariable;\n"
+ "@property(nonatomic, weak) NSNumber *text;\n"
+ "@property(nonatomic, weak, readonly) NSArray *array;",
+ PropertyAlignment);
+ verifyFormat("@property(nonatomic, weak) NSNumberThingy *text;\n"
+ "@property(nonatomic, strong) NSArray *array;\n"
+ "@property(nonatomic, strong)\n"
+ " NSLooooooooooooooonnnnnnngggggggTyyyyyppppppeeee *loooooooooonnnnngVariable;\n"
+ "@property(nonatomic, weak) NSNumber *text;\n"
+ "@property(nonatomic, weak, readonly) NSArray *array;",
+ PropertyAlignment);
+ verifyFormat("@property(nonatomic, weak) NSNumber *text;\n"
+ "@property(nonatomic, weak, readonly) NSArray *array;\n"
+ "@property(nonatomic, strong)\n"
+ " NSLooooooooooooooonnnnnnngggggggTyyyyyppppppeeee *loooooooooonnnnngVariable;",
+ PropertyAlignment);
+ verifyFormat("@property(nonatomic, strong) NSMutableString *text;\n"
+ "@property(nonatomic, strong, readonly) NSString *\n"
+ " loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongVariableName;\n"
+ "@property(nonatomic, weak) NSNumber *text;\n"
+ "@property(nonatomic) BOOL trueOrFalse;",
+ PropertyAlignment);
+
+ PropertyAlignment.AlignTrailingComments = false;
+ verifyFormat("@property(nonatomic, strong) NSMutableString *text; // Comment\n"
+ "@property(nonatomic, strong, readonly) NSString *readonly; // Comment",
+ PropertyAlignment);
+ verifyFormat("@property(nonatomic, strong, readonly) NSString *readonly; // Comment\n"
+ "@property(nonatomic, strong) NSMutableString *text; // Comment",
+ PropertyAlignment);
+ PropertyAlignment.AlignTrailingComments = true;
+ verifyFormat("@property(nonatomic, strong) NSMutableString *text; // Comment\n"
+ "@property(nonatomic, strong, readonly) NSString *readonly; // Comment",
+ PropertyAlignment);
+ verifyFormat("@property(nonatomic, strong, readonly) NSString *readonly; // Comment\n"
+ "@property(nonatomic, strong) NSMutableString *text; // Comment",
+ PropertyAlignment);
+
+ PropertyAlignment.ObjCSpaceAfterProperty = true;
+ verifyFormat("@property (nonatomic, strong) NSMutableString *text;\n"
+ "@property (nonatomic, strong, readonly) NSString *readonly;\n"
+ "@property (nonatomic, weak) NSNumber *text;\n"
+ "@property (nonatomic) BOOL trueOrFalse;",
+ PropertyAlignment);
+ PropertyAlignment.PointerAlignment = FormatStyle::PAS_Left;
+ verifyFormat("@property (nonatomic, strong) NSMutableString* text;\n"
+ "@property (nonatomic, strong, readonly) NSString* readonly;",
+ PropertyAlignment);
+ PropertyAlignment.PointerAlignment = FormatStyle::PAS_Middle;
+ verifyFormat("@property (nonatomic, strong) NSMutableString * text;\n"
+ "@property (nonatomic, strong, readonly) NSString * readonly;",
+ PropertyAlignment);
+ PropertyAlignment.ObjCSpaceAfterProperty = false;
+ PropertyAlignment.PointerAlignment = FormatStyle::PAS_Left;
+ verifyFormat("@property(nonatomic, strong) NSMutableString* text;\n"
+ "@property(nonatomic, strong, readonly) NSString* readonly;",
+ PropertyAlignment);
+ PropertyAlignment.PointerAlignment = FormatStyle::PAS_Middle;
+ verifyFormat("@property(nonatomic, strong) NSMutableString * text;\n"
+ "@property(nonatomic, strong, readonly) NSString * readonly;",
+ PropertyAlignment);
+}
+
TEST_F(FormatTest, LinuxBraceBreaking) {
FormatStyle LinuxBraceStyle = getLLVMStyle();
LinuxBraceStyle.BreakBeforeBraces = FormatStyle::BS_Linux;
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits