1
0
mirror of https://github.com/chylex/IntelliJ-Keyboard-Master.git synced 2025-11-29 16:38:22 +01:00

Compare commits

...

4 Commits

7 changed files with 175 additions and 8 deletions

View File

@@ -6,7 +6,7 @@ plugins {
} }
group = "com.chylex.intellij.keyboardmaster" group = "com.chylex.intellij.keyboardmaster"
version = "0.6.4" version = "0.6.5"
repositories { repositories {
mavenCentral() mavenCentral()

View File

@@ -24,14 +24,22 @@ internal interface KeyStrokeNode<T> {
fun getChild(keyEvent: KeyEvent): KeyStrokeNode<T> { fun getChild(keyEvent: KeyEvent): KeyStrokeNode<T> {
val keyStroke = when { val keyStroke = when {
keyEvent.keyChar != KeyEvent.CHAR_UNDEFINED -> KeyStroke.getKeyStroke(keyEvent.keyChar, keyEvent.modifiersEx and KeyEvent.SHIFT_DOWN_MASK.inv()) isCharEvent(keyEvent) -> KeyStroke.getKeyStroke(keyEvent.keyChar, keyEvent.modifiersEx and KeyEvent.SHIFT_DOWN_MASK.inv())
keyEvent.id == KeyEvent.KEY_PRESSED -> KeyStroke.getKeyStroke(keyEvent.keyCode, keyEvent.modifiersEx, false) isCodeEvent(keyEvent) -> KeyStroke.getKeyStroke(keyEvent.keyCode, keyEvent.modifiersEx, false)
else -> return this else -> return this
} }
return keys[keyStroke] ?: this return keys[keyStroke] ?: this
} }
private fun isCharEvent(keyEvent: KeyEvent): Boolean {
return keyEvent.keyChar != KeyEvent.CHAR_UNDEFINED && (keyEvent.modifiersEx and KeyEvent.CTRL_DOWN_MASK) == 0
}
private fun isCodeEvent(keyEvent: KeyEvent): Boolean {
return keyEvent.id == KeyEvent.KEY_PRESSED
}
operator fun plus(other: Parent<T>): Parent<T> { operator fun plus(other: Parent<T>): Parent<T> {
val mergedKeys = HashMap(keys) val mergedKeys = HashMap(keys)

View File

@@ -135,6 +135,10 @@ internal open class VimNavigationDispatcher<T : JComponent>(final override val c
} }
final override fun update(e: AnActionEvent) { final override fun update(e: AnActionEvent) {
if (!isSearching.get() && SpeedSearchSupply.getSupply(component)?.isPopupActive == true) {
isSearching.set(true)
}
e.presentation.isEnabled = !ignoreEventDueToActiveSearch(e) && !ignoreEventDueToActiveEditing(e) e.presentation.isEnabled = !ignoreEventDueToActiveSearch(e) && !ignoreEventDueToActiveEditing(e)
} }
@@ -148,7 +152,7 @@ internal open class VimNavigationDispatcher<T : JComponent>(final override val c
} }
final override fun getActionUpdateThread(): ActionUpdateThread { final override fun getActionUpdateThread(): ActionUpdateThread {
return ActionUpdateThread.BGT return ActionUpdateThread.EDT
} }
private sealed interface WrappedAction { private sealed interface WrappedAction {

View File

@@ -1,5 +1,6 @@
package com.chylex.intellij.keyboardmaster.feature.vimNavigation.components package com.chylex.intellij.keyboardmaster.feature.vimNavigation.components
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.ActionNode import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.ActionNode
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.IdeaAction import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.IdeaAction
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.Parent import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.Parent
@@ -8,10 +9,14 @@ import com.chylex.intellij.keyboardmaster.feature.vimNavigation.VimNavigationDis
import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.wm.ToolWindowId import com.intellij.openapi.wm.ToolWindowId
import com.intellij.openapi.wm.ex.ToolWindowManagerEx import com.intellij.openapi.wm.ex.ToolWindowManagerEx
import com.intellij.ui.ComponentUtil
import com.intellij.ui.speedSearch.SpeedSearchActivator import com.intellij.ui.speedSearch.SpeedSearchActivator
import com.intellij.ui.speedSearch.SpeedSearchSupply import com.intellij.ui.speedSearch.SpeedSearchSupply
import java.awt.Point
import java.awt.event.KeyEvent import java.awt.event.KeyEvent
import javax.swing.JComponent import javax.swing.JComponent
import javax.swing.JScrollPane
import javax.swing.JViewport
import javax.swing.KeyStroke import javax.swing.KeyStroke
internal object VimCommonNavigation { internal object VimCommonNavigation {
@@ -28,6 +33,12 @@ internal object VimCommonNavigation {
) )
) )
inline fun <T> withShiftModifier(keyCode: Int, modifiers: Int, constructor: (Boolean) -> KeyStrokeNode<T>): Array<Pair<KeyStroke, KeyStrokeNode<T>>> {
return arrayOf(
KeyStroke.getKeyStroke(keyCode, modifiers) to constructor(false),
KeyStroke.getKeyStroke(keyCode, modifiers or KeyEvent.SHIFT_DOWN_MASK) to constructor(true),
)
}
private class StartSearch<T : JComponent> : ActionNode<VimNavigationDispatcher<T>> { private class StartSearch<T : JComponent> : ActionNode<VimNavigationDispatcher<T>> {
@Suppress("UnstableApiUsage") @Suppress("UnstableApiUsage")
@@ -63,4 +74,25 @@ internal object VimCommonNavigation {
} }
} }
} }
fun JComponent.findScrollPane(): JScrollPane? {
return ComponentUtil.getParentOfType(JScrollPane::class.java, this)
}
fun JScrollPane.scrollByPages(pages: Float) {
scrollBy(this) { (it.height.toFloat() * pages).toInt() }
}
fun JScrollPane.scrollBy(amount: Int) {
scrollBy(this) { amount }
}
private inline fun scrollBy(scrollPane: JScrollPane, amount: (JViewport) -> Int) {
val viewport = scrollPane.viewport
val position = viewport.viewPosition
val scrollTo = (position.y + amount(viewport)).coerceIn(0, viewport.viewSize.height - viewport.height)
viewport.viewPosition = Point(position.x, scrollTo)
}
} }

