mirror of
				https://github.com/chylex/IntelliJ-IdeaVim.git
				synced 2025-10-31 02:17:13 +01:00 
			
		
		
		
	Compare commits
	
		
			19 Commits
		
	
	
		
			0f0a73c139
			...
			customized
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| c79286b9b0 | |||
| 5f59b47b19 | |||
| 8d51537f79 | |||
| 052de10e3a | |||
| 9ece9a7a04 | |||
| 84c868afc3 | |||
| f29ebab390 | |||
| 0cb8bba3fd | |||
| c0ff2b5cd0 | |||
| 460234553d | |||
| cdd5b2abaf | |||
| 9db1732eb3 | |||
| 63e292b21f | |||
| 362175431d | |||
| 5e2cab4eda | |||
| b63792c8f8 | |||
| f543b6a1d1 | |||
| d367b3bc72 | |||
| da2d8d707f | 
							
								
								
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| * text=auto eol=lf | ||||
							
								
								
									
										4
									
								
								.github/workflows/runUiOctopusTests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/runUiOctopusTests.yml
									
									
									
									
										vendored
									
									
								
							| @@ -23,7 +23,7 @@ jobs: | ||||
|       - name: Run Idea | ||||
|         run: | | ||||
|           mkdir -p build/reports | ||||
|           gradle testIdeUi -Doctopus.handler=false > build/reports/idea.log & | ||||
|           gradle runIdeForUiTests -Doctopus.handler=false > build/reports/idea.log & | ||||
|       - name: Wait for Idea started | ||||
|         uses: jtalk/url-health-check-action@v3 | ||||
|         with: | ||||
| @@ -63,7 +63,7 @@ jobs: | ||||
| #          export DISPLAY=:99.0 | ||||
| #          Xvfb -ac :99 -screen 0 1920x1080x16 & | ||||
| #          mkdir -p build/reports | ||||
| #          gradle :testIdeUi #> build/reports/idea.log | ||||
| #          gradle :runIdeForUiTests #> build/reports/idea.log | ||||
| #      - name: Wait for Idea started | ||||
| #        uses: jtalk/url-health-check-action@1.5 | ||||
| #        with: | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/runUiPyTests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/runUiPyTests.yml
									
									
									
									
										vendored
									
									
								
							| @@ -26,7 +26,7 @@ jobs: | ||||
|       - name: Run Idea | ||||
|         run: | | ||||
|           mkdir -p build/reports | ||||
|           gradle :testIdeUi -PideaType=PC > build/reports/idea.log & | ||||
|           gradle :runIdeForUiTests -PideaType=PC > build/reports/idea.log & | ||||
|       - name: Wait for Idea started | ||||
|         uses: jtalk/url-health-check-action@v3 | ||||
|         with: | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/workflows/runUiTests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/runUiTests.yml
									
									
									
									
										vendored
									
									
								
							| @@ -23,7 +23,7 @@ jobs: | ||||
