mirror of
https://github.com/chylex/IntelliJ-Keyboard-Master.git
synced 2025-10-16 17:39:37 +02:00
Compare commits
6 Commits
2d0570de1a
...
main
Author | SHA1 | Date | |
---|---|---|---|
46657f2f7e
|
|||
48b342644b
|
|||
5c7e20fd78
|
|||
ce7ec24d3a
|
|||
9fb1774f6d
|
|||
90372acdfe
|
@@ -6,7 +6,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = "com.chylex.intellij.keyboardmaster"
|
||||
version = "0.6.2"
|
||||
version = "0.6.4"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
@@ -1,19 +0,0 @@
|
||||
package com.chylex.intellij.keyboardmaster.feature.vimNavigation
|
||||
|
||||
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.components.VimListNavigation
|
||||
import com.intellij.ui.UiInterceptors.PersistentUiInterceptor
|
||||
import com.intellij.ui.awt.RelativePoint
|
||||
import com.intellij.ui.popup.AbstractPopup
|
||||
import com.intellij.ui.popup.list.ListPopupImpl
|
||||
|
||||
internal object PopupInterceptor : PersistentUiInterceptor<AbstractPopup>(AbstractPopup::class.java) {
|
||||
override fun shouldIntercept(component: AbstractPopup): Boolean {
|
||||
if (component is ListPopupImpl && VimNavigation.isEnabled) {
|
||||
VimListNavigation.install(component.list, component)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun doIntercept(component: AbstractPopup, owner: RelativePoint?) {}
|
||||
}
|
@@ -6,11 +6,15 @@ import com.chylex.intellij.keyboardmaster.feature.vimNavigation.components.VimTa
|
||||
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.components.VimTreeNavigation
|
||||
import com.intellij.ide.ui.UISettings
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.ui.UiInterceptors
|
||||
import com.intellij.openapi.ui.getUserData
|
||||
import com.intellij.openapi.ui.popup.util.PopupUtil
|
||||
import com.intellij.openapi.ui.putUserData
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.util.ui.StartupUiUtil
|
||||
import java.awt.AWTEvent
|
||||
import java.awt.event.FocusEvent
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JList
|
||||
import javax.swing.JTable
|
||||
import javax.swing.JTree
|
||||
@@ -18,6 +22,8 @@ import javax.swing.KeyStroke
|
||||
import javax.swing.UIManager
|
||||
|
||||
object VimNavigation {
|
||||
private val KEY_INSTALLED = Key.create<Boolean>("KeyboardMaster-VimNavigation-Installed")
|
||||
|
||||
private val isEnabledFlag = AtomicBoolean(false)
|
||||
private var originalPopupBindings: Array<*>? = null
|
||||
|
||||
@@ -28,7 +34,6 @@ object VimNavigation {
|
||||
val disposable = ApplicationManager.getApplication().getService(PluginDisposableService::class.java)
|
||||
|
||||
StartupUiUtil.addAwtListener(AWTEvent.FOCUS_EVENT_MASK, disposable, ::handleEvent)
|
||||
UiInterceptors.registerPersistent(disposable, PopupInterceptor)
|
||||
}
|
||||
|
||||
fun setEnabled(enabled: Boolean) {
|
||||
@@ -66,9 +71,19 @@ object VimNavigation {
|
||||
private fun handleEvent(event: AWTEvent) {
|
||||
if (event is FocusEvent && event.id == FocusEvent.FOCUS_GAINED && isEnabled) {
|
||||
when (val source = event.source) {
|
||||
is JList<*> -> VimListNavigation.install(source)
|
||||
is JTree -> VimTreeNavigation.install(source)
|
||||
is JTable -> VimTableNavigation.install(source)
|
||||
is JList<*> -> installTo(source, VimListNavigation::install)
|
||||
is JTree -> installTo(source, VimTreeNavigation::install)
|
||||
is JTable -> installTo(source, VimTableNavigation::install)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T : JComponent> installTo(component: T, installer: (T) -> Unit) {
|
||||
if (component.getUserData(KEY_INSTALLED) == null) {
|
||||
component.putUserData(KEY_INSTALLED, true)
|
||||
|
||||
if (PopupUtil.getPopupContainerFor(component) == null) {
|
||||
installer(component)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -22,11 +22,7 @@ internal object VimListNavigation {
|
||||
|
||||
private val ROOT_NODE = VimCommonNavigation.commonRootNode<JList<*>>() + Parent(
|
||||
mapOf(
|
||||
KeyStroke.getKeyStroke('g') to Parent(
|
||||
mapOf(
|
||||
KeyStroke.getKeyStroke('g') to IdeaAction("List-selectFirstRow"),
|
||||
)
|
||||
),
|
||||
KeyStroke.getKeyStroke('g') to IdeaAction("List-selectFirstRow"),
|
||||
KeyStroke.getKeyStroke('G') to IdeaAction("List-selectLastRow"),
|
||||
KeyStroke.getKeyStroke('h') to IdeaAction("List-selectPreviousColumn"),
|
||||
KeyStroke.getKeyStroke('H') to IdeaAction("List-selectPreviousColumnExtendSelection"),
|
||||
|
@@ -14,11 +14,7 @@ internal object VimTableNavigation {
|
||||
|
||||
private val ROOT_NODE = VimCommonNavigation.commonRootNode<JTable>() + Parent(
|
||||
mapOf(
|
||||
KeyStroke.getKeyStroke('g') to Parent(
|
||||
mapOf(
|
||||
KeyStroke.getKeyStroke('g') to IdeaAction("Table-selectFirstRow"),
|
||||
)
|
||||
),
|
||||
KeyStroke.getKeyStroke('g') to IdeaAction("Table-selectFirstRow"),
|
||||
KeyStroke.getKeyStroke('G') to IdeaAction("Table-selectLastRow"),
|
||||
KeyStroke.getKeyStroke('h') to IdeaAction("Table-selectPreviousColumn"),
|
||||
KeyStroke.getKeyStroke('H') to IdeaAction("Table-selectPreviousColumnExtendSelection"),
|
||||
|
@@ -13,6 +13,7 @@ import com.intellij.ui.tree.ui.DefaultTreeUI
|
||||
import java.awt.event.KeyEvent
|
||||
import javax.swing.JTree
|
||||
import javax.swing.KeyStroke
|
||||
import javax.swing.tree.TreeModel
|
||||
import javax.swing.tree.TreePath
|
||||
|
||||
internal object VimTreeNavigation {
|
||||
@@ -20,31 +21,36 @@ internal object VimTreeNavigation {
|
||||
|
||||
private val ROOT_NODE = VimCommonNavigation.commonRootNode<JTree>() + Parent(
|
||||
mapOf(
|
||||
KeyStroke.getKeyStroke('a') to Parent(
|
||||
mapOf(
|
||||
KeyStroke.getKeyStroke('o') to ExpandAll,
|
||||
KeyStroke.getKeyStroke('x') to CollapseAll,
|
||||
)
|
||||
),
|
||||
KeyStroke.getKeyStroke('f') to SelectFirstSibling,
|
||||
KeyStroke.getKeyStroke('F') to SelectLastSibling,
|
||||
KeyStroke.getKeyStroke('g') to Parent(
|
||||
mapOf(
|
||||
KeyStroke.getKeyStroke('g') to IdeaAction("Tree-selectFirst"),
|
||||
KeyStroke.getKeyStroke('j') to SelectLastSibling,
|
||||
KeyStroke.getKeyStroke('k') to SelectFirstSibling,
|
||||
KeyStroke.getKeyStroke('o') to ExpandChildrenToNextLevel,
|
||||
KeyStroke.getKeyStroke('j') to IdeaAction("Tree-selectNextSibling"),
|
||||
KeyStroke.getKeyStroke('k') to IdeaAction("Tree-selectPreviousSibling"),
|
||||
)
|
||||
),
|
||||
KeyStroke.getKeyStroke('G') to IdeaAction("Tree-selectLast"),
|
||||
KeyStroke.getKeyStroke('h') to CollapseSelfOrMoveToParentNode,
|
||||
KeyStroke.getKeyStroke('H') to CollapseUntilRootNode,
|
||||
KeyStroke.getKeyStroke('j') to IdeaAction("Tree-selectNext"),
|
||||
KeyStroke.getKeyStroke('j', KeyEvent.ALT_DOWN_MASK) to IdeaAction("Tree-selectNextSibling"),
|
||||
KeyStroke.getKeyStroke('J') to IdeaAction("Tree-selectNextExtendSelection"),
|
||||
KeyStroke.getKeyStroke('k') to IdeaAction("Tree-selectPrevious"),
|
||||
KeyStroke.getKeyStroke('k', KeyEvent.ALT_DOWN_MASK) to IdeaAction("Tree-selectPreviousSibling"),
|
||||
KeyStroke.getKeyStroke('K') to IdeaAction("Tree-selectPreviousExtendSelection"),
|
||||
KeyStroke.getKeyStroke('l') to ExpandSelfOrMoveToFirstChildNode,
|
||||
KeyStroke.getKeyStroke('L') to ExpandUntilFirstLeafNode,
|
||||
KeyStroke.getKeyStroke('o') to ExpandOrCollapseTreeNode,
|
||||
KeyStroke.getKeyStroke('o') to ExpandChildrenToNextLevel,
|
||||
KeyStroke.getKeyStroke('O') to IdeaAction("FullyExpandTreeNode"),
|
||||
KeyStroke.getKeyStroke('p') to IdeaAction("Tree-selectParentNoCollapse"),
|
||||
KeyStroke.getKeyStroke('P') to IdeaAction("Tree-selectFirst"),
|
||||
KeyStroke.getKeyStroke('x') to CollapseSelfOrParentNode,
|
||||
KeyStroke.getKeyStroke('X') to CollapseAll,
|
||||
KeyStroke.getKeyStroke('x') to CollapseChildrenToPreviousLevel,
|
||||
KeyStroke.getKeyStroke('X') to CollapseSelf,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -54,20 +60,138 @@ internal object VimTreeNavigation {
|
||||
}
|
||||
}
|
||||
|
||||
private data object ExpandOrCollapseTreeNode : ActionNode<VimNavigationDispatcher<JTree>> {
|
||||
private data object CollapseSelf : ActionNode<VimNavigationDispatcher<JTree>> {
|
||||
override fun performAction(holder: VimNavigationDispatcher<JTree>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
|
||||
val tree = holder.component
|
||||
val path = tree.selectionPath ?: return
|
||||
|
||||
collapseAndScroll(tree, path)
|
||||
}
|
||||
}
|
||||
|
||||
private data object CollapseSelfOrMoveToParentNode : ActionNode<VimNavigationDispatcher<JTree>> {
|
||||
override fun performAction(holder: VimNavigationDispatcher<JTree>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
|
||||
val tree = holder.component
|
||||
val path = tree.selectionPath ?: return
|
||||
|
||||
if (tree.isExpanded(path)) {
|
||||
tree.collapsePath(path)
|
||||
collapseAndScroll(tree, path)
|
||||
}
|
||||
else {
|
||||
runWithoutAutoExpand(tree) { tree.expandPath(path) }
|
||||
withParentPath(tree, path) { selectRow(tree, it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data object CollapseUntilRootNode : ActionNode<VimNavigationDispatcher<JTree>> {
|
||||
override fun performAction(holder: VimNavigationDispatcher<JTree>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
|
||||
val tree = holder.component
|
||||
val path = tree.selectionPath ?: return
|
||||
|
||||
var parentPath = path
|
||||
|
||||
while (true) {
|
||||
parentPath = parentPath.parentPath.takeUnless { isInvisibleRoot(tree, it) } ?: break
|
||||
}
|
||||
|
||||
collapseAndScroll(tree, parentPath)
|
||||
}
|
||||
}
|
||||
|
||||
private data object CollapseAll : ActionNode<VimNavigationDispatcher<JTree>> {
|
||||
override fun performAction(holder: VimNavigationDispatcher<JTree>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
|
||||
val tree = holder.component
|
||||
|
||||
var row = 0
|
||||
|
||||
while (row < tree.rowCount) {
|
||||
tree.collapseRow(row)
|
||||
row++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data object CollapseChildrenToPreviousLevel : ActionNode<VimNavigationDispatcher<JTree>> {
|
||||
override fun performAction(holder: VimNavigationDispatcher<JTree>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
|
||||
val tree = holder.component
|
||||
val model = tree.model
|
||||
val path = tree.selectionPath?.takeIf(tree::isExpanded) ?: return
|
||||
|
||||
var currentLevel = mutableListOf(path)
|
||||
|
||||
while (true) {
|
||||
val nextLevel = mutableListOf<TreePath>()
|
||||
|
||||
for (parentPath in currentLevel) {
|
||||
forEachChild(model, parentPath) {
|
||||
val childPath = parentPath.pathByAddingChild(it)
|
||||
if (tree.isExpanded(childPath)) {
|
||||
nextLevel.add(childPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nextLevel.isNotEmpty()) {
|
||||
currentLevel = nextLevel
|
||||
}
|
||||
else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for (parentPath in currentLevel) {
|
||||
tree.collapsePath(parentPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data object ExpandAll : ActionNode<VimNavigationDispatcher<JTree>> {
|
||||
override fun performAction(holder: VimNavigationDispatcher<JTree>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
|
||||
val tree = holder.component
|
||||
|
||||
var row = 0
|
||||
|
||||
while (row < tree.rowCount) {
|
||||
tree.expandRow(row)
|
||||
row++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data object ExpandChildrenToNextLevel : ActionNode<VimNavigationDispatcher<JTree>> {
|
||||
override fun performAction(holder: VimNavigationDispatcher<JTree>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
|
||||
val tree = holder.component
|
||||
val model = tree.model
|
||||
val path = tree.selectionPath?.takeUnless { isLeaf(tree, it) } ?: return
|
||||
|
||||
var pathsToExpand = mutableListOf(path)
|
||||
|
||||
do {
|
||||
if (pathsToExpand.any(tree::isCollapsed)) {
|
||||
runWithoutAutoExpand(tree) { pathsToExpand.forEach(tree::expandPath) }
|
||||
|
||||
for (path in pathsToExpand) {
|
||||
forEachChild(model, path) { tree.collapsePath(path.pathByAddingChild(it)) }
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
val nextPathsToExpand = mutableListOf<TreePath>()
|
||||
|
||||
for (parentPath in pathsToExpand) {
|
||||
forEachChild(model, parentPath) {
|
||||
if (!model.isLeaf(it)) {
|
||||
nextPathsToExpand.add(parentPath.pathByAddingChild(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pathsToExpand = nextPathsToExpand
|
||||
} while (pathsToExpand.isNotEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
private data object ExpandSelfOrMoveToFirstChildNode : ActionNode<VimNavigationDispatcher<JTree>> {
|
||||
override fun performAction(holder: VimNavigationDispatcher<JTree>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
|
||||
val tree = holder.component
|
||||
@@ -98,96 +222,6 @@ internal object VimTreeNavigation {
|
||||
}
|
||||
}
|
||||
|
||||
private data object CollapseSelfOrMoveToParentNode : ActionNode<VimNavigationDispatcher<JTree>> {
|
||||
override fun performAction(holder: VimNavigationDispatcher<JTree>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
|
||||
val tree = holder.component
|
||||
val path = tree.selectionPath ?: return
|
||||
|
||||
if (tree.isExpanded(path)) {
|
||||
collapseAndScroll(tree, path)
|
||||
}
|
||||
else {
|
||||
withParentPath(tree, path) { selectRow(tree, it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data object CollapseSelfOrParentNode : ActionNode<VimNavigationDispatcher<JTree>> {
|
||||
override fun performAction(holder: VimNavigationDispatcher<JTree>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
|
||||
val tree = holder.component
|
||||
val path = tree.selectionPath ?: return
|
||||
|
||||
if (tree.isExpanded(path)) {
|
||||
collapseAndScroll(tree, path)
|
||||
}
|
||||
else {
|
||||
withParentPath(tree, path) { collapseAndScroll(tree, it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data object CollapseUntilRootNode : ActionNode<VimNavigationDispatcher<JTree>> {
|
||||
override fun performAction(holder: VimNavigationDispatcher<JTree>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
|
||||
val tree = holder.component
|
||||
val path = tree.selectionPath ?: return
|
||||
|
||||
var parentPath = path
|
||||
|
||||
while (true) {
|
||||
parentPath = parentPath.parentPath.takeUnless { isInvisibleRoot(tree, it) } ?: break
|
||||
}
|
||||
|
||||
collapseAndScroll(tree, parentPath)
|
||||
}
|
||||
}
|
||||
|
||||
private data object CollapseAll : ActionNode<VimNavigationDispatcher<JTree>> {
|
||||
override fun performAction(holder: VimNavigationDispatcher<JTree>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
|
||||
val tree = holder.component
|
||||
|
||||
CollapseUntilRootNode.performAction(holder, actionEvent, keyEvent)
|
||||
|
||||
var row = 0
|
||||
|
||||
while (row < tree.rowCount) {
|
||||
tree.collapseRow(row)
|
||||
row++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data object ExpandChildrenToNextLevel : ActionNode<VimNavigationDispatcher<JTree>> {
|
||||
override fun performAction(holder: VimNavigationDispatcher<JTree>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
|
||||
val tree = holder.component
|
||||
val model = tree.model
|
||||
val path = tree.selectionPath?.takeUnless { isLeaf(tree, it) } ?: return
|
||||
|
||||
var pathsToExpand = mutableListOf(path)
|
||||
|
||||
do {
|
||||
if (pathsToExpand.any(tree::isCollapsed)) {
|
||||
runWithoutAutoExpand(tree) { pathsToExpand.forEach(tree::expandPath) }
|
||||
break
|
||||
}
|
||||
|
||||
val nextPathsToExpand = mutableListOf<TreePath>()
|
||||
|
||||
for (parentPath in pathsToExpand) {
|
||||
val lastPathComponent = parentPath.lastPathComponent
|
||||
|
||||
for (i in 0 until model.getChildCount(lastPathComponent)) {
|
||||
val child = model.getChild(lastPathComponent, i)
|
||||
if (!model.isLeaf(child)) {
|
||||
nextPathsToExpand.add(parentPath.pathByAddingChild(child))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pathsToExpand = nextPathsToExpand
|
||||
} while (pathsToExpand.isNotEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
private data object SelectFirstSibling : ActionNode<VimNavigationDispatcher<JTree>> {
|
||||
override fun performAction(holder: VimNavigationDispatcher<JTree>, actionEvent: AnActionEvent, keyEvent: KeyEvent) {
|
||||
val tree = holder.component
|
||||
@@ -226,6 +260,14 @@ internal object VimTreeNavigation {
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun forEachChild(model: TreeModel, path: TreePath, action: (Any) -> Unit) {
|
||||
val lastPathComponent = path.lastPathComponent
|
||||
|
||||
for (i in 0 until model.getChildCount(lastPathComponent)) {
|
||||
action(model.getChild(lastPathComponent, i))
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun runWithoutAutoExpand(tree: JTree, action: () -> Unit) {
|
||||
val previousAutoExpandValue = ClientProperty.get(tree, DefaultTreeUI.AUTO_EXPAND_ALLOWED)
|
||||
ClientProperty.put(tree, DefaultTreeUI.AUTO_EXPAND_ALLOWED, false)
|
||||
|
Reference in New Issue
Block a user