1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2024-11-24 22:42:53 +01:00

Compare commits

..

13 Commits

6 changed files with 113 additions and 126 deletions

View File

@ -14,7 +14,7 @@ ideaVersion=2024.1
ideaType=IC
downloadIdeaSources=true
instrumentPluginCode=true
version=chylex-33
version=chylex-34
javaVersion=17
remoteRobotVersion=0.11.22
antlrVersion=4.10.1

View File

@ -8,21 +8,25 @@
package com.maddyhome.idea.vim.extension.nerdtree
import com.intellij.ide.projectView.ProjectView
import com.intellij.ide.projectView.impl.AbstractProjectViewPane
import com.intellij.ide.projectView.impl.ProjectViewImpl
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.PlatformCoreDataKeys
import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.ui.getUserData
import com.intellij.openapi.ui.putUserData
import com.intellij.openapi.util.Key
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.startup.ProjectActivity
import com.intellij.openapi.wm.ToolWindow
import com.intellij.openapi.wm.ToolWindowId
import com.intellij.openapi.wm.ex.ToolWindowManagerEx
import com.intellij.openapi.wm.ex.ToolWindowManagerListener
import com.intellij.ui.KeyStrokeAdapter
import com.intellij.ui.TreeExpandCollapse
import com.intellij.ui.speedSearch.SpeedSearchSupply
@ -49,8 +53,6 @@ import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
import java.awt.event.KeyEvent
import javax.swing.JComponent
import javax.swing.JTree
import javax.swing.KeyStroke
import javax.swing.SwingConstants
@ -130,6 +132,7 @@ internal class NerdTree : VimExtension {
synchronized(Util.monitor) {
Util.commandsRegistered = true
ProjectManager.getInstance().openProjects.forEach { project -> installDispatcher(project) }
}
}
@ -161,8 +164,39 @@ internal class NerdTree : VimExtension {
}
}
class ProjectViewListener(private val project: Project) : ToolWindowManagerListener {
override fun toolWindowShown(toolWindow: ToolWindow) {
if (ToolWindowId.PROJECT_VIEW != toolWindow.id) return
val dispatcher = NerdDispatcher.getInstance(project)
if (dispatcher.speedSearchListenerInstalled) return
// I specify nullability explicitly as we've got a lot of exceptions saying this property is null
val currentProjectViewPane: AbstractProjectViewPane? = ProjectView.getInstance(project).currentProjectViewPane
val tree = currentProjectViewPane?.tree ?: return
val supply = SpeedSearchSupply.getSupply(tree, true) ?: return
// NB: Here might be some issues with concurrency, but it's not really bad, I think
dispatcher.speedSearchListenerInstalled = true
supply.addChangeListener {
dispatcher.waitForSearch = false
}
}
}
// TODO I'm not sure is this activity runs at all? Should we use [RunOnceUtil] instead?
class NerdStartupActivity : ProjectActivity {
override suspend fun execute(project: Project) {
synchronized(Util.monitor) {
if (!Util.commandsRegistered) return
installDispatcher(project)
}
}
}
class NerdDispatcher : DumbAwareAction() {
internal var waitForSearch = false
internal var speedSearchListenerInstalled = false
override fun actionPerformed(e: AnActionEvent) {
var keyStroke = getKeyStroke(e) ?: return
@ -210,6 +244,10 @@ internal class NerdTree : VimExtension {
}
companion object {
fun getInstance(project: Project): NerdDispatcher {
return project.getService(NerdDispatcher::class.java)
}
private const val ESCAPE_KEY_CODE = 27
}
@ -245,14 +283,19 @@ internal class NerdTree : VimExtension {
registerCommand(
"NERDTreeMapActivateNode",
"o",
NerdAction.Code { _, dataContext, e ->
val tree = getTree(e) ?: return@Code
NerdAction.Code { project, dataContext, _ ->
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
val row = tree.selectionRows?.getOrNull(0) ?: return@Code
if (tree.isExpanded(row)) {
tree.collapseRow(row)
val array = CommonDataKeys.NAVIGATABLE_ARRAY.getData(dataContext)?.filter { it.canNavigateToSource() }
if (array.isNullOrEmpty()) {
val row = tree.selectionRows?.getOrNull(0) ?: return@Code
if (tree.isExpanded(row)) {
tree.collapseRow(row)
} else {
tree.expandRow(row)
}
} else {
tree.expandRow(row)
array.forEach { it.navigate(true) }
}
},
)
@ -331,8 +374,8 @@ internal class NerdTree : VimExtension {
registerCommand(
"NERDTreeMapOpenRecursively",
"O",
NerdAction.Code { _, _, e ->
val tree = getTree(e) ?: return@Code
NerdAction.Code { project, _, _ ->
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
TreeExpandCollapse.expandAll(tree)
tree.selectionPath?.let {
TreeUtil.scrollToVisible(tree, it, false)
@ -342,8 +385,8 @@ internal class NerdTree : VimExtension {
registerCommand(
"NERDTreeMapCloseChildren",
"X",
NerdAction.Code { _, _, e ->
val tree = getTree(e) ?: return@Code
NerdAction.Code { project, _, _ ->
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
TreeExpandCollapse.collapse(tree)
tree.selectionPath?.let {
TreeUtil.scrollToVisible(tree, it, false)
@ -353,8 +396,8 @@ internal class NerdTree : VimExtension {
registerCommand(
"NERDTreeMapCloseDir",
"x",
NerdAction.Code { _, _, e ->
val tree = getTree(e) ?: return@Code
NerdAction.Code { project, _, _ ->
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
val currentPath = tree.selectionPath ?: return@Code
if (tree.isExpanded(currentPath)) {
tree.collapsePath(currentPath)
@ -372,8 +415,8 @@ internal class NerdTree : VimExtension {
registerCommand(
"NERDTreeMapJumpParent",
"p",
NerdAction.Code { _, _, e ->
val tree = getTree(e) ?: return@Code
NerdAction.Code { project, _, _ ->
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
val currentPath = tree.selectionPath ?: return@Code
val parentPath = currentPath.parentPath ?: return@Code
if (parentPath.parentPath != null) {
@ -386,8 +429,8 @@ internal class NerdTree : VimExtension {
registerCommand(
"NERDTreeMapJumpFirstChild",
"K",
NerdAction.Code { _, _, e ->
val tree = getTree(e) ?: return@Code
NerdAction.Code { project, _, _ ->
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
val currentPath = tree.selectionPath ?: return@Code
val parent = currentPath.parentPath ?: return@Code
val row = tree.getRowForPath(parent)
@ -399,8 +442,8 @@ internal class NerdTree : VimExtension {
registerCommand(
"NERDTreeMapJumpLastChild",
"J",
NerdAction.Code { _, _, e ->
val tree = getTree(e) ?: return@Code
NerdAction.Code { project, _, _ ->
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
val currentPath = tree.selectionPath ?: return@Code
val currentPathCount = currentPath.pathCount
@ -421,12 +464,12 @@ internal class NerdTree : VimExtension {
)
registerCommand(
"NERDTreeMapJumpNextSibling",
"<A-J>",
"<C-J>",
NerdAction.ToIj("Tree-selectNextSibling"),
)
registerCommand(
"NERDTreeMapJumpPrevSibling",
"<A-K>",
"<C-K>",
NerdAction.ToIj("Tree-selectPreviousSibling"),
)
registerCommand(
@ -445,26 +488,20 @@ internal class NerdTree : VimExtension {
registerCommand(
"/",
NerdAction.Code { _, _, e ->
val tree = getTree(e) ?: return@Code
tree.getUserData(KEY)?.waitForSearch = true
NerdAction.Code { project, _, _ ->
NerdDispatcher.getInstance(project).waitForSearch = true
},
)
registerCommand(
"<ESC>",
NerdAction.Code { _, _, e ->
val tree = getTree(e) ?: return@Code
tree.getUserData(KEY)?.waitForSearch = false
NerdAction.Code { project, _, _ ->
val instance = NerdDispatcher.getInstance(project)
if (instance.waitForSearch) {
instance.waitForSearch = false
}
},
)
for (c in ('a'..'z') + ('A'..'Z')) {
val ks = KeyStroke.getKeyStroke(c)
if (ks !in actionsRoot) {
registerCommand(c.toString(), NerdAction.Code { _, _, _ -> })
}
}
}
object Util {
@ -489,21 +526,6 @@ internal class NerdTree : VimExtension {
companion object {
const val pluginName = "NERDTree"
private val LOG = logger<NerdTree>()
private val KEY = Key.create<NerdDispatcher>("IdeaVim-NerdTree-Dispatcher")
fun installDispatcher(component: JComponent) {
if (component.getUserData(KEY) != null) return
val dispatcher = NerdDispatcher()
component.putUserData(KEY, dispatcher)
val shortcuts = collectShortcuts(actionsRoot).map { RequiredShortcut(it, MappingOwner.Plugin.get(pluginName)) }
dispatcher.registerCustomShortcutSet(KeyGroup.toShortcutSet(shortcuts), component)
SpeedSearchSupply.getSupply(component, true)?.addChangeListener {
dispatcher.waitForSearch = false
}
}
}
}
@ -538,6 +560,12 @@ private fun collectShortcuts(node: Node<NerdAction>): Set<KeyStroke> {
}
}
private fun getTree(e: AnActionEvent): JTree? {
return e.dataContext.getData(PlatformCoreDataKeys.CONTEXT_COMPONENT) as? JTree
private fun installDispatcher(project: Project) {
val dispatcher = NerdTree.NerdDispatcher.getInstance(project)
val shortcuts =
collectShortcuts(actionsRoot).map { RequiredShortcut(it, MappingOwner.Plugin.get(NerdTree.pluginName)) }
dispatcher.registerCustomShortcutSet(
KeyGroup.toShortcutSet(shortcuts),
(ProjectView.getInstance(project) as ProjectViewImpl).component,
)
}

View File

@ -1,25 +0,0 @@
package com.maddyhome.idea.vim.extension.nerdtree
import com.intellij.ide.ApplicationInitializedListener
import com.intellij.openapi.application.ApplicationManager
import com.intellij.util.ui.StartupUiUtil
import kotlinx.coroutines.CoroutineScope
import java.awt.AWTEvent
import java.awt.event.FocusEvent
import javax.swing.JTree
@Suppress("UnstableApiUsage")
internal class NerdTreeApplicationListener : ApplicationInitializedListener {
override suspend fun execute(asyncScope: CoroutineScope) {
StartupUiUtil.addAwtListener(::handleEvent, AWTEvent.FOCUS_EVENT_MASK, ApplicationManager.getApplication().getService(NerdTreeDisposableService::class.java))
}
private fun handleEvent(event: AWTEvent) {
if (event is FocusEvent && event.id == FocusEvent.FOCUS_GAINED) {
val source = event.source
if (source is JTree) {
NerdTree.installDispatcher(source)
}
}
}
}

View File

@ -1,9 +0,0 @@
package com.maddyhome.idea.vim.extension.nerdtree
import com.intellij.openapi.Disposable
import com.intellij.openapi.components.Service
@Service
internal class NerdTreeDisposableService : Disposable {
override fun dispose() {}
}

View File

@ -61,18 +61,15 @@ internal class UndoRedoHelper : UndoRedoBase() {
undoManager: UndoManager,
fileEditor: TextEditor,
editor: VimEditor,
) {if (injector.globalIjOptions().oldundo) {
SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) }
) {
if (injector.globalIjOptions().oldundo) {
SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) }
restoreVisualMode(editor)
} else {
// TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
editor.runWithChangeTracking {
undoManager.undo(fileEditor)
restoreVisualMode(editor)
} else {
// TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
editor.runWithChangeTracking {
undoManager.undo(fileEditor)
// We execute undo one more time if the previous one just restored selection
if (!hasChanges && hasSelection(editor) && undoManager.isUndoAvailable(fileEditor)) {
undoManager.undo(fileEditor)
}
}
CommandProcessor.getInstance().runUndoTransparentAction {
@ -91,15 +88,7 @@ internal class UndoRedoHelper : UndoRedoBase() {
// TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
editor.runWithChangeTracking {
undoManager.undo(fileEditor)
// We execute undo one more time if the previous one just restored selection
if (!hasChanges && hasSelection(editor) && undoManager.isUndoAvailable(fileEditor)) {
undoManager.undo(fileEditor)
}
}
CommandProcessor.getInstance().runUndoTransparentAction {
removeSelections(editor)
restoreVisualMode(editor)
}
} else {
runWithBooleanRegistryOption("ide.undo.transparent.caret.movement", true) {
@ -138,17 +127,17 @@ internal class UndoRedoHelper : UndoRedoBase() {
undoManager: UndoManager,
fileEditor: TextEditor,
editor: VimEditor,
) {if (injector.globalIjOptions().oldundo) {
SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) }
restoreVisualMode(editor)
} else {
) {
if (injector.globalIjOptions().oldundo) {
SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) }
} else {
undoManager.redo(fileEditor)
CommandProcessor.getInstance().runUndoTransparentAction {
editor.carets().forEach { it.ij.removeSelection() }
}
// TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
editor.runWithChangeTracking {
undoManager.redo(fileEditor)
CommandProcessor.getInstance().runUndoTransparentAction {
editor.carets().forEach { it.ij.removeSelection() }
}
// TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
editor.runWithChangeTracking {
undoManager.redo(fileEditor)
// We execute undo one more time if the previous one just restored selection
if (!hasChanges && hasSelection(editor) && undoManager.isRedoAvailable(fileEditor)) {

View File

@ -136,7 +136,11 @@
<!-- IdeaVim extensions-->
<extensions defaultExtensionNs="com.intellij">
<applicationService serviceImplementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$NerdDispatcher"/>
<applicationInitializedListener implementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTreeApplicationListener"/>
<projectService serviceImplementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$NerdDispatcher"/>
<postStartupActivity implementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$NerdStartupActivity"/>
</extensions>
<projectListeners>
<listener class="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$ProjectViewListener"
topic="com.intellij.openapi.wm.ex.ToolWindowManagerListener"/>
</projectListeners>
</idea-plugin>