mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
[java, completion] IJPL-207762 ParenthInsertHandler is frontend compatible
GitOrigin-RevId: 4533706e69b021e76114bb5790d9ba314552e0b8
This commit is contained in:
committed by
intellij-monorepo-bot
parent
27b90b18a3
commit
8f9d4cb5b0
@@ -81,6 +81,7 @@
|
||||
|
||||
<completion.frontendFriendlyInsertHandler implementationClass="com.intellij.codeInsight.completion.method.DiamondInsertHandler"/>
|
||||
<completion.frontendFriendlyInsertHandler implementationClass="com.intellij.codeInsight.completion.method.RefStartInsertHandler"/>
|
||||
<completion.frontendFriendlyInsertHandler implementationClass="com.intellij.codeInsight.completion.method.FrontendFriendlyParenthesesInsertHandler"/>
|
||||
<completion.frontendFriendlyInsertHandler implementationClass="com.intellij.codeInsight.completion.method.MethodCallInstallerHandler"/>
|
||||
<completion.frontendFriendlyInsertHandler implementationClass="com.intellij.codeInsight.completion.method.NegationInsertHandler"/>
|
||||
|
||||
|
||||
+182
@@ -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<LookupElement>(
|
||||
/* 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)..<afterTailOffset && TabOutScopesTracker.getInstance().removeScopeEndingAt(editor, caretOffset) > 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
|
||||
}
|
||||
}
|
||||
+32
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user