mirror of
				https://github.com/chylex/IntelliJ-IdeaVim.git
				synced 2025-11-02 23:31:30 +01:00 
			
		
		
		
	Compare commits
	
		
			16 Commits
		
	
	
		
			a978505530
			...
			customized
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						0612367c59
	
				 | 
					
					
						|||
| 
						
						
							
						
						755f05791a
	
				 | 
					
					
						|||
| 
						
						
							
						
						2d170dd15b
	
				 | 
					
					
						|||
| 
						
						
							
						
						943dffd43a
	
				 | 
					
					
						|||
| 
						
						
							
						
						f17e99dd46
	
				 | 
					
					
						|||
| 
						
						
							
						
						aa4caaa722
	
				 | 
					
					
						|||
| 
						
						
							
						
						4380b88cbd
	
				 | 
					
					
						|||
| 
						
						
							
						
						55ce038d51
	
				 | 
					
					
						|||
| 
						
						
							
						
						deec7eef2e
	
				 | 
					
					
						|||
| 
						
						
							
						
						2feffa9ff4
	
				 | 
					
					
						|||
| 
						
						
							
						
						f7f663f29a
	
				 | 
					
					
						|||
| 
						
						
							
						
						badbcd83d6
	
				 | 
					
					
						|||
| 
						
						
							
						
						d978901edf
	
				 | 
					
					
						|||
| 
						
						
							
						
						08940fdaba
	
				 | 
					
					
						|||
| 
						
						
							
						
						3a11fb9bd3
	
				 | 
					
					
						|||
| 
						
						
							
						
						fa9bb6adf4
	
				 | 
					
					
						
							
								
								
									
										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" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -353,8 +353,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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,11 +11,12 @@
 | 
				
			|||||||
ideaVersion=2023.3.2
 | 
					ideaVersion=2023.3.2
 | 
				
			||||||
downloadIdeaSources=true
 | 
					downloadIdeaSources=true
 | 
				
			||||||
instrumentPluginCode=true
 | 
					instrumentPluginCode=true
 | 
				
			||||||
version=SNAPSHOT
 | 
					version=chylex-23
 | 
				
			||||||
javaVersion=17
 | 
					javaVersion=17
 | 
				
			||||||
remoteRobotVersion=0.11.21
 | 
					remoteRobotVersion=0.11.21
 | 
				
			||||||
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.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -21,6 +21,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
 | 
				
			||||||
