Skip to content

Commit

Permalink
Fix(VIM-3204): Add checker that verifies the configuratin of the keymap
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexPl292 committed Nov 23, 2023
1 parent a1da23d commit 4d75ef2
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 0 deletions.
65 changes: 65 additions & 0 deletions src/main/java/com/maddyhome/idea/vim/group/NotificationService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.KeyboardShortcut
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.ide.CopyPasteManager
import com.intellij.openapi.keymap.KeymapUtil
import com.intellij.openapi.keymap.ex.KeymapManagerEx
import com.intellij.openapi.keymap.impl.ui.KeymapPanel
import com.intellij.openapi.options.ShowSettingsUtil
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.Project
Expand All @@ -32,6 +35,7 @@ import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.handler.KeyMapIssue
import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.key.ShortcutOwner
import com.maddyhome.idea.vim.key.ShortcutOwnerInfo
Expand Down Expand Up @@ -180,6 +184,65 @@ internal class NotificationService(private val project: Project?) {
ActionIdNotifier.notifyActionId(id, project)
}

fun notifyKeymapIssues(issues: ArrayList<KeyMapIssue>) {
val keymapManager = KeymapManagerEx.getInstanceEx()
val keymap = keymapManager.activeKeymap
val message = buildString {
if (issues.size == 1) {
issues.forEach {
appendLine("Current IDE keymap (${keymap.name}) doesn't have ${it.key} key assigned to the ${it.action} action.<br/>")
}
}
else {
appendLine("With current IDE keymap (${keymap.name}):<br/>")
issues.forEach {
appendLine("- ${it.key} key is not assigned to the ${it.action} action.<br/>")
}
}
appendLine("<br/>")
appendLine("This is required for proper plugin work.")
}
val notification = IDEAVIM_STICKY_GROUP.createNotification(
IDEAVIM_NOTIFICATION_TITLE,
message,
NotificationType.ERROR,
)
notification.subtitle = "IDE keymap misconfigured"
notification.addAction(object : DumbAwareAction("Assign Required Shortcuts") {
override fun actionPerformed(e: AnActionEvent) {
issues.forEach {
keymap.addShortcut(it.actionId, KeyboardShortcut(it.keyStroke, null))
}
LOG.info("Set shortcuts for ${issues.map { it.key }}")
notification.expire()
requiredShortcutsAssigned()
}
})
notification.addAction(object : DumbAwareAction("Ignore") {
override fun actionPerformed(e: AnActionEvent) {
LOG.info("Suggestion to set shortcuts ignored for ${issues.map { it.key }}")
notification.hideBalloon()
}
})
notification.notify(project)
}

private fun requiredShortcutsAssigned() {
val notification = Notification(
IDEAVIM_NOTIFICATION_ID,
IDEAVIM_NOTIFICATION_TITLE,
"Required shortcuts assigned",
NotificationType.INFORMATION,
)
notification.addAction(object : DumbAwareAction("Open Keymap Settings") {
override fun actionPerformed(e: AnActionEvent) {
ShowSettingsUtil.getInstance().showSettingsDialog(e.project, KeymapPanel::class.java)
notification.hideBalloon()
}
})
notification.notify(project)
}

object ActionIdNotifier {
private var notification: Notification? = null
private const val NO_ID = "<i>Cannot detect action id</i>"
Expand Down Expand Up @@ -314,6 +377,8 @@ internal class NotificationService(private val project: Project?) {
const val IDEAVIM_NOTIFICATION_TITLE = "IdeaVim"
const val ideajoinExamplesUrl = "https://jb.gg/f9zji9"

private val LOG = logger<NotificationService>()

private fun createIdeaVimRcManually(message: String, project: Project?) {
val notification =
Notification(IDEAVIM_NOTIFICATION_ID, IDEAVIM_NOTIFICATION_TITLE, message, NotificationType.WARNING)
Expand Down
86 changes: 86 additions & 0 deletions src/main/java/com/maddyhome/idea/vim/handler/KeymapChecker.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/

package com.maddyhome.idea.vim.handler

import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.keymap.Keymap
import com.intellij.openapi.keymap.KeymapManagerListener
import com.intellij.openapi.keymap.ex.KeymapManagerEx
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupActivity
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.key
import javax.swing.KeyStroke

/**
* This checker verifies that the keymap has a correct configuration that is required for IdeaVim plugin
*/
internal class KeymapChecker : StartupActivity {
override fun runActivity(project: Project) {
verifyKeymap(project)
}
}

internal class IdeaVimKeymapChangedListener : KeymapManagerListener {
override fun activeKeymapChanged(keymap: Keymap?) {
verifyKeymap(null)
}

override fun shortcutChanged(keymap: Keymap, actionId: String) {
verifyKeymap(null)
}

override fun shortcutChanged(keymap: Keymap, actionId: String, fromSettings: Boolean) {
verifyKeymap(null)
}
}

/**
* After migration to the editor action handlers, we have to make sure that the keymap has a correct configuration.
* For example, that esc key is assigned to esc editor action
*
* Usually this is not a problem because this is a standard mapping, but the problem may appear in a misconfiguration
* like it was in VIM-3204
*/
private fun verifyKeymap(project: Project?) {
val keymapManager = KeymapManagerEx.getInstanceEx()
val keymap = keymapManager.activeKeymap
val keymapShortcutsForEsc = keymap.getShortcuts(IdeActions.ACTION_EDITOR_ESCAPE)
val keymapShortcutsForEnter = keymap.getShortcuts(IdeActions.ACTION_EDITOR_ENTER)

val issues = ArrayList<KeyMapIssue>()
if ("[pressed ESCAPE]" !in keymapShortcutsForEsc.map { it.toString() }) {
issues += KeyMapIssue(
"esc",
"editor escape",
IdeActions.ACTION_EDITOR_ESCAPE,
key("<esc>")
)
}

if ("[pressed ENTER]" !in keymapShortcutsForEnter.map { it.toString() }) {
issues += KeyMapIssue(
"enter",
"editor enter",
IdeActions.ACTION_EDITOR_ENTER,
key("<enter>")
)
}

if (issues.isNotEmpty()) {
VimPlugin.getNotifications(project).notifyKeymapIssues(issues)
}
}

internal class KeyMapIssue(
val key: String,
val action: String,
val actionId: String,
val keyStroke: KeyStroke,
)
2 changes: 2 additions & 0 deletions src/main/resources/META-INF/includes/VimListeners.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
topic="com.intellij.ide.ui.LafManagerListener"/>
<listener class="com.maddyhome.idea.vim.extension.highlightedyank.HighlightColorResetter"
topic="com.intellij.ide.ui.LafManagerListener"/>
<listener class="com.maddyhome.idea.vim.handler.IdeaVimKeymapChangedListener"
topic="com.intellij.openapi.keymap.KeymapManagerListener"/>
</applicationListeners>
<projectListeners>
<listener class="com.maddyhome.idea.vim.ui.ExOutputPanel$LafListener"
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
core platform activities have IDs, so we can't use "before ID". We have to use "first" -->
<postStartupActivity implementation="com.maddyhome.idea.vim.PluginStartup" order="first"/>
<postStartupActivity implementation="com.maddyhome.idea.vim.handler.EditorHandlersChainLogger"/>
<postStartupActivity implementation="com.maddyhome.idea.vim.handler.KeymapChecker"/>

<editorFloatingToolbarProvider implementation="com.maddyhome.idea.vim.ui.ReloadFloatingToolbar"/>

Expand Down

0 comments on commit 4d75ef2

Please sign in to comment.