1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-06-05 22:34:03 +02:00

Fix(VIM-3376): Refactor the way IdeaVim executes actions

Now instead of a few hacks, we use a special function from the platform
This commit is contained in:
Alex Plate 2024-07-10 10:06:44 +03:00
parent ff44596c1a
commit 24514039e1
No known key found for this signature in database
GPG Key ID: 0B97153C8FFEC09F
7 changed files with 74 additions and 55 deletions
.teamcity/_Self
gradle.properties
src
main/java/com/maddyhome/idea/vim
test/java/org/jetbrains/plugins/ideavim/action

View File

@ -5,13 +5,13 @@ object Constants {
const val EAP_CHANNEL = "eap" const val EAP_CHANNEL = "eap"
const val DEV_CHANNEL = "Dev" const val DEV_CHANNEL = "Dev"
const val GITHUB_TESTS = "2024.1.1" const val GITHUB_TESTS = "2024.1.2"
const val NVIM_TESTS = "2024.1.1" const val NVIM_TESTS = "2024.1.2"
const val PROPERTY_TESTS = "2024.1.1" const val PROPERTY_TESTS = "2024.1.2"
const val LONG_RUNNING_TESTS = "2024.1.1" const val LONG_RUNNING_TESTS = "2024.1.2"
const val QODANA_TESTS = "2024.1.1" const val QODANA_TESTS = "2024.1.2"
const val RELEASE = "2024.1.1" const val RELEASE = "2024.1.2"
const val RELEASE_DEV = "2024.1.1" const val RELEASE_DEV = "2024.1.2"
const val RELEASE_EAP = "2024.1.1" const val RELEASE_EAP = "2024.1.2"
} }

View File

@ -16,7 +16,7 @@
# https://data.services.jetbrains.com/products?code=IC # https://data.services.jetbrains.com/products?code=IC
# Maven releases are here: https://www.jetbrains.com/intellij-repository/releases # Maven releases are here: https://www.jetbrains.com/intellij-repository/releases
# And snapshots: https://www.jetbrains.com/intellij-repository/snapshots # And snapshots: https://www.jetbrains.com/intellij-repository/snapshots
ideaVersion=2024.1.1 ideaVersion=2024.1.2
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type # Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
ideaType=IC ideaType=IC
instrumentPluginCode=true instrumentPluginCode=true

View File

@ -36,6 +36,7 @@ import com.maddyhome.idea.vim.helper.isPrimaryEditor
import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
import com.maddyhome.idea.vim.newapi.actionStartedFromVim import com.maddyhome.idea.vim.newapi.actionStartedFromVim
import com.maddyhome.idea.vim.newapi.globalIjOptions import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.newapi.runningIJAction
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
import java.awt.event.KeyEvent import java.awt.event.KeyEvent
@ -164,6 +165,7 @@ internal abstract class OctopusHandler(private val nextHandler: EditorActionHand
} }
if (dataContext?.actionStartedFromVim == true) return true if (dataContext?.actionStartedFromVim == true) return true
if (runningIJAction) return true
return false return false
} }

View File

