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