diff --git a/java/java-impl/src/META-INF/JavaPlugin.xml b/java/java-impl/src/META-INF/JavaPlugin.xml
index e0d840ee5bfb..cfea29dbe29e 100644
--- a/java/java-impl/src/META-INF/JavaPlugin.xml
+++ b/java/java-impl/src/META-INF/JavaPlugin.xml
@@ -1322,7 +1322,7 @@
-
+
diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/JavaFormatterUtil.java b/java/java-impl/src/com/intellij/psi/formatter/java/JavaFormatterUtil.java
index c3982f0e0334..241a3e90cda4 100644
--- a/java/java-impl/src/com/intellij/psi/formatter/java/JavaFormatterUtil.java
+++ b/java/java-impl/src/com/intellij/psi/formatter/java/JavaFormatterUtil.java
@@ -19,7 +19,6 @@ import com.intellij.psi.impl.source.tree.ElementType;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
-import com.intellij.psi.util.PsiLiteralUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
@@ -57,7 +56,7 @@ public final class JavaFormatterUtil {
* @param node1 node to check
* @param node2 node to check
* @return {@code true} if given nodes are binary expressions and have the same priority;
- * {@code false} otherwise
+ * {@code false} otherwise
*/
public static boolean areSamePriorityBinaryExpressions(ASTNode node1, ASTNode node2) {
if (node1 == null || node2 == null) {
@@ -83,7 +82,7 @@ public final class JavaFormatterUtil {
* if there's a line break after it and "keep line breaks" is on.
*
* @param settings The current settings
- * @param node The node to check.
+ * @param node The node to check.
* @return True for call chunk start.
*/
static boolean isStartOfCallChunk(@NotNull CommonCodeStyleSettings settings, @NotNull ASTNode node) {
@@ -127,9 +126,9 @@ public final class JavaFormatterUtil {
* @return wrap to use for the sub-blocks of the given block
*/
static @Nullable Wrap createDefaultWrap(ASTBlock block,
- CommonCodeStyleSettings settings,
- JavaCodeStyleSettings javaSettings,
- ReservedWrapsProvider reservedWrapsProvider) {
+ CommonCodeStyleSettings settings,
+ JavaCodeStyleSettings javaSettings,
+ ReservedWrapsProvider reservedWrapsProvider) {
ASTNode node = block.getNode();
Wrap wrap = block.getWrap();
if (node == null) return null;
@@ -211,11 +210,11 @@ public final class JavaFormatterUtil {
* {@code null} otherwise
*/
static @Nullable Wrap arrangeChildWrap(ASTNode child,
- ASTNode parent,
- CommonCodeStyleSettings settings,
- JavaCodeStyleSettings javaSettings,
- Wrap suggestedWrap,
- AbstractJavaBlock reservedWrapsProvider) {
+ ASTNode parent,
+ CommonCodeStyleSettings settings,
+ JavaCodeStyleSettings javaSettings,
+ Wrap suggestedWrap,
+ AbstractJavaBlock reservedWrapsProvider) {
ASTNode directParent = child.getTreeParent();
int role = ((CompositeElement)directParent).getChildRole(child);
@@ -291,7 +290,8 @@ public final class JavaFormatterUtil {
ASTNode last = prev.getLastChildNode();
if (last != null && last.getElementType() == JavaElementType.ANNOTATION) {
if (javaSettings.DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION && isModifierListWithSingleAnnotation(prev, JavaElementType.FIELD) ||
- javaSettings.DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION_IN_PARAMETER && isModifierListWithSingleAnnotation(prev, JavaElementType.PARAMETER) ||
+ javaSettings.DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION_IN_PARAMETER &&
+ isModifierListWithSingleAnnotation(prev, JavaElementType.PARAMETER) ||
isAnnotationAfterKeyword(last)
) {
return Wrap.createWrap(WrapType.NONE, false);
@@ -456,7 +456,10 @@ public final class JavaFormatterUtil {
return false;
}
- private static int getAnnotationWrapType(ASTNode parent, ASTNode child, CommonCodeStyleSettings settings, JavaCodeStyleSettings javaSettings) {
+ private static int getAnnotationWrapType(ASTNode parent,
+ ASTNode child,
+ CommonCodeStyleSettings settings,
+ JavaCodeStyleSettings javaSettings) {
IElementType nodeType = parent.getElementType();
if (nodeType == JavaElementType.METHOD) {
@@ -528,9 +531,9 @@ public final class JavaFormatterUtil {
* Traverses the children of the node and collects nodes with type method calls or reference expressions to the list.
* If the quantity of the call expressions is greater than {@link JavaFormatterUtil#CALL_EXPRESSION_DEPTH}, call expressions will not be
* collected, and you should not format them.
- * @param nodes List in which the method add nodes
- * @param node Node to traverse
*
+ * @param nodes List in which the method add nodes
+ * @param node Node to traverse
*/
public static void collectCallExpressionNodes(@NotNull List super ASTNode> nodes, @NotNull ASTNode node) {
ArrayDeque stack = new ArrayDeque<>(CALL_EXPRESSION_DEPTH);
@@ -541,51 +544,81 @@ public final class JavaFormatterUtil {
return;
}
ASTNode currentNode = stack.removeLast();
- if (!FormatterUtil.containsWhiteSpacesOnly(currentNode)) {
- IElementType type = currentNode.getElementType();
- if (type == JavaElementType.METHOD_CALL_EXPRESSION ||
- type == JavaElementType.REFERENCE_EXPRESSION) {
- ASTNode firstChild = currentNode.getFirstChildNode();
- currentNode = FormatterUtil.getNextNonWhitespaceSibling(currentNode);
- ContainerUtil.addIfNotNull(stack, currentNode);
- ContainerUtil.addIfNotNull(stack, firstChild);
- }
- else {
- nodes.add(currentNode);
- currentNode = FormatterUtil.getNextNonWhitespaceSibling(currentNode);
- ContainerUtil.addIfNotNull(stack, currentNode);
- }
- } else {
+ if (!FormatterUtil.containsWhiteSpacesOnly(currentNode)) {
+ IElementType type = currentNode.getElementType();
+ if (type == JavaElementType.METHOD_CALL_EXPRESSION ||
+ type == JavaElementType.REFERENCE_EXPRESSION) {
+ ASTNode firstChild = currentNode.getFirstChildNode();
+ currentNode = FormatterUtil.getNextNonWhitespaceSibling(currentNode);
+ ContainerUtil.addIfNotNull(stack, currentNode);
+ ContainerUtil.addIfNotNull(stack, firstChild);
+ }
+ else {
+ nodes.add(currentNode);
currentNode = FormatterUtil.getNextNonWhitespaceSibling(currentNode);
ContainerUtil.addIfNotNull(stack, currentNode);
}
+ }
+ else {
+ currentNode = FormatterUtil.getNextNonWhitespaceSibling(currentNode);
+ ContainerUtil.addIfNotNull(stack, currentNode);
+ }
}
}
/**
* Extracts text ranges corresponding to the lines in a given literal multiline text.
*
- * @param text the literal text to extract text ranges from
- * @param indent the number of spaces used for indentation
- * @param shouldTreatWholeLine specifies whether the entire line should be treated as a text range or
+ * @param text the literal text to extract text ranges from
+ * @param indent the number of spaces used for indentation
* @return a list of {@code TextRange} objects representing the extracted text ranges
*/
- public static @NotNull List extractTextRangesFromLiteralText(@NotNull String text, int indent, boolean shouldTreatWholeLine) {
+ public static @NotNull List extractTextRangesFromLiteralText(@NotNull String text, int indent) {
List linesRanges = new ArrayList<>();
-
+ boolean isLastLine = false;
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 && !shouldTreatWholeLine) start += indent;
- if (start != end || shouldTreatWholeLine) linesRanges.add(new TextRange(start, end));
+ if (end == -1) {
+ isLastLine = true;
+ end = text.length();
+ }
+ if (start + indent <= end) {
+ int quoteStartIndex = end - 3;
+ if (!isLastLine && allEmpty(start + indent, end, text)) {
+ // todo here we can delete and the last \s\s\s"""
+ start = end;
+ }
+ else if (isLastLine && allEmpty(start + indent, quoteStartIndex, text) && isEndsWithTripleQuote(quoteStartIndex, end, text)) {
+ start = quoteStartIndex;
+ }
+ else {
+ start += indent;
+ }
+ }
+ else {
+ start = end;
+ }
+ linesRanges.add(new TextRange(start, end));
start = end + 1;
}
return linesRanges;
}
+
+ private static boolean isEndsWithTripleQuote(int start, int end, @NotNull String text) {
+ if (end - start != 3 || start < 0) return false;
+ String tripleQuote = text.substring(start, end);
+ return tripleQuote.equals("\"\"\"");
+ }
+
+ private static boolean allEmpty(int i, int end, @NotNull String text) {
+ for (int j = i; j < end; j++) {
+ if(!Character.isWhitespace(text.charAt(j))) return false;
+ }
+ return true;
+ }
}
diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/TextBlockBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/TextBlockBlock.java
index ff90fc11c92b..7b2ee25c908e 100644
--- a/java/java-impl/src/com/intellij/psi/formatter/java/TextBlockBlock.java
+++ b/java/java-impl/src/com/intellij/psi/formatter/java/TextBlockBlock.java
@@ -5,7 +5,6 @@ import com.intellij.formatting.*;
import com.intellij.formatting.alignment.AlignmentStrategy;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange;
-import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiLiteralExpression;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
@@ -57,7 +56,7 @@ public class TextBlockBlock extends AbstractJavaBlock {
if (indent == -1) return Collections.emptyList();
String text = literal.getText();
- return extractTextRangesFromLiteralText(text, indent, false);
+ return extractTextRangesFromLiteralText(text, indent);
}
@Override
diff --git a/java/java-tests/testData/psi/formatter/java/textBlock/CaretInWhitespaceLineInMiddleAfterAlignment.java b/java/java-tests/testData/psi/formatter/java/textBlock/CaretInWhitespaceLineInMiddleAfterAlignment.java
index 408a18a3a7a6..dcc2eb241f51 100644
--- a/java/java-tests/testData/psi/formatter/java/textBlock/CaretInWhitespaceLineInMiddleAfterAlignment.java
+++ b/java/java-tests/testData/psi/formatter/java/textBlock/CaretInWhitespaceLineInMiddleAfterAlignment.java
@@ -1,7 +1,7 @@
public class Formatter {
void foo() {
String s2 = """
-
+
""";
}
}
\ No newline at end of file
diff --git a/java/java-tests/testData/psi/formatter/java/textBlock/CaretInWhitespaceLineInMiddleAfterAlignment_after.java b/java/java-tests/testData/psi/formatter/java/textBlock/CaretInWhitespaceLineInMiddleAfterAlignment_after.java
index 28f17f6f3a08..aeb4d2057e51 100644
--- a/java/java-tests/testData/psi/formatter/java/textBlock/CaretInWhitespaceLineInMiddleAfterAlignment_after.java
+++ b/java/java-tests/testData/psi/formatter/java/textBlock/CaretInWhitespaceLineInMiddleAfterAlignment_after.java
@@ -1,7 +1,7 @@
public class Formatter {
void foo() {
String s2 = """
-
+
""";
}
}
\ No newline at end of file
diff --git a/java/java-tests/testData/psi/formatter/java/textBlock/CaretInWhitespaceLineInMiddleBeforeAlignment_after.java b/java/java-tests/testData/psi/formatter/java/textBlock/CaretInWhitespaceLineInMiddleBeforeAlignment_after.java
index 28f17f6f3a08..80a8ada3a618 100644
--- a/java/java-tests/testData/psi/formatter/java/textBlock/CaretInWhitespaceLineInMiddleBeforeAlignment_after.java
+++ b/java/java-tests/testData/psi/formatter/java/textBlock/CaretInWhitespaceLineInMiddleBeforeAlignment_after.java
@@ -1,7 +1,7 @@
public class Formatter {
void foo() {
String s2 = """
-
+
""";
}
}
\ No newline at end of file
diff --git a/java/java-tests/testData/psi/formatter/java/textBlock/EmptyLastLine.java b/java/java-tests/testData/psi/formatter/java/textBlock/EmptyLastLine.java
new file mode 100644
index 000000000000..b8fe8fbca1da
--- /dev/null
+++ b/java/java-tests/testData/psi/formatter/java/textBlock/EmptyLastLine.java
@@ -0,0 +1,7 @@
+public class Formatter {
+ void foo() {
+ String s2 = """
+ a
+""";
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/psi/formatter/java/textBlock/EmptyLastLine_after.java b/java/java-tests/testData/psi/formatter/java/textBlock/EmptyLastLine_after.java
new file mode 100644
index 000000000000..6ef8d2018247
--- /dev/null
+++ b/java/java-tests/testData/psi/formatter/java/textBlock/EmptyLastLine_after.java
@@ -0,0 +1,7 @@
+public class Formatter {
+ void foo() {
+ String s2 = """
+ a
+ """;
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithSymbolsAfterAlignment.java b/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithSymbolsAfterAlignment.java
new file mode 100644
index 000000000000..5cecda210339
--- /dev/null
+++ b/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithSymbolsAfterAlignment.java
@@ -0,0 +1,7 @@
+public class Formatter {
+ void foo() {
+ String s2 = """
+ a
+ d d ds \t""";
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithSymbolsAfterAlignment_after.java b/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithSymbolsAfterAlignment_after.java
new file mode 100644
index 000000000000..5cecda210339
--- /dev/null
+++ b/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithSymbolsAfterAlignment_after.java
@@ -0,0 +1,7 @@
+public class Formatter {
+ void foo() {
+ String s2 = """
+ a
+ d d ds \t""";
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithSymbolsBeforeAlignment.java b/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithSymbolsBeforeAlignment.java
new file mode 100644
index 000000000000..e61bc895b6ce
--- /dev/null
+++ b/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithSymbolsBeforeAlignment.java
@@ -0,0 +1,7 @@
+public class Formatter {
+ void foo() {
+ String s2 = """
+ a
+ d d ds \t""";
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithSymbolsBeforeAlignment_after.java b/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithSymbolsBeforeAlignment_after.java
new file mode 100644
index 000000000000..0a23c2ce74bd
--- /dev/null
+++ b/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithSymbolsBeforeAlignment_after.java
@@ -0,0 +1,7 @@
+public class Formatter {
+ void foo() {
+ String s2 = """
+ a
+ d d ds \t""";
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithoutSymbolsAfterAlignment.java b/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithoutSymbolsAfterAlignment.java
new file mode 100644
index 000000000000..3b7e93b05d69
--- /dev/null
+++ b/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithoutSymbolsAfterAlignment.java
@@ -0,0 +1,7 @@
+public class Formatter {
+ void foo() {
+ String s2 = """
+ a
+ """;
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithoutSymbolsAfterAlignment_after.java b/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithoutSymbolsAfterAlignment_after.java
new file mode 100644
index 000000000000..0f28cccd4286
--- /dev/null
+++ b/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithoutSymbolsAfterAlignment_after.java
@@ -0,0 +1,7 @@
+public class Formatter {
+ void foo() {
+ String s2 = """
+ a
+ """;
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithoutSymbolsBeforeAlignment.java b/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithoutSymbolsBeforeAlignment.java
new file mode 100644
index 000000000000..37bfd120b871
--- /dev/null
+++ b/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithoutSymbolsBeforeAlignment.java
@@ -0,0 +1,7 @@
+public class Formatter {
+ void foo() {
+ String s2 = """
+ a
+ """;
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithoutSymbolsBeforeAlignment_after.java b/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithoutSymbolsBeforeAlignment_after.java
new file mode 100644
index 000000000000..336a9a3ea057
--- /dev/null
+++ b/java/java-tests/testData/psi/formatter/java/textBlock/LastLineWithoutSymbolsBeforeAlignment_after.java
@@ -0,0 +1,7 @@
+public class Formatter {
+ void foo() {
+ String s2 = """
+ a
+ """;
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/AdjustWhitespaceLineTextBlockReformatPostProcessorTest.kt b/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/TextBlockBlankLinesFormatterTest.kt
similarity index 87%
rename from java/java-tests/testSrc/com/intellij/java/psi/formatter/java/AdjustWhitespaceLineTextBlockReformatPostProcessorTest.kt
rename to java/java-tests/testSrc/com/intellij/java/psi/formatter/java/TextBlockBlankLinesFormatterTest.kt
index d7417956eb7d..ac0875e364a1 100644
--- a/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/AdjustWhitespaceLineTextBlockReformatPostProcessorTest.kt
+++ b/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/TextBlockBlankLinesFormatterTest.kt
@@ -9,7 +9,7 @@ import com.intellij.psi.codeStyle.CommonCodeStyleSettings
import com.intellij.psi.codeStyle.JavaCodeStyleSettings
import com.intellij.testFramework.LightPlatformCodeInsightTestCase
-class AdjustWhitespaceLineTextBlockReformatPostProcessorTest : LightPlatformCodeInsightTestCase() {
+class TextBlockBlankLinesFormatterTest : LightPlatformCodeInsightTestCase() {
override fun getTestDataPath(): String = "${JavaTestUtil.getJavaTestDataPath()}/psi/formatter/java/textBlock/"
fun testWhitespacesLessThanAlignment() = doTest()
@@ -71,6 +71,26 @@ class AdjustWhitespaceLineTextBlockReformatPostProcessorTest : LightPlatformCode
doTest()
}
+ fun testLastLineWithoutSymbolsAfterAlignment() {
+ doTest()
+ }
+
+ fun testLastLineWithSymbolsAfterAlignment() {
+ doTest()
+ }
+
+ fun testLastLineWithoutSymbolsBeforeAlignment() {
+ doTest()
+ }
+
+ fun testLastLineWithSymbolsBeforeAlignment() {
+ doTest()
+ }
+
+ fun testEmptyLastLine() {
+ doTest()
+ }
+
private fun getCommonSettings(): CommonCodeStyleSettings = currentCodeStyleSettings.getCommonSettings(JavaLanguage.INSTANCE)