@ -8,7 +8,6 @@
package com.maddyhome.idea.vim.helper package com.maddyhome.idea.vim.helper
import com.intellij.openapi.actionSystem.ActionGroup
import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.ActionPlaces import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnAction
@ -17,6 +16,7 @@ import com.intellij.openapi.actionSystem.AnActionResult
import com.intellij.openapi.actionSystem.DataContextWrapper import com.intellij.openapi.actionSystem.DataContextWrapper
import com.intellij.openapi.actionSystem.EmptyAction import com.intellij.openapi.actionSystem.EmptyAction
import com.intellij.openapi.actionSystem.IdeActions import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.actionSystem.PlatformCoreDataKeys
import com.intellij.openapi.actionSystem.PlatformDataKeys import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.actionSystem.ex.ActionManagerEx import com.intellij.openapi.actionSystem.ex.ActionManagerEx
import com.intellij.openapi.actionSystem.ex.ActionUtil import com.intellij.openapi.actionSystem.ex.ActionUtil
@ -24,10 +24,13 @@ import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.command.UndoConfirmationPolicy import com.intellij.openapi.command.UndoConfirmationPolicy
import com.intellij.openapi.components.Service import com.intellij.openapi.components.Service
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.editor.actionSystem.DocCommandGroupId import com.intellij.openapi.editor.actionSystem.DocCommandGroupId
import com.intellij.openapi.project.IndexNotReadyException import com.intellij.openapi.project.IndexNotReadyException
import com.intellij.openapi.ui.popup.JBPopupFactory import com.intellij.openapi.util.ActionCallback
import com.intellij.openapi.util.NlsContexts import com.intellij.openapi.util.NlsContexts
import com.intellij.openapi.util.await
import com.intellij.openapi.util.registry.Registry
import com.intellij.util.SlowOperations import com.intellij.util.SlowOperations
import com.maddyhome.idea.vim.RegisterActions import com.maddyhome.idea.vim.RegisterActions
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
@ -39,10 +42,11 @@ import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
import com.maddyhome.idea.vim.newapi.IjNativeAction import com.maddyhome.idea.vim.newapi.IjNativeAction
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.runFromVimKey import com.maddyhome.idea.vim.newapi.runFromVimKey
import com.maddyhome.idea.vim.newapi.runningIJAction
import kotlinx.coroutines.runBlocking
import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.NonNls
import java.awt.Component import java.awt.Component
import javax.swing.JComponent import javax.swing.JComponent
import javax.swing.SwingUtilities
@Service @Service
internal class IjActionExecutor : VimActionExecutor { internal class IjActionExecutor : VimActionExecutor {
@ -77,45 +81,54 @@ internal class IjActionExecutor : VimActionExecutor {
val dataContext = DataContextWrapper(context.ij) val dataContext = DataContextWrapper(context.ij)
dataContext.putUserData(runFromVimKey, true) dataContext.putUserData(runFromVimKey, true)
val actionId = ActionManager.getInstance().getId(ijAction) val contextComponent = PlatformCoreDataKeys.CONTEXT_COMPONENT.getData(dataContext)
val event = AnActionEvent( ?: editor?.ij?.component ?: run {
null, LOG.error("Can't get the context component")
dataContext, return false
ActionPlaces.KEYBOARD_SHORTCUT,
ijAction.templatePresentation.clone(),
ActionManager.getInstance(),
0,
)
// beforeActionPerformedUpdate should be called to update the action. It fixes some rider-specific problems.
// because rider use async update method. See VIM-1819.
// This method executes inside of lastUpdateAndCheckDumb
// Another related issue: VIM-2604
// This is a hack to fix the tests and fix VIM-3332
// We should get rid of it in VIM-3376
if (actionId == "RunClass" || actionId == IdeActions.ACTION_COMMENT_LINE || actionId == IdeActions.ACTION_COMMENT_BLOCK) {
ijAction.beforeActionPerformedUpdate(event)
if (!event.presentation.isEnabled) return false
} else {
if (!ActionUtil.lastUpdateAndCheckDumb(ijAction, event, false)) return false
}
if (ijAction is ActionGroup && !event.presentation.isPerformGroup) {
// Some ActionGroups should not be performed, but shown as a popup
val popup = JBPopupFactory.getInstance()
.createActionGroupPopup(event.presentation.text, ijAction, dataContext, false, null, -1)
val component = dataContext.getData(PlatformDataKeys.CONTEXT_COMPONENT)
if (component != null) {
val window = SwingUtilities.getWindowAncestor(component)
if (window != null) {
popup.showInCenterOf(window)
}
return true
} }
popup.showInFocusCenter()
return true val result = withRunningAction {
} else { val result = withStringRegistryOption {
performDumbAwareWithCallbacks(ijAction, event) { ijAction.actionPerformed(event) } ActionManager.getInstance()
return true .tryToExecute(ijAction, null, contextComponent, ActionPlaces.KEYBOARD_SHORTCUT, true)
}
result.wait()
}
return result.isDone
}
private fun <T> withRunningAction(block: () -> T): T {
runningIJAction = true
try {
return block()
} finally {
runningIJAction = false
}
}
private fun ActionCallback.wait(): ActionCallback {
runBlocking {
try {
await()
} catch (_: RuntimeException) {
// Nothing
// The exception happens when the action is rejected
// and the exception message explains the reason for rejection
// At the moment, we don't process this information
}
}
return this
}
@Suppress("SameParameterValue")
private fun <T> withStringRegistryOption(block: () -> T): T {
val registry = Registry.get("actionSystem.update.beforeActionPerformedUpdate")
val oldValue = registry.asString()
registry.setValue("on")
try {
return block()
} finally {
registry.setValue(oldValue)
} }
} }
@ -245,4 +258,8 @@ internal class IjActionExecutor : VimActionExecutor {
override fun getActionIdList(idPrefix: String): List<String> { override fun getActionIdList(idPrefix: String): List<String> {
return ActionManager.getInstance().getActionIdList(idPrefix) return ActionManager.getInstance().getActionIdList(idPrefix)
} }
companion object {
private val LOG = logger<IjActionExecutor>()
}
} }

