mirror of
				https://github.com/chylex/IntelliJ-IdeaVim.git
				synced 2025-10-31 02:17:13 +01:00 
			
		
		
		
	Compare commits
	
		
			23 Commits
		
	
	
		
			0f0a73c139
			...
			customized
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| cc9385f2a9 | |||
| 32a2384a46 | |||
| fdd850de5a | |||
| 0f7116b136 | |||
| db8f0251fb | |||
| 2ca2c1e774 | |||
| f3c32da4d1 | |||
| 1a3b34d457 | |||
| 1f9159996d | |||
| 65c3acd891 | |||
| 223f65c003 | |||
| 293b854620 | |||
| 9e7c5fd603 | |||
| 00c799595f | |||
| 8577b5ed20 | |||
| 3af7a991a0 | |||
| 212af1798d | |||
| 002ef8f72f | |||
| 9115af6b3d | |||
| 25ca42d371 | |||
| 408687c9b3 | |||
| 85e00bf8fc | |||
| bd6f2d4b2f | 
							
								
								
									
										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 | ||||
|     plugin("AceJump", "3.8.19") | ||||
|     plugin("com.intellij.classic.ui", "242.20224.159") | ||||
|   } | ||||
|  | ||||
|   moduleSources(project(":vim-engine", "sourcesJarArtifacts")) | ||||
| @@ -210,6 +211,8 @@ tasks { | ||||
|   } | ||||
|  | ||||
|   compileTestKotlin { | ||||
|     enabled = false | ||||
|      | ||||
|     kotlinOptions { | ||||
|       jvmTarget = javaVersion | ||||
|       apiVersion = "1.9" | ||||
|   | ||||
| @@ -16,11 +16,11 @@ | ||||
| # https://data.services.jetbrains.com/products?code=IC | ||||
| # Maven releases are here: https://www.jetbrains.com/intellij-repository/releases | ||||
| # 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 | ||||
| ideaType=IC | ||||
| instrumentPluginCode=true | ||||
| version=SNAPSHOT | ||||
| version=chylex-40 | ||||
| javaVersion=17 | ||||
| remoteRobotVersion=0.11.23 | ||||
| antlrVersion=4.10.1 | ||||
| @@ -47,7 +47,6 @@ youtrackToken= | ||||
|  | ||||
| # Gradle settings | ||||
| org.gradle.jvmargs='-Dfile.encoding=UTF-8' | ||||
| org.gradle.configuration-cache=true | ||||
| org.gradle.caching=true | ||||
|  | ||||
| # 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) | ||||
|         } | ||||
|       } | ||||
|   } | ||||
| } | ||||
| @@ -22,6 +22,11 @@ import com.maddyhome.idea.vim.handler.VimActionHandler | ||||
| import com.maddyhome.idea.vim.helper.enumSetOf | ||||
| import java.util.* | ||||
|  | ||||
| @CommandOrMotion(keys = ["<C-H>", "<BS>"], modes = [Mode.INSERT]) | ||||
| internal class VimEditorBackSpace : IdeActionHandler(IdeActions.ACTION_EDITOR_BACKSPACE) { | ||||
|   override val type: Command.Type = Command.Type.DELETE | ||||
| } | ||||
|  | ||||
| @CommandOrMotion(keys = ["<Del>"], modes = [Mode.INSERT]) | ||||
| internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELETE) { | ||||
|   override val type: Command.Type = Command.Type.DELETE | ||||
|   | ||||
| @@ -69,7 +69,8 @@ object VimExtensionFacade { | ||||
|  | ||||
|  | ||||
|   @JvmStatic | ||||
|   @Deprecated("Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)", | ||||
|   @Deprecated( | ||||
|     "Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)", | ||||
|     ReplaceWith( | ||||
|       "VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)", | ||||
|       "com.maddyhome.idea.vim.VimPlugin" | ||||
| @@ -195,7 +196,7 @@ object VimExtensionFacade { | ||||
|  | ||||
|   @JvmStatic | ||||
|   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 | ||||
|   } | ||||
|  | ||||
| @@ -208,7 +209,7 @@ object VimExtensionFacade { | ||||
|   /** Set the current contents of the given register */ | ||||
|   @JvmStatic | ||||
|   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 */ | ||||
| @@ -277,4 +278,4 @@ fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFu | ||||
|  | ||||
| fun interface ScriptFunction { | ||||
|   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.util.Alarm | ||||
| import com.intellij.util.Alarm.ThreadToUse | ||||
| import com.jetbrains.rd.util.first | ||||
| 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.injector | ||||
| import com.maddyhome.idea.vim.common.ModeChangeListener | ||||
| @@ -117,9 +114,9 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis | ||||
|     initialised = false | ||||
|   } | ||||
|  | ||||
|   override fun yankPerformed(caretToRange: Map<ImmutableVimCaret, TextRange>) { | ||||
|   override fun yankPerformed(editor: VimEditor, range: TextRange) { | ||||
|     ensureInitialised() | ||||
|     highlightHandler.highlightYankRange(caretToRange) | ||||
|     highlightHandler.highlightYankRange(editor.ij, range) | ||||
|   } | ||||
|  | ||||
|   override fun modeChanged(editor: VimEditor, oldMode: Mode) { | ||||
| @@ -140,25 +137,22 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis | ||||
|     private var lastEditor: Editor? = null | ||||
|     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 | ||||
|       clearYankHighlighters() | ||||
|  | ||||
|       val editor = caretToRange.first().key.editor.ij | ||||
|       lastEditor = editor | ||||
|  | ||||
|       val attributes = getHighlightTextAttributes(editor) | ||||
|       for (range in caretToRange.values) { | ||||
|         for (i in 0 until range.size()) { | ||||
|           val highlighter = editor.markupModel.addRangeHighlighter( | ||||
|             range.startOffsets[i], | ||||
|             range.endOffsets[i], | ||||
|             HighlighterLayer.SELECTION, | ||||
|             attributes, | ||||
|             HighlighterTargetArea.EXACT_RANGE, | ||||
|           ) | ||||
|           highlighters.add(highlighter) | ||||
|         } | ||||
|       for (i in 0 until range.size()) { | ||||
|         val highlighter = editor.markupModel.addRangeHighlighter( | ||||
|           range.startOffsets[i], | ||||
|           range.endOffsets[i], | ||||
|           HighlighterLayer.SELECTION, | ||||
|           attributes, | ||||
|           HighlighterTargetArea.EXACT_RANGE, | ||||
|         ) | ||||
|         highlighters.add(highlighter) | ||||
|       } | ||||
|  | ||||
|       // 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") { | ||||
|       this.cMakePatterns | ||||
|     } 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) { | ||||
|   val registerGroup = injector.registerGroup | ||||
|   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 usedText = savedRegister.text | ||||
| @@ -180,4 +180,4 @@ private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimC | ||||
|       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.api.ExecutionContext | ||||
| 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.endsWithNewLine | ||||
| 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.group.findBlockRange | ||||
| 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.newapi.IjVimCaret | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| 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) | ||||
|     } | ||||
|  | ||||
|     VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator()) | ||||
|     VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator(supportsMultipleCursors = false, count = 1)) // TODO | ||||
|   } | ||||
|  | ||||
|   private class YSurroundHandler : ExtensionHandler { | ||||
| @@ -108,7 +112,7 @@ internal class VimSurroundExtension : VimExtension { | ||||
|         val lastNonWhiteSpaceOffset = getLastNonWhitespaceCharacterOffset(editor.text(), lineStartOffset, lineEndOffset) | ||||
|         if (lastNonWhiteSpaceOffset != null) { | ||||
|           val range = TextRange(lineStartOffset, lastNonWhiteSpaceOffset + 1) | ||||
|           performSurround(pair, range, it) | ||||
|           performSurround(pair, range, it, count = operatorArguments.count1) | ||||
|         } | ||||
| //        it.moveToOffset(lineStartOffset) | ||||
|       } | ||||
| @@ -131,15 +135,13 @@ internal class VimSurroundExtension : VimExtension { | ||||
|  | ||||
|   private class VSurroundHandler : ExtensionHandler { | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { | ||||
|       val selectionStart = editor.ij.caretModel.primaryCaret.selectionStart | ||||
|       // 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 | ||||
|       } | ||||
|       runWriteAction { | ||||
|         // Leave visual mode | ||||
|         editor.exitVisualMode() | ||||
|         editor.ij.caretModel.moveToOffset(selectionStart) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -160,6 +162,10 @@ internal class VimSurroundExtension : VimExtension { | ||||
|  | ||||
|     companion object { | ||||
|       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 | ||||
|         val surroundings = editor.sortedCarets() | ||||
|           .map { | ||||
| @@ -267,20 +273,41 @@ internal class VimSurroundExtension : VimExtension { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private class Operator : OperatorFunction { | ||||
|     override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean { | ||||
|       val ijEditor = editor.ij | ||||
|   private class Operator(private val supportsMultipleCursors: Boolean, private val count: Int) : OperatorFunction { | ||||
|     override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean { | ||||
|       val ijEditor = vimEditor.ij | ||||
|       val c = getChar(ijEditor) | ||||
|       if (c.code == 0) return true | ||||
|  | ||||
|       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 | ||||
|       performSurround(pair, range, editor.currentCaret(), selectionType == SelectionType.LINE_WISE) | ||||
|       // Jump back to start | ||||
|       executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor) | ||||
|  | ||||
|       runWriteAction { | ||||
|         val change = VimPlugin.getChange() | ||||
|         if (supportsMultipleCursors) { | ||||
|           ijEditor.runWithEveryCaretAndRestore { | ||||
|             applyOnce(ijEditor, change, pair, count) | ||||
|           } | ||||
|         } | ||||
|         else { | ||||
|           applyOnce(ijEditor, change, pair, count) | ||||
|           // Jump back to start | ||||
|           executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor) | ||||
|         } | ||||
|       } | ||||
|       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? { | ||||
|       val editor = caret.editor | ||||
| @@ -375,15 +402,15 @@ private fun getChar(editor: Editor): Char { | ||||
|   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 { | ||||
|     val editor = caret.editor | ||||
|     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 hasNewLine = editor.endsWithNewLine() | ||||
|     val rightSurround = if (tagsOnNewLines) { | ||||
|     val rightSurround = (if (tagsOnNewLines) { | ||||
|       if (isEOF && !hasNewLine) { | ||||
|         "\n" + pair.second | ||||
|       } else { | ||||
| @@ -391,7 +418,7 @@ private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: | ||||
|       } | ||||
|     } else { | ||||
|       pair.second | ||||
|     } | ||||
|     }).let { RepeatedCharSequence.of(it, count) } | ||||
|  | ||||
|     change.insertText(editor, caret, range.startOffset, leftSurround) | ||||
|     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.ij | ||||
| import com.maddyhome.idea.vim.state.mode.Mode | ||||
| import kotlin.math.min | ||||
|  | ||||
| /** | ||||
|  * Provides all the insert/replace related functionality | ||||
| @@ -104,11 +103,6 @@ class ChangeGroup : VimChangeGroupBase() { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   override fun processBackspace(editor: VimEditor, context: ExecutionContext) { | ||||
|     injector.actionExecutor.executeAction(editor, name = IdeActions.ACTION_EDITOR_BACKSPACE, context = context) | ||||
|     injector.scroll.scrollCaretIntoView(editor) | ||||
|   } | ||||
|  | ||||
|   private fun restoreCursor(editor: VimEditor, caret: VimCaret, startLine: Int) { | ||||
|     if (caret != editor.primaryCaret()) { | ||||
|       (editor as IjVimEditor).editor.caretModel.addCaret( | ||||
| @@ -130,6 +124,7 @@ class ChangeGroup : VimChangeGroupBase() { | ||||
|     context: ExecutionContext, | ||||
|     range: TextRange, | ||||
|   ) { | ||||
|     val startPos = editor.offsetToBufferPosition(caret.offset) | ||||
|     val startOffset = editor.getLineStartForOffset(range.startOffset) | ||||
|     val endOffset = editor.getLineEndForOffset(range.endOffset) | ||||
|     val ijEditor = (editor as IjVimEditor).editor | ||||
| @@ -154,11 +149,7 @@ class ChangeGroup : VimChangeGroupBase() { | ||||
|       } | ||||
|     } | ||||
|     val afterAction = { | ||||
|       val firstLine = editor.offsetToBufferPosition( | ||||
|         min(startOffset.toDouble(), endOffset.toDouble()).toInt() | ||||
|       ).line | ||||
|       val newOffset = injector.motion.moveCaretToLineStartSkipLeading(editor, firstLine) | ||||
|       caret.moveToOffset(newOffset) | ||||
|       caret.moveToOffset(injector.motion.moveCaretToLineStartSkipLeading(editor, startPos.line)) | ||||
|       restoreCursor(editor, caret, (caret as IjVimCaret).caret.logicalPosition.line) | ||||
|     } | ||||
|     if (project != null) { | ||||
|   | ||||
| @@ -139,7 +139,7 @@ object IjOptions { | ||||
|   // Temporary feature flags during development, not really intended for external use | ||||
|   val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", 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 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.macro.VimMacroBase | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
|  | ||||
| /** | ||||
|  * Used to handle playback of macros | ||||
| @@ -93,6 +94,9 @@ internal class MacroGroup : VimMacroBase() { | ||||
|         } finally { | ||||
|           keyStack.removeFirst() | ||||
|         } | ||||
|         if (!isInternalMacro) { | ||||
|           MacroAutoImport.run(editor.ij, context.ij) | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       if (isInternalMacro) { | ||||
|   | ||||
| @@ -218,13 +218,17 @@ internal class VimEnterHandler(nextHandler: EditorActionHandler?) : VimKeyHandle | ||||
| internal class VimEscHandler(nextHandler: EditorActionHandler) : VimKeyHandler(nextHandler) { | ||||
|   override val key: String = "<Esc>" | ||||
|  | ||||
|   private val ideaVimSupportDialog | ||||
|     get() = injector.globalIjOptions().ideavimsupport.contains(IjOptionConstants.ideavimsupport_dialog) | ||||
|    | ||||
|   override fun isHandlerEnabled(editor: Editor, dataContext: DataContext?): Boolean { | ||||
|     val ideaVimSupportDialog = | ||||
|       injector.globalIjOptions().ideavimsupport.contains(IjOptionConstants.ideavimsupport_dialog) | ||||
|  | ||||
|     return editor.isPrimaryEditor() || | ||||
|       EditorHelper.isFileEditor(editor) && !editor.vim.mode.inNormalMode || | ||||
|       ideaVimSupportDialog && !editor.vim.mode.inNormalMode | ||||
|       EditorHelper.isFileEditor(editor) && vimStateNeedsToHandleEscape(editor) || | ||||
|       ideaVimSupportDialog && vimStateNeedsToHandleEscape(editor) | ||||
|   } | ||||
|    | ||||
|   private fun vimStateNeedsToHandleEscape(editor: Editor): Boolean { | ||||
|     return !editor.vim.mode.inNormalMode || KeyHandler.getInstance().keyHandlerState.mappingState.hasKeys | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -326,7 +326,7 @@ public class EditorHelper { | ||||
|  | ||||
|     final int offset = y - ((screenHeight - lineHeight) / lineHeight / 2 * lineHeight); | ||||
|     @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); | ||||
|  | ||||
|     // 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.openapi.editor.Caret | ||||
| import com.intellij.openapi.editor.CaretState | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.intellij.openapi.editor.ex.util.EditorUtil | ||||
| 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.group.IjOptionConstants | ||||
| 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 javax.swing.JComponent | ||||
| import javax.swing.JTable | ||||
| @@ -96,3 +99,41 @@ internal val Caret.vimLine: Int | ||||
|  */ | ||||
| internal val Editor.vimLine: Int | ||||
|   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.performDumbAwareWithCallbacks | ||||
| 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.command.CommandProcessor | ||||
| import com.intellij.openapi.command.UndoConfirmationPolicy | ||||
| @@ -94,6 +95,7 @@ internal class IjActionExecutor : VimActionExecutor { | ||||
|       ActionManager.getInstance(), | ||||
|       0, | ||||
|     ) | ||||
|     Utils.initUpdateSession(event) | ||||
|     // beforeActionPerformedUpdate should be called to update the action. It fixes some rider-specific problems. | ||||
|     //   because rider use async update method. See VIM-1819. | ||||
|     // 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. | ||||
|     val topLine = getVisualLineAtTopOfScreen(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 | ||||
|     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.globalIjOptions | ||||
| 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 | ||||
|  | ||||
| /** | ||||
| @@ -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 | ||||
|       editor.runWithChangeTracking { | ||||
|         undoManager.undo(fileEditor) | ||||
|  | ||||
|         // We execute undo one more time if the previous one just restored selection | ||||
|         if (!hasChanges && hasSelection(editor) && undoManager.isUndoAvailable(fileEditor)) { | ||||
|           undoManager.undo(fileEditor) | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       CommandProcessor.getInstance().runUndoTransparentAction { | ||||
|         removeSelections(editor) | ||||
|         restoreVisualMode(editor) | ||||
|       } | ||||
|     } else { | ||||
|       notifyAboutNewUndo(editor.ij.project) | ||||
| @@ -108,7 +102,7 @@ internal class UndoRedoHelper : UndoRedoBase() { | ||||
|   private fun hasSelection(editor: VimEditor): Boolean { | ||||
|     return editor.primaryCaret().ij.hasSelection() | ||||
|   } | ||||
|    | ||||
|  | ||||
|   override fun redo(editor: VimEditor, context: ExecutionContext): Boolean { | ||||
|     val ijContext = context.context as DataContext | ||||
|     val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false | ||||
| @@ -229,4 +223,21 @@ internal class UndoRedoHelper : UndoRedoBase() { | ||||
|     val hasChanges: Boolean | ||||
|       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,16 +18,13 @@ import com.intellij.openapi.editor.VisualPosition | ||||
| import com.intellij.openapi.editor.markup.RangeHighlighter | ||||
| import com.intellij.openapi.util.Key | ||||
| 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.SelectionInfo | ||||
| import com.maddyhome.idea.vim.common.InsertSequence | ||||
| import com.maddyhome.idea.vim.ex.ExOutputModel | ||||
| import com.maddyhome.idea.vim.group.visual.VisualChange | ||||
| import com.maddyhome.idea.vim.group.visual.vimLeadSelectionOffset | ||||
| import com.maddyhome.idea.vim.common.InsertSequence | ||||
| import com.maddyhome.idea.vim.common.VimEditorReplaceMask | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.state.VimStateMachine | ||||
| import com.maddyhome.idea.vim.state.mode.Mode | ||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | ||||
| import com.maddyhome.idea.vim.ui.ExOutputPanel | ||||
| @@ -97,7 +94,6 @@ internal var Caret.vimInsertStart: RangeMarker by userDataOr { | ||||
| } | ||||
|  | ||||
| // 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.lastSelectionInfo: SelectionInfo? by userDataCaretToEditor() | ||||
|  | ||||
| @@ -127,7 +123,6 @@ internal var Editor.vimExOutput: ExOutputModel? by userData() | ||||
| internal var Editor.vimTestInputModel: TestInputModel? by userData() | ||||
|  | ||||
| internal var Editor.vimChangeActionSwitchMode: Mode? by userData() | ||||
| internal var Editor.replaceMask: VimEditorReplaceMask? by userData() | ||||
|  | ||||
| internal var Caret.currentInsert: InsertSequence? by userData() | ||||
| internal val Caret.insertHistory: MutableList<InsertSequence> by userDataOr { mutableListOf() } | ||||
|   | ||||
| @@ -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,8 +10,6 @@ package com.maddyhome.idea.vim.listener | ||||
|  | ||||
| import com.intellij.execution.impl.ConsoleViewImpl | ||||
| import com.intellij.openapi.application.ApplicationManager | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.intellij.openapi.editor.EditorKind | ||||
| import com.maddyhome.idea.vim.KeyHandler | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| @@ -19,7 +17,6 @@ import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.OperatorArguments | ||||
| 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.newapi.ij | ||||
| import com.maddyhome.idea.vim.state.mode.Mode | ||||
| @@ -32,8 +29,8 @@ import com.maddyhome.idea.vim.state.mode.Mode | ||||
|  */ | ||||
| class IJEditorFocusListener : EditorListener { | ||||
|   override fun focusGained(editor: VimEditor) { | ||||
|     val oldEditor = KeyHandler.getInstance().editorInFocus | ||||
|     if (oldEditor != null && oldEditor.ij == editor.ij) return | ||||
|     val editorInFocus = KeyHandler.getInstance().editorInFocus | ||||
|     if (editorInFocus != null && editorInFocus.ij == editor.ij) return | ||||
|  | ||||
|     KeyHandler.getInstance().editorInFocus = editor | ||||
|  | ||||
| @@ -64,9 +61,7 @@ class IJEditorFocusListener : EditorListener { | ||||
|       val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor) | ||||
|       VimPlugin.getChange().insertBeforeCursor(editor, context) | ||||
|     } | ||||
|     if (isTerminal(ijEditor) && !ijEditor.inInsertMode) { | ||||
|       switchToInsertMode.run() | ||||
|     } else if (ijEditor.isInsertMode && ((oldEditor != null && isTerminal(oldEditor.ij)) || !ijEditor.document.isWritable)) { | ||||
|     if (!ijEditor.document.isWritable) { | ||||
|       val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor) | ||||
|       val mode = injector.vimState.mode | ||||
|       when (mode) { | ||||
| @@ -83,12 +78,4 @@ class IJEditorFocusListener : EditorListener { | ||||
|     } | ||||
|     KeyHandler.getInstance().reset(editor) | ||||
|   } | ||||
|  | ||||
|   // By "terminal" we refer to some editor that should switch to INSERT mode on focus | ||||
|   private fun isTerminal(ijEditor: Editor): Boolean { | ||||
|     return !ijEditor.isViewer && | ||||
|       !EditorHelper.isFileEditor(ijEditor) && | ||||
|       ijEditor.document.isWritable && | ||||
|       ijEditor.editorKind != EditorKind.DIFF | ||||
|   } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -28,6 +28,7 @@ import com.intellij.openapi.actionSystem.CommonDataKeys | ||||
| import com.intellij.openapi.actionSystem.ex.AnActionListener | ||||
| import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.intellij.openapi.editor.impl.ScrollingModelImpl | ||||
| import com.intellij.openapi.keymap.KeymapManager | ||||
| import com.intellij.openapi.project.DumbAwareToggleAction | ||||
| import com.intellij.openapi.util.TextRange | ||||
| @@ -58,6 +59,7 @@ internal object IdeaSpecifics { | ||||
|     private val surrounderAction = | ||||
|       "com.intellij.codeInsight.generation.surroundWith.SurroundWithHandler\$InvokeSurrounderAction" | ||||
|     private var editor: Editor? = null | ||||
|     private var caretOffset = -1 | ||||
|     private var completionPrevDocumentLength: Int? = null | ||||
|     private var completionPrevDocumentOffset: Int? = null | ||||
|  | ||||
| @@ -67,6 +69,7 @@ internal object IdeaSpecifics { | ||||
|       val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR) | ||||
|       if (hostEditor != null) { | ||||
|         editor = hostEditor | ||||
|         caretOffset = hostEditor.caretModel.offset | ||||
|       } | ||||
|  | ||||
|       val isVimAction = (action as? AnActionWrapper)?.delegate is VimShortcutKeyAction | ||||
| @@ -115,42 +118,57 @@ internal object IdeaSpecifics { | ||||
|       if (VimPlugin.isNotEnabled()) return | ||||
|  | ||||
|       val editor = editor | ||||
|       if (editor != null && action is ChooseItemAction && injector.registerGroup.isRecording) { | ||||
|         val prevDocumentLength = completionPrevDocumentLength | ||||
|         val prevDocumentOffset = completionPrevDocumentOffset | ||||
|       if (editor != null) { | ||||
|         if (action is ChooseItemAction && injector.registerGroup.isRecording) { | ||||
|           val prevDocumentLength = completionPrevDocumentLength | ||||
|           val prevDocumentOffset = completionPrevDocumentOffset | ||||
|  | ||||
|         if (prevDocumentLength != null && prevDocumentOffset != null) { | ||||
|           val register = VimPlugin.getRegister() | ||||
|           val addedTextLength = editor.document.textLength - prevDocumentLength | ||||
|           val caretShift = addedTextLength - (editor.caretModel.primaryCaret.offset - prevDocumentOffset) | ||||
|           val leftArrow = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0) | ||||
|           if (prevDocumentLength != null && prevDocumentOffset != null) { | ||||
|             val register = VimPlugin.getRegister() | ||||
|             val addedTextLength = editor.document.textLength - prevDocumentLength | ||||
|             val caretShift = addedTextLength - (editor.caretModel.primaryCaret.offset - prevDocumentOffset) | ||||
|             val leftArrow = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0) | ||||
|  | ||||
|           register.recordText(editor.document.getText(TextRange(prevDocumentOffset, prevDocumentOffset + addedTextLength))) | ||||
|           repeat(caretShift.coerceAtLeast(0)) { | ||||
|             register.recordKeyStroke(leftArrow) | ||||
|             register.recordText(editor.document.getText(TextRange(prevDocumentOffset, prevDocumentOffset + addedTextLength))) | ||||
|             repeat(caretShift.coerceAtLeast(0)) { | ||||
|               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 | ||||
|         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) | ||||
|         if (caretOffset != -1 && caretOffset != editor.caretModel.offset) { | ||||
|           val scrollModel = editor.scrollingModel as ScrollingModelImpl | ||||
|           if (scrollModel.isScrollingNow) { | ||||
|             val v = scrollModel.verticalScrollOffset | ||||
|             val h = scrollModel.horizontalScrollOffset | ||||
|             scrollModel.finishAnimation() | ||||
|             scrollModel.scroll(h, v) | ||||
|             scrollModel.finishAnimation() | ||||
|           } | ||||
|           injector.scroll.scrollCaretIntoView(editor.vim) | ||||
|         } | ||||
|       } | ||||
|       //endregion | ||||
|  | ||||
|       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.FocusChangeListener | ||||
| 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.FileEditorManagerEvent | ||||
| 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.EditorWindow | ||||
| 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.Key | ||||
| import com.intellij.openapi.util.removeUserData | ||||
| import com.intellij.openapi.vfs.VirtualFile | ||||
| import com.intellij.util.ExceptionUtil | ||||
| import com.jetbrains.rd.util.lifetime.Lifetime | ||||
| import com.maddyhome.idea.vim.EventFacade | ||||
| import com.maddyhome.idea.vim.KeyHandler | ||||
| 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.GuicursorChangeListener | ||||
| 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.exitVisualMode | ||||
| 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.ij | ||||
| 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.selectionType | ||||
| 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.mode.listeners.ModeWidgetListener | ||||
| import com.maddyhome.idea.vim.ui.widgets.mode.modeWidgetOptionListener | ||||
| import com.maddyhome.idea.vim.vimDisposable | ||||
| import java.awt.event.MouseAdapter | ||||
| import java.awt.event.MouseEvent | ||||
| 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 | ||||
|       if (vimDisabled(editor)) return | ||||
|  | ||||
|       // As I understand, there is no need to pass a disposable that also disposes on editor close | ||||
|       //   because all editor resources will be garbage collected anyway on editor close | ||||
|       // Note that this uses the plugin's main disposable, rather than VimPlugin.onOffDisposable, because we don't need | ||||
|       // to - we explicitly call VimListenerManager.removeAll from VimPlugin.turnOffPlugin, and this disposes each | ||||
|       // editor's disposable individually. | ||||
|       val disposable = editor.project?.vimDisposable ?: return | ||||
|       val pluginLifetime = VimPlugin.getInstance().createLifetime() | ||||
|       val editorLifetime = (editor as EditorImpl).disposable.createLifetime() | ||||
|       val disposable = | ||||
|         Lifetime.intersect(pluginLifetime, editorLifetime).createNestedDisposable("MyLifetimedDisposable") | ||||
|  | ||||
|       // Protect against double initialisation | ||||
|       if (editor.getUserData(editorListenersDisposableKey) != null) { | ||||
| @@ -386,7 +387,17 @@ internal object VimListenerManager { | ||||
|     override fun selectionChanged(event: FileEditorManagerEvent) { | ||||
|       // 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 | ||||
|  | ||||
|        | ||||
|       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) | ||||
|       FileGroup.fileEditorManagerSelectionChangedCallback(event) | ||||
|       VimPlugin.getSearch().fileEditorManagerSelectionChangedCallback(event) | ||||
| @@ -458,8 +469,6 @@ internal object VimListenerManager { | ||||
|  | ||||
|         event.editor.putUserData(openingEditorKey, OpeningEditor(openingEditor, owningEditorWindow, isPreview, canBeReused)) | ||||
|       } | ||||
|  | ||||
|       VimStandalonePluginUpdateChecker.getInstance().pluginUsed() | ||||
|     } | ||||
|  | ||||
|     override fun editorReleased(event: EditorFactoryEvent) { | ||||
|   | ||||
| @@ -17,24 +17,6 @@ internal class IjLiveRange(val marker: RangeMarker) : LiveRange { | ||||
|  | ||||
|   override val endOffset: Int | ||||
|     get() = marker.endOffset | ||||
|  | ||||
|   override fun equals(other: Any?): Boolean { | ||||
|     if (this === other) return true | ||||
|     if (javaClass != other?.javaClass) return false | ||||
|  | ||||
|     other as IjLiveRange | ||||
|  | ||||
|     if (startOffset != other.startOffset) return false | ||||
|     if (endOffset != other.endOffset) return false | ||||
|  | ||||
|     return true | ||||
|   } | ||||
|  | ||||
|   override fun hashCode(): Int { | ||||
|     var result = startOffset | ||||
|     result = 31 * result + endOffset | ||||
|     return result | ||||
|   } | ||||
| } | ||||
|  | ||||
| val RangeMarker.vim: LiveRange | ||||
|   | ||||
| @@ -12,8 +12,6 @@ import com.intellij.openapi.editor.Caret | ||||
| import com.intellij.openapi.editor.LogicalPosition | ||||
| import com.intellij.openapi.editor.VisualPosition | ||||
| 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.LocalMarkStorage | ||||
| 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.markStorage | ||||
| 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.vimInsertStart | ||||
| 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() { | ||||
|  | ||||
|   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 | ||||
|     get() { | ||||
|       var storage = this.caret.markStorage | ||||
|   | ||||
| @@ -44,8 +44,6 @@ import com.maddyhome.idea.vim.common.IndentConfig.Companion.create | ||||
| import com.maddyhome.idea.vim.common.LiveRange | ||||
| import com.maddyhome.idea.vim.common.ModeChangeListener | ||||
| import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.common.VimEditorReplaceMask | ||||
| import com.maddyhome.idea.vim.common.forgetAllReplaceMasks | ||||
| import com.maddyhome.idea.vim.group.visual.vimSetSystemBlockSelectionSilently | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper | ||||
| import com.maddyhome.idea.vim.helper.StrictMode | ||||
| @@ -55,7 +53,6 @@ import com.maddyhome.idea.vim.helper.fileSize | ||||
| import com.maddyhome.idea.vim.helper.getTopLevelEditor | ||||
| import com.maddyhome.idea.vim.helper.inExMode | ||||
| import com.maddyhome.idea.vim.helper.isTemplateActive | ||||
| import com.maddyhome.idea.vim.helper.replaceMask | ||||
| import com.maddyhome.idea.vim.helper.vimChangeActionSwitchMode | ||||
| import com.maddyhome.idea.vim.helper.vimLastSelectionType | ||||
| import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl | ||||
| @@ -78,11 +75,6 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor, VimEditorBase( | ||||
|   // TBH, I don't like the names. Need to think a bit more about this | ||||
|   val editor = editor.getTopLevelEditor() | ||||
|   val originalEditor = editor | ||||
|   override var replaceMask: VimEditorReplaceMask? | ||||
|     get() = editor.replaceMask | ||||
|     set(value) { | ||||
|       editor.replaceMask = value | ||||
|     } | ||||
|  | ||||
|   override fun updateMode(mode: Mode) { | ||||
|     (injector.vimState as VimStateMachineImpl).mode = mode | ||||
| @@ -173,21 +165,38 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor, VimEditorBase( | ||||
|     return editor.caretModel.allCarets.map { IjVimCaret(it) } | ||||
|   } | ||||
|  | ||||
|   override var isFirstCaret = true | ||||
|   override var isReversingCarets = false | ||||
|    | ||||
|   @Suppress("ideavimRunForEachCaret") | ||||
|   override fun forEachCaret(action: (VimCaret) -> Unit) { | ||||
|     if (editor.vim.inBlockSelection) { | ||||
|       action(IjVimCaret(editor.caretModel.primaryCaret)) | ||||
|     } else { | ||||
|       editor.caretModel.runForEachCaret({ | ||||
|         if (it.isValid) { | ||||
|           action(IjVimCaret(it)) | ||||
|         } | ||||
|       }, false) | ||||
|       try { | ||||
|         editor.caretModel.runForEachCaret({ | ||||
|           if (it.isValid) { | ||||
|             action(IjVimCaret(it)) | ||||
|             isFirstCaret = false | ||||
|           } | ||||
|         }, false) | ||||
|       } finally { | ||||
|         isFirstCaret = true | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   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 { | ||||
| @@ -462,7 +471,6 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor, VimEditorBase( | ||||
|     get() = (editor as? EditorEx)?.isInsertMode ?: false | ||||
|     set(value) { | ||||
|       (editor as? EditorEx)?.isInsertMode = value | ||||
|       forgetAllReplaceMasks() | ||||
|     } | ||||
|  | ||||
|   override val document: VimDocument | ||||
| @@ -539,4 +547,4 @@ internal class InsertTimeRecorder: ModeChangeListener { | ||||
|       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.VimPlugin; | ||||
| 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.helper.SearchHighlightsHelper; | ||||
| import com.maddyhome.idea.vim.helper.UiHelper; | ||||
| @@ -348,7 +351,7 @@ public class ExEntryPanel extends JPanel implements VimCommandLine { | ||||
|         // coerced to at least 1. | ||||
|         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 | ||||
|           int pattenEnd = injector.getSearchGroup().findEndOfPattern(searchText, separator, 0); | ||||
|           final String pattern = searchText.substring(0, pattenEnd); | ||||
|   | ||||
| @@ -1,12 +1,4 @@ | ||||
| <!-- | ||||
|   ~ 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"> | ||||
| <idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude"> | ||||
|   <name>IdeaVim</name> | ||||
|   <id>IdeaVIM</id> | ||||
|   <description><![CDATA[ | ||||
| @@ -21,7 +13,7 @@ | ||||
|         <li><a href="https://youtrack.jetbrains.com/issues/VIM">Issue tracker</a>: feature requests and bug reports</li> | ||||
|       </ul> | ||||
|     ]]></description> | ||||
|   <version>SNAPSHOT</version> | ||||
|   <version>chylex</version> | ||||
|   <vendor>JetBrains</vendor> | ||||
|  | ||||
|   <!-- 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/*)"/> | ||||
|  | ||||
|   <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"/> | ||||
|     </action> | ||||
|  | ||||
|       <action id="VimPluginToggle" class="com.maddyhome.idea.vim.action.VimPluginToggleAction"/> | ||||
|       <action id="VimRunLastMacroInOpenFiles" class="com.maddyhome.idea.vim.action.VimRunLastMacroInOpenFiles"/> | ||||
|     </group> | ||||
|      | ||||
|     <!-- Internal --> | ||||
|     <!--suppress PluginXmlI18n --> | ||||
|     <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> | ||||
|  | ||||
|     <action id="VimFindActionIdAction" class="com.maddyhome.idea.vim.listener.FindActionIdAction"/> | ||||
|     <action id="VimJumpToSource" class="com.intellij.diff.actions.impl.OpenInEditorAction" /> | ||||
|   </actions> | ||||
| </idea-plugin> | ||||
|   | ||||
| @@ -7,7 +7,6 @@ | ||||
|  */ | ||||
| package org.jetbrains.plugins.ideavim.action | ||||
|  | ||||
| import com.intellij.idea.TestFor | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.state.mode.Mode | ||||
| import com.maddyhome.idea.vim.state.mode.ReturnTo | ||||
| @@ -1069,14 +1068,4 @@ foobaz | ||||
|       Mode.NORMAL(), | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   @Test | ||||
|   @TestFor(issues = ["VIM-2074"]) | ||||
|   fun `backspace with replace mode`() { | ||||
|     configureByText("${c}Hello world") | ||||
|     typeText("R1111") | ||||
|     assertState("1111o world") | ||||
|     typeText("<BS><BS><BS>") | ||||
|     assertState("1ello world") | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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.common.TextRange | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | ||||
| import org.jetbrains.plugins.ideavim.VimBehaviorDiffers | ||||
| import org.jetbrains.plugins.ideavim.VimTestCase | ||||
| @@ -2173,7 +2172,7 @@ rtyfg${c}hzxc""" | ||||
|     val editor = configureByText(before) | ||||
|     injector.registerGroup.storeText('*', "fgh") | ||||
|     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")) | ||||
|     val after = "fg${c}hqfg${c}hwe asd zxc rty fg${c}hfgh vbn" | ||||
|     assertState(after) | ||||
|   | ||||
| @@ -33,7 +33,7 @@ class IdeaPutNotificationsTest : VimTestCase() { | ||||
|     appReadySetup(false) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     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")) | ||||
|  | ||||
|     val notification = notifications().last() | ||||
| @@ -53,7 +53,7 @@ class IdeaPutNotificationsTest : VimTestCase() { | ||||
|     appReadySetup(false) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     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")) | ||||
|  | ||||
|     val notifications = notifications() | ||||
| @@ -71,7 +71,7 @@ class IdeaPutNotificationsTest : VimTestCase() { | ||||
|     appReadySetup(true) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     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")) | ||||
|  | ||||
|     val notifications = EventLog.getLogModel(fixture.project).notifications | ||||
|   | ||||
| @@ -88,7 +88,7 @@ class PutTestAfterCursorActionTest : VimTestCase() { | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -127,7 +127,6 @@ class PutTestAfterCursorActionTest : VimTestCase() { | ||||
|     val vimEditor = editor.vim | ||||
|     injector.registerGroup.storeText( | ||||
|       vimEditor, | ||||
|       vimEditor.primaryCaret(), | ||||
|       before rangeOf "I found it in a legendary land\n", | ||||
|       SelectionType.LINE_WISE, | ||||
|       false, | ||||
| @@ -157,7 +156,7 @@ class PutTestAfterCursorActionTest : VimTestCase() { | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
|   | ||||
| @@ -31,7 +31,7 @@ class PutTextBeforeCursorActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|   | ||||
| @@ -54,7 +54,7 @@ class PutViaIdeaTest : VimTestCase() { | ||||
|  | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|  | ||||
|     typeText("ppp") | ||||
|     val after = "Ilegendarylegendarylegendar${c}y found it in a legendary land" | ||||
| @@ -74,7 +74,6 @@ class PutViaIdeaTest : VimTestCase() { | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText( | ||||
|         vimEditor, | ||||
|         vimEditor.primaryCaret(), | ||||
|         before rangeOf "legendary$randomUUID", | ||||
|         SelectionType.CHARACTER_WISE, | ||||
|         false, | ||||
| @@ -100,7 +99,6 @@ class PutViaIdeaTest : VimTestCase() { | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     VimPlugin.getRegister().storeText( | ||||
|       vimEditor, | ||||
|       vimEditor.primaryCaret(), | ||||
|       before rangeOf "\nLorem ipsum dolor sit amet,\n", | ||||
|       SelectionType.CHARACTER_WISE, | ||||
|       false, | ||||
|   | ||||
| @@ -75,7 +75,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = "legendar${c}y it in a legendary land" | ||||
|     assertState(after) | ||||
| @@ -87,7 +87,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = "legendarylegendar${c}y in a legendary land" | ||||
|     assertState(after) | ||||
| @@ -99,7 +99,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = "legendarylegendar${c}y" | ||||
|     assertState(after) | ||||
| @@ -133,7 +133,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     val before = "I foun${c}d it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = "I legendar${c}y it in a legendary land" | ||||
|     assertState(after) | ||||
| @@ -154,7 +154,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -182,7 +182,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -210,7 +210,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -238,7 +238,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -455,7 +455,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -491,7 +491,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -529,7 +529,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -640,7 +640,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -666,7 +666,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -703,7 +703,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -876,7 +876,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -902,7 +902,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -939,7 +939,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1117,7 +1117,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1172,7 +1172,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1233,7 +1233,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1409,7 +1409,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1435,7 +1435,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1461,7 +1461,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1487,7 +1487,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1526,7 +1526,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1554,7 +1554,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1593,7 +1593,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1633,7 +1633,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1672,7 +1672,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1707,7 +1707,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1743,7 +1743,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1779,7 +1779,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1815,7 +1815,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1852,7 +1852,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
|   | ||||
| @@ -33,7 +33,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = "legendarylegendary$c in a legendary land" | ||||
|     assertState(after) | ||||
| @@ -45,7 +45,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|  | ||||
| @@ -61,7 +61,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = "legendary\n$c" | ||||
|     assertState(after) | ||||
| @@ -88,7 +88,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(file) | ||||
|     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")) | ||||
|     assertState(newFile) | ||||
|   } | ||||
| @@ -134,7 +134,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|  | ||||
| @@ -150,7 +150,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = "legendarylegendary$c in a legendary land" | ||||
|     assertState(after) | ||||
| @@ -162,7 +162,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = "legendarylegendar${c}y" | ||||
|     assertState(after) | ||||
| @@ -174,7 +174,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = "legendary\n$c" | ||||
|     assertState(after) | ||||
| @@ -273,7 +273,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             ${c}fgh | ||||
| @@ -299,7 +299,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = """ | ||||
|             q | ||||
| @@ -320,7 +320,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     val before = "${c}qwe asd ${c}zxc rty ${c}fgh vbn" | ||||
|     val editor = configureByText(before) | ||||
|     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")) | ||||
|     val after = "fghfgh$c fghfgh$c fghfgh$c" | ||||
|     assertState(after) | ||||
|   | ||||
| @@ -91,7 +91,7 @@ class YankVisualActionTest : VimTestCase() { | ||||
|     typeText(injector.parser.parseKeys("viw" + "y")) | ||||
|     val editor = fixture.editor.vim | ||||
|     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) | ||||
|   } | ||||
|  | ||||
| @@ -172,7 +172,7 @@ class YankVisualActionTest : VimTestCase() { | ||||
|     typeText(injector.parser.parseKeys("V" + "y")) | ||||
|     val editor = fixture.editor.vim | ||||
|     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("all rocks and lavender and tufted grass,\n", "hard by the torrent of a mountain pass.\n"), | ||||
|       registers, | ||||
|   | ||||
| @@ -130,7 +130,7 @@ class MultipleCaretsTest : VimTestCase() { | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     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")) | ||||
|     val after = """ | ||||
|           qwe | ||||
| @@ -165,7 +165,7 @@ class MultipleCaretsTest : VimTestCase() { | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     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")) | ||||
|     val after = """ | ||||
|           qwe | ||||
| @@ -201,7 +201,7 @@ class MultipleCaretsTest : VimTestCase() { | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     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")) | ||||
|     val after = """ | ||||
|           qwe | ||||
| @@ -237,7 +237,7 @@ class MultipleCaretsTest : VimTestCase() { | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     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")) | ||||
|     val after = """ | ||||
|           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 editor = configureByText(before) | ||||
|     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(commandToKeys("pu")) | ||||
|   | ||||
| @@ -9,7 +9,6 @@ | ||||
| package org.jetbrains.plugins.ideavim.ex.implementation.commands | ||||
|  | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.register.RegisterConstants | ||||
| import org.jetbrains.plugins.ideavim.VimTestCase | ||||
| 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. | ||||
|         |""".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) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     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")) | ||||
|     assertState("one on${c}e three") | ||||
|     assertEquals("one", VimPlugin.getRegister().lastRegister?.text) | ||||
| @@ -170,7 +170,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | ||||
|     configureByText(text) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     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")) | ||||
|     assertState("one on${c}e four") | ||||
|     assertEquals("one", VimPlugin.getRegister().lastRegister?.text) | ||||
| @@ -184,7 +184,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | ||||
|     configureByText(text) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     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")) | ||||
|     assertState("one two one four") | ||||
|     assertEquals("one", VimPlugin.getRegister().lastRegister?.text) | ||||
| @@ -197,7 +197,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | ||||
|     configureByText(text) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     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" + ".")) | ||||
|     assertState("one one on${c}e four") | ||||
|     assertEquals("one", VimPlugin.getRegister().lastRegister?.text) | ||||
| @@ -247,7 +247,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | ||||
|     configureByText(text) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     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")) | ||||
|     assertState( | ||||
|       """ | ||||
| @@ -414,7 +414,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | ||||
|     configureByText(text) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     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")) | ||||
|     assertState( | ||||
|       """ | ||||
| @@ -485,7 +485,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | ||||
|     configureByText(text) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     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")) | ||||
|     assertState( | ||||
|       """ | ||||
|   | ||||
| @@ -16,7 +16,7 @@ import com.maddyhome.idea.vim.command.Command | ||||
| import com.maddyhome.idea.vim.command.OperatorArguments | ||||
| 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() { | ||||
|   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.handler.VimActionHandler | ||||
|  | ||||
| @CommandOrMotion(keys = ["u", "<Undo>"], modes = [Mode.NORMAL]) | ||||
| @CommandOrMotion(keys = ["u", "<Undo>"], modes = [Mode.NORMAL, Mode.VISUAL]) | ||||
| class UndoAction : VimActionHandler.SingleExecution() { | ||||
|   override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,6 @@ | ||||
| package com.maddyhome.idea.vim.action.change.change | ||||
|  | ||||
| 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.VimCaret | ||||
| import com.maddyhome.idea.vim.api.VimChangeGroup | ||||
| @@ -23,7 +22,7 @@ import com.maddyhome.idea.vim.helper.CharacterHelper | ||||
| /** | ||||
|  * @author vlan | ||||
|  */ | ||||
| @CommandOrMotion(keys = ["u"], modes = [Mode.VISUAL]) | ||||
| @CommandOrMotion(keys = [], modes = []) | ||||
| class ChangeCaseLowerVisualAction : VisualOperatorActionHandler.ForEachCaret() { | ||||
|   override val type: Command.Type = Command.Type.CHANGE | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,6 @@ | ||||
| package com.maddyhome.idea.vim.action.change.change | ||||
|  | ||||
| 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.VimCaret | ||||
| import com.maddyhome.idea.vim.api.VimChangeGroup | ||||
| @@ -23,7 +22,7 @@ import com.maddyhome.idea.vim.helper.CharacterHelper | ||||
| /** | ||||
|  * @author vlan | ||||
|  */ | ||||
| @CommandOrMotion(keys = ["U"], modes = [Mode.VISUAL]) | ||||
| @CommandOrMotion(keys = [], modes = []) | ||||
| class ChangeCaseUpperVisualAction : VisualOperatorActionHandler.ForEachCaret() { | ||||
|   override val type: Command.Type = Command.Type.CHANGE | ||||
|  | ||||
|   | ||||
| @@ -15,7 +15,6 @@ import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.Argument | ||||
| import com.maddyhome.idea.vim.command.Command | ||||
| import com.maddyhome.idea.vim.command.OperatorArguments | ||||
| import com.maddyhome.idea.vim.common.VimEditorReplaceMask | ||||
| import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler | ||||
|  | ||||
| @CommandOrMotion(keys = ["R"], modes = [Mode.NORMAL]) | ||||
| @@ -40,5 +39,4 @@ class ChangeReplaceAction : ChangeEditorActionHandler.SingleExecution() { | ||||
|  */ | ||||
| private fun changeReplace(editor: VimEditor, context: ExecutionContext) { | ||||
|   injector.changeGroup.initInsert(editor, context, com.maddyhome.idea.vim.state.mode.Mode.REPLACE) | ||||
|   editor.replaceMask = VimEditorReplaceMask() | ||||
| } | ||||
|   | ||||
| @@ -1,40 +0,0 @@ | ||||
| /* | ||||
|  * Copyright 2003-2024 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.action.change.insert | ||||
|  | ||||
| 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.VimEditor | ||||
| import com.maddyhome.idea.vim.api.getLineStartForOffset | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.Command | ||||
| import com.maddyhome.idea.vim.command.OperatorArguments | ||||
| import com.maddyhome.idea.vim.handler.VimActionHandler | ||||
|  | ||||
| @CommandOrMotion(keys = ["<C-H>", "<BS>"], modes = [Mode.INSERT]) | ||||
| internal class InsertBackspaceAction : VimActionHandler.SingleExecution() { | ||||
|   override val type: Command.Type = Command.Type.OTHER_WRITABLE | ||||
|  | ||||
|   override fun execute( editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments, ): Boolean { | ||||
|     if (editor.insertMode) { | ||||
|       injector.changeGroup.processBackspace(editor, context) | ||||
|     } else { | ||||
|       for (caret in editor.carets()) { | ||||
|         val offset = (caret.offset - 1).takeIf { it > 0 } ?: continue | ||||
|         val oldChar = editor.replaceMask?.popChange(editor, offset) | ||||
|         if (oldChar != null) { | ||||
|           injector.changeGroup.replaceText(editor, caret, offset, offset + 1, oldChar.toString()) | ||||
|         } | ||||
|         caret.moveToOffset(offset) | ||||
|       } | ||||
|     } | ||||
|     return true | ||||
|   } | ||||
| } | ||||
| @@ -10,7 +10,6 @@ package com.maddyhome.idea.vim.action.copy | ||||
| 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.ImmutableVimCaret | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.Argument | ||||
| @@ -36,7 +35,15 @@ sealed class PutTextBaseAction( | ||||
|     val count = operatorArguments.count1 | ||||
|     val sortedCarets = editor.sortedCarets() | ||||
|     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 | ||||
|       injector.application.runWriteAction { | ||||
|         caretToPutData.forEach { | ||||
| @@ -45,28 +52,24 @@ sealed class PutTextBaseAction( | ||||
|       } | ||||
|       result | ||||
|     } else { | ||||
|       val putData = getPutDataForCaret(sortedCarets.single(), count) | ||||
|       injector.put.putText(editor, context, putData, operatorArguments) | ||||
|       injector.put.putText(editor, context, getPutData(count), operatorArguments) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private fun getPutDataForCaret(caret: ImmutableVimCaret, count: Int): PutData { | ||||
|     val registerService = injector.registerGroup | ||||
|     val registerChar = if (caret.editor.carets().size == 1) { | ||||
|       registerService.currentRegister | ||||
|     } else { | ||||
|       registerService.getCurrentRegisterForMulticaret() | ||||
|     } | ||||
|     val register = caret.registerStorage.getRegister(registerChar) | ||||
|     val textData = register?.let { | ||||
|       TextData( | ||||
|         register.text ?: injector.parser.toPrintableString(register.keys), | ||||
|         register.type, | ||||
|         register.transferableData, | ||||
|         register.name, | ||||
|       ) | ||||
|     } | ||||
|     return PutData(textData, null, count, insertTextBeforeCaret, indent, caretAfterInsertedText, -1) | ||||
|   private fun getPutData(count: Int): PutData { | ||||
|     return PutData(getRegisterTextData(), null, count, insertTextBeforeCaret, indent, caretAfterInsertedText, -1) | ||||
|   } | ||||
| } | ||||
|  | ||||
| fun getRegisterTextData(): TextData? { | ||||
|   val register = injector.registerGroup.getRegister(injector.registerGroup.currentRegister) | ||||
|   return register?.let { | ||||
|     TextData( | ||||
|       register.text ?: injector.parser.toPrintableString(register.keys), | ||||
|       register.type, | ||||
|       register.transferableData, | ||||
|       register.name, | ||||
|     ) | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -41,7 +41,21 @@ sealed class PutVisualTextBaseAction( | ||||
|   ): Boolean { | ||||
|     if (caretsAndSelections.isEmpty()) return false | ||||
|     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() | ||||
|     var result = true | ||||
|     injector.application.runWriteAction { | ||||
| @@ -51,18 +65,8 @@ sealed class PutVisualTextBaseAction( | ||||
|     } | ||||
|     return result | ||||
|   } | ||||
|  | ||||
|   private fun getPutDataForCaret(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, | ||||
|       ) | ||||
|     } | ||||
|    | ||||
|   private fun getPutDataForCaret(textData: PutData.TextData?, caret: VimCaret, selection: VimSelection?, count: Int): PutData { | ||||
|     val visualSelection = selection?.let { PutData.VisualSelection(mapOf(caret to it), it.type) } | ||||
|     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) | ||||
|       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.NoMotion | ||||
|     if (offsetAndMotion == null) return Motion.Error | ||||
|     parentAction.motionType = offsetAndMotion.second | ||||
|     return offsetAndMotion.first.toMotionOrError() | ||||
|   } | ||||
|   | ||||
| @@ -82,6 +82,13 @@ sealed class TillCharacterMotion( | ||||
|       ) | ||||
|     } | ||||
|     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() | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -9,20 +9,16 @@ | ||||
| package com.maddyhome.idea.vim.api | ||||
|  | ||||
| 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.vimMoveBlockSelectionToOffset | ||||
| import com.maddyhome.idea.vim.group.visual.vimMoveSelectionToCaret | ||||
| import com.maddyhome.idea.vim.handler.Motion | ||||
| import com.maddyhome.idea.vim.helper.StrictMode | ||||
| 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.inCommandLineMode | ||||
| import com.maddyhome.idea.vim.state.mode.inSelectMode | ||||
| 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. | ||||
| @@ -61,7 +57,6 @@ interface ImmutableVimCaret { | ||||
|   fun hasSelection(): Boolean | ||||
|  | ||||
|   var lastSelectionInfo: SelectionInfo | ||||
|   val registerStorage: CaretRegisterStorage | ||||
|   val markStorage: LocalMarkStorage | ||||
| } | ||||
|  | ||||
| @@ -147,21 +142,3 @@ fun VimCaret.moveToMotion(motion: Motion): VimCaret { | ||||
|     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 | ||||
|  | ||||
| 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 | ||||
|  | ||||
| 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) | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -41,7 +41,6 @@ interface VimChangeGroup { | ||||
|  | ||||
|   fun processEnter(editor: VimEditor, caret: VimCaret, context: ExecutionContext) | ||||
|   fun processEnter(editor: VimEditor, context: ExecutionContext) | ||||
|   fun processBackspace(editor: VimEditor, context: ExecutionContext) | ||||
|  | ||||
|   fun processPostChangeModeSwitch(editor: VimEditor, context: ExecutionContext, toSwitch: Mode) | ||||
|  | ||||
| @@ -145,7 +144,7 @@ interface VimChangeGroup { | ||||
|     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 | ||||
|  | ||||
|   | ||||
| @@ -180,7 +180,7 @@ abstract class VimChangeGroupBase : VimChangeGroup { | ||||
|     if (type == null || | ||||
|       (mode == Mode.INSERT || mode == Mode.REPLACE) || | ||||
|       !saveToRegister || | ||||
|       caret.registerStorage.storeText(editor, updatedRange, type, true) | ||||
|       injector.registerGroup.storeText(editor, updatedRange, type, true, !editor.isFirstCaret, editor.isReversingCarets) | ||||
|     ) { | ||||
|       val startOffsets = updatedRange.startOffsets | ||||
|       val endOffsets = updatedRange.endOffsets | ||||
| @@ -209,7 +209,7 @@ abstract class VimChangeGroupBase : VimChangeGroup { | ||||
|    * @param caret  The caret to start insertion in | ||||
|    * @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) | ||||
|     val newCaret = caret.moveToInlayAwareOffset(offset + str.length) | ||||
|  | ||||
| @@ -731,14 +731,12 @@ abstract class VimChangeGroupBase : VimChangeGroup { | ||||
|   ): Boolean { | ||||
|     logger.debug { "processKey($key)" } | ||||
|     if (key.keyChar != KeyEvent.CHAR_UNDEFINED) { | ||||
|       editor.replaceMask?.recordChangeAtCaret(editor) | ||||
|       processResultBuilder.addExecutionStep { _, lambdaEditor, lambdaContext -> type(lambdaEditor, lambdaContext, key.keyChar) } | ||||
|       return true | ||||
|     } | ||||
|  | ||||
|     // Shift-space | ||||
|     if (key.keyCode == 32 && key.modifiers and KeyEvent.SHIFT_DOWN_MASK != 0) { | ||||
|       editor.replaceMask?.recordChangeAtCaret(editor) | ||||
|       processResultBuilder.addExecutionStep { _, lambdaEditor, lambdaContext -> type(lambdaEditor, lambdaContext, ' ') } | ||||
|       return true | ||||
|     } | ||||
|   | ||||
| @@ -11,7 +11,6 @@ package com.maddyhome.idea.vim.api | ||||
| import com.maddyhome.idea.vim.command.OperatorArguments | ||||
| import com.maddyhome.idea.vim.common.LiveRange | ||||
| import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.common.VimEditorReplaceMask | ||||
| import com.maddyhome.idea.vim.state.mode.Mode | ||||
| import com.maddyhome.idea.vim.state.mode.ReturnTo | ||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | ||||
| @@ -129,7 +128,6 @@ interface VimEditor { | ||||
|   val lfMakesNewLine: Boolean | ||||
|   var vimChangeActionSwitchMode: Mode? | ||||
|   val indentConfig: VimIndentConfig | ||||
|   var replaceMask: VimEditorReplaceMask? | ||||
|  | ||||
|   fun fileSize(): Long | ||||
|  | ||||
| @@ -172,7 +170,8 @@ interface VimEditor { | ||||
|    * This method should perform caret merging after the operations. This is similar to IJ runForEachCaret | ||||
|    * TODO review | ||||
|    */ | ||||
|  | ||||
|   val isFirstCaret: Boolean | ||||
|   val isReversingCarets: Boolean | ||||
|   fun forEachCaret(action: (VimCaret) -> Unit) | ||||
|   fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean = false) | ||||
|   fun isInForEachCaretScope(): Boolean | ||||
| @@ -350,4 +349,4 @@ interface VimFoldRegion { | ||||
|   var isExpanded: Boolean | ||||
|   val startOffset: Int | ||||
|   val endOffset: Int | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -8,7 +8,6 @@ | ||||
|  | ||||
| package com.maddyhome.idea.vim.api | ||||
|  | ||||
| import com.maddyhome.idea.vim.common.forgetAllReplaceMasks | ||||
| import com.maddyhome.idea.vim.state.mode.Mode | ||||
|  | ||||
| abstract class VimEditorBase : VimEditor { | ||||
| @@ -19,9 +18,6 @@ abstract class VimEditorBase : VimEditor { | ||||
|       if (vimState.mode == value) return | ||||
|  | ||||
|       val oldValue = vimState.mode | ||||
|       if (oldValue == Mode.REPLACE) { | ||||
|         forgetAllReplaceMasks() | ||||
|       } | ||||
|       updateMode(value) | ||||
|       injector.listenersNotifier.notifyModeChanged(this, oldValue) | ||||
|     } | ||||
|   | ||||
| @@ -25,7 +25,6 @@ interface VimSearchGroup { | ||||
|    * Last used pattern to perform a substitution. | ||||
|    */ | ||||
|   var lastSubstitutePattern: String? | ||||
|  | ||||
|   fun searchBackward(editor: VimEditor, offset: Int, count: Int): TextRange? | ||||
|  | ||||
|   /** | ||||
| @@ -195,4 +194,17 @@ interface VimSearchGroup { | ||||
|    * Gets the direction lastly used in a search. | ||||
|    */ | ||||
|   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 direction       The direction to search | ||||
|    */ | ||||
|   @TestOnly | ||||
|   fun setLastSearchState( | ||||
|   override fun setLastSearchState( | ||||
|     pattern: String, | ||||
|     patternOffset: String, | ||||
|     direction: Direction, | ||||
|   | ||||
| @@ -37,6 +37,9 @@ class MappingState: Cloneable { | ||||
|  | ||||
|   val keys: Iterable<KeyStroke> | ||||
|     get() = keyList | ||||
|    | ||||
|   val hasKeys | ||||
|     get() = keyList.isNotEmpty() | ||||
|  | ||||
|   private var timer = VimTimer(injector.globalOptions().timeoutlen) | ||||
|   private var keyList = mutableListOf<KeyStroke>() | ||||
|   | ||||
| @@ -1,35 +0,0 @@ | ||||
| /* | ||||
|  * Copyright 2003-2024 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.common | ||||
|  | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
|  | ||||
| class VimEditorReplaceMask { | ||||
|   private val changedChars = mutableMapOf<LiveRange, Char>() | ||||
|  | ||||
|   fun recordChangeAtCaret(editor: VimEditor) { | ||||
|     for (caret in editor.carets()) { | ||||
|       val offset = caret.offset | ||||
|       val marker = editor.createLiveMarker(offset, offset) | ||||
|       changedChars[marker] = editor.charAt(offset) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   fun popChange(editor: VimEditor, offset: Int): Char? { | ||||
|     val marker = editor.createLiveMarker(offset, offset) | ||||
|     val change = changedChars[marker] | ||||
|     changedChars.remove(marker) | ||||
|     return change | ||||
|   } | ||||
| } | ||||
|  | ||||
| fun forgetAllReplaceMasks() { | ||||
|   injector.editorGroup.getEditors().forEach { it.replaceMask = null } | ||||
| } | ||||
| @@ -8,8 +8,6 @@ | ||||
|  | ||||
| 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.injector | ||||
| import com.maddyhome.idea.vim.state.mode.Mode | ||||
| @@ -73,9 +71,9 @@ class VimListenersNotifier { | ||||
|     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 | ||||
|     yankListeners.forEach { it.yankPerformed(caretToRange) } | ||||
|     yankListeners.forEach { it.yankPerformed(editor, range) } | ||||
|   } | ||||
|  | ||||
|   fun reset() { | ||||
| @@ -85,4 +83,4 @@ class VimListenersNotifier { | ||||
|     vimPluginListeners.clear() | ||||
|     isReplaceCharListeners.clear() | ||||
|   } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -8,8 +8,8 @@ | ||||
|  | ||||
| package com.maddyhome.idea.vim.common | ||||
|  | ||||
| import com.maddyhome.idea.vim.api.ImmutableVimCaret | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
|  | ||||
| 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) { | ||||
|     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 { | ||||
|   | ||||
| @@ -26,7 +26,6 @@ import com.maddyhome.idea.vim.api.VirtualFile | ||||
| import com.maddyhome.idea.vim.command.OperatorArguments | ||||
| import com.maddyhome.idea.vim.common.LiveRange | ||||
| import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.common.VimEditorReplaceMask | ||||
| import com.maddyhome.idea.vim.ex.ExException | ||||
| import com.maddyhome.idea.vim.helper.noneOfEnum | ||||
| import com.maddyhome.idea.vim.regexp.engine.VimRegexEngine | ||||
| @@ -607,7 +606,6 @@ class VimRegex(pattern: String) { | ||||
|     override var vimChangeActionSwitchMode: Mode? = null | ||||
|     override val indentConfig: VimIndentConfig | ||||
|       get() = TODO("Not yet implemented") | ||||
|     override var replaceMask: VimEditorReplaceMask? = null | ||||
|  | ||||
|     override fun fileSize(): Long { | ||||
|       return text.length.toLong() | ||||
| @@ -626,7 +624,13 @@ class VimRegex(pattern: String) { | ||||
|     override fun carets(): 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 forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean) {} | ||||
|   | ||||
| @@ -75,6 +75,11 @@ class Register { | ||||
|     addKeys(injector.parser.stringToKeys(text)) | ||||
|     transferableData.clear() | ||||
|   } | ||||
|    | ||||
|   fun prependTextAndResetTransferableData(text: String) { | ||||
|     this.keys.addAll(0, injector.parser.stringToKeys(text)) | ||||
|     transferableData.clear() | ||||
|   } | ||||
|  | ||||
|   fun addKeys(keys: List<KeyStroke>) { | ||||
|     this.keys.addAll(keys) | ||||
|   | ||||
| @@ -7,10 +7,9 @@ | ||||
|  */ | ||||
| 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.state.mode.SelectionType | ||||
| import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | ||||
| import org.jetbrains.annotations.TestOnly | ||||
| import javax.swing.KeyStroke | ||||
|  | ||||
| @@ -48,10 +47,11 @@ interface VimRegisterGroup { | ||||
|   /** Store text into the last register. */ | ||||
|   fun storeText( | ||||
|     editor: VimEditor, | ||||
|     caret: ImmutableVimCaret, | ||||
|     range: TextRange, | ||||
|     type: SelectionType, | ||||
|     isDelete: Boolean, | ||||
|     forceAppend: Boolean = false, | ||||
|     prependInsteadOfAppend: Boolean = false | ||||
|   ): Boolean | ||||
|  | ||||
|   /** | ||||
|   | ||||
| @@ -8,7 +8,6 @@ | ||||
|  | ||||
| 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.VimEditor | ||||
| import com.maddyhome.idea.vim.api.getText | ||||
| @@ -171,12 +170,13 @@ abstract class VimRegisterGroupBase : VimRegisterGroup { | ||||
|  | ||||
|   fun storeTextInternal( | ||||
|     editor: VimEditor, | ||||
|     caret: ImmutableVimCaret, | ||||
|     range: TextRange, | ||||
|     text: String, | ||||
|     type: SelectionType, | ||||
|     register: Char, | ||||
|     isDelete: Boolean, | ||||
|     forceAppend: Boolean, | ||||
|     prependInsteadOfAppend: Boolean, | ||||
|   ): Boolean { | ||||
|     // Null register doesn't get saved, but acts like it was | ||||
|     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 | ||||
|     val transferableData: List<Any> = | ||||
|       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 | ||||
|     logger.debug { | ||||
|       val transferableClasses = transferableData.joinToString(",") { it.javaClass.name } | ||||
|       "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 r = myRegisters[lreg] | ||||
|       // Append the text if the lowercase register existed | ||||
|       if (r != null) { | ||||
|         r.addTextAndResetTransferableData(processedText) | ||||
|         if (prependInsteadOfAppend) { | ||||
|           r.prependTextAndResetTransferableData(processedText) | ||||
|         } | ||||
|         else { | ||||
|           r.addTextAndResetTransferableData(processedText) | ||||
|         } | ||||
|       } else { | ||||
|         myRegisters[lreg] = Register(lreg, type, processedText, ArrayList(transferableData)) | ||||
|         logger.debug { "register '$register' contains: \"$processedText\"" } | ||||
| @@ -290,14 +301,15 @@ abstract class VimRegisterGroupBase : VimRegisterGroup { | ||||
|    */ | ||||
|   override fun storeText( | ||||
|     editor: VimEditor, | ||||
|     caret: ImmutableVimCaret, | ||||
|     range: TextRange, | ||||
|     type: SelectionType, | ||||
|     isDelete: Boolean, | ||||
|     forceAppend: Boolean, | ||||
|     prependInsteadOfAppend: Boolean | ||||
|   ): Boolean { | ||||
|     if (isRegisterWritable()) { | ||||
|       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 | ||||
|   | ||||
| @@ -10,7 +10,6 @@ package com.maddyhome.idea.vim.yank | ||||
|  | ||||
| import com.maddyhome.idea.vim.action.motion.updown.MotionDownLess1FirstNonSpaceAction | ||||
| 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.VimEditor | ||||
| 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.command.Argument | ||||
| 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.state.mode.SelectionType | ||||
| import org.jetbrains.annotations.Contract | ||||
| import kotlin.math.min | ||||
|  | ||||
| open class YankGroupBase : VimYankGroup { | ||||
|   private fun yankRange( | ||||
|     editor: VimEditor, | ||||
|     caretToRange: Map<ImmutableVimCaret, TextRange>, | ||||
|     range: TextRange, | ||||
|     type: SelectionType, | ||||
|     startOffsets: Map<VimCaret, Int>?, | ||||
| @@ -35,13 +33,8 @@ open class YankGroupBase : VimYankGroup { | ||||
|       caret.moveToOffset(offset) | ||||
|     } | ||||
|  | ||||
|     injector.listenersNotifier.notifyYankPerformed(caretToRange) | ||||
|  | ||||
|     var result = true | ||||
|     for ((caret, myRange) in caretToRange) { | ||||
|       result = caret.registerStorage.storeText(editor, myRange, type, false) && result | ||||
|     } | ||||
|     return result | ||||
|     injector.listenersNotifier.notifyYankPerformed(editor, range) | ||||
|     return injector.registerGroup.storeText(editor, range, type, false) | ||||
|   } | ||||
|  | ||||
|   @Contract("_, _ -> new") | ||||
| @@ -86,7 +79,6 @@ open class YankGroupBase : VimYankGroup { | ||||
|     val nativeCaretCount = editor.nativeCarets().size | ||||
|     if (nativeCaretCount <= 0) return false | ||||
|  | ||||
|     val caretToRange = HashMap<ImmutableVimCaret, TextRange>(nativeCaretCount) | ||||
|     val ranges = ArrayList<Pair<Int, Int>>(nativeCaretCount) | ||||
|  | ||||
|     // This logic is from original vim | ||||
| @@ -99,7 +91,6 @@ open class YankGroupBase : VimYankGroup { | ||||
|       assert(motionRange.size() == 1) | ||||
|       ranges.add(motionRange.startOffset to motionRange.endOffset) | ||||
|       startOffsets?.put(caret, motionRange.normalize().startOffset) | ||||
|       caretToRange[caret] = TextRange(motionRange.startOffset, motionRange.endOffset) | ||||
|     } | ||||
|  | ||||
|     val range = getTextRange(ranges, type) ?: return false | ||||
| @@ -108,7 +99,6 @@ open class YankGroupBase : VimYankGroup { | ||||
|  | ||||
|     return yankRange( | ||||
|       editor, | ||||
|       caretToRange, | ||||
|       range, | ||||
|       type, | ||||
|       startOffsets, | ||||
| @@ -125,7 +115,6 @@ open class YankGroupBase : VimYankGroup { | ||||
|   override fun yankLine(editor: VimEditor, count: Int): Boolean { | ||||
|     val caretCount = editor.nativeCarets().size | ||||
|     val ranges = ArrayList<Pair<Int, Int>>(caretCount) | ||||
|     val caretToRange = HashMap<ImmutableVimCaret, TextRange>(caretCount) | ||||
|     for (caret in editor.nativeCarets()) { | ||||
|       val start = injector.motion.moveCaretToCurrentLineStart(editor, caret) | ||||
|       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 | ||||
|  | ||||
|       ranges.add(start to end) | ||||
|       caretToRange[caret] = TextRange(start, end) | ||||
|     } | ||||
|  | ||||
|     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 { | ||||
|     range ?: return false | ||||
|     val caretToRange = HashMap<ImmutableVimCaret, TextRange>() | ||||
|  | ||||
|     if (type == SelectionType.LINE_WISE) { | ||||
|       for (i in 0 until range.size()) { | ||||
| @@ -170,19 +157,17 @@ open class YankGroupBase : VimYankGroup { | ||||
|     val startOffsets = HashMap<VimCaret, Int>(editor.nativeCarets().size) | ||||
|     if (type == SelectionType.BLOCK_WISE) { | ||||
|       startOffsets[editor.primaryCaret()] = range.normalize().startOffset | ||||
|       caretToRange[editor.primaryCaret()] = range | ||||
|     } else { | ||||
|       for ((i, caret) in editor.nativeCarets().withIndex()) { | ||||
|         val textRange = TextRange(rangeStartOffsets[i], rangeEndOffsets[i]) | ||||
|         startOffsets[caret] = textRange.normalize().startOffset | ||||
|         caretToRange[caret] = textRange | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return if (moveCursor) { | ||||
|       yankRange(editor, caretToRange, range, type, startOffsets) | ||||
|       yankRange(editor, range, type, startOffsets) | ||||
|     } else { | ||||
|       yankRange(editor, caretToRange, range, type, null) | ||||
|       yankRange(editor, range, type, null) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user