mirror of
				https://github.com/chylex/IntelliJ-IdeaVim.git
				synced 2025-11-04 01:40:12 +01:00 
			
		
		
		
	Compare commits
	
		
			237 Commits
		
	
	
		
			VIM-2227
			...
			customized
		
	
	| 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,28 +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)
 | 
			
		||||
        // Extract the inner value
 | 
			
		||||
        perform("di" + pick(charFrom), editor)
 | 
			
		||||
        val innerValue: MutableList<KeyStroke> = getRegister(REGISTER)?.toMutableList() ?: mutableListOf()
 | 
			
		||||
        // 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) }
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      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()
 | 
			
		||||
        }
 | 
			
		||||
        pasteSurround(innerValue, editor)
 | 
			
		||||
        // Restore the old value
 | 
			
		||||
        setRegister(REGISTER, oldValue)
 | 
			
		||||
        // Jump back to start
 | 
			
		||||
        executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      private fun perform(sequence: String, editor: Editor) {
 | 
			
		||||
@@ -144,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' -> ']'
 | 
			
		||||
@@ -167,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) {
 | 
			
		||||
@@ -175,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