mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
AbstractJavaBlock: added formatting for java text blocks (IDEA-216737)
GitOrigin-RevId: 091cd09f3dac6b9d3dc130d5e2e65bee5fd88792
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8b1b040a7a
commit
37137f76ac
@@ -1,7 +1,6 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -64,22 +63,19 @@ public class TextBlockMigrationInspection extends AbstractBaseJavaLocalInspectio
|
||||
if (expression == null) return;
|
||||
Document document = PsiDocumentManager.getInstance(project).getDocument(expression.getContainingFile());
|
||||
if (document == null) return;
|
||||
int expressionOffset = expression.getTextOffset();
|
||||
int offset = expressionOffset - document.getLineStartOffset(document.getLineNumber(expressionOffset));
|
||||
PsiLiteralExpressionImpl literalExpression = tryCast(expression, PsiLiteralExpressionImpl.class);
|
||||
if (literalExpression != null && literalExpression.getLiteralElementType() == JavaTokenType.STRING_LITERAL) {
|
||||
replaceWithTextBlock(new PsiLiteralExpressionImpl[]{literalExpression}, offset, literalExpression);
|
||||
replaceWithTextBlock(new PsiLiteralExpressionImpl[]{literalExpression}, literalExpression);
|
||||
return;
|
||||
}
|
||||
PsiPolyadicExpression polyadicExpression = tryCast(expression, PsiPolyadicExpression.class);
|
||||
if (polyadicExpression == null || !isConcatenation(polyadicExpression)) return;
|
||||
replaceWithTextBlock(polyadicExpression.getOperands(), offset, polyadicExpression);
|
||||
replaceWithTextBlock(polyadicExpression.getOperands(), polyadicExpression);
|
||||
}
|
||||
|
||||
private static void replaceWithTextBlock(@NotNull PsiExpression[] operands, int offset, @NotNull PsiExpression toReplace) {
|
||||
private static void replaceWithTextBlock(@NotNull PsiExpression[] operands, @NotNull PsiExpression toReplace) {
|
||||
StringBuilder textBlock = new StringBuilder();
|
||||
String indent = Strings.repeat(" ", offset);
|
||||
textBlock.append("\"\"\"\n").append(indent);
|
||||
textBlock.append("\"\"\"\n");
|
||||
boolean escapeStartQuote = false;
|
||||
for (int i = 0; i < operands.length; i++) {
|
||||
PsiExpression operand = operands[i];
|
||||
@@ -88,7 +84,7 @@ public class TextBlockMigrationInspection extends AbstractBaseJavaLocalInspectio
|
||||
boolean isLastLine = i == operands.length - 1;
|
||||
text = PsiLiteralUtil.escapeTextBlockCharacters(text, escapeStartQuote, isLastLine, isLastLine);
|
||||
escapeStartQuote = text.endsWith("\"");
|
||||
textBlock.append(text.replaceAll("\n", '\n' + indent));
|
||||
textBlock.append(text);
|
||||
}
|
||||
textBlock.append("\"\"\"");
|
||||
PsiReplacementUtil.replaceExpression(toReplace, textBlock.toString(), new CommentTracker());
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
package com.intellij.ide;
|
||||
|
||||
import com.intellij.application.options.*;
|
||||
import com.intellij.application.options.codeStyle.properties.*;
|
||||
import com.intellij.application.options.codeStyle.properties.CodeStyleFieldAccessor;
|
||||
import com.intellij.application.options.codeStyle.properties.CodeStylePropertiesUtil;
|
||||
import com.intellij.application.options.codeStyle.properties.CodeStylePropertyAccessor;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.application.ApplicationBundle;
|
||||
@@ -173,6 +175,7 @@ public class JavaLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSett
|
||||
"ALIGN_MULTILINE_PARENTHESIZED_EXPRESSION",
|
||||
"ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION",
|
||||
"ALIGN_GROUP_FIELD_DECLARATIONS",
|
||||
"ALIGN_MULTILINE_TEXT_BLOCKS",
|
||||
"BRACE_STYLE",
|
||||
"CLASS_BRACE_STYLE",
|
||||
"METHOD_BRACE_STYLE",
|
||||
@@ -204,6 +207,11 @@ public class JavaLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSett
|
||||
ApplicationBundle.message("wrapping.align.when.multiline"),
|
||||
ApplicationBundle.message("wrapping.annotation.parameters"));
|
||||
|
||||
consumer.showCustomOption(JavaCodeStyleSettings.class,
|
||||
"ALIGN_MULTILINE_TEXT_BLOCKS",
|
||||
ApplicationBundle.message("wrapping.align.when.multiline"),
|
||||
ApplicationBundle.message("wrapping.text.blocks") );
|
||||
|
||||
String groupName = ApplicationBundle.message("wrapping.fields.annotation");
|
||||
consumer.showCustomOption(JavaCodeStyleSettings.class, "DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION", "Do not wrap after single annotation", groupName);
|
||||
}
|
||||
|
||||
@@ -126,6 +126,8 @@ public class JavaCodeStyleSettings extends CustomCodeStyleSettings implements Im
|
||||
public int ANNOTATION_PARAMETER_WRAP = CommonCodeStyleSettings.DO_NOT_WRAP;
|
||||
public boolean ALIGN_MULTILINE_ANNOTATION_PARAMETERS;
|
||||
|
||||
public boolean ALIGN_MULTILINE_TEXT_BLOCKS = false;
|
||||
|
||||
public int BLANK_LINES_AROUND_INITIALIZER = 1;
|
||||
|
||||
public static final int FULLY_QUALIFY_NAMES_IF_NOT_IMPORTED = 1;
|
||||
|
||||
@@ -20,8 +20,10 @@ import com.intellij.psi.impl.source.codeStyle.ShiftIndentInsideHelper;
|
||||
import com.intellij.psi.impl.source.tree.*;
|
||||
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
|
||||
import com.intellij.psi.impl.source.tree.java.ClassElement;
|
||||
import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl;
|
||||
import com.intellij.psi.jsp.JspElementType;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -226,6 +228,9 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
if (elementType == JavaDocElementType.DOC_COMMENT) {
|
||||
return new DocCommentBlock(child, wrap, alignment, actualIndent, settings, javaSettings, formattingMode);
|
||||
}
|
||||
if (isTextBlock(childPsi)) {
|
||||
return new TextBlockBlock(child, wrap, alignmentStrategy, actualIndent, settings, javaSettings, formattingMode);
|
||||
}
|
||||
|
||||
SimpleJavaBlock simpleJavaBlock = new SimpleJavaBlock(child, wrap, alignmentStrategy, actualIndent, settings, javaSettings, myFormattingMode);
|
||||
simpleJavaBlock.setStartOffset(startOffset);
|
||||
@@ -436,6 +441,9 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
}
|
||||
return createAlignment(mySettings.ALIGN_MULTILINE_BINARY_OPERATION, defaultAlignment);
|
||||
}
|
||||
if (isTextBlock(myNode.getPsi())) {
|
||||
return createAlignment(myJavaSettings.ALIGN_MULTILINE_TEXT_BLOCKS,null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -459,6 +467,11 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isTextBlock(@NotNull PsiElement childPsi) {
|
||||
PsiLiteralExpressionImpl literal = ObjectUtils.tryCast(childPsi, PsiLiteralExpressionImpl.class);
|
||||
return literal != null && literal.getLiteralElementType() == JavaTokenType.TEXT_BLOCK_LITERAL;
|
||||
}
|
||||
|
||||
private boolean shouldInheritAlignment() {
|
||||
if (myNode instanceof PsiPolyadicExpression) {
|
||||
final ASTNode treeParent = myNode.getTreeParent();
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.psi.formatter.java;
|
||||
|
||||
import com.intellij.formatting.*;
|
||||
import com.intellij.formatting.alignment.AlignmentStrategy;
|
||||
import com.intellij.formatting.blocks.TextLineBlock;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
|
||||
import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class TextBlockBlock extends AbstractJavaBlock {
|
||||
|
||||
private final Indent myIndent;
|
||||
|
||||
public TextBlockBlock(ASTNode textBlock,
|
||||
Wrap wrap,
|
||||
AlignmentStrategy alignment,
|
||||
Indent indent,
|
||||
CommonCodeStyleSettings settings,
|
||||
JavaCodeStyleSettings javaSettings,
|
||||
@NotNull FormattingMode formattingMode) {
|
||||
super(textBlock, wrap, alignment, indent, settings, javaSettings, formattingMode);
|
||||
myIndent = indent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Block> buildChildren() {
|
||||
if (getFormattingMode() != FormattingMode.REFORMAT) return Collections.emptyList();
|
||||
int offset = myNode.getStartOffset();
|
||||
Alignment alignment = createChildAlignment();
|
||||
List<TextRange> textRanges = extractLinesRanges();
|
||||
List<Block> children = new ArrayList<>(textRanges.size());
|
||||
for (int i = 0; i < textRanges.size(); i++) {
|
||||
TextRange range = textRanges.get(i).shiftRight(offset);
|
||||
Indent indent = i == 0 ? Indent.getNoneIndent() : Indent.getContinuationIndent();
|
||||
children.add(new TextLineBlock(range, alignment, indent, null));
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<TextRange> extractLinesRanges() {
|
||||
PsiLiteralExpressionImpl literal = ObjectUtils.tryCast(myNode.getPsi(), PsiLiteralExpressionImpl.class);
|
||||
if (literal == null || literal.getLiteralElementType() != JavaTokenType.TEXT_BLOCK_LITERAL) return Collections.emptyList();
|
||||
int indent = literal.getTextBlockIndent();
|
||||
if (indent == -1) return Collections.emptyList();
|
||||
String text = myNode.getText();
|
||||
|
||||
List<TextRange> linesRanges = new ArrayList<>();
|
||||
// open quotes
|
||||
int start = StringUtil.indexOf(text, '\n', 3);
|
||||
if (start == -1) return Collections.emptyList();
|
||||
linesRanges.add(new TextRange(0, start));
|
||||
start += 1;
|
||||
|
||||
while (start < text.length()) {
|
||||
int end = StringUtil.indexOf(text, '\n', start);
|
||||
if (end == -1) end = text.length();
|
||||
if (start + indent < end) start += indent;
|
||||
if (start != end) linesRanges.add(new TextRange(start, end));
|
||||
start = end + 1;
|
||||
}
|
||||
|
||||
return linesRanges;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Spacing getSpacing(@Nullable Block child1, @NotNull Block child2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Indent getIndent() {
|
||||
return myIndent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeaf() {
|
||||
return getFormattingMode() != FormattingMode.REFORMAT;
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -3,9 +3,9 @@ class C {
|
||||
void x(int a, int b) {
|
||||
//keep me
|
||||
String s = java.text.MessageFormat.format("""
|
||||
the text
|
||||
block
|
||||
line2
|
||||
{0}{1} "to" be""", a, b);
|
||||
the text
|
||||
block
|
||||
line2
|
||||
{0}{1} "to" be""", a, b);
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
class C {
|
||||
String empty = """
|
||||
""\"
|
||||
target""\"<caret>""";
|
||||
String empty = """
|
||||
""\"
|
||||
target""\"<caret>""";
|
||||
}
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
class C {
|
||||
String empty = """
|
||||
<caret>""";
|
||||
String empty = """
|
||||
<caret>""";
|
||||
}
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
class C {
|
||||
String x = """
|
||||
"\"
|
||||
target\""
|
||||
""";
|
||||
String x = """
|
||||
"\"
|
||||
target\""
|
||||
""";
|
||||
}
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
class C {
|
||||
String x = """
|
||||
"<caret>"
|
||||
""";
|
||||
String x = """
|
||||
"<caret>"
|
||||
""";
|
||||
}
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
class C {
|
||||
String empty = """
|
||||
<html>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
<html>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"align_multiline_parenthesized_expression": false,
|
||||
"align_multiline_resources": true,
|
||||
"align_multiline_ternary_operation": false,
|
||||
"align_multiline_text_blocks": false,
|
||||
"align_multiline_throws_list": false,
|
||||
"align_subsequent_simple_methods": false,
|
||||
"align_throws_keyword": false,
|
||||
|
||||
+3
-3
@@ -4,9 +4,9 @@ class TextBlockMigration {
|
||||
|
||||
void concatenation() {
|
||||
String foobarbaz = """
|
||||
foo
|
||||
bar
|
||||
baz""";
|
||||
foo
|
||||
bar
|
||||
baz""";
|
||||
}
|
||||
|
||||
}
|
||||
+4
-4
@@ -4,9 +4,9 @@ class TextBlockMigration {
|
||||
|
||||
void concatenationWithMultipleNewLines() {
|
||||
String text = """
|
||||
This text should be on the same line as \\n this one
|
||||
foo
|
||||
bar
|
||||
""";
|
||||
This text should be on the same line as \\n this one
|
||||
foo
|
||||
bar
|
||||
""";
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -4,11 +4,11 @@ class TextBlockMigration {
|
||||
|
||||
void concatenationWithMultipleNewLines() {
|
||||
String html = """
|
||||
<html>
|
||||
<body>
|
||||
<p>Hello, world</p>
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
<html>
|
||||
<body>
|
||||
<p>Hello, world</p>
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -4,11 +4,11 @@ class TextBlockMigration {
|
||||
|
||||
void concatenationWithNonStrings() {
|
||||
String answer = """
|
||||
The answer to the meaning of life,
|
||||
the universe,
|
||||
and everything
|
||||
is 42
|
||||
""";
|
||||
The answer to the meaning of life,
|
||||
the universe,
|
||||
and everything
|
||||
is 42
|
||||
""";
|
||||
}
|
||||
|
||||
}
|
||||
+4
-4
@@ -4,10 +4,10 @@ class TextBlockMigration {
|
||||
|
||||
void concatenationWithExtraSpaces() {
|
||||
String code = """
|
||||
<html>\040\040
|
||||
<body>
|
||||
</body>
|
||||
</html>\040\040""";
|
||||
<html>\040\040
|
||||
<body>
|
||||
</body>
|
||||
</html>\040\040""";
|
||||
}
|
||||
|
||||
}
|
||||
+4
-4
@@ -4,10 +4,10 @@ class TextBlockMigration {
|
||||
|
||||
void concatenationWithThreeQuotes() {
|
||||
String quotes = """
|
||||
this concatenation contains
|
||||
three quotes
|
||||
one after another
|
||||
"\"\"""";
|
||||
this concatenation contains
|
||||
three quotes
|
||||
one after another
|
||||
"\"\"""";
|
||||
}
|
||||
|
||||
}
|
||||
+4
-4
@@ -4,10 +4,10 @@ class TextBlockMigration {
|
||||
|
||||
void literalWithNewLine() {
|
||||
String foo = """
|
||||
foo
|
||||
bar
|
||||
baz
|
||||
""";
|
||||
foo
|
||||
bar
|
||||
baz
|
||||
""";
|
||||
}
|
||||
|
||||
}
|
||||
+22
@@ -929,6 +929,28 @@ public class JavaFormatterAlignmentTest extends AbstractJavaFormatterTest {
|
||||
);
|
||||
}
|
||||
|
||||
public void test_alignMultilineTextBlock() {
|
||||
getJavaSettings().ALIGN_MULTILINE_TEXT_BLOCKS = true;
|
||||
doTextTest(
|
||||
"public class Test {\n" +
|
||||
" void foo() {\n" +
|
||||
" String block = \"\"\"\n" +
|
||||
" text\n" +
|
||||
" block\n" +
|
||||
" \"\"\";\n" +
|
||||
" }\n" +
|
||||
"}",
|
||||
"public class Test {\n" +
|
||||
" void foo() {\n" +
|
||||
" String block = \"\"\"\n" +
|
||||
" text\n" +
|
||||
" block\n" +
|
||||
" \"\"\";\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void _testIdea199677() {
|
||||
getSettings().ALIGN_CONSECUTIVE_VARIABLE_DECLARATIONS = true;
|
||||
|
||||
+22
@@ -640,4 +640,26 @@ public class JavaFormatterIndentationTest extends AbstractJavaFormatterTest {
|
||||
doTextTest(before, after);
|
||||
}
|
||||
|
||||
public void testTextBlock() {
|
||||
String before = "class Formatting {\n" +
|
||||
" void test() {\n" +
|
||||
" String block = \"\"\"\n" +
|
||||
" \n" +
|
||||
" text\n" +
|
||||
"block\"\"\";\n" +
|
||||
" " +
|
||||
" }\n" +
|
||||
"}";
|
||||
|
||||
String after = "class Formatting {\n" +
|
||||
" void test() {\n" +
|
||||
" String block = \"\"\"\n" +
|
||||
" \n" +
|
||||
" text\n" +
|
||||
" block\"\"\";\n" +
|
||||
" " +
|
||||
"}\n" +
|
||||
"}";
|
||||
doTextTest(before, after);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,6 +246,7 @@ wrapping.long.lines=Ensure right margin is not exceeded
|
||||
wrapping.comments=Comments
|
||||
wrapping.comments.wrap.at.right.margin=Wrap at right margin
|
||||
wrapping.annotation.parameters=Annotation parameters
|
||||
wrapping.text.blocks=Text blocks
|
||||
|
||||
checkbox.align.multiline.chained.methods=Chained methods
|
||||
checkbox.align.multiline.method.parameters=Method parameters
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ class X {
|
||||
String s = """
|
||||
\"""";
|
||||
String t = """
|
||||
" """;
|
||||
" """;
|
||||
String u = """
|
||||
""\" """;
|
||||
String v = """
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ class X {
|
||||
\'""";
|
||||
String t = """
|
||||
|
||||
""";
|
||||
""";
|
||||
String u = """
|
||||
\" ""\" "\"" \"""
|
||||
\""
|
||||
|
||||
+4
-4
@@ -2,9 +2,9 @@ class TextBlocks {
|
||||
static {
|
||||
//c1
|
||||
System.out.println("""
|
||||
first
|
||||
second
|
||||
third
|
||||
\\for<caret>th\"""");
|
||||
first
|
||||
second
|
||||
third
|
||||
\\for<caret>th\"""");
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -2,10 +2,10 @@ class TextBlocks {
|
||||
static {
|
||||
//c1
|
||||
System.out.println("""
|
||||
first
|
||||
second
|
||||
third
|
||||
forth
|
||||
<caret> """);
|
||||
first
|
||||
second
|
||||
third
|
||||
forth
|
||||
<caret> """);
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -2,9 +2,9 @@ class TextBlocks {
|
||||
static {
|
||||
//c1
|
||||
System.out.println("""
|
||||
first
|
||||
second
|
||||
third_no sp<caret>ace
|
||||
""");
|
||||
first
|
||||
second
|
||||
third_no sp<caret>ace
|
||||
""");
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
class C {
|
||||
//keep me
|
||||
String s = String.format("""
|
||||
the text
|
||||
block
|
||||
line2
|
||||
%d%d t<caret>o be""", 1, 2);
|
||||
the text
|
||||
block
|
||||
line2
|
||||
%d%d t<caret>o be""", 1, 2);
|
||||
}
|
||||
Reference in New Issue
Block a user