mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2024-11-25 07:42:59 +01:00
Compare commits
15 Commits
12575c3cb9
...
30de641513
Author | SHA1 | Date | |
---|---|---|---|
30de641513 | |||
d1b58c9d39 | |||
45ed79d865 | |||
527eb4cbb3 | |||
e32ac125b2 | |||
4d1d3b697c | |||
3246832528 | |||
6505bfc9aa | |||
0c63890e9d | |||
259958e702 | |||
4916545b53 | |||
b8a9bddfa9 | |||
95688b33c8 | |||
07f44f1c93 | |||
2ce6239ad6 |
@ -14,7 +14,7 @@ ideaVersion=2024.1
|
||||
ideaType=IC
|
||||
downloadIdeaSources=true
|
||||
instrumentPluginCode=true
|
||||
version=chylex-34
|
||||
version=chylex-33
|
||||
javaVersion=17
|
||||
remoteRobotVersion=0.11.22
|
||||
antlrVersion=4.10.1
|
||||
|
@ -8,25 +8,21 @@
|
||||
|
||||
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.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.startup.ProjectActivity
|
||||
import com.intellij.openapi.wm.ToolWindow
|
||||
import com.intellij.openapi.ui.getUserData
|
||||
import com.intellij.openapi.ui.putUserData
|
||||
import com.intellij.openapi.util.Key
|
||||
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
|
||||
@ -53,6 +49,8 @@ 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
|
||||
|
||||
@ -132,7 +130,6 @@ internal class NerdTree : VimExtension {
|
||||
|
||||
synchronized(Util.monitor) {
|
||||
Util.commandsRegistered = true
|
||||
ProjectManager.getInstance().openProjects.forEach { project -> installDispatcher(project) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,39 +161,8 @@ 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
|
||||
@ -244,10 +210,6 @@ internal class NerdTree : VimExtension {
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getInstance(project: Project): NerdDispatcher {
|
||||
return project.getService(NerdDispatcher::class.java)
|
||||
}
|
||||
|
||||
private const val ESCAPE_KEY_CODE = 27
|
||||
}
|
||||
|
||||
@ -283,19 +245,14 @@ internal class NerdTree : VimExtension {
|
||||
registerCommand(
|
||||
"NERDTreeMapActivateNode",
|
||||
"o",
|
||||
NerdAction.Code { project, dataContext, _ ->
|
||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
||||
NerdAction.Code { _, dataContext, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
|
||||
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)
|
||||
}
|
||||
val row = tree.selectionRows?.getOrNull(0) ?: return@Code
|
||||
if (tree.isExpanded(row)) {
|
||||
tree.collapseRow(row)
|
||||
} else {
|
||||
array.forEach { it.navigate(true) }
|
||||
tree.expandRow(row)
|
||||
}
|
||||
},
|
||||
)
|
||||
@ -374,8 +331,8 @@ internal class NerdTree : VimExtension {
|
||||
registerCommand(
|
||||
"NERDTreeMapOpenRecursively",
|
||||
"O",
|
||||
NerdAction.Code { project, _, _ ->
|
||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
||||
NerdAction.Code { _, _, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
TreeExpandCollapse.expandAll(tree)
|
||||
tree.selectionPath?.let {
|
||||
TreeUtil.scrollToVisible(tree, it, false)
|
||||
@ -385,8 +342,8 @@ internal class NerdTree : VimExtension {
|
||||
registerCommand(
|
||||
"NERDTreeMapCloseChildren",
|
||||
"X",
|
||||
NerdAction.Code { project, _, _ ->
|
||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
||||
NerdAction.Code { _, _, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
TreeExpandCollapse.collapse(tree)
|
||||
tree.selectionPath?.let {
|
||||
TreeUtil.scrollToVisible(tree, it, false)
|
||||
@ -396,8 +353,8 @@ internal class NerdTree : VimExtension {
|
||||
registerCommand(
|
||||
"NERDTreeMapCloseDir",
|
||||
"x",
|
||||
NerdAction.Code { project, _, _ ->
|
||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
||||
NerdAction.Code { _, _, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
val currentPath = tree.selectionPath ?: return@Code
|
||||
if (tree.isExpanded(currentPath)) {
|
||||
tree.collapsePath(currentPath)
|
||||
@ -415,8 +372,8 @@ internal class NerdTree : VimExtension {
|
||||
registerCommand(
|
||||
"NERDTreeMapJumpParent",
|
||||
"p",
|
||||
NerdAction.Code { project, _, _ ->
|
||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
||||
NerdAction.Code { _, _, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
val currentPath = tree.selectionPath ?: return@Code
|
||||
val parentPath = currentPath.parentPath ?: return@Code
|
||||
if (parentPath.parentPath != null) {
|
||||
@ -429,8 +386,8 @@ internal class NerdTree : VimExtension {
|
||||
registerCommand(
|
||||
"NERDTreeMapJumpFirstChild",
|
||||
"K",
|
||||
NerdAction.Code { project, _, _ ->
|
||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
||||
NerdAction.Code { _, _, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
val currentPath = tree.selectionPath ?: return@Code
|
||||
val parent = currentPath.parentPath ?: return@Code
|
||||
val row = tree.getRowForPath(parent)
|
||||
@ -442,8 +399,8 @@ internal class NerdTree : VimExtension {
|
||||
registerCommand(
|
||||
"NERDTreeMapJumpLastChild",
|
||||
"J",
|
||||
NerdAction.Code { project, _, _ ->
|
||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
||||
NerdAction.Code { _, _, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
val currentPath = tree.selectionPath ?: return@Code
|
||||
|
||||
val currentPathCount = currentPath.pathCount
|
||||
@ -464,12 +421,12 @@ internal class NerdTree : VimExtension {
|
||||
)
|
||||
registerCommand(
|
||||
"NERDTreeMapJumpNextSibling",
|
||||
"<C-J>",
|
||||
"<A-J>",
|
||||
NerdAction.ToIj("Tree-selectNextSibling"),
|
||||
)
|
||||
registerCommand(
|
||||
"NERDTreeMapJumpPrevSibling",
|
||||
"<C-K>",
|
||||
"<A-K>",
|
||||
NerdAction.ToIj("Tree-selectPreviousSibling"),
|
||||
)
|
||||
registerCommand(
|
||||
@ -488,20 +445,26 @@ internal class NerdTree : VimExtension {
|
||||
|
||||
registerCommand(
|
||||
"/",
|
||||
NerdAction.Code { project, _, _ ->
|
||||
NerdDispatcher.getInstance(project).waitForSearch = true
|
||||
NerdAction.Code { _, _, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
tree.getUserData(KEY)?.waitForSearch = true
|
||||
},
|
||||
)
|
||||
|
||||
registerCommand(
|
||||
"<ESC>",
|
||||
NerdAction.Code { project, _, _ ->
|
||||
val instance = NerdDispatcher.getInstance(project)
|
||||
if (instance.waitForSearch) {
|
||||
instance.waitForSearch = false
|
||||
}
|
||||
NerdAction.Code { _, _, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
tree.getUserData(KEY)?.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 {
|
||||
@ -526,6 +489,21 @@ 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -560,12 +538,6 @@ private fun collectShortcuts(node: Node<NerdAction>): Set<KeyStroke> {
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
)
|
||||
private fun getTree(e: AnActionEvent): JTree? {
|
||||
return e.dataContext.getData(PlatformCoreDataKeys.CONTEXT_COMPONENT) as? JTree
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
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() {}
|
||||
}
|
@ -61,15 +61,18 @@ internal class UndoRedoHelper : UndoRedoBase() {
|
||||
undoManager: UndoManager,
|
||||
fileEditor: TextEditor,
|
||||
editor: VimEditor,
|
||||
) {
|
||||
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)
|
||||
) {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)
|
||||
|
||||
// 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 {
|
||||
@ -88,7 +91,15 @@ 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)
|
||||
restoreVisualMode(editor)
|
||||
|
||||
// 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)
|
||||
}
|
||||
} else {
|
||||
runWithBooleanRegistryOption("ide.undo.transparent.caret.movement", true) {
|
||||
@ -104,7 +115,7 @@ internal class UndoRedoHelper : UndoRedoBase() {
|
||||
private fun hasSelection(editor: VimEditor): Boolean {
|
||||
return editor.primaryCaret().ij.hasSelection()
|
||||
}
|
||||
|
||||
|
||||
override fun redo(editor: VimEditor, context: ExecutionContext): Boolean {
|
||||
val ijContext = context.context as DataContext
|
||||
val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false
|
||||
@ -127,17 +138,17 @@ internal class UndoRedoHelper : UndoRedoBase() {
|
||||
undoManager: UndoManager,
|
||||
fileEditor: TextEditor,
|
||||
editor: VimEditor,
|
||||
) {
|
||||
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 {
|
||||
) {if (injector.globalIjOptions().oldundo) {
|
||||
SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) }
|
||||
restoreVisualMode(editor)
|
||||
} 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)
|
||||
|
||||
// We execute undo one more time if the previous one just restored selection
|
||||
if (!hasChanges && hasSelection(editor) && undoManager.isRedoAvailable(fileEditor)) {
|
||||
@ -233,7 +244,7 @@ internal class UndoRedoHelper : UndoRedoBase() {
|
||||
private fun restoreVisualMode(editor: VimEditor) {
|
||||
if (!editor.inVisualMode && editor.getSelectionModel().hasSelection()) {
|
||||
val detectedMode = VimPlugin.getVisualMotion().autodetectVisualSubmode(editor)
|
||||
|
||||
|
||||
// Visual block selection is restored into multiple carets, so multi-carets that form a block are always
|
||||
// identified as visual block mode, leading to false positives.
|
||||
// Since I use visual block mode much less often than multi-carets, this is a judgment call to never restore
|
||||
@ -242,7 +253,7 @@ internal class UndoRedoHelper : UndoRedoBase() {
|
||||
SelectionType.CHARACTER_WISE
|
||||
else
|
||||
detectedMode
|
||||
|
||||
|
||||
VimPlugin.getVisualMotion().enterVisualMode(editor, wantedMode)
|
||||
}
|
||||
}
|
||||
|
@ -136,11 +136,7 @@
|
||||
|
||||
<!-- IdeaVim extensions-->
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<projectService serviceImplementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$NerdDispatcher"/>
|
||||
<postStartupActivity implementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$NerdStartupActivity"/>
|
||||
<applicationService serviceImplementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$NerdDispatcher"/>
|
||||
<applicationInitializedListener implementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTreeApplicationListener"/>
|
||||
</extensions>
|
||||
<projectListeners>
|
||||
<listener class="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$ProjectViewListener"
|
||||
topic="com.intellij.openapi.wm.ex.ToolWindowManagerListener"/>
|
||||
</projectListeners>
|
||||
</idea-plugin>
|
||||
|
Loading…
Reference in New Issue
Block a user