Skip to content

Commit 470eca4

Browse files
committed
[clang-format] Handle Java text blocks
Fix #61954
1 parent eb341f0 commit 470eca4

File tree

3 files changed

+99
-0
lines changed

3 files changed

+99
-0
lines changed

clang/lib/Format/FormatTokenLexer.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,49 @@ bool FormatTokenLexer::canPrecedeRegexLiteral(FormatToken *Prev) {
694694
return true;
695695
}
696696

697+
void FormatTokenLexer::tryParseJavaTextBlock() {
698+
if (FormatTok->TokenText != "\"\"")
699+
return;
700+
701+
const auto *Str = Lex->getBufferLocation();
702+
const auto *End = Lex->getBuffer().end();
703+
704+
if (Str == End || *Str != '\"')
705+
return;
706+
707+
// Skip the `"""` that begins a text block.
708+
const auto *S = Str + 1;
709+
710+
// From docs.oracle.com/en/java/javase/15/text-blocks/#text-block-syntax:
711+
// A text block begins with three double-quote characters followed by a line
712+
// terminator.
713+
while (S < End && *S != '\n') {
714+
if (!isblank(*S))
715+
return;
716+
++S;
717+
}
718+
719+
// Find the `"""` that ends the text block.
720+
for (int Count = 0; Count < 3; ++S) {
721+
if (S == End)
722+
return;
723+
724+
switch (*S) {
725+
case '\\':
726+
Count = -1;
727+
break;
728+
case '\"':
729+
++Count;
730+
break;
731+
default:
732+
Count = 0;
733+
}
734+
}
735+
736+
// Skip the text block.
737+
resetLexer(SourceMgr.getFileOffset(Lex->getSourceLocation(S)));
738+
}
739+
697740
// Tries to parse a JavaScript Regex literal starting at the current token,
698741
// if that begins with a slash and is in a location where JavaScript allows
699742
// regex literals. Changes the current token to a regex literal and updates
@@ -1374,6 +1417,8 @@ FormatToken *FormatTokenLexer::getNextToken() {
13741417
FormatTok->TokenText = FormatTok->TokenText.substr(0, 1);
13751418
++Column;
13761419
StateStack.push(LexerState::TOKEN_STASHED);
1420+
} else if (Style.isJava() && FormatTok->is(tok::string_literal)) {
1421+
tryParseJavaTextBlock();
13771422
}
13781423

13791424
if (Style.isVerilog() && Tokens.size() > 0 &&

clang/lib/Format/FormatTokenLexer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ class FormatTokenLexer {
7272

7373
bool canPrecedeRegexLiteral(FormatToken *Prev);
7474

75+
void tryParseJavaTextBlock();
76+
7577
// Tries to parse a JavaScript Regex literal starting at the current token,
7678
// if that begins with a slash and is in a location where JavaScript allows
7779
// regex literals. Changes the current token to a regex literal and updates

clang/unittests/Format/FormatTestJava.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,58 @@ TEST_F(FormatTestJava, AlignCaseArrows) {
791791
Style);
792792
}
793793

794+
TEST_F(FormatTestJava, TextBlock) {
795+
verifyNoChange("String myStr = \"\"\"\n"
796+
"hello\n"
797+
"there\n"
798+
"\"\"\";");
799+
800+
verifyNoChange("String tb = \"\"\"\n"
801+
" the new\"\"\";");
802+
803+
verifyNoChange("System.out.println(\"\"\"\n"
804+
" This is the first line\n"
805+
" This is the second line\n"
806+
" \"\"\");");
807+
808+
verifyNoChange("void writeHTML() {\n"
809+
" String html = \"\"\" \n"
810+
" <html>\n"
811+
" <p>Hello World.</p>\n"
812+
" </html>\n"
813+
"\"\"\";\n"
814+
" writeOutput(html);\n"
815+
"}");
816+
817+
verifyNoChange("String colors = \"\"\"\t\n"
818+
" red\n"
819+
" green\n"
820+
" blue\"\"\".indent(4);");
821+
822+
verifyNoChange("String code = \"\"\"\n"
823+
" String source = \\\"\"\"\n"
824+
" String message = \"Hello, World!\";\n"
825+
" System.out.println(message);\n"
826+
" \\\"\"\";\n"
827+
" \"\"\";");
828+
829+
verifyNoChange(
830+
"class Outer {\n"
831+
" void printPoetry() {\n"
832+
" String lilacs = \"\"\"\n"
833+
"Passing the apple-tree blows of white and pink in the orchards\n"
834+
"\"\"\";\n"
835+
" System.out.println(lilacs);\n"
836+
" }\n"
837+
"}");
838+
839+
verifyNoChange("String name = \"\"\"\n"
840+
" red\n"
841+
" green\n"
842+
" blue\\\n"
843+
" \"\"\";");
844+
}
845+
794846
} // namespace
795847
} // namespace test
796848
} // namespace format

0 commit comments

Comments
 (0)