[Java. Code Formatting] IDEA-223507 Write tests for formatting conditional expressions

GitOrigin-RevId: b5ec80926db80796a899ccded49c38acb45c6dac
This commit is contained in:
Georgii Ustinov
2025-11-06 10:53:18 +00:00
committed by intellij-monorepo-bot
parent 92bc9cbbb3
commit 9e3542c650
36 changed files with 547 additions and 16 deletions
@@ -337,8 +337,8 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
}
return Indent.getNoneIndent();
}
final ASTNode grandParent = skipParenthesesUp(parent.getTreeParent());
if (grandParent != null && grandParent.getElementType() == JavaElementType.CONDITIONAL_EXPRESSION) {
if (JavaFormatterConditionalExpressionUtil.isInsideConditionalExpression(parent)) {
return Indent.getSpaceIndent(0, true);
}
}
@@ -838,19 +838,10 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
return
new LegacyChainedMethodCallsBlockBuilder(alignment, blockWrap, indent, mySettings, myJavaSettings, myFormattingMode).build(nodes);
}
return new ChainMethodCallsBlockBuilder(alignment, blockWrap, indent, mySettings, myJavaSettings, myFormattingMode, shouldUseSpaceIndentInCallChain(node)).build(nodes);
return new ChainMethodCallsBlockBuilder(alignment, blockWrap, indent, mySettings, myJavaSettings,
myFormattingMode, JavaFormatterConditionalExpressionUtil.isInsideConditionalExpression(node)).build(nodes);
}
private static boolean shouldUseSpaceIndentInCallChain(@NotNull ASTNode node) {
ASTNode parent = skipParenthesesUp(node);
if (parent == null) return false;
while (parent != null && parent.getElementType() == JavaElementType.REFERENCE_EXPRESSION) {
parent = parent.getTreeParent();
if (parent == null || parent.getElementType() != JavaElementType.METHOD_CALL_EXPRESSION) return false;
parent = skipParenthesesUp(parent);
}
return parent != null && parent.getElementType() == JavaElementType.CONDITIONAL_EXPRESSION;
}
private boolean shouldAlignChild(final @NotNull ASTNode child) {
int role = getChildRole(child);
@@ -0,0 +1,26 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.psi.formatter.java
import com.intellij.lang.ASTNode
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil
import com.intellij.psi.impl.source.tree.ChildRole
import com.intellij.psi.impl.source.tree.CompositeElement
import com.intellij.psi.impl.source.tree.JavaElementType
import com.intellij.psi.tree.ParentAwareTokenSet
internal object JavaFormatterConditionalExpressionUtil {
private val STOP_TOKENS = ParentAwareTokenSet.create(JavaElementType.METHOD, JavaElementType.LOCAL_VARIABLE, JavaElementType.METHOD)
/**
* Checks if the given AST node is inside a conditional expression then or else branch.
*/
@JvmStatic
fun isInsideConditionalExpression(node : ASTNode) : Boolean {
val child = BasicJavaAstTreeUtil.getAncestorWithParentOfType(node, JavaElementType.CONDITIONAL_EXPRESSION, STOP_TOKENS)
if (child == null) return false
val parent = child.treeParent
if (parent !is CompositeElement) return false
val childRole = parent.getChildRole(child)
return childRole != ChildRole.CONDITION
}
}
@@ -301,9 +301,20 @@ public final class BasicJavaAstTreeUtil {
return findParent(e, set);
}
private static @Nullable ASTNode findParent(@NotNull ASTNode element, @NotNull IElementType type) {
private static @Nullable ASTNode findAncestorWithParentOfType(@NotNull ASTNode element, @NotNull IElementType type, @Nullable ParentAwareTokenSet stopAt) {
for (ASTNode currentElement = element, parent = element.getTreeParent(); parent != null; currentElement = parent, parent = parent.getTreeParent()) {
IElementType parentType = parent.getElementType();
if (is(parentType, type)) return currentElement;
else if (stopAt != null && is(parentType, stopAt)) return null;
}
return null;
}
private static @Nullable ASTNode findParent(@NotNull ASTNode element, @NotNull IElementType type, @Nullable ParentAwareTokenSet stopAt) {
for (ASTNode parent = element.getTreeParent(); parent != null; parent = parent.getTreeParent()) {
if (is(parent.getElementType(), type)) return parent;
IElementType parentType = parent.getElementType();
if (is(parentType, type)) return parent;
else if (stopAt != null && is(parentType, stopAt)) return null;
}
return null;
}
@@ -317,10 +328,24 @@ public final class BasicJavaAstTreeUtil {
public static @Nullable ASTNode getParentOfType(@Nullable ASTNode e, @NotNull IElementType elementType) {
return getParentOfType(e, elementType, null);
}
/**
* Finds the first ancestor which parent element type is equals to {@code elementType}
*/
public static @Nullable ASTNode getAncestorWithParentOfType(@Nullable ASTNode e, @NotNull IElementType elementType, @Nullable ParentAwareTokenSet stopAt) {
if (e == null) {
return null;
}
return findParent(e, elementType);
return findAncestorWithParentOfType(e, elementType, stopAt);
}
public static @Nullable ASTNode getParentOfType(@Nullable ASTNode e, @NotNull IElementType elementType, @Nullable ParentAwareTokenSet stopAt) {
if (e == null) {
return null;
}
return findParent(e, elementType, stopAt);
}
public static @Nullable ASTNode getMethodExpression(@Nullable ASTNode element) {
@@ -0,0 +1,13 @@
package org.example;
public class A {
void f() {
String x = true
? (new StringBuilder()
.append("Hello")
.toString())
: (new StringBuilder()
.append("World")
.toString());
}
}
@@ -0,0 +1,13 @@
package org.example;
public class A {
void f() {
String x = true
? (new StringBuilder()
.append("Hello")
.toString())
: (new StringBuilder()
.append("World")
.toString());
}
}
@@ -0,0 +1,11 @@
package org.example;
public class A {
void f() {
String x = true ? (new StringBuilder()
.append("Hello")
.toString()) : (new StringBuilder()
.append("World")
.toString());
}
}
@@ -0,0 +1,11 @@
package org.example;
public class A {
void f() {
String x = true ? (new StringBuilder()
.append("Hello")
.toString()) : (new StringBuilder()
.append("World")
.toString());
}
}
@@ -0,0 +1,13 @@
package org.example;
public class A {
void f() {
String x = true
? new StringBuilder()
.append("Hello")
.toString()
: new StringBuilder()
.append("World")
.toString();
}
}
@@ -0,0 +1,13 @@
package org.example;
public class A {
void f() {
String x = true
? new StringBuilder()
.append("Hello")
.toString()
: new StringBuilder()
.append("World")
.toString();
}
}
@@ -0,0 +1,11 @@
package org.example;
public class A {
void f() {
String x = true ? new StringBuilder()
.append("Hello")
.toString() : new StringBuilder()
.append("World")
.toString();
}
}
@@ -0,0 +1,11 @@
package org.example;
public class A {
void f() {
String x = true ? new StringBuilder()
.append("Hello")
.toString() : new StringBuilder()
.append("World")
.toString();
}
}
@@ -0,0 +1,13 @@
package org.example;
public class A {
void f() {
String x = true ?
(new StringBuilder()
.append("Hello"))
.toString() :
(new StringBuilder()
.append("World"))
.toString();
}
}
@@ -0,0 +1,13 @@
package org.example;
public class A {
void f() {
String x = true ?
(new StringBuilder()
.append("Hello"))
.toString() :
(new StringBuilder()
.append("World"))
.toString();
}
}
@@ -0,0 +1,11 @@
package org.example;
public class A {
void f() {
String x = true ? (new StringBuilder()
.append("Hello"))
.toString() : (new StringBuilder()
.append("World"))
.toString();
}
}
@@ -0,0 +1,11 @@
package org.example;
public class A {
void f() {
String x = true ? (new StringBuilder()
.append("Hello"))
.toString() : (new StringBuilder()
.append("World"))
.toString();
}
}
@@ -0,0 +1,11 @@
package org.example;
public class A {
void f() {
String x = true
? ("hello"
+ "world")
: ("goodbye"
+ "world");
}
}
@@ -0,0 +1,11 @@
package org.example;
public class A {
void f() {
String x = true
? ("hello"
+ "world")
: ("goodbye"
+ "world");
}
}
@@ -0,0 +1,9 @@
package org.example;
public class A {
void f() {
String x = true ? ("hello"
+ "world") : ("goodbye"
+ "world");
}
}
@@ -0,0 +1,9 @@
package org.example;
public class A {
void f() {
String x = true ? ("hello"
+ "world") : ("goodbye"
+ "world");
}
}
@@ -0,0 +1,11 @@
package org.example;
public class A {
void f() {
String x = true
? "hello"
+ "world"
: "goodbye"
+ "world";
}
}
@@ -0,0 +1,11 @@
package org.example;
public class A {
void f() {
String x = true
? "hello"
+ "world"
: "goodbye"
+ "world";
}
}
@@ -0,0 +1,9 @@
package org.example;
public class A {
void f() {
String x = true ? "hello"
+ "world" : "goodbye"
+ "world";
}
}
@@ -0,0 +1,9 @@
package org.example;
public class A {
void f() {
String x = true ? "hello"
+ "world" : "goodbye"
+ "world";
}
}
@@ -0,0 +1,13 @@
package org.example;
public class A {
void f() {
String x = true
? ("hello"
+ "world")
+ "!"
: ("goodbye"
+ "world")
+ "!";
}
}
@@ -0,0 +1,13 @@
package org.example;
public class A {
void f() {
String x = true
? ("hello"
+ "world")
+ "!"
: ("goodbye"
+ "world")
+ "!";
}
}
@@ -0,0 +1,11 @@
package org.example;
public class A {
void f() {
String x = true ? ("hello"
+ "world")
+ "!" : ("goodbye"
+ "world")
+ "!";
}
}
@@ -0,0 +1,11 @@
package org.example;
public class A {
void f() {
String x = true ? ("hello"
+ "world")
+ "!" : ("goodbye"
+ "world")
+ "!";
}
}
@@ -0,0 +1,17 @@
package org.example;
public class A {
void f() {
String x = true
? ("first ")
+ "prefix"
+ ((new StringBuilder()
.append("Hello"))
.toString())
: ("alternative"
+ "prefix")
+ ((new StringBuilder()
.append("World"))
.toString());
}
}
@@ -0,0 +1,17 @@
package org.example;
public class A {
void f() {
String x = true
? ("first ")
+ "prefix"
+ ((new StringBuilder()
.append("Hello"))
.toString())
: ("alternative"
+ "prefix")
+ ((new StringBuilder()
.append("World"))
.toString());
}
}
@@ -0,0 +1,15 @@
package org.example;
public class A {
void f() {
String x = true ? ("first ")
+ "prefix"
+ ((new StringBuilder()
.append("Hello"))
.toString()) : ("alternative"
+ "prefix")
+ ((new StringBuilder()
.append("World"))
.toString());
}
}
@@ -0,0 +1,15 @@
package org.example;
public class A {
void f() {
String x = true ? ("first ")
+ "prefix"
+ ((new StringBuilder()
.append("Hello"))
.toString()) : ("alternative"
+ "prefix")
+ ((new StringBuilder()
.append("World"))
.toString());
}
}
@@ -0,0 +1,17 @@
package org.example;
public class A {
void f() {
String x = true
? "first "
+ "prefix"
+ new StringBuilder()
.append("Hello")
.toString()
: "alternative"
+ "prefix"
+ new StringBuilder()
.append("World")
.toString();
}
}
@@ -0,0 +1,17 @@
package org.example;
public class A {
void f() {
String x = true
? "first "
+ "prefix"
+ new StringBuilder()
.append("Hello")
.toString()
: "alternative"
+ "prefix"
+ new StringBuilder()
.append("World")
.toString();
}
}
@@ -0,0 +1,15 @@
package org.example;
public class A {
void f() {
String x = true ? "first "
+ "prefix"
+ new StringBuilder()
.append("Hello")
.toString() : "alternative"
+ "prefix"
+ new StringBuilder()
.append("World")
.toString();
}
}
@@ -0,0 +1,15 @@
package org.example;
public class A {
void f() {
String x = true ? "first "
+ "prefix"
+ new StringBuilder()
.append("Hello")
.toString() : "alternative"
+ "prefix"
+ new StringBuilder()
.append("World")
.toString();
}
}
@@ -0,0 +1,89 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.psi.formatter.java
class ConditionalExpressionFormatterTest : JavaFormatterTestCase() {
override fun getBasePath(): String = "psi/formatter/conditionalExpression"
fun testLiteralOperatorOnPreviousLine() {
settings.ALIGN_MULTILINE_TERNARY_OPERATION = true
doTest()
}
fun testLiteralOperatorOnNewLine() {
settings.ALIGN_MULTILINE_TERNARY_OPERATION = true
doTest()
}
fun testLiteralInParensOperatorOnPreviousLine() {
settings.ALIGN_MULTILINE_TERNARY_OPERATION = true
doTest()
}
fun testLiteralInParensOperatorOnNewLine() {
settings.ALIGN_MULTILINE_TERNARY_OPERATION = true
doTest()
}
fun testLiteralPartialInParensOperatorOnPreviousLine() {
settings.ALIGN_MULTILINE_TERNARY_OPERATION = true
doTest()
}
fun testLiteralPartialInParensOperatorOnNewLine() {
settings.ALIGN_MULTILINE_TERNARY_OPERATION = true
doTest()
}
fun testCallChainOperatorOnPreviousLine() {
doTest()
}
fun testCallChainOperatorOnNewLine() {
doTest()
}
fun testCallChainInParensOperatorOnPreviousLine() {
doTest()
}
fun testCallChainInParensOperatorOnNewLine() {
doTest()
}
fun testCallChainPartialInParensOperatorOnPreviousLine() {
doTest()
}
fun testCallChainPartialInParensOperatorOnNewLine() {
doTest()
}
fun testMixedOperatorOnPreviousLine() {
settings.ALIGN_MULTILINE_TERNARY_OPERATION = true
doTest()
}
fun testMixedOperatorOnNewLine() {
settings.ALIGN_MULTILINE_TERNARY_OPERATION = true
doTest()
}
fun testMixedInParensOperatorOnPreviousLine() {
settings.ALIGN_MULTILINE_TERNARY_OPERATION = true
doTest()
}
fun testMixedInParensOperatorOnNewLine() {
settings.ALIGN_MULTILINE_TERNARY_OPERATION = true
doTest()
}
override fun doTest() {
val fileNameBefore = getTestName(true)
val fileNameAfter = fileNameBefore + "_after"
doTest(fileNameBefore, fileNameAfter)
doTest(fileNameAfter, fileNameAfter)
super.doTest()
}
}