mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2024-11-24 22:42:53 +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
|
ideaType=IC
|
||||||
downloadIdeaSources=true
|
downloadIdeaSources=true
|
||||||
instrumentPluginCode=true
|
instrumentPluginCode=true
|
||||||
version=chylex-1
|
version=chylex-33
|
||||||
javaVersion=17
|
javaVersion=17
|
||||||
remoteRobotVersion=0.11.22
|
remoteRobotVersion=0.11.22
|
||||||
antlrVersion=4.10.1
|
antlrVersion=4.10.1
|
||||||
|
@ -8,25 +8,21 @@
|
|||||||
|
|
||||||
package com.maddyhome.idea.vim.extension.nerdtree
|
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.ActionManager
|
||||||
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||||
|
import com.intellij.openapi.actionSystem.PlatformCoreDataKeys
|
||||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.diagnostic.logger
|
import com.intellij.openapi.diagnostic.logger
|
||||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
||||||
import com.intellij.openapi.project.DumbAwareAction
|
import com.intellij.openapi.project.DumbAwareAction
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.ui.getUserData
|
||||||
import com.intellij.openapi.project.ProjectManager
|
import com.intellij.openapi.ui.putUserData
|
||||||
import com.intellij.openapi.startup.ProjectActivity
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.openapi.wm.ToolWindow
|
|
||||||
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.openapi.wm.ex.ToolWindowManagerListener
|
|
||||||
import com.intellij.ui.KeyStrokeAdapter
|
import com.intellij.ui.KeyStrokeAdapter
|
||||||
import com.intellij.ui.TreeExpandCollapse
|
import com.intellij.ui.TreeExpandCollapse
|
||||||
import com.intellij.ui.speedSearch.SpeedSearchSupply
|
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.newapi.vim
|
||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
|
import javax.swing.JComponent
|
||||||
|
import javax.swing.JTree
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
import javax.swing.SwingConstants
|
import javax.swing.SwingConstants
|
||||||
|
|
||||||
@ -132,7 +130,6 @@ internal class NerdTree : VimExtension {
|
|||||||
|
|
||||||
synchronized(Util.monitor) {
|
synchronized(Util.monitor) {
|
||||||
Util.commandsRegistered = true
|
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() {
|
class NerdDispatcher : DumbAwareAction() {
|
||||||
internal var waitForSearch = false
|
internal var waitForSearch = false
|
||||||
internal var speedSearchListenerInstalled = false
|
|
||||||
|
|
||||||
override fun actionPerformed(e: AnActionEvent) {
|
override fun actionPerformed(e: AnActionEvent) {
|
||||||
var keyStroke = getKeyStroke(e) ?: return
|
var keyStroke = getKeyStroke(e) ?: return
|
||||||
@ -244,10 +210,6 @@ internal class NerdTree : VimExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getInstance(project: Project): NerdDispatcher {
|
|
||||||
return project.getService(NerdDispatcher::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
private const val ESCAPE_KEY_CODE = 27
|
private const val ESCAPE_KEY_CODE = 27
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,19 +245,14 @@ internal class NerdTree : VimExtension {
|
|||||||
registerCommand(
|
registerCommand(
|
||||||
"NERDTreeMapActivateNode",
|
"NERDTreeMapActivateNode",
|
||||||
"o",
|
"o",
|
||||||
NerdAction.Code { project, dataContext, _ ->
|
NerdAction.Code { _, dataContext, e ->
|
||||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
val tree = getTree(e) ?: return@Code
|
||||||
|
|
||||||
val array = CommonDataKeys.NAVIGATABLE_ARRAY.getData(dataContext)?.filter { it.canNavigateToSource() }
|
val row = tree.selectionRows?.getOrNull(0) ?: return@Code
|
||||||
if (array.isNullOrEmpty()) {
|
if (tree.isExpanded(row)) {
|
||||||
val row = tree.selectionRows?.getOrNull(0) ?: return@Code
|
tree.collapseRow(row)
|
||||||
if (tree.isExpanded(row)) {
|
|
||||||
tree.collapseRow(row)
|
|
||||||
} else {
|
|
||||||
tree.expandRow(row)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
array.forEach { it.navigate(true) }
|
tree.expandRow(row)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -374,8 +331,8 @@ internal class NerdTree : VimExtension {
|
|||||||
registerCommand(
|
registerCommand(
|
||||||
"NERDTreeMapOpenRecursively",
|
"NERDTreeMapOpenRecursively",
|
||||||
"O",
|
"O",
|
||||||
NerdAction.Code { project, _, _ ->
|
NerdAction.Code { _, _, e ->
|
||||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
val tree = getTree(e) ?: return@Code
|
||||||
TreeExpandCollapse.expandAll(tree)
|
TreeExpandCollapse.expandAll(tree)
|
||||||
tree.selectionPath?.let {
|
tree.selectionPath?.let {
|
||||||
TreeUtil.scrollToVisible(tree, it, false)
|
TreeUtil.scrollToVisible(tree, it, false)
|
||||||
@ -385,8 +342,8 @@ internal class NerdTree : VimExtension {
|
|||||||
registerCommand(
|
registerCommand(
|
||||||
"NERDTreeMapCloseChildren",
|
"NERDTreeMapCloseChildren",
|
||||||
"X",
|
"X",
|
||||||
NerdAction.Code { project, _, _ ->
|
NerdAction.Code { _, _, e ->
|
||||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
val tree = getTree(e) ?: return@Code
|
||||||
TreeExpandCollapse.collapse(tree)
|
TreeExpandCollapse.collapse(tree)
|
||||||
tree.selectionPath?.let {
|
tree.selectionPath?.let {
|
||||||
TreeUtil.scrollToVisible(tree, it, false)
|
TreeUtil.scrollToVisible(tree, it, false)
|
||||||
@ -396,8 +353,8 @@ internal class NerdTree : VimExtension {
|
|||||||
registerCommand(
|
registerCommand(
|
||||||
"NERDTreeMapCloseDir",
|
"NERDTreeMapCloseDir",
|
||||||
"x",
|
"x",
|
||||||
NerdAction.Code { project, _, _ ->
|
NerdAction.Code { _, _, e ->
|
||||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
val tree = getTree(e) ?: return@Code
|
||||||
val currentPath = tree.selectionPath ?: return@Code
|
val currentPath = tree.selectionPath ?: return@Code
|
||||||
if (tree.isExpanded(currentPath)) {
|
if (tree.isExpanded(currentPath)) {
|
||||||
tree.collapsePath(currentPath)
|
tree.collapsePath(currentPath)
|
||||||
@ -415,8 +372,8 @@ internal class NerdTree : VimExtension {
|
|||||||
registerCommand(
|
registerCommand(
|
||||||
"NERDTreeMapJumpParent",
|
"NERDTreeMapJumpParent",
|
||||||
"p",
|
"p",
|
||||||
NerdAction.Code { project, _, _ ->
|
NerdAction.Code { _, _, e ->
|
||||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
val tree = getTree(e) ?: return@Code
|
||||||
val currentPath = tree.selectionPath ?: return@Code
|
val currentPath = tree.selectionPath ?: return@Code
|
||||||
val parentPath = currentPath.parentPath ?: return@Code
|
val parentPath = currentPath.parentPath ?: return@Code
|
||||||
if (parentPath.parentPath != null) {
|
if (parentPath.parentPath != null) {
|
||||||
@ -429,8 +386,8 @@ internal class NerdTree : VimExtension {
|
|||||||
registerCommand(
|
registerCommand(
|
||||||
"NERDTreeMapJumpFirstChild",
|
"NERDTreeMapJumpFirstChild",
|
||||||
"K",
|
"K",
|
||||||
NerdAction.Code { project, _, _ ->
|
NerdAction.Code { _, _, e ->
|
||||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
val tree = getTree(e) ?: return@Code
|
||||||
val currentPath = tree.selectionPath ?: return@Code
|
val currentPath = tree.selectionPath ?: return@Code
|
||||||
val parent = currentPath.parentPath ?: return@Code
|
val parent = currentPath.parentPath ?: return@Code
|
||||||
val row = tree.getRowForPath(parent)
|
val row = tree.getRowForPath(parent)
|
||||||
@ -442,8 +399,8 @@ internal class NerdTree : VimExtension {
|
|||||||
registerCommand(
|
registerCommand(
|
||||||
"NERDTreeMapJumpLastChild",
|
"NERDTreeMapJumpLastChild",
|
||||||
"J",
|
"J",
|
||||||
NerdAction.Code { project, _, _ ->
|
NerdAction.Code { _, _, e ->
|
||||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
val tree = getTree(e) ?: return@Code
|
||||||
val currentPath = tree.selectionPath ?: return@Code
|
val currentPath = tree.selectionPath ?: return@Code
|
||||||
|
|
||||||
val currentPathCount = currentPath.pathCount
|
val currentPathCount = currentPath.pathCount
|
||||||
@ -464,12 +421,12 @@ internal class NerdTree : VimExtension {
|
|||||||
)
|
)
|
||||||
registerCommand(
|
registerCommand(
|
||||||
"NERDTreeMapJumpNextSibling",
|
"NERDTreeMapJumpNextSibling",
|
||||||
"<C-J>",
|
"<A-J>",
|
||||||
NerdAction.ToIj("Tree-selectNextSibling"),
|
NerdAction.ToIj("Tree-selectNextSibling"),
|
||||||
)
|
)
|
||||||
registerCommand(
|
registerCommand(
|
||||||
"NERDTreeMapJumpPrevSibling",
|
"NERDTreeMapJumpPrevSibling",
|
||||||
"<C-K>",
|
"<A-K>",
|
||||||
NerdAction.ToIj("Tree-selectPreviousSibling"),
|
NerdAction.ToIj("Tree-selectPreviousSibling"),
|
||||||
)
|
)
|
||||||
registerCommand(
|
registerCommand(
|
||||||
@ -488,20 +445,26 @@ internal class NerdTree : VimExtension {
|
|||||||
|
|
||||||
registerCommand(
|
registerCommand(
|
||||||
"/",
|
"/",
|
||||||
NerdAction.Code { project, _, _ ->
|
NerdAction.Code { _, _, e ->
|
||||||
NerdDispatcher.getInstance(project).waitForSearch = true
|
val tree = getTree(e) ?: return@Code
|
||||||
|
tree.getUserData(KEY)?.waitForSearch = true
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
registerCommand(
|
registerCommand(
|
||||||
"<ESC>",
|
"<ESC>",
|
||||||
NerdAction.Code { project, _, _ ->
|
NerdAction.Code { _, _, e ->
|
||||||
val instance = NerdDispatcher.getInstance(project)
|
val tree = getTree(e) ?: return@Code
|
||||||
if (instance.waitForSearch) {
|
tree.getUserData(KEY)?.waitForSearch = false
|
||||||
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 {
|
object Util {
|
||||||
@ -526,6 +489,21 @@ internal class NerdTree : VimExtension {
|
|||||||
companion object {
|
companion object {
|
||||||
const val pluginName = "NERDTree"
|
const val pluginName = "NERDTree"
|
||||||
private val LOG = logger<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) {
|
private fun getTree(e: AnActionEvent): JTree? {
|
||||||
val dispatcher = NerdTree.NerdDispatcher.getInstance(project)
|
return e.dataContext.getData(PlatformCoreDataKeys.CONTEXT_COMPONENT) as? JTree
|
||||||
val shortcuts =
|
|
||||||
collectShortcuts(actionsRoot).map { RequiredShortcut(it, MappingOwner.Plugin.get(NerdTree.pluginName)) }
|
|
||||||
dispatcher.registerCustomShortcutSet(
|
|
||||||
KeyGroup.toShortcutSet(shortcuts),
|
|
||||||
(ProjectView.getInstance(project) as ProjectViewImpl).component,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -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() {}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.maddyhome.idea.vim.extension.surround
|
||||||
|
|
||||||
|
import com.intellij.util.text.CharSequenceSubSequence
|
||||||
|
|
||||||
|
internal data class RepeatedCharSequence(val text: CharSequence, val count: Int) : CharSequence {
|
||||||
|
override val length = text.length * count
|
||||||
|
|
||||||
|
override fun get(index: Int): Char {
|
||||||
|
if (index < 0 || index >= length) throw IndexOutOfBoundsException()
|
||||||
|
return text[index % text.length]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
|
||||||
|
return CharSequenceSubSequence(this, startIndex, endIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return text.repeat(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun of(text: CharSequence, count: Int): CharSequence {
|
||||||
|
return when (count) {
|
||||||
|
0 -> ""
|
||||||
|
1 -> text
|
||||||
|
else -> RepeatedCharSequence(text, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ import com.intellij.openapi.editor.Editor
|
|||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.VimCaret
|
import com.maddyhome.idea.vim.api.VimCaret
|
||||||
|
import com.maddyhome.idea.vim.api.VimChangeGroup
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.endsWithNewLine
|
import com.maddyhome.idea.vim.api.endsWithNewLine
|
||||||
import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
|
import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
|
||||||
@ -35,7 +36,10 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
|
|||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
|
||||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||||
import com.maddyhome.idea.vim.group.findBlockRange
|
import com.maddyhome.idea.vim.group.findBlockRange
|
||||||
|
import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore
|
||||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||||
|
import com.maddyhome.idea.vim.newapi.IjVimCaret
|
||||||
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper
|
import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper
|
||||||
@ -78,7 +82,7 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
putKeyMappingIfMissing(MappingMode.XO, injector.parser.parseKeys("S"), owner, injector.parser.parseKeys("<Plug>VSurround"), true)
|
putKeyMappingIfMissing(MappingMode.XO, injector.parser.parseKeys("S"), owner, injector.parser.parseKeys("<Plug>VSurround"), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator())
|
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator(supportsMultipleCursors = false, count = 1)) // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
private class YSurroundHandler : ExtensionHandler {
|
private class YSurroundHandler : ExtensionHandler {
|
||||||
@ -106,7 +110,7 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
val lastNonWhiteSpaceOffset = getLastNonWhitespaceCharacterOffset(editor.text(), lineStartOffset, lineEndOffset)
|
val lastNonWhiteSpaceOffset = getLastNonWhitespaceCharacterOffset(editor.text(), lineStartOffset, lineEndOffset)
|
||||||
if (lastNonWhiteSpaceOffset != null) {
|
if (lastNonWhiteSpaceOffset != null) {
|
||||||
val range = TextRange(lineStartOffset, lastNonWhiteSpaceOffset + 1)
|
val range = TextRange(lineStartOffset, lastNonWhiteSpaceOffset + 1)
|
||||||
performSurround(pair, range, it)
|
performSurround(pair, range, it, count = operatorArguments.count1)
|
||||||
}
|
}
|
||||||
// it.moveToOffset(lineStartOffset)
|
// it.moveToOffset(lineStartOffset)
|
||||||
}
|
}
|
||||||
@ -126,15 +130,13 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
|
|
||||||
private class VSurroundHandler : ExtensionHandler {
|
private class VSurroundHandler : ExtensionHandler {
|
||||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||||
val selectionStart = editor.ij.caretModel.primaryCaret.selectionStart
|
|
||||||
// NB: Operator ignores SelectionType anyway
|
// NB: Operator ignores SelectionType anyway
|
||||||
if (!Operator().apply(editor, context, editor.mode.selectionType)) {
|
if (!Operator(supportsMultipleCursors = true, count = operatorArguments.count1).apply(editor, context, editor.mode.selectionType)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
runWriteAction {
|
runWriteAction {
|
||||||
// Leave visual mode
|
// Leave visual mode
|
||||||
executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
|
executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
|
||||||
editor.ij.caretModel.moveToOffset(selectionStart)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,6 +157,10 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) {
|
fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) {
|
||||||
|
editor.ij.runWithEveryCaretAndRestore { changeAtCaret(editor, context, charFrom, newSurround) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun changeAtCaret(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) {
|
||||||
// Save old register values for carets
|
// Save old register values for carets
|
||||||
val surroundings = editor.sortedCarets()
|
val surroundings = editor.sortedCarets()
|
||||||
.map {
|
.map {
|
||||||
@ -262,20 +268,41 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Operator : OperatorFunction {
|
private class Operator(private val supportsMultipleCursors: Boolean, private val count: Int) : OperatorFunction {
|
||||||
override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
|
override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
|
||||||
val ijEditor = editor.ij
|
val ijEditor = vimEditor.ij
|
||||||
val c = getChar(ijEditor)
|
val c = getChar(ijEditor)
|
||||||
if (c.code == 0) return true
|
if (c.code == 0) return true
|
||||||
|
|
||||||
val pair = getOrInputPair(c, ijEditor, context.ij) ?: return false
|
val pair = getOrInputPair(c, ijEditor, context.ij) ?: return false
|
||||||
// XXX: Will it work with line-wise or block-wise selections?
|
|
||||||
val range = getSurroundRange(editor.currentCaret()) ?: return false
|
runWriteAction {
|
||||||
performSurround(pair, range, editor.currentCaret(), selectionType == SelectionType.LINE_WISE)
|
val change = VimPlugin.getChange()
|
||||||
// Jump back to start
|
if (supportsMultipleCursors) {
|
||||||
executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
|
ijEditor.runWithEveryCaretAndRestore {
|
||||||
|
applyOnce(ijEditor, change, pair, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
applyOnce(ijEditor, change, pair, count)
|
||||||
|
// Jump back to start
|
||||||
|
executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
|
||||||
|
}
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun applyOnce(editor: Editor, change: VimChangeGroup, pair: Pair<String, String>, count: Int) {
|
||||||
|
// XXX: Will it work with line-wise or block-wise selections?
|
||||||
|
val primaryCaret = editor.caretModel.primaryCaret
|
||||||
|
val range = getSurroundRange(primaryCaret.vim)
|
||||||
|
if (range != null) {
|
||||||
|
val start = RepeatedCharSequence.of(pair.first, count)
|
||||||
|
val end = RepeatedCharSequence.of(pair.second, count)
|
||||||
|
change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, start)
|
||||||
|
change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.endOffset + start.length, end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getSurroundRange(caret: VimCaret): TextRange? {
|
private fun getSurroundRange(caret: VimCaret): TextRange? {
|
||||||
val editor = caret.editor
|
val editor = caret.editor
|
||||||
@ -362,15 +389,15 @@ private fun getChar(editor: Editor): Char {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: VimCaret, tagsOnNewLines: Boolean = false) {
|
private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: VimCaret, count: Int, tagsOnNewLines: Boolean = false) {
|
||||||
runWriteAction {
|
runWriteAction {
|
||||||
val editor = caret.editor
|
val editor = caret.editor
|
||||||
val change = VimPlugin.getChange()
|
val change = VimPlugin.getChange()
|
||||||
val leftSurround = pair.first + if (tagsOnNewLines) "\n" else ""
|
val leftSurround = RepeatedCharSequence.of(pair.first + if (tagsOnNewLines) "\n" else "", count)
|
||||||
|
|
||||||
val isEOF = range.endOffset == editor.text().length
|
val isEOF = range.endOffset == editor.text().length
|
||||||
val hasNewLine = editor.endsWithNewLine()
|
val hasNewLine = editor.endsWithNewLine()
|
||||||
val rightSurround = if (tagsOnNewLines) {
|
val rightSurround = (if (tagsOnNewLines) {
|
||||||
if (isEOF && !hasNewLine) {
|
if (isEOF && !hasNewLine) {
|
||||||
"\n" + pair.second
|
"\n" + pair.second
|
||||||
} else {
|
} else {
|
||||||
@ -378,7 +405,7 @@ private fun performSurround(pair: Pair<String, String>, range: TextRange, caret:
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pair.second
|
pair.second
|
||||||
}
|
}).let { RepeatedCharSequence.of(it, count) }
|
||||||
|
|
||||||
change.insertText(editor, caret, range.startOffset, leftSurround)
|
change.insertText(editor, caret, range.startOffset, leftSurround)
|
||||||
change.insertText(editor, caret, range.endOffset + leftSurround.length, rightSurround)
|
change.insertText(editor, caret, range.endOffset + leftSurround.length, rightSurround)
|
||||||
|
@ -79,7 +79,6 @@ import java.math.BigInteger
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides all the insert/replace related functionality
|
* Provides all the insert/replace related functionality
|
||||||
@ -395,6 +394,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
|||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
range: TextRange,
|
range: TextRange,
|
||||||
) {
|
) {
|
||||||
|
val startPos = editor.offsetToBufferPosition(caret.offset)
|
||||||
val startOffset = editor.getLineStartForOffset(range.startOffset)
|
val startOffset = editor.getLineStartForOffset(range.startOffset)
|
||||||
val endOffset = editor.getLineEndForOffset(range.endOffset)
|
val endOffset = editor.getLineEndForOffset(range.endOffset)
|
||||||
val ijEditor = (editor as IjVimEditor).editor
|
val ijEditor = (editor as IjVimEditor).editor
|
||||||
@ -419,11 +419,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val afterAction = {
|
val afterAction = {
|
||||||
val firstLine = editor.offsetToBufferPosition(
|
caret.moveToOffset(injector.motion.moveCaretToLineStartSkipLeading(editor, startPos.line))
|
||||||
min(startOffset.toDouble(), endOffset.toDouble()).toInt()
|
|
||||||
).line
|
|
||||||
val newOffset = injector.motion.moveCaretToLineStartSkipLeading(editor, firstLine)
|
|
||||||
caret.moveToOffset(newOffset)
|
|
||||||
restoreCursor(editor, caret, (caret as IjVimCaret).caret.logicalPosition.line)
|
restoreCursor(editor, caret, (caret as IjVimCaret).caret.logicalPosition.line)
|
||||||
}
|
}
|
||||||
if (project != null) {
|
if (project != null) {
|
||||||
|
@ -85,7 +85,7 @@ public object IjOptions {
|
|||||||
public val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true))
|
public val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true))
|
||||||
public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true))
|
public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true))
|
||||||
public val exCommandAnnotation: ToggleOption = addOption(ToggleOption("excommandannotation", GLOBAL, "excommandannotation", true, isHidden = true))
|
public val exCommandAnnotation: ToggleOption = addOption(ToggleOption("excommandannotation", GLOBAL, "excommandannotation", true, isHidden = true))
|
||||||
public val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", false, isHidden = true))
|
public val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isHidden = true))
|
||||||
public val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true))
|
public val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true))
|
||||||
public val useNewRegex: ToggleOption = addOption(ToggleOption("usenewregex", GLOBAL, "usenewregex", true, isHidden = true))
|
public val useNewRegex: ToggleOption = addOption(ToggleOption("usenewregex", GLOBAL, "usenewregex", true, isHidden = true))
|
||||||
public val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isHidden = true))
|
public val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isHidden = true))
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
package com.maddyhome.idea.vim.group
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.daemon.ReferenceImporter
|
||||||
|
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.application.ReadAction
|
||||||
|
import com.intellij.openapi.command.WriteCommandAction
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||||
|
import com.intellij.openapi.progress.ProgressIndicator
|
||||||
|
import com.intellij.openapi.progress.ProgressManager
|
||||||
|
import com.intellij.openapi.progress.Task
|
||||||
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiRecursiveElementWalkingVisitor
|
||||||
|
import java.util.function.BooleanSupplier
|
||||||
|
|
||||||
|
internal object MacroAutoImport {
|
||||||
|
fun run(editor: Editor, dataContext: DataContext) {
|
||||||
|
val project = CommonDataKeys.PROJECT.getData(dataContext) ?: return
|
||||||
|
val file = PsiDocumentManager.getInstance(project).getPsiFile(editor.document) ?: return
|
||||||
|
|
||||||
|
if (!FileDocumentManager.getInstance().requestWriting(editor.document, project)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val importers = ReferenceImporter.EP_NAME.extensionList
|
||||||
|
if (importers.isEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressManager.getInstance().run(object : Task.Backgroundable(project, "Auto import", true) {
|
||||||
|
override fun run(indicator: ProgressIndicator) {
|
||||||
|
val fixes = ReadAction.nonBlocking<List<BooleanSupplier>> {
|
||||||
|
val fixes = mutableListOf<BooleanSupplier>()
|
||||||
|
|
||||||
|
file.accept(object : PsiRecursiveElementWalkingVisitor() {
|
||||||
|
override fun visitElement(element: PsiElement) {
|
||||||
|
for (reference in element.references) {
|
||||||
|
if (reference.resolve() != null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for (importer in importers) {
|
||||||
|
importer.computeAutoImportAtOffset(editor, file, element.textRange.startOffset, true)
|
||||||
|
?.let(fixes::add)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.visitElement(element)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return@nonBlocking fixes
|
||||||
|
}.executeSynchronously()
|
||||||
|
|
||||||
|
ApplicationManager.getApplication().invokeAndWait {
|
||||||
|
WriteCommandAction.writeCommandAction(project)
|
||||||
|
.withName("Auto Import")
|
||||||
|
.withGroupId("IdeaVimAutoImportAfterMacro")
|
||||||
|
.shouldRecordActionForActiveDocument(true)
|
||||||
|
.run<RuntimeException> {
|
||||||
|
fixes.forEach { it.asBoolean }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,7 @@ import com.maddyhome.idea.vim.api.injector
|
|||||||
import com.maddyhome.idea.vim.helper.MessageHelper.message
|
import com.maddyhome.idea.vim.helper.MessageHelper.message
|
||||||
import com.maddyhome.idea.vim.macro.VimMacroBase
|
import com.maddyhome.idea.vim.macro.VimMacroBase
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to handle playback of macros
|
* Used to handle playback of macros
|
||||||
@ -93,6 +94,9 @@ internal class MacroGroup : VimMacroBase() {
|
|||||||
} finally {
|
} finally {
|
||||||
keyStack.removeFirst()
|
keyStack.removeFirst()
|
||||||
}
|
}
|
||||||
|
if (!isInternalMacro) {
|
||||||
|
MacroAutoImport.run(editor.ij, context.ij)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInternalMacro) {
|
if (isInternalMacro) {
|
||||||
|
@ -210,8 +210,8 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
|
|||||||
* @param patternOffset The pattern offset, e.g. `/{pattern}/{offset}`
|
* @param patternOffset The pattern offset, e.g. `/{pattern}/{offset}`
|
||||||
* @param direction The direction to search
|
* @param direction The direction to search
|
||||||
*/
|
*/
|
||||||
@TestOnly
|
@Override
|
||||||
public void setLastSearchState(@SuppressWarnings("unused") @NotNull Editor editor, @NotNull String pattern,
|
public void setLastSearchState(@SuppressWarnings("unused") @NotNull VimEditor editor, @NotNull String pattern,
|
||||||
@NotNull String patternOffset, Direction direction) {
|
@NotNull String patternOffset, Direction direction) {
|
||||||
if (globalIjOptions(injector).getUseNewRegex()) {
|
if (globalIjOptions(injector).getUseNewRegex()) {
|
||||||
super.setLastSearchState(pattern, patternOffset, direction);
|
super.setLastSearchState(pattern, patternOffset, direction);
|
||||||
|
@ -323,7 +323,7 @@ public class EditorHelper {
|
|||||||
|
|
||||||
final int offset = y - ((screenHeight - lineHeight) / lineHeight / 2 * lineHeight);
|
final int offset = y - ((screenHeight - lineHeight) / lineHeight / 2 * lineHeight);
|
||||||
@NotNull final VimEditor editor1 = new IjVimEditor(editor);
|
@NotNull final VimEditor editor1 = new IjVimEditor(editor);
|
||||||
final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) - 1;
|
final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) + editor.getSettings().getAdditionalLinesCount();
|
||||||
final int offsetForLastLineAtBottom = getOffsetToScrollVisualLineToBottomOfScreen(editor, lastVisualLine);
|
final int offsetForLastLineAtBottom = getOffsetToScrollVisualLineToBottomOfScreen(editor, lastVisualLine);
|
||||||
|
|
||||||
// For `zz`, we want to use virtual space and move any line, including the last one, to the middle of the screen.
|
// For `zz`, we want to use virtual space and move any line, including the last one, to the middle of the screen.
|
||||||
|
@ -12,6 +12,7 @@ package com.maddyhome.idea.vim.helper
|
|||||||
|
|
||||||
import com.intellij.codeWithMe.ClientId
|
import com.intellij.codeWithMe.ClientId
|
||||||
import com.intellij.openapi.editor.Caret
|
import com.intellij.openapi.editor.Caret
|
||||||
|
import com.intellij.openapi.editor.CaretState
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.editor.ex.util.EditorUtil
|
import com.intellij.openapi.editor.ex.util.EditorUtil
|
||||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
||||||
@ -20,6 +21,8 @@ import com.maddyhome.idea.vim.api.StringListOptionValue
|
|||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.group.IjOptionConstants
|
import com.maddyhome.idea.vim.group.IjOptionConstants
|
||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||||
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
|
import com.maddyhome.idea.vim.state.mode.inBlockSelection
|
||||||
import java.awt.Component
|
import java.awt.Component
|
||||||
import javax.swing.JComponent
|
import javax.swing.JComponent
|
||||||
import javax.swing.JTable
|
import javax.swing.JTable
|
||||||
@ -96,3 +99,41 @@ internal val Caret.vimLine: Int
|
|||||||
*/
|
*/
|
||||||
internal val Editor.vimLine: Int
|
internal val Editor.vimLine: Int
|
||||||
get() = this.caretModel.currentCaret.vimLine
|
get() = this.caretModel.currentCaret.vimLine
|
||||||
|
|
||||||
|
internal inline fun Editor.runWithEveryCaretAndRestore(action: () -> Unit) {
|
||||||
|
val caretModel = this.caretModel
|
||||||
|
val carets = if (this.vim.inBlockSelection) null else caretModel.allCarets
|
||||||
|
if (carets == null || carets.size == 1) {
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var initialDocumentSize = this.document.textLength
|
||||||
|
var documentSizeDifference = 0
|
||||||
|
|
||||||
|
val caretOffsets = carets.map { it.selectionStart to it.selectionEnd }
|
||||||
|
val restoredCarets = mutableListOf<CaretState>()
|
||||||
|
|
||||||
|
caretModel.removeSecondaryCarets()
|
||||||
|
|
||||||
|
for ((selectionStart, selectionEnd) in caretOffsets) {
|
||||||
|
if (selectionStart == selectionEnd) {
|
||||||
|
caretModel.primaryCaret.moveToOffset(selectionStart + documentSizeDifference)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
caretModel.primaryCaret.setSelection(
|
||||||
|
selectionStart + documentSizeDifference,
|
||||||
|
selectionEnd + documentSizeDifference
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
action()
|
||||||
|
restoredCarets.add(caretModel.caretsAndSelections.single())
|
||||||
|
|
||||||
|
val documentLength = this.document.textLength
|
||||||
|
documentSizeDifference += documentLength - initialDocumentSize
|
||||||
|
initialDocumentSize = documentLength
|
||||||
|
}
|
||||||
|
|
||||||
|
caretModel.caretsAndSelections = restoredCarets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -21,6 +21,7 @@ import com.intellij.openapi.actionSystem.PlatformDataKeys
|
|||||||
import com.intellij.openapi.actionSystem.ex.ActionManagerEx
|
import com.intellij.openapi.actionSystem.ex.ActionManagerEx
|
||||||
import com.intellij.openapi.actionSystem.ex.ActionUtil
|
import com.intellij.openapi.actionSystem.ex.ActionUtil
|
||||||
import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
|
import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
|
||||||
|
import com.intellij.openapi.actionSystem.impl.Utils
|
||||||
import com.intellij.openapi.command.CommandProcessor
|
import com.intellij.openapi.command.CommandProcessor
|
||||||
import com.intellij.openapi.command.UndoConfirmationPolicy
|
import com.intellij.openapi.command.UndoConfirmationPolicy
|
||||||
import com.intellij.openapi.components.Service
|
import com.intellij.openapi.components.Service
|
||||||
@ -86,6 +87,7 @@ internal class IjActionExecutor : VimActionExecutor {
|
|||||||
ActionManager.getInstance(),
|
ActionManager.getInstance(),
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
|
Utils.initUpdateSession(event)
|
||||||
// beforeActionPerformedUpdate should be called to update the action. It fixes some rider-specific problems.
|
// beforeActionPerformedUpdate should be called to update the action. It fixes some rider-specific problems.
|
||||||
// because rider use async update method. See VIM-1819.
|
// because rider use async update method. See VIM-1819.
|
||||||
// This method executes inside of lastUpdateAndCheckDumb
|
// This method executes inside of lastUpdateAndCheckDumb
|
||||||
|
@ -56,7 +56,7 @@ internal object ScrollViewHelper {
|
|||||||
// that this needs to be replaced as a more or less dumb line for line rewrite.
|
// that this needs to be replaced as a more or less dumb line for line rewrite.
|
||||||
val topLine = getVisualLineAtTopOfScreen(editor)
|
val topLine = getVisualLineAtTopOfScreen(editor)
|
||||||
val bottomLine = getVisualLineAtBottomOfScreen(editor)
|
val bottomLine = getVisualLineAtBottomOfScreen(editor)
|
||||||
val lastLine = vimEditor.getVisualLineCount() - 1
|
val lastLine = vimEditor.getVisualLineCount() + editor.settings.additionalLinesCount
|
||||||
|
|
||||||
// We need the non-normalised value here, so we can handle cases such as so=999 to keep the current line centred
|
// We need the non-normalised value here, so we can handle cases such as so=999 to keep the current line centred
|
||||||
val scrollOffset = injector.options(vimEditor).scrolloff
|
val scrollOffset = injector.options(vimEditor).scrolloff
|
||||||
|
@ -17,6 +17,7 @@ import com.intellij.openapi.components.Service
|
|||||||
import com.intellij.openapi.fileEditor.TextEditor
|
import com.intellij.openapi.fileEditor.TextEditor
|
||||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
|
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
|
||||||
import com.intellij.openapi.util.registry.Registry
|
import com.intellij.openapi.util.registry.Registry
|
||||||
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
@ -24,6 +25,8 @@ import com.maddyhome.idea.vim.common.ChangesListener
|
|||||||
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
|
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
|
||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
|
import com.maddyhome.idea.vim.state.mode.inVisualMode
|
||||||
import com.maddyhome.idea.vim.undo.UndoRedoBase
|
import com.maddyhome.idea.vim.undo.UndoRedoBase
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,13 +61,13 @@ internal class UndoRedoHelper : UndoRedoBase() {
|
|||||||
undoManager: UndoManager,
|
undoManager: UndoManager,
|
||||||
fileEditor: TextEditor,
|
fileEditor: TextEditor,
|
||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
) {
|
) {if (injector.globalIjOptions().oldundo) {
|
||||||
if (injector.globalIjOptions().oldundo) {
|
SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) }
|
||||||
SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) }
|
restoreVisualMode(editor)
|
||||||
} else {
|
} else {
|
||||||
// TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
|
// TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
|
||||||
editor.runWithChangeTracking {
|
editor.runWithChangeTracking {
|
||||||
undoManager.undo(fileEditor)
|
undoManager.undo(fileEditor)
|
||||||
|
|
||||||
// We execute undo one more time if the previous one just restored selection
|
// We execute undo one more time if the previous one just restored selection
|
||||||
if (!hasChanges && hasSelection(editor) && undoManager.isUndoAvailable(fileEditor)) {
|
if (!hasChanges && hasSelection(editor) && undoManager.isUndoAvailable(fileEditor)) {
|
||||||
@ -135,17 +138,17 @@ internal class UndoRedoHelper : UndoRedoBase() {
|
|||||||
undoManager: UndoManager,
|
undoManager: UndoManager,
|
||||||
fileEditor: TextEditor,
|
fileEditor: TextEditor,
|
||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
) {
|
) {if (injector.globalIjOptions().oldundo) {
|
||||||
if (injector.globalIjOptions().oldundo) {
|
SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) }
|
||||||
SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) }
|
restoreVisualMode(editor)
|
||||||
} else {
|
} 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)
|
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
|
// We execute undo one more time if the previous one just restored selection
|
||||||
if (!hasChanges && hasSelection(editor) && undoManager.isRedoAvailable(fileEditor)) {
|
if (!hasChanges && hasSelection(editor) && undoManager.isRedoAvailable(fileEditor)) {
|
||||||
@ -237,4 +240,21 @@ internal class UndoRedoHelper : UndoRedoBase() {
|
|||||||
val hasChanges: Boolean
|
val hasChanges: Boolean
|
||||||
get() = changeListener.hasChanged || initialPath != editor.getPath()
|
get() = changeListener.hasChanged || initialPath != editor.getPath()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
// visual block mode.
|
||||||
|
val wantedMode = if (detectedMode == SelectionType.BLOCK_WISE)
|
||||||
|
SelectionType.CHARACTER_WISE
|
||||||
|
else
|
||||||
|
detectedMode
|
||||||
|
|
||||||
|
VimPlugin.getVisualMotion().enterVisualMode(editor, wantedMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import com.intellij.openapi.actionSystem.CommonDataKeys
|
|||||||
import com.intellij.openapi.actionSystem.ex.AnActionListener
|
import com.intellij.openapi.actionSystem.ex.AnActionListener
|
||||||
import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
|
import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.editor.impl.ScrollingModelImpl
|
||||||
import com.intellij.openapi.project.DumbAwareToggleAction
|
import com.intellij.openapi.project.DumbAwareToggleAction
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.maddyhome.idea.vim.KeyHandler
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
@ -57,6 +58,7 @@ internal object IdeaSpecifics {
|
|||||||
private val surrounderAction =
|
private val surrounderAction =
|
||||||
"com.intellij.codeInsight.generation.surroundWith.SurroundWithHandler\$InvokeSurrounderAction"
|
"com.intellij.codeInsight.generation.surroundWith.SurroundWithHandler\$InvokeSurrounderAction"
|
||||||
private var editor: Editor? = null
|
private var editor: Editor? = null
|
||||||
|
private var caretOffset = -1
|
||||||
private var completionPrevDocumentLength: Int? = null
|
private var completionPrevDocumentLength: Int? = null
|
||||||
private var completionPrevDocumentOffset: Int? = null
|
private var completionPrevDocumentOffset: Int? = null
|
||||||
override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) {
|
override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) {
|
||||||
@ -65,6 +67,7 @@ internal object IdeaSpecifics {
|
|||||||
val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR)
|
val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR)
|
||||||
if (hostEditor != null) {
|
if (hostEditor != null) {
|
||||||
editor = hostEditor
|
editor = hostEditor
|
||||||
|
caretOffset = hostEditor.caretModel.offset
|
||||||
}
|
}
|
||||||
|
|
||||||
val isVimAction = (action as? AnActionWrapper)?.delegate is VimShortcutKeyAction
|
val isVimAction = (action as? AnActionWrapper)?.delegate is VimShortcutKeyAction
|
||||||
@ -96,43 +99,58 @@ internal object IdeaSpecifics {
|
|||||||
if (VimPlugin.isNotEnabled()) return
|
if (VimPlugin.isNotEnabled()) return
|
||||||
|
|
||||||
val editor = editor
|
val editor = editor
|
||||||
if (editor != null && action is ChooseItemAction && injector.registerGroup.isRecording) {
|
if (editor != null) {
|
||||||
val prevDocumentLength = completionPrevDocumentLength
|
if (action is ChooseItemAction && injector.registerGroup.isRecording) {
|
||||||
val prevDocumentOffset = completionPrevDocumentOffset
|
val prevDocumentLength = completionPrevDocumentLength
|
||||||
|
val prevDocumentOffset = completionPrevDocumentOffset
|
||||||
|
|
||||||
if (prevDocumentLength != null && prevDocumentOffset != null) {
|
if (prevDocumentLength != null && prevDocumentOffset != null) {
|
||||||
val register = VimPlugin.getRegister()
|
val register = VimPlugin.getRegister()
|
||||||
val addedTextLength = editor.document.textLength - prevDocumentLength
|
val addedTextLength = editor.document.textLength - prevDocumentLength
|
||||||
val caretShift = addedTextLength - (editor.caretModel.primaryCaret.offset - prevDocumentOffset)
|
val caretShift = addedTextLength - (editor.caretModel.primaryCaret.offset - prevDocumentOffset)
|
||||||
val leftArrow = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0)
|
val leftArrow = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0)
|
||||||
|
|
||||||
register.recordText(editor.document.getText(TextRange(prevDocumentOffset, prevDocumentOffset + addedTextLength)))
|
register.recordText(editor.document.getText(TextRange(prevDocumentOffset, prevDocumentOffset + addedTextLength)))
|
||||||
repeat(caretShift.coerceAtLeast(0)) {
|
repeat(caretShift.coerceAtLeast(0)) {
|
||||||
register.recordKeyStroke(leftArrow)
|
register.recordKeyStroke(leftArrow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.completionPrevDocumentLength = null
|
||||||
|
this.completionPrevDocumentOffset = null
|
||||||
|
}
|
||||||
|
|
||||||
|
//region Enter insert mode after surround with if
|
||||||
|
if (surrounderAction == action.javaClass.name && surrounderItems.any {
|
||||||
|
action.templatePresentation.text.endsWith(
|
||||||
|
it,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
editor?.let {
|
||||||
|
val commandState = it.vim.vimStateMachine
|
||||||
|
it.vim.mode = Mode.NORMAL()
|
||||||
|
VimPlugin.getChange().insertBeforeCursor(it.vim, event.dataContext.vim)
|
||||||
|
KeyHandler.getInstance().reset(it.vim)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
this.completionPrevDocumentLength = null
|
if (caretOffset != -1 && caretOffset != editor.caretModel.offset) {
|
||||||
this.completionPrevDocumentOffset = null
|
val scrollModel = editor.scrollingModel as ScrollingModelImpl
|
||||||
}
|
if (scrollModel.isScrollingNow) {
|
||||||
|
val v = scrollModel.verticalScrollOffset
|
||||||
//region Enter insert mode after surround with if
|
val h = scrollModel.horizontalScrollOffset
|
||||||
if (surrounderAction == action.javaClass.name && surrounderItems.any {
|
scrollModel.finishAnimation()
|
||||||
action.templatePresentation.text.endsWith(
|
scrollModel.scroll(h, v)
|
||||||
it,
|
scrollModel.finishAnimation()
|
||||||
)
|
}
|
||||||
}
|
injector.scroll.scrollCaretIntoView(editor.vim)
|
||||||
) {
|
|
||||||
editor?.let {
|
|
||||||
val commandState = it.vim.vimStateMachine
|
|
||||||
it.vim.mode = Mode.NORMAL()
|
|
||||||
VimPlugin.getChange().insertBeforeCursor(it.vim, event.dataContext.vim)
|
|
||||||
KeyHandler.getInstance().reset(it.vim)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//endregion
|
|
||||||
|
|
||||||
this.editor = null
|
this.editor = null
|
||||||
|
this.caretOffset = -1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ import com.intellij.openapi.editor.ex.DocumentEx
|
|||||||
import com.intellij.openapi.editor.ex.EditorEventMulticasterEx
|
import com.intellij.openapi.editor.ex.EditorEventMulticasterEx
|
||||||
import com.intellij.openapi.editor.ex.FocusChangeListener
|
import com.intellij.openapi.editor.ex.FocusChangeListener
|
||||||
import com.intellij.openapi.editor.impl.EditorComponentImpl
|
import com.intellij.openapi.editor.impl.EditorComponentImpl
|
||||||
|
import com.intellij.openapi.editor.impl.EditorImpl
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener
|
import com.intellij.openapi.fileEditor.FileEditorManagerListener
|
||||||
@ -45,11 +46,14 @@ import com.intellij.openapi.fileEditor.ex.FileEditorWithProvider
|
|||||||
import com.intellij.openapi.fileEditor.impl.EditorComposite
|
import com.intellij.openapi.fileEditor.impl.EditorComposite
|
||||||
import com.intellij.openapi.fileEditor.impl.EditorWindow
|
import com.intellij.openapi.fileEditor.impl.EditorWindow
|
||||||
import com.intellij.openapi.project.ProjectManager
|
import com.intellij.openapi.project.ProjectManager
|
||||||
|
import com.intellij.openapi.rd.createLifetime
|
||||||
|
import com.intellij.openapi.rd.createNestedDisposable
|
||||||
import com.intellij.openapi.util.Disposer
|
import com.intellij.openapi.util.Disposer
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.openapi.util.removeUserData
|
import com.intellij.openapi.util.removeUserData
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.util.ExceptionUtil
|
import com.intellij.util.ExceptionUtil
|
||||||
|
import com.jetbrains.rd.util.lifetime.Lifetime
|
||||||
import com.maddyhome.idea.vim.EventFacade
|
import com.maddyhome.idea.vim.EventFacade
|
||||||
import com.maddyhome.idea.vim.KeyHandler
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
import com.maddyhome.idea.vim.KeyHandlerStateResetter
|
import com.maddyhome.idea.vim.KeyHandlerStateResetter
|
||||||
@ -101,7 +105,6 @@ import com.maddyhome.idea.vim.ui.widgets.macro.MacroWidgetListener
|
|||||||
import com.maddyhome.idea.vim.ui.widgets.macro.macroWidgetOptionListener
|
import com.maddyhome.idea.vim.ui.widgets.macro.macroWidgetOptionListener
|
||||||
import com.maddyhome.idea.vim.ui.widgets.mode.listeners.ModeWidgetListener
|
import com.maddyhome.idea.vim.ui.widgets.mode.listeners.ModeWidgetListener
|
||||||
import com.maddyhome.idea.vim.ui.widgets.mode.modeWidgetOptionListener
|
import com.maddyhome.idea.vim.ui.widgets.mode.modeWidgetOptionListener
|
||||||
import com.maddyhome.idea.vim.vimDisposable
|
|
||||||
import java.awt.event.MouseAdapter
|
import java.awt.event.MouseAdapter
|
||||||
import java.awt.event.MouseEvent
|
import java.awt.event.MouseEvent
|
||||||
import javax.swing.SwingUtilities
|
import javax.swing.SwingUtilities
|
||||||
@ -264,12 +267,10 @@ internal object VimListenerManager {
|
|||||||
// TODO: If the user changes the 'ideavimsupport' option, existing editors won't be initialised
|
// TODO: If the user changes the 'ideavimsupport' option, existing editors won't be initialised
|
||||||
if (vimDisabled(editor)) return
|
if (vimDisabled(editor)) return
|
||||||
|
|
||||||
// As I understand, there is no need to pass a disposable that also disposes on editor close
|
val pluginLifetime = VimPlugin.getInstance().createLifetime()
|
||||||
// because all editor resources will be garbage collected anyway on editor close
|
val editorLifetime = (editor as EditorImpl).disposable.createLifetime()
|
||||||
// Note that this uses the plugin's main disposable, rather than VimPlugin.onOffDisposable, because we don't need
|
val disposable =
|
||||||
// to - we explicitly call VimListenerManager.removeAll from VimPlugin.turnOffPlugin, and this disposes each
|
Lifetime.intersect(pluginLifetime, editorLifetime).createNestedDisposable("MyLifetimedDisposable")
|
||||||
// editor's disposable individually.
|
|
||||||
val disposable = editor.project?.vimDisposable ?: return
|
|
||||||
|
|
||||||
val listenersDisposable = Disposer.newDisposable(disposable)
|
val listenersDisposable = Disposer.newDisposable(disposable)
|
||||||
editor.putUserData(editorListenersDisposableKey, listenersDisposable)
|
editor.putUserData(editorListenersDisposableKey, listenersDisposable)
|
||||||
|
@ -31,7 +31,7 @@ import com.maddyhome.idea.vim.vimscript.model.functions.handlers.SubmatchFunctio
|
|||||||
import org.jetbrains.annotations.TestOnly
|
import org.jetbrains.annotations.TestOnly
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
public open class IjVimSearchGroup : VimSearchGroupBase() {
|
public abstract class IjVimSearchGroup : VimSearchGroupBase() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// TODO: Investigate migrating these listeners to use the effective value change listener
|
// TODO: Investigate migrating these listeners to use the effective value change listener
|
||||||
@ -173,4 +173,4 @@ public open class IjVimSearchGroup : VimSearchGroupBase() {
|
|||||||
showSearchHighlight = false
|
showSearchHighlight = false
|
||||||
updateSearchHighlights(false)
|
updateSearchHighlights(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,11 +136,7 @@
|
|||||||
|
|
||||||
<!-- IdeaVim extensions-->
|
<!-- IdeaVim extensions-->
|
||||||
<extensions defaultExtensionNs="com.intellij">
|
<extensions defaultExtensionNs="com.intellij">
|
||||||
<projectService serviceImplementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$NerdDispatcher"/>
|
<applicationService serviceImplementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$NerdDispatcher"/>
|
||||||
<postStartupActivity implementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$NerdStartupActivity"/>
|
<applicationInitializedListener implementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTreeApplicationListener"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
<projectListeners>
|
|
||||||
<listener class="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$ProjectViewListener"
|
|
||||||
topic="com.intellij.openapi.wm.ex.ToolWindowManagerListener"/>
|
|
||||||
</projectListeners>
|
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
@ -151,5 +151,6 @@
|
|||||||
</group>
|
</group>
|
||||||
|
|
||||||
<action id="VimFindActionIdAction" class="com.maddyhome.idea.vim.listener.FindActionIdAction"/>
|
<action id="VimFindActionIdAction" class="com.maddyhome.idea.vim.listener.FindActionIdAction"/>
|
||||||
|
<action id="VimJumpToSource" class="com.intellij.diff.actions.impl.OpenInEditorAction" />
|
||||||
</actions>
|
</actions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
@ -13,6 +13,7 @@ package org.jetbrains.plugins.ideavim.action.motion.gn
|
|||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.common.Direction
|
import com.maddyhome.idea.vim.common.Direction
|
||||||
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
||||||
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
|
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
|
||||||
@ -85,7 +86,7 @@ class GnNextTextObjectTest : VimTestCase() {
|
|||||||
|
|
||||||
private fun doTestWithSearch(keys: List<KeyStroke>, before: String, after: String) {
|
private fun doTestWithSearch(keys: List<KeyStroke>, before: String, after: String) {
|
||||||
configureByText(before)
|
configureByText(before)
|
||||||
VimPlugin.getSearch().setLastSearchState(fixture.editor, "test", "", Direction.FORWARDS)
|
VimPlugin.getSearch().setLastSearchState(fixture.editor.vim, "test", "", Direction.FORWARDS)
|
||||||
typeText(keys)
|
typeText(keys)
|
||||||
assertState(after)
|
assertState(after)
|
||||||
assertState(Mode.NORMAL())
|
assertState(Mode.NORMAL())
|
||||||
|
@ -13,6 +13,7 @@ package org.jetbrains.plugins.ideavim.action.motion.gn
|
|||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.common.Direction
|
import com.maddyhome.idea.vim.common.Direction
|
||||||
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
||||||
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
|
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
|
||||||
@ -63,7 +64,7 @@ class GnPreviousTextObjectTest : VimTestCase() {
|
|||||||
|
|
||||||
private fun doTestWithSearch(keys: List<KeyStroke>, before: String, after: String) {
|
private fun doTestWithSearch(keys: List<KeyStroke>, before: String, after: String) {
|
||||||
configureByText(before)
|
configureByText(before)
|
||||||
VimPlugin.getSearch().setLastSearchState(fixture.editor, "test", "", Direction.FORWARDS)
|
VimPlugin.getSearch().setLastSearchState(fixture.editor.vim, "test", "", Direction.FORWARDS)
|
||||||
typeText(keys)
|
typeText(keys)
|
||||||
assertState(after)
|
assertState(after)
|
||||||
assertState(Mode.NORMAL())
|
assertState(Mode.NORMAL())
|
||||||
|
@ -12,6 +12,7 @@ import com.maddyhome.idea.vim.VimPlugin
|
|||||||
import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction
|
import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.common.Direction
|
import com.maddyhome.idea.vim.common.Direction
|
||||||
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
||||||
@ -57,7 +58,7 @@ class VisualSelectNextSearchTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun testWithoutSpaces() {
|
fun testWithoutSpaces() {
|
||||||
configureByText("test<caret>test")
|
configureByText("test<caret>test")
|
||||||
VimPlugin.getSearch().setLastSearchState(fixture.editor, "test", "", Direction.FORWARDS)
|
VimPlugin.getSearch().setLastSearchState(fixture.editor.vim, "test", "", Direction.FORWARDS)
|
||||||
typeText(injector.parser.parseKeys("gn"))
|
typeText(injector.parser.parseKeys("gn"))
|
||||||
assertOffset(7)
|
assertOffset(7)
|
||||||
assertSelection("test")
|
assertSelection("test")
|
||||||
|
@ -12,6 +12,7 @@ import com.maddyhome.idea.vim.VimPlugin
|
|||||||
import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction
|
import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.common.Direction
|
import com.maddyhome.idea.vim.common.Direction
|
||||||
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
||||||
@ -54,7 +55,7 @@ class VisualSelectPreviousSearchTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun testWithoutSpaces() {
|
fun testWithoutSpaces() {
|
||||||
configureByText("tes<caret>ttest")
|
configureByText("tes<caret>ttest")
|
||||||
VimPlugin.getSearch().setLastSearchState(fixture.editor, "test", "", Direction.FORWARDS)
|
VimPlugin.getSearch().setLastSearchState(fixture.editor.vim, "test", "", Direction.FORWARDS)
|
||||||
typeText(injector.parser.parseKeys("gN"))
|
typeText(injector.parser.parseKeys("gN"))
|
||||||
assertOffset(0)
|
assertOffset(0)
|
||||||
assertSelection("test")
|
assertSelection("test")
|
||||||
|
@ -10,6 +10,7 @@ package org.jetbrains.plugins.ideavim.action.motion.search
|
|||||||
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.common.Direction
|
import com.maddyhome.idea.vim.common.Direction
|
||||||
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
||||||
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
|
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
|
||||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||||
@ -167,7 +168,7 @@ class SearchAgainPreviousActionTest : VimTestCase() {
|
|||||||
|
|
||||||
private fun doTestWithSearch(keys: String, before: String, after: String) {
|
private fun doTestWithSearch(keys: String, before: String, after: String) {
|
||||||
doTest(keys, before, after) {
|
doTest(keys, before, after) {
|
||||||
VimPlugin.getSearch().setLastSearchState(it, "all", "", Direction.FORWARDS)
|
VimPlugin.getSearch().setLastSearchState(it.vim, "all", "", Direction.FORWARDS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ import com.maddyhome.idea.vim.command.Command
|
|||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||||
|
|
||||||
@CommandOrMotion(keys = ["<C-R>"], modes = [Mode.NORMAL])
|
@CommandOrMotion(keys = ["U", "<C-R>"], modes = [Mode.NORMAL, Mode.VISUAL])
|
||||||
public class RedoAction : VimActionHandler.SingleExecution() {
|
public class RedoAction : VimActionHandler.SingleExecution() {
|
||||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ import com.maddyhome.idea.vim.command.Command
|
|||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||||
|
|
||||||
@CommandOrMotion(keys = ["u", "<Undo>"], modes = [Mode.NORMAL])
|
@CommandOrMotion(keys = ["u", "<Undo>"], modes = [Mode.NORMAL, Mode.VISUAL])
|
||||||
public class UndoAction : VimActionHandler.SingleExecution() {
|
public class UndoAction : VimActionHandler.SingleExecution() {
|
||||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
package com.maddyhome.idea.vim.action.change.change
|
package com.maddyhome.idea.vim.action.change.change
|
||||||
|
|
||||||
import com.intellij.vim.annotations.CommandOrMotion
|
import com.intellij.vim.annotations.CommandOrMotion
|
||||||
import com.intellij.vim.annotations.Mode
|
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.VimCaret
|
import com.maddyhome.idea.vim.api.VimCaret
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
@ -22,7 +21,7 @@ import com.maddyhome.idea.vim.helper.CharacterHelper
|
|||||||
/**
|
/**
|
||||||
* @author vlan
|
* @author vlan
|
||||||
*/
|
*/
|
||||||
@CommandOrMotion(keys = ["u"], modes = [Mode.VISUAL])
|
@CommandOrMotion(keys = [], modes = [])
|
||||||
public class ChangeCaseLowerVisualAction : VisualOperatorActionHandler.ForEachCaret() {
|
public class ChangeCaseLowerVisualAction : VisualOperatorActionHandler.ForEachCaret() {
|
||||||
override val type: Command.Type = Command.Type.CHANGE
|
override val type: Command.Type = Command.Type.CHANGE
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
package com.maddyhome.idea.vim.action.change.change
|
package com.maddyhome.idea.vim.action.change.change
|
||||||
|
|
||||||
import com.intellij.vim.annotations.CommandOrMotion
|
import com.intellij.vim.annotations.CommandOrMotion
|
||||||
import com.intellij.vim.annotations.Mode
|
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.VimCaret
|
import com.maddyhome.idea.vim.api.VimCaret
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
@ -22,7 +21,7 @@ import com.maddyhome.idea.vim.helper.CharacterHelper
|
|||||||
/**
|
/**
|
||||||
* @author vlan
|
* @author vlan
|
||||||
*/
|
*/
|
||||||
@CommandOrMotion(keys = ["U"], modes = [Mode.VISUAL])
|
@CommandOrMotion(keys = [], modes = [])
|
||||||
public class ChangeCaseUpperVisualAction : VisualOperatorActionHandler.ForEachCaret() {
|
public class ChangeCaseUpperVisualAction : VisualOperatorActionHandler.ForEachCaret() {
|
||||||
override val type: Command.Type = Command.Type.CHANGE
|
override val type: Command.Type = Command.Type.CHANGE
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ public class FilterVisualLinesAction : VimActionHandler.SingleExecution() {
|
|||||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_MOT_LINEWISE)
|
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_MOT_LINEWISE)
|
||||||
|
|
||||||
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
||||||
|
injector.markService.setVisualSelectionMarks(editor)
|
||||||
injector.processGroup.startFilterCommand(editor, context, cmd)
|
injector.processGroup.startFilterCommand(editor, context, cmd)
|
||||||
editor.exitVisualMode()
|
editor.exitVisualMode()
|
||||||
return true
|
return true
|
||||||
|
@ -82,6 +82,13 @@ public sealed class TillCharacterMotion(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
injector.motion.setLastFTCmd(tillCharacterMotionType, argument.character)
|
injector.motion.setLastFTCmd(tillCharacterMotionType, argument.character)
|
||||||
|
|
||||||
|
val offset = if (!finishBeforeCharacter) ""
|
||||||
|
else if (direction == Direction.FORWARDS) "s-1"
|
||||||
|
else "s+1"
|
||||||
|
|
||||||
|
injector.searchGroup.setLastSearchState(editor, argument.character.let { if (it == '.') "\\." else it.toString() }, offset, direction)
|
||||||
|
|
||||||
return res.toMotionOrError()
|
return res.toMotionOrError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ public interface VimChangeGroup {
|
|||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: String): VimCaret
|
public fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: CharSequence): VimCaret
|
||||||
|
|
||||||
public fun insertText(editor: VimEditor, caret: VimCaret, str: String): VimCaret
|
public fun insertText(editor: VimEditor, caret: VimCaret, str: String): VimCaret
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
|
|||||||
* @param caret The caret to start insertion in
|
* @param caret The caret to start insertion in
|
||||||
* @param str The text to insert
|
* @param str The text to insert
|
||||||
*/
|
*/
|
||||||
override fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: String): VimCaret {
|
override fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: CharSequence): VimCaret {
|
||||||
(editor as MutableVimEditor).insertText(offset, str)
|
(editor as MutableVimEditor).insertText(offset, str)
|
||||||
val newCaret = caret.moveToInlayAwareOffset(offset + str.length)
|
val newCaret = caret.moveToInlayAwareOffset(offset + str.length)
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ public interface VimSearchGroup {
|
|||||||
* Last used pattern to perform a substitution.
|
* Last used pattern to perform a substitution.
|
||||||
*/
|
*/
|
||||||
public var lastSubstitutePattern: String?
|
public var lastSubstitutePattern: String?
|
||||||
|
public fun setLastSearchState(editor: VimEditor, pattern: String, patternOffset: String, direction: Direction?)
|
||||||
public fun searchBackward(editor: VimEditor, offset: Int, count: Int): TextRange?
|
public fun searchBackward(editor: VimEditor, offset: Int, count: Int): TextRange?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user