mirror of
				https://github.com/chylex/IntelliJ-IdeaVim.git
				synced 2025-10-31 02:17:13 +01:00 
			
		
		
		
	Compare commits
	
		
			21 Commits
		
	
	
		
			0f0a73c139
			...
			customized
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0fbe675373 | |||
| 43f30cdf7c | |||
| 44ad2c9780 | |||
| 7c10a19ad3 | |||
| 7478a2bf39 | |||
| 98fad582f2 | |||
| 41250fcf5f | |||
| 5679ac79f6 | |||
| ce9dcb75ac | |||
| 458514697d | |||
| 643e359dd7 | |||
| 2d4ae3c72d | |||
| 2109d55d18 | |||
| 7e4777dba3 | |||
| 3c1698720f | |||
| e517af3a6e | |||
| 46210112fa | |||
| 308589fec5 | |||
| c55e43e2ae | |||
| fbe262826c | |||
| de52305556 | 
							
								
								
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | * text=auto eol=lf | ||||||
| @@ -125,6 +125,7 @@ dependencies { | |||||||
|  |  | ||||||
|     // AceJump is an optional dependency. We use their SessionManager class to check if it's active |     // AceJump is an optional dependency. We use their SessionManager class to check if it's active | ||||||
|     plugin("AceJump", "3.8.19") |     plugin("AceJump", "3.8.19") | ||||||
|  |     plugin("com.intellij.classic.ui", "242.20224.159") | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   moduleSources(project(":vim-engine", "sourcesJarArtifacts")) |   moduleSources(project(":vim-engine", "sourcesJarArtifacts")) | ||||||
| @@ -210,6 +211,8 @@ tasks { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   compileTestKotlin { |   compileTestKotlin { | ||||||
|  |     enabled = false | ||||||
|  |      | ||||||
|     kotlinOptions { |     kotlinOptions { | ||||||
|       jvmTarget = javaVersion |       jvmTarget = javaVersion | ||||||
|       apiVersion = "1.9" |       apiVersion = "1.9" | ||||||
|   | |||||||
| @@ -16,11 +16,11 @@ | |||||||
| # https://data.services.jetbrains.com/products?code=IC | # https://data.services.jetbrains.com/products?code=IC | ||||||
| # Maven releases are here: https://www.jetbrains.com/intellij-repository/releases | # Maven releases are here: https://www.jetbrains.com/intellij-repository/releases | ||||||
| # And snapshots: https://www.jetbrains.com/intellij-repository/snapshots | # And snapshots: https://www.jetbrains.com/intellij-repository/snapshots | ||||||
| ideaVersion=2024.1.1 | ideaVersion=2024.2 | ||||||
| # Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type | # Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type | ||||||
| ideaType=IC | ideaType=IC | ||||||
| instrumentPluginCode=true | instrumentPluginCode=true | ||||||
| version=SNAPSHOT | version=chylex-39 | ||||||
| javaVersion=17 | javaVersion=17 | ||||||
| remoteRobotVersion=0.11.23 | remoteRobotVersion=0.11.23 | ||||||
| antlrVersion=4.10.1 | antlrVersion=4.10.1 | ||||||
| @@ -47,7 +47,6 @@ youtrackToken= | |||||||
|  |  | ||||||
| # Gradle settings | # Gradle settings | ||||||
| org.gradle.jvmargs='-Dfile.encoding=UTF-8' | org.gradle.jvmargs='-Dfile.encoding=UTF-8' | ||||||
| org.gradle.configuration-cache=true |  | ||||||
| org.gradle.caching=true | org.gradle.caching=true | ||||||
|  |  | ||||||
| # Disable warning from gradle-intellij-plugin. Kotlin stdlib is included as compileOnly, so the warning is unnecessary | # Disable warning from gradle-intellij-plugin. Kotlin stdlib is included as compileOnly, so the warning is unnecessary | ||||||
|   | |||||||
| @@ -0,0 +1,52 @@ | |||||||
|  | package com.maddyhome.idea.vim.action | ||||||
|  |  | ||||||
|  | import com.intellij.openapi.actionSystem.ActionUpdateThread | ||||||
|  | import com.intellij.openapi.actionSystem.AnActionEvent | ||||||
|  | import com.intellij.openapi.command.UndoConfirmationPolicy | ||||||
|  | import com.intellij.openapi.command.WriteCommandAction | ||||||
|  | import com.intellij.openapi.fileEditor.TextEditor | ||||||
|  | import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx | ||||||
|  | import com.intellij.openapi.project.DumbAwareAction | ||||||
|  | import com.maddyhome.idea.vim.KeyHandler | ||||||
|  | import com.maddyhome.idea.vim.api.injector | ||||||
|  | import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext | ||||||
|  | import com.maddyhome.idea.vim.newapi.vim | ||||||
|  | import com.maddyhome.idea.vim.state.mode.Mode | ||||||
|  |  | ||||||
|  | class VimRunLastMacroInOpenFiles : DumbAwareAction() { | ||||||
|  |   override fun update(e: AnActionEvent) { | ||||||
|  |     val lastRegister = injector.macro.lastRegister | ||||||
|  |     val isEnabled = lastRegister != 0.toChar() | ||||||
|  |  | ||||||
|  |     e.presentation.isEnabled = isEnabled | ||||||
|  |     e.presentation.text = if (isEnabled) "Run Macro '${lastRegister}' in Open Files" else "Run Last Macro in Open Files" | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   override fun getActionUpdateThread(): ActionUpdateThread { | ||||||
|  |     return ActionUpdateThread.EDT | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   override fun actionPerformed(e: AnActionEvent) { | ||||||
|  |     val project = e.project ?: return | ||||||
|  |     val fileEditorManager = FileEditorManagerEx.getInstanceExIfCreated(project) ?: return | ||||||
|  |     val editors = fileEditorManager.allEditors.filterIsInstance<TextEditor>() | ||||||
|  |      | ||||||
|  |     WriteCommandAction.writeCommandAction(project) | ||||||
|  |       .withName(e.presentation.text) | ||||||
|  |       .withGlobalUndo() | ||||||
|  |       .withUndoConfirmationPolicy(UndoConfirmationPolicy.REQUEST_CONFIRMATION) | ||||||
|  |       .run<RuntimeException> { | ||||||
|  |         val reg = injector.macro.lastRegister | ||||||
|  |          | ||||||
|  |         for (editor in editors) { | ||||||
|  |           fileEditorManager.openFile(editor.file, true) | ||||||
|  |            | ||||||
|  |           val vimEditor = editor.editor.vim | ||||||
|  |           vimEditor.mode = Mode.NORMAL() | ||||||
|  |           KeyHandler.getInstance().reset(vimEditor) | ||||||
|  |            | ||||||
|  |           injector.macro.playbackRegister(vimEditor, IjEditorExecutionContext(e.dataContext), reg, 1) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -69,7 +69,8 @@ object VimExtensionFacade { | |||||||
|  |  | ||||||
|  |  | ||||||
|   @JvmStatic |   @JvmStatic | ||||||
|   @Deprecated("Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)", |   @Deprecated( | ||||||
|  |     "Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)", | ||||||
|     ReplaceWith( |     ReplaceWith( | ||||||
|       "VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)", |       "VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)", | ||||||
|       "com.maddyhome.idea.vim.VimPlugin" |       "com.maddyhome.idea.vim.VimPlugin" | ||||||
| @@ -195,7 +196,7 @@ object VimExtensionFacade { | |||||||
|  |  | ||||||
|   @JvmStatic |   @JvmStatic | ||||||
|   fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? { |   fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? { | ||||||
|     val reg = caret.registerStorage.getRegister(register) ?: return null |     val reg = injector.registerGroup.getRegister(register) ?: return null | ||||||
|     return reg.keys |     return reg.keys | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -208,7 +209,7 @@ object VimExtensionFacade { | |||||||
|   /** Set the current contents of the given register */ |   /** Set the current contents of the given register */ | ||||||
|   @JvmStatic |   @JvmStatic | ||||||
|   fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) { |   fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) { | ||||||
|     caret.registerStorage.setKeys(register, keys?.filterNotNull() ?: emptyList()) |     injector.registerGroup.setKeys(register, keys?.filterNotNull() ?: emptyList()) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /** Set the current contents of the given register */ |   /** Set the current contents of the given register */ | ||||||
| @@ -277,4 +278,4 @@ fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFu | |||||||
|  |  | ||||||
| fun interface ScriptFunction { | fun interface ScriptFunction { | ||||||
|   fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult |   fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult | ||||||
| } | } | ||||||
|   | |||||||
| @@ -21,10 +21,7 @@ import com.intellij.openapi.editor.markup.TextAttributes | |||||||
| import com.intellij.openapi.util.Disposer | import com.intellij.openapi.util.Disposer | ||||||
| import com.intellij.util.Alarm | import com.intellij.util.Alarm | ||||||
| import com.intellij.util.Alarm.ThreadToUse | import com.intellij.util.Alarm.ThreadToUse | ||||||
| import com.jetbrains.rd.util.first |  | ||||||
| import com.maddyhome.idea.vim.VimPlugin | import com.maddyhome.idea.vim.VimPlugin | ||||||
| import com.maddyhome.idea.vim.api.ImmutableVimCaret |  | ||||||
| import com.maddyhome.idea.vim.api.VimCaret |  | ||||||
| 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 | ||||||
| import com.maddyhome.idea.vim.common.ModeChangeListener | import com.maddyhome.idea.vim.common.ModeChangeListener | ||||||
| @@ -117,9 +114,9 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis | |||||||
|     initialised = false |     initialised = false | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   override fun yankPerformed(caretToRange: Map<ImmutableVimCaret, TextRange>) { |   override fun yankPerformed(editor: VimEditor, range: TextRange) { | ||||||
|     ensureInitialised() |     ensureInitialised() | ||||||
|     highlightHandler.highlightYankRange(caretToRange) |     highlightHandler.highlightYankRange(editor.ij, range) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   override fun modeChanged(editor: VimEditor, oldMode: Mode) { |   override fun modeChanged(editor: VimEditor, oldMode: Mode) { | ||||||
| @@ -140,25 +137,22 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis | |||||||
|     private var lastEditor: Editor? = null |     private var lastEditor: Editor? = null | ||||||
|     private val highlighters = mutableSetOf<RangeHighlighter>() |     private val highlighters = mutableSetOf<RangeHighlighter>() | ||||||
|  |  | ||||||
|     fun highlightYankRange(caretToRange: Map<ImmutableVimCaret, TextRange>) { |     fun highlightYankRange(editor: Editor, range: TextRange) { | ||||||
|       // from vim-highlightedyank docs: When a new text is yanked or user starts editing, the old highlighting would be deleted |       // from vim-highlightedyank docs: When a new text is yanked or user starts editing, the old highlighting would be deleted | ||||||
|       clearYankHighlighters() |       clearYankHighlighters() | ||||||
|  |  | ||||||
|       val editor = caretToRange.first().key.editor.ij |  | ||||||
|       lastEditor = editor |       lastEditor = editor | ||||||
|  |  | ||||||
|       val attributes = getHighlightTextAttributes(editor) |       val attributes = getHighlightTextAttributes(editor) | ||||||
|       for (range in caretToRange.values) { |       for (i in 0 until range.size()) { | ||||||
|         for (i in 0 until range.size()) { |         val highlighter = editor.markupModel.addRangeHighlighter( | ||||||
|           val highlighter = editor.markupModel.addRangeHighlighter( |           range.startOffsets[i], | ||||||
|             range.startOffsets[i], |           range.endOffsets[i], | ||||||
|             range.endOffsets[i], |           HighlighterLayer.SELECTION, | ||||||
|             HighlighterLayer.SELECTION, |           attributes, | ||||||
|             attributes, |           HighlighterTargetArea.EXACT_RANGE, | ||||||
|             HighlighterTargetArea.EXACT_RANGE, |         ) | ||||||
|           ) |         highlighters.add(highlighter) | ||||||
|           highlighters.add(highlighter) |  | ||||||
|         } |  | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       // from vim-highlightedyank docs: A negative number makes the highlight persistent. |       // from vim-highlightedyank docs: A negative number makes the highlight persistent. | ||||||
|   | |||||||
| @@ -234,7 +234,7 @@ private object FileTypePatterns { | |||||||
|     } else if (fileTypeName == "CMakeLists.txt" || fileName == "CMakeLists") { |     } else if (fileTypeName == "CMakeLists.txt" || fileName == "CMakeLists") { | ||||||
|       this.cMakePatterns |       this.cMakePatterns | ||||||
|     } else { |     } else { | ||||||
|       return null |       this.htmlPatterns | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -144,7 +144,7 @@ internal class ReplaceWithRegister : VimExtension { | |||||||
| private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) { | private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) { | ||||||
|   val registerGroup = injector.registerGroup |   val registerGroup = injector.registerGroup | ||||||
|   val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret() |   val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret() | ||||||
|   val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return |   val savedRegister = registerGroup.getRegister(lastRegisterChar) ?: return | ||||||
|  |  | ||||||
|   var usedType = savedRegister.type |   var usedType = savedRegister.type | ||||||
|   var usedText = savedRegister.text |   var usedText = savedRegister.text | ||||||
| @@ -180,4 +180,4 @@ private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimC | |||||||
|       saveToRegister = false |       saveToRegister = false | ||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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 | ||||||
| @@ -36,7 +37,10 @@ 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.exitVisualMode | import com.maddyhome.idea.vim.helper.exitVisualMode | ||||||
|  | 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 | ||||||
| @@ -80,7 +84,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 { | ||||||
| @@ -108,7 +112,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) | ||||||
|       } |       } | ||||||
| @@ -131,15 +135,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 | ||||||
|         editor.exitVisualMode() |         editor.exitVisualMode() | ||||||
|         editor.ij.caretModel.moveToOffset(selectionStart) |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -160,6 +162,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 { | ||||||
| @@ -267,20 +273,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 | ||||||
| @@ -375,15 +402,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 { | ||||||
| @@ -391,7 +418,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) | ||||||
|   | |||||||
| @@ -39,7 +39,6 @@ import com.maddyhome.idea.vim.newapi.IjVimCaret | |||||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | 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.state.mode.Mode | import com.maddyhome.idea.vim.state.mode.Mode | ||||||
| import kotlin.math.min |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Provides all the insert/replace related functionality |  * Provides all the insert/replace related functionality | ||||||
| @@ -125,6 +124,7 @@ 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 | ||||||
| @@ -149,11 +149,7 @@ 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) { | ||||||
|   | |||||||
| @@ -139,7 +139,7 @@ object IjOptions { | |||||||
|   // Temporary feature flags during development, not really intended for external use |   // Temporary feature flags during development, not really intended for external use | ||||||
|   val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true)) |   val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true)) | ||||||
|   val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true)) |   val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true)) | ||||||
|   val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", false, isHidden = true)) |   val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isHidden = true)) | ||||||
|   val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true)) |   val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true)) | ||||||
|   val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isHidden = true)) |   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) { | ||||||
|   | |||||||
| @@ -326,7 +326,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 | ||||||
|  |   }  | ||||||
|  | } | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ import com.intellij.openapi.actionSystem.PlatformDataKeys | |||||||
| import com.intellij.openapi.actionSystem.ex.ActionUtil | import com.intellij.openapi.actionSystem.ex.ActionUtil | ||||||
| import com.intellij.openapi.actionSystem.ex.ActionUtil.performDumbAwareWithCallbacks | import com.intellij.openapi.actionSystem.ex.ActionUtil.performDumbAwareWithCallbacks | ||||||
| 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.application.ex.ApplicationManagerEx | import com.intellij.openapi.application.ex.ApplicationManagerEx | ||||||
| import com.intellij.openapi.command.CommandProcessor | import com.intellij.openapi.command.CommandProcessor | ||||||
| import com.intellij.openapi.command.UndoConfirmationPolicy | import com.intellij.openapi.command.UndoConfirmationPolicy | ||||||
| @@ -92,6 +93,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 | ||||||
|   | |||||||
| @@ -60,7 +60,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 | ||||||
|   | |||||||
| @@ -28,6 +28,8 @@ import com.maddyhome.idea.vim.common.InsertSequence | |||||||
| import com.maddyhome.idea.vim.newapi.IjVimCaret | import com.maddyhome.idea.vim.newapi.IjVimCaret | ||||||
| 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 | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -66,15 +68,7 @@ internal class UndoRedoHelper : UndoRedoBase() { | |||||||
|       // 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) | ||||||
|  |         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 { |     } else { | ||||||
|       notifyAboutNewUndo(editor.ij.project) |       notifyAboutNewUndo(editor.ij.project) | ||||||
| @@ -108,7 +102,7 @@ internal class UndoRedoHelper : UndoRedoBase() { | |||||||
|   private fun hasSelection(editor: VimEditor): Boolean { |   private fun hasSelection(editor: VimEditor): Boolean { | ||||||
|     return editor.primaryCaret().ij.hasSelection() |     return editor.primaryCaret().ij.hasSelection() | ||||||
|   } |   } | ||||||
|    |  | ||||||
|   override fun redo(editor: VimEditor, context: ExecutionContext): Boolean { |   override fun redo(editor: VimEditor, context: ExecutionContext): Boolean { | ||||||
|     val ijContext = context.context as DataContext |     val ijContext = context.context as DataContext | ||||||
|     val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false |     val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false | ||||||
| @@ -229,4 +223,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) | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -18,13 +18,12 @@ import com.intellij.openapi.editor.VisualPosition | |||||||
| import com.intellij.openapi.editor.markup.RangeHighlighter | import com.intellij.openapi.editor.markup.RangeHighlighter | ||||||
| import com.intellij.openapi.util.Key | import com.intellij.openapi.util.Key | ||||||
| import com.intellij.openapi.util.UserDataHolder | import com.intellij.openapi.util.UserDataHolder | ||||||
| import com.maddyhome.idea.vim.api.CaretRegisterStorageBase |  | ||||||
| import com.maddyhome.idea.vim.api.LocalMarkStorage | import com.maddyhome.idea.vim.api.LocalMarkStorage | ||||||
| import com.maddyhome.idea.vim.api.SelectionInfo | import com.maddyhome.idea.vim.api.SelectionInfo | ||||||
|  | import com.maddyhome.idea.vim.common.InsertSequence | ||||||
| import com.maddyhome.idea.vim.ex.ExOutputModel | import com.maddyhome.idea.vim.ex.ExOutputModel | ||||||
| import com.maddyhome.idea.vim.group.visual.VisualChange | import com.maddyhome.idea.vim.group.visual.VisualChange | ||||||
| import com.maddyhome.idea.vim.group.visual.vimLeadSelectionOffset | import com.maddyhome.idea.vim.group.visual.vimLeadSelectionOffset | ||||||
| import com.maddyhome.idea.vim.common.InsertSequence |  | ||||||
| import com.maddyhome.idea.vim.newapi.vim | import com.maddyhome.idea.vim.newapi.vim | ||||||
| import com.maddyhome.idea.vim.state.VimStateMachine | import com.maddyhome.idea.vim.state.VimStateMachine | ||||||
| import com.maddyhome.idea.vim.state.mode.Mode | import com.maddyhome.idea.vim.state.mode.Mode | ||||||
| @@ -96,7 +95,6 @@ internal var Caret.vimInsertStart: RangeMarker by userDataOr { | |||||||
| } | } | ||||||
|  |  | ||||||
| // TODO: Data could be lost during visual block motion | // TODO: Data could be lost during visual block motion | ||||||
| internal var Caret.registerStorage: CaretRegisterStorageBase? by userDataCaretToEditor() |  | ||||||
| internal var Caret.markStorage: LocalMarkStorage? by userDataCaretToEditor() | internal var Caret.markStorage: LocalMarkStorage? by userDataCaretToEditor() | ||||||
| internal var Caret.lastSelectionInfo: SelectionInfo? by userDataCaretToEditor() | internal var Caret.lastSelectionInfo: SelectionInfo? by userDataCaretToEditor() | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,32 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2003-2023 The IdeaVim authors |  | ||||||
|  * |  | ||||||
|  * Use of this source code is governed by an MIT-style |  | ||||||
|  * license that can be found in the LICENSE.txt file or at |  | ||||||
|  * https://opensource.org/licenses/MIT. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| package com.maddyhome.idea.vim.helper |  | ||||||
|  |  | ||||||
| import com.intellij.ide.plugins.StandalonePluginUpdateChecker |  | ||||||
| import com.intellij.openapi.components.Service |  | ||||||
| import com.intellij.openapi.components.service |  | ||||||
| import com.maddyhome.idea.vim.VimPlugin |  | ||||||
| import com.maddyhome.idea.vim.group.NotificationService |  | ||||||
| import com.maddyhome.idea.vim.icons.VimIcons |  | ||||||
|  |  | ||||||
| @Service(Service.Level.APP) |  | ||||||
| internal class VimStandalonePluginUpdateChecker : StandalonePluginUpdateChecker( |  | ||||||
|   VimPlugin.getPluginId(), |  | ||||||
|   updateTimestampProperty = PROPERTY_NAME, |  | ||||||
|   NotificationService.IDEAVIM_STICKY_GROUP, |  | ||||||
|   VimIcons.IDEAVIM, |  | ||||||
| ) { |  | ||||||
|  |  | ||||||
|   override fun skipUpdateCheck(): Boolean = VimPlugin.isNotEnabled() || "dev" in VimPlugin.getVersion() |  | ||||||
|  |  | ||||||
|   companion object { |  | ||||||
|     private const val PROPERTY_NAME = "ideavim.statistics.timestamp" |  | ||||||
|     fun getInstance(): VimStandalonePluginUpdateChecker = service() |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -10,7 +10,6 @@ package com.maddyhome.idea.vim.listener | |||||||
|  |  | ||||||
| import com.intellij.execution.impl.ConsoleViewImpl | import com.intellij.execution.impl.ConsoleViewImpl | ||||||
| import com.intellij.openapi.application.ApplicationManager | import com.intellij.openapi.application.ApplicationManager | ||||||
| import com.intellij.openapi.editor.EditorKind |  | ||||||
| import com.maddyhome.idea.vim.KeyHandler | import com.maddyhome.idea.vim.KeyHandler | ||||||
| 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 | ||||||
| @@ -18,7 +17,6 @@ import com.maddyhome.idea.vim.api.VimEditor | |||||||
| import com.maddyhome.idea.vim.api.injector | import com.maddyhome.idea.vim.api.injector | ||||||
| import com.maddyhome.idea.vim.command.OperatorArguments | import com.maddyhome.idea.vim.command.OperatorArguments | ||||||
| import com.maddyhome.idea.vim.common.EditorListener | import com.maddyhome.idea.vim.common.EditorListener | ||||||
| import com.maddyhome.idea.vim.helper.EditorHelper |  | ||||||
| import com.maddyhome.idea.vim.helper.inInsertMode | import com.maddyhome.idea.vim.helper.inInsertMode | ||||||
| import com.maddyhome.idea.vim.newapi.ij | import com.maddyhome.idea.vim.newapi.ij | ||||||
| import com.maddyhome.idea.vim.state.mode.Mode | import com.maddyhome.idea.vim.state.mode.Mode | ||||||
| @@ -63,13 +61,7 @@ class IJEditorFocusListener : EditorListener { | |||||||
|       val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor) |       val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor) | ||||||
|       VimPlugin.getChange().insertBeforeCursor(editor, context) |       VimPlugin.getChange().insertBeforeCursor(editor, context) | ||||||
|     } |     } | ||||||
|     if (!ijEditor.isViewer && |     if (!ijEditor.document.isWritable) { | ||||||
|       !EditorHelper.isFileEditor(ijEditor) && |  | ||||||
|       ijEditor.document.isWritable && |  | ||||||
|       !ijEditor.inInsertMode && ijEditor.editorKind != EditorKind.DIFF |  | ||||||
|     ) { |  | ||||||
|       switchToInsertMode.run() |  | ||||||
|     } else if (!ijEditor.document.isWritable) { |  | ||||||
|       val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor) |       val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor) | ||||||
|       val mode = injector.vimState.mode |       val mode = injector.vimState.mode | ||||||
|       when (mode) { |       when (mode) { | ||||||
| @@ -86,4 +78,4 @@ class IJEditorFocusListener : EditorListener { | |||||||
|     } |     } | ||||||
|     KeyHandler.getInstance().reset(editor) |     KeyHandler.getInstance().reset(editor) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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.keymap.KeymapManager | import com.intellij.openapi.keymap.KeymapManager | ||||||
| import com.intellij.openapi.project.DumbAwareToggleAction | import com.intellij.openapi.project.DumbAwareToggleAction | ||||||
| import com.intellij.openapi.util.TextRange | import com.intellij.openapi.util.TextRange | ||||||
| @@ -58,6 +59,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 | ||||||
|  |  | ||||||
| @@ -67,6 +69,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 | ||||||
| @@ -115,42 +118,57 @@ 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 { | ||||||
|  |             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 { |  | ||||||
|           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.VimKeyListener | import com.maddyhome.idea.vim.VimKeyListener | ||||||
| @@ -79,7 +83,6 @@ import com.maddyhome.idea.vim.handler.keyCheckRequests | |||||||
| import com.maddyhome.idea.vim.helper.CaretVisualAttributesListener | import com.maddyhome.idea.vim.helper.CaretVisualAttributesListener | ||||||
| import com.maddyhome.idea.vim.helper.GuicursorChangeListener | import com.maddyhome.idea.vim.helper.GuicursorChangeListener | ||||||
| import com.maddyhome.idea.vim.helper.StrictMode | import com.maddyhome.idea.vim.helper.StrictMode | ||||||
| import com.maddyhome.idea.vim.helper.VimStandalonePluginUpdateChecker |  | ||||||
| import com.maddyhome.idea.vim.helper.exitSelectMode | import com.maddyhome.idea.vim.helper.exitSelectMode | ||||||
| import com.maddyhome.idea.vim.helper.exitVisualMode | import com.maddyhome.idea.vim.helper.exitVisualMode | ||||||
| import com.maddyhome.idea.vim.helper.forceBarCursor | import com.maddyhome.idea.vim.helper.forceBarCursor | ||||||
| @@ -95,6 +98,7 @@ import com.maddyhome.idea.vim.newapi.IjVimSearchGroup | |||||||
| import com.maddyhome.idea.vim.newapi.InsertTimeRecorder | import com.maddyhome.idea.vim.newapi.InsertTimeRecorder | ||||||
| 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.state.mode.Mode | ||||||
| import com.maddyhome.idea.vim.state.mode.inSelectMode | import com.maddyhome.idea.vim.state.mode.inSelectMode | ||||||
| import com.maddyhome.idea.vim.state.mode.selectionType | import com.maddyhome.idea.vim.state.mode.selectionType | ||||||
| import com.maddyhome.idea.vim.ui.ShowCmdOptionChangeListener | import com.maddyhome.idea.vim.ui.ShowCmdOptionChangeListener | ||||||
| @@ -103,7 +107,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 | ||||||
| @@ -285,12 +288,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 |  | ||||||
|  |  | ||||||
|       // Protect against double initialisation |       // Protect against double initialisation | ||||||
|       if (editor.getUserData(editorListenersDisposableKey) != null) { |       if (editor.getUserData(editorListenersDisposableKey) != null) { | ||||||
| @@ -386,7 +387,17 @@ internal object VimListenerManager { | |||||||
|     override fun selectionChanged(event: FileEditorManagerEvent) { |     override fun selectionChanged(event: FileEditorManagerEvent) { | ||||||
|       // We can't rely on being passed a non-null editor, so check for Code With Me scenarios explicitly |       // We can't rely on being passed a non-null editor, so check for Code With Me scenarios explicitly | ||||||
|       if (VimPlugin.isNotEnabled() || !ClientId.isCurrentlyUnderLocalId) return |       if (VimPlugin.isNotEnabled() || !ClientId.isCurrentlyUnderLocalId) return | ||||||
|  |        | ||||||
|  |       val newEditor = event.newEditor | ||||||
|  |       if (newEditor is TextEditor) { | ||||||
|  |         val editor = newEditor.editor | ||||||
|  |         if (editor.isInsertMode) { | ||||||
|  |           editor.vim.mode = Mode.NORMAL() | ||||||
|  |           KeyHandler.getInstance().reset(editor.vim) | ||||||
|  |         } | ||||||
|  |         injector.scroll.scrollCaretIntoView(editor.vim) | ||||||
|  |       } | ||||||
|  |        | ||||||
|       MotionGroup.fileEditorManagerSelectionChangedCallback(event) |       MotionGroup.fileEditorManagerSelectionChangedCallback(event) | ||||||
|       FileGroup.fileEditorManagerSelectionChangedCallback(event) |       FileGroup.fileEditorManagerSelectionChangedCallback(event) | ||||||
|       VimPlugin.getSearch().fileEditorManagerSelectionChangedCallback(event) |       VimPlugin.getSearch().fileEditorManagerSelectionChangedCallback(event) | ||||||
| @@ -458,8 +469,6 @@ internal object VimListenerManager { | |||||||
|  |  | ||||||
|         event.editor.putUserData(openingEditorKey, OpeningEditor(openingEditor, owningEditorWindow, isPreview, canBeReused)) |         event.editor.putUserData(openingEditorKey, OpeningEditor(openingEditor, owningEditorWindow, isPreview, canBeReused)) | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       VimStandalonePluginUpdateChecker.getInstance().pluginUsed() |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     override fun editorReleased(event: EditorFactoryEvent) { |     override fun editorReleased(event: EditorFactoryEvent) { | ||||||
|   | |||||||
| @@ -12,8 +12,6 @@ import com.intellij.openapi.editor.Caret | |||||||
| import com.intellij.openapi.editor.LogicalPosition | import com.intellij.openapi.editor.LogicalPosition | ||||||
| import com.intellij.openapi.editor.VisualPosition | import com.intellij.openapi.editor.VisualPosition | ||||||
| import com.maddyhome.idea.vim.api.BufferPosition | import com.maddyhome.idea.vim.api.BufferPosition | ||||||
| import com.maddyhome.idea.vim.api.CaretRegisterStorage |  | ||||||
| import com.maddyhome.idea.vim.api.CaretRegisterStorageBase |  | ||||||
| import com.maddyhome.idea.vim.api.ImmutableVimCaret | import com.maddyhome.idea.vim.api.ImmutableVimCaret | ||||||
| import com.maddyhome.idea.vim.api.LocalMarkStorage | import com.maddyhome.idea.vim.api.LocalMarkStorage | ||||||
| import com.maddyhome.idea.vim.api.SelectionInfo | import com.maddyhome.idea.vim.api.SelectionInfo | ||||||
| @@ -29,7 +27,6 @@ import com.maddyhome.idea.vim.helper.insertHistory | |||||||
| import com.maddyhome.idea.vim.helper.lastSelectionInfo | import com.maddyhome.idea.vim.helper.lastSelectionInfo | ||||||
| import com.maddyhome.idea.vim.helper.markStorage | import com.maddyhome.idea.vim.helper.markStorage | ||||||
| import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset | import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset | ||||||
| import com.maddyhome.idea.vim.helper.registerStorage |  | ||||||
| import com.maddyhome.idea.vim.helper.resetVimLastColumn | import com.maddyhome.idea.vim.helper.resetVimLastColumn | ||||||
| import com.maddyhome.idea.vim.helper.vimInsertStart | import com.maddyhome.idea.vim.helper.vimInsertStart | ||||||
| import com.maddyhome.idea.vim.helper.vimLastColumn | import com.maddyhome.idea.vim.helper.vimLastColumn | ||||||
| @@ -41,18 +38,6 @@ import com.maddyhome.idea.vim.state.mode.SelectionType | |||||||
|  |  | ||||||
| internal class IjVimCaret(val caret: Caret) : VimCaretBase() { | internal class IjVimCaret(val caret: Caret) : VimCaretBase() { | ||||||
|  |  | ||||||
|   override val registerStorage: CaretRegisterStorage |  | ||||||
|     get() { |  | ||||||
|       var storage = this.caret.registerStorage |  | ||||||
|       if (storage == null) { |  | ||||||
|         initInjector() // To initialize injector used in CaretRegisterStorageBase |  | ||||||
|         storage = CaretRegisterStorageBase(this) |  | ||||||
|         this.caret.registerStorage = storage |  | ||||||
|       } else if (storage.caret != this) { |  | ||||||
|         storage.caret = this |  | ||||||
|       } |  | ||||||
|       return storage |  | ||||||
|     } |  | ||||||
|   override val markStorage: LocalMarkStorage |   override val markStorage: LocalMarkStorage | ||||||
|     get() { |     get() { | ||||||
|       var storage = this.caret.markStorage |       var storage = this.caret.markStorage | ||||||
|   | |||||||
| @@ -165,21 +165,38 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor, VimEditorBase( | |||||||
|     return editor.caretModel.allCarets.map { IjVimCaret(it) } |     return editor.caretModel.allCarets.map { IjVimCaret(it) } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   override var isFirstCaret = true | ||||||
|  |   override var isReversingCarets = false | ||||||
|  |    | ||||||
|   @Suppress("ideavimRunForEachCaret") |   @Suppress("ideavimRunForEachCaret") | ||||||
|   override fun forEachCaret(action: (VimCaret) -> Unit) { |   override fun forEachCaret(action: (VimCaret) -> Unit) { | ||||||
|     if (editor.vim.inBlockSelection) { |     if (editor.vim.inBlockSelection) { | ||||||
|       action(IjVimCaret(editor.caretModel.primaryCaret)) |       action(IjVimCaret(editor.caretModel.primaryCaret)) | ||||||
|     } else { |     } else { | ||||||
|       editor.caretModel.runForEachCaret({ |       try { | ||||||
|         if (it.isValid) { |         editor.caretModel.runForEachCaret({ | ||||||
|           action(IjVimCaret(it)) |           if (it.isValid) { | ||||||
|         } |             action(IjVimCaret(it)) | ||||||
|       }, false) |             isFirstCaret = false | ||||||
|  |           } | ||||||
|  |         }, false) | ||||||
|  |       } finally { | ||||||
|  |         isFirstCaret = true | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   override fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean) { |   override fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean) { | ||||||
|     editor.caretModel.runForEachCaret({ action(IjVimCaret(it)) }, reverse) |     isReversingCarets = reverse | ||||||
|  |     try { | ||||||
|  |       editor.caretModel.runForEachCaret({ | ||||||
|  |         action(IjVimCaret(it)) | ||||||
|  |         isFirstCaret = false | ||||||
|  |       }, reverse) | ||||||
|  |     } finally { | ||||||
|  |       isFirstCaret = true | ||||||
|  |       isReversingCarets = false | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   override fun isInForEachCaretScope(): Boolean { |   override fun isInForEachCaretScope(): Boolean { | ||||||
| @@ -530,4 +547,4 @@ internal class InsertTimeRecorder: ModeChangeListener { | |||||||
|       editor.forEachCaret { undo.endInsertSequence(it, it.offset, nanoTime) } |       editor.forEachCaret { undo.endInsertSequence(it, it.offset, nanoTime) } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -23,7 +23,10 @@ import com.maddyhome.idea.vim.EventFacade; | |||||||
| import com.maddyhome.idea.vim.KeyHandler; | import com.maddyhome.idea.vim.KeyHandler; | ||||||
| import com.maddyhome.idea.vim.VimPlugin; | import com.maddyhome.idea.vim.VimPlugin; | ||||||
| import com.maddyhome.idea.vim.action.VimShortcutKeyAction; | import com.maddyhome.idea.vim.action.VimShortcutKeyAction; | ||||||
| import com.maddyhome.idea.vim.api.*; | import com.maddyhome.idea.vim.api.VimCommandLine; | ||||||
|  | import com.maddyhome.idea.vim.api.VimCommandLineCaret; | ||||||
|  | import com.maddyhome.idea.vim.api.VimEditor; | ||||||
|  | import com.maddyhome.idea.vim.api.VimKeyGroupBase; | ||||||
| import com.maddyhome.idea.vim.ex.ranges.LineRange; | import com.maddyhome.idea.vim.ex.ranges.LineRange; | ||||||
| import com.maddyhome.idea.vim.helper.SearchHighlightsHelper; | import com.maddyhome.idea.vim.helper.SearchHighlightsHelper; | ||||||
| import com.maddyhome.idea.vim.helper.UiHelper; | import com.maddyhome.idea.vim.helper.UiHelper; | ||||||
| @@ -348,7 +351,7 @@ public class ExEntryPanel extends JPanel implements VimCommandLine { | |||||||
|         // coerced to at least 1. |         // coerced to at least 1. | ||||||
|         int count1 = KeyHandler.getInstance().getKeyHandlerState().getEditorCommandBuilder().getAggregatedUncommittedCount(); |         int count1 = KeyHandler.getInstance().getKeyHandlerState().getEditorCommandBuilder().getAggregatedUncommittedCount(); | ||||||
|  |  | ||||||
|         if (labelText.equals("/") || labelText.equals("?") || searchCommand) { |         if ((labelText.equals("/") || labelText.equals("?") || searchCommand) && !injector.getMacro().isExecutingMacro()) { | ||||||
|           final boolean forwards = !labelText.equals("?");  // :s, :g, :v are treated as forwards |           final boolean forwards = !labelText.equals("?");  // :s, :g, :v are treated as forwards | ||||||
|           int pattenEnd = injector.getSearchGroup().findEndOfPattern(searchText, separator, 0); |           int pattenEnd = injector.getSearchGroup().findEndOfPattern(searchText, separator, 0); | ||||||
|           final String pattern = searchText.substring(0, pattenEnd); |           final String pattern = searchText.substring(0, pattenEnd); | ||||||
|   | |||||||
| @@ -1,12 +1,4 @@ | |||||||
| <!-- | <idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude"> | ||||||
|   ~ Copyright 2003-2023 The IdeaVim authors |  | ||||||
|   ~ |  | ||||||
|   ~ Use of this source code is governed by an MIT-style |  | ||||||
|   ~ license that can be found in the LICENSE.txt file or at |  | ||||||
|   ~ https://opensource.org/licenses/MIT. |  | ||||||
|   --> |  | ||||||
|  |  | ||||||
| <idea-plugin url="https://plugins.jetbrains.com/plugin/164" xmlns:xi="http://www.w3.org/2001/XInclude"> |  | ||||||
|   <name>IdeaVim</name> |   <name>IdeaVim</name> | ||||||
|   <id>IdeaVIM</id> |   <id>IdeaVIM</id> | ||||||
|   <description><![CDATA[ |   <description><![CDATA[ | ||||||
| @@ -21,7 +13,7 @@ | |||||||
|         <li><a href="https://youtrack.jetbrains.com/issues/VIM">Issue tracker</a>: feature requests and bug reports</li> |         <li><a href="https://youtrack.jetbrains.com/issues/VIM">Issue tracker</a>: feature requests and bug reports</li> | ||||||
|       </ul> |       </ul> | ||||||
|     ]]></description> |     ]]></description> | ||||||
|   <version>SNAPSHOT</version> |   <version>chylex</version> | ||||||
|   <vendor>JetBrains</vendor> |   <vendor>JetBrains</vendor> | ||||||
|  |  | ||||||
|   <!-- Mark the plugin as compatible with RubyMine and other products based on the IntelliJ platform (including CWM) --> |   <!-- Mark the plugin as compatible with RubyMine and other products based on the IntelliJ platform (including CWM) --> | ||||||
| @@ -133,10 +125,12 @@ | |||||||
|   <xi:include href="/META-INF/includes/VimListeners.xml" xpointer="xpointer(/idea-plugin/*)"/> |   <xi:include href="/META-INF/includes/VimListeners.xml" xpointer="xpointer(/idea-plugin/*)"/> | ||||||
|  |  | ||||||
|   <actions resource-bundle="messages.IdeaVimBundle"> |   <actions resource-bundle="messages.IdeaVimBundle"> | ||||||
|     <action id="VimPluginToggle" class="com.maddyhome.idea.vim.action.VimPluginToggleAction"> |     <group id="com.chylex.intellij.vim" text="Vim" popup="true"> | ||||||
|       <add-to-group group-id="ToolsMenu" anchor="last"/> |       <add-to-group group-id="ToolsMenu" anchor="last"/> | ||||||
|     </action> |       <action id="VimPluginToggle" class="com.maddyhome.idea.vim.action.VimPluginToggleAction"/> | ||||||
|  |       <action id="VimRunLastMacroInOpenFiles" class="com.maddyhome.idea.vim.action.VimRunLastMacroInOpenFiles"/> | ||||||
|  |     </group> | ||||||
|  |      | ||||||
|     <!-- Internal --> |     <!-- Internal --> | ||||||
|     <!--suppress PluginXmlI18n --> |     <!--suppress PluginXmlI18n --> | ||||||
|     <action id="VimInternalAddBlockInlays" class="com.maddyhome.idea.vim.action.internal.AddBlockInlaysAction" text="Add Test Block Inlays | IdeaVim Internal" internal="true"/> |     <action id="VimInternalAddBlockInlays" class="com.maddyhome.idea.vim.action.internal.AddBlockInlaysAction" text="Add Test Block Inlays | IdeaVim Internal" internal="true"/> | ||||||
| @@ -154,5 +148,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,7 +13,6 @@ 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.TextRange | import com.maddyhome.idea.vim.common.TextRange | ||||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||||
| import com.maddyhome.idea.vim.newapi.vim |  | ||||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | import com.maddyhome.idea.vim.state.mode.SelectionType | ||||||
| import org.jetbrains.plugins.ideavim.VimBehaviorDiffers | import org.jetbrains.plugins.ideavim.VimBehaviorDiffers | ||||||
| import org.jetbrains.plugins.ideavim.VimTestCase | import org.jetbrains.plugins.ideavim.VimTestCase | ||||||
| @@ -2173,7 +2172,7 @@ rtyfg${c}hzxc""" | |||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     injector.registerGroup.storeText('*', "fgh") |     injector.registerGroup.storeText('*', "fgh") | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(IjVimEditor(editor), editor.vim.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) |       .storeText(IjVimEditor(editor), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("\"*P")) |     typeText(injector.parser.parseKeys("\"*P")) | ||||||
|     val after = "fg${c}hqfg${c}hwe asd zxc rty fg${c}hfgh vbn" |     val after = "fg${c}hqfg${c}hwe asd zxc rty fg${c}hfgh vbn" | ||||||
|     assertState(after) |     assertState(after) | ||||||
|   | |||||||
| @@ -33,7 +33,7 @@ class IdeaPutNotificationsTest : VimTestCase() { | |||||||
|     appReadySetup(false) |     appReadySetup(false) | ||||||
|     val vimEditor = fixture.editor.vim |     val vimEditor = fixture.editor.vim | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) |       .storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("p")) |     typeText(injector.parser.parseKeys("p")) | ||||||
|  |  | ||||||
|     val notification = notifications().last() |     val notification = notifications().last() | ||||||
| @@ -53,7 +53,7 @@ class IdeaPutNotificationsTest : VimTestCase() { | |||||||
|     appReadySetup(false) |     appReadySetup(false) | ||||||
|     val vimEditor = fixture.editor.vim |     val vimEditor = fixture.editor.vim | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) |       .storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("p")) |     typeText(injector.parser.parseKeys("p")) | ||||||
|  |  | ||||||
|     val notifications = notifications() |     val notifications = notifications() | ||||||
| @@ -71,7 +71,7 @@ class IdeaPutNotificationsTest : VimTestCase() { | |||||||
|     appReadySetup(true) |     appReadySetup(true) | ||||||
|     val vimEditor = fixture.editor.vim |     val vimEditor = fixture.editor.vim | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) |       .storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("p")) |     typeText(injector.parser.parseKeys("p")) | ||||||
|  |  | ||||||
|     val notifications = EventLog.getLogModel(fixture.project).notifications |     val notifications = EventLog.getLogModel(fixture.project).notifications | ||||||
|   | |||||||
| @@ -88,7 +88,7 @@ class PutTestAfterCursorActionTest : VimTestCase() { | |||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) |       .storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("p")) |     typeText(injector.parser.parseKeys("p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -127,7 +127,6 @@ class PutTestAfterCursorActionTest : VimTestCase() { | |||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     injector.registerGroup.storeText( |     injector.registerGroup.storeText( | ||||||
|       vimEditor, |       vimEditor, | ||||||
|       vimEditor.primaryCaret(), |  | ||||||
|       before rangeOf "I found it in a legendary land\n", |       before rangeOf "I found it in a legendary land\n", | ||||||
|       SelectionType.LINE_WISE, |       SelectionType.LINE_WISE, | ||||||
|       false, |       false, | ||||||
| @@ -157,7 +156,7 @@ class PutTestAfterCursorActionTest : VimTestCase() { | |||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) |       .storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("vep")) |     typeText(injector.parser.parseKeys("vep")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
|   | |||||||
| @@ -31,7 +31,7 @@ class PutTextBeforeCursorActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     injector.registerGroup.storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) |     injector.registerGroup.storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("V" + "P")) |     typeText(injector.parser.parseKeys("V" + "P")) | ||||||
|     typeText(injector.parser.parseKeys("V" + "P")) |     typeText(injector.parser.parseKeys("V" + "P")) | ||||||
|     val after = """ |     val after = """ | ||||||
|   | |||||||
| @@ -54,7 +54,7 @@ class PutViaIdeaTest : VimTestCase() { | |||||||
|  |  | ||||||
|     val vimEditor = fixture.editor.vim |     val vimEditor = fixture.editor.vim | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) |       .storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||||
|  |  | ||||||
|     typeText("ppp") |     typeText("ppp") | ||||||
|     val after = "Ilegendarylegendarylegendar${c}y found it in a legendary land" |     val after = "Ilegendarylegendarylegendar${c}y found it in a legendary land" | ||||||
| @@ -74,7 +74,6 @@ class PutViaIdeaTest : VimTestCase() { | |||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText( |       .storeText( | ||||||
|         vimEditor, |         vimEditor, | ||||||
|         vimEditor.primaryCaret(), |  | ||||||
|         before rangeOf "legendary$randomUUID", |         before rangeOf "legendary$randomUUID", | ||||||
|         SelectionType.CHARACTER_WISE, |         SelectionType.CHARACTER_WISE, | ||||||
|         false, |         false, | ||||||
| @@ -100,7 +99,6 @@ class PutViaIdeaTest : VimTestCase() { | |||||||
|     val vimEditor = fixture.editor.vim |     val vimEditor = fixture.editor.vim | ||||||
|     VimPlugin.getRegister().storeText( |     VimPlugin.getRegister().storeText( | ||||||
|       vimEditor, |       vimEditor, | ||||||
|       vimEditor.primaryCaret(), |  | ||||||
|       before rangeOf "\nLorem ipsum dolor sit amet,\n", |       before rangeOf "\nLorem ipsum dolor sit amet,\n", | ||||||
|       SelectionType.CHARACTER_WISE, |       SelectionType.CHARACTER_WISE, | ||||||
|       false, |       false, | ||||||
|   | |||||||
| @@ -75,7 +75,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     val before = "${c}I found it in a legendary land" |     val before = "${c}I found it in a legendary land" | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("ve" + "p")) |     typeText(injector.parser.parseKeys("ve" + "p")) | ||||||
|     val after = "legendar${c}y it in a legendary land" |     val after = "legendar${c}y it in a legendary land" | ||||||
|     assertState(after) |     assertState(after) | ||||||
| @@ -87,7 +87,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     val before = "${c}I found it in a legendary land" |     val before = "${c}I found it in a legendary land" | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("v2e" + "2p")) |     typeText(injector.parser.parseKeys("v2e" + "2p")) | ||||||
|     val after = "legendarylegendar${c}y in a legendary land" |     val after = "legendarylegendar${c}y in a legendary land" | ||||||
|     assertState(after) |     assertState(after) | ||||||
| @@ -99,7 +99,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     val before = "${c}I found it in a legendary land" |     val before = "${c}I found it in a legendary land" | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("v$" + "2p")) |     typeText(injector.parser.parseKeys("v$" + "2p")) | ||||||
|     val after = "legendarylegendar${c}y" |     val after = "legendarylegendar${c}y" | ||||||
|     assertState(after) |     assertState(after) | ||||||
| @@ -133,7 +133,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     val before = "I foun${c}d it in a legendary land" |     val before = "I foun${c}d it in a legendary land" | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("vb" + "p")) |     typeText(injector.parser.parseKeys("vb" + "p")) | ||||||
|     val after = "I legendar${c}y it in a legendary land" |     val after = "I legendar${c}y it in a legendary land" | ||||||
|     assertState(after) |     assertState(after) | ||||||
| @@ -154,7 +154,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("ve" + "p")) |     typeText(injector.parser.parseKeys("ve" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -182,7 +182,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("ve" + "p")) |     typeText(injector.parser.parseKeys("ve" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -210,7 +210,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("ve" + "p")) |     typeText(injector.parser.parseKeys("ve" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -238,7 +238,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("v$" + "p")) |     typeText(injector.parser.parseKeys("v$" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -455,7 +455,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("ve" + "p")) |     typeText(injector.parser.parseKeys("ve" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -491,7 +491,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("ve" + "p")) |     typeText(injector.parser.parseKeys("ve" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -529,7 +529,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("ve" + "2p")) |     typeText(injector.parser.parseKeys("ve" + "2p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -640,7 +640,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("V" + "p")) |     typeText(injector.parser.parseKeys("V" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -666,7 +666,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("V" + "2p")) |     typeText(injector.parser.parseKeys("V" + "2p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -703,7 +703,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("V" + "p")) |     typeText(injector.parser.parseKeys("V" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -876,7 +876,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("V" + "p")) |     typeText(injector.parser.parseKeys("V" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -902,7 +902,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("V" + "2p")) |     typeText(injector.parser.parseKeys("V" + "2p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -939,7 +939,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("V" + "p")) |     typeText(injector.parser.parseKeys("V" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -1117,7 +1117,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("V" + "p")) |     typeText(injector.parser.parseKeys("V" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -1172,7 +1172,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("V" + "p")) |     typeText(injector.parser.parseKeys("V" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -1233,7 +1233,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("V" + "2p")) |     typeText(injector.parser.parseKeys("V" + "2p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -1409,7 +1409,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("<C-V>2e2j" + "p")) |     typeText(injector.parser.parseKeys("<C-V>2e2j" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -1435,7 +1435,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("<C-V>3e2k" + "p")) |     typeText(injector.parser.parseKeys("<C-V>3e2k" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -1461,7 +1461,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("<C-V>2e2j" + "2p")) |     typeText(injector.parser.parseKeys("<C-V>2e2j" + "2p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -1487,7 +1487,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("<C-V>3j$" + "p")) |     typeText(injector.parser.parseKeys("<C-V>3j$" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -1526,7 +1526,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("<C-V>2e2j" + "p")) |     typeText(injector.parser.parseKeys("<C-V>2e2j" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -1554,7 +1554,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("<C-V>2e2j" + "P")) |     typeText(injector.parser.parseKeys("<C-V>2e2j" + "P")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -1593,7 +1593,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("<C-V>2e2j" + "2p")) |     typeText(injector.parser.parseKeys("<C-V>2e2j" + "2p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -1633,7 +1633,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("<C-V>2e3j" + "p")) |     typeText(injector.parser.parseKeys("<C-V>2e3j" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -1672,7 +1672,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("<C-V>2j$" + "p")) |     typeText(injector.parser.parseKeys("<C-V>2j$" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -1707,7 +1707,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("<C-V>2e2j" + "p")) |     typeText(injector.parser.parseKeys("<C-V>2e2j" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -1743,7 +1743,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("<C-V>2e3j" + "p")) |     typeText(injector.parser.parseKeys("<C-V>2e3j" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -1779,7 +1779,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("<C-V>2ej" + "p")) |     typeText(injector.parser.parseKeys("<C-V>2ej" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -1815,7 +1815,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("<C-V>elj" + "p")) |     typeText(injector.parser.parseKeys("<C-V>elj" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
| @@ -1852,7 +1852,7 @@ class PutVisualTextActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("<C-V>2j$" + "p")) |     typeText(injector.parser.parseKeys("<C-V>2j$" + "p")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             A Discovery |             A Discovery | ||||||
|   | |||||||
| @@ -33,7 +33,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | |||||||
|     val before = "${c}I found it in a legendary land" |     val before = "${c}I found it in a legendary land" | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.CHARACTER_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("v2e" + "2gp")) |     typeText(injector.parser.parseKeys("v2e" + "2gp")) | ||||||
|     val after = "legendarylegendary$c in a legendary land" |     val after = "legendarylegendary$c in a legendary land" | ||||||
|     assertState(after) |     assertState(after) | ||||||
| @@ -45,7 +45,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | |||||||
|     val before = "${c}I found it in a legendary land" |     val before = "${c}I found it in a legendary land" | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.LINE_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.LINE_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("v2e" + "gp")) |     typeText(injector.parser.parseKeys("v2e" + "gp")) | ||||||
|     val after = """ |     val after = """ | ||||||
|  |  | ||||||
| @@ -61,7 +61,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | |||||||
|     val before = "${c}I found it in a legendary land" |     val before = "${c}I found it in a legendary land" | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.CHARACTER_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("V" + "gp")) |     typeText(injector.parser.parseKeys("V" + "gp")) | ||||||
|     val after = "legendary\n$c" |     val after = "legendary\n$c" | ||||||
|     assertState(after) |     assertState(after) | ||||||
| @@ -88,7 +88,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(file) |     val editor = configureByText(file) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(2, 11), SelectionType.LINE_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, TextRange(2, 11), SelectionType.LINE_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("V" + "gp")) |     typeText(injector.parser.parseKeys("V" + "gp")) | ||||||
|     assertState(newFile) |     assertState(newFile) | ||||||
|   } |   } | ||||||
| @@ -134,7 +134,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | |||||||
|     val before = "${c}I found it in a legendary land" |     val before = "${c}I found it in a legendary land" | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.LINE_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.LINE_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("v2e" + "gP")) |     typeText(injector.parser.parseKeys("v2e" + "gP")) | ||||||
|     val after = """ |     val after = """ | ||||||
|  |  | ||||||
| @@ -150,7 +150,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | |||||||
|     val before = "${c}I found it in a legendary land" |     val before = "${c}I found it in a legendary land" | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.CHARACTER_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("v2e" + "2gP")) |     typeText(injector.parser.parseKeys("v2e" + "2gP")) | ||||||
|     val after = "legendarylegendary$c in a legendary land" |     val after = "legendarylegendary$c in a legendary land" | ||||||
|     assertState(after) |     assertState(after) | ||||||
| @@ -162,7 +162,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | |||||||
|     val before = "${c}I found it in a legendary land" |     val before = "${c}I found it in a legendary land" | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.CHARACTER_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("v$" + "2gP")) |     typeText(injector.parser.parseKeys("v$" + "2gP")) | ||||||
|     val after = "legendarylegendar${c}y" |     val after = "legendarylegendar${c}y" | ||||||
|     assertState(after) |     assertState(after) | ||||||
| @@ -174,7 +174,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | |||||||
|     val before = "${c}I found it in a legendary land" |     val before = "${c}I found it in a legendary land" | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.CHARACTER_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("V" + "gP")) |     typeText(injector.parser.parseKeys("V" + "gP")) | ||||||
|     val after = "legendary\n$c" |     val after = "legendary\n$c" | ||||||
|     assertState(after) |     assertState(after) | ||||||
| @@ -273,7 +273,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.BLOCK_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 19), SelectionType.BLOCK_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("<S-v>" + "gp")) |     typeText(injector.parser.parseKeys("<S-v>" + "gp")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             ${c}fgh |             ${c}fgh | ||||||
| @@ -299,7 +299,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | |||||||
|     """.trimIndent() |     """.trimIndent() | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.LINE_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 19), SelectionType.LINE_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("<C-v>" + "h" + "gp")) |     typeText(injector.parser.parseKeys("<C-v>" + "h" + "gp")) | ||||||
|     val after = """ |     val after = """ | ||||||
|             q |             q | ||||||
| @@ -320,7 +320,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | |||||||
|     val before = "${c}qwe asd ${c}zxc rty ${c}fgh vbn" |     val before = "${c}qwe asd ${c}zxc rty ${c}fgh vbn" | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) |     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("v2e" + "2gp")) |     typeText(injector.parser.parseKeys("v2e" + "2gp")) | ||||||
|     val after = "fghfgh$c fghfgh$c fghfgh$c" |     val after = "fghfgh$c fghfgh$c fghfgh$c" | ||||||
|     assertState(after) |     assertState(after) | ||||||
|   | |||||||
| @@ -91,7 +91,7 @@ class YankVisualActionTest : VimTestCase() { | |||||||
|     typeText(injector.parser.parseKeys("viw" + "y")) |     typeText(injector.parser.parseKeys("viw" + "y")) | ||||||
|     val editor = fixture.editor.vim |     val editor = fixture.editor.vim | ||||||
|     val lastRegister = injector.registerGroup.lastRegisterChar |     val lastRegister = injector.registerGroup.lastRegisterChar | ||||||
|     val registers = editor.carets().map { it.registerStorage.getRegister(lastRegister)?.rawText } |     val registers = editor.carets().map { injector.registerGroup.getRegister(lastRegister)?.rawText } | ||||||
|     kotlin.test.assertEquals(listOf("found", "was"), registers) |     kotlin.test.assertEquals(listOf("found", "was"), registers) | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -172,7 +172,7 @@ class YankVisualActionTest : VimTestCase() { | |||||||
|     typeText(injector.parser.parseKeys("V" + "y")) |     typeText(injector.parser.parseKeys("V" + "y")) | ||||||
|     val editor = fixture.editor.vim |     val editor = fixture.editor.vim | ||||||
|     val lastRegister = injector.registerGroup.lastRegisterChar |     val lastRegister = injector.registerGroup.lastRegisterChar | ||||||
|     val registers = editor.carets().map { it.registerStorage.getRegister(lastRegister)?.rawText } |     val registers = editor.carets().map { injector.registerGroup.getRegister(lastRegister)?.rawText } | ||||||
|     kotlin.test.assertEquals( |     kotlin.test.assertEquals( | ||||||
|       listOf("all rocks and lavender and tufted grass,\n", "hard by the torrent of a mountain pass.\n"), |       listOf("all rocks and lavender and tufted grass,\n", "hard by the torrent of a mountain pass.\n"), | ||||||
|       registers, |       registers, | ||||||
|   | |||||||
| @@ -130,7 +130,7 @@ class MultipleCaretsTest : VimTestCase() { | |||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) |       .storeText(vimEditor, TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(commandToKeys("pu")) |     typeText(commandToKeys("pu")) | ||||||
|     val after = """ |     val after = """ | ||||||
|           qwe |           qwe | ||||||
| @@ -165,7 +165,7 @@ class MultipleCaretsTest : VimTestCase() { | |||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) |       .storeText(vimEditor, TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(commandToKeys("pu")) |     typeText(commandToKeys("pu")) | ||||||
|     val after = """ |     val after = """ | ||||||
|           qwe |           qwe | ||||||
| @@ -201,7 +201,7 @@ class MultipleCaretsTest : VimTestCase() { | |||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) |       .storeText(vimEditor, TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(commandToKeys("4pu")) |     typeText(commandToKeys("4pu")) | ||||||
|     val after = """ |     val after = """ | ||||||
|           qwe |           qwe | ||||||
| @@ -237,7 +237,7 @@ class MultipleCaretsTest : VimTestCase() { | |||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     val vimEditor = editor.vim |     val vimEditor = editor.vim | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) |       .storeText(vimEditor, TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(commandToKeys("4pu")) |     typeText(commandToKeys("4pu")) | ||||||
|     val after = """ |     val after = """ | ||||||
|           qwe |           qwe | ||||||
| @@ -258,7 +258,7 @@ class MultipleCaretsTest : VimTestCase() { | |||||||
|     val before = "${c}qwe\n" + "rty\n" + "as${c}d\n" + "fgh\n" + "zxc\n" + "vbn\n" |     val before = "${c}qwe\n" + "rty\n" + "as${c}d\n" + "fgh\n" + "zxc\n" + "vbn\n" | ||||||
|     val editor = configureByText(before) |     val editor = configureByText(before) | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(editor.vim, editor.vim.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) |       .storeText(editor.vim, TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||||
|  |  | ||||||
|     typeText("vj") |     typeText("vj") | ||||||
|     typeText(commandToKeys("pu")) |     typeText(commandToKeys("pu")) | ||||||
|   | |||||||
| @@ -9,7 +9,6 @@ | |||||||
| package org.jetbrains.plugins.ideavim.ex.implementation.commands | package org.jetbrains.plugins.ideavim.ex.implementation.commands | ||||||
|  |  | ||||||
| import com.maddyhome.idea.vim.VimPlugin | import com.maddyhome.idea.vim.VimPlugin | ||||||
| import com.maddyhome.idea.vim.newapi.vim |  | ||||||
| import com.maddyhome.idea.vim.register.RegisterConstants | import com.maddyhome.idea.vim.register.RegisterConstants | ||||||
| import org.jetbrains.plugins.ideavim.VimTestCase | import org.jetbrains.plugins.ideavim.VimTestCase | ||||||
| import org.junit.jupiter.api.Test | import org.junit.jupiter.api.Test | ||||||
| @@ -303,33 +302,4 @@ class YankLinesCommandTest : VimTestCase() { | |||||||
|         |Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. |         |Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. | ||||||
|         |""".trimMargin()) |         |""".trimMargin()) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Test |  | ||||||
|   fun `test multicaret yank`() { |  | ||||||
|     configureByText( |  | ||||||
|       """ |  | ||||||
|         |Lorem ipsum dolor sit amet, consectetur adipiscing elit. |  | ||||||
|         |${c}Morbi nec luctus tortor, id venenatis lacus. |  | ||||||
|         |${c}Nunc sit amet tellus vel purus cursus posuere et at purus. |  | ||||||
|         |${c}Ut id dapibus augue. |  | ||||||
|         |Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. |  | ||||||
|         |Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui. |  | ||||||
|       """.trimMargin() |  | ||||||
|     ) |  | ||||||
|     enterCommand("y") |  | ||||||
|     val carets = fixture.editor.vim.carets() |  | ||||||
|     assertEquals(3, carets.size) |  | ||||||
|     assertEquals( |  | ||||||
|       "Morbi nec luctus tortor, id venenatis lacus.\n", |  | ||||||
|       carets[0].registerStorage.getRegister(RegisterConstants.UNNAMED_REGISTER)?.text |  | ||||||
|     ) |  | ||||||
|     assertEquals( |  | ||||||
|       "Nunc sit amet tellus vel purus cursus posuere et at purus.\n", |  | ||||||
|       carets[1].registerStorage.getRegister(RegisterConstants.UNNAMED_REGISTER)?.text |  | ||||||
|     ) |  | ||||||
|     assertEquals( |  | ||||||
|       "Ut id dapibus augue.\n", |  | ||||||
|       carets[2].registerStorage.getRegister(RegisterConstants.UNNAMED_REGISTER)?.text |  | ||||||
|     ) |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -50,7 +50,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | |||||||
|     configureByText(text) |     configureByText(text) | ||||||
|     val vimEditor = fixture.editor.vim |     val vimEditor = fixture.editor.vim | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "one", SelectionType.CHARACTER_WISE, false) |       .storeText(vimEditor, text rangeOf "one", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("griw")) |     typeText(injector.parser.parseKeys("griw")) | ||||||
|     assertState("one on${c}e three") |     assertState("one on${c}e three") | ||||||
|     assertEquals("one", VimPlugin.getRegister().lastRegister?.text) |     assertEquals("one", VimPlugin.getRegister().lastRegister?.text) | ||||||
| @@ -170,7 +170,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | |||||||
|     configureByText(text) |     configureByText(text) | ||||||
|     val vimEditor = fixture.editor.vim |     val vimEditor = fixture.editor.vim | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "one", SelectionType.CHARACTER_WISE, false) |       .storeText(vimEditor, text rangeOf "one", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("3griw")) |     typeText(injector.parser.parseKeys("3griw")) | ||||||
|     assertState("one on${c}e four") |     assertState("one on${c}e four") | ||||||
|     assertEquals("one", VimPlugin.getRegister().lastRegister?.text) |     assertEquals("one", VimPlugin.getRegister().lastRegister?.text) | ||||||
| @@ -184,7 +184,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | |||||||
|     configureByText(text) |     configureByText(text) | ||||||
|     val vimEditor = fixture.editor.vim |     val vimEditor = fixture.editor.vim | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "one", SelectionType.CHARACTER_WISE, false) |       .storeText(vimEditor, text rangeOf "one", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("griw")) |     typeText(injector.parser.parseKeys("griw")) | ||||||
|     assertState("one two one four") |     assertState("one two one four") | ||||||
|     assertEquals("one", VimPlugin.getRegister().lastRegister?.text) |     assertEquals("one", VimPlugin.getRegister().lastRegister?.text) | ||||||
| @@ -197,7 +197,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | |||||||
|     configureByText(text) |     configureByText(text) | ||||||
|     val vimEditor = fixture.editor.vim |     val vimEditor = fixture.editor.vim | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "one", SelectionType.CHARACTER_WISE, false) |       .storeText(vimEditor, text rangeOf "one", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("griw" + "w" + ".")) |     typeText(injector.parser.parseKeys("griw" + "w" + ".")) | ||||||
|     assertState("one one on${c}e four") |     assertState("one one on${c}e four") | ||||||
|     assertEquals("one", VimPlugin.getRegister().lastRegister?.text) |     assertEquals("one", VimPlugin.getRegister().lastRegister?.text) | ||||||
| @@ -247,7 +247,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | |||||||
|     configureByText(text) |     configureByText(text) | ||||||
|     val vimEditor = fixture.editor.vim |     val vimEditor = fixture.editor.vim | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "legendary", SelectionType.CHARACTER_WISE, false) |       .storeText(vimEditor, text rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("grr")) |     typeText(injector.parser.parseKeys("grr")) | ||||||
|     assertState( |     assertState( | ||||||
|       """ |       """ | ||||||
| @@ -414,7 +414,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | |||||||
|     configureByText(text) |     configureByText(text) | ||||||
|     val vimEditor = fixture.editor.vim |     val vimEditor = fixture.editor.vim | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "legendary", SelectionType.CHARACTER_WISE, false) |       .storeText(vimEditor, text rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("viw" + "gr")) |     typeText(injector.parser.parseKeys("viw" + "gr")) | ||||||
|     assertState( |     assertState( | ||||||
|       """ |       """ | ||||||
| @@ -485,7 +485,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | |||||||
|     configureByText(text) |     configureByText(text) | ||||||
|     val vimEditor = fixture.editor.vim |     val vimEditor = fixture.editor.vim | ||||||
|     VimPlugin.getRegister() |     VimPlugin.getRegister() | ||||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "legendary", SelectionType.CHARACTER_WISE, false) |       .storeText(vimEditor, text rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||||
|     typeText(injector.parser.parseKeys("V" + "gr")) |     typeText(injector.parser.parseKeys("V" + "gr")) | ||||||
|     assertState( |     assertState( | ||||||
|       """ |       """ | ||||||
|   | |||||||
| @@ -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]) | ||||||
| class RedoAction : VimActionHandler.SingleExecution() { | 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]) | ||||||
| class UndoAction : VimActionHandler.SingleExecution() { | 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.VimChangeGroup | import com.maddyhome.idea.vim.api.VimChangeGroup | ||||||
| @@ -23,7 +22,7 @@ import com.maddyhome.idea.vim.helper.CharacterHelper | |||||||
| /** | /** | ||||||
|  * @author vlan |  * @author vlan | ||||||
|  */ |  */ | ||||||
| @CommandOrMotion(keys = ["u"], modes = [Mode.VISUAL]) | @CommandOrMotion(keys = [], modes = []) | ||||||
| class ChangeCaseLowerVisualAction : VisualOperatorActionHandler.ForEachCaret() { | 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.VimChangeGroup | import com.maddyhome.idea.vim.api.VimChangeGroup | ||||||
| @@ -23,7 +22,7 @@ import com.maddyhome.idea.vim.helper.CharacterHelper | |||||||
| /** | /** | ||||||
|  * @author vlan |  * @author vlan | ||||||
|  */ |  */ | ||||||
| @CommandOrMotion(keys = ["U"], modes = [Mode.VISUAL]) | @CommandOrMotion(keys = [], modes = []) | ||||||
| class ChangeCaseUpperVisualAction : VisualOperatorActionHandler.ForEachCaret() { | class ChangeCaseUpperVisualAction : VisualOperatorActionHandler.ForEachCaret() { | ||||||
|   override val type: Command.Type = Command.Type.CHANGE |   override val type: Command.Type = Command.Type.CHANGE | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,7 +10,6 @@ package com.maddyhome.idea.vim.action.copy | |||||||
| import com.intellij.vim.annotations.CommandOrMotion | import com.intellij.vim.annotations.CommandOrMotion | ||||||
| import com.intellij.vim.annotations.Mode | 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.ImmutableVimCaret |  | ||||||
| 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 | ||||||
| import com.maddyhome.idea.vim.command.Argument | import com.maddyhome.idea.vim.command.Argument | ||||||
| @@ -36,7 +35,15 @@ sealed class PutTextBaseAction( | |||||||
|     val count = operatorArguments.count1 |     val count = operatorArguments.count1 | ||||||
|     val sortedCarets = editor.sortedCarets() |     val sortedCarets = editor.sortedCarets() | ||||||
|     return if (sortedCarets.size > 1) { |     return if (sortedCarets.size > 1) { | ||||||
|       val caretToPutData = sortedCarets.associateWith { getPutDataForCaret(it, count) } |       val putData = getPutData(count) | ||||||
|  |  | ||||||
|  |       val splitText = putData.textData?.rawText?.split('\n')?.dropLastWhile(String::isEmpty) | ||||||
|  |       val caretToPutData = if (splitText != null && splitText.size == sortedCarets.size) { | ||||||
|  |         sortedCarets.mapIndexed { index, caret -> caret to putData.copy(textData = putData.textData.copy(rawText = splitText[splitText.lastIndex - index])) }.toMap() | ||||||
|  |       } else { | ||||||
|  |         sortedCarets.associateWith { putData } | ||||||
|  |       } | ||||||
|  |        | ||||||
|       var result = true |       var result = true | ||||||
|       injector.application.runWriteAction { |       injector.application.runWriteAction { | ||||||
|         caretToPutData.forEach { |         caretToPutData.forEach { | ||||||
| @@ -45,28 +52,24 @@ sealed class PutTextBaseAction( | |||||||
|       } |       } | ||||||
|       result |       result | ||||||
|     } else { |     } else { | ||||||
|       val putData = getPutDataForCaret(sortedCarets.single(), count) |       injector.put.putText(editor, context, getPutData(count), operatorArguments) | ||||||
|       injector.put.putText(editor, context, putData, operatorArguments) |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private fun getPutDataForCaret(caret: ImmutableVimCaret, count: Int): PutData { |   private fun getPutData(count: Int): PutData { | ||||||
|     val registerService = injector.registerGroup |     return PutData(getRegisterTextData(), null, count, insertTextBeforeCaret, indent, caretAfterInsertedText, -1) | ||||||
|     val registerChar = if (caret.editor.carets().size == 1) { |   } | ||||||
|       registerService.currentRegister | } | ||||||
|     } else { |  | ||||||
|       registerService.getCurrentRegisterForMulticaret() | fun getRegisterTextData(): TextData? { | ||||||
|     } |   val register = injector.registerGroup.getRegister(injector.registerGroup.currentRegister) | ||||||
|     val register = caret.registerStorage.getRegister(registerChar) |   return register?.let { | ||||||
|     val textData = register?.let { |     TextData( | ||||||
|       TextData( |       register.text ?: injector.parser.toPrintableString(register.keys), | ||||||
|         register.text ?: injector.parser.toPrintableString(register.keys), |       register.type, | ||||||
|         register.type, |       register.transferableData, | ||||||
|         register.transferableData, |       register.name, | ||||||
|         register.name, |     ) | ||||||
|       ) |  | ||||||
|     } |  | ||||||
|     return PutData(textData, null, count, insertTextBeforeCaret, indent, caretAfterInsertedText, -1) |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -41,7 +41,21 @@ sealed class PutVisualTextBaseAction( | |||||||
|   ): Boolean { |   ): Boolean { | ||||||
|     if (caretsAndSelections.isEmpty()) return false |     if (caretsAndSelections.isEmpty()) return false | ||||||
|     val count = cmd.count |     val count = cmd.count | ||||||
|     val caretToPutData = editor.sortedCarets().associateWith { getPutDataForCaret(it, caretsAndSelections[it], count) } |     val sortedCarets = editor.sortedCarets() | ||||||
|  |  | ||||||
|  |     val textData = getRegisterTextData() | ||||||
|  |     val splitText = textData?.rawText?.split('\n')?.dropLastWhile(String::isEmpty) | ||||||
|  |  | ||||||
|  |     val caretToTextData = if (splitText != null && splitText.size == sortedCarets.size) { | ||||||
|  |       sortedCarets.mapIndexed { index, caret -> caret to textData.copy(rawText = splitText[splitText.lastIndex - index]) }.toMap() | ||||||
|  |     } else { | ||||||
|  |       sortedCarets.associateWith { textData } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     val caretToPutData = caretToTextData.mapValues { (caret, textData) -> | ||||||
|  |       getPutDataForCaret(textData, caret, caretsAndSelections[caret], count) | ||||||
|  |     } | ||||||
|  |      | ||||||
|     injector.registerGroup.resetRegister() |     injector.registerGroup.resetRegister() | ||||||
|     var result = true |     var result = true | ||||||
|     injector.application.runWriteAction { |     injector.application.runWriteAction { | ||||||
| @@ -51,18 +65,8 @@ sealed class PutVisualTextBaseAction( | |||||||
|     } |     } | ||||||
|     return result |     return result | ||||||
|   } |   } | ||||||
|  |    | ||||||
|   private fun getPutDataForCaret(caret: VimCaret, selection: VimSelection?, count: Int): PutData { |   private fun getPutDataForCaret(textData: PutData.TextData?, caret: VimCaret, selection: VimSelection?, count: Int): PutData { | ||||||
|     val lastRegisterChar = injector.registerGroup.lastRegisterChar |  | ||||||
|     val register = caret.registerStorage.getRegister(lastRegisterChar) |  | ||||||
|     val textData = register?.let { |  | ||||||
|       PutData.TextData( |  | ||||||
|         register.text ?: injector.parser.toPrintableString(register.keys), |  | ||||||
|         register.type, |  | ||||||
|         register.transferableData, |  | ||||||
|         register.name, |  | ||||||
|       ) |  | ||||||
|     } |  | ||||||
|     val visualSelection = selection?.let { PutData.VisualSelection(mapOf(caret to it), it.type) } |     val visualSelection = selection?.let { PutData.VisualSelection(mapOf(caret to it), it.type) } | ||||||
|     return PutData(textData, visualSelection, count, insertTextBeforeCaret, indent, caretAfterInsertedText) |     return PutData(textData, visualSelection, count, insertTextBeforeCaret, indent, caretAfterInsertedText) | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -68,8 +68,7 @@ class ProcessSearchEntryAction(private val parentAction: ProcessExEntryAction) : | |||||||
|       '?' -> injector.searchGroup.processSearchCommand(editor, argument.string, caret.offset, operatorArguments.count1, Direction.BACKWARDS) |       '?' -> injector.searchGroup.processSearchCommand(editor, argument.string, caret.offset, operatorArguments.count1, Direction.BACKWARDS) | ||||||
|       else -> throw ExException("Unexpected search label ${argument.character}") |       else -> throw ExException("Unexpected search label ${argument.character}") | ||||||
|     } |     } | ||||||
|     // Vim doesn't treat not finding something as an error, although it might report either an error or warning message |     if (offsetAndMotion == null) return Motion.Error | ||||||
|     if (offsetAndMotion == null) return Motion.NoMotion |  | ||||||
|     parentAction.motionType = offsetAndMotion.second |     parentAction.motionType = offsetAndMotion.second | ||||||
|     return offsetAndMotion.first.toMotionOrError() |     return offsetAndMotion.first.toMotionOrError() | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -82,6 +82,13 @@ 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(argument.character.let { if (it == '.') "\\." else it.toString() }, offset, direction) | ||||||
|  |      | ||||||
|     return res.toMotionOrError() |     return res.toMotionOrError() | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,20 +9,16 @@ | |||||||
| package com.maddyhome.idea.vim.api | package com.maddyhome.idea.vim.api | ||||||
|  |  | ||||||
| import com.maddyhome.idea.vim.common.LiveRange | import com.maddyhome.idea.vim.common.LiveRange | ||||||
| import com.maddyhome.idea.vim.common.TextRange |  | ||||||
| import com.maddyhome.idea.vim.group.visual.VisualChange | import com.maddyhome.idea.vim.group.visual.VisualChange | ||||||
| import com.maddyhome.idea.vim.group.visual.vimMoveBlockSelectionToOffset | import com.maddyhome.idea.vim.group.visual.vimMoveBlockSelectionToOffset | ||||||
| import com.maddyhome.idea.vim.group.visual.vimMoveSelectionToCaret | import com.maddyhome.idea.vim.group.visual.vimMoveSelectionToCaret | ||||||
| import com.maddyhome.idea.vim.handler.Motion | import com.maddyhome.idea.vim.handler.Motion | ||||||
| import com.maddyhome.idea.vim.helper.StrictMode | import com.maddyhome.idea.vim.helper.StrictMode | ||||||
| import com.maddyhome.idea.vim.helper.exitVisualMode | import com.maddyhome.idea.vim.helper.exitVisualMode | ||||||
| import com.maddyhome.idea.vim.register.Register |  | ||||||
| import com.maddyhome.idea.vim.state.mode.SelectionType |  | ||||||
| import com.maddyhome.idea.vim.state.mode.inBlockSelection | import com.maddyhome.idea.vim.state.mode.inBlockSelection | ||||||
| import com.maddyhome.idea.vim.state.mode.inCommandLineMode | import com.maddyhome.idea.vim.state.mode.inCommandLineMode | ||||||
| import com.maddyhome.idea.vim.state.mode.inSelectMode | import com.maddyhome.idea.vim.state.mode.inSelectMode | ||||||
| import com.maddyhome.idea.vim.state.mode.inVisualMode | import com.maddyhome.idea.vim.state.mode.inVisualMode | ||||||
| import javax.swing.KeyStroke |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Immutable interface of the caret. Immutable caret is an important concept of Fleet. |  * Immutable interface of the caret. Immutable caret is an important concept of Fleet. | ||||||
| @@ -61,7 +57,6 @@ interface ImmutableVimCaret { | |||||||
|   fun hasSelection(): Boolean |   fun hasSelection(): Boolean | ||||||
|  |  | ||||||
|   var lastSelectionInfo: SelectionInfo |   var lastSelectionInfo: SelectionInfo | ||||||
|   val registerStorage: CaretRegisterStorage |  | ||||||
|   val markStorage: LocalMarkStorage |   val markStorage: LocalMarkStorage | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -147,21 +142,3 @@ fun VimCaret.moveToMotion(motion: Motion): VimCaret { | |||||||
|     this |     this | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| interface CaretRegisterStorage { |  | ||||||
|   val caret: ImmutableVimCaret |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Stores text to caret's recordable (named/numbered/unnamed) register |  | ||||||
|    */ |  | ||||||
|   fun storeText(editor: VimEditor, range: TextRange, type: SelectionType, isDelete: Boolean): Boolean |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Gets text from caret's recordable register |  | ||||||
|    * If the register is not recordable - global text state will be returned |  | ||||||
|    */ |  | ||||||
|   fun getRegister(r: Char): Register? |  | ||||||
|  |  | ||||||
|   fun setKeys(register: Char, keys: List<KeyStroke>) |  | ||||||
|   fun saveRegister(r: Char, register: Register) |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -8,75 +8,4 @@ | |||||||
|  |  | ||||||
| package com.maddyhome.idea.vim.api | package com.maddyhome.idea.vim.api | ||||||
|  |  | ||||||
| import com.maddyhome.idea.vim.common.TextRange |  | ||||||
| import com.maddyhome.idea.vim.register.Register |  | ||||||
| import com.maddyhome.idea.vim.register.RegisterConstants |  | ||||||
| import com.maddyhome.idea.vim.register.VimRegisterGroupBase |  | ||||||
| import com.maddyhome.idea.vim.state.mode.SelectionType |  | ||||||
| import javax.swing.KeyStroke |  | ||||||
|  |  | ||||||
| abstract class VimCaretBase : VimCaret | abstract class VimCaretBase : VimCaret | ||||||
|  |  | ||||||
| open class CaretRegisterStorageBase(override var caret: ImmutableVimCaret) : CaretRegisterStorage, VimRegisterGroupBase() { |  | ||||||
|   companion object { |  | ||||||
|     private const val ALLOWED_TO_STORE_REGISTERS = RegisterConstants.RECORDABLE_REGISTERS + |  | ||||||
|       RegisterConstants.SMALL_DELETION_REGISTER + |  | ||||||
|       RegisterConstants.BLACK_HOLE_REGISTER + |  | ||||||
|       RegisterConstants.LAST_INSERTED_TEXT_REGISTER + |  | ||||||
|       RegisterConstants.LAST_SEARCH_REGISTER |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   override var lastRegisterChar: Char |  | ||||||
|     get() { |  | ||||||
|       return injector.registerGroup.lastRegisterChar |  | ||||||
|     } |  | ||||||
|     set(_) {} |  | ||||||
|  |  | ||||||
|   override var isRegisterSpecifiedExplicitly: Boolean |  | ||||||
|     get() { |  | ||||||
|       return injector.registerGroup.isRegisterSpecifiedExplicitly |  | ||||||
|     } |  | ||||||
|     set(_) {} |  | ||||||
|  |  | ||||||
|   override fun storeText(editor: VimEditor, range: TextRange, type: SelectionType, isDelete: Boolean): Boolean { |  | ||||||
|     val registerChar = if (caret.editor.carets().size == 1) currentRegister else getCurrentRegisterForMulticaret() |  | ||||||
|     if (caret.isPrimary) { |  | ||||||
|       val registerService = injector.registerGroup |  | ||||||
|       registerService.lastRegisterChar = registerChar |  | ||||||
|       return registerService.storeText(editor, caret, range, type, isDelete) |  | ||||||
|     } else { |  | ||||||
|       if (!ALLOWED_TO_STORE_REGISTERS.contains(registerChar)) { |  | ||||||
|         return false |  | ||||||
|       } |  | ||||||
|       val text = preprocessTextBeforeStoring(editor.getText(range), type) |  | ||||||
|       return storeTextInternal(editor, caret, range, text, type, registerChar, isDelete) |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   override fun getRegister(r: Char): Register? { |  | ||||||
|     if (caret.isPrimary || !RegisterConstants.RECORDABLE_REGISTERS.contains(r)) { |  | ||||||
|       return injector.registerGroup.getRegister(r) |  | ||||||
|     } |  | ||||||
|     return super.getRegister(r) ?: injector.registerGroup.getRegister(r) |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   override fun setKeys(register: Char, keys: List<KeyStroke>) { |  | ||||||
|     if (caret.isPrimary) { |  | ||||||
|       injector.registerGroup.setKeys(register, keys) |  | ||||||
|     } |  | ||||||
|     if (!RegisterConstants.RECORDABLE_REGISTERS.contains(register)) { |  | ||||||
|       return |  | ||||||
|     } |  | ||||||
|     return super.setKeys(register, keys) |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   override fun saveRegister(r: Char, register: Register) { |  | ||||||
|     if (caret.isPrimary) { |  | ||||||
|       injector.registerGroup.saveRegister(r, register) |  | ||||||
|     } |  | ||||||
|     if (!RegisterConstants.RECORDABLE_REGISTERS.contains(r)) { |  | ||||||
|       return |  | ||||||
|     } |  | ||||||
|     return super.saveRegister(r, register) |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -144,7 +144,7 @@ interface VimChangeGroup { | |||||||
|     operatorArguments: OperatorArguments, |     operatorArguments: OperatorArguments, | ||||||
|   ) |   ) | ||||||
|  |  | ||||||
|   fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: String): VimCaret |   fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: CharSequence): VimCaret | ||||||
|  |  | ||||||
|   fun insertText(editor: VimEditor, caret: VimCaret, str: String): VimCaret |   fun insertText(editor: VimEditor, caret: VimCaret, str: String): VimCaret | ||||||
|  |  | ||||||
|   | |||||||
| @@ -180,7 +180,7 @@ abstract class VimChangeGroupBase : VimChangeGroup { | |||||||
|     if (type == null || |     if (type == null || | ||||||
|       (mode == Mode.INSERT || mode == Mode.REPLACE) || |       (mode == Mode.INSERT || mode == Mode.REPLACE) || | ||||||
|       !saveToRegister || |       !saveToRegister || | ||||||
|       caret.registerStorage.storeText(editor, updatedRange, type, true) |       injector.registerGroup.storeText(editor, updatedRange, type, true, !editor.isFirstCaret, editor.isReversingCarets) | ||||||
|     ) { |     ) { | ||||||
|       val startOffsets = updatedRange.startOffsets |       val startOffsets = updatedRange.startOffsets | ||||||
|       val endOffsets = updatedRange.endOffsets |       val endOffsets = updatedRange.endOffsets | ||||||
| @@ -209,7 +209,7 @@ 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(caret, offset, str) |     (editor as MutableVimEditor).insertText(caret, offset, str) | ||||||
|     val newCaret = caret.moveToInlayAwareOffset(offset + str.length) |     val newCaret = caret.moveToInlayAwareOffset(offset + str.length) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -172,7 +172,8 @@ interface VimEditor { | |||||||
|    * This method should perform caret merging after the operations. This is similar to IJ runForEachCaret |    * This method should perform caret merging after the operations. This is similar to IJ runForEachCaret | ||||||
|    * TODO review |    * TODO review | ||||||
|    */ |    */ | ||||||
|  |   val isFirstCaret: Boolean | ||||||
|  |   val isReversingCarets: Boolean | ||||||
|   fun forEachCaret(action: (VimCaret) -> Unit) |   fun forEachCaret(action: (VimCaret) -> Unit) | ||||||
|   fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean = false) |   fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean = false) | ||||||
|   fun isInForEachCaretScope(): Boolean |   fun isInForEachCaretScope(): Boolean | ||||||
| @@ -350,4 +351,4 @@ interface VimFoldRegion { | |||||||
|   var isExpanded: Boolean |   var isExpanded: Boolean | ||||||
|   val startOffset: Int |   val startOffset: Int | ||||||
|   val endOffset: Int |   val endOffset: Int | ||||||
| } | } | ||||||
|   | |||||||
| @@ -25,7 +25,6 @@ interface VimSearchGroup { | |||||||
|    * Last used pattern to perform a substitution. |    * Last used pattern to perform a substitution. | ||||||
|    */ |    */ | ||||||
|   var lastSubstitutePattern: String? |   var lastSubstitutePattern: String? | ||||||
|  |  | ||||||
|   fun searchBackward(editor: VimEditor, offset: Int, count: Int): TextRange? |   fun searchBackward(editor: VimEditor, offset: Int, count: Int): TextRange? | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
| @@ -195,4 +194,17 @@ interface VimSearchGroup { | |||||||
|    * Gets the direction lastly used in a search. |    * Gets the direction lastly used in a search. | ||||||
|    */ |    */ | ||||||
|   fun getLastSearchDirection(): Direction |   fun getLastSearchDirection(): Direction | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Sets the last search state purely for tests | ||||||
|  |    * | ||||||
|  |    * @param pattern         The pattern to save. This is the last search pattern, not the last substitute pattern | ||||||
|  |    * @param patternOffset   The pattern offset, e.g. `/{pattern}/{offset}` | ||||||
|  |    * @param direction       The direction to search | ||||||
|  |    */ | ||||||
|  |   fun setLastSearchState( | ||||||
|  |     pattern: String, | ||||||
|  |     patternOffset: String, | ||||||
|  |     direction: Direction, | ||||||
|  |   ) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1369,8 +1369,7 @@ abstract class VimSearchGroupBase : VimSearchGroup { | |||||||
|    * @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 fun setLastSearchState( | ||||||
|   fun setLastSearchState( |  | ||||||
|     pattern: String, |     pattern: String, | ||||||
|     patternOffset: String, |     patternOffset: String, | ||||||
|     direction: Direction, |     direction: Direction, | ||||||
|   | |||||||
| @@ -8,8 +8,6 @@ | |||||||
|  |  | ||||||
| package com.maddyhome.idea.vim.common | package com.maddyhome.idea.vim.common | ||||||
|  |  | ||||||
| import com.maddyhome.idea.vim.api.ImmutableVimCaret |  | ||||||
| import com.maddyhome.idea.vim.api.VimCaret |  | ||||||
| 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 | ||||||
| import com.maddyhome.idea.vim.state.mode.Mode | import com.maddyhome.idea.vim.state.mode.Mode | ||||||
| @@ -73,9 +71,9 @@ class VimListenersNotifier { | |||||||
|     isReplaceCharListeners.forEach { it.isReplaceCharChanged(editor) } |     isReplaceCharListeners.forEach { it.isReplaceCharChanged(editor) } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   fun notifyYankPerformed(caretToRange: Map<ImmutableVimCaret, TextRange>) { |   fun notifyYankPerformed(editor: VimEditor, range: TextRange) { | ||||||
|     if (!injector.enabler.isEnabled()) return // we remove all the listeners when turning the plugin off, but let's do it just in case |     if (!injector.enabler.isEnabled()) return // we remove all the listeners when turning the plugin off, but let's do it just in case | ||||||
|     yankListeners.forEach { it.yankPerformed(caretToRange) } |     yankListeners.forEach { it.yankPerformed(editor, range) } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   fun reset() { |   fun reset() { | ||||||
| @@ -85,4 +83,4 @@ class VimListenersNotifier { | |||||||
|     vimPluginListeners.clear() |     vimPluginListeners.clear() | ||||||
|     isReplaceCharListeners.clear() |     isReplaceCharListeners.clear() | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,8 +8,8 @@ | |||||||
|  |  | ||||||
| package com.maddyhome.idea.vim.common | package com.maddyhome.idea.vim.common | ||||||
|  |  | ||||||
| import com.maddyhome.idea.vim.api.ImmutableVimCaret | import com.maddyhome.idea.vim.api.VimEditor | ||||||
|  |  | ||||||
| interface VimYankListener { | interface VimYankListener { | ||||||
|   fun yankPerformed(caretToRange: Map<ImmutableVimCaret, TextRange>) |   fun yankPerformed(editor: VimEditor, range: TextRange) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -255,7 +255,11 @@ class ToActionMappingInfo( | |||||||
|  |  | ||||||
|   override fun execute(editor: VimEditor, context: ExecutionContext, keyState: KeyHandlerState) { |   override fun execute(editor: VimEditor, context: ExecutionContext, keyState: KeyHandlerState) { | ||||||
|     LOG.debug("Executing 'ToAction' mapping...") |     LOG.debug("Executing 'ToAction' mapping...") | ||||||
|     injector.actionExecutor.executeAction(editor, name = action, context = context) |     val commandBuilder = KeyHandler.getInstance().keyHandlerState.commandBuilder | ||||||
|  |     for (i in 0 until commandBuilder.count.coerceAtLeast(1)) { | ||||||
|  |       injector.actionExecutor.executeAction(editor, name = action, context = context) | ||||||
|  |     } | ||||||
|  |     commandBuilder.resetCount() | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   companion object { |   companion object { | ||||||
|   | |||||||
| @@ -624,7 +624,13 @@ class VimRegex(pattern: String) { | |||||||
|     override fun carets(): List<VimCaret> = emptyList() |     override fun carets(): List<VimCaret> = emptyList() | ||||||
|  |  | ||||||
|     override fun nativeCarets(): List<VimCaret> = emptyList() |     override fun nativeCarets(): List<VimCaret> = emptyList() | ||||||
|  |      | ||||||
|  |     override val isFirstCaret: Boolean | ||||||
|  |       get() = false | ||||||
|  |  | ||||||
|  |     override val isReversingCarets: Boolean | ||||||
|  |       get() = false | ||||||
|  |      | ||||||
|     override fun forEachCaret(action: (VimCaret) -> Unit) {} |     override fun forEachCaret(action: (VimCaret) -> Unit) {} | ||||||
|  |  | ||||||
|     override fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean) {} |     override fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean) {} | ||||||
|   | |||||||
| @@ -75,6 +75,11 @@ class Register { | |||||||
|     addKeys(injector.parser.stringToKeys(text)) |     addKeys(injector.parser.stringToKeys(text)) | ||||||
|     transferableData.clear() |     transferableData.clear() | ||||||
|   } |   } | ||||||
|  |    | ||||||
|  |   fun prependTextAndResetTransferableData(text: String) { | ||||||
|  |     this.keys.addAll(0, injector.parser.stringToKeys(text)) | ||||||
|  |     transferableData.clear() | ||||||
|  |   } | ||||||
|  |  | ||||||
|   fun addKeys(keys: List<KeyStroke>) { |   fun addKeys(keys: List<KeyStroke>) { | ||||||
|     this.keys.addAll(keys) |     this.keys.addAll(keys) | ||||||
|   | |||||||
| @@ -7,10 +7,9 @@ | |||||||
|  */ |  */ | ||||||
| package com.maddyhome.idea.vim.register | package com.maddyhome.idea.vim.register | ||||||
|  |  | ||||||
| import com.maddyhome.idea.vim.api.ImmutableVimCaret |  | ||||||
| import com.maddyhome.idea.vim.api.VimEditor | import com.maddyhome.idea.vim.api.VimEditor | ||||||
| import com.maddyhome.idea.vim.state.mode.SelectionType |  | ||||||
| import com.maddyhome.idea.vim.common.TextRange | import com.maddyhome.idea.vim.common.TextRange | ||||||
|  | import com.maddyhome.idea.vim.state.mode.SelectionType | ||||||
| import org.jetbrains.annotations.TestOnly | import org.jetbrains.annotations.TestOnly | ||||||
| import javax.swing.KeyStroke | import javax.swing.KeyStroke | ||||||
|  |  | ||||||
| @@ -48,10 +47,11 @@ interface VimRegisterGroup { | |||||||
|   /** Store text into the last register. */ |   /** Store text into the last register. */ | ||||||
|   fun storeText( |   fun storeText( | ||||||
|     editor: VimEditor, |     editor: VimEditor, | ||||||
|     caret: ImmutableVimCaret, |  | ||||||
|     range: TextRange, |     range: TextRange, | ||||||
|     type: SelectionType, |     type: SelectionType, | ||||||
|     isDelete: Boolean, |     isDelete: Boolean, | ||||||
|  |     forceAppend: Boolean = false, | ||||||
|  |     prependInsteadOfAppend: Boolean = false | ||||||
|   ): Boolean |   ): Boolean | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|   | |||||||
| @@ -8,7 +8,6 @@ | |||||||
|  |  | ||||||
| package com.maddyhome.idea.vim.register | package com.maddyhome.idea.vim.register | ||||||
|  |  | ||||||
| import com.maddyhome.idea.vim.api.ImmutableVimCaret |  | ||||||
| import com.maddyhome.idea.vim.api.Options | import com.maddyhome.idea.vim.api.Options | ||||||
| import com.maddyhome.idea.vim.api.VimEditor | import com.maddyhome.idea.vim.api.VimEditor | ||||||
| import com.maddyhome.idea.vim.api.getText | import com.maddyhome.idea.vim.api.getText | ||||||
| @@ -171,12 +170,13 @@ abstract class VimRegisterGroupBase : VimRegisterGroup { | |||||||
|  |  | ||||||
|   fun storeTextInternal( |   fun storeTextInternal( | ||||||
|     editor: VimEditor, |     editor: VimEditor, | ||||||
|     caret: ImmutableVimCaret, |  | ||||||
|     range: TextRange, |     range: TextRange, | ||||||
|     text: String, |     text: String, | ||||||
|     type: SelectionType, |     type: SelectionType, | ||||||
|     register: Char, |     register: Char, | ||||||
|     isDelete: Boolean, |     isDelete: Boolean, | ||||||
|  |     forceAppend: Boolean, | ||||||
|  |     prependInsteadOfAppend: Boolean, | ||||||
|   ): Boolean { |   ): Boolean { | ||||||
|     // Null register doesn't get saved, but acts like it was |     // Null register doesn't get saved, but acts like it was | ||||||
|     if (lastRegisterChar == BLACK_HOLE_REGISTER) return true |     if (lastRegisterChar == BLACK_HOLE_REGISTER) return true | ||||||
| @@ -198,18 +198,29 @@ abstract class VimRegisterGroupBase : VimRegisterGroup { | |||||||
|     // If this is an uppercase register, we need to append the text to the corresponding lowercase register |     // If this is an uppercase register, we need to append the text to the corresponding lowercase register | ||||||
|     val transferableData: List<Any> = |     val transferableData: List<Any> = | ||||||
|       if (start != -1) injector.clipboardManager.getTransferableData(editor, range, text) else ArrayList() |       if (start != -1) injector.clipboardManager.getTransferableData(editor, range, text) else ArrayList() | ||||||
|     val processedText = |     var processedText = | ||||||
|       if (start != -1) injector.clipboardManager.preprocessText(editor, range, text, transferableData) else text |       if (start != -1) injector.clipboardManager.preprocessText(editor, range, text, transferableData) else text | ||||||
|     logger.debug { |     logger.debug { | ||||||
|       val transferableClasses = transferableData.joinToString(",") { it.javaClass.name } |       val transferableClasses = transferableData.joinToString(",") { it.javaClass.name } | ||||||
|       "Copy to '$lastRegister' with transferable data: $transferableClasses" |       "Copy to '$lastRegister' with transferable data: $transferableClasses" | ||||||
|     } |     } | ||||||
|     if (Character.isUpperCase(register)) { |     if (Character.isUpperCase(register) || forceAppend) { | ||||||
|  |       if (forceAppend && type == SelectionType.CHARACTER_WISE) { | ||||||
|  |         processedText = if (prependInsteadOfAppend) | ||||||
|  |           processedText + '\n' | ||||||
|  |         else | ||||||
|  |           '\n' + processedText | ||||||
|  |       } | ||||||
|       val lreg = Character.toLowerCase(register) |       val lreg = Character.toLowerCase(register) | ||||||
|       val r = myRegisters[lreg] |       val r = myRegisters[lreg] | ||||||
|       // Append the text if the lowercase register existed |       // Append the text if the lowercase register existed | ||||||
|       if (r != null) { |       if (r != null) { | ||||||
|         r.addTextAndResetTransferableData(processedText) |         if (prependInsteadOfAppend) { | ||||||
|  |           r.prependTextAndResetTransferableData(processedText) | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |           r.addTextAndResetTransferableData(processedText) | ||||||
|  |         } | ||||||
|       } else { |       } else { | ||||||
|         myRegisters[lreg] = Register(lreg, type, processedText, ArrayList(transferableData)) |         myRegisters[lreg] = Register(lreg, type, processedText, ArrayList(transferableData)) | ||||||
|         logger.debug { "register '$register' contains: \"$processedText\"" } |         logger.debug { "register '$register' contains: \"$processedText\"" } | ||||||
| @@ -290,14 +301,15 @@ abstract class VimRegisterGroupBase : VimRegisterGroup { | |||||||
|    */ |    */ | ||||||
|   override fun storeText( |   override fun storeText( | ||||||
|     editor: VimEditor, |     editor: VimEditor, | ||||||
|     caret: ImmutableVimCaret, |  | ||||||
|     range: TextRange, |     range: TextRange, | ||||||
|     type: SelectionType, |     type: SelectionType, | ||||||
|     isDelete: Boolean, |     isDelete: Boolean, | ||||||
|  |     forceAppend: Boolean, | ||||||
|  |     prependInsteadOfAppend: Boolean | ||||||
|   ): Boolean { |   ): Boolean { | ||||||
|     if (isRegisterWritable()) { |     if (isRegisterWritable()) { | ||||||
|       val text = preprocessTextBeforeStoring(editor.getText(range), type) |       val text = preprocessTextBeforeStoring(editor.getText(range), type) | ||||||
|       return storeTextInternal(editor, caret, range, text, type, lastRegisterChar, isDelete) |       return storeTextInternal(editor, range, text, type, lastRegisterChar, isDelete, forceAppend, prependInsteadOfAppend) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return false |     return false | ||||||
|   | |||||||
| @@ -10,7 +10,6 @@ package com.maddyhome.idea.vim.yank | |||||||
|  |  | ||||||
| import com.maddyhome.idea.vim.action.motion.updown.MotionDownLess1FirstNonSpaceAction | import com.maddyhome.idea.vim.action.motion.updown.MotionDownLess1FirstNonSpaceAction | ||||||
| import com.maddyhome.idea.vim.api.ExecutionContext | import com.maddyhome.idea.vim.api.ExecutionContext | ||||||
| import com.maddyhome.idea.vim.api.ImmutableVimCaret |  | ||||||
| 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 | ||||||
| import com.maddyhome.idea.vim.api.getLineEndForOffset | import com.maddyhome.idea.vim.api.getLineEndForOffset | ||||||
| @@ -18,15 +17,14 @@ import com.maddyhome.idea.vim.api.getLineStartForOffset | |||||||
| import com.maddyhome.idea.vim.api.injector | import com.maddyhome.idea.vim.api.injector | ||||||
| import com.maddyhome.idea.vim.command.Argument | import com.maddyhome.idea.vim.command.Argument | ||||||
| import com.maddyhome.idea.vim.command.OperatorArguments | import com.maddyhome.idea.vim.command.OperatorArguments | ||||||
| import com.maddyhome.idea.vim.state.mode.SelectionType |  | ||||||
| import com.maddyhome.idea.vim.common.TextRange | import com.maddyhome.idea.vim.common.TextRange | ||||||
|  | import com.maddyhome.idea.vim.state.mode.SelectionType | ||||||
| import org.jetbrains.annotations.Contract | import org.jetbrains.annotations.Contract | ||||||
| import kotlin.math.min | import kotlin.math.min | ||||||
|  |  | ||||||
| open class YankGroupBase : VimYankGroup { | open class YankGroupBase : VimYankGroup { | ||||||
|   private fun yankRange( |   private fun yankRange( | ||||||
|     editor: VimEditor, |     editor: VimEditor, | ||||||
|     caretToRange: Map<ImmutableVimCaret, TextRange>, |  | ||||||
|     range: TextRange, |     range: TextRange, | ||||||
|     type: SelectionType, |     type: SelectionType, | ||||||
|     startOffsets: Map<VimCaret, Int>?, |     startOffsets: Map<VimCaret, Int>?, | ||||||
| @@ -35,13 +33,8 @@ open class YankGroupBase : VimYankGroup { | |||||||
|       caret.moveToOffset(offset) |       caret.moveToOffset(offset) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     injector.listenersNotifier.notifyYankPerformed(caretToRange) |     injector.listenersNotifier.notifyYankPerformed(editor, range) | ||||||
|  |     return injector.registerGroup.storeText(editor, range, type, false) | ||||||
|     var result = true |  | ||||||
|     for ((caret, myRange) in caretToRange) { |  | ||||||
|       result = caret.registerStorage.storeText(editor, myRange, type, false) && result |  | ||||||
|     } |  | ||||||
|     return result |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Contract("_, _ -> new") |   @Contract("_, _ -> new") | ||||||
| @@ -86,7 +79,6 @@ open class YankGroupBase : VimYankGroup { | |||||||
|     val nativeCaretCount = editor.nativeCarets().size |     val nativeCaretCount = editor.nativeCarets().size | ||||||
|     if (nativeCaretCount <= 0) return false |     if (nativeCaretCount <= 0) return false | ||||||
|  |  | ||||||
|     val caretToRange = HashMap<ImmutableVimCaret, TextRange>(nativeCaretCount) |  | ||||||
|     val ranges = ArrayList<Pair<Int, Int>>(nativeCaretCount) |     val ranges = ArrayList<Pair<Int, Int>>(nativeCaretCount) | ||||||
|  |  | ||||||
|     // This logic is from original vim |     // This logic is from original vim | ||||||
| @@ -99,7 +91,6 @@ open class YankGroupBase : VimYankGroup { | |||||||
|       assert(motionRange.size() == 1) |       assert(motionRange.size() == 1) | ||||||
|       ranges.add(motionRange.startOffset to motionRange.endOffset) |       ranges.add(motionRange.startOffset to motionRange.endOffset) | ||||||
|       startOffsets?.put(caret, motionRange.normalize().startOffset) |       startOffsets?.put(caret, motionRange.normalize().startOffset) | ||||||
|       caretToRange[caret] = TextRange(motionRange.startOffset, motionRange.endOffset) |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     val range = getTextRange(ranges, type) ?: return false |     val range = getTextRange(ranges, type) ?: return false | ||||||
| @@ -108,7 +99,6 @@ open class YankGroupBase : VimYankGroup { | |||||||
|  |  | ||||||
|     return yankRange( |     return yankRange( | ||||||
|       editor, |       editor, | ||||||
|       caretToRange, |  | ||||||
|       range, |       range, | ||||||
|       type, |       type, | ||||||
|       startOffsets, |       startOffsets, | ||||||
| @@ -125,7 +115,6 @@ open class YankGroupBase : VimYankGroup { | |||||||
|   override fun yankLine(editor: VimEditor, count: Int): Boolean { |   override fun yankLine(editor: VimEditor, count: Int): Boolean { | ||||||
|     val caretCount = editor.nativeCarets().size |     val caretCount = editor.nativeCarets().size | ||||||
|     val ranges = ArrayList<Pair<Int, Int>>(caretCount) |     val ranges = ArrayList<Pair<Int, Int>>(caretCount) | ||||||
|     val caretToRange = HashMap<ImmutableVimCaret, TextRange>(caretCount) |  | ||||||
|     for (caret in editor.nativeCarets()) { |     for (caret in editor.nativeCarets()) { | ||||||
|       val start = injector.motion.moveCaretToCurrentLineStart(editor, caret) |       val start = injector.motion.moveCaretToCurrentLineStart(editor, caret) | ||||||
|       val end = min(injector.motion.moveCaretToRelativeLineEnd(editor, caret, count - 1, true) + 1, editor.fileSize().toInt()) |       val end = min(injector.motion.moveCaretToRelativeLineEnd(editor, caret, count - 1, true) + 1, editor.fileSize().toInt()) | ||||||
| @@ -133,11 +122,10 @@ open class YankGroupBase : VimYankGroup { | |||||||
|       if (end == -1) continue |       if (end == -1) continue | ||||||
|  |  | ||||||
|       ranges.add(start to end) |       ranges.add(start to end) | ||||||
|       caretToRange[caret] = TextRange(start, end) |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     val range = getTextRange(ranges, SelectionType.LINE_WISE) ?: return false |     val range = getTextRange(ranges, SelectionType.LINE_WISE) ?: return false | ||||||
|     return yankRange(editor, caretToRange, range, SelectionType.LINE_WISE, null) |     return yankRange(editor, range, SelectionType.LINE_WISE, null) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
| @@ -150,7 +138,6 @@ open class YankGroupBase : VimYankGroup { | |||||||
|    */ |    */ | ||||||
|   override fun yankRange(editor: VimEditor, range: TextRange?, type: SelectionType, moveCursor: Boolean): Boolean { |   override fun yankRange(editor: VimEditor, range: TextRange?, type: SelectionType, moveCursor: Boolean): Boolean { | ||||||
|     range ?: return false |     range ?: return false | ||||||
|     val caretToRange = HashMap<ImmutableVimCaret, TextRange>() |  | ||||||
|  |  | ||||||
|     if (type == SelectionType.LINE_WISE) { |     if (type == SelectionType.LINE_WISE) { | ||||||
|       for (i in 0 until range.size()) { |       for (i in 0 until range.size()) { | ||||||
| @@ -170,19 +157,17 @@ open class YankGroupBase : VimYankGroup { | |||||||
|     val startOffsets = HashMap<VimCaret, Int>(editor.nativeCarets().size) |     val startOffsets = HashMap<VimCaret, Int>(editor.nativeCarets().size) | ||||||
|     if (type == SelectionType.BLOCK_WISE) { |     if (type == SelectionType.BLOCK_WISE) { | ||||||
|       startOffsets[editor.primaryCaret()] = range.normalize().startOffset |       startOffsets[editor.primaryCaret()] = range.normalize().startOffset | ||||||
|       caretToRange[editor.primaryCaret()] = range |  | ||||||
|     } else { |     } else { | ||||||
|       for ((i, caret) in editor.nativeCarets().withIndex()) { |       for ((i, caret) in editor.nativeCarets().withIndex()) { | ||||||
|         val textRange = TextRange(rangeStartOffsets[i], rangeEndOffsets[i]) |         val textRange = TextRange(rangeStartOffsets[i], rangeEndOffsets[i]) | ||||||
|         startOffsets[caret] = textRange.normalize().startOffset |         startOffsets[caret] = textRange.normalize().startOffset | ||||||
|         caretToRange[caret] = textRange |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return if (moveCursor) { |     return if (moveCursor) { | ||||||
|       yankRange(editor, caretToRange, range, type, startOffsets) |       yankRange(editor, range, type, startOffsets) | ||||||
|     } else { |     } else { | ||||||
|       yankRange(editor, caretToRange, range, type, null) |       yankRange(editor, range, type, null) | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user