View File

@@ -1,8 +1,14 @@
package com.chylex.intellij.keyboardmaster.feature.vimNavigation.components package com.chylex.intellij.keyboardmaster.feature.vimNavigation.components
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.ActionNode
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.IdeaAction import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.IdeaAction
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.Parent import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.Parent
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.VimNavigationDispatcher import com.chylex.intellij.keyboardmaster.feature.vimNavigation.VimNavigationDispatcher
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.components.VimCommonNavigation.findScrollPane
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.components.VimCommonNavigation.scrollBy
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.components.VimCommonNavigation.scrollByPages
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.components.VimCommonNavigation.withShiftModifier
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.ui.getUserData import com.intellij.openapi.ui.getUserData
import com.intellij.openapi.ui.putUserData import com.intellij.openapi.ui.putUserData
@@ -32,6 +38,10 @@ internal object VimListNavigation {
KeyStroke.getKeyStroke('K') to IdeaAction("List-selectPreviousRowExtendSelection"), KeyStroke.getKeyStroke('K') to IdeaAction("List-selectPreviousRowExtendSelection"),
KeyStroke.getKeyStroke('l') to IdeaAction("List-selectNextColumn"), KeyStroke.getKeyStroke('l') to IdeaAction("List-selectNextColumn"),
KeyStroke.getKeyStroke('L') to IdeaAction("List-selectNextColumnExtendSelection"), KeyStroke.getKeyStroke('L') to IdeaAction("List-selectNextColumnExtendSelection"),
*withShiftModifier(KeyEvent.VK_B, KeyEvent.CTRL_DOWN_MASK) { ScrollVerticallyAndSelect(pages = -1.0F, extendSelection = it) },
*withShiftModifier(KeyEvent.VK_D, KeyEvent.CTRL_DOWN_MASK) { ScrollVerticallyAndSelect(pages = +0.5F, extendSelection = it) },
*withShiftModifier(KeyEvent.VK_F, KeyEvent.CTRL_DOWN_MASK) { ScrollVerticallyAndSelect(pages = +1.0F, extendSelection = it) },
*withShiftModifier(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK) { ScrollVerticallyAndSelect(pages = -0.5F, extendSelection = it) },
) )
) )
@@ -55,6 +65,35 @@ internal object VimListNavigation {
} }
} }
private data class ScrollVerticallyAndSelect(private val pages: Float, private val extendSelection: Boolean) : ActionNode<VimNavigationDispatcher<JList<*>>> {
override fun performAction(holder: VimNavigationDispatcher<JList<*>>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
val list = holder.component
val scrollPane = list.findScrollPane() ?: return
scrollPane.scrollByPages(pages)
if (pages < 0F) {
val topItemBounds = list.firstVisibleIndex.let { list.getCellBounds(it, it) } ?: return
scrollPane.scrollBy(topItemBounds.height - 1)
}
val topItemIndex = list.firstVisibleIndex
if (topItemIndex == -1) {
return
}
if (extendSelection) {
val anchor = list.anchorSelectionIndex.takeIf { it in 0 until list.model.size } ?: 0
list.setSelectionInterval(anchor, topItemIndex)
}
else {
list.selectedIndex = topItemIndex
}
list.ensureIndexIsVisible(topItemIndex)
}
}
@Suppress("serial") @Suppress("serial")
private class VimPopupListNavigationDispatcher(component: JList<*>, override val popup: WizardPopup) : VimNavigationDispatcher<JList<*>>(component, POPUP_LIST_ROOT_NODE, popup.parent) { private class VimPopupListNavigationDispatcher(component: JList<*>, override val popup: WizardPopup) : VimNavigationDispatcher<JList<*>>(component, POPUP_LIST_ROOT_NODE, popup.parent) {
init { init {

View File

@@ -1,11 +1,19 @@
package com.chylex.intellij.keyboardmaster.feature.vimNavigation.components package com.chylex.intellij.keyboardmaster.feature.vimNavigation.components
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.ActionNode
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.IdeaAction import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.IdeaAction
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.Parent import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.Parent
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.VimNavigationDispatcher import com.chylex.intellij.keyboardmaster.feature.vimNavigation.VimNavigationDispatcher
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.components.VimCommonNavigation.findScrollPane
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.components.VimCommonNavigation.scrollBy
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.components.VimCommonNavigation.scrollByPages
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.components.VimCommonNavigation.withShiftModifier
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.ui.getUserData import com.intellij.openapi.ui.getUserData
import com.intellij.openapi.ui.putUserData import com.intellij.openapi.ui.putUserData
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import java.awt.Rectangle
import java.awt.event.KeyEvent
import javax.swing.JTable import javax.swing.JTable
import javax.swing.KeyStroke import javax.swing.KeyStroke
@@ -24,6 +32,10 @@ internal object VimTableNavigation {
KeyStroke.getKeyStroke('K') to IdeaAction("Table-selectPreviousRowExtendSelection"), KeyStroke.getKeyStroke('K') to IdeaAction("Table-selectPreviousRowExtendSelection"),
KeyStroke.getKeyStroke('l') to IdeaAction("Table-selectNextColumn"), KeyStroke.getKeyStroke('l') to IdeaAction("Table-selectNextColumn"),
KeyStroke.getKeyStroke('L') to IdeaAction("Table-selectNextColumnExtendSelection"), KeyStroke.getKeyStroke('L') to IdeaAction("Table-selectNextColumnExtendSelection"),
*withShiftModifier(KeyEvent.VK_B, KeyEvent.CTRL_DOWN_MASK) { ScrollVerticallyAndSelect(pages = -1.0F, extendSelection = it) },
*withShiftModifier(KeyEvent.VK_D, KeyEvent.CTRL_DOWN_MASK) { ScrollVerticallyAndSelect(pages = +0.5F, extendSelection = it) },
*withShiftModifier(KeyEvent.VK_F, KeyEvent.CTRL_DOWN_MASK) { ScrollVerticallyAndSelect(pages = +1.0F, extendSelection = it) },
*withShiftModifier(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK) { ScrollVerticallyAndSelect(pages = -0.5F, extendSelection = it) },
) )
) )
@@ -32,4 +44,29 @@ internal object VimTableNavigation {
component.putUserData(KEY, VimNavigationDispatcher(component, ROOT_NODE)) component.putUserData(KEY, VimNavigationDispatcher(component, ROOT_NODE))
} }
} }
private data class ScrollVerticallyAndSelect(private val pages: Float, private val extendSelection: Boolean) : ActionNode<VimNavigationDispatcher<JTable>> {
override fun performAction(holder: VimNavigationDispatcher<JTable>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
val table = holder.component
val scrollPane = table.findScrollPane() ?: return
scrollPane.scrollByPages(pages)
if (pages < 0F) {
scrollPane.scrollBy(table.rowHeight - 1)
}
val visibleRect = table.visibleRect
val rowIndexToSelect = table.rowAtPoint(visibleRect.location)
if (rowIndexToSelect == -1) {
return
}
val rowRect = table.getCellRect(rowIndexToSelect, 0, true)
val adjustedRect = Rectangle(visibleRect.x, rowRect.y, visibleRect.width, visibleRect.height)
table.changeSelection(rowIndexToSelect, table.selectedColumn, false, extendSelection)
table.scrollRectToVisible(adjustedRect)
}
}
} }

