Skip to content

Commit ce33e11

Browse files
committed
[clang-format] Add OneLineFormatOffRegex option
Close #54334
1 parent d403c70 commit ce33e11

File tree

7 files changed

+158
-0
lines changed

7 files changed

+158
-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 line comment that is the first/only token of a
5203+
line, clang-format skips the next line. Otherwise, clang-format skips the
5204+
line that contains 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
@@ -692,6 +692,7 @@ clang-format
692692
top of the file.
693693
- Add ``EnumTrailingComma`` option for inserting/removing commas at the end of
694694
``enum`` enumerator lists.
695+
- Add ``OneLineFormatOffRegex`` option for turning formatting off for one line.
695696

696697
libclang
697698
--------

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 line comment that is the first/only token of a
3659+
/// line, clang-format skips the next line. Otherwise, clang-format skips the
3660+
/// line that contains 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: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,42 @@ 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_CurrentLine;
102+
Tok.Finalized = true;
103+
} else {
104+
FormatOff = FO_None;
105+
}
106+
break;
107+
default:
108+
if (!FormattingDisabled && FormatOffRegex.match(Tok.TokenText)) {
109+
if (Tok.TokenText.starts_with("//") &&
110+
(NewlinesBefore > 0 || &Tok == Tokens.front())) {
111+
FormatOff = FO_NextLine;
112+
} else {
113+
FormatOff = FO_CurrentLine;
114+
for (auto *Token : reverse(Tokens)) {
115+
Token->Finalized = true;
116+
if (Token->NewlinesBefore > 0)
117+
break;
118+
}
119+
}
120+
}
121+
}
88122
if (Style.isJavaScript()) {
89123
tryParseJSRegexLiteral();
90124
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: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24954,6 +24954,82 @@ 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("int i;\n"
24986+
"int j ; // format off\n"
24987+
"int k;",
24988+
"int i ;\n"
24989+
"int j ; // format off\n"
24990+
"int k ;",
24991+
Style);
24992+
24993+
verifyFormat("// clang-format off\n"
24994+
"int i ;\n"
24995+
"int j ; // format off\n"
24996+
"int k ;\n"
24997+
"// clang-format on\n"
24998+
"f();",
24999+
"// clang-format off\n"
25000+
"int i ;\n"
25001+
"int j ; // format off\n"
25002+
"int k ;\n"
25003+
"// clang-format on\n"
25004+
"f() ;",
25005+
Style);
25006+
25007+
Style.OneLineFormatOffRegex = "^/\\* format off \\*/";
25008+
verifyFormat("int i;\n"
25009+
" /* format off */ int j ;\n"
25010+
"int k;",
25011+
"int i ;\n"
25012+
" /* format off */ int j ;\n"
25013+
"int k ;",
25014+
Style);
25015+
verifyFormat("f(\"/* format off */\");", "f(\"/* format off */\") ;", Style);
25016+
25017+
Style.ColumnLimit = 50;
25018+
Style.OneLineFormatOffRegex = "^LogErrorPrint$";
25019+
verifyFormat("myproject::LogErrorPrint(logger, \"Don't split me!\");\n"
25020+
"myproject::MyLogErrorPrinter(myLogger,\n"
25021+
" \"Split me!\");",
25022+
"myproject::LogErrorPrint(logger, \"Don't split me!\");\n"
25023+
"myproject::MyLogErrorPrinter(myLogger, \"Split me!\");",
25024+
Style);
25025+
25026+
Style.OneLineFormatOffRegex = "//(< clang-format off| NO_TRANSLATION)$";
25027+
verifyNoChange(
25028+
"int i ; //< clang-format off\n"
25029+
"msg = sprintf(\"Long string with placeholders.\"); // NO_TRANSLATION",
25030+
Style);
25031+
}
25032+
2495725033
TEST_F(FormatTest, DoNotCrashOnInvalidInput) {
2495825034
format("? ) =");
2495925035
verifyNoCrash("#define a\\\n /**/}");

0 commit comments

Comments
 (0)