From 07eb11c3570a4776cb4feae13a05070db3bd66aa Mon Sep 17 00:00:00 2001 From: Mikhail Pyltsin Date: Tue, 22 Apr 2025 15:34:47 +0200 Subject: [PATCH] [command-completion] IDEA-371054 Add 'inject' completion command GitOrigin-RevId: 203ecfaa191398c5b438434549c41514ebdb7523 --- .../JavaCommandCompletionFactory.java | 3 ++ .../commands/IntentionCompletionCommand.kt | 6 ++-- .../KotlinCommandCompletionFactory.kt | 36 +++++++++++++++++-- .../META-INF/command-completion-k2.xml | 3 ++ 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/JavaCommandCompletionFactory.java b/java/java-impl/src/com/intellij/codeInsight/completion/commands/JavaCommandCompletionFactory.java index 69dee5241c5d..961365c5737d 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/JavaCommandCompletionFactory.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/JavaCommandCompletionFactory.java @@ -76,6 +76,9 @@ class JavaCommandCompletionFactory implements CommandCompletionFactory, DumbAwar } results.add(currentOffset); if (element == null) continue; + if (element.getParent() instanceof PsiLiteralExpression literalExpression && literalExpression.getValue() instanceof String) { + results.add(literalExpression.getTextRange().getEndOffset() - (literalExpression.isTextBlock() ? 3 : 1)); + } PsiElement parent = element.getParent(); if (element instanceof PsiJavaToken) { Character open = braces.get(element.getText().charAt(0)); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/IntentionCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/IntentionCompletionCommand.kt index af27ad0a1ff5..582a25c58e00 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/IntentionCompletionCommand.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/IntentionCompletionCommand.kt @@ -22,7 +22,7 @@ internal class IntentionCompletionCommand( override val highlightInfo: HighlightInfoLookup?, private val myOffset: Int, private val previewProvider: () -> IntentionPreviewInfo?, - ) : CompletionCommand(), CompletionCommandWithPreview { +) : CompletionCommand(), CompletionCommandWithPreview { override val name: String get() = intentionAction.text @@ -44,8 +44,8 @@ internal class IntentionCompletionCommand( if (availableFor) { ShowIntentionActionsHandler.chooseActionAndInvoke(psiFile, editor, intentionAction.action, name) } - if (targetMarker.isValid && targetMarker.startOffset != editor.caretModel.offset) { - //probably, intention moves the cursor + if (!intentionAction.action.startInWriteAction() || (targetMarker.isValid && targetMarker.startOffset != editor.caretModel.offset)) { + //probably, intention moves the cursor or async gui return } if (marker.isValid) { diff --git a/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/commands/KotlinCommandCompletionFactory.kt b/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/commands/KotlinCommandCompletionFactory.kt index 87202feff2e0..90f35f374960 100644 --- a/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/commands/KotlinCommandCompletionFactory.kt +++ b/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/commands/KotlinCommandCompletionFactory.kt @@ -2,9 +2,17 @@ package org.jetbrains.kotlin.idea.completion.impl.k2.contributors.commands import com.intellij.codeInsight.completion.command.CommandCompletionFactory +import com.intellij.codeInsight.completion.command.commands.IntentionCommandOffsetProvider import com.intellij.openapi.project.DumbAware import com.intellij.psi.PsiFile +import com.intellij.psi.PsiLanguageInjectionHost import com.intellij.testFramework.LightVirtualFile +import org.jetbrains.kotlin.analysis.api.KaExperimentalApi +import org.jetbrains.kotlin.analysis.api.KaImplementationDetail +import org.jetbrains.kotlin.analysis.api.projectStructure.analysisContextModule +import org.jetbrains.kotlin.analysis.api.projectStructure.contextModule +import org.jetbrains.kotlin.idea.base.codeInsight.handlers.fixers.range +import org.jetbrains.kotlin.idea.base.projectStructure.getKaModule import org.jetbrains.kotlin.idea.base.psi.copied import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.isAncestor @@ -28,9 +36,11 @@ class KotlinCommandCompletionFactory : CommandCompletionFactory, DumbAware { return true } + @OptIn(KaImplementationDetail::class, KaExperimentalApi::class) override fun createFile(originalFile: PsiFile, text: String): PsiFile? { - val newFile = KtPsiFactory.contextual(originalFile, eventSystemEnabled = true).createFile(originalFile.name, text) - newFile.originalFile = originalFile + val newFile = + KtPsiFactory(originalFile.project, eventSystemEnabled = true, markGenerated = false).createFile(originalFile.name, text) + newFile.contextModule = originalFile.getKaModule(originalFile.project, useSiteModule = null) if (originalFile.name.endsWith(".kts")) { createCopyOfScript(originalFile, newFile)?.let { return it } } @@ -38,9 +48,9 @@ class KotlinCommandCompletionFactory : CommandCompletionFactory, DumbAware { val virtualFile = newFile.virtualFile val originalVirtualFile = originalFile.virtualFile if (virtualFile is LightVirtualFile && originalVirtualFile != null) { + virtualFile.analysisContextModule = originalFile.getKaModule(originalFile.project, useSiteModule = null) virtualFile.originalFile = originalVirtualFile virtualFile.fileType = originalVirtualFile.fileType - } return newFile } @@ -54,4 +64,24 @@ class KotlinCommandCompletionFactory : CommandCompletionFactory, DumbAware { copyOfOriginalBlockExpression.replace(copiedNewBlockExpression.copy()) return newFileCopy } + + class KotlinIntentionCommandOffsetProvider : IntentionCommandOffsetProvider { + override fun findOffsets(psiFile: PsiFile, offset: Int): List { + val offsets = mutableListOf() + offsets.add(offset) + if (offset == 0) { + return offsets + } + val previousElement = psiFile.findElementAt(offset - 1) + if (previousElement?.parent is PsiLanguageInjectionHost) { + val delta = if (previousElement.parent.text.endsWith("\"\"\"")) { + 3 + } else { + 1 + } + offsets.add(previousElement.parent.range.endOffset - delta) + } + return offsets + } + } } diff --git a/plugins/kotlin/plugin/k2/resources/META-INF/command-completion-k2.xml b/plugins/kotlin/plugin/k2/resources/META-INF/command-completion-k2.xml index 015c576efe9b..f9d5451c5fcc 100644 --- a/plugins/kotlin/plugin/k2/resources/META-INF/command-completion-k2.xml +++ b/plugins/kotlin/plugin/k2/resources/META-INF/command-completion-k2.xml @@ -1,6 +1,9 @@ + +