View File

@@ -4,6 +4,10 @@ import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.Ac
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.IdeaAction import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.IdeaAction
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.Parent import com.chylex.intellij.keyboardmaster.feature.vimNavigation.KeyStrokeNode.Parent
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.VimNavigationDispatcher import com.chylex.intellij.keyboardmaster.feature.vimNavigation.VimNavigationDispatcher
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.components.VimCommonNavigation.findScrollPane
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.components.VimCommonNavigation.scrollBy
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.components.VimCommonNavigation.scrollByPages
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.components.VimCommonNavigation.withShiftModifier
import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.ui.getUserData import com.intellij.openapi.ui.getUserData
import com.intellij.openapi.ui.putUserData import com.intellij.openapi.ui.putUserData
@@ -27,8 +31,6 @@ internal object VimTreeNavigation {
KeyStroke.getKeyStroke('x') to CollapseAll, KeyStroke.getKeyStroke('x') to CollapseAll,
) )
), ),
KeyStroke.getKeyStroke('f') to SelectFirstSibling,
KeyStroke.getKeyStroke('F') to SelectLastSibling,
KeyStroke.getKeyStroke('g') to Parent( KeyStroke.getKeyStroke('g') to Parent(
mapOf( mapOf(
KeyStroke.getKeyStroke('g') to IdeaAction("Tree-selectFirst"), KeyStroke.getKeyStroke('g') to IdeaAction("Tree-selectFirst"),
@@ -49,8 +51,14 @@ internal object VimTreeNavigation {
KeyStroke.getKeyStroke('O') to IdeaAction("FullyExpandTreeNode"), KeyStroke.getKeyStroke('O') to IdeaAction("FullyExpandTreeNode"),
KeyStroke.getKeyStroke('p') to IdeaAction("Tree-selectParentNoCollapse"), KeyStroke.getKeyStroke('p') to IdeaAction("Tree-selectParentNoCollapse"),
KeyStroke.getKeyStroke('P') to IdeaAction("Tree-selectFirst"), KeyStroke.getKeyStroke('P') to IdeaAction("Tree-selectFirst"),
KeyStroke.getKeyStroke('s') to SelectFirstSibling,
KeyStroke.getKeyStroke('S') to SelectLastSibling,
KeyStroke.getKeyStroke('x') to CollapseChildrenToPreviousLevel, KeyStroke.getKeyStroke('x') to CollapseChildrenToPreviousLevel,
KeyStroke.getKeyStroke('X') to CollapseSelf, KeyStroke.getKeyStroke('X') to CollapseSelf,
*withShiftModifier(KeyEvent.VK_B, KeyEvent.CTRL_DOWN_MASK) { ScrollVerticallyAndSelect(pages = -1.0F, extendSelection = it) },
*withShiftModifier(KeyEvent.VK_D, KeyEvent.CTRL_DOWN_MASK) { ScrollVerticallyAndSelect(pages = +0.5F, extendSelection = it) },
*withShiftModifier(KeyEvent.VK_F, KeyEvent.CTRL_DOWN_MASK) { ScrollVerticallyAndSelect(pages = +1.0F, extendSelection = it) },
*withShiftModifier(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK) { ScrollVerticallyAndSelect(pages = -0.5F, extendSelection = it) },
) )
) )
@@ -60,6 +68,45 @@ internal object VimTreeNavigation {
} }
} }
private data class ScrollVerticallyAndSelect(private val pages: Float, private val extendSelection: Boolean) : ActionNode<VimNavigationDispatcher<JTree>> {
override fun performAction(holder: VimNavigationDispatcher<JTree>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
val tree = holder.component
val scrollPane = tree.findScrollPane() ?: return
scrollPane.scrollByPages(pages)
if (pages < 0F) {
val topPath = pathOnTop(tree) ?: return
val topPathBounds = tree.getPathBounds(topPath) ?: return
scrollPane.scrollBy(topPathBounds.height - 1)
}
val pathToSelect = pathOnTop(tree) ?: return
if (extendSelection) {
val anchor = tree.anchorSelectionPath
val anchorRow = if (anchor == null) -1 else tree.getRowForPath(anchor)
if (anchorRow < 0) {
tree.selectionPath = pathToSelect
}
else {
tree.setSelectionInterval(tree.getRowForPath(pathToSelect), anchorRow)
tree.setAnchorSelectionPath(anchor)
tree.leadSelectionPath = pathToSelect
}
}
else {
tree.selectionPath = pathToSelect
}
tree.scrollRectToVisible(tree.getPathBounds(pathToSelect))
}
private fun pathOnTop(tree: JTree): TreePath? {
return tree.visibleRect.let { tree.getClosestPathForLocation(it.x, it.y) }
}
}
private data object CollapseSelf : ActionNode<VimNavigationDispatcher<JTree>> { private data object CollapseSelf : ActionNode<VimNavigationDispatcher<JTree>> {
override fun performAction(holder: VimNavigationDispatcher<JTree>, actionEvent: AnActionEvent, keyEvent: KeyEvent) { override fun performAction(holder: VimNavigationDispatcher<JTree>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
val tree = holder.component val tree = holder.component
@@ -289,7 +336,7 @@ internal object VimTreeNavigation {
private fun collapseAndScroll(tree: JTree, path: TreePath) { private fun collapseAndScroll(tree: JTree, path: TreePath) {
tree.collapsePath(path) tree.collapsePath(path)
tree.scrollRowToVisible(tree.getRowForPath(path)) tree.scrollPathToVisible(path)
} }
private inline fun withParentPath(tree: JTree, path: TreePath, action: (TreePath) -> Unit) { private inline fun withParentPath(tree: JTree, path: TreePath, action: (TreePath) -> Unit) {