|       - name: Run Idea | ||||
|         run: | | ||||
|           mkdir -p build/reports | ||||
|           gradle testIdeUi > build/reports/idea.log & | ||||
|           gradle runIdeForUiTests > build/reports/idea.log & | ||||
|       - name: Wait for Idea started | ||||
|         uses: jtalk/url-health-check-action@v3 | ||||
|         with: | ||||
| @@ -63,7 +63,7 @@ jobs: | ||||
| #          export DISPLAY=:99.0 | ||||
| #          Xvfb -ac :99 -screen 0 1920x1080x16 & | ||||
| #          mkdir -p build/reports | ||||
| #          gradle :testIdeUi #> build/reports/idea.log | ||||
| #          gradle :runIdeForUiTests #> build/reports/idea.log | ||||
| #      - name: Wait for Idea started | ||||
| #        uses: jtalk/url-health-check-action@1.5 | ||||
| #        with: | ||||
|   | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,5 @@ | ||||
| *.swp | ||||
| /.gradle/ | ||||
| /.intellijPlatform/ | ||||
|  | ||||
| /.idea/ | ||||
| !/.idea/scopes | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
|       <option name="taskNames"> | ||||
|         <list> | ||||
|           <option value="check" /> | ||||
|           <option value="verifyPlugin" /> | ||||
|           <option value="runPluginVerifier" /> | ||||
|         </list> | ||||
|       </option> | ||||
|       <option name="vmOptions" value="" /> | ||||
| @@ -20,7 +20,6 @@ | ||||
|     <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess> | ||||
|     <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess> | ||||
|     <DebugAllEnabled>false</DebugAllEnabled> | ||||
|     <RunAsTest>false</RunAsTest> | ||||
|     <method v="2" /> | ||||
|   </configuration> | ||||
| </component> | ||||
							
								
								
									
										2
									
								
								.teamcity/_Self/buildTypes/PluginVerifier.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.teamcity/_Self/buildTypes/PluginVerifier.kt
									
									
									
									
										vendored
									
									
								
							| @@ -22,7 +22,7 @@ object PluginVerifier : IdeaVimBuildType({ | ||||
|  | ||||
|   steps { | ||||
|     gradle { | ||||
|       tasks = "clean verifyPlugin" | ||||
|       tasks = "clean runPluginVerifier" | ||||
|       buildFile = "" | ||||
|       enableStacktrace = true | ||||
|     } | ||||
|   | ||||
							
								
								
									
										146
									
								
								build.gradle.kts
									
									
									
									
									
								
							
							
						
						
									
										146
									
								
								build.gradle.kts
									
									
									
									
									
								
							| @@ -32,8 +32,6 @@ import org.eclipse.jgit.api.Git | ||||
| import org.eclipse.jgit.lib.RepositoryBuilder | ||||
| import org.intellij.markdown.ast.getTextInNode | ||||
| import org.jetbrains.changelog.Changelog | ||||
| import org.jetbrains.intellij.platform.gradle.TestFrameworkType | ||||
| import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware | ||||
| import org.kohsuke.github.GHUser | ||||
| import java.net.HttpURLConnection | ||||
| import java.net.URL | ||||
| @@ -69,20 +67,22 @@ plugins { | ||||
|   kotlin("jvm") version "1.9.22" | ||||
|   application | ||||
|   id("java-test-fixtures") | ||||
|   id("org.jetbrains.intellij.platform") version "2.0.0-beta8" | ||||
|  | ||||
|   id("org.jetbrains.intellij") version "1.17.3" | ||||
|   id("org.jetbrains.changelog") version "2.2.0" | ||||
|  | ||||
|   id("org.jetbrains.kotlinx.kover") version "0.6.1" | ||||
|   id("com.dorongold.task-tree") version "4.0.0" | ||||
|  | ||||
|   id("com.google.devtools.ksp") version "1.9.22-1.0.17" | ||||
| } | ||||
|  | ||||
| val moduleSources by configurations.registering | ||||
|  | ||||
| // Import variables from gradle.properties file | ||||
| val javaVersion: String by project | ||||
| val kotlinVersion: String by project | ||||
| val ideaVersion: String by project | ||||
| val ideaType: String by project | ||||
| val downloadIdeaSources: String by project | ||||
| val instrumentPluginCode: String by project | ||||
| val remoteRobotVersion: String by project | ||||
| val splitModeVersion: String by project | ||||
| @@ -97,9 +97,7 @@ val releaseType: String? by project | ||||
|  | ||||
| repositories { | ||||
|   mavenCentral() | ||||
|   intellijPlatform { | ||||
|     defaultRepositories() | ||||
|   } | ||||
|   maven { url = uri("https://cache-redirector.jetbrains.com/intellij-dependencies") } | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
| @@ -110,27 +108,10 @@ dependencies { | ||||
|   compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") | ||||
|   compileOnly("org.jetbrains:annotations:24.1.0") | ||||
|  | ||||
|   intellijPlatform { | ||||
|     // Note that it is also possible to use local("...") to compile against a locally installed IDE | ||||
|     // E.g. local("/Users/{user}/Applications/IntelliJ IDEA Ultimate.app") | ||||
|     // Or something like: intellijIdeaUltimate(ideaVersion) | ||||
|     create(ideaType, ideaVersion) | ||||
|  | ||||
|     pluginVerifier() | ||||
|     zipSigner() | ||||
|     instrumentationTools() | ||||
|  | ||||
|     testFramework(TestFrameworkType.Platform) | ||||
|     testFramework(TestFrameworkType.JUnit5) | ||||
|  | ||||
|     // AceJump is an optional dependency. We use their SessionManager class to check if it's active | ||||
|     plugin("AceJump", "3.8.11") | ||||
|   } | ||||
|  | ||||
|   moduleSources(project(":vim-engine", "sourcesJarArtifacts")) | ||||
|  | ||||
|   // --------- Test dependencies ---------- | ||||
|  | ||||
|   testImplementation(testFixtures(project(":"))) | ||||
|  | ||||
|   testApi("com.squareup.okhttp3:okhttp:4.12.0") | ||||
|  | ||||
|   // https://mvnrepository.com/artifact/com.ensarsarajcic.neovim.java/neovim-api | ||||
| @@ -204,37 +185,15 @@ tasks { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Note that this will run the plugin installed in the IDE specified in dependencies. To run in a different IDE, use | ||||
|   // a custom task (see below) | ||||
|   runIde { | ||||
|     systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true) | ||||
|   } | ||||
|  | ||||
|   // Uncomment to run the plugin in a custom IDE, rather than the IDE specified as a compile target in dependencies | ||||
|   // Note that the version must be greater than the plugin's target version, for obvious reasons | ||||
| //  val runIdeCustom by intellijPlatformTesting.runIde.registering { | ||||
| //    type = IntelliJPlatformType.Rider | ||||
| //    version = "2024.1.2" | ||||
| //  } | ||||
|  | ||||
|   // Uncomment to run the plugin in a locally installed IDE | ||||
| //  val runIdeLocal by intellijPlatformTesting.runIde.registering { | ||||
| //    localPath = file("/Users/{user}/Applications/WebStorm.app") | ||||
| //  } | ||||
|  | ||||
|   val runIdeSplitMode by intellijPlatformTesting.runIde.registering { | ||||
|     splitMode = true | ||||
|     splitModeTarget = SplitModeAware.SplitModeTarget.FRONTEND | ||||
|  | ||||
|     // Frontend split mode support requires 242+ | ||||
|     // TODO: Remove this once IdeaVim targets 242, as the task will naturally use the target version to run | ||||
|     version.set(splitModeVersion) | ||||
|   downloadRobotServerPlugin { | ||||
|     version.set(remoteRobotVersion) | ||||
|   } | ||||
|  | ||||
|   // Start the default IDE with both IdeaVim and the robot server plugin installed, ready to run a UI test task. The | ||||
|   // robot server plugin is automatically added as a dependency to this task, and Gradle will take care of downloading. | ||||
|   // Note that the CustomTestIdeUiTask can be used to run tests against a different IDE | ||||
|   testIdeUi { | ||||
|   runIdeForUiTests { | ||||
|     systemProperty("robot-server.port", "8082") | ||||
|     systemProperty("ide.mac.message.dialogs.as.sheets", "false") | ||||
|     systemProperty("jb.privacy.policy.text", "<!--999.999-->") | ||||
| @@ -245,21 +204,28 @@ tasks { | ||||
|   } | ||||
|  | ||||
|   // Add plugin open API sources to the plugin ZIP | ||||
|   val sourcesJar by registering(Jar::class) { | ||||
|     dependsOn(moduleSources) | ||||
|     destinationDirectory.set(layout.buildDirectory.dir("libs")) | ||||
|     archiveClassifier.set(DocsType.SOURCES) | ||||
|     from(sourceSets.main.map { it.kotlin }) | ||||
|     from(provider { | ||||
|       moduleSources.map { | ||||
|         it.map { jarFile -> zipTree(jarFile) } | ||||
|   val createOpenApiSourceJar by registering(Jar::class) { | ||||
|     // Java sources | ||||
|     from(sourceSets.main.get().java) { | ||||
|       include("**/com/maddyhome/idea/vim/**/*.java") | ||||
|     } | ||||
|     }) | ||||
|     from(project(":vim-engine").sourceSets.main.get().java) { | ||||
|       include("**/com/maddyhome/idea/vim/**/*.java") | ||||
|     } | ||||
|     // Kotlin sources | ||||
|     from(kotlin.sourceSets.main.get().kotlin) { | ||||
|       include("**/com/maddyhome/idea/vim/**/*.kt") | ||||
|     } | ||||
|     from(project(":vim-engine").kotlin.sourceSets.main.get().kotlin) { | ||||
|       include("**/com/maddyhome/idea/vim/**/*.kt") | ||||
|     } | ||||
|     destinationDirectory.set(layout.buildDirectory.dir("libs")) | ||||
|     archiveClassifier.set("src") | ||||
|   } | ||||
|  | ||||
|   buildPlugin { | ||||
|     dependsOn(sourcesJar) | ||||
|     from(sourcesJar) { into("lib/src") } | ||||
|     dependsOn(createOpenApiSourceJar) | ||||
|     from(createOpenApiSourceJar) { into("lib/src") } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -284,44 +250,44 @@ gradle.projectsEvaluated { | ||||
|  | ||||
| // --- Intellij plugin | ||||
|  | ||||
| intellijPlatform { | ||||
|   pluginConfiguration { | ||||
|     name = "IdeaVim" | ||||
|     changeNotes.set( | ||||
|       """<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>""" | ||||
|     ) | ||||
| intellij { | ||||
|   version.set(ideaVersion) | ||||
|   type.set(ideaType) | ||||
|   pluginName.set("IdeaVim") | ||||
|  | ||||
|     ideaVersion { | ||||
|       // Let the Gradle plugin set the since-build version. It defaults to the version of the IDE we're building against | ||||
|       // specified as two components, `{branch}.{build}` (e.g., "241.15989"). There is no third component specified. | ||||
|       // The until-build version defaults to `{branch}.*`, but we want to support _all_ future versions, so we set it | ||||
|       // with a null provider (the provider is important). | ||||
|       // By letting the Gradle plugin handle this, the Plugin DevKit IntelliJ plugin cannot help us with the "Usage of | ||||
|       // IntelliJ API not available in older IDEs" inspection. However, since our since-build is the version we compile | ||||
|       // against, we can never get an API that's newer - it would be an unresolved symbol. | ||||
|       untilBuild.set(provider { null }) | ||||
|     } | ||||
|   } | ||||
|   updateSinceUntilBuild.set(false) | ||||
|  | ||||
|   publishing { | ||||
|   downloadSources.set(downloadIdeaSources.toBoolean()) | ||||
|   instrumentCode.set(instrumentPluginCode.toBoolean()) | ||||
|   intellijRepository.set("https://www.jetbrains.com/intellij-repository") | ||||
|   plugins.set(listOf("AceJump:3.8.11")) | ||||
| } | ||||
|  | ||||
| tasks { | ||||
|   publishPlugin { | ||||
|     channels.set(publishChannels.split(",")) | ||||
|     token.set(publishToken) | ||||
|   } | ||||
|  | ||||
|   signing { | ||||
|   signPlugin { | ||||
|     certificateChain.set(providers.environmentVariable("CERTIFICATE_CHAIN")) | ||||
|     privateKey.set(providers.environmentVariable("PRIVATE_KEY")) | ||||
|     password.set(providers.environmentVariable("PRIVATE_KEY_PASSWORD")) | ||||
|   } | ||||
|  | ||||
|   verifyPlugin { | ||||
|     teamCityOutputFormat = true | ||||
|     ides { | ||||
|       recommended() | ||||
|     } | ||||
|   runPluginVerifier { | ||||
|     downloadDir.set("${project.buildDir}/pluginVerifier/ides") | ||||
|     teamCityOutputFormat.set(true) | ||||
|   } | ||||
|  | ||||
|   instrumentCode.set(instrumentPluginCode.toBoolean()) | ||||
|   patchPluginXml { | ||||
|     // Don't forget to update plugin.xml | ||||
|     sinceBuild.set("241.15989.150") | ||||
|  | ||||
|     changeNotes.set( | ||||
|       """<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>""" | ||||
|     ) | ||||
|   } | ||||
| } | ||||
|  | ||||
| ksp { | ||||
| @@ -925,12 +891,12 @@ fun changes(): List<Change> { | ||||
|   println("Start changes processing") | ||||
|   for (message in messages) { | ||||
|     println("Processing '$message'...") | ||||
|     val lowercaseMessage = message.lowercase() | ||||
|     val lowercaseMessage = message.toLowerCase() | ||||
|     val regex = "^fix\\((vim-\\d+)\\):".toRegex() | ||||
|     val findResult = regex.find(lowercaseMessage) | ||||
|     if (findResult != null) { | ||||
|       println("Message matches") | ||||
|       val value = findResult.groups[1]!!.value.uppercase() | ||||
|       val value = findResult.groups[1]!!.value.toUpperCase() | ||||
|       val shortMessage = message.drop(findResult.range.last + 1).trim() | ||||
|       newFixes += Change(value, shortMessage) | ||||
|     } else { | ||||
|   | ||||
| @@ -19,8 +19,9 @@ | ||||
| ideaVersion=2024.1.1 | ||||
| # Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type | ||||
| ideaType=IC | ||||
| downloadIdeaSources=true | ||||
| instrumentPluginCode=true | ||||
| version=SNAPSHOT | ||||
| version=chylex-36 | ||||
| javaVersion=17 | ||||
| remoteRobotVersion=0.11.22 | ||||
| antlrVersion=4.10.1 | ||||
| @@ -47,7 +48,6 @@ youtrackToken= | ||||
|  | ||||
| # Gradle settings | ||||
| org.gradle.jvmargs='-Dfile.encoding=UTF-8' | ||||
| org.gradle.configuration-cache=true | ||||
| org.gradle.caching=true | ||||
|  | ||||
| # Disable warning from gradle-intellij-plugin. Kotlin stdlib is included as compileOnly, so the warning is unnecessary | ||||
|   | ||||
| @@ -68,7 +68,8 @@ object VimExtensionFacade { | ||||
|  | ||||
|  | ||||
|   @JvmStatic | ||||
|   @Deprecated("Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)", | ||||
|   @Deprecated( | ||||
|     "Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)", | ||||
|     ReplaceWith( | ||||
|       "VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)", | ||||
|       "com.maddyhome.idea.vim.VimPlugin" | ||||
| @@ -194,7 +195,7 @@ object VimExtensionFacade { | ||||
|  | ||||
|   @JvmStatic | ||||
|   fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? { | ||||
|     val reg = caret.registerStorage.getRegister(register) ?: return null | ||||
|     val reg = injector.registerGroup.getRegister(register) ?: return null | ||||
|     return reg.keys | ||||
|   } | ||||
|  | ||||
| @@ -207,7 +208,7 @@ object VimExtensionFacade { | ||||
|   /** Set the current contents of the given register */ | ||||
|   @JvmStatic | ||||
|   fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) { | ||||
|     caret.registerStorage.setKeys(register, keys?.filterNotNull() ?: emptyList()) | ||||
|     injector.registerGroup.setKeys(register, keys?.filterNotNull() ?: emptyList()) | ||||
|   } | ||||
|  | ||||
|   /** Set the current contents of the given register */ | ||||
|   | ||||
| @@ -234,7 +234,7 @@ private object FileTypePatterns { | ||||
|     } else if (fileTypeName == "CMakeLists.txt" || fileName == "CMakeLists") { | ||||
|       this.cMakePatterns | ||||
|     } else { | ||||
|       return null | ||||
|       this.htmlPatterns | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -144,7 +144,7 @@ internal class ReplaceWithRegister : VimExtension { | ||||
| private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) { | ||||
|   val registerGroup = injector.registerGroup | ||||
|   val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret() | ||||
|   val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return | ||||
|   val savedRegister = registerGroup.getRegister(lastRegisterChar) ?: return | ||||
|  | ||||
|   var usedType = savedRegister.type | ||||
|   var usedText = savedRegister.text | ||||
|   | ||||
| @@ -0,0 +1,30 @@ | ||||
| package com.maddyhome.idea.vim.extension.surround | ||||
|  | ||||
| import com.intellij.util.text.CharSequenceSubSequence | ||||
|  | ||||
| internal data class RepeatedCharSequence(val text: CharSequence, val count: Int) : CharSequence { | ||||
|   override val length = text.length * count | ||||
|  | ||||
|   override fun get(index: Int): Char { | ||||
|     if (index < 0 || index >= length) throw IndexOutOfBoundsException() | ||||
|     return text[index % text.length] | ||||
|   } | ||||
|  | ||||
|   override fun subSequence(startIndex: Int, endIndex: Int): CharSequence { | ||||
|     return CharSequenceSubSequence(this, startIndex, endIndex) | ||||
|   } | ||||
|  | ||||
|   override fun toString(): String { | ||||
|     return text.repeat(count) | ||||
|   } | ||||
|    | ||||
|   companion object { | ||||
|     fun of(text: CharSequence, count: Int): CharSequence { | ||||
|       return when (count) { | ||||
|         0 -> "" | ||||
|         1 -> text | ||||
|         else -> RepeatedCharSequence(text, count) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -14,6 +14,7 @@ import com.intellij.openapi.editor.Editor | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimCaret | ||||
| import com.maddyhome.idea.vim.api.VimChangeGroup | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.endsWithNewLine | ||||
| import com.maddyhome.idea.vim.api.getLeadingCharacterOffset | ||||
| @@ -35,7 +36,10 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret | ||||
| import com.maddyhome.idea.vim.extension.exportOperatorFunction | ||||
| import com.maddyhome.idea.vim.group.findBlockRange | ||||
| import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore | ||||
| import com.maddyhome.idea.vim.key.OperatorFunction | ||||
| import com.maddyhome.idea.vim.newapi.IjVimCaret | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper | ||||
| @@ -78,7 +82,7 @@ internal class VimSurroundExtension : VimExtension { | ||||
|       putKeyMappingIfMissing(MappingMode.XO, injector.parser.parseKeys("S"), owner, injector.parser.parseKeys("<Plug>VSurround"), true) | ||||
|     } | ||||
|  | ||||
|     VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator()) | ||||
|     VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator(supportsMultipleCursors = false, count = 1)) // TODO | ||||
|   } | ||||
|  | ||||
|   private class YSurroundHandler : ExtensionHandler { | ||||
| @@ -106,7 +110,7 @@ internal class VimSurroundExtension : VimExtension { | ||||
|         val lastNonWhiteSpaceOffset = getLastNonWhitespaceCharacterOffset(editor.text(), lineStartOffset, lineEndOffset) | ||||
|         if (lastNonWhiteSpaceOffset != null) { | ||||
|           val range = TextRange(lineStartOffset, lastNonWhiteSpaceOffset + 1) | ||||
|           performSurround(pair, range, it) | ||||
|           performSurround(pair, range, it, count = operatorArguments.count1) | ||||
|         } | ||||
| //        it.moveToOffset(lineStartOffset) | ||||
|       } | ||||
| @@ -126,15 +130,13 @@ internal class VimSurroundExtension : VimExtension { | ||||
|  | ||||
|   private class VSurroundHandler : ExtensionHandler { | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { | ||||
|       val selectionStart = editor.ij.caretModel.primaryCaret.selectionStart | ||||
|       // NB: Operator ignores SelectionType anyway | ||||
|       if (!Operator().apply(editor, context, editor.mode.selectionType)) { | ||||
|       if (!Operator(supportsMultipleCursors = true, count = operatorArguments.count1).apply(editor, context, editor.mode.selectionType)) { | ||||
|         return | ||||
|       } | ||||
|       runWriteAction { | ||||
|         // Leave visual mode | ||||
|         executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij) | ||||
|         editor.ij.caretModel.moveToOffset(selectionStart) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -155,6 +157,10 @@ internal class VimSurroundExtension : VimExtension { | ||||
|  | ||||
|     companion object { | ||||
|       fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) { | ||||
|         editor.ij.runWithEveryCaretAndRestore { changeAtCaret(editor, context, charFrom, newSurround) } | ||||
|       } | ||||
|        | ||||
|       fun changeAtCaret(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) { | ||||
|         // Save old register values for carets | ||||
|         val surroundings = editor.sortedCarets() | ||||
|           .map { | ||||
| @@ -262,21 +268,42 @@ internal class VimSurroundExtension : VimExtension { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private class Operator : OperatorFunction { | ||||
|     override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean { | ||||
|       val ijEditor = editor.ij | ||||
|   private class Operator(private val supportsMultipleCursors: Boolean, private val count: Int) : OperatorFunction { | ||||
|     override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean { | ||||
|       val ijEditor = vimEditor.ij | ||||
|       val c = getChar(ijEditor) | ||||
|       if (c.code == 0) return true | ||||
|  | ||||
|       val pair = getOrInputPair(c, ijEditor, context.ij) ?: return false | ||||
|       // XXX: Will it work with line-wise or block-wise selections? | ||||
|       val range = getSurroundRange(editor.currentCaret()) ?: return false | ||||
|       performSurround(pair, range, editor.currentCaret(), selectionType == SelectionType.LINE_WISE) | ||||
|  | ||||
|       runWriteAction { | ||||
|         val change = VimPlugin.getChange() | ||||
|         if (supportsMultipleCursors) { | ||||
|           ijEditor.runWithEveryCaretAndRestore { | ||||
|             applyOnce(ijEditor, change, pair, count) | ||||
|           } | ||||
|         } | ||||
|         else { | ||||
|           applyOnce(ijEditor, change, pair, count) | ||||
|           // Jump back to start | ||||
|           executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor) | ||||
|         } | ||||
|       } | ||||
|       return true | ||||
|     } | ||||
|      | ||||
|     private fun applyOnce(editor: Editor, change: VimChangeGroup, pair: Pair<String, String>, count: Int) { | ||||
|       // XXX: Will it work with line-wise or block-wise selections? | ||||
|       val primaryCaret = editor.caretModel.primaryCaret | ||||
|       val range = getSurroundRange(primaryCaret.vim) | ||||
|       if (range != null) { | ||||
|         val start = RepeatedCharSequence.of(pair.first, count) | ||||
|         val end = RepeatedCharSequence.of(pair.second, count) | ||||
|         change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, start) | ||||
|         change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.endOffset + start.length, end) | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     private fun getSurroundRange(caret: VimCaret): TextRange? { | ||||
|       val editor = caret.editor | ||||
|       val ijEditor = editor.ij | ||||
| @@ -362,15 +389,15 @@ private fun getChar(editor: Editor): Char { | ||||
|   return res | ||||
| } | ||||
|  | ||||
| private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: VimCaret, tagsOnNewLines: Boolean = false) { | ||||
| private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: VimCaret, count: Int, tagsOnNewLines: Boolean = false) { | ||||
|   runWriteAction { | ||||
|     val editor = caret.editor | ||||
|     val change = VimPlugin.getChange() | ||||
|     val leftSurround = pair.first + if (tagsOnNewLines) "\n" else "" | ||||
|     val leftSurround = RepeatedCharSequence.of(pair.first + if (tagsOnNewLines) "\n" else "", count) | ||||
|  | ||||
|     val isEOF = range.endOffset == editor.text().length | ||||
|     val hasNewLine = editor.endsWithNewLine() | ||||
|     val rightSurround = if (tagsOnNewLines) { | ||||
|     val rightSurround = (if (tagsOnNewLines) { | ||||
|       if (isEOF && !hasNewLine) { | ||||
|         "\n" + pair.second | ||||
|       } else { | ||||
| @@ -378,7 +405,7 @@ private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: | ||||
|       } | ||||
|     } else { | ||||
|       pair.second | ||||
|     } | ||||
|     }).let { RepeatedCharSequence.of(it, count) } | ||||
|  | ||||
|     change.insertText(editor, caret, range.startOffset, leftSurround) | ||||
|     change.insertText(editor, caret, range.endOffset + leftSurround.length, rightSurround) | ||||
|   | ||||
| @@ -80,7 +80,6 @@ import java.math.BigInteger | ||||
| import java.util.* | ||||
| import java.util.function.Consumer | ||||
| import kotlin.math.max | ||||
| import kotlin.math.min | ||||
|  | ||||
| /** | ||||
|  * Provides all the insert/replace related functionality | ||||
| @@ -399,6 +398,7 @@ class ChangeGroup : VimChangeGroupBase() { | ||||
|     context: ExecutionContext, | ||||
|     range: TextRange, | ||||
|   ) { | ||||
|     val startPos = editor.offsetToBufferPosition(caret.offset) | ||||
|     val startOffset = editor.getLineStartForOffset(range.startOffset) | ||||
|     val endOffset = editor.getLineEndForOffset(range.endOffset) | ||||
|     val ijEditor = (editor as IjVimEditor).editor | ||||
| @@ -423,11 +423,7 @@ class ChangeGroup : VimChangeGroupBase() { | ||||
|       } | ||||
|     } | ||||
|     val afterAction = { | ||||
|       val firstLine = editor.offsetToBufferPosition( | ||||
|         min(startOffset.toDouble(), endOffset.toDouble()).toInt() | ||||
|       ).line | ||||
|       val newOffset = injector.motion.moveCaretToLineStartSkipLeading(editor, firstLine) | ||||
|       caret.moveToOffset(newOffset) | ||||
|       caret.moveToOffset(injector.motion.moveCaretToLineStartSkipLeading(editor, startPos.line)) | ||||
|       restoreCursor(editor, caret, (caret as IjVimCaret).caret.logicalPosition.line) | ||||
|     } | ||||
|     if (project != null) { | ||||
|   | ||||
| @@ -0,0 +1,68 @@ | ||||
| package com.maddyhome.idea.vim.group | ||||
|  | ||||
| import com.intellij.codeInsight.daemon.ReferenceImporter | ||||
| import com.intellij.openapi.actionSystem.CommonDataKeys | ||||
| import com.intellij.openapi.actionSystem.DataContext | ||||
| import com.intellij.openapi.application.ApplicationManager | ||||
| import com.intellij.openapi.application.ReadAction | ||||
| import com.intellij.openapi.command.WriteCommandAction | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.intellij.openapi.fileEditor.FileDocumentManager | ||||
| import com.intellij.openapi.progress.ProgressIndicator | ||||
| import com.intellij.openapi.progress.ProgressManager | ||||
| import com.intellij.openapi.progress.Task | ||||
| import com.intellij.psi.PsiDocumentManager | ||||
| import com.intellij.psi.PsiElement | ||||
| import com.intellij.psi.PsiRecursiveElementWalkingVisitor | ||||
| import java.util.function.BooleanSupplier | ||||
|  | ||||
| internal object MacroAutoImport { | ||||
|   fun run(editor: Editor, dataContext: DataContext) { | ||||
|     val project = CommonDataKeys.PROJECT.getData(dataContext) ?: return | ||||
|     val file = PsiDocumentManager.getInstance(project).getPsiFile(editor.document) ?: return | ||||
|  | ||||
|     if (!FileDocumentManager.getInstance().requestWriting(editor.document, project)) { | ||||
|       return | ||||
|     } | ||||
|  | ||||
|     val importers = ReferenceImporter.EP_NAME.extensionList | ||||
|     if (importers.isEmpty()) { | ||||
|       return | ||||
|     } | ||||
|  | ||||
|     ProgressManager.getInstance().run(object : Task.Backgroundable(project, "Auto import", true) { | ||||
|       override fun run(indicator: ProgressIndicator) { | ||||
|         val fixes = ReadAction.nonBlocking<List<BooleanSupplier>> { | ||||
|           val fixes = mutableListOf<BooleanSupplier>() | ||||
|  | ||||
|           file.accept(object : PsiRecursiveElementWalkingVisitor() { | ||||
|             override fun visitElement(element: PsiElement) { | ||||
|               for (reference in element.references) { | ||||
|                 if (reference.resolve() != null) { | ||||
|                   continue | ||||
|                 } | ||||
|                 for (importer in importers) { | ||||
|                   importer.computeAutoImportAtOffset(editor, file, element.textRange.startOffset, true) | ||||
|                     ?.let(fixes::add) | ||||
|                 } | ||||
|               } | ||||
|               super.visitElement(element) | ||||
|             } | ||||
|           }) | ||||
|  | ||||
|           return@nonBlocking fixes | ||||
|         }.executeSynchronously() | ||||
|  | ||||
|         ApplicationManager.getApplication().invokeAndWait { | ||||
|           WriteCommandAction.writeCommandAction(project) | ||||
|             .withName("Auto Import") | ||||
|             .withGroupId("IdeaVimAutoImportAfterMacro") | ||||
|             .shouldRecordActionForActiveDocument(true) | ||||
|             .run<RuntimeException> { | ||||
|               fixes.forEach { it.asBoolean } | ||||
|             } | ||||
|         } | ||||
|       } | ||||
|     }) | ||||
|   } | ||||
| } | ||||
| @@ -22,6 +22,7 @@ import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.helper.MessageHelper.message | ||||
| import com.maddyhome.idea.vim.macro.VimMacroBase | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
|  | ||||
| /** | ||||
|  * Used to handle playback of macros | ||||
| @@ -93,6 +94,9 @@ internal class MacroGroup : VimMacroBase() { | ||||
|         } finally { | ||||
|           keyStack.removeFirst() | ||||
|         } | ||||
|         if (!isInternalMacro) { | ||||
|           MacroAutoImport.run(editor.ij, context.ij) | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       if (isInternalMacro) { | ||||
|   | ||||
| @@ -326,7 +326,7 @@ public class EditorHelper { | ||||
|  | ||||
|     final int offset = y - ((screenHeight - lineHeight) / lineHeight / 2 * lineHeight); | ||||
|     @NotNull final VimEditor editor1 = new IjVimEditor(editor); | ||||
|     final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) - 1; | ||||
|     final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) + editor.getSettings().getAdditionalLinesCount(); | ||||
|     final int offsetForLastLineAtBottom = getOffsetToScrollVisualLineToBottomOfScreen(editor, lastVisualLine); | ||||
|  | ||||
|     // For `zz`, we want to use virtual space and move any line, including the last one, to the middle of the screen. | ||||
|   | ||||
| @@ -12,6 +12,7 @@ package com.maddyhome.idea.vim.helper | ||||
|  | ||||
| import com.intellij.codeWithMe.ClientId | ||||
| import com.intellij.openapi.editor.Caret | ||||
| import com.intellij.openapi.editor.CaretState | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.intellij.openapi.editor.ex.util.EditorUtil | ||||
| import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx | ||||
| @@ -20,6 +21,8 @@ import com.maddyhome.idea.vim.api.StringListOptionValue | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.group.IjOptionConstants | ||||
| import com.maddyhome.idea.vim.newapi.globalIjOptions | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.state.mode.inBlockSelection | ||||
| import java.awt.Component | ||||
| import javax.swing.JComponent | ||||
| import javax.swing.JTable | ||||
| @@ -96,3 +99,41 @@ internal val Caret.vimLine: Int | ||||
|  */ | ||||
| internal val Editor.vimLine: Int | ||||
|   get() = this.caretModel.currentCaret.vimLine | ||||
|  | ||||
| internal inline fun Editor.runWithEveryCaretAndRestore(action: () -> Unit) { | ||||
|   val caretModel = this.caretModel | ||||
|   val carets = if (this.vim.inBlockSelection) null else caretModel.allCarets | ||||
|   if (carets == null || carets.size == 1) { | ||||
|     action() | ||||
|   } | ||||
|   else { | ||||
|     var initialDocumentSize = this.document.textLength | ||||
|     var documentSizeDifference = 0 | ||||
|  | ||||
|     val caretOffsets = carets.map { it.selectionStart to it.selectionEnd } | ||||
|     val restoredCarets = mutableListOf<CaretState>() | ||||
|  | ||||
|     caretModel.removeSecondaryCarets() | ||||
|      | ||||
|     for ((selectionStart, selectionEnd) in caretOffsets) { | ||||
|       if (selectionStart == selectionEnd) { | ||||
|         caretModel.primaryCaret.moveToOffset(selectionStart + documentSizeDifference) | ||||
|       } | ||||
|       else { | ||||
|         caretModel.primaryCaret.setSelection( | ||||
|           selectionStart + documentSizeDifference, | ||||
|           selectionEnd + documentSizeDifference | ||||
|         ) | ||||
|       } | ||||
|        | ||||
|       action() | ||||
|       restoredCarets.add(caretModel.caretsAndSelections.single()) | ||||
|  | ||||
|       val documentLength = this.document.textLength | ||||
|       documentSizeDifference += documentLength - initialDocumentSize | ||||
|       initialDocumentSize = documentLength | ||||
|     } | ||||
|  | ||||
|     caretModel.caretsAndSelections = restoredCarets | ||||
|   }  | ||||
| } | ||||
|   | ||||
| @@ -21,6 +21,7 @@ import com.intellij.openapi.actionSystem.PlatformDataKeys | ||||
| import com.intellij.openapi.actionSystem.ex.ActionManagerEx | ||||
| import com.intellij.openapi.actionSystem.ex.ActionUtil | ||||
| import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet | ||||
| import com.intellij.openapi.actionSystem.impl.Utils | ||||
| import com.intellij.openapi.command.CommandProcessor | ||||
| import com.intellij.openapi.command.UndoConfirmationPolicy | ||||
| import com.intellij.openapi.components.Service | ||||
| @@ -86,6 +87,7 @@ internal class IjActionExecutor : VimActionExecutor { | ||||
|       ActionManager.getInstance(), | ||||
|       0, | ||||
|     ) | ||||
|     Utils.initUpdateSession(event) | ||||
|     // beforeActionPerformedUpdate should be called to update the action. It fixes some rider-specific problems. | ||||
|     //   because rider use async update method. See VIM-1819. | ||||
|     // This method executes inside of lastUpdateAndCheckDumb | ||||
|   | ||||
| @@ -60,7 +60,7 @@ internal object ScrollViewHelper { | ||||
|     // that this needs to be replaced as a more or less dumb line for line rewrite. | ||||
|     val topLine = getVisualLineAtTopOfScreen(editor) | ||||
|     val bottomLine = getVisualLineAtBottomOfScreen(editor) | ||||
|     val lastLine = vimEditor.getVisualLineCount() - 1 | ||||
|     val lastLine = vimEditor.getVisualLineCount() + editor.settings.additionalLinesCount | ||||
|  | ||||
|     // We need the non-normalised value here, so we can handle cases such as so=999 to keep the current line centred | ||||
|     val scrollOffset = injector.options(vimEditor).scrolloff | ||||
|   | ||||
| @@ -17,6 +17,7 @@ import com.intellij.openapi.diagnostic.logger | ||||
| import com.intellij.openapi.fileEditor.TextEditor | ||||
| import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider | ||||
| import com.intellij.openapi.util.registry.Registry | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimCaret | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| @@ -26,6 +27,8 @@ import com.maddyhome.idea.vim.newapi.IjVimCaret | ||||
| import com.maddyhome.idea.vim.common.InsertSequence | ||||
| import com.maddyhome.idea.vim.newapi.globalIjOptions | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | ||||
| import com.maddyhome.idea.vim.state.mode.inVisualMode | ||||
| import com.maddyhome.idea.vim.undo.UndoRedoBase | ||||
|  | ||||
| /** | ||||
| @@ -64,15 +67,7 @@ internal class UndoRedoHelper : UndoRedoBase() { | ||||
|       // TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo | ||||
|       editor.runWithChangeTracking { | ||||
|         undoManager.undo(fileEditor) | ||||
|  | ||||
|         // We execute undo one more time if the previous one just restored selection | ||||
|         if (!hasChanges && hasSelection(editor) && undoManager.isUndoAvailable(fileEditor)) { | ||||
|           undoManager.undo(fileEditor) | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       CommandProcessor.getInstance().runUndoTransparentAction { | ||||
|         removeSelections(editor) | ||||
|         restoreVisualMode(editor) | ||||
|       } | ||||
|     } else { | ||||
|       runWithBooleanRegistryOption("ide.undo.transparent.caret.movement", true) { | ||||
| @@ -219,4 +214,21 @@ internal class UndoRedoHelper : UndoRedoBase() { | ||||
|     val hasChanges: Boolean | ||||
|       get() = changeListener.hasChanged || initialPath != editor.getPath() | ||||
|   } | ||||
|  | ||||
|   private fun restoreVisualMode(editor: VimEditor) { | ||||
|     if (!editor.inVisualMode && editor.getSelectionModel().hasSelection()) { | ||||
|       val detectedMode = VimPlugin.getVisualMotion().autodetectVisualSubmode(editor) | ||||
|  | ||||
|       // Visual block selection is restored into multiple carets, so multi-carets that form a block are always | ||||
|       // identified as visual block mode, leading to false positives. | ||||
|       // Since I use visual block mode much less often than multi-carets, this is a judgment call to never restore | ||||
|       // visual block mode. | ||||
|       val wantedMode = if (detectedMode == SelectionType.BLOCK_WISE) | ||||
|         SelectionType.CHARACTER_WISE | ||||
|       else | ||||
|         detectedMode | ||||
|  | ||||
|       VimPlugin.getVisualMotion().enterVisualMode(editor, wantedMode) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -18,13 +18,12 @@ import com.intellij.openapi.editor.VisualPosition | ||||
| import com.intellij.openapi.editor.markup.RangeHighlighter | ||||
| import com.intellij.openapi.util.Key | ||||
| import com.intellij.openapi.util.UserDataHolder | ||||
| import com.maddyhome.idea.vim.api.CaretRegisterStorageBase | ||||
| import com.maddyhome.idea.vim.api.LocalMarkStorage | ||||
| import com.maddyhome.idea.vim.api.SelectionInfo | ||||
| import com.maddyhome.idea.vim.common.InsertSequence | ||||
| import com.maddyhome.idea.vim.ex.ExOutputModel | ||||
| import com.maddyhome.idea.vim.group.visual.VisualChange | ||||
| import com.maddyhome.idea.vim.group.visual.vimLeadSelectionOffset | ||||
| import com.maddyhome.idea.vim.common.InsertSequence | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.state.VimStateMachine | ||||
| import com.maddyhome.idea.vim.state.mode.Mode | ||||
| @@ -96,7 +95,6 @@ internal var Caret.vimInsertStart: RangeMarker by userDataOr { | ||||
| } | ||||
|  | ||||
| // TODO: Data could be lost during visual block motion | ||||
| internal var Caret.registerStorage: CaretRegisterStorageBase? by userDataCaretToEditor() | ||||
| internal var Caret.markStorage: LocalMarkStorage? by userDataCaretToEditor() | ||||
| internal var Caret.lastSelectionInfo: SelectionInfo? by userDataCaretToEditor() | ||||
|  | ||||
|   | ||||
| @@ -1,32 +0,0 @@ | ||||
| /* | ||||
|  * Copyright 2003-2023 The IdeaVim authors | ||||
|  * | ||||
|  * Use of this source code is governed by an MIT-style | ||||
|  * license that can be found in the LICENSE.txt file or at | ||||
|  * https://opensource.org/licenses/MIT. | ||||
|  */ | ||||
|  | ||||
| package com.maddyhome.idea.vim.helper | ||||
|  | ||||
| import com.intellij.ide.plugins.StandalonePluginUpdateChecker | ||||
| import com.intellij.openapi.components.Service | ||||
| import com.intellij.openapi.components.service | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.group.NotificationService | ||||
| import com.maddyhome.idea.vim.icons.VimIcons | ||||
|  | ||||
| @Service(Service.Level.APP) | ||||
| internal class VimStandalonePluginUpdateChecker : StandalonePluginUpdateChecker( | ||||
|   VimPlugin.getPluginId(), | ||||
|   updateTimestampProperty = PROPERTY_NAME, | ||||
|   NotificationService.IDEAVIM_STICKY_GROUP, | ||||
|   VimIcons.IDEAVIM, | ||||
| ) { | ||||
|  | ||||
|   override fun skipUpdateCheck(): Boolean = VimPlugin.isNotEnabled() || "dev" in VimPlugin.getVersion() | ||||
|  | ||||
|   companion object { | ||||
|     private const val PROPERTY_NAME = "ideavim.statistics.timestamp" | ||||
|     val instance: VimStandalonePluginUpdateChecker = service() | ||||
|   } | ||||
| } | ||||
| @@ -28,6 +28,7 @@ import com.intellij.openapi.actionSystem.CommonDataKeys | ||||
| import com.intellij.openapi.actionSystem.ex.AnActionListener | ||||
| import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.intellij.openapi.editor.impl.ScrollingModelImpl | ||||
| import com.intellij.openapi.project.DumbAwareToggleAction | ||||
| import com.intellij.openapi.util.TextRange | ||||
| import com.maddyhome.idea.vim.KeyHandler | ||||
| @@ -57,6 +58,7 @@ internal object IdeaSpecifics { | ||||
|     private val surrounderAction = | ||||
|       "com.intellij.codeInsight.generation.surroundWith.SurroundWithHandler\$InvokeSurrounderAction" | ||||
|     private var editor: Editor? = null | ||||
|     private var caretOffset = -1 | ||||
|     private var completionPrevDocumentLength: Int? = null | ||||
|     private var completionPrevDocumentOffset: Int? = null | ||||
|     override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) { | ||||
| @@ -65,6 +67,7 @@ internal object IdeaSpecifics { | ||||
|       val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR) | ||||
|       if (hostEditor != null) { | ||||
|         editor = hostEditor | ||||
|         caretOffset = hostEditor.caretModel.offset | ||||
|       } | ||||
|  | ||||
|       val isVimAction = (action as? AnActionWrapper)?.delegate is VimShortcutKeyAction | ||||
| @@ -96,7 +99,8 @@ internal object IdeaSpecifics { | ||||
|       if (VimPlugin.isNotEnabled()) return | ||||
|  | ||||
|       val editor = editor | ||||
|       if (editor != null && action is ChooseItemAction && injector.registerGroup.isRecording) { | ||||
|       if (editor != null) { | ||||
|         if (action is ChooseItemAction && injector.registerGroup.isRecording) { | ||||
|           val prevDocumentLength = completionPrevDocumentLength | ||||
|           val prevDocumentOffset = completionPrevDocumentOffset | ||||
|  | ||||
| @@ -132,7 +136,21 @@ internal object IdeaSpecifics { | ||||
|         } | ||||
|         //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.caretOffset = -1 | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -35,6 +35,7 @@ import com.intellij.openapi.editor.ex.DocumentEx | ||||
| import com.intellij.openapi.editor.ex.EditorEventMulticasterEx | ||||
| import com.intellij.openapi.editor.ex.FocusChangeListener | ||||
| import com.intellij.openapi.editor.impl.EditorComponentImpl | ||||
| import com.intellij.openapi.editor.impl.EditorImpl | ||||
| import com.intellij.openapi.fileEditor.FileEditorManager | ||||
| import com.intellij.openapi.fileEditor.FileEditorManagerEvent | ||||
| import com.intellij.openapi.fileEditor.FileEditorManagerListener | ||||
| @@ -45,11 +46,14 @@ import com.intellij.openapi.fileEditor.ex.FileEditorWithProvider | ||||
| import com.intellij.openapi.fileEditor.impl.EditorComposite | ||||
| import com.intellij.openapi.fileEditor.impl.EditorWindow | ||||
| import com.intellij.openapi.project.ProjectManager | ||||
| import com.intellij.openapi.rd.createLifetime | ||||
| import com.intellij.openapi.rd.createNestedDisposable | ||||
| import com.intellij.openapi.util.Disposer | ||||
| import com.intellij.openapi.util.Key | ||||
| import com.intellij.openapi.util.removeUserData | ||||
| import com.intellij.openapi.vfs.VirtualFile | ||||
| import com.intellij.util.ExceptionUtil | ||||
| import com.jetbrains.rd.util.lifetime.Lifetime | ||||
| import com.maddyhome.idea.vim.EventFacade | ||||
| import com.maddyhome.idea.vim.KeyHandler | ||||
| import com.maddyhome.idea.vim.KeyHandlerStateResetter | ||||
| @@ -80,7 +84,6 @@ import com.maddyhome.idea.vim.handler.keyCheckRequests | ||||
| import com.maddyhome.idea.vim.helper.CaretVisualAttributesListener | ||||
| import com.maddyhome.idea.vim.helper.GuicursorChangeListener | ||||
| import com.maddyhome.idea.vim.helper.StrictMode | ||||
| import com.maddyhome.idea.vim.helper.VimStandalonePluginUpdateChecker | ||||
| import com.maddyhome.idea.vim.helper.exitSelectMode | ||||
| import com.maddyhome.idea.vim.helper.exitVisualMode | ||||
| import com.maddyhome.idea.vim.helper.forceBarCursor | ||||
| @@ -96,6 +99,7 @@ import com.maddyhome.idea.vim.newapi.InsertTimeRecorder | ||||
| import com.maddyhome.idea.vim.newapi.IjVimSearchGroup | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.state.mode.Mode | ||||
| import com.maddyhome.idea.vim.state.mode.inSelectMode | ||||
| import com.maddyhome.idea.vim.state.mode.selectionType | ||||
| import com.maddyhome.idea.vim.ui.ShowCmdOptionChangeListener | ||||
| @@ -104,7 +108,6 @@ import com.maddyhome.idea.vim.ui.widgets.macro.MacroWidgetListener | ||||
| import com.maddyhome.idea.vim.ui.widgets.macro.macroWidgetOptionListener | ||||
| import com.maddyhome.idea.vim.ui.widgets.mode.listeners.ModeWidgetListener | ||||
| import com.maddyhome.idea.vim.ui.widgets.mode.modeWidgetOptionListener | ||||
| import com.maddyhome.idea.vim.vimDisposable | ||||
| import java.awt.event.MouseAdapter | ||||
| import java.awt.event.MouseEvent | ||||
| import javax.swing.SwingUtilities | ||||
| @@ -285,12 +288,10 @@ internal object VimListenerManager { | ||||
|       // TODO: If the user changes the 'ideavimsupport' option, existing editors won't be initialised | ||||
|       if (vimDisabled(editor)) return | ||||
|  | ||||
|       // As I understand, there is no need to pass a disposable that also disposes on editor close | ||||
|       //   because all editor resources will be garbage collected anyway on editor close | ||||
|       // Note that this uses the plugin's main disposable, rather than VimPlugin.onOffDisposable, because we don't need | ||||
|       // to - we explicitly call VimListenerManager.removeAll from VimPlugin.turnOffPlugin, and this disposes each | ||||
|       // editor's disposable individually. | ||||
|       val disposable = editor.project?.vimDisposable ?: return | ||||
|       val pluginLifetime = VimPlugin.getInstance().createLifetime() | ||||
|       val editorLifetime = (editor as EditorImpl).disposable.createLifetime() | ||||
|       val disposable = | ||||
|         Lifetime.intersect(pluginLifetime, editorLifetime).createNestedDisposable("MyLifetimedDisposable") | ||||
|  | ||||
|       // Protect against double initialisation | ||||
|       if (editor.getUserData(editorListenersDisposableKey) != null) { | ||||
| @@ -387,6 +388,15 @@ internal object VimListenerManager { | ||||
|       // We can't rely on being passed a non-null editor, so check for Code With Me scenarios explicitly | ||||
|       if (VimPlugin.isNotEnabled() || !ClientId.isCurrentlyUnderLocalId) return | ||||
|        | ||||
|       val newEditor = event.newEditor | ||||
|       if (newEditor is TextEditor) { | ||||
|         val editor = newEditor.editor | ||||
|         if (editor.isInsertMode) { | ||||
|           editor.vim.mode = Mode.NORMAL() | ||||
|           KeyHandler.getInstance().reset(editor.vim) | ||||
|         } | ||||
|       } | ||||
|        | ||||
|       MotionGroup.fileEditorManagerSelectionChangedCallback(event) | ||||
|       FileGroup.fileEditorManagerSelectionChangedCallback(event) | ||||
|       VimPlugin.getSearch().fileEditorManagerSelectionChangedCallback(event) | ||||
| @@ -458,8 +468,6 @@ internal object VimListenerManager { | ||||
|  | ||||
|         event.editor.putUserData(openingEditorKey, OpeningEditor(openingEditor, owningEditorWindow, isPreview, canBeReused)) | ||||
|       } | ||||
|  | ||||
|       VimStandalonePluginUpdateChecker.instance.pluginUsed() | ||||
|     } | ||||
|  | ||||
|     override fun editorReleased(event: EditorFactoryEvent) { | ||||
|   | ||||
| @@ -12,8 +12,6 @@ import com.intellij.openapi.editor.Caret | ||||
| import com.intellij.openapi.editor.LogicalPosition | ||||
| import com.intellij.openapi.editor.VisualPosition | ||||
| import com.maddyhome.idea.vim.api.BufferPosition | ||||
| import com.maddyhome.idea.vim.api.CaretRegisterStorage | ||||
| import com.maddyhome.idea.vim.api.CaretRegisterStorageBase | ||||
| import com.maddyhome.idea.vim.api.ImmutableVimCaret | ||||
| import com.maddyhome.idea.vim.api.LocalMarkStorage | ||||
| import com.maddyhome.idea.vim.api.SelectionInfo | ||||
| @@ -29,7 +27,6 @@ import com.maddyhome.idea.vim.helper.insertHistory | ||||
| import com.maddyhome.idea.vim.helper.lastSelectionInfo | ||||
| import com.maddyhome.idea.vim.helper.markStorage | ||||
| import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset | ||||
| import com.maddyhome.idea.vim.helper.registerStorage | ||||
| import com.maddyhome.idea.vim.helper.resetVimLastColumn | ||||
| import com.maddyhome.idea.vim.helper.vimInsertStart | ||||
| import com.maddyhome.idea.vim.helper.vimLastColumn | ||||
| @@ -41,17 +38,6 @@ import com.maddyhome.idea.vim.state.mode.SelectionType | ||||
|  | ||||
| internal class IjVimCaret(val caret: Caret) : VimCaretBase() { | ||||
|  | ||||
|   override val registerStorage: CaretRegisterStorage | ||||
|     get() { | ||||
|       var storage = this.caret.registerStorage | ||||
|       if (storage == null) { | ||||
|         storage = CaretRegisterStorageBase(this) | ||||
|         this.caret.registerStorage = storage | ||||
|       } else if (storage.caret != this) { | ||||
|         storage.caret = this | ||||
|       } | ||||
|       return storage | ||||
|     } | ||||
|   override val markStorage: LocalMarkStorage | ||||
|     get() { | ||||
|       var storage = this.caret.markStorage | ||||
|   | ||||
| @@ -151,21 +151,40 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() { | ||||
|     return editor.caretModel.allCarets.map { IjVimCaret(it) } | ||||
|   } | ||||
|  | ||||
|   override var isFirstCaret = false | ||||
|   override var isReversingCarets = false | ||||
|    | ||||
|   @Suppress("ideavimRunForEachCaret") | ||||
|   override fun forEachCaret(action: (VimCaret) -> Unit) { | ||||
|     if (editor.vim.inBlockSelection) { | ||||
|       action(IjVimCaret(editor.caretModel.primaryCaret)) | ||||
|     } else { | ||||
|       isFirstCaret = true | ||||
|       try { | ||||
|         editor.caretModel.runForEachCaret({ | ||||
|           if (it.isValid) { | ||||
|             action(IjVimCaret(it)) | ||||
|             isFirstCaret = false | ||||
|           } | ||||
|         }, false) | ||||
|       } finally { | ||||
|         isFirstCaret = false | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   override fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean) { | ||||
|     editor.caretModel.runForEachCaret({ action(IjVimCaret(it)) }, reverse) | ||||
|     isFirstCaret = true | ||||
|     isReversingCarets = reverse | ||||
|     try { | ||||
|       editor.caretModel.runForEachCaret({ | ||||
|         action(IjVimCaret(it)) | ||||
|         isFirstCaret = false | ||||
|       }, reverse) | ||||
|     } finally { | ||||
|       isFirstCaret = false | ||||
|       isReversingCarets = false | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   override fun isInForEachCaretScope(): Boolean { | ||||
|   | ||||
| @@ -1,12 +1,4 @@ | ||||
| <!-- | ||||
|   ~ Copyright 2003-2023 The IdeaVim authors | ||||
|   ~ | ||||
|   ~ Use of this source code is governed by an MIT-style | ||||
|   ~ license that can be found in the LICENSE.txt file or at | ||||
|   ~ https://opensource.org/licenses/MIT. | ||||
|   --> | ||||
|  | ||||
| <idea-plugin url="https://plugins.jetbrains.com/plugin/164" xmlns:xi="http://www.w3.org/2001/XInclude"> | ||||
| <idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude"> | ||||
|   <name>IdeaVim</name> | ||||
|   <id>IdeaVIM</id> | ||||
|   <description><![CDATA[ | ||||
| @@ -21,7 +13,7 @@ | ||||
|         <li><a href="https://youtrack.jetbrains.com/issues/VIM">Issue tracker</a>: feature requests and bug reports</li> | ||||
|       </ul> | ||||
|     ]]></description> | ||||
|   <version>SNAPSHOT</version> | ||||
|   <version>chylex</version> | ||||
|   <vendor>JetBrains</vendor> | ||||
|  | ||||
|   <!-- Mark the plugin as compatible with RubyMine and other products based on the IntelliJ platform (including CWM) --> | ||||
| @@ -154,5 +146,6 @@ | ||||
|     </group> | ||||
|  | ||||
|     <action id="VimFindActionIdAction" class="com.maddyhome.idea.vim.listener.FindActionIdAction"/> | ||||
|     <action id="VimJumpToSource" class="com.intellij.diff.actions.impl.OpenInEditorAction" /> | ||||
|   </actions> | ||||
| </idea-plugin> | ||||
|   | ||||
| @@ -13,7 +13,6 @@ import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | ||||
| import org.jetbrains.plugins.ideavim.VimBehaviorDiffers | ||||
| import org.jetbrains.plugins.ideavim.VimTestCase | ||||
| @@ -2173,7 +2172,7 @@ rtyfg${c}hzxc""" | ||||
|     val editor = configureByText(before) | ||||
|     injector.registerGroup.storeText('*', "fgh") | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(IjVimEditor(editor), editor.vim.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(IjVimEditor(editor), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("\"*P")) | ||||
|     val after = "fg${c}hqfg${c}hwe asd zxc rty fg${c}hfgh vbn" | ||||
|     assertState(after) | ||||
|   | ||||
| @@ -33,7 +33,7 @@ class IdeaPutNotificationsTest : VimTestCase() { | ||||
|     appReadySetup(false) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("p")) | ||||
|  | ||||
|     val notification = notifications().last() | ||||
| @@ -53,7 +53,7 @@ class IdeaPutNotificationsTest : VimTestCase() { | ||||
|     appReadySetup(false) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("p")) | ||||
|  | ||||
|     val notifications = notifications() | ||||
| @@ -71,7 +71,7 @@ class IdeaPutNotificationsTest : VimTestCase() { | ||||
|     appReadySetup(true) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("p")) | ||||
|  | ||||
|     val notifications = EventLog.getLogModel(fixture.project).notifications | ||||
|   | ||||
| @@ -88,7 +88,7 @@ class PutTestAfterCursorActionTest : VimTestCase() { | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|       .storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -127,7 +127,6 @@ class PutTestAfterCursorActionTest : VimTestCase() { | ||||
|     val vimEditor = editor.vim | ||||
|     injector.registerGroup.storeText( | ||||
|       vimEditor, | ||||
|       vimEditor.primaryCaret(), | ||||
|       before rangeOf "I found it in a legendary land\n", | ||||
|       SelectionType.LINE_WISE, | ||||
|       false, | ||||
| @@ -157,7 +156,7 @@ class PutTestAfterCursorActionTest : VimTestCase() { | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("vep")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
|   | ||||
| @@ -31,7 +31,7 @@ class PutTextBeforeCursorActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     injector.registerGroup.storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|     injector.registerGroup.storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("V" + "P")) | ||||
|     typeText(injector.parser.parseKeys("V" + "P")) | ||||
|     val after = """ | ||||
|   | ||||
| @@ -54,7 +54,7 @@ class PutViaIdeaTest : VimTestCase() { | ||||
|  | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|  | ||||
|     typeText("ppp") | ||||
|     val after = "Ilegendarylegendarylegendar${c}y found it in a legendary land" | ||||
| @@ -74,7 +74,6 @@ class PutViaIdeaTest : VimTestCase() { | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText( | ||||
|         vimEditor, | ||||
|         vimEditor.primaryCaret(), | ||||
|         before rangeOf "legendary$randomUUID", | ||||
|         SelectionType.CHARACTER_WISE, | ||||
|         false, | ||||
| @@ -100,7 +99,6 @@ class PutViaIdeaTest : VimTestCase() { | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     VimPlugin.getRegister().storeText( | ||||
|       vimEditor, | ||||
|       vimEditor.primaryCaret(), | ||||
|       before rangeOf "\nLorem ipsum dolor sit amet,\n", | ||||
|       SelectionType.CHARACTER_WISE, | ||||
|       false, | ||||
|   | ||||
| @@ -75,7 +75,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("ve" + "p")) | ||||
|     val after = "legendar${c}y it in a legendary land" | ||||
|     assertState(after) | ||||
| @@ -87,7 +87,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("v2e" + "2p")) | ||||
|     val after = "legendarylegendar${c}y in a legendary land" | ||||
|     assertState(after) | ||||
| @@ -99,7 +99,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("v$" + "2p")) | ||||
|     val after = "legendarylegendar${c}y" | ||||
|     assertState(after) | ||||
| @@ -133,7 +133,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     val before = "I foun${c}d it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("vb" + "p")) | ||||
|     val after = "I legendar${c}y it in a legendary land" | ||||
|     assertState(after) | ||||
| @@ -154,7 +154,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("ve" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -182,7 +182,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("ve" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -210,7 +210,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("ve" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -238,7 +238,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("v$" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -455,7 +455,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("ve" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -491,7 +491,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("ve" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -529,7 +529,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("ve" + "2p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -640,7 +640,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("V" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -666,7 +666,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("V" + "2p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -703,7 +703,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("V" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -876,7 +876,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("V" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -902,7 +902,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("V" + "2p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -939,7 +939,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("V" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1117,7 +1117,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("V" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1172,7 +1172,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("V" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1233,7 +1233,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("V" + "2p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1409,7 +1409,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("<C-V>2e2j" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1435,7 +1435,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("<C-V>3e2k" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1461,7 +1461,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("<C-V>2e2j" + "2p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1487,7 +1487,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("<C-V>3j$" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1526,7 +1526,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("<C-V>2e2j" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1554,7 +1554,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("<C-V>2e2j" + "P")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1593,7 +1593,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("<C-V>2e2j" + "2p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1633,7 +1633,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("<C-V>2e3j" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1672,7 +1672,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("<C-V>2j$" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1707,7 +1707,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("<C-V>2e2j" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1743,7 +1743,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("<C-V>2e3j" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1779,7 +1779,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("<C-V>2ej" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1815,7 +1815,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("<C-V>elj" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
| @@ -1852,7 +1852,7 @@ class PutVisualTextActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("<C-V>2j$" + "p")) | ||||
|     val after = """ | ||||
|             A Discovery | ||||
|   | ||||
| @@ -34,7 +34,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.CHARACTER_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("v2e" + "2gp")) | ||||
|     val after = "legendarylegendary$c in a legendary land" | ||||
|     assertState(after) | ||||
| @@ -46,7 +46,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.LINE_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.LINE_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("v2e" + "gp")) | ||||
|     val after = """ | ||||
|  | ||||
| @@ -62,7 +62,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.CHARACTER_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("V" + "gp")) | ||||
|     val after = "legendary\n$c" | ||||
|     assertState(after) | ||||
| @@ -89,7 +89,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(file) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(2, 11), SelectionType.LINE_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, TextRange(2, 11), SelectionType.LINE_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("V" + "gp")) | ||||
|     assertState(newFile) | ||||
|   } | ||||
| @@ -135,7 +135,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.LINE_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.LINE_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("v2e" + "gP")) | ||||
|     val after = """ | ||||
|  | ||||
| @@ -151,7 +151,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.CHARACTER_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("v2e" + "2gP")) | ||||
|     val after = "legendarylegendary$c in a legendary land" | ||||
|     assertState(after) | ||||
| @@ -163,7 +163,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.CHARACTER_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("v$" + "2gP")) | ||||
|     val after = "legendarylegendar${c}y" | ||||
|     assertState(after) | ||||
| @@ -175,7 +175,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     val before = "${c}I found it in a legendary land" | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.CHARACTER_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("V" + "gP")) | ||||
|     val after = "legendary\n$c" | ||||
|     assertState(after) | ||||
| @@ -274,7 +274,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.BLOCK_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 19), SelectionType.BLOCK_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("<S-v>" + "gp")) | ||||
|     val after = """ | ||||
|             ${c}fgh | ||||
| @@ -300,7 +300,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     """.trimIndent() | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.LINE_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 19), SelectionType.LINE_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("<C-v>" + "h" + "gp")) | ||||
|     val after = """ | ||||
|             q | ||||
| @@ -323,7 +323,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() { | ||||
|     val before = "${c}qwe asd ${c}zxc rty ${c}fgh vbn" | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||
|     VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("v2e" + "2gp")) | ||||
|     val after = "fghfgh$c fghfgh$c fghfgh$c" | ||||
|     assertState(after) | ||||
|   | ||||
| @@ -91,7 +91,7 @@ class YankVisualActionTest : VimTestCase() { | ||||
|     typeText(injector.parser.parseKeys("viw" + "y")) | ||||
|     val editor = fixture.editor.vim | ||||
|     val lastRegister = injector.registerGroup.lastRegisterChar | ||||
|     val registers = editor.carets().map { it.registerStorage.getRegister(lastRegister)?.rawText } | ||||
|     val registers = editor.carets().map { injector.registerGroup.getRegister(lastRegister)?.rawText } | ||||
|     kotlin.test.assertEquals(listOf("found", "was"), registers) | ||||
|   } | ||||
|  | ||||
| @@ -172,7 +172,7 @@ class YankVisualActionTest : VimTestCase() { | ||||
|     typeText(injector.parser.parseKeys("V" + "y")) | ||||
|     val editor = fixture.editor.vim | ||||
|     val lastRegister = injector.registerGroup.lastRegisterChar | ||||
|     val registers = editor.carets().map { it.registerStorage.getRegister(lastRegister)?.rawText } | ||||
|     val registers = editor.carets().map { injector.registerGroup.getRegister(lastRegister)?.rawText } | ||||
|     kotlin.test.assertEquals( | ||||
|       listOf("all rocks and lavender and tufted grass,\n", "hard by the torrent of a mountain pass.\n"), | ||||
|       registers, | ||||
|   | ||||
| @@ -130,7 +130,7 @@ class MultipleCaretsTest : VimTestCase() { | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(vimEditor, TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(commandToKeys("pu")) | ||||
|     val after = """ | ||||
|           qwe | ||||
| @@ -165,7 +165,7 @@ class MultipleCaretsTest : VimTestCase() { | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(vimEditor, TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(commandToKeys("pu")) | ||||
|     val after = """ | ||||
|           qwe | ||||
| @@ -201,7 +201,7 @@ class MultipleCaretsTest : VimTestCase() { | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(vimEditor, TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(commandToKeys("4pu")) | ||||
|     val after = """ | ||||
|           qwe | ||||
| @@ -237,7 +237,7 @@ class MultipleCaretsTest : VimTestCase() { | ||||
|     val editor = configureByText(before) | ||||
|     val vimEditor = editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(vimEditor, TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(commandToKeys("4pu")) | ||||
|     val after = """ | ||||
|           qwe | ||||
| @@ -258,7 +258,7 @@ class MultipleCaretsTest : VimTestCase() { | ||||
|     val before = "${c}qwe\n" + "rty\n" + "as${c}d\n" + "fgh\n" + "zxc\n" + "vbn\n" | ||||
|     val editor = configureByText(before) | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(editor.vim, editor.vim.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(editor.vim, TextRange(16, 19), SelectionType.CHARACTER_WISE, false) | ||||
|  | ||||
|     typeText("vj") | ||||
|     typeText(commandToKeys("pu")) | ||||
|   | ||||
| @@ -9,7 +9,6 @@ | ||||
| package org.jetbrains.plugins.ideavim.ex.implementation.commands | ||||
|  | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.register.RegisterConstants | ||||
| import org.jetbrains.plugins.ideavim.VimTestCase | ||||
| import org.junit.jupiter.api.Test | ||||
| @@ -303,33 +302,4 @@ class YankLinesCommandTest : VimTestCase() { | ||||
|         |Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. | ||||
|         |""".trimMargin()) | ||||
|   } | ||||
|  | ||||
|   @Test | ||||
|   fun `test multicaret yank`() { | ||||
|     configureByText( | ||||
|       """ | ||||
|         |Lorem ipsum dolor sit amet, consectetur adipiscing elit. | ||||
|         |${c}Morbi nec luctus tortor, id venenatis lacus. | ||||
|         |${c}Nunc sit amet tellus vel purus cursus posuere et at purus. | ||||
|         |${c}Ut id dapibus augue. | ||||
|         |Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. | ||||
|         |Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui. | ||||
|       """.trimMargin() | ||||
|     ) | ||||
|     enterCommand("y") | ||||
|     val carets = fixture.editor.vim.carets() | ||||
|     assertEquals(3, carets.size) | ||||
|     assertEquals( | ||||
|       "Morbi nec luctus tortor, id venenatis lacus.\n", | ||||
|       carets[0].registerStorage.getRegister(RegisterConstants.UNNAMED_REGISTER)?.text | ||||
|     ) | ||||
|     assertEquals( | ||||
|       "Nunc sit amet tellus vel purus cursus posuere et at purus.\n", | ||||
|       carets[1].registerStorage.getRegister(RegisterConstants.UNNAMED_REGISTER)?.text | ||||
|     ) | ||||
|     assertEquals( | ||||
|       "Ut id dapibus augue.\n", | ||||
|       carets[2].registerStorage.getRegister(RegisterConstants.UNNAMED_REGISTER)?.text | ||||
|     ) | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -50,7 +50,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | ||||
|     configureByText(text) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "one", SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(vimEditor, text rangeOf "one", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("griw")) | ||||
|     assertState("one on${c}e three") | ||||
|     assertEquals("one", VimPlugin.getRegister().lastRegister?.text) | ||||
| @@ -170,7 +170,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | ||||
|     configureByText(text) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "one", SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(vimEditor, text rangeOf "one", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("3griw")) | ||||
|     assertState("one on${c}e four") | ||||
|     assertEquals("one", VimPlugin.getRegister().lastRegister?.text) | ||||
| @@ -184,7 +184,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | ||||
|     configureByText(text) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "one", SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(vimEditor, text rangeOf "one", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("griw")) | ||||
|     assertState("one two one four") | ||||
|     assertEquals("one", VimPlugin.getRegister().lastRegister?.text) | ||||
| @@ -197,7 +197,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | ||||
|     configureByText(text) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "one", SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(vimEditor, text rangeOf "one", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("griw" + "w" + ".")) | ||||
|     assertState("one one on${c}e four") | ||||
|     assertEquals("one", VimPlugin.getRegister().lastRegister?.text) | ||||
| @@ -247,7 +247,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | ||||
|     configureByText(text) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(vimEditor, text rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("grr")) | ||||
|     assertState( | ||||
|       """ | ||||
| @@ -414,7 +414,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | ||||
|     configureByText(text) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(vimEditor, text rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("viw" + "gr")) | ||||
|     assertState( | ||||
|       """ | ||||
| @@ -485,7 +485,7 @@ class ReplaceWithRegisterTest : VimTestCase() { | ||||
|     configureByText(text) | ||||
|     val vimEditor = fixture.editor.vim | ||||
|     VimPlugin.getRegister() | ||||
|       .storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|       .storeText(vimEditor, text rangeOf "legendary", SelectionType.CHARACTER_WISE, false) | ||||
|     typeText(injector.parser.parseKeys("V" + "gr")) | ||||
|     assertState( | ||||
|       """ | ||||
|   | ||||
| @@ -1,5 +1,3 @@ | ||||
| import org.jetbrains.intellij.platform.gradle.TestFrameworkType | ||||
|  | ||||
| /* | ||||
|  * Copyright 2003-2024 The IdeaVim authors | ||||
|  * | ||||
| @@ -11,20 +9,16 @@ import org.jetbrains.intellij.platform.gradle.TestFrameworkType | ||||
| plugins { | ||||
|   id("java") | ||||
|   kotlin("jvm") | ||||
|   id("org.jetbrains.intellij.platform.module") | ||||
|   id("org.jetbrains.intellij") | ||||
| } | ||||
|  | ||||
| val kotlinVersion: String by project | ||||
| val ideaType: String by project | ||||
| val ideaVersion: String by project | ||||
| val javaVersion: String by project | ||||
|  | ||||
| repositories { | ||||
|   mavenCentral() | ||||
|  | ||||
|   intellijPlatform { | ||||
|     defaultRepositories() | ||||
|   } | ||||
|   maven { url = uri("https://cache-redirector.jetbrains.com/intellij-dependencies") } | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
| @@ -32,24 +26,35 @@ dependencies { | ||||
|   compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") | ||||
|   testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion") | ||||
|   testImplementation(testFixtures(project(":"))) // The root project | ||||
|  | ||||
|   intellijPlatform { | ||||
|     create(ideaType, ideaVersion) | ||||
|     testFramework(TestFrameworkType.Platform) | ||||
|     testFramework(TestFrameworkType.JUnit5) | ||||
|     bundledPlugins("com.intellij.java", "org.jetbrains.plugins.yaml") | ||||
|     instrumentationTools() | ||||
|   } | ||||
| } | ||||
|  | ||||
| intellijPlatform { | ||||
|   buildSearchableOptions = false | ||||
| } | ||||
|  | ||||
| tasks { | ||||
|   test { | ||||
|     useJUnitPlatform() | ||||
|   } | ||||
|  | ||||
|   verifyPlugin { | ||||
|     enabled = false | ||||
|   } | ||||
|  | ||||
|   publishPlugin { | ||||
|     enabled = false | ||||
|   } | ||||
|  | ||||
|   runIde { | ||||
|     enabled = false | ||||
|   } | ||||
|  | ||||
|   runPluginVerifier { | ||||
|     enabled = false | ||||
|   } | ||||
| } | ||||
|  | ||||
| intellij { | ||||
|   version.set(ideaVersion) | ||||
|   type.set("IC") | ||||
|   // Yaml is only used for testing. It's part of the IdeaIC distribution, but needs to be included as a reference | ||||
|   plugins.set(listOf("java", "yaml")) | ||||
| } | ||||
|  | ||||
| java { | ||||
|   | ||||
| @@ -1,22 +1,15 @@ | ||||
| import org.jetbrains.intellij.platform.gradle.TestFrameworkType | ||||
| import org.jetbrains.intellij.platform.gradle.extensions.intellijPlatform | ||||
|  | ||||
| plugins { | ||||
|   java | ||||
|   kotlin("jvm") | ||||
|   id("org.jetbrains.intellij.platform.module") | ||||
|   id("org.jetbrains.intellij") | ||||
| } | ||||
|  | ||||
| repositories { | ||||
|   mavenCentral() | ||||
|  | ||||
|   intellijPlatform { | ||||
|     defaultRepositories() | ||||
|   } | ||||
|   maven { url = uri("https://cache-redirector.jetbrains.com/intellij-dependencies") } | ||||
| } | ||||
|  | ||||
| val kotlinVersion: String by project | ||||
| val ideaType: String by project | ||||
| val ideaVersion: String by project | ||||
| val javaVersion: String by project | ||||
|  | ||||
| @@ -25,35 +18,42 @@ dependencies { | ||||
|   compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") | ||||
|   testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion") | ||||
|   testImplementation(testFixtures(project(":"))) // The root project | ||||
|  | ||||
|   intellijPlatform { | ||||
|     create(ideaType, ideaVersion) | ||||
|     testFramework(TestFrameworkType.Platform) | ||||
|     testFramework(TestFrameworkType.JUnit5) | ||||
|     instrumentationTools() | ||||
|   } | ||||
| } | ||||
|  | ||||
| intellijPlatform { | ||||
|   buildSearchableOptions = false | ||||
| } | ||||
|  | ||||
| tasks { | ||||
|   // This task is disabled because it should be excluded from `gradle test` run (because it's slow) | ||||
|   // I didn't find a better way to exclude except disabling and defining a new task with a different name | ||||
|   // Note that useJUnitTestPlatform() is required to prevent red code | ||||
|   test { | ||||
|     enabled = false | ||||
|     useJUnitPlatform() | ||||
|   } | ||||
|  | ||||
|   // The `test` task is automatically set up with IntelliJ goodness. A custom test task needs to be configured for it | ||||
|   val testLongRunning by intellijPlatformTesting.testIde.registering { | ||||
|     task { | ||||
|   register<Test>("testLongRunning") { | ||||
|     group = "verification" | ||||
|     useJUnitPlatform() | ||||
|   } | ||||
|  | ||||
|   verifyPlugin { | ||||
|     enabled = false | ||||
|   } | ||||
|  | ||||
|   publishPlugin { | ||||
|     enabled = false | ||||
|   } | ||||
|  | ||||
|   runIde { | ||||
|     enabled = false | ||||
|   } | ||||
|  | ||||
|   runPluginVerifier { | ||||
|     enabled = false | ||||
|   } | ||||
| } | ||||
|  | ||||
| intellij { | ||||
|   version.set(ideaVersion) | ||||
|   type.set("IC") | ||||
|   plugins.set(listOf("java")) | ||||
| } | ||||
|  | ||||
| java { | ||||
|   | ||||
| @@ -1,22 +1,15 @@ | ||||
| import org.jetbrains.intellij.platform.gradle.TestFrameworkType | ||||
| import org.jetbrains.intellij.platform.gradle.extensions.intellijPlatform | ||||
|  | ||||
| plugins { | ||||
|   java | ||||
|   kotlin("jvm") | ||||
|   id("org.jetbrains.intellij.platform.module") | ||||
|   id("org.jetbrains.intellij") | ||||
| } | ||||
|  | ||||
| repositories { | ||||
|   mavenCentral() | ||||
|  | ||||
|   intellijPlatform { | ||||
|     defaultRepositories() | ||||
|   } | ||||
|   maven { url = uri("https://cache-redirector.jetbrains.com/intellij-dependencies") } | ||||
| } | ||||
|  | ||||
| val kotlinVersion: String by project | ||||
| val ideaType: String by project | ||||
| val ideaVersion: String by project | ||||
| val javaVersion: String by project | ||||
|  | ||||
| @@ -25,18 +18,6 @@ dependencies { | ||||
|   compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") | ||||
|   testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion") | ||||
|   testImplementation(testFixtures(project(":"))) // The root project | ||||
|  | ||||
|   intellijPlatform { | ||||
|     create(ideaType, ideaVersion) | ||||
|     bundledPlugins("com.intellij.java") | ||||
|     testFramework(TestFrameworkType.Platform) | ||||
|     testFramework(TestFrameworkType.JUnit5) | ||||
|     instrumentationTools() | ||||
|   } | ||||
| } | ||||
|  | ||||
| intellijPlatform { | ||||
|   buildSearchableOptions = false | ||||
| } | ||||
|  | ||||
| tasks { | ||||
| @@ -47,13 +28,32 @@ tasks { | ||||
|     enabled = false | ||||
|   } | ||||
|  | ||||
|   // The `test` task is automatically set up with IntelliJ goodness. A custom test task needs to be configured for it | ||||
|   val testPropertyBased by intellijPlatformTesting.testIde.registering { | ||||
|     task { | ||||
|   register<Test>("testPropertyBased") { | ||||
|     group = "verification" | ||||
|     useJUnitPlatform() | ||||
|   } | ||||
|  | ||||
|   verifyPlugin { | ||||
|     enabled = false | ||||
|   } | ||||
|  | ||||
|   publishPlugin { | ||||
|     enabled = false | ||||
|   } | ||||
|  | ||||
|   runIde { | ||||
|     enabled = false | ||||
|   } | ||||
|  | ||||
|   runPluginVerifier { | ||||
|     enabled = false | ||||
|   } | ||||
| } | ||||
|  | ||||
| intellij { | ||||
|   version.set(ideaVersion) | ||||
|   type.set("IC") | ||||
|   plugins.set(listOf("java")) | ||||
| } | ||||
|  | ||||
| java { | ||||
|   | ||||
| @@ -16,12 +16,6 @@ plugins { | ||||
|     antlr | ||||
| } | ||||
|  | ||||
| val sourcesJarArtifacts by configurations.registering { | ||||
|   attributes { | ||||
|     attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType.SOURCES)) | ||||
|   } | ||||
| } | ||||
|  | ||||
| val kotlinVersion: String by project | ||||
| val kotlinxSerializationVersion: String by project | ||||
|  | ||||
| @@ -103,8 +97,6 @@ java { | ||||
|   withJavadocJar() | ||||
| } | ||||
|  | ||||
| artifacts.add(sourcesJarArtifacts.name, tasks.named("sourcesJar")) | ||||
|  | ||||
| val spaceUsername: String by project | ||||
| val spacePassword: String by project | ||||
| val engineVersion: String by project | ||||
|   | ||||
| @@ -16,7 +16,7 @@ import com.maddyhome.idea.vim.command.Command | ||||
| import com.maddyhome.idea.vim.command.OperatorArguments | ||||
| import com.maddyhome.idea.vim.handler.VimActionHandler | ||||
|  | ||||
| @CommandOrMotion(keys = ["<C-R>"], modes = [Mode.NORMAL]) | ||||
| @CommandOrMotion(keys = ["U", "<C-R>"], modes = [Mode.NORMAL, Mode.VISUAL]) | ||||
| class RedoAction : VimActionHandler.SingleExecution() { | ||||
|   override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED | ||||
|  | ||||
|   | ||||
| @@ -16,7 +16,7 @@ import com.maddyhome.idea.vim.command.Command | ||||
| import com.maddyhome.idea.vim.command.OperatorArguments | ||||
| import com.maddyhome.idea.vim.handler.VimActionHandler | ||||
|  | ||||
| @CommandOrMotion(keys = ["u", "<Undo>"], modes = [Mode.NORMAL]) | ||||
| @CommandOrMotion(keys = ["u", "<Undo>"], modes = [Mode.NORMAL, Mode.VISUAL]) | ||||
| class UndoAction : VimActionHandler.SingleExecution() { | ||||
|   override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,6 @@ | ||||
| package com.maddyhome.idea.vim.action.change.change | ||||
|  | ||||
| import com.intellij.vim.annotations.CommandOrMotion | ||||
| import com.intellij.vim.annotations.Mode | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimCaret | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| @@ -22,7 +21,7 @@ import com.maddyhome.idea.vim.helper.CharacterHelper | ||||
| /** | ||||
|  * @author vlan | ||||
|  */ | ||||
| @CommandOrMotion(keys = ["u"], modes = [Mode.VISUAL]) | ||||
| @CommandOrMotion(keys = [], modes = []) | ||||
| class ChangeCaseLowerVisualAction : VisualOperatorActionHandler.ForEachCaret() { | ||||
|   override val type: Command.Type = Command.Type.CHANGE | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,6 @@ | ||||
| package com.maddyhome.idea.vim.action.change.change | ||||
|  | ||||
| import com.intellij.vim.annotations.CommandOrMotion | ||||
| import com.intellij.vim.annotations.Mode | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimCaret | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| @@ -22,7 +21,7 @@ import com.maddyhome.idea.vim.helper.CharacterHelper | ||||
| /** | ||||
|  * @author vlan | ||||
|  */ | ||||
| @CommandOrMotion(keys = ["U"], modes = [Mode.VISUAL]) | ||||
| @CommandOrMotion(keys = [], modes = []) | ||||
| class ChangeCaseUpperVisualAction : VisualOperatorActionHandler.ForEachCaret() { | ||||
|   override val type: Command.Type = Command.Type.CHANGE | ||||
|  | ||||
|   | ||||
| @@ -10,7 +10,6 @@ package com.maddyhome.idea.vim.action.copy | ||||
| import com.intellij.vim.annotations.CommandOrMotion | ||||
| import com.intellij.vim.annotations.Mode | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.ImmutableVimCaret | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.Argument | ||||
| @@ -36,7 +35,15 @@ sealed class PutTextBaseAction( | ||||
|     val count = operatorArguments.count1 | ||||
|     val sortedCarets = editor.sortedCarets() | ||||
|     return if (sortedCarets.size > 1) { | ||||
|       val caretToPutData = sortedCarets.associateWith { getPutDataForCaret(it, count) } | ||||
|       val putData = getPutData(count) | ||||
|  | ||||
|       val splitText = putData.textData?.rawText?.split('\n')?.dropLastWhile(String::isEmpty) | ||||
|       val caretToPutData = if (splitText != null && splitText.size == sortedCarets.size) { | ||||
|         sortedCarets.mapIndexed { index, caret -> caret to putData.copy(textData = putData.textData.copy(rawText = splitText[splitText.lastIndex - index])) }.toMap() | ||||
|       } else { | ||||
|         sortedCarets.associateWith { putData } | ||||
|       } | ||||
|        | ||||
|       var result = true | ||||
|       injector.application.runWriteAction { | ||||
|         caretToPutData.forEach { | ||||
| @@ -45,20 +52,18 @@ sealed class PutTextBaseAction( | ||||
|       } | ||||
|       result | ||||
|     } else { | ||||
|       val putData = getPutDataForCaret(sortedCarets.single(), count) | ||||
|       injector.put.putText(editor, context, putData, operatorArguments) | ||||
|       injector.put.putText(editor, context, getPutData(count), operatorArguments) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private fun getPutDataForCaret(caret: ImmutableVimCaret, count: Int): PutData { | ||||
|     val registerService = injector.registerGroup | ||||
|     val registerChar = if (caret.editor.carets().size == 1) { | ||||
|       registerService.currentRegister | ||||
|     } else { | ||||
|       registerService.getCurrentRegisterForMulticaret() | ||||
|   private fun getPutData(count: Int): PutData { | ||||
|     return PutData(getRegisterTextData(), null, count, insertTextBeforeCaret, indent, caretAfterInsertedText, -1) | ||||
|   } | ||||
|     val register = caret.registerStorage.getRegister(registerChar) | ||||
|     val textData = register?.let { | ||||
| } | ||||
|  | ||||
| fun getRegisterTextData(): TextData? { | ||||
|   val register = injector.registerGroup.getRegister(injector.registerGroup.currentRegister) | ||||
|   return register?.let { | ||||
|     TextData( | ||||
|       register.text ?: injector.parser.toPrintableString(register.keys), | ||||
|       register.type, | ||||
| @@ -66,8 +71,6 @@ sealed class PutTextBaseAction( | ||||
|       register.name, | ||||
|     ) | ||||
|   } | ||||
|     return PutData(textData, null, count, insertTextBeforeCaret, indent, caretAfterInsertedText, -1) | ||||
|   } | ||||
| } | ||||
|  | ||||
| @CommandOrMotion(keys = ["p"], modes = [Mode.NORMAL]) | ||||
|   | ||||
| @@ -41,7 +41,21 @@ sealed class PutVisualTextBaseAction( | ||||
|   ): Boolean { | ||||
|     if (caretsAndSelections.isEmpty()) return false | ||||
|     val count = cmd.count | ||||
|     val caretToPutData = editor.sortedCarets().associateWith { getPutDataForCaret(it, caretsAndSelections[it], count) } | ||||
|     val sortedCarets = editor.sortedCarets() | ||||
|  | ||||
|     val textData = getRegisterTextData() | ||||
|     val splitText = textData?.rawText?.split('\n')?.dropLastWhile(String::isEmpty) | ||||
|  | ||||
|     val caretToTextData = if (splitText != null && splitText.size == sortedCarets.size) { | ||||
|       sortedCarets.mapIndexed { index, caret -> caret to textData.copy(rawText = splitText[splitText.lastIndex - index]) }.toMap() | ||||
|     } else { | ||||
|       sortedCarets.associateWith { textData } | ||||
|     } | ||||
|      | ||||
|     val caretToPutData = caretToTextData.mapValues { (caret, textData) -> | ||||
|       getPutDataForCaret(textData, caret, caretsAndSelections[caret], count) | ||||
|     } | ||||
|      | ||||
|     injector.registerGroup.resetRegister() | ||||
|     var result = true | ||||
|     injector.application.runWriteAction { | ||||
| @@ -52,17 +66,7 @@ sealed class PutVisualTextBaseAction( | ||||
|     return result | ||||
|   } | ||||
|    | ||||
|   private fun getPutDataForCaret(caret: VimCaret, selection: VimSelection?, count: Int): PutData { | ||||
|     val lastRegisterChar = injector.registerGroup.lastRegisterChar | ||||
|     val register = caret.registerStorage.getRegister(lastRegisterChar) | ||||
|     val textData = register?.let { | ||||
|       PutData.TextData( | ||||
|         register.text ?: injector.parser.toPrintableString(register.keys), | ||||
|         register.type, | ||||
|         register.transferableData, | ||||
|         register.name, | ||||
|       ) | ||||
|     } | ||||
|   private fun getPutDataForCaret(textData: PutData.TextData?, caret: VimCaret, selection: VimSelection?, count: Int): PutData { | ||||
|     val visualSelection = selection?.let { PutData.VisualSelection(mapOf(caret to it), it.type) } | ||||
|     return PutData(textData, visualSelection, count, insertTextBeforeCaret, indent, caretAfterInsertedText) | ||||
|   } | ||||
|   | ||||
| @@ -82,6 +82,13 @@ sealed class TillCharacterMotion( | ||||
|       ) | ||||
|     } | ||||
|     injector.motion.setLastFTCmd(tillCharacterMotionType, argument.character) | ||||
|      | ||||
|     val offset = if (!finishBeforeCharacter) "" | ||||
|     else if (direction == Direction.FORWARDS) "s-1" | ||||
|     else "s+1" | ||||
|      | ||||
|     injector.searchGroup.setLastSearchState(argument.character.let { if (it == '.') "\\." else it.toString() }, offset, direction) | ||||
|      | ||||
|     return res.toMotionOrError() | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -9,20 +9,16 @@ | ||||
| package com.maddyhome.idea.vim.api | ||||
|  | ||||
| import com.maddyhome.idea.vim.common.LiveRange | ||||
| import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.group.visual.VisualChange | ||||
| import com.maddyhome.idea.vim.group.visual.vimMoveBlockSelectionToOffset | ||||
| import com.maddyhome.idea.vim.group.visual.vimMoveSelectionToCaret | ||||
| import com.maddyhome.idea.vim.handler.Motion | ||||
| import com.maddyhome.idea.vim.helper.StrictMode | ||||
| import com.maddyhome.idea.vim.helper.exitVisualMode | ||||
| import com.maddyhome.idea.vim.register.Register | ||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | ||||
| import com.maddyhome.idea.vim.state.mode.inBlockSelection | ||||
| import com.maddyhome.idea.vim.state.mode.inCommandLineMode | ||||
| import com.maddyhome.idea.vim.state.mode.inSelectMode | ||||
| import com.maddyhome.idea.vim.state.mode.inVisualMode | ||||
| import javax.swing.KeyStroke | ||||
|  | ||||
| /** | ||||
|  * Immutable interface of the caret. Immutable caret is an important concept of Fleet. | ||||
| @@ -61,7 +57,6 @@ interface ImmutableVimCaret { | ||||
|   fun hasSelection(): Boolean | ||||
|  | ||||
|   var lastSelectionInfo: SelectionInfo | ||||
|   val registerStorage: CaretRegisterStorage | ||||
|   val markStorage: LocalMarkStorage | ||||
| } | ||||
|  | ||||
| @@ -147,21 +142,3 @@ fun VimCaret.moveToMotion(motion: Motion): VimCaret { | ||||
|     this | ||||
|   } | ||||
| } | ||||
|  | ||||
| interface CaretRegisterStorage { | ||||
|   val caret: ImmutableVimCaret | ||||
|  | ||||
|   /** | ||||
|    * Stores text to caret's recordable (named/numbered/unnamed) register | ||||
|    */ | ||||
|   fun storeText(editor: VimEditor, range: TextRange, type: SelectionType, isDelete: Boolean): Boolean | ||||
|  | ||||
|   /** | ||||
|    * Gets text from caret's recordable register | ||||
|    * If the register is not recordable - global text state will be returned | ||||
|    */ | ||||
|   fun getRegister(r: Char): Register? | ||||
|  | ||||
|   fun setKeys(register: Char, keys: List<KeyStroke>) | ||||
|   fun saveRegister(r: Char, register: Register) | ||||
| } | ||||
|   | ||||
| @@ -8,75 +8,4 @@ | ||||
|  | ||||
| package com.maddyhome.idea.vim.api | ||||
|  | ||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | ||||
| import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.register.Register | ||||
| import com.maddyhome.idea.vim.register.RegisterConstants | ||||
| import com.maddyhome.idea.vim.register.VimRegisterGroupBase | ||||
| import javax.swing.KeyStroke | ||||
|  | ||||
| abstract class VimCaretBase : VimCaret | ||||
|  | ||||
| open class CaretRegisterStorageBase(override var caret: ImmutableVimCaret) : CaretRegisterStorage, VimRegisterGroupBase() { | ||||
|   companion object { | ||||
|     private const val ALLOWED_TO_STORE_REGISTERS = RegisterConstants.RECORDABLE_REGISTERS + | ||||
|       RegisterConstants.SMALL_DELETION_REGISTER + | ||||
|       RegisterConstants.BLACK_HOLE_REGISTER + | ||||
|       RegisterConstants.LAST_INSERTED_TEXT_REGISTER + | ||||
|       RegisterConstants.LAST_SEARCH_REGISTER | ||||
|   } | ||||
|  | ||||
|   override var lastRegisterChar: Char | ||||
|     get() { | ||||
|       return injector.registerGroup.lastRegisterChar | ||||
|     } | ||||
|     set(_) {} | ||||
|  | ||||
|   override var isRegisterSpecifiedExplicitly: Boolean | ||||
|     get() { | ||||
|       return injector.registerGroup.isRegisterSpecifiedExplicitly | ||||
|     } | ||||
|     set(_) {} | ||||
|  | ||||
|   override fun storeText(editor: VimEditor, range: TextRange, type: SelectionType, isDelete: Boolean): Boolean { | ||||
|     val registerChar = if (caret.editor.carets().size == 1) currentRegister else getCurrentRegisterForMulticaret() | ||||
|     if (caret.isPrimary) { | ||||
|       val registerService = injector.registerGroup | ||||
|       registerService.lastRegisterChar = registerChar | ||||
|       return registerService.storeText(editor, caret, range, type, isDelete) | ||||
|     } else { | ||||
|       if (!ALLOWED_TO_STORE_REGISTERS.contains(registerChar)) { | ||||
|         return false | ||||
|       } | ||||
|       val text = preprocessTextBeforeStoring(editor.getText(range), type) | ||||
|       return storeTextInternal(editor, caret, range, text, type, registerChar, isDelete) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   override fun getRegister(r: Char): Register? { | ||||
|     if (caret.isPrimary || !RegisterConstants.RECORDABLE_REGISTERS.contains(r)) { | ||||
|       return injector.registerGroup.getRegister(r) | ||||
|     } | ||||
|     return super.getRegister(r) ?: injector.registerGroup.getRegister(r) | ||||
|   } | ||||
|  | ||||
|   override fun setKeys(register: Char, keys: List<KeyStroke>) { | ||||
|     if (caret.isPrimary) { | ||||
|       injector.registerGroup.setKeys(register, keys) | ||||
|     } | ||||
|     if (!RegisterConstants.RECORDABLE_REGISTERS.contains(register)) { | ||||
|       return | ||||
|     } | ||||
|     return super.setKeys(register, keys) | ||||
|   } | ||||
|  | ||||
|   override fun saveRegister(r: Char, register: Register) { | ||||
|     if (caret.isPrimary) { | ||||
|       injector.registerGroup.saveRegister(r, register) | ||||
|     } | ||||
|     if (!RegisterConstants.RECORDABLE_REGISTERS.contains(r)) { | ||||
|       return | ||||
|     } | ||||
|     return super.saveRegister(r, register) | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -146,7 +146,7 @@ interface VimChangeGroup { | ||||
|     operatorArguments: OperatorArguments, | ||||
|   ) | ||||
|  | ||||
|   fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: String): VimCaret | ||||
|   fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: CharSequence): VimCaret | ||||
|  | ||||
|   fun insertText(editor: VimEditor, caret: VimCaret, str: String): VimCaret | ||||
|  | ||||
|   | ||||
| @@ -172,7 +172,7 @@ abstract class VimChangeGroupBase : VimChangeGroup { | ||||
|     if (type == null || | ||||
|       (mode == Mode.INSERT || mode == Mode.REPLACE) || | ||||
|       !saveToRegister || | ||||
|       caret.registerStorage.storeText(editor, updatedRange, type, true) | ||||
|       injector.registerGroup.storeText(editor, updatedRange, type, true, !editor.isFirstCaret, editor.isReversingCarets) | ||||
|     ) { | ||||
|       val startOffsets = updatedRange.startOffsets | ||||
|       val endOffsets = updatedRange.endOffsets | ||||
| @@ -201,7 +201,7 @@ abstract class VimChangeGroupBase : VimChangeGroup { | ||||
|    * @param caret  The caret to start insertion in | ||||
|    * @param str    The text to insert | ||||
|    */ | ||||
|   override fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: String): VimCaret { | ||||
|   override fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: CharSequence): VimCaret { | ||||
|     (editor as MutableVimEditor).insertText(caret, offset, str) | ||||
|     val newCaret = caret.moveToInlayAwareOffset(offset + str.length) | ||||
|  | ||||
|   | ||||
| @@ -188,7 +188,8 @@ interface VimEditor { | ||||
|    * This method should perform caret merging after the operations. This is similar to IJ runForEachCaret | ||||
|    * TODO review | ||||
|    */ | ||||
|  | ||||
|   val isFirstCaret: Boolean | ||||
|   val isReversingCarets: Boolean | ||||
|   fun forEachCaret(action: (VimCaret) -> Unit) | ||||
|   fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean = false) | ||||
|   fun isInForEachCaretScope(): Boolean | ||||
|   | ||||
| @@ -25,7 +25,6 @@ interface VimSearchGroup { | ||||
|    * Last used pattern to perform a substitution. | ||||
|    */ | ||||
|   var lastSubstitutePattern: String? | ||||
|  | ||||
|   fun searchBackward(editor: VimEditor, offset: Int, count: Int): TextRange? | ||||
|  | ||||
|   /** | ||||
| @@ -195,4 +194,17 @@ interface VimSearchGroup { | ||||
|    * Gets the direction lastly used in a search. | ||||
|    */ | ||||
|   fun getLastSearchDirection(): Direction | ||||
|  | ||||
|   /** | ||||
|    * Sets the last search state purely for tests | ||||
|    * | ||||
|    * @param pattern         The pattern to save. This is the last search pattern, not the last substitute pattern | ||||
|    * @param patternOffset   The pattern offset, e.g. `/{pattern}/{offset}` | ||||
|    * @param direction       The direction to search | ||||
|    */ | ||||
|   fun setLastSearchState( | ||||
|     pattern: String, | ||||
|     patternOffset: String, | ||||
|     direction: Direction, | ||||
|   ) | ||||
| } | ||||
|   | ||||
| @@ -1188,8 +1188,7 @@ abstract class VimSearchGroupBase : VimSearchGroup { | ||||
|    * @param patternOffset   The pattern offset, e.g. `/{pattern}/{offset}` | ||||
|    * @param direction       The direction to search | ||||
|    */ | ||||
|   @TestOnly | ||||
|   fun setLastSearchState( | ||||
|   override fun setLastSearchState( | ||||
|     pattern: String, | ||||
|     patternOffset: String, | ||||
|     direction: Direction, | ||||
|   | ||||
| @@ -255,8 +255,12 @@ class ToActionMappingInfo( | ||||
|  | ||||
|   override fun execute(editor: VimEditor, context: ExecutionContext, keyState: KeyHandlerState) { | ||||
|     LOG.debug("Executing 'ToAction' mapping...") | ||||
|     val commandBuilder = KeyHandler.getInstance().keyHandlerState.commandBuilder | ||||
|     for (i in 0 until commandBuilder.count.coerceAtLeast(1)) { | ||||
|       injector.actionExecutor.executeAction(action, context) | ||||
|     } | ||||
|     commandBuilder.resetCount() | ||||
|   } | ||||
|  | ||||
|   companion object { | ||||
|     private val LOG = vimLogger<ToActionMappingInfo>() | ||||
|   | ||||
| @@ -613,6 +613,12 @@ class VimRegex(pattern: String) { | ||||
|  | ||||
|     override fun nativeCarets(): List<VimCaret> = emptyList() | ||||
|      | ||||
|     override val isFirstCaret: Boolean | ||||
|       get() = false | ||||
|  | ||||
|     override val isReversingCarets: Boolean | ||||
|       get() = false | ||||
|      | ||||
|     override fun forEachCaret(action: (VimCaret) -> Unit) {} | ||||
|  | ||||
|     override fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean) {} | ||||
|   | ||||
| @@ -76,6 +76,11 @@ class Register { | ||||
|     transferableData.clear() | ||||
|   } | ||||
|    | ||||
|   fun prependTextAndResetTransferableData(text: String) { | ||||
|     this.keys.addAll(0, injector.parser.stringToKeys(text)) | ||||
|     transferableData.clear() | ||||
|   } | ||||
|  | ||||
|   fun addKeys(keys: List<KeyStroke>) { | ||||
|     this.keys.addAll(keys) | ||||
|   } | ||||
|   | ||||
| @@ -7,10 +7,9 @@ | ||||
|  */ | ||||
| package com.maddyhome.idea.vim.register | ||||
|  | ||||
| import com.maddyhome.idea.vim.api.ImmutableVimCaret | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | ||||
| import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | ||||
| import org.jetbrains.annotations.TestOnly | ||||
| import javax.swing.KeyStroke | ||||
|  | ||||
| @@ -48,10 +47,11 @@ interface VimRegisterGroup { | ||||
|   /** Store text into the last register. */ | ||||
|   fun storeText( | ||||
|     editor: VimEditor, | ||||
|     caret: ImmutableVimCaret, | ||||
|     range: TextRange, | ||||
|     type: SelectionType, | ||||
|     isDelete: Boolean, | ||||
|     forceAppend: Boolean = false, | ||||
|     prependInsteadOfAppend: Boolean = false | ||||
|   ): Boolean | ||||
|  | ||||
|   /** | ||||
|   | ||||
| @@ -8,7 +8,6 @@ | ||||
|  | ||||
| package com.maddyhome.idea.vim.register | ||||
|  | ||||
| import com.maddyhome.idea.vim.api.ImmutableVimCaret | ||||
| import com.maddyhome.idea.vim.api.Options | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.getText | ||||
| @@ -171,12 +170,13 @@ abstract class VimRegisterGroupBase : VimRegisterGroup { | ||||
|  | ||||
|   fun storeTextInternal( | ||||
|     editor: VimEditor, | ||||
|     caret: ImmutableVimCaret, | ||||
|     range: TextRange, | ||||
|     text: String, | ||||
|     type: SelectionType, | ||||
|     register: Char, | ||||
|     isDelete: Boolean, | ||||
|     forceAppend: Boolean, | ||||
|     prependInsteadOfAppend: Boolean, | ||||
|   ): Boolean { | ||||
|     // Null register doesn't get saved, but acts like it was | ||||
|     if (lastRegisterChar == BLACK_HOLE_REGISTER) return true | ||||
| @@ -198,18 +198,29 @@ abstract class VimRegisterGroupBase : VimRegisterGroup { | ||||
|     // If this is an uppercase register, we need to append the text to the corresponding lowercase register | ||||
|     val transferableData: List<Any> = | ||||
|       if (start != -1) injector.clipboardManager.getTransferableData(editor, range, text) else ArrayList() | ||||
|     val processedText = | ||||
|     var processedText = | ||||
|       if (start != -1) injector.clipboardManager.preprocessText(editor, range, text, transferableData) else text | ||||
|     logger.debug { | ||||
|       val transferableClasses = transferableData.joinToString(",") { it.javaClass.name } | ||||
|       "Copy to '$lastRegister' with transferable data: $transferableClasses" | ||||
|     } | ||||
|     if (Character.isUpperCase(register)) { | ||||
|     if (Character.isUpperCase(register) || forceAppend) { | ||||
|       if (forceAppend && type == SelectionType.CHARACTER_WISE) { | ||||
|         processedText = if (prependInsteadOfAppend) | ||||
|           processedText + '\n' | ||||
|         else | ||||
|           '\n' + processedText | ||||
|       } | ||||
|       val lreg = Character.toLowerCase(register) | ||||
|       val r = myRegisters[lreg] | ||||
|       // Append the text if the lowercase register existed | ||||
|       if (r != null) { | ||||
|         if (prependInsteadOfAppend) { | ||||
|           r.prependTextAndResetTransferableData(processedText) | ||||
|         } | ||||
|         else { | ||||
|           r.addTextAndResetTransferableData(processedText) | ||||
|         } | ||||
|       } else { | ||||
|         myRegisters[lreg] = Register(lreg, type, processedText, ArrayList(transferableData)) | ||||
|         logger.debug { "register '$register' contains: \"$processedText\"" } | ||||
| @@ -290,14 +301,15 @@ abstract class VimRegisterGroupBase : VimRegisterGroup { | ||||
|    */ | ||||
|   override fun storeText( | ||||
|     editor: VimEditor, | ||||
|     caret: ImmutableVimCaret, | ||||
|     range: TextRange, | ||||
|     type: SelectionType, | ||||
|     isDelete: Boolean, | ||||
|     forceAppend: Boolean, | ||||
|     prependInsteadOfAppend: Boolean | ||||
|   ): Boolean { | ||||
|     if (isRegisterWritable()) { | ||||
|       val text = preprocessTextBeforeStoring(editor.getText(range), type) | ||||
|       return storeTextInternal(editor, caret, range, text, type, lastRegisterChar, isDelete) | ||||
|       return storeTextInternal(editor, range, text, type, lastRegisterChar, isDelete, forceAppend, prependInsteadOfAppend) | ||||
|     } | ||||
|  | ||||
|     return false | ||||
|   | ||||
| @@ -10,7 +10,6 @@ package com.maddyhome.idea.vim.yank | ||||
|  | ||||
| import com.maddyhome.idea.vim.action.motion.updown.MotionDownLess1FirstNonSpaceAction | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.ImmutableVimCaret | ||||
| import com.maddyhome.idea.vim.api.VimCaret | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.getLineEndForOffset | ||||
| @@ -18,9 +17,9 @@ import com.maddyhome.idea.vim.api.getLineStartForOffset | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.Argument | ||||
| import com.maddyhome.idea.vim.command.OperatorArguments | ||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | ||||
| import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.listener.VimYankListener | ||||
| import com.maddyhome.idea.vim.state.mode.SelectionType | ||||
| import org.jetbrains.annotations.Contract | ||||
| import kotlin.math.min | ||||
|  | ||||
| @@ -29,7 +28,6 @@ open class YankGroupBase : VimYankGroup { | ||||
|  | ||||
|   private fun yankRange( | ||||
|     editor: VimEditor, | ||||
|     caretToRange: Map<ImmutableVimCaret, TextRange>, | ||||
|     range: TextRange, | ||||
|     type: SelectionType, | ||||
|     startOffsets: Map<VimCaret, Int>?, | ||||
| @@ -39,12 +37,7 @@ open class YankGroupBase : VimYankGroup { | ||||
|     } | ||||
|  | ||||
|     notifyListeners(editor, range) | ||||
|  | ||||
|     var result = true | ||||
|     for ((caret, myRange) in caretToRange) { | ||||
|       result = caret.registerStorage.storeText(editor, myRange, type, false) && result | ||||
|     } | ||||
|     return result | ||||
|     return injector.registerGroup.storeText(editor, range, type, false) | ||||
|   } | ||||
|  | ||||
|   @Contract("_, _ -> new") | ||||
| @@ -89,7 +82,6 @@ open class YankGroupBase : VimYankGroup { | ||||
|     val nativeCaretCount = editor.nativeCarets().size | ||||
|     if (nativeCaretCount <= 0) return false | ||||
|  | ||||
|     val caretToRange = HashMap<ImmutableVimCaret, TextRange>(nativeCaretCount) | ||||
|     val ranges = ArrayList<Pair<Int, Int>>(nativeCaretCount) | ||||
|  | ||||
|     // This logic is from original vim | ||||
| @@ -102,7 +94,6 @@ open class YankGroupBase : VimYankGroup { | ||||
|       assert(motionRange.size() == 1) | ||||
|       ranges.add(motionRange.startOffset to motionRange.endOffset) | ||||
|       startOffsets?.put(caret, motionRange.normalize().startOffset) | ||||
|       caretToRange[caret] = TextRange(motionRange.startOffset, motionRange.endOffset) | ||||
|     } | ||||
|  | ||||
|     val range = getTextRange(ranges, type) ?: return false | ||||
| @@ -111,7 +102,6 @@ open class YankGroupBase : VimYankGroup { | ||||
|  | ||||
|     return yankRange( | ||||
|       editor, | ||||
|       caretToRange, | ||||
|       range, | ||||
|       type, | ||||
|       startOffsets, | ||||
| @@ -128,7 +118,6 @@ open class YankGroupBase : VimYankGroup { | ||||
|   override fun yankLine(editor: VimEditor, count: Int): Boolean { | ||||
|     val caretCount = editor.nativeCarets().size | ||||
|     val ranges = ArrayList<Pair<Int, Int>>(caretCount) | ||||
|     val caretToRange = HashMap<ImmutableVimCaret, TextRange>(caretCount) | ||||
|     for (caret in editor.nativeCarets()) { | ||||
|       val start = injector.motion.moveCaretToCurrentLineStart(editor, caret) | ||||
|       val end = min(injector.motion.moveCaretToRelativeLineEnd(editor, caret, count - 1, true) + 1, editor.fileSize().toInt()) | ||||
| @@ -136,11 +125,10 @@ open class YankGroupBase : VimYankGroup { | ||||
|       if (end == -1) continue | ||||
|  | ||||
|       ranges.add(start to end) | ||||
|       caretToRange[caret] = TextRange(start, end) | ||||
|     } | ||||
|  | ||||
|     val range = getTextRange(ranges, SelectionType.LINE_WISE) ?: return false | ||||
|     return yankRange(editor, caretToRange, range, SelectionType.LINE_WISE, null) | ||||
|     return yankRange(editor, range, SelectionType.LINE_WISE, null) | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -153,7 +141,6 @@ open class YankGroupBase : VimYankGroup { | ||||
|    */ | ||||
|   override fun yankRange(editor: VimEditor, range: TextRange?, type: SelectionType, moveCursor: Boolean): Boolean { | ||||
|     range ?: return false | ||||
|     val caretToRange = HashMap<ImmutableVimCaret, TextRange>() | ||||
|  | ||||
|     if (type == SelectionType.LINE_WISE) { | ||||
|       for (i in 0 until range.size()) { | ||||
| @@ -173,19 +160,17 @@ open class YankGroupBase : VimYankGroup { | ||||
|     val startOffsets = HashMap<VimCaret, Int>(editor.nativeCarets().size) | ||||
|     if (type == SelectionType.BLOCK_WISE) { | ||||
|       startOffsets[editor.primaryCaret()] = range.normalize().startOffset | ||||
|       caretToRange[editor.primaryCaret()] = range | ||||
|     } else { | ||||
|       for ((i, caret) in editor.nativeCarets().withIndex()) { | ||||
|         val textRange = TextRange(rangeStartOffsets[i], rangeEndOffsets[i]) | ||||
|         startOffsets[caret] = textRange.normalize().startOffset | ||||
|         caretToRange[caret] = textRange | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return if (moveCursor) { | ||||
|       yankRange(editor, caretToRange, range, type, startOffsets) | ||||
|       yankRange(editor, range, type, startOffsets) | ||||
|     } else { | ||||
|       yankRange(editor, caretToRange, range, type, null) | ||||
|       yankRange(editor, range, type, null) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user