diff --git a/java/java-frontback-impl/resource/intellij.java.frontback.impl.xml b/java/java-frontback-impl/resource/intellij.java.frontback.impl.xml
index b5a581e1a928..3fa16e8cd284 100644
--- a/java/java-frontback-impl/resource/intellij.java.frontback.impl.xml
+++ b/java/java-frontback-impl/resource/intellij.java.frontback.impl.xml
@@ -81,6 +81,7 @@
+
diff --git a/java/java-frontback-impl/src/com/intellij/codeInsight/completion/JavaFrontendCompletionUtil.kt b/java/java-frontback-impl/src/com/intellij/codeInsight/completion/JavaFrontendCompletionUtil.kt
new file mode 100644
index 000000000000..4f21265440b5
--- /dev/null
+++ b/java/java-frontback-impl/src/com/intellij/codeInsight/completion/JavaFrontendCompletionUtil.kt
@@ -0,0 +1,182 @@
+// 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.codeInsight.completion
+
+import com.intellij.codeInsight.AutoPopupController
+import com.intellij.codeInsight.AutoPopupControllerHelper
+import com.intellij.codeInsight.CodeInsightSettings
+import com.intellij.codeInsight.TailType
+import com.intellij.codeInsight.TailTypes
+import com.intellij.codeInsight.completion.util.CompletionStyleUtil
+import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler
+import com.intellij.codeInsight.editorActions.TabOutScopesTracker
+import com.intellij.codeInsight.lookup.EqTailType
+import com.intellij.codeInsight.lookup.Lookup
+import com.intellij.codeInsight.lookup.LookupElement
+import com.intellij.codeInsight.lookup.LookupItem
+import com.intellij.openapi.editor.ex.EditorSettingsExternalizable
+import com.intellij.patterns.PlatformPatterns
+import com.intellij.psi.*
+import com.intellij.psi.util.PsiTreeUtil
+import com.intellij.psi.util.PsiUtilCore
+import com.intellij.util.PlatformUtils
+import com.intellij.util.ThreeState
+
+object JavaFrontendCompletionUtil {
+ /**
+ * If used in a [FrontendFriendlyInsertHandler], make sure that the original backend item does not have [LookupItem.getTailType].
+ * This tail type is going to be lost if used in the Remote Development scenario.
+ */
+ @JvmStatic
+ fun insertParentheses(
+ context: InsertionContext,
+ item: LookupElement,
+ overloadsMatter: Boolean,
+ hasParams: ThreeState, // UNSURE if providing no arguments is a valid situation
+ forceClosingParenthesis: Boolean,
+ ) {
+ var hasParams = hasParams
+ val editor = context.editor
+ val completionChar = context.completionChar
+ val file = context.file
+
+ val tailType = when (completionChar) {
+ '(' -> TailTypes.noneType()
+ ':' -> TailTypes.conditionalExpressionColonType()
+ else -> LookupItem.handleCompletionChar(context.editor, item, completionChar)
+ }
+
+ val hasTail = tailType !== TailTypes.noneType() && tailType !== TailTypes.unknownType()
+ val smart = completionChar == Lookup.COMPLETE_STATEMENT_SELECT_CHAR
+
+ if (completionChar == '(' || completionChar == '.' || completionChar == ',' || completionChar == ';' || completionChar == ':' || completionChar == ' ') {
+ context.setAddCompletionChar(false)
+ }
+
+ if (hasTail) {
+ hasParams = ThreeState.NO
+ }
+
+ val needRightParenth = forceClosingParenthesis ||
+ !smart && (CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET || hasParams == ThreeState.NO && completionChar != '(')
+
+ context.commitDocument()
+
+ val styleSettings = CompletionStyleUtil.getCodeStyleSettings(context)
+ val elementAt = file.findElementAt(context.startOffset)
+ if (elementAt == null || elementAt.parent !is PsiMethodReferenceExpression) {
+ val hasParameters = hasParams
+ val spaceBetweenParentheses = hasParams == ThreeState.YES && styleSettings.SPACE_WITHIN_METHOD_CALL_PARENTHESES ||
+ hasParams == ThreeState.UNSURE && styleSettings.SPACE_WITHIN_EMPTY_METHOD_CALL_PARENTHESES
+
+ object : ParenthesesInsertHandler(
+ /* spaceBeforeParentheses = */ styleSettings.SPACE_BEFORE_METHOD_CALL_PARENTHESES,
+ /* spaceBetweenParentheses = */ spaceBetweenParentheses,
+ /* mayInsertRightParenthesis = */ needRightParenth,
+ /* allowParametersOnNextLine = */ styleSettings.METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE
+ ) {
+ override fun placeCaretInsideParentheses(context: InsertionContext?, item: LookupElement): Boolean {
+ return hasParameters != ThreeState.NO
+ }
+
+ override fun findExistingLeftParenthesis(context: InsertionContext): PsiElement? {
+ val token = super.findExistingLeftParenthesis(context)
+ return if (isPartOfLambda(token)) null else token
+ }
+
+ fun isPartOfLambda(token: PsiElement?): Boolean {
+ return token != null &&
+ token.parent is PsiExpressionList &&
+ PsiUtilCore.getElementType(PsiTreeUtil.nextVisibleLeaf(token.parent)) === JavaTokenType.ARROW
+ }
+ }.handleInsert(context, item)
+ }
+
+ if (hasParams != ThreeState.NO) {
+ // Invoke parameters popup
+ AutoPopupControllerHelper.getInstance(file.project).autoPopupParameterInfoAfterCompletion(
+ editor = editor,
+ selectedItem = if (overloadsMatter) null else item
+ )
+ }
+
+ if (smart || !needRightParenth || !EditorSettingsExternalizable.getInstance().isInsertParenthesesAutomatically) {
+ return
+ }
+
+ if (!insertTail(context, item, tailType, hasTail)) {
+ return
+ }
+
+ when (completionChar) {
+ '.' -> {
+ AutoPopupController.getInstance(file.project).scheduleAutoPopup(context.editor)
+ }
+ ',' -> {
+ AutoPopupController.getInstance(file.project).autoPopupParameterInfo(context.editor, null)
+ }
+ }
+ }
+
+ private fun insertTail(
+ context: InsertionContext,
+ item: LookupElement,
+ tailType: TailType,
+ hasTail: Boolean,
+ ): Boolean {
+ var toInsert = tailType
+ if (toInsert === EqTailType.INSTANCE) {
+ toInsert = TailTypes.unknownType()
+ }
+
+ val lookupItem = item.`as`(LookupItem.CLASS_CONDITION_KEY)
+ if (lookupItem == null || lookupItem.getAttribute(LookupItem.TAIL_TYPE_ATTR) !== TailTypes.unknownType()) {
+ if (!hasTail && item.getObject() is PsiMethod && PsiTypes.voidType() == (item.getObject() as PsiMethod).returnType) {
+ PsiDocumentManager.getInstance(context.project).commitAllDocuments()
+ if (PlatformPatterns.psiElement().beforeLeaf(PlatformPatterns.psiElement().withText(".")).accepts(context.file.findElementAt(context.tailOffset - 1))) {
+ return false
+ }
+
+ var insertAdditionalSemicolon = true
+ val leaf = context.file.findElementAt(context.startOffset)
+ val composite = leaf?.parent
+ if (composite is PsiReferenceExpression) {
+ var parent = composite.parent
+ if (parent is PsiMethodCallExpression) {
+ parent = parent.parent
+ }
+
+ if (parent is PsiLambdaExpression && !insertSemicolonAfter(parent)) {
+ insertAdditionalSemicolon = false
+ }
+ if (parent is PsiExpressionStatement && (parent.parent as? PsiForStatement)?.update === parent) {
+ insertAdditionalSemicolon = false
+ }
+ }
+ if (insertAdditionalSemicolon) {
+ toInsert = TailTypes.semicolonType()
+ }
+ }
+ }
+ val editor = context.editor
+ val tailOffset = context.tailOffset
+ val afterTailOffset = toInsert.processTail(editor, tailOffset)
+ val caretOffset = editor.caretModel.offset
+
+ // todo IJPL-207762 what's this??? shall it run on frontend or backend???
+ if (tailOffset in (caretOffset + 1).. 0) {
+ TabOutScopesTracker.getInstance().registerEmptyScope(editor, caretOffset, afterTailOffset)
+ }
+
+ return true
+ }
+
+ @JvmStatic
+ fun insertSemicolonAfter(lambdaExpression: PsiLambdaExpression): Boolean {
+ return lambdaExpression.body is PsiCodeBlock || insertSemicolon(lambdaExpression.parent)
+ }
+
+ @JvmStatic
+ fun insertSemicolon(parent: PsiElement?): Boolean {
+ return parent !is PsiExpressionList && parent !is PsiExpression
+ }
+}
\ No newline at end of file
diff --git a/java/java-frontback-impl/src/com/intellij/codeInsight/completion/method/FrontendFriendlyParenthesesInsertHandler.kt b/java/java-frontback-impl/src/com/intellij/codeInsight/completion/method/FrontendFriendlyParenthesesInsertHandler.kt
new file mode 100644
index 000000000000..4661823c2671
--- /dev/null
+++ b/java/java-frontback-impl/src/com/intellij/codeInsight/completion/method/FrontendFriendlyParenthesesInsertHandler.kt
@@ -0,0 +1,32 @@
+// 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.codeInsight.completion.method
+
+import com.intellij.codeInsight.completion.FrontendFriendlyInsertHandler
+import com.intellij.codeInsight.completion.InsertionContext
+import com.intellij.codeInsight.completion.JavaFrontendCompletionUtil
+import com.intellij.codeInsight.lookup.LookupElement
+import com.intellij.util.ThreeState
+import kotlinx.serialization.Serializable
+
+@Suppress("NonDefaultConstructor")
+@Serializable
+class FrontendFriendlyParenthesesInsertHandler(
+ private val hasParameters: Boolean
+) : FrontendFriendlyInsertHandler {
+ override fun handleInsert(context: InsertionContext, item: LookupElement) {
+ // todo FrontendFriendlyParenthInsertHandler differs from ParenthInsertHandler
+ // in that it does not check lookup elements for overloads with `MethodParenthesesHandler.overloadsHaveParameters`
+ // not sure if this can be reliably implemented on frontend
+ insertParenthesesForJavaMethod(item, context, ThreeState.fromBoolean(hasParameters))
+ }
+
+ companion object {
+ fun insertParenthesesForJavaMethod(
+ item: LookupElement,
+ context: InsertionContext,
+ hasParams: ThreeState,
+ ) {
+ JavaFrontendCompletionUtil.insertParentheses(context, item, false, hasParams, false)
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java
index dbb511db00f4..f3b7e37ae652 100644
--- a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java
+++ b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java
@@ -423,8 +423,8 @@ public final class ExpectedTypesProvider {
final PsiType functionalInterfaceType = lambdaExpression.getFunctionalInterfaceType();
final PsiMethod scopeMethod = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
if (scopeMethod != null) {
- visitMethodReturnType(scopeMethod, LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType),
- JavaCompletionUtil.insertSemicolonAfter(lambdaExpression));
+ visitMethodReturnType(scopeMethod, LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType),
+ JavaFrontendCompletionUtil.insertSemicolonAfter(lambdaExpression));
}
}
@@ -438,7 +438,7 @@ public final class ExpectedTypesProvider {
final PsiType functionalInterfaceType = ((PsiLambdaExpression)psiElement).getFunctionalInterfaceType();
method = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
type = LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType);
- tailTypeSemicolon = JavaCompletionUtil.insertSemicolonAfter((PsiLambdaExpression)psiElement);
+ tailTypeSemicolon = JavaFrontendCompletionUtil.insertSemicolonAfter((PsiLambdaExpression)psiElement);
}
else if (psiElement instanceof PsiMethod) {
method = (PsiMethod)psiElement;
diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/ConstructorInsertHandler.java b/java/java-impl/src/com/intellij/codeInsight/completion/ConstructorInsertHandler.java
index dd6e78117cab..fb3f68cb8b0d 100644
--- a/java/java-impl/src/com/intellij/codeInsight/completion/ConstructorInsertHandler.java
+++ b/java/java-impl/src/com/intellij/codeInsight/completion/ConstructorInsertHandler.java
@@ -244,7 +244,7 @@ public final class ConstructorInsertHandler implements InsertHandler processLabelReference(PsiLabelReference reference) {
diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java
index 76006ddc3e66..8412a18d29c5 100644
--- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java
+++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java
@@ -1,12 +1,14 @@
// 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.codeInsight.completion;
-import com.intellij.codeInsight.*;
+import com.intellij.codeInsight.CodeInsightSettings;
+import com.intellij.codeInsight.CodeInsightUtilCore;
+import com.intellij.codeInsight.ExpectedTypeInfo;
+import com.intellij.codeInsight.JavaProjectCodeInsightSettings;
import com.intellij.codeInsight.completion.impl.CamelHumpMatcher;
import com.intellij.codeInsight.completion.scope.CompletionElement;
import com.intellij.codeInsight.completion.scope.JavaCompletionProcessor;
import com.intellij.codeInsight.completion.util.CompletionStyleUtil;
-import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler;
import com.intellij.codeInsight.editorActions.TabOutScopesTracker;
import com.intellij.codeInsight.guess.GuessManager;
import com.intellij.codeInsight.lookup.*;
@@ -15,9 +17,7 @@ import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
-import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.module.JdkApiCompatibilityService;
import com.intellij.openapi.project.DumbService;
@@ -65,7 +65,6 @@ import java.util.function.Consumer;
import java.util.function.Function;
import static com.intellij.codeInsight.completion.ReferenceExpressionCompletionContributor.findConstantsUsedInSwitch;
-import static com.intellij.patterns.PlatformPatterns.psiElement;
import static com.intellij.psi.util.proximity.ReferenceListWeigher.ReferenceListApplicability.inapplicable;
public final class JavaCompletionUtil {
@@ -466,14 +465,6 @@ public final class JavaCompletionUtil {
return type instanceof PsiClassType ? ((PsiClassType)type).rawType() : type;
}
- public static boolean insertSemicolonAfter(@NotNull PsiLambdaExpression lambdaExpression) {
- return lambdaExpression.getBody() instanceof PsiCodeBlock || insertSemicolon(lambdaExpression.getParent());
- }
-
- static boolean insertSemicolon(PsiElement parent) {
- return !(parent instanceof PsiExpressionList) && !(parent instanceof PsiExpression);
- }
-
static class JavaLookupElementHighlighter {
private final @NotNull PsiElement myPlace;
private final @Nullable VirtualFile myOriginalFile;
@@ -794,125 +785,7 @@ public final class JavaCompletionUtil {
@NotNull LookupElement item,
boolean overloadsMatter,
boolean hasParams) {
- insertParentheses(context, item, overloadsMatter, ThreeState.fromBoolean(hasParams), false);
- }
-
- static void insertParentheses(@NotNull InsertionContext context,
- @NotNull LookupElement item,
- boolean overloadsMatter,
- @NotNull ThreeState hasParams, // UNSURE if providing no arguments is a valid situation
- boolean forceClosingParenthesis) {
- Editor editor = context.getEditor();
- char completionChar = context.getCompletionChar();
- PsiFile file = context.getFile();
-
- TailType tailType = completionChar == '(' ? TailTypes.noneType() :
- completionChar == ':' ? TailTypes.conditionalExpressionColonType() :
- LookupItem.handleCompletionChar(context.getEditor(), item, completionChar);
- boolean hasTail = tailType != TailTypes.noneType() && tailType != TailTypes.unknownType();
- boolean smart = completionChar == Lookup.COMPLETE_STATEMENT_SELECT_CHAR;
-
- if (completionChar == '(' || completionChar == '.' || completionChar == ',' || completionChar == ';' || completionChar == ':' || completionChar == ' ') {
- context.setAddCompletionChar(false);
- }
-
- if (hasTail) {
- hasParams = ThreeState.NO;
- }
- boolean needRightParenth = forceClosingParenthesis ||
- !smart && (CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET ||
- hasParams == ThreeState.NO && completionChar != '(');
-
- context.commitDocument();
-
- CommonCodeStyleSettings styleSettings = CompletionStyleUtil.getCodeStyleSettings(context);
- PsiElement elementAt = file.findElementAt(context.getStartOffset());
- if (elementAt == null || !(elementAt.getParent() instanceof PsiMethodReferenceExpression)) {
- ThreeState hasParameters = hasParams;
- boolean spaceBetweenParentheses = hasParams == ThreeState.YES && styleSettings.SPACE_WITHIN_METHOD_CALL_PARENTHESES ||
- hasParams == ThreeState.UNSURE && styleSettings.SPACE_WITHIN_EMPTY_METHOD_CALL_PARENTHESES;
- new ParenthesesInsertHandler<>(styleSettings.SPACE_BEFORE_METHOD_CALL_PARENTHESES, spaceBetweenParentheses,
- needRightParenth, styleSettings.METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE) {
- @Override
- protected boolean placeCaretInsideParentheses(InsertionContext context1, LookupElement item1) {
- return hasParameters != ThreeState.NO;
- }
-
- @Override
- protected PsiElement findExistingLeftParenthesis(@NotNull InsertionContext context) {
- PsiElement token = super.findExistingLeftParenthesis(context);
- return isPartOfLambda(token) ? null : token;
- }
-
- private static boolean isPartOfLambda(PsiElement token) {
- return token != null && token.getParent() instanceof PsiExpressionList &&
- PsiUtilCore.getElementType(PsiTreeUtil.nextVisibleLeaf(token.getParent())) == JavaTokenType.ARROW;
- }
- }.handleInsert(context, item);
- }
-
- if (hasParams != ThreeState.NO) {
- // Invoke parameters popup
- AutoPopupController.getInstance(file.getProject()).autoPopupParameterInfo(editor, overloadsMatter ? null : (PsiElement)item.getObject());
- }
-
- if (smart || !needRightParenth || !EditorSettingsExternalizable.getInstance().isInsertParenthesesAutomatically() ||
- !insertTail(context, item, tailType, hasTail)) {
- return;
- }
-
- if (completionChar == '.') {
- AutoPopupController.getInstance(file.getProject()).scheduleAutoPopup(context.getEditor());
- } else if (completionChar == ',') {
- AutoPopupController.getInstance(file.getProject()).autoPopupParameterInfo(context.getEditor(), null);
- }
- }
-
- private static boolean insertTail(InsertionContext context, LookupElement item, TailType tailType, boolean hasTail) {
- TailType toInsert = tailType;
- LookupItem> lookupItem = item.as(LookupItem.CLASS_CONDITION_KEY);
- if (toInsert == EqTailType.INSTANCE) {
- toInsert = TailTypes.unknownType();
- }
- if (lookupItem == null || lookupItem.getAttribute(LookupItem.TAIL_TYPE_ATTR) != TailTypes.unknownType()) {
- if (!hasTail && item.getObject() instanceof PsiMethod && PsiTypes.voidType().equals(((PsiMethod)item.getObject()).getReturnType())) {
- PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments();
- if (psiElement().beforeLeaf(psiElement().withText(".")).accepts(context.getFile().findElementAt(context.getTailOffset() - 1))) {
- return false;
- }
-
- boolean insertAdditionalSemicolon = true;
- PsiElement leaf = context.getFile().findElementAt(context.getStartOffset());
- PsiElement composite = leaf == null ? null : leaf.getParent();
- if (composite instanceof PsiReferenceExpression) {
- PsiElement parent = composite.getParent();
- if (parent instanceof PsiMethodCallExpression) {
- parent = parent.getParent();
- }
- if (parent instanceof PsiLambdaExpression lambda && !insertSemicolonAfter(lambda)) {
- insertAdditionalSemicolon = false;
- }
- if (parent instanceof PsiExpressionStatement && parent.getParent() instanceof PsiForStatement forStatement &&
- forStatement.getUpdate() == parent) {
- insertAdditionalSemicolon = false;
- }
- }
- if (insertAdditionalSemicolon) {
- toInsert = TailTypes.semicolonType();
- }
-
- }
- }
- Editor editor = context.getEditor();
- int tailOffset = context.getTailOffset();
- int afterTailOffset = toInsert.processTail(editor, tailOffset);
- int caretOffset = editor.getCaretModel().getOffset();
- if (afterTailOffset > tailOffset &&
- tailOffset > caretOffset &&
- TabOutScopesTracker.getInstance().removeScopeEndingAt(editor, caretOffset) > 0) {
- TabOutScopesTracker.getInstance().registerEmptyScope(editor, caretOffset, afterTailOffset);
- }
- return true;
+ JavaFrontendCompletionUtil.insertParentheses(context, item, overloadsMatter, ThreeState.fromBoolean(hasParams), false);
}
//need to shorten references in type argument list
diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaMethodCallInsertHandler.kt b/java/java-impl/src/com/intellij/codeInsight/completion/JavaMethodCallInsertHandler.kt
index d377dc698152..7f2c2070f730 100644
--- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaMethodCallInsertHandler.kt
+++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaMethodCallInsertHandler.kt
@@ -2,6 +2,7 @@
package com.intellij.codeInsight.completion
import com.intellij.codeInsight.CodeInsightSettings
+import com.intellij.codeInsight.TailTypes
import com.intellij.codeInsight.completion.JavaMethodCallElement.areParameterTemplatesEnabledOnCompletion
import com.intellij.codeInsight.completion.JavaMethodCallInsertHandler.Companion.needParameterHints
import com.intellij.codeInsight.completion.JavaMethodCallInsertHandler.Companion.showParameterHints
@@ -70,7 +71,7 @@ public class JavaMethodCallInsertHandler(
private val myHandlers: List> = listOfNotNull(
RefStartInsertHandler(),
createDiamondInsertHandler(item),
- ParenthInsertHandler(),
+ ParenthInsertHandler.create(item),
beforeHandler,
ImportQualifyAndInsertTypeParametersHandler.create(needImportOrQualify, needExplicitTypeParameters, item),
MethodCallInstallerHandler(),
@@ -192,12 +193,28 @@ private fun createDiamondInsertHandler(item: JavaMethodCallElement): InsertHandl
return DiamondInsertHandler()
}
-private class ParenthInsertHandler : InsertHandler {
+private class ParenthInsertHandler private constructor(
+ private val hasParameters: Boolean,
+ private val hasTailType: Boolean,
+) : InsertHandler, FrontendConvertibleInsertHandler {
override fun handleInsert(context: InsertionContext, item: JavaMethodCallElement) {
val method = item.getObject()
val allItems = context.elements
- val hasParams = if (method.parameterList.isEmpty) ThreeState.NO else MethodParenthesesHandler.overloadsHaveParameters(allItems, method)
- JavaCompletionUtil.insertParentheses(context, item, false, hasParams, false)
+ val hasParams = if (hasParameters) MethodParenthesesHandler.overloadsHaveParameters(allItems, method) else ThreeState.NO
+ FrontendFriendlyParenthesesInsertHandler.insertParenthesesForJavaMethod(item, context, hasParams)
+ }
+
+ override fun asFrontendFriendly(): FrontendFriendlyInsertHandler? {
+ if (hasTailType) return null
+ return FrontendFriendlyParenthesesInsertHandler(hasParameters)
+ }
+
+ companion object {
+ fun create(item: JavaMethodCallElement): InsertHandler {
+ val method = item.getObject()
+ val hasTailType = item.tailType != TailTypes.unknownType()
+ return ParenthInsertHandler(!method.parameterList.isEmpty, hasTailType)
+ }
}
}
diff --git a/platform/completion/common/resources/intellij.platform.completion.common.xml b/platform/completion/common/resources/intellij.platform.completion.common.xml
index aa52f3dbdcd0..80cc23c2cfad 100644
--- a/platform/completion/common/resources/intellij.platform.completion.common.xml
+++ b/platform/completion/common/resources/intellij.platform.completion.common.xml
@@ -1,2 +1,7 @@
+
+
+
+
diff --git a/platform/completion/common/src/AutoPopupControllerHelperImpl.kt b/platform/completion/common/src/AutoPopupControllerHelperImpl.kt
new file mode 100644
index 000000000000..88a4f15ff513
--- /dev/null
+++ b/platform/completion/common/src/AutoPopupControllerHelperImpl.kt
@@ -0,0 +1,36 @@
+// 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.platform.completion.common
+
+import com.intellij.codeInsight.AutoPopupController
+import com.intellij.codeInsight.AutoPopupControllerHelper
+import com.intellij.codeInsight.lookup.LookupElement
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.editor.impl.editorId
+import com.intellij.openapi.project.Project
+import com.intellij.platform.project.projectId
+import com.intellij.psi.PsiElement
+import com.intellij.util.PlatformUtils
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+internal class AutoPopupControllerHelperImpl(
+ private val project: Project,
+ private val scope: CoroutineScope,
+) : AutoPopupControllerHelper {
+ override fun autoPopupParameterInfoAfterCompletion(editor: Editor, selectedItem: LookupElement?) {
+ if (PlatformUtils.isJetBrainsClient()) {
+ triggerAutopopupParameterInfoOnBackend(editor, selectedItem)
+ }
+ else {
+ val highlightedMethod = selectedItem?.`object` as? PsiElement
+ AutoPopupController.getInstance(project).autoPopupParameterInfo(editor, highlightedMethod)
+ }
+ }
+
+ private fun triggerAutopopupParameterInfoOnBackend(editor: Editor, selectedItem: LookupElement?) {
+ scope.launch {
+ val rpcId = (selectedItem as? LookupElementWithRpcId)?.rpcId
+ AutoPopupControllerRpc.getInstance().autoPopupParameterInfo(editor.editorId(), project.projectId(), rpcId)
+ }
+ }
+}
\ No newline at end of file
diff --git a/platform/completion/common/src/AutoPopupControllerRpc.kt b/platform/completion/common/src/AutoPopupControllerRpc.kt
new file mode 100644
index 000000000000..ef8569d2c357
--- /dev/null
+++ b/platform/completion/common/src/AutoPopupControllerRpc.kt
@@ -0,0 +1,28 @@
+// 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.platform.completion.common
+
+import com.intellij.openapi.editor.impl.EditorId
+import com.intellij.platform.completion.common.protocol.RpcCompletionItemId
+import com.intellij.platform.project.ProjectId
+import com.intellij.platform.rpc.RemoteApiProviderService
+import fleet.rpc.RemoteApi
+import fleet.rpc.Rpc
+import fleet.rpc.remoteApiDescriptor
+import org.jetbrains.annotations.ApiStatus
+
+/**
+ * This is a hack for Java plugin
+ * Available in split mode only, do not try to use in monolith mode.
+ */
+@ApiStatus.Internal
+@Rpc
+interface AutoPopupControllerRpc : RemoteApi {
+ suspend fun autoPopupParameterInfo(editorId: EditorId, projectId: ProjectId, item: RpcCompletionItemId?)
+
+ companion object {
+ @JvmStatic
+ suspend fun getInstance(): AutoPopupControllerRpc {
+ return RemoteApiProviderService.resolve(remoteApiDescriptor())
+ }
+ }
+}
\ No newline at end of file
diff --git a/platform/completion/common/src/LookupElementWithRpcId.kt b/platform/completion/common/src/LookupElementWithRpcId.kt
new file mode 100644
index 000000000000..b71739ae3bde
--- /dev/null
+++ b/platform/completion/common/src/LookupElementWithRpcId.kt
@@ -0,0 +1,16 @@
+// 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.platform.completion.common
+
+import com.intellij.codeInsight.lookup.LookupElement
+import com.intellij.platform.completion.common.protocol.RpcCompletionItem
+import com.intellij.platform.completion.common.protocol.RpcCompletionItemId
+
+/**
+ * Marker interface for [LookupElement]s that are backed by [RpcCompletionItem]s.
+ */
+interface LookupElementWithRpcId {
+ /**
+ * ID of the corresponding [RpcCompletionItem] or `null` if the element is not backed by an RPC completion item.
+ */
+ val rpcId: RpcCompletionItemId?
+}
\ No newline at end of file
diff --git a/platform/completion/common/src/package-info.java b/platform/completion/common/src/package-info.java
new file mode 100644
index 000000000000..1acf0322b500
--- /dev/null
+++ b/platform/completion/common/src/package-info.java
@@ -0,0 +1,5 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+@ApiStatus.Internal
+package com.intellij.platform.completion.common;
+
+import org.jetbrains.annotations.ApiStatus;
\ No newline at end of file
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupControllerHelper.kt b/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupControllerHelper.kt
new file mode 100644
index 000000000000..e9eef601b3aa
--- /dev/null
+++ b/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupControllerHelper.kt
@@ -0,0 +1,27 @@
+// 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.codeInsight
+
+import com.intellij.codeInsight.lookup.LookupElement
+import com.intellij.openapi.components.service
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.project.Project
+import com.intellij.psi.PsiElement
+import org.jetbrains.annotations.ApiStatus
+
+/**
+ * Hack for Java plugin for talking to backend in rem-dev scenarios.
+ * Must be removed once Java plugin is converted to v2.
+ */
+@ApiStatus.Internal
+interface AutoPopupControllerHelper {
+ /**
+ * Invokes parameter info popup for the given method.
+ * In the RemDev scenario, the popup will be scheduled on the backend
+ */
+ fun autoPopupParameterInfoAfterCompletion(editor: Editor, selectedItem: LookupElement?)
+
+ companion object {
+ @JvmStatic
+ fun getInstance(project: Project): AutoPopupControllerHelper = project.service()
+ }
+}
\ No newline at end of file