[clang-format] Handle Java text blocks (#141334)

Fix #61954

(cherry picked from commit b7f5950bb3b97eac979925a3bbf015530c26962e)
This commit is contained in:
Owen Pan 2025-05-25 15:40:45 -07:00 committed by Tom Stellard
parent 0e1ef696f1
commit 4b6e5a2866
3 changed files with 92 additions and 0 deletions

View File

@ -636,6 +636,36 @@ bool FormatTokenLexer::canPrecedeRegexLiteral(FormatToken *Prev) {
return true;
}
void FormatTokenLexer::tryParseJavaTextBlock() {
if (FormatTok->TokenText != "\"\"")
return;
const auto *S = Lex->getBufferLocation();
const auto *End = Lex->getBuffer().end();
if (S == End || *S != '\"')
return;
++S; // Skip the `"""` that begins a text block.
// Find the `"""` that ends the text block.
for (int Count = 0; Count < 3 && S < End; ++S) {
switch (*S) {
case '\\':
Count = -1;
break;
case '\"':
++Count;
break;
default:
Count = 0;
}
}
// Ignore the possibly invalid text block.
resetLexer(SourceMgr.getFileOffset(Lex->getSourceLocation(S)));
}
// Tries to parse a JavaScript Regex literal starting at the current token,
// if that begins with a slash and is in a location where JavaScript allows
// regex literals. Changes the current token to a regex literal and updates
@ -1326,6 +1356,9 @@ FormatToken *FormatTokenLexer::getNextToken() {
FormatTok->TokenText = FormatTok->TokenText.substr(0, 1);
++Column;
StateStack.push(LexerState::TOKEN_STASHED);
} else if (Style.Language == FormatStyle::LK_Java &&
FormatTok->is(tok::string_literal)) {
tryParseJavaTextBlock();
}
if (Style.isVerilog() && Tokens.size() > 0 &&

View File

@ -71,6 +71,8 @@ private:
bool canPrecedeRegexLiteral(FormatToken *Prev);
void tryParseJavaTextBlock();
// Tries to parse a JavaScript Regex literal starting at the current token,
// if that begins with a slash and is in a location where JavaScript allows
// regex literals. Changes the current token to a regex literal and updates

View File

@ -789,6 +789,63 @@ TEST_F(FormatTestJava, AlignCaseArrows) {
Style);
}
TEST_F(FormatTestJava, TextBlock) {
verifyNoChange("String myStr = \"\"\"\n"
"hello\n"
"there\n"
"\"\"\";");
verifyNoChange("String tb = \"\"\"\n"
" the new\"\"\";");
verifyNoChange("System.out.println(\"\"\"\n"
" This is the first line\n"
" This is the second line\n"
" \"\"\");");
verifyNoChange("void writeHTML() {\n"
" String html = \"\"\" \n"
" <html>\n"
" <p>Hello World.</p>\n"
" </html>\n"
"\"\"\";\n"
" writeOutput(html);\n"
"}");
verifyNoChange("String colors = \"\"\"\t\n"
" red\n"
" green\n"
" blue\"\"\".indent(4);");
verifyNoChange("String code = \"\"\"\n"
" String source = \\\"\"\"\n"
" String message = \"Hello, World!\";\n"
" System.out.println(message);\n"
" \\\"\"\";\n"
" \"\"\";");
verifyNoChange(
"class Outer {\n"
" void printPoetry() {\n"
" String lilacs = \"\"\"\n"
"Passing the apple-tree blows of white and pink in the orchards\n"
"\"\"\";\n"
" System.out.println(lilacs);\n"
" }\n"
"}");
verifyNoChange("String name = \"\"\"\r\n"
" red\n"
" green\n"
" blue\\\n"
" \"\"\";");
verifyFormat("String name = \"\"\"Pat Q. Smith\"\"\";");
verifyNoChange("String name = \"\"\"\n"
" Pat Q. Smith");
}
} // namespace
} // namespace test
} // namespace format