View File

@ -18,6 +18,11 @@ internal open class IjEditorExecutionContext(override val context: DataContext)
// This key is stored in data context when the action is started from vim // This key is stored in data context when the action is started from vim
internal val runFromVimKey = Key.create<Boolean>("RunFromVim") internal val runFromVimKey = Key.create<Boolean>("RunFromVim")
/**
* Sometimes we can't rely on [runFromVimKey] because the data context is created after the execution
*/
internal var runningIJAction: Boolean = false
/** /**
* Check if the action with this data context was started from Vim * Check if the action with this data context was started from Vim
*/ */

View File

@ -16,7 +16,6 @@ import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestWithoutNeovim import org.jetbrains.plugins.ideavim.TestWithoutNeovim
import org.jetbrains.plugins.ideavim.VimBehaviorDiffers import org.jetbrains.plugins.ideavim.VimBehaviorDiffers
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
/** /**
@ -51,7 +50,6 @@ class MotionActionTest : VimTestCase() {
} }
@Test @Test
@Disabled("VIM-3376")
fun testEscapeInCommand() { fun testEscapeInCommand() {
val content = """ val content = """
on${c}e two on${c}e two

View File

@ -13,7 +13,6 @@ import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestWithoutNeovim import org.jetbrains.plugins.ideavim.TestWithoutNeovim
import org.jetbrains.plugins.ideavim.VimBehaviorDiffers import org.jetbrains.plugins.ideavim.VimBehaviorDiffers
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
/** /**
@ -74,7 +73,6 @@ class SelectKeyHandlerTest : VimTestCase() {
@TestWithoutNeovim(SkipNeovimReason.SELECT_MODE) @TestWithoutNeovim(SkipNeovimReason.SELECT_MODE)
@Test @Test
@Disabled("VIM-3376")
fun `test char mode backspace`() { fun `test char mode backspace`() {
this.doTest( this.doTest(
listOf("gh", "<BS>"), listOf("gh", "<BS>"),
@ -100,7 +98,6 @@ class SelectKeyHandlerTest : VimTestCase() {
@TestWithoutNeovim(SkipNeovimReason.SELECT_MODE) @TestWithoutNeovim(SkipNeovimReason.SELECT_MODE)
@Test @Test
@Disabled("VIM-3376")
fun `test char mode delete`() { fun `test char mode delete`() {
this.doTest( this.doTest(
listOf("gh", "<DEL>"), listOf("gh", "<DEL>"),