mirror of
				https://github.com/chylex/IntelliJ-IdeaVim.git
				synced 2025-10-31 11:17:13 +01:00 
			
		
		
		
	Compare commits
	
		
			237 Commits
		
	
	
		
			VIM-2227
			...
			9f469d0eb2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 9f469d0eb2 | |||
| f59d2f769c | |||
| dc00b59733 | |||
| bc20e6af9c | |||
| c40f07714a | |||
| fa68842c2d | |||
| eca9258607 | |||
| 46fb030977 | |||
|   | da3d83ecc6 | ||
|   | 4af8e574c4 | ||
|   | bdcb5c4f33 | ||
|   | 013f7a42c2 | ||
|   | d03398f3e8 | ||
|   | 7a26307a2b | ||
|   | fa6a0369b8 | ||
|   | ad8cb0ba09 | ||
|   | 8125ce5072 | ||
|   | 6c0cc7285f | ||
|   | d3424021c8 | ||
|   | 623aa40acd | ||
|   | c131cb338e | ||
|   | 14d242a233 | ||
|   | a131b7d29a | ||
|   | 85a1fbe89e | ||
|   | 142550a1f8 | ||
|   | e3d3b73903 | ||
|   | 45f18ff91c | ||
|   | 2103163207 | ||
|   | 19dd49670c | ||
|   | e738a1a821 | ||
|   | 6e0f301fb8 | ||
|   | c76b8db293 | ||
|   | 9fa4ca8fb3 | ||
|   | 871b60fe8d | ||
|   | 6715a5b61f | ||
|   | d7d91f1cc5 | ||
|   | 9f00dbd6f4 | ||
|   | f95cf3d671 | ||
|   | 7fbc17624f | ||
|   | b9c2ea37cb | ||
|   | ca0db15e01 | ||
|   | c32c62eacc | ||
|   | 43a79dbad4 | ||
|   | 2829a13187 | ||
|   | efc8c9207d | ||
|   | 183ed10592 | ||
|   | 926b47a31e | ||
|   | d272c919ea | ||
|   | f6e7d04fd5 | ||
|   | ccdff4f087 | ||
|   | ff14303e88 | ||
|   | 48a592340b | ||
|   | da8f5f3231 | ||
|   | f8fa8b73fa | ||
|   | aee126b625 | ||
|   | 396ac86939 | ||
|   | 81816f903f | ||
|   | 06a85b784b | ||
|   | 7f1e3bb155 | ||
|   | 241f554133 | ||
|   | 9498d0779c | ||
|   | b12fd5100f | ||
|   | 92f622430d | ||
|   | ef518f5b23 | ||
|   | 7acb17ebdb | ||
|   | 479a7dbbaf | ||
|   | 4fd1a25557 | ||
|   | f32d42e625 | ||
|   | 362b9a5c3a | ||
|   | e42b4d0ea3 | ||
|   | 43767b8500 | ||
|   | d05098c870 | ||
|   | 505f485568 | ||
|   | 86f512fb91 | ||
|   | 12903066b9 | ||
|   | 118d0433cb | ||
|   | eb781c3679 | ||
|   | 3c6bffba03 | ||
|   | 89623b04d6 | ||
|   | 444a96132c | ||
|   | c008553127 | ||
|   | 162d7b021f | ||
|   | c82364c1dd | ||
|   | 977402c6b0 | ||
|   | b14e59ab5b | ||
|   | 47dcefcfbf | ||
|   | 2b299cb422 | ||
|   | d0ccbb4620 | ||
|   | 187b207271 | ||
|   | a3e22c844c | ||
|   | f8384b2732 | ||
|   | 2dae43258c | ||
|   | 48033ecb85 | ||
|   | ad8df027ac | ||
|   | 91f580771d | ||
|   | 9c6f0981f5 | ||
|   | 2212569688 | ||
|   | 6711f1dbab | ||
|   | 0b7a883efb | ||
|   | 8ae84c62c0 | ||
|   | 0d168c8115 | ||
| c7b51b7fa5 | |||
|   | 86bf723791 | ||
|   | 71f2e9de4a | ||
|   | 33d3f270a3 | ||
|   | da94edd386 | ||
|   | 90dfaefd11 | ||
|   | 8bc616cc55 | ||
|   | a9e79d62c5 | ||
|   | 1998221a0b | ||
|   | a9b1625749 | ||
|   | b411836570 | ||
|   | df7e0221a8 | ||
|   | 8ff8f2b685 | ||
|   | 65dea7e3f7 | ||
|   | 1942f86633 | ||
|   | ee4ce5033a | ||
|   | 040fe806c8 | ||
|   | 97f5c9225e | ||
|   | 09b86c15f9 | ||
|   | 8f34285d8c | ||
|   | d3c3b71e3e | ||
|   | aa6f49c9b1 | ||
|   | c011628420 | ||
|   | 1c9fa9d662 | ||
|   | 7b9bc64364 | ||
|   | 729062bfdd | ||
|   | bc6c726a45 | ||
|   | dfc3df713e | ||
|   | 42eca1d5f2 | ||
|   | 66245e2730 | ||
|   | d44b82c1d1 | ||
|   | 5440e48fa3 | ||
|   | 1c513cf8aa | ||
|   | a17c4b8d43 | ||
|   | 15eb4ac278 | ||
|   | 0d9b81eab3 | ||
|   | f02e1a20c7 | ||
|   | a11991dad7 | ||
|   | 1238828bfd | ||
|   | ba409cb24c | ||
|   | d597670275 | ||
|   | d8540e95f8 | ||
|   | d35ebf00dd | ||
|   | 2b32cb26b1 | ||
|   | ca95fcb658 | ||
|   | cc18bbd168 | ||
|   | 8c8ea800cb | ||
|   | 0746dcc686 | ||
|   | 930650be9d | ||
|   | 4e3a9ffa40 | ||
|   | 3bf68a2bb8 | ||
|   | a80f6feab0 | ||
|   | 3cf8ae52ed | ||
|   | 62632a4514 | ||
|   | 249bd3778a | ||
|   | ab9e5d7a4a | ||
|   | 083b7dc952 | ||
|   | 1791692d92 | ||
|   | ba23c9ab5e | ||
|   | f96ab37bcb | ||
|   | 0da34bbb34 | ||
|   | 51e7c745ea | ||
|   | 8347251572 | ||
|   | ce8512f4e0 | ||
|   | a724a19d00 | ||
|   | 7eae7a98e8 | ||
|   | fe9566eebd | ||
|   | b69756730f | ||
|   | 6cd1a60b53 | ||
|   | 9d935e47b5 | ||
|   | a7d5372d06 | ||
|   | a575942c81 | ||
|   | 3cf6c53a8e | ||
|   | 91d86680de | ||
|   | d1d082fb99 | ||
|   | 2c634d1bf0 | ||
|   | 02a6fe4dc9 | ||
|   | 223d681526 | ||
|   | f42ef1c2fc | ||
|   | f4817b2111 | ||
|   | 6f5def0abf | ||
|   | f0fcd7f133 | ||
|   | 6115adb72e | ||
|   | bfd0b5fd91 | ||
|   | 6c0a52155b | ||
|   | c7ebce39bf | ||
|   | a0dc7a792f | ||
|   | fdb09a8f1f | ||
|   | 4ac2aa2339 | ||
|   | ebbc20692a | ||
|   | 5c82d112c9 | ||
|   | 40fd50d7f1 | ||
|   | f7b948fee2 | ||
|   | 6cdcf133bb | ||
|   | f3025757b6 | ||
|   | a49811a3f9 | ||
|   | 2ac4b265d1 | ||
|   | 46e994b563 | ||
|   | 81482bd298 | ||
|   | f1a239c085 | ||
|   | a48e38de7b | ||
|   | 0aaacee117 | ||
|   | b8373af69f | ||
|   | e99b2ee73d | ||
|   | 705022331a | ||
|   | bf62d444bf | ||
|   | 5dcff5657b | ||
|   | 7e79d5a960 | ||
|   | c1b480976d | ||
|   | ddabbe6891 | ||
|   | ffa3052b50 | ||
|   | 654ea88851 | ||
|   | 4dc7982baa | ||
|   | ae2fc1cd85 | ||
|   | 78bc8666e6 | ||
|   | 9c2de2cfc7 | ||
|   | 71e81465f3 | ||
|   | 885031e086 | ||
|   | d77c5bb5cf | ||
|   | 781bce0000 | ||
|   | a3ca1b965b | ||
|   | dd20b480a7 | ||
|   | 38292e97af | ||
|   | 46ea752164 | ||
|   | 194b744361 | ||
| b50197f7ce | |||
|   | c00703d1d0 | ||
|   | 6e12377116 | ||
|   | b0c4391ad8 | ||
|   | f43ac2538a | ||
|   | 9eaf8b5d2d | ||
|   | e365d0b07c | ||
|   | 69c273c4a5 | ||
|   | f7950e7adb | ||
|   | 7c1ae9812e | ||
|   | 5c794ac40e | 
							
								
								
									
										3
									
								
								.github/workflows/mergePr.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/workflows/mergePr.yml
									
									
									
									
										vendored
									
									
								
							| @@ -4,6 +4,7 @@ | ||||
| name: Update Changelog On PR | ||||
|  | ||||
| on: | ||||
|   workflow_dispatch: | ||||
|   pull_request: | ||||
|     types: [ closed ] | ||||
|  | ||||
| @@ -29,7 +30,7 @@ jobs: | ||||
|         id: update_authors | ||||
|         run: ./gradlew updateMergedPr -PprId=${{ github.event.number }} | ||||
|         env: | ||||
|           GITHUB_OAUTH: ${{ secrets.AUTOMATION_TOKEN }} | ||||
|           GITHUB_OAUTH: ${{ secrets.MERGE_PR }} | ||||
|  | ||||
|       - name: Commit changes | ||||
|         uses: stefanzweifel/git-auto-commit-action@v4 | ||||
|   | ||||
							
								
								
									
										40
									
								
								.github/workflows/mergePrTest.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								.github/workflows/mergePrTest.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| # This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created | ||||
| # For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle | ||||
|  | ||||
| name: Update Changelog On PR (Test) | ||||
|  | ||||
| on: | ||||
|   workflow_dispatch: | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|  | ||||
|     runs-on: ubuntu-latest | ||||
|  | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|         with: | ||||
|           fetch-depth: 50 | ||||
|       - name: Set up JDK 11 | ||||
|         uses: actions/setup-java@v2 | ||||
|         with: | ||||
|           java-version: '11' | ||||
|           distribution: 'adopt' | ||||
|           server-id: github # Value of the distributionManagement/repository/id field of the pom.xml | ||||
|           settings-path: ${{ github.workspace }} # location for the settings.xml file | ||||
|  | ||||
|       - name: Update authors | ||||
|         id: update_authors | ||||
|         run: ./gradlew updateMergedPr -PprId=525 | ||||
|         env: | ||||
|           GITHUB_OAUTH: ${{ secrets.MERGE_PR }} | ||||
|  | ||||
| #      - name: Commit changes | ||||
| #        uses: stefanzweifel/git-auto-commit-action@v4 | ||||
| #        with: | ||||
| #          branch: master | ||||
| #          commit_message: Update changelog  after merging PR | ||||
| #          commit_user_name: Alex Plate | ||||
| #          commit_user_email: aleksei.plate@jetbrains.com | ||||
| #          commit_author: Alex Plate <aleksei.plate@jetbrains.com> | ||||
| #          file_pattern: CHANGES.md | ||||
							
								
								
									
										2
									
								
								.github/workflows/updateAuthors.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/updateAuthors.yml
									
									
									
									
										vendored
									
									
								
							| @@ -37,7 +37,7 @@ jobs: | ||||
|         run: ./gradlew updateAuthors --stacktrace | ||||
|         env: | ||||
|           SUCCESS_COMMIT: ${{ steps.last_successful_commit.outputs.commit_hash }} | ||||
|           GITHUB_OAUTH: ${{ secrets.AUTOMATION_TOKEN }} | ||||
|           GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }} | ||||
|  | ||||
|       - name: Commit changes | ||||
|         uses: stefanzweifel/git-auto-commit-action@v4 | ||||
|   | ||||
							
								
								
									
										7
									
								
								.teamcity/_Self/Constants.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.teamcity/_Self/Constants.kt
									
									
									
									
										vendored
									
									
								
							| @@ -5,15 +5,16 @@ object Constants { | ||||
|   const val EAP_CHANNEL = "eap" | ||||
|   const val DEV_CHANNEL = "Dev" | ||||
|  | ||||
|   const val VERSION = "1.10.3" | ||||
|   const val DEV_VERSION = "1.11.0" | ||||
|   const val VERSION = "1.11.1" | ||||
|   const val DEV_VERSION = "1.12.0" | ||||
|  | ||||
|   const val GITHUB_TESTS = "LATEST-EAP-SNAPSHOT" | ||||
|   const val NVIM_TESTS = "LATEST-EAP-SNAPSHOT" | ||||
|   const val PROPERTY_TESTS = "LATEST-EAP-SNAPSHOT" | ||||
|   const val LONG_RUNNING_TESTS = "LATEST-EAP-SNAPSHOT" | ||||
|   const val QODANA_TESTS = "LATEST-EAP-SNAPSHOT" | ||||
|   const val RELEASE = "2021.3" | ||||
|   const val RELEASE = "LATEST-EAP-SNAPSHOT" | ||||
|  | ||||
|   const val RELEASE_DEV = "LATEST-EAP-SNAPSHOT" | ||||
|   const val RELEASE_EAP = "LATEST-EAP-SNAPSHOT" | ||||
| } | ||||
|   | ||||
							
								
								
									
										16
									
								
								.teamcity/_Self/Project.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.teamcity/_Self/Project.kt
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,13 @@ | ||||
| package _Self | ||||
|  | ||||
| import _Self.buildTypes.* | ||||
| import _Self.buildTypes.Compatibility | ||||
| import _Self.buildTypes.LongRunning | ||||
| import _Self.buildTypes.Nvim | ||||
| import _Self.buildTypes.PluginVerifier | ||||
| import _Self.buildTypes.PropertyBased | ||||
| import _Self.buildTypes.Qodana | ||||
| import _Self.buildTypes.TestsForIntelliJ20222 | ||||
| import _Self.buildTypes.TestsForIntelliJEAP | ||||
| import _Self.subprojects.GitHub | ||||
| import _Self.subprojects.OldTests | ||||
| import _Self.subprojects.Releases | ||||
| @@ -10,6 +17,7 @@ import _Self.vcsRoots.Branch_191_193 | ||||
| import _Self.vcsRoots.Branch_201 | ||||
| import _Self.vcsRoots.Branch_202 | ||||
| import _Self.vcsRoots.Branch_203_212 | ||||
| import _Self.vcsRoots.Branch_213_221 | ||||
| import _Self.vcsRoots.Branch_Release | ||||
| import _Self.vcsRoots.GitHubPullRequest | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.Project | ||||
| @@ -26,18 +34,20 @@ object Project : Project({ | ||||
|   vcsRoot(Branch_201) | ||||
|   vcsRoot(Branch_202) | ||||
|   vcsRoot(Branch_203_212) | ||||
|   vcsRoot(Branch_213_221) | ||||
|   vcsRoot(Branch_Release) | ||||
|   vcsRoot(GitHubPullRequest) | ||||
|  | ||||
|   // Builds | ||||
|   buildType(TestsForIntelliJ20213) | ||||
|   buildType(TestsForIntelliJEAP) | ||||
|   buildType(TestsForIntelliJ20222) | ||||
|  | ||||
|   buildType(PropertyBased) | ||||
|   buildType(LongRunning) | ||||
|  | ||||
|   buildType(Nvim) | ||||
|   buildType(PluginVerifier) | ||||
|   buildType(Compatibility) | ||||
|  | ||||
|   buildType(Qodana) | ||||
|  | ||||
| @@ -47,7 +57,7 @@ object Project : Project({ | ||||
|       type = "CloudImage" | ||||
|       id = "PROJECT_EXT_768" | ||||
|       param("agent_pool_id", "41") | ||||
|       param("amazon-id", "ami-0d1a6a32faa92923e") | ||||
|       param("amazon-id", "ami-0fa17ce8238eb8868") | ||||
|       param("ebs-optimized", "false") | ||||
|       param("image-instances-limit", "") | ||||
|       param("image-name-prefix", "BuildAgentsIdeaVim") | ||||
|   | ||||
							
								
								
									
										2
									
								
								.teamcity/_Self/buildTypes/ActiveTests.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.teamcity/_Self/buildTypes/ActiveTests.kt
									
									
									
									
										vendored
									
									
								
							| @@ -55,4 +55,4 @@ sealed class ActiveTests(buildName: String, ijVersion: String) : BuildType({ | ||||
| }) | ||||
|  | ||||
| object TestsForIntelliJEAP : ActiveTests("Tests for IntelliJ Latest EAP", "LATEST-EAP-SNAPSHOT") | ||||
| object TestsForIntelliJ20213 : ActiveTests("Tests for IntelliJ 2021.3", "2021.3.2") | ||||
| object TestsForIntelliJ20222 : ActiveTests("Tests for IntelliJ 2022.2", "2022.2.1") | ||||
|   | ||||
							
								
								
									
										49
									
								
								.teamcity/_Self/buildTypes/Compatibility.kt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								.teamcity/_Self/buildTypes/Compatibility.kt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| package _Self.buildTypes | ||||
|  | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.DslContext | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.golang | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.schedule | ||||
|  | ||||
| object Compatibility : BuildType({ | ||||
|   id("IdeaVimCompatibility") | ||||
|   name = "IdeaVim compatibility with external plugins" | ||||
|  | ||||
|   vcs { | ||||
|     root(DslContext.settingsRoot) | ||||
|   } | ||||
|  | ||||
|   steps { | ||||
|     script { | ||||
|       name = "Check" | ||||
|       scriptContent = """ | ||||
|                 java -jar verifier/verifier-cli-dev-all.jar check-plugin '${'$'}org.jetbrains.IdeaVim-EasyMotion' [latest-IU] -team-city | ||||
|                 java -jar verifier/verifier-cli-dev-all.jar check-plugin '${'$'}io.github.mishkun.ideavimsneak' [latest-IU] -team-city | ||||
|                 java -jar verifier/verifier-cli-dev-all.jar check-plugin '${'$'}eu.theblob42.idea.whichkey' [latest-IU] -team-city | ||||
|                 java -jar verifier/verifier-cli-dev-all.jar check-plugin '${'$'}IdeaVimExtension' [latest-IU] -team-city | ||||
|                 # Outdated java -jar verifier/verifier-cli-dev-all.jar check-plugin '${'$'}github.zgqq.intellij-enhance' [latest-IU] -team-city | ||||
|                 java -jar verifier/verifier-cli-dev-all.jar check-plugin '${'$'}com.github.copilot' [latest-IU] -team-city | ||||
|                 java -jar verifier/verifier-cli-dev-all.jar check-plugin '${'$'}com.github.dankinsoid.multicursor' [latest-IU] -team-city | ||||
|                 java -jar verifier/verifier-cli-dev-all.jar check-plugin '${'$'}com.joshestein.ideavim-quickscope' [latest-IU] -team-city | ||||
|             """.trimIndent() | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   triggers { | ||||
|     schedule { | ||||
|       schedulingPolicy = daily { | ||||
|         hour = 4 | ||||
|       } | ||||
|       branchFilter = "" | ||||
|       triggerBuild = always() | ||||
|       withPendingChangesOnly = false | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   features { | ||||
|     golang { | ||||
|       testFormat = "json" | ||||
|     } | ||||
|   } | ||||
| }) | ||||
							
								
								
									
										2
									
								
								.teamcity/_Self/buildTypes/Nvim.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.teamcity/_Self/buildTypes/Nvim.kt
									
									
									
									
										vendored
									
									
								
							| @@ -31,7 +31,7 @@ object Nvim : BuildType({ | ||||
|     script { | ||||
|       name = "Set up NeoVim" | ||||
|       scriptContent = """ | ||||
|               wget https://github.com/neovim/neovim/releases/download/v0.4.4/nvim-linux64.tar.gz | ||||
|               wget https://github.com/neovim/neovim/releases/download/v0.7.2/nvim-linux64.tar.gz | ||||
|               tar xzf nvim-linux64.tar.gz | ||||
|               cd nvim-linux64/bin | ||||
|               chmod +x nvim | ||||
|   | ||||
							
								
								
									
										6
									
								
								.teamcity/_Self/buildTypes/Qodana.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.teamcity/_Self/buildTypes/Qodana.kt
									
									
									
									
										vendored
									
									
								
							| @@ -4,6 +4,7 @@ import _Self.Constants.QODANA_TESTS | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.DslContext | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.Qodana | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.qodana | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.BuildFailureOnMetric | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.failOnMetricChange | ||||
| @@ -46,12 +47,15 @@ object Qodana : BuildType({ | ||||
|       param("clonefinder-enable", "true") | ||||
|       param("clonefinder-reference-projects", "src") | ||||
|       param("yaml-configuration", "") | ||||
|       linter = jvm { | ||||
|         version = Qodana.JVMVersion.LATEST | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   triggers { | ||||
|     vcs { | ||||
|       enabled = false | ||||
|       enabled = true | ||||
|       branchFilter = "" | ||||
|     } | ||||
|     schedule { | ||||
|   | ||||
							
								
								
									
										62
									
								
								.teamcity/_Self/buildTypes/TestsForIntelliJ_213-221.kt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								.teamcity/_Self/buildTypes/TestsForIntelliJ_213-221.kt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| @file:Suppress("ClassName") | ||||
|  | ||||
| package _Self.buildTypes | ||||
|  | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.BuildFailureOnMetric | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.failOnMetricChange | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.vcs | ||||
|  | ||||
| sealed class TestsForIntelliJ_213_221_branch(private val version: String) : BuildType({ | ||||
|   name = "Tests for IntelliJ $version" | ||||
|  | ||||
|   params { | ||||
|     param("env.ORG_GRADLE_PROJECT_downloadIdeaSources", "false") | ||||
|     param("env.ORG_GRADLE_PROJECT_legacyNoJavaPlugin", "true") | ||||
|     param("env.ORG_GRADLE_PROJECT_ideaVersion", "IC-$version") | ||||
|     param("env.ORG_GRADLE_PROJECT_instrumentPluginCode", "false") | ||||
|     param("env.ORG_GRADLE_PROJECT_javaVersion", "1.8") | ||||
|   } | ||||
|  | ||||
|   vcs { | ||||
|     root(_Self.vcsRoots.Branch_213_221) | ||||
|  | ||||
|     checkoutMode = CheckoutMode.AUTO | ||||
|   } | ||||
|  | ||||
|   steps { | ||||
|     gradle { | ||||
|       tasks = "clean test" | ||||
|       buildFile = "" | ||||
|       enableStacktrace = true | ||||
|       param("org.jfrog.artifactory.selectedDeployableServer.defaultModuleVersionConfiguration", "GLOBAL") | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   triggers { | ||||
|     vcs { | ||||
|       branchFilter = "" | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   requirements { | ||||
|     noLessThanVer("teamcity.agent.jvm.version", "1.8") | ||||
|   } | ||||
|  | ||||
|   failureConditions { | ||||
|     failOnMetricChange { | ||||
|       metric = BuildFailureOnMetric.MetricType.TEST_COUNT | ||||
|       threshold = 20 | ||||
|       units = BuildFailureOnMetric.MetricUnit.PERCENTS | ||||
|       comparison = BuildFailureOnMetric.MetricComparison.LESS | ||||
|       compareTo = build { | ||||
|         buildRule = lastSuccessful() | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| }) | ||||
|  | ||||
|  | ||||
| object TestsForIntelliJ20213 : TestsForIntelliJ_213_221_branch("2021.3.2") | ||||
							
								
								
									
										2
									
								
								.teamcity/_Self/subprojects/OldTests.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.teamcity/_Self/subprojects/OldTests.kt
									
									
									
									
										vendored
									
									
								
							| @@ -11,6 +11,7 @@ import _Self.buildTypes.TestsForIntelliJ20202 | ||||
| import _Self.buildTypes.TestsForIntelliJ20203 | ||||
| import _Self.buildTypes.TestsForIntelliJ20211 | ||||
| import _Self.buildTypes.TestsForIntelliJ20212 | ||||
| import _Self.buildTypes.TestsForIntelliJ20213 | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.Project | ||||
|  | ||||
| object OldTests : Project({ | ||||
| @@ -28,4 +29,5 @@ object OldTests : Project({ | ||||
|   buildType(TestsForIntelliJ20203) | ||||
|   buildType(TestsForIntelliJ20211) | ||||
|   buildType(TestsForIntelliJ20212) | ||||
|   buildType(TestsForIntelliJ20213) | ||||
| }) | ||||
|   | ||||
							
								
								
									
										12
									
								
								.teamcity/_Self/vcsRoots/Branch_213_221.kt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.teamcity/_Self/vcsRoots/Branch_213_221.kt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| @file:Suppress("ClassName") | ||||
|  | ||||
| package _Self.vcsRoots | ||||
|  | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.vcs.GitVcsRoot | ||||
|  | ||||
| object Branch_213_221 : GitVcsRoot({ | ||||
|   id("HttpsGithubComJetBrainsIdeavimBranch213221") | ||||
|   name = "https://github.com/JetBrains/ideavim (branch 213-221)" | ||||
|   url = "https://github.com/JetBrains/ideavim.git" | ||||
|   branch = "213-221" | ||||
| }) | ||||
							
								
								
									
										25
									
								
								.teamcity/patches/buildTypes/Nvim.kts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								.teamcity/patches/buildTypes/Nvim.kts
									
									
									
									
										vendored
									
									
								
							| @@ -1,25 +0,0 @@ | ||||
| package patches.buildTypes | ||||
|  | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.* | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.VcsTrigger | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.vcs | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.ui.* | ||||
|  | ||||
| /* | ||||
| This patch script was generated by TeamCity on settings change in UI. | ||||
| To apply the patch, change the buildType with id = 'Nvim' | ||||
| accordingly, and delete the patch script. | ||||
| */ | ||||
| changeBuildType(RelativeId("Nvim")) { | ||||
|     triggers { | ||||
|         val trigger1 = find<VcsTrigger> { | ||||
|             vcs { | ||||
|                 branchFilter = "" | ||||
|             } | ||||
|         } | ||||
|         trigger1.apply { | ||||
|             enabled = false | ||||
|  | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										25
									
								
								.teamcity/patches/buildTypes/PluginVerifier.kts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								.teamcity/patches/buildTypes/PluginVerifier.kts
									
									
									
									
										vendored
									
									
								
							| @@ -1,25 +0,0 @@ | ||||
| package patches.buildTypes | ||||
|  | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.* | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.VcsTrigger | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.vcs | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.ui.* | ||||
|  | ||||
| /* | ||||
| This patch script was generated by TeamCity on settings change in UI. | ||||
| To apply the patch, change the buildType with id = 'PluginVerifier' | ||||
| accordingly, and delete the patch script. | ||||
| */ | ||||
| changeBuildType(RelativeId("PluginVerifier")) { | ||||
|     triggers { | ||||
|         val trigger1 = find<VcsTrigger> { | ||||
|             vcs { | ||||
|                 branchFilter = "" | ||||
|             } | ||||
|         } | ||||
|         trigger1.apply { | ||||
|             enabled = false | ||||
|  | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										47
									
								
								.teamcity/patches/buildTypes/Qodana.kts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								.teamcity/patches/buildTypes/Qodana.kts
									
									
									
									
										vendored
									
									
								
							| @@ -2,9 +2,8 @@ package patches.buildTypes | ||||
|  | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.* | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.Qodana | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.qodana | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.ScheduleTrigger | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.schedule | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.ui.* | ||||
|  | ||||
| /* | ||||
| @@ -16,9 +15,8 @@ changeBuildType(RelativeId("Qodana")) { | ||||
|     expectSteps { | ||||
|         qodana { | ||||
|             name = "Qodana" | ||||
|             reportAsTests = true | ||||
|             inspectionProfile = customProfile { | ||||
|                 path = ".idea/inspectionProfiles/Qodana.xml" | ||||
|             linter = jvm { | ||||
|                 version = Qodana.JVMVersion.LATEST | ||||
|             } | ||||
|             param("clonefinder-enable", "true") | ||||
|             param("clonefinder-languages", "Java") | ||||
| @@ -26,7 +24,6 @@ changeBuildType(RelativeId("Qodana")) { | ||||
|             param("clonefinder-mode", "") | ||||
|             param("clonefinder-queried-project", "src") | ||||
|             param("clonefinder-reference-projects", "src") | ||||
|             param("fail-build-on-errors", "") | ||||
|             param("licenseaudit-enable", "true") | ||||
|             param("namesAndTagsCustom", "repo.labs.intellij.net/static-analyser/qodana") | ||||
|             param("report-version", "") | ||||
| @@ -34,27 +31,25 @@ changeBuildType(RelativeId("Qodana")) { | ||||
|         } | ||||
|     } | ||||
|     steps { | ||||
|         update<Qodana>(0) { | ||||
|         insert(0) { | ||||
|             gradle { | ||||
|                 name = "Generate grammar" | ||||
|                 tasks = "generateGrammarSource" | ||||
|                 param("org.jfrog.artifactory.selectedDeployableServer.defaultModuleVersionConfiguration", "GLOBAL") | ||||
|             } | ||||
|         } | ||||
|         update<Qodana>(1) { | ||||
|             clearConditions() | ||||
|             linter = jvm { | ||||
|                 version = Qodana.JVMVersion.LATEST | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     triggers { | ||||
|         val trigger1 = find<ScheduleTrigger> { | ||||
|             schedule { | ||||
|                 schedulingPolicy = weekly { | ||||
|                     dayOfWeek = ScheduleTrigger.DAY.Tuesday | ||||
|                 } | ||||
|                 branchFilter = "" | ||||
|                 triggerBuild = always() | ||||
|             } | ||||
|         } | ||||
|         trigger1.apply { | ||||
|             enabled = false | ||||
|  | ||||
|             reportAsTests = true | ||||
|             argumentsCommandDocker = "-e QODANA_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJvcmdhbml6YXRpb24iOiIzUFZrQSIsInByb2plY3QiOiIzN1FlQSIsInRva2VuIjoiM0t2bXoifQ.uohp81tM7iAfvvB6k8faarfpV-OjusAaEbWQ8iNrOgs" | ||||
|             argumentsEntryPointDocker = "--baseline qodana.sarif.json" | ||||
|             param("clonefinder-languages", "") | ||||
|             param("collect-anonymous-statistics", "") | ||||
|             param("licenseaudit-enable", "") | ||||
|             param("clonefinder-languages-container", "") | ||||
|             param("clonefinder-queried-project", "") | ||||
|             param("clonefinder-enable", "") | ||||
|             param("clonefinder-reference-projects", "") | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										27
									
								
								.teamcity/patches/buildTypes/ReleaseEap.kts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								.teamcity/patches/buildTypes/ReleaseEap.kts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| package patches.buildTypes | ||||
|  | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.* | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.VcsLabeling | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.vcsLabeling | ||||
| import jetbrains.buildServer.configs.kotlin.v2019_2.ui.* | ||||
|  | ||||
| /* | ||||
| This patch script was generated by TeamCity on settings change in UI. | ||||
| To apply the patch, change the buildType with id = 'ReleaseEap' | ||||
| accordingly, and delete the patch script. | ||||
| */ | ||||
| changeBuildType(RelativeId("ReleaseEap")) { | ||||
|     features { | ||||
|         val feature1 = find<VcsLabeling> { | ||||
|             vcsLabeling { | ||||
|                 vcsRootId = "${DslContext.settingsRoot.id}" | ||||
|                 labelingPattern = "%system.build.number%" | ||||
|                 successfulOnly = true | ||||
|                 branchFilter = "" | ||||
|             } | ||||
|         } | ||||
|         feature1.apply { | ||||
|             successfulOnly = false | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										73
									
								
								AUTHORS.md
									
									
									
									
									
								
							
							
						
						
									
										73
									
								
								AUTHORS.md
									
									
									
									
									
								
							| @@ -32,16 +32,48 @@ Contributors: | ||||
|   [![icon][github]](https://github.com/yole) | ||||
|     | ||||
|   Dmitry Jemerov | ||||
| * [![icon][mail]](mailto:tony.kay@gmail.com) | ||||
|   [![icon][github]](https://github.com/awkay) | ||||
|     | ||||
|   Tony Kay | ||||
| * [![icon][mail]](mailto:jamescmartinez@gmail.com) | ||||
|   [![icon][github]](https://github.com/jamescmartinez) | ||||
|     | ||||
|   James Martinez | ||||
| * [![icon][mail]](mailto:almas337519@gmail.com) | ||||
|   [![icon][github]](https://github.com/strogiyotec) | ||||
|     | ||||
|   strogiyotec | ||||
| * [![icon][mail]](mailto:raimon49@hotmail.com) | ||||
|   [![icon][github]](https://github.com/raimon49) | ||||
|     | ||||
|   raimon | ||||
| * [![icon][mail]](mailto:agrsbm@gmail.com) | ||||
|   [![icon][github-off]](#) | ||||
|     | ||||
|   Alexander Griesbaum | ||||
| * [![icon][mail]](mailto:manwe64@gmail.com) | ||||
|   [![icon][github]](https://github.com/baldrs) | ||||
|     | ||||
|   Baldrs | ||||
| * [![icon][mail]](mailto:yury@shurup.com) | ||||
|   [![icon][github]](https://github.com/zyv) | ||||
|     | ||||
|   Yury V. Zaytsev | ||||
| * [![icon][mail]](mailto:jflorian@doubledog.org) | ||||
|   [![icon][github]](https://github.com/jflorian) | ||||
|     | ||||
|   John Florian | ||||
| * [![icon][mail]](mailto:marquis@marquiswang.com) | ||||
|   [![icon][github]](https://github.com/marquiswang) | ||||
|     | ||||
|   Marquis Wang | ||||
| * [![icon][mail]](mailto:madgnome@gmail.com) | ||||
|   [![icon][github-off]](#) | ||||
|   [![icon][github]](https://github.com/madgnome) | ||||
|     | ||||
|   Julien Hoarau   | ||||
| * [![icon][mail]](mailto:masanobu.imai@gmail.com) | ||||
|   [![icon][github-off]](#) | ||||
|   [![icon][github]](https://github.com/masanobuimai) | ||||
|     | ||||
|   Masanobu Imai | ||||
| * [![icon][mail]](mailto:poxvuibr@gmail.com) | ||||
| @@ -57,7 +89,7 @@ Contributors: | ||||
|     | ||||
|   John Lindquist | ||||
| * [![icon][mail]](mailto:iklotzko@ltech.com) | ||||
|   [![icon][github-off]](#) | ||||
|   [![icon][github]](https://github.com/iklotzko) | ||||
|     | ||||
|   Ira Klotzko | ||||
| * [![icon][mail]](mailto:alex@selesse.com) | ||||
| @@ -65,7 +97,7 @@ Contributors: | ||||
|     | ||||
|   Alex Selesse | ||||
| * [![icon][mail]](mailto:dbennett@palantir.com) | ||||
|   [![icon][github-off]](#) | ||||
|   [![icon][github]](https://github.com/dathanb) | ||||
|     | ||||
|   Dathan Bennett | ||||
| * [![icon][mail]](mailto:kphayen@gmail.com) | ||||
| @@ -77,11 +109,11 @@ Contributors: | ||||
|     | ||||
|   Alexey Shmalko | ||||
| * [![icon][mail]](mailto:a.m.brookins@gmail.com) | ||||
|   [![icon][github-off]](#) | ||||
|   [![icon][github]](https://github.com/abrookins) | ||||
|     | ||||
|   Andrew Brookins | ||||
| * [![icon][mail]](mailto:changwang83@gmail.com) | ||||
|   [![icon][github-off]](#) | ||||
|   [![icon][github]](https://github.com/changwang) | ||||
|     | ||||
|   Chang Wang | ||||
| * [![icon][mail]](mailto:josejaime.sanchez@gmail.com) | ||||
| @@ -89,19 +121,19 @@ Contributors: | ||||
|     | ||||
|   Jaime Sanchez | ||||
| * [![icon][mail]](mailto:thomas@homburg.dk) | ||||
|   [![icon][github-off]](#) | ||||
|   [![icon][github]](https://github.com/homburg) | ||||
|     | ||||
|   Thomas B Homburg | ||||
| * [![icon][mail]](mailto:smartbomb@server.fake) | ||||
|   [![icon][github-off]](#) | ||||
|   [![icon][github]](https://github.com/smartbomb) | ||||
|     | ||||
|   smartbomb | ||||
| * [![icon][mail]](mailto:tuomas.tynkkynen@iki.fi) | ||||
|   [![icon][github-off]](#) | ||||
|   [![icon][github]](https://github.com/dezgeg) | ||||
|     | ||||
|   Tuomas Tynkkynen | ||||
| * [![icon][mail]](mailto:jackson@donorschoose.org) | ||||
|   [![icon][github-off]](#) | ||||
|   [![icon][github]](https://github.com/jdpopkin) | ||||
|     | ||||
|   Jackson Popkin | ||||
| * [![icon][mail]](mailto:yuyuyu1999@gmail.com) | ||||
| @@ -109,7 +141,7 @@ Contributors: | ||||
|     | ||||
|   Teruo Kunihiro | ||||
| * [![icon][mail]](mailto:lubashka.994@mail.ru) | ||||
|   [![icon][github-off]](#) | ||||
|   [![icon][github]](https://github.com/lubba) | ||||
|     | ||||
|   Liubov Paina | ||||
| * [![icon][mail]](mailto:me@dhleong.net) | ||||
| @@ -137,7 +169,7 @@ Contributors: | ||||
|     | ||||
|   tieTYT | ||||
| * [![icon][mail]](mailto:nickgieschen@gmail.com) | ||||
|   [![icon][github-off]](#) | ||||
|   [![icon][github]](https://github.com/nickgieschen) | ||||
|     | ||||
|   Nick Gieschen | ||||
| * [![icon][mail]](mailto:ikenox@gmail.com) | ||||
| @@ -149,7 +181,7 @@ Contributors: | ||||
|     | ||||
|   Maximilian Luz | ||||
| * [![icon][mail]](mailto:vparfinenko@excelsior-usa.com) | ||||
|   [![icon][github-off]](#) | ||||
|   [![icon][github]](https://github.com/cypok) | ||||
|     | ||||
|   Vladimir Parfinenko | ||||
| * [![icon][mail]](mailto:hassmann@hwdev.de) | ||||
| @@ -193,7 +225,7 @@ Contributors: | ||||
|     | ||||
|   Marcel Hild | ||||
| * [![icon][mail]](mailto:vedranb@gmail.com) | ||||
|   [![icon][github-off]](#) | ||||
|   [![icon][github]](https://github.com/vedran) | ||||
|     | ||||
|   Vedran Budimcic | ||||
| * [![icon][mail]](mailto:andreigasparovici1@gmail.com) | ||||
| @@ -208,10 +240,13 @@ Contributors: | ||||
|   [![icon][github]](https://github.com/TonyArra) | ||||
|     | ||||
|   Tony Arra | ||||
| * [![icon][mail]](mailto:bradziolko@gmail.com) | ||||
|   [![icon][github]](https://github.com/bradziolko) | ||||
| * [![icon][mail]](mailto:mj@ziolko.dev) | ||||
|   [![icon][github]](https://github.com/mjziolko) | ||||
|     | ||||
|   Brad Ziolko | ||||
|   Madeline Ziolko | ||||
|   [Original contribution from: | ||||
|   [![icon][mail]](mailto:bradziolko@gmail.com) | ||||
|   [![icon][github]](https://github.com/bradziolko)] | ||||
| * [![icon][mail]](mailto:sumoooru2@gmail.com) | ||||
|   [![icon][github]](https://github.com/sumoooru2) | ||||
|     | ||||
| @@ -416,6 +451,10 @@ Contributors: | ||||
|   [![icon][github]](https://github.com/Vvalter) | ||||
|     | ||||
|   Simon Rainer | ||||
| * [![icon][mail]](mailto:filipp.vakhitov@jetbrains.com) | ||||
|   [![icon][github]](https://github.com/lippfi) | ||||
|     | ||||
|   lippfi | ||||
|                          | ||||
| If you are a contributor and your name is not listed here, feel free to | ||||
| contact the maintainers. | ||||
|   | ||||
							
								
								
									
										24
									
								
								CHANGES.md
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								CHANGES.md
									
									
									
									
									
								
							| @@ -25,6 +25,17 @@ usual beta standards. | ||||
|  | ||||
| ## To Be Released | ||||
|  | ||||
| ### Fixes: | ||||
| * [VIM-1758](https://youtrack.jetbrains.com/issue/VIM-1758) Commentary plugin in rider | ||||
| * [VIM-1903](https://youtrack.jetbrains.com/issue/VIM-1903) Autoindent now works in rider | ||||
| * [VIM-2744](https://youtrack.jetbrains.com/issue/VIM-2744) Fix undo from ex line | ||||
| * [VIM-2749](https://youtrack.jetbrains.com/issue/VIM-2749) Fix :tabn and :tabN commands | ||||
| * [VIM-2718](https://youtrack.jetbrains.com/issue/VIM-2718) Fixed case where the primary caret was changed | ||||
| * [VIM-2766](https://youtrack.jetbrains.com/issue/VIM-2766) Move NERDTree update to background thread | ||||
| * [VIM-2768](https://youtrack.jetbrains.com/issue/VIM-2768) Refactor listeners | ||||
|  | ||||
| ## 1.11.0, 2022-08-09 | ||||
|  | ||||
| ### Features: | ||||
| * Add `gcu` command for Commentary plugin | ||||
| * Add `:Commentary` command, which works great for commands such as `:%g/fun/Commentary` | ||||
| @@ -36,6 +47,7 @@ usual beta standards. | ||||
|   E.g. `<Plug>Commentary` instead of `<Plug>(CommentMotion)`. Old mappings are maintained for compatibility. | ||||
| * If you open `~/.ideavimrc` in IDE, remove a mapping, and reload the config using the reload button, | ||||
|   the mapping will actually be unmapped. | ||||
| * New vim (and IdeaVim) behaviour: `ci(`& friends searches for the brackets in the line. | ||||
|  | ||||
| ### Fixes: | ||||
| * [VIM-2587](https://youtrack.jetbrains.com/issue/VIM-2587) Use ctrl-6 as ctrl-^ | ||||
| @@ -52,9 +64,14 @@ usual beta standards. | ||||
| * [VIM-2595](https://youtrack.jetbrains.com/issue/VIM-2595) Support plugins in macro execution | ||||
| * [VIM-2671](https://youtrack.jetbrains.com/issue/VIM-2671) Fix using plugins from mappings | ||||
| * [VIM-2675](https://youtrack.jetbrains.com/issue/VIM-2675) Fix numbering register in visual mode | ||||
| * [VIM-696](https://youtrack.jetbrains.com/issue/VIM-696/vim-selection-issue-after-undo) Fix selection after undo | ||||
| * [VIM-744](https://youtrack.jetbrains.com/issue/VIM-744/Use-undoredo-with-count-modifier) Add count to undo/redo | ||||
| * [VIM-1862](https://youtrack.jetbrains.com/issue/VIM-1862/Ex-commands-executed-in-keymaps-and-macros-are-added-to-the-command-history) Fix command history | ||||
| * [VIM-2227](https://youtrack.jetbrains.com/issue/VIM-2227) Wrong behavior when deleting / changing surround with invalid character | ||||
| * [VIM-2691](https://youtrack.jetbrains.com/issue/VIM-2691) Save file on :w | ||||
| * [VIM-2710](https://youtrack.jetbrains.com/issue/VIM-2710) Show options value on `set opt` | ||||
| * [VIM-913](https://youtrack.jetbrains.com/issue/VIM-913) Partially fix the issue with macros and autocompletion | ||||
| * [VIM-2723](https://youtrack.jetbrains.com/issue/VIM-2723) Move focus to editor after :q | ||||
| * [VIM-2728](https://youtrack.jetbrains.com/issue/VIM-2728) Give access to global variables | ||||
|  | ||||
| ### Merged PRs: | ||||
| * [468](https://github.com/JetBrains/ideavim/pull/468) by [Thomas Schouten](https://github.com/PHPirates): Implement UserDataHolder for EditorDataContext | ||||
| @@ -63,6 +80,11 @@ usual beta standards. | ||||
| * [493](https://github.com/JetBrains/ideavim/pull/493) by [Matt Ellis](https://github.com/citizenmatt): Improvements to Commentary extension | ||||
| * [494](https://github.com/JetBrains/ideavim/pull/494) by [Matt Ellis](https://github.com/citizenmatt): Cleanup pre-212 CaretVisualAttributes compatibility code | ||||
| * [504](https://github.com/JetBrains/ideavim/pull/504) by [Matt Ellis](https://github.com/citizenmatt): Minor bug fixes | ||||
| * [519](https://github.com/JetBrains/ideavim/pull/519) by [chylex](https://github.com/chylex): Fix(VIM-2227): Wrong behavior when deleting / changing surround with invalid character | ||||
| * [525](https://github.com/JetBrains/ideavim/pull/525) by [Matt Ellis](https://github.com/citizenmatt): Improve handling of fractional width fonts | ||||
| * [526](https://github.com/JetBrains/ideavim/pull/526) by [Alex Pláte](https://github.com/AlexPl292): Create gradle.properties | ||||
| * [528](https://github.com/JetBrains/ideavim/pull/528) by [chylex](https://github.com/chylex): Implement partial code completion support in macros | ||||
| * [531](https://github.com/JetBrains/ideavim/pull/531) by [Matt Ellis](https://github.com/citizenmatt): Consolidate doTest methods | ||||
|  | ||||
| ## 1.10.0, 2022-02-17 | ||||
|  | ||||
|   | ||||
| @@ -5,7 +5,7 @@ IdeaVim is an open source project created by 80+ contributors. Would you like to | ||||
| This page is created to help you start contributing. And who knows, maybe in a few days this project will be brighter than ever! | ||||
|  | ||||
| :warning: The plugin is currently under a huge refactoring aiming to split into vim-engine and IdeaVim in order to | ||||
| support the new [Fleet IDE](https://www.jetbrains.com/fleet/). | ||||
| support the new [Fleet IDE](https://www.jetbrains.com/fleet/). Please see [Fleet refactoring](#Fleet-refactoring). | ||||
|  | ||||
| ## Before you begin | ||||
|  | ||||
| @@ -32,7 +32,7 @@ OK, ready to do some coding? | ||||
| Yoo hoo! You’re all set to begin contributing. | ||||
| We've prepared some useful configurations for you: | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| And here are useful gradle commands: | ||||
|  | ||||
| @@ -120,6 +120,17 @@ so you can reuse your `.vimrc` settings. | ||||
| We also support proper command mappings (functions are mapped to `<Plug>...`), the operator function (`OperatorFunction`), and so on. | ||||
| - Magic is supported as well. See `Magic`. | ||||
|  | ||||
|  | ||||
| ## Fleet refactoring | ||||
| At the moment, IdeaVim is under an active refactoring aiming to split IdeaVim into two modules: vim-engine and IdeaVim. | ||||
|  | ||||
| If you develop a plugin that depends on IdeaVim: We have an instrument to check that our changes don't affect | ||||
| the plugins in the marketplace. Also, we commit to support currently used API at least till the end of 2022. | ||||
| If you still encounter any issues with the newer versions of IdeaVim, please [contact maintainers](https://github.com/JetBrains/ideavim#contact-maintainers). | ||||
| We kindly ask you not to use anything from the new API (like `VimEditor`, `injector`) because at the moment we don't | ||||
| guarantee the compatibility of this API in the future versions. | ||||
|  | ||||
|  | ||||
| ----- | ||||
|  | ||||
| ### I read the whole page but something is still unclear. | ||||
|   | ||||
							
								
								
									
										13
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								README.md
									
									
									
									
									
								
							| @@ -11,7 +11,7 @@ IdeaVim | ||||
| [![Gitter][gitter-svg]][gitter] | ||||
| [![Twitter][twitter-svg]][twitter] | ||||
|  | ||||
| IdeaVim is a Vim emulation plugin for IntelliJ Platform-based IDEs. | ||||
| IdeaVim is a Vim engine for JetBrains IDEs. | ||||
|  | ||||
| ##### Contact maintainers: | ||||
| * [Bug tracker](https://youtrack.jetbrains.com/issues/VIM) | ||||
| @@ -36,7 +36,7 @@ Setup | ||||
| - IdeaVim can be installed via `Settings | Plugins`. | ||||
| See the [detailed instructions](https://www.jetbrains.com/help/idea/managing-plugins.html#). | ||||
|  | ||||
| - Use `Tools | Vim Emulator` in the menu to enable or disable emulation. | ||||
| - Use `Tools | Vim` in the menu to enable or disable vim. | ||||
|  | ||||
| - Use the `~/.ideavimrc` file as an analog of `~/.vimrc` ([learn more](#Files)). The XDG standard is supported, as well. | ||||
|  | ||||
| @@ -88,7 +88,7 @@ Here are some examples of supported vim features and commands: | ||||
| * Vim web help | ||||
| * `~/.ideavimrc` configuration file | ||||
|  | ||||
| [Emulated Vim plugins](https://github.com/JetBrains/ideavim/wiki/Emulated-plugins): | ||||
| [IdeaVim plugins](https://github.com/JetBrains/ideavim/wiki/Emulated-plugins): | ||||
|  | ||||
| * vim-easymotion | ||||
| * NERDTree | ||||
| @@ -198,7 +198,7 @@ Alternatively, you can set up initialization commands using [XDG](https://specif | ||||
| Put your settings to `$XDG_CONFIG_HOME/ideavim/ideavimrc` file. | ||||
|  | ||||
|  | ||||
| Emulated Vim Plugins | ||||
| IdeaVim Plugins | ||||
| -------------------- | ||||
|  | ||||
| See [doc/emulated-plugins.md](https://github.com/JetBrains/ideavim/wiki/Emulated-plugins) | ||||
| @@ -226,7 +226,10 @@ Ex commands or via `:map` command mappings: | ||||
|  | ||||
|     <details> | ||||
|         <summary><strong>"Track action Ids" Details</strong> (click to see)</summary> | ||||
|         <img src="assets/readme/track_action_id.gif" alt="track action ids"/> | ||||
|         <picture> | ||||
|             <source media="(prefers-color-scheme: dark)" srcset="assets/readme/track_action_dark.gif"> | ||||
|             <img src="assets/readme/track_action_light.gif" alt="track action ids"/> | ||||
|         </picture> | ||||
|     </details> | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								assets/contributing/configs-dark.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/contributing/configs-dark.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 27 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/contributing/configs-light.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/contributing/configs-light.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 31 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 22 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/readme/track_action_dark.gif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/readme/track_action_dark.gif
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.0 MiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 2.1 MiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/readme/track_action_light.gif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/readme/track_action_light.gif
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 981 KiB | 
| @@ -15,7 +15,9 @@ buildscript { | ||||
|         classpath("com.github.AlexPl292:mark-down-to-slack:1.1.2") | ||||
|         classpath("org.eclipse.jgit:org.eclipse.jgit:6.1.0.202203080745-r") | ||||
|         classpath("org.kohsuke:github-api:1.305") | ||||
|         classpath("org.jetbrains:markdown:0.3.1") | ||||
|  | ||||
|         // This comes from the changelog plugin | ||||
| //        classpath("org.jetbrains:markdown:0.3.1") | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -24,11 +26,11 @@ plugins { | ||||
|     java | ||||
|     kotlin("jvm") version "1.6.21" | ||||
|  | ||||
|     id("org.jetbrains.intellij") version "1.6.0" | ||||
|     id("org.jetbrains.intellij") version "1.10.0-SNAPSHOT" | ||||
|     id("org.jetbrains.changelog") version "1.3.1" | ||||
|  | ||||
|     // ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle | ||||
|     id("org.jlleitschuh.gradle.ktlint") version "10.2.1" | ||||
|     id("org.jlleitschuh.gradle.ktlint") version "10.3.0" | ||||
| } | ||||
|  | ||||
| // Import variables from gradle.properties file | ||||
| @@ -70,7 +72,9 @@ dependencies { | ||||
|     runtimeOnly("org.antlr:antlr4-runtime:$antlrVersion") | ||||
|     antlr("org.antlr:antlr4:$antlrVersion") | ||||
|  | ||||
|     implementation(project(":vim-engine")) | ||||
|     api(project(":vim-engine")) | ||||
|  | ||||
|     testApi("com.squareup.okhttp3:okhttp:4.10.0") | ||||
| } | ||||
|  | ||||
| configurations { | ||||
| @@ -85,6 +89,8 @@ tasks.register<Test>("testWithNeovim") { | ||||
|     group = "verification" | ||||
|     systemProperty("ideavim.nvim.test", "true") | ||||
|     exclude("/ui/**") | ||||
|     exclude("**/longrunning/**") | ||||
|     exclude("**/propertybased/**") | ||||
| } | ||||
|  | ||||
| tasks.register<Test>("testPropertyBased") { | ||||
| @@ -115,6 +121,8 @@ tasks { | ||||
|         include("**/*test.class") | ||||
|         include("**/*Tests.class") | ||||
|         exclude("**/ParserTest.class") | ||||
|         exclude("**/longrunning/**") | ||||
|         exclude("**/propertybased/**") | ||||
|     } | ||||
|  | ||||
|     val testPropertyBased by getting(Test::class) { | ||||
| @@ -144,19 +152,32 @@ tasks { | ||||
|     compileKotlin { | ||||
|         kotlinOptions { | ||||
|             jvmTarget = javaVersion | ||||
|             apiVersion = "1.5" | ||||
|             apiVersion = "1.6" | ||||
|             freeCompilerArgs = listOf("-Xjvm-default=all-compatibility") | ||||
| //            allWarningsAsErrors = true | ||||
|         } | ||||
|     } | ||||
|     compileTestKotlin { | ||||
|         kotlinOptions { | ||||
|             jvmTarget = javaVersion | ||||
|             apiVersion = "1.5" | ||||
|             apiVersion = "1.6" | ||||
| //            allWarningsAsErrors = true | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| java { | ||||
|     toolchain { | ||||
|         languageVersion.set(JavaLanguageVersion.of(javaVersion)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| kotlin { | ||||
|     jvmToolchain { | ||||
|         (this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(javaVersion)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| gradle.projectsEvaluated { | ||||
|     tasks.compileJava { | ||||
| //        options.compilerArgs.add("-Werror") | ||||
| @@ -199,7 +220,7 @@ tasks { | ||||
|     runPluginVerifier { | ||||
|         downloadDir.set("${project.buildDir}/pluginVerifier/ides") | ||||
|         teamCityOutputFormat.set(true) | ||||
|         ideVersions.set(listOf("IC-2021.3.4")) | ||||
| //        ideVersions.set(listOf("IC-2021.3.4")) | ||||
|     } | ||||
|  | ||||
|     generateGrammarSource { | ||||
| @@ -211,12 +232,37 @@ tasks { | ||||
|     named("compileKotlin") { | ||||
|         dependsOn("generateGrammarSource") | ||||
|     } | ||||
|  | ||||
|     // Add plugin open API sources to the plugin ZIP | ||||
|     val createOpenApiSourceJar by registering(Jar::class) { | ||||
|         // Java sources | ||||
|         from(sourceSets.main.get().java) { | ||||
|             include("**/com/maddyhome/idea/vim/**/*.java") | ||||
|         } | ||||
|         // Kotlin sources | ||||
|         from(kotlin.sourceSets.main.get().kotlin) { | ||||
|             include("**/com/maddyhome/idea/vim/**/*.kt") | ||||
|         } | ||||
|         destinationDirectory.set(layout.buildDirectory.dir("libs")) | ||||
|         archiveClassifier.set("src") | ||||
|     } | ||||
|  | ||||
|     buildPlugin { | ||||
|         dependsOn(createOpenApiSourceJar) | ||||
|         from(createOpenApiSourceJar) { into("lib/src") } | ||||
|     } | ||||
|  | ||||
|     // Don't forget to update plugin.xml | ||||
|     patchPluginXml { | ||||
|         sinceBuild.set("222") | ||||
|     } | ||||
| } | ||||
|  | ||||
| // --- Linting | ||||
|  | ||||
| ktlint { | ||||
|     disabledRules.add("no-wildcard-imports") | ||||
|     version.set("0.43.0") | ||||
| } | ||||
|  | ||||
| // --- Tests | ||||
| @@ -241,7 +287,7 @@ changelog { | ||||
|     itemPrefix.set("*") | ||||
|     path.set("${project.projectDir}/CHANGES.md") | ||||
|     unreleasedTerm.set("To Be Released") | ||||
|     headerParserRegex.set("\\d\\.\\d+(.\\d+)?".toRegex()) | ||||
|     headerParserRegex.set("(\\d\\.\\d+(.\\d+)?)".toRegex()) | ||||
| //    header = { "${project.version}" } | ||||
| //    version = "0.60" | ||||
| } | ||||
| @@ -294,20 +340,30 @@ tasks.register("slackNotification") { | ||||
|             println("Response code: $postRc") | ||||
|             if (postRc == 200) { | ||||
|                 println(inputStream.bufferedReader().use { it.readText() }) | ||||
|             } else { | ||||
|                 println(errorStream.bufferedReader().use { it.readText() }) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| // --- Update authors | ||||
| // Uncomment to enable FUS testing mode | ||||
| // tasks { | ||||
| //    withType<org.jetbrains.intellij.tasks.RunIdeTask> { | ||||
| //        jvmArgs("-Didea.is.internal=true") | ||||
| //        jvmArgs("-Dfus.internal.test.mode=true") | ||||
| //    } | ||||
| // } | ||||
|  | ||||
| // --- Update authors | ||||
| tasks.register("updateAuthors") { | ||||
|     doLast { | ||||
|         val uncheckedEmails = setOf( | ||||
|             "aleksei.plate@jetbrains.com", | ||||
|             "aleksei.plate@teamcity", | ||||
|             "aleksei.plate@TeamCity", | ||||
|             "alex.plate@192.168.0.109" | ||||
|             "alex.plate@192.168.0.109", | ||||
|             "nikita.koshcheev@TeamCity", | ||||
|         ) | ||||
|         updateAuthors(uncheckedEmails) | ||||
|     } | ||||
| @@ -318,6 +374,7 @@ val prId: String by project | ||||
| tasks.register("updateMergedPr") { | ||||
|     doLast { | ||||
|         if (project.hasProperty("prId")) { | ||||
|             println("Got pr id: $prId") | ||||
|             updateMergedPr(prId.toInt()) | ||||
|         } else { | ||||
|             error("Cannot get prId") | ||||
| @@ -481,7 +538,9 @@ data class Change(val id: String, val text: String) | ||||
|  | ||||
| fun updateMergedPr(number: Int) { | ||||
|     val gitHub = org.kohsuke.github.GitHub.connect() | ||||
|     println("Connecting to the repo...") | ||||
|     val repository = gitHub.getRepository("JetBrains/ideavim") | ||||
|     println("Getting pull requests...") | ||||
|     val pullRequest = repository.getPullRequest(number) | ||||
|     if (pullRequest.user.login == "dependabot[bot]") return | ||||
|  | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| # suppress inspection "UnusedProperty" for whole file | ||||
|  | ||||
| ideaVersion=LATEST-EAP-SNAPSHOT | ||||
| ideaVersion=2022.2.2 | ||||
| downloadIdeaSources=true | ||||
| instrumentPluginCode=true | ||||
| version=SNAPSHOT | ||||
| javaVersion=11 | ||||
| remoteRobotVersion=0.11.10 | ||||
| version=chylex-13 | ||||
| javaVersion=17 | ||||
| remoteRobotVersion=0.11.15 | ||||
| antlrVersion=4.10.1 | ||||
|  | ||||
| # Please don't forget to update kotlin version in buildscript section | ||||
|   | ||||
							
								
								
									
										79982
									
								
								qodana.sarif.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79982
									
								
								qodana.sarif.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										21
									
								
								qodana.yaml
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								qodana.yaml
									
									
									
									
									
								
							| @@ -1,6 +1,8 @@ | ||||
| version: 1.0 | ||||
| profile: | ||||
|   name: Qodana | ||||
| include: | ||||
|   - name: CheckDependencyLicenses | ||||
| exclude: | ||||
|   - name: MoveVariableDeclarationIntoWhen | ||||
|   - name: PluginXmlValidity | ||||
| @@ -9,12 +11,15 @@ exclude: | ||||
|   - name: UnusedReturnValue | ||||
|   - name: All | ||||
|     paths: | ||||
|       - build.gradle | ||||
|       - build.gradle.kts | ||||
|       - gradle/wrapper/gradle-wrapper.properties | ||||
|       - resources/icons/youtrack.svg | ||||
|       - src/com/maddyhome/idea/vim/ex/vimscript/VimScriptCommandHandler.java | ||||
|       - src/com/maddyhome/idea/vim/helper/SearchHelper.java | ||||
|       - src/com/maddyhome/idea/vim/regexp/RegExp.java | ||||
|       - test/org/jetbrains/plugins/ideavim/propertybased/samples/JavaText.kt | ||||
|       - test/org/jetbrains/plugins/ideavim/propertybased/samples/LoremText.kt | ||||
|       - test/org/jetbrains/plugins/ideavim/propertybased/samples/SimpleText.kt | ||||
|       - src/main/resources/icons/youtrack.svg | ||||
|       - src/main/java/com/maddyhome/idea/vim/helper/SearchHelper.java | ||||
|       - src/main/java/com/maddyhome/idea/vim/regexp/RegExp.kt | ||||
|       - src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/JavaText.kt | ||||
|       - src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/LoremText.kt | ||||
|       - src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/SimpleText.kt | ||||
|       - src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated/VimscriptListener.java | ||||
|       - src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated/VimscriptLexer.java | ||||
|       - src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated/VimscriptParser.java | ||||
|       - src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated/VimscriptVisitor.java | ||||
| @@ -1,3 +1,13 @@ | ||||
| // Set repository for snapshot versions of gradle plugin | ||||
| pluginManagement { | ||||
|   repositories { | ||||
|     maven { | ||||
|       url 'https://oss.sonatype.org/content/repositories/snapshots/' | ||||
|     } | ||||
|     gradlePluginPortal() | ||||
|   } | ||||
| } | ||||
|  | ||||
| rootProject.name = 'IdeaVIM' | ||||
| include 'vim-engine' | ||||
|  | ||||
|   | ||||
| @@ -146,7 +146,7 @@ rShift: GREATER+; | ||||
|  | ||||
| letCommands: | ||||
|     (WS | COLON)* range? (WS | COLON)* LET WS+ expr WS* | ||||
|         assignmentOperator =  (ASSIGN | PLUS_ASSIGN | MINUS_ASSIGN | STAR_ASSIGN | DIV_ASSIGN | MOD_ASSIGN | DOT_ASSIGN) | ||||
|         assignmentOperator | ||||
|         WS* expr WS* ((inline_comment NEW_LINE) | (NEW_LINE | BAR)+) | ||||
|     #Let1Command| | ||||
|  | ||||
| @@ -154,6 +154,21 @@ letCommands: | ||||
|     #Let2Command | ||||
| ; | ||||
|  | ||||
| assignmentOperator: | ||||
|     ASSIGN | plusAssign | minusAssign | startAssign | divAssign | modAssign | dotAssign; | ||||
| plusAssign: | ||||
|     PLUS ASSIGN; | ||||
| minusAssign: | ||||
|     MINUS ASSIGN; | ||||
| startAssign: | ||||
|     STAR ASSIGN; | ||||
| divAssign: | ||||
|     DIV ASSIGN; | ||||
| modAssign: | ||||
|     MOD ASSIGN; | ||||
| dotAssign: | ||||
|     DOT ASSIGN; | ||||
|  | ||||
| shortRange: | ||||
|     ((QUESTION (~QUESTION)* QUESTION?) | (DIV (~DIV)* DIV?)); | ||||
| range: | ||||
| @@ -778,12 +793,12 @@ IS_NOT_CS:              'isnot#'; | ||||
|  | ||||
| // Assignment operators | ||||
| ASSIGN:                 '='; | ||||
| PLUS_ASSIGN:            '+='; | ||||
| MINUS_ASSIGN:           '-='; | ||||
| STAR_ASSIGN:            '*='; | ||||
| DIV_ASSIGN:             '/='; | ||||
| MOD_ASSIGN:             '%='; | ||||
| DOT_ASSIGN:             '.='; | ||||
| //PLUS_ASSIGN:            '+='; | ||||
| //MINUS_ASSIGN:           '-='; | ||||
| //STAR_ASSIGN:            '*='; | ||||
| //DIV_ASSIGN:             '/='; | ||||
| //MOD_ASSIGN:             '%='; | ||||
| //DOT_ASSIGN:             '.='; | ||||
|  | ||||
| // Escaped chars | ||||
| ESCAPED_QUESTION:       '\\?'; | ||||
|   | ||||
| @@ -27,6 +27,7 @@ import com.intellij.openapi.editor.EditorFactory; | ||||
| import com.intellij.openapi.editor.actionSystem.TypedAction; | ||||
| import com.intellij.openapi.editor.actionSystem.TypedActionHandler; | ||||
| import com.intellij.openapi.editor.event.*; | ||||
| import com.intellij.openapi.util.Disposer; | ||||
| import com.maddyhome.idea.vim.helper.HandlerInjector; | ||||
| import org.jetbrains.annotations.NotNull; | ||||
| import org.jetbrains.annotations.Nullable; | ||||
| @@ -101,38 +102,41 @@ public class EventFacade { | ||||
|     EditorFactory.getInstance().addEditorFactoryListener(listener, parentDisposable); | ||||
|   } | ||||
|  | ||||
|   @SuppressWarnings("deprecation") | ||||
|   public void removeEditorFactoryListener(@NotNull EditorFactoryListener listener) { | ||||
|     // Listener is removed not only if application is disposed | ||||
|     EditorFactory.getInstance().removeEditorFactoryListener(listener); | ||||
|   } | ||||
|  | ||||
|   public void addEditorMouseListener(@NotNull Editor editor, @NotNull EditorMouseListener listener) { | ||||
|     editor.addEditorMouseListener(listener); | ||||
|   public void addEditorMouseListener(@NotNull Editor editor, | ||||
|                                      @NotNull EditorMouseListener listener, | ||||
|                                      @NotNull Disposable disposable) { | ||||
|     editor.addEditorMouseListener(listener, disposable); | ||||
|   } | ||||
|  | ||||
|   public void removeEditorMouseListener(@NotNull Editor editor, @NotNull EditorMouseListener listener) { | ||||
|     editor.removeEditorMouseListener(listener); | ||||
|   } | ||||
|  | ||||
|   public void addComponentMouseListener(@NotNull Component component, @NotNull MouseListener mouseListener) { | ||||
|   public void addComponentMouseListener(@NotNull Component component, | ||||
|                                         @NotNull MouseListener mouseListener, | ||||
|                                         @NotNull Disposable disposable) { | ||||
|     component.addMouseListener(mouseListener); | ||||
|     Disposer.register(disposable, () -> component.removeMouseListener(mouseListener)); | ||||
|   } | ||||
|  | ||||
|   public void removeComponentMouseListener(@NotNull Component component, @NotNull MouseListener mouseListener) { | ||||
|     component.removeMouseListener(mouseListener); | ||||
|   } | ||||
|  | ||||
|   public void addEditorMouseMotionListener(@NotNull Editor editor, @NotNull EditorMouseMotionListener listener) { | ||||
|     editor.addEditorMouseMotionListener(listener); | ||||
|   public void addEditorMouseMotionListener(@NotNull Editor editor, | ||||
|                                            @NotNull EditorMouseMotionListener listener, | ||||
|                                            @NotNull Disposable disposable) { | ||||
|     editor.addEditorMouseMotionListener(listener, disposable); | ||||
|   } | ||||
|  | ||||
|   public void removeEditorMouseMotionListener(@NotNull Editor editor, @NotNull EditorMouseMotionListener listener) { | ||||
|     editor.removeEditorMouseMotionListener(listener); | ||||
|   } | ||||
|  | ||||
|   public void addEditorSelectionListener(@NotNull Editor editor, @NotNull SelectionListener listener) { | ||||
|     editor.getSelectionModel().addSelectionListener(listener); | ||||
|   public void addEditorSelectionListener(@NotNull Editor editor, | ||||
|                                          @NotNull SelectionListener listener, | ||||
|                                          @NotNull Disposable disposable) { | ||||
|     editor.getSelectionModel().addSelectionListener(listener, disposable); | ||||
|   } | ||||
|  | ||||
|   public void removeEditorSelectionListener(@NotNull Editor editor, @NotNull SelectionListener listener) { | ||||
|   | ||||
| @@ -18,8 +18,14 @@ | ||||
|  | ||||
| package com.maddyhome.idea.vim | ||||
|  | ||||
| import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx | ||||
| import com.intellij.openapi.project.Project | ||||
| import com.intellij.openapi.project.ProjectManagerListener | ||||
| import com.intellij.openapi.startup.StartupActivity | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper | ||||
| import com.maddyhome.idea.vim.helper.localEditors | ||||
| import com.maddyhome.idea.vim.options.OptionScope | ||||
|  | ||||
| /** | ||||
|  * @author Alex Plate | ||||
| @@ -36,3 +42,19 @@ class PluginStartup : StartupActivity.DumbAware/*, LightEditCompatible*/ { | ||||
|     VimPlugin.getInstance().initialize() | ||||
|   } | ||||
| } | ||||
|  | ||||
| // This is a temporal workaround for VIM-2487 | ||||
| class PyNotebooksCloseWorkaround : ProjectManagerListener { | ||||
|   override fun projectClosingBeforeSave(project: Project) { | ||||
|     val close = injector.optionService.getOptionValue(OptionScope.GLOBAL, "closenotebooks").asBoolean() | ||||
|     if (close) { | ||||
|       localEditors().forEach { editor -> | ||||
|         val virtualFile = EditorHelper.getVirtualFile(editor) | ||||
|         if (virtualFile?.extension == "ipynb") { | ||||
|           val fileEditorManager = FileEditorManagerEx.getInstanceEx(project) | ||||
|           fileEditorManager.closeFile(virtualFile) | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -19,8 +19,6 @@ package com.maddyhome.idea.vim; | ||||
|  | ||||
| import com.intellij.ide.plugins.IdeaPluginDescriptor; | ||||
| import com.intellij.ide.plugins.PluginManagerCore; | ||||
| import com.intellij.notification.Notification; | ||||
| import com.intellij.notification.NotificationListener; | ||||
| import com.intellij.openapi.Disposable; | ||||
| import com.intellij.openapi.application.Application; | ||||
| import com.intellij.openapi.application.ApplicationManager; | ||||
| @@ -32,9 +30,9 @@ import com.intellij.openapi.extensions.PluginId; | ||||
| import com.intellij.openapi.keymap.Keymap; | ||||
| import com.intellij.openapi.keymap.ex.KeymapManagerEx; | ||||
| import com.intellij.openapi.keymap.impl.DefaultKeymap; | ||||
| import com.intellij.openapi.options.ShowSettingsUtil; | ||||
| import com.intellij.openapi.project.Project; | ||||
| import com.intellij.openapi.ui.Messages; | ||||
| import com.intellij.openapi.util.Disposer; | ||||
| import com.intellij.openapi.util.SystemInfo; | ||||
| import com.maddyhome.idea.vim.api.VimInjectorKt; | ||||
| import com.maddyhome.idea.vim.api.VimKeyGroup; | ||||
| @@ -48,20 +46,17 @@ import com.maddyhome.idea.vim.group.visual.VisualMotionGroup; | ||||
| import com.maddyhome.idea.vim.helper.MacKeyRepeat; | ||||
| import com.maddyhome.idea.vim.listener.VimListenerManager; | ||||
| import com.maddyhome.idea.vim.newapi.IjVimInjector; | ||||
| import com.maddyhome.idea.vim.options.OptionService; | ||||
| import com.maddyhome.idea.vim.ui.StatusBarIconFactory; | ||||
| import com.maddyhome.idea.vim.ui.VimEmulationConfigurable; | ||||
| import com.maddyhome.idea.vim.ui.ex.ExEntryPanel; | ||||
| import com.maddyhome.idea.vim.vimscript.services.FunctionStorage; | ||||
| import com.maddyhome.idea.vim.vimscript.services.IjVimOptionService; | ||||
| import com.maddyhome.idea.vim.vimscript.services.VimVariableService; | ||||
| import com.maddyhome.idea.vim.vimscript.services.OptionService; | ||||
| import com.maddyhome.idea.vim.vimscript.services.VariableService; | ||||
| import org.jdom.Element; | ||||
| import org.jetbrains.annotations.Nls; | ||||
| import org.jetbrains.annotations.NotNull; | ||||
| import org.jetbrains.annotations.Nullable; | ||||
|  | ||||
| import javax.swing.event.HyperlinkEvent; | ||||
|  | ||||
| import static com.maddyhome.idea.vim.group.EditorGroup.EDITOR_STORE_ELEMENT; | ||||
| import static com.maddyhome.idea.vim.group.KeyGroup.SHORTCUT_CONFLICTS_ELEMENT; | ||||
| import static com.maddyhome.idea.vim.vimscript.services.VimRcService.executeIdeaVimRc; | ||||
| @@ -94,6 +89,8 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable | ||||
|  | ||||
|   private final @NotNull VimState state = new VimState(); | ||||
|  | ||||
|   public Disposable onOffDisposable; | ||||
|  | ||||
|   VimPlugin() { | ||||
|     ApplicationConfigurationMigrator.getInstance().migrate(); | ||||
|   } | ||||
| @@ -117,7 +114,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable | ||||
|   @Override | ||||
|   public void dispose() { | ||||
|     LOG.debug("disposeComponent"); | ||||
|     turnOffPlugin(); | ||||
|     turnOffPlugin(false); | ||||
|     LOG.debug("done"); | ||||
|   } | ||||
|  | ||||
| @@ -226,8 +223,8 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable | ||||
|     return (PutGroup)VimInjectorKt.getInjector().getPut(); | ||||
|   } | ||||
|  | ||||
|   public static @NotNull VimVariableService getVariableService() { | ||||
|     return ApplicationManager.getApplication().getService(VimVariableService.class); | ||||
|   public static @NotNull VariableService getVariableService() { | ||||
|     return ApplicationManager.getApplication().getService(VariableService.class); | ||||
|   } | ||||
|  | ||||
|   public static @NotNull OptionService getOptionService() { | ||||
| @@ -275,7 +272,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable | ||||
|     if (isEnabled() == enabled) return; | ||||
|  | ||||
|     if (!enabled) { | ||||
|       getInstance().turnOffPlugin(); | ||||
|       getInstance().turnOffPlugin(true); | ||||
|     } | ||||
|  | ||||
|     getInstance().enabled = enabled; | ||||
| @@ -341,6 +338,8 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable | ||||
|    *      execution, what theoretically may cause bugs (e.g. VIM-2540) | ||||
|    */ | ||||
|   private void turnOnPlugin() { | ||||
|     onOffDisposable = Disposer.newDisposable(this, "IdeaVimOnOffDisposer"); | ||||
|  | ||||
|     // 1) Update state | ||||
|     ApplicationManager.getApplication().invokeLater(this::updateState); | ||||
|  | ||||
| @@ -366,16 +365,20 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable | ||||
|     VimListenerManager.INSTANCE.turnOn(); | ||||
|   } | ||||
|  | ||||
|   private void turnOffPlugin() { | ||||
|   private void turnOffPlugin(boolean unsubscribe) { | ||||
|     SearchGroup searchGroup = getSearchIfCreated(); | ||||
|     if (searchGroup != null) { | ||||
|       searchGroup.turnOff(); | ||||
|     } | ||||
|     if (unsubscribe) { | ||||
|       VimListenerManager.INSTANCE.turnOff(); | ||||
|     } | ||||
|     ExEntryPanel.fullReset(); | ||||
|  | ||||
|     // Unregister vim actions in command mode | ||||
|     RegisterActions.unregisterActions(); | ||||
|  | ||||
|     Disposer.dispose(onOffDisposable); | ||||
|   } | ||||
|  | ||||
|   private boolean stateUpdated = false; | ||||
| @@ -389,7 +392,9 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable | ||||
|         final Boolean enabled = keyRepeat.isEnabled(); | ||||
|         final Boolean isKeyRepeat = getEditor().isKeyRepeat(); | ||||
|         if ((enabled == null || !enabled) && (isKeyRepeat == null || isKeyRepeat)) { | ||||
|           if (VimPlugin.getNotifications().enableRepeatingMode() == Messages.YES) { | ||||
|           // This system property is used in IJ ui robot to hide the startup tips | ||||
|           boolean showNotification = Boolean.getBoolean("ide.show.tips.on.startup.default.value"); | ||||
|           if (showNotification && VimPlugin.getNotifications().enableRepeatingMode() == Messages.YES) { | ||||
|             getEditor().setKeyRepeat(true); | ||||
|             keyRepeat.setEnabled(true); | ||||
|           } | ||||
| @@ -408,12 +413,6 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable | ||||
|           keymap = manager.getKeymap(DefaultKeymap.getInstance().getDefaultKeymapName()); | ||||
|         } | ||||
|         assert keymap != null : "Default keymap not found"; | ||||
|         VimPlugin.getNotifications().specialKeymap(keymap, new NotificationListener.Adapter() { | ||||
|           @Override | ||||
|           protected void hyperlinkActivated(@NotNull Notification notification, @NotNull HyperlinkEvent e) { | ||||
|             ShowSettingsUtil.getInstance().showSettingsDialog(null, VimEmulationConfigurable.class); | ||||
|           } | ||||
|         }); | ||||
|         manager.setActiveKeymap(keymap); | ||||
|       } | ||||
|       if (previousStateVersion > 0 && previousStateVersion < 4) { | ||||
|   | ||||
| @@ -19,6 +19,7 @@ | ||||
| package com.maddyhome.idea.vim.action | ||||
|  | ||||
| import com.intellij.openapi.actionSystem.ActionPlaces | ||||
| import com.intellij.openapi.actionSystem.ActionUpdateThread | ||||
| import com.intellij.openapi.actionSystem.AnActionEvent | ||||
| import com.intellij.openapi.project.DumbAwareToggleAction | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| @@ -42,4 +43,6 @@ class VimPluginToggleAction : DumbAwareToggleAction()/*, LightEditCompatible*/ { | ||||
|       if (VimPlugin.isEnabled()) MessageHelper.message("action.VimPluginToggle.enabled") else MessageHelper.message("action.VimPluginToggle.enable") | ||||
|     } else MessageHelper.message("action.VimPluginToggle.text") | ||||
|   } | ||||
|  | ||||
|   override fun getActionUpdateThread() = ActionUpdateThread.BGT | ||||
| } | ||||
|   | ||||
| @@ -20,6 +20,7 @@ package com.maddyhome.idea.vim.action | ||||
| import com.google.common.collect.ImmutableSet | ||||
| import com.intellij.codeInsight.lookup.LookupManager | ||||
| import com.intellij.openapi.actionSystem.ActionManager | ||||
| import com.intellij.openapi.actionSystem.ActionUpdateThread | ||||
| import com.intellij.openapi.actionSystem.AnAction | ||||
| import com.intellij.openapi.actionSystem.AnActionEvent | ||||
| import com.intellij.openapi.actionSystem.EmptyAction | ||||
| @@ -95,6 +96,10 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // There is a chance that we can use BGT, but we call for isCell inside the update. | ||||
|   // Not sure if can can use BGT with this call. Let's use EDT for now. | ||||
|   override fun getActionUpdateThread() = ActionUpdateThread.EDT | ||||
|  | ||||
|   override fun update(e: AnActionEvent) { | ||||
|     val start = if (traceTime) System.currentTimeMillis() else null | ||||
|     e.presentation.isEnabled = isEnabled(e) | ||||
| @@ -149,6 +154,10 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ { | ||||
|  | ||||
|       if ((keyCode == KeyEvent.VK_TAB || keyCode == KeyEvent.VK_ENTER) && editor.appCodeTemplateCaptured()) return false | ||||
|        | ||||
|       if (keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_RIGHT) return false | ||||
|       if (keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN) return false | ||||
|       if (keyCode == KeyEvent.VK_HOME || keyCode == KeyEvent.VK_END) return false | ||||
|  | ||||
|       if (editor.inInsertMode) { | ||||
|         if (keyCode == KeyEvent.VK_TAB) { | ||||
|           // TODO: This stops VimEditorTab seeing <Tab> in insert mode and correctly scrolling the view | ||||
|   | ||||
| @@ -22,6 +22,7 @@ import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimCaret | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.Argument | ||||
| import com.maddyhome.idea.vim.command.Command | ||||
| import com.maddyhome.idea.vim.command.CommandFlags | ||||
| @@ -34,13 +35,13 @@ import com.maddyhome.idea.vim.group.visual.VimSelection | ||||
| import com.maddyhome.idea.vim.handler.VimActionHandler | ||||
| import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler | ||||
| import com.maddyhome.idea.vim.helper.MessageHelper | ||||
| import com.maddyhome.idea.vim.helper.commandState | ||||
| import com.maddyhome.idea.vim.helper.enumSetOf | ||||
| import com.maddyhome.idea.vim.helper.vimStateMachine | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
| import java.util.* | ||||
|  | ||||
| private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, selectionType: SelectionType): Boolean { | ||||
|   val operatorFunction = VimPlugin.getKey().operatorFunction | ||||
|   val operatorFunction = injector.keyGroup.operatorFunction | ||||
|   if (operatorFunction == null) { | ||||
|     VimPlugin.showMessage(MessageHelper.message("E774")) | ||||
|     return false | ||||
| @@ -49,7 +50,7 @@ private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textR | ||||
|   val saveRepeatHandler = VimRepeater.repeatHandler | ||||
|   VimPlugin.getMark().setChangeMarks(editor, textRange) | ||||
|   KeyHandler.getInstance().reset(editor) | ||||
|   val result = operatorFunction.apply(editor.ij, context.ij, selectionType) | ||||
|   val result = operatorFunction.apply(editor, context, selectionType) | ||||
|   VimRepeater.repeatHandler = saveRepeatHandler | ||||
|   return result | ||||
| } | ||||
| @@ -61,7 +62,7 @@ class OperatorAction : VimActionHandler.SingleExecution() { | ||||
|  | ||||
|   override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean { | ||||
|     val argument = cmd.argument ?: return false | ||||
|     if (!editor.commandState.isDotRepeatInProgress) { | ||||
|     if (!editor.vimStateMachine.isDotRepeatInProgress) { | ||||
|       argumentCaptured = argument | ||||
|     } | ||||
|     val range = getMotionRange(editor, context, argument, operatorArguments) | ||||
|   | ||||
| @@ -25,14 +25,14 @@ import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.Command | ||||
| import com.maddyhome.idea.vim.command.OperatorArguments | ||||
| import com.maddyhome.idea.vim.handler.VimActionHandler | ||||
| import com.maddyhome.idea.vim.helper.commandState | ||||
| import com.maddyhome.idea.vim.helper.vimStateMachine | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
|  | ||||
| class RepeatChangeAction : VimActionHandler.SingleExecution() { | ||||
|   override val type: Command.Type = Command.Type.OTHER_WRITABLE | ||||
|  | ||||
|   override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean { | ||||
|     val state = editor.commandState | ||||
|     val state = editor.vimStateMachine | ||||
|     val lastCommand = VimRepeater.lastChangeCommand | ||||
|  | ||||
|     if (lastCommand == null && Extension.lastExtensionHandler == null) return false | ||||
|   | ||||
| @@ -45,7 +45,7 @@ class DeleteJoinLinesAction : ChangeEditorActionHandler.SingleExecution() { | ||||
|     val res = arrayOf(true) | ||||
|     editor.forEachNativeCaret( | ||||
|       { caret: VimCaret -> | ||||
|         if (!injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, false)) res[0] = false | ||||
|         if (!injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, false, operatorArguments)) res[0] = false | ||||
|       }, | ||||
|       true | ||||
|     ) | ||||
|   | ||||
| @@ -45,7 +45,7 @@ class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecution() | ||||
|     val res = arrayOf(true) | ||||
|     editor.forEachNativeCaret( | ||||
|       { caret: VimCaret -> | ||||
|         if (!injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, true)) res[0] = false | ||||
|         if (!injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, true, operatorArguments)) res[0] = false | ||||
|       }, | ||||
|       true | ||||
|     ) | ||||
|   | ||||
| @@ -57,7 +57,14 @@ class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExecution( | ||||
|         caret: VimCaret -> | ||||
|         if (!caret.isValid) return@forEachNativeCaret | ||||
|         val range = caretsAndSelections[caret] ?: return@forEachNativeCaret | ||||
|         if (!injector.changeGroup.deleteJoinRange(editor, caret, range.toVimTextRange(true).normalize(), false)) { | ||||
|         if (!injector.changeGroup.deleteJoinRange( | ||||
|             editor, | ||||
|             caret, | ||||
|             range.toVimTextRange(true).normalize(), | ||||
|             false, | ||||
|             operatorArguments | ||||
|           ) | ||||
|         ) { | ||||
|           res[0] = false | ||||
|         } | ||||
|       }, true | ||||
|   | ||||
| @@ -56,7 +56,14 @@ class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.SingleExec | ||||
|       { caret: VimCaret -> | ||||
|         if (!caret.isValid) return@forEachNativeCaret | ||||
|         val range = caretsAndSelections[caret] ?: return@forEachNativeCaret | ||||
|         if (!injector.changeGroup.deleteJoinRange(editor, caret, range.toVimTextRange(true).normalize(), true)) { | ||||
|         if (!injector.changeGroup.deleteJoinRange( | ||||
|             editor, | ||||
|             caret, | ||||
|             range.toVimTextRange(true).normalize(), | ||||
|             true, | ||||
|             operatorArguments | ||||
|           ) | ||||
|         ) { | ||||
|           res[0] = false | ||||
|         } | ||||
|       }, | ||||
|   | ||||
| @@ -18,6 +18,7 @@ | ||||
| package com.maddyhome.idea.vim.action.internal | ||||
|  | ||||
| import com.intellij.ide.ui.AntialiasingType | ||||
| import com.intellij.ide.ui.UISettings | ||||
| import com.intellij.openapi.actionSystem.AnAction | ||||
| import com.intellij.openapi.actionSystem.AnActionEvent | ||||
| import com.intellij.openapi.actionSystem.CommonDataKeys | ||||
| @@ -124,7 +125,7 @@ class AddBlockInlaysAction : AnAction() { | ||||
|         val editorContext = FontInfo.getFontRenderContext(editor.contentComponent) | ||||
|         return FontRenderContext( | ||||
|           editorContext.transform, AntialiasingType.getKeyForCurrentScope(false), | ||||
|           if (editor is EditorImpl) editor.myFractionalMetricsHintValue else RenderingHints.VALUE_FRACTIONALMETRICS_OFF | ||||
|           if (editor is EditorImpl) UISettings.editorFractionalMetricsHint else RenderingHints.VALUE_FRACTIONALMETRICS_OFF | ||||
|         ) | ||||
|       } | ||||
|  | ||||
|   | ||||
							
								
								
									
										97
									
								
								src/main/java/com/maddyhome/idea/vim/command/CommandState.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/main/java/com/maddyhome/idea/vim/command/CommandState.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| /* | ||||
|  * IdeaVim - Vim emulator for IDEs based on the IntelliJ platform | ||||
|  * Copyright (C) 2003-2022 The IdeaVim authors | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| package com.maddyhome.idea.vim.command | ||||
|  | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.maddyhome.idea.vim.helper.vimStateMachine | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
|  | ||||
| /** | ||||
|  * COMPATIBILITY-LAYER: Additional class | ||||
|  * Please see: https://jb.gg/zo8n0r | ||||
|  */ | ||||
| class CommandState(private val machine: VimStateMachine) { | ||||
|  | ||||
|   val isOperatorPending: Boolean | ||||
|     get() = machine.isOperatorPending | ||||
|  | ||||
|   val mode: CommandState.Mode | ||||
|     get() = machine.mode.ij | ||||
|  | ||||
|   val commandBuilder: CommandBuilder | ||||
|     get() = machine.commandBuilder | ||||
|  | ||||
|   val mappingState: MappingState | ||||
|     get() = machine.mappingState | ||||
|  | ||||
|   enum class Mode { | ||||
|     // Basic modes | ||||
|     COMMAND, VISUAL, SELECT, INSERT, CMD_LINE, /*EX*/ | ||||
|  | ||||
|     // Additional modes | ||||
|     OP_PENDING, REPLACE /*, VISUAL_REPLACE*/, INSERT_NORMAL, INSERT_VISUAL, INSERT_SELECT | ||||
|   } | ||||
|  | ||||
|   enum class SubMode { | ||||
|     NONE, VISUAL_CHARACTER, VISUAL_LINE, VISUAL_BLOCK | ||||
|   } | ||||
|  | ||||
|   companion object { | ||||
|     @JvmStatic | ||||
|     fun getInstance(editor: Editor): CommandState { | ||||
|       return CommandState(editor.vim.vimStateMachine) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| val CommandState.SubMode.engine: VimStateMachine.SubMode | ||||
|   get() = when (this) { | ||||
|     CommandState.SubMode.NONE -> VimStateMachine.SubMode.NONE | ||||
|     CommandState.SubMode.VISUAL_CHARACTER -> VimStateMachine.SubMode.VISUAL_CHARACTER | ||||
|     CommandState.SubMode.VISUAL_LINE -> VimStateMachine.SubMode.VISUAL_LINE | ||||
|     CommandState.SubMode.VISUAL_BLOCK -> VimStateMachine.SubMode.VISUAL_BLOCK | ||||
|   } | ||||
|  | ||||
| val CommandState.Mode.engine: VimStateMachine.Mode | ||||
|   get() = when (this) { | ||||
|     CommandState.Mode.COMMAND -> VimStateMachine.Mode.COMMAND | ||||
|     CommandState.Mode.VISUAL -> VimStateMachine.Mode.VISUAL | ||||
|     CommandState.Mode.SELECT -> VimStateMachine.Mode.SELECT | ||||
|     CommandState.Mode.INSERT -> VimStateMachine.Mode.INSERT | ||||
|     CommandState.Mode.CMD_LINE -> VimStateMachine.Mode.CMD_LINE | ||||
|     CommandState.Mode.OP_PENDING -> VimStateMachine.Mode.OP_PENDING | ||||
|     CommandState.Mode.REPLACE -> VimStateMachine.Mode.REPLACE | ||||
|     CommandState.Mode.INSERT_NORMAL -> VimStateMachine.Mode.INSERT_NORMAL | ||||
|     CommandState.Mode.INSERT_VISUAL -> VimStateMachine.Mode.INSERT_VISUAL | ||||
|     CommandState.Mode.INSERT_SELECT -> VimStateMachine.Mode.INSERT_SELECT | ||||
|   } | ||||
|  | ||||
| val VimStateMachine.Mode.ij: CommandState.Mode | ||||
|   get() = when (this) { | ||||
|     VimStateMachine.Mode.COMMAND -> CommandState.Mode.COMMAND | ||||
|     VimStateMachine.Mode.VISUAL -> CommandState.Mode.VISUAL | ||||
|     VimStateMachine.Mode.SELECT -> CommandState.Mode.SELECT | ||||
|     VimStateMachine.Mode.INSERT -> CommandState.Mode.INSERT | ||||
|     VimStateMachine.Mode.CMD_LINE -> CommandState.Mode.CMD_LINE | ||||
|     VimStateMachine.Mode.OP_PENDING -> CommandState.Mode.OP_PENDING | ||||
|     VimStateMachine.Mode.REPLACE -> CommandState.Mode.REPLACE | ||||
|     VimStateMachine.Mode.INSERT_NORMAL -> CommandState.Mode.INSERT_NORMAL | ||||
|     VimStateMachine.Mode.INSERT_VISUAL -> CommandState.Mode.INSERT_VISUAL | ||||
|     VimStateMachine.Mode.INSERT_SELECT -> CommandState.Mode.INSERT_SELECT | ||||
|   } | ||||
| @@ -23,14 +23,15 @@ import com.intellij.openapi.editor.Editor | ||||
| import com.maddyhome.idea.vim.KeyHandler | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.action.change.Extension | ||||
| import com.maddyhome.idea.vim.api.VimCaret | ||||
| import com.maddyhome.idea.vim.command.MappingMode | ||||
| import com.maddyhome.idea.vim.command.SelectionType | ||||
| import com.maddyhome.idea.vim.common.CommandAlias | ||||
| import com.maddyhome.idea.vim.common.CommandAliasHandler | ||||
| import com.maddyhome.idea.vim.common.MappingMode | ||||
| import com.maddyhome.idea.vim.helper.CommandLineHelper | ||||
| import com.maddyhome.idea.vim.helper.EditorDataContext | ||||
| import com.maddyhome.idea.vim.helper.TestInputModel | ||||
| import com.maddyhome.idea.vim.helper.commandState | ||||
| import com.maddyhome.idea.vim.helper.vimStateMachine | ||||
| import com.maddyhome.idea.vim.key.MappingOwner | ||||
| import com.maddyhome.idea.vim.key.OperatorFunction | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| @@ -48,6 +49,22 @@ import javax.swing.KeyStroke | ||||
| object VimExtensionFacade { | ||||
|   /** The 'map' command for mapping keys to handlers defined in extensions. */ | ||||
|   @JvmStatic | ||||
|   fun putExtensionHandlerMapping( | ||||
|     modes: Set<MappingMode>, | ||||
|     fromKeys: List<KeyStroke>, | ||||
|     pluginOwner: MappingOwner, | ||||
|     extensionHandler: ExtensionHandler, | ||||
|     recursive: Boolean, | ||||
|   ) { | ||||
|     VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive) | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * COMPATIBILITY-LAYER: Additional method | ||||
|    * Please see: https://jb.gg/zo8n0r | ||||
|    */ | ||||
|   /** The 'map' command for mapping keys to handlers defined in extensions. */ | ||||
|   @JvmStatic | ||||
|   fun putExtensionHandlerMapping( | ||||
|     modes: Set<MappingMode>, | ||||
|     fromKeys: List<KeyStroke>, | ||||
| @@ -110,7 +127,7 @@ object VimExtensionFacade { | ||||
|   /** Sets the value of 'operatorfunc' to be used as the operator function in 'g@'. */ | ||||
|   @JvmStatic | ||||
|   fun setOperatorFunction(function: OperatorFunction) { | ||||
|     VimPlugin.getKey().setOperatorFunction(function) | ||||
|     VimPlugin.getKey().operatorFunction = function | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -129,7 +146,7 @@ object VimExtensionFacade { | ||||
|   /** Returns a single key stroke from the user input similar to 'getchar()'. */ | ||||
|   @JvmStatic | ||||
|   fun inputKeyStroke(editor: Editor): KeyStroke { | ||||
|     if (editor.vim.commandState.isDotRepeatInProgress) { | ||||
|     if (editor.vim.vimStateMachine.isDotRepeatInProgress) { | ||||
|       val input = Extension.consumeKeystroke() | ||||
|       return input ?: error("Not enough keystrokes saved: ${Extension.lastExtensionHandler}") | ||||
|     } | ||||
| @@ -137,7 +154,7 @@ object VimExtensionFacade { | ||||
|     val key: KeyStroke? = if (ApplicationManager.getApplication().isUnitTestMode) { | ||||
|       val mappingStack = KeyHandler.getInstance().keyStack | ||||
|       mappingStack.feedSomeStroke() ?: TestInputModel.getInstance(editor).nextKeyStroke()?.also { | ||||
|         if (editor.vim.commandState.isRecording) { | ||||
|         if (editor.vim.vimStateMachine.isRecording) { | ||||
|           KeyHandler.getInstance().modalEntryKeys += it | ||||
|         } | ||||
|       } | ||||
| @@ -167,12 +184,24 @@ object VimExtensionFacade { | ||||
|     return reg.keys | ||||
|   } | ||||
|  | ||||
|   @JvmStatic | ||||
|   fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? { | ||||
|     val reg = caret.registerStorage.getRegister(caret, register) ?: return null | ||||
|     return reg.keys | ||||
|   } | ||||
|  | ||||
|   /** Set the current contents of the given register */ | ||||
|   @JvmStatic | ||||
|   fun setRegister(register: Char, keys: List<KeyStroke?>?) { | ||||
|     VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList()) | ||||
|   } | ||||
|  | ||||
|   /** Set the current contents of the given register */ | ||||
|   @JvmStatic | ||||
|   fun setRegisterForCaret(register: Char, caret: VimCaret, keys: List<KeyStroke?>?) { | ||||
|     caret.registerStorage.setKeys(caret, register, keys?.filterNotNull() ?: emptyList()) | ||||
|   } | ||||
|  | ||||
|   /** Set the current contents of the given register */ | ||||
|   @JvmStatic | ||||
|   fun setRegister(register: Char, keys: List<KeyStroke?>?, type: SelectionType) { | ||||
|   | ||||
| @@ -0,0 +1,39 @@ | ||||
| /* | ||||
|  * IdeaVim - Vim emulator for IDEs based on the IntelliJ platform | ||||
|  * Copyright (C) 2003-2022 The IdeaVim authors | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| package com.maddyhome.idea.vim.extension | ||||
|  | ||||
| import com.intellij.openapi.actionSystem.DataContext | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
|  | ||||
| /** | ||||
|  * COMPATIBILITY-LAYER: Created a class, renamed original class | ||||
|  * Please see: https://jb.gg/zo8n0r | ||||
|  */ | ||||
| interface VimExtensionHandler : ExtensionHandler { | ||||
|   override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
|     execute(editor.ij, context.ij) | ||||
|   } | ||||
|  | ||||
|   fun execute(editor: Editor, context: DataContext) | ||||
|  | ||||
|   abstract class WithCallback : ExtensionHandler.WithCallback(), VimExtensionHandler | ||||
| } | ||||
| @@ -25,9 +25,9 @@ import com.maddyhome.idea.vim.api.VimExtensionRegistrator | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.ex.ExException | ||||
| import com.maddyhome.idea.vim.key.MappingOwner.Plugin.Companion.remove | ||||
| import com.maddyhome.idea.vim.option.ToggleOption | ||||
| import com.maddyhome.idea.vim.options.OptionChangeListener | ||||
| import com.maddyhome.idea.vim.options.OptionScope | ||||
| import com.maddyhome.idea.vim.options.ToggleOption | ||||
| import com.maddyhome.idea.vim.statistic.PluginState | ||||
| import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType | ||||
|  | ||||
|   | ||||
| @@ -18,24 +18,21 @@ | ||||
|  | ||||
| package com.maddyhome.idea.vim.extension.argtextobj; | ||||
|  | ||||
| import com.intellij.openapi.actionSystem.DataContext; | ||||
| import com.intellij.openapi.editor.Caret; | ||||
| import com.intellij.openapi.editor.Document; | ||||
| import com.intellij.openapi.editor.Editor; | ||||
| import com.maddyhome.idea.vim.VimPlugin; | ||||
| import com.maddyhome.idea.vim.api.*; | ||||
| import com.maddyhome.idea.vim.command.*; | ||||
| import com.maddyhome.idea.vim.common.MappingMode; | ||||
| import com.maddyhome.idea.vim.command.MappingMode; | ||||
| import com.maddyhome.idea.vim.common.TextRange; | ||||
| import com.maddyhome.idea.vim.extension.VimExtension; | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionHandler; | ||||
| import com.maddyhome.idea.vim.extension.ExtensionHandler; | ||||
| import com.maddyhome.idea.vim.handler.TextObjectActionHandler; | ||||
| import com.maddyhome.idea.vim.helper.InlayHelperKt; | ||||
| import com.maddyhome.idea.vim.helper.MessageHelper; | ||||
| import com.maddyhome.idea.vim.helper.VimNlsSafe; | ||||
| import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor; | ||||
| import com.maddyhome.idea.vim.listener.VimListenerSuppressor; | ||||
| import com.maddyhome.idea.vim.newapi.IjExecutionContext; | ||||
| import com.maddyhome.idea.vim.newapi.IjVimCaret; | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor; | ||||
| import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString; | ||||
| @@ -186,7 +183,7 @@ public class VimArgTextObjExtension implements VimExtension { | ||||
|   /** | ||||
|    * A text object for an argument to a function definition or a call. | ||||
|    */ | ||||
|   static class ArgumentHandler implements VimExtensionHandler { | ||||
|   static class ArgumentHandler implements ExtensionHandler { | ||||
|     final boolean isInner; | ||||
|  | ||||
|     ArgumentHandler(boolean isInner) { | ||||
| @@ -262,17 +259,17 @@ public class VimArgTextObjExtension implements VimExtension { | ||||
|     public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context) { | ||||
|  | ||||
|       IjVimEditor vimEditor = (IjVimEditor) editor; | ||||
|       @NotNull CommandState commandState = CommandState.getInstance(vimEditor); | ||||
|       int count = Math.max(1, commandState.getCommandBuilder().getCount()); | ||||
|       @NotNull VimStateMachine vimStateMachine = VimStateMachine.getInstance(vimEditor); | ||||
|       int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount()); | ||||
|  | ||||
|       final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner); | ||||
|       //noinspection DuplicatedCode | ||||
|       if (!commandState.isOperatorPending()) { | ||||
|       if (!vimStateMachine.isOperatorPending()) { | ||||
|         vimEditor.getEditor().getCaretModel().runForEachCaret((Caret caret) -> { | ||||
|           final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, count, 0, null); | ||||
|           if (range != null) { | ||||
|             try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) { | ||||
|               if (commandState.getMode() == CommandState.Mode.VISUAL) { | ||||
|               if (vimStateMachine.getMode() == VimStateMachine.Mode.VISUAL) { | ||||
|                 vimSetSelection(caret, range.getStartOffset(), range.getEndOffset() - 1, true); | ||||
|               } else { | ||||
|                 InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset()); | ||||
| @@ -282,7 +279,7 @@ public class VimArgTextObjExtension implements VimExtension { | ||||
|  | ||||
|         }); | ||||
|       } else { | ||||
|         commandState.getCommandBuilder().completeCommandPart(new Argument(new Command(count, | ||||
|         vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count, | ||||
|                                                                                          textObjectHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags.class)))); | ||||
|       } | ||||
|     } | ||||
|   | ||||
| @@ -17,10 +17,11 @@ | ||||
|  */ | ||||
| package com.maddyhome.idea.vim.extension.commentary | ||||
|  | ||||
| import com.intellij.openapi.actionSystem.DataContext | ||||
| import com.intellij.codeInsight.actions.AsyncActionExecutionService | ||||
| import com.intellij.openapi.actionSystem.IdeActions | ||||
| import com.intellij.openapi.application.runWriteAction | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.intellij.openapi.util.Ref | ||||
| import com.intellij.psi.PsiComment | ||||
| import com.intellij.psi.PsiElement | ||||
| import com.intellij.psi.PsiFile | ||||
| @@ -34,13 +35,14 @@ import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.Argument | ||||
| import com.maddyhome.idea.vim.command.Command | ||||
| import com.maddyhome.idea.vim.command.CommandFlags | ||||
| import com.maddyhome.idea.vim.command.CommandState | ||||
| import com.maddyhome.idea.vim.command.MappingMode | ||||
| import com.maddyhome.idea.vim.command.SelectionType | ||||
| import com.maddyhome.idea.vim.command.TextObjectVisualType | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.common.CommandAliasHandler | ||||
| import com.maddyhome.idea.vim.common.MappingMode | ||||
| import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.ex.ranges.Ranges | ||||
| import com.maddyhome.idea.vim.extension.ExtensionHandler | ||||
| import com.maddyhome.idea.vim.extension.VimExtension | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.addCommand | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping | ||||
| @@ -48,28 +50,31 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMa | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionHandler | ||||
| import com.maddyhome.idea.vim.handler.TextObjectActionHandler | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper | ||||
| import com.maddyhome.idea.vim.helper.PsiHelper | ||||
| import com.maddyhome.idea.vim.helper.commandState | ||||
| import com.maddyhome.idea.vim.helper.vimStateMachine | ||||
| import com.maddyhome.idea.vim.key.OperatorFunction | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import java.util.* | ||||
|  | ||||
| class CommentaryExtension : VimExtension { | ||||
|  | ||||
|   companion object { | ||||
|     fun doCommentary(editor: VimEditor, context: ExecutionContext, range: TextRange, selectionType: SelectionType, resetCaret: Boolean): Boolean { | ||||
|       val mode = editor.commandState.mode | ||||
|       if (mode !== CommandState.Mode.VISUAL) { | ||||
|     fun doCommentary( | ||||
|       editor: VimEditor, | ||||
|       context: ExecutionContext, | ||||
|       range: TextRange, | ||||
|       selectionType: SelectionType, | ||||
|       resetCaret: Boolean, | ||||
|     ): Boolean { | ||||
|       val mode = editor.vimStateMachine.mode | ||||
|       if (mode !== VimStateMachine.Mode.VISUAL) { | ||||
|         editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset) | ||||
|       } | ||||
|  | ||||
|       return runWriteAction { | ||||
|         try { | ||||
|         // Treat block- and character-wise selections as block comments. Be ready to fall back to if the first action | ||||
|         // isn't available | ||||
|         val actions = if (selectionType === SelectionType.LINE_WISE) { | ||||
| @@ -78,11 +83,27 @@ class CommentaryExtension : VimExtension { | ||||
|           listOf(IdeActions.ACTION_COMMENT_BLOCK, IdeActions.ACTION_COMMENT_LINE) | ||||
|         } | ||||
|  | ||||
|           injector.actionExecutor.executeAction(actions[0], context) || | ||||
|             injector.actionExecutor.executeAction(actions[1], context) | ||||
|         } finally { | ||||
|         val res = Ref.create<Boolean>(true) | ||||
|         AsyncActionExecutionService.getInstance(editor.ij.project!!).withExecutionAfterAction(actions[0], { | ||||
|           res.set(injector.actionExecutor.executeAction(actions[0], context)) | ||||
|         }, { afterCommenting(mode, editor, resetCaret, range) }) | ||||
|         if (!res.get()) { | ||||
|           AsyncActionExecutionService.getInstance(editor.ij.project!!).withExecutionAfterAction(actions[1], { | ||||
|             res.set(injector.actionExecutor.executeAction(actions[1], context)) | ||||
|           }, { afterCommenting(mode, editor, resetCaret, range) }) | ||||
|         } | ||||
|         res.get() | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     private fun afterCommenting( | ||||
|       mode: VimStateMachine.Mode, | ||||
|       editor: VimEditor, | ||||
|       resetCaret: Boolean, | ||||
|       range: TextRange, | ||||
|     ) { | ||||
|       // Remove the selection, if we added it | ||||
|           if (mode !== CommandState.Mode.VISUAL) { | ||||
|       if (mode !== VimStateMachine.Mode.VISUAL) { | ||||
|         editor.removeSelection() | ||||
|       } | ||||
|  | ||||
| @@ -98,8 +119,6 @@ class CommentaryExtension : VimExtension { | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   override fun getName() = "commentary" | ||||
|  | ||||
| @@ -112,7 +131,13 @@ class CommentaryExtension : VimExtension { | ||||
|  | ||||
|     putKeyMappingIfMissing(MappingMode.NXO, injector.parser.parseKeys("gc"), owner, plugCommentaryKeys, true) | ||||
|     putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("gcc"), owner, plugCommentaryLineKeys, true) | ||||
|     putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("gcu"), owner, injector.parser.parseKeys("<Plug>Commentary<Plug>Commentary"), true) | ||||
|     putKeyMappingIfMissing( | ||||
|       MappingMode.N, | ||||
|       injector.parser.parseKeys("gcu"), | ||||
|       owner, | ||||
|       injector.parser.parseKeys("<Plug>Commentary<Plug>Commentary"), | ||||
|       true | ||||
|     ) | ||||
|  | ||||
|     // Previous versions of IdeaVim used different mappings to Vim's Commentary. Make sure everything works if someone | ||||
|     // is still using the old mapping | ||||
| @@ -129,17 +154,22 @@ class CommentaryExtension : VimExtension { | ||||
|    * E.g. handles the `gc` in `gc_`, by setting the operator function, then invoking `g@` to receive the `_` motion to | ||||
|    * invoke the operator. This object is both the mapping handler and the operator function. | ||||
|    */ | ||||
|   private class CommentaryOperatorHandler : OperatorFunction, VimExtensionHandler { | ||||
|   private class CommentaryOperatorHandler : OperatorFunction, ExtensionHandler { | ||||
|     override val isRepeatable = true | ||||
|  | ||||
|     // In this operator we process selection by ourselves. This is necessary for rider, VIM-1758 | ||||
|     override fun postProcessSelection(): Boolean { | ||||
|       return false | ||||
|     } | ||||
|  | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
|       setOperatorFunction(this) | ||||
|       executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij) | ||||
|     } | ||||
|  | ||||
|     override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean { | ||||
|       val range = VimPlugin.getMark().getChangeMarks(editor.vim) ?: return false | ||||
|       return doCommentary(editor.vim, context.vim, range, selectionType, true) | ||||
|     override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean { | ||||
|       val range = VimPlugin.getMark().getChangeMarks(editor) ?: return false | ||||
|       return doCommentary(editor, context, range, selectionType, true) | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -148,11 +178,11 @@ class CommentaryExtension : VimExtension { | ||||
|    * | ||||
|    * This object is both the `<Plug>Commentary` mapping handler and the text object handler | ||||
|    */ | ||||
|   private class CommentaryTextObjectMotionHandler : TextObjectActionHandler(), VimExtensionHandler { | ||||
|   private class CommentaryTextObjectMotionHandler : TextObjectActionHandler(), ExtensionHandler { | ||||
|     override val isRepeatable = true | ||||
|  | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
|       val commandState = editor.commandState | ||||
|       val commandState = editor.vimStateMachine | ||||
|       val count = maxOf(1, commandState.commandBuilder.count) | ||||
|  | ||||
|       val textObjectHandler = this | ||||
| @@ -174,7 +204,7 @@ class CommentaryExtension : VimExtension { | ||||
|       context: ExecutionContext, | ||||
|       count: Int, | ||||
|       rawCount: Int, | ||||
|       argument: Argument? | ||||
|       argument: Argument?, | ||||
|     ): TextRange? { | ||||
|  | ||||
|       val nativeEditor = (editor as IjVimEditor).editor | ||||
|   | ||||
| @@ -18,7 +18,6 @@ | ||||
|  | ||||
| package com.maddyhome.idea.vim.extension.exchange | ||||
|  | ||||
| import com.intellij.openapi.actionSystem.DataContext | ||||
| import com.intellij.openapi.application.runWriteAction | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.intellij.openapi.editor.LogicalPosition | ||||
| @@ -31,10 +30,11 @@ import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.CommandState | ||||
| import com.maddyhome.idea.vim.command.MappingMode | ||||
| import com.maddyhome.idea.vim.command.SelectionType | ||||
| import com.maddyhome.idea.vim.common.MappingMode | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.extension.ExtensionHandler | ||||
| import com.maddyhome.idea.vim.extension.VimExtension | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister | ||||
| @@ -42,7 +42,6 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMa | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionHandler | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper | ||||
| import com.maddyhome.idea.vim.helper.fileSize | ||||
| import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition | ||||
| @@ -95,7 +94,7 @@ class VimExchangeExtension : VimExtension { | ||||
|     val EXCHANGE_KEY = Key<Exchange>("exchange") | ||||
|  | ||||
|     // End mark has always greater of eq offset than start mark | ||||
|     class Exchange(val type: CommandState.SubMode, val start: Mark, val end: Mark, val text: String) { | ||||
|     class Exchange(val type: VimStateMachine.SubMode, val start: Mark, val end: Mark, val text: String) { | ||||
|       private var myHighlighter: RangeHighlighter? = null | ||||
|       fun setHighlighter(highlighter: RangeHighlighter) { | ||||
|         myHighlighter = highlighter | ||||
| @@ -112,7 +111,7 @@ class VimExchangeExtension : VimExtension { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private class ExchangeHandler(private val isLine: Boolean) : VimExtensionHandler { | ||||
|   private class ExchangeHandler(private val isLine: Boolean) : ExtensionHandler { | ||||
|     override val isRepeatable = true | ||||
|  | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
| @@ -121,37 +120,38 @@ class VimExchangeExtension : VimExtension { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private class ExchangeClearHandler : VimExtensionHandler { | ||||
|   private class ExchangeClearHandler : ExtensionHandler { | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
|       clearExchange(editor.ij) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private class VExchangeHandler : VimExtensionHandler { | ||||
|   private class VExchangeHandler : ExtensionHandler { | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
|       runWriteAction { | ||||
|         val subMode = editor.subMode | ||||
|         // Leave visual mode to create selection marks | ||||
|         executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij) | ||||
|         Operator(true).apply(editor.ij, context.ij, SelectionType.fromSubMode(subMode)) | ||||
|         Operator(true).apply(editor, context, SelectionType.fromSubMode(subMode)) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private class Operator(private val isVisual: Boolean) : OperatorFunction { | ||||
|     fun Editor.getMarkOffset(mark: Mark) = EditorHelper.getOffset(this, mark.logicalLine, mark.col) | ||||
|     fun CommandState.SubMode.getString() = when (this) { | ||||
|       CommandState.SubMode.VISUAL_CHARACTER -> "v" | ||||
|       CommandState.SubMode.VISUAL_LINE -> "V" | ||||
|       CommandState.SubMode.VISUAL_BLOCK -> "\\<C-V>" | ||||
|     fun VimStateMachine.SubMode.getString() = when (this) { | ||||
|       VimStateMachine.SubMode.VISUAL_CHARACTER -> "v" | ||||
|       VimStateMachine.SubMode.VISUAL_LINE -> "V" | ||||
|       VimStateMachine.SubMode.VISUAL_BLOCK -> "\\<C-V>" | ||||
|       else -> error("Invalid SubMode: $this") | ||||
|     } | ||||
|  | ||||
|     override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean { | ||||
|     override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean { | ||||
|       val editor = vimEditor.ij | ||||
|       fun highlightExchange(ex: Exchange): RangeHighlighter { | ||||
|         val attributes = editor.colorsScheme.getAttributes(EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES) | ||||
|         val hlArea = when (ex.type) { | ||||
|           CommandState.SubMode.VISUAL_LINE -> HighlighterTargetArea.LINES_IN_RANGE | ||||
|           VimStateMachine.SubMode.VISUAL_LINE -> HighlighterTargetArea.LINES_IN_RANGE | ||||
|           // TODO: handle other modes | ||||
|           else -> HighlighterTargetArea.EXACT_RANGE | ||||
|         } | ||||
| @@ -274,7 +274,7 @@ class VimExchangeExtension : VimExtension { | ||||
|           x.logicalLine - y.logicalLine | ||||
|         } | ||||
|  | ||||
|       return if (x.type == CommandState.SubMode.VISUAL_BLOCK && y.type == CommandState.SubMode.VISUAL_BLOCK) { | ||||
|       return if (x.type == VimStateMachine.SubMode.VISUAL_BLOCK && y.type == VimStateMachine.SubMode.VISUAL_BLOCK) { | ||||
|         when { | ||||
|           intersects(x, y) -> { | ||||
|             ExchangeCompareResult.OVERLAP | ||||
|   | ||||
| @@ -33,21 +33,21 @@ import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.Argument | ||||
| import com.maddyhome.idea.vim.command.Command | ||||
| import com.maddyhome.idea.vim.command.CommandFlags | ||||
| import com.maddyhome.idea.vim.command.MappingMode | ||||
| import com.maddyhome.idea.vim.command.MotionType | ||||
| import com.maddyhome.idea.vim.command.OperatorArguments | ||||
| import com.maddyhome.idea.vim.common.Direction | ||||
| import com.maddyhome.idea.vim.common.MappingMode | ||||
| import com.maddyhome.idea.vim.extension.ExtensionHandler | ||||
| import com.maddyhome.idea.vim.extension.VimExtension | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionHandler | ||||
| import com.maddyhome.idea.vim.handler.Motion | ||||
| import com.maddyhome.idea.vim.handler.MotionActionHandler | ||||
| import com.maddyhome.idea.vim.handler.toMotionOrError | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper | ||||
| import com.maddyhome.idea.vim.helper.PsiHelper | ||||
| import com.maddyhome.idea.vim.helper.commandState | ||||
| import com.maddyhome.idea.vim.helper.enumSetOf | ||||
| import com.maddyhome.idea.vim.helper.vimStateMachine | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import java.util.* | ||||
| @@ -94,14 +94,14 @@ class Matchit : VimExtension { | ||||
|     override var motionType: MotionType = MotionType.INCLUSIVE | ||||
|   } | ||||
|  | ||||
|   private class MatchitHandler(private val reverse: Boolean) : VimExtensionHandler { | ||||
|   private class MatchitHandler(private val reverse: Boolean) : ExtensionHandler { | ||||
|  | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
|       val commandState = editor.commandState | ||||
|       val commandState = editor.vimStateMachine | ||||
|       val count = commandState.commandBuilder.count | ||||
|  | ||||
|       // Reset the command count so it doesn't transfer onto subsequent commands. | ||||
|       editor.commandState.commandBuilder.resetCount() | ||||
|       editor.vimStateMachine.commandBuilder.resetCount() | ||||
|  | ||||
|       // Normally we want to jump to the start of the matching pair. But when moving forward in operator | ||||
|       // pending mode, we want to include the entire match. isInOpPending makes that distinction. | ||||
| @@ -233,7 +233,7 @@ private object FileTypePatterns { | ||||
|     } else if (fileTypeName == "CMakeLists.txt" || fileName == "CMakeLists") { | ||||
|       this.cMakePatterns | ||||
|     } else { | ||||
|       return null | ||||
|       this.htmlPatterns | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -29,13 +29,13 @@ import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.CommandState | ||||
| import com.maddyhome.idea.vim.common.MappingMode | ||||
| import com.maddyhome.idea.vim.command.MappingMode | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.extension.ExtensionHandler | ||||
| import com.maddyhome.idea.vim.extension.VimExtension | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionHandler | ||||
| import com.maddyhome.idea.vim.group.MotionGroup | ||||
| import com.maddyhome.idea.vim.group.visual.vimSetSelection | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper | ||||
| @@ -111,7 +111,7 @@ class VimMultipleCursorsExtension : VimExtension { | ||||
|     putKeyMappingIfMissing(MappingMode.X, injector.parser.parseKeys("<A-p>"), owner, injector.parser.parseKeys(REMOVE_OCCURRENCE), true) | ||||
|   } | ||||
|  | ||||
|   abstract class WriteActionHandler : VimExtensionHandler { | ||||
|   abstract class WriteActionHandler : ExtensionHandler { | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
|       ApplicationManager.getApplication().runWriteAction { | ||||
|         executeInWriteAction(editor.ij, context.ij) | ||||
| @@ -313,7 +313,7 @@ class VimMultipleCursorsExtension : VimExtension { | ||||
|  | ||||
|   private fun enterVisualMode(editor: VimEditor) { | ||||
|     // We need to reset the key handler to make sure we pick up the fact that we're in visual mode | ||||
|     VimPlugin.getVisualMotion().enterVisualMode(editor, CommandState.SubMode.VISUAL_CHARACTER) | ||||
|     VimPlugin.getVisualMotion().enterVisualMode(editor, VimStateMachine.SubMode.VISUAL_CHARACTER) | ||||
|     KeyHandler.getInstance().reset(editor) | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -21,6 +21,7 @@ package com.maddyhome.idea.vim.extension.nerdtree | ||||
| import com.intellij.ide.projectView.ProjectView | ||||
| import com.intellij.ide.projectView.impl.ProjectViewImpl | ||||
| import com.intellij.openapi.actionSystem.ActionManager | ||||
| import com.intellij.openapi.actionSystem.ActionUpdateThread | ||||
| import com.intellij.openapi.actionSystem.AnActionEvent | ||||
| import com.intellij.openapi.actionSystem.CommonDataKeys | ||||
| import com.intellij.openapi.application.ApplicationManager | ||||
| @@ -44,18 +45,18 @@ import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.common.CommandAlias | ||||
| import com.maddyhome.idea.vim.common.CommandAliasHandler | ||||
| import com.maddyhome.idea.vim.common.CommandNode | ||||
| import com.maddyhome.idea.vim.common.CommandPartNode | ||||
| import com.maddyhome.idea.vim.common.Node | ||||
| import com.maddyhome.idea.vim.common.RootNode | ||||
| import com.maddyhome.idea.vim.common.addLeafs | ||||
| import com.maddyhome.idea.vim.ex.ranges.Ranges | ||||
| import com.maddyhome.idea.vim.extension.VimExtension | ||||
| import com.maddyhome.idea.vim.group.KeyGroup | ||||
| import com.maddyhome.idea.vim.helper.MessageHelper | ||||
| import com.maddyhome.idea.vim.helper.runAfterGotFocus | ||||
| import com.maddyhome.idea.vim.key.CommandNode | ||||
| import com.maddyhome.idea.vim.key.CommandPartNode | ||||
| import com.maddyhome.idea.vim.key.MappingOwner | ||||
| import com.maddyhome.idea.vim.key.Node | ||||
| import com.maddyhome.idea.vim.key.RequiredShortcut | ||||
| import com.maddyhome.idea.vim.key.RootNode | ||||
| import com.maddyhome.idea.vim.key.addLeafs | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString | ||||
| @@ -172,7 +173,7 @@ class NerdTree : VimExtension { | ||||
|   } | ||||
|  | ||||
|   class ProjectViewListener(private val project: Project) : ToolWindowManagerListener { | ||||
|     override fun toolWindowShown(id: String, toolWindow: ToolWindow) { | ||||
|     override fun toolWindowShown(toolWindow: ToolWindow) { | ||||
|       if (ToolWindowId.PROJECT_VIEW != toolWindow.id) return | ||||
|  | ||||
|       val dispatcher = NerdDispatcher.getInstance(project) | ||||
| @@ -242,6 +243,8 @@ class NerdTree : VimExtension { | ||||
|       e.presentation.isEnabled = !speedSearchIsHere(project) | ||||
|     } | ||||
|  | ||||
|     override fun getActionUpdateThread() = ActionUpdateThread.EDT | ||||
|  | ||||
|     private fun speedSearchIsHere(project: Project): Boolean { | ||||
|       val component = ProjectView.getInstance(project).currentProjectViewPane.tree ?: return false | ||||
|       return SpeedSearchSupply.getSupply(component) != null | ||||
| @@ -342,7 +345,7 @@ class NerdTree : VimExtension { | ||||
|         if (file.isDirectory) return@Code | ||||
|         val splitters = FileEditorManagerEx.getInstanceEx(project).splitters | ||||
|         val currentWindow = splitters.currentWindow | ||||
|         currentWindow.split(SwingConstants.HORIZONTAL, true, file, true) | ||||
|         currentWindow?.split(SwingConstants.HORIZONTAL, true, file, true) | ||||
|       } | ||||
|     ) | ||||
|     registerCommand( | ||||
| @@ -351,7 +354,7 @@ class NerdTree : VimExtension { | ||||
|         val file = event.getData(CommonDataKeys.VIRTUAL_FILE) ?: return@Code | ||||
|         val splitters = FileEditorManagerEx.getInstanceEx(project).splitters | ||||
|         val currentWindow = splitters.currentWindow | ||||
|         currentWindow.split(SwingConstants.VERTICAL, true, file, true) | ||||
|         currentWindow?.split(SwingConstants.VERTICAL, true, file, true) | ||||
|  | ||||
|         // FIXME: 22.01.2021 This solution bouncing a bit | ||||
|         callAction("ActivateProjectToolWindow", context.vim) | ||||
| @@ -363,7 +366,7 @@ class NerdTree : VimExtension { | ||||
|         val file = event.getData(CommonDataKeys.VIRTUAL_FILE) ?: return@Code | ||||
|         val splitters = FileEditorManagerEx.getInstanceEx(project).splitters | ||||
|         val currentWindow = splitters.currentWindow | ||||
|         currentWindow.split(SwingConstants.HORIZONTAL, true, file, true) | ||||
|         currentWindow?.split(SwingConstants.HORIZONTAL, true, file, true) | ||||
|  | ||||
|         callAction("ActivateProjectToolWindow", context.vim) | ||||
|       } | ||||
| @@ -560,7 +563,3 @@ class NerdTree : VimExtension { | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| private fun <T> Node<T>.addLeafs(keys: String, actionHolder: T) { | ||||
|   addLeafs(injector.parser.parseKeys(keys), actionHolder) | ||||
| } | ||||
|   | ||||
| @@ -23,11 +23,11 @@ import com.intellij.openapi.editor.Editor | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.common.MappingMode | ||||
| import com.maddyhome.idea.vim.command.MappingMode | ||||
| import com.maddyhome.idea.vim.extension.ExtensionHandler | ||||
| import com.maddyhome.idea.vim.extension.VimExtension | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionHandler | ||||
| import com.maddyhome.idea.vim.group.MotionGroup | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper | ||||
| import com.maddyhome.idea.vim.helper.SearchHelper | ||||
| @@ -45,7 +45,7 @@ class ParagraphMotion : VimExtension { | ||||
|     putKeyMappingIfMissing(MappingMode.NXO, injector.parser.parseKeys("{"), owner, injector.parser.parseKeys("<Plug>(ParagraphPrevMotion)"), true) | ||||
|   } | ||||
|  | ||||
|   private class ParagraphMotionHandler(private val count: Int) : VimExtensionHandler { | ||||
|   private class ParagraphMotionHandler(private val count: Int) : ExtensionHandler { | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
|       editor.ij.vimForEachCaret { caret -> | ||||
|         val motion = moveCaretToNextParagraph(editor.ij, caret, count) | ||||
|   | ||||
| @@ -18,28 +18,30 @@ | ||||
|  | ||||
| package com.maddyhome.idea.vim.extension.replacewithregister | ||||
|  | ||||
| import com.intellij.openapi.actionSystem.DataContext | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimCaret | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.CommandState | ||||
| import com.maddyhome.idea.vim.command.MappingMode | ||||
| import com.maddyhome.idea.vim.command.OperatorArguments | ||||
| import com.maddyhome.idea.vim.command.SelectionType | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.command.isLine | ||||
| import com.maddyhome.idea.vim.common.MappingMode | ||||
| import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.extension.ExtensionHandler | ||||
| import com.maddyhome.idea.vim.extension.VimExtension | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionHandler | ||||
| import com.maddyhome.idea.vim.group.visual.VimSelection | ||||
| import com.maddyhome.idea.vim.helper.EditorDataContext | ||||
| import com.maddyhome.idea.vim.helper.editorMode | ||||
| import com.maddyhome.idea.vim.helper.mode | ||||
| import com.maddyhome.idea.vim.helper.subMode | ||||
| import com.maddyhome.idea.vim.helper.vimStateMachine | ||||
| import com.maddyhome.idea.vim.key.OperatorFunction | ||||
| import com.maddyhome.idea.vim.newapi.IjExecutionContext | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||
| @@ -63,22 +65,21 @@ class ReplaceWithRegister : VimExtension { | ||||
|     putKeyMappingIfMissing(MappingMode.X, injector.parser.parseKeys("gr"), owner, injector.parser.parseKeys(RWR_VISUAL), true) | ||||
|   } | ||||
|  | ||||
|   private class RwrVisual : VimExtensionHandler { | ||||
|   private class RwrVisual : ExtensionHandler { | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
|       val caretsAndSelections = mutableMapOf<VimCaret, VimSelection>() | ||||
|       val typeInEditor = SelectionType.fromSubMode(editor.subMode) | ||||
|       editor.forEachCaret { caret -> | ||||
|         val selectionStart = caret.selectionStart | ||||
|         val selectionEnd = caret.selectionEnd | ||||
|  | ||||
|         caretsAndSelections += caret to VimSelection.create(selectionStart, selectionEnd - 1, typeInEditor, editor) | ||||
|         val visualSelection = caret to VimSelection.create(selectionStart, selectionEnd - 1, typeInEditor, editor) | ||||
|         doReplace(editor.ij, caret, PutData.VisualSelection(mapOf(visualSelection), typeInEditor)) | ||||
|       } | ||||
|       doReplace(editor.ij, PutData.VisualSelection(caretsAndSelections, typeInEditor)) | ||||
|       editor.exitVisualModeNative() | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private class RwrMotion : VimExtensionHandler { | ||||
|   private class RwrMotion : ExtensionHandler { | ||||
|     override val isRepeatable: Boolean = true | ||||
|  | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
| @@ -87,7 +88,7 @@ class ReplaceWithRegister : VimExtension { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private class RwrLine : VimExtensionHandler { | ||||
|   private class RwrLine : ExtensionHandler { | ||||
|     override val isRepeatable: Boolean = true | ||||
|  | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
| @@ -97,11 +98,11 @@ class ReplaceWithRegister : VimExtension { | ||||
|         val lineStart = editor.getLineStartOffset(logicalLine) | ||||
|         val lineEnd = editor.getLineEndOffset(logicalLine, true) | ||||
|  | ||||
|         caretsAndSelections += caret to VimSelection.create(lineStart, lineEnd, SelectionType.LINE_WISE, editor) | ||||
|       } | ||||
|         val visualSelection = caret to VimSelection.create(lineStart, lineEnd, SelectionType.LINE_WISE, editor) | ||||
|         caretsAndSelections += visualSelection | ||||
|  | ||||
|       val visualSelection = PutData.VisualSelection(caretsAndSelections, SelectionType.LINE_WISE) | ||||
|       doReplace(editor.ij, visualSelection) | ||||
|         doReplace(editor.ij, caret, PutData.VisualSelection(mapOf(visualSelection), SelectionType.LINE_WISE)) | ||||
|       } | ||||
|  | ||||
|       editor.forEachCaret { caret -> | ||||
|         val vimStart = caretsAndSelections[caret]?.vimStart | ||||
| @@ -113,26 +114,28 @@ class ReplaceWithRegister : VimExtension { | ||||
|   } | ||||
|  | ||||
|   private class Operator : OperatorFunction { | ||||
|     override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean { | ||||
|     override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean { | ||||
|       val editor = (vimEditor as IjVimEditor).editor | ||||
|       val range = getRange(editor) ?: return false | ||||
|       val visualSelection = PutData.VisualSelection( | ||||
|         mapOf( | ||||
|           editor.caretModel.primaryCaret.vim to VimSelection.create( | ||||
|           vimEditor.primaryCaret() to VimSelection.create( | ||||
|             range.startOffset, | ||||
|             range.endOffset - 1, | ||||
|             selectionType, | ||||
|             IjVimEditor(editor) | ||||
|             vimEditor | ||||
|           ) | ||||
|         ), | ||||
|         selectionType | ||||
|       ) | ||||
|       doReplace(editor, visualSelection) | ||||
|       // todo multicaret | ||||
|       doReplace(editor, vimEditor.primaryCaret(), visualSelection) | ||||
|       return true | ||||
|     } | ||||
|  | ||||
|     private fun getRange(editor: Editor): TextRange? = when (editor.vim.mode) { | ||||
|       CommandState.Mode.COMMAND -> VimPlugin.getMark().getChangeMarks(editor.vim) | ||||
|       CommandState.Mode.VISUAL -> editor.caretModel.primaryCaret.run { TextRange(selectionStart, selectionEnd) } | ||||
|       VimStateMachine.Mode.COMMAND -> VimPlugin.getMark().getChangeMarks(editor.vim) | ||||
|       VimStateMachine.Mode.VISUAL -> editor.caretModel.primaryCaret.run { TextRange(selectionStart, selectionEnd) } | ||||
|       else -> null | ||||
|     } | ||||
|   } | ||||
| @@ -147,8 +150,9 @@ class ReplaceWithRegister : VimExtension { | ||||
|     @NonNls | ||||
|     private const val RWR_VISUAL = "<Plug>ReplaceWithRegisterVisual" | ||||
|  | ||||
|     private fun doReplace(editor: Editor, visualSelection: PutData.VisualSelection) { | ||||
|       val savedRegister = VimPlugin.getRegister().lastRegister ?: return | ||||
|     private fun doReplace(editor: Editor, caret: VimCaret, visualSelection: PutData.VisualSelection) { | ||||
|       val lastRegisterChar = injector.registerGroup.lastRegisterChar | ||||
|       val savedRegister = caret.registerStorage.getRegister(caret, lastRegisterChar) ?: return | ||||
|  | ||||
|       var usedType = savedRegister.type | ||||
|       var usedText = savedRegister.text | ||||
| @@ -170,11 +174,19 @@ class ReplaceWithRegister : VimExtension { | ||||
|         putToLine = -1 | ||||
|       ) | ||||
|       ClipboardOptionHelper.IdeaputDisabler().use { | ||||
|         VimPlugin.getPut().putText(IjVimEditor(editor), IjExecutionContext(EditorDataContext.init(editor)), putData) | ||||
|         VimPlugin.getPut().putText( | ||||
|           IjVimEditor(editor), | ||||
|           IjExecutionContext(EditorDataContext.init(editor)), | ||||
|           putData, | ||||
|           operatorArguments = OperatorArguments( | ||||
|             editor.vimStateMachine?.isOperatorPending ?: false, | ||||
|             0, editor.editorMode, editor.subMode | ||||
|           ) | ||||
|         ) | ||||
|       } | ||||
|  | ||||
|       VimPlugin.getRegister().saveRegister(savedRegister.name, savedRegister) | ||||
|       VimPlugin.getRegister().saveRegister(VimPlugin.getRegister().defaultRegister, savedRegister) | ||||
|       caret.registerStorage.saveRegister(caret, savedRegister.name, savedRegister) | ||||
|       caret.registerStorage.saveRegister(caret, VimPlugin.getRegister().defaultRegister, savedRegister) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -17,35 +17,37 @@ | ||||
|  */ | ||||
| package com.maddyhome.idea.vim.extension.surround | ||||
|  | ||||
| import com.intellij.openapi.actionSystem.DataContext | ||||
| import com.intellij.openapi.application.runWriteAction | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimCaret | ||||
| import com.maddyhome.idea.vim.api.VimChangeGroup | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.CommandState | ||||
| import com.maddyhome.idea.vim.command.MappingMode | ||||
| import com.maddyhome.idea.vim.command.SelectionType | ||||
| import com.maddyhome.idea.vim.common.MappingMode | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.extension.ExtensionHandler | ||||
| import com.maddyhome.idea.vim.extension.VimExtension | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegisterForCaret | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.inputKeyStroke | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.inputString | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionHandler | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper | ||||
| import com.maddyhome.idea.vim.helper.mode | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret | ||||
| import com.maddyhome.idea.vim.helper.editorMode | ||||
| import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore | ||||
| import com.maddyhome.idea.vim.key.OperatorFunction | ||||
| import com.maddyhome.idea.vim.newapi.IjVimCaret | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper | ||||
| import com.maddyhome.idea.vim.put.PutData | ||||
| import org.jetbrains.annotations.NonNls | ||||
| import java.awt.event.KeyEvent | ||||
| import javax.swing.KeyStroke | ||||
| @@ -80,31 +82,29 @@ class VimSurroundExtension : VimExtension { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private class YSurroundHandler : VimExtensionHandler { | ||||
|   private class YSurroundHandler : ExtensionHandler { | ||||
|     override val isRepeatable = true | ||||
|  | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
|       setOperatorFunction(Operator()) | ||||
|       setOperatorFunction(Operator(supportsMultipleCursors = false)) // TODO | ||||
|       executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private class VSurroundHandler : VimExtensionHandler { | ||||
|   private class VSurroundHandler : ExtensionHandler { | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
|       val selectionStart = editor.ij.caretModel.primaryCaret.selectionStart | ||||
|       // NB: Operator ignores SelectionType anyway | ||||
|       if (!Operator().apply(editor.ij, context.ij, SelectionType.CHARACTER_WISE)) { | ||||
|       if (!Operator(supportsMultipleCursors = true).apply(editor, context, SelectionType.CHARACTER_WISE)) { | ||||
|         return | ||||
|       } | ||||
|       runWriteAction { | ||||
|         // Leave visual mode | ||||
|         executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij) | ||||
|         editor.ij.caretModel.moveToOffset(selectionStart) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private class CSurroundHandler : VimExtensionHandler { | ||||
|   private class CSurroundHandler : ExtensionHandler { | ||||
|     override val isRepeatable = true | ||||
|  | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
| @@ -115,33 +115,74 @@ class VimSurroundExtension : VimExtension { | ||||
|       if (charTo.code == 0) return | ||||
|  | ||||
|       val newSurround = getOrInputPair(charTo, editor.ij) ?: return | ||||
|       runWriteAction { change(editor.ij, charFrom, newSurround) } | ||||
|       runWriteAction { change(editor, context, charFrom, newSurround) } | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
|       fun change(editor: Editor, charFrom: Char, newSurround: Pair<String, String>?) { | ||||
|         // We take over the " register, so preserve it | ||||
|         val oldValue: List<KeyStroke>? = getRegister(REGISTER) | ||||
|         // Empty the " register | ||||
|         setRegister(REGISTER, null) | ||||
|         // Extract the inner value | ||||
|         perform("di" + pick(charFrom), editor) | ||||
|         val innerValue: MutableList<KeyStroke> = getRegister(REGISTER)?.toMutableList() ?: mutableListOf() | ||||
|         // If the surrounding characters were not found, the register will be empty | ||||
|         if (innerValue.isNotEmpty()) { | ||||
|           // Delete the surrounding | ||||
|           perform("da" + pick(charFrom), editor) | ||||
|           // Insert the surrounding characters and paste | ||||
|           if (newSurround != null) { | ||||
|             innerValue.addAll(0, injector.parser.parseKeys(newSurround.first)) | ||||
|             innerValue.addAll(injector.parser.parseKeys(newSurround.second)) | ||||
|       fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) { | ||||
|         editor.ij.runWithEveryCaretAndRestore { changeAtCaret(editor, context, charFrom, newSurround) } | ||||
|       } | ||||
|           pasteSurround(innerValue, editor) | ||||
|           // Jump back to start | ||||
|           executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor) | ||||
|        | ||||
|       fun changeAtCaret(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) { | ||||
|         // Save old register values for carets | ||||
|         val surroundings = editor.sortedCarets() | ||||
|           .map { | ||||
|             val oldValue: List<KeyStroke>? = getRegisterForCaret(REGISTER, it) | ||||
|             setRegisterForCaret(REGISTER, it, null) | ||||
|             SurroundingInfo(it, null, oldValue, null) | ||||
|           } | ||||
|  | ||||
|         // Delete surrounding's content | ||||
|         perform("di" + pick(charFrom), editor.ij) | ||||
|  | ||||
|         // Add info about surrounding's inner text and location | ||||
|         surroundings.forEach { | ||||
|           val registerValue = getRegisterForCaret(REGISTER, it.caret) | ||||
|           val innerValue = if (registerValue.isNullOrEmpty()) null else registerValue | ||||
|           it.innerText = innerValue | ||||
|  | ||||
|           val lineEndOffset = injector.engineEditorHelper.getLineEndOffset(editor, it.caret.getLine().line, false) | ||||
|           if (lineEndOffset == it.caret.offset.point) { | ||||
|             it.isLineEnd = true | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         // Remove surrounding | ||||
|         perform("da" + pick(charFrom), editor.ij) | ||||
|  | ||||
|         surroundings.forEach { | ||||
|           if (it.innerText == null && getRegisterForCaret(REGISTER, it.caret)?.isNotEmpty() == true) { | ||||
|             it.innerText = emptyList() | ||||
|           } | ||||
|  | ||||
|           // caret should be placed at the first char of inserted text | ||||
|           // the best solution would be using [ mark after the paste, but marks are not supported by multicaret | ||||
|           // todo | ||||
|           if (it.innerText != null) { | ||||
|             it.offset = it.caret.offset.point | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         surroundings | ||||
|           .filter { it.innerText != null } // we do nothing with carets that are not inside the surrounding | ||||
|           .map { surrounding -> | ||||
|             val innerValue = injector.parser.toPrintableString(surrounding.innerText!!) | ||||
|             val text = newSurround?.let { it.first + innerValue + it.second } ?: innerValue | ||||
|             val textData = PutData.TextData(text, SelectionType.CHARACTER_WISE, emptyList()) | ||||
|             val putData = PutData(textData, null, 1, insertTextBeforeCaret = !surrounding.isLineEnd, rawIndent = true, caretAfterInsertedText = false) | ||||
|  | ||||
|             surrounding.caret to putData | ||||
|           }.forEach { | ||||
|             injector.put.putTextForCaret(editor, it.first, context, it.second) | ||||
|           } | ||||
|  | ||||
|         surroundings.forEach { | ||||
|           it.restoreRegister() | ||||
|         } | ||||
|  | ||||
|         if (surroundings.size == 1) { | ||||
|           surroundings.first().moveCaret() | ||||
|         } | ||||
|         // Restore the old value | ||||
|         setRegister(REGISTER, oldValue) | ||||
|       } | ||||
|  | ||||
|       private fun perform(sequence: String, editor: Editor) { | ||||
| @@ -149,21 +190,6 @@ class VimSurroundExtension : VimExtension { | ||||
|           .use { executeNormalWithoutMapping(injector.parser.parseKeys("\"" + REGISTER + sequence), editor) } | ||||
|       } | ||||
|  | ||||
|       private fun pasteSurround( | ||||
|         innerValue: List<KeyStroke?>, | ||||
|         editor: Editor, | ||||
|       ) { // This logic is direct from vim-surround | ||||
|         val offset = editor.caretModel.offset | ||||
|         val lineEndOffset = EditorHelper.getLineEndForOffset(editor, offset) | ||||
|         val motionEndMark = VimPlugin.getMark().getMark(editor.vim, ']') | ||||
|         val motionEndOffset = if (motionEndMark != null) { | ||||
|           EditorHelper.getOffset(editor, motionEndMark.logicalLine, motionEndMark.col) | ||||
|         } else -1 | ||||
|         val pasteCommand = if (motionEndOffset == lineEndOffset && offset + 1 == lineEndOffset) "p" else "P" | ||||
|         setRegister(REGISTER, innerValue) | ||||
|         perform(pasteCommand, editor) | ||||
|       } | ||||
|  | ||||
|       private fun pick(charFrom: Char) = when (charFrom) { | ||||
|         'a' -> '>' | ||||
|         'r' -> ']' | ||||
| @@ -172,7 +198,19 @@ class VimSurroundExtension : VimExtension { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private class DSurroundHandler : VimExtensionHandler { | ||||
|   private data class SurroundingInfo(val caret: VimCaret, var innerText: List<KeyStroke>?, val oldRegisterContent: List<KeyStroke>?, var offset: Int?, var isLineEnd: Boolean = false) { | ||||
|     fun restoreRegister() { | ||||
|       setRegisterForCaret(REGISTER, caret, oldRegisterContent) | ||||
|     } | ||||
|  | ||||
|     fun moveCaret() { | ||||
|       if (innerText != null && offset != null) { | ||||
|         caret.moveToOffset(offset!! + if (isLineEnd) 1 else 0) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private class DSurroundHandler : ExtensionHandler { | ||||
|     override val isRepeatable = true | ||||
|  | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
| @@ -180,33 +218,52 @@ class VimSurroundExtension : VimExtension { | ||||
|       val charFrom = getChar(editor.ij) | ||||
|       if (charFrom.code == 0) return | ||||
|  | ||||
|       runWriteAction { CSurroundHandler.change(editor.ij, charFrom, null) } | ||||
|       runWriteAction { CSurroundHandler.change(editor, context, charFrom, null) } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private class Operator : OperatorFunction { | ||||
|     override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean { | ||||
|   private class Operator(private val supportsMultipleCursors: Boolean) : OperatorFunction { | ||||
|     override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean { | ||||
|       val editor = vimEditor.ij | ||||
|       val c = getChar(editor) | ||||
|       if (c.code == 0) return true | ||||
|  | ||||
|       val pair = getOrInputPair(c, editor) ?: return false | ||||
|       // XXX: Will it work with line-wise or block-wise selections? | ||||
|       val range = getSurroundRange(editor) ?: return false | ||||
|  | ||||
|       runWriteAction { | ||||
|         val change = VimPlugin.getChange() | ||||
|         val leftSurround = pair.first | ||||
|         val primaryCaret = editor.caretModel.primaryCaret | ||||
|         change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, leftSurround) | ||||
|         change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.endOffset + leftSurround.length, pair.second) | ||||
|         if (supportsMultipleCursors) { | ||||
|           editor.runWithEveryCaretAndRestore { | ||||
|             applyOnce(editor, change, pair) | ||||
|           } | ||||
|         } | ||||
|         else { | ||||
|           applyOnce(editor, change, pair) | ||||
|           // Jump back to start | ||||
|           executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor) | ||||
|         } | ||||
|       } | ||||
|       return true | ||||
|     } | ||||
|      | ||||
|     private fun getSurroundRange(editor: Editor): TextRange? = when (editor.mode) { | ||||
|       CommandState.Mode.COMMAND -> VimPlugin.getMark().getChangeMarks(editor.vim) | ||||
|       CommandState.Mode.VISUAL -> editor.caretModel.primaryCaret.run { TextRange(selectionStart, selectionEnd) } | ||||
|     private fun applyOnce(editor: Editor, change: VimChangeGroup, pair: Pair<String, String>) { | ||||
|       // XXX: Will it work with line-wise or block-wise selections? | ||||
|       val range = getSurroundRange(editor) | ||||
|       if (range != null) { | ||||
|         val primaryCaret = editor.caretModel.primaryCaret | ||||
|         change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, pair.first) | ||||
|         change.insertText( | ||||
|           IjVimEditor(editor), | ||||
|           IjVimCaret(primaryCaret), | ||||
|           range.endOffset + pair.first.length, | ||||
|           pair.second | ||||
|         ) | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     private fun getSurroundRange(editor: Editor): TextRange? = when (editor.editorMode) { | ||||
|       VimStateMachine.Mode.COMMAND -> VimPlugin.getMark().getChangeMarks(editor.vim) | ||||
|       VimStateMachine.Mode.VISUAL -> editor.caretModel.primaryCaret.run { TextRange(selectionStart, selectionEnd) } | ||||
|       else -> null | ||||
|     } | ||||
|   } | ||||
|   | ||||
| @@ -18,26 +18,22 @@ | ||||
|  | ||||
| package com.maddyhome.idea.vim.extension.textobjentire; | ||||
|  | ||||
| import com.intellij.openapi.actionSystem.DataContext; | ||||
| import com.intellij.openapi.editor.Caret; | ||||
| import com.intellij.openapi.editor.Editor; | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext; | ||||
| import com.maddyhome.idea.vim.api.VimCaret; | ||||
| import com.maddyhome.idea.vim.api.VimEditor; | ||||
| import com.maddyhome.idea.vim.api.VimInjectorKt; | ||||
| import com.maddyhome.idea.vim.command.*; | ||||
| import com.maddyhome.idea.vim.common.MappingMode; | ||||
| import com.maddyhome.idea.vim.command.MappingMode; | ||||
| import com.maddyhome.idea.vim.common.TextRange; | ||||
| import com.maddyhome.idea.vim.extension.VimExtension; | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionHandler; | ||||
| import com.maddyhome.idea.vim.extension.ExtensionHandler; | ||||
| import com.maddyhome.idea.vim.handler.TextObjectActionHandler; | ||||
| import com.maddyhome.idea.vim.helper.InlayHelperKt; | ||||
| import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor; | ||||
| import com.maddyhome.idea.vim.listener.VimListenerSuppressor; | ||||
| import com.maddyhome.idea.vim.newapi.IjExecutionContext; | ||||
| import com.maddyhome.idea.vim.newapi.IjVimCaret; | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor; | ||||
| import org.apache.tools.ant.taskdefs.Exec; | ||||
| import org.jetbrains.annotations.NotNull; | ||||
| import org.jetbrains.annotations.Nullable; | ||||
|  | ||||
| @@ -88,7 +84,7 @@ public class VimTextObjEntireExtension implements VimExtension { | ||||
|     putKeyMappingIfMissing(MappingMode.XO, VimInjectorKt.getInjector().getParser().parseKeys("ie"), getOwner(), VimInjectorKt.getInjector().getParser().parseKeys("<Plug>textobj-entire-i"), true); | ||||
|   } | ||||
|  | ||||
|   static class EntireHandler implements VimExtensionHandler { | ||||
|   static class EntireHandler implements ExtensionHandler { | ||||
|     final boolean ignoreLeadingAndTrailing; | ||||
|  | ||||
|     EntireHandler(boolean ignoreLeadingAndTrailing) { | ||||
| @@ -148,17 +144,17 @@ public class VimTextObjEntireExtension implements VimExtension { | ||||
|  | ||||
|     @Override | ||||
|     public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context) { | ||||
|       @NotNull CommandState commandState = CommandState.getInstance(editor); | ||||
|       int count = Math.max(1, commandState.getCommandBuilder().getCount()); | ||||
|       @NotNull VimStateMachine vimStateMachine = VimStateMachine.getInstance(editor); | ||||
|       int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount()); | ||||
|  | ||||
|       final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing); | ||||
|       //noinspection DuplicatedCode | ||||
|       if (!commandState.isOperatorPending()) { | ||||
|       if (!vimStateMachine.isOperatorPending()) { | ||||
|         ((IjVimEditor) editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> { | ||||
|           final TextRange range = textObjectHandler.getRange(editor, new IjVimCaret(caret), context, count, 0, null); | ||||
|           if (range != null) { | ||||
|             try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) { | ||||
|               if (commandState.getMode() == CommandState.Mode.VISUAL) { | ||||
|               if (vimStateMachine.getMode() == VimStateMachine.Mode.VISUAL) { | ||||
|                 vimSetSelection(caret, range.getStartOffset(), range.getEndOffset() - 1, true); | ||||
|               } else { | ||||
|                 InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset()); | ||||
| @@ -168,7 +164,7 @@ public class VimTextObjEntireExtension implements VimExtension { | ||||
|  | ||||
|         }); | ||||
|       } else { | ||||
|         commandState.getCommandBuilder().completeCommandPart(new Argument(new Command(count, | ||||
|         vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count, | ||||
|                                                                                          textObjectHandler, Command.Type.MOTION, | ||||
|                                                                                          EnumSet.noneOf(CommandFlags.class)))); | ||||
|       } | ||||
|   | ||||
| @@ -18,23 +18,20 @@ | ||||
|  | ||||
| package com.maddyhome.idea.vim.extension.textobjindent; | ||||
|  | ||||
| import com.intellij.openapi.actionSystem.DataContext; | ||||
| import com.intellij.openapi.editor.Caret; | ||||
| import com.intellij.openapi.editor.Editor; | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext; | ||||
| import com.maddyhome.idea.vim.api.VimCaret; | ||||
| import com.maddyhome.idea.vim.api.VimEditor; | ||||
| import com.maddyhome.idea.vim.api.VimInjectorKt; | ||||
| import com.maddyhome.idea.vim.command.*; | ||||
| import com.maddyhome.idea.vim.common.MappingMode; | ||||
| import com.maddyhome.idea.vim.command.MappingMode; | ||||
| import com.maddyhome.idea.vim.common.TextRange; | ||||
| import com.maddyhome.idea.vim.extension.VimExtension; | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionHandler; | ||||
| import com.maddyhome.idea.vim.extension.ExtensionHandler; | ||||
| import com.maddyhome.idea.vim.handler.TextObjectActionHandler; | ||||
| import com.maddyhome.idea.vim.helper.InlayHelperKt; | ||||
| import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor; | ||||
| import com.maddyhome.idea.vim.listener.VimListenerSuppressor; | ||||
| import com.maddyhome.idea.vim.newapi.IjExecutionContext; | ||||
| import com.maddyhome.idea.vim.newapi.IjVimCaret; | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor; | ||||
| import org.jetbrains.annotations.NotNull; | ||||
| @@ -86,7 +83,7 @@ public class VimIndentObject implements VimExtension { | ||||
|     putKeyMapping(MappingMode.XO, VimInjectorKt.getInjector().getParser().parseKeys("ii"), getOwner(), VimInjectorKt.getInjector().getParser().parseKeys("<Plug>textobj-indent-ii"), true); | ||||
|   } | ||||
|  | ||||
|   static class IndentObject implements VimExtensionHandler { | ||||
|   static class IndentObject implements ExtensionHandler { | ||||
|     final boolean includeAbove; | ||||
|     final boolean includeBelow; | ||||
|  | ||||
| @@ -275,17 +272,17 @@ public class VimIndentObject implements VimExtension { | ||||
|     @Override | ||||
|     public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context) { | ||||
|       IjVimEditor vimEditor = (IjVimEditor)editor; | ||||
|       @NotNull CommandState commandState = CommandState.getInstance(vimEditor); | ||||
|       int count = Math.max(1, commandState.getCommandBuilder().getCount()); | ||||
|       @NotNull VimStateMachine vimStateMachine = VimStateMachine.getInstance(vimEditor); | ||||
|       int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount()); | ||||
|  | ||||
|       final IndentObjectHandler textObjectHandler = new IndentObjectHandler(includeAbove, includeBelow); | ||||
|  | ||||
|       if (!commandState.isOperatorPending()) { | ||||
|       if (!vimStateMachine.isOperatorPending()) { | ||||
|         ((IjVimEditor)editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> { | ||||
|           final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, count, 0, null); | ||||
|           if (range != null) { | ||||
|             try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) { | ||||
|               if (commandState.getMode() == CommandState.Mode.VISUAL) { | ||||
|               if (vimStateMachine.getMode() == VimStateMachine.Mode.VISUAL) { | ||||
|                 vimSetSelection(caret, range.getStartOffset(), range.getEndOffset() - 1, true); | ||||
|               } else { | ||||
|                 InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset()); | ||||
| @@ -295,7 +292,7 @@ public class VimIndentObject implements VimExtension { | ||||
|  | ||||
|         }); | ||||
|       } else { | ||||
|         commandState.getCommandBuilder().completeCommandPart(new Argument(new Command(count, | ||||
|         vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count, | ||||
|                                                                                          textObjectHandler, Command.Type.MOTION, | ||||
|                                                                                          EnumSet.noneOf(CommandFlags.class)))); | ||||
|       } | ||||
|   | ||||
| @@ -20,7 +20,10 @@ package com.maddyhome.idea.vim.group; | ||||
| import com.google.common.base.Splitter; | ||||
| import com.google.common.collect.ImmutableSet; | ||||
| import com.google.common.collect.Lists; | ||||
| import com.intellij.codeInsight.actions.AsyncActionExecutionService; | ||||
| import com.intellij.openapi.Disposable; | ||||
| import com.intellij.openapi.actionSystem.DataContext; | ||||
| import com.intellij.openapi.actionSystem.IdeActions; | ||||
| import com.intellij.openapi.application.ApplicationManager; | ||||
| import com.intellij.openapi.command.CommandProcessor; | ||||
| import com.intellij.openapi.command.UndoConfirmationPolicy; | ||||
| @@ -33,13 +36,15 @@ import com.intellij.openapi.editor.event.EditorMouseEvent; | ||||
| import com.intellij.openapi.editor.event.EditorMouseListener; | ||||
| import com.intellij.openapi.editor.impl.TextRangeInterval; | ||||
| import com.intellij.openapi.project.Project; | ||||
| import com.intellij.openapi.ui.MessageType; | ||||
| import com.intellij.openapi.ui.popup.Balloon; | ||||
| import com.intellij.openapi.ui.popup.JBPopupFactory; | ||||
| import com.intellij.openapi.util.text.StringUtil; | ||||
| import com.intellij.psi.PsiFile; | ||||
| import com.intellij.psi.codeStyle.CodeStyleManager; | ||||
| import com.intellij.psi.util.PsiUtilBase; | ||||
| import com.intellij.util.containers.ContainerUtil; | ||||
| import com.maddyhome.idea.vim.EventFacade; | ||||
| import com.maddyhome.idea.vim.RegisterActions; | ||||
| import com.maddyhome.idea.vim.VimPlugin; | ||||
| import com.maddyhome.idea.vim.api.*; | ||||
| import com.maddyhome.idea.vim.command.*; | ||||
| @@ -49,13 +54,19 @@ import com.maddyhome.idea.vim.ex.ranges.LineRange; | ||||
| import com.maddyhome.idea.vim.group.visual.VimSelection; | ||||
| import com.maddyhome.idea.vim.group.visual.VisualModeHelperKt; | ||||
| import com.maddyhome.idea.vim.helper.*; | ||||
| import com.maddyhome.idea.vim.icons.VimIcons; | ||||
| import com.maddyhome.idea.vim.key.KeyHandlerKeeper; | ||||
| import com.maddyhome.idea.vim.listener.VimInsertListener; | ||||
| import com.maddyhome.idea.vim.newapi.*; | ||||
| import com.maddyhome.idea.vim.newapi.IjExecutionContext; | ||||
| import com.maddyhome.idea.vim.newapi.IjExecutionContextKt; | ||||
| import com.maddyhome.idea.vim.newapi.IjVimCaret; | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor; | ||||
| import com.maddyhome.idea.vim.options.OptionConstants; | ||||
| import com.maddyhome.idea.vim.options.OptionScope; | ||||
| import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString; | ||||
| import kotlin.Pair; | ||||
| import kotlin.Unit; | ||||
| import kotlin.jvm.functions.Function0; | ||||
| import kotlin.text.StringsKt; | ||||
| import org.jetbrains.annotations.NonNls; | ||||
| import org.jetbrains.annotations.NotNull; | ||||
| @@ -86,6 +97,8 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|  | ||||
|   private final List<VimInsertListener> insertListeners = ContainerUtil.createLockFreeCopyOnWriteList(); | ||||
|  | ||||
|   private long lastShownTime = 0L; | ||||
|  | ||||
|   /** | ||||
|    * Inserts a new line above the caret position | ||||
|    * | ||||
| @@ -107,7 +120,7 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|       injector.getMotion().moveCaret(editor, caret, VimPlugin.getMotion().moveCaretToLineEnd(editor, caret)); | ||||
|     } | ||||
|  | ||||
|     UserDataManager.setVimChangeActionSwitchMode(((IjVimEditor) editor).getEditor(), CommandState.Mode.INSERT); | ||||
|     UserDataManager.setVimChangeActionSwitchMode(((IjVimEditor) editor).getEditor(), VimStateMachine.Mode.INSERT); | ||||
|     insertText(editor, caret, "\n" + IndentConfig.create(((IjVimEditor) editor).getEditor()).createIndentBySize(col)); | ||||
|  | ||||
|     if (firstLiner) { | ||||
| @@ -123,10 +136,10 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|    * @param col    The column to indent to | ||||
|    */ | ||||
|   private void insertNewLineBelow(@NotNull VimEditor editor, @NotNull VimCaret caret, int col) { | ||||
|     if (((IjVimEditor) editor).getEditor().isOneLineMode()) return; | ||||
|     if (editor.isOneLineMode()) return; | ||||
|  | ||||
|     injector.getMotion().moveCaret(editor, caret, VimPlugin.getMotion().moveCaretToLineEnd(editor, caret)); | ||||
|     UserDataManager.setVimChangeActionSwitchMode(((IjVimEditor) editor).getEditor(), CommandState.Mode.INSERT); | ||||
|     caret.moveToOffset(injector.getMotion().moveCaretToLineEnd(editor, caret)); | ||||
|     editor.setVimChangeActionSwitchMode(VimStateMachine.Mode.INSERT); | ||||
|     insertText(editor, caret, "\n" + IndentConfig.create(((IjVimEditor) editor).getEditor()).createIndentBySize(col)); | ||||
|   } | ||||
|  | ||||
| @@ -143,14 +156,12 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   @Override | ||||
|   public void editorCreated(VimEditor editor) { | ||||
|     EventFacade.getInstance().addEditorMouseListener(((IjVimEditor) editor).getEditor(), listener); | ||||
|   public void editorCreated(Editor editor, @NotNull Disposable disposable) { | ||||
|     EventFacade.getInstance().addEditorMouseListener(editor, listener, disposable); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void editorReleased(VimEditor editor) { | ||||
|     EventFacade.getInstance().removeEditorMouseListener(((IjVimEditor) editor).getEditor(), listener); | ||||
|   public void editorReleased(Editor editor) { | ||||
|     EventFacade.getInstance().removeEditorMouseListener(editor, listener); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
| @@ -165,41 +176,6 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|   } | ||||
|  | ||||
|  | ||||
|   @Override | ||||
|   public @Nullable Pair<@NotNull TextRange, @NotNull SelectionType> getDeleteRangeAndType(@NotNull VimEditor editor, | ||||
|                                                                         @NotNull VimCaret caret, | ||||
|                                                                         @NotNull ExecutionContext context, | ||||
|                                                                         final @NotNull Argument argument, | ||||
|                                                                         boolean isChange, | ||||
|                                                                         @NotNull OperatorArguments operatorArguments) { | ||||
|     final TextRange range = | ||||
|       injector.getMotion().getMotionRange(editor, caret, context, argument, operatorArguments); | ||||
|     if (range == null) return null; | ||||
|  | ||||
|     // Delete motion commands that are not linewise become linewise if all the following are true: | ||||
|     // 1) The range is across multiple lines | ||||
|     // 2) There is only whitespace before the start of the range | ||||
|     // 3) There is only whitespace after the end of the range | ||||
|     SelectionType type; | ||||
|     if (argument.getMotion().isLinewiseMotion()) { | ||||
|       type = SelectionType.LINE_WISE; | ||||
|     } | ||||
|     else { | ||||
|       type = SelectionType.CHARACTER_WISE; | ||||
|     } | ||||
|     final Command motion = argument.getMotion(); | ||||
|     if (!isChange && !motion.isLinewiseMotion()) { | ||||
|       VimLogicalPosition start = editor.offsetToLogicalPosition(range.getStartOffset()); | ||||
|       VimLogicalPosition end = editor.offsetToLogicalPosition(range.getEndOffset()); | ||||
|       if (start.getLine() != end.getLine()) { | ||||
|         if (!SearchHelper.anyNonWhitespace(((IjVimEditor) editor).getEditor(), range.getStartOffset(), -1) && | ||||
|             !SearchHelper.anyNonWhitespace(((IjVimEditor) editor).getEditor(), range.getEndOffset(), 1)) { | ||||
|           type = SelectionType.LINE_WISE; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return new Pair<>(range, type); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public @Nullable Pair<@NotNull TextRange, @NotNull SelectionType> getDeleteRangeAndType2(@NotNull VimEditor editor, | ||||
| @@ -236,60 +212,6 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|     return new Pair<>(range, type); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Delete the range of text. | ||||
|    * | ||||
|    * @param editor   The editor to delete the text from | ||||
|    * @param caret    The caret to be moved after deletion | ||||
|    * @param range    The range to delete | ||||
|    * @param type     The type of deletion | ||||
|    * @param isChange Is from a change action | ||||
|    * @return true if able to delete the text, false if not | ||||
|    */ | ||||
|   @Override | ||||
|   public boolean deleteRange(@NotNull VimEditor editor, | ||||
|                              @NotNull VimCaret caret, | ||||
|                              @NotNull TextRange range, | ||||
|                              @Nullable SelectionType type, | ||||
|                              boolean isChange) { | ||||
|  | ||||
|     // Update the last column before we delete, or we might be retrieving the data for a line that no longer exists | ||||
|     UserDataManager.setVimLastColumn(((IjVimCaret) caret).getCaret(), InlayHelperKt.getInlayAwareVisualColumn(((IjVimCaret) caret).getCaret())); | ||||
|  | ||||
|     boolean removeLastNewLine = removeLastNewLine(editor, range, type); | ||||
|     final boolean res = deleteText(editor, range, type); | ||||
|     if (removeLastNewLine) { | ||||
|       int textLength = ((IjVimEditor) editor).getEditor().getDocument().getTextLength(); | ||||
|       ((IjVimEditor) editor).getEditor().getDocument().deleteString(textLength - 1, textLength); | ||||
|     } | ||||
|  | ||||
|     if (res) { | ||||
|       int pos = EditorHelper.normalizeOffset(((IjVimEditor) editor).getEditor(), range.getStartOffset(), isChange); | ||||
|       if (type == SelectionType.LINE_WISE) { | ||||
|         pos = VimPlugin.getMotion() | ||||
|           .moveCaretToLineWithStartOfLineOption(editor, editor.offsetToLogicalPosition(pos).getLine(), | ||||
|                                                 caret); | ||||
|       } | ||||
|       injector.getMotion().moveCaret(editor, caret, pos); | ||||
|     } | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   private boolean removeLastNewLine(@NotNull VimEditor editor, @NotNull TextRange range, @Nullable SelectionType type) { | ||||
|     int endOffset = range.getEndOffset(); | ||||
|     int fileSize = EditorHelperRt.getFileSize(((IjVimEditor) editor).getEditor()); | ||||
|     if (endOffset > fileSize) { | ||||
|       if (injector.getOptionService().isSet(OptionScope.GLOBAL.INSTANCE, OptionConstants.ideastrictmodeName, OptionConstants.ideastrictmodeName)) { | ||||
|         throw new IllegalStateException("Incorrect offset. File size: " + fileSize + ", offset: " + endOffset); | ||||
|       } | ||||
|       endOffset = fileSize; | ||||
|     } | ||||
|     return type == SelectionType.LINE_WISE && | ||||
|            range.getStartOffset() != 0 && | ||||
|            ((IjVimEditor) editor).getEditor().getDocument().getCharsSequence().charAt(endOffset - 1) != '\n' && | ||||
|            endOffset == fileSize; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void insertLineAround(@NotNull VimEditor editor, @NotNull ExecutionContext context, int shift) { | ||||
|     com.maddyhome.idea.vim.newapi.ChangeGroupKt.insertLineAround(editor, context, shift); | ||||
| @@ -303,48 +225,6 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|     return com.maddyhome.idea.vim.newapi.ChangeGroupKt.deleteRange(editor, caret, range, type); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Delete count characters and then enter insert mode | ||||
|    * | ||||
|    * @param editor The editor to change | ||||
|    * @param caret  The caret to be moved | ||||
|    * @param count  The number of characters to change | ||||
|    * @return true if able to delete count characters, false if not | ||||
|    */ | ||||
|   @Override | ||||
|   public boolean changeCharacters(@NotNull VimEditor editor, @NotNull VimCaret caret, int count) { | ||||
|     int len = EditorHelper.getLineLength(((IjVimEditor) editor).getEditor()); | ||||
|     int col = ((IjVimCaret) caret).getCaret().getLogicalPosition().column; | ||||
|     if (col + count >= len) { | ||||
|       return changeEndOfLine(editor, caret, 1); | ||||
|     } | ||||
|  | ||||
|     boolean res = deleteCharacter(editor, caret, count, true); | ||||
|     if (res) { | ||||
|       UserDataManager.setVimChangeActionSwitchMode(((IjVimEditor) editor).getEditor(), CommandState.Mode.INSERT); | ||||
|     } | ||||
|  | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Delete from the cursor to the end of count - 1 lines down and enter insert mode | ||||
|    * | ||||
|    * @param editor The editor to change | ||||
|    * @param caret  The caret to perform action on | ||||
|    * @param count  The number of lines to change | ||||
|    * @return true if able to delete count lines, false if not | ||||
|    */ | ||||
|   @Override | ||||
|   public boolean changeEndOfLine(@NotNull VimEditor editor, @NotNull VimCaret caret, int count) { | ||||
|     boolean res = deleteEndOfLine(editor, caret, count); | ||||
|     if (res) { | ||||
|       injector.getMotion().moveCaret(editor, caret, VimPlugin.getMotion().moveCaretToLineEnd(editor, caret)); | ||||
|       UserDataManager.setVimChangeActionSwitchMode(((IjVimEditor) editor).getEditor(), CommandState.Mode.INSERT); | ||||
|     } | ||||
|  | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Delete the text covered by the motion command argument and enter insert mode | ||||
| @@ -368,35 +248,35 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|     String id = motion.getAction().getId(); | ||||
|     boolean kludge = false; | ||||
|     boolean bigWord = id.equals(VIM_MOTION_BIG_WORD_RIGHT); | ||||
|     final CharSequence chars = ((IjVimEditor) editor).getEditor().getDocument().getCharsSequence(); | ||||
|     final int offset = ((IjVimCaret) caret).getCaret().getOffset(); | ||||
|     int fileSize = EditorHelperRt.getFileSize(((IjVimEditor) editor).getEditor()); | ||||
|     final CharSequence chars = editor.text(); | ||||
|     final int offset = caret.getOffset().getPoint(); | ||||
|     int fileSize = ((int)editor.fileSize()); | ||||
|     if (fileSize > 0 && offset < fileSize) { | ||||
|       final CharacterHelper.CharacterType charType = CharacterHelper.charType(chars.charAt(offset), bigWord); | ||||
|       if (charType != CharacterHelper.CharacterType.WHITESPACE) { | ||||
|         final boolean lastWordChar = | ||||
|           offset >= fileSize - 1 || CharacterHelper.charType(chars.charAt(offset + 1), bigWord) != charType; | ||||
|         if (wordMotions.contains(id) && lastWordChar && motion.getCount() == 1) { | ||||
|           final boolean res = deleteCharacter(editor, caret, 1, true); | ||||
|           final boolean res = deleteCharacter(editor, caret, 1, true, operatorArguments); | ||||
|           if (res) { | ||||
|             UserDataManager.setVimChangeActionSwitchMode(((IjVimEditor) editor).getEditor(), CommandState.Mode.INSERT); | ||||
|             editor.setVimChangeActionSwitchMode(VimStateMachine.Mode.INSERT); | ||||
|           } | ||||
|           return res; | ||||
|         } | ||||
|         switch (id) { | ||||
|           case VIM_MOTION_WORD_RIGHT: | ||||
|             kludge = true; | ||||
|             motion.setAction(RegisterActions.findActionOrDie(VIM_MOTION_WORD_END_RIGHT)); | ||||
|             motion.setAction(injector.getActionExecutor().findVimActionOrDie(VIM_MOTION_WORD_END_RIGHT)); | ||||
|  | ||||
|             break; | ||||
|           case VIM_MOTION_BIG_WORD_RIGHT: | ||||
|             kludge = true; | ||||
|             motion.setAction(RegisterActions.findActionOrDie(VIM_MOTION_BIG_WORD_END_RIGHT)); | ||||
|             motion.setAction(injector.getActionExecutor().findVimActionOrDie(VIM_MOTION_BIG_WORD_END_RIGHT)); | ||||
|  | ||||
|             break; | ||||
|           case VIM_MOTION_CAMEL_RIGHT: | ||||
|             kludge = true; | ||||
|             motion.setAction(RegisterActions.findActionOrDie(VIM_MOTION_CAMEL_END_RIGHT)); | ||||
|             motion.setAction(injector.getActionExecutor().findVimActionOrDie(VIM_MOTION_CAMEL_END_RIGHT)); | ||||
|  | ||||
|             break; | ||||
|         } | ||||
| @@ -405,8 +285,8 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|  | ||||
|     if (kludge) { | ||||
|       int cnt = operatorArguments.getCount1() * motion.getCount(); | ||||
|       int pos1 = SearchHelper.findNextWordEnd(chars, offset, fileSize, cnt, bigWord, false); | ||||
|       int pos2 = SearchHelper.findNextWordEnd(chars, pos1, fileSize, -cnt, bigWord, false); | ||||
|       int pos1 = injector.getSearchHelper().findNextWordEnd(chars, offset, fileSize, cnt, bigWord, false); | ||||
|       int pos2 = injector.getSearchHelper().findNextWordEnd(chars, pos1, fileSize, -cnt, bigWord, false); | ||||
|       if (logger.isDebugEnabled()) { | ||||
|         logger.debug("pos=" + offset); | ||||
|         logger.debug("pos1=" + pos1); | ||||
| @@ -427,38 +307,22 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (VimPlugin.getOptionService().isSet(OptionScope.GLOBAL.INSTANCE, OptionConstants.experimentalapiName, OptionConstants.experimentalapiName)) { | ||||
|     if (injector.getOptionService().isSet(OptionScope.GLOBAL.INSTANCE, OptionConstants.experimentalapiName, OptionConstants.experimentalapiName)) { | ||||
|       Pair<TextRange, SelectionType> deleteRangeAndType = | ||||
|         getDeleteRangeAndType2(editor, caret, context, argument, true, operatorArguments.withCount0(count0)); | ||||
|       if (deleteRangeAndType == null) return false; | ||||
|       ChangeGroupKt.changeRange(((IjVimEditor) editor).getEditor(), ((IjVimCaret) caret).getCaret(), deleteRangeAndType.getFirst(), deleteRangeAndType.getSecond(), ((IjExecutionContext) context).getContext()); | ||||
|       //ChangeGroupKt.changeRange(((IjVimEditor) editor).getEditor(), ((IjVimCaret) caret).getCaret(), deleteRangeAndType.getFirst(), deleteRangeAndType.getSecond(), ((IjExecutionContext) context).getContext()); | ||||
|       return true; | ||||
|     } | ||||
|     else { | ||||
|       Pair<TextRange, SelectionType> deleteRangeAndType = | ||||
|         getDeleteRangeAndType(editor, caret, context, argument, true, operatorArguments.withCount0(count0)); | ||||
|       if (deleteRangeAndType == null) return false; | ||||
|       return changeRange(editor, caret, deleteRangeAndType.getFirst(), deleteRangeAndType.getSecond(), context); | ||||
|       return changeRange(editor, caret, deleteRangeAndType.getFirst(), deleteRangeAndType.getSecond(), context, | ||||
|                          operatorArguments); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Counts number of lines in the visual block. | ||||
|    * <p> | ||||
|    * The result includes empty and short lines which does not have explicit start position (caret). | ||||
|    * | ||||
|    * @param editor The editor the block was selected in | ||||
|    * @param range  The range corresponding to the selected block | ||||
|    * @return total number of lines | ||||
|    */ | ||||
|   public static int getLinesCountInVisualBlock(@NotNull VimEditor editor, @NotNull TextRange range) { | ||||
|     final int[] startOffsets = range.getStartOffsets(); | ||||
|     if (startOffsets.length == 0) return 0; | ||||
|     final VimLogicalPosition firstStart = editor.offsetToLogicalPosition(startOffsets[0]); | ||||
|     final VimLogicalPosition lastStart = editor.offsetToLogicalPosition(startOffsets[range.size() - 1]); | ||||
|     return lastStart.getLine() - firstStart.getLine() + 1; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Toggles the case of count characters | ||||
|    * | ||||
| @@ -484,11 +348,11 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|                              @NotNull TextRange range, | ||||
|                              boolean append, | ||||
|                              @NotNull OperatorArguments operatorArguments) { | ||||
|     final int lines = getLinesCountInVisualBlock(editor, range); | ||||
|     final int lines = VimChangeGroupBase.Companion.getLinesCountInVisualBlock(editor, range); | ||||
|     final VimLogicalPosition startPosition = editor.offsetToLogicalPosition(range.getStartOffset()); | ||||
|  | ||||
|     boolean visualBlockMode = operatorArguments.getMode() == CommandState.Mode.VISUAL && | ||||
|                               operatorArguments.getSubMode() == CommandState.SubMode.VISUAL_BLOCK; | ||||
|     boolean visualBlockMode = operatorArguments.getMode() == VimStateMachine.Mode.VISUAL && | ||||
|                               operatorArguments.getSubMode() == VimStateMachine.SubMode.VISUAL_BLOCK; | ||||
|     for (Caret caret : ((IjVimEditor) editor).getEditor().getCaretModel().getAllCarets()) { | ||||
|       final int line = startPosition.getLine(); | ||||
|       int column = startPosition.getColumn(); | ||||
| @@ -578,6 +442,7 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|    * @param caret             The caret to be moved after range deletion | ||||
|    * @param range             The range to change | ||||
|    * @param type              The type of the range | ||||
|    * @param operatorArguments | ||||
|    * @return true if able to delete the range, false if not | ||||
|    */ | ||||
|   @Override | ||||
| @@ -585,28 +450,29 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|                              @NotNull VimCaret caret, | ||||
|                              @NotNull TextRange range, | ||||
|                              @NotNull SelectionType type, | ||||
|                              ExecutionContext context) { | ||||
|                              @Nullable ExecutionContext context, | ||||
|                              @NotNull OperatorArguments operatorArguments) { | ||||
|     int col = 0; | ||||
|     int lines = 0; | ||||
|     if (type == SelectionType.BLOCK_WISE) { | ||||
|       lines = getLinesCountInVisualBlock(editor, range); | ||||
|       lines = VimChangeGroupBase.Companion.getLinesCountInVisualBlock(editor, range); | ||||
|       col = editor.offsetToLogicalPosition(range.getStartOffset()).getColumn(); | ||||
|       if (UserDataManager.getVimLastColumn(((IjVimCaret) caret).getCaret()) == VimMotionGroupBase.LAST_COLUMN) { | ||||
|       if (caret.getVimLastColumn() == VimMotionGroupBase.LAST_COLUMN) { | ||||
|         col = VimMotionGroupBase.LAST_COLUMN; | ||||
|       } | ||||
|     } | ||||
|     boolean after = range.getEndOffset() >= EditorHelperRt.getFileSize(((IjVimEditor) editor).getEditor()); | ||||
|     boolean after = range.getEndOffset() >= editor.fileSize(); | ||||
|  | ||||
|     final VimLogicalPosition lp = editor.offsetToLogicalPosition(VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, caret)); | ||||
|     final VimLogicalPosition lp = editor.offsetToLogicalPosition(injector.getMotion().moveCaretToLineStartSkipLeading(editor, caret)); | ||||
|  | ||||
|     boolean res = deleteRange(editor, caret, range, type, true); | ||||
|     boolean res = deleteRange(editor, caret, range, type, true, operatorArguments); | ||||
|     if (res) { | ||||
|       if (type == SelectionType.LINE_WISE) { | ||||
|         // Please don't use `getDocument().getText().isEmpty()` because it converts CharSequence into String | ||||
|         if (((IjVimEditor) editor).getEditor().getDocument().getTextLength() == 0) { | ||||
|         if (editor.fileSize() == 0) { | ||||
|           insertBeforeCursor(editor, context); | ||||
|         } | ||||
|         else if (after && !EditorHelperRt.endsWithNewLine(((IjVimEditor) editor).getEditor())) { | ||||
|         else if (after && !EngineEditorHelperKt.endsWithNewLine(editor)) { | ||||
|           insertNewLineBelow(editor, caret, lp.getColumn()); | ||||
|         } | ||||
|         else { | ||||
| @@ -617,7 +483,7 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|         if (type == SelectionType.BLOCK_WISE) { | ||||
|           setInsertRepeat(lines, col, false); | ||||
|         } | ||||
|         UserDataManager.setVimChangeActionSwitchMode(((IjVimEditor) editor).getEditor(), CommandState.Mode.INSERT); | ||||
|         UserDataManager.setVimChangeActionSwitchMode(((IjVimEditor) editor).getEditor(), VimStateMachine.Mode.INSERT); | ||||
|       } | ||||
|     } | ||||
|     else { | ||||
| @@ -717,17 +583,31 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|     final int startOffset = injector.getEngineEditorHelper().getLineStartForOffset(editor, range.getStartOffset()); | ||||
|     final int endOffset = injector.getEngineEditorHelper().getLineEndForOffset(editor, range.getEndOffset()); | ||||
|  | ||||
|     VisualModeHelperKt.vimSetSystemSelectionSilently(((IjVimEditor) editor).getEditor().getSelectionModel(), startOffset, endOffset); | ||||
|     Editor ijEditor = ((IjVimEditor)editor).getEditor(); | ||||
|     VisualModeHelperKt.vimSetSystemSelectionSilently(ijEditor.getSelectionModel(), startOffset, endOffset); | ||||
|  | ||||
|     Project project = ijEditor.getProject(); | ||||
|     Function0<Unit> actionExecution = () -> { | ||||
|       NativeAction joinLinesAction = VimInjectorKt.getInjector().getNativeActionManager().getIndentLines(); | ||||
|       if (joinLinesAction != null) { | ||||
|         VimInjectorKt.getInjector().getActionExecutor().executeAction(joinLinesAction, context); | ||||
|       } | ||||
|  | ||||
|       return null; | ||||
|     }; | ||||
|     Function0<Unit> afterAction = () -> { | ||||
|       final int firstLine = editor.offsetToLogicalPosition(Math.min(startOffset, endOffset)).getLine(); | ||||
|       final int newOffset = VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, firstLine); | ||||
|       injector.getMotion().moveCaret(editor, caret, newOffset); | ||||
|     restoreCursor(editor, caret, ((IjVimCaret) caret).getCaret().getLogicalPosition().line); | ||||
|       restoreCursor(editor, caret, ((IjVimCaret)caret).getCaret().getLogicalPosition().line); | ||||
|       return null; | ||||
|     }; | ||||
|     if (project != null) { | ||||
|       AsyncActionExecutionService.Companion.getInstance(project) | ||||
|         .withExecutionAfterAction(IdeActions.ACTION_EDITOR_AUTO_INDENT_LINES, actionExecution, afterAction); | ||||
|     } else { | ||||
|       actionExecution.invoke(); | ||||
|       afterAction.invoke(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
| @@ -735,10 +615,11 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|                           @NotNull VimCaret caret, | ||||
|                           @NotNull ExecutionContext context, | ||||
|                           int lines, | ||||
|                           int dir) { | ||||
|                           int dir, | ||||
|                           @NotNull OperatorArguments operatorArguments) { | ||||
|     int start = ((IjVimCaret) caret).getCaret().getOffset(); | ||||
|     int end = VimPlugin.getMotion().moveCaretToLineEndOffset(editor, caret, lines - 1, true); | ||||
|     indentRange(editor, caret, context, new TextRange(start, end), 1, dir); | ||||
|     indentRange(editor, caret, context, new TextRange(start, end), 1, dir, operatorArguments); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
| @@ -751,7 +632,7 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|     final TextRange range = | ||||
|       injector.getMotion().getMotionRange(editor, caret, context, argument, operatorArguments); | ||||
|     if (range != null) { | ||||
|       indentRange(editor, caret, context, range, 1, dir); | ||||
|       indentRange(editor, caret, context, range, 1, dir, operatorArguments); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -778,7 +659,8 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|                           @NotNull ExecutionContext context, | ||||
|                           @NotNull TextRange range, | ||||
|                           int count, | ||||
|                           int dir) { | ||||
|                           int dir, | ||||
|                           @NotNull OperatorArguments operatorArguments) { | ||||
|     if (logger.isDebugEnabled()) { | ||||
|       logger.debug("count=" + count); | ||||
|     } | ||||
| @@ -824,7 +706,7 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|               } | ||||
|             } | ||||
|             if (pos > wsoff) { | ||||
|               deleteText(editor, new TextRange(wsoff, pos), null); | ||||
|               deleteText(editor, new TextRange(wsoff, pos), null, caret, operatorArguments); | ||||
|             } | ||||
|           } | ||||
|         } | ||||
| @@ -917,6 +799,23 @@ public class ChangeGroup extends VimChangeGroupBase { | ||||
|                                         @NotNull TextRange selectedRange, | ||||
|                                         final int count, | ||||
|                                         boolean avalanche) { | ||||
|  | ||||
|     // Just an easter egg | ||||
|     if (avalanche) { | ||||
|       long currentTime = System.currentTimeMillis(); | ||||
|       if (currentTime - lastShownTime > 60_000) { | ||||
|         lastShownTime = currentTime; | ||||
|         ApplicationManager.getApplication().invokeLater(() -> { | ||||
|           final Balloon balloon = JBPopupFactory.getInstance() | ||||
|             .createHtmlTextBalloonBuilder("Wow, nice vim skills!", VimIcons.IDEAVIM, | ||||
|                                           MessageType.INFO.getTitleForeground(), MessageType.INFO.getPopupBackground(), | ||||
|                                           null).createBalloon(); | ||||
|           balloon.show(JBPopupFactory.getInstance().guessBestPopupLocation(((IjVimEditor)editor).getEditor()), | ||||
|                        Balloon.Position.below); | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     String nf = ((VimString) VimPlugin.getOptionService().getOptionValue(new OptionScope.LOCAL(editor), OptionConstants.nrformatsName, OptionConstants.nrformatsName)).getValue(); | ||||
|     boolean alpha = nf.contains("alpha"); | ||||
|     boolean hex = nf.contains("hex"); | ||||
|   | ||||
| @@ -232,7 +232,7 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor | ||||
|       KeyHandler.getInstance().reset(new IjVimEditor(editor)); | ||||
|     } | ||||
|     updateCaretsVisualAttributes(editor); | ||||
|     editor.getSettings().setRefrainFromScrolling(REFRAIN_FROM_SCROLLING_VIM_VALUE); | ||||
|     //editor.getSettings().setRefrainFromScrolling(REFRAIN_FROM_SCROLLING_VIM_VALUE); | ||||
|   } | ||||
|  | ||||
|   public void editorDeinit(@NotNull Editor editor, boolean isReleased) { | ||||
|   | ||||
| @@ -20,6 +20,7 @@ package com.maddyhome.idea.vim.group; | ||||
|  | ||||
| import com.intellij.openapi.actionSystem.DataContext; | ||||
| import com.intellij.openapi.actionSystem.PlatformDataKeys; | ||||
| import com.intellij.openapi.application.ApplicationManager; | ||||
| import com.intellij.openapi.diagnostic.Logger; | ||||
| import com.intellij.openapi.editor.Document; | ||||
| import com.intellij.openapi.editor.Editor; | ||||
| @@ -27,6 +28,7 @@ import com.intellij.openapi.editor.LogicalPosition; | ||||
| import com.intellij.openapi.fileEditor.*; | ||||
| import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; | ||||
| import com.intellij.openapi.fileEditor.impl.EditorWindow; | ||||
| import com.intellij.openapi.fileEditor.impl.EditorsSplitters; | ||||
| import com.intellij.openapi.fileTypes.FileType; | ||||
| import com.intellij.openapi.fileTypes.FileTypeManager; | ||||
| import com.intellij.openapi.project.Project; | ||||
| @@ -38,8 +40,7 @@ import com.intellij.openapi.vfs.VirtualFile; | ||||
| import com.intellij.openapi.vfs.VirtualFileVisitor; | ||||
| import com.maddyhome.idea.vim.VimPlugin; | ||||
| import com.maddyhome.idea.vim.api.*; | ||||
| import com.maddyhome.idea.vim.command.CommandState; | ||||
| import com.maddyhome.idea.vim.common.GoalCommand; | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine; | ||||
| import com.maddyhome.idea.vim.common.TextRange; | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper; | ||||
| import com.maddyhome.idea.vim.helper.EditorHelperRt; | ||||
| @@ -48,7 +49,6 @@ import com.maddyhome.idea.vim.helper.SearchHelper; | ||||
| import com.maddyhome.idea.vim.newapi.ExecuteExtensionKt; | ||||
| import com.maddyhome.idea.vim.newapi.IjExecutionContext; | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor; | ||||
| import com.maddyhome.idea.vim.options.OptionConstants; | ||||
| import com.maddyhome.idea.vim.options.OptionScope; | ||||
| import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString; | ||||
| import com.maddyhome.idea.vim.vimscript.services.IjVimOptionService; | ||||
| @@ -161,6 +161,10 @@ public class FileGroup extends VimFileBase { | ||||
|       if (virtualFile != null && window != null) { | ||||
|         window.closeFile(virtualFile); | ||||
|       } | ||||
|       if (!ApplicationManager.getApplication().isUnitTestMode()) { | ||||
|         // This thing doesn't have an implementation in test mode | ||||
|         EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -177,6 +181,10 @@ public class FileGroup extends VimFileBase { | ||||
|     if (number >= 0 && number < editors.length) { | ||||
|       fileEditorManager.closeFile(editors[number], window); | ||||
|     } | ||||
|     if (!ApplicationManager.getApplication().isUnitTestMode()) { | ||||
|       // This thing doesn't have an implementation in test mode | ||||
|       EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -290,7 +298,7 @@ public class FileGroup extends VimFileBase { | ||||
|     StringBuilder msg = new StringBuilder(); | ||||
|     Document doc = editor.getDocument(); | ||||
|  | ||||
|     if (CommandState.getInstance(new IjVimEditor(editor)).getMode() != CommandState.Mode.VISUAL) { | ||||
|     if (VimStateMachine.getInstance(new IjVimEditor(editor)).getMode() != VimStateMachine.Mode.VISUAL) { | ||||
|       LogicalPosition lp = editor.getCaretModel().getLogicalPosition(); | ||||
|       int col = editor.getCaretModel().getOffset() - doc.getLineStartOffset(lp.line); | ||||
|       int endoff = doc.getLineEndOffset(lp.line); | ||||
|   | ||||
| @@ -36,6 +36,7 @@ class IjVimStorageService : VimStorageServiceBase() { | ||||
|     editor.ij.putUserData(getOrCreateIjKey(key), data) | ||||
|   } | ||||
|  | ||||
|   @Suppress("UNCHECKED_CAST") | ||||
|   override fun <T> getDataFromBuffer(editor: VimEditor, key: com.maddyhome.idea.vim.api.Key<T>): T? { | ||||
|     val buffer = EditorHelper.getVirtualFile(editor.ij)?.path ?: "empty path" | ||||
|     return bufferToKey[buffer]?.get(key.name) as T? | ||||
| @@ -60,6 +61,7 @@ class IjVimStorageService : VimStorageServiceBase() { | ||||
|   } | ||||
|  | ||||
|   private val ijKeys = mutableMapOf<String, Key<out Any?>>() | ||||
|   @Suppress("UNCHECKED_CAST") | ||||
|   private fun <T> getOrCreateIjKey(key: com.maddyhome.idea.vim.api.Key<T>): Key<T> { | ||||
|     val storedIjKey = ijKeys[key.name] | ||||
|     if (storedIjKey != null) { | ||||
|   | ||||
| @@ -37,9 +37,8 @@ import com.maddyhome.idea.vim.VimPlugin; | ||||
| import com.maddyhome.idea.vim.action.ComplicatedKeysAction; | ||||
| import com.maddyhome.idea.vim.action.VimShortcutKeyAction; | ||||
| import com.maddyhome.idea.vim.api.*; | ||||
| import com.maddyhome.idea.vim.common.MappingMode; | ||||
| import com.maddyhome.idea.vim.common.Node; | ||||
| import com.maddyhome.idea.vim.common.NodesKt; | ||||
| import com.maddyhome.idea.vim.command.MappingMode; | ||||
| import com.maddyhome.idea.vim.key.Node; | ||||
| import com.maddyhome.idea.vim.ex.ExOutputModel; | ||||
| import com.maddyhome.idea.vim.handler.EditorActionHandlerBase; | ||||
| import com.maddyhome.idea.vim.helper.HelperKt; | ||||
| @@ -74,8 +73,6 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen | ||||
|  | ||||
|   private static final Logger logger = Logger.getInstance(KeyGroup.class); | ||||
|  | ||||
|   private @Nullable OperatorFunction operatorFunction = null; | ||||
|  | ||||
|   public void registerRequiredShortcutKeys(@NotNull VimEditor editor) { | ||||
|     EventFacade.getInstance() | ||||
|       .registerCustomShortcutSet(VimShortcutKeyAction.getInstance(), toShortcutSet(getRequiredShortcutKeys()), | ||||
| @@ -120,14 +117,6 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   public @Nullable OperatorFunction getOperatorFunction() { | ||||
|     return operatorFunction; | ||||
|   } | ||||
|  | ||||
|   public void setOperatorFunction(@NotNull OperatorFunction function) { | ||||
|     operatorFunction = function; | ||||
|   } | ||||
|  | ||||
|   public void saveData(@NotNull Element element) { | ||||
|     final Element conflictsElement = new Element(SHORTCUT_CONFLICTS_ELEMENT); | ||||
|     for (Map.Entry<KeyStroke, ShortcutOwnerInfo> entry : myShortcutConflicts.entrySet()) { | ||||
|   | ||||
| @@ -108,7 +108,9 @@ public class MacroGroup extends VimMacroBase { | ||||
|               KeyHandler.getInstance().handleKey(editor, key, context); | ||||
|             }); | ||||
|           } | ||||
|           keyStack.resetFirst(); | ||||
|         } | ||||
|         keyStack.removeFirst(); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|   | ||||
| @@ -44,7 +44,6 @@ import com.maddyhome.idea.vim.helper.EditorHelper; | ||||
| import com.maddyhome.idea.vim.helper.HelperKt; | ||||
| import com.maddyhome.idea.vim.mark.*; | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor; | ||||
| import com.maddyhome.idea.vim.options.OptionConstants; | ||||
| import com.maddyhome.idea.vim.options.OptionScope; | ||||
| import com.maddyhome.idea.vim.vimscript.services.IjVimOptionService; | ||||
| import org.jdom.Element; | ||||
| @@ -60,8 +59,7 @@ import static com.maddyhome.idea.vim.mark.VimMarkConstants.SAVE_FILE_MARKS; | ||||
|  * This class contains all the mark related functionality | ||||
|  */ | ||||
| @State(name = "VimMarksSettings", storages = { | ||||
|   @Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED) | ||||
| }) | ||||
|   @Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)}) | ||||
| public class MarkGroup extends VimMarkGroupBase implements PersistentStateComponent<Element> { | ||||
|   public void editorReleased(@NotNull EditorFactoryEvent event) { | ||||
|     // Save off the last caret position of the file before it is closed | ||||
| @@ -127,7 +125,8 @@ public class MarkGroup extends VimMarkGroupBase implements PersistentStateCompon | ||||
|  | ||||
|   public void saveData(@NotNull Element element) { | ||||
|     Element marksElem = new Element("globalmarks"); | ||||
|     if (!VimPlugin.getOptionService().isSet(OptionScope.GLOBAL.INSTANCE, IjVimOptionService.ideamarksName, IjVimOptionService.ideamarksName)) { | ||||
|     if (!VimPlugin.getOptionService() | ||||
|       .isSet(OptionScope.GLOBAL.INSTANCE, IjVimOptionService.ideamarksName, IjVimOptionService.ideamarksName)) { | ||||
|       for (Mark mark : globalMarks.values()) { | ||||
|         if (!mark.isClear()) { | ||||
|           Element markElem = new Element("mark"); | ||||
| @@ -165,8 +164,7 @@ public class MarkGroup extends VimMarkGroupBase implements PersistentStateCompon | ||||
|         fileMarkElem.setAttribute("name", file); | ||||
|         fileMarkElem.setAttribute("timestamp", Long.toString(marks.getMyTimestamp().getTime())); | ||||
|         for (Mark mark : marks.values()) { | ||||
|           if (!mark.isClear() && !Character.isUpperCase(mark.getKey()) && | ||||
|               SAVE_FILE_MARKS.indexOf(mark.getKey()) >= 0) { | ||||
|           if (!mark.isClear() && !Character.isUpperCase(mark.getKey()) && SAVE_FILE_MARKS.indexOf(mark.getKey()) >= 0) { | ||||
|             Element markElem = new Element("mark"); | ||||
|             markElem.setAttribute("key", Character.toString(mark.getKey())); | ||||
|             markElem.setAttribute("line", Integer.toString(mark.getLogicalLine())); | ||||
| @@ -200,14 +198,15 @@ public class MarkGroup extends VimMarkGroupBase implements PersistentStateCompon | ||||
|     // (see com.intellij.openapi.application.Application.runReadAction()) | ||||
|  | ||||
|     Element marksElem = element.getChild("globalmarks"); | ||||
|     if (marksElem != null && !VimPlugin.getOptionService().isSet(OptionScope.GLOBAL.INSTANCE, IjVimOptionService.ideamarksName, IjVimOptionService.ideamarksName)) { | ||||
|     if (marksElem != null && | ||||
|         !VimPlugin.getOptionService() | ||||
|           .isSet(OptionScope.GLOBAL.INSTANCE, IjVimOptionService.ideamarksName, IjVimOptionService.ideamarksName)) { | ||||
|       List<Element> markList = marksElem.getChildren("mark"); | ||||
|       for (Element aMarkList : markList) { | ||||
|         Mark mark = VimMark.create(aMarkList.getAttributeValue("key").charAt(0), | ||||
|                                    Integer.parseInt(aMarkList.getAttributeValue("line")), | ||||
|                                    Integer.parseInt(aMarkList.getAttributeValue("column")), | ||||
|                                    aMarkList.getAttributeValue("filename"), | ||||
|                                    aMarkList.getAttributeValue("protocol")); | ||||
|                                    aMarkList.getAttributeValue("filename"), aMarkList.getAttributeValue("protocol")); | ||||
|  | ||||
|         if (mark != null) { | ||||
|           globalMarks.put(mark.getKey(), mark); | ||||
| @@ -239,8 +238,7 @@ public class MarkGroup extends VimMarkGroupBase implements PersistentStateCompon | ||||
|         for (Element aMarkList : markList) { | ||||
|           Mark mark = VimMark.create(aMarkList.getAttributeValue("key").charAt(0), | ||||
|                                      Integer.parseInt(aMarkList.getAttributeValue("line")), | ||||
|                                      Integer.parseInt(aMarkList.getAttributeValue("column")), | ||||
|                                      filename, | ||||
|                                      Integer.parseInt(aMarkList.getAttributeValue("column")), filename, | ||||
|                                      aMarkList.getAttributeValue("protocol")); | ||||
|  | ||||
|           if (mark != null) fmarks.put(mark.getKey(), mark); | ||||
| @@ -290,6 +288,7 @@ public class MarkGroup extends VimMarkGroupBase implements PersistentStateCompon | ||||
|   public static class MarkUpdater implements DocumentListener { | ||||
|  | ||||
|     public static MarkUpdater INSTANCE = new MarkUpdater(); | ||||
|  | ||||
|     /** | ||||
|      * Creates the listener for the supplied editor | ||||
|      */ | ||||
| @@ -313,8 +312,7 @@ public class MarkGroup extends VimMarkGroupBase implements PersistentStateCompon | ||||
|       Editor anEditor = getAnEditor(doc); | ||||
|       VimInjectorKt.getInjector().getMarkGroup() | ||||
|         .updateMarkFromDelete(anEditor == null ? null : new IjVimEditor(anEditor), | ||||
|                               VimPlugin.getMark().getAllFileMarks(doc), | ||||
|                               event.getOffset(), event.getOldLength()); | ||||
|                               VimPlugin.getMark().getAllFileMarks(doc), event.getOffset(), event.getOldLength()); | ||||
|       // TODO - update jumps | ||||
|     } | ||||
|  | ||||
| @@ -361,7 +359,10 @@ public class MarkGroup extends VimMarkGroupBase implements PersistentStateCompon | ||||
|     @Override | ||||
|     public void bookmarkAdded(@NotNull BookmarkGroup group, com.intellij.ide.bookmark.@NotNull Bookmark bookmark) { | ||||
|       if (!VimPlugin.isEnabled()) return; | ||||
|       if (!VimPlugin.getOptionService().isSet(OptionScope.GLOBAL.INSTANCE, IjVimOptionService.ideamarksName, IjVimOptionService.ideamarksName)) return; | ||||
|       if (!VimPlugin.getOptionService() | ||||
|         .isSet(OptionScope.GLOBAL.INSTANCE, IjVimOptionService.ideamarksName, IjVimOptionService.ideamarksName)) { | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       if (!(bookmark instanceof LineBookmark)) return; | ||||
|       BookmarksManager bookmarksManager = BookmarksManager.getInstance(myProject); | ||||
| @@ -378,7 +379,10 @@ public class MarkGroup extends VimMarkGroupBase implements PersistentStateCompon | ||||
|     @Override | ||||
|     public void bookmarkRemoved(@NotNull BookmarkGroup group, com.intellij.ide.bookmark.@NotNull Bookmark bookmark) { | ||||
|       if (!VimPlugin.isEnabled()) return; | ||||
|       if (!VimPlugin.getOptionService().isSet(OptionScope.GLOBAL.INSTANCE, IjVimOptionService.ideamarksName, IjVimOptionService.ideamarksName)) return; | ||||
|       if (!VimPlugin.getOptionService() | ||||
|         .isSet(OptionScope.GLOBAL.INSTANCE, IjVimOptionService.ideamarksName, IjVimOptionService.ideamarksName)) { | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       if (!(bookmark instanceof LineBookmark)) return; | ||||
|       BookmarksManager bookmarksManager = BookmarksManager.getInstance(myProject); | ||||
| @@ -387,7 +391,8 @@ public class MarkGroup extends VimMarkGroupBase implements PersistentStateCompon | ||||
|       if (type == null) return; | ||||
|       char ch = type.getMnemonic(); | ||||
|       if (GLOBAL_MARKS.indexOf(ch) != -1) { | ||||
|         FileMarks<Character, Mark> fmarks = VimPlugin.getMark().getFileMarks(((LineBookmark)bookmark).getFile().getPath()); | ||||
|         FileMarks<Character, Mark> fmarks = | ||||
|           VimPlugin.getMark().getFileMarks(((LineBookmark)bookmark).getFile().getPath()); | ||||
|         fmarks.remove(ch); | ||||
|         VimPlugin.getMark().globalMarks.remove(ch); | ||||
|       } | ||||
| @@ -404,6 +409,17 @@ public class MarkGroup extends VimMarkGroupBase implements PersistentStateCompon | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * COMPATIBILITY-LAYER: Method added | ||||
|    * Please see: <a href="https://jb.gg/zo8n0r">doc</a> | ||||
|    * | ||||
|    * @deprecated Please use method with VimEditor | ||||
|    */ | ||||
|   @Deprecated | ||||
|   public void saveJumpLocation(Editor editor) { | ||||
|     this.saveJumpLocation(new IjVimEditor(editor)); | ||||
|   } | ||||
|  | ||||
|   private static final int SAVE_MARK_COUNT = 20; | ||||
|  | ||||
|   private static final Logger logger = Logger.getInstance(MarkGroup.class.getName()); | ||||
|   | ||||
| @@ -23,8 +23,10 @@ import com.intellij.openapi.editor.ex.util.EditorUtil; | ||||
| import com.intellij.openapi.fileEditor.FileEditor; | ||||
| import com.intellij.openapi.fileEditor.FileEditorManagerEvent; | ||||
| import com.intellij.openapi.fileEditor.TextEditor; | ||||
| import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; | ||||
| import com.intellij.openapi.fileEditor.impl.EditorTabbedContainer; | ||||
| import com.intellij.openapi.fileEditor.impl.EditorWindow; | ||||
| import com.intellij.openapi.project.Project; | ||||
| import com.intellij.openapi.vfs.LocalFileSystem; | ||||
| import com.intellij.openapi.vfs.VirtualFile; | ||||
| import com.intellij.openapi.vfs.VirtualFileManager; | ||||
| @@ -711,7 +713,7 @@ public class MotionGroup extends VimMotionGroupBase { | ||||
|   } | ||||
|  | ||||
|   private static int getScrollJump(@NotNull Editor editor, int height) { | ||||
|     final EnumSet<CommandFlags> flags = CommandState.getInstance(new IjVimEditor(editor)).getExecutingCommandFlags(); | ||||
|     final EnumSet<CommandFlags> flags = VimStateMachine.getInstance(new IjVimEditor(editor)).getExecutingCommandFlags(); | ||||
|     final boolean scrollJump = !flags.contains(CommandFlags.FLAG_IGNORE_SCROLL_JUMP); | ||||
|  | ||||
|     // Default value is 1. Zero is a valid value, but we normalise to 1 - we always want to scroll at least one line | ||||
| @@ -736,7 +738,7 @@ public class MotionGroup extends VimMotionGroupBase { | ||||
|     final int halfWidth = getApproximateScreenWidth(editor) / 2; | ||||
|     final int scrollOffset = getNormalizedSideScrollOffset(editor); | ||||
|  | ||||
|     final EnumSet<CommandFlags> flags = CommandState.getInstance(new IjVimEditor(editor)).getExecutingCommandFlags(); | ||||
|     final EnumSet<CommandFlags> flags = VimStateMachine.getInstance(new IjVimEditor(editor)).getExecutingCommandFlags(); | ||||
|     final boolean allowSidescroll = !flags.contains(CommandFlags.FLAG_IGNORE_SIDE_SCROLL_JUMP); | ||||
|     int sidescroll = ((VimInt) VimPlugin.getOptionService().getOptionValue(new OptionScope.LOCAL(new IjVimEditor(editor)), OptionConstants.sidescrollName, OptionConstants.sidescrollName)).getValue(); | ||||
|  | ||||
| @@ -1168,15 +1170,25 @@ public class MotionGroup extends VimMotionGroupBase { | ||||
|  | ||||
|   @Override | ||||
|   public int moveCaretGotoPreviousTab(@NotNull VimEditor editor, @NotNull ExecutionContext context, int rawCount) { | ||||
|     switchEditorTab(EditorWindow.DATA_KEY.getData((DataContext)context.getContext()), rawCount >= 1 ? -rawCount : -1, false); | ||||
|     Project project = ((IjVimEditor)editor).getEditor().getProject(); | ||||
|     if (project == null) { | ||||
|       return editor.currentCaret().getOffset().getPoint(); | ||||
|     } | ||||
|     EditorWindow currentWindow = FileEditorManagerEx.getInstanceEx(project).getSplitters().getCurrentWindow(); | ||||
|     switchEditorTab(currentWindow, rawCount >= 1 ? -rawCount : -1, false); | ||||
|     return editor.currentCaret().getOffset().getPoint(); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public int moveCaretGotoNextTab(@NotNull VimEditor editor, @NotNull ExecutionContext context, int rawCount) { | ||||
|     final boolean absolute = rawCount >= 1; | ||||
|     switchEditorTab(EditorWindow.DATA_KEY.getData((DataContext)context.getContext()), absolute ? rawCount - 1 : 1, | ||||
|                     absolute); | ||||
|  | ||||
|     Project project = ((IjVimEditor)editor).getEditor().getProject(); | ||||
|     if (project == null) { | ||||
|       return editor.currentCaret().getOffset().getPoint(); | ||||
|     } | ||||
|     EditorWindow currentWindow = FileEditorManagerEx.getInstanceEx(project).getSplitters().getCurrentWindow(); | ||||
|     switchEditorTab(currentWindow, absolute ? rawCount - 1 : 1, absolute); | ||||
|     return editor.currentCaret().getOffset().getPoint(); | ||||
|   } | ||||
|  | ||||
| @@ -1200,7 +1212,7 @@ public class MotionGroup extends VimMotionGroupBase { | ||||
|     if (fileEditor instanceof TextEditor) { | ||||
|       final Editor editor = ((TextEditor)fileEditor).getEditor(); | ||||
|       ExOutputModel.getInstance(editor).clear(); | ||||
|       if (CommandState.getInstance(new IjVimEditor(editor)).getMode() == CommandState.Mode.VISUAL) { | ||||
|       if (VimStateMachine.getInstance(new IjVimEditor(editor)).getMode() == VimStateMachine.Mode.VISUAL) { | ||||
|         ModeHelper.exitVisualMode(editor); | ||||
|         KeyHandler.getInstance().reset(new IjVimEditor(editor)); | ||||
|       } | ||||
|   | ||||
| @@ -20,26 +20,24 @@ package com.maddyhome.idea.vim.group | ||||
|  | ||||
| import com.intellij.icons.AllIcons | ||||
| import com.intellij.ide.BrowserUtil | ||||
| import com.intellij.ide.IdeBundle | ||||
| import com.intellij.ide.actions.OpenFileAction | ||||
| import com.intellij.ide.actions.RevealFileAction | ||||
| import com.intellij.notification.ActionCenter | ||||
| import com.intellij.notification.Notification | ||||
| import com.intellij.notification.NotificationGroup | ||||
| import com.intellij.notification.NotificationGroupManager | ||||
| import com.intellij.notification.NotificationListener | ||||
| import com.intellij.notification.NotificationType | ||||
| import com.intellij.openapi.actionSystem.ActionUpdateThread | ||||
| import com.intellij.openapi.actionSystem.AnAction | ||||
| import com.intellij.openapi.actionSystem.AnActionEvent | ||||
| import com.intellij.openapi.actionSystem.KeyboardShortcut | ||||
| import com.intellij.openapi.ide.CopyPasteManager | ||||
| import com.intellij.openapi.keymap.Keymap | ||||
| import com.intellij.openapi.keymap.KeymapUtil | ||||
| import com.intellij.openapi.options.ShowSettingsUtil | ||||
| import com.intellij.openapi.project.DumbAwareAction | ||||
| import com.intellij.openapi.project.Project | ||||
| import com.intellij.openapi.ui.Messages | ||||
| import com.intellij.openapi.util.SystemInfo | ||||
| import com.intellij.openapi.util.registry.Registry | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.helper.MessageHelper | ||||
| import com.maddyhome.idea.vim.key.ShortcutOwner | ||||
| @@ -50,7 +48,6 @@ import com.maddyhome.idea.vim.statistic.ActionTracker | ||||
| import com.maddyhome.idea.vim.ui.VimEmulationConfigurable | ||||
| import com.maddyhome.idea.vim.vimscript.services.IjVimOptionService | ||||
| import com.maddyhome.idea.vim.vimscript.services.VimRcService | ||||
| import org.jetbrains.annotations.Nls | ||||
| import java.awt.datatransfer.StringSelection | ||||
| import java.io.File | ||||
| import javax.swing.KeyStroke | ||||
| @@ -115,23 +112,6 @@ class NotificationService(private val project: Project?) { | ||||
|     Messages.getQuestionIcon() | ||||
|   ) | ||||
|  | ||||
|   fun specialKeymap(keymap: Keymap, listener: NotificationListener.Adapter) { | ||||
|     val notification = IDEAVIM_STICKY_GROUP.createNotification( | ||||
|       IDEAVIM_NOTIFICATION_TITLE, | ||||
|       "IdeaVim plugin doesn't use the special \"Vim\" keymap any longer. " + | ||||
|         "Switching to \"${keymap.presentableName}\" keymap.<br/><br/>" + | ||||
|         "Now it is possible to set up:<br/>" + | ||||
|         "<ul>" + | ||||
|         "<li>Vim keys in your ~/.ideavimrc file using key mapping commands</li>" + | ||||
|         "<li>IDE action shortcuts in \"File | Settings | Keymap\"</li>" + | ||||
|         "<li>Vim or IDE handlers for conflicting shortcuts in <a href='#settings'>Vim Emulation</a> settings</li>" + | ||||
|         "</ul>", | ||||
|       NotificationType.INFORMATION | ||||
|     ) | ||||
|     notification.setListener(listener) | ||||
|     notification.notify(project) | ||||
|   } | ||||
|  | ||||
|   fun noVimrcAsDefault() { | ||||
|     val notification = IDEAVIM_STICKY_GROUP.createNotification( | ||||
|       IDEAVIM_NOTIFICATION_TITLE, | ||||
| @@ -208,7 +188,7 @@ class NotificationService(private val project: Project?) { | ||||
|       Notification(IDEAVIM_NOTIFICATION_ID, IDEAVIM_NOTIFICATION_TITLE, content, NotificationType.INFORMATION).let { | ||||
|         notification = it | ||||
|         it.whenExpired { notification = null } | ||||
|         it.setContent(it.content + "<br><br><small>Use ${getToolwindowName()} to see previous ids</small>") | ||||
|         it.setContent(it.content + "<br><br><small>Use ${ActionCenter.getToolwindowName()} to see previous ids</small>") | ||||
|  | ||||
|         it.addAction(StopTracking()) | ||||
|  | ||||
| @@ -222,15 +202,6 @@ class NotificationService(private val project: Project?) { | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // [VERSION UPDATE] 221+ Use ActionCenter.getToolWindowName | ||||
|     private fun getToolwindowName(): @Nls String { | ||||
|       return IdeBundle.message(if (isEnabled()) "toolwindow.stripe.Notifications" else "toolwindow.stripe.Event_Log") | ||||
|     } | ||||
|  | ||||
|     private fun isEnabled(): Boolean { | ||||
|       return Registry.`is`("ide.notification.action.center", true) | ||||
|     } | ||||
|  | ||||
|     class CopyActionId(val id: String?, val project: Project?) : DumbAwareAction(MessageHelper.message("action.copy.action.id.text")) { | ||||
|       override fun actionPerformed(e: AnActionEvent) { | ||||
|         CopyPasteManager.getInstance().setContents(StringSelection(id ?: "")) | ||||
| @@ -251,6 +222,8 @@ class NotificationService(private val project: Project?) { | ||||
|       override fun update(e: AnActionEvent) { | ||||
|         e.presentation.isEnabled = id != null | ||||
|       } | ||||
|  | ||||
|       override fun getActionUpdateThread() = ActionUpdateThread.BGT | ||||
|     } | ||||
|  | ||||
|     class StopTracking : DumbAwareAction("Stop Tracking") { | ||||
| @@ -287,6 +260,8 @@ class NotificationService(private val project: Project?) { | ||||
|       val actionText = if (VimRcService.findIdeaVimRc() != null) "Open ~/.ideavimrc" else "Create ~/.ideavimrc" | ||||
|       e.presentation.text = actionText | ||||
|     } | ||||
|  | ||||
|     override fun getActionUpdateThread() = ActionUpdateThread.BGT | ||||
|   } | ||||
|  | ||||
|   @Suppress("DialogTitleCapitalization") | ||||
|   | ||||
| @@ -37,7 +37,7 @@ import com.maddyhome.idea.vim.KeyHandler; | ||||
| import com.maddyhome.idea.vim.VimPlugin; | ||||
| import com.maddyhome.idea.vim.api.*; | ||||
| import com.maddyhome.idea.vim.command.Command; | ||||
| import com.maddyhome.idea.vim.command.CommandState; | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine; | ||||
| import com.maddyhome.idea.vim.ex.ExException; | ||||
| import com.maddyhome.idea.vim.ex.InvalidCommandException; | ||||
| import com.maddyhome.idea.vim.helper.UiHelper; | ||||
| @@ -90,9 +90,9 @@ public class ProcessGroup extends VimProcessGroupBase { | ||||
|     if (editor.isOneLineMode()) return; | ||||
|  | ||||
|     String initText = getRange(((IjVimEditor) editor).getEditor(), cmd); | ||||
|     CommandState.getInstance(editor).pushModes(CommandState.Mode.CMD_LINE, CommandState.SubMode.NONE); | ||||
|     VimStateMachine.getInstance(editor).pushModes(VimStateMachine.Mode.CMD_LINE, VimStateMachine.SubMode.NONE); | ||||
|     ExEntryPanel panel = ExEntryPanel.getInstance(); | ||||
|     panel.activate(((IjVimEditor) editor).getEditor(), ((IjExecutionContext) context).getContext(), ":", initText, 1); | ||||
|     panel.activate(((IjVimEditor) editor).getEditor(), ((IjExecutionContext) context).getContext(), ":", initText, cmd.getCount()); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
| @@ -108,7 +108,7 @@ public class ProcessGroup extends VimProcessGroupBase { | ||||
|       return true; | ||||
|     } | ||||
|     else { | ||||
|       CommandState.getInstance(editor).popModes(); | ||||
|       VimStateMachine.getInstance(editor).popModes(); | ||||
|       KeyHandler.getInstance().reset(editor); | ||||
|       return false; | ||||
|     } | ||||
| @@ -119,11 +119,11 @@ public class ProcessGroup extends VimProcessGroupBase { | ||||
|     panel.deactivate(true); | ||||
|     boolean res = true; | ||||
|     try { | ||||
|       CommandState.getInstance(editor).popModes(); | ||||
|       VimStateMachine.getInstance(editor).popModes(); | ||||
|  | ||||
|       logger.debug("processing command"); | ||||
|  | ||||
|       final String text = panel.getText(); | ||||
|       String text = panel.getText(); | ||||
|  | ||||
|       if (!panel.getLabel().equals(":")) { | ||||
|         // Search is handled via Argument.Type.EX_STRING. Although ProcessExEntryAction is registered as the handler for | ||||
| @@ -134,8 +134,16 @@ public class ProcessGroup extends VimProcessGroupBase { | ||||
|  | ||||
|       if (logger.isDebugEnabled()) logger.debug("swing=" + SwingUtilities.isEventDispatchThread()); | ||||
|  | ||||
|       int repeat = 1; | ||||
|       if (text.contains("raction ")) { | ||||
|         text = text.replace("raction ", "action "); | ||||
|         repeat = panel.getCount(); | ||||
|       } | ||||
|  | ||||
|       for (int i = 0; i < repeat; i++) { | ||||
|         VimInjectorKt.getInjector().getVimscriptExecutor().execute(text, editor, context, skipHistory(editor), true, CommandLineVimLContext.INSTANCE); | ||||
|       } | ||||
|     } | ||||
|     catch (ExException e) { | ||||
|       VimPlugin.showMessage(e.getMessage()); | ||||
|       VimPlugin.indicateError(); | ||||
| @@ -152,11 +160,11 @@ public class ProcessGroup extends VimProcessGroupBase { | ||||
|  | ||||
|   // commands executed from map command / macro should not be added to history | ||||
|   private boolean skipHistory(VimEditor editor) { | ||||
|     return CommandState.getInstance(editor).getMappingState().isExecutingMap() || injector.getMacro().isExecutingMacro(); | ||||
|     return VimStateMachine.getInstance(editor).getMappingState().isExecutingMap() || injector.getMacro().isExecutingMacro(); | ||||
|   } | ||||
|  | ||||
|   public void cancelExEntry(final @NotNull VimEditor editor, boolean resetCaret) { | ||||
|     CommandState.getInstance(editor).popModes(); | ||||
|     VimStateMachine.getInstance(editor).popModes(); | ||||
|     KeyHandler.getInstance().reset(editor); | ||||
|     ExEntryPanel panel = ExEntryPanel.getInstance(); | ||||
|     panel.deactivate(true, resetCaret); | ||||
| @@ -165,14 +173,14 @@ public class ProcessGroup extends VimProcessGroupBase { | ||||
|   @Override | ||||
|   public void startFilterCommand(@NotNull VimEditor editor, ExecutionContext context, @NotNull Command cmd) { | ||||
|     String initText = getRange(((IjVimEditor) editor).getEditor(), cmd) + "!"; | ||||
|     CommandState.getInstance(editor).pushModes(CommandState.Mode.CMD_LINE, CommandState.SubMode.NONE); | ||||
|     VimStateMachine.getInstance(editor).pushModes(VimStateMachine.Mode.CMD_LINE, VimStateMachine.SubMode.NONE); | ||||
|     ExEntryPanel panel = ExEntryPanel.getInstance(); | ||||
|     panel.activate(((IjVimEditor) editor).getEditor(), ((IjExecutionContext) context).getContext(), ":", initText, 1); | ||||
|   } | ||||
|  | ||||
|   private @NotNull String getRange(Editor editor, @NotNull Command cmd) { | ||||
|     String initText = ""; | ||||
|     if (CommandState.getInstance(new IjVimEditor(editor)).getMode() == CommandState.Mode.VISUAL) { | ||||
|     if (VimStateMachine.getInstance(new IjVimEditor(editor)).getMode() == VimStateMachine.Mode.VISUAL) { | ||||
|       initText = "'<,'>"; | ||||
|     } | ||||
|     else if (cmd.getRawCount() > 0) { | ||||
|   | ||||
| @@ -890,6 +890,10 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo | ||||
|   @Override | ||||
|   public void setLastSearchPattern(@Nullable String lastSearchPattern) { | ||||
|     this.lastSearch = lastSearchPattern; | ||||
|     if (showSearchHighlight) { | ||||
|       resetIncsearchHighlights(); | ||||
|       updateSearchHighlights(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   | ||||
| @@ -61,7 +61,10 @@ internal fun Project.createLineBookmark(editor: Editor, line: Int, mnemonic: Cha | ||||
|   val type = BookmarkType.get(mnemonic) | ||||
|   if (type == BookmarkType.DEFAULT) return null | ||||
|  | ||||
|   val group = bookmarksManager.defaultGroup ?: bookmarksManager.addGroup("IdeaVim", true) ?: return null | ||||
|   val group = bookmarksManager.defaultGroup | ||||
|     ?: bookmarksManager.getGroup("IdeaVim") | ||||
|     ?: bookmarksManager.addGroup("IdeaVim", true) | ||||
|     ?: return null | ||||
|   if (group.canAdd(bookmark)) { | ||||
|     group.add(bookmark, type) | ||||
|     return bookmark | ||||
|   | ||||
| @@ -21,8 +21,8 @@ package com.maddyhome.idea.vim.group; | ||||
| import com.intellij.openapi.actionSystem.DataContext; | ||||
| import com.intellij.openapi.actionSystem.PlatformDataKeys; | ||||
| import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; | ||||
| import com.intellij.openapi.fileEditor.impl.EditorComposite; | ||||
| import com.intellij.openapi.fileEditor.impl.EditorWindow; | ||||
| import com.intellij.openapi.fileEditor.impl.EditorWithProviderComposite; | ||||
| import com.intellij.openapi.project.Project; | ||||
| import com.intellij.openapi.vfs.VirtualFile; | ||||
| import com.intellij.util.concurrency.annotations.RequiresReadLock; | ||||
| @@ -183,7 +183,7 @@ public class WindowGroup extends WindowGroupBase { | ||||
|   } | ||||
|  | ||||
|   private static @Nullable Rectangle getEditorWindowRectangle(@NotNull EditorWindow window) { | ||||
|     final EditorWithProviderComposite editor = window.getSelectedEditor(); | ||||
|     final EditorComposite editor = window.getSelectedComposite(); | ||||
|     if (editor != null) { | ||||
|       final Point point = editor.getComponent().getLocationOnScreen(); | ||||
|       final Dimension dimension = editor.getComponent().getSize(); | ||||
|   | ||||
| @@ -34,8 +34,9 @@ import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimCaret | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.CommandState | ||||
| import com.maddyhome.idea.vim.command.OperatorArguments | ||||
| import com.maddyhome.idea.vim.command.SelectionType | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.command.isBlock | ||||
| import com.maddyhome.idea.vim.command.isChar | ||||
| import com.maddyhome.idea.vim.command.isLine | ||||
| @@ -43,7 +44,9 @@ import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper | ||||
| import com.maddyhome.idea.vim.helper.TestClipboardModel | ||||
| import com.maddyhome.idea.vim.helper.fileSize | ||||
| import com.maddyhome.idea.vim.helper.mode | ||||
| import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset | ||||
| import com.maddyhome.idea.vim.helper.subMode | ||||
| import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_POS | ||||
| import com.maddyhome.idea.vim.newapi.IjVimCaret | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||
| @@ -60,10 +63,20 @@ import java.awt.datatransfer.DataFlavor | ||||
| import kotlin.math.min | ||||
|  | ||||
| class PutGroup : VimPutBase() { | ||||
|   override fun putTextForCaret(editor: VimEditor, caret: VimCaret, context: ExecutionContext, data: PutData): Boolean { | ||||
|   override fun putTextForCaret(editor: VimEditor, caret: VimCaret, context: ExecutionContext, data: PutData, updateVisualMarks: Boolean): Boolean { | ||||
|     val additionalData = collectPreModificationData(editor, data) | ||||
|     data.visualSelection?.let { | ||||
|       deleteSelectedText( | ||||
|         editor, | ||||
|         data, | ||||
|         OperatorArguments(false, 0, editor.mode, editor.subMode) | ||||
|       ) | ||||
|     } | ||||
|     val processedText = processText(editor, data) ?: return false | ||||
|     putForCaret(editor, caret, data, additionalData, context, processedText) | ||||
|     if (editor.primaryCaret() == caret && updateVisualMarks) { | ||||
|       wrapInsertedTextWithVisualMarks(editor, data, processedText) | ||||
|     } | ||||
|     return true | ||||
|   } | ||||
|  | ||||
| @@ -75,7 +88,7 @@ class PutGroup : VimPutBase() { | ||||
|     additionalData: Map<String, Any>, | ||||
|   ) { | ||||
|     val visualSelection = data.visualSelection | ||||
|     val subMode = visualSelection?.typeInEditor?.toSubMode() ?: CommandState.SubMode.NONE | ||||
|     val subMode = visualSelection?.typeInEditor?.toSubMode() ?: VimStateMachine.SubMode.NONE | ||||
|     if (OptionConstants.clipboard_ideaput in ( | ||||
|       injector.optionService | ||||
|         .getOptionValue(OptionScope.GLOBAL, OptionConstants.clipboardName) as VimString | ||||
| @@ -89,7 +102,6 @@ class PutGroup : VimPutBase() { | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     notifyAboutIdeaPut(editor) | ||||
|     logger.debug("Perform put via plugin") | ||||
|     val myCarets = if (visualSelection != null) { | ||||
|       visualSelection.caretsAndSelections.keys.sortedByDescending { it.getLogicalPosition() } | ||||
| @@ -109,16 +121,19 @@ class PutGroup : VimPutBase() { | ||||
|     context: ExecutionContext, | ||||
|     text: ProcessedTextData, | ||||
|   ) { | ||||
|     notifyAboutIdeaPut(editor) | ||||
|     if (data.visualSelection?.typeInEditor?.isLine == true && editor.isOneLineMode()) return | ||||
|     val startOffsets = prepareDocumentAndGetStartOffsets(editor, caret, text.typeInRegister, data, additionalData) | ||||
|  | ||||
|     startOffsets.forEach { startOffset -> | ||||
|       val subMode = data.visualSelection?.typeInEditor?.toSubMode() ?: CommandState.SubMode.NONE | ||||
|       val subMode = data.visualSelection?.typeInEditor?.toSubMode() ?: VimStateMachine.SubMode.NONE | ||||
|       val endOffset = putTextInternal( | ||||
|         editor, caret, context, text.text, text.typeInRegister, subMode, | ||||
|         startOffset, data.count, data.indent, data.caretAfterInsertedText | ||||
|       ) | ||||
|       if (caret == editor.primaryCaret()) { | ||||
|         VimPlugin.getMark().setChangeMarks(editor, TextRange(startOffset, endOffset)) | ||||
|       } | ||||
|       moveCaretToEndPosition( | ||||
|         editor, | ||||
|         caret, | ||||
| @@ -175,7 +190,12 @@ class PutGroup : VimPutBase() { | ||||
|           } | ||||
|         } | ||||
|         visualSelection.typeInEditor.isLine -> { | ||||
|           if (caret.offset == editor.fileSize && editor.fileSize != 0) { | ||||
|           val lastChar = if (editor.fileSize > 0) { | ||||
|             editor.document.getText(com.intellij.openapi.util.TextRange(editor.fileSize - 1, editor.fileSize))[0] | ||||
|           } else { | ||||
|             null | ||||
|           } | ||||
|           if (caret.offset == editor.fileSize && editor.fileSize != 0 && lastChar != '\n') { | ||||
|             application.runWriteAction { editor.document.insertString(caret.offset, "\n") } | ||||
|             listOf(caret.offset + 1) | ||||
|           } else listOf(caret.offset) | ||||
| @@ -233,7 +253,7 @@ class PutGroup : VimPutBase() { | ||||
|     vimEditor: VimEditor, | ||||
|     vimContext: ExecutionContext, | ||||
|     text: ProcessedTextData, | ||||
|     subMode: CommandState.SubMode, | ||||
|     subMode: VimStateMachine.SubMode, | ||||
|     data: PutData, | ||||
|     additionalData: Map<String, Any>, | ||||
|   ) { | ||||
|   | ||||
| @@ -18,23 +18,20 @@ | ||||
|  | ||||
| package com.maddyhome.idea.vim.group.copy | ||||
|  | ||||
| import com.intellij.openapi.editor.Caret | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.intellij.util.containers.ContainerUtil | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.action.motion.updown.MotionDownLess1FirstNonSpaceAction | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimCaret | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.Argument | ||||
| import com.maddyhome.idea.vim.command.OperatorArguments | ||||
| import com.maddyhome.idea.vim.command.SelectionType | ||||
| import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.group.MotionGroup | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper | ||||
| import com.maddyhome.idea.vim.helper.fileSize | ||||
| import com.maddyhome.idea.vim.listener.VimYankListener | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.yank.YankGroupBase | ||||
| import org.jetbrains.annotations.Contract | ||||
| import kotlin.math.min | ||||
| @@ -46,8 +43,8 @@ class YankGroup : YankGroupBase() { | ||||
|  | ||||
|   fun removeListener(listener: VimYankListener) = yankListeners.remove(listener) | ||||
|  | ||||
|   private fun notifyListeners(editor: Editor, textRange: TextRange) = yankListeners.forEach { | ||||
|     it.yankPerformed(editor, textRange) | ||||
|   private fun notifyListeners(editor: VimEditor, textRange: TextRange) = yankListeners.forEach { | ||||
|     it.yankPerformed(editor.ij, textRange) | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -67,32 +64,38 @@ class YankGroup : YankGroupBase() { | ||||
|     operatorArguments: OperatorArguments | ||||
|   ): Boolean { | ||||
|     val motion = argument.motion | ||||
|     val type = if (motion.isLinewiseMotion()) SelectionType.LINE_WISE else SelectionType.CHARACTER_WISE | ||||
|  | ||||
|     val caretModel = editor.ij.caretModel | ||||
|     if (caretModel.caretCount <= 0) return false | ||||
|     val nativeCaretCount = editor.nativeCarets().size | ||||
|     if (nativeCaretCount <= 0) return false | ||||
|  | ||||
|     val ranges = ArrayList<Pair<Int, Int>>(caretModel.caretCount) | ||||
|     val carretToRange = HashMap<VimCaret, TextRange>(nativeCaretCount) | ||||
|     val ranges = ArrayList<Pair<Int, Int>>(nativeCaretCount) | ||||
|  | ||||
|     // This logic is from original vim | ||||
|     val startOffsets = | ||||
|       if (argument.motion.action is MotionDownLess1FirstNonSpaceAction) null else HashMap<Caret, Int>(caretModel.caretCount) | ||||
|     val startOffsets = if (argument.motion.action is MotionDownLess1FirstNonSpaceAction) null else HashMap<VimCaret, Int>(nativeCaretCount) | ||||
|  | ||||
|     for (caret in caretModel.allCarets) { | ||||
|       val motionRange = MotionGroup.getMotionRange(editor.ij, caret, context.ij, argument, operatorArguments) | ||||
|     for (caret in editor.nativeCarets()) { | ||||
|       val motionRange = injector.motion.getMotionRange(editor, caret, context, argument, operatorArguments) | ||||
|         ?: continue | ||||
|  | ||||
|       assert(motionRange.size() == 1) | ||||
|       ranges.add(motionRange.startOffset to motionRange.endOffset) | ||||
|       startOffsets?.put(caret, motionRange.normalize().startOffset) | ||||
|       carretToRange[caret] = TextRange(motionRange.startOffset, motionRange.endOffset) | ||||
|     } | ||||
|  | ||||
|     val type = if (motion.isLinewiseMotion()) SelectionType.LINE_WISE else SelectionType.CHARACTER_WISE | ||||
|     val range = getTextRange(ranges, type) ?: return false | ||||
|  | ||||
|     if (range.size() == 0) return false | ||||
|  | ||||
|     val selectionType = if (type == SelectionType.CHARACTER_WISE && range.isMultiple) SelectionType.BLOCK_WISE else type | ||||
|     return yankRange(editor.ij, range, selectionType, startOffsets) | ||||
|     return yankRange( | ||||
|       editor, | ||||
|       carretToRange, | ||||
|       range, | ||||
|       type, | ||||
|       startOffsets | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -103,19 +106,21 @@ class YankGroup : YankGroupBase() { | ||||
|    * @return true if able to yank the lines, false if not | ||||
|    */ | ||||
|   override fun yankLine(editor: VimEditor, count: Int): Boolean { | ||||
|     val caretModel = editor.ij.caretModel | ||||
|     val ranges = ArrayList<Pair<Int, Int>>(caretModel.caretCount) | ||||
|     for (caret in caretModel.allCarets) { | ||||
|       val start = VimPlugin.getMotion().moveCaretToLineStart(editor, caret.vim) | ||||
|       val end = min(VimPlugin.getMotion().moveCaretToLineEndOffset(editor, caret.vim, count - 1, true) + 1, editor.fileSize().toInt()) | ||||
|     val caretCount = editor.nativeCarets().size | ||||
|     val ranges = ArrayList<Pair<Int, Int>>(caretCount) | ||||
|     val caretToRange = HashMap<VimCaret, TextRange>(caretCount) | ||||
|     for (caret in editor.nativeCarets()) { | ||||
|       val start = injector.motion.moveCaretToLineStart(editor, caret) | ||||
|       val end = min(injector.motion.moveCaretToLineEndOffset(editor, caret, count - 1, true) + 1, editor.fileSize().toInt()) | ||||
|  | ||||
|       if (end == -1) continue | ||||
|  | ||||
|       ranges.add(start to end) | ||||
|       caretToRange[caret] = TextRange(start, end) | ||||
|     } | ||||
|  | ||||
|     val range = getTextRange(ranges, SelectionType.LINE_WISE) ?: return false | ||||
|     return yankRange(editor.ij, range, SelectionType.LINE_WISE, null) | ||||
|     return yankRange(editor, caretToRange, range, SelectionType.LINE_WISE, null) | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -129,6 +134,7 @@ class YankGroup : YankGroupBase() { | ||||
|   override fun yankRange(editor: VimEditor, range: TextRange?, type: SelectionType, moveCursor: Boolean): Boolean { | ||||
|     range ?: return false | ||||
|  | ||||
|     val caretToRange = HashMap<VimCaret, TextRange>() | ||||
|     val selectionType = if (type == SelectionType.CHARACTER_WISE && range.isMultiple) SelectionType.BLOCK_WISE else type | ||||
|  | ||||
|     if (type == SelectionType.LINE_WISE) { | ||||
| @@ -143,24 +149,25 @@ class YankGroup : YankGroupBase() { | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     val caretModel = editor.ij.caretModel | ||||
|     val rangeStartOffsets = range.startOffsets | ||||
|     val rangeEndOffsets = range.endOffsets | ||||
|  | ||||
|     return if (moveCursor) { | ||||
|       val startOffsets = HashMap<Caret, Int>(caretModel.caretCount) | ||||
|     val startOffsets = HashMap<VimCaret, Int>(editor.nativeCarets().size) | ||||
|     if (type == SelectionType.BLOCK_WISE) { | ||||
|         startOffsets[caretModel.primaryCaret] = range.normalize().startOffset | ||||
|       startOffsets[editor.primaryCaret()] = range.normalize().startOffset | ||||
|       caretToRange[editor.primaryCaret()] = range | ||||
|     } else { | ||||
|         val carets = caretModel.allCarets | ||||
|         for (i in carets.indices) { | ||||
|           startOffsets[carets[i]] = TextRange(rangeStartOffsets[i], rangeEndOffsets[i]).normalize().startOffset | ||||
|       for ((i, caret) in editor.nativeCarets().withIndex()) { | ||||
|         val textRange = TextRange(rangeStartOffsets[i], rangeEndOffsets[i]) | ||||
|         startOffsets[caret] = textRange.normalize().startOffset | ||||
|         caretToRange[caret] = textRange | ||||
|       } | ||||
|     } | ||||
|  | ||||
|       yankRange(editor.ij, range, selectionType, startOffsets) | ||||
|     return if (moveCursor) { | ||||
|       yankRange(editor, caretToRange, range, selectionType, startOffsets) | ||||
|     } else { | ||||
|       yankRange(editor.ij, range, selectionType, null) | ||||
|       yankRange(editor, caretToRange, range, selectionType, null) | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -192,15 +199,20 @@ class YankGroup : YankGroupBase() { | ||||
|   } | ||||
|  | ||||
|   private fun yankRange( | ||||
|     editor: Editor, | ||||
|     editor: VimEditor, | ||||
|     caretToRange: Map<VimCaret, TextRange>, | ||||
|     range: TextRange, | ||||
|     type: SelectionType, | ||||
|     startOffsets: Map<Caret, Int>?, | ||||
|     startOffsets: Map<VimCaret, Int>?, | ||||
|   ): Boolean { | ||||
|     startOffsets?.forEach { (caret, offset) -> MotionGroup.moveCaret(editor, caret, offset) } | ||||
|     startOffsets?.forEach { (caret, offset) -> injector.motion.moveCaret(editor, caret, offset) } | ||||
|  | ||||
|     notifyListeners(editor, range) | ||||
|  | ||||
|     return VimPlugin.getRegister().storeText(editor.vim, range, type, false) | ||||
|     var result = true | ||||
|     for ((caret, myRange) in caretToRange) { | ||||
|       result = caret.registerStorage.storeText(caret, editor, myRange, type, false) && result | ||||
|     } | ||||
|     return result | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -23,9 +23,9 @@ import com.intellij.openapi.diagnostic.trace | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.maddyhome.idea.vim.KeyHandler | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.command.CommandState | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.helper.EditorDataContext | ||||
| import com.maddyhome.idea.vim.helper.commandState | ||||
| import com.maddyhome.idea.vim.helper.editorMode | ||||
| import com.maddyhome.idea.vim.helper.exitSelectMode | ||||
| import com.maddyhome.idea.vim.helper.exitVisualMode | ||||
| import com.maddyhome.idea.vim.helper.hasVisualSelection | ||||
| @@ -35,8 +35,8 @@ import com.maddyhome.idea.vim.helper.inSelectMode | ||||
| import com.maddyhome.idea.vim.helper.inVisualMode | ||||
| import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere | ||||
| import com.maddyhome.idea.vim.helper.isTemplateActive | ||||
| import com.maddyhome.idea.vim.helper.mode | ||||
| import com.maddyhome.idea.vim.helper.popAllModes | ||||
| import com.maddyhome.idea.vim.helper.vimStateMachine | ||||
| import com.maddyhome.idea.vim.listener.VimListenerManager | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| @@ -58,7 +58,7 @@ object IdeaSelectionControl { | ||||
|     editor: Editor, | ||||
|     selectionSource: VimListenerManager.SelectionSource = VimListenerManager.SelectionSource.OTHER, | ||||
|   ) { | ||||
|     VimVisualTimer.singleTask(editor.mode) { initialMode -> | ||||
|     VimVisualTimer.singleTask(editor.editorMode) { initialMode -> | ||||
|  | ||||
|       if (editor.isIdeaVimDisabledHere) return@singleTask | ||||
|  | ||||
| @@ -80,13 +80,13 @@ object IdeaSelectionControl { | ||||
|           return@singleTask | ||||
|         } | ||||
|  | ||||
|         logger.debug("Some carets have selection. State before adjustment: ${editor.vim.commandState.toSimpleString()}") | ||||
|         logger.debug("Some carets have selection. State before adjustment: ${editor.vim.vimStateMachine.toSimpleString()}") | ||||
|  | ||||
|         editor.popAllModes() | ||||
|  | ||||
|         activateMode(editor, chooseSelectionMode(editor, selectionSource, true)) | ||||
|       } else { | ||||
|         logger.debug("None of carets have selection. State before adjustment: ${editor.vim.commandState.toSimpleString()}") | ||||
|         logger.debug("None of carets have selection. State before adjustment: ${editor.vim.vimStateMachine.toSimpleString()}") | ||||
|         if (editor.inVisualMode) editor.exitVisualMode() | ||||
|         if (editor.inSelectMode) editor.exitSelectMode(false) | ||||
|  | ||||
| @@ -96,7 +96,7 @@ object IdeaSelectionControl { | ||||
|       } | ||||
|  | ||||
|       KeyHandler.getInstance().reset(editor.vim) | ||||
|       logger.debug("${editor.mode} is enabled") | ||||
|       logger.debug("${editor.editorMode} is enabled") | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -110,68 +110,68 @@ object IdeaSelectionControl { | ||||
|    * This method is created to improve user experience. It allows avoiding delay in some operations | ||||
|    *   (because [controlNonVimSelectionChange] is not executed immediately) | ||||
|    */ | ||||
|   fun predictMode(editor: Editor, selectionSource: VimListenerManager.SelectionSource): CommandState.Mode { | ||||
|   fun predictMode(editor: Editor, selectionSource: VimListenerManager.SelectionSource): VimStateMachine.Mode { | ||||
|     if (editor.selectionModel.hasSelection(true)) { | ||||
|       if (dontChangeMode(editor)) return editor.mode | ||||
|       if (dontChangeMode(editor)) return editor.editorMode | ||||
|       return chooseSelectionMode(editor, selectionSource, false) | ||||
|     } else { | ||||
|       return chooseNonSelectionMode(editor) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private fun activateMode(editor: Editor, mode: CommandState.Mode) { | ||||
|   private fun activateMode(editor: Editor, mode: VimStateMachine.Mode) { | ||||
|     when (mode) { | ||||
|       CommandState.Mode.VISUAL -> VimPlugin.getVisualMotion() | ||||
|       VimStateMachine.Mode.VISUAL -> VimPlugin.getVisualMotion() | ||||
|         .enterVisualMode(editor.vim, VimPlugin.getVisualMotion().autodetectVisualSubmode(editor.vim)) | ||||
|       CommandState.Mode.SELECT -> VimPlugin.getVisualMotion() | ||||
|       VimStateMachine.Mode.SELECT -> VimPlugin.getVisualMotion() | ||||
|         .enterSelectMode(editor.vim, VimPlugin.getVisualMotion().autodetectVisualSubmode(editor.vim)) | ||||
|       CommandState.Mode.INSERT -> VimPlugin.getChange().insertBeforeCursor( | ||||
|       VimStateMachine.Mode.INSERT -> VimPlugin.getChange().insertBeforeCursor( | ||||
|         editor.vim, | ||||
|         EditorDataContext.init(editor).vim | ||||
|       ) | ||||
|       CommandState.Mode.COMMAND -> Unit | ||||
|       VimStateMachine.Mode.COMMAND -> Unit | ||||
|       else -> error("Unexpected mode: $mode") | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private fun dontChangeMode(editor: Editor): Boolean = | ||||
|     editor.isTemplateActive() && (IdeaRefactorModeHelper.keepMode() || editor.mode.hasVisualSelection) | ||||
|     editor.isTemplateActive() && (IdeaRefactorModeHelper.keepMode() || editor.editorMode.hasVisualSelection) | ||||
|  | ||||
|   private fun chooseNonSelectionMode(editor: Editor): CommandState.Mode { | ||||
|   private fun chooseNonSelectionMode(editor: Editor): VimStateMachine.Mode { | ||||
|     val templateActive = editor.isTemplateActive() | ||||
|     if (templateActive && editor.inNormalMode || editor.inInsertMode) { | ||||
|       return CommandState.Mode.INSERT | ||||
|       return VimStateMachine.Mode.INSERT | ||||
|     } | ||||
|     return CommandState.Mode.COMMAND | ||||
|     return VimStateMachine.Mode.COMMAND | ||||
|   } | ||||
|  | ||||
|   private fun chooseSelectionMode( | ||||
|     editor: Editor, | ||||
|     selectionSource: VimListenerManager.SelectionSource, | ||||
|     logReason: Boolean, | ||||
|   ): CommandState.Mode { | ||||
|   ): VimStateMachine.Mode { | ||||
|     val selectmode = (VimPlugin.getOptionService().getOptionValue(OptionScope.LOCAL(IjVimEditor(editor)), OptionConstants.selectmodeName) as VimString).value | ||||
|     return when { | ||||
|       editor.isOneLineMode -> { | ||||
|         if (logReason) logger.debug("Enter select mode. Reason: one line mode") | ||||
|         CommandState.Mode.SELECT | ||||
|         VimStateMachine.Mode.SELECT | ||||
|       } | ||||
|       selectionSource == VimListenerManager.SelectionSource.MOUSE && OptionConstants.selectmode_mouse in selectmode -> { | ||||
|         if (logReason) logger.debug("Enter select mode. Selection source is mouse and selectMode option has mouse") | ||||
|         CommandState.Mode.SELECT | ||||
|         VimStateMachine.Mode.SELECT | ||||
|       } | ||||
|       editor.isTemplateActive() && IdeaRefactorModeHelper.selectMode() -> { | ||||
|         if (logReason) logger.debug("Enter select mode. Template is active and selectMode has template") | ||||
|         CommandState.Mode.SELECT | ||||
|         VimStateMachine.Mode.SELECT | ||||
|       } | ||||
|       selectionSource == VimListenerManager.SelectionSource.OTHER && | ||||
|         OptionConstants.selectmode_ideaselection in (VimPlugin.getOptionService().getOptionValue(OptionScope.GLOBAL, OptionConstants.selectmodeName) as VimString).value -> { | ||||
|         if (logReason) logger.debug("Enter select mode. Selection source is OTHER and selectMode has refactoring") | ||||
|         CommandState.Mode.SELECT | ||||
|         VimStateMachine.Mode.SELECT | ||||
|       } | ||||
|       else -> { | ||||
|         if (logReason) logger.debug("Enter visual mode") | ||||
|         CommandState.Mode.VISUAL | ||||
|         VimStateMachine.Mode.VISUAL | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   | ||||
| @@ -19,7 +19,7 @@ | ||||
| package com.maddyhome.idea.vim.group.visual | ||||
|  | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.command.CommandState | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.group.visual.VimVisualTimer.mode | ||||
| import com.maddyhome.idea.vim.group.visual.VimVisualTimer.singleTask | ||||
| import com.maddyhome.idea.vim.options.OptionScope | ||||
| @@ -66,9 +66,9 @@ import javax.swing.Timer | ||||
| object VimVisualTimer { | ||||
|  | ||||
|   var swingTimer: Timer? = null | ||||
|   var mode: CommandState.Mode? = null | ||||
|   var mode: VimStateMachine.Mode? = null | ||||
|  | ||||
|   inline fun singleTask(currentMode: CommandState.Mode, crossinline task: (initialMode: CommandState.Mode?) -> Unit) { | ||||
|   inline fun singleTask(currentMode: VimStateMachine.Mode, crossinline task: (initialMode: VimStateMachine.Mode?) -> Unit) { | ||||
|     swingTimer?.stop() | ||||
|  | ||||
|     if (mode == null) mode = currentMode | ||||
| @@ -92,7 +92,7 @@ object VimVisualTimer { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   inline fun timerAction(task: (initialMode: CommandState.Mode?) -> Unit) { | ||||
|   inline fun timerAction(task: (initialMode: VimStateMachine.Mode?) -> Unit) { | ||||
|     task(mode) | ||||
|     swingTimer = null | ||||
|     mode = null | ||||
|   | ||||
| @@ -23,13 +23,13 @@ import com.intellij.openapi.editor.Editor | ||||
| import com.intellij.openapi.editor.VisualPosition | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.api.VimMotionGroupBase | ||||
| import com.maddyhome.idea.vim.command.CommandState | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper | ||||
| import com.maddyhome.idea.vim.helper.editorMode | ||||
| import com.maddyhome.idea.vim.helper.inBlockSubMode | ||||
| import com.maddyhome.idea.vim.helper.inSelectMode | ||||
| import com.maddyhome.idea.vim.helper.inVisualMode | ||||
| import com.maddyhome.idea.vim.helper.isEndAllowed | ||||
| import com.maddyhome.idea.vim.helper.mode | ||||
| import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset | ||||
| import com.maddyhome.idea.vim.helper.subMode | ||||
| import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes | ||||
| @@ -108,7 +108,7 @@ val Caret.vimLeadSelectionOffset: Int | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       return if (editor.subMode == CommandState.SubMode.VISUAL_LINE) { | ||||
|       return if (editor.subMode == VimStateMachine.SubMode.VISUAL_LINE) { | ||||
|         val selectionStartLine = editor.offsetToLogicalPosition(selectionStart).line | ||||
|         val caretLine = editor.offsetToLogicalPosition(this.offset).line | ||||
|         if (caretLine == selectionStartLine) { | ||||
| @@ -132,8 +132,8 @@ val Caret.vimLeadSelectionOffset: Int | ||||
|     return caretOffset | ||||
|   } | ||||
|  | ||||
| fun moveCaretOneCharLeftFromSelectionEnd(editor: Editor, predictedMode: CommandState.Mode) { | ||||
|   if (predictedMode != CommandState.Mode.VISUAL) { | ||||
| fun moveCaretOneCharLeftFromSelectionEnd(editor: Editor, predictedMode: VimStateMachine.Mode) { | ||||
|   if (predictedMode != VimStateMachine.Mode.VISUAL) { | ||||
|     if (!predictedMode.isEndAllowed) { | ||||
|       editor.caretModel.allCarets.forEach { caret -> | ||||
|         val lineEnd = EditorHelper.getLineEndForOffset(editor, caret.offset) | ||||
| @@ -161,18 +161,18 @@ private fun setVisualSelection(selectionStart: Int, selectionEnd: Int, caret: Ca | ||||
|   val (start, end) = if (selectionStart > selectionEnd) selectionEnd to selectionStart else selectionStart to selectionEnd | ||||
|   val editor = caret.editor | ||||
|   val subMode = editor.subMode | ||||
|   val mode = editor.mode | ||||
|   val mode = editor.editorMode | ||||
|   val vimEditor = IjVimEditor(editor) | ||||
|   when (subMode) { | ||||
|     CommandState.SubMode.VISUAL_CHARACTER -> { | ||||
|     VimStateMachine.SubMode.VISUAL_CHARACTER -> { | ||||
|       val (nativeStart, nativeEnd) = charToNativeSelection(vimEditor, start, end, mode) | ||||
|       caret.vimSetSystemSelectionSilently(nativeStart, nativeEnd) | ||||
|     } | ||||
|     CommandState.SubMode.VISUAL_LINE -> { | ||||
|     VimStateMachine.SubMode.VISUAL_LINE -> { | ||||
|       val (nativeStart, nativeEnd) = lineToNativeSelection(vimEditor, start, end) | ||||
|       caret.vimSetSystemSelectionSilently(nativeStart, nativeEnd) | ||||
|     } | ||||
|     CommandState.SubMode.VISUAL_BLOCK -> { | ||||
|     VimStateMachine.SubMode.VISUAL_BLOCK -> { | ||||
|       editor.caretModel.removeSecondaryCarets() | ||||
|  | ||||
|       // Set system selection | ||||
| @@ -200,7 +200,7 @@ private fun setVisualSelection(selectionStart: Int, selectionEnd: Int, caret: Ca | ||||
|           // Put right caret position for tab character | ||||
|           aCaret.moveToVisualPosition(visualPosition) | ||||
|         } | ||||
|         if (mode != CommandState.Mode.SELECT && | ||||
|         if (mode != VimStateMachine.Mode.SELECT && | ||||
|           !EditorHelper.isLineEmpty(editor, line, false) && | ||||
|           aCaret.offset == aCaret.selectionEnd && | ||||
|           aCaret.selectionEnd - 1 >= lineStartOffset && | ||||
|   | ||||
| @@ -19,22 +19,34 @@ | ||||
| package com.maddyhome.idea.vim.group.visual | ||||
|  | ||||
| import com.intellij.find.FindManager | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.VimVisualMotionGroupBase | ||||
| import com.maddyhome.idea.vim.command.CommandState | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.command.engine | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
|  | ||||
| /** | ||||
|  * @author Alex Plate | ||||
|  */ | ||||
| class VisualMotionGroup : VimVisualMotionGroupBase() { | ||||
|   override fun autodetectVisualSubmode(editor: VimEditor): CommandState.SubMode { | ||||
|   override fun autodetectVisualSubmode(editor: VimEditor): VimStateMachine.SubMode { | ||||
|     // IJ specific. See https://youtrack.jetbrains.com/issue/VIM-1924. | ||||
|     val project = editor.ij.project | ||||
|     if (project != null && FindManager.getInstance(project).selectNextOccurrenceWasPerformed()) { | ||||
|       return CommandState.SubMode.VISUAL_CHARACTER | ||||
|       return VimStateMachine.SubMode.VISUAL_CHARACTER | ||||
|     } | ||||
|  | ||||
|     return super.autodetectVisualSubmode(editor) | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * COMPATIBILITY-LAYER: Added a method | ||||
|    * Please see: https://jb.gg/zo8n0r | ||||
|    */ | ||||
|   fun enterVisualMode(editor: Editor, subMode: CommandState.SubMode? = null): Boolean { | ||||
|     return this.enterVisualMode(editor.vim, subMode?.engine) | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -21,7 +21,7 @@ package com.maddyhome.idea.vim.handler | ||||
| import com.intellij.serviceContainer.BaseKeyedLazyInstance | ||||
| import com.intellij.util.SmartList | ||||
| import com.intellij.util.xmlb.annotations.Attribute | ||||
| import com.maddyhome.idea.vim.common.MappingMode | ||||
| import com.maddyhome.idea.vim.command.MappingMode | ||||
| import javax.swing.KeyStroke | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -23,7 +23,7 @@ import com.intellij.openapi.editor.CaretVisualAttributes | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.intellij.openapi.editor.ex.EditorEx | ||||
| import com.intellij.openapi.editor.ex.EditorSettingsExternalizable | ||||
| import com.maddyhome.idea.vim.command.CommandState | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.options.OptionChangeListener | ||||
| import com.maddyhome.idea.vim.options.helpers.GuiCursorMode | ||||
| @@ -75,7 +75,7 @@ object GuicursorChangeListener : OptionChangeListener<VimDataType> { | ||||
| } | ||||
|  | ||||
| private fun Editor.guicursorMode(): GuiCursorMode { | ||||
|   if (this.vim.commandState.isReplaceCharacter) { | ||||
|   if (this.vim.vimStateMachine.isReplaceCharacter) { | ||||
|     // Can be true for NORMAL and VISUAL | ||||
|     return GuiCursorMode.REPLACE | ||||
|   } | ||||
| @@ -84,18 +84,18 @@ private fun Editor.guicursorMode(): GuiCursorMode { | ||||
|   // makes much more use of SELECT than Vim does (e.g. it's the default for idearefactormode) so it makes sense for us | ||||
|   // to more visually distinguish VISUAL and SELECT. So we use INSERT; a selection and the insert caret is intuitively | ||||
|   // the same as SELECT | ||||
|   return when (mode) { | ||||
|     CommandState.Mode.COMMAND -> GuiCursorMode.NORMAL | ||||
|     CommandState.Mode.VISUAL -> GuiCursorMode.VISUAL // TODO: VISUAL_EXCLUSIVE | ||||
|     CommandState.Mode.SELECT -> GuiCursorMode.INSERT | ||||
|     CommandState.Mode.INSERT -> GuiCursorMode.INSERT | ||||
|     CommandState.Mode.OP_PENDING -> GuiCursorMode.OP_PENDING | ||||
|     CommandState.Mode.REPLACE -> GuiCursorMode.REPLACE | ||||
|   return when (editorMode) { | ||||
|     VimStateMachine.Mode.COMMAND -> GuiCursorMode.NORMAL | ||||
|     VimStateMachine.Mode.VISUAL -> GuiCursorMode.VISUAL // TODO: VISUAL_EXCLUSIVE | ||||
|     VimStateMachine.Mode.SELECT -> GuiCursorMode.INSERT | ||||
|     VimStateMachine.Mode.INSERT -> GuiCursorMode.INSERT | ||||
|     VimStateMachine.Mode.OP_PENDING -> GuiCursorMode.OP_PENDING | ||||
|     VimStateMachine.Mode.REPLACE -> GuiCursorMode.REPLACE | ||||
|     // This doesn't handle ci and cr, but we don't care - our CMD_LINE will never call this | ||||
|     CommandState.Mode.CMD_LINE -> GuiCursorMode.CMD_LINE | ||||
|     CommandState.Mode.INSERT_NORMAL -> GuiCursorMode.NORMAL | ||||
|     CommandState.Mode.INSERT_VISUAL -> GuiCursorMode.VISUAL | ||||
|     CommandState.Mode.INSERT_SELECT -> GuiCursorMode.INSERT | ||||
|     VimStateMachine.Mode.CMD_LINE -> GuiCursorMode.CMD_LINE | ||||
|     VimStateMachine.Mode.INSERT_NORMAL -> GuiCursorMode.NORMAL | ||||
|     VimStateMachine.Mode.INSERT_VISUAL -> GuiCursorMode.VISUAL | ||||
|     VimStateMachine.Mode.INSERT_SELECT -> GuiCursorMode.INSERT | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -34,7 +34,7 @@ class CommandLineHelper : VimCommandLineHelper { | ||||
|  | ||||
|   override fun inputString(vimEditor: VimEditor, prompt: String, finishOn: Char?): String? { | ||||
|     val editor = vimEditor.ij | ||||
|     if (vimEditor.commandState.isDotRepeatInProgress) { | ||||
|     if (vimEditor.vimStateMachine.isDotRepeatInProgress) { | ||||
|       val input = Extension.consumeString() | ||||
|       return input ?: error("Not enough strings saved: ${Extension.lastExtensionHandler}") | ||||
|     } | ||||
|   | ||||
| @@ -23,89 +23,106 @@ package com.maddyhome.idea.vim.helper | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.command.CommandState | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.command.engine | ||||
| import com.maddyhome.idea.vim.command.ij | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
|  | ||||
| val Editor.isEndAllowed: Boolean | ||||
|   get() = when (this.mode) { | ||||
|     CommandState.Mode.INSERT, CommandState.Mode.VISUAL, CommandState.Mode.SELECT, CommandState.Mode.INSERT_VISUAL, CommandState.Mode.INSERT_SELECT -> true | ||||
|     CommandState.Mode.COMMAND, CommandState.Mode.CMD_LINE, CommandState.Mode.REPLACE, CommandState.Mode.OP_PENDING, CommandState.Mode.INSERT_NORMAL -> { | ||||
|   get() = when (this.editorMode) { | ||||
|     VimStateMachine.Mode.INSERT, VimStateMachine.Mode.VISUAL, VimStateMachine.Mode.SELECT, VimStateMachine.Mode.INSERT_VISUAL, VimStateMachine.Mode.INSERT_SELECT -> true | ||||
|     VimStateMachine.Mode.COMMAND, VimStateMachine.Mode.CMD_LINE, VimStateMachine.Mode.REPLACE, VimStateMachine.Mode.OP_PENDING, VimStateMachine.Mode.INSERT_NORMAL -> { | ||||
|       // One day we'll use a proper insert_normal mode | ||||
|       if (this.mode.inSingleMode) true else usesVirtualSpace | ||||
|       if (this.editorMode.inSingleMode) true else usesVirtualSpace | ||||
|     } | ||||
|   } | ||||
|  | ||||
| val CommandState.Mode.isEndAllowedIgnoringOnemore: Boolean | ||||
| val VimStateMachine.Mode.isEndAllowedIgnoringOnemore: Boolean | ||||
|   get() = when (this) { | ||||
|     CommandState.Mode.INSERT, CommandState.Mode.VISUAL, CommandState.Mode.SELECT -> true | ||||
|     CommandState.Mode.COMMAND, CommandState.Mode.CMD_LINE, CommandState.Mode.REPLACE, CommandState.Mode.OP_PENDING -> false | ||||
|     CommandState.Mode.INSERT_NORMAL -> false | ||||
|     CommandState.Mode.INSERT_VISUAL -> true | ||||
|     CommandState.Mode.INSERT_SELECT -> true | ||||
|     VimStateMachine.Mode.INSERT, VimStateMachine.Mode.VISUAL, VimStateMachine.Mode.SELECT -> true | ||||
|     VimStateMachine.Mode.COMMAND, VimStateMachine.Mode.CMD_LINE, VimStateMachine.Mode.REPLACE, VimStateMachine.Mode.OP_PENDING -> false | ||||
|     VimStateMachine.Mode.INSERT_NORMAL -> false | ||||
|     VimStateMachine.Mode.INSERT_VISUAL -> true | ||||
|     VimStateMachine.Mode.INSERT_SELECT -> true | ||||
|   } | ||||
|  | ||||
| val CommandState.Mode.hasVisualSelection | ||||
| val VimStateMachine.Mode.hasVisualSelection | ||||
|   get() = when (this) { | ||||
|     CommandState.Mode.VISUAL, CommandState.Mode.SELECT -> true | ||||
|     CommandState.Mode.REPLACE, CommandState.Mode.CMD_LINE, CommandState.Mode.COMMAND, CommandState.Mode.INSERT, CommandState.Mode.OP_PENDING -> false | ||||
|     CommandState.Mode.INSERT_NORMAL -> false | ||||
|     CommandState.Mode.INSERT_VISUAL -> true | ||||
|     CommandState.Mode.INSERT_SELECT -> true | ||||
|     VimStateMachine.Mode.VISUAL, VimStateMachine.Mode.SELECT -> true | ||||
|     VimStateMachine.Mode.REPLACE, VimStateMachine.Mode.CMD_LINE, VimStateMachine.Mode.COMMAND, VimStateMachine.Mode.INSERT, VimStateMachine.Mode.OP_PENDING -> false | ||||
|     VimStateMachine.Mode.INSERT_NORMAL -> false | ||||
|     VimStateMachine.Mode.INSERT_VISUAL -> true | ||||
|     VimStateMachine.Mode.INSERT_SELECT -> true | ||||
|   } | ||||
|  | ||||
| val Editor.editorMode | ||||
|   get() = this.vim.vimStateMachine.mode | ||||
|  | ||||
| /** | ||||
|  * COMPATIBILITY-LAYER: New method | ||||
|  * Please see: https://jb.gg/zo8n0r | ||||
|  */ | ||||
| val Editor.mode | ||||
|   get() = this.vim.commandState.mode | ||||
|   get() = this.vim.vimStateMachine.mode.ij | ||||
|  | ||||
| /** | ||||
|  * COMPATIBILITY-LAYER: New method | ||||
|  * Please see: https://jb.gg/zo8n0r | ||||
|  */ | ||||
| val CommandState.Mode.isEndAllowed: Boolean | ||||
|   get() = this.engine.isEndAllowed | ||||
|  | ||||
| var Editor.subMode | ||||
|   get() = this.vim.commandState.subMode | ||||
|   get() = this.vim.vimStateMachine.subMode | ||||
|   set(value) { | ||||
|     this.vim.commandState.subMode = value | ||||
|     this.vim.vimStateMachine.subMode = value | ||||
|   } | ||||
|  | ||||
| @get:JvmName("inNormalMode") | ||||
| val Editor.inNormalMode | ||||
|   get() = this.mode.inNormalMode | ||||
|   get() = this.editorMode.inNormalMode | ||||
|  | ||||
| @get:JvmName("inNormalMode") | ||||
| val CommandState.Mode.inNormalMode | ||||
|   get() = this == CommandState.Mode.COMMAND || this == CommandState.Mode.INSERT_NORMAL | ||||
| val VimStateMachine.Mode.inNormalMode | ||||
|   get() = this == VimStateMachine.Mode.COMMAND || this == VimStateMachine.Mode.INSERT_NORMAL | ||||
|  | ||||
| @get:JvmName("inInsertMode") | ||||
| val Editor.inInsertMode | ||||
|   get() = this.mode == CommandState.Mode.INSERT || this.mode == CommandState.Mode.REPLACE | ||||
|   get() = this.editorMode == VimStateMachine.Mode.INSERT || this.editorMode == VimStateMachine.Mode.REPLACE | ||||
|  | ||||
| @get:JvmName("inRepeatMode") | ||||
| val Editor.inRepeatMode | ||||
|   get() = this.vim.commandState.isDotRepeatInProgress | ||||
|   get() = this.vim.vimStateMachine.isDotRepeatInProgress | ||||
|  | ||||
| @get:JvmName("inVisualMode") | ||||
| val Editor.inVisualMode | ||||
|   get() = this.mode.inVisualMode | ||||
|   get() = this.editorMode.inVisualMode | ||||
|  | ||||
| @get:JvmName("inSelectMode") | ||||
| val Editor.inSelectMode | ||||
|   get() = this.mode == CommandState.Mode.SELECT || this.mode == CommandState.Mode.INSERT_SELECT | ||||
|   get() = this.editorMode == VimStateMachine.Mode.SELECT || this.editorMode == VimStateMachine.Mode.INSERT_SELECT | ||||
|  | ||||
| val VimEditor.inSelectMode | ||||
|   get() = this.mode == CommandState.Mode.SELECT || this.mode == CommandState.Mode.INSERT_SELECT | ||||
|   get() = this.mode == VimStateMachine.Mode.SELECT || this.mode == VimStateMachine.Mode.INSERT_SELECT | ||||
|  | ||||
| @get:JvmName("inBlockSubMode") | ||||
| val Editor.inBlockSubMode | ||||
|   get() = this.subMode == CommandState.SubMode.VISUAL_BLOCK | ||||
|   get() = this.subMode == VimStateMachine.SubMode.VISUAL_BLOCK | ||||
|  | ||||
| @get:JvmName("inSingleCommandMode") | ||||
| val Editor.inSingleCommandMode: Boolean | ||||
|   get() = this.mode.inSingleMode | ||||
|   get() = this.editorMode.inSingleMode | ||||
|  | ||||
| @get:JvmName("inSingleMode") | ||||
| val CommandState.Mode.inSingleMode: Boolean | ||||
| val VimStateMachine.Mode.inSingleMode: Boolean | ||||
|   get() = when (this) { | ||||
|     CommandState.Mode.INSERT_NORMAL, CommandState.Mode.INSERT_SELECT, CommandState.Mode.INSERT_VISUAL -> true | ||||
|     VimStateMachine.Mode.INSERT_NORMAL, VimStateMachine.Mode.INSERT_SELECT, VimStateMachine.Mode.INSERT_VISUAL -> true | ||||
|     else -> false | ||||
|   } | ||||
|  | ||||
| @get:JvmName("inSingleNormalMode") | ||||
| val CommandState.Mode.inSingleNormalMode: Boolean | ||||
| val VimStateMachine.Mode.inSingleNormalMode: Boolean | ||||
|   get() = when (this) { | ||||
|     CommandState.Mode.INSERT_NORMAL -> true | ||||
|     VimStateMachine.Mode.INSERT_NORMAL -> true | ||||
|     else -> false | ||||
|   } | ||||
|   | ||||
| @@ -37,6 +37,7 @@ import org.jetbrains.annotations.Nullable; | ||||
| import org.jetbrains.annotations.Range; | ||||
|  | ||||
| import java.awt.*; | ||||
| import java.awt.geom.Point2D; | ||||
| import java.nio.CharBuffer; | ||||
| import java.util.Collections; | ||||
| import java.util.Comparator; | ||||
| @@ -152,6 +153,14 @@ public class EditorHelper { | ||||
|            : VimInjectorKt.getInjector().getEngineEditorHelper().logicalLineToVisualLine(editor, count - 1) + 1; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * COMPATIBILITY-LAYER: Created a function | ||||
|    * Please see: <a href="https://jb.gg/zo8n0r">doc</a> | ||||
|    */ | ||||
|   public static int getVisualLineCount(final @NotNull Editor editor) { | ||||
|     return getVisualLineCount(new IjVimEditor(editor)); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Gets the number of actual lines in the file | ||||
|    * | ||||
| @@ -238,12 +247,27 @@ public class EditorHelper { | ||||
|    * font. It does not include inlays or folds. | ||||
|    * <p> | ||||
|    * Note that this value is only approximate and should be avoided whenever possible! | ||||
|    * </p> | ||||
|    * | ||||
|    * @param editor The editor | ||||
|    * @return The number of screen columns | ||||
|    */ | ||||
|   public static int getApproximateScreenWidth(final @NotNull Editor editor) { | ||||
|     return getVisibleArea(editor).width / EditorUtil.getPlainSpaceWidth(editor); | ||||
|     return (int)(getVisibleArea(editor).width / getPlainSpaceWidthFloat(editor)); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Gets the width of the space character in the editor's plain font as a float. | ||||
|    * <p> | ||||
|    * Font width can be fractional, but {@link EditorUtil#getPlainSpaceWidth(Editor)} returns it as an int, which can | ||||
|    * lead to rounding errors. | ||||
|    * </p> | ||||
|    * | ||||
|    * @param editor The editor | ||||
|    * @return The width of the space character in the editor's plain font in pixels. It might be a fractional value. | ||||
|    */ | ||||
|   public static float getPlainSpaceWidthFloat(final @NotNull Editor editor) { | ||||
|     return EditorUtil.fontForChar(' ', Font.PLAIN, editor).charWidth2D(' '); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @@ -795,19 +819,20 @@ public class EditorHelper { | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     final int columnLeftX = editor.visualPositionToXY(new VisualPosition(visualLine, targetVisualColumn)).x; | ||||
|     final int columnLeftX = (int) Math.round(editor.visualPositionToPoint2D(new VisualPosition(visualLine, targetVisualColumn)).getX()); | ||||
|     scrollHorizontally(editor, columnLeftX); | ||||
|   } | ||||
|  | ||||
|   public static void scrollColumnToMiddleOfScreen(@NotNull Editor editor, int visualLine, int visualColumn) { | ||||
|     final Point point = editor.visualPositionToXY(new VisualPosition(visualLine, visualColumn)); | ||||
|     final Point2D point = editor.visualPositionToPoint2D(new VisualPosition(visualLine, visualColumn)); | ||||
|     final int screenWidth = getVisibleArea(editor).width; | ||||
|  | ||||
|     // Snap the column to the nearest standard column grid. This positions us nicely if there are an odd or even number | ||||
|     // of columns. It also works with inline inlays and folds. It is slightly inaccurate for proportional fonts, but is | ||||
|     // still a good solution. Besides, what kind of monster uses Vim with proportional fonts? | ||||
|     final int standardColumnWidth = EditorUtil.getPlainSpaceWidth(editor); | ||||
|     final int x = max(0, point.x - (screenWidth / standardColumnWidth / 2 * standardColumnWidth)); | ||||
|     final float standardColumnWidth = EditorHelper.getPlainSpaceWidthFloat(editor); | ||||
|     final int screenMidColumn = (int) (screenWidth / standardColumnWidth / 2); | ||||
|     final int x = max(0, (int) Math.round(point.getX() - (screenMidColumn * standardColumnWidth))); | ||||
|     scrollHorizontally(editor, x); | ||||
|   } | ||||
|  | ||||
| @@ -832,7 +857,7 @@ public class EditorHelper { | ||||
|     } | ||||
|  | ||||
|     // Scroll to the left edge of the target column, minus a screenwidth, and adjusted for inlays | ||||
|     final int targetColumnRightX = editor.visualPositionToXY(new VisualPosition(visualLine, targetVisualColumn + 1)).x; | ||||
|     final int targetColumnRightX = (int) Math.round(editor.visualPositionToPoint2D(new VisualPosition(visualLine, targetVisualColumn + 1)).getX()); | ||||
|     final int screenWidth = getVisibleArea(editor).width; | ||||
|     scrollHorizontally(editor, targetColumnRightX - screenWidth); | ||||
|   } | ||||
| @@ -853,7 +878,7 @@ public class EditorHelper { | ||||
|     if (inlayAwareOffset == vimLastColumn) return vimLastColumn; | ||||
|  | ||||
|     Editor editor = caret.getEditor(); | ||||
|     boolean isEndAllowed = CommandStateHelper.isEndAllowedIgnoringOnemore(CommandStateHelper.getMode(editor)); | ||||
|     boolean isEndAllowed = CommandStateHelper.isEndAllowedIgnoringOnemore(CommandStateHelper.getEditorMode(editor)); | ||||
|     final LogicalPosition logicalPosition = caret.getLogicalPosition(); | ||||
|     int lastColumn = EditorHelper.lastColumnForLine(editor, logicalPosition.line, isEndAllowed); | ||||
|  | ||||
| @@ -994,18 +1019,18 @@ public class EditorHelper { | ||||
|     // Note that visualPos.leansRight will be true for the right half side of the character grid | ||||
|     VisualPosition closestVisualPosition = editor.xyToVisualPosition(new Point(x, y)); | ||||
|  | ||||
|     // Make sure we get the character that contains this XY, not the editor's decision about closest character. The | ||||
|     // editor will give us the next character if X is over half way through the character grid. | ||||
|     int xActualLeft = editor.visualPositionToXY(closestVisualPosition).x; | ||||
|     // Make sure we get the character that contains this XY, not the editor's decision about the closest character. The | ||||
|     // editor will give us the next character if X is over halfway through the character grid. Take into account that | ||||
|     // the font size might be fractional, but the editor's area is integer. Use floating point values and round. | ||||
|     long xActualLeft = Math.round(editor.visualPositionToPoint2D(closestVisualPosition).getX()); | ||||
|     if (xActualLeft > x) { | ||||
|       closestVisualPosition = getPreviousNonInlayVisualPosition(editor, closestVisualPosition); | ||||
|       xActualLeft = editor.visualPositionToXY(closestVisualPosition).x; | ||||
|       xActualLeft = Math.round(editor.visualPositionToPoint2D(closestVisualPosition).getX()); | ||||
|     } | ||||
|  | ||||
|     if (xActualLeft >= leftBound) { | ||||
|       final int xActualRight = | ||||
|         editor.visualPositionToXY(new VisualPosition(closestVisualPosition.line, closestVisualPosition.column + 1)).x - | ||||
|         1; | ||||
|       final VisualPosition nextVisualPosition = new VisualPosition(closestVisualPosition.line, closestVisualPosition.column + 1); | ||||
|       final long xActualRight = Math.round(editor.visualPositionToPoint2D(nextVisualPosition).getX()) - 1; | ||||
|       if (xActualRight <= rightBound) { | ||||
|         return closestVisualPosition.column; | ||||
|       } | ||||
|   | ||||
| @@ -22,6 +22,7 @@ package com.maddyhome.idea.vim.helper | ||||
|  | ||||
| import com.intellij.codeWithMe.ClientId | ||||
| import com.intellij.openapi.editor.Caret | ||||
| import com.intellij.openapi.editor.CaretState | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.intellij.openapi.editor.ex.util.EditorUtil | ||||
| import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx | ||||
| @@ -106,3 +107,41 @@ val Caret.vimLine: Int | ||||
|  */ | ||||
| val Editor.vimLine: Int | ||||
|   get() = this.caretModel.currentCaret.vimLine | ||||
|  | ||||
| inline fun Editor.runWithEveryCaretAndRestore(action: () -> Unit) { | ||||
|   val caretModel = this.caretModel | ||||
|   val carets = if (this.inBlockSubMode) 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 | ||||
|   }  | ||||
| } | ||||
|   | ||||
| @@ -22,6 +22,7 @@ import com.intellij.codeInsight.template.TemplateManager | ||||
| import com.intellij.codeWithMe.ClientId | ||||
| import com.intellij.injected.editor.EditorWindow | ||||
| import com.intellij.openapi.editor.Caret | ||||
| import com.intellij.openapi.editor.ClientEditorManager | ||||
| import com.intellij.openapi.editor.Document | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.intellij.openapi.editor.EditorFactory | ||||
| @@ -30,6 +31,7 @@ import com.intellij.openapi.util.Key | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.options.OptionConstants | ||||
| import com.maddyhome.idea.vim.options.OptionScope | ||||
| import kotlin.streams.toList | ||||
|  | ||||
| /** | ||||
|  * This annotation is created for test functions (methods). | ||||
| @@ -83,10 +85,9 @@ fun Editor.getTopLevelEditor() = if (this is EditorWindow) this.delegate else th | ||||
|  | ||||
| /** | ||||
|  * Return list of editors for local host (for code with me plugin) | ||||
|  * [VERSION UPDATE] 212+ ClientEditorManager.editors() | ||||
|  */ | ||||
| fun localEditors(): List<Editor> { | ||||
|   return EditorFactory.getInstance().allEditors.filter { editor -> editor.editorClientId.let { it == null || it == ClientId.currentOrNull } } | ||||
|   return ClientEditorManager.getCurrentInstance().editors().toList() | ||||
| } | ||||
|  | ||||
| fun localEditors(doc: Document): List<Editor> { | ||||
|   | ||||
| @@ -21,18 +21,21 @@ package com.maddyhome.idea.vim.helper | ||||
| import com.intellij.openapi.actionSystem.ActionGroup | ||||
| import com.intellij.openapi.actionSystem.ActionManager | ||||
| import com.intellij.openapi.actionSystem.ActionPlaces | ||||
| import com.intellij.openapi.actionSystem.AnAction | ||||
| import com.intellij.openapi.actionSystem.AnActionEvent | ||||
| import com.intellij.openapi.actionSystem.DataContext | ||||
| import com.intellij.openapi.actionSystem.AnActionResult | ||||
| import com.intellij.openapi.actionSystem.IdeActions | ||||
| import com.intellij.openapi.actionSystem.PlatformDataKeys | ||||
| import com.intellij.openapi.actionSystem.Presentation | ||||
| import com.intellij.openapi.actionSystem.ex.ActionManagerEx | ||||
| import com.intellij.openapi.actionSystem.ex.ActionUtil | ||||
| import com.intellij.openapi.command.CommandProcessor | ||||
| import com.intellij.openapi.command.UndoConfirmationPolicy | ||||
| import com.intellij.openapi.components.Service | ||||
| import com.intellij.openapi.editor.actionSystem.DocCommandGroupId | ||||
| import com.intellij.openapi.project.IndexNotReadyException | ||||
| import com.intellij.openapi.ui.popup.JBPopupFactory | ||||
| import com.intellij.openapi.util.NlsContexts | ||||
| import com.intellij.util.SlowOperations | ||||
| import com.maddyhome.idea.vim.RegisterActions | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.NativeAction | ||||
| @@ -80,7 +83,7 @@ class IjActionExecutor : VimActionExecutor { | ||||
|     // This method executes inside of lastUpdateAndCheckDumb | ||||
|     // Another related issue: VIM-2604 | ||||
|     if (!ActionUtil.lastUpdateAndCheckDumb(ijAction, event, false)) return false | ||||
|     if (ijAction is ActionGroup && !canBePerformed(event, ijAction, context.ij)) { | ||||
|     if (ijAction is ActionGroup && !event.presentation.isPerformGroup) { | ||||
|       // Some ActionGroups should not be performed, but shown as a popup | ||||
|       val popup = JBPopupFactory.getInstance() | ||||
|         .createActionGroupPopup(event.presentation.text, ijAction, context.ij, false, null, -1) | ||||
| @@ -95,19 +98,44 @@ class IjActionExecutor : VimActionExecutor { | ||||
|       popup.showInFocusCenter() | ||||
|       return true | ||||
|     } else { | ||||
|       ActionUtil.performActionDumbAwareWithCallbacks(ijAction, event) | ||||
|       performDumbAwareWithCallbacks(ijAction, event) { ijAction.actionPerformed(event) } | ||||
|       return true | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private fun canBePerformed(event: AnActionEvent, action: ActionGroup, context: DataContext): Boolean { | ||||
|     val presentation = event.presentation | ||||
|     return try { | ||||
|       // [VERSION UPDATE] 221+ Just use Presentation.isPerformGroup | ||||
|       val method = Presentation::class.java.getMethod("isPerformGroup") | ||||
|       method.invoke(presentation) as Boolean | ||||
|     } catch (e: Exception) { | ||||
|       action.canBePerformed(context) | ||||
|   // This is taken directly from ActionUtil.performActionDumbAwareWithCallbacks | ||||
|   // But with one check removed. With this check some actions (like `:w` doesn't work) | ||||
|   // https://youtrack.jetbrains.com/issue/VIM-2691/File-is-not-saved-on-w | ||||
|   private fun performDumbAwareWithCallbacks( | ||||
|     action: AnAction, | ||||
|     event: AnActionEvent, | ||||
|     performRunnable: Runnable, | ||||
|   ) { | ||||
|     val project = event.project | ||||
|     var indexError: IndexNotReadyException? = null | ||||
|     val manager = ActionManagerEx.getInstanceEx() | ||||
|     manager.fireBeforeActionPerformed(action, event) | ||||
|     var result: AnActionResult? = null | ||||
|     try { | ||||
|       SlowOperations.allowSlowOperations(SlowOperations.ACTION_PERFORM).use { | ||||
|         performRunnable.run() | ||||
|         result = AnActionResult.PERFORMED | ||||
|       } | ||||
|     } catch (ex: IndexNotReadyException) { | ||||
|       indexError = ex | ||||
|       result = AnActionResult.failed(ex) | ||||
|     } catch (ex: RuntimeException) { | ||||
|       result = AnActionResult.failed(ex) | ||||
|       throw ex | ||||
|     } catch (ex: Error) { | ||||
|       result = AnActionResult.failed(ex) | ||||
|       throw ex | ||||
|     } finally { | ||||
|       if (result == null) result = AnActionResult.failed(Throwable()) | ||||
|       manager.fireAfterActionPerformed(action, event, result!!) | ||||
|     } | ||||
|     if (indexError != null) { | ||||
|       ActionUtil.showDumbModeWarning(project, event) | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -155,6 +183,10 @@ class IjActionExecutor : VimActionExecutor { | ||||
|     return RegisterActions.findAction(id) | ||||
|   } | ||||
|  | ||||
|   override fun findVimActionOrDie(id: String): EditorActionHandlerBase { | ||||
|     return RegisterActions.findActionOrDie(id) | ||||
|   } | ||||
|  | ||||
|   override fun getAction(actionId: String): NativeAction? { | ||||
|     return ActionManager.getInstance().getAction(actionId)?.let { IjNativeAction(it) } | ||||
|   } | ||||
|   | ||||
| @@ -143,4 +143,8 @@ class IjEditorHelper : EngineEditorHelper { | ||||
|   override fun getLeadingWhitespace(editor: VimEditor, line: Int): String { | ||||
|     return EditorHelper.getLeadingWhitespace(editor.ij, line) | ||||
|   } | ||||
|  | ||||
|   override fun anyNonWhitespace(editor: VimEditor, offset: Int, dir: Int): Boolean { | ||||
|     return SearchHelper.anyNonWhitespace(editor.ij, offset, dir) | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -25,9 +25,9 @@ import com.intellij.openapi.editor.Caret | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.command.CommandState | ||||
| import com.maddyhome.idea.vim.command.OperatorArguments | ||||
| import com.maddyhome.idea.vim.command.SelectionType | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor | ||||
| import com.maddyhome.idea.vim.newapi.IjExecutionContext | ||||
| @@ -39,8 +39,8 @@ import com.maddyhome.idea.vim.newapi.vim | ||||
|  * Pop all modes, but leave editor state. E.g. editor selection is not removed. | ||||
|  */ | ||||
| fun Editor.popAllModes() { | ||||
|   val commandState = this.vim.commandState | ||||
|   while (commandState.mode != CommandState.Mode.COMMAND) { | ||||
|   val commandState = this.vim.vimStateMachine | ||||
|   while (commandState.mode != VimStateMachine.Mode.COMMAND) { | ||||
|     commandState.popModes() | ||||
|   } | ||||
| } | ||||
| @@ -63,7 +63,7 @@ fun Editor.exitVisualMode() { | ||||
|     VimPlugin.getMark().setVisualSelectionMarks(this.vim, TextRange(vimSelectionStart, primaryCaret.offset)) | ||||
|     this.caretModel.allCarets.forEach { it.vimSelectionStartClear() } | ||||
|  | ||||
|     this.vim.commandState.popModes() | ||||
|     this.vim.vimStateMachine.popModes() | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -71,7 +71,7 @@ fun Editor.exitVisualMode() { | ||||
| fun Editor.exitSelectMode(adjustCaretPosition: Boolean) { | ||||
|   if (!this.inSelectMode) return | ||||
|  | ||||
|   this.vim.commandState.popModes() | ||||
|   this.vim.vimStateMachine.popModes() | ||||
|   SelectionVimListenerSuppressor.lock().use { | ||||
|     this.caretModel.allCarets.forEach { | ||||
|       it.removeSelection() | ||||
| @@ -91,7 +91,7 @@ fun Editor.exitSelectMode(adjustCaretPosition: Boolean) { | ||||
| fun VimEditor.exitSelectMode(adjustCaretPosition: Boolean) { | ||||
|   if (!this.inSelectMode) return | ||||
|  | ||||
|   this.commandState.popModes() | ||||
|   this.vimStateMachine.popModes() | ||||
|   SelectionVimListenerSuppressor.lock().use { | ||||
|     this.carets().forEach { vimCaret -> | ||||
|       val caret = (vimCaret as IjVimCaret).caret | ||||
|   | ||||
| @@ -31,7 +31,8 @@ import com.intellij.psi.PsiElement; | ||||
| import com.intellij.psi.PsiFile; | ||||
| import com.intellij.psi.util.PsiTreeUtil; | ||||
| import com.maddyhome.idea.vim.VimPlugin; | ||||
| import com.maddyhome.idea.vim.command.CommandState; | ||||
| import com.maddyhome.idea.vim.api.VimSearchHelperBase; | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine; | ||||
| import com.maddyhome.idea.vim.common.CharacterPosition; | ||||
| import com.maddyhome.idea.vim.common.Direction; | ||||
| import com.maddyhome.idea.vim.common.TextRange; | ||||
| @@ -529,6 +530,9 @@ public class SearchHelper { | ||||
|         CharSequence subSequence = chars.subSequence(startOffset, endOffset); | ||||
|         int inQuotePos = pos - startOffset; | ||||
|         int inQuoteStart = findBlockLocation(subSequence, close, type, Direction.BACKWARDS, inQuotePos, count, false); | ||||
|         if (inQuoteStart == -1) { | ||||
|           inQuoteStart = findBlockLocation(subSequence, close, type, Direction.FORWARDS, inQuotePos, count, false); | ||||
|         } | ||||
|         if (inQuoteStart != -1) { | ||||
|           startPosInStringFound = true; | ||||
|           int inQuoteEnd = findBlockLocation(subSequence, type, close, Direction.FORWARDS, inQuoteStart, 1, false); | ||||
| @@ -542,6 +546,9 @@ public class SearchHelper { | ||||
|  | ||||
|     if (!startPosInStringFound) { | ||||
|       bstart = findBlockLocation(chars, close, type, Direction.BACKWARDS, pos, count, false); | ||||
|       if (bstart == -1) { | ||||
|         bstart = findBlockLocation(chars, close, type, Direction.FORWARDS, pos, count, false); | ||||
|       } | ||||
|       if (bstart != -1) { | ||||
|         bend = findBlockLocation(chars, type, close, Direction.FORWARDS, bstart, 1, false); | ||||
|       } | ||||
| @@ -893,8 +900,8 @@ public class SearchHelper { | ||||
|         selectionEndWithoutNewline++; | ||||
|       } | ||||
|  | ||||
|       final CommandState.Mode mode = CommandState.getInstance(new IjVimEditor(editor)).getMode(); | ||||
|       if (mode == CommandState.Mode.VISUAL) { | ||||
|       final VimStateMachine.Mode mode = VimStateMachine.getInstance(new IjVimEditor(editor)).getMode(); | ||||
|       if (mode == VimStateMachine.Mode.VISUAL) { | ||||
|         if (closingTagTextRange.getStartOffset() == selectionEndWithoutNewline && | ||||
|           openingTag.getEndOffset() == selectionStart) { | ||||
|           // Special case: if the inner tag is already selected we should like isOuter is active | ||||
| @@ -1195,7 +1202,7 @@ public class SearchHelper { | ||||
|     int last = -1; | ||||
|     int res = start; | ||||
|     while (true) { | ||||
|       res = findNextWordOne(chars, res, end, 1, true, false); | ||||
|       res = (int)VimSearchHelperBase.Companion.findNextWordOne(chars, res, end, 1, true, false); | ||||
|       if (res == start || res == 0 || res > end || res == last) { | ||||
|         break; | ||||
|       } | ||||
| @@ -1224,105 +1231,6 @@ public class SearchHelper { | ||||
|     return new CountPosition(count, position); | ||||
|   } | ||||
|  | ||||
|   public static int findNextWord(@NotNull Editor editor, int searchFrom, int count, boolean bigWord) { | ||||
|     CharSequence chars = editor.getDocument().getCharsSequence(); | ||||
|     final int size = EditorHelperRt.getFileSize(editor); | ||||
|  | ||||
|     return findNextWord(chars, searchFrom, size, count, bigWord, false); | ||||
|   } | ||||
|  | ||||
|   public static int findNextWord(@NotNull CharSequence chars, | ||||
|                                  int pos, | ||||
|                                  int size, | ||||
|                                  int count, | ||||
|                                  boolean bigWord, | ||||
|                                  boolean spaceWords) { | ||||
|     int step = count >= 0 ? 1 : -1; | ||||
|     count = Math.abs(count); | ||||
|  | ||||
|     int res = pos; | ||||
|     for (int i = 0; i < count; i++) { | ||||
|       res = findNextWordOne(chars, res, size, step, bigWord, spaceWords); | ||||
|       if (res == pos || res == 0 || res == size - 1) { | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   private static int findNextWordOne(@NotNull CharSequence chars, | ||||
|                                      int pos, | ||||
|                                      int size, | ||||
|                                      int step, | ||||
|                                      boolean bigWord, | ||||
|                                      boolean spaceWords) { | ||||
|     boolean found = false; | ||||
|     pos = pos < size ? pos : Math.min(size, chars.length() - 1); | ||||
|     // For back searches, skip any current whitespace so we start at the end of a word | ||||
|     if (step < 0 && pos > 0) { | ||||
|       if (CharacterHelper.charType(chars.charAt(pos - 1), bigWord) == CharacterHelper.CharacterType.WHITESPACE && | ||||
|           !spaceWords) { | ||||
|         pos = skipSpace(chars, pos - 1, step, size) + 1; | ||||
|       } | ||||
|       if (pos > 0 && | ||||
|           CharacterHelper.charType(chars.charAt(pos), bigWord) != | ||||
|           CharacterHelper.charType(chars.charAt(pos - 1), bigWord)) { | ||||
|         pos += step; | ||||
|       } | ||||
|     } | ||||
|     int res = pos; | ||||
|     if (pos < 0 || pos >= size) { | ||||
|       return pos; | ||||
|     } | ||||
|  | ||||
|     CharacterHelper.CharacterType type = CharacterHelper.charType(chars.charAt(pos), bigWord); | ||||
|     if (type == CharacterHelper.CharacterType.WHITESPACE && step < 0 && pos > 0 && !spaceWords) { | ||||
|       type = CharacterHelper.charType(chars.charAt(pos - 1), bigWord); | ||||
|     } | ||||
|  | ||||
|     pos += step; | ||||
|     while (pos >= 0 && pos < size && !found) { | ||||
|       CharacterHelper.CharacterType newType = CharacterHelper.charType(chars.charAt(pos), bigWord); | ||||
|       if (newType != type) { | ||||
|         if (newType == CharacterHelper.CharacterType.WHITESPACE && step >= 0 && !spaceWords) { | ||||
|           pos = skipSpace(chars, pos, step, size); | ||||
|           res = pos; | ||||
|         } | ||||
|         else if (step < 0) { | ||||
|           res = pos + 1; | ||||
|         } | ||||
|         else { | ||||
|           res = pos; | ||||
|         } | ||||
|  | ||||
|         type = CharacterHelper.charType(chars.charAt(res), bigWord); | ||||
|         found = true; | ||||
|       } | ||||
|  | ||||
|       pos += step; | ||||
|     } | ||||
|  | ||||
|     if (found) { | ||||
|       if (res < 0) //(pos <= 0) | ||||
|       { | ||||
|         res = 0; | ||||
|       } | ||||
|       else if (res >= size) //(pos >= size) | ||||
|       { | ||||
|         res = size - 1; | ||||
|       } | ||||
|     } | ||||
|     else if (pos <= 0) { | ||||
|       res = 0; | ||||
|     } | ||||
|     else if (pos >= size) { | ||||
|       res = size; | ||||
|     } | ||||
|  | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   public static @NotNull List<Pair<TextRange, NumberType>> findNumbersInRange(final @NotNull Editor editor, | ||||
|                                                                               @NotNull TextRange textRange, | ||||
|                                                                               final boolean alpha, | ||||
| @@ -1648,10 +1556,10 @@ public class SearchHelper { | ||||
|  | ||||
|     if ((!onWordStart && !(startSpace && isOuter)) || hasSelection || (count > 1 && dir == -1)) { | ||||
|       if (dir == 1) { | ||||
|         start = findNextWord(chars, pos, max, -1, isBig, !isOuter); | ||||
|         start = (int)VimSearchHelperBase.Companion.findNextWord(chars, pos, max, -1, isBig, !isOuter); | ||||
|       } | ||||
|       else { | ||||
|         start = findNextWord(chars, pos, max, -(count - (onWordStart && !hasSelection ? 1 : 0)), isBig, !isOuter); | ||||
|         start = (int)VimSearchHelperBase.Companion.findNextWord(chars, pos, max, -(count - (onWordStart && !hasSelection ? 1 : 0)), isBig, !isOuter); | ||||
|       } | ||||
|  | ||||
|       start = EditorHelper.normalizeOffset(editor, start, false); | ||||
| @@ -1799,7 +1707,7 @@ public class SearchHelper { | ||||
|     if (step > 0 && pos < size - 1) { | ||||
|       if (CharacterHelper.charType(chars.charAt(pos + 1), bigWord) == CharacterHelper.CharacterType.WHITESPACE && | ||||
|           !spaceWords) { | ||||
|         pos = skipSpace(chars, pos + 1, step, size) - 1; | ||||
|         pos = (int)(VimSearchHelperBase.Companion.skipSpace(chars, pos + 1, step, size) - 1); | ||||
|       } | ||||
|       if (pos < size - 1 && | ||||
|           CharacterHelper.charType(chars.charAt(pos), bigWord) != | ||||
| @@ -1824,7 +1732,7 @@ public class SearchHelper { | ||||
|           res = pos - 1; | ||||
|         } | ||||
|         else if (newType == CharacterHelper.CharacterType.WHITESPACE && step < 0 && !spaceWords) { | ||||
|           pos = skipSpace(chars, pos, step, size); | ||||
|           pos = (int)VimSearchHelperBase.Companion.skipSpace(chars, pos, step, size); | ||||
|           res = pos; | ||||
|         } | ||||
|         else { | ||||
| @@ -1852,34 +1760,6 @@ public class SearchHelper { | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Skip whitespace starting with the supplied position. | ||||
|    * <p> | ||||
|    * An empty line is considered a whitespace break. | ||||
|    * | ||||
|    * @param chars  The text as a character array | ||||
|    * @param offset The starting position | ||||
|    * @param step   The direction to move | ||||
|    * @param size   The size of the document | ||||
|    * @return The new position. This will be the first non-whitespace character found or an empty line | ||||
|    */ | ||||
|   private static int skipSpace(@NotNull CharSequence chars, int offset, int step, int size) { | ||||
|     char prev = 0; | ||||
|     while (offset >= 0 && offset < size) { | ||||
|       final char c = chars.charAt(offset); | ||||
|       if (c == '\n' && c == prev) { | ||||
|         break; | ||||
|       } | ||||
|       if (CharacterHelper.charType(c, false) != CharacterHelper.CharacterType.WHITESPACE) { | ||||
|         break; | ||||
|       } | ||||
|       prev = c; | ||||
|       offset += step; | ||||
|     } | ||||
|  | ||||
|     return offset < size ? offset : size - 1; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * This locates the position with the document of the count-th occurrence of ch on the current line | ||||
|    * | ||||
|   | ||||
							
								
								
									
										45
									
								
								src/main/java/com/maddyhome/idea/vim/helper/StringHelper.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/main/java/com/maddyhome/idea/vim/helper/StringHelper.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| /* | ||||
|  * IdeaVim - Vim emulator for IDEs based on the IntelliJ platform | ||||
|  * Copyright (C) 2003-2022 The IdeaVim authors | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| package com.maddyhome.idea.vim.helper | ||||
|  | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import java.util.* | ||||
| import java.util.stream.Collectors | ||||
| import javax.swing.KeyStroke | ||||
|  | ||||
| /** | ||||
|  * COMPATIBILITY-LAYER: Created a helper class | ||||
|  * Please see: https://jb.gg/zo8n0r | ||||
|  */ | ||||
| object StringHelper { | ||||
|   @JvmStatic | ||||
|   fun parseKeys(string: String): List<KeyStroke> { | ||||
|     return injector.parser.parseKeys(string) | ||||
|   } | ||||
|  | ||||
|   @JvmStatic | ||||
|   fun parseKeys(vararg string: String): List<KeyStroke> { | ||||
|     return Arrays.stream(string).flatMap { o: String -> injector.parser.parseKeys(o).stream() } | ||||
|       .collect(Collectors.toList()) | ||||
|   } | ||||
|  | ||||
|   @JvmStatic | ||||
|   fun isCloseKeyStroke(stroke: KeyStroke): Boolean { | ||||
|     return stroke.isCloseKeyStroke() | ||||
|   } | ||||
| } | ||||
| @@ -21,8 +21,11 @@ package com.maddyhome.idea.vim.helper | ||||
| import com.intellij.openapi.actionSystem.CommonDataKeys | ||||
| import com.intellij.openapi.actionSystem.DataContext | ||||
| import com.intellij.openapi.actionSystem.PlatformDataKeys | ||||
| import com.intellij.openapi.command.CommandProcessor | ||||
| import com.intellij.openapi.command.impl.UndoManagerImpl | ||||
| import com.intellij.openapi.command.undo.UndoManager | ||||
| import com.intellij.openapi.components.Service | ||||
| import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.common.ChangesListener | ||||
| @@ -39,27 +42,36 @@ import com.maddyhome.idea.vim.vimscript.services.IjVimOptionService | ||||
|  */ | ||||
| @Service | ||||
| class UndoRedoHelper : UndoRedoBase() { | ||||
|   init { | ||||
|     injector.optionService.addListener(IjVimOptionService.oldUndo, { UndoManagerImpl.ourNeverAskUser = !injector.optionService.isSet(OptionScope.GLOBAL, IjVimOptionService.oldUndo) }, true) | ||||
|   } | ||||
|  | ||||
|   override fun undo(context: ExecutionContext): Boolean { | ||||
|     val ijContext = context.context as DataContext | ||||
|     val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false | ||||
|     val fileEditor = PlatformDataKeys.FILE_EDITOR.getData(ijContext) | ||||
|     val editor = CommonDataKeys.EDITOR.getData(context.ij) ?: return false | ||||
|     val vimEditor = editor.vim | ||||
|     val fileEditor = TextEditorProvider.getInstance().getTextEditor(editor) | ||||
|     val undoManager = UndoManager.getInstance(project) | ||||
|     if (fileEditor != null && undoManager.isUndoAvailable(fileEditor)) { | ||||
|     if (undoManager.isUndoAvailable(fileEditor)) { | ||||
|       if (injector.optionService.isSet(OptionScope.GLOBAL, IjVimOptionService.oldUndo)) { | ||||
|         SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) } | ||||
|       } else { | ||||
|         val editor = CommonDataKeys.EDITOR.getData(context.ij)?.vim | ||||
|         performUntilFileChanges(editor, { undoManager.isUndoAvailable(fileEditor) }, { undoManager.undo(fileEditor) }) | ||||
|         editor?.carets()?.forEach { | ||||
|         performUntilFileChanges(vimEditor, { undoManager.isUndoAvailable(fileEditor) }, { undoManager.undo(fileEditor) }) | ||||
|  | ||||
|         vimEditor.carets().forEach { | ||||
|           val ijCaret = it.ij | ||||
|           val hasSelection = ijCaret.hasSelection() | ||||
|           if (hasSelection) { | ||||
|             val selectionStart = ijCaret.selectionStart | ||||
|             CommandProcessor.getInstance().runUndoTransparentAction { | ||||
|               it.ij.removeSelection() | ||||
|               it.ij.moveToOffset(selectionStart) | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       return true | ||||
|     } | ||||
|     return false | ||||
| @@ -68,15 +80,18 @@ class UndoRedoHelper : UndoRedoBase() { | ||||
|   override fun redo(context: ExecutionContext): Boolean { | ||||
|     val ijContext = context.context as DataContext | ||||
|     val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false | ||||
|     val fileEditor = PlatformDataKeys.FILE_EDITOR.getData(ijContext) | ||||
|     val editor = CommonDataKeys.EDITOR.getData(context.ij) ?: return false | ||||
|     val vimEditor = editor.vim | ||||
|     val fileEditor = TextEditorProvider.getInstance().getTextEditor(editor) | ||||
|     val undoManager = UndoManager.getInstance(project) | ||||
|     if (fileEditor != null && undoManager.isRedoAvailable(fileEditor)) { | ||||
|     if (undoManager.isRedoAvailable(fileEditor)) { | ||||
|       if (injector.optionService.isSet(OptionScope.GLOBAL, IjVimOptionService.oldUndo)) { | ||||
|         SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) } | ||||
|       } else { | ||||
|         val editor = CommonDataKeys.EDITOR.getData(context.ij)?.vim | ||||
|         performUntilFileChanges(editor, { undoManager.isRedoAvailable(fileEditor) }, { undoManager.redo(fileEditor) }) | ||||
|         editor?.carets()?.forEach { it.ij.removeSelection() } | ||||
|         performUntilFileChanges(vimEditor, { undoManager.isRedoAvailable(fileEditor) }, { undoManager.redo(fileEditor) }) | ||||
|         CommandProcessor.getInstance().runUndoTransparentAction { | ||||
|           vimEditor.carets().forEach { it.ij.removeSelection() } | ||||
|         } | ||||
|       } | ||||
|       return true | ||||
|     } | ||||
| @@ -95,9 +110,15 @@ class UndoRedoHelper : UndoRedoBase() { | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     val oldPath = editor.getPath() | ||||
|     vimDocument.addChangeListener(changeListener) | ||||
|     while (check() && !changeListener.hasChanged) { | ||||
|     while (check() && !changeListener.hasChanged && !ifFilePathChanged(editor, oldPath)) { | ||||
|       action.run() | ||||
|     } | ||||
|     vimDocument.removeChangeListener(changeListener) | ||||
|   } | ||||
|  | ||||
|   private fun ifFilePathChanged(editor: IjVimEditor, oldPath: String?): Boolean { | ||||
|     return editor.getPath() != oldPath | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -27,8 +27,9 @@ import com.intellij.openapi.editor.RangeMarker | ||||
| import com.intellij.openapi.editor.markup.RangeHighlighter | ||||
| import com.intellij.openapi.util.Key | ||||
| import com.intellij.openapi.util.UserDataHolder | ||||
| import com.maddyhome.idea.vim.command.CommandState | ||||
| import com.maddyhome.idea.vim.api.CaretRegisterStorageBase | ||||
| import com.maddyhome.idea.vim.command.SelectionType | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.ex.ExOutputModel | ||||
| import com.maddyhome.idea.vim.group.visual.VisualChange | ||||
| import com.maddyhome.idea.vim.group.visual.vimLeadSelectionOffset | ||||
| @@ -73,11 +74,12 @@ var Caret.vimInsertStart: RangeMarker by userDataOr { | ||||
|     this.offset | ||||
|   ) | ||||
| } | ||||
| var Caret.registerStorage: CaretRegisterStorageBase? by userDataCaretToEditor() | ||||
|  | ||||
| // ------------------ Editor | ||||
| fun unInitializeEditor(editor: Editor) { | ||||
|   editor.vimLastSelectionType = null | ||||
|   editor.vimCommandState = null | ||||
|   editor.vimStateMachine = null | ||||
|   editor.vimMorePanel = null | ||||
|   editor.vimExOutput = null | ||||
|   editor.vimLastHighlighters = null | ||||
| @@ -91,7 +93,7 @@ var Editor.vimIncsearchCurrentMatchOffset: Int? by userData() | ||||
|  * @see :help visualmode() | ||||
|  */ | ||||
| var Editor.vimLastSelectionType: SelectionType? by userData() | ||||
| var Editor.vimCommandState: CommandState? by userData() | ||||
| var Editor.vimStateMachine: VimStateMachine? by userData() | ||||
| var Editor.vimEditorGroup: Boolean by userDataOr { false } | ||||
| var Editor.vimLineNumbersInitialState: Boolean by userDataOr { false } | ||||
| var Editor.vimHasRelativeLineNumbersInstalled: Boolean by userDataOr { false } | ||||
| @@ -103,7 +105,7 @@ var Editor.vimTestInputModel: TestInputModel? by userData() | ||||
|  * Checks whether a keeping visual mode visual operator action is performed on editor. | ||||
|  */ | ||||
| var Editor.vimKeepingVisualOperatorAction: Boolean by userDataOr { false } | ||||
| var Editor.vimChangeActionSwitchMode: CommandState.Mode? by userData() | ||||
| var Editor.vimChangeActionSwitchMode: VimStateMachine.Mode? by userData() | ||||
|  | ||||
| /** | ||||
|  * Function for delegated properties. | ||||
|   | ||||
| @@ -19,8 +19,6 @@ | ||||
| package com.maddyhome.idea.vim.key | ||||
|  | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.common.Node | ||||
| import com.maddyhome.idea.vim.common.addLeafs | ||||
|  | ||||
| fun <T> Node<T>.addLeafs(keys: String, actionHolder: T) { | ||||
|   addLeafs(injector.parser.parseKeys(keys), actionHolder) | ||||
|   | ||||
| @@ -21,8 +21,8 @@ package com.maddyhome.idea.vim.listener | ||||
| import com.intellij.openapi.actionSystem.ActionManager | ||||
| import com.intellij.openapi.actionSystem.AnAction | ||||
| import com.intellij.openapi.actionSystem.AnActionEvent | ||||
| import com.intellij.openapi.actionSystem.AnActionResult | ||||
| import com.intellij.openapi.actionSystem.CommonDataKeys | ||||
| import com.intellij.openapi.actionSystem.DataContext | ||||
| import com.intellij.openapi.actionSystem.IdeActions | ||||
| import com.intellij.openapi.actionSystem.ex.AnActionListener | ||||
| import com.intellij.openapi.editor.Caret | ||||
| @@ -35,7 +35,6 @@ import com.maddyhome.idea.vim.group.visual.VimVisualTimer | ||||
| import com.maddyhome.idea.vim.helper.fileSize | ||||
| import com.maddyhome.idea.vim.helper.inVisualMode | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import org.jetbrains.annotations.NotNull | ||||
|  | ||||
| /** | ||||
|  * A collection of hacks to improve the interaction with fancy AppCode templates | ||||
| @@ -50,16 +49,16 @@ object AppCodeTemplates { | ||||
|  | ||||
|     private var editor: Editor? = null | ||||
|  | ||||
|     override fun beforeActionPerformed(action: AnAction, dataContext: DataContext, event: AnActionEvent) { | ||||
|     override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) { | ||||
|       if (!VimPlugin.isEnabled()) return | ||||
|  | ||||
|       val hostEditor = dataContext.getData(CommonDataKeys.HOST_EDITOR) | ||||
|       val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR) | ||||
|       if (hostEditor != null) { | ||||
|         editor = hostEditor | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     override fun afterActionPerformed(action: AnAction, dataContext: DataContext, event: AnActionEvent) { | ||||
|     override fun afterActionPerformed(action: AnAction, event: AnActionEvent, result: AnActionResult) { | ||||
|       if (!VimPlugin.isEnabled()) return | ||||
|  | ||||
|       if (ActionManager.getInstance().getId(action) == IdeActions.ACTION_CHOOSE_LOOKUP_ITEM) { | ||||
| @@ -77,8 +76,8 @@ object AppCodeTemplates { | ||||
|  | ||||
|   @JvmStatic | ||||
|   fun onMovement( | ||||
|     editor: @NotNull Editor, | ||||
|     caret: @NotNull Caret, | ||||
|     editor: Editor, | ||||
|     caret: Caret, | ||||
|     toRight: Boolean, | ||||
|   ) { | ||||
|     val offset = caret.offset | ||||
|   | ||||
| @@ -19,8 +19,10 @@ | ||||
| package com.maddyhome.idea.vim.listener | ||||
|  | ||||
| import com.intellij.codeInsight.lookup.Lookup | ||||
| import com.intellij.codeInsight.lookup.LookupManager | ||||
| import com.intellij.codeInsight.lookup.LookupManagerListener | ||||
| import com.intellij.codeInsight.lookup.impl.LookupImpl | ||||
| import com.intellij.codeInsight.lookup.impl.actions.ChooseItemAction | ||||
| import com.intellij.codeInsight.template.Template | ||||
| import com.intellij.codeInsight.template.TemplateEditingAdapter | ||||
| import com.intellij.codeInsight.template.TemplateManagerListener | ||||
| @@ -29,25 +31,28 @@ import com.intellij.find.FindModelListener | ||||
| import com.intellij.openapi.actionSystem.ActionManager | ||||
| import com.intellij.openapi.actionSystem.AnAction | ||||
| import com.intellij.openapi.actionSystem.AnActionEvent | ||||
| import com.intellij.openapi.actionSystem.AnActionResult | ||||
| import com.intellij.openapi.actionSystem.CommonDataKeys | ||||
| import com.intellij.openapi.actionSystem.DataContext | ||||
| import com.intellij.openapi.actionSystem.ex.AnActionListener | ||||
| import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.intellij.openapi.project.DumbAwareToggleAction | ||||
| import com.intellij.openapi.util.TextRange | ||||
| import com.maddyhome.idea.vim.KeyHandler | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.command.CommandState | ||||
| import com.maddyhome.idea.vim.group.NotificationService | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.helper.EditorDataContext | ||||
| import com.maddyhome.idea.vim.helper.commandState | ||||
| import com.maddyhome.idea.vim.helper.inNormalMode | ||||
| import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere | ||||
| import com.maddyhome.idea.vim.helper.vimStateMachine | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.options.OptionConstants | ||||
| import com.maddyhome.idea.vim.options.OptionScope | ||||
| import com.maddyhome.idea.vim.vimscript.model.datatypes.VimInt | ||||
| import com.maddyhome.idea.vim.vimscript.model.options.helpers.IdeaRefactorModeHelper | ||||
| import org.jetbrains.annotations.NonNls | ||||
| import java.awt.event.KeyEvent | ||||
| import javax.swing.KeyStroke | ||||
|  | ||||
| /** | ||||
|  * @author Alex Plate | ||||
| @@ -59,27 +64,62 @@ object IdeaSpecifics { | ||||
|     private val surrounderAction = | ||||
|       "com.intellij.codeInsight.generation.surroundWith.SurroundWithHandler\$InvokeSurrounderAction" | ||||
|     private var editor: Editor? = null | ||||
|     override fun beforeActionPerformed(action: AnAction, dataContext: DataContext, event: AnActionEvent) { | ||||
|     private var completionPrevDocumentLength: Int? = null | ||||
|     private var completionPrevDocumentOffset: Int? = null | ||||
|     override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) { | ||||
|       if (!VimPlugin.isEnabled()) return | ||||
|  | ||||
|       val hostEditor = dataContext.getData(CommonDataKeys.HOST_EDITOR) | ||||
|       val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR) | ||||
|       if (hostEditor != null) { | ||||
|         editor = hostEditor | ||||
|       } | ||||
|  | ||||
|       //region Track action id | ||||
|       if (VimPlugin.getOptionService().isSet(OptionScope.GLOBAL, OptionConstants.trackactionidsName)) { | ||||
|         if (action !is NotificationService.ActionIdNotifier.CopyActionId && action !is NotificationService.ActionIdNotifier.StopTracking) { | ||||
|           val id: String? = ActionManager.getInstance().getId(action) | ||||
|           VimPlugin.getNotifications(dataContext.getData(CommonDataKeys.PROJECT)).notifyActionId(id) | ||||
|         } | ||||
|       } | ||||
|       //endregion | ||||
|         val id: String? = ActionManager.getInstance().getId(action) ?: (action.shortcutSet as? ProxyShortcutSet)?.actionId | ||||
|         VimPlugin.getNotifications(event.dataContext.getData(CommonDataKeys.PROJECT)).notifyActionId(id) | ||||
|       } | ||||
|  | ||||
|     override fun afterActionPerformed(action: AnAction, dataContext: DataContext, event: AnActionEvent) { | ||||
|       if (hostEditor != null && action is ChooseItemAction && hostEditor.vimStateMachine?.isRecording == true) { | ||||
|         val lookup = LookupManager.getActiveLookup(hostEditor) | ||||
|         if (lookup != null) { | ||||
|           val charsToRemove = hostEditor.caretModel.primaryCaret.offset - lookup.lookupStart | ||||
|  | ||||
|           val register = VimPlugin.getRegister() | ||||
|           val backSpace = KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0) | ||||
|           repeat(charsToRemove) { | ||||
|             register.recordKeyStroke(backSpace) | ||||
|           } | ||||
|  | ||||
|           completionPrevDocumentLength = hostEditor.document.textLength - charsToRemove | ||||
|           completionPrevDocumentOffset = lookup.lookupStart | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     override fun afterActionPerformed(action: AnAction, event: AnActionEvent, result: AnActionResult) { | ||||
|       if (!VimPlugin.isEnabled()) return | ||||
|  | ||||
|       val editor = editor | ||||
|       if (editor != null && action is ChooseItemAction && editor.vimStateMachine?.isRecording == true) { | ||||
|         val prevDocumentLength = completionPrevDocumentLength | ||||
|         val prevDocumentOffset = completionPrevDocumentOffset | ||||
|  | ||||
|         if (prevDocumentLength != null && prevDocumentOffset != null) { | ||||
|           val register = VimPlugin.getRegister() | ||||
|           val addedTextLength = editor.document.textLength - prevDocumentLength | ||||
|           val caretShift = addedTextLength - (editor.caretModel.primaryCaret.offset - prevDocumentOffset) | ||||
|           val leftArrow = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0) | ||||
|  | ||||
|           register.recordText(editor.document.getText(TextRange(prevDocumentOffset, prevDocumentOffset + addedTextLength))) | ||||
|           repeat(caretShift.coerceAtLeast(0)) { | ||||
|             register.recordKeyStroke(leftArrow) | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         this.completionPrevDocumentLength = null | ||||
|         this.completionPrevDocumentOffset = null | ||||
|       } | ||||
|  | ||||
|       //region Enter insert mode after surround with if | ||||
|       if (surrounderAction == action.javaClass.name && surrounderItems.any { | ||||
|         action.templatePresentation.text.endsWith( | ||||
| @@ -88,17 +128,17 @@ object IdeaSpecifics { | ||||
|       } | ||||
|       ) { | ||||
|         editor?.let { | ||||
|           val commandState = it.vim.commandState | ||||
|           while (commandState.mode != CommandState.Mode.COMMAND) { | ||||
|           val commandState = it.vim.vimStateMachine | ||||
|           while (commandState.mode != VimStateMachine.Mode.COMMAND) { | ||||
|             commandState.popModes() | ||||
|           } | ||||
|           VimPlugin.getChange().insertBeforeCursor(it.vim, dataContext.vim) | ||||
|           VimPlugin.getChange().insertBeforeCursor(it.vim, event.dataContext.vim) | ||||
|           KeyHandler.getInstance().reset(it.vim) | ||||
|         } | ||||
|       } | ||||
|       //endregion | ||||
|  | ||||
|       editor = null | ||||
|       this.editor = null | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -21,8 +21,8 @@ package com.maddyhome.idea.vim.listener | ||||
| import com.intellij.openapi.actionSystem.ActionManager | ||||
| import com.intellij.openapi.actionSystem.AnAction | ||||
| import com.intellij.openapi.actionSystem.AnActionEvent | ||||
| import com.intellij.openapi.actionSystem.AnActionResult | ||||
| import com.intellij.openapi.actionSystem.CommonDataKeys | ||||
| import com.intellij.openapi.actionSystem.DataContext | ||||
| import com.intellij.openapi.actionSystem.IdeActions | ||||
| import com.intellij.openapi.actionSystem.ex.AnActionListener | ||||
| import com.intellij.openapi.editor.Editor | ||||
| @@ -36,16 +36,16 @@ import com.maddyhome.idea.vim.helper.getTopLevelEditor | ||||
| class RiderActionListener : AnActionListener { | ||||
|  | ||||
|   private var editor: Editor? = null | ||||
|   override fun beforeActionPerformed(action: AnAction, dataContext: DataContext, event: AnActionEvent) { | ||||
|   override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) { | ||||
|     if (!VimPlugin.isEnabled()) return | ||||
|  | ||||
|     val hostEditor = dataContext.getData(CommonDataKeys.HOST_EDITOR) | ||||
|     val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR) | ||||
|     if (hostEditor != null) { | ||||
|       editor = hostEditor | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   override fun afterActionPerformed(action: AnAction, dataContext: DataContext, event: AnActionEvent) { | ||||
|   override fun afterActionPerformed(action: AnAction, event: AnActionEvent, result: AnActionResult) { | ||||
|     if (!VimPlugin.isEnabled()) return | ||||
|  | ||||
|     //region Extend Selection for Rider | ||||
|   | ||||
| @@ -36,15 +36,20 @@ import com.intellij.openapi.editor.event.SelectionEvent | ||||
| import com.intellij.openapi.editor.event.SelectionListener | ||||
| import com.intellij.openapi.editor.ex.DocumentEx | ||||
| import com.intellij.openapi.editor.impl.EditorComponentImpl | ||||
| import com.intellij.openapi.editor.impl.EditorImpl | ||||
| import com.intellij.openapi.fileEditor.FileEditorManagerEvent | ||||
| import com.intellij.openapi.fileEditor.FileEditorManagerListener | ||||
| import com.intellij.openapi.rd.createLifetime | ||||
| import com.intellij.openapi.rd.createNestedDisposable | ||||
| import com.intellij.openapi.util.Disposer | ||||
| import com.intellij.util.ExceptionUtil | ||||
| import com.jetbrains.rd.util.lifetime.intersect | ||||
| import com.maddyhome.idea.vim.EventFacade | ||||
| import com.maddyhome.idea.vim.KeyHandler | ||||
| import com.maddyhome.idea.vim.VimKeyListener | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.VimTypedActionHandler | ||||
| import com.maddyhome.idea.vim.command.CommandState | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.ex.ExOutputModel | ||||
| import com.maddyhome.idea.vim.group.EditorGroup | ||||
| import com.maddyhome.idea.vim.group.FileGroup | ||||
| @@ -73,8 +78,6 @@ import com.maddyhome.idea.vim.helper.vimLastColumn | ||||
| import com.maddyhome.idea.vim.listener.MouseEventsDataHolder.skipEvents | ||||
| import com.maddyhome.idea.vim.listener.MouseEventsDataHolder.skipNDragEvents | ||||
| import com.maddyhome.idea.vim.listener.VimListenerManager.EditorListeners.add | ||||
| import com.maddyhome.idea.vim.listener.VimListenerManager.EditorListeners.remove | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
| import com.maddyhome.idea.vim.options.OptionConstants | ||||
| import com.maddyhome.idea.vim.options.OptionScope | ||||
| @@ -122,9 +125,9 @@ object VimListenerManager { | ||||
|       VimPlugin.getOptionService().addListener(OptionConstants.guicursorName, GuicursorChangeListener) | ||||
|       VimPlugin.getOptionService().addListener(OptionConstants.iskeywordName, KeywordOptionChangeListener, true) | ||||
|  | ||||
|       EventFacade.getInstance().addEditorFactoryListener(VimEditorFactoryListener, VimPlugin.getInstance()) | ||||
|       EventFacade.getInstance().addEditorFactoryListener(VimEditorFactoryListener, VimPlugin.getInstance().onOffDisposable) | ||||
|  | ||||
|       EditorFactory.getInstance().eventMulticaster.addCaretListener(VimCaretListener, VimPlugin.getInstance()) | ||||
|       EditorFactory.getInstance().eventMulticaster.addCaretListener(VimCaretListener, VimPlugin.getInstance().onOffDisposable) | ||||
|     } | ||||
|  | ||||
|     fun disable() { | ||||
| @@ -136,10 +139,6 @@ object VimListenerManager { | ||||
|       VimPlugin.getOptionService().removeListener(OptionConstants.showcmdName, ShowCmdOptionChangeListener) | ||||
|       VimPlugin.getOptionService().removeListener(OptionConstants.guicursorName, GuicursorChangeListener) | ||||
|       VimPlugin.getOptionService().removeListener(OptionConstants.iskeywordName, KeywordOptionChangeListener) | ||||
|  | ||||
|       EventFacade.getInstance().removeEditorFactoryListener(VimEditorFactoryListener) | ||||
|  | ||||
|       EditorFactory.getInstance().eventMulticaster.removeCaretListener(VimCaretListener) | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -157,17 +156,26 @@ object VimListenerManager { | ||||
|     } | ||||
|  | ||||
|     fun add(editor: Editor) { | ||||
|       val pluginLifetime = VimPlugin.getInstance().createLifetime() | ||||
|       val editorLifetime = (editor as EditorImpl).disposable.createLifetime() | ||||
|       val disposable = editorLifetime.intersect(pluginLifetime).createNestedDisposable("MyLifetimedDisposable") | ||||
|  | ||||
|       editor.contentComponent.addKeyListener(VimKeyListener) | ||||
|       Disposer.register(disposable) { editor.contentComponent.removeKeyListener(VimKeyListener) } | ||||
|  | ||||
|       val eventFacade = EventFacade.getInstance() | ||||
|       eventFacade.addEditorMouseListener(editor, EditorMouseHandler) | ||||
|       eventFacade.addEditorMouseMotionListener(editor, EditorMouseHandler) | ||||
|       eventFacade.addEditorSelectionListener(editor, EditorSelectionHandler) | ||||
|       eventFacade.addComponentMouseListener(editor.contentComponent, ComponentMouseListener) | ||||
|       eventFacade.addEditorMouseListener(editor, EditorMouseHandler, disposable) | ||||
|       eventFacade.addEditorMouseMotionListener(editor, EditorMouseHandler, disposable) | ||||
|       eventFacade.addEditorSelectionListener(editor, EditorSelectionHandler, disposable) | ||||
|       eventFacade.addComponentMouseListener(editor.contentComponent, ComponentMouseListener, disposable) | ||||
|  | ||||
|       VimPlugin.getEditor().editorCreated(editor) | ||||
|  | ||||
|       VimPlugin.getChange().editorCreated(IjVimEditor(editor)) | ||||
|       VimPlugin.getChange().editorCreated(editor, disposable) | ||||
|  | ||||
|       Disposer.register(disposable) { | ||||
|         VimPlugin.getEditorIfCreated()?.editorDeinit(editor, true) | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     fun remove(editor: Editor, isReleased: Boolean) { | ||||
| @@ -181,7 +189,7 @@ object VimListenerManager { | ||||
|  | ||||
|       VimPlugin.getEditorIfCreated()?.editorDeinit(editor, isReleased) | ||||
|  | ||||
|       VimPlugin.getChange().editorReleased(IjVimEditor(editor)) | ||||
|       VimPlugin.getChange().editorReleased(editor) | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -213,7 +221,6 @@ object VimListenerManager { | ||||
|     } | ||||
|  | ||||
|     override fun editorReleased(event: EditorFactoryEvent) { | ||||
|       remove(event.editor, true) | ||||
|       VimPlugin.getMark().editorReleased(event) | ||||
|     } | ||||
|   } | ||||
| @@ -425,7 +432,7 @@ object VimListenerManager { | ||||
|         ExOutputModel.getInstance(editor).clear() | ||||
|  | ||||
|         val caretModel = editor.caretModel | ||||
|         if (editor.subMode != CommandState.SubMode.NONE) { | ||||
|         if (editor.subMode != VimStateMachine.SubMode.NONE) { | ||||
|           caretModel.removeSecondaryCarets() | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -28,11 +28,12 @@ import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.LineDeleteShift | ||||
| import com.maddyhome.idea.vim.api.VimCaret | ||||
| import com.maddyhome.idea.vim.api.VimChangeGroupBase | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.VimMotionGroupBase | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.CommandState | ||||
| import com.maddyhome.idea.vim.command.SelectionType | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.common.EditorLine | ||||
| import com.maddyhome.idea.vim.common.IndentConfig | ||||
| import com.maddyhome.idea.vim.common.OperatedRange | ||||
| @@ -40,7 +41,6 @@ import com.maddyhome.idea.vim.common.TextRange | ||||
| import com.maddyhome.idea.vim.common.VimRange | ||||
| import com.maddyhome.idea.vim.common.including | ||||
| import com.maddyhome.idea.vim.common.offset | ||||
| import com.maddyhome.idea.vim.group.ChangeGroup | ||||
| import com.maddyhome.idea.vim.group.MotionGroup | ||||
| import com.maddyhome.idea.vim.helper.EditorHelper | ||||
| import com.maddyhome.idea.vim.helper.inlayAwareVisualColumn | ||||
| @@ -62,7 +62,7 @@ fun changeRange( | ||||
|   var col = 0 | ||||
|   var lines = 0 | ||||
|   if (type === SelectionType.BLOCK_WISE) { | ||||
|     lines = ChangeGroup.getLinesCountInVisualBlock(IjVimEditor(editor), range) | ||||
|     lines = VimChangeGroupBase.getLinesCountInVisualBlock(IjVimEditor(editor), range) | ||||
|     col = editor.offsetToLogicalPosition(range.startOffset).column | ||||
|     if (caret.vimLastColumn == VimMotionGroupBase.LAST_COLUMN) { | ||||
|       col = VimMotionGroupBase.LAST_COLUMN | ||||
| @@ -99,11 +99,12 @@ fun changeRange( | ||||
|           vimCaret.moveToOffset(deletedInfo.leftOffset.point) | ||||
|         } | ||||
|         is OperatedRange.Block -> TODO() | ||||
|         else -> TODO() | ||||
|       } | ||||
|       if (type == SelectionType.BLOCK_WISE) { | ||||
|         VimPlugin.getChange().setInsertRepeat(lines, col, false) | ||||
|       } | ||||
|       editor.vimChangeActionSwitchMode = CommandState.Mode.INSERT | ||||
|       editor.vimChangeActionSwitchMode = VimStateMachine.Mode.INSERT | ||||
|     } | ||||
|   } else { | ||||
|     VimPlugin.getChange().insertBeforeCursor(editor.vim, context.vim) | ||||
| @@ -158,9 +159,9 @@ fun deleteRange( | ||||
| fun insertLineAround(editor: VimEditor, context: ExecutionContext, shift: Int) { | ||||
|   val project = (editor as IjVimEditor).editor.project | ||||
|  | ||||
|   VimPlugin.getChange().initInsert(editor, context, CommandState.Mode.INSERT) | ||||
|   VimPlugin.getChange().initInsert(editor, context, VimStateMachine.Mode.INSERT) | ||||
|  | ||||
|   if (!CommandState.getInstance(editor).isDotRepeatInProgress) { | ||||
|   if (!VimStateMachine.getInstance(editor).isDotRepeatInProgress) { | ||||
|     for (vimCaret in editor.carets()) { | ||||
|       val caret = (vimCaret as IjVimCaret).caret | ||||
|       val line = vimCaret.getLine() | ||||
| @@ -179,7 +180,7 @@ fun insertLineAround(editor: VimEditor, context: ExecutionContext, shift: Int) { | ||||
|       } | ||||
|       val position = EditorLine.Offset.init(editor.offsetToLogicalPosition(lineEndOffset).line + shift, editor) | ||||
|  | ||||
|       val insertedLine = editor.addLine(position) ?: continue | ||||
|       val insertedLine = editor.addLine(position) | ||||
|       VimPlugin.getChange().saveStrokes("\n") | ||||
|  | ||||
|       var lineStart = editor.getLineRange(insertedLine).first | ||||
|   | ||||
| @@ -49,6 +49,7 @@ class IjClipboardManager : VimClipboardManager { | ||||
|     return Pair(res, transferableData) | ||||
|   } | ||||
|  | ||||
|   @Suppress("UNCHECKED_CAST") | ||||
|   override fun setClipboardText(text: String, rawText: String, transferableData: List<Any>): Any? { | ||||
|     val transferableData1 = (transferableData as List<TextBlockTransferableData>).toMutableList() | ||||
|     try { | ||||
| @@ -97,6 +98,7 @@ class IjClipboardManager : VimClipboardManager { | ||||
|     return transferableData | ||||
|   } | ||||
|  | ||||
|   @Suppress("UNCHECKED_CAST") | ||||
|   override fun preprocessText( | ||||
|     vimEditor: VimEditor, | ||||
|     textRange: TextRange, | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user