mirror of
				https://github.com/chylex/IntelliJ-IdeaVim.git
				synced 2025-10-31 02:17:13 +01:00 
			
		
		
		
	Compare commits
	
		
			21 Commits
		
	
	
		
			0f0a73c139
			...
			customized
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 8de7a9f741 | |||
| 5977840cef | |||
| d0fea229c4 | |||
| 9eeac4a788 | |||
| 8370661b0d | |||
| 3f88e64abf | |||
| 28e3a7c894 | |||
| bc859c65f5 | |||
| 141fc8ed22 | |||
| dec494639c | |||
| d06688033b | |||
| 98110b9132 | |||
| a0404b8664 | |||
| a2a30d545e | |||
| 4f3bf039c9 | |||
| 1f4b97fa00 | |||
| 61301fdbf5 | |||
| c980250529 | |||
| bdb4b02016 | |||
| 62039a00d3 | |||
| 95c66fd4a6 | 
							
								
								
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | * text=auto eol=lf | ||||||
							
								
								
									
										1
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
								
							| @@ -6,6 +6,7 @@ | |||||||
|         <option name="CONTINUATION_INDENT_SIZE" value="4" /> |         <option name="CONTINUATION_INDENT_SIZE" value="4" /> | ||||||
|       </value> |       </value> | ||||||
|     </option> |     </option> | ||||||
|  |     <option name="LINE_SEPARATOR" value="
" /> | ||||||
|     <JavaCodeStyleSettings> |     <JavaCodeStyleSettings> | ||||||
|       <option name="FIELD_NAME_PREFIX" value="my" /> |       <option name="FIELD_NAME_PREFIX" value="my" /> | ||||||
|       <option name="STATIC_FIELD_NAME_PREFIX" value="our" /> |       <option name="STATIC_FIELD_NAME_PREFIX" value="our" /> | ||||||
|   | |||||||
| @@ -70,7 +70,7 @@ plugins { | |||||||
|   application |   application | ||||||
|   id("java-test-fixtures") |   id("java-test-fixtures") | ||||||
|  |  | ||||||
|   id("org.jetbrains.intellij") version "1.17.0" |   id("org.jetbrains.intellij") version "1.17.1" | ||||||
|   id("org.jetbrains.changelog") version "2.2.0" |   id("org.jetbrains.changelog") version "2.2.0" | ||||||
|  |  | ||||||
|   id("org.jetbrains.kotlinx.kover") version "0.6.1" |   id("org.jetbrains.kotlinx.kover") version "0.6.1" | ||||||
| @@ -308,8 +308,6 @@ tasks { | |||||||
|     val pluginVersion = version |     val pluginVersion = version | ||||||
|     // Don't forget to update plugin.xml |     // Don't forget to update plugin.xml | ||||||
|     patchPluginXml { |     patchPluginXml { | ||||||
|     sinceBuild.set("233.11799.30") |  | ||||||
|  |  | ||||||
|         // Get the latest available change notes from the changelog file |         // Get the latest available change notes from the changelog file | ||||||
|         changeNotes.set( |         changeNotes.set( | ||||||
|             provider { |             provider { | ||||||
|   | |||||||
| @@ -8,16 +8,17 @@ | |||||||
|  |  | ||||||
| # suppress inspection "UnusedProperty" for whole file | # suppress inspection "UnusedProperty" for whole file | ||||||
|  |  | ||||||
| ideaVersion=2023.3.2 | ideaVersion=2023.3.3 | ||||||
| # Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type | # Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type | ||||||
| ideaType=IC | ideaType=IC | ||||||
| downloadIdeaSources=true | downloadIdeaSources=true | ||||||
| instrumentPluginCode=true | instrumentPluginCode=true | ||||||
| version=SNAPSHOT | version=chylex-27 | ||||||
| javaVersion=17 | javaVersion=17 | ||||||
| remoteRobotVersion=0.11.22 | remoteRobotVersion=0.11.22 | ||||||
| antlrVersion=4.10.1 | antlrVersion=4.10.1 | ||||||
|  |  | ||||||
|  | kotlin.incremental.useClasspathSnapshot=false | ||||||
|  |  | ||||||
| # Please don't forget to update kotlin version in buildscript section | # Please don't forget to update kotlin version in buildscript section | ||||||
| # Also update kotlinxSerializationVersion version | # Also update kotlinxSerializationVersion version | ||||||
|   | |||||||
| @@ -217,6 +217,8 @@ private object FileTypePatterns { | |||||||
|  |  | ||||||
|     return if (fileTypeName in htmlLikeFileTypes) { |     return if (fileTypeName in htmlLikeFileTypes) { | ||||||
|       this.htmlPatterns |       this.htmlPatterns | ||||||
|  |     } else if (fileTypeName == "JAVA" || fileExtension == "java") { | ||||||
|  |       this.javaPatterns | ||||||
|     } else if (fileTypeName == "Ruby" || fileExtension == "rb") { |     } else if (fileTypeName == "Ruby" || fileExtension == "rb") { | ||||||
|       this.rubyPatterns |       this.rubyPatterns | ||||||
|     } else if (fileTypeName == "RHTML" || fileExtension == "erb") { |     } else if (fileTypeName == "RHTML" || fileExtension == "erb") { | ||||||
| @@ -231,7 +233,7 @@ private object FileTypePatterns { | |||||||
|     } else if (fileTypeName == "CMakeLists.txt" || fileName == "CMakeLists") { |     } else if (fileTypeName == "CMakeLists.txt" || fileName == "CMakeLists") { | ||||||
|       this.cMakePatterns |       this.cMakePatterns | ||||||
|     } else { |     } else { | ||||||
|       return null |       this.htmlPatterns | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -242,6 +244,7 @@ private object FileTypePatterns { | |||||||
|   ) |   ) | ||||||
|  |  | ||||||
|   private val htmlPatterns = createHtmlPatterns() |   private val htmlPatterns = createHtmlPatterns() | ||||||
|  |   private val javaPatterns = createJavaPatterns() | ||||||
|   private val rubyPatterns = createRubyPatterns() |   private val rubyPatterns = createRubyPatterns() | ||||||
|   private val rubyAndHtmlPatterns = rubyPatterns + htmlPatterns |   private val rubyAndHtmlPatterns = rubyPatterns + htmlPatterns | ||||||
|   private val phpPatterns = createPhpPatterns() |   private val phpPatterns = createPhpPatterns() | ||||||
| @@ -271,6 +274,14 @@ private object FileTypePatterns { | |||||||
|       ) |       ) | ||||||
|   } |   } | ||||||
|    |    | ||||||
|  |   private fun createJavaPatterns(): LanguagePatterns { | ||||||
|  |     return ( | ||||||
|  |         LanguagePatterns("\\b(?<!else\\s+)if\\b", "\\belse\\s+if\\b", "\\belse(?!\\s+if)\\b") + | ||||||
|  |           LanguagePatterns("\\bdo\\b", "\\bwhile\\b") + | ||||||
|  |           LanguagePatterns("\\btry\\b", "\\bcatch\\b", "\\bfinally\\b") | ||||||
|  |       ) | ||||||
|  |   } | ||||||
|  |  | ||||||
|   private fun createRubyPatterns(): LanguagePatterns { |   private fun createRubyPatterns(): LanguagePatterns { | ||||||
|     // Original patterns: https://github.com/vim/vim/blob/master/runtime/ftplugin/ruby.vim |     // Original patterns: https://github.com/vim/vim/blob/master/runtime/ftplugin/ruby.vim | ||||||
|     // We use non-capturing groups (?:) since we don't need any back refs. The \\b marker takes care of word boundaries. |     // We use non-capturing groups (?:) since we don't need any back refs. The \\b marker takes care of word boundaries. | ||||||
|   | |||||||
| @@ -502,6 +502,13 @@ internal class NerdTree : VimExtension { | |||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|     ) |     ) | ||||||
|  |      | ||||||
|  |     for (c in ('a'..'z') + ('A'..'Z')) { | ||||||
|  |       val ks = KeyStroke.getKeyStroke(c) | ||||||
|  |       if (ks !in actionsRoot) { | ||||||
|  |         registerCommand(c.toString(), NerdAction.Code { _, _, _ -> }) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   companion object { |   companion object { | ||||||
|   | |||||||
| @@ -11,8 +11,8 @@ package com.maddyhome.idea.vim.extension.paragraphmotion | |||||||
| import com.intellij.openapi.editor.Caret | import com.intellij.openapi.editor.Caret | ||||||
| import com.maddyhome.idea.vim.api.ExecutionContext | import com.maddyhome.idea.vim.api.ExecutionContext | ||||||
| import com.maddyhome.idea.vim.api.VimEditor | import com.maddyhome.idea.vim.api.VimEditor | ||||||
|  | import com.maddyhome.idea.vim.api.getLineEndForOffset | ||||||
| import com.maddyhome.idea.vim.api.injector | import com.maddyhome.idea.vim.api.injector | ||||||
| import com.maddyhome.idea.vim.api.normalizeOffset |  | ||||||
| import com.maddyhome.idea.vim.command.MappingMode | import com.maddyhome.idea.vim.command.MappingMode | ||||||
| import com.maddyhome.idea.vim.command.OperatorArguments | import com.maddyhome.idea.vim.command.OperatorArguments | ||||||
| import com.maddyhome.idea.vim.extension.ExtensionHandler | import com.maddyhome.idea.vim.extension.ExtensionHandler | ||||||
| @@ -45,8 +45,7 @@ internal class ParagraphMotion : VimExtension { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun moveCaretToNextParagraph(editor: VimEditor, caret: Caret, count: Int): Int? { |     fun moveCaretToNextParagraph(editor: VimEditor, caret: Caret, count: Int): Int? { | ||||||
|       return injector.searchHelper.findNextParagraph(editor, caret.vim, count, true) |       return injector.searchHelper.findNextParagraph(editor, caret.vim, count, true)?.let(editor::getLineEndForOffset) | ||||||
|         ?.let { editor.normalizeOffset(it, true) } |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -13,6 +13,7 @@ import com.intellij.openapi.editor.Editor | |||||||
| import com.maddyhome.idea.vim.VimPlugin | import com.maddyhome.idea.vim.VimPlugin | ||||||
| import com.maddyhome.idea.vim.api.ExecutionContext | import com.maddyhome.idea.vim.api.ExecutionContext | ||||||
| import com.maddyhome.idea.vim.api.VimCaret | import com.maddyhome.idea.vim.api.VimCaret | ||||||
|  | import com.maddyhome.idea.vim.api.VimChangeGroup | ||||||
| import com.maddyhome.idea.vim.api.VimEditor | import com.maddyhome.idea.vim.api.VimEditor | ||||||
| import com.maddyhome.idea.vim.api.endsWithNewLine | import com.maddyhome.idea.vim.api.endsWithNewLine | ||||||
| import com.maddyhome.idea.vim.api.getLeadingCharacterOffset | import com.maddyhome.idea.vim.api.getLeadingCharacterOffset | ||||||
| @@ -31,7 +32,10 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMa | |||||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing | import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing | ||||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction | import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction | ||||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret | import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret | ||||||
|  | import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore | ||||||
| import com.maddyhome.idea.vim.key.OperatorFunction | import com.maddyhome.idea.vim.key.OperatorFunction | ||||||
|  | import com.maddyhome.idea.vim.newapi.IjVimCaret | ||||||
|  | import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||||
| import com.maddyhome.idea.vim.newapi.ij | import com.maddyhome.idea.vim.newapi.ij | ||||||
| import com.maddyhome.idea.vim.newapi.vim | import com.maddyhome.idea.vim.newapi.vim | ||||||
| import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper | import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper | ||||||
| @@ -80,7 +84,7 @@ internal class VimSurroundExtension : VimExtension { | |||||||
|     override val isRepeatable = true |     override val isRepeatable = true | ||||||
|  |  | ||||||
|     override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { |     override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { | ||||||
|       setOperatorFunction(Operator()) |       setOperatorFunction(Operator(supportsMultipleCursors = false, count = 1)) // TODO | ||||||
|       executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij) |       executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij) | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -101,7 +105,7 @@ internal class VimSurroundExtension : VimExtension { | |||||||
|         val lastNonWhiteSpaceOffset = getLastNonWhitespaceCharacterOffset(editor.text(), lineStartOffset, lineEndOffset) |         val lastNonWhiteSpaceOffset = getLastNonWhitespaceCharacterOffset(editor.text(), lineStartOffset, lineEndOffset) | ||||||
|         if (lastNonWhiteSpaceOffset != null) { |         if (lastNonWhiteSpaceOffset != null) { | ||||||
|           val range = TextRange(lineStartOffset, lastNonWhiteSpaceOffset + 1) |           val range = TextRange(lineStartOffset, lastNonWhiteSpaceOffset + 1) | ||||||
|           performSurround(pair, range, it) |           performSurround(pair, range, it, count = operatorArguments.count1) | ||||||
|         } |         } | ||||||
| //        it.moveToOffset(lineStartOffset) | //        it.moveToOffset(lineStartOffset) | ||||||
|       } |       } | ||||||
| @@ -121,15 +125,13 @@ internal class VimSurroundExtension : VimExtension { | |||||||
|  |  | ||||||
|   private class VSurroundHandler : ExtensionHandler { |   private class VSurroundHandler : ExtensionHandler { | ||||||
|     override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { |     override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { | ||||||
|       val selectionStart = editor.ij.caretModel.primaryCaret.selectionStart |  | ||||||
|       // NB: Operator ignores SelectionType anyway |       // NB: Operator ignores SelectionType anyway | ||||||
|       if (!Operator().apply(editor, context, editor.mode.selectionType)) { |       if (!Operator(supportsMultipleCursors = true, count = operatorArguments.count1).apply(editor, context, editor.mode.selectionType)) { | ||||||
|         return |         return | ||||||
|       } |       } | ||||||
|       runWriteAction { |       runWriteAction { | ||||||
|         // Leave visual mode |         // Leave visual mode | ||||||
|         executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij) |         executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij) | ||||||
|         editor.ij.caretModel.moveToOffset(selectionStart) |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -150,6 +152,10 @@ internal class VimSurroundExtension : VimExtension { | |||||||
|  |  | ||||||
|     companion object { |     companion object { | ||||||
|       fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) { |       fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) { | ||||||
|  |         editor.ij.runWithEveryCaretAndRestore { changeAtCaret(editor, context, charFrom, newSurround) } | ||||||
|  |       } | ||||||
|  |        | ||||||
|  |       fun changeAtCaret(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) { | ||||||
|         // Save old register values for carets |         // Save old register values for carets | ||||||
|         val surroundings = editor.sortedCarets() |         val surroundings = editor.sortedCarets() | ||||||
|           .map { |           .map { | ||||||
| @@ -257,21 +263,42 @@ internal class VimSurroundExtension : VimExtension { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private class Operator : OperatorFunction { |   private class Operator(private val supportsMultipleCursors: Boolean, private val count: Int) : OperatorFunction { | ||||||
|     override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean { |     override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean { | ||||||
|       val ijEditor = editor.ij |       val editor = vimEditor.ij | ||||||
|       val c = getChar(ijEditor) |       val c = getChar(editor) | ||||||
|       if (c.code == 0) return true |       if (c.code == 0) return true | ||||||
|  |  | ||||||
|       val pair = getOrInputPair(c, ijEditor) ?: return false |       val pair = getOrInputPair(c, editor) ?: return false | ||||||
|       // XXX: Will it work with line-wise or block-wise selections? |  | ||||||
|       val range = getSurroundRange(editor.currentCaret()) ?: return false |       runWriteAction { | ||||||
|       performSurround(pair, range, editor.currentCaret(), selectionType == SelectionType.LINE_WISE) |         val change = VimPlugin.getChange() | ||||||
|  |         if (supportsMultipleCursors) { | ||||||
|  |           editor.runWithEveryCaretAndRestore { | ||||||
|  |             applyOnce(editor, change, pair, count) | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |           applyOnce(editor, change, pair, count) | ||||||
|           // Jump back to start |           // Jump back to start | ||||||
|       executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor) |           executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|       return true |       return true | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     private fun applyOnce(editor: Editor, change: VimChangeGroup, pair: Pair<String, String>, count: Int) { | ||||||
|  |       // XXX: Will it work with line-wise or block-wise selections? | ||||||
|  |       val primaryCaret = editor.caretModel.primaryCaret | ||||||
|  |       val range = getSurroundRange(primaryCaret.vim) | ||||||
|  |       if (range != null) { | ||||||
|  |         val start = RepeatedCharSequence.of(pair.first, count) | ||||||
|  |         val end = RepeatedCharSequence.of(pair.second, count) | ||||||
|  |         change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, start) | ||||||
|  |         change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.endOffset + start.length, end) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     private fun getSurroundRange(caret: VimCaret): TextRange? { |     private fun getSurroundRange(caret: VimCaret): TextRange? { | ||||||
|       val editor = caret.editor |       val editor = caret.editor | ||||||
|       val ijEditor = editor.ij |       val ijEditor = editor.ij | ||||||
| @@ -354,15 +381,15 @@ private fun getChar(editor: Editor): Char { | |||||||
|   return res |   return res | ||||||
| } | } | ||||||
|  |  | ||||||
| private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: VimCaret, tagsOnNewLines: Boolean = false) { | private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: VimCaret, count: Int, tagsOnNewLines: Boolean = false) { | ||||||
|   runWriteAction { |   runWriteAction { | ||||||
|     val editor = caret.editor |     val editor = caret.editor | ||||||
|     val change = VimPlugin.getChange() |     val change = VimPlugin.getChange() | ||||||
|     val leftSurround = pair.first + if (tagsOnNewLines) "\n" else "" |     val leftSurround = RepeatedCharSequence.of(pair.first + if (tagsOnNewLines) "\n" else "", count) | ||||||
|  |  | ||||||
|     val isEOF = range.endOffset == editor.text().length |     val isEOF = range.endOffset == editor.text().length | ||||||
|     val hasNewLine = editor.endsWithNewLine() |     val hasNewLine = editor.endsWithNewLine() | ||||||
|     val rightSurround = if (tagsOnNewLines) { |     val rightSurround = (if (tagsOnNewLines) { | ||||||
|       if (isEOF && !hasNewLine) { |       if (isEOF && !hasNewLine) { | ||||||
|         "\n" + pair.second |         "\n" + pair.second | ||||||
|       } else { |       } else { | ||||||
| @@ -370,7 +397,7 @@ private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: | |||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|       pair.second |       pair.second | ||||||
|     } |     }).let { RepeatedCharSequence.of(it, count) } | ||||||
|  |  | ||||||
|     change.insertText(editor, caret, range.startOffset, leftSurround) |     change.insertText(editor, caret, range.startOffset, leftSurround) | ||||||
|     change.insertText(editor, caret, range.endOffset + leftSurround.length, rightSurround) |     change.insertText(editor, caret, range.endOffset + leftSurround.length, rightSurround) | ||||||
|   | |||||||
| @@ -78,7 +78,6 @@ import java.math.BigInteger | |||||||
| import java.util.* | import java.util.* | ||||||
| import java.util.function.Consumer | import java.util.function.Consumer | ||||||
| import kotlin.math.max | import kotlin.math.max | ||||||
| import kotlin.math.min |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Provides all the insert/replace related functionality |  * Provides all the insert/replace related functionality | ||||||
| @@ -395,6 +394,7 @@ public class ChangeGroup : VimChangeGroupBase() { | |||||||
|     context: ExecutionContext, |     context: ExecutionContext, | ||||||
|     range: TextRange, |     range: TextRange, | ||||||
|   ) { |   ) { | ||||||
|  |     val startPos = editor.offsetToBufferPosition(caret.offset.point) | ||||||
|     val startOffset = editor.getLineStartForOffset(range.startOffset) |     val startOffset = editor.getLineStartForOffset(range.startOffset) | ||||||
|     val endOffset = editor.getLineEndForOffset(range.endOffset) |     val endOffset = editor.getLineEndForOffset(range.endOffset) | ||||||
|     val ijEditor = (editor as IjVimEditor).editor |     val ijEditor = (editor as IjVimEditor).editor | ||||||
| @@ -419,11 +419,7 @@ public class ChangeGroup : VimChangeGroupBase() { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     val afterAction = { |     val afterAction = { | ||||||
|       val firstLine = editor.offsetToBufferPosition( |       caret.moveToOffset(injector.motion.moveCaretToLineStartSkipLeading(editor, startPos.line)) | ||||||
|         min(startOffset.toDouble(), endOffset.toDouble()).toInt() |  | ||||||
|       ).line |  | ||||||
|       val newOffset = injector.motion.moveCaretToLineStartSkipLeading(editor, firstLine) |  | ||||||
|       caret.moveToOffset(newOffset) |  | ||||||
|       restoreCursor(editor, caret, (caret as IjVimCaret).caret.logicalPosition.line) |       restoreCursor(editor, caret, (caret as IjVimCaret).caret.logicalPosition.line) | ||||||
|     } |     } | ||||||
|     if (project != null) { |     if (project != null) { | ||||||
|   | |||||||
| @@ -83,7 +83,7 @@ public object IjOptions { | |||||||
|   public val trackactionids: ToggleOption = addOption(ToggleOption("trackactionids", GLOBAL, "tai", false)) |   public val trackactionids: ToggleOption = addOption(ToggleOption("trackactionids", GLOBAL, "tai", false)) | ||||||
|   public val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true)) |   public val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true)) | ||||||
|   public val visualdelay: UnsignedNumberOption = addOption(UnsignedNumberOption("visualdelay", GLOBAL, "visualdelay", 100)) |   public val visualdelay: UnsignedNumberOption = addOption(UnsignedNumberOption("visualdelay", GLOBAL, "visualdelay", 100)) | ||||||
|   public val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", false, isTemporary = true)) |   public val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isTemporary = true)) | ||||||
|   public val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isTemporary = true)) |   public val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isTemporary = true)) | ||||||
|   public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isTemporary = true)) |   public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isTemporary = true)) | ||||||
|   public val showmodewidget: ToggleOption = addOption(ToggleOption("showmodewidget", GLOBAL, "showmodewidget", false, isTemporary = true)) |   public val showmodewidget: ToggleOption = addOption(ToggleOption("showmodewidget", GLOBAL, "showmodewidget", false, isTemporary = 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 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -19,6 +19,7 @@ import com.maddyhome.idea.vim.api.injector | |||||||
| import com.maddyhome.idea.vim.helper.MessageHelper.message | import com.maddyhome.idea.vim.helper.MessageHelper.message | ||||||
| import com.maddyhome.idea.vim.macro.VimMacroBase | import com.maddyhome.idea.vim.macro.VimMacroBase | ||||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||||
|  | import com.maddyhome.idea.vim.newapi.ij | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Used to handle playback of macros |  * Used to handle playback of macros | ||||||
| @@ -88,6 +89,9 @@ internal class MacroGroup : VimMacroBase() { | |||||||
|         } finally { |         } finally { | ||||||
|           keyStack.removeFirst() |           keyStack.removeFirst() | ||||||
|         } |         } | ||||||
|  |         if (!isInternalMacro) { | ||||||
|  |           MacroAutoImport.run(editor.ij, context.ij) | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       if (isInternalMacro) { |       if (isInternalMacro) { | ||||||
|   | |||||||
| @@ -214,8 +214,8 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp | |||||||
|    * @param patternOffset   The pattern offset, e.g. `/{pattern}/{offset}` |    * @param patternOffset   The pattern offset, e.g. `/{pattern}/{offset}` | ||||||
|    * @param direction       The direction to search |    * @param direction       The direction to search | ||||||
|    */ |    */ | ||||||
|   @TestOnly |   @Override | ||||||
|   public void setLastSearchState(@SuppressWarnings("unused") @NotNull Editor editor, @NotNull String pattern, |   public void setLastSearchState(@SuppressWarnings("unused") @NotNull VimEditor editor, @NotNull String pattern, | ||||||
|                                  @NotNull String patternOffset, Direction direction) { |                                  @NotNull String patternOffset, Direction direction) { | ||||||
|     if (globalIjOptions(injector).getUseNewRegex()) { |     if (globalIjOptions(injector).getUseNewRegex()) { | ||||||
|       super.setLastSearchState(pattern, patternOffset, direction); |       super.setLastSearchState(pattern, patternOffset, direction); | ||||||
|   | |||||||
| @@ -329,7 +329,7 @@ public class EditorHelper { | |||||||
|  |  | ||||||
|     final int offset = y - ((screenHeight - lineHeight) / lineHeight / 2 * lineHeight); |     final int offset = y - ((screenHeight - lineHeight) / lineHeight / 2 * lineHeight); | ||||||
|     @NotNull final VimEditor editor1 = new IjVimEditor(editor); |     @NotNull final VimEditor editor1 = new IjVimEditor(editor); | ||||||
|     final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) - 1; |     final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) + editor.getSettings().getAdditionalLinesCount(); | ||||||
|     final int offsetForLastLineAtBottom = getOffsetToScrollVisualLineToBottomOfScreen(editor, lastVisualLine); |     final int offsetForLastLineAtBottom = getOffsetToScrollVisualLineToBottomOfScreen(editor, lastVisualLine); | ||||||
|  |  | ||||||
|     // For `zz`, we want to use virtual space and move any line, including the last one, to the middle of the screen. |     // For `zz`, we want to use virtual space and move any line, including the last one, to the middle of the screen. | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ package com.maddyhome.idea.vim.helper | |||||||
|  |  | ||||||
| import com.intellij.codeWithMe.ClientId | import com.intellij.codeWithMe.ClientId | ||||||
| import com.intellij.openapi.editor.Caret | import com.intellij.openapi.editor.Caret | ||||||
|  | import com.intellij.openapi.editor.CaretState | ||||||
| import com.intellij.openapi.editor.Editor | import com.intellij.openapi.editor.Editor | ||||||
| import com.intellij.openapi.editor.ex.util.EditorUtil | import com.intellij.openapi.editor.ex.util.EditorUtil | ||||||
| import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx | import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx | ||||||
| @@ -19,6 +20,8 @@ import com.intellij.util.ui.table.JBTableRowEditor | |||||||
| import com.maddyhome.idea.vim.api.injector | import com.maddyhome.idea.vim.api.injector | ||||||
| import com.maddyhome.idea.vim.group.IjOptionConstants | import com.maddyhome.idea.vim.group.IjOptionConstants | ||||||
| import com.maddyhome.idea.vim.newapi.globalIjOptions | import com.maddyhome.idea.vim.newapi.globalIjOptions | ||||||
|  | import com.maddyhome.idea.vim.newapi.vim | ||||||
|  | import com.maddyhome.idea.vim.state.mode.inBlockSelection | ||||||
| import java.awt.Component | import java.awt.Component | ||||||
| import javax.swing.JComponent | import javax.swing.JComponent | ||||||
| import javax.swing.JTable | import javax.swing.JTable | ||||||
| @@ -93,3 +96,41 @@ internal val Caret.vimLine: Int | |||||||
|  */ |  */ | ||||||
| internal val Editor.vimLine: Int | internal val Editor.vimLine: Int | ||||||
|   get() = this.caretModel.currentCaret.vimLine |   get() = this.caretModel.currentCaret.vimLine | ||||||
|  |  | ||||||
|  | internal inline fun Editor.runWithEveryCaretAndRestore(action: () -> Unit) { | ||||||
|  |   val caretModel = this.caretModel | ||||||
|  |   val carets = if (this.vim.inBlockSelection) null else caretModel.allCarets | ||||||
|  |   if (carets == null || carets.size == 1) { | ||||||
|  |     action() | ||||||
|  |   } | ||||||
|  |   else { | ||||||
|  |     var initialDocumentSize = this.document.textLength | ||||||
|  |     var documentSizeDifference = 0 | ||||||
|  |  | ||||||
|  |     val caretOffsets = carets.map { it.selectionStart to it.selectionEnd } | ||||||
|  |     val restoredCarets = mutableListOf<CaretState>() | ||||||
|  |  | ||||||
|  |     caretModel.removeSecondaryCarets() | ||||||
|  |      | ||||||
|  |     for ((selectionStart, selectionEnd) in caretOffsets) { | ||||||
|  |       if (selectionStart == selectionEnd) { | ||||||
|  |         caretModel.primaryCaret.moveToOffset(selectionStart + documentSizeDifference) | ||||||
|  |       } | ||||||
|  |       else { | ||||||
|  |         caretModel.primaryCaret.setSelection( | ||||||
|  |           selectionStart + documentSizeDifference, | ||||||
|  |           selectionEnd + documentSizeDifference | ||||||
|  |         ) | ||||||
|  |       } | ||||||
|  |        | ||||||
|  |       action() | ||||||
|  |       restoredCarets.add(caretModel.caretsAndSelections.single()) | ||||||
|  |  | ||||||
|  |       val documentLength = this.document.textLength | ||||||
|  |       documentSizeDifference += documentLength - initialDocumentSize | ||||||
|  |       initialDocumentSize = documentLength | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     caretModel.caretsAndSelections = restoredCarets | ||||||
|  |   }  | ||||||
|  | } | ||||||
|   | |||||||
| @@ -14,7 +14,6 @@ import com.maddyhome.idea.vim.api.injector | |||||||
| import com.maddyhome.idea.vim.api.normalizeVisualColumn | import com.maddyhome.idea.vim.api.normalizeVisualColumn | ||||||
| import com.maddyhome.idea.vim.api.options | import com.maddyhome.idea.vim.api.options | ||||||
| import com.maddyhome.idea.vim.command.CommandFlags | import com.maddyhome.idea.vim.command.CommandFlags | ||||||
| import com.maddyhome.idea.vim.state.VimStateMachine |  | ||||||
| import com.maddyhome.idea.vim.helper.EditorHelper.getApproximateScreenHeight | import com.maddyhome.idea.vim.helper.EditorHelper.getApproximateScreenHeight | ||||||
| import com.maddyhome.idea.vim.helper.EditorHelper.getApproximateScreenWidth | import com.maddyhome.idea.vim.helper.EditorHelper.getApproximateScreenWidth | ||||||
| import com.maddyhome.idea.vim.helper.EditorHelper.getNonNormalizedVisualLineAtBottomOfScreen | import com.maddyhome.idea.vim.helper.EditorHelper.getNonNormalizedVisualLineAtBottomOfScreen | ||||||
| @@ -29,6 +28,7 @@ import com.maddyhome.idea.vim.helper.EditorHelper.scrollVisualLineToBottomOfScre | |||||||
| import com.maddyhome.idea.vim.helper.EditorHelper.scrollVisualLineToMiddleOfScreen | import com.maddyhome.idea.vim.helper.EditorHelper.scrollVisualLineToMiddleOfScreen | ||||||
| import com.maddyhome.idea.vim.helper.EditorHelper.scrollVisualLineToTopOfScreen | import com.maddyhome.idea.vim.helper.EditorHelper.scrollVisualLineToTopOfScreen | ||||||
| import com.maddyhome.idea.vim.newapi.vim | import com.maddyhome.idea.vim.newapi.vim | ||||||
|  | import com.maddyhome.idea.vim.state.VimStateMachine | ||||||
| import kotlin.math.max | import kotlin.math.max | ||||||
| import kotlin.math.min | import kotlin.math.min | ||||||
| import kotlin.math.roundToInt | import kotlin.math.roundToInt | ||||||
| @@ -56,7 +56,7 @@ internal object ScrollViewHelper { | |||||||
|     // that this needs to be replaced as a more or less dumb line for line rewrite. |     // that this needs to be replaced as a more or less dumb line for line rewrite. | ||||||
|     val topLine = getVisualLineAtTopOfScreen(editor) |     val topLine = getVisualLineAtTopOfScreen(editor) | ||||||
|     val bottomLine = getVisualLineAtBottomOfScreen(editor) |     val bottomLine = getVisualLineAtBottomOfScreen(editor) | ||||||
|     val lastLine = vimEditor.getVisualLineCount() - 1 |     val lastLine = vimEditor.getVisualLineCount() + editor.settings.additionalLinesCount | ||||||
|  |  | ||||||
|     // We need the non-normalised value here, so we can handle cases such as so=999 to keep the current line centred |     // We need the non-normalised value here, so we can handle cases such as so=999 to keep the current line centred | ||||||
|     val scrollOffset = injector.options(vimEditor).scrolloff |     val scrollOffset = injector.options(vimEditor).scrolloff | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ | |||||||
| package com.maddyhome.idea.vim.helper; | package com.maddyhome.idea.vim.helper; | ||||||
|  |  | ||||||
| import com.google.common.collect.Lists; | import com.google.common.collect.Lists; | ||||||
|  | import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx; | ||||||
| import com.intellij.lang.CodeDocumentationAwareCommenter; | import com.intellij.lang.CodeDocumentationAwareCommenter; | ||||||
| import com.intellij.lang.Commenter; | import com.intellij.lang.Commenter; | ||||||
| import com.intellij.lang.Language; | import com.intellij.lang.Language; | ||||||
| @@ -16,22 +17,30 @@ import com.intellij.lang.LanguageCommenters; | |||||||
| import com.intellij.openapi.diagnostic.Logger; | import com.intellij.openapi.diagnostic.Logger; | ||||||
| import com.intellij.openapi.editor.Caret; | import com.intellij.openapi.editor.Caret; | ||||||
| import com.intellij.openapi.editor.Editor; | import com.intellij.openapi.editor.Editor; | ||||||
|  | import com.intellij.openapi.project.Project; | ||||||
| import com.intellij.psi.PsiComment; | import com.intellij.psi.PsiComment; | ||||||
| import com.intellij.psi.PsiElement; | import com.intellij.psi.PsiElement; | ||||||
| import com.intellij.psi.PsiFile; | import com.intellij.psi.PsiFile; | ||||||
| import com.intellij.psi.util.PsiTreeUtil; | import com.intellij.psi.util.PsiTreeUtil; | ||||||
|  | import com.intellij.spellchecker.SpellCheckerSeveritiesProvider; | ||||||
| import com.maddyhome.idea.vim.VimPlugin; | import com.maddyhome.idea.vim.VimPlugin; | ||||||
| import com.maddyhome.idea.vim.api.EngineEditorHelperKt; | import com.maddyhome.idea.vim.api.EngineEditorHelperKt; | ||||||
| import com.maddyhome.idea.vim.api.VimEditor; | import com.maddyhome.idea.vim.api.VimEditor; | ||||||
| import com.maddyhome.idea.vim.regexp.*; | import com.maddyhome.idea.vim.regexp.*; | ||||||
| import com.maddyhome.idea.vim.regexp.match.VimMatchResult; | import com.maddyhome.idea.vim.regexp.match.VimMatchResult; | ||||||
| import com.maddyhome.idea.vim.state.mode.Mode; |  | ||||||
| import com.maddyhome.idea.vim.state.VimStateMachine; |  | ||||||
| import com.maddyhome.idea.vim.common.CharacterPosition; | import com.maddyhome.idea.vim.common.CharacterPosition; | ||||||
| import com.maddyhome.idea.vim.common.Direction; | import com.maddyhome.idea.vim.common.Direction; | ||||||
| import com.maddyhome.idea.vim.common.TextRange; | import com.maddyhome.idea.vim.common.TextRange; | ||||||
| import com.maddyhome.idea.vim.newapi.IjVimCaret; | import com.maddyhome.idea.vim.newapi.IjVimCaret; | ||||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor; | import com.maddyhome.idea.vim.newapi.IjVimEditor; | ||||||
|  | import com.maddyhome.idea.vim.regexp.CharPointer; | ||||||
|  | import com.maddyhome.idea.vim.regexp.RegExp; | ||||||
|  | import com.maddyhome.idea.vim.state.VimStateMachine; | ||||||
|  | import com.maddyhome.idea.vim.state.mode.Mode; | ||||||
|  | import it.unimi.dsi.fastutil.ints.IntComparator; | ||||||
|  | import it.unimi.dsi.fastutil.ints.IntIterator; | ||||||
|  | import it.unimi.dsi.fastutil.ints.IntRBTreeSet; | ||||||
|  | import it.unimi.dsi.fastutil.ints.IntSortedSet; | ||||||
| import kotlin.Pair; | import kotlin.Pair; | ||||||
| import org.jetbrains.annotations.Contract; | import org.jetbrains.annotations.Contract; | ||||||
| import org.jetbrains.annotations.NotNull; | import org.jetbrains.annotations.NotNull; | ||||||
| @@ -1573,6 +1582,42 @@ public class SearchHelper { | |||||||
|     return PsiHelper.findMethodEnd(editor, caret.getOffset(), count); |     return PsiHelper.findMethodEnd(editor, caret.getOffset(), count); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public static int findMisspelledWords(@NotNull Editor editor, | ||||||
|  |                                        int startOffset, | ||||||
|  |                                        int endOffset, | ||||||
|  |                                        int skipCount, | ||||||
|  |                                        IntComparator offsetOrdering) { | ||||||
|  |     Project project = editor.getProject(); | ||||||
|  |     if (project == null) { | ||||||
|  |       return -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     IntSortedSet offsets = new IntRBTreeSet(offsetOrdering); | ||||||
|  |     DaemonCodeAnalyzerEx.processHighlights(editor.getDocument(), project, SpellCheckerSeveritiesProvider.TYPO, | ||||||
|  |                                            startOffset, endOffset, highlight -> { | ||||||
|  |         if (highlight.getSeverity() == SpellCheckerSeveritiesProvider.TYPO) { | ||||||
|  |           int offset = highlight.getStartOffset(); | ||||||
|  |           if (offset >= startOffset && offset <= endOffset) { | ||||||
|  |             offsets.add(offset); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |       }); | ||||||
|  |      | ||||||
|  |     if (offsets.isEmpty()) { | ||||||
|  |       return -1; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     if (skipCount >= offsets.size()) { | ||||||
|  |       return offsets.lastInt(); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       IntIterator offsetIterator = offsets.iterator(); | ||||||
|  |       offsetIterator.skip(skipCount); | ||||||
|  |       return offsetIterator.nextInt(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   private static @NotNull String parseMatchPairsOption(final VimEditor vimEditor) { |   private static @NotNull String parseMatchPairsOption(final VimEditor vimEditor) { | ||||||
|     List<String> pairs = options(injector, vimEditor).getMatchpairs(); |     List<String> pairs = options(injector, vimEditor).getMatchpairs(); | ||||||
|     StringBuilder res = new StringBuilder(); |     StringBuilder res = new StringBuilder(); | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ import com.intellij.openapi.command.CommandProcessor | |||||||
| import com.intellij.openapi.command.undo.UndoManager | import com.intellij.openapi.command.undo.UndoManager | ||||||
| import com.intellij.openapi.components.Service | import com.intellij.openapi.components.Service | ||||||
| import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider | import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider | ||||||
|  | import com.maddyhome.idea.vim.VimPlugin | ||||||
| import com.maddyhome.idea.vim.api.ExecutionContext | import com.maddyhome.idea.vim.api.ExecutionContext | ||||||
| import com.maddyhome.idea.vim.api.VimEditor | import com.maddyhome.idea.vim.api.VimEditor | ||||||
| import com.maddyhome.idea.vim.api.injector | import com.maddyhome.idea.vim.api.injector | ||||||
| @@ -21,6 +22,8 @@ import com.maddyhome.idea.vim.common.ChangesListener | |||||||
| import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor | import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor | ||||||
| import com.maddyhome.idea.vim.newapi.globalIjOptions | import com.maddyhome.idea.vim.newapi.globalIjOptions | ||||||
| import com.maddyhome.idea.vim.newapi.ij | import com.maddyhome.idea.vim.newapi.ij | ||||||
|  | import com.maddyhome.idea.vim.state.mode.SelectionType | ||||||
|  | import com.maddyhome.idea.vim.state.mode.inVisualMode | ||||||
| import com.maddyhome.idea.vim.undo.UndoRedoBase | import com.maddyhome.idea.vim.undo.UndoRedoBase | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -39,6 +42,7 @@ internal class UndoRedoHelper : UndoRedoBase() { | |||||||
|  |  | ||||||
|       if (injector.globalIjOptions().oldundo) { |       if (injector.globalIjOptions().oldundo) { | ||||||
|         SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) } |         SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) } | ||||||
|  |         restoreVisualMode(editor) | ||||||
|       } else { |       } else { | ||||||
|         // TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo |         // TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo | ||||||
|         editor.runWithChangeTracking { |         editor.runWithChangeTracking { | ||||||
| @@ -74,6 +78,7 @@ internal class UndoRedoHelper : UndoRedoBase() { | |||||||
|     if (undoManager.isRedoAvailable(fileEditor)) { |     if (undoManager.isRedoAvailable(fileEditor)) { | ||||||
|       if (injector.globalIjOptions().oldundo) { |       if (injector.globalIjOptions().oldundo) { | ||||||
|         SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) } |         SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) } | ||||||
|  |         restoreVisualMode(editor) | ||||||
|       } else { |       } else { | ||||||
|         undoManager.redo(fileEditor) |         undoManager.redo(fileEditor) | ||||||
|         CommandProcessor.getInstance().runUndoTransparentAction { |         CommandProcessor.getInstance().runUndoTransparentAction { | ||||||
| @@ -131,4 +136,21 @@ internal class UndoRedoHelper : UndoRedoBase() { | |||||||
|     val hasChanges: Boolean |     val hasChanges: Boolean | ||||||
|       get() = changeListener.hasChanged || initialPath != editor.getPath() |       get() = changeListener.hasChanged || initialPath != editor.getPath() | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   private fun restoreVisualMode(editor: VimEditor) { | ||||||
|  |     if (!editor.inVisualMode && editor.getSelectionModel().hasSelection()) { | ||||||
|  |       val detectedMode = VimPlugin.getVisualMotion().autodetectVisualSubmode(editor) | ||||||
|  |        | ||||||
|  |       // Visual block selection is restored into multiple carets, so multi-carets that form a block are always | ||||||
|  |       // identified as visual block mode, leading to false positives. | ||||||
|  |       // Since I use visual block mode much less often than multi-carets, this is a judgment call to never restore | ||||||
|  |       // visual block mode. | ||||||
|  |       val wantedMode = if (detectedMode == SelectionType.BLOCK_WISE) | ||||||
|  |         SelectionType.CHARACTER_WISE | ||||||
|  |       else | ||||||
|  |         detectedMode | ||||||
|  |        | ||||||
|  |       VimPlugin.getVisualMotion().enterVisualMode(editor, wantedMode) | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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" |  | ||||||
|     val instance: VimStandalonePluginUpdateChecker = service() |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -27,6 +27,7 @@ import com.intellij.openapi.actionSystem.CommonDataKeys | |||||||
| import com.intellij.openapi.actionSystem.ex.AnActionListener | import com.intellij.openapi.actionSystem.ex.AnActionListener | ||||||
| import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet | import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet | ||||||
| import com.intellij.openapi.editor.Editor | import com.intellij.openapi.editor.Editor | ||||||
|  | import com.intellij.openapi.editor.impl.ScrollingModelImpl | ||||||
| import com.intellij.openapi.project.DumbAwareToggleAction | import com.intellij.openapi.project.DumbAwareToggleAction | ||||||
| import com.intellij.openapi.util.TextRange | import com.intellij.openapi.util.TextRange | ||||||
| import com.maddyhome.idea.vim.KeyHandler | import com.maddyhome.idea.vim.KeyHandler | ||||||
| @@ -56,6 +57,7 @@ internal object IdeaSpecifics { | |||||||
|     private val surrounderAction = |     private val surrounderAction = | ||||||
|       "com.intellij.codeInsight.generation.surroundWith.SurroundWithHandler\$InvokeSurrounderAction" |       "com.intellij.codeInsight.generation.surroundWith.SurroundWithHandler\$InvokeSurrounderAction" | ||||||
|     private var editor: Editor? = null |     private var editor: Editor? = null | ||||||
|  |     private var caretOffset = -1 | ||||||
|     private var completionPrevDocumentLength: Int? = null |     private var completionPrevDocumentLength: Int? = null | ||||||
|     private var completionPrevDocumentOffset: Int? = null |     private var completionPrevDocumentOffset: Int? = null | ||||||
|     override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) { |     override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) { | ||||||
| @@ -64,6 +66,7 @@ internal object IdeaSpecifics { | |||||||
|       val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR) |       val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR) | ||||||
|       if (hostEditor != null) { |       if (hostEditor != null) { | ||||||
|         editor = hostEditor |         editor = hostEditor | ||||||
|  |         caretOffset = hostEditor.caretModel.offset | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       val isVimAction = (action as? AnActionWrapper)?.delegate is VimShortcutKeyAction |       val isVimAction = (action as? AnActionWrapper)?.delegate is VimShortcutKeyAction | ||||||
| @@ -95,7 +98,8 @@ internal object IdeaSpecifics { | |||||||
|       if (VimPlugin.isNotEnabled()) return |       if (VimPlugin.isNotEnabled()) return | ||||||
|  |  | ||||||
|       val editor = editor |       val editor = editor | ||||||
|       if (editor != null && action is ChooseItemAction && editor.vimStateMachine?.isRecording == true) { |       if (editor != null) { | ||||||
|  |         if (action is ChooseItemAction && editor.vimStateMachine?.isRecording == true) { | ||||||
|           val prevDocumentLength = completionPrevDocumentLength |           val prevDocumentLength = completionPrevDocumentLength | ||||||
|           val prevDocumentOffset = completionPrevDocumentOffset |           val prevDocumentOffset = completionPrevDocumentOffset | ||||||
|  |  | ||||||
| @@ -122,16 +126,28 @@ internal object IdeaSpecifics { | |||||||
|             ) |             ) | ||||||
|           } |           } | ||||||
|         ) { |         ) { | ||||||
|         editor?.let { |           val commandState = editor.vim.vimStateMachine | ||||||
|           val commandState = it.vim.vimStateMachine |  | ||||||
|           commandState.mode = Mode.NORMAL() |           commandState.mode = Mode.NORMAL() | ||||||
|           VimPlugin.getChange().insertBeforeCursor(it.vim, event.dataContext.vim) |           VimPlugin.getChange().insertBeforeCursor(editor.vim, event.dataContext.vim) | ||||||
|           KeyHandler.getInstance().reset(it.vim) |           KeyHandler.getInstance().reset(editor.vim) | ||||||
|         } |  | ||||||
|         } |         } | ||||||
|         //endregion |         //endregion | ||||||
|  |  | ||||||
|  |         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) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|       this.editor = null |       this.editor = null | ||||||
|  |       this.caretOffset = -1 | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -75,7 +75,6 @@ import com.maddyhome.idea.vim.handler.correctorRequester | |||||||
| import com.maddyhome.idea.vim.handler.keyCheckRequests | import com.maddyhome.idea.vim.handler.keyCheckRequests | ||||||
| import com.maddyhome.idea.vim.helper.GuicursorChangeListener | import com.maddyhome.idea.vim.helper.GuicursorChangeListener | ||||||
| import com.maddyhome.idea.vim.helper.StrictMode | import com.maddyhome.idea.vim.helper.StrictMode | ||||||
| import com.maddyhome.idea.vim.helper.VimStandalonePluginUpdateChecker |  | ||||||
| import com.maddyhome.idea.vim.helper.exitSelectMode | import com.maddyhome.idea.vim.helper.exitSelectMode | ||||||
| import com.maddyhome.idea.vim.helper.exitVisualMode | import com.maddyhome.idea.vim.helper.exitVisualMode | ||||||
| import com.maddyhome.idea.vim.helper.forceBarCursor | import com.maddyhome.idea.vim.helper.forceBarCursor | ||||||
| @@ -92,6 +91,8 @@ import com.maddyhome.idea.vim.listener.MouseEventsDataHolder.skipNDragEvents | |||||||
| import com.maddyhome.idea.vim.listener.VimListenerManager.EditorListeners.add | import com.maddyhome.idea.vim.listener.VimListenerManager.EditorListeners.add | ||||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||||
| import com.maddyhome.idea.vim.newapi.vim | import com.maddyhome.idea.vim.newapi.vim | ||||||
|  | import com.maddyhome.idea.vim.state.VimStateMachine | ||||||
|  | import com.maddyhome.idea.vim.state.mode.Mode | ||||||
| import com.maddyhome.idea.vim.state.mode.inSelectMode | import com.maddyhome.idea.vim.state.mode.inSelectMode | ||||||
| import com.maddyhome.idea.vim.state.mode.mode | import com.maddyhome.idea.vim.state.mode.mode | ||||||
| import com.maddyhome.idea.vim.state.mode.selectionType | import com.maddyhome.idea.vim.state.mode.selectionType | ||||||
| @@ -305,6 +306,16 @@ internal object VimListenerManager { | |||||||
|   class VimFileEditorManagerListener : FileEditorManagerListener { |   class VimFileEditorManagerListener : FileEditorManagerListener { | ||||||
|     override fun selectionChanged(event: FileEditorManagerEvent) { |     override fun selectionChanged(event: FileEditorManagerEvent) { | ||||||
|       if (VimPlugin.isNotEnabled()) return |       if (VimPlugin.isNotEnabled()) return | ||||||
|  |        | ||||||
|  |       val newEditor = event.newEditor | ||||||
|  |       if (newEditor is TextEditor) { | ||||||
|  |         val editor = newEditor.editor | ||||||
|  |         if (editor.isInsertMode) { | ||||||
|  |           VimStateMachine.getInstance(editor).mode = Mode.NORMAL() | ||||||
|  |           KeyHandler.getInstance().reset(editor.vim) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |        | ||||||
|       MotionGroup.fileEditorManagerSelectionChangedCallback(event) |       MotionGroup.fileEditorManagerSelectionChangedCallback(event) | ||||||
|       FileGroup.fileEditorManagerSelectionChangedCallback(event) |       FileGroup.fileEditorManagerSelectionChangedCallback(event) | ||||||
|       VimPlugin.getSearch().fileEditorManagerSelectionChangedCallback(event) |       VimPlugin.getSearch().fileEditorManagerSelectionChangedCallback(event) | ||||||
| @@ -369,8 +380,6 @@ internal object VimListenerManager { | |||||||
|  |  | ||||||
|         event.editor.putUserData(openingEditorKey, OpeningEditor(openingEditor, owningEditorWindow, isPreview, canBeReused)) |         event.editor.putUserData(openingEditorKey, OpeningEditor(openingEditor, owningEditorWindow, isPreview, canBeReused)) | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       VimStandalonePluginUpdateChecker.instance.pluginUsed() |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     override fun editorReleased(event: EditorFactoryEvent) { |     override fun editorReleased(event: EditorFactoryEvent) { | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ import com.maddyhome.idea.vim.vimscript.parser.VimscriptParser.parseExpression | |||||||
| import org.jetbrains.annotations.TestOnly | import org.jetbrains.annotations.TestOnly | ||||||
| import javax.swing.KeyStroke | import javax.swing.KeyStroke | ||||||
|  |  | ||||||
| public open class IjVimSearchGroup : VimSearchGroupBase() { | public abstract class IjVimSearchGroup : VimSearchGroupBase() { | ||||||
|  |  | ||||||
|   init { |   init { | ||||||
|     // TODO: Investigate migrating these listeners to use the effective value change listener |     // TODO: Investigate migrating these listeners to use the effective value change listener | ||||||
|   | |||||||
| @@ -29,6 +29,8 @@ import com.maddyhome.idea.vim.helper.checkInString | |||||||
| import com.maddyhome.idea.vim.helper.fileSize | import com.maddyhome.idea.vim.helper.fileSize | ||||||
| import com.maddyhome.idea.vim.state.VimStateMachine.Companion.getInstance | import com.maddyhome.idea.vim.state.VimStateMachine.Companion.getInstance | ||||||
| import com.maddyhome.idea.vim.state.mode.Mode.VISUAL | import com.maddyhome.idea.vim.state.mode.Mode.VISUAL | ||||||
|  | import it.unimi.dsi.fastutil.ints.IntComparator | ||||||
|  | import it.unimi.dsi.fastutil.ints.IntComparators | ||||||
| import java.util.* | import java.util.* | ||||||
| import java.util.function.Function | import java.util.function.Function | ||||||
| import java.util.regex.Pattern | import java.util.regex.Pattern | ||||||
| @@ -689,4 +691,26 @@ internal class IjVimSearchHelper : VimSearchHelperBase() { | |||||||
|     // End offset exclusive |     // End offset exclusive | ||||||
|     return TextRange(bstart, bend + 1) |     return TextRange(bstart, bend + 1) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   override fun findMisspelledWord(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int { | ||||||
|  |     val startOffset: Int | ||||||
|  |     val endOffset: Int | ||||||
|  |     val skipCount: Int | ||||||
|  |     val offsetOrdering: IntComparator | ||||||
|  |      | ||||||
|  |     if (count < 0) { | ||||||
|  |       startOffset = 0 | ||||||
|  |       endOffset = caret.offset.point - 1 | ||||||
|  |       skipCount = -count - 1 | ||||||
|  |       offsetOrdering = IntComparators.OPPOSITE_COMPARATOR | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       startOffset = caret.offset.point + 1 | ||||||
|  |       endOffset = editor.ij.document.textLength | ||||||
|  |       skipCount = count - 1 | ||||||
|  |       offsetOrdering = IntComparators.NATURAL_COMPARATOR | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return SearchHelper.findMisspelledWords(editor.ij, startOffset, endOffset, skipCount, offsetOrdering) | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -333,7 +333,7 @@ | |||||||
|  * |[m|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodPreviousStartAction} |  * |[m|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodPreviousStartAction} | ||||||
|  * |[p|                   {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorNoIndentAction} |  * |[p|                   {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorNoIndentAction} | ||||||
|  * |[p|                   {@link com.maddyhome.idea.vim.action.copy.PutTextAfterCursorNoIndentAction} |  * |[p|                   {@link com.maddyhome.idea.vim.action.copy.PutTextAfterCursorNoIndentAction} | ||||||
|  * |[s|                   TO BE IMPLEMENTED |  * |[s|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionMisspelledWordPreviousAction} | ||||||
|  * |[z|                   TO BE IMPLEMENTED |  * |[z|                   TO BE IMPLEMENTED | ||||||
|  * |[{|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedBraceOpenAction} |  * |[{|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedBraceOpenAction} | ||||||
|  * |]_CTRL-D|             TO BE IMPLEMENTED |  * |]_CTRL-D|             TO BE IMPLEMENTED | ||||||
| @@ -358,7 +358,7 @@ | |||||||
|  * |]m|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodNextStartAction} |  * |]m|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodNextStartAction} | ||||||
|  * |]p|                   {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorNoIndentAction} |  * |]p|                   {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorNoIndentAction} | ||||||
|  * |]p|                   {@link com.maddyhome.idea.vim.action.copy.PutTextAfterCursorNoIndentAction} |  * |]p|                   {@link com.maddyhome.idea.vim.action.copy.PutTextAfterCursorNoIndentAction} | ||||||
|  * |]s|                   TO BE IMPLEMENTED |  * |]s|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionMisspelledWordNextAction} | ||||||
|  * |]z|                   TO BE IMPLEMENTED |  * |]z|                   TO BE IMPLEMENTED | ||||||
|  * |]}|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedBraceCloseAction} |  * |]}|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedBraceCloseAction} | ||||||
|  * |  * | ||||||
|   | |||||||
| @@ -1,12 +1,4 @@ | |||||||
| <!-- | <idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude"> | ||||||
|   ~ Copyright 2003-2023 The IdeaVim authors |  | ||||||
|   ~ |  | ||||||
|   ~ Use of this source code is governed by an MIT-style |  | ||||||
|   ~ license that can be found in the LICENSE.txt file or at |  | ||||||
|   ~ https://opensource.org/licenses/MIT. |  | ||||||
|   --> |  | ||||||
|  |  | ||||||
| <idea-plugin url="https://plugins.jetbrains.com/plugin/164" xmlns:xi="http://www.w3.org/2001/XInclude"> |  | ||||||
|   <name>IdeaVim</name> |   <name>IdeaVim</name> | ||||||
|   <id>IdeaVIM</id> |   <id>IdeaVIM</id> | ||||||
|   <description><![CDATA[ |   <description><![CDATA[ | ||||||
| @@ -21,13 +13,13 @@ | |||||||
|         <li><a href="https://youtrack.jetbrains.com/issues/VIM">Issue tracker</a>: feature requests and bug reports</li> |         <li><a href="https://youtrack.jetbrains.com/issues/VIM">Issue tracker</a>: feature requests and bug reports</li> | ||||||
|       </ul> |       </ul> | ||||||
|     ]]></description> |     ]]></description> | ||||||
|   <version>SNAPSHOT</version> |   <version>chylex</version> | ||||||
|   <vendor>JetBrains</vendor> |   <vendor>JetBrains</vendor> | ||||||
|  |  | ||||||
|   <!-- Please search for "[VERSION UPDATE]" in project in case you update the since-build version --> |   <!-- Please search for "[VERSION UPDATE]" in project in case you update the since-build version --> | ||||||
|   <!-- Check for [Version Update] tag in YouTrack as well --> |   <!-- Check for [Version Update] tag in YouTrack as well --> | ||||||
|   <!-- Also, please update the value in build.gradle.kts file--> |   <!-- Also, please update the value in build.gradle.kts file--> | ||||||
|   <idea-version since-build="233.11799.30"/> |   <idea-version since-build="232"/> | ||||||
|  |  | ||||||
|   <!-- Mark the plugin as compatible with RubyMine and other products based on the IntelliJ platform (including CWM) --> |   <!-- Mark the plugin as compatible with RubyMine and other products based on the IntelliJ platform (including CWM) --> | ||||||
|   <depends>com.intellij.modules.platform</depends> |   <depends>com.intellij.modules.platform</depends> | ||||||
| @@ -158,5 +150,6 @@ | |||||||
|     </group> |     </group> | ||||||
|  |  | ||||||
|     <action id="VimFindActionIdAction" class="com.maddyhome.idea.vim.listener.FindActionIdAction"/> |     <action id="VimFindActionIdAction" class="com.maddyhome.idea.vim.listener.FindActionIdAction"/> | ||||||
|  |     <action id="VimJumpToSource" class="com.intellij.diff.actions.impl.OpenInEditorAction" /> | ||||||
|   </actions> |   </actions> | ||||||
| </idea-plugin> | </idea-plugin> | ||||||
|   | |||||||
| @@ -12,8 +12,9 @@ package org.jetbrains.plugins.ideavim.action.motion.gn | |||||||
|  |  | ||||||
| import com.maddyhome.idea.vim.VimPlugin | import com.maddyhome.idea.vim.VimPlugin | ||||||
| import com.maddyhome.idea.vim.api.injector | import com.maddyhome.idea.vim.api.injector | ||||||
| import com.maddyhome.idea.vim.state.mode.Mode |  | ||||||
| import com.maddyhome.idea.vim.common.Direction | import com.maddyhome.idea.vim.common.Direction | ||||||
|  | import com.maddyhome.idea.vim.newapi.vim | ||||||
|  | import com.maddyhome.idea.vim.state.mode.Mode | ||||||
| import org.jetbrains.plugins.ideavim.SkipNeovimReason | import org.jetbrains.plugins.ideavim.SkipNeovimReason | ||||||
| import org.jetbrains.plugins.ideavim.TestWithoutNeovim | import org.jetbrains.plugins.ideavim.TestWithoutNeovim | ||||||
| import org.jetbrains.plugins.ideavim.VimTestCase | import org.jetbrains.plugins.ideavim.VimTestCase | ||||||
| @@ -85,7 +86,7 @@ class GnNextTextObjectTest : VimTestCase() { | |||||||
|  |  | ||||||
|   private fun doTestWithSearch(keys: List<KeyStroke>, before: String, after: String) { |   private fun doTestWithSearch(keys: List<KeyStroke>, before: String, after: String) { | ||||||
|     configureByText(before) |     configureByText(before) | ||||||
|     VimPlugin.getSearch().setLastSearchState(fixture.editor, "test", "", Direction.FORWARDS) |     VimPlugin.getSearch().setLastSearchState(fixture.editor.vim, "test", "", Direction.FORWARDS) | ||||||
|     typeText(keys) |     typeText(keys) | ||||||
|     assertState(after) |     assertState(after) | ||||||
|     assertState(Mode.NORMAL()) |     assertState(Mode.NORMAL()) | ||||||
|   | |||||||
| @@ -12,8 +12,9 @@ package org.jetbrains.plugins.ideavim.action.motion.gn | |||||||
|  |  | ||||||
| import com.maddyhome.idea.vim.VimPlugin | import com.maddyhome.idea.vim.VimPlugin | ||||||
| import com.maddyhome.idea.vim.api.injector | import com.maddyhome.idea.vim.api.injector | ||||||
| import com.maddyhome.idea.vim.state.mode.Mode |  | ||||||
| import com.maddyhome.idea.vim.common.Direction | import com.maddyhome.idea.vim.common.Direction | ||||||
|  | import com.maddyhome.idea.vim.newapi.vim | ||||||
|  | import com.maddyhome.idea.vim.state.mode.Mode | ||||||
| import org.jetbrains.plugins.ideavim.SkipNeovimReason | import org.jetbrains.plugins.ideavim.SkipNeovimReason | ||||||
| import org.jetbrains.plugins.ideavim.TestWithoutNeovim | import org.jetbrains.plugins.ideavim.TestWithoutNeovim | ||||||
| import org.jetbrains.plugins.ideavim.VimTestCase | import org.jetbrains.plugins.ideavim.VimTestCase | ||||||
| @@ -63,7 +64,7 @@ class GnPreviousTextObjectTest : VimTestCase() { | |||||||
|  |  | ||||||
|   private fun doTestWithSearch(keys: List<KeyStroke>, before: String, after: String) { |   private fun doTestWithSearch(keys: List<KeyStroke>, before: String, after: String) { | ||||||
|     configureByText(before) |     configureByText(before) | ||||||
|     VimPlugin.getSearch().setLastSearchState(fixture.editor, "test", "", Direction.FORWARDS) |     VimPlugin.getSearch().setLastSearchState(fixture.editor.vim, "test", "", Direction.FORWARDS) | ||||||
|     typeText(keys) |     typeText(keys) | ||||||
|     assertState(after) |     assertState(after) | ||||||
|     assertState(Mode.NORMAL()) |     assertState(Mode.NORMAL()) | ||||||
|   | |||||||
| @@ -11,9 +11,10 @@ import com.intellij.idea.TestFor | |||||||
| import com.maddyhome.idea.vim.VimPlugin | import com.maddyhome.idea.vim.VimPlugin | ||||||
| import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction | import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction | ||||||
| import com.maddyhome.idea.vim.api.injector | import com.maddyhome.idea.vim.api.injector | ||||||
|  | import com.maddyhome.idea.vim.common.Direction | ||||||
|  | import com.maddyhome.idea.vim.newapi.vim | ||||||
| import com.maddyhome.idea.vim.state.mode.Mode | import com.maddyhome.idea.vim.state.mode.Mode | ||||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | import com.maddyhome.idea.vim.state.mode.SelectionType | ||||||
| import com.maddyhome.idea.vim.common.Direction |  | ||||||
| import org.jetbrains.plugins.ideavim.SkipNeovimReason | import org.jetbrains.plugins.ideavim.SkipNeovimReason | ||||||
| import org.jetbrains.plugins.ideavim.TestWithoutNeovim | import org.jetbrains.plugins.ideavim.TestWithoutNeovim | ||||||
| import org.jetbrains.plugins.ideavim.VimTestCase | import org.jetbrains.plugins.ideavim.VimTestCase | ||||||
| @@ -57,7 +58,7 @@ class VisualSelectNextSearchTest : VimTestCase() { | |||||||
|   @Test |   @Test | ||||||
|   fun testWithoutSpaces() { |   fun testWithoutSpaces() { | ||||||
|     configureByText("test<caret>test") |     configureByText("test<caret>test") | ||||||
|     VimPlugin.getSearch().setLastSearchState(fixture.editor, "test", "", Direction.FORWARDS) |     VimPlugin.getSearch().setLastSearchState(fixture.editor.vim, "test", "", Direction.FORWARDS) | ||||||
|     typeText(injector.parser.parseKeys("gn")) |     typeText(injector.parser.parseKeys("gn")) | ||||||
|     assertOffset(7) |     assertOffset(7) | ||||||
|     assertSelection("test") |     assertSelection("test") | ||||||
|   | |||||||
| @@ -11,9 +11,10 @@ import com.intellij.idea.TestFor | |||||||
| import com.maddyhome.idea.vim.VimPlugin | import com.maddyhome.idea.vim.VimPlugin | ||||||
| import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction | import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction | ||||||
| import com.maddyhome.idea.vim.api.injector | import com.maddyhome.idea.vim.api.injector | ||||||
|  | import com.maddyhome.idea.vim.common.Direction | ||||||
|  | import com.maddyhome.idea.vim.newapi.vim | ||||||
| import com.maddyhome.idea.vim.state.mode.Mode | import com.maddyhome.idea.vim.state.mode.Mode | ||||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | import com.maddyhome.idea.vim.state.mode.SelectionType | ||||||
| import com.maddyhome.idea.vim.common.Direction |  | ||||||
| import org.jetbrains.plugins.ideavim.SkipNeovimReason | import org.jetbrains.plugins.ideavim.SkipNeovimReason | ||||||
| import org.jetbrains.plugins.ideavim.TestWithoutNeovim | import org.jetbrains.plugins.ideavim.TestWithoutNeovim | ||||||
| import org.jetbrains.plugins.ideavim.VimTestCase | import org.jetbrains.plugins.ideavim.VimTestCase | ||||||
| @@ -54,7 +55,7 @@ class VisualSelectPreviousSearchTest : VimTestCase() { | |||||||
|   @Test |   @Test | ||||||
|   fun testWithoutSpaces() { |   fun testWithoutSpaces() { | ||||||
|     configureByText("tes<caret>ttest") |     configureByText("tes<caret>ttest") | ||||||
|     VimPlugin.getSearch().setLastSearchState(fixture.editor, "test", "", Direction.FORWARDS) |     VimPlugin.getSearch().setLastSearchState(fixture.editor.vim, "test", "", Direction.FORWARDS) | ||||||
|     typeText(injector.parser.parseKeys("gN")) |     typeText(injector.parser.parseKeys("gN")) | ||||||
|     assertOffset(0) |     assertOffset(0) | ||||||
|     assertSelection("test") |     assertSelection("test") | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ package org.jetbrains.plugins.ideavim.action.motion.search | |||||||
|  |  | ||||||
| import com.maddyhome.idea.vim.VimPlugin | import com.maddyhome.idea.vim.VimPlugin | ||||||
| import com.maddyhome.idea.vim.common.Direction | import com.maddyhome.idea.vim.common.Direction | ||||||
|  | import com.maddyhome.idea.vim.newapi.vim | ||||||
| import org.jetbrains.plugins.ideavim.SkipNeovimReason | import org.jetbrains.plugins.ideavim.SkipNeovimReason | ||||||
| import org.jetbrains.plugins.ideavim.TestWithoutNeovim | import org.jetbrains.plugins.ideavim.TestWithoutNeovim | ||||||
| import org.jetbrains.plugins.ideavim.VimTestCase | import org.jetbrains.plugins.ideavim.VimTestCase | ||||||
| @@ -167,7 +168,7 @@ class SearchAgainPreviousActionTest : VimTestCase() { | |||||||
|  |  | ||||||
|   private fun doTestWithSearch(keys: String, before: String, after: String) { |   private fun doTestWithSearch(keys: String, before: String, after: String) { | ||||||
|     doTest(keys, before, after) { |     doTest(keys, before, after) { | ||||||
|       VimPlugin.getSearch().setLastSearchState(it, "all", "", Direction.FORWARDS) |       VimPlugin.getSearch().setLastSearchState(it.vim, "all", "", Direction.FORWARDS) | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ import com.maddyhome.idea.vim.command.Command | |||||||
| import com.maddyhome.idea.vim.command.OperatorArguments | import com.maddyhome.idea.vim.command.OperatorArguments | ||||||
| import com.maddyhome.idea.vim.handler.VimActionHandler | import com.maddyhome.idea.vim.handler.VimActionHandler | ||||||
|  |  | ||||||
| @CommandOrMotion(keys = ["<C-R>"], modes = [Mode.NORMAL]) | @CommandOrMotion(keys = ["U", "<C-R>"], modes = [Mode.NORMAL, Mode.VISUAL]) | ||||||
| public class RedoAction : VimActionHandler.SingleExecution() { | public class RedoAction : VimActionHandler.SingleExecution() { | ||||||
|   override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED |   override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ import com.maddyhome.idea.vim.command.Command | |||||||
| import com.maddyhome.idea.vim.command.OperatorArguments | import com.maddyhome.idea.vim.command.OperatorArguments | ||||||
| import com.maddyhome.idea.vim.handler.VimActionHandler | import com.maddyhome.idea.vim.handler.VimActionHandler | ||||||
|  |  | ||||||
| @CommandOrMotion(keys = ["u", "<Undo>"], modes = [Mode.NORMAL]) | @CommandOrMotion(keys = ["u", "<Undo>"], modes = [Mode.NORMAL, Mode.VISUAL]) | ||||||
| public class UndoAction : VimActionHandler.SingleExecution() { | public class UndoAction : VimActionHandler.SingleExecution() { | ||||||
|   override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED |   override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,7 +8,6 @@ | |||||||
| package com.maddyhome.idea.vim.action.change.change | package com.maddyhome.idea.vim.action.change.change | ||||||
|  |  | ||||||
| import com.intellij.vim.annotations.CommandOrMotion | import com.intellij.vim.annotations.CommandOrMotion | ||||||
| import com.intellij.vim.annotations.Mode |  | ||||||
| import com.maddyhome.idea.vim.api.ExecutionContext | import com.maddyhome.idea.vim.api.ExecutionContext | ||||||
| import com.maddyhome.idea.vim.api.VimCaret | import com.maddyhome.idea.vim.api.VimCaret | ||||||
| import com.maddyhome.idea.vim.api.VimEditor | import com.maddyhome.idea.vim.api.VimEditor | ||||||
| @@ -22,7 +21,7 @@ import com.maddyhome.idea.vim.helper.CharacterHelper | |||||||
| /** | /** | ||||||
|  * @author vlan |  * @author vlan | ||||||
|  */ |  */ | ||||||
| @CommandOrMotion(keys = ["u"], modes = [Mode.VISUAL]) | @CommandOrMotion(keys = [], modes = []) | ||||||
| public class ChangeCaseLowerVisualAction : VisualOperatorActionHandler.ForEachCaret() { | public class ChangeCaseLowerVisualAction : VisualOperatorActionHandler.ForEachCaret() { | ||||||
|   override val type: Command.Type = Command.Type.CHANGE |   override val type: Command.Type = Command.Type.CHANGE | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,7 +8,6 @@ | |||||||
| package com.maddyhome.idea.vim.action.change.change | package com.maddyhome.idea.vim.action.change.change | ||||||
|  |  | ||||||
| import com.intellij.vim.annotations.CommandOrMotion | import com.intellij.vim.annotations.CommandOrMotion | ||||||
| import com.intellij.vim.annotations.Mode |  | ||||||
| import com.maddyhome.idea.vim.api.ExecutionContext | import com.maddyhome.idea.vim.api.ExecutionContext | ||||||
| import com.maddyhome.idea.vim.api.VimCaret | import com.maddyhome.idea.vim.api.VimCaret | ||||||
| import com.maddyhome.idea.vim.api.VimEditor | import com.maddyhome.idea.vim.api.VimEditor | ||||||
| @@ -22,7 +21,7 @@ import com.maddyhome.idea.vim.helper.CharacterHelper | |||||||
| /** | /** | ||||||
|  * @author vlan |  * @author vlan | ||||||
|  */ |  */ | ||||||
| @CommandOrMotion(keys = ["U"], modes = [Mode.VISUAL]) | @CommandOrMotion(keys = [], modes = []) | ||||||
| public class ChangeCaseUpperVisualAction : VisualOperatorActionHandler.ForEachCaret() { | public class ChangeCaseUpperVisualAction : VisualOperatorActionHandler.ForEachCaret() { | ||||||
|   override val type: Command.Type = Command.Type.CHANGE |   override val type: Command.Type = Command.Type.CHANGE | ||||||
|  |  | ||||||
|   | |||||||
| @@ -30,6 +30,7 @@ public class FilterVisualLinesAction : VimActionHandler.SingleExecution() { | |||||||
|   override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_MOT_LINEWISE) |   override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_MOT_LINEWISE) | ||||||
|  |  | ||||||
|   override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean { |   override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean { | ||||||
|  |     injector.markService.setVisualSelectionMarks(editor) | ||||||
|     injector.processGroup.startFilterCommand(editor, context, cmd) |     injector.processGroup.startFilterCommand(editor, context, cmd) | ||||||
|     editor.exitVisualMode() |     editor.exitVisualMode() | ||||||
|     return true |     return true | ||||||
|   | |||||||
| @@ -82,6 +82,13 @@ public sealed class TillCharacterMotion( | |||||||
|       ) |       ) | ||||||
|     } |     } | ||||||
|     injector.motion.setLastFTCmd(tillCharacterMotionType, argument.character) |     injector.motion.setLastFTCmd(tillCharacterMotionType, argument.character) | ||||||
|  |      | ||||||
|  |     val offset = if (!finishBeforeCharacter) "" | ||||||
|  |     else if (direction == Direction.FORWARDS) "s-1" | ||||||
|  |     else "s+1" | ||||||
|  |      | ||||||
|  |     injector.searchGroup.setLastSearchState(editor, argument.character.let { if (it == '.') "\\." else it.toString() }, offset, direction) | ||||||
|  |      | ||||||
|     return res.toMotionOrError() |     return res.toMotionOrError() | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,58 @@ | |||||||
|  | /* | ||||||
|  |  * 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.action.motion.text | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  | import com.maddyhome.idea.vim.command.CommandFlags | ||||||
|  | import com.maddyhome.idea.vim.command.MotionType | ||||||
|  | import com.maddyhome.idea.vim.command.OperatorArguments | ||||||
|  | import com.maddyhome.idea.vim.handler.Motion | ||||||
|  | import com.maddyhome.idea.vim.handler.MotionActionHandler | ||||||
|  | import com.maddyhome.idea.vim.handler.toMotionOrError | ||||||
|  | import com.maddyhome.idea.vim.helper.enumSetOf | ||||||
|  | import java.util.* | ||||||
|  |  | ||||||
|  | @CommandOrMotion(keys = ["]s"], modes = [Mode.NORMAL, Mode.VISUAL, Mode.OP_PENDING]) | ||||||
|  | public class MotionMisspelledWordNextAction : MotionActionHandler.ForEachCaret() { | ||||||
|  |   override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_JUMP) | ||||||
|  |  | ||||||
|  |   override fun getOffset( | ||||||
|  |     editor: VimEditor, | ||||||
|  |     caret: ImmutableVimCaret, | ||||||
|  |     context: ExecutionContext, | ||||||
|  |     argument: Argument?, | ||||||
|  |     operatorArguments: OperatorArguments, | ||||||
|  |   ): Motion { | ||||||
|  |     return injector.searchHelper.findMisspelledWord(editor, caret, operatorArguments.count1).toMotionOrError() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   override val motionType: MotionType = MotionType.EXCLUSIVE | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @CommandOrMotion(keys = ["[s"], modes = [Mode.NORMAL, Mode.VISUAL, Mode.OP_PENDING]) | ||||||
|  | public class MotionMisspelledWordPreviousAction : MotionActionHandler.ForEachCaret() { | ||||||
|  |   override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_JUMP) | ||||||
|  |  | ||||||
|  |   override fun getOffset( | ||||||
|  |     editor: VimEditor, | ||||||
|  |     caret: ImmutableVimCaret, | ||||||
|  |     context: ExecutionContext, | ||||||
|  |     argument: Argument?, | ||||||
|  |     operatorArguments: OperatorArguments, | ||||||
|  |   ): Motion { | ||||||
|  |     return injector.searchHelper.findMisspelledWord(editor, caret, -operatorArguments.count1).toMotionOrError() | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   override val motionType: MotionType = MotionType.EXCLUSIVE | ||||||
|  | } | ||||||
| @@ -145,7 +145,7 @@ public interface VimChangeGroup { | |||||||
|     operatorArguments: OperatorArguments, |     operatorArguments: OperatorArguments, | ||||||
|   ) |   ) | ||||||
|  |  | ||||||
|   public fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: String): VimCaret |   public fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: CharSequence): VimCaret | ||||||
|  |  | ||||||
|   public fun insertText(editor: VimEditor, caret: VimCaret, str: String): VimCaret |   public fun insertText(editor: VimEditor, caret: VimCaret, str: String): VimCaret | ||||||
|  |  | ||||||
|   | |||||||
| @@ -203,7 +203,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup { | |||||||
|    * @param caret  The caret to start insertion in |    * @param caret  The caret to start insertion in | ||||||
|    * @param str    The text to insert |    * @param str    The text to insert | ||||||
|    */ |    */ | ||||||
|   override fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: String): VimCaret { |   override fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: CharSequence): VimCaret { | ||||||
|     (editor as MutableVimEditor).insertText(Offset(offset), str) |     (editor as MutableVimEditor).insertText(Offset(offset), str) | ||||||
|     val newCaret = caret.moveToInlayAwareOffset(offset + str.length) |     val newCaret = caret.moveToInlayAwareOffset(offset + str.length) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ public interface VimSearchGroup { | |||||||
|    * Last used pattern to perform a substitution. |    * Last used pattern to perform a substitution. | ||||||
|    */ |    */ | ||||||
|   public var lastSubstitutePattern: String? |   public var lastSubstitutePattern: String? | ||||||
|  |   public fun setLastSearchState(editor: VimEditor, pattern: String, patternOffset: String, direction: Direction?) | ||||||
|   public fun searchBackward(editor: VimEditor, offset: Int, count: Int): TextRange? |   public fun searchBackward(editor: VimEditor, offset: Int, count: Int): TextRange? | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|   | |||||||
| @@ -239,4 +239,10 @@ public interface VimSearchHelper { | |||||||
|     count: Int, |     count: Int, | ||||||
|     isOuter: Boolean, |     isOuter: Boolean, | ||||||
|   ): TextRange? |   ): TextRange? | ||||||
|  |  | ||||||
|  |   public fun findMisspelledWord( | ||||||
|  |     editor: VimEditor, | ||||||
|  |     caret: ImmutableVimCaret, | ||||||
|  |     count: Int, | ||||||
|  |   ): Int | ||||||
| } | } | ||||||
|   | |||||||
| @@ -259,8 +259,13 @@ public class ToActionMappingInfo( | |||||||
|     LOG.debug("Executing 'ToAction' mapping...") |     LOG.debug("Executing 'ToAction' mapping...") | ||||||
|     val editorDataContext = injector.executionContextManager.onEditor(editor, context) |     val editorDataContext = injector.executionContextManager.onEditor(editor, context) | ||||||
|     val dataContext = injector.executionContextManager.onCaret(editor.currentCaret(), editorDataContext) |     val dataContext = injector.executionContextManager.onCaret(editor.currentCaret(), editorDataContext) | ||||||
|  |  | ||||||
|  |     val commandBuilder = editor.vimStateMachine.commandBuilder | ||||||
|  |     for (i in 0 until commandBuilder.count.coerceAtLeast(1)) { | ||||||
|       injector.actionExecutor.executeAction(action, dataContext) |       injector.actionExecutor.executeAction(action, dataContext) | ||||||
|     } |     } | ||||||
|  |     commandBuilder.resetCount() | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public companion object { |   public companion object { | ||||||
|     private val LOG = vimLogger<ToActionMappingInfo>() |     private val LOG = vimLogger<ToActionMappingInfo>() | ||||||
|   | |||||||
| @@ -19,7 +19,6 @@ import com.maddyhome.idea.vim.api.VimMarkService | |||||||
| import com.maddyhome.idea.vim.api.getText | import com.maddyhome.idea.vim.api.getText | ||||||
| import com.maddyhome.idea.vim.api.injector | import com.maddyhome.idea.vim.api.injector | ||||||
| import com.maddyhome.idea.vim.command.OperatorArguments | import com.maddyhome.idea.vim.command.OperatorArguments | ||||||
| import com.maddyhome.idea.vim.state.mode.SelectionType |  | ||||||
| import com.maddyhome.idea.vim.common.TextRange | import com.maddyhome.idea.vim.common.TextRange | ||||||
| import com.maddyhome.idea.vim.ex.ExException | import com.maddyhome.idea.vim.ex.ExException | ||||||
| import com.maddyhome.idea.vim.ex.InvalidRangeException | import com.maddyhome.idea.vim.ex.InvalidRangeException | ||||||
| @@ -29,6 +28,7 @@ import com.maddyhome.idea.vim.helper.Msg | |||||||
| import com.maddyhome.idea.vim.mark.Mark | import com.maddyhome.idea.vim.mark.Mark | ||||||
| import com.maddyhome.idea.vim.mark.VimMark | import com.maddyhome.idea.vim.mark.VimMark | ||||||
| import com.maddyhome.idea.vim.put.PutData | import com.maddyhome.idea.vim.put.PutData | ||||||
|  | import com.maddyhome.idea.vim.state.mode.SelectionType | ||||||
| import com.maddyhome.idea.vim.vimscript.model.ExecutionResult | import com.maddyhome.idea.vim.vimscript.model.ExecutionResult | ||||||
| import kotlin.math.min | import kotlin.math.min | ||||||
|  |  | ||||||
| @@ -49,23 +49,14 @@ public data class MoveTextCommand(val ranges: Ranges, val argument: String) : Co | |||||||
|     val caretPosition = caret.getBufferPosition() |     val caretPosition = caret.getBufferPosition() | ||||||
|  |  | ||||||
|     val goToLineCommand = injector.vimscriptParser.parseCommand(argument) ?: throw ExException("E16: Invalid range") |     val goToLineCommand = injector.vimscriptParser.parseCommand(argument) ?: throw ExException("E16: Invalid range") | ||||||
|  |  | ||||||
|     val range = getTextRange(editor, caret, false) |  | ||||||
|  |  | ||||||
|     /* |  | ||||||
|     FIXME: see VIM-2884. It's absolutely not the best way to resolve this bug |  | ||||||
|      */ |  | ||||||
|     caret.moveToOffset(range.startOffset) |  | ||||||
|  |  | ||||||
|     val lineRange = getLineRange(editor, caret) |     val lineRange = getLineRange(editor, caret) | ||||||
|  |  | ||||||
|     val line = min(editor.fileSize().toInt(), normalizeLine(editor, caret, goToLineCommand, lineRange)) |     val line = min(editor.fileSize().toInt(), normalizeLine(editor, caret, goToLineCommand, lineRange)) | ||||||
|     val linesMoved = lineRange.endLine - lineRange.startLine + 1 |     val range = getTextRange(editor, caret, false) | ||||||
|     if (line < -1 || line + linesMoved >= editor.lineCount()) { |  | ||||||
|       caret.moveToBufferPosition(caretPosition) |  | ||||||
|       throw ExException("E16: Invalid range") |  | ||||||
|     } |  | ||||||
|     val shift = line + 1 - editor.offsetToBufferPosition(range.startOffset).line |     val shift = line + 1 - editor.offsetToBufferPosition(range.startOffset).line | ||||||
|  |  | ||||||
|  |     val text = editor.getText(range) | ||||||
|  |  | ||||||
|     val localMarks = injector.markService.getAllLocalMarks(caret) |     val localMarks = injector.markService.getAllLocalMarks(caret) | ||||||
|       .filter { range.contains(it.offset(editor)) } |       .filter { range.contains(it.offset(editor)) } | ||||||
|       .filter { it.key != VimMarkService.SELECTION_START_MARK && it.key != VimMarkService.SELECTION_END_MARK } |       .filter { it.key != VimMarkService.SELECTION_START_MARK && it.key != VimMarkService.SELECTION_END_MARK } | ||||||
| @@ -78,33 +69,24 @@ public data class MoveTextCommand(val ranges: Ranges, val argument: String) : Co | |||||||
|     val selectionStartOffset = lastSelectionInfo.start?.let { editor.bufferPositionToOffset(it) } |     val selectionStartOffset = lastSelectionInfo.start?.let { editor.bufferPositionToOffset(it) } | ||||||
|     val selectionEndOffset = lastSelectionInfo.end?.let { editor.bufferPositionToOffset(it) } |     val selectionEndOffset = lastSelectionInfo.end?.let { editor.bufferPositionToOffset(it) } | ||||||
|  |  | ||||||
|     val text = editor.getText(range) |  | ||||||
|     val textData = PutData.TextData(text, SelectionType.LINE_WISE, emptyList(), null) |  | ||||||
|  |  | ||||||
|     val dropNewLineInEnd = (line + linesMoved == editor.lineCount() - 1 && text.last() == '\n') || |  | ||||||
|       (lineRange.endLine == editor.lineCount() - 1) |  | ||||||
|  |  | ||||||
|     editor.deleteString(range) |     editor.deleteString(range) | ||||||
|     val putData = if (line == -1) { |  | ||||||
|       caret.moveToOffset(0) |  | ||||||
|       PutData(textData, null, 1, insertTextBeforeCaret = true, rawIndent = true, caretAfterInsertedText = false) |  | ||||||
|     } else { |  | ||||||
|       PutData(textData, null, 1, insertTextBeforeCaret = false, rawIndent = true, caretAfterInsertedText = false, putToLine = line) |  | ||||||
|     } |  | ||||||
|     injector.put.putTextForCaret(editor, caret, context, putData) |  | ||||||
|  |  | ||||||
|     if (dropNewLineInEnd) { |     val textData = PutData.TextData(text, SelectionType.LINE_WISE, emptyList(), null) | ||||||
|       assert(editor.text().last() == '\n') |     val putData = PutData( | ||||||
|       editor.deleteString(TextRange(editor.text().length - 1, editor.text().length)) |       textData, | ||||||
|     } |       null, | ||||||
|  |       1, | ||||||
|  |       insertTextBeforeCaret = false, | ||||||
|  |       rawIndent = true, | ||||||
|  |       caretAfterInsertedText = false, | ||||||
|  |       putToLine = line | ||||||
|  |     ) | ||||||
|  |     injector.put.putTextForCaret(editor, caret, context, putData) | ||||||
|  |  | ||||||
|     globalMarks.forEach { shiftGlobalMark(editor, it, shift) } |     globalMarks.forEach { shiftGlobalMark(editor, it, shift) } | ||||||
|     localMarks.forEach { shiftLocalMark(caret, it, shift) } |     localMarks.forEach { shiftLocalMark(caret, it, shift) } | ||||||
|     shiftSelectionInfo(caret, selectionStartOffset, selectionEndOffset, lastSelectionInfo, shift, range) |     shiftSelectionInfo(caret, selectionStartOffset, selectionEndOffset, lastSelectionInfo, shift, range) | ||||||
|  |  | ||||||
|     val newCaretPosition = shiftBufferPosition(caretPosition, shift) |  | ||||||
|     caret.moveToBufferPosition(newCaretPosition) |  | ||||||
|  |  | ||||||
|     return ExecutionResult.Success |     return ExecutionResult.Success | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user