Skip to content

Commit b8bb1cc

Browse files
authored
[clang-format] Add OneLineFormatOffRegex option (#137577)
Close #54334
1 parent f175030 commit b8bb1cc

File tree

7 files changed

+182
-0
lines changed

7 files changed

+182
-0
lines changed

clang/docs/ClangFormatStyleOptions.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5195,6 +5195,29 @@ the configuration (without a prefix: ``Auto``).
51955195
Add a space in front of an Objective-C protocol list, i.e. use
51965196
``Foo <Protocol>`` instead of ``Foo<Protocol>``.
51975197

5198+
.. _OneLineFormatOffRegex:
5199+
5200+
**OneLineFormatOffRegex** (``String``) :versionbadge:`clang-format 21` :ref:`<OneLineFormatOffRegex>`
5201+
A regular expression that describes markers for turning formatting off for
5202+
one line. If it matches a comment that is the only token of a line,
5203+
clang-format skips the comment and the next line. Otherwise, clang-format
5204+
skips lines containing a matched token.
5205+
5206+
.. code-block:: c++
5207+
5208+
// OneLineFormatOffRegex: ^(// NOLINT|logger$)
5209+
// results in the output below:
5210+
int a;
5211+
int b ; // NOLINT
5212+
int c;
5213+
// NOLINTNEXTLINE
5214+
int d ;
5215+
int e;
5216+
s = "// NOLINT";
5217+
logger() ;
5218+
logger2();
5219+
my_logger();
5220+
51985221
.. _PPIndentWidth:
51995222

52005223
**PPIndentWidth** (``Integer``) :versionbadge:`clang-format 13` :ref:`<PPIndentWidth>`

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,7 @@ clang-format
720720
top of the file.
721721
- Add ``EnumTrailingComma`` option for inserting/removing commas at the end of
722722
``enum`` enumerator lists.
723+
- Add ``OneLineFormatOffRegex`` option for turning formatting off for one line.
723724

724725
libclang
725726
--------

clang/include/clang/Format/Format.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3654,6 +3654,27 @@ struct FormatStyle {
36543654
/// \version 3.7
36553655
bool ObjCSpaceBeforeProtocolList;
36563656

3657+
/// A regular expression that describes markers for turning formatting off for
3658+
/// one line. If it matches a comment that is the only token of a line,
3659+
/// clang-format skips the comment and the next line. Otherwise, clang-format
3660+
/// skips lines containing a matched token.
3661+
/// \code
3662+
/// // OneLineFormatOffRegex: ^(// NOLINT|logger$)
3663+
/// // results in the output below:
3664+
/// int a;
3665+
/// int b ; // NOLINT
3666+
/// int c;
3667+
/// // NOLINTNEXTLINE
3668+
/// int d ;
3669+
/// int e;
3670+
/// s = "// NOLINT";
3671+
/// logger() ;
3672+
/// logger2();
3673+
/// my_logger();
3674+
/// \endcode
3675+
/// \version 21
3676+
std::string OneLineFormatOffRegex;
3677+
36573678
/// Different ways to try to fit all constructor initializers on a line.
36583679
enum PackConstructorInitializersStyle : int8_t {
36593680
/// Always put each constructor initializer on its own line.
@@ -5399,6 +5420,7 @@ struct FormatStyle {
53995420
ObjCPropertyAttributeOrder == R.ObjCPropertyAttributeOrder &&
54005421
ObjCSpaceAfterProperty == R.ObjCSpaceAfterProperty &&
54015422
ObjCSpaceBeforeProtocolList == R.ObjCSpaceBeforeProtocolList &&
5423+
OneLineFormatOffRegex == R.OneLineFormatOffRegex &&
54025424
PackConstructorInitializers == R.PackConstructorInitializers &&
54035425
PenaltyBreakAssignment == R.PenaltyBreakAssignment &&
54045426
PenaltyBreakBeforeFirstCallParameter ==

clang/lib/Format/Format.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,7 @@ template <> struct MappingTraits<FormatStyle> {
11001100
IO.mapOptional("ObjCSpaceAfterProperty", Style.ObjCSpaceAfterProperty);
11011101
IO.mapOptional("ObjCSpaceBeforeProtocolList",
11021102
Style.ObjCSpaceBeforeProtocolList);
1103+
IO.mapOptional("OneLineFormatOffRegex", Style.OneLineFormatOffRegex);
11031104
IO.mapOptional("PackConstructorInitializers",
11041105
Style.PackConstructorInitializers);
11051106
IO.mapOptional("PenaltyBreakAssignment", Style.PenaltyBreakAssignment);

clang/lib/Format/FormatTokenLexer.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,43 @@ FormatTokenLexer::FormatTokenLexer(
8383
ArrayRef<FormatToken *> FormatTokenLexer::lex() {
8484
assert(Tokens.empty());
8585
assert(FirstInLineIndex == 0);
86+
const llvm::Regex FormatOffRegex(Style.OneLineFormatOffRegex);
87+
enum { FO_None, FO_CurrentLine, FO_NextLine } FormatOff = FO_None;
8688
do {
8789
Tokens.push_back(getNextToken());
90+
auto &Tok = *Tokens.back();
91+
const auto NewlinesBefore = Tok.NewlinesBefore;
92+
switch (FormatOff) {
93+
case FO_CurrentLine:
94+
if (NewlinesBefore == 0)
95+
Tok.Finalized = true;
96+
else
97+
FormatOff = FO_None;
98+
break;
99+
case FO_NextLine:
100+
if (NewlinesBefore > 1) {
101+
FormatOff = FO_None;
102+
} else {
103+
Tok.Finalized = true;
104+
FormatOff = FO_CurrentLine;
105+
}
106+
break;
107+
default:
108+
if (!FormattingDisabled && FormatOffRegex.match(Tok.TokenText)) {
109+
if (Tok.is(tok::comment) &&
110+
(NewlinesBefore > 0 || &Tok == Tokens.front())) {
111+
Tok.Finalized = true;
112+
FormatOff = FO_NextLine;
113+
} else {
114+
for (auto *Token : reverse(Tokens)) {
115+
Token->Finalized = true;
116+
if (Token->NewlinesBefore > 0)
117+
break;
118+
}
119+
FormatOff = FO_CurrentLine;
120+
}
121+
}
122+
}
88123
if (Style.isJavaScript()) {
89124
tryParseJSRegexLiteral();
90125
handleTemplateStrings();

clang/unittests/Format/ConfigParseTest.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ TEST(ConfigParseTest, ParsesConfiguration) {
295295
FormatStyle Style = {};
296296
Style.Language = FormatStyle::LK_Cpp;
297297
CHECK_PARSE("CommentPragmas: '// abc$'", CommentPragmas, "// abc$");
298+
CHECK_PARSE("OneLineFormatOffRegex: // ab$", OneLineFormatOffRegex, "// ab$");
298299

299300
Style.QualifierAlignment = FormatStyle::QAS_Right;
300301
CHECK_PARSE("QualifierAlignment: Leave", QualifierAlignment,

clang/unittests/Format/FormatTest.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24954,6 +24954,105 @@ TEST_F(FormatTest, DisableRegions) {
2495424954
"// clang-format on");
2495524955
}
2495624956

24957+
TEST_F(FormatTest, OneLineFormatOffRegex) {
24958+
auto Style = getLLVMStyle();
24959+
Style.OneLineFormatOffRegex = "// format off$";
24960+
24961+
verifyFormat(" // format off\n"
24962+
" int i ;\n"
24963+
"int j;",
24964+
" // format off\n"
24965+
" int i ;\n"
24966+
" int j ;",
24967+
Style);
24968+
verifyFormat("// format off?\n"
24969+
"int i;",
24970+
" // format off?\n"
24971+
" int i ;",
24972+
Style);
24973+
verifyFormat("f(\"// format off\");", " f(\"// format off\") ;", Style);
24974+
24975+
verifyFormat("int i;\n"
24976+
" // format off\n"
24977+
" int j ;\n"
24978+
"int k;",
24979+
" int i ;\n"
24980+
" // format off\n"
24981+
" int j ;\n"
24982+
" int k ;",
24983+
Style);
24984+
24985+
verifyFormat(" // format off\n"
24986+
"\n"
24987+
"int i;",
24988+
" // format off\n"
24989+
" \n"
24990+
" int i ;",
24991+
Style);
24992+
24993+
verifyFormat("int i;\n"
24994+
" int j ; // format off\n"
24995+
"int k;",
24996+
" int i ;\n"
24997+
" int j ; // format off\n"
24998+
" int k ;",
24999+
Style);
25000+
25001+
verifyFormat("// clang-format off\n"
25002+
" int i ;\n"
25003+
" int j ; // format off\n"
25004+
" int k ;\n"
25005+
"// clang-format on\n"
25006+
"f();",
25007+
" // clang-format off\n"
25008+
" int i ;\n"
25009+
" int j ; // format off\n"
25010+
" int k ;\n"
25011+
" // clang-format on\n"
25012+
" f() ;",
25013+
Style);
25014+
25015+
Style.OneLineFormatOffRegex = "^/\\* format off \\*/";
25016+
verifyFormat("int i;\n"
25017+
" /* format off */ int j ;\n"
25018+
"int k;",
25019+
" int i ;\n"
25020+
" /* format off */ int j ;\n"
25021+
" int k ;",
25022+
Style);
25023+
verifyFormat("f(\"/* format off */\");", " f(\"/* format off */\") ;", Style);
25024+
25025+
Style.AlignEscapedNewlines = FormatStyle::ENAS_DontAlign;
25026+
verifyFormat("#define A \\\n"
25027+
" do { \\\n"
25028+
" /* format off */\\\n"
25029+
" f() ; \\\n"
25030+
" g(); \\\n"
25031+
" } while (0)",
25032+
"# define A\\\n"
25033+
" do{ \\\n"
25034+
" /* format off */\\\n"
25035+
" f() ; \\\n"
25036+
" g() ;\\\n"
25037+
" } while (0 )",
25038+
Style);
25039+
25040+
Style.ColumnLimit = 50;
25041+
Style.OneLineFormatOffRegex = "^LogErrorPrint$";
25042+
verifyFormat(" myproject::LogErrorPrint(logger, \"Don't split me!\");\n"
25043+
"myproject::MyLogErrorPrinter(myLogger,\n"
25044+
" \"Split me!\");",
25045+
" myproject::LogErrorPrint(logger, \"Don't split me!\");\n"
25046+
" myproject::MyLogErrorPrinter(myLogger, \"Split me!\");",
25047+
Style);
25048+
25049+
Style.OneLineFormatOffRegex = "//(< clang-format off| NO_TRANSLATION)$";
25050+
verifyNoChange(
25051+
" int i ; //< clang-format off\n"
25052+
" msg = sprintf(\"Long string with placeholders.\"); // NO_TRANSLATION",
25053+
Style);
25054+
}
25055+
2495725056
TEST_F(FormatTest, DoNotCrashOnInvalidInput) {
2495825057
format("? ) =");
2495925058
verifyNoCrash("#define a\\\n /**/}");

0 commit comments

Comments
 (0)