@@ -90,6 +91,9 @@ internal class MacroGroup : VimMacroBase() {
 | 
				
			|||||||
        } finally {
 | 
					        } finally {
 | 
				
			||||||
          keyStack.removeFirst()
 | 
					          keyStack.removeFirst()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if (!isInternalMacro) {
 | 
				
			||||||
 | 
					          MacroAutoImport.run(editor.ij, context.ij)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (isInternalMacro) {
 | 
					      if (isInternalMacro) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -192,8 +192,8 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
 | 
				
			|||||||
   * @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) {
 | 
				
			||||||
    setLastUsedPattern(pattern, RE_SEARCH, true);
 | 
					    setLastUsedPattern(pattern, RE_SEARCH, true);
 | 
				
			||||||
    lastIgnoreSmartCase = false;
 | 
					    lastIgnoreSmartCase = false;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -335,7 +335,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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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()
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -74,7 +74,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
 | 
				
			||||||
@@ -91,6 +90,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
 | 
				
			||||||
@@ -304,6 +305,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)
 | 
				
			||||||
      SearchGroup.fileEditorManagerSelectionChangedCallback(event)
 | 
					      SearchGroup.fileEditorManagerSelectionChangedCallback(event)
 | 
				
			||||||
@@ -368,8 +379,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) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -226,12 +226,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    <!-- Change -->
 | 
					    <!-- Change -->
 | 
				
			||||||
    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerMotionAction" mappingModes="N" keys="gu"/>
 | 
					    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerMotionAction" mappingModes="N" keys="gu"/>
 | 
				
			||||||
    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerVisualAction" mappingModes="X" keys="u"/>
 | 
					<!--    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerVisualAction" mappingModes="X" keys="u"/>-->
 | 
				
			||||||
    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleCharacterAction" mappingModes="N" keys="~"/>
 | 
					    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleCharacterAction" mappingModes="N" keys="~"/>
 | 
				
			||||||
    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleMotionAction" mappingModes="N" keys="g~"/>
 | 
					    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleMotionAction" mappingModes="N" keys="g~"/>
 | 
				
			||||||
    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleVisualAction" mappingModes="X" keys="~"/>
 | 
					    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleVisualAction" mappingModes="X" keys="~"/>
 | 
				
			||||||
    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperMotionAction" mappingModes="N" keys="gU"/>
 | 
					    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperMotionAction" mappingModes="N" keys="gU"/>
 | 
				
			||||||
    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperVisualAction" mappingModes="X" keys="U"/>
 | 
					<!--    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperVisualAction" mappingModes="X" keys="U"/>-->
 | 
				
			||||||
    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCharacterAction" mappingModes="N" keys="r"/>
 | 
					    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCharacterAction" mappingModes="N" keys="r"/>
 | 
				
			||||||
    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCharactersAction" mappingModes="N" keys="s"/>
 | 
					    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCharactersAction" mappingModes="N" keys="s"/>
 | 
				
			||||||
    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeEndOfLineAction" mappingModes="N" keys="C"/>
 | 
					    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeEndOfLineAction" mappingModes="N" keys="C"/>
 | 
				
			||||||
@@ -329,8 +329,8 @@
 | 
				
			|||||||
    <vimAction implementation="com.maddyhome.idea.vim.action.change.RepeatChangeAction" mappingModes="N" keys="."/>
 | 
					    <vimAction implementation="com.maddyhome.idea.vim.action.change.RepeatChangeAction" mappingModes="N" keys="."/>
 | 
				
			||||||
    <vimAction implementation="com.maddyhome.idea.vim.action.ExEntryAction" mappingModes="NXO" keys=":"/>
 | 
					    <vimAction implementation="com.maddyhome.idea.vim.action.ExEntryAction" mappingModes="NXO" keys=":"/>
 | 
				
			||||||
    <vimAction implementation="com.maddyhome.idea.vim.action.ResetModeAction" mappingModes="ALL" keys="«C-\»«C-N»"/>
 | 
					    <vimAction implementation="com.maddyhome.idea.vim.action.ResetModeAction" mappingModes="ALL" keys="«C-\»«C-N»"/>
 | 
				
			||||||
    <vimAction implementation="com.maddyhome.idea.vim.action.change.RedoAction" mappingModes="N" keys="«C-R»"/>
 | 
					    <vimAction implementation="com.maddyhome.idea.vim.action.change.RedoAction" mappingModes="NX" keys="U,«C-R»"/>
 | 
				
			||||||
    <vimAction implementation="com.maddyhome.idea.vim.action.change.UndoAction" mappingModes="N"/>
 | 
					    <vimAction implementation="com.maddyhome.idea.vim.action.change.UndoAction" mappingModes="NX"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- Keys -->
 | 
					    <!-- Keys -->
 | 
				
			||||||
    <vimAction implementation="com.maddyhome.idea.vim.action.change.OperatorAction" mappingModes="N" keys="g@"/>
 | 
					    <vimAction implementation="com.maddyhome.idea.vim.action.change.OperatorAction" mappingModes="N" keys="g@"/>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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>
 | 
				
			||||||
@@ -169,5 +161,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>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -102,7 +102,7 @@ import kotlin.test.assertTrue
 | 
				
			|||||||
 * This is done as we have no mechanism to guarantee compatibility as we update this test case.
 | 
					 * This is done as we have no mechanism to guarantee compatibility as we update this test case.
 | 
				
			||||||
 * Feel free to copy this class into your plugin, or copy just needed functions.
 | 
					 * Feel free to copy this class into your plugin, or copy just needed functions.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@RunInEdt(writeIntent = true)
 | 
					@RunInEdt
 | 
				
			||||||
@ApiStatus.Internal
 | 
					@ApiStatus.Internal
 | 
				
			||||||
abstract class VimTestCase {
 | 
					abstract class VimTestCase {
 | 
				
			||||||
  protected lateinit var fixture: CodeInsightTestFixture
 | 
					  protected lateinit var fixture: CodeInsightTestFixture
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ import com.maddyhome.idea.vim.handler.VimActionHandler
 | 
				
			|||||||
import java.awt.event.KeyEvent
 | 
					import java.awt.event.KeyEvent
 | 
				
			||||||
import javax.swing.KeyStroke
 | 
					import javax.swing.KeyStroke
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@CommandOrMotion(keys = ["u", "<Undo>"], modes = [Mode.NORMAL])
 | 
					@CommandOrMotion(keys = ["u", "<Undo>"], modes = [Mode.NORMAL, Mode.VISUAL])
 | 
				
			||||||
public class UndoAction : VimActionHandler.SingleExecution(), ComplicatedKeysAction {
 | 
					public class UndoAction : VimActionHandler.SingleExecution(), ComplicatedKeysAction {
 | 
				
			||||||
  override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
 | 
					  override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
 | 
				
			||||||
    injector.parser.parseKeys("u"),
 | 
					    injector.parser.parseKeys("u"),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -144,7 +144,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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@ import com.maddyhome.idea.vim.vimscript.model.VimLContext
 | 
				
			|||||||
public interface VimSearchGroup {
 | 
					public interface VimSearchGroup {
 | 
				
			||||||
  public var lastSearchPattern: String?
 | 
					  public var lastSearchPattern: String?
 | 
				
			||||||
  public var lastSubstitutePattern: String?
 | 
					  public var lastSubstitutePattern: String?
 | 
				
			||||||
 | 
					  public fun setLastSearchState(editor: VimEditor, pattern: String, patternOffset: String, direction: Direction?)
 | 
				
			||||||
  public fun findUnderCaret(editor: VimEditor): TextRange?
 | 
					  public fun findUnderCaret(editor: VimEditor): TextRange?
 | 
				
			||||||
  public fun searchBackward(editor: VimEditor, offset: Int, count: Int): TextRange?
 | 
					  public fun searchBackward(editor: VimEditor, offset: Int, count: Int): TextRange?
 | 
				
			||||||
  public fun getNextSearchRange(editor: VimEditor, count: Int, forwards: Boolean): TextRange?
 | 
					  public fun getNextSearchRange(editor: VimEditor, count: Int, forwards: Boolean): TextRange?
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -255,8 +255,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