mirror of
				https://github.com/chylex/IntelliJ-IdeaVim.git
				synced 2025-11-04 01:40:12 +01:00 
			
		
		
		
	Compare commits
	
		
			215 Commits
		
	
	
		
			customized
			...
			customized
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						cc9385f2a9
	
				 | 
					
					
						|||
| 
						
						
							
						
						32a2384a46
	
				 | 
					
					
						|||
| 
						
						
							
						
						fdd850de5a
	
				 | 
					
					
						|||
| 
						
						
							
						
						0f7116b136
	
				 | 
					
					
						|||
| 
						
						
							
						
						db8f0251fb
	
				 | 
					
					
						|||
| 
						
						
							
						
						2ca2c1e774
	
				 | 
					
					
						|||
| 
						
						
							
						
						f3c32da4d1
	
				 | 
					
					
						|||
| 
						
						
							
						
						1a3b34d457
	
				 | 
					
					
						|||
| 
						
						
							
						
						1f9159996d
	
				 | 
					
					
						|||
| 
						
						
							
						
						65c3acd891
	
				 | 
					
					
						|||
| 
						
						
							
						
						223f65c003
	
				 | 
					
					
						|||
| 
						
						
							
						
						293b854620
	
				 | 
					
					
						|||
| 
						
						
							
						
						9e7c5fd603
	
				 | 
					
					
						|||
| 
						
						
							
						
						00c799595f
	
				 | 
					
					
						|||
| 
						
						
							
						
						8577b5ed20
	
				 | 
					
					
						|||
| 
						
						
							
						
						3af7a991a0
	
				 | 
					
					
						|||
| 
						
						
							
						
						212af1798d
	
				 | 
					
					
						|||
| 
						
						
							
						
						002ef8f72f
	
				 | 
					
					
						|||
| 
						
						
							
						
						9115af6b3d
	
				 | 
					
					
						|||
| 
						
						
							
						
						25ca42d371
	
				 | 
					
					
						|||
| 
						
						
							
						
						408687c9b3
	
				 | 
					
					
						|||
| 
						
						
							
						
						85e00bf8fc
	
				 | 
					
					
						|||
| 
						
						
							
						
						bd6f2d4b2f
	
				 | 
					
					
						|||
| 
						 | 
					e406885ec6 | ||
| 
						 | 
					9bbeab8062 | ||
| 
						 | 
					373bfc4eab | ||
| 
						 | 
					050f2f7b97 | ||
| 
						 | 
					e30bc14843 | ||
| 
						 | 
					76d590be11 | ||
| 
						 | 
					b005328b4a | ||
| 
						 | 
					ad20021cee | ||
| 
						 | 
					126de5c218 | ||
| 
						 | 
					0f7aef3f15 | ||
| 
						 | 
					f352b84922 | ||
| 
						 | 
					789faa7cb2 | ||
| 
						 | 
					a338f5768a | ||
| 
						 | 
					8205c74571 | ||
| 
						 | 
					def40eb409 | ||
| 
						 | 
					010e8a7541 | ||
| 
						 | 
					46c6778b3a | ||
| 
						 | 
					0977bd4400 | ||
| 
						 | 
					db092e9b0a | ||
| 
						 | 
					5cfb98e188 | ||
| 
						 | 
					3a95b62885 | ||
| 
						 | 
					93e6adf5a9 | ||
| 
						 | 
					37204398ff | ||
| 
						 | 
					b2f450d14d | ||
| 
						 | 
					61da888571 | ||
| 
						 | 
					fcda97cfb8 | ||
| 
						 | 
					1dc7ea6363 | ||
| 
						 | 
					bb507db884 | ||
| 
						 | 
					7b0482ed94 | ||
| 
						 | 
					1c79b0d59a | ||
| 
						 | 
					ff4eb31418 | ||
| 
						 | 
					cb1078cf70 | ||
| 
						 | 
					da3e40eaf6 | ||
| 
						 | 
					17f77a9639 | ||
| 
						 | 
					3d03494354 | ||
| 
						 | 
					642caddda7 | ||
| 
						 | 
					1d97c43e30 | ||
| 
						 | 
					7ed3e3b53c | ||
| 
						 | 
					aa39ca2006 | ||
| 
						 | 
					bb122903de | ||
| 
						 | 
					c84a3cf64d | ||
| 
						 | 
					f4c9464b8a | ||
| 
						 | 
					3ba14d05b4 | ||
| 
						 | 
					2189b70b87 | ||
| 
						 | 
					ea2222f9d5 | ||
| 
						 | 
					7d68d41888 | ||
| 
						 | 
					44749b6d8c | ||
| 
						 | 
					1ce4dbc569 | ||
| 
						 | 
					db59513505 | ||
| 
						 | 
					8c9ff9465f | ||
| 
						 | 
					1a2322ddec | ||
| 
						 | 
					fbbd1ebc0d | ||
| 
						 | 
					8d5df11372 | ||
| 
						 | 
					dfebe542d8 | ||
| 
						 | 
					7fde47c08b | ||
| 
						 | 
					629919f634 | ||
| 
						 | 
					43fff1c73e | ||
| 
						 | 
					d02f0e17ca | ||
| 
						 | 
					5dc860f61e | ||
| 
						 | 
					956e726c31 | ||
| 
						 | 
					446067e2fe | ||
| 
						 | 
					bd53a895c0 | ||
| 
						 | 
					e61fed2467 | ||
| 
						 | 
					f5e125759f | ||
| 
						 | 
					622eb887c8 | ||
| 
						 | 
					9fb614e16c | ||
| 
						 | 
					1e0fa07768 | ||
| 
						 | 
					bfb1d5b7f5 | ||
| 
						 | 
					2fad8790a9 | ||
| 
						 | 
					9719106a14 | ||
| 
						 | 
					870a0da2a2 | ||
| 
						 | 
					d3c315d299 | ||
| 
						 | 
					2583b6e792 | ||
| 
						 | 
					89417eb4f6 | ||
| 
						 | 
					560700c9aa | ||
| 
						 | 
					24514039e1 | ||
| 
						 | 
					ff44596c1a | ||
| 
						 | 
					b001d63fd9 | ||
| 
						 | 
					5db96bef28 | ||
| 
						 | 
					39c615cddd | ||
| 
						 | 
					961173a93b | ||
| 
						 | 
					92741c6356 | ||
| 
						 | 
					643eb2a85f | ||
| 
						 | 
					883744e4ee | ||
| 
						 | 
					66173e03be | ||
| 
						 | 
					e455722758 | ||
| 
						 | 
					823bdc1561 | ||
| 
						 | 
					f91fda2ca5 | ||
| 
						 | 
					92abd76615 | ||
| 
						 | 
					57c45ca153 | ||
| 
						 | 
					7c623ae4b5 | ||
| 
						 | 
					f2ef92cdef | ||
| 
						 | 
					e8e6eabe97 | ||
| 
						 | 
					ef1c915264 | ||
| 
						 | 
					a5e2168f7f | ||
| 
						 | 
					83e5470b3a | ||
| 
						 | 
					c446de8979 | ||
| 
						 | 
					13426915f4 | ||
| 
						 | 
					d766c3b8ee | ||
| 
						 | 
					844bc01537 | ||
| 
						 | 
					3d2d32b022 | ||
| 
						 | 
					a8677d3dd7 | ||
| 
						 | 
					7217fdf734 | ||
| 
						 | 
					0c867b3869 | ||
| 
						 | 
					b3bcab4336 | ||
| 
						 | 
					fe1b48a9b3 | ||
| 
						 | 
					b5a0862520 | ||
| 
						 | 
					babc1f54e5 | ||
| 
						 | 
					32b910a65b | ||
| 
						 | 
					2aa71a0008 | ||
| 
						 | 
					c2c0c2aba2 | ||
| 
						 | 
					6a10cf5e0d | ||
| 
						 | 
					90474a3a4f | ||
| 
						 | 
					be43f74bc6 | ||
| 
						 | 
					5916c42cd1 | ||
| 
						 | 
					a43c7ece32 | ||
| 
						 | 
					40c1070b1a | ||
| 
						 | 
					75ccdb2a4d | ||
| 
						 | 
					3de7b0ca78 | ||
| 
						 | 
					448e32a6cc | ||
| 
						 | 
					4a85058ba2 | ||
| 
						 | 
					7e28deb328 | ||
| 
						 | 
					f3767b53b7 | ||
| 
						 | 
					1026e27e64 | ||
| 
						 | 
					18d653a9ae | ||
| 
						 | 
					fcf4b44443 | ||
| 
						 | 
					907e44b1d7 | ||
| 
						 | 
					6c9b39a623 | ||
| 
						 | 
					a3cb093b42 | ||
| 
						 | 
					524e854c61 | ||
| 
						 | 
					5588c27037 | ||
| 
						 | 
					90d36eea98 | ||
| 
						 | 
					f4414de86c | ||
| 
						 | 
					d46102ccaf | ||
| 
						 | 
					82347f5f0d | ||
| 
						 | 
					c594f28acb | ||
| 
						 | 
					bf6517e58f | ||
| 
						 | 
					aec2f4c435 | ||
| 
						 | 
					8f905758d5 | ||
| 
						 | 
					80cc236f48 | ||
| 
						 | 
					e432a02a45 | ||
| 
						 | 
					d7894fa7f4 | ||
| 
						 | 
					853d7032f0 | ||
| 
						 | 
					5f9f57e1c0 | ||
| 
						 | 
					f4381c8216 | ||
| 
						 | 
					20eee7cae7 | ||
| 
						 | 
					33392c2148 | ||
| 
						 | 
					bb67564fbe | ||
| 
						 | 
					61ccbcd788 | ||
| 
						 | 
					1dbaa3be6d | ||
| 
						 | 
					872bc22830 | ||
| 
						 | 
					ce23ed814c | ||
| 
						 | 
					82cd534756 | ||
| 
						 | 
					673809d6b9 | ||
| 
						 | 
					cdbaf73b1e | ||
| 
						 | 
					7f911b7e72 | ||
| 
						 | 
					c03a2dfe7e | ||
| 
						 | 
					75935ce4d1 | ||
| 
						 | 
					f0b203409e | ||
| 
						 | 
					b3b369eb59 | ||
| 
						 | 
					96072982cf | ||
| 
						 | 
					de016fc445 | ||
| 
						 | 
					554d9b5f7b | ||
| 
						 | 
					149edefad5 | ||
| 
						 | 
					52d3840c83 | ||
| 
						 | 
					793677d4fd | ||
| 
						 | 
					2376ee4877 | ||
| 
						 | 
					68dcab6262 | ||
| 
						 | 
					f38fd3512c | ||
| 
						 | 
					e515278ba3 | ||
| 
						 | 
					9cc69e41ee | ||
| 
						 | 
					2109ff235c | ||
| 
						 | 
					d6910aa81d | ||
| 
						 | 
					8369391902 | ||
| 
						 | 
					f336807498 | ||
| 
						 | 
					14ba5d7126 | ||
| 
						 | 
					288394d25f | ||
| 
						 | 
					fb08b5fd65 | ||
| 
						 | 
					3465e11c3a | ||
| 
						 | 
					e07a16863e | ||
| 
						 | 
					64f7532510 | ||
| 
						 | 
					dd892e77fb | ||
| 
						 | 
					65aeeba521 | ||
| 
						 | 
					ca3e56d0d6 | ||
| 
						 | 
					bcbfb0dc32 | ||
| 
						 | 
					46a4a10e63 | ||
| 
						 | 
					d5bddd077f | ||
| 
						 | 
					e22fd263cc | ||
| 
						 | 
					f3902d0ae0 | ||
| 
						 | 
					f9213ee45d | ||
| 
						 | 
					281bc2573e | ||
| 
						 | 
					ae8c7f6bfa | 
							
								
								
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
* text=auto eol=lf
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/integrationsTest.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/integrationsTest.yml
									
									
									
									
										vendored
									
									
								
							@@ -27,7 +27,7 @@ jobs:
 | 
			
		||||
          settings-path: ${{ github.workspace }} # location for the settings.xml file
 | 
			
		||||
 | 
			
		||||
      - name: Run tests
 | 
			
		||||
        run: ./gradlew integrationsTest
 | 
			
		||||
        run: ./gradlew --no-configuration-cache integrationsTest
 | 
			
		||||
        env:
 | 
			
		||||
          YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
 | 
			
		||||
          GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								.github/workflows/runUiOctopusTests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/workflows/runUiOctopusTests.yml
									
									
									
									
										vendored
									
									
								
							@@ -16,14 +16,14 @@ jobs:
 | 
			
		||||
          java-version: 17
 | 
			
		||||
      - name: Setup FFmpeg
 | 
			
		||||
        run: brew install ffmpeg
 | 
			
		||||
      - name: Setup Gradle
 | 
			
		||||
        uses: gradle/gradle-build-action@v2.4.2
 | 
			
		||||
#      - name: Setup Gradle
 | 
			
		||||
#        uses: gradle/gradle-build-action@v2.4.2
 | 
			
		||||
      - name: Build Plugin
 | 
			
		||||
        run: gradle :buildPlugin
 | 
			
		||||
      - name: Run Idea
 | 
			
		||||
        run: |
 | 
			
		||||
          mkdir -p build/reports
 | 
			
		||||
          gradle testIdeUi -Doctopus.handler=false > build/reports/idea.log &
 | 
			
		||||
          gradle --no-configuration-cache runIdeForUiTests -Doctopus.handler=false > build/reports/idea.log &
 | 
			
		||||
      - name: Wait for Idea started
 | 
			
		||||
        uses: jtalk/url-health-check-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
@@ -37,7 +37,7 @@ jobs:
 | 
			
		||||
        run: mv tests/ui-ij-tests/video build/reports
 | 
			
		||||
      - name: Move sandbox logs
 | 
			
		||||
        if: always()
 | 
			
		||||
        run: mv build/idea-sandbox/system/log sandbox-idea-log
 | 
			
		||||
        run: mv build/idea-sandbox/IC-2024.1.2/log_runIdeForUiTests idea-sandbox-log
 | 
			
		||||
      - name: Save report
 | 
			
		||||
        if: always()
 | 
			
		||||
        uses: actions/upload-artifact@v4
 | 
			
		||||
@@ -46,7 +46,7 @@ jobs:
 | 
			
		||||
          path: |
 | 
			
		||||
            build/reports
 | 
			
		||||
            tests/ui-ij-tests/build/reports
 | 
			
		||||
            sandbox-idea-log
 | 
			
		||||
            idea-sandbox-log
 | 
			
		||||
#  build-for-ui-test-linux:
 | 
			
		||||
#    runs-on: ubuntu-latest
 | 
			
		||||
#    steps:
 | 
			
		||||
@@ -63,7 +63,7 @@ jobs:
 | 
			
		||||
#          export DISPLAY=:99.0
 | 
			
		||||
#          Xvfb -ac :99 -screen 0 1920x1080x16 &
 | 
			
		||||
#          mkdir -p build/reports
 | 
			
		||||
#          gradle :testIdeUi #> build/reports/idea.log
 | 
			
		||||
#          gradle :runIdeForUiTests #> build/reports/idea.log
 | 
			
		||||
#      - name: Wait for Idea started
 | 
			
		||||
#        uses: jtalk/url-health-check-action@1.5
 | 
			
		||||
#        with:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								.github/workflows/runUiPyTests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/runUiPyTests.yml
									
									
									
									
										vendored
									
									
								
							@@ -19,14 +19,14 @@ jobs:
 | 
			
		||||
          python-version: '3.10'
 | 
			
		||||
      - name: Setup FFmpeg
 | 
			
		||||
        run: brew install ffmpeg
 | 
			
		||||
      - name: Setup Gradle
 | 
			
		||||
        uses: gradle/gradle-build-action@v2.4.2
 | 
			
		||||
#      - name: Setup Gradle
 | 
			
		||||
#        uses: gradle/gradle-build-action@v2.4.2
 | 
			
		||||
      - name: Build Plugin
 | 
			
		||||
        run: gradle :buildPlugin
 | 
			
		||||
      - name: Run Idea
 | 
			
		||||
        run: |
 | 
			
		||||
          mkdir -p build/reports
 | 
			
		||||
          gradle :testIdeUi -PideaType=PC > build/reports/idea.log &
 | 
			
		||||
          gradle --no-configuration-cache :runIdeForUiTests -PideaType=PC > build/reports/idea.log &
 | 
			
		||||
      - name: Wait for Idea started
 | 
			
		||||
        uses: jtalk/url-health-check-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
@@ -40,7 +40,7 @@ jobs:
 | 
			
		||||
        run: mv tests/ui-py-tests/video build/reports
 | 
			
		||||
      - name: Move sandbox logs
 | 
			
		||||
        if: always()
 | 
			
		||||
        run: mv build/idea-sandbox/system/log sandbox-idea-log
 | 
			
		||||
        run: mv build/idea-sandbox/PC-2024.1.2/log_runIdeForUiTests idea-sandbox-log
 | 
			
		||||
      - name: Save report
 | 
			
		||||
        if: always()
 | 
			
		||||
        uses: actions/upload-artifact@v4
 | 
			
		||||
@@ -49,4 +49,4 @@ jobs:
 | 
			
		||||
          path: |
 | 
			
		||||
            build/reports
 | 
			
		||||
            tests/ui-py-tests/build/reports
 | 
			
		||||
            sandbox-idea-log
 | 
			
		||||
            idea-sandbox-log
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								.github/workflows/runUiTests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/workflows/runUiTests.yml
									
									
									
									
										vendored
									
									
								
							@@ -16,14 +16,14 @@ jobs:
 | 
			
		||||
          java-version: 17
 | 
			
		||||
      - name: Setup FFmpeg
 | 
			
		||||
        run: brew install ffmpeg
 | 
			
		||||
      - name: Setup Gradle
 | 
			
		||||
        uses: gradle/gradle-build-action@v2.4.2
 | 
			
		||||
#      - name: Setup Gradle
 | 
			
		||||
#        uses: gradle/gradle-build-action@v2.4.2
 | 
			
		||||
      - name: Build Plugin
 | 
			
		||||
        run: gradle :buildPlugin
 | 
			
		||||
      - name: Run Idea
 | 
			
		||||
        run: |
 | 
			
		||||
          mkdir -p build/reports
 | 
			
		||||
          gradle testIdeUi > build/reports/idea.log &
 | 
			
		||||
          gradle --no-configuration-cache runIdeForUiTests > build/reports/idea.log &
 | 
			
		||||
      - name: Wait for Idea started
 | 
			
		||||
        uses: jtalk/url-health-check-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
@@ -37,7 +37,7 @@ jobs:
 | 
			
		||||
        run: mv tests/ui-ij-tests/video build/reports
 | 
			
		||||
      - name: Move sandbox logs
 | 
			
		||||
        if: always()
 | 
			
		||||
        run: mv build/idea-sandbox/system/log sandbox-idea-log
 | 
			
		||||
        run: mv build/idea-sandbox/IC-2024.1.2/log_runIdeForUiTests idea-sandbox-log
 | 
			
		||||
      - name: Save report
 | 
			
		||||
        if: always()
 | 
			
		||||
        uses: actions/upload-artifact@v4
 | 
			
		||||
@@ -46,7 +46,7 @@ jobs:
 | 
			
		||||
          path: |
 | 
			
		||||
            build/reports
 | 
			
		||||
            tests/ui-ij-tests/build/reports
 | 
			
		||||
            sandbox-idea-log
 | 
			
		||||
            idea-sandbox-log
 | 
			
		||||
#  build-for-ui-test-linux:
 | 
			
		||||
#    runs-on: ubuntu-latest
 | 
			
		||||
#    steps:
 | 
			
		||||
@@ -63,7 +63,7 @@ jobs:
 | 
			
		||||
#          export DISPLAY=:99.0
 | 
			
		||||
#          Xvfb -ac :99 -screen 0 1920x1080x16 &
 | 
			
		||||
#          mkdir -p build/reports
 | 
			
		||||
#          gradle :testIdeUi #> build/reports/idea.log
 | 
			
		||||
#          gradle :runIdeForUiTests #> build/reports/idea.log
 | 
			
		||||
#      - name: Wait for Idea started
 | 
			
		||||
#        uses: jtalk/url-health-check-action@1.5
 | 
			
		||||
#        with:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -27,9 +27,8 @@
 | 
			
		||||
# Generated by gradle task "generateGrammarSource"
 | 
			
		||||
vim-engine/src/main/java/com/maddyhome/idea/vim/parser/generated
 | 
			
		||||
vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
 | 
			
		||||
# Generated JSONs for lazy classloading
 | 
			
		||||
/vim-engine/src/main/resources/ksp-generated
 | 
			
		||||
/src/main/resources/ksp-generated
 | 
			
		||||
 | 
			
		||||
# Created by github automation
 | 
			
		||||
settings.xml
 | 
			
		||||
 | 
			
		||||
.kotlin
 | 
			
		||||
							
								
								
									
										2
									
								
								.teamcity/_Self/buildTypes/Compatibility.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.teamcity/_Self/buildTypes/Compatibility.kt
									
									
									
									
										vendored
									
									
								
							@@ -37,7 +37,7 @@ object Compatibility : IdeaVimBuildType({
 | 
			
		||||
              java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}eu.theblob42.idea.whichkey' [latest-IU] -team-city
 | 
			
		||||
              java -jar verifier1/verifier-cli-dev-all-1.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 verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.github.copilot' [latest-IU] -team-city
 | 
			
		||||
              # java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.github.copilot' [latest-IU] -team-city
 | 
			
		||||
              java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.github.dankinsoid.multicursor' [latest-IU] -team-city
 | 
			
		||||
              java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.joshestein.ideavim-quickscope' [latest-IU] -team-city
 | 
			
		||||
            """.trimIndent()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								.teamcity/_Self/buildTypes/ReleasePlugin.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.teamcity/_Self/buildTypes/ReleasePlugin.kt
									
									
									
									
										vendored
									
									
								
							@@ -144,6 +144,7 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
 | 
			
		||||
    gradle {
 | 
			
		||||
      name = "Run Integrations"
 | 
			
		||||
      tasks = "releaseActions"
 | 
			
		||||
      gradleParams = "--no-configuration-cache"
 | 
			
		||||
    }
 | 
			
		||||
//    gradle {
 | 
			
		||||
//      name = "Slack Notification"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								AUTHORS.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								AUTHORS.md
									
									
									
									
									
								
							@@ -523,6 +523,18 @@ Contributors:
 | 
			
		||||
  [![icon][github]](https://github.com/LazyScaper)
 | 
			
		||||
   
 | 
			
		||||
  Jake
 | 
			
		||||
* [![icon][mail]](mailto:the1xdeveloper@gmail.com)
 | 
			
		||||
  [![icon][github]](https://github.com/The1xDeveloper)
 | 
			
		||||
   
 | 
			
		||||
  The1xDeveloper
 | 
			
		||||
* [![icon][mail]](mailto:shaunewilliams@gmail.com)
 | 
			
		||||
  [![icon][github]](https://github.com/shaunlebron)
 | 
			
		||||
   
 | 
			
		||||
  shaun
 | 
			
		||||
* [![icon][mail]](mailto:i.i.babko@gmail.com)
 | 
			
		||||
  [![icon][github]](https://github.com/igorbabko)
 | 
			
		||||
   
 | 
			
		||||
  Igor Babko
 | 
			
		||||
 | 
			
		||||
Previous contributors:
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,8 +27,8 @@ usual beta standards.
 | 
			
		||||
 | 
			
		||||
Since version 2.9.0, the changelog can be found on YouTrack
 | 
			
		||||
 | 
			
		||||
To Be Released: https://youtrack.jetbrains.com/issues/VIM?q=%23%7BReady%20To%20Release%7D%20
 | 
			
		||||
Latest Fixes: https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20sort%20by:%20updated%20
 | 
			
		||||
* [To Be Released](https://youtrack.jetbrains.com/issues/VIM?q=%23%7BReady%20To%20Release%7D%20)
 | 
			
		||||
* [Version Fixes](https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20sort%20by:%20%7BFix%20versions%7D%20asc)
 | 
			
		||||
 | 
			
		||||
## 2.9.0, 2024-02-20
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -62,12 +62,16 @@ for a few days or send it to a friend for testing.
 | 
			
		||||
If you are looking for:
 | 
			
		||||
 | 
			
		||||
- Vim commands (`w`, `<C-O>`, `p`, etc.):
 | 
			
		||||
    - Any particular command: `package-info.java`.
 | 
			
		||||
    - Any particular command:
 | 
			
		||||
      - [Commands common for Fleet and IdeaVim](vim-engine/src/main/resources/ksp-generated/engine_commands.json)
 | 
			
		||||
      - [IdeaVim only commands](src/main/resources/ksp-generated/intellij_commands.json)
 | 
			
		||||
    - How commands are executed in common: `EditorActionHandlerBase`.
 | 
			
		||||
    - Key mapping: `KeyHandler.handleKey()`.
 | 
			
		||||
 | 
			
		||||
- Ex commands (`:set`, `:s`, `:nohlsearch`):
 | 
			
		||||
    - Any particular ex command: package `com.maddyhome.idea.vim.ex.handler`.
 | 
			
		||||
    - Any particular command:
 | 
			
		||||
        - [Commands common for Fleet and IdeaVim](vim-engine/src/main/resources/ksp-generated/engine_ex_commands.json)
 | 
			
		||||
        - [IdeaVim only commands](src/main/resources/ksp-generated/intellij_ex_commands.json)
 | 
			
		||||
    - Vim script grammar: `Vimscript.g4`.
 | 
			
		||||
    - Vim script parsing: package `com.maddyhome.idea.vim.vimscript.parser`.
 | 
			
		||||
    - Vim script executor: `Executor`.
 | 
			
		||||
 
 | 
			
		||||
@@ -109,7 +109,6 @@ etc
 | 
			
		||||
 | 
			
		||||
See also:
 | 
			
		||||
 | 
			
		||||
* [The list of all supported commands](src/main/java/com/maddyhome/idea/vim/package-info.java)
 | 
			
		||||
* [Top feature requests and bugs](https://youtrack.jetbrains.com/issues/VIM?q=%23Unresolved+sort+by%3A+votes)
 | 
			
		||||
* [Vimscript support roadmap](vimscript-info/VIMSCRIPT_ROADMAP.md)
 | 
			
		||||
* [List of supported in-build functions](vimscript-info/FUNCTIONS_INFO.MD)
 | 
			
		||||
@@ -222,13 +221,13 @@ Ex commands or via `:map` command mappings:
 | 
			
		||||
    * Execute an action by `{action_id}`. Works from Ex command line.
 | 
			
		||||
    * Please don't use `:action` in mappings. Use `<Action>` instead.
 | 
			
		||||
 | 
			
		||||
### Finding action ids:
 | 
			
		||||
### Finding action IDs:
 | 
			
		||||
 | 
			
		||||
* IJ provides `IdeaVim: track action Ids` command to show the id of the executed actions.
 | 
			
		||||
* IJ provides `IdeaVim: track action IDs` command to show the id of the executed actions.
 | 
			
		||||
  This command can be found in "Search everywhere" (double `shift`).
 | 
			
		||||
 | 
			
		||||
    <details>
 | 
			
		||||
        <summary><strong>"Track action Ids" Details</strong> (click to see)</summary>
 | 
			
		||||
        <summary><strong>"Track action IDs" Details</strong> (click to see)</summary>
 | 
			
		||||
        <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"/>
 | 
			
		||||
@@ -369,6 +368,8 @@ is the full list of synonyms.
 | 
			
		||||
- Fancy constants for [undolevels](https://vimhelp.org/options.txt.html#%27undolevels%27):
 | 
			
		||||
  > The local value is set to -123456 when the global value is to be used.
 | 
			
		||||
 | 
			
		||||
- Vi (not Vim) is a POSIX standard, and [has a spec](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/vi.html)! Vim is mostly POSIX compliant when Vi compatibility is selected with the `'compatible'` option, but there are still some differences that can be changed with `'copoptions'`. The spec is interesting because it documents the behaviour of different commands in a stricter style than the user documentation, describing the current line and column after the command, for example. [More details can be found by reading `:help posix`](https://vimhelp.org/vi_diff.txt.html#posix).
 | 
			
		||||
 | 
			
		||||
License
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
 | 
			
		||||
plugins {
 | 
			
		||||
  kotlin("jvm")
 | 
			
		||||
  kotlin("plugin.serialization") version "1.9.22"
 | 
			
		||||
  kotlin("plugin.serialization") version "2.0.0"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
val kotlinxSerializationVersion: String by project
 | 
			
		||||
@@ -21,7 +21,7 @@ repositories {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
  compileOnly("com.google.devtools.ksp:symbol-processing-api:2.0.0-1.0.22")
 | 
			
		||||
  compileOnly("com.google.devtools.ksp:symbol-processing-api:2.0.0-1.0.24")
 | 
			
		||||
  implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:$kotlinxSerializationVersion") {
 | 
			
		||||
    // kotlin stdlib is provided by IJ, so there is no need to include it into the distribution
 | 
			
		||||
    exclude("org.jetbrains.kotlin", "kotlin-stdlib")
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,8 @@ class CommandOrMotionProcessor(private val environment: SymbolProcessorEnvironme
 | 
			
		||||
    Files.createDirectories(generatedDirPath)
 | 
			
		||||
 | 
			
		||||
    val filePath = generatedDirPath.resolve(environment.options["commands_file"]!!)
 | 
			
		||||
    val fileContent = json.encodeToString(commands)
 | 
			
		||||
    val sortedCommands = commands.sortedWith(compareBy({ it.keys }, { it.`class` }))
 | 
			
		||||
    val fileContent = json.encodeToString(sortedCommands)
 | 
			
		||||
    filePath.writeText(fileContent)
 | 
			
		||||
 | 
			
		||||
    return emptyList()
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,8 @@ class ExCommandProcessor(private val environment: SymbolProcessorEnvironment): S
 | 
			
		||||
    Files.createDirectories(generatedDirPath)
 | 
			
		||||
 | 
			
		||||
    val filePath = generatedDirPath.resolve(environment.options["ex_commands_file"]!!)
 | 
			
		||||
    val fileContent = json.encodeToString(commandToClass)
 | 
			
		||||
    val sortedCommandToClass = commandToClass.toList().sortedWith(compareBy({ it.first }, { it.second })).toMap()
 | 
			
		||||
    val fileContent = json.encodeToString(sortedCommandToClass)
 | 
			
		||||
    filePath.writeText(fileContent)
 | 
			
		||||
 | 
			
		||||
    return emptyList()
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,8 @@ class VimscriptFunctionProcessor(private val environment: SymbolProcessorEnviron
 | 
			
		||||
    Files.createDirectories(generatedDirPath)
 | 
			
		||||
 | 
			
		||||
    val filePath = generatedDirPath.resolve(environment.options["vimscript_functions_file"]!!)
 | 
			
		||||
    val fileContent = json.encodeToString(nameToClass)
 | 
			
		||||
    val sortedNameToClass = nameToClass.toList().sortedWith(compareBy({ it.first }, { it.second })).toMap()
 | 
			
		||||
    val fileContent = json.encodeToString(sortedNameToClass)
 | 
			
		||||
    filePath.writeText(fileContent)
 | 
			
		||||
 | 
			
		||||
    return emptyList()
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,7 @@ buildscript {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  dependencies {
 | 
			
		||||
    classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22")
 | 
			
		||||
    classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0")
 | 
			
		||||
    classpath("com.github.AlexPl292:mark-down-to-slack:1.1.2")
 | 
			
		||||
    classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
 | 
			
		||||
 | 
			
		||||
@@ -66,14 +66,14 @@ buildscript {
 | 
			
		||||
 | 
			
		||||
plugins {
 | 
			
		||||
  java
 | 
			
		||||
  kotlin("jvm") version "1.9.22"
 | 
			
		||||
  kotlin("jvm") version "2.0.0"
 | 
			
		||||
  application
 | 
			
		||||
  id("java-test-fixtures")
 | 
			
		||||
  id("org.jetbrains.intellij.platform") version "2.0.0-beta8"
 | 
			
		||||
  id("org.jetbrains.changelog") version "2.2.0"
 | 
			
		||||
  id("org.jetbrains.intellij.platform") version "2.0.0-rc2"
 | 
			
		||||
  id("org.jetbrains.changelog") version "2.2.1"
 | 
			
		||||
  id("org.jetbrains.kotlinx.kover") version "0.6.1"
 | 
			
		||||
  id("com.dorongold.task-tree") version "4.0.0"
 | 
			
		||||
  id("com.google.devtools.ksp") version "1.9.22-1.0.17"
 | 
			
		||||
  id("com.google.devtools.ksp") version "2.0.0-1.0.23"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
val moduleSources by configurations.registering
 | 
			
		||||
@@ -124,7 +124,8 @@ dependencies {
 | 
			
		||||
    testFramework(TestFrameworkType.JUnit5)
 | 
			
		||||
 | 
			
		||||
    // AceJump is an optional dependency. We use their SessionManager class to check if it's active
 | 
			
		||||
    plugin("AceJump", "3.8.11")
 | 
			
		||||
    plugin("AceJump", "3.8.19")
 | 
			
		||||
    plugin("com.intellij.classic.ui", "242.20224.159")
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  moduleSources(project(":vim-engine", "sourcesJarArtifacts"))
 | 
			
		||||
@@ -144,7 +145,7 @@ dependencies {
 | 
			
		||||
  testFixturesImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
 | 
			
		||||
 | 
			
		||||
  // https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin
 | 
			
		||||
  testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1")
 | 
			
		||||
  testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
 | 
			
		||||
 | 
			
		||||
  testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
 | 
			
		||||
  testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.3")
 | 
			
		||||
@@ -152,6 +153,12 @@ dependencies {
 | 
			
		||||
  testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
 | 
			
		||||
  testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.3")
 | 
			
		||||
  testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.3")
 | 
			
		||||
 | 
			
		||||
  // Temp workaround suggested in https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-faq.html#junit5-test-framework-refers-to-junit4
 | 
			
		||||
  // Can be removed when IJPL-159134 is fixed
 | 
			
		||||
//  testRuntimeOnly("junit:junit:4.13.2")
 | 
			
		||||
  testImplementation("org.junit.vintage:junit-vintage-engine:5.10.3")
 | 
			
		||||
//  testFixturesImplementation("org.junit.vintage:junit-vintage-engine:5.10.3")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
configurations {
 | 
			
		||||
@@ -191,15 +198,28 @@ tasks {
 | 
			
		||||
      // See https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#kotlin-standard-library
 | 
			
		||||
      // For the list of bundled versions
 | 
			
		||||
      apiVersion = "1.9"
 | 
			
		||||
      freeCompilerArgs = listOf("-Xjvm-default=all-compatibility")
 | 
			
		||||
      freeCompilerArgs = listOf(
 | 
			
		||||
        "-Xjvm-default=all-compatibility",
 | 
			
		||||
 | 
			
		||||
        // Needed to compile the AceJump which uses kotlin beta
 | 
			
		||||
        //  Without these two option compilation fails
 | 
			
		||||
        "-Xskip-prerelease-check",
 | 
			
		||||
        "-Xallow-unstable-dependencies",
 | 
			
		||||
      )
 | 
			
		||||
//            allWarningsAsErrors = true
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  compileTestKotlin {
 | 
			
		||||
    enabled = false
 | 
			
		||||
    
 | 
			
		||||
    kotlinOptions {
 | 
			
		||||
      jvmTarget = javaVersion
 | 
			
		||||
      apiVersion = "1.9"
 | 
			
		||||
 | 
			
		||||
      // Needed to compile the AceJump which uses kotlin beta
 | 
			
		||||
      //  Without these two option compilation fails
 | 
			
		||||
      freeCompilerArgs += listOf("-Xskip-prerelease-check", "-Xallow-unstable-dependencies")
 | 
			
		||||
//            allWarningsAsErrors = true
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@@ -222,6 +242,25 @@ tasks {
 | 
			
		||||
//    localPath = file("/Users/{user}/Applications/WebStorm.app")
 | 
			
		||||
//  }
 | 
			
		||||
 | 
			
		||||
  val runIdeForUiTests by intellijPlatformTesting.runIde.registering {
 | 
			
		||||
    task {
 | 
			
		||||
      jvmArgumentProviders += CommandLineArgumentProvider {
 | 
			
		||||
        listOf(
 | 
			
		||||
          "-Drobot-server.port=8082",
 | 
			
		||||
          "-Dide.mac.message.dialogs.as.sheets=false",
 | 
			
		||||
          "-Djb.privacy.policy.text=<!--999.999-->",
 | 
			
		||||
          "-Djb.consents.confirmation.enabled=false",
 | 
			
		||||
          "-Dide.show.tips.on.startup.default.value=false",
 | 
			
		||||
          "-Doctopus.handler=" + (System.getProperty("octopus.handler") ?: true),
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    plugins {
 | 
			
		||||
      robotServerPlugin(remoteRobotVersion)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  val runIdeSplitMode by intellijPlatformTesting.runIde.registering {
 | 
			
		||||
    splitMode = true
 | 
			
		||||
    splitModeTarget = SplitModeAware.SplitModeTarget.FRONTEND
 | 
			
		||||
@@ -231,19 +270,6 @@ tasks {
 | 
			
		||||
    version.set(splitModeVersion)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Start the default IDE with both IdeaVim and the robot server plugin installed, ready to run a UI test task. The
 | 
			
		||||
  // robot server plugin is automatically added as a dependency to this task, and Gradle will take care of downloading.
 | 
			
		||||
  // Note that the CustomTestIdeUiTask can be used to run tests against a different IDE
 | 
			
		||||
  testIdeUi {
 | 
			
		||||
    systemProperty("robot-server.port", "8082")
 | 
			
		||||
    systemProperty("ide.mac.message.dialogs.as.sheets", "false")
 | 
			
		||||
    systemProperty("jb.privacy.policy.text", "<!--999.999-->")
 | 
			
		||||
    systemProperty("jb.consents.confirmation.enabled", "false")
 | 
			
		||||
    systemProperty("ide.show.tips.on.startup.default.value", "false")
 | 
			
		||||
 | 
			
		||||
    systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Add plugin open API sources to the plugin ZIP
 | 
			
		||||
  val sourcesJar by registering(Jar::class) {
 | 
			
		||||
    dependsOn(moduleSources)
 | 
			
		||||
@@ -288,7 +314,14 @@ intellijPlatform {
 | 
			
		||||
  pluginConfiguration {
 | 
			
		||||
    name = "IdeaVim"
 | 
			
		||||
    changeNotes.set(
 | 
			
		||||
      """<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>"""
 | 
			
		||||
      """
 | 
			
		||||
        Undo in IdeaVim now works like in Vim<br/>
 | 
			
		||||
        Caret movement is no longer a separate undo step, and full insert is undoable in one step.<br/>
 | 
			
		||||
        <a href="https://youtrack.jetbrains.com/issue/VIM-547/Undo-splits-Insert-mode-edits-into-separate-undo-chunks">Share Feedback</a>
 | 
			
		||||
        <br/>
 | 
			
		||||
        <br/>
 | 
			
		||||
        <a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    ideaVersion {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
Welcome to the IdeaVim wiki!
 | 
			
		||||
 | 
			
		||||
- List of IdeaVim plugins: [plugins](IdeaVim%20Plugins.md)
 | 
			
		||||
- Examples of `ideajoin` option (also known as "smart join"): ["ideajoin" examples](ideajoin-examples.md)
 | 
			
		||||
- List of "set" commands: ["set" commands](set-commands.md)
 | 
			
		||||
- Docs about "select" mode in vim: [select mode](Select-mode.md)
 | 
			
		||||
- List of IdeaVim plugins: [plugins](IdeaVim%20Plugins)
 | 
			
		||||
- Examples of `ideajoin` option (also known as "smart join"): ["ideajoin" examples](ideajoin-examples)
 | 
			
		||||
- List of "set" commands: ["set" commands](set-commands)
 | 
			
		||||
- Docs about "select" mode in vim: [select mode](Select-mode)
 | 
			
		||||
 
 | 
			
		||||
@@ -82,7 +82,7 @@ Original plugin: [NERDTree](https://github.com/preservim/nerdtree).
 | 
			
		||||
   
 | 
			
		||||
### Instructions
 | 
			
		||||
   
 | 
			
		||||
[See here](NERDTree-support.md).
 | 
			
		||||
[See here](NERDTree-support).
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ Using actions from external plugins is the same, as tracking and reusing any IDE
 | 
			
		||||
1. Install the plugin via Marketplace
 | 
			
		||||
2. Enable action tracking. You can enable it by one of the following ways:
 | 
			
		||||
    * Execute `:set trackactionids` ex command or just `:set tai`
 | 
			
		||||
    * Open the "Find actions" window by pressing `Ctrl-Shift-A` and search for "Track Action Ids" to find the toggle that enables and disables action tracking
 | 
			
		||||
    * Open the "Find actions" window by pressing `Ctrl-Shift-A` and search for "Track Action IDs" to find the toggle that enables and disables action tracking
 | 
			
		||||
3. Execute the plugin action the way intended by plugin author "See Edit menu or use ⇧ + ⌥ + U / Shift + Alt + U" or just find the `Toggle Camel Case` action in the "Find actions" window (`Ctrl-Shift-A`). If you action tracking is on, you will see this notification in your right bottom corner:
 | 
			
		||||
   
 | 
			
		||||
   <img alt="Notification" src="images/action-id-notification.png"/>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
# Support Guide
 | 
			
		||||
 | 
			
		||||
This document is created to help our support team.
 | 
			
		||||
It's not intended to be read by the users as it brings to value to them.
 | 
			
		||||
It's not intended to be read by the users as it brings no value to them.
 | 
			
		||||
 | 
			
		||||
## Support channels
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,13 +16,13 @@
 | 
			
		||||
# https://data.services.jetbrains.com/products?code=IC
 | 
			
		||||
# Maven releases are here: https://www.jetbrains.com/intellij-repository/releases
 | 
			
		||||
# And snapshots: https://www.jetbrains.com/intellij-repository/snapshots
 | 
			
		||||
ideaVersion=2024.1.1
 | 
			
		||||
ideaVersion=2024.2
 | 
			
		||||
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
 | 
			
		||||
ideaType=IC
 | 
			
		||||
instrumentPluginCode=true
 | 
			
		||||
version=SNAPSHOT
 | 
			
		||||
version=chylex-40
 | 
			
		||||
javaVersion=17
 | 
			
		||||
remoteRobotVersion=0.11.22
 | 
			
		||||
remoteRobotVersion=0.11.23
 | 
			
		||||
antlrVersion=4.10.1
 | 
			
		||||
 | 
			
		||||
# [VERSION UPDATE] 2024.2 - remove when IdeaVim targets 2024.2
 | 
			
		||||
@@ -34,7 +34,7 @@ splitModeVersion=242-EAP-SNAPSHOT
 | 
			
		||||
 | 
			
		||||
# Please don't forget to update kotlin version in buildscript section
 | 
			
		||||
# Also update kotlinxSerializationVersion version
 | 
			
		||||
kotlinVersion=1.9.22
 | 
			
		||||
kotlinVersion=2.0.0
 | 
			
		||||
publishToken=token
 | 
			
		||||
publishChannels=eap
 | 
			
		||||
 | 
			
		||||
@@ -47,7 +47,6 @@ youtrackToken=
 | 
			
		||||
 | 
			
		||||
# Gradle settings
 | 
			
		||||
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
 | 
			
		||||
org.gradle.configuration-cache=true
 | 
			
		||||
org.gradle.caching=true
 | 
			
		||||
 | 
			
		||||
# Disable warning from gradle-intellij-plugin. Kotlin stdlib is included as compileOnly, so the warning is unnecessary
 | 
			
		||||
 
 | 
			
		||||
@@ -19,9 +19,8 @@ exclude:
 | 
			
		||||
      - 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
 | 
			
		||||
      - src/main/java/com/maddyhome/idea/vim/package-info.java
 | 
			
		||||
      - vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
 | 
			
		||||
      - vim-engine/src/main/java/com/maddyhome/idea/vim/parser/generated
 | 
			
		||||
      - src/main/java/com/maddyhome/idea/vim/group/SearchGroup.java
 | 
			
		||||
      - tests/ui-fixtures
 | 
			
		||||
dependencyIgnores:
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ repositories {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
  compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.24")
 | 
			
		||||
  compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.25")
 | 
			
		||||
 | 
			
		||||
  implementation("io.ktor:ktor-client-core:2.3.12")
 | 
			
		||||
  implementation("io.ktor:ktor-client-cio:2.3.10")
 | 
			
		||||
 
 | 
			
		||||
@@ -8,14 +8,19 @@
 | 
			
		||||
 | 
			
		||||
package com.maddyhome.idea.vim
 | 
			
		||||
 | 
			
		||||
import com.intellij.openapi.application.ApplicationManager
 | 
			
		||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
 | 
			
		||||
import com.intellij.openapi.project.Project
 | 
			
		||||
import com.intellij.openapi.project.ProjectManagerListener
 | 
			
		||||
import com.intellij.openapi.startup.ProjectActivity
 | 
			
		||||
import com.intellij.openapi.updateSettings.impl.UpdateSettings
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.helper.EditorHelper
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.initInjector
 | 
			
		||||
import com.maddyhome.idea.vim.ui.JoinEap
 | 
			
		||||
import com.maddyhome.idea.vim.ui.JoinEap.EAP_LINK
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Alex Plate
 | 
			
		||||
@@ -28,6 +33,11 @@ internal class PluginStartup : ProjectActivity/*, LightEditCompatible*/ {
 | 
			
		||||
    if (firstInitializationOccurred) return
 | 
			
		||||
    firstInitializationOccurred = true
 | 
			
		||||
 | 
			
		||||
    if (!VimPlugin.getVimState().wasSubscibedToEAPAutomatically && ApplicationManager.getApplication().isEAP && !JoinEap.eapActive()) {
 | 
			
		||||
      VimPlugin.getVimState().wasSubscibedToEAPAutomatically = true
 | 
			
		||||
      UpdateSettings.getInstance().storedPluginHosts += EAP_LINK
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // This code should be executed once
 | 
			
		||||
    VimPlugin.getInstance().initialize()
 | 
			
		||||
  }
 | 
			
		||||
@@ -36,6 +46,7 @@ internal class PluginStartup : ProjectActivity/*, LightEditCompatible*/ {
 | 
			
		||||
// This is a temporal workaround for VIM-2487
 | 
			
		||||
internal class PyNotebooksCloseWorkaround : ProjectManagerListener {
 | 
			
		||||
  override fun projectClosingBeforeSave(project: Project) {
 | 
			
		||||
    initInjector()
 | 
			
		||||
    // TODO: Confirm context in CWM scenario
 | 
			
		||||
    if (injector.globalIjOptions().closenotebooks) {
 | 
			
		||||
      injector.editorGroup.getEditors().forEach { vimEditor ->
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,7 @@ 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.newapi.IjVimInjectorKt;
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimSearchGroup;
 | 
			
		||||
import com.maddyhome.idea.vim.ui.StatusBarIconFactory;
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.services.VariableService;
 | 
			
		||||
@@ -67,7 +68,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
 | 
			
		||||
  private static final Logger LOG = Logger.getInstance(VimPlugin.class);
 | 
			
		||||
 | 
			
		||||
  static {
 | 
			
		||||
    VimInjectorKt.setInjector(new IjVimInjector());
 | 
			
		||||
    IjVimInjectorKt.initInjector();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private final @NotNull VimState state = new VimState();
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@ import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
 | 
			
		||||
internal class VimProjectService(val project: Project) : Disposable {
 | 
			
		||||
  override fun dispose() {
 | 
			
		||||
    // Not sure if this is a best solution
 | 
			
		||||
    ExEntryPanel.getInstance().editor = null
 | 
			
		||||
    ExEntryPanel.getInstance().setEditor(null)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  companion object {
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,52 @@
 | 
			
		||||
package com.maddyhome.idea.vim.action
 | 
			
		||||
 | 
			
		||||
import com.intellij.openapi.actionSystem.ActionUpdateThread
 | 
			
		||||
import com.intellij.openapi.actionSystem.AnActionEvent
 | 
			
		||||
import com.intellij.openapi.command.UndoConfirmationPolicy
 | 
			
		||||
import com.intellij.openapi.command.WriteCommandAction
 | 
			
		||||
import com.intellij.openapi.fileEditor.TextEditor
 | 
			
		||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
 | 
			
		||||
import com.intellij.openapi.project.DumbAwareAction
 | 
			
		||||
import com.maddyhome.idea.vim.KeyHandler
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.vim
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
			
		||||
 | 
			
		||||
class VimRunLastMacroInOpenFiles : DumbAwareAction() {
 | 
			
		||||
  override fun update(e: AnActionEvent) {
 | 
			
		||||
    val lastRegister = injector.macro.lastRegister
 | 
			
		||||
    val isEnabled = lastRegister != 0.toChar()
 | 
			
		||||
 | 
			
		||||
    e.presentation.isEnabled = isEnabled
 | 
			
		||||
    e.presentation.text = if (isEnabled) "Run Macro '${lastRegister}' in Open Files" else "Run Last Macro in Open Files"
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun getActionUpdateThread(): ActionUpdateThread {
 | 
			
		||||
    return ActionUpdateThread.EDT
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun actionPerformed(e: AnActionEvent) {
 | 
			
		||||
    val project = e.project ?: return
 | 
			
		||||
    val fileEditorManager = FileEditorManagerEx.getInstanceExIfCreated(project) ?: return
 | 
			
		||||
    val editors = fileEditorManager.allEditors.filterIsInstance<TextEditor>()
 | 
			
		||||
    
 | 
			
		||||
    WriteCommandAction.writeCommandAction(project)
 | 
			
		||||
      .withName(e.presentation.text)
 | 
			
		||||
      .withGlobalUndo()
 | 
			
		||||
      .withUndoConfirmationPolicy(UndoConfirmationPolicy.REQUEST_CONFIRMATION)
 | 
			
		||||
      .run<RuntimeException> {
 | 
			
		||||
        val reg = injector.macro.lastRegister
 | 
			
		||||
        
 | 
			
		||||
        for (editor in editors) {
 | 
			
		||||
          fileEditorManager.openFile(editor.file, true)
 | 
			
		||||
          
 | 
			
		||||
          val vimEditor = editor.editor.vim
 | 
			
		||||
          vimEditor.mode = Mode.NORMAL()
 | 
			
		||||
          KeyHandler.getInstance().reset(vimEditor)
 | 
			
		||||
          
 | 
			
		||||
          injector.macro.playbackRegister(vimEditor, IjEditorExecutionContext(e.dataContext), reg, 1)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -43,6 +43,7 @@ import com.maddyhome.idea.vim.key.ShortcutOwnerInfo
 | 
			
		||||
import com.maddyhome.idea.vim.listener.AceJumpService
 | 
			
		||||
import com.maddyhome.idea.vim.listener.AppCodeTemplates.appCodeTemplateCaptured
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.initInjector
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.vim
 | 
			
		||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
 | 
			
		||||
import com.maddyhome.idea.vim.ui.ex.ExTextField
 | 
			
		||||
@@ -61,10 +62,13 @@ import javax.swing.KeyStroke
 | 
			
		||||
 *   way to get ideavim keys for this plugin. See VIM-3085
 | 
			
		||||
 */
 | 
			
		||||
class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
 | 
			
		||||
 | 
			
		||||
  init {
 | 
			
		||||
    initInjector()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private val traceTime: Boolean
 | 
			
		||||
    get() {
 | 
			
		||||
      // Make sure the injector is initialized
 | 
			
		||||
      VimPlugin.getInstance()
 | 
			
		||||
      return injector.globalOptions().ideatracetime
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -257,7 +261,7 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
 | 
			
		||||
  private fun getEditor(e: AnActionEvent): Editor? {
 | 
			
		||||
    return e.getData(PlatformDataKeys.EDITOR)
 | 
			
		||||
      ?: if (e.getData(PlatformDataKeys.CONTEXT_COMPONENT) is ExTextField) {
 | 
			
		||||
        ExEntryPanel.getInstance().editor
 | 
			
		||||
        ExEntryPanel.getInstance().ijEditor
 | 
			
		||||
      } else {
 | 
			
		||||
        null
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -79,7 +79,7 @@ internal class VimQuickJavaDoc : VimActionHandler.SingleExecution() {
 | 
			
		||||
  override val type: Command.Type = Command.Type.OTHER_READONLY
 | 
			
		||||
 | 
			
		||||
  override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
 | 
			
		||||
    injector.actionExecutor.executeAction(IdeActions.ACTION_QUICK_JAVADOC, context)
 | 
			
		||||
    injector.actionExecutor.executeAction(editor, IdeActions.ACTION_QUICK_JAVADOC, context)
 | 
			
		||||
    return true
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										72
									
								
								src/main/java/com/maddyhome/idea/vim/command/CommandState.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/main/java/com/maddyhome/idea/vim/command/CommandState.kt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2003-2023 The IdeaVim authors
 | 
			
		||||
 *
 | 
			
		||||
 * Use of this source code is governed by an MIT-style
 | 
			
		||||
 * license that can be found in the LICENSE.txt file or at
 | 
			
		||||
 * https://opensource.org/licenses/MIT.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.maddyhome.idea.vim.command
 | 
			
		||||
 | 
			
		||||
import com.intellij.openapi.editor.Editor
 | 
			
		||||
import com.maddyhome.idea.vim.KeyHandler
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.state.VimStateMachine
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * COMPATIBILITY-LAYER: Additional class
 | 
			
		||||
 * Please see: https://jb.gg/zo8n0r
 | 
			
		||||
 */
 | 
			
		||||
@Deprecated("Use `injector.vimState`")
 | 
			
		||||
class CommandState(private val machine: VimStateMachine) {
 | 
			
		||||
 | 
			
		||||
  val mode: Mode
 | 
			
		||||
    get() {
 | 
			
		||||
      val myMode = machine.mode
 | 
			
		||||
      return when (myMode) {
 | 
			
		||||
        is com.maddyhome.idea.vim.state.mode.Mode.CMD_LINE -> Mode.CMD_LINE
 | 
			
		||||
        com.maddyhome.idea.vim.state.mode.Mode.INSERT -> Mode.INSERT
 | 
			
		||||
        is com.maddyhome.idea.vim.state.mode.Mode.NORMAL -> Mode.COMMAND
 | 
			
		||||
        is com.maddyhome.idea.vim.state.mode.Mode.OP_PENDING -> Mode.OP_PENDING
 | 
			
		||||
        com.maddyhome.idea.vim.state.mode.Mode.REPLACE -> Mode.REPLACE
 | 
			
		||||
        is com.maddyhome.idea.vim.state.mode.Mode.SELECT -> Mode.SELECT
 | 
			
		||||
        is com.maddyhome.idea.vim.state.mode.Mode.VISUAL -> Mode.VISUAL
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  @Deprecated("Use `KeyHandler.keyHandlerState.commandBuilder", ReplaceWith(
 | 
			
		||||
    "KeyHandler.getInstance().keyHandlerState.commandBuilder",
 | 
			
		||||
    "com.maddyhome.idea.vim.KeyHandler"
 | 
			
		||||
  )
 | 
			
		||||
  )
 | 
			
		||||
  val commandBuilder: CommandBuilder
 | 
			
		||||
    get() = KeyHandler.getInstance().keyHandlerState.commandBuilder
 | 
			
		||||
 | 
			
		||||
  @Deprecated("Use `KeyHandler.keyHandlerState.mappingState", ReplaceWith(
 | 
			
		||||
    "KeyHandler.getInstance().keyHandlerState.mappingState",
 | 
			
		||||
    "com.maddyhome.idea.vim.KeyHandler"
 | 
			
		||||
  )
 | 
			
		||||
  )
 | 
			
		||||
  val mappingState: MappingState
 | 
			
		||||
    get() = KeyHandler.getInstance().keyHandlerState.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
 | 
			
		||||
    @Deprecated("Use `injector.vimState`")
 | 
			
		||||
    fun getInstance(editor: Editor): CommandState {
 | 
			
		||||
      return CommandState(injector.vimState)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -12,17 +12,17 @@ import com.intellij.application.options.CodeStyle
 | 
			
		||||
import com.intellij.openapi.editor.Editor
 | 
			
		||||
import com.intellij.openapi.project.Project
 | 
			
		||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings.IndentOptions
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimIndentConfig
 | 
			
		||||
 | 
			
		||||
internal class IndentConfig private constructor(indentOptions: IndentOptions) {
 | 
			
		||||
internal class IndentConfig private constructor(indentOptions: IndentOptions): VimIndentConfig {
 | 
			
		||||
  private val indentSize = indentOptions.INDENT_SIZE
 | 
			
		||||
  private val tabSize = indentOptions.TAB_SIZE
 | 
			
		||||
  private val isUseTabs = indentOptions.USE_TAB_CHARACTER
 | 
			
		||||
 | 
			
		||||
  fun getTotalIndent(count: Int): Int = indentSize * count
 | 
			
		||||
  override fun getIndentSize(depth: Int): Int = indentSize * depth
 | 
			
		||||
  override fun createIndentByDepth(depth: Int): String = createIndentBySize(getIndentSize(depth))
 | 
			
		||||
 | 
			
		||||
  fun createIndentByCount(count: Int): String = createIndentBySize(getTotalIndent(count))
 | 
			
		||||
 | 
			
		||||
  fun createIndentBySize(size: Int): String {
 | 
			
		||||
  override fun createIndentBySize(size: Int): String {
 | 
			
		||||
    val tabCount: Int
 | 
			
		||||
    val spaceCount: Int
 | 
			
		||||
    if (isUseTabs) {
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,8 @@ import kotlin.reflect.KProperty
 | 
			
		||||
internal class VimState {
 | 
			
		||||
  var isIdeaJoinNotified by StateProperty("idea-join")
 | 
			
		||||
  var isIdeaPutNotified by StateProperty("idea-put")
 | 
			
		||||
  var isNewUndoNotified by StateProperty("idea-undo")
 | 
			
		||||
  var wasSubscibedToEAPAutomatically by StateProperty("was-automatically-subscribed-to-eap")
 | 
			
		||||
 | 
			
		||||
  fun readData(element: Element) {
 | 
			
		||||
    val notifications = element.getChild("notifications")
 | 
			
		||||
 
 | 
			
		||||
@@ -9,14 +9,15 @@ package com.maddyhome.idea.vim.ex
 | 
			
		||||
 | 
			
		||||
import com.intellij.openapi.application.ApplicationManager
 | 
			
		||||
import com.intellij.openapi.editor.Editor
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimOutputPanel
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimOutputPanelBase
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.helper.vimExOutput
 | 
			
		||||
import com.maddyhome.idea.vim.ui.ExOutputPanel
 | 
			
		||||
import java.lang.ref.WeakReference
 | 
			
		||||
import javax.swing.KeyStroke
 | 
			
		||||
 | 
			
		||||
// TODO: We need a nicer way to handle output, especially wrt testing, appending + clearing
 | 
			
		||||
class ExOutputModel(private val myEditor: WeakReference<Editor>) : VimOutputPanel {
 | 
			
		||||
class ExOutputModel(private val myEditor: WeakReference<Editor>) : VimOutputPanelBase() {
 | 
			
		||||
  private var isActiveInTestMode = false
 | 
			
		||||
 | 
			
		||||
  val editor get() = myEditor.get()
 | 
			
		||||
@@ -48,6 +49,24 @@ class ExOutputModel(private val myEditor: WeakReference<Editor>) : VimOutputPane
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun scrollPage() {
 | 
			
		||||
    val notNullEditor = editor ?: return
 | 
			
		||||
    val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
 | 
			
		||||
    panel.scrollPage()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun scrollHalfPage() {
 | 
			
		||||
    val notNullEditor = editor ?: return
 | 
			
		||||
    val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
 | 
			
		||||
    panel.scrollHalfPage()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun scrollLine() {
 | 
			
		||||
    val notNullEditor = editor ?: return
 | 
			
		||||
    val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
 | 
			
		||||
    panel.scrollLine()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override var text: String = ""
 | 
			
		||||
    get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
 | 
			
		||||
      editor?.let { ExOutputPanel.getInstance(it).text } ?: ""
 | 
			
		||||
@@ -66,6 +85,17 @@ class ExOutputModel(private val myEditor: WeakReference<Editor>) : VimOutputPane
 | 
			
		||||
        isActiveInTestMode = newValue.isNotEmpty()
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  override var label: String
 | 
			
		||||
    get() {
 | 
			
		||||
      val notNullEditor = editor ?: return ""
 | 
			
		||||
      val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return ""
 | 
			
		||||
      return panel.myLabel.text
 | 
			
		||||
    }
 | 
			
		||||
    set(value) {
 | 
			
		||||
      val notNullEditor = editor ?: return
 | 
			
		||||
      val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
 | 
			
		||||
      panel.myLabel.text = value
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  fun output(text: String) {
 | 
			
		||||
    this.text = text
 | 
			
		||||
@@ -75,6 +105,25 @@ class ExOutputModel(private val myEditor: WeakReference<Editor>) : VimOutputPane
 | 
			
		||||
    text = ""
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override val atEnd: Boolean
 | 
			
		||||
    get() {
 | 
			
		||||
      val notNullEditor = editor ?: return false
 | 
			
		||||
      val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return false
 | 
			
		||||
      return panel.isAtEnd()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  override fun onBadKey() {
 | 
			
		||||
    val notNullEditor = editor ?: return
 | 
			
		||||
    val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
 | 
			
		||||
    panel.onBadKey()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun close(key: KeyStroke?) {
 | 
			
		||||
    val notNullEditor = editor ?: return
 | 
			
		||||
    val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
 | 
			
		||||
    panel.close(key)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun close() {
 | 
			
		||||
    if (!ApplicationManager.getApplication().isUnitTestMode) {
 | 
			
		||||
      editor?.let { ExOutputPanel.getInstance(it).close() }
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@ import com.maddyhome.idea.vim.key.OperatorFunction
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.vim
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
			
		||||
import com.maddyhome.idea.vim.ui.ModalEntry
 | 
			
		||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanelService
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.Executable
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.VimLContext
 | 
			
		||||
@@ -68,7 +69,8 @@ object VimExtensionFacade {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  @JvmStatic
 | 
			
		||||
  @Deprecated("Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
 | 
			
		||||
  @Deprecated(
 | 
			
		||||
    "Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
 | 
			
		||||
    ReplaceWith(
 | 
			
		||||
      "VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
 | 
			
		||||
      "com.maddyhome.idea.vim.VimPlugin"
 | 
			
		||||
@@ -182,7 +184,7 @@ object VimExtensionFacade {
 | 
			
		||||
  /** Returns a string typed in the input box similar to 'input()'. */
 | 
			
		||||
  @JvmStatic
 | 
			
		||||
  fun inputString(editor: Editor, context: DataContext, prompt: String, finishOn: Char?): String {
 | 
			
		||||
    return injector.commandLine.inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
 | 
			
		||||
    return (injector.commandLine as ExEntryPanelService).inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Get the current contents of the given register similar to 'getreg()'. */
 | 
			
		||||
@@ -194,7 +196,7 @@ object VimExtensionFacade {
 | 
			
		||||
 | 
			
		||||
  @JvmStatic
 | 
			
		||||
  fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
 | 
			
		||||
    val reg = caret.registerStorage.getRegister(register) ?: return null
 | 
			
		||||
    val reg = injector.registerGroup.getRegister(register) ?: return null
 | 
			
		||||
    return reg.keys
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -207,7 +209,7 @@ object VimExtensionFacade {
 | 
			
		||||
  /** Set the current contents of the given register */
 | 
			
		||||
  @JvmStatic
 | 
			
		||||
  fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) {
 | 
			
		||||
    caret.registerStorage.setKeys(register, keys?.filterNotNull() ?: emptyList())
 | 
			
		||||
    injector.registerGroup.setKeys(register, keys?.filterNotNull() ?: emptyList())
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Set the current contents of the given register */
 | 
			
		||||
 
 | 
			
		||||
@@ -79,11 +79,12 @@ internal class CommentaryExtension : VimExtension {
 | 
			
		||||
 | 
			
		||||
        val project = editor.ij.project!!
 | 
			
		||||
        val callback = { afterCommenting(mode, editor, resetCaret, range) }
 | 
			
		||||
        actions.any { executeActionWithCallbackOnSuccess(it, project, context, callback) }
 | 
			
		||||
        actions.any { executeActionWithCallbackOnSuccess(editor, it, project, context, callback) }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun executeActionWithCallbackOnSuccess(
 | 
			
		||||
      editor: VimEditor,
 | 
			
		||||
      action: String,
 | 
			
		||||
      project: Project,
 | 
			
		||||
      context: ExecutionContext,
 | 
			
		||||
@@ -92,7 +93,7 @@ internal class CommentaryExtension : VimExtension {
 | 
			
		||||
      val res = Ref.create<Boolean>(false)
 | 
			
		||||
      AsyncActionExecutionService.getInstance(project).withExecutionAfterAction(
 | 
			
		||||
        action,
 | 
			
		||||
        { res.set(injector.actionExecutor.executeAction(action, context)) },
 | 
			
		||||
        { res.set(injector.actionExecutor.executeAction(editor, name = action, context = context)) },
 | 
			
		||||
        { if (res.get()) callback() })
 | 
			
		||||
      return res.get()
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -23,13 +23,15 @@ import com.intellij.util.Alarm
 | 
			
		||||
import com.intellij.util.Alarm.ThreadToUse
 | 
			
		||||
import com.maddyhome.idea.vim.VimPlugin
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.common.ModeChangeListener
 | 
			
		||||
import com.maddyhome.idea.vim.common.TextRange
 | 
			
		||||
import com.maddyhome.idea.vim.common.VimYankListener
 | 
			
		||||
import com.maddyhome.idea.vim.extension.VimExtension
 | 
			
		||||
import com.maddyhome.idea.vim.helper.MessageHelper
 | 
			
		||||
import com.maddyhome.idea.vim.helper.VimNlsSafe
 | 
			
		||||
import com.maddyhome.idea.vim.listener.VimInsertListener
 | 
			
		||||
import com.maddyhome.idea.vim.listener.VimYankListener
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.ij
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
 | 
			
		||||
import org.jetbrains.annotations.NonNls
 | 
			
		||||
@@ -75,7 +77,7 @@ internal class HighlightColorResetter : LafManagerListener {
 | 
			
		||||
 *
 | 
			
		||||
 * When a new text is yanked or user starts editing, the old highlighting would be deleted.
 | 
			
		||||
 */
 | 
			
		||||
internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertListener {
 | 
			
		||||
internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeListener {
 | 
			
		||||
  private val highlightHandler = HighlightHandler()
 | 
			
		||||
  private var initialised = false
 | 
			
		||||
 | 
			
		||||
@@ -83,8 +85,8 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertList
 | 
			
		||||
 | 
			
		||||
  override fun init() {
 | 
			
		||||
    // Note that these listeners will still be registered when IdeaVim is disabled. However, they'll never get called
 | 
			
		||||
    VimPlugin.getYank().addListener(this)
 | 
			
		||||
    VimPlugin.getChange().addInsertListener(this)
 | 
			
		||||
    injector.listenersNotifier.modeChangeListeners.add(this)
 | 
			
		||||
    injector.listenersNotifier.yankListeners.add(this)
 | 
			
		||||
 | 
			
		||||
    // Register our own disposable to remove highlights when IdeaVim is disabled. Somehow, we need to re-register when
 | 
			
		||||
    // IdeaVim is re-enabled. We don't get a call back for that, but because the listeners are active until the
 | 
			
		||||
@@ -105,8 +107,8 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertList
 | 
			
		||||
 | 
			
		||||
  override fun dispose() {
 | 
			
		||||
    // Called when the extension is disabled with `:set no{extension}` or if the plugin owning the extension unloads
 | 
			
		||||
    VimPlugin.getYank().removeListener(this)
 | 
			
		||||
    VimPlugin.getChange().removeInsertListener(this)
 | 
			
		||||
    injector.listenersNotifier.modeChangeListeners.remove(this)
 | 
			
		||||
    injector.listenersNotifier.yankListeners.remove(this)
 | 
			
		||||
 | 
			
		||||
    highlightHandler.clearYankHighlighters()
 | 
			
		||||
    initialised = false
 | 
			
		||||
@@ -117,7 +119,8 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertList
 | 
			
		||||
    highlightHandler.highlightYankRange(editor.ij, range)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun insertModeStarted(editor: Editor) {
 | 
			
		||||
  override fun modeChanged(editor: VimEditor, oldMode: Mode) {
 | 
			
		||||
    if (editor.mode !is Mode.INSERT) return
 | 
			
		||||
    ensureInitialised()
 | 
			
		||||
    highlightHandler.clearYankHighlighters()
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -234,7 +234,7 @@ private object FileTypePatterns {
 | 
			
		||||
    } else if (fileTypeName == "CMakeLists.txt" || fileName == "CMakeLists") {
 | 
			
		||||
      this.cMakePatterns
 | 
			
		||||
    } else {
 | 
			
		||||
      return null
 | 
			
		||||
      this.htmlPatterns
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -481,6 +481,9 @@ internal class NerdTree : VimExtension {
 | 
			
		||||
    registerCommand("NERDTreeMapNewFile", "n", NerdAction.ToIj("NewFile"))
 | 
			
		||||
    registerCommand("NERDTreeMapNewDir", "N", NerdAction.ToIj("NewDir"))
 | 
			
		||||
    registerCommand("NERDTreeMapDelete", "d", NerdAction.ToIj("\$Delete"))
 | 
			
		||||
    registerCommand("NERDTreeMapCopy", "y", NerdAction.ToIj("\$Copy"))
 | 
			
		||||
    registerCommand("NERDTreeMapPaste", "v", NerdAction.ToIj("\$Paste"))
 | 
			
		||||
    registerCommand("NERDTreeMapRename", "<C-r>", NerdAction.ToIj("RenameElement"))
 | 
			
		||||
    registerCommand("NERDTreeMapRefreshRoot", "R", NerdAction.ToIj("Synchronize"))
 | 
			
		||||
    registerCommand("NERDTreeMapMenu", "m", NerdAction.ToIj("ShowPopupMenu"))
 | 
			
		||||
    registerCommand("NERDTreeMapQuit", "q", NerdAction.ToIj("HideActiveWindow"))
 | 
			
		||||
 
 | 
			
		||||
@@ -144,7 +144,7 @@ internal class ReplaceWithRegister : VimExtension {
 | 
			
		||||
private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
 | 
			
		||||
  val registerGroup = injector.registerGroup
 | 
			
		||||
  val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
 | 
			
		||||
  val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return
 | 
			
		||||
  val savedRegister = registerGroup.getRegister(lastRegisterChar) ?: return
 | 
			
		||||
 | 
			
		||||
  var usedType = savedRegister.type
 | 
			
		||||
  var usedText = savedRegister.text
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,30 @@
 | 
			
		||||
package com.maddyhome.idea.vim.extension.surround
 | 
			
		||||
 | 
			
		||||
import com.intellij.util.text.CharSequenceSubSequence
 | 
			
		||||
 | 
			
		||||
internal data class RepeatedCharSequence(val text: CharSequence, val count: Int) : CharSequence {
 | 
			
		||||
  override val length = text.length * count
 | 
			
		||||
 | 
			
		||||
  override fun get(index: Int): Char {
 | 
			
		||||
    if (index < 0 || index >= length) throw IndexOutOfBoundsException()
 | 
			
		||||
    return text[index % text.length]
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
 | 
			
		||||
    return CharSequenceSubSequence(this, startIndex, endIndex)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun toString(): String {
 | 
			
		||||
    return text.repeat(count)
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  companion object {
 | 
			
		||||
    fun of(text: CharSequence, count: Int): CharSequence {
 | 
			
		||||
      return when (count) {
 | 
			
		||||
        0 -> ""
 | 
			
		||||
        1 -> text
 | 
			
		||||
        else -> RepeatedCharSequence(text, count)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -14,6 +14,7 @@ import com.intellij.openapi.editor.Editor
 | 
			
		||||
import com.maddyhome.idea.vim.VimPlugin
 | 
			
		||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimCaret
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimChangeGroup
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.api.endsWithNewLine
 | 
			
		||||
import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
 | 
			
		||||
@@ -35,7 +36,11 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
 | 
			
		||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
 | 
			
		||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
 | 
			
		||||
import com.maddyhome.idea.vim.group.findBlockRange
 | 
			
		||||
import com.maddyhome.idea.vim.helper.exitVisualMode
 | 
			
		||||
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
 | 
			
		||||
@@ -46,6 +51,7 @@ import com.maddyhome.idea.vim.state.mode.selectionType
 | 
			
		||||
import org.jetbrains.annotations.NonNls
 | 
			
		||||
import java.awt.event.KeyEvent
 | 
			
		||||
import javax.swing.KeyStroke
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.returnTo
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Port of vim-surround.
 | 
			
		||||
@@ -78,7 +84,7 @@ internal class VimSurroundExtension : VimExtension {
 | 
			
		||||
      putKeyMappingIfMissing(MappingMode.XO, injector.parser.parseKeys("S"), owner, injector.parser.parseKeys("<Plug>VSurround"), true)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator())
 | 
			
		||||
    VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator(supportsMultipleCursors = false, count = 1)) // TODO
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private class YSurroundHandler : ExtensionHandler {
 | 
			
		||||
@@ -106,11 +112,14 @@ internal class VimSurroundExtension : VimExtension {
 | 
			
		||||
        val lastNonWhiteSpaceOffset = getLastNonWhitespaceCharacterOffset(editor.text(), lineStartOffset, lineEndOffset)
 | 
			
		||||
        if (lastNonWhiteSpaceOffset != null) {
 | 
			
		||||
          val range = TextRange(lineStartOffset, lastNonWhiteSpaceOffset + 1)
 | 
			
		||||
          performSurround(pair, range, it)
 | 
			
		||||
          performSurround(pair, range, it, count = operatorArguments.count1)
 | 
			
		||||
        }
 | 
			
		||||
//        it.moveToOffset(lineStartOffset)
 | 
			
		||||
      }
 | 
			
		||||
      // Jump back to start
 | 
			
		||||
      if (editor.mode !is Mode.NORMAL) {
 | 
			
		||||
        editor.mode = Mode.NORMAL()
 | 
			
		||||
      }
 | 
			
		||||
      executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -126,15 +135,13 @@ internal class VimSurroundExtension : VimExtension {
 | 
			
		||||
 | 
			
		||||
  private class VSurroundHandler : ExtensionHandler {
 | 
			
		||||
    override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
 | 
			
		||||
      val selectionStart = editor.ij.caretModel.primaryCaret.selectionStart
 | 
			
		||||
      // NB: Operator ignores SelectionType anyway
 | 
			
		||||
      if (!Operator().apply(editor, context, editor.mode.selectionType)) {
 | 
			
		||||
      if (!Operator(supportsMultipleCursors = true, count = operatorArguments.count1).apply(editor, context, editor.mode.selectionType)) {
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
      runWriteAction {
 | 
			
		||||
        // Leave visual mode
 | 
			
		||||
        executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
 | 
			
		||||
        editor.ij.caretModel.moveToOffset(selectionStart)
 | 
			
		||||
        editor.exitVisualMode()
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@@ -155,6 +162,10 @@ internal class VimSurroundExtension : VimExtension {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
      fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) {
 | 
			
		||||
        editor.ij.runWithEveryCaretAndRestore { changeAtCaret(editor, context, charFrom, newSurround) }
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      fun changeAtCaret(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) {
 | 
			
		||||
        // Save old register values for carets
 | 
			
		||||
        val surroundings = editor.sortedCarets()
 | 
			
		||||
          .map {
 | 
			
		||||
@@ -262,25 +273,48 @@ internal class VimSurroundExtension : VimExtension {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private class Operator : OperatorFunction {
 | 
			
		||||
    override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
 | 
			
		||||
      val ijEditor = editor.ij
 | 
			
		||||
  private class Operator(private val supportsMultipleCursors: Boolean, private val count: Int) : OperatorFunction {
 | 
			
		||||
    override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
 | 
			
		||||
      val ijEditor = vimEditor.ij
 | 
			
		||||
      val c = getChar(ijEditor)
 | 
			
		||||
      if (c.code == 0) return true
 | 
			
		||||
 | 
			
		||||
      val pair = getOrInputPair(c, ijEditor, context.ij) ?: return false
 | 
			
		||||
      // XXX: Will it work with line-wise or block-wise selections?
 | 
			
		||||
      val range = getSurroundRange(editor.currentCaret()) ?: return false
 | 
			
		||||
      performSurround(pair, range, editor.currentCaret(), selectionType == SelectionType.LINE_WISE)
 | 
			
		||||
 | 
			
		||||
      runWriteAction {
 | 
			
		||||
        val change = VimPlugin.getChange()
 | 
			
		||||
        if (supportsMultipleCursors) {
 | 
			
		||||
          ijEditor.runWithEveryCaretAndRestore {
 | 
			
		||||
            applyOnce(ijEditor, change, pair, count)
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
          applyOnce(ijEditor, change, pair, count)
 | 
			
		||||
          // Jump back to start
 | 
			
		||||
          executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return true
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    private fun applyOnce(editor: Editor, change: VimChangeGroup, pair: Pair<String, String>, count: Int) {
 | 
			
		||||
      // XXX: Will it work with line-wise or block-wise selections?
 | 
			
		||||
      val primaryCaret = editor.caretModel.primaryCaret
 | 
			
		||||
      val range = getSurroundRange(primaryCaret.vim)
 | 
			
		||||
      if (range != null) {
 | 
			
		||||
        val start = RepeatedCharSequence.of(pair.first, count)
 | 
			
		||||
        val end = RepeatedCharSequence.of(pair.second, count)
 | 
			
		||||
        change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, start)
 | 
			
		||||
        change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.endOffset + start.length, end)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getSurroundRange(caret: VimCaret): TextRange? {
 | 
			
		||||
      val editor = caret.editor
 | 
			
		||||
      val ijEditor = editor.ij
 | 
			
		||||
      return when (ijEditor.vim.mode) {
 | 
			
		||||
      if (editor.mode is Mode.CMD_LINE) {
 | 
			
		||||
        editor.mode = (editor.mode as Mode.CMD_LINE).returnTo()
 | 
			
		||||
      }
 | 
			
		||||
      return when (editor.mode) {
 | 
			
		||||
        is Mode.NORMAL -> injector.markService.getChangeMarks(caret)
 | 
			
		||||
        is Mode.VISUAL -> caret.run { TextRange(selectionStart, selectionEnd) }
 | 
			
		||||
        else -> null
 | 
			
		||||
@@ -323,6 +357,9 @@ private fun getSurroundPair(c: Char): Pair<String, String>? = if (c in SURROUND_
 | 
			
		||||
 | 
			
		||||
private fun inputTagPair(editor: Editor, context: DataContext): Pair<String, String>? {
 | 
			
		||||
  val tagInput = inputString(editor, context, "<", '>')
 | 
			
		||||
  if (editor.vim.mode is Mode.CMD_LINE) {
 | 
			
		||||
    editor.vim.mode = editor.vim.mode.returnTo()
 | 
			
		||||
  }
 | 
			
		||||
  val matcher = tagNameAndAttributesCapturePattern.matcher(tagInput)
 | 
			
		||||
  return if (matcher.find()) {
 | 
			
		||||
    val tagName = matcher.group(1)
 | 
			
		||||
@@ -339,6 +376,9 @@ private fun inputFunctionName(
 | 
			
		||||
  withInternalSpaces: Boolean,
 | 
			
		||||
): Pair<String, String>? {
 | 
			
		||||
  val functionNameInput = inputString(editor, context, "function: ", null)
 | 
			
		||||
  if (editor.vim.mode is Mode.CMD_LINE) {
 | 
			
		||||
    editor.vim.mode = editor.vim.mode.returnTo()
 | 
			
		||||
  }
 | 
			
		||||
  if (functionNameInput.isEmpty()) return null
 | 
			
		||||
  return if (withInternalSpaces) "$functionNameInput( " to " )" else "$functionNameInput(" to ")"
 | 
			
		||||
}
 | 
			
		||||
@@ -362,15 +402,15 @@ private fun getChar(editor: Editor): Char {
 | 
			
		||||
  return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: VimCaret, tagsOnNewLines: Boolean = false) {
 | 
			
		||||
private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: VimCaret, count: Int, tagsOnNewLines: Boolean = false) {
 | 
			
		||||
  runWriteAction {
 | 
			
		||||
    val editor = caret.editor
 | 
			
		||||
    val change = VimPlugin.getChange()
 | 
			
		||||
    val leftSurround = pair.first + if (tagsOnNewLines) "\n" else ""
 | 
			
		||||
    val leftSurround = RepeatedCharSequence.of(pair.first + if (tagsOnNewLines) "\n" else "", count)
 | 
			
		||||
 | 
			
		||||
    val isEOF = range.endOffset == editor.text().length
 | 
			
		||||
    val hasNewLine = editor.endsWithNewLine()
 | 
			
		||||
    val rightSurround = if (tagsOnNewLines) {
 | 
			
		||||
    val rightSurround = (if (tagsOnNewLines) {
 | 
			
		||||
      if (isEOF && !hasNewLine) {
 | 
			
		||||
        "\n" + pair.second
 | 
			
		||||
      } else {
 | 
			
		||||
@@ -378,7 +418,7 @@ private fun performSurround(pair: Pair<String, String>, range: TextRange, caret:
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      pair.second
 | 
			
		||||
    }
 | 
			
		||||
    }).let { RepeatedCharSequence.of(it, count) }
 | 
			
		||||
 | 
			
		||||
    change.insertText(editor, caret, range.startOffset, leftSurround)
 | 
			
		||||
    change.insertText(editor, caret, range.endOffset + leftSurround.length, rightSurround)
 | 
			
		||||
 
 | 
			
		||||
@@ -15,78 +15,35 @@ import com.intellij.openapi.command.CommandProcessor
 | 
			
		||||
import com.intellij.openapi.command.UndoConfirmationPolicy
 | 
			
		||||
import com.intellij.openapi.diagnostic.logger
 | 
			
		||||
import com.intellij.openapi.editor.Editor
 | 
			
		||||
import com.intellij.openapi.editor.LogicalPosition
 | 
			
		||||
import com.intellij.openapi.editor.actions.EnterAction
 | 
			
		||||
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.util.UserDataHolder
 | 
			
		||||
import com.intellij.openapi.util.text.StringUtil
 | 
			
		||||
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.api.BufferPosition
 | 
			
		||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
			
		||||
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.anyNonWhitespace
 | 
			
		||||
import com.maddyhome.idea.vim.api.getLineEndForOffset
 | 
			
		||||
import com.maddyhome.idea.vim.api.getLineEndOffset
 | 
			
		||||
import com.maddyhome.idea.vim.api.getLineStartForOffset
 | 
			
		||||
import com.maddyhome.idea.vim.api.getText
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.api.lineLength
 | 
			
		||||
import com.maddyhome.idea.vim.api.normalizeOffset
 | 
			
		||||
import com.maddyhome.idea.vim.api.options
 | 
			
		||||
import com.maddyhome.idea.vim.command.Argument
 | 
			
		||||
import com.maddyhome.idea.vim.command.OperatorArguments
 | 
			
		||||
import com.maddyhome.idea.vim.common.IndentConfig.Companion.create
 | 
			
		||||
import com.maddyhome.idea.vim.common.TextRange
 | 
			
		||||
import com.maddyhome.idea.vim.ex.ranges.LineRange
 | 
			
		||||
import com.maddyhome.idea.vim.group.MotionGroup.Companion.getMotionRange2
 | 
			
		||||
import com.maddyhome.idea.vim.group.visual.VimSelection
 | 
			
		||||
import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
 | 
			
		||||
import com.maddyhome.idea.vim.handler.Motion
 | 
			
		||||
import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
 | 
			
		||||
import com.maddyhome.idea.vim.handler.commandContinuation
 | 
			
		||||
import com.maddyhome.idea.vim.helper.CharacterHelper
 | 
			
		||||
import com.maddyhome.idea.vim.helper.CharacterHelper.changeCase
 | 
			
		||||
import com.maddyhome.idea.vim.helper.CharacterHelper.charType
 | 
			
		||||
import com.maddyhome.idea.vim.helper.EditorHelper
 | 
			
		||||
import com.maddyhome.idea.vim.helper.NumberType
 | 
			
		||||
import com.maddyhome.idea.vim.helper.endOffsetInclusive
 | 
			
		||||
import com.maddyhome.idea.vim.helper.findNumberUnderCursor
 | 
			
		||||
import com.maddyhome.idea.vim.helper.findNumbersInRange
 | 
			
		||||
import com.maddyhome.idea.vim.helper.inInsertMode
 | 
			
		||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition
 | 
			
		||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
 | 
			
		||||
import com.maddyhome.idea.vim.key.KeyHandlerKeeper.Companion.getInstance
 | 
			
		||||
import com.maddyhome.idea.vim.listener.VimInsertListener
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
 | 
			
		||||
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.regexp.VimRegex
 | 
			
		||||
import com.maddyhome.idea.vim.regexp.match.VimMatchResult
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.commands.SortOption
 | 
			
		||||
import org.jetbrains.annotations.TestOnly
 | 
			
		||||
import java.math.BigInteger
 | 
			
		||||
import java.util.*
 | 
			
		||||
import java.util.function.Consumer
 | 
			
		||||
import kotlin.math.max
 | 
			
		||||
import kotlin.math.min
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Provides all the insert/replace related functionality
 | 
			
		||||
 */
 | 
			
		||||
class ChangeGroup : VimChangeGroupBase() {
 | 
			
		||||
  private val insertListeners = ContainerUtil.createLockFreeCopyOnWriteList<VimInsertListener>()
 | 
			
		||||
  private val listener: EditorMouseListener = object : EditorMouseListener {
 | 
			
		||||
    override fun mouseClicked(event: EditorMouseEvent) {
 | 
			
		||||
      val editor = event.editor
 | 
			
		||||
@@ -146,163 +103,6 @@ class ChangeGroup : VimChangeGroupBase() {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun getDeleteRangeAndType2(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    caret: VimCaret,
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
    argument: Argument,
 | 
			
		||||
    isChange: Boolean,
 | 
			
		||||
    operatorArguments: OperatorArguments,
 | 
			
		||||
  ): Pair<TextRange, SelectionType>? {
 | 
			
		||||
    val range = getMotionRange2(
 | 
			
		||||
      (editor as IjVimEditor).editor,
 | 
			
		||||
      (caret as IjVimCaret).caret,
 | 
			
		||||
      (context as IjEditorExecutionContext).context,
 | 
			
		||||
      argument,
 | 
			
		||||
      operatorArguments
 | 
			
		||||
    )
 | 
			
		||||
      ?: 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
 | 
			
		||||
    var type: SelectionType
 | 
			
		||||
    type = if (argument.motion.isLinewiseMotion()) {
 | 
			
		||||
      SelectionType.LINE_WISE
 | 
			
		||||
    } else {
 | 
			
		||||
      SelectionType.CHARACTER_WISE
 | 
			
		||||
    }
 | 
			
		||||
    val motion = argument.motion
 | 
			
		||||
    if (!isChange && !motion.isLinewiseMotion()) {
 | 
			
		||||
      val start = editor.offsetToBufferPosition(range.startOffset)
 | 
			
		||||
      val end = editor.offsetToBufferPosition(range.endOffset)
 | 
			
		||||
      if (start.line != end.line) {
 | 
			
		||||
        val offset1 = range.startOffset
 | 
			
		||||
        if (!editor.anyNonWhitespace(offset1, -1)) {
 | 
			
		||||
          val offset = range.endOffset
 | 
			
		||||
          if (!editor.anyNonWhitespace(offset, 1)) {
 | 
			
		||||
            type = SelectionType.LINE_WISE
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return Pair(range, type)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Toggles the case of count characters
 | 
			
		||||
   *
 | 
			
		||||
   * @param editor The editor to change
 | 
			
		||||
   * @param caret  The caret on which the operation is performed
 | 
			
		||||
   * @param count  The number of characters to change
 | 
			
		||||
   * @return true if able to change count characters
 | 
			
		||||
   */
 | 
			
		||||
  override fun changeCaseToggleCharacter(editor: VimEditor, caret: VimCaret, count: Int): Boolean {
 | 
			
		||||
    val allowWrap = injector.options(editor).whichwrap.contains("~")
 | 
			
		||||
    var motion = injector.motion.getHorizontalMotion(editor, caret, count, true, allowWrap)
 | 
			
		||||
    if (motion is Motion.Error) return false
 | 
			
		||||
    changeCase(editor, caret, caret.offset, (motion as AbsoluteOffset).offset, CharacterHelper.CASE_TOGGLE)
 | 
			
		||||
    motion = injector.motion.getHorizontalMotion(
 | 
			
		||||
      editor,
 | 
			
		||||
      caret,
 | 
			
		||||
      count,
 | 
			
		||||
      false,
 | 
			
		||||
      allowWrap
 | 
			
		||||
    ) // same but without allow end because we can change till end, but can't move caret there
 | 
			
		||||
    if (motion is AbsoluteOffset) {
 | 
			
		||||
      caret.moveToOffset(editor.normalizeOffset(motion.offset, false))
 | 
			
		||||
    }
 | 
			
		||||
    return true
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun blockInsert(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
    range: TextRange,
 | 
			
		||||
    append: Boolean,
 | 
			
		||||
    operatorArguments: OperatorArguments,
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
    val lines = getLinesCountInVisualBlock(editor, range)
 | 
			
		||||
    val startPosition = editor.offsetToBufferPosition(range.startOffset)
 | 
			
		||||
    val mode = operatorArguments.mode
 | 
			
		||||
    val visualBlockMode = mode is VISUAL && mode.selectionType === SelectionType.BLOCK_WISE
 | 
			
		||||
    for (caret in editor.carets()) {
 | 
			
		||||
      val line = startPosition.line
 | 
			
		||||
      var column = startPosition.column
 | 
			
		||||
      if (!visualBlockMode) {
 | 
			
		||||
        column = 0
 | 
			
		||||
      } else if (append) {
 | 
			
		||||
        column += range.maxLength
 | 
			
		||||
        if (caret.vimLastColumn == VimMotionGroupBase.LAST_COLUMN) {
 | 
			
		||||
          column = VimMotionGroupBase.LAST_COLUMN
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      val lineLength = editor.lineLength(line)
 | 
			
		||||
      if (column < VimMotionGroupBase.LAST_COLUMN && lineLength < column) {
 | 
			
		||||
        val pad = EditorHelper.pad((editor as IjVimEditor).editor, line, column)
 | 
			
		||||
        val offset = editor.getLineEndOffset(line)
 | 
			
		||||
        insertText(editor, caret, offset, pad)
 | 
			
		||||
      }
 | 
			
		||||
      if (visualBlockMode || !append) {
 | 
			
		||||
        (caret as IjVimCaret).caret.moveToInlayAwareLogicalPosition(LogicalPosition(line, column))
 | 
			
		||||
      }
 | 
			
		||||
      if (visualBlockMode) {
 | 
			
		||||
        setInsertRepeat(lines, column, append)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (visualBlockMode || !append) {
 | 
			
		||||
      insertBeforeCursor(editor, context)
 | 
			
		||||
    } else {
 | 
			
		||||
      insertAfterCursor(editor, context)
 | 
			
		||||
    }
 | 
			
		||||
    return true
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Changes the case of all the characters in the range
 | 
			
		||||
   *
 | 
			
		||||
   * @param editor The editor to change
 | 
			
		||||
   * @param caret  The caret to be moved
 | 
			
		||||
   * @param range  The range to change
 | 
			
		||||
   * @param type   The case change type (TOGGLE, UPPER, LOWER)
 | 
			
		||||
   * @return true if able to delete the text, false if not
 | 
			
		||||
   */
 | 
			
		||||
  override fun changeCaseRange(editor: VimEditor, caret: VimCaret, range: TextRange, type: Char): Boolean {
 | 
			
		||||
    val starts = range.startOffsets
 | 
			
		||||
    val ends = range.endOffsets
 | 
			
		||||
    for (i in ends.indices.reversed()) {
 | 
			
		||||
      changeCase(editor, caret, starts[i], ends[i], type)
 | 
			
		||||
    }
 | 
			
		||||
    caret.moveToOffset(range.startOffset)
 | 
			
		||||
    return true
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * This performs the actual case change.
 | 
			
		||||
   *
 | 
			
		||||
   * @param editor The editor to change
 | 
			
		||||
   * @param start  The start offset to change
 | 
			
		||||
   * @param end    The end offset to change
 | 
			
		||||
   * @param type   The type of change (TOGGLE, UPPER, LOWER)
 | 
			
		||||
   */
 | 
			
		||||
  private fun changeCase(editor: VimEditor, caret: VimCaret, start: Int, end: Int, type: Char) {
 | 
			
		||||
    var start = start
 | 
			
		||||
    var end = end
 | 
			
		||||
    if (start > end) {
 | 
			
		||||
      val t = end
 | 
			
		||||
      end = start
 | 
			
		||||
      start = t
 | 
			
		||||
    }
 | 
			
		||||
    end = editor.normalizeOffset(end, true)
 | 
			
		||||
    val chars = editor.text()
 | 
			
		||||
    val sb = StringBuilder()
 | 
			
		||||
    for (i in start until end) {
 | 
			
		||||
      sb.append(changeCase(chars[i], type))
 | 
			
		||||
    }
 | 
			
		||||
    replaceText(editor, caret, start, end, sb.toString())
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun restoreCursor(editor: VimEditor, caret: VimCaret, startLine: Int) {
 | 
			
		||||
    if (caret != editor.primaryCaret()) {
 | 
			
		||||
      (editor as IjVimEditor).editor.caretModel.addCaret(
 | 
			
		||||
@@ -311,94 +111,20 @@ class ChangeGroup : VimChangeGroupBase() {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Changes the case of all the character moved over by the motion argument.
 | 
			
		||||
   *
 | 
			
		||||
   * @param editor   The editor to change
 | 
			
		||||
   * @param caret    The caret on which motion pretends to be performed
 | 
			
		||||
   * @param context  The data context
 | 
			
		||||
   * @param type     The case change type (TOGGLE, UPPER, LOWER)
 | 
			
		||||
   * @param argument The motion command
 | 
			
		||||
   * @return true if able to delete the text, false if not
 | 
			
		||||
   */
 | 
			
		||||
  override fun changeCaseMotion(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    caret: VimCaret,
 | 
			
		||||
    context: ExecutionContext?,
 | 
			
		||||
    type: Char,
 | 
			
		||||
    argument: Argument,
 | 
			
		||||
    operatorArguments: OperatorArguments,
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
    val range = injector.motion.getMotionRange(
 | 
			
		||||
      editor, caret, context!!, argument,
 | 
			
		||||
      operatorArguments
 | 
			
		||||
    )
 | 
			
		||||
    return range != null && changeCaseRange(editor, caret, range, type)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun reformatCodeMotion(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    caret: VimCaret,
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
    argument: Argument,
 | 
			
		||||
    operatorArguments: OperatorArguments,
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
    val range = injector.motion.getMotionRange(
 | 
			
		||||
      editor, caret, context, argument,
 | 
			
		||||
      operatorArguments
 | 
			
		||||
    )
 | 
			
		||||
    return range != null && reformatCodeRange(editor, caret, range)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun reformatCodeSelection(editor: VimEditor, caret: VimCaret, range: VimSelection) {
 | 
			
		||||
    val textRange = range.toVimTextRange(true)
 | 
			
		||||
    reformatCodeRange(editor, caret, textRange)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun reformatCodeRange(editor: VimEditor, caret: VimCaret, range: TextRange): Boolean {
 | 
			
		||||
    val starts = range.startOffsets
 | 
			
		||||
    val ends = range.endOffsets
 | 
			
		||||
    val firstLine = editor.offsetToBufferPosition(range.startOffset).line
 | 
			
		||||
    for (i in ends.indices.reversed()) {
 | 
			
		||||
      val startOffset = editor.getLineStartForOffset(starts[i])
 | 
			
		||||
      val offset = ends[i] - if (startOffset == ends[i]) 0 else 1
 | 
			
		||||
      val endOffset = editor.getLineEndForOffset(offset)
 | 
			
		||||
      reformatCode(editor, startOffset, endOffset)
 | 
			
		||||
    }
 | 
			
		||||
    val newOffset = injector.motion.moveCaretToLineStartSkipLeading(editor, firstLine)
 | 
			
		||||
    caret.moveToOffset(newOffset)
 | 
			
		||||
    return true
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun reformatCode(editor: VimEditor, start: Int, end: Int) {
 | 
			
		||||
  override fun reformatCode(editor: VimEditor, start: Int, end: Int) {
 | 
			
		||||
    val project = (editor as IjVimEditor).editor.project ?: return
 | 
			
		||||
    val file = PsiUtilBase.getPsiFileInEditor(editor.editor, project) ?: return
 | 
			
		||||
    val textRange = com.intellij.openapi.util.TextRange.create(start, end)
 | 
			
		||||
    CodeStyleManager.getInstance(project).reformatText(file, listOf(textRange))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun autoIndentMotion(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    caret: VimCaret,
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
    argument: Argument,
 | 
			
		||||
    operatorArguments: OperatorArguments,
 | 
			
		||||
  ) {
 | 
			
		||||
    val range = injector.motion.getMotionRange(editor, caret, context, argument, operatorArguments)
 | 
			
		||||
    if (range != null) {
 | 
			
		||||
      autoIndentRange(
 | 
			
		||||
        editor, caret, context,
 | 
			
		||||
        TextRange(range.startOffset, range.endOffsetInclusive)
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun autoIndentRange(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    caret: VimCaret,
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
    range: TextRange,
 | 
			
		||||
  ) {
 | 
			
		||||
    val startPos = editor.offsetToBufferPosition(caret.offset)
 | 
			
		||||
    val startOffset = editor.getLineStartForOffset(range.startOffset)
 | 
			
		||||
    val endOffset = editor.getLineEndForOffset(range.endOffset)
 | 
			
		||||
    val ijEditor = (editor as IjVimEditor).editor
 | 
			
		||||
@@ -423,11 +149,7 @@ class ChangeGroup : VimChangeGroupBase() {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    val afterAction = {
 | 
			
		||||
      val firstLine = editor.offsetToBufferPosition(
 | 
			
		||||
        min(startOffset.toDouble(), endOffset.toDouble()).toInt()
 | 
			
		||||
      ).line
 | 
			
		||||
      val newOffset = injector.motion.moveCaretToLineStartSkipLeading(editor, firstLine)
 | 
			
		||||
      caret.moveToOffset(newOffset)
 | 
			
		||||
      caret.moveToOffset(injector.motion.moveCaretToLineStartSkipLeading(editor, startPos.line))
 | 
			
		||||
      restoreCursor(editor, caret, (caret as IjVimCaret).caret.logicalPosition.line)
 | 
			
		||||
    }
 | 
			
		||||
    if (project != null) {
 | 
			
		||||
@@ -450,361 +172,14 @@ class ChangeGroup : VimChangeGroupBase() {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun indentLines(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    caret: VimCaret,
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
    lines: Int,
 | 
			
		||||
    dir: Int,
 | 
			
		||||
    operatorArguments: OperatorArguments,
 | 
			
		||||
  ) {
 | 
			
		||||
    val start = caret.offset
 | 
			
		||||
    val end = injector.motion.moveCaretToRelativeLineEnd(editor, caret, lines - 1, true)
 | 
			
		||||
    indentRange(editor, caret, context, TextRange(start, end), 1, dir, operatorArguments)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun indentMotion(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    caret: VimCaret,
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
    argument: Argument,
 | 
			
		||||
    dir: Int,
 | 
			
		||||
    operatorArguments: OperatorArguments,
 | 
			
		||||
  ) {
 | 
			
		||||
    val range = injector.motion.getMotionRange(editor, caret, context, argument, operatorArguments)
 | 
			
		||||
    if (range != null) {
 | 
			
		||||
      indentRange(editor, caret, context, range, 1, dir, operatorArguments)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun indentRange(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    caret: VimCaret,
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
    range: TextRange,
 | 
			
		||||
    count: Int,
 | 
			
		||||
    dir: Int,
 | 
			
		||||
    operatorArguments: OperatorArguments,
 | 
			
		||||
  ) {
 | 
			
		||||
    if (logger.isDebugEnabled) {
 | 
			
		||||
      logger.debug("count=$count")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Remember the current caret column
 | 
			
		||||
    val intendedColumn = caret.vimLastColumn
 | 
			
		||||
    val indentConfig = create((editor as IjVimEditor).editor)
 | 
			
		||||
    val sline = editor.offsetToBufferPosition(range.startOffset).line
 | 
			
		||||
    val endLogicalPosition = editor.offsetToBufferPosition(range.endOffset)
 | 
			
		||||
    val eline = if (endLogicalPosition.column == 0) max((endLogicalPosition.line - 1).toDouble(), 0.0)
 | 
			
		||||
      .toInt() else endLogicalPosition.line
 | 
			
		||||
    if (range.isMultiple) {
 | 
			
		||||
      val from = editor.offsetToBufferPosition(range.startOffset).column
 | 
			
		||||
      if (dir == 1) {
 | 
			
		||||
        // Right shift blockwise selection
 | 
			
		||||
        val indent = indentConfig.createIndentByCount(count)
 | 
			
		||||
        for (l in sline..eline) {
 | 
			
		||||
          val len = editor.lineLength(l)
 | 
			
		||||
          if (len > from) {
 | 
			
		||||
            val spos = BufferPosition(l, from, false)
 | 
			
		||||
            insertText(editor, caret, spos, indent)
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        // Left shift blockwise selection
 | 
			
		||||
        val chars = editor.text()
 | 
			
		||||
        for (l in sline..eline) {
 | 
			
		||||
          val len = editor.lineLength(l)
 | 
			
		||||
          if (len > from) {
 | 
			
		||||
            val spos = BufferPosition(l, from, false)
 | 
			
		||||
            val epos = BufferPosition(l, from + indentConfig.getTotalIndent(count) - 1, false)
 | 
			
		||||
            val wsoff = editor.bufferPositionToOffset(spos)
 | 
			
		||||
            val weoff = editor.bufferPositionToOffset(epos)
 | 
			
		||||
            var pos: Int
 | 
			
		||||
            pos = wsoff
 | 
			
		||||
            while (pos <= weoff) {
 | 
			
		||||
              if (charType(editor, chars[pos], false) !== CharacterHelper.CharacterType.WHITESPACE) {
 | 
			
		||||
                break
 | 
			
		||||
              }
 | 
			
		||||
              pos++
 | 
			
		||||
            }
 | 
			
		||||
            if (pos > wsoff) {
 | 
			
		||||
              deleteText(editor, TextRange(wsoff, pos), null, caret, operatorArguments, true)
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      // Shift non-blockwise selection
 | 
			
		||||
      for (l in sline..eline) {
 | 
			
		||||
        val soff = editor.getLineStartOffset(l)
 | 
			
		||||
        val eoff = editor.getLineEndOffset(l, true)
 | 
			
		||||
        val woff = injector.motion.moveCaretToLineStartSkipLeading(editor, l)
 | 
			
		||||
        val col = editor.offsetToBufferPosition(woff).column
 | 
			
		||||
        val limit = max(0.0, (col + dir * indentConfig.getTotalIndent(count)).toDouble())
 | 
			
		||||
          .toInt()
 | 
			
		||||
        if (col > 0 || soff != eoff) {
 | 
			
		||||
          val indent = indentConfig.createIndentBySize(limit)
 | 
			
		||||
          replaceText(editor, caret, soff, woff, indent)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (!editor.editor.inInsertMode) {
 | 
			
		||||
      if (!range.isMultiple) {
 | 
			
		||||
        // The caret has moved, so reset the intended column before trying to get the expected offset
 | 
			
		||||
        val newCaret = caret.setVimLastColumnAndGetCaret(intendedColumn)
 | 
			
		||||
        val offset = injector.motion.moveCaretToLineWithStartOfLineOption(editor, sline, caret)
 | 
			
		||||
        newCaret.moveToOffset(offset)
 | 
			
		||||
      } else {
 | 
			
		||||
        caret.moveToOffset(range.startOffset)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Sort range of text with a given comparator
 | 
			
		||||
   *
 | 
			
		||||
   * @param editor         The editor to replace text in
 | 
			
		||||
   * @param range          The range to sort
 | 
			
		||||
   * @param lineComparator The comparator to use to sort
 | 
			
		||||
   * @param sortOptions     The option to sort the range
 | 
			
		||||
   * @return true if able to sort the text, false if not
 | 
			
		||||
   */
 | 
			
		||||
  override fun sortRange(
 | 
			
		||||
    editor: VimEditor, caret: VimCaret, range: LineRange, lineComparator: Comparator<String>,
 | 
			
		||||
    sortOptions: SortOption,
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
    val startLine = range.startLine
 | 
			
		||||
    val endLine = range.endLine
 | 
			
		||||
    val count = range.size
 | 
			
		||||
    if (count < 2) {
 | 
			
		||||
      return false
 | 
			
		||||
    }
 | 
			
		||||
    val startOffset = editor.getLineStartOffset(startLine)
 | 
			
		||||
    val endOffset = editor.getLineEndOffset(endLine)
 | 
			
		||||
 | 
			
		||||
    val selectedText = (editor as IjVimEditor).editor.document.getText(TextRangeInterval(startOffset, endOffset))
 | 
			
		||||
    val lines = selectedText.split("\n")
 | 
			
		||||
    val modifiedLines = sortOptions.pattern?.let {
 | 
			
		||||
      if (sortOptions.sortOnPattern) {
 | 
			
		||||
        extractPatternFromLines(editor, lines, startLine, it)
 | 
			
		||||
      } else {
 | 
			
		||||
        deletePatternFromLines(editor, lines, startLine, it)
 | 
			
		||||
      }
 | 
			
		||||
    } ?: lines
 | 
			
		||||
    val sortedLines = lines.zip(modifiedLines)
 | 
			
		||||
      .sortedWith { l1, l2 -> lineComparator.compare(l1.second, l2.second) }
 | 
			
		||||
      .map {it.first}
 | 
			
		||||
      .toMutableList()
 | 
			
		||||
 | 
			
		||||
    if (sortOptions.unique) {
 | 
			
		||||
      val iterator = sortedLines.iterator()
 | 
			
		||||
      var previous: String? = null
 | 
			
		||||
      while (iterator.hasNext()) {
 | 
			
		||||
        val current = iterator.next()
 | 
			
		||||
        if (current == previous || sortOptions.ignoreCase && current.equals(previous, ignoreCase = true)) {
 | 
			
		||||
          iterator.remove()
 | 
			
		||||
        } else {
 | 
			
		||||
          previous = current
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (sortedLines.isEmpty()) {
 | 
			
		||||
      return false
 | 
			
		||||
    }
 | 
			
		||||
    replaceText(editor, caret, startOffset, endOffset, StringUtil.join(sortedLines, "\n"))
 | 
			
		||||
    return true
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun extractPatternFromLines(editor: VimEditor, lines: List<String>, startLine: Int, pattern: String): List<String> {
 | 
			
		||||
    val regex = VimRegex(pattern)
 | 
			
		||||
    return lines.mapIndexed { i: Int, line: String ->
 | 
			
		||||
      val result = regex.findInLine(editor, startLine + i, 0)
 | 
			
		||||
      when (result) {
 | 
			
		||||
        is VimMatchResult.Success -> result.value
 | 
			
		||||
        is VimMatchResult.Failure -> line
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun deletePatternFromLines(editor: VimEditor, lines: List<String>, startLine: Int, pattern: String): List<String> {
 | 
			
		||||
    val regex = VimRegex(pattern)
 | 
			
		||||
    return lines.mapIndexed { i: Int, line: String ->
 | 
			
		||||
      val result = regex.findInLine(editor, startLine + i, 0)
 | 
			
		||||
      when (result) {
 | 
			
		||||
        is VimMatchResult.Success -> line.substring(result.value.length, line.length)
 | 
			
		||||
        is VimMatchResult.Failure -> line
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Perform increment and decrement for numbers in visual mode
 | 
			
		||||
   *
 | 
			
		||||
   *
 | 
			
		||||
   * Flag [avalanche] marks if increment (or decrement) should be performed in avalanche mode
 | 
			
		||||
   * (for v_g_Ctrl-A and v_g_Ctrl-X commands)
 | 
			
		||||
   *
 | 
			
		||||
   * @return true
 | 
			
		||||
   */
 | 
			
		||||
  override fun changeNumberVisualMode(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    caret: VimCaret,
 | 
			
		||||
    selectedRange: TextRange,
 | 
			
		||||
    count: Int,
 | 
			
		||||
    avalanche: Boolean,
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
 | 
			
		||||
    val nf: List<String> = injector.options(editor).nrformats
 | 
			
		||||
    val alpha = nf.contains("alpha")
 | 
			
		||||
    val hex = nf.contains("hex")
 | 
			
		||||
    val octal = nf.contains("octal")
 | 
			
		||||
    val numberRanges = findNumbersInRange((editor as IjVimEditor).editor, selectedRange, alpha, hex, octal)
 | 
			
		||||
    val newNumbers: MutableList<String?> = ArrayList()
 | 
			
		||||
    for (i in numberRanges.indices) {
 | 
			
		||||
      val numberRange = numberRanges[i]
 | 
			
		||||
      val iCount = if (avalanche) (i + 1) * count else count
 | 
			
		||||
      val newNumber = changeNumberInRange(editor, numberRange, iCount, alpha, hex, octal)
 | 
			
		||||
      newNumbers.add(newNumber)
 | 
			
		||||
    }
 | 
			
		||||
    for (i in newNumbers.indices.reversed()) {
 | 
			
		||||
      // Replace text bottom up. In other direction ranges will be desynchronized after inc numbers like 99
 | 
			
		||||
      val (first) = numberRanges[i]
 | 
			
		||||
      val newNumber = newNumbers[i]
 | 
			
		||||
      replaceText(editor, caret, first.startOffset, first.endOffset, newNumber!!)
 | 
			
		||||
    }
 | 
			
		||||
    (caret as IjVimCaret).caret.moveToInlayAwareOffset(selectedRange.startOffset)
 | 
			
		||||
    return true
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun changeNumber(editor: VimEditor, caret: VimCaret, count: Int): Boolean {
 | 
			
		||||
    val nf: List<String> = injector.options(editor).nrformats
 | 
			
		||||
    val alpha = nf.contains("alpha")
 | 
			
		||||
    val hex = nf.contains("hex")
 | 
			
		||||
    val octal = nf.contains("octal")
 | 
			
		||||
    val range = findNumberUnderCursor((editor as IjVimEditor).editor, (caret as IjVimCaret).caret, alpha, hex, octal)
 | 
			
		||||
    if (range == null) {
 | 
			
		||||
      logger.debug("no number on line")
 | 
			
		||||
      return false
 | 
			
		||||
    }
 | 
			
		||||
    val newNumber = changeNumberInRange(editor, range, count, alpha, hex, octal)
 | 
			
		||||
    return if (newNumber == null) {
 | 
			
		||||
      false
 | 
			
		||||
    } else {
 | 
			
		||||
      replaceText(editor, caret, range.first.startOffset, range.first.endOffset, newNumber)
 | 
			
		||||
      caret.caret.moveToInlayAwareOffset(range.first.startOffset + newNumber.length - 1)
 | 
			
		||||
      true
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun reset() {
 | 
			
		||||
    strokes.clear()
 | 
			
		||||
    repeatCharsCount = 0
 | 
			
		||||
    if (lastStrokes != null) {
 | 
			
		||||
      lastStrokes!!.clear()
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun saveStrokes(newStrokes: String?) {
 | 
			
		||||
    val chars = newStrokes!!.toCharArray()
 | 
			
		||||
    strokes.add(chars)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun changeNumberInRange(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    range: Pair<TextRange, NumberType>,
 | 
			
		||||
    count: Int,
 | 
			
		||||
    alpha: Boolean,
 | 
			
		||||
    hex: Boolean,
 | 
			
		||||
    octal: Boolean,
 | 
			
		||||
  ): String? {
 | 
			
		||||
    val text = editor.getText(range.first)
 | 
			
		||||
    val numberType = range.second
 | 
			
		||||
    if (logger.isDebugEnabled) {
 | 
			
		||||
      logger.debug("found range $range")
 | 
			
		||||
      logger.debug("text=$text")
 | 
			
		||||
    }
 | 
			
		||||
    var number = text
 | 
			
		||||
    if (text.isEmpty()) {
 | 
			
		||||
      return null
 | 
			
		||||
    }
 | 
			
		||||
    var ch = text[0]
 | 
			
		||||
    if (hex && NumberType.HEX == numberType) {
 | 
			
		||||
      if (!text.lowercase(Locale.getDefault()).startsWith(HEX_START)) {
 | 
			
		||||
        throw RuntimeException("Hex number should start with 0x: $text")
 | 
			
		||||
      }
 | 
			
		||||
      for (i in text.length - 1 downTo 2) {
 | 
			
		||||
        val index = "abcdefABCDEF".indexOf(text[i])
 | 
			
		||||
        if (index >= 0) {
 | 
			
		||||
          lastLower = index < 6
 | 
			
		||||
          break
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      var num = BigInteger(text.substring(2), 16)
 | 
			
		||||
      num = num.add(BigInteger.valueOf(count.toLong()))
 | 
			
		||||
      if (num.compareTo(BigInteger.ZERO) < 0) {
 | 
			
		||||
        num = BigInteger(MAX_HEX_INTEGER, 16).add(BigInteger.ONE).add(num)
 | 
			
		||||
      }
 | 
			
		||||
      number = num.toString(16)
 | 
			
		||||
      number = number.padStart(text.length - 2, '0')
 | 
			
		||||
      if (!lastLower) {
 | 
			
		||||
        number = number.uppercase(Locale.getDefault())
 | 
			
		||||
      }
 | 
			
		||||
      number = text.substring(0, 2) + number
 | 
			
		||||
    } else if (octal && NumberType.OCT == numberType && text.length > 1) {
 | 
			
		||||
      if (!text.startsWith("0")) throw RuntimeException("Oct number should start with 0: $text")
 | 
			
		||||
      var num = BigInteger(text, 8).add(BigInteger.valueOf(count.toLong()))
 | 
			
		||||
      if (num.compareTo(BigInteger.ZERO) < 0) {
 | 
			
		||||
        num = BigInteger("1777777777777777777777", 8).add(BigInteger.ONE).add(num)
 | 
			
		||||
      }
 | 
			
		||||
      number = num.toString(8)
 | 
			
		||||
      number = "0" + number.padStart(text.length - 1, '0')
 | 
			
		||||
    } else if (alpha && NumberType.ALPHA == numberType) {
 | 
			
		||||
      if (!Character.isLetter(ch)) throw RuntimeException("Not alpha number : $text")
 | 
			
		||||
      ch += count.toChar().code
 | 
			
		||||
      if (Character.isLetter(ch)) {
 | 
			
		||||
        number = ch.toString()
 | 
			
		||||
      }
 | 
			
		||||
    } else if (NumberType.DEC == numberType) {
 | 
			
		||||
      if (ch != '-' && !Character.isDigit(ch)) throw RuntimeException("Not dec number : $text")
 | 
			
		||||
      var pad = ch == '0'
 | 
			
		||||
      var len = text.length
 | 
			
		||||
      if (ch == '-' && text[1] == '0') {
 | 
			
		||||
        pad = true
 | 
			
		||||
        len--
 | 
			
		||||
      }
 | 
			
		||||
      var num = BigInteger(text)
 | 
			
		||||
      num = num.add(BigInteger.valueOf(count.toLong()))
 | 
			
		||||
      number = num.toString()
 | 
			
		||||
      if (!octal && pad) {
 | 
			
		||||
        var neg = false
 | 
			
		||||
        if (number[0] == '-') {
 | 
			
		||||
          neg = true
 | 
			
		||||
          number = number.substring(1)
 | 
			
		||||
        }
 | 
			
		||||
        number = number.padStart(len, '0')
 | 
			
		||||
        if (neg) {
 | 
			
		||||
          number = "-$number"
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return number
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Deprecated(message = "Please use listenersNotifier", replaceWith = ReplaceWith("injector.listenersNotifier.modeChangeListeners.add", imports = ["import com.maddyhome.idea.vim.api.injector"]))
 | 
			
		||||
  fun addInsertListener(listener: VimInsertListener) {
 | 
			
		||||
    insertListeners.add(listener)
 | 
			
		||||
    injector.listenersNotifier.modeChangeListeners.add(listener)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Deprecated(message = "Please use listenersNotifier", replaceWith = ReplaceWith("injector.listenersNotifier.modeChangeListeners.remove", imports = ["import com.maddyhome.idea.vim.api.injector"]))
 | 
			
		||||
  fun removeInsertListener(listener: VimInsertListener) {
 | 
			
		||||
    insertListeners.remove(listener)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun notifyListeners(editor: VimEditor) {
 | 
			
		||||
    insertListeners.forEach(Consumer { listener: VimInsertListener -> listener.insertModeStarted((editor as IjVimEditor).editor) })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @TestOnly
 | 
			
		||||
  override fun resetRepeat() {
 | 
			
		||||
    setInsertRepeat(0, 0, false)
 | 
			
		||||
    injector.listenersNotifier.modeChangeListeners.remove(listener)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private companion object {
 | 
			
		||||
 
 | 
			
		||||
@@ -11,11 +11,14 @@ package com.maddyhome.idea.vim.group;
 | 
			
		||||
import com.intellij.openapi.diagnostic.Logger;
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimDigraphGroupBase;
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor;
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimOutputPanel;
 | 
			
		||||
import com.maddyhome.idea.vim.ex.ExOutputModel;
 | 
			
		||||
import com.maddyhome.idea.vim.helper.EditorHelper;
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
			
		||||
import org.jetbrains.annotations.NotNull;
 | 
			
		||||
 | 
			
		||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
 | 
			
		||||
 | 
			
		||||
public class DigraphGroup extends VimDigraphGroupBase {
 | 
			
		||||
 | 
			
		||||
  public void showDigraphs(@NotNull VimEditor editor) {
 | 
			
		||||
@@ -71,7 +74,9 @@ public class DigraphGroup extends VimDigraphGroupBase {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ExOutputModel.getInstance(((IjVimEditor) editor).getEditor()).output(res.toString());
 | 
			
		||||
    VimOutputPanel output = injector.getOutputPanel().getOrCreate(editor, injector.getExecutionContextManager().getEditorExecutionContext(editor));
 | 
			
		||||
    output.addText(res.toString(), true );
 | 
			
		||||
    output.show();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static final Logger logger = Logger.getInstance(DigraphGroup.class.getName());
 | 
			
		||||
 
 | 
			
		||||
@@ -8,9 +8,7 @@
 | 
			
		||||
 | 
			
		||||
package com.maddyhome.idea.vim.group;
 | 
			
		||||
 | 
			
		||||
import com.intellij.execution.impl.ConsoleViewImpl;
 | 
			
		||||
import com.intellij.find.EditorSearchSession;
 | 
			
		||||
import com.intellij.openapi.application.ApplicationManager;
 | 
			
		||||
import com.intellij.openapi.client.ClientAppSession;
 | 
			
		||||
import com.intellij.openapi.client.ClientKind;
 | 
			
		||||
import com.intellij.openapi.client.ClientSessionsManager;
 | 
			
		||||
@@ -23,12 +21,10 @@ import com.intellij.openapi.editor.event.CaretListener;
 | 
			
		||||
import com.intellij.openapi.editor.ex.EditorEx;
 | 
			
		||||
import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
 | 
			
		||||
import com.intellij.openapi.project.Project;
 | 
			
		||||
import com.maddyhome.idea.vim.KeyHandler;
 | 
			
		||||
import com.maddyhome.idea.vim.VimPlugin;
 | 
			
		||||
import com.maddyhome.idea.vim.api.*;
 | 
			
		||||
import com.maddyhome.idea.vim.ex.ExOutputModel;
 | 
			
		||||
import com.maddyhome.idea.vim.helper.CaretVisualAttributesHelperKt;
 | 
			
		||||
import com.maddyhome.idea.vim.helper.CommandStateHelper;
 | 
			
		||||
import com.maddyhome.idea.vim.helper.EditorHelper;
 | 
			
		||||
import com.maddyhome.idea.vim.helper.UserDataManager;
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimDocument;
 | 
			
		||||
@@ -218,8 +214,10 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
 | 
			
		||||
      editorEx.addPropertyChangeListener(FontSizeChangeListener.INSTANCE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (injector.getApplication().isUnitTest()) {
 | 
			
		||||
      updateCaretsVisualAttributes(new IjVimEditor(editor));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void editorDeinit(@NotNull Editor editor) {
 | 
			
		||||
    deinitLineNumbers(editor);
 | 
			
		||||
@@ -240,9 +238,8 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
 | 
			
		||||
    VimPlugin.getNotifications(project).notifyAboutIdeaJoin(editor);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Nullable
 | 
			
		||||
  @Override
 | 
			
		||||
  public Element getState() {
 | 
			
		||||
  public @Nullable Element getState() {
 | 
			
		||||
    Element element = new Element("editor");
 | 
			
		||||
    saveData(element);
 | 
			
		||||
    return element;
 | 
			
		||||
@@ -318,18 +315,16 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
 | 
			
		||||
      .collect(Collectors.toList());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @NotNull
 | 
			
		||||
  @Override
 | 
			
		||||
  public Collection<VimEditor> getEditors() {
 | 
			
		||||
  public @NotNull Collection<VimEditor> getEditors() {
 | 
			
		||||
    return getLocalEditors()
 | 
			
		||||
      .filter(UserDataManager::getVimInitialised)
 | 
			
		||||
      .map(IjVimEditor::new)
 | 
			
		||||
      .collect(Collectors.toList());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @NotNull
 | 
			
		||||
  @Override
 | 
			
		||||
  public Collection<VimEditor> getEditors(@NotNull VimDocument buffer) {
 | 
			
		||||
  public @NotNull Collection<VimEditor> getEditors(@NotNull VimDocument buffer) {
 | 
			
		||||
    final Document document = ((IjVimDocument)buffer).getDocument();
 | 
			
		||||
    return getLocalEditors()
 | 
			
		||||
      .filter(editor -> UserDataManager.getVimInitialised(editor) && editor.getDocument().equals(document))
 | 
			
		||||
@@ -349,7 +344,7 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
 | 
			
		||||
    // events such as document change (to update search highlights), and these can come from CWM guests, and we'd get
 | 
			
		||||
    // the remote editors.
 | 
			
		||||
    // This invocation will always get local editors, regardless of the current context.
 | 
			
		||||
    List<ClientAppSession> appSessions = ClientSessionsManager.getAppSessions(ClientKind.ALL);
 | 
			
		||||
    List<ClientAppSession> appSessions = ClientSessionsManager.getAppSessions(ClientKind.LOCAL);
 | 
			
		||||
    if (!appSessions.isEmpty()) {
 | 
			
		||||
      ClientAppSession localSession = appSessions.get(0);
 | 
			
		||||
      return localSession.getService(ClientEditorManager.class).editors();
 | 
			
		||||
@@ -377,11 +372,15 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
 | 
			
		||||
          // Note that IDE scale is handled by LafManager.lookAndFeelChanged
 | 
			
		||||
          VimCommandLine activeCommandLine = injector.getCommandLine().getActiveCommandLine();
 | 
			
		||||
          if (activeCommandLine != null) {
 | 
			
		||||
            injector.getProcessGroup().cancelExEntry(new IjVimEditor(editor), true, false);
 | 
			
		||||
            activeCommandLine.close(true, false);
 | 
			
		||||
          }
 | 
			
		||||
          ExOutputModel exOutputModel = ExOutputModel.tryGetInstance(editor);
 | 
			
		||||
          if (exOutputModel != null) {
 | 
			
		||||
            exOutputModel.close();
 | 
			
		||||
          VimOutputPanel outputPanel = injector.getOutputPanel().getCurrentOutputPanel();
 | 
			
		||||
          if (outputPanel != null) {
 | 
			
		||||
            outputPanel.close();
 | 
			
		||||
          }
 | 
			
		||||
          VimModalInput modalInput = injector.getModalInput().getCurrentModalInput();
 | 
			
		||||
          if (modalInput != null) {
 | 
			
		||||
            modalInput.deactivate(true, false);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -173,20 +173,20 @@ class FileGroup : VimFileBase() {
 | 
			
		||||
  /**
 | 
			
		||||
   * Saves specific file in the project.
 | 
			
		||||
   */
 | 
			
		||||
  override fun saveFile(context: ExecutionContext) {
 | 
			
		||||
  override fun saveFile(editor: VimEditor, context: ExecutionContext) {
 | 
			
		||||
    val action = if (injector.globalIjOptions().ideawrite.contains(IjOptionConstants.ideawrite_all)) {
 | 
			
		||||
      injector.nativeActionManager.saveAll
 | 
			
		||||
    } else {
 | 
			
		||||
      injector.nativeActionManager.saveCurrent
 | 
			
		||||
    }
 | 
			
		||||
    action.execute(context)
 | 
			
		||||
    action.execute(editor, context)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Saves all files in the project.
 | 
			
		||||
   */
 | 
			
		||||
  override fun saveFiles(context: ExecutionContext) {
 | 
			
		||||
    injector.nativeActionManager.saveAll.execute(context)
 | 
			
		||||
  override fun saveFiles(editor: VimEditor, context: ExecutionContext) {
 | 
			
		||||
    injector.nativeActionManager.saveAll.execute(editor, context)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
 
 | 
			
		||||
@@ -14,9 +14,7 @@ import com.intellij.openapi.components.State;
 | 
			
		||||
import com.intellij.openapi.components.Storage;
 | 
			
		||||
import com.intellij.openapi.diagnostic.Logger;
 | 
			
		||||
import com.maddyhome.idea.vim.VimPlugin;
 | 
			
		||||
import com.maddyhome.idea.vim.history.HistoryBlock;
 | 
			
		||||
import com.maddyhome.idea.vim.history.HistoryEntry;
 | 
			
		||||
import com.maddyhome.idea.vim.history.VimHistoryBase;
 | 
			
		||||
import com.maddyhome.idea.vim.history.*;
 | 
			
		||||
import org.jdom.Element;
 | 
			
		||||
import org.jetbrains.annotations.NotNull;
 | 
			
		||||
import org.jetbrains.annotations.Nullable;
 | 
			
		||||
@@ -35,21 +33,20 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
 | 
			
		||||
    logger.debug("saveData");
 | 
			
		||||
    Element hist = new Element("history");
 | 
			
		||||
 | 
			
		||||
    saveData(hist, SEARCH);
 | 
			
		||||
    saveData(hist, COMMAND);
 | 
			
		||||
    saveData(hist, EXPRESSION);
 | 
			
		||||
    saveData(hist, INPUT);
 | 
			
		||||
    for (Type type : getHistories().keySet()) {
 | 
			
		||||
      saveData(hist, type);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    element.addContent(hist);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void saveData(@NotNull Element element, String key) {
 | 
			
		||||
    final HistoryBlock block = getHistories().get(key);
 | 
			
		||||
  private void saveData(@NotNull Element element, VimHistory.Type type) {
 | 
			
		||||
    final HistoryBlock block = getHistories().get(type);
 | 
			
		||||
    if (block == null) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final Element root = new Element("history-" + key);
 | 
			
		||||
    final Element root = new Element("history-" + typeToKey(type));
 | 
			
		||||
 | 
			
		||||
    for (HistoryEntry entry : block.getEntries()) {
 | 
			
		||||
      final Element entryElement = new Element("entry");
 | 
			
		||||
@@ -67,10 +64,10 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    readData(hist, SEARCH);
 | 
			
		||||
    readData(hist, COMMAND);
 | 
			
		||||
    readData(hist, EXPRESSION);
 | 
			
		||||
    readData(hist, INPUT);
 | 
			
		||||
    for (Element child : hist.getChildren()) {
 | 
			
		||||
      String key = child.getName().replace("history-", "");
 | 
			
		||||
      readData(hist, key);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void readData(@NotNull Element element, String key) {
 | 
			
		||||
@@ -80,7 +77,7 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    block = new HistoryBlock();
 | 
			
		||||
    getHistories().put(key, block);
 | 
			
		||||
    getHistories().put(getTypeForString(key), block);
 | 
			
		||||
 | 
			
		||||
    final Element root = element.getChild("history-" + key);
 | 
			
		||||
    if (root != null) {
 | 
			
		||||
@@ -94,6 +91,25 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private String typeToKey(VimHistory.Type type) {
 | 
			
		||||
    if (type instanceof VimHistory.Type.Search) {
 | 
			
		||||
      return SEARCH;
 | 
			
		||||
    }
 | 
			
		||||
    if (type instanceof VimHistory.Type.Command) {
 | 
			
		||||
      return COMMAND;
 | 
			
		||||
    }
 | 
			
		||||
    if (type instanceof VimHistory.Type.Expression) {
 | 
			
		||||
      return EXPRESSION;
 | 
			
		||||
    }
 | 
			
		||||
    if (type instanceof VimHistory.Type.Input) {
 | 
			
		||||
      return INPUT;
 | 
			
		||||
    }
 | 
			
		||||
    if (type instanceof VimHistory.Type.Custom) {
 | 
			
		||||
      return ((Type.Custom) type).getId();
 | 
			
		||||
    }
 | 
			
		||||
    return "unreachable";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Nullable
 | 
			
		||||
  @Override
 | 
			
		||||
  public Element getState() {
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ class IjVimPsiService: VimPsiService {
 | 
			
		||||
    val psiFile = PsiHelper.getFile(editor.ij) ?: return null
 | 
			
		||||
    val psiElement = psiFile.findElementAt(pos) ?: return null
 | 
			
		||||
    val language = psiElement.language
 | 
			
		||||
    val commenter = LanguageCommenters.INSTANCE.forLanguage(language)
 | 
			
		||||
    val commenter = LanguageCommenters.INSTANCE.forLanguage(language) ?: return null
 | 
			
		||||
    val psiComment = PsiTreeUtil.getParentOfType(psiElement, PsiComment::class.java, false) ?: return null
 | 
			
		||||
    val commentText = psiComment.text
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -26,10 +26,7 @@ import com.maddyhome.idea.vim.EventFacade;
 | 
			
		||||
import com.maddyhome.idea.vim.VimPlugin;
 | 
			
		||||
import com.maddyhome.idea.vim.action.VimShortcutKeyAction;
 | 
			
		||||
import com.maddyhome.idea.vim.action.change.LazyVimCommand;
 | 
			
		||||
import com.maddyhome.idea.vim.api.NativeAction;
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor;
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimInjectorKt;
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimKeyGroupBase;
 | 
			
		||||
import com.maddyhome.idea.vim.api.*;
 | 
			
		||||
import com.maddyhome.idea.vim.command.MappingMode;
 | 
			
		||||
import com.maddyhome.idea.vim.ex.ExOutputModel;
 | 
			
		||||
import com.maddyhome.idea.vim.key.*;
 | 
			
		||||
@@ -80,25 +77,6 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
 | 
			
		||||
                                                          ((IjVimEditor)editor).getEditor().getComponent());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public boolean showKeyMappings(@NotNull Set<? extends MappingMode> modes, @NotNull Editor editor) {
 | 
			
		||||
    List<Pair<EnumSet<MappingMode>, MappingInfo>> rows = getKeyMappingRows(modes);
 | 
			
		||||
    final StringBuilder builder = new StringBuilder();
 | 
			
		||||
    for (Pair<EnumSet<MappingMode>, MappingInfo> row : rows) {
 | 
			
		||||
      MappingInfo mappingInfo = row.getSecond();
 | 
			
		||||
      builder.append(StringsKt.padEnd(getModesStringCode(row.getFirst()), 2, ' '));
 | 
			
		||||
      builder.append(" ");
 | 
			
		||||
      builder.append(StringsKt.padEnd(VimInjectorKt.getInjector().getParser().toKeyNotation(mappingInfo.getFromKeys()), 11, ' '));
 | 
			
		||||
      builder.append(" ");
 | 
			
		||||
      builder.append(mappingInfo.isRecursive() ? " " : "*");
 | 
			
		||||
      builder.append(" ");
 | 
			
		||||
      builder.append(mappingInfo.getPresentableString());
 | 
			
		||||
      builder.append("\n");
 | 
			
		||||
    }
 | 
			
		||||
    ExOutputModel.getInstance(editor).output(builder.toString());
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void updateShortcutKeysRegistration() {
 | 
			
		||||
    for (VimEditor editor : injector.getEditorGroup().getEditors()) {
 | 
			
		||||
@@ -358,6 +336,22 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public boolean showKeyMappings(@NotNull Set<? extends MappingMode> modes, @NotNull VimEditor editor) {
 | 
			
		||||
    return showKeyMappings(modes, ((IjVimEditor) editor).getEditor());
 | 
			
		||||
    List<Pair<EnumSet<MappingMode>, MappingInfo>> rows = getKeyMappingRows(modes);
 | 
			
		||||
    final StringBuilder builder = new StringBuilder();
 | 
			
		||||
    for (Pair<EnumSet<MappingMode>, MappingInfo> row : rows) {
 | 
			
		||||
      MappingInfo mappingInfo = row.getSecond();
 | 
			
		||||
      builder.append(StringsKt.padEnd(getModesStringCode(row.getFirst()), 2, ' '));
 | 
			
		||||
      builder.append(" ");
 | 
			
		||||
      builder.append(StringsKt.padEnd(VimInjectorKt.getInjector().getParser().toKeyNotation(mappingInfo.getFromKeys()), 11, ' '));
 | 
			
		||||
      builder.append(" ");
 | 
			
		||||
      builder.append(mappingInfo.isRecursive() ? " " : "*");
 | 
			
		||||
      builder.append(" ");
 | 
			
		||||
      builder.append(mappingInfo.getPresentableString());
 | 
			
		||||
      builder.append("\n");
 | 
			
		||||
    }
 | 
			
		||||
    VimOutputPanel outputPanel = injector.getOutputPanel().getOrCreate(editor, injector.getExecutionContextManager().getEditorExecutionContext(editor));
 | 
			
		||||
    outputPanel.addText(builder.toString(), true);
 | 
			
		||||
    outputPanel.show();
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,68 @@
 | 
			
		||||
package com.maddyhome.idea.vim.group
 | 
			
		||||
 | 
			
		||||
import com.intellij.codeInsight.daemon.ReferenceImporter
 | 
			
		||||
import com.intellij.openapi.actionSystem.CommonDataKeys
 | 
			
		||||
import com.intellij.openapi.actionSystem.DataContext
 | 
			
		||||
import com.intellij.openapi.application.ApplicationManager
 | 
			
		||||
import com.intellij.openapi.application.ReadAction
 | 
			
		||||
import com.intellij.openapi.command.WriteCommandAction
 | 
			
		||||
import com.intellij.openapi.editor.Editor
 | 
			
		||||
import com.intellij.openapi.fileEditor.FileDocumentManager
 | 
			
		||||
import com.intellij.openapi.progress.ProgressIndicator
 | 
			
		||||
import com.intellij.openapi.progress.ProgressManager
 | 
			
		||||
import com.intellij.openapi.progress.Task
 | 
			
		||||
import com.intellij.psi.PsiDocumentManager
 | 
			
		||||
import com.intellij.psi.PsiElement
 | 
			
		||||
import com.intellij.psi.PsiRecursiveElementWalkingVisitor
 | 
			
		||||
import java.util.function.BooleanSupplier
 | 
			
		||||
 | 
			
		||||
internal object MacroAutoImport {
 | 
			
		||||
  fun run(editor: Editor, dataContext: DataContext) {
 | 
			
		||||
    val project = CommonDataKeys.PROJECT.getData(dataContext) ?: return
 | 
			
		||||
    val file = PsiDocumentManager.getInstance(project).getPsiFile(editor.document) ?: return
 | 
			
		||||
 | 
			
		||||
    if (!FileDocumentManager.getInstance().requestWriting(editor.document, project)) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val importers = ReferenceImporter.EP_NAME.extensionList
 | 
			
		||||
    if (importers.isEmpty()) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ProgressManager.getInstance().run(object : Task.Backgroundable(project, "Auto import", true) {
 | 
			
		||||
      override fun run(indicator: ProgressIndicator) {
 | 
			
		||||
        val fixes = ReadAction.nonBlocking<List<BooleanSupplier>> {
 | 
			
		||||
          val fixes = mutableListOf<BooleanSupplier>()
 | 
			
		||||
 | 
			
		||||
          file.accept(object : PsiRecursiveElementWalkingVisitor() {
 | 
			
		||||
            override fun visitElement(element: PsiElement) {
 | 
			
		||||
              for (reference in element.references) {
 | 
			
		||||
                if (reference.resolve() != null) {
 | 
			
		||||
                  continue
 | 
			
		||||
                }
 | 
			
		||||
                for (importer in importers) {
 | 
			
		||||
                  importer.computeAutoImportAtOffset(editor, file, element.textRange.startOffset, true)
 | 
			
		||||
                    ?.let(fixes::add)
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
              super.visitElement(element)
 | 
			
		||||
            }
 | 
			
		||||
          })
 | 
			
		||||
 | 
			
		||||
          return@nonBlocking fixes
 | 
			
		||||
        }.executeSynchronously()
 | 
			
		||||
 | 
			
		||||
        ApplicationManager.getApplication().invokeAndWait {
 | 
			
		||||
          WriteCommandAction.writeCommandAction(project)
 | 
			
		||||
            .withName("Auto Import")
 | 
			
		||||
            .withGroupId("IdeaVimAutoImportAfterMacro")
 | 
			
		||||
            .shouldRecordActionForActiveDocument(true)
 | 
			
		||||
            .run<RuntimeException> {
 | 
			
		||||
              fixes.forEach { it.asBoolean }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -22,6 +22,7 @@ import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.helper.MessageHelper.message
 | 
			
		||||
import com.maddyhome.idea.vim.macro.VimMacroBase
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.ij
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Used to handle playback of macros
 | 
			
		||||
@@ -93,6 +94,9 @@ internal class MacroGroup : VimMacroBase() {
 | 
			
		||||
        } finally {
 | 
			
		||||
          keyStack.removeFirst()
 | 
			
		||||
        }
 | 
			
		||||
        if (!isInternalMacro) {
 | 
			
		||||
          MacroAutoImport.run(editor.ij, context.ij)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (isInternalMacro) {
 | 
			
		||||
 
 | 
			
		||||
@@ -47,11 +47,14 @@ import com.maddyhome.idea.vim.helper.getNormalizedScrollOffset
 | 
			
		||||
import com.maddyhome.idea.vim.helper.getNormalizedSideScrollOffset
 | 
			
		||||
import com.maddyhome.idea.vim.helper.isEndAllowed
 | 
			
		||||
import com.maddyhome.idea.vim.helper.vimLastColumn
 | 
			
		||||
import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
 | 
			
		||||
import com.maddyhome.idea.vim.listener.AppCodeTemplates
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.ij
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.vim
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.ReturnTo
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.returnTo
 | 
			
		||||
import org.jetbrains.annotations.Range
 | 
			
		||||
import kotlin.math.max
 | 
			
		||||
import kotlin.math.min
 | 
			
		||||
@@ -312,13 +315,26 @@ internal class MotionGroup : VimMotionGroupBase() {
 | 
			
		||||
                KeyHandler.getInstance().reset(vimEditor)
 | 
			
		||||
              }
 | 
			
		||||
              is Mode.CMD_LINE -> {
 | 
			
		||||
                injector.processGroup.cancelExEntry(vimEditor, refocusOwningEditor = false, resetCaret = false)
 | 
			
		||||
                ExOutputModel.tryGetInstance(editor)?.close()
 | 
			
		||||
                val commandLine = injector.commandLine.getActiveCommandLine() ?: return
 | 
			
		||||
                commandLine.close(refocusOwningEditor = false, resetCaret = false)
 | 
			
		||||
                injector.outputPanel.getCurrentOutputPanel()?.close()
 | 
			
		||||
              }
 | 
			
		||||
              else -> {}
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        val state = injector.vimState as VimStateMachineImpl
 | 
			
		||||
        if (state.mode is Mode.VISUAL) {
 | 
			
		||||
          val returnTo = state.mode.returnTo
 | 
			
		||||
          when (returnTo) {
 | 
			
		||||
            ReturnTo.INSERT -> state.mode = Mode.INSERT
 | 
			
		||||
            ReturnTo.REPLACE -> state.mode = Mode.REPLACE
 | 
			
		||||
            null -> state.mode = Mode.NORMAL()
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        val keyHandler = KeyHandler.getInstance()
 | 
			
		||||
        KeyHandler.getInstance().reset(keyHandler.keyHandlerState, state.mode)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -62,6 +62,25 @@ internal class NotificationService(private val project: Project?) {
 | 
			
		||||
  @Suppress("unused")
 | 
			
		||||
  constructor() : this(null)
 | 
			
		||||
 | 
			
		||||
  fun notifyAboutNewUndo() {
 | 
			
		||||
    val notification = Notification(
 | 
			
		||||
      IDEAVIM_NOTIFICATION_ID,
 | 
			
		||||
      "Undo in IdeaVim now works like in Vim",
 | 
			
		||||
      """
 | 
			
		||||
        Caret movement is no longer a separate undo step, and full insert is undoable in one step.
 | 
			
		||||
      """.trimIndent(),
 | 
			
		||||
      NotificationType.INFORMATION,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    notification.addAction(object : DumbAwareAction("Share Feedback") {
 | 
			
		||||
      override fun actionPerformed(p0: AnActionEvent) {
 | 
			
		||||
        BrowserUtil.browse("https://youtrack.jetbrains.com/issue/VIM-547/Undo-splits-Insert-mode-edits-into-separate-undo-chunks")
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    notification.notify(project)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun notifyAboutIdeaPut() {
 | 
			
		||||
    val notification = Notification(
 | 
			
		||||
      IDEAVIM_NOTIFICATION_ID,
 | 
			
		||||
@@ -182,8 +201,8 @@ internal class NotificationService(private val project: Project?) {
 | 
			
		||||
    ).notify(project)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun notifyActionId(id: String?) {
 | 
			
		||||
    ActionIdNotifier.notifyActionId(id, project)
 | 
			
		||||
  fun notifyActionId(id: String?, candidates: List<String>? = null) {
 | 
			
		||||
    ActionIdNotifier.notifyActionId(id, project, candidates)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun notifyKeymapIssues(issues: ArrayList<KeyMapIssue>) {
 | 
			
		||||
@@ -259,20 +278,31 @@ internal class NotificationService(private val project: Project?) {
 | 
			
		||||
 | 
			
		||||
  object ActionIdNotifier {
 | 
			
		||||
    private var notification: Notification? = null
 | 
			
		||||
    private const val NO_ID = "<i>Cannot detect action id</i>"
 | 
			
		||||
 | 
			
		||||
    fun notifyActionId(id: String?, project: Project?) {
 | 
			
		||||
    fun notifyActionId(id: String?, project: Project?, candidates: List<String>? = null) {
 | 
			
		||||
      notification?.expire()
 | 
			
		||||
 | 
			
		||||
      val content = if (id != null) "Action id: $id" else NO_ID
 | 
			
		||||
      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 ${ActionCenter.getToolwindowName()} to see previous ids</small>")
 | 
			
		||||
      val possibleIDs = candidates?.distinct()?.sorted()
 | 
			
		||||
      val content = when {
 | 
			
		||||
        id != null -> "Action ID: <code>$id</code><br><br>"
 | 
			
		||||
        possibleIDs.isNullOrEmpty() -> "<i>Cannot detect action ID</i><br><br>"
 | 
			
		||||
        possibleIDs.size == 1 -> "Possible action ID: <code>${possibleIDs[0]}</code><br><br>"
 | 
			
		||||
        else -> {
 | 
			
		||||
          buildString {
 | 
			
		||||
            append("<p>Multiple possible action IDs. Candidates include:<ul>")
 | 
			
		||||
            possibleIDs.forEach { append("<li><code>$it</code></li>") }
 | 
			
		||||
            append("</ul></p>")
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      } + "<small>See the ${ActionCenter.getToolwindowName()} tool window for previous IDs</small>"
 | 
			
		||||
 | 
			
		||||
      notification = Notification(IDEAVIM_NOTIFICATION_ID, IDEAVIM_NOTIFICATION_TITLE, content, NotificationType.INFORMATION).also {
 | 
			
		||||
        it.whenExpired { notification = null }
 | 
			
		||||
        it.addAction(StopTracking())
 | 
			
		||||
 | 
			
		||||
        if (id != null) it.addAction(CopyActionId(id, project))
 | 
			
		||||
        if (id != null || possibleIDs?.size == 1) {
 | 
			
		||||
          it.addAction(CopyActionId(id ?: possibleIDs?.get(0), project))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        it.notify(project)
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -95,8 +95,6 @@ class ProcessGroup : VimProcessGroupBase() {
 | 
			
		||||
        val progressIndicator = ProgressIndicatorProvider.getInstance().progressIndicator
 | 
			
		||||
        val output = handler.runProcessWithProgressIndicator(progressIndicator)
 | 
			
		||||
 | 
			
		||||
        lastCommand = command
 | 
			
		||||
 | 
			
		||||
        if (output.isCancelled) {
 | 
			
		||||
          // TODO: Vim will use whatever text has already been written to stdout
 | 
			
		||||
          // For whatever reason, we're not getting any here, so just throw an exception
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,8 @@ import com.intellij.openapi.components.State;
 | 
			
		||||
import com.intellij.openapi.components.Storage;
 | 
			
		||||
import com.intellij.openapi.diagnostic.Logger;
 | 
			
		||||
import com.maddyhome.idea.vim.VimPlugin;
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimInjectorKt;
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimInjectorKt;
 | 
			
		||||
import com.maddyhome.idea.vim.register.Register;
 | 
			
		||||
import com.maddyhome.idea.vim.register.VimRegisterGroupBase;
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.SelectionType;
 | 
			
		||||
@@ -35,6 +37,10 @@ import java.util.List;
 | 
			
		||||
})
 | 
			
		||||
public class RegisterGroup extends VimRegisterGroupBase implements PersistentStateComponent<Element> {
 | 
			
		||||
 | 
			
		||||
  static {
 | 
			
		||||
    IjVimInjectorKt.initInjector();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static final Logger logger = Logger.getInstance(RegisterGroup.class);
 | 
			
		||||
 | 
			
		||||
  public RegisterGroup() {
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ import com.maddyhome.idea.vim.mark.Jump
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.ij
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.initInjector
 | 
			
		||||
import org.jdom.Element
 | 
			
		||||
 | 
			
		||||
@State(name = "VimJumpsSettings", storages = [Storage(value = "\$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)])
 | 
			
		||||
@@ -65,6 +66,7 @@ internal class VimJumpServiceImpl : VimJumpServiceBase(), PersistentStateCompone
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun loadState(state: Element) {
 | 
			
		||||
    initInjector()
 | 
			
		||||
    val projectElements = state.getChildren("project")
 | 
			
		||||
    for (projectElement in projectElements) {
 | 
			
		||||
      val jumps = mutableListOf<Jump>()
 | 
			
		||||
@@ -89,6 +91,7 @@ internal class VimJumpServiceImpl : VimJumpServiceBase(), PersistentStateCompone
 | 
			
		||||
 | 
			
		||||
internal class JumpsListener(val project: Project) : RecentPlacesListener {
 | 
			
		||||
  override fun recentPlaceAdded(changePlace: PlaceInfo, isChanged: Boolean) {
 | 
			
		||||
    initInjector()
 | 
			
		||||
    if (!injector.globalIjOptions().unifyjumps) return
 | 
			
		||||
    
 | 
			
		||||
    val jumpService = injector.jumpService
 | 
			
		||||
 
 | 
			
		||||
@@ -46,6 +46,7 @@ import com.maddyhome.idea.vim.put.PutData
 | 
			
		||||
import com.maddyhome.idea.vim.put.VimPasteProvider
 | 
			
		||||
import com.maddyhome.idea.vim.put.VimPutBase
 | 
			
		||||
import com.maddyhome.idea.vim.register.RegisterConstants
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.isBlock
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.isChar
 | 
			
		||||
@@ -83,7 +84,7 @@ internal class PutGroup : VimPutBase() {
 | 
			
		||||
    val editor = (vimEditor as IjVimEditor).editor
 | 
			
		||||
    val context = vimContext.context as DataContext
 | 
			
		||||
    val carets: MutableMap<Caret, RangeMarker> = mutableMapOf()
 | 
			
		||||
    if (editor.isInsertMode) {
 | 
			
		||||
    if (injector.vimState.mode is Mode.INSERT) {
 | 
			
		||||
      val undo = injector.undo
 | 
			
		||||
      val nanoTime = System.nanoTime()
 | 
			
		||||
      vimEditor.forEachCaret { undo.startInsertSequence(it, it.offset, nanoTime) }
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@ import com.intellij.openapi.keymap.ex.KeymapManagerEx
 | 
			
		||||
import com.intellij.openapi.project.Project
 | 
			
		||||
import com.intellij.openapi.startup.ProjectActivity
 | 
			
		||||
import com.maddyhome.idea.vim.api.key
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.initInjector
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Logs the chain of handlers for esc and enter
 | 
			
		||||
@@ -34,6 +35,8 @@ internal class EditorHandlersChainLogger : ProjectActivity {
 | 
			
		||||
  private val editorHandlers = ExtensionPointName<EditorActionHandlerBean>("com.intellij.editorActionHandler")
 | 
			
		||||
 | 
			
		||||
  override suspend fun execute(project: Project) {
 | 
			
		||||
    initInjector()
 | 
			
		||||
 | 
			
		||||
    if (!enableOctopus) return
 | 
			
		||||
 | 
			
		||||
    val escHandlers = editorHandlers.extensionList
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ import com.maddyhome.idea.vim.command.OperatorArguments
 | 
			
		||||
 */
 | 
			
		||||
internal abstract class IdeActionHandler(private val actionName: String) : VimActionHandler.SingleExecution() {
 | 
			
		||||
  override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
 | 
			
		||||
    injector.actionExecutor.executeAction(actionName, context)
 | 
			
		||||
    injector.actionExecutor.executeAction(editor, name = actionName, context = context)
 | 
			
		||||
    injector.scroll.scrollCaretIntoView(editor)
 | 
			
		||||
    return true
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -218,13 +218,17 @@ internal class VimEnterHandler(nextHandler: EditorActionHandler?) : VimKeyHandle
 | 
			
		||||
internal class VimEscHandler(nextHandler: EditorActionHandler) : VimKeyHandler(nextHandler) {
 | 
			
		||||
  override val key: String = "<Esc>"
 | 
			
		||||
 | 
			
		||||
  override fun isHandlerEnabled(editor: Editor, dataContext: DataContext?): Boolean {
 | 
			
		||||
    val ideaVimSupportDialog =
 | 
			
		||||
      injector.globalIjOptions().ideavimsupport.contains(IjOptionConstants.ideavimsupport_dialog)
 | 
			
		||||
  private val ideaVimSupportDialog
 | 
			
		||||
    get() = injector.globalIjOptions().ideavimsupport.contains(IjOptionConstants.ideavimsupport_dialog)
 | 
			
		||||
  
 | 
			
		||||
  override fun isHandlerEnabled(editor: Editor, dataContext: DataContext?): Boolean {
 | 
			
		||||
    return editor.isPrimaryEditor() ||
 | 
			
		||||
      EditorHelper.isFileEditor(editor) && !editor.vim.mode.inNormalMode ||
 | 
			
		||||
      ideaVimSupportDialog && !editor.vim.mode.inNormalMode
 | 
			
		||||
      EditorHelper.isFileEditor(editor) && vimStateNeedsToHandleEscape(editor) ||
 | 
			
		||||
      ideaVimSupportDialog && vimStateNeedsToHandleEscape(editor)
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  private fun vimStateNeedsToHandleEscape(editor: Editor): Boolean {
 | 
			
		||||
    return !editor.vim.mode.inNormalMode || KeyHandler.getInstance().keyHandlerState.mappingState.hasKeys
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ import com.maddyhome.idea.vim.VimPlugin
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.api.globalOptions
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.common.EditorListener
 | 
			
		||||
import com.maddyhome.idea.vim.common.IsReplaceCharListener
 | 
			
		||||
import com.maddyhome.idea.vim.common.ModeChangeListener
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
			
		||||
@@ -86,6 +87,7 @@ private fun isBlockCursorOverride() = EditorSettingsExternalizable.getInstance()
 | 
			
		||||
 | 
			
		||||
private fun Editor.updatePrimaryCaretVisualAttributes() {
 | 
			
		||||
  if (VimPlugin.isNotEnabled()) thisLogger().error("The caret attributes should not be updated if the IdeaVim is disabled")
 | 
			
		||||
  if (isIdeaVimDisabledHere) return
 | 
			
		||||
  caretModel.primaryCaret.visualAttributes = AttributesCache.getCaretVisualAttributes(this)
 | 
			
		||||
 | 
			
		||||
  // Make sure the caret is visible as soon as it's set. It might be invisible while blinking
 | 
			
		||||
@@ -99,6 +101,7 @@ private fun Editor.updatePrimaryCaretVisualAttributes() {
 | 
			
		||||
 | 
			
		||||
private fun Editor.updateSecondaryCaretsVisualAttributes() {
 | 
			
		||||
  if (VimPlugin.isNotEnabled()) thisLogger().error("The caret attributes should not be updated if the IdeaVim is disabled")
 | 
			
		||||
  if (isIdeaVimDisabledHere) return
 | 
			
		||||
  // IntelliJ simulates visual block with multiple carets with selections. Do our best to hide them
 | 
			
		||||
  val attributes = if (this.vim.inBlockSelection) HIDDEN else AttributesCache.getCaretVisualAttributes(this)
 | 
			
		||||
  this.caretModel.allCarets.forEach {
 | 
			
		||||
@@ -144,24 +147,28 @@ private object AttributesCache {
 | 
			
		||||
@TestOnly
 | 
			
		||||
internal fun getGuiCursorMode(editor: Editor) = editor.guicursorMode()
 | 
			
		||||
 | 
			
		||||
class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeListener {
 | 
			
		||||
class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeListener, EditorListener {
 | 
			
		||||
  override fun isReplaceCharChanged(editor: VimEditor) {
 | 
			
		||||
    updateCaretsVisual()
 | 
			
		||||
    updateCaretsVisual(editor)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun modeChanged(editor: VimEditor, oldMode: Mode) {
 | 
			
		||||
    updateCaretsVisual()
 | 
			
		||||
    updateCaretsVisual(editor)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun updateCaretsVisual() {
 | 
			
		||||
    updateAllEditorsCaretsVisual()
 | 
			
		||||
  override fun focusGained(editor: VimEditor) {
 | 
			
		||||
    updateCaretsVisual(editor)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun updateAllEditorsCaretsVisual() {
 | 
			
		||||
    injector.editorGroup.getEditors().forEach { editor ->
 | 
			
		||||
  private fun updateCaretsVisual(editor: VimEditor) {
 | 
			
		||||
    val ijEditor = (editor as IjVimEditor).editor
 | 
			
		||||
    ijEditor.updateCaretsVisualAttributes()
 | 
			
		||||
    ijEditor.updateCaretsVisualPosition()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun updateAllEditorsCaretsVisual() {
 | 
			
		||||
    injector.editorGroup.getEditors().forEach { editor ->
 | 
			
		||||
      updateCaretsVisual(editor)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -326,7 +326,7 @@ public class EditorHelper {
 | 
			
		||||
 | 
			
		||||
    final int offset = y - ((screenHeight - lineHeight) / lineHeight / 2 * lineHeight);
 | 
			
		||||
    @NotNull final VimEditor editor1 = new IjVimEditor(editor);
 | 
			
		||||
    final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) - 1;
 | 
			
		||||
    final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) + editor.getSettings().getAdditionalLinesCount();
 | 
			
		||||
    final int offsetForLastLineAtBottom = getOffsetToScrollVisualLineToBottomOfScreen(editor, lastVisualLine);
 | 
			
		||||
 | 
			
		||||
    // For `zz`, we want to use virtual space and move any line, including the last one, to the middle of the screen.
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ package com.maddyhome.idea.vim.helper
 | 
			
		||||
 | 
			
		||||
import com.intellij.codeWithMe.ClientId
 | 
			
		||||
import com.intellij.openapi.editor.Caret
 | 
			
		||||
import com.intellij.openapi.editor.CaretState
 | 
			
		||||
import com.intellij.openapi.editor.Editor
 | 
			
		||||
import com.intellij.openapi.editor.ex.util.EditorUtil
 | 
			
		||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
 | 
			
		||||
@@ -20,6 +21,8 @@ import com.maddyhome.idea.vim.api.StringListOptionValue
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.group.IjOptionConstants
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.vim
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.inBlockSelection
 | 
			
		||||
import java.awt.Component
 | 
			
		||||
import javax.swing.JComponent
 | 
			
		||||
import javax.swing.JTable
 | 
			
		||||
@@ -96,3 +99,41 @@ internal val Caret.vimLine: Int
 | 
			
		||||
 */
 | 
			
		||||
internal val Editor.vimLine: Int
 | 
			
		||||
  get() = this.caretModel.currentCaret.vimLine
 | 
			
		||||
 | 
			
		||||
internal inline fun Editor.runWithEveryCaretAndRestore(action: () -> Unit) {
 | 
			
		||||
  val caretModel = this.caretModel
 | 
			
		||||
  val carets = if (this.vim.inBlockSelection) null else caretModel.allCarets
 | 
			
		||||
  if (carets == null || carets.size == 1) {
 | 
			
		||||
    action()
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    var initialDocumentSize = this.document.textLength
 | 
			
		||||
    var documentSizeDifference = 0
 | 
			
		||||
 | 
			
		||||
    val caretOffsets = carets.map { it.selectionStart to it.selectionEnd }
 | 
			
		||||
    val restoredCarets = mutableListOf<CaretState>()
 | 
			
		||||
 | 
			
		||||
    caretModel.removeSecondaryCarets()
 | 
			
		||||
    
 | 
			
		||||
    for ((selectionStart, selectionEnd) in caretOffsets) {
 | 
			
		||||
      if (selectionStart == selectionEnd) {
 | 
			
		||||
        caretModel.primaryCaret.moveToOffset(selectionStart + documentSizeDifference)
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        caretModel.primaryCaret.setSelection(
 | 
			
		||||
          selectionStart + documentSizeDifference,
 | 
			
		||||
          selectionEnd + documentSizeDifference
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      action()
 | 
			
		||||
      restoredCarets.add(caretModel.caretsAndSelections.single())
 | 
			
		||||
 | 
			
		||||
      val documentLength = this.document.textLength
 | 
			
		||||
      documentSizeDifference += documentLength - initialDocumentSize
 | 
			
		||||
      initialDocumentSize = documentLength
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    caretModel.caretsAndSelections = restoredCarets
 | 
			
		||||
  } 
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,22 +13,23 @@ 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.AnActionResult
 | 
			
		||||
import com.intellij.openapi.actionSystem.DataContextWrapper
 | 
			
		||||
import com.intellij.openapi.actionSystem.EmptyAction
 | 
			
		||||
import com.intellij.openapi.actionSystem.IdeActions
 | 
			
		||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
 | 
			
		||||
import com.intellij.openapi.actionSystem.ex.ActionManagerEx
 | 
			
		||||
import com.intellij.openapi.actionSystem.ex.ActionUtil
 | 
			
		||||
import com.intellij.openapi.actionSystem.ex.ActionUtil.performDumbAwareWithCallbacks
 | 
			
		||||
import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
 | 
			
		||||
import com.intellij.openapi.actionSystem.impl.Utils
 | 
			
		||||
import com.intellij.openapi.application.ex.ApplicationManagerEx
 | 
			
		||||
import com.intellij.openapi.command.CommandProcessor
 | 
			
		||||
import com.intellij.openapi.command.UndoConfirmationPolicy
 | 
			
		||||
import com.intellij.openapi.components.Service
 | 
			
		||||
import com.intellij.openapi.diagnostic.thisLogger
 | 
			
		||||
import com.intellij.openapi.editor.actionSystem.DocCommandGroupId
 | 
			
		||||
import com.intellij.openapi.project.IndexNotReadyException
 | 
			
		||||
import com.intellij.openapi.progress.util.ProgressIndicatorUtils
 | 
			
		||||
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
 | 
			
		||||
@@ -60,6 +61,8 @@ internal class IjActionExecutor : VimActionExecutor {
 | 
			
		||||
    get() = IdeActions.ACTION_EXPAND_REGION
 | 
			
		||||
  override val ACTION_EXPAND_REGION_RECURSIVELY: String
 | 
			
		||||
    get() = IdeActions.ACTION_EXPAND_REGION_RECURSIVELY
 | 
			
		||||
  override val ACTION_EXPAND_COLLAPSE_TOGGLE: String
 | 
			
		||||
    get() = "ExpandCollapseToggleAction"
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Execute an action
 | 
			
		||||
@@ -68,6 +71,12 @@ internal class IjActionExecutor : VimActionExecutor {
 | 
			
		||||
   * @param context The context to run it in
 | 
			
		||||
   */
 | 
			
		||||
  override fun executeAction(editor: VimEditor?, action: NativeAction, context: ExecutionContext): Boolean {
 | 
			
		||||
    val applicationEx = ApplicationManagerEx.getApplicationEx()
 | 
			
		||||
    if (ProgressIndicatorUtils.isWriteActionRunningOrPending(applicationEx)) {
 | 
			
		||||
      // This is needed for VIM-3376 and it should turn into error at soeme moment
 | 
			
		||||
      thisLogger().warn(RuntimeException("Actions cannot be updated when write-action is running or pending", ))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val ijAction = (action as IjNativeAction).action
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -86,6 +95,7 @@ internal class IjActionExecutor : VimActionExecutor {
 | 
			
		||||
      ActionManager.getInstance(),
 | 
			
		||||
      0,
 | 
			
		||||
    )
 | 
			
		||||
    Utils.initUpdateSession(event)
 | 
			
		||||
    // beforeActionPerformedUpdate should be called to update the action. It fixes some rider-specific problems.
 | 
			
		||||
    //   because rider use async update method. See VIM-1819.
 | 
			
		||||
    // This method executes inside of lastUpdateAndCheckDumb
 | 
			
		||||
@@ -119,51 +129,15 @@ internal class IjActionExecutor : VimActionExecutor {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 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, action, event)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Execute an action by name
 | 
			
		||||
   *
 | 
			
		||||
   * @param name    The name of the action to execute
 | 
			
		||||
   * @param context The context to run it in
 | 
			
		||||
   */
 | 
			
		||||
  override fun executeAction(name: @NonNls String, context: ExecutionContext): Boolean {
 | 
			
		||||
  override fun executeAction(editor: VimEditor, name: @NonNls String, context: ExecutionContext): Boolean {
 | 
			
		||||
    val action = getAction(name, context)
 | 
			
		||||
    return action != null && executeAction(null, IjNativeAction(action), context)
 | 
			
		||||
    return action != null && executeAction(editor, IjNativeAction(action), context)
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  private fun getAction(name: String, context: ExecutionContext): AnAction? {
 | 
			
		||||
@@ -209,8 +183,8 @@ internal class IjActionExecutor : VimActionExecutor {
 | 
			
		||||
    CommandProcessor.getInstance().executeCommand(editor?.ij?.project, runnable, name, groupId)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun executeEsc(context: ExecutionContext): Boolean {
 | 
			
		||||
    return executeAction(IdeActions.ACTION_EDITOR_ESCAPE, context)
 | 
			
		||||
  override fun executeEsc(editor: VimEditor, context: ExecutionContext): Boolean {
 | 
			
		||||
    return executeAction(editor, IdeActions.ACTION_EDITOR_ESCAPE, context)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun executeVimAction(
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ import com.intellij.openapi.editor.VisualPosition
 | 
			
		||||
import com.intellij.openapi.editor.actionSystem.EditorActionManager
 | 
			
		||||
import com.intellij.openapi.editor.ex.util.EditorUtil
 | 
			
		||||
import com.maddyhome.idea.vim.api.EngineEditorHelper
 | 
			
		||||
import com.maddyhome.idea.vim.api.EngineEditorHelperBase
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimRangeMarker
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimVisualPosition
 | 
			
		||||
@@ -22,7 +23,7 @@ import com.maddyhome.idea.vim.newapi.ij
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.vim
 | 
			
		||||
 | 
			
		||||
@Service
 | 
			
		||||
internal class IjEditorHelper : EngineEditorHelper {
 | 
			
		||||
internal class IjEditorHelper : EngineEditorHelperBase() {
 | 
			
		||||
  override fun amountOfInlaysBeforeVisualPosition(editor: VimEditor, pos: VimVisualPosition): Int {
 | 
			
		||||
    return (editor as IjVimEditor).editor.amountOfInlaysBeforeVisualPosition(
 | 
			
		||||
      VisualPosition(
 | 
			
		||||
@@ -51,10 +52,6 @@ internal class IjEditorHelper : EngineEditorHelper {
 | 
			
		||||
    return EditorHelper.getVisualLineAtBottomOfScreen(editor.ij)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun pad(editor: VimEditor, line: Int, to: Int): String {
 | 
			
		||||
    return EditorHelper.pad(editor.ij, line, to)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun inlayAwareOffsetToVisualPosition(editor: VimEditor, offset: Int): VimVisualPosition {
 | 
			
		||||
    return EditorUtil.inlayAwareOffsetToVisualPosition(editor.ij, offset).vim
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,7 @@ internal object ScrollViewHelper {
 | 
			
		||||
    // that this needs to be replaced as a more or less dumb line for line rewrite.
 | 
			
		||||
    val topLine = getVisualLineAtTopOfScreen(editor)
 | 
			
		||||
    val bottomLine = getVisualLineAtBottomOfScreen(editor)
 | 
			
		||||
    val lastLine = vimEditor.getVisualLineCount() - 1
 | 
			
		||||
    val lastLine = vimEditor.getVisualLineCount() + editor.settings.additionalLinesCount
 | 
			
		||||
 | 
			
		||||
    // We need the non-normalised value here, so we can handle cases such as so=999 to keep the current line centred
 | 
			
		||||
    val scrollOffset = injector.options(vimEditor).scrolloff
 | 
			
		||||
 
 | 
			
		||||
@@ -16,18 +16,15 @@ import com.intellij.openapi.editor.Editor
 | 
			
		||||
import com.intellij.spellchecker.SpellCheckerSeveritiesProvider
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.api.getLineEndOffset
 | 
			
		||||
import com.maddyhome.idea.vim.api.getText
 | 
			
		||||
import com.maddyhome.idea.vim.api.globalOptions
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.common.TextRange
 | 
			
		||||
import com.maddyhome.idea.vim.helper.CharacterHelper.charType
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.vim
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.IntComparator
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.IntIterator
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.IntRBTreeSet
 | 
			
		||||
import it.unimi.dsi.fastutil.ints.IntSortedSet
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Check ignorecase and smartcase options to see if a case insensitive search should be performed with the given pattern.
 | 
			
		||||
@@ -97,210 +94,6 @@ fun countWords(
 | 
			
		||||
  return CountPosition(count, position)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun findNumbersInRange(
 | 
			
		||||
  editor: Editor,
 | 
			
		||||
  textRange: TextRange,
 | 
			
		||||
  alpha: Boolean,
 | 
			
		||||
  hex: Boolean,
 | 
			
		||||
  octal: Boolean,
 | 
			
		||||
): List<Pair<TextRange, NumberType>> {
 | 
			
		||||
  val result: MutableList<Pair<TextRange, NumberType>> = ArrayList()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  for (i in 0 until textRange.size()) {
 | 
			
		||||
    val startOffset = textRange.startOffsets[i]
 | 
			
		||||
    val end = textRange.endOffsets[i]
 | 
			
		||||
    val text: String = editor.vim.getText(startOffset, end)
 | 
			
		||||
    val textChunks = text.split("\\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
 | 
			
		||||
    var chunkStart = 0
 | 
			
		||||
    for (chunk in textChunks) {
 | 
			
		||||
      val number = findNumberInText(chunk, 0, alpha, hex, octal)
 | 
			
		||||
 | 
			
		||||
      if (number != null) {
 | 
			
		||||
        result.add(
 | 
			
		||||
          Pair(
 | 
			
		||||
            TextRange(
 | 
			
		||||
              number.first.startOffset + startOffset + chunkStart,
 | 
			
		||||
              number.first.endOffset + startOffset + chunkStart
 | 
			
		||||
            ),
 | 
			
		||||
            number.second
 | 
			
		||||
          )
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
      chunkStart += 1 + chunk.length
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun findNumberUnderCursor(
 | 
			
		||||
  editor: Editor,
 | 
			
		||||
  caret: Caret,
 | 
			
		||||
  alpha: Boolean,
 | 
			
		||||
  hex: Boolean,
 | 
			
		||||
  octal: Boolean,
 | 
			
		||||
): Pair<TextRange, NumberType>? {
 | 
			
		||||
  val lline = caret.logicalPosition.line
 | 
			
		||||
  val text = IjVimEditor(editor).getLineText(lline).lowercase(Locale.getDefault())
 | 
			
		||||
  val startLineOffset = IjVimEditor(editor).getLineStartOffset(lline)
 | 
			
		||||
  val posOnLine = caret.offset - startLineOffset
 | 
			
		||||
 | 
			
		||||
  val numberTextRange = findNumberInText(text, posOnLine, alpha, hex, octal) ?: return null
 | 
			
		||||
 | 
			
		||||
  return Pair(
 | 
			
		||||
    TextRange(
 | 
			
		||||
      numberTextRange.first.startOffset + startLineOffset,
 | 
			
		||||
      numberTextRange.first.endOffset + startLineOffset
 | 
			
		||||
    ),
 | 
			
		||||
    numberTextRange.second
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Search for number in given text from start position
 | 
			
		||||
 *
 | 
			
		||||
 * @param textInRange    - text to search in
 | 
			
		||||
 * @param startPosOnLine - start offset to search
 | 
			
		||||
 * @return - text range with number
 | 
			
		||||
 */
 | 
			
		||||
fun findNumberInText(
 | 
			
		||||
  textInRange: String,
 | 
			
		||||
  startPosOnLine: Int,
 | 
			
		||||
  alpha: Boolean,
 | 
			
		||||
  hex: Boolean,
 | 
			
		||||
  octal: Boolean,
 | 
			
		||||
): Pair<TextRange, NumberType>? {
 | 
			
		||||
  if (logger.isDebugEnabled) {
 | 
			
		||||
    logger.debug("text=$textInRange")
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  var pos = startPosOnLine
 | 
			
		||||
  val lineEndOffset = textInRange.length
 | 
			
		||||
 | 
			
		||||
  while (true) {
 | 
			
		||||
    // Skip over current whitespace if any
 | 
			
		||||
    while (pos < lineEndOffset && !isNumberChar(textInRange[pos], alpha, hex, octal, true)) {
 | 
			
		||||
      pos++
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (logger.isDebugEnabled) logger.debug("pos=$pos")
 | 
			
		||||
    if (pos >= lineEndOffset) {
 | 
			
		||||
      logger.debug("no number char on line")
 | 
			
		||||
      return null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val isHexChar = "abcdefABCDEF".indexOf(textInRange[pos]) >= 0
 | 
			
		||||
 | 
			
		||||
    if (hex) {
 | 
			
		||||
      // Ox and OX handling
 | 
			
		||||
      if (textInRange[pos] == '0' && pos < lineEndOffset - 1 && "xX".indexOf(textInRange[pos + 1]) >= 0) {
 | 
			
		||||
        pos += 2
 | 
			
		||||
      } else if ("xX".indexOf(textInRange[pos]) >= 0 && pos > 0 && textInRange[pos - 1] == '0') {
 | 
			
		||||
        pos++
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      logger.debug("checking hex")
 | 
			
		||||
      val range = findRange(textInRange, pos, false, true, false, false)
 | 
			
		||||
      val start = range.first
 | 
			
		||||
      val end = range.second
 | 
			
		||||
 | 
			
		||||
      // Ox and OX
 | 
			
		||||
      if (start >= 2 && textInRange.substring(start - 2, start).equals("0x", ignoreCase = true)) {
 | 
			
		||||
        logger.debug("found hex")
 | 
			
		||||
        return Pair(TextRange(start - 2, end), NumberType.HEX)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!isHexChar || alpha) {
 | 
			
		||||
        break
 | 
			
		||||
      } else {
 | 
			
		||||
        pos++
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      break
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (octal) {
 | 
			
		||||
    logger.debug("checking octal")
 | 
			
		||||
    val range = findRange(textInRange, pos, false, false, true, false)
 | 
			
		||||
    val start = range.first
 | 
			
		||||
    val end = range.second
 | 
			
		||||
 | 
			
		||||
    if (end - start == 1 && textInRange[start] == '0') {
 | 
			
		||||
      return Pair(TextRange(start, end), NumberType.DEC)
 | 
			
		||||
    }
 | 
			
		||||
    if (textInRange[start] == '0' && end > start &&
 | 
			
		||||
      !(start > 0 && isNumberChar(textInRange[start - 1], false, false, false, true))
 | 
			
		||||
    ) {
 | 
			
		||||
      logger.debug("found octal")
 | 
			
		||||
      return Pair(TextRange(start, end), NumberType.OCT)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (alpha) {
 | 
			
		||||
    if (logger.isDebugEnabled) logger.debug("checking alpha for " + textInRange[pos])
 | 
			
		||||
    if (isNumberChar(textInRange[pos], true, false, false, false)) {
 | 
			
		||||
      if (logger.isDebugEnabled) logger.debug("found alpha at $pos")
 | 
			
		||||
      return Pair(TextRange(pos, pos + 1), NumberType.ALPHA)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  val range = findRange(textInRange, pos, false, false, false, true)
 | 
			
		||||
  var start = range.first
 | 
			
		||||
  val end = range.second
 | 
			
		||||
  if (start > 0 && textInRange[start - 1] == '-') {
 | 
			
		||||
    start--
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return Pair(TextRange(start, end), NumberType.DEC)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Searches for digits block that matches parameters
 | 
			
		||||
 */
 | 
			
		||||
private fun findRange(
 | 
			
		||||
  text: String,
 | 
			
		||||
  pos: Int,
 | 
			
		||||
  alpha: Boolean,
 | 
			
		||||
  hex: Boolean,
 | 
			
		||||
  octal: Boolean,
 | 
			
		||||
  decimal: Boolean,
 | 
			
		||||
): Pair<Int, Int> {
 | 
			
		||||
  var end = pos
 | 
			
		||||
  while (end < text.length && isNumberChar(text[end], alpha, hex, octal, decimal || octal)) {
 | 
			
		||||
    end++
 | 
			
		||||
  }
 | 
			
		||||
  var start = pos
 | 
			
		||||
  while (start >= 0 && isNumberChar(text[start], alpha, hex, octal, decimal || octal)) {
 | 
			
		||||
    start--
 | 
			
		||||
  }
 | 
			
		||||
  if (start < end &&
 | 
			
		||||
    (start == -1 ||
 | 
			
		||||
      0 <= start && start < text.length &&
 | 
			
		||||
      !isNumberChar(text[start], alpha, hex, octal, decimal || octal))
 | 
			
		||||
  ) {
 | 
			
		||||
    start++
 | 
			
		||||
  }
 | 
			
		||||
  if (octal) {
 | 
			
		||||
    for (i in start until end) {
 | 
			
		||||
      if (!isNumberChar(text[i], false, false, true, false)) return Pair(0, 0)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return Pair(start, end)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private fun isNumberChar(ch: Char, alpha: Boolean, hex: Boolean, octal: Boolean, decimal: Boolean): Boolean {
 | 
			
		||||
  return if (alpha && ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) {
 | 
			
		||||
    true
 | 
			
		||||
  } else if (octal && (ch >= '0' && ch <= '7')) {
 | 
			
		||||
    true
 | 
			
		||||
  } else if (hex && ((ch >= '0' && ch <= '9') || "abcdefABCDEF".indexOf(ch) >= 0)) {
 | 
			
		||||
    true
 | 
			
		||||
  } else {
 | 
			
		||||
    decimal && (ch >= '0' && ch <= '9')
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Find the word under the cursor or the next word to the right of the cursor on the current line.
 | 
			
		||||
 *
 | 
			
		||||
 
 | 
			
		||||
@@ -16,16 +16,20 @@ import com.intellij.openapi.components.Service
 | 
			
		||||
import com.intellij.openapi.diagnostic.logger
 | 
			
		||||
import com.intellij.openapi.fileEditor.TextEditor
 | 
			
		||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
 | 
			
		||||
import com.intellij.openapi.project.Project
 | 
			
		||||
import com.intellij.openapi.util.registry.Registry
 | 
			
		||||
import com.maddyhome.idea.vim.VimPlugin
 | 
			
		||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimCaret
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.common.ChangesListener
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimCaret
 | 
			
		||||
import com.maddyhome.idea.vim.common.InsertSequence
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimCaret
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.ij
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.inVisualMode
 | 
			
		||||
import com.maddyhome.idea.vim.undo.UndoRedoBase
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -64,17 +68,10 @@ internal class UndoRedoHelper : UndoRedoBase() {
 | 
			
		||||
      // TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
 | 
			
		||||
      editor.runWithChangeTracking {
 | 
			
		||||
        undoManager.undo(fileEditor)
 | 
			
		||||
 | 
			
		||||
        // We execute undo one more time if the previous one just restored selection
 | 
			
		||||
        if (!hasChanges && hasSelection(editor) && undoManager.isUndoAvailable(fileEditor)) {
 | 
			
		||||
          undoManager.undo(fileEditor)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      CommandProcessor.getInstance().runUndoTransparentAction {
 | 
			
		||||
        removeSelections(editor)
 | 
			
		||||
        restoreVisualMode(editor)
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      notifyAboutNewUndo(editor.ij.project)
 | 
			
		||||
      runWithBooleanRegistryOption("ide.undo.transparent.caret.movement", true) {
 | 
			
		||||
        var nextUndoNanoTime = undoManager.getNextUndoNanoTime(fileEditor)
 | 
			
		||||
        val insertInfo = (editor.primaryCaret() as IjVimCaret).getInsertSequenceForTime(nextUndoNanoTime)
 | 
			
		||||
@@ -95,6 +92,13 @@ internal class UndoRedoHelper : UndoRedoBase() {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun notifyAboutNewUndo(project: Project?) {
 | 
			
		||||
    if (VimPlugin.getVimState().isNewUndoNotified) return
 | 
			
		||||
    VimPlugin.getVimState().isNewUndoNotified = true
 | 
			
		||||
 | 
			
		||||
    VimPlugin.getNotifications(project).notifyAboutNewUndo()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun hasSelection(editor: VimEditor): Boolean {
 | 
			
		||||
    return editor.primaryCaret().ij.hasSelection()
 | 
			
		||||
  }
 | 
			
		||||
@@ -219,4 +223,21 @@ internal class UndoRedoHelper : UndoRedoBase() {
 | 
			
		||||
    val hasChanges: Boolean
 | 
			
		||||
      get() = changeListener.hasChanged || initialPath != editor.getPath()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun restoreVisualMode(editor: VimEditor) {
 | 
			
		||||
    if (!editor.inVisualMode && editor.getSelectionModel().hasSelection()) {
 | 
			
		||||
      val detectedMode = VimPlugin.getVisualMotion().autodetectVisualSubmode(editor)
 | 
			
		||||
 | 
			
		||||
      // Visual block selection is restored into multiple carets, so multi-carets that form a block are always
 | 
			
		||||
      // identified as visual block mode, leading to false positives.
 | 
			
		||||
      // Since I use visual block mode much less often than multi-carets, this is a judgment call to never restore
 | 
			
		||||
      // visual block mode.
 | 
			
		||||
      val wantedMode = if (detectedMode == SelectionType.BLOCK_WISE)
 | 
			
		||||
        SelectionType.CHARACTER_WISE
 | 
			
		||||
      else
 | 
			
		||||
        detectedMode
 | 
			
		||||
 | 
			
		||||
      VimPlugin.getVisualMotion().enterVisualMode(editor, wantedMode)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,15 +18,13 @@ import com.intellij.openapi.editor.VisualPosition
 | 
			
		||||
import com.intellij.openapi.editor.markup.RangeHighlighter
 | 
			
		||||
import com.intellij.openapi.util.Key
 | 
			
		||||
import com.intellij.openapi.util.UserDataHolder
 | 
			
		||||
import com.maddyhome.idea.vim.api.CaretRegisterStorageBase
 | 
			
		||||
import com.maddyhome.idea.vim.api.LocalMarkStorage
 | 
			
		||||
import com.maddyhome.idea.vim.api.SelectionInfo
 | 
			
		||||
import com.maddyhome.idea.vim.common.InsertSequence
 | 
			
		||||
import com.maddyhome.idea.vim.ex.ExOutputModel
 | 
			
		||||
import com.maddyhome.idea.vim.group.visual.VisualChange
 | 
			
		||||
import com.maddyhome.idea.vim.group.visual.vimLeadSelectionOffset
 | 
			
		||||
import com.maddyhome.idea.vim.common.InsertSequence
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.vim
 | 
			
		||||
import com.maddyhome.idea.vim.state.VimStateMachine
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
			
		||||
import com.maddyhome.idea.vim.ui.ExOutputPanel
 | 
			
		||||
@@ -96,7 +94,6 @@ internal var Caret.vimInsertStart: RangeMarker by userDataOr {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: Data could be lost during visual block motion
 | 
			
		||||
internal var Caret.registerStorage: CaretRegisterStorageBase? by userDataCaretToEditor()
 | 
			
		||||
internal var Caret.markStorage: LocalMarkStorage? by userDataCaretToEditor()
 | 
			
		||||
internal var Caret.lastSelectionInfo: SelectionInfo? by userDataCaretToEditor()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,32 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2003-2023 The IdeaVim authors
 | 
			
		||||
 *
 | 
			
		||||
 * Use of this source code is governed by an MIT-style
 | 
			
		||||
 * license that can be found in the LICENSE.txt file or at
 | 
			
		||||
 * https://opensource.org/licenses/MIT.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.maddyhome.idea.vim.helper
 | 
			
		||||
 | 
			
		||||
import com.intellij.ide.plugins.StandalonePluginUpdateChecker
 | 
			
		||||
import com.intellij.openapi.components.Service
 | 
			
		||||
import com.intellij.openapi.components.service
 | 
			
		||||
import com.maddyhome.idea.vim.VimPlugin
 | 
			
		||||
import com.maddyhome.idea.vim.group.NotificationService
 | 
			
		||||
import com.maddyhome.idea.vim.icons.VimIcons
 | 
			
		||||
 | 
			
		||||
@Service(Service.Level.APP)
 | 
			
		||||
internal class VimStandalonePluginUpdateChecker : StandalonePluginUpdateChecker(
 | 
			
		||||
  VimPlugin.getPluginId(),
 | 
			
		||||
  updateTimestampProperty = PROPERTY_NAME,
 | 
			
		||||
  NotificationService.IDEAVIM_STICKY_GROUP,
 | 
			
		||||
  VimIcons.IDEAVIM,
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
  override fun skipUpdateCheck(): Boolean = VimPlugin.isNotEnabled() || "dev" in VimPlugin.getVersion()
 | 
			
		||||
 | 
			
		||||
  companion object {
 | 
			
		||||
    private const val PROPERTY_NAME = "ideavim.statistics.timestamp"
 | 
			
		||||
    val instance: VimStandalonePluginUpdateChecker = service()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -10,16 +10,16 @@ package com.maddyhome.idea.vim.listener
 | 
			
		||||
 | 
			
		||||
import com.intellij.execution.impl.ConsoleViewImpl
 | 
			
		||||
import com.intellij.openapi.application.ApplicationManager
 | 
			
		||||
import com.intellij.openapi.editor.EditorKind
 | 
			
		||||
import com.maddyhome.idea.vim.KeyHandler
 | 
			
		||||
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.OperatorArguments
 | 
			
		||||
import com.maddyhome.idea.vim.common.EditorListener
 | 
			
		||||
import com.maddyhome.idea.vim.helper.EditorHelper
 | 
			
		||||
import com.maddyhome.idea.vim.helper.inInsertMode
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.ij
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This listener is similar to the one we introduce in vim-engine to handle focus change,
 | 
			
		||||
@@ -29,6 +29,11 @@ import com.maddyhome.idea.vim.newapi.ij
 | 
			
		||||
 */
 | 
			
		||||
class IJEditorFocusListener : EditorListener {
 | 
			
		||||
  override fun focusGained(editor: VimEditor) {
 | 
			
		||||
    val editorInFocus = KeyHandler.getInstance().editorInFocus
 | 
			
		||||
    if (editorInFocus != null && editorInFocus.ij == editor.ij) return
 | 
			
		||||
 | 
			
		||||
    KeyHandler.getInstance().editorInFocus = editor
 | 
			
		||||
 | 
			
		||||
    // We add Vim bindings to all opened editors, including editors used as UI controls rather than just project file
 | 
			
		||||
    // editors. This includes editors used as part of the UI, such as the VCS commit message, or used as read-only
 | 
			
		||||
    // viewers for text output, such as log files in run configurations or the Git Console tab. And editors are used for
 | 
			
		||||
@@ -48,17 +53,21 @@ class IJEditorFocusListener : EditorListener {
 | 
			
		||||
    // not file based, is writable, and not a viewer, but we don't want to treat this as an interactive editor.
 | 
			
		||||
    // Note that we need a similar check in `VimEditor.isWritable` to allow Escape to work to exit insert mode. We need
 | 
			
		||||
    // to know that a read-only editor that is hosting a console view with a running process can be treated as writable.
 | 
			
		||||
 | 
			
		||||
    val ijEditor = editor.ij
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    val switchToInsertMode = Runnable {
 | 
			
		||||
      val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor)
 | 
			
		||||
      VimPlugin.getChange().insertBeforeCursor(editor, context)
 | 
			
		||||
    }
 | 
			
		||||
    val ijEditor = editor.ij
 | 
			
		||||
    if (!ijEditor.isViewer &&
 | 
			
		||||
      !EditorHelper.isFileEditor(ijEditor) &&
 | 
			
		||||
      ijEditor.document.isWritable &&
 | 
			
		||||
      !ijEditor.inInsertMode && ijEditor.editorKind != EditorKind.DIFF
 | 
			
		||||
    ) {
 | 
			
		||||
      switchToInsertMode.run()
 | 
			
		||||
    if (!ijEditor.document.isWritable) {
 | 
			
		||||
      val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor)
 | 
			
		||||
      val mode = injector.vimState.mode
 | 
			
		||||
      when (mode) {
 | 
			
		||||
        is Mode.INSERT -> editor.exitInsertMode(context, OperatorArguments(false, 0, mode))
 | 
			
		||||
        else -> {}
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    ApplicationManager.getApplication().invokeLater {
 | 
			
		||||
      if (ijEditor.isDisposed) return@invokeLater
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,8 @@ import com.intellij.openapi.actionSystem.CommonDataKeys
 | 
			
		||||
import com.intellij.openapi.actionSystem.ex.AnActionListener
 | 
			
		||||
import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
 | 
			
		||||
import com.intellij.openapi.editor.Editor
 | 
			
		||||
import com.intellij.openapi.editor.impl.ScrollingModelImpl
 | 
			
		||||
import com.intellij.openapi.keymap.KeymapManager
 | 
			
		||||
import com.intellij.openapi.project.DumbAwareToggleAction
 | 
			
		||||
import com.intellij.openapi.util.TextRange
 | 
			
		||||
import com.maddyhome.idea.vim.KeyHandler
 | 
			
		||||
@@ -37,6 +39,7 @@ import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.group.NotificationService
 | 
			
		||||
import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.initInjector
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.vim
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.inNormalMode
 | 
			
		||||
@@ -56,21 +59,41 @@ internal object IdeaSpecifics {
 | 
			
		||||
    private val surrounderAction =
 | 
			
		||||
      "com.intellij.codeInsight.generation.surroundWith.SurroundWithHandler\$InvokeSurrounderAction"
 | 
			
		||||
    private var editor: Editor? = null
 | 
			
		||||
    private var caretOffset = -1
 | 
			
		||||
    private var completionPrevDocumentLength: Int? = null
 | 
			
		||||
    private var completionPrevDocumentOffset: Int? = null
 | 
			
		||||
 | 
			
		||||
    override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) {
 | 
			
		||||
      if (VimPlugin.isNotEnabled()) return
 | 
			
		||||
 | 
			
		||||
      val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR)
 | 
			
		||||
      if (hostEditor != null) {
 | 
			
		||||
        editor = hostEditor
 | 
			
		||||
        caretOffset = hostEditor.caretModel.offset
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      val isVimAction = (action as? AnActionWrapper)?.delegate is VimShortcutKeyAction
 | 
			
		||||
      if (!isVimAction && injector.globalIjOptions().trackactionids) {
 | 
			
		||||
        if (action !is NotificationService.ActionIdNotifier.CopyActionId && action !is NotificationService.ActionIdNotifier.StopTracking) {
 | 
			
		||||
          val id: String? = ActionManager.getInstance().getId(action) ?: (action.shortcutSet as? ProxyShortcutSet)?.actionId
 | 
			
		||||
          VimPlugin.getNotifications(event.dataContext.getData(CommonDataKeys.PROJECT)).notifyActionId(id)
 | 
			
		||||
          val candidates = if (id == null) {
 | 
			
		||||
            // Some actions are specific to the component they're registered for, and are copies of a global action,
 | 
			
		||||
            // reusing the action ID and shortcuts (e.g. `NextTab` is different for editor tabs and tool window tabs).
 | 
			
		||||
            // Unfortunately, ActionManager doesn't know about these "local" actions, so can't return the action ID.
 | 
			
		||||
            // However, the new "local" action does copy the shortcuts of the global template action, so we can look up
 | 
			
		||||
            // all actions with matching shortcuts. We might return more action IDs than expected, so this is a list of
 | 
			
		||||
            // candidates, not a definite match of the action being executed, but the list should include our target
 | 
			
		||||
            // action. Note that we might return duplicate IDs because the keymap might have multiple shortcuts mapped
 | 
			
		||||
            // to the same action. The notifier will handle de-duplication and sorting as a presentation detail.
 | 
			
		||||
            action.shortcutSet.shortcuts.flatMap { KeymapManager.getInstance().activeKeymap.getActionIdList(it) }
 | 
			
		||||
          }
 | 
			
		||||
          else {
 | 
			
		||||
            emptyList()
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          // We can still get empty ID and empty candidates. Notably, for the tool window toggle buttons on the new UI.
 | 
			
		||||
          // We could filter out action events with `place == ActionPlaces.TOOLWINDOW_TOOLBAR_BAR`
 | 
			
		||||
          VimPlugin.getNotifications(event.dataContext.getData(CommonDataKeys.PROJECT)).notifyActionId(id, candidates)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@@ -95,7 +118,8 @@ internal object IdeaSpecifics {
 | 
			
		||||
      if (VimPlugin.isNotEnabled()) return
 | 
			
		||||
 | 
			
		||||
      val editor = editor
 | 
			
		||||
      if (editor != null && action is ChooseItemAction && injector.registerGroup.isRecording) {
 | 
			
		||||
      if (editor != null) {
 | 
			
		||||
        if (action is ChooseItemAction && injector.registerGroup.isRecording) {
 | 
			
		||||
          val prevDocumentLength = completionPrevDocumentLength
 | 
			
		||||
          val prevDocumentOffset = completionPrevDocumentOffset
 | 
			
		||||
 | 
			
		||||
@@ -130,7 +154,21 @@ internal object IdeaSpecifics {
 | 
			
		||||
        }
 | 
			
		||||
        //endregion
 | 
			
		||||
 | 
			
		||||
        if (caretOffset != -1 && caretOffset != editor.caretModel.offset) {
 | 
			
		||||
          val scrollModel = editor.scrollingModel as ScrollingModelImpl
 | 
			
		||||
          if (scrollModel.isScrollingNow) {
 | 
			
		||||
            val v = scrollModel.verticalScrollOffset
 | 
			
		||||
            val h = scrollModel.horizontalScrollOffset
 | 
			
		||||
            scrollModel.finishAnimation()
 | 
			
		||||
            scrollModel.scroll(h, v)
 | 
			
		||||
            scrollModel.finishAnimation()
 | 
			
		||||
          }
 | 
			
		||||
          injector.scroll.scrollCaretIntoView(editor.vim)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this.editor = null
 | 
			
		||||
      this.caretOffset = -1
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -207,9 +245,13 @@ internal object IdeaSpecifics {
 | 
			
		||||
 | 
			
		||||
//region Find action ID
 | 
			
		||||
internal class FindActionIdAction : DumbAwareToggleAction() {
 | 
			
		||||
  override fun isSelected(e: AnActionEvent): Boolean = injector.globalIjOptions().trackactionids
 | 
			
		||||
  override fun isSelected(e: AnActionEvent): Boolean {
 | 
			
		||||
    initInjector()
 | 
			
		||||
    return injector.globalIjOptions().trackactionids
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun setSelected(e: AnActionEvent, state: Boolean) {
 | 
			
		||||
    initInjector()
 | 
			
		||||
    injector.globalIjOptions().trackactionids = !injector.globalIjOptions().trackactionids
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2003-2023 The IdeaVim authors
 | 
			
		||||
 * Copyright 2003-2024 The IdeaVim authors
 | 
			
		||||
 *
 | 
			
		||||
 * Use of this source code is governed by an MIT-style
 | 
			
		||||
 * license that can be found in the LICENSE.txt file or at
 | 
			
		||||
@@ -9,7 +9,18 @@
 | 
			
		||||
package com.maddyhome.idea.vim.listener
 | 
			
		||||
 | 
			
		||||
import com.intellij.openapi.editor.Editor
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.common.ModeChangeListener
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.ij
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
			
		||||
 | 
			
		||||
@Deprecated(message = "Please use ModeChangeListener", replaceWith = ReplaceWith("ModeChangeListener", imports = ["import com.maddyhome.idea.vim.common.ModeChangeListener"]))
 | 
			
		||||
interface VimInsertListener : ModeChangeListener {
 | 
			
		||||
  override fun modeChanged(editor: VimEditor, oldMode: Mode) {
 | 
			
		||||
    if (editor.mode == Mode.INSERT) {
 | 
			
		||||
      insertModeStarted(editor.ij)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
interface VimInsertListener {
 | 
			
		||||
  fun insertModeStarted(editor: Editor)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,7 @@ import com.intellij.openapi.editor.ex.DocumentEx
 | 
			
		||||
import com.intellij.openapi.editor.ex.EditorEventMulticasterEx
 | 
			
		||||
import com.intellij.openapi.editor.ex.FocusChangeListener
 | 
			
		||||
import com.intellij.openapi.editor.impl.EditorComponentImpl
 | 
			
		||||
import com.intellij.openapi.editor.impl.EditorImpl
 | 
			
		||||
import com.intellij.openapi.fileEditor.FileEditorManager
 | 
			
		||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
 | 
			
		||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener
 | 
			
		||||
@@ -45,11 +46,14 @@ import com.intellij.openapi.fileEditor.ex.FileEditorWithProvider
 | 
			
		||||
import com.intellij.openapi.fileEditor.impl.EditorComposite
 | 
			
		||||
import com.intellij.openapi.fileEditor.impl.EditorWindow
 | 
			
		||||
import com.intellij.openapi.project.ProjectManager
 | 
			
		||||
import com.intellij.openapi.rd.createLifetime
 | 
			
		||||
import com.intellij.openapi.rd.createNestedDisposable
 | 
			
		||||
import com.intellij.openapi.util.Disposer
 | 
			
		||||
import com.intellij.openapi.util.Key
 | 
			
		||||
import com.intellij.openapi.util.removeUserData
 | 
			
		||||
import com.intellij.openapi.vfs.VirtualFile
 | 
			
		||||
import com.intellij.util.ExceptionUtil
 | 
			
		||||
import com.jetbrains.rd.util.lifetime.Lifetime
 | 
			
		||||
import com.maddyhome.idea.vim.EventFacade
 | 
			
		||||
import com.maddyhome.idea.vim.KeyHandler
 | 
			
		||||
import com.maddyhome.idea.vim.VimKeyListener
 | 
			
		||||
@@ -79,7 +83,6 @@ import com.maddyhome.idea.vim.handler.keyCheckRequests
 | 
			
		||||
import com.maddyhome.idea.vim.helper.CaretVisualAttributesListener
 | 
			
		||||
import com.maddyhome.idea.vim.helper.GuicursorChangeListener
 | 
			
		||||
import com.maddyhome.idea.vim.helper.StrictMode
 | 
			
		||||
import com.maddyhome.idea.vim.helper.VimStandalonePluginUpdateChecker
 | 
			
		||||
import com.maddyhome.idea.vim.helper.exitSelectMode
 | 
			
		||||
import com.maddyhome.idea.vim.helper.exitVisualMode
 | 
			
		||||
import com.maddyhome.idea.vim.helper.forceBarCursor
 | 
			
		||||
@@ -91,10 +94,11 @@ import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
 | 
			
		||||
import com.maddyhome.idea.vim.helper.vimDisabled
 | 
			
		||||
import com.maddyhome.idea.vim.helper.vimInitialised
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.InsertTimeRecorder
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimSearchGroup
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.InsertTimeRecorder
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.ij
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.vim
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.inSelectMode
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.selectionType
 | 
			
		||||
import com.maddyhome.idea.vim.ui.ShowCmdOptionChangeListener
 | 
			
		||||
@@ -103,7 +107,6 @@ import com.maddyhome.idea.vim.ui.widgets.macro.MacroWidgetListener
 | 
			
		||||
import com.maddyhome.idea.vim.ui.widgets.macro.macroWidgetOptionListener
 | 
			
		||||
import com.maddyhome.idea.vim.ui.widgets.mode.listeners.ModeWidgetListener
 | 
			
		||||
import com.maddyhome.idea.vim.ui.widgets.mode.modeWidgetOptionListener
 | 
			
		||||
import com.maddyhome.idea.vim.vimDisposable
 | 
			
		||||
import java.awt.event.MouseAdapter
 | 
			
		||||
import java.awt.event.MouseEvent
 | 
			
		||||
import javax.swing.SwingUtilities
 | 
			
		||||
@@ -158,6 +161,7 @@ internal object VimListenerManager {
 | 
			
		||||
    check(keyCheckRequests.tryEmit(Unit))
 | 
			
		||||
 | 
			
		||||
    val caretVisualAttributesListener = CaretVisualAttributesListener()
 | 
			
		||||
    injector.listenersNotifier.myEditorListeners.add(caretVisualAttributesListener)
 | 
			
		||||
    injector.listenersNotifier.modeChangeListeners.add(caretVisualAttributesListener)
 | 
			
		||||
    injector.listenersNotifier.isReplaceCharListeners.add(caretVisualAttributesListener)
 | 
			
		||||
    caretVisualAttributesListener.updateAllEditorsCaretsVisual()
 | 
			
		||||
@@ -284,12 +288,10 @@ internal object VimListenerManager {
 | 
			
		||||
      // TODO: If the user changes the 'ideavimsupport' option, existing editors won't be initialised
 | 
			
		||||
      if (vimDisabled(editor)) return
 | 
			
		||||
 | 
			
		||||
      // As I understand, there is no need to pass a disposable that also disposes on editor close
 | 
			
		||||
      //   because all editor resources will be garbage collected anyway on editor close
 | 
			
		||||
      // Note that this uses the plugin's main disposable, rather than VimPlugin.onOffDisposable, because we don't need
 | 
			
		||||
      // to - we explicitly call VimListenerManager.removeAll from VimPlugin.turnOffPlugin, and this disposes each
 | 
			
		||||
      // editor's disposable individually.
 | 
			
		||||
      val disposable = editor.project?.vimDisposable ?: return
 | 
			
		||||
      val pluginLifetime = VimPlugin.getInstance().createLifetime()
 | 
			
		||||
      val editorLifetime = (editor as EditorImpl).disposable.createLifetime()
 | 
			
		||||
      val disposable =
 | 
			
		||||
        Lifetime.intersect(pluginLifetime, editorLifetime).createNestedDisposable("MyLifetimedDisposable")
 | 
			
		||||
 | 
			
		||||
      // Protect against double initialisation
 | 
			
		||||
      if (editor.getUserData(editorListenersDisposableKey) != null) {
 | 
			
		||||
@@ -386,6 +388,16 @@ internal object VimListenerManager {
 | 
			
		||||
      // We can't rely on being passed a non-null editor, so check for Code With Me scenarios explicitly
 | 
			
		||||
      if (VimPlugin.isNotEnabled() || !ClientId.isCurrentlyUnderLocalId) return
 | 
			
		||||
      
 | 
			
		||||
      val newEditor = event.newEditor
 | 
			
		||||
      if (newEditor is TextEditor) {
 | 
			
		||||
        val editor = newEditor.editor
 | 
			
		||||
        if (editor.isInsertMode) {
 | 
			
		||||
          editor.vim.mode = Mode.NORMAL()
 | 
			
		||||
          KeyHandler.getInstance().reset(editor.vim)
 | 
			
		||||
        }
 | 
			
		||||
        injector.scroll.scrollCaretIntoView(editor.vim)
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      MotionGroup.fileEditorManagerSelectionChangedCallback(event)
 | 
			
		||||
      FileGroup.fileEditorManagerSelectionChangedCallback(event)
 | 
			
		||||
      VimPlugin.getSearch().fileEditorManagerSelectionChangedCallback(event)
 | 
			
		||||
@@ -457,8 +469,6 @@ internal object VimListenerManager {
 | 
			
		||||
 | 
			
		||||
        event.editor.putUserData(openingEditorKey, OpeningEditor(openingEditor, owningEditorWindow, isPreview, canBeReused))
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      VimStandalonePluginUpdateChecker.instance.pluginUsed()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun editorReleased(event: EditorFactoryEvent) {
 | 
			
		||||
@@ -735,12 +745,10 @@ internal object VimListenerManager {
 | 
			
		||||
 | 
			
		||||
      if (event.area == EditorMouseEventArea.EDITING_AREA) {
 | 
			
		||||
        val editor = event.editor
 | 
			
		||||
        val commandLine = injector.commandLine.getActiveCommandLine()
 | 
			
		||||
        if (commandLine != null) {
 | 
			
		||||
          injector.processGroup.cancelExEntry(editor.vim, refocusOwningEditor = true, resetCaret = false)
 | 
			
		||||
        }
 | 
			
		||||
        injector.commandLine.getActiveCommandLine()?.close(refocusOwningEditor = true, resetCaret = false)
 | 
			
		||||
        injector.modalInput.getCurrentModalInput()?.deactivate(refocusOwningEditor = true, resetCaret = false)
 | 
			
		||||
 | 
			
		||||
        ExOutputModel.tryGetInstance(editor)?.close()
 | 
			
		||||
        injector.outputPanel.getCurrentOutputPanel()?.close()
 | 
			
		||||
 | 
			
		||||
        val caretModel = editor.caretModel
 | 
			
		||||
        if (editor.vim.mode.selectionType != null) {
 | 
			
		||||
@@ -766,12 +774,10 @@ internal object VimListenerManager {
 | 
			
		||||
        event.area != EditorMouseEventArea.FOLDING_OUTLINE_AREA &&
 | 
			
		||||
        event.mouseEvent.button != MouseEvent.BUTTON3
 | 
			
		||||
      ) {
 | 
			
		||||
        val commandLine = injector.commandLine.getActiveCommandLine()
 | 
			
		||||
        if (commandLine != null) {
 | 
			
		||||
          injector.processGroup.cancelExEntry(event.editor.vim, refocusOwningEditor = true, resetCaret = false)
 | 
			
		||||
        }
 | 
			
		||||
        injector.commandLine.getActiveCommandLine()?.close(refocusOwningEditor = true, resetCaret = false)
 | 
			
		||||
        injector.modalInput.getCurrentModalInput()?.deactivate(refocusOwningEditor = true, resetCaret = false)
 | 
			
		||||
 | 
			
		||||
        ExOutputModel.getInstance(event.editor).close()
 | 
			
		||||
        injector.outputPanel.getCurrentOutputPanel()?.close()
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -11,12 +11,13 @@ package com.maddyhome.idea.vim.newapi
 | 
			
		||||
import com.intellij.openapi.editor.VisualPosition
 | 
			
		||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
			
		||||
import com.maddyhome.idea.vim.api.NativeAction
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimVisualPosition
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
 | 
			
		||||
internal fun NativeAction?.execute(context: ExecutionContext) {
 | 
			
		||||
internal fun NativeAction?.execute(editor: VimEditor, context: ExecutionContext) {
 | 
			
		||||
  if (this == null) return
 | 
			
		||||
  injector.actionExecutor.executeAction(null, this, context)
 | 
			
		||||
  injector.actionExecutor.executeAction(editor, this, context)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
internal val VisualPosition.vim: VimVisualPosition
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,9 @@ import com.maddyhome.idea.vim.common.LiveRange
 | 
			
		||||
internal class IjLiveRange(val marker: RangeMarker) : LiveRange {
 | 
			
		||||
  override val startOffset: Int
 | 
			
		||||
    get() = marker.startOffset
 | 
			
		||||
 | 
			
		||||
  override val endOffset: Int
 | 
			
		||||
    get() = marker.endOffset
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
val RangeMarker.vim: LiveRange
 | 
			
		||||
 
 | 
			
		||||
@@ -12,8 +12,6 @@ import com.intellij.openapi.editor.Caret
 | 
			
		||||
import com.intellij.openapi.editor.LogicalPosition
 | 
			
		||||
import com.intellij.openapi.editor.VisualPosition
 | 
			
		||||
import com.maddyhome.idea.vim.api.BufferPosition
 | 
			
		||||
import com.maddyhome.idea.vim.api.CaretRegisterStorage
 | 
			
		||||
import com.maddyhome.idea.vim.api.CaretRegisterStorageBase
 | 
			
		||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
 | 
			
		||||
import com.maddyhome.idea.vim.api.LocalMarkStorage
 | 
			
		||||
import com.maddyhome.idea.vim.api.SelectionInfo
 | 
			
		||||
@@ -29,7 +27,6 @@ import com.maddyhome.idea.vim.helper.insertHistory
 | 
			
		||||
import com.maddyhome.idea.vim.helper.lastSelectionInfo
 | 
			
		||||
import com.maddyhome.idea.vim.helper.markStorage
 | 
			
		||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
 | 
			
		||||
import com.maddyhome.idea.vim.helper.registerStorage
 | 
			
		||||
import com.maddyhome.idea.vim.helper.resetVimLastColumn
 | 
			
		||||
import com.maddyhome.idea.vim.helper.vimInsertStart
 | 
			
		||||
import com.maddyhome.idea.vim.helper.vimLastColumn
 | 
			
		||||
@@ -41,17 +38,6 @@ import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
			
		||||
 | 
			
		||||
internal class IjVimCaret(val caret: Caret) : VimCaretBase() {
 | 
			
		||||
 | 
			
		||||
  override val registerStorage: CaretRegisterStorage
 | 
			
		||||
    get() {
 | 
			
		||||
      var storage = this.caret.registerStorage
 | 
			
		||||
      if (storage == null) {
 | 
			
		||||
        storage = CaretRegisterStorageBase(this)
 | 
			
		||||
        this.caret.registerStorage = storage
 | 
			
		||||
      } else if (storage.caret != this) {
 | 
			
		||||
        storage.caret = this
 | 
			
		||||
      }
 | 
			
		||||
      return storage
 | 
			
		||||
    }
 | 
			
		||||
  override val markStorage: LocalMarkStorage
 | 
			
		||||
    get() {
 | 
			
		||||
      var storage = this.caret.markStorage
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,9 @@ import com.maddyhome.idea.vim.api.VimCaret
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimCaretListener
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimDocument
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditorBase
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimFoldRegion
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimIndentConfig
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimScrollingModel
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimSelectionModel
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimVisualPosition
 | 
			
		||||
@@ -38,6 +40,7 @@ import com.maddyhome.idea.vim.api.VirtualFile
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.command.OperatorArguments
 | 
			
		||||
import com.maddyhome.idea.vim.common.IndentConfig
 | 
			
		||||
import com.maddyhome.idea.vim.common.IndentConfig.Companion.create
 | 
			
		||||
import com.maddyhome.idea.vim.common.LiveRange
 | 
			
		||||
import com.maddyhome.idea.vim.common.ModeChangeListener
 | 
			
		||||
import com.maddyhome.idea.vim.common.TextRange
 | 
			
		||||
@@ -52,6 +55,7 @@ import com.maddyhome.idea.vim.helper.inExMode
 | 
			
		||||
import com.maddyhome.idea.vim.helper.isTemplateActive
 | 
			
		||||
import com.maddyhome.idea.vim.helper.vimChangeActionSwitchMode
 | 
			
		||||
import com.maddyhome.idea.vim.helper.vimLastSelectionType
 | 
			
		||||
import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.inBlockSelection
 | 
			
		||||
@@ -59,7 +63,7 @@ import org.jetbrains.annotations.ApiStatus
 | 
			
		||||
import java.lang.System.identityHashCode
 | 
			
		||||
 | 
			
		||||
@ApiStatus.Internal
 | 
			
		||||
internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
 | 
			
		||||
internal class IjVimEditor(editor: Editor) : MutableLinearEditor, VimEditorBase() {
 | 
			
		||||
  companion object {
 | 
			
		||||
    // For cases where Editor does not have a project (for some reason)
 | 
			
		||||
    // It's something IJ Platform related and stored here because of this reason
 | 
			
		||||
@@ -72,12 +76,22 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
 | 
			
		||||
  val editor = editor.getTopLevelEditor()
 | 
			
		||||
  val originalEditor = editor
 | 
			
		||||
 | 
			
		||||
  override fun updateMode(mode: Mode) {
 | 
			
		||||
    (injector.vimState as VimStateMachineImpl).mode = mode
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun updateIsReplaceCharacter(isReplaceCharacter: Boolean) {
 | 
			
		||||
    (injector.vimState as VimStateMachineImpl).isReplaceCharacter = isReplaceCharacter
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override val lfMakesNewLine: Boolean = true
 | 
			
		||||
  override var vimChangeActionSwitchMode: Mode?
 | 
			
		||||
    get() = editor.vimChangeActionSwitchMode
 | 
			
		||||
    set(value) {
 | 
			
		||||
      editor.vimChangeActionSwitchMode = value
 | 
			
		||||
    }
 | 
			
		||||
  override val indentConfig: VimIndentConfig
 | 
			
		||||
    get() = create(editor)
 | 
			
		||||
 | 
			
		||||
  override fun fileSize(): Long = editor.fileSize.toLong()
 | 
			
		||||
 | 
			
		||||
@@ -119,7 +133,7 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun insertText(caret: VimCaret, atPosition: Int, text: CharSequence) {
 | 
			
		||||
    if (editor.isInsertMode) {
 | 
			
		||||
    if (injector.vimState.mode is Mode.INSERT) {
 | 
			
		||||
      injector.undo.startInsertSequence(caret, atPosition, System.nanoTime())
 | 
			
		||||
    }
 | 
			
		||||
    editor.document.insertString(atPosition, text)
 | 
			
		||||
@@ -151,21 +165,38 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
 | 
			
		||||
    return editor.caretModel.allCarets.map { IjVimCaret(it) }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override var isFirstCaret = true
 | 
			
		||||
  override var isReversingCarets = false
 | 
			
		||||
  
 | 
			
		||||
  @Suppress("ideavimRunForEachCaret")
 | 
			
		||||
  override fun forEachCaret(action: (VimCaret) -> Unit) {
 | 
			
		||||
    if (editor.vim.inBlockSelection) {
 | 
			
		||||
      action(IjVimCaret(editor.caretModel.primaryCaret))
 | 
			
		||||
    } else {
 | 
			
		||||
      try {
 | 
			
		||||
        editor.caretModel.runForEachCaret({
 | 
			
		||||
          if (it.isValid) {
 | 
			
		||||
            action(IjVimCaret(it))
 | 
			
		||||
            isFirstCaret = false
 | 
			
		||||
          }
 | 
			
		||||
        }, false)
 | 
			
		||||
      } finally {
 | 
			
		||||
        isFirstCaret = true
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean) {
 | 
			
		||||
    editor.caretModel.runForEachCaret({ action(IjVimCaret(it)) }, reverse)
 | 
			
		||||
    isReversingCarets = reverse
 | 
			
		||||
    try {
 | 
			
		||||
      editor.caretModel.runForEachCaret({
 | 
			
		||||
        action(IjVimCaret(it))
 | 
			
		||||
        isFirstCaret = false
 | 
			
		||||
      }, reverse)
 | 
			
		||||
    } finally {
 | 
			
		||||
      isFirstCaret = true
 | 
			
		||||
      isReversingCarets = false
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun isInForEachCaretScope(): Boolean {
 | 
			
		||||
@@ -433,7 +464,7 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
 | 
			
		||||
      // This is faster than simply calling Editor#logicalToVisualPosition
 | 
			
		||||
      return editor.offsetToVisualLine(editor.document.getLineStartOffset(line))
 | 
			
		||||
    }
 | 
			
		||||
    return super.bufferLineToVisualLine(line)
 | 
			
		||||
    return super<MutableLinearEditor>.bufferLineToVisualLine(line)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override var insertMode: Boolean
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@ import com.maddyhome.idea.vim.api.VimApplication
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimChangeGroup
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimClipboardManager
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimCommandGroup
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimCommandLine
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimCommandLineService
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimDigraphGroup
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
			
		||||
@@ -36,6 +37,7 @@ import com.maddyhome.idea.vim.api.VimKeyGroup
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimLookupManager
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimMarkService
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimMessages
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimModalInputService
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimMotionGroup
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimOptionGroup
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimOutputPanelService
 | 
			
		||||
@@ -56,8 +58,9 @@ import com.maddyhome.idea.vim.api.VimrcFileState
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimscriptExecutor
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimscriptFunctionService
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimscriptParser
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.api.isInjectorInitialized
 | 
			
		||||
import com.maddyhome.idea.vim.diagnostic.VimLogger
 | 
			
		||||
import com.maddyhome.idea.vim.ex.ExOutputModel
 | 
			
		||||
import com.maddyhome.idea.vim.extension.VimExtensionRegistrar
 | 
			
		||||
import com.maddyhome.idea.vim.group.CommandGroup
 | 
			
		||||
import com.maddyhome.idea.vim.group.EditorGroup
 | 
			
		||||
@@ -83,6 +86,7 @@ import com.maddyhome.idea.vim.put.VimPut
 | 
			
		||||
import com.maddyhome.idea.vim.register.VimRegisterGroup
 | 
			
		||||
import com.maddyhome.idea.vim.state.VimStateMachine
 | 
			
		||||
import com.maddyhome.idea.vim.ui.VimRcFileState
 | 
			
		||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanelService
 | 
			
		||||
import com.maddyhome.idea.vim.undo.VimUndoRedo
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.Executor
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.services.VariableService
 | 
			
		||||
@@ -90,6 +94,16 @@ import com.maddyhome.idea.vim.yank.VimYankGroup
 | 
			
		||||
import com.maddyhome.idea.vim.yank.YankGroupBase
 | 
			
		||||
import javax.swing.JTextArea
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Currently, injector has to be initialized in all "entry points" from the IJ platform.
 | 
			
		||||
 * This means Project Activities, listeners, status bar widgets, statistic collectors, etc.
 | 
			
		||||
 * This is a bad pattern and we need to find a solution where the plugin doesn't have an "uninitialized state"
 | 
			
		||||
 */
 | 
			
		||||
internal fun initInjector() {
 | 
			
		||||
  if (isInjectorInitialized()) return
 | 
			
		||||
  injector = IjVimInjector()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
internal class IjVimInjector : VimInjectorBase() {
 | 
			
		||||
  override fun <T : Any> getLogger(clazz: Class<T>): VimLogger = IjVimLogger(Logger.getInstance(clazz))
 | 
			
		||||
 | 
			
		||||
@@ -183,6 +197,8 @@ internal class IjVimInjector : VimInjectorBase() {
 | 
			
		||||
    get() = com.maddyhome.idea.vim.vimscript.parser.VimscriptParser
 | 
			
		||||
  override val commandLine: VimCommandLineService
 | 
			
		||||
    get() = service()
 | 
			
		||||
  override val modalInput: VimModalInputService
 | 
			
		||||
    get() = commandLine as ExEntryPanelService
 | 
			
		||||
  override val outputPanel: VimOutputPanelService
 | 
			
		||||
    get() = service()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -97,59 +97,6 @@ open class IjVimSearchGroup : VimSearchGroupBase(), PersistentStateComponent<Ele
 | 
			
		||||
    updateSearchHighlights(getLastUsedPattern(), lastIgnoreSmartCase, showSearchHighlight, true)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun confirmChoice(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
    match: String,
 | 
			
		||||
    caret: VimCaret,
 | 
			
		||||
    startOffset: Int,
 | 
			
		||||
  ): ReplaceConfirmationChoice {
 | 
			
		||||
    val result: Ref<ReplaceConfirmationChoice> = Ref.create(ReplaceConfirmationChoice.QUIT)
 | 
			
		||||
    val keyStrokeProcessor: Function1<KeyStroke, Boolean> = label@{ key: KeyStroke ->
 | 
			
		||||
      val choice: ReplaceConfirmationChoice
 | 
			
		||||
      val c = key.keyChar
 | 
			
		||||
      choice = if (key.isCloseKeyStroke() || c == 'q') {
 | 
			
		||||
        ReplaceConfirmationChoice.QUIT
 | 
			
		||||
      } else if (c == 'y') {
 | 
			
		||||
        ReplaceConfirmationChoice.SUBSTITUTE_THIS
 | 
			
		||||
      } else if (c == 'l') {
 | 
			
		||||
        ReplaceConfirmationChoice.SUBSTITUTE_LAST
 | 
			
		||||
      } else if (c == 'n') {
 | 
			
		||||
        ReplaceConfirmationChoice.SKIP
 | 
			
		||||
      } else if (c == 'a') {
 | 
			
		||||
        ReplaceConfirmationChoice.SUBSTITUTE_ALL
 | 
			
		||||
      } else {
 | 
			
		||||
        return@label true
 | 
			
		||||
      }
 | 
			
		||||
      // TODO: Handle <C-E> and <C-Y>
 | 
			
		||||
      result.set(choice)
 | 
			
		||||
      false
 | 
			
		||||
    }
 | 
			
		||||
    if (ApplicationManager.getApplication().isUnitTestMode) {
 | 
			
		||||
      caret.moveToOffset(startOffset)
 | 
			
		||||
      val inputModel = getInstance(editor.ij)
 | 
			
		||||
      var key = inputModel.nextKeyStroke()
 | 
			
		||||
      while (key != null) {
 | 
			
		||||
        if (!keyStrokeProcessor.invoke(key)) {
 | 
			
		||||
          break
 | 
			
		||||
        }
 | 
			
		||||
        key = inputModel.nextKeyStroke()
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      // XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for this method
 | 
			
		||||
      val exEntryPanel = injector.commandLine.createWithoutShortcuts(
 | 
			
		||||
        editor,
 | 
			
		||||
        context,
 | 
			
		||||
        MessageHelper.message("replace.with.0", match),
 | 
			
		||||
        "",
 | 
			
		||||
      )
 | 
			
		||||
      caret.moveToOffset(startOffset)
 | 
			
		||||
      ModalEntry.activate(editor, keyStrokeProcessor)
 | 
			
		||||
      exEntryPanel.deactivate(refocusOwningEditor = true, resetCaret = false)
 | 
			
		||||
    }
 | 
			
		||||
    return result.get()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun addSubstitutionConfirmationHighlight(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    startOffset: Int,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,718 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2003-2023 The IdeaVim authors
 | 
			
		||||
 *
 | 
			
		||||
 * Use of this source code is governed by an MIT-style
 | 
			
		||||
 * license that can be found in the LICENSE.txt file or at
 | 
			
		||||
 * https://opensource.org/licenses/MIT.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * IdeaVim command index.
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * 1. Insert mode
 | 
			
		||||
 *
 | 
			
		||||
 * tag                    action
 | 
			
		||||
 * -------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * |i_CTRL-@|             {@link com.maddyhome.idea.vim.action.change.insert.InsertPreviousInsertExitAction}
 | 
			
		||||
 * |i_CTRL-A|             {@link com.maddyhome.idea.vim.action.change.insert.InsertPreviousInsertAction}
 | 
			
		||||
 * |i_CTRL-C|             {@link com.maddyhome.idea.vim.action.change.insert.InsertExitModeAction}
 | 
			
		||||
 * |i_CTRL-D|             {@link com.maddyhome.idea.vim.action.change.shift.ShiftLeftLinesAction}
 | 
			
		||||
 * |i_CTRL-E|             {@link com.maddyhome.idea.vim.action.change.insert.InsertCharacterBelowCursorAction}
 | 
			
		||||
 * |i_CTRL-G_j|           TO BE IMPLEMENTED
 | 
			
		||||
 * |i_CTRL-G_k|           TO BE IMPLEMENTED
 | 
			
		||||
 * |i_CTRL-G_u|           TO BE IMPLEMENTED
 | 
			
		||||
 * |i_<BS>|               {@link com.maddyhome.idea.vim.action.editor.VimEditorBackSpace}
 | 
			
		||||
 * |i_digraph|            IdeaVim enter digraph
 | 
			
		||||
 * |i_CTRL-H|             IntelliJ editor backspace
 | 
			
		||||
 * |i_<Tab>|              {@link com.maddyhome.idea.vim.action.editor.VimEditorTab}
 | 
			
		||||
 * |i_CTRL-I|             IntelliJ editor tab
 | 
			
		||||
 * |i_<NL>|               {@link com.maddyhome.idea.vim.action.change.insert.InsertEnterAction}
 | 
			
		||||
 * |i_CTRL-J|             TO BE IMPLEMENTED
 | 
			
		||||
 * |i_CTRL-K|             {@link com.maddyhome.idea.vim.action.change.insert.InsertCompletedDigraphAction}
 | 
			
		||||
 * |i_CTRL-L|             TO BE IMPLEMENTED
 | 
			
		||||
 * |i_<CR>|               {@link com.maddyhome.idea.vim.action.change.insert.InsertEnterAction}
 | 
			
		||||
 * |i_CTRL-M|             {@link com.maddyhome.idea.vim.action.change.insert.InsertEnterAction}
 | 
			
		||||
 * |i_CTRL-N|             {@link com.maddyhome.idea.vim.action.window.LookupDownAction}
 | 
			
		||||
 * |i_CTRL-O|             {@link com.maddyhome.idea.vim.action.change.insert.InsertSingleCommandAction}
 | 
			
		||||
 * |i_CTRL-P|             {@link com.maddyhome.idea.vim.action.window.LookupUpAction}
 | 
			
		||||
 * |i_CTRL-Q|             TO BE IMPLEMENTED
 | 
			
		||||
 * |i_CTRL-R|             {@link com.maddyhome.idea.vim.action.change.insert.InsertRegisterAction}
 | 
			
		||||
 * |i_CTRL-R_CTRL-R|      TO BE IMPLEMENTED
 | 
			
		||||
 * |i_CTRL-R_CTRL-O|      TO BE IMPLEMENTED
 | 
			
		||||
 * |i_CTRL-R_CTRL-P|      TO BE IMPLEMENTED
 | 
			
		||||
 * |i_CTRL-T|             {@link com.maddyhome.idea.vim.action.change.shift.ShiftRightLinesAction}
 | 
			
		||||
 * |i_CTRL-U|             {@link com.maddyhome.idea.vim.action.change.insert.InsertDeleteInsertedTextAction}
 | 
			
		||||
 * |i_CTRL-V|             {@link com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction}
 | 
			
		||||
 * |i_CTRL-V_digit|       {@link com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction}
 | 
			
		||||
 * |i_CTRL-W|             {@link com.maddyhome.idea.vim.action.change.insert.InsertDeletePreviousWordAction}
 | 
			
		||||
 * |i_CTRL-X|             TO BE IMPLEMENTED
 | 
			
		||||
 * |i_CTRL-Y|             {@link com.maddyhome.idea.vim.action.change.insert.InsertCharacterAboveCursorAction}
 | 
			
		||||
 * |i_CTRL-Z|             TO BE IMPLEMENTED
 | 
			
		||||
 * |i_<Esc>|              {@link com.maddyhome.idea.vim.action.change.insert.InsertExitModeAction}
 | 
			
		||||
 * |i_CTRL-[|             {@link com.maddyhome.idea.vim.action.change.insert.InsertExitModeAction}
 | 
			
		||||
 * |i_CTRL-\_CTRL-N|      {@link com.maddyhome.idea.vim.action.ResetModeAction}
 | 
			
		||||
 * |i_CTRL-\_CTRL-G|      TO BE IMPLEMENTED
 | 
			
		||||
 * |i_CTRL-]}             TO BE IMPLEMENTED
 | 
			
		||||
 * |i_CTRL-^|             TO BE IMPLEMENTED
 | 
			
		||||
 * |i_CTRL-_|             TO BE IMPLEMENTED
 | 
			
		||||
 * |i_0_CTRL-D|           TO BE IMPLEMENTED
 | 
			
		||||
 * |i_^_CTRL-D|           TO BE IMPLEMENTED
 | 
			
		||||
 * |i_<Del>|              {@link com.maddyhome.idea.vim.action.editor.VimEditorDelete}
 | 
			
		||||
 * |i_<Left>|             {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLeftInsertModeAction}
 | 
			
		||||
 * |i_<S-Left>|           {@link com.maddyhome.idea.vim.action.motion.text.MotionWordLeftInsertAction}
 | 
			
		||||
 * |i_<C-Left>|           {@link com.maddyhome.idea.vim.action.motion.text.MotionWordLeftInsertAction}
 | 
			
		||||
 * |i_<Right>|            {@link com.maddyhome.idea.vim.action.motion.leftright.MotionRightInsertAction}
 | 
			
		||||
 * |i_<S-Right>|          {@link com.maddyhome.idea.vim.action.motion.text.MotionWordRightInsertAction}
 | 
			
		||||
 * |i_<C-Right>|          {@link com.maddyhome.idea.vim.action.motion.text.MotionWordRightInsertAction}
 | 
			
		||||
 * |i_<Up>|               {@link com.maddyhome.idea.vim.action.editor.VimEditorUp}
 | 
			
		||||
 * |i_<S-Up>|             {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageUpInsertModeAction}
 | 
			
		||||
 * |i_<Down>|             {@link com.maddyhome.idea.vim.action.editor.VimEditorDown}
 | 
			
		||||
 * |i_<S-Down>|           {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageDownInsertModeAction}
 | 
			
		||||
 * |i_<Home>|             {@link com.maddyhome.idea.vim.action.motion.leftright.MotionFirstColumnInsertModeAction}
 | 
			
		||||
 * |i_<C-Home>|           {@link com.maddyhome.idea.vim.action.motion.updown.MotionGotoLineFirstInsertAction}
 | 
			
		||||
 * |i_<End>|              {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLastColumnInsertAction}
 | 
			
		||||
 * |i_<C-End>|            {@link com.maddyhome.idea.vim.action.motion.updown.MotionGotoLineLastEndInsertAction}
 | 
			
		||||
 * |i_<Insert>|           {@link com.maddyhome.idea.vim.action.change.insert.InsertInsertAction}
 | 
			
		||||
 * |i_<PageUp>|           {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageUpInsertModeAction}
 | 
			
		||||
 * |i_<PageDown>|         {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageDownInsertModeAction}
 | 
			
		||||
 * |i_<F1>|               IntelliJ help
 | 
			
		||||
 * |i_<Insert>|           IntelliJ editor toggle insert/replace
 | 
			
		||||
 * |i_CTRL-X_index|       TO BE IMPLEMENTED
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * 2. Normal mode
 | 
			
		||||
 *
 | 
			
		||||
 * tag                    action
 | 
			
		||||
 * -------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * |CTRL-A|               {@link com.maddyhome.idea.vim.action.change.change.number.ChangeNumberIncAction}
 | 
			
		||||
 * |CTRL-B|               {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageUpAction}
 | 
			
		||||
 * |CTRL-C|               TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-D|               {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollHalfPageDownAction}
 | 
			
		||||
 * |CTRL-E|               {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLineDownAction}
 | 
			
		||||
 * |CTRL-F|               {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageDownAction}
 | 
			
		||||
 * |CTRL-G|               {@link com.maddyhome.idea.vim.action.file.FileGetFileInfoAction}
 | 
			
		||||
 * |<BS>|                 {@link com.maddyhome.idea.vim.action.motion.leftright.MotionBackspaceAction}
 | 
			
		||||
 * |CTRL-H|               {@link com.maddyhome.idea.vim.action.motion.leftright.MotionBackspaceAction}
 | 
			
		||||
 * |<Tab>|                TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-I|               {@link com.maddyhome.idea.vim.action.motion.mark.MotionJumpNextAction}
 | 
			
		||||
 * |<NL>|                 {@link com.maddyhome.idea.vim.action.motion.updown.MotionDownNotLineWiseAction}
 | 
			
		||||
 * |CTRL-J|               TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-L|               not applicable
 | 
			
		||||
 * |<CR>|                 {@link com.maddyhome.idea.vim.action.motion.updown.EnterNormalAction}
 | 
			
		||||
 * |CTRL-M|               {@link com.maddyhome.idea.vim.action.motion.updown.MotionDownFirstNonSpaceAction}
 | 
			
		||||
 * |CTRL-N|               {@link com.maddyhome.idea.vim.action.motion.updown.MotionDownCtrlNAction}
 | 
			
		||||
 * |CTRL-O|               {@link com.maddyhome.idea.vim.action.motion.mark.MotionJumpPreviousAction}
 | 
			
		||||
 * |CTRL-P|               {@link com.maddyhome.idea.vim.action.motion.updown.MotionUpCtrlPAction}
 | 
			
		||||
 * |CTRL-R|               {@link com.maddyhome.idea.vim.action.change.RedoAction}
 | 
			
		||||
 * |CTRL-T|               TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-U|               {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollHalfPageUpAction}
 | 
			
		||||
 * |CTRL-V|               {@link com.maddyhome.idea.vim.action.motion.visual.VisualToggleBlockModeAction}
 | 
			
		||||
 * |CTRL-W|               see window commands
 | 
			
		||||
 * |CTRL-X|               {@link com.maddyhome.idea.vim.action.change.change.number.ChangeNumberDecAction}
 | 
			
		||||
 * |CTRL-Y|               {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLineUpAction}
 | 
			
		||||
 * |CTRL-Z|               TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-]|               {@link com.maddyhome.idea.vim.action.motion.search.GotoDeclarationAction}
 | 
			
		||||
 * |CTRL-6|               {@link com.maddyhome.idea.vim.action.file.FilePreviousAction}
 | 
			
		||||
 * |CTRL-\CTRL-N|         {@link com.maddyhome.idea.vim.action.ResetModeAction}
 | 
			
		||||
 * |<Space>|              {@link com.maddyhome.idea.vim.action.motion.leftright.MotionSpaceAction}
 | 
			
		||||
 * |!|                    {@link com.maddyhome.idea.vim.action.change.change.FilterMotionAction}
 | 
			
		||||
 * |!!|                   translated to !_
 | 
			
		||||
 * |quote|                handled by command key parser
 | 
			
		||||
 * |#|                    {@link com.maddyhome.idea.vim.action.motion.search.SearchWholeWordBackwardAction}
 | 
			
		||||
 * |$|                    {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLastColumnAction}
 | 
			
		||||
 * |%|                    {@link com.maddyhome.idea.vim.action.motion.updown.MotionPercentOrMatchAction}
 | 
			
		||||
 * |&|                    {@link com.maddyhome.idea.vim.action.change.change.ChangeLastSearchReplaceAction}
 | 
			
		||||
 * |'|                    {@link com.maddyhome.idea.vim.action.motion.mark.MotionGotoMarkLineAction}
 | 
			
		||||
 * |''|                   ?
 | 
			
		||||
 * ...
 | 
			
		||||
 * |(|                    {@link com.maddyhome.idea.vim.action.motion.text.MotionSentencePreviousStartAction}
 | 
			
		||||
 * |)|                    {@link com.maddyhome.idea.vim.action.motion.text.MotionSentenceNextStartAction}
 | 
			
		||||
 * |star|                 {@link com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction}
 | 
			
		||||
 * |+|                    {@link com.maddyhome.idea.vim.action.motion.updown.MotionDownFirstNonSpaceAction}
 | 
			
		||||
 * |,|                    {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLastMatchCharReverseAction}
 | 
			
		||||
 * |-|                    {@link com.maddyhome.idea.vim.action.motion.updown.MotionUpFirstNonSpaceAction}
 | 
			
		||||
 * |.|                    {@link com.maddyhome.idea.vim.action.change.RepeatChangeAction}
 | 
			
		||||
 * |/|                    {@link com.maddyhome.idea.vim.action.motion.search.SearchEntryFwdAction}
 | 
			
		||||
 * |:|                    {@link com.maddyhome.idea.vim.action.ex.ExEntryAction}
 | 
			
		||||
 * |;|                    {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLastMatchCharAction}
 | 
			
		||||
 * |<|                    {@link com.maddyhome.idea.vim.action.change.shift.ShiftLeftMotionAction}
 | 
			
		||||
 * |<<|                   translated to <_
 | 
			
		||||
 * |=|                    {@link com.maddyhome.idea.vim.action.change.shift.AutoIndentMotionAction}
 | 
			
		||||
 * |==|                   translated to =_
 | 
			
		||||
 * |>|                    {@link com.maddyhome.idea.vim.action.change.shift.ShiftRightMotionAction}
 | 
			
		||||
 * |>>|                   translated to >_
 | 
			
		||||
 * |?|                    {@link com.maddyhome.idea.vim.action.motion.search.SearchEntryRevAction}
 | 
			
		||||
 * |@|                    {@link com.maddyhome.idea.vim.action.macro.PlaybackRegisterAction}
 | 
			
		||||
 * |A|                    {@link com.maddyhome.idea.vim.action.change.insert.InsertAfterLineEndAction}
 | 
			
		||||
 * |B|                    {@link com.maddyhome.idea.vim.action.motion.text.MotionBigWordLeftAction}
 | 
			
		||||
 * |C|                    {@link com.maddyhome.idea.vim.action.change.change.ChangeEndOfLineAction}
 | 
			
		||||
 * |D|                    {@link com.maddyhome.idea.vim.action.change.delete.DeleteEndOfLineAction}
 | 
			
		||||
 * |E|                    {@link com.maddyhome.idea.vim.action.motion.text.MotionBigWordEndRightAction}
 | 
			
		||||
 * |F|                    {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLeftMatchCharAction}
 | 
			
		||||
 * |G|                    {@link com.maddyhome.idea.vim.action.motion.updown.MotionGotoLineLastAction}
 | 
			
		||||
 * |H|                    {@link com.maddyhome.idea.vim.action.motion.screen.MotionFirstScreenLineAction}
 | 
			
		||||
 * |H|                    {@link com.maddyhome.idea.vim.action.motion.screen.MotionOpPendingFirstScreenLineAction}
 | 
			
		||||
 * |I|                    {@link com.maddyhome.idea.vim.action.change.insert.InsertBeforeFirstNonBlankAction}
 | 
			
		||||
 * |J|                    {@link com.maddyhome.idea.vim.action.change.delete.DeleteJoinLinesSpacesAction}
 | 
			
		||||
 * |K|                    {@link com.maddyhome.idea.vim.action.editor.VimQuickJavaDoc}
 | 
			
		||||
 * |L|                    {@link com.maddyhome.idea.vim.action.motion.screen.MotionLastScreenLineAction}
 | 
			
		||||
 * |L|                    {@link com.maddyhome.idea.vim.action.motion.screen.MotionOpPendingLastScreenLineAction}
 | 
			
		||||
 * |M|                    {@link com.maddyhome.idea.vim.action.motion.screen.MotionMiddleScreenLineAction}
 | 
			
		||||
 * |N|                    {@link com.maddyhome.idea.vim.action.motion.search.SearchAgainPreviousAction}
 | 
			
		||||
 * |O|                    {@link com.maddyhome.idea.vim.action.change.insert.InsertNewLineAboveAction}
 | 
			
		||||
 * |P|                    {@link com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorAction}
 | 
			
		||||
 * |Q|                    TO BE IMPLEMENTED
 | 
			
		||||
 * |R|                    {@link com.maddyhome.idea.vim.action.change.change.ChangeReplaceAction}
 | 
			
		||||
 * |S|                    {@link com.maddyhome.idea.vim.action.change.change.ChangeLineAction}
 | 
			
		||||
 * |T|                    {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLeftTillMatchCharAction}
 | 
			
		||||
 * |U|                    ?
 | 
			
		||||
 * |V|                    {@link com.maddyhome.idea.vim.action.motion.visual.VisualToggleLineModeAction}
 | 
			
		||||
 * |W|                    {@link com.maddyhome.idea.vim.action.motion.text.MotionBigWordRightAction}
 | 
			
		||||
 * |X|                    {@link com.maddyhome.idea.vim.action.change.delete.DeleteCharacterLeftAction}
 | 
			
		||||
 * |Y|                    {@link com.maddyhome.idea.vim.action.copy.YankLineAction}
 | 
			
		||||
 * |ZZ|                   {@link com.maddyhome.idea.vim.action.file.FileSaveCloseAction}
 | 
			
		||||
 * |ZQ|                   {@link com.maddyhome.idea.vim.action.file.FileSaveCloseAction}
 | 
			
		||||
 * |[|                    see bracket commands
 | 
			
		||||
 * |]|                    see bracket commands
 | 
			
		||||
 * |^|                    {@link com.maddyhome.idea.vim.action.motion.leftright.MotionFirstNonSpaceAction}
 | 
			
		||||
 * |_|                    {@link com.maddyhome.idea.vim.action.motion.updown.MotionDownLess1FirstNonSpaceAction}
 | 
			
		||||
 * |`|                    {@link com.maddyhome.idea.vim.action.motion.mark.MotionGotoMarkAction}
 | 
			
		||||
 * |``|                   ?
 | 
			
		||||
 * ...
 | 
			
		||||
 * |0|                    {@link com.maddyhome.idea.vim.action.motion.leftright.MotionFirstColumnAction}
 | 
			
		||||
 * |a|                    {@link com.maddyhome.idea.vim.action.change.insert.InsertAfterCursorAction}
 | 
			
		||||
 * |b|                    {@link com.maddyhome.idea.vim.action.motion.text.MotionWordLeftAction}
 | 
			
		||||
 * |c|                    {@link com.maddyhome.idea.vim.action.change.change.ChangeMotionAction}
 | 
			
		||||
 * |cc|                   translated to c_
 | 
			
		||||
 * |d|                    {@link com.maddyhome.idea.vim.action.change.delete.DeleteMotionAction}
 | 
			
		||||
 * |dd|                   translated to d_
 | 
			
		||||
 * |do|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |dp|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |e|                    {@link com.maddyhome.idea.vim.action.motion.text.MotionWordEndRightAction}
 | 
			
		||||
 * |f|                    {@link com.maddyhome.idea.vim.action.motion.leftright.MotionRightMatchCharAction}
 | 
			
		||||
 * |g|                    see commands starting with 'g'
 | 
			
		||||
 * |h|                    {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLeftAction}
 | 
			
		||||
 * |i|                    {@link com.maddyhome.idea.vim.action.change.insert.InsertBeforeCursorAction}
 | 
			
		||||
 * |j|                    {@link com.maddyhome.idea.vim.action.motion.updown.MotionDownAction}
 | 
			
		||||
 * |k|                    {@link com.maddyhome.idea.vim.action.motion.updown.MotionUpAction}
 | 
			
		||||
 * |l|                    {@link com.maddyhome.idea.vim.action.motion.leftright.MotionRightAction}
 | 
			
		||||
 * |m|                    {@link com.maddyhome.idea.vim.action.motion.mark.MotionMarkAction}
 | 
			
		||||
 * |n|                    {@link com.maddyhome.idea.vim.action.motion.search.SearchAgainNextAction}
 | 
			
		||||
 * |o|                    {@link com.maddyhome.idea.vim.action.change.insert.InsertNewLineBelowAction}
 | 
			
		||||
 * |p|                    {@link com.maddyhome.idea.vim.action.copy.PutTextAfterCursorAction}
 | 
			
		||||
 * |q|                    {@link com.maddyhome.idea.vim.action.macro.ToggleRecordingAction}
 | 
			
		||||
 * |r|                    {@link com.maddyhome.idea.vim.action.change.change.ChangeCharacterAction}
 | 
			
		||||
 * |s|                    {@link com.maddyhome.idea.vim.action.change.change.ChangeCharactersAction}
 | 
			
		||||
 * |t|                    {@link com.maddyhome.idea.vim.action.motion.leftright.MotionRightTillMatchCharAction}
 | 
			
		||||
 * |u|                    {@link com.maddyhome.idea.vim.action.change.UndoAction}
 | 
			
		||||
 * |v|                    {@link com.maddyhome.idea.vim.action.motion.visual.VisualToggleCharacterModeAction}
 | 
			
		||||
 * |w|                    {@link com.maddyhome.idea.vim.action.motion.text.MotionWordRightAction}
 | 
			
		||||
 * |x|                    {@link com.maddyhome.idea.vim.action.change.delete.DeleteCharacterRightAction}
 | 
			
		||||
 * |y|                    {@link com.maddyhome.idea.vim.action.copy.YankMotionAction}
 | 
			
		||||
 * |yy|                   translated to y_
 | 
			
		||||
 * |z|                    see commands starting with 'z'
 | 
			
		||||
 * |{|                    {@link com.maddyhome.idea.vim.action.motion.text.MotionParagraphPreviousAction}
 | 
			
		||||
 * |bar|                  {@link com.maddyhome.idea.vim.action.motion.leftright.MotionColumnAction}
 | 
			
		||||
 * |}|                    {@link com.maddyhome.idea.vim.action.motion.text.MotionParagraphNextAction}
 | 
			
		||||
 * |~|                    {@link com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleCharacterAction}
 | 
			
		||||
 * |<C-End>|              {@link com.maddyhome.idea.vim.action.motion.updown.MotionGotoLineLastEndAction}
 | 
			
		||||
 * |<C-Home>|             {@link com.maddyhome.idea.vim.action.motion.updown.MotionGotoLineFirstAction}
 | 
			
		||||
 * |<C-Left>|             {@link com.maddyhome.idea.vim.action.motion.text.MotionWordLeftAction}
 | 
			
		||||
 * |<C-Right>|            {@link com.maddyhome.idea.vim.action.motion.text.MotionWordRightAction}
 | 
			
		||||
 * |<C-Down>|             {@link com.maddyhome.idea.vim.action.motion.scroll.CtrlDownAction}
 | 
			
		||||
 * |<C-Up>|               {@link com.maddyhome.idea.vim.action.motion.scroll.CtrlUpAction}
 | 
			
		||||
 * |<Del>|                {@link com.maddyhome.idea.vim.action.change.delete.DeleteCharacterAction}
 | 
			
		||||
 * |<Down>|               {@link com.maddyhome.idea.vim.action.motion.updown.MotionArrowDownAction}
 | 
			
		||||
 * |<End>|                {@link com.maddyhome.idea.vim.action.motion.leftright.MotionEndAction}
 | 
			
		||||
 * |<F1>|                 IntelliJ help
 | 
			
		||||
 * |<Home>|               {@link com.maddyhome.idea.vim.action.motion.leftright.MotionHomeAction}
 | 
			
		||||
 * |<Insert>|             {@link com.maddyhome.idea.vim.action.change.insert.InsertBeforeCursorAction}
 | 
			
		||||
 * |<Left>|               {@link com.maddyhome.idea.vim.action.motion.leftright.MotionArrowLeftAction}
 | 
			
		||||
 * |<PageDown>|           {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageDownAction}
 | 
			
		||||
 * |<PageUp>|             {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageUpAction}
 | 
			
		||||
 * |<Right>|              {@link com.maddyhome.idea.vim.action.motion.leftright.MotionArrowRightAction}
 | 
			
		||||
 * |<S-Down>|             {@link com.maddyhome.idea.vim.action.motion.updown.MotionShiftDownAction}
 | 
			
		||||
 * |<S-Left>|             {@link com.maddyhome.idea.vim.action.motion.leftright.MotionShiftLeftAction}
 | 
			
		||||
 * |<S-Right>|            {@link com.maddyhome.idea.vim.action.motion.leftright.MotionShiftRightAction}
 | 
			
		||||
 * |<S-Up>|               {@link com.maddyhome.idea.vim.action.motion.updown.MotionShiftUpAction}
 | 
			
		||||
 * |<S-Home>|             {@link com.maddyhome.idea.vim.action.motion.leftright.MotionShiftHomeAction}
 | 
			
		||||
 * |<S-End>|              {@link com.maddyhome.idea.vim.action.motion.leftright.MotionShiftEndAction}
 | 
			
		||||
 * |<Up>|                 {@link com.maddyhome.idea.vim.action.motion.updown.MotionArrowUpAction}
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * 2.1. Text objects
 | 
			
		||||
 *
 | 
			
		||||
 * Text object commands are listed in the visual mode section.
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * 2.2. Window commands
 | 
			
		||||
 *
 | 
			
		||||
 * tag                    action
 | 
			
		||||
 * -------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * |CTRL-W_+|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_-|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_<|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_=|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_>|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_H|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_J|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_K|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_L|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_P|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_R|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_S|             {@link com.maddyhome.idea.vim.action.window.HorizontalSplitAction}
 | 
			
		||||
 * |CTRL-W_T|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_W|             {@link com.maddyhome.idea.vim.action.window.WindowPrevAction}
 | 
			
		||||
 * |CTRL-W_]|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_^|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W__|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_b|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_c|             {@link com.maddyhome.idea.vim.action.window.CloseWindowAction}
 | 
			
		||||
 * |CTRL-W_d|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_f|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W-F|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W-g]|            TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W-g}|            TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W-gf|            TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W-gF|            TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_h|             {@link com.maddyhome.idea.vim.action.window.WindowLeftAction}
 | 
			
		||||
 * |CTRL-W_i|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_j|             {@link com.maddyhome.idea.vim.action.window.WindowDownAction}
 | 
			
		||||
 * |CTRL-W_k|             {@link com.maddyhome.idea.vim.action.window.WindowUpAction}
 | 
			
		||||
 * |CTRL-W_l|             {@link com.maddyhome.idea.vim.action.window.WindowRightAction}
 | 
			
		||||
 * |CTRL-W_n|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_o|             {@link com.maddyhome.idea.vim.action.window.WindowOnlyAction}
 | 
			
		||||
 * |CTRL-W_p|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_q|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_r|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_s|             {@link com.maddyhome.idea.vim.action.window.HorizontalSplitAction}
 | 
			
		||||
 * |CTRL-W_t|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_v|             {@link com.maddyhome.idea.vim.action.window.VerticalSplitAction}
 | 
			
		||||
 * |CTRL-W_w|             {@link com.maddyhome.idea.vim.action.window.WindowNextAction}
 | 
			
		||||
 * |CTRL-W_x|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_z|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_bar|           TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_}|             TO BE IMPLEMENTED
 | 
			
		||||
 * |CTRL-W_<Down>|        {@link com.maddyhome.idea.vim.action.window.WindowDownAction}
 | 
			
		||||
 * |CTRL-W_<Up>|          {@link com.maddyhome.idea.vim.action.window.WindowUpAction}
 | 
			
		||||
 * |CTRL-W_<Left>|        {@link com.maddyhome.idea.vim.action.window.WindowLeftAction}
 | 
			
		||||
 * |CTRL-W_<Right>|       {@link com.maddyhome.idea.vim.action.window.WindowRightAction}
 | 
			
		||||
 * |CTRL-W_CTRL-H|        {@link com.maddyhome.idea.vim.action.window.WindowLeftAction}
 | 
			
		||||
 * |CTRL-W_CTRL-J|        {@link com.maddyhome.idea.vim.action.window.WindowDownAction}
 | 
			
		||||
 * |CTRL-W_CTRL-K|        {@link com.maddyhome.idea.vim.action.window.WindowUpAction}
 | 
			
		||||
 * |CTRL-W_CTRL-L|        {@link com.maddyhome.idea.vim.action.window.WindowRightAction}
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * 2.3. Square bracket commands
 | 
			
		||||
 *
 | 
			
		||||
 * tag                    action
 | 
			
		||||
 * -------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
 * |[_CTRL-D|             TO BE IMPLEMENTED
 | 
			
		||||
 * |[_CTRL-I|             TO BE IMPLEMENTED
 | 
			
		||||
 * |[#|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |['|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |[(|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedParenOpenAction}
 | 
			
		||||
 * |[star|                TO BE IMPLEMENTED
 | 
			
		||||
 * |[`|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |[/|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |[D|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |[I|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |[M|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodPreviousEndAction}
 | 
			
		||||
 * |[P|                   {@link com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorNoIndentAction}
 | 
			
		||||
 * |[P|                   {@link com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorNoIndentAction}
 | 
			
		||||
 * |[[|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionSectionBackwardStartAction}
 | 
			
		||||
 * |[]|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionSectionBackwardEndAction}
 | 
			
		||||
 * |[c|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |[d|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |[f|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |[i|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |[m|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodPreviousStartAction}
 | 
			
		||||
 * |[p|                   {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorNoIndentAction}
 | 
			
		||||
 * |[p|                   {@link com.maddyhome.idea.vim.action.copy.PutTextAfterCursorNoIndentAction}
 | 
			
		||||
 * |[s|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionMisspelledWordPreviousAction}
 | 
			
		||||
 * |[z|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |[{|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedBraceOpenAction}
 | 
			
		||||
 * |]_CTRL-D|             TO BE IMPLEMENTED
 | 
			
		||||
 * |]_CTRL-I|             TO BE IMPLEMENTED
 | 
			
		||||
 * |]#|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |]'|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |])|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedParenCloseAction}
 | 
			
		||||
 * |]star|                TO BE IMPLEMENTED
 | 
			
		||||
 * |]`|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |]/|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |]D|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |]I|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |]M|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodNextEndAction}
 | 
			
		||||
 * |]P|                   {@link com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorNoIndentAction}
 | 
			
		||||
 * |]P|                   {@link com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorNoIndentAction}
 | 
			
		||||
 * |][|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionSectionForwardEndAction}
 | 
			
		||||
 * |]]|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionSectionForwardStartAction}
 | 
			
		||||
 * |]c|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |]d|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |]f|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |]i|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |]m|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodNextStartAction}
 | 
			
		||||
 * |]p|                   {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorNoIndentAction}
 | 
			
		||||
 * |]p|                   {@link com.maddyhome.idea.vim.action.copy.PutTextAfterCursorNoIndentAction}
 | 
			
		||||
 * |]s|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionMisspelledWordNextAction}
 | 
			
		||||
 * |]z|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |]}|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedBraceCloseAction}
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * 2.4. Commands starting with 'g'
 | 
			
		||||
 *
 | 
			
		||||
 * tag                    action
 | 
			
		||||
 * -------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * |g_CTRL-A|             not applicable
 | 
			
		||||
 * |g_CTRL-G|             {@link com.maddyhome.idea.vim.action.file.FileGetLocationInfoAction}
 | 
			
		||||
 * |g_CTRL-H|             {@link com.maddyhome.idea.vim.action.motion.select.SelectEnableBlockModeAction}
 | 
			
		||||
 * |g_CTRL-]|             TO BE IMPLEMENTED
 | 
			
		||||
 * |g#|                   {@link com.maddyhome.idea.vim.action.motion.search.SearchWordBackwardAction}
 | 
			
		||||
 * |g$|                   {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLastScreenColumnAction}
 | 
			
		||||
 * |g&|                   {@link com.maddyhome.idea.vim.action.change.change.ChangeLastGlobalSearchReplaceAction}
 | 
			
		||||
 * |v_g'|                 {@link com.maddyhome.idea.vim.action.motion.mark.MotionGotoFileMarkLineNoSaveJumpAction}
 | 
			
		||||
 * |g'|                   {@link com.maddyhome.idea.vim.action.motion.mark.MotionGotoMarkLineNoSaveJumpAction}
 | 
			
		||||
 * |g`|                   {@link com.maddyhome.idea.vim.action.motion.mark.MotionGotoMarkNoSaveJumpAction}
 | 
			
		||||
 * |gstar|                {@link com.maddyhome.idea.vim.action.motion.search.SearchWordForwardAction}
 | 
			
		||||
 * |g+|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |g,|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |g-|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |g0|                   {@link com.maddyhome.idea.vim.action.motion.leftright.MotionFirstScreenColumnAction}
 | 
			
		||||
 * |g8|                   {@link com.maddyhome.idea.vim.action.file.FileGetHexAction}
 | 
			
		||||
 * |g;|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |g<|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |g?|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |g?g?|                 TO BE IMPLEMENTED
 | 
			
		||||
 * |gD|                   {@link com.maddyhome.idea.vim.action.motion.search.GotoDeclarationAction}
 | 
			
		||||
 * |gE|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionBigWordEndLeftAction}
 | 
			
		||||
 * |gF|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |gH|                   {@link com.maddyhome.idea.vim.action.motion.select.SelectEnableLineModeAction}
 | 
			
		||||
 * |gI|                   {@link com.maddyhome.idea.vim.action.change.insert.InsertLineStartAction}
 | 
			
		||||
 * |gJ|                   {@link com.maddyhome.idea.vim.action.change.delete.DeleteJoinLinesAction}
 | 
			
		||||
 * |gN|                   {@link com.maddyhome.idea.vim.action.motion.gn.VisualSelectPreviousSearch}
 | 
			
		||||
 * |gN|                   {@link com.maddyhome.idea.vim.action.motion.gn.GnPreviousTextObject}
 | 
			
		||||
 * |gP|                   {@link com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorMoveCursorAction}
 | 
			
		||||
 * |gP|                   {@link com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorActionMoveCursor}
 | 
			
		||||
 * |gQ|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |gR|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |gT|                   {@link com.maddyhome.idea.vim.action.window.tabs.PreviousTabAction}
 | 
			
		||||
 * |gU|                   {@link com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperMotionAction}
 | 
			
		||||
 * |gV|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |g]|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |g^|                   {@link com.maddyhome.idea.vim.action.motion.leftright.MotionFirstScreenNonSpaceAction}
 | 
			
		||||
 * |g_|                   {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLastNonSpaceAction}
 | 
			
		||||
 * |ga|                   {@link com.maddyhome.idea.vim.action.file.FileGetAsciiAction}
 | 
			
		||||
 * |gd|                   {@link com.maddyhome.idea.vim.action.motion.search.GotoDeclarationAction}
 | 
			
		||||
 * |ge|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionWordEndLeftAction}
 | 
			
		||||
 * |gf|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |gg|                   {@link com.maddyhome.idea.vim.action.motion.updown.MotionGotoLineFirstAction}
 | 
			
		||||
 * |gh|                   {@link com.maddyhome.idea.vim.action.motion.select.SelectEnableCharacterModeAction}
 | 
			
		||||
 * |gi|                   {@link com.maddyhome.idea.vim.action.change.insert.InsertAtPreviousInsertAction}
 | 
			
		||||
 * |gj|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |gk|                   {@link com.maddyhome.idea.vim.action.motion.updown.MotionUpNotLineWiseAction}
 | 
			
		||||
 * |gn|                   {@link com.maddyhome.idea.vim.action.motion.gn.VisualSelectNextSearch}
 | 
			
		||||
 * |gn|                   {@link com.maddyhome.idea.vim.action.motion.gn.GnNextTextObject}
 | 
			
		||||
 * |gm|                   {@link com.maddyhome.idea.vim.action.motion.leftright.MotionMiddleColumnAction}
 | 
			
		||||
 * |go|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionNthCharacterAction}
 | 
			
		||||
 * |gp|                   {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorMoveCursorAction}
 | 
			
		||||
 * |gp|                   {@link com.maddyhome.idea.vim.action.copy.PutTextAfterCursorActionMoveCursor}
 | 
			
		||||
 * |gq|                   {@link com.maddyhome.idea.vim.action.change.change.ReformatCodeMotionAction}
 | 
			
		||||
 * |gr|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |gs|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |gt|                   {@link com.maddyhome.idea.vim.action.window.tabs.NextTabAction}
 | 
			
		||||
 * |gu|                   {@link com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerMotionAction}
 | 
			
		||||
 * |gv|                   {@link com.maddyhome.idea.vim.action.motion.visual.VisualSelectPreviousAction}
 | 
			
		||||
 * |gw|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |g@|                   {@link com.maddyhome.idea.vim.action.change.OperatorAction}
 | 
			
		||||
 * |g~|                   {@link com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleMotionAction}
 | 
			
		||||
 * |g<Down>|              TO BE IMPLEMENTED
 | 
			
		||||
 * |g<End>|               {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLastScreenColumnAction}
 | 
			
		||||
 * |g<Home>|              {@link com.maddyhome.idea.vim.action.motion.leftright.MotionFirstScreenColumnAction}
 | 
			
		||||
 * |g<Up>|                {@link com.maddyhome.idea.vim.action.motion.updown.MotionUpNotLineWiseAction}
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * 2.5. Commands starting with 'z'
 | 
			
		||||
 *
 | 
			
		||||
 * tag                    action
 | 
			
		||||
 * -------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
 * |z<CR>|                {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollFirstScreenLineStartAction}
 | 
			
		||||
 * |z+|                   {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollFirstScreenLinePageStartAction}
 | 
			
		||||
 * |z-|                   {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLastScreenLineStartAction}
 | 
			
		||||
 * |z.|                   {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollMiddleScreenLineStartAction}
 | 
			
		||||
 * |z=|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zA|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zC|                   {@link com.maddyhome.idea.vim.action.fold.VimCollapseRegionRecursively}
 | 
			
		||||
 * |zD|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zE|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zF|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zG|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zH|                   {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollHalfWidthLeftAction}
 | 
			
		||||
 * |zL|                   {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollHalfWidthRightAction}
 | 
			
		||||
 * |zM|                   {@link com.maddyhome.idea.vim.action.fold.VimCollapseAllRegions}
 | 
			
		||||
 * |zN|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zO|                   {@link com.maddyhome.idea.vim.action.fold.VimExpandRegionRecursively}
 | 
			
		||||
 * |zR|                   {@link com.maddyhome.idea.vim.action.fold.VimExpandAllRegions}
 | 
			
		||||
 * |zW|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zX|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |z^|                   {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLastScreenLinePageStartAction}
 | 
			
		||||
 * |za|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zb|                   {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLastScreenLineAction}
 | 
			
		||||
 * |zc|                   {@link com.maddyhome.idea.vim.action.fold.VimCollapseRegion}
 | 
			
		||||
 * |zd|                   not applicable
 | 
			
		||||
 * |ze|                   {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLastScreenColumnAction}
 | 
			
		||||
 * |zf|                   not applicable
 | 
			
		||||
 * |zg|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zh|                   {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollColumnRightAction}
 | 
			
		||||
 * |zi|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zj|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zk|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zl|                   {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollColumnLeftAction}
 | 
			
		||||
 * |zm|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zn|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zo|                   {@link com.maddyhome.idea.vim.action.fold.VimExpandRegion}
 | 
			
		||||
 * |zr|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zs|                   {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollFirstScreenColumnAction}
 | 
			
		||||
 * |zt|                   {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollFirstScreenLineAction}
 | 
			
		||||
 * |zv|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zw|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zx|                   TO BE IMPLEMENTED
 | 
			
		||||
 * |zz|                   {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollMiddleScreenLineAction}
 | 
			
		||||
 * |z<Left>|              {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollColumnRightAction}
 | 
			
		||||
 * |z<Right>|             {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollColumnLeftAction}
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * 3. Visual mode
 | 
			
		||||
 *
 | 
			
		||||
 * tag                    action
 | 
			
		||||
 * -------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * |v_CTRL-\_CTRL-N|      {@link com.maddyhome.idea.vim.action.motion.visual.VisualExitModeAction}
 | 
			
		||||
 * |v_CTRL-\_CTRL-G|      TO BE IMPLEMENTED
 | 
			
		||||
 * |v_CTRL-A|             {@link com.maddyhome.idea.vim.action.change.change.number.ChangeVisualNumberIncAction}
 | 
			
		||||
 * |v_CTRL-C|             {@link com.maddyhome.idea.vim.action.motion.visual.VisualExitModeAction}
 | 
			
		||||
 * |v_CTRL-G|             {@link com.maddyhome.idea.vim.action.motion.select.SelectToggleVisualMode}
 | 
			
		||||
 * |v_<BS>|               NVO mapping
 | 
			
		||||
 * |v_CTRL-H|             NVO mapping
 | 
			
		||||
 * |v_CTRL-O|             TO BE IMPLEMENTED
 | 
			
		||||
 * |v_CTRL-V|             NVO mapping
 | 
			
		||||
 * |v_<Esc>|              {@link com.maddyhome.idea.vim.action.motion.visual.VisualExitModeAction}
 | 
			
		||||
 * |v_CTRL-X|             {@link com.maddyhome.idea.vim.action.change.change.number.ChangeVisualNumberDecAction}
 | 
			
		||||
 * |v_CTRL-]|             TO BE IMPLEMENTED
 | 
			
		||||
 * |v_!|                  {@link com.maddyhome.idea.vim.action.change.change.FilterVisualLinesAction}
 | 
			
		||||
 * |v_:|                  NVO mapping
 | 
			
		||||
 * |v_<|                  {@link com.maddyhome.idea.vim.action.change.shift.ShiftLeftVisualAction}
 | 
			
		||||
 * |v_=|                  {@link com.maddyhome.idea.vim.action.change.change.AutoIndentLinesVisualAction}
 | 
			
		||||
 * |v_>|                  {@link com.maddyhome.idea.vim.action.change.shift.ShiftRightVisualAction}
 | 
			
		||||
 * |v_b_A|                {@link com.maddyhome.idea.vim.action.change.insert.VisualBlockAppendAction}
 | 
			
		||||
 * |v_C|                  {@link com.maddyhome.idea.vim.action.change.change.ChangeVisualLinesEndAction}
 | 
			
		||||
 * |v_D|                  {@link com.maddyhome.idea.vim.action.change.delete.DeleteVisualLinesEndAction}
 | 
			
		||||
 * |v_b_I|                {@link com.maddyhome.idea.vim.action.change.insert.VisualBlockInsertAction}
 | 
			
		||||
 * |v_J|                  {@link com.maddyhome.idea.vim.action.change.delete.DeleteJoinVisualLinesSpacesAction}
 | 
			
		||||
 * |v_K|                  TO BE IMPLEMENTED
 | 
			
		||||
 * |v_O|                  {@link com.maddyhome.idea.vim.action.motion.visual.VisualSwapEndsBlockAction}
 | 
			
		||||
 * |v_P|                  {@link com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorAction}
 | 
			
		||||
 * |v_R|                  {@link com.maddyhome.idea.vim.action.change.change.ChangeVisualLinesAction}
 | 
			
		||||
 * |v_S|                  {@link com.maddyhome.idea.vim.action.change.change.ChangeVisualLinesAction}
 | 
			
		||||
 * |v_U|                  {@link com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperVisualAction}
 | 
			
		||||
 * |v_V|                  NV mapping
 | 
			
		||||
 * |v_X|                  {@link com.maddyhome.idea.vim.action.change.delete.DeleteVisualLinesAction}
 | 
			
		||||
 * |v_Y|                  {@link com.maddyhome.idea.vim.action.copy.YankVisualLinesAction}
 | 
			
		||||
 * |v_aquote|             {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockDoubleQuoteAction}
 | 
			
		||||
 * |v_a'|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockSingleQuoteAction}
 | 
			
		||||
 * |v_a(|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockParenAction}
 | 
			
		||||
 * |v_a)|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockParenAction}
 | 
			
		||||
 * |v_a<|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockAngleAction}
 | 
			
		||||
 * |v_a>|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockAngleAction}
 | 
			
		||||
 * |v_aB|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockBraceAction}
 | 
			
		||||
 * |v_aW|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBigWordAction}
 | 
			
		||||
 * |v_a[|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockBracketAction}
 | 
			
		||||
 * |v_a]|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockBracketAction}
 | 
			
		||||
 * |v_a`|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockBackQuoteAction}
 | 
			
		||||
 * |v_ab|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockParenAction}
 | 
			
		||||
 * |v_ap|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterParagraphAction}
 | 
			
		||||
 * |v_as|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterSentenceAction}
 | 
			
		||||
 * |v_at|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockTagAction}
 | 
			
		||||
 * |v_aw|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterWordAction}
 | 
			
		||||
 * |v_a{|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockBraceAction}
 | 
			
		||||
 * |v_a}|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockBraceAction}
 | 
			
		||||
 * |v_c|                  {@link com.maddyhome.idea.vim.action.change.change.ChangeVisualAction}
 | 
			
		||||
 * |v_d|                  {@link com.maddyhome.idea.vim.action.change.delete.DeleteVisualAction}
 | 
			
		||||
 * |v_gCTRL-A|            {@link com.maddyhome.idea.vim.action.change.change.number.ChangeVisualNumberAvalancheIncAction}
 | 
			
		||||
 * |v_gCTRL-X|            {@link com.maddyhome.idea.vim.action.change.change.number.ChangeVisualNumberAvalancheDecAction}
 | 
			
		||||
 * |v_gJ|                 {@link com.maddyhome.idea.vim.action.change.delete.DeleteJoinVisualLinesAction}
 | 
			
		||||
 * |v_gq|                 {@link com.maddyhome.idea.vim.action.change.change.ReformatCodeVisualAction}
 | 
			
		||||
 * |v_gv|                 {@link com.maddyhome.idea.vim.action.motion.visual.VisualSwapSelectionsAction}
 | 
			
		||||
 * |v_g`|                 {@link com.maddyhome.idea.vim.action.motion.mark.MotionGotoFileMarkNoSaveJumpAction}
 | 
			
		||||
 * |v_g@|                 {@link com.maddyhome.idea.vim.action.change.VisualOperatorAction}
 | 
			
		||||
 * |v_iquote|             {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockDoubleQuoteAction}
 | 
			
		||||
 * |v_i'|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockSingleQuoteAction}
 | 
			
		||||
 * |v_i(|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockParenAction}
 | 
			
		||||
 * |v_i)|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockParenAction}
 | 
			
		||||
 * |v_i<|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockAngleAction}
 | 
			
		||||
 * |v_i>|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockAngleAction}
 | 
			
		||||
 * |v_iB|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockBraceAction}
 | 
			
		||||
 * |v_iW|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBigWordAction}
 | 
			
		||||
 * |v_i[|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockBracketAction}
 | 
			
		||||
 * |v_i]|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockBracketAction}
 | 
			
		||||
 * |v_i`|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockBackQuoteAction}
 | 
			
		||||
 * |v_ib|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockParenAction}
 | 
			
		||||
 * |v_ip|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerParagraphAction}
 | 
			
		||||
 * |v_is|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerSentenceAction}
 | 
			
		||||
 * |v_it|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockTagAction}
 | 
			
		||||
 * |v_iw|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerWordAction}
 | 
			
		||||
 * |v_i{|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockBraceAction}
 | 
			
		||||
 * |v_i}|                 {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockBraceAction}
 | 
			
		||||
 * |v_o|                  {@link com.maddyhome.idea.vim.action.motion.visual.VisualSwapEndsAction}
 | 
			
		||||
 * |v_p|                  {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorAction}
 | 
			
		||||
 * |v_r|                  {@link com.maddyhome.idea.vim.action.change.change.ChangeVisualCharacterAction}
 | 
			
		||||
 * |v_s|                  {@link com.maddyhome.idea.vim.action.change.change.ChangeVisualAction}
 | 
			
		||||
 * |v_u|                  {@link com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerVisualAction}
 | 
			
		||||
 * |v_v|                  NV mapping
 | 
			
		||||
 * |v_x|                  {@link com.maddyhome.idea.vim.action.change.delete.DeleteVisualAction}
 | 
			
		||||
 * |v_y|                  {@link com.maddyhome.idea.vim.action.copy.YankVisualAction}
 | 
			
		||||
 * |v_~|                  {@link com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleVisualAction}
 | 
			
		||||
 * |v_`|                  {@link com.maddyhome.idea.vim.action.motion.mark.MotionGotoFileMarkAction}
 | 
			
		||||
 * |v_'|                  {@link com.maddyhome.idea.vim.action.motion.mark.MotionGotoFileMarkLineAction}
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * 4. Select mode
 | 
			
		||||
 *
 | 
			
		||||
 * tag                    action
 | 
			
		||||
 * -------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
 * |<BS>|                 {@link com.maddyhome.idea.vim.action.motion.select.SelectDeleteAction}
 | 
			
		||||
 * |<CR>|                 {@link com.maddyhome.idea.vim.action.motion.select.SelectEnterAction}
 | 
			
		||||
 * |<DEL>|                {@link com.maddyhome.idea.vim.action.motion.select.SelectDeleteAction}
 | 
			
		||||
 * |<ESC>|                {@link com.maddyhome.idea.vim.action.motion.select.SelectEscapeAction}
 | 
			
		||||
 * |<C-G>|                {@link com.maddyhome.idea.vim.action.motion.select.SelectToggleVisualMode}
 | 
			
		||||
 * |<S-Down>|             {@link com.maddyhome.idea.vim.action.motion.updown.MotionShiftDownAction}
 | 
			
		||||
 * |<S-Left>|             {@link com.maddyhome.idea.vim.action.motion.leftright.MotionShiftLeftAction}
 | 
			
		||||
 * |<S-Right>|            {@link com.maddyhome.idea.vim.action.motion.leftright.MotionShiftRightAction}
 | 
			
		||||
 * |<S-Up>|               {@link com.maddyhome.idea.vim.action.motion.updown.MotionShiftUpAction}
 | 
			
		||||
 * |<Down>|               {@link com.maddyhome.idea.vim.action.motion.updown.MotionArrowDownAction}
 | 
			
		||||
 * |<Left>|               {@link com.maddyhome.idea.vim.action.motion.select.motion.SelectMotionLeftAction}
 | 
			
		||||
 * |<Right>|              {@link com.maddyhome.idea.vim.action.motion.select.motion.SelectMotionRightAction}
 | 
			
		||||
 * |<Up>|                 {@link com.maddyhome.idea.vim.action.motion.updown.MotionArrowUpAction}
 | 
			
		||||
 *
 | 
			
		||||
 * 5. Command line editing
 | 
			
		||||
 *
 | 
			
		||||
 * tag                    action
 | 
			
		||||
 * -------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * |c_CTRL-A|             TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-B|             {@link javax.swing.text.DefaultEditorKit#beginLineAction}
 | 
			
		||||
 * |c_CTRL-C|             {@link com.maddyhome.idea.vim.ui.ex.CancelEntryAction}
 | 
			
		||||
 * |c_CTRL-D|             TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-E|             {@link javax.swing.text.DefaultEditorKit#endLineAction}
 | 
			
		||||
 * |c_CTRL-G|             TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-H|             {@link com.maddyhome.idea.vim.ui.ex.DeletePreviousCharAction}
 | 
			
		||||
 * |c_CTRL-I|             TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-J|             {@link com.maddyhome.idea.vim.ui.ex.CompleteEntryAction}
 | 
			
		||||
 * |c_CTRL-K|             Handled by KeyHandler
 | 
			
		||||
 * |c_CTRL-L|             TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-M|             {@link com.maddyhome.idea.vim.action.ex.ProcessExEntryAction}
 | 
			
		||||
 * |c_CTRL-N|             {@link com.maddyhome.idea.vim.ui.ex.HistoryDownAction}
 | 
			
		||||
 * |c_CTRL-P|             {@link com.maddyhome.idea.vim.ui.ex.HistoryUpAction}
 | 
			
		||||
 * |c_CTRL-Q|             Handled by KeyHandler
 | 
			
		||||
 * |c_CTRL-R_CTRL-A|      TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-R_CTRL-F|      TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-R_CTRL-L|      TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-R_CTRL-O|      TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-R_CTRL-P|      TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-R_CTRL-R|      TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-R_CTRL-W|      TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-T|             TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-U|             {@link com.maddyhome.idea.vim.ui.ex.DeleteToCursorAction}
 | 
			
		||||
 * |c_CTRL-V|             Handled by KeyHandler
 | 
			
		||||
 * |c_CTRL-W|             {@link com.maddyhome.idea.vim.ui.ex.DeletePreviousWordAction}
 | 
			
		||||
 * |c_CTRL-Y|             TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-\_e|           TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-\_CTRL-G|      TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-\_CTRL-N|      TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-_|             not applicable
 | 
			
		||||
 * |c_CTRL-^|             not applicable
 | 
			
		||||
 * |c_CTRL-]|             TO BE IMPLEMENTED
 | 
			
		||||
 * |c_CTRL-[|             {@link com.maddyhome.idea.vim.ui.ex.EscapeCharAction}
 | 
			
		||||
 * |c_<BS>|               {@link com.maddyhome.idea.vim.ui.ex.DeletePreviousCharAction}
 | 
			
		||||
 * |c_<CR>|               {@link com.maddyhome.idea.vim.ui.ex.CompleteEntryAction}
 | 
			
		||||
 * |c_<C-Left>|           {@link javax.swing.text.DefaultEditorKit#previousWordAction}
 | 
			
		||||
 * |c_<C-Right>|          {@link javax.swing.text.DefaultEditorKit#nextWordAction}
 | 
			
		||||
 * |c_<Del>|              {@link javax.swing.text.DefaultEditorKit#deleteNextCharAction}
 | 
			
		||||
 * |c_<Down>|             {@link com.maddyhome.idea.vim.ui.ex.HistoryDownFilterAction}
 | 
			
		||||
 * |c_<End>|              {@link javax.swing.text.DefaultEditorKit#endLineAction}
 | 
			
		||||
 * |c_<Esc>|              {@link com.maddyhome.idea.vim.ui.ex.EscapeCharAction}
 | 
			
		||||
 * |c_<Home>|             {@link javax.swing.text.DefaultEditorKit#beginLineAction}
 | 
			
		||||
 * |c_<Insert>|           {@link com.maddyhome.idea.vim.ui.ex.ToggleInsertReplaceAction}
 | 
			
		||||
 * |c_<Left>|             {@link javax.swing.text.DefaultEditorKit#backwardAction}
 | 
			
		||||
 * |c_<LeftMouse>|        not applicable
 | 
			
		||||
 * |c_<MiddleMouse>|      TO BE IMPLEMENTED
 | 
			
		||||
 * |c_<NL>|               {@link com.maddyhome.idea.vim.ui.ex.CompleteEntryAction}
 | 
			
		||||
 * |c_<PageUp>|           {@link com.maddyhome.idea.vim.ui.ex.HistoryUpAction}
 | 
			
		||||
 * |c_<PageDown>|         {@link com.maddyhome.idea.vim.ui.ex.HistoryDownAction}
 | 
			
		||||
 * |c_<Right>|            {@link javax.swing.text.DefaultEditorKit#forwardAction}
 | 
			
		||||
 * |c_<S-Down>|           {@link com.maddyhome.idea.vim.ui.ex.HistoryDownAction}
 | 
			
		||||
 * |c_<S-Left>|           {@link javax.swing.text.DefaultEditorKit#previousWordAction}
 | 
			
		||||
 * |c_<S-Right>|          {@link javax.swing.text.DefaultEditorKit#nextWordAction}
 | 
			
		||||
 * |c_<S-Tab>|            TO BE IMPLEMENTED
 | 
			
		||||
 * |c_<S-Up>|             {@link com.maddyhome.idea.vim.ui.ex.HistoryUpAction}
 | 
			
		||||
 * |c_<Tab>|              TO BE IMPLEMENTED
 | 
			
		||||
 * |c_<Up>|               {@link com.maddyhome.idea.vim.ui.ex.HistoryUpFilterAction}
 | 
			
		||||
 * |c_digraph|            {char1} <BS> {char2}
 | 
			
		||||
 * |c_wildchar|           TO BE IMPLEMENTED
 | 
			
		||||
 * |'cedit'|              TO BE IMPLEMENTED
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * 6. Ex commands
 | 
			
		||||
 *
 | 
			
		||||
 * tag                    handler
 | 
			
		||||
 * -------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * |:map|                 {@link com.maddyhome.idea.vim.vimscript.model.commands.mapping.MapCommand}
 | 
			
		||||
 * |:nmap|                ...
 | 
			
		||||
 * |:vmap|                ...
 | 
			
		||||
 * |:omap|                ...
 | 
			
		||||
 * |:imap|                ...
 | 
			
		||||
 * |:cmap|                ...
 | 
			
		||||
 * |:noremap|             ...
 | 
			
		||||
 * |:nnoremap|            ...
 | 
			
		||||
 * |:vnoremap|            ...
 | 
			
		||||
 * |:onoremap|            ...
 | 
			
		||||
 * |:inoremap|            ...
 | 
			
		||||
 * |:cnoremap|            ...
 | 
			
		||||
 * |:shell|               {@link com.maddyhome.idea.vim.vimscript.model.commands.ShellCommand}
 | 
			
		||||
 * |:sort|                {@link com.maddyhome.idea.vim.vimscript.model.commands.SortCommand}
 | 
			
		||||
 * |:source|              {@link com.maddyhome.idea.vim.vimscript.model.commands.SourceCommand}
 | 
			
		||||
 * |:qall|                {@link com.maddyhome.idea.vim.vimscript.model.commands.ExitCommand}
 | 
			
		||||
 * |:quitall|             {@link com.maddyhome.idea.vim.vimscript.model.commands.ExitCommand}
 | 
			
		||||
 * |:quitall|             {@link com.maddyhome.idea.vim.vimscript.model.commands.ExitCommand}
 | 
			
		||||
 * |:wqall|               {@link com.maddyhome.idea.vim.vimscript.model.commands.ExitCommand}
 | 
			
		||||
 * |:xall|                {@link com.maddyhome.idea.vim.vimscript.model.commands.ExitCommand}
 | 
			
		||||
 * |:command|             {@link com.maddyhome.idea.vim.vimscript.model.commands.CmdCommand}
 | 
			
		||||
 * |:delcommand|          {@link com.maddyhome.idea.vim.vimscript.model.commands.DelCmdCommand}
 | 
			
		||||
 * |:comclear|            {@link com.maddyhome.idea.vim.vimscript.model.commands.CmdClearCommand}
 | 
			
		||||
 * ...
 | 
			
		||||
 *
 | 
			
		||||
 * The list of supported Ex commands is incomplete.
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * A. Misc commands
 | 
			
		||||
 *
 | 
			
		||||
 * tag                    handler
 | 
			
		||||
 * -------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
 * |]b|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionCamelEndLeftAction}
 | 
			
		||||
 * |]w|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionCamelEndRightAction}
 | 
			
		||||
 * |[b|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionCamelLeftAction}
 | 
			
		||||
 * |[w|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionCamelRightAction}
 | 
			
		||||
 * |g(|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionSentencePreviousEndAction}
 | 
			
		||||
 * |g)|                   {@link com.maddyhome.idea.vim.action.motion.text.MotionSentenceNextEndAction}
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * See also :help index.
 | 
			
		||||
 *
 | 
			
		||||
 * @author vlan
 | 
			
		||||
 */
 | 
			
		||||
package com.maddyhome.idea.vim;
 | 
			
		||||
@@ -19,11 +19,16 @@ import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.group.IjOptionConstants
 | 
			
		||||
import com.maddyhome.idea.vim.group.IjOptions
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.initInjector
 | 
			
		||||
import com.maddyhome.idea.vim.options.OptionAccessScope
 | 
			
		||||
import com.maddyhome.idea.vim.options.OptionConstants
 | 
			
		||||
 | 
			
		||||
internal class OptionsState : ApplicationUsagesCollector() {
 | 
			
		||||
 | 
			
		||||
  init {
 | 
			
		||||
    initInjector()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun getGroup(): EventLogGroup = GROUP
 | 
			
		||||
 | 
			
		||||
  override fun getMetrics(): Set<MetricEvent> {
 | 
			
		||||
 
 | 
			
		||||
@@ -13,11 +13,16 @@ import com.intellij.internal.statistic.eventLog.EventLogGroup
 | 
			
		||||
import com.intellij.internal.statistic.eventLog.events.EventFields
 | 
			
		||||
import com.intellij.internal.statistic.eventLog.events.VarargEventId
 | 
			
		||||
import com.intellij.internal.statistic.service.fus.collectors.ApplicationUsagesCollector
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.initInjector
 | 
			
		||||
import com.maddyhome.idea.vim.statistic.PluginState.Util.extensionNames
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.services.VimRcService
 | 
			
		||||
 | 
			
		||||
internal class VimscriptState : ApplicationUsagesCollector() {
 | 
			
		||||
 | 
			
		||||
  init {
 | 
			
		||||
    initInjector()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun getGroup(): EventLogGroup = GROUP
 | 
			
		||||
 | 
			
		||||
  override fun getMetrics(): Set<MetricEvent> {
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,7 @@ internal class Troubleshooter {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  companion object {
 | 
			
		||||
    val instance = service<Troubleshooter>()
 | 
			
		||||
    fun getInstance() = service<Troubleshooter>()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ import com.maddyhome.idea.vim.KeyHandler;
 | 
			
		||||
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.VimOutputPanel;
 | 
			
		||||
import com.maddyhome.idea.vim.diagnostic.VimLogger;
 | 
			
		||||
import com.maddyhome.idea.vim.helper.MessageHelper;
 | 
			
		||||
import com.maddyhome.idea.vim.helper.UiHelper;
 | 
			
		||||
@@ -46,12 +47,11 @@ import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
 | 
			
		||||
public class ExOutputPanel extends JPanel {
 | 
			
		||||
  private final @NotNull Editor myEditor;
 | 
			
		||||
 | 
			
		||||
  private final @NotNull JLabel myLabel = new JLabel("more");
 | 
			
		||||
  public final @NotNull JLabel myLabel = new JLabel("more");
 | 
			
		||||
  private final @NotNull JTextArea myText = new JTextArea();
 | 
			
		||||
  private final @NotNull JScrollPane myScrollPane =
 | 
			
		||||
    new JBScrollPane(myText, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
 | 
			
		||||
  private final @NotNull ComponentAdapter myAdapter;
 | 
			
		||||
  private boolean myAtEnd = false;
 | 
			
		||||
  private int myLineHeight = 0;
 | 
			
		||||
 | 
			
		||||
  private @Nullable JComponent myOldGlass = null;
 | 
			
		||||
@@ -224,42 +224,31 @@ public class ExOutputPanel extends JPanel {
 | 
			
		||||
    myLabel.setFont(UiHelper.selectEditorFont(myEditor, myLabel.getText()));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void scrollLine() {
 | 
			
		||||
  public void scrollLine() {
 | 
			
		||||
    scrollOffset(myLineHeight);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void scrollPage() {
 | 
			
		||||
  public void scrollPage() {
 | 
			
		||||
    scrollOffset(myScrollPane.getVerticalScrollBar().getVisibleAmount());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void scrollHalfPage() {
 | 
			
		||||
  public void scrollHalfPage() {
 | 
			
		||||
    double sa = myScrollPane.getVerticalScrollBar().getVisibleAmount() / 2.0;
 | 
			
		||||
    double offset = Math.ceil(sa / myLineHeight) * myLineHeight;
 | 
			
		||||
    scrollOffset((int)offset);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void handleEnter() {
 | 
			
		||||
    if (myAtEnd) {
 | 
			
		||||
      close();
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      scrollLine();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void badKey() {
 | 
			
		||||
  public void onBadKey() {
 | 
			
		||||
    myLabel.setText(MessageHelper.message("more.ret.line.space.page.d.half.page.q.quit"));
 | 
			
		||||
    myLabel.setFont(UiHelper.selectEditorFont(myEditor, myLabel.getText()));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void scrollOffset(int more) {
 | 
			
		||||
    myAtEnd = false;
 | 
			
		||||
    int val = myScrollPane.getVerticalScrollBar().getValue();
 | 
			
		||||
    myScrollPane.getVerticalScrollBar().setValue(val + more);
 | 
			
		||||
    myScrollPane.getHorizontalScrollBar().setValue(0);
 | 
			
		||||
    if (val + more >=
 | 
			
		||||
        myScrollPane.getVerticalScrollBar().getMaximum() - myScrollPane.getVerticalScrollBar().getVisibleAmount()) {
 | 
			
		||||
      myAtEnd = true;
 | 
			
		||||
      myLabel.setText(MessageHelper.message("hit.enter.or.type.command.to.continue"));
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
@@ -268,6 +257,11 @@ public class ExOutputPanel extends JPanel {
 | 
			
		||||
    myLabel.setFont(UiHelper.selectEditorFont(myEditor, myLabel.getText()));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public boolean isAtEnd() {
 | 
			
		||||
    int val = myScrollPane.getVerticalScrollBar().getValue();
 | 
			
		||||
    return val >= myScrollPane.getVerticalScrollBar().getMaximum() - myScrollPane.getVerticalScrollBar().getVisibleAmount();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void positionPanel() {
 | 
			
		||||
    final JComponent contentComponent = myEditor.getContentComponent();
 | 
			
		||||
    Container scroll = SwingUtilities.getAncestorOfClass(JScrollPane.class, contentComponent);
 | 
			
		||||
@@ -308,14 +302,13 @@ public class ExOutputPanel extends JPanel {
 | 
			
		||||
    close(null);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void close(final @Nullable KeyEvent e) {
 | 
			
		||||
  public void close(final @Nullable KeyStroke key) {
 | 
			
		||||
    ApplicationManager.getApplication().invokeLater(() -> {
 | 
			
		||||
      deactivate(true);
 | 
			
		||||
 | 
			
		||||
      final Project project = myEditor.getProject();
 | 
			
		||||
 | 
			
		||||
      if (project != null && e != null && e.getKeyChar() != '\n') {
 | 
			
		||||
        final KeyStroke key = KeyStroke.getKeyStrokeForEvent(e);
 | 
			
		||||
      if (project != null && key != null && key.getKeyChar() != '\n') {
 | 
			
		||||
        final List<KeyStroke> keys = new ArrayList<>(1);
 | 
			
		||||
        keys.add(key);
 | 
			
		||||
        if (LOG.isTrace()) {
 | 
			
		||||
@@ -324,7 +317,9 @@ public class ExOutputPanel extends JPanel {
 | 
			
		||||
        }
 | 
			
		||||
        KeyHandler.getInstance().getKeyStack().addKeys(keys);
 | 
			
		||||
        ExecutionContext context = injector.getExecutionContextManager().getEditorExecutionContext(new IjVimEditor(myEditor));
 | 
			
		||||
        VimPlugin.getMacro().playbackKeys(new IjVimEditor(myEditor), context, 1);
 | 
			
		||||
        injector.getApplication().runWriteAction(() -> { VimPlugin.getMacro().playbackKeys(new IjVimEditor(myEditor), context, 1);
 | 
			
		||||
          return null;
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
@@ -341,40 +336,14 @@ public class ExOutputPanel extends JPanel {
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void keyTyped(@NotNull KeyEvent e) {
 | 
			
		||||
      if (myExOutputPanel.myAtEnd) {
 | 
			
		||||
        myExOutputPanel.close(e);
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        switch (e.getKeyChar()) {
 | 
			
		||||
          case ' ':
 | 
			
		||||
            myExOutputPanel.scrollPage();
 | 
			
		||||
            break;
 | 
			
		||||
          case 'd':
 | 
			
		||||
            myExOutputPanel.scrollHalfPage();
 | 
			
		||||
            break;
 | 
			
		||||
          case 'q':
 | 
			
		||||
          case '\u001b':
 | 
			
		||||
            myExOutputPanel.close();
 | 
			
		||||
            break;
 | 
			
		||||
          case '\n':
 | 
			
		||||
            myExOutputPanel.handleEnter();
 | 
			
		||||
            break;
 | 
			
		||||
          case KeyEvent.CHAR_UNDEFINED: {
 | 
			
		||||
            switch (e.getKeyCode()) {
 | 
			
		||||
              case KeyEvent.VK_ENTER:
 | 
			
		||||
                myExOutputPanel.handleEnter();
 | 
			
		||||
                break;
 | 
			
		||||
              case KeyEvent.VK_ESCAPE:
 | 
			
		||||
                myExOutputPanel.close();
 | 
			
		||||
                break;
 | 
			
		||||
              default:
 | 
			
		||||
                myExOutputPanel.badKey();
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          default:
 | 
			
		||||
            myExOutputPanel.badKey();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      VimOutputPanel currentPanel = injector.getOutputPanel().getCurrentOutputPanel();
 | 
			
		||||
      if (currentPanel == null) return;
 | 
			
		||||
 | 
			
		||||
      int keyCode = e.getKeyCode();
 | 
			
		||||
      Character keyChar = e.getKeyChar();
 | 
			
		||||
      int modifiers = e.getModifiersEx();
 | 
			
		||||
      KeyStroke keyStroke = (keyChar == KeyEvent.CHAR_UNDEFINED) ? KeyStroke.getKeyStroke(keyCode, modifiers) : KeyStroke.getKeyStroke(keyChar, modifiers);
 | 
			
		||||
      currentPanel.handleKey(keyStroke);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -150,7 +150,7 @@ internal class ReloadVimRc : DumbAwareAction() {
 | 
			
		||||
  override fun actionPerformed(e: AnActionEvent) {
 | 
			
		||||
    val editor = e.getData(PlatformDataKeys.EDITOR) ?: return
 | 
			
		||||
    injector.keyGroup.removeKeyMapping(MappingOwner.IdeaVim.InitScript)
 | 
			
		||||
    Troubleshooter.instance.removeByType("old-action-notation-in-mappings")
 | 
			
		||||
    Troubleshooter.getInstance().removeByType("old-action-notation-in-mappings")
 | 
			
		||||
 | 
			
		||||
    // Reload the ideavimrc in the context of the current window, as though we had called `:source ~/.ideavimrc`
 | 
			
		||||
    executeIdeaVimRc(editor.vim)
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,6 @@ import com.intellij.openapi.wm.WindowManager
 | 
			
		||||
import com.intellij.openapi.wm.impl.status.EditorBasedWidget
 | 
			
		||||
import com.intellij.util.Consumer
 | 
			
		||||
import com.maddyhome.idea.vim.KeyHandler
 | 
			
		||||
import com.maddyhome.idea.vim.VimPlugin
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.api.globalOptions
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
@@ -28,6 +27,7 @@ import com.maddyhome.idea.vim.common.EditorListener
 | 
			
		||||
import com.maddyhome.idea.vim.helper.EngineStringHelper
 | 
			
		||||
import com.maddyhome.idea.vim.helper.VimNlsSafe
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.ij
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.initInjector
 | 
			
		||||
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener
 | 
			
		||||
import org.jetbrains.annotations.NonNls
 | 
			
		||||
import java.awt.Component
 | 
			
		||||
@@ -72,6 +72,11 @@ internal object ShowCmdOptionChangeListener : GlobalOptionChangeListener {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
internal class ShowCmdStatusBarWidgetFactory : StatusBarWidgetFactory/*, LightEditCompatible*/ {
 | 
			
		||||
 | 
			
		||||
  init {
 | 
			
		||||
    initInjector()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun getId() = ShowCmd.ID
 | 
			
		||||
 | 
			
		||||
  override fun getDisplayName(): String = ShowCmd.displayName
 | 
			
		||||
@@ -81,7 +86,6 @@ internal class ShowCmdStatusBarWidgetFactory : StatusBarWidgetFactory/*, LightEd
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun isAvailable(project: Project): Boolean {
 | 
			
		||||
    VimPlugin.getInstance()
 | 
			
		||||
    return injector.globalOptions().showcmd
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -267,7 +267,7 @@ private object ShortcutConflictsSettings : DumbAwareAction(MessageHelper.message
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
internal object JoinEap : DumbAwareAction()/*, LightEditCompatible*/ {
 | 
			
		||||
  private const val EAP_LINK = "https://plugins.jetbrains.com/plugins/eap/ideavim"
 | 
			
		||||
  const val EAP_LINK = "https://plugins.jetbrains.com/plugins/eap/ideavim"
 | 
			
		||||
 | 
			
		||||
  fun eapActive() = EAP_LINK in UpdateSettings.getInstance().storedPluginHosts
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,263 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2003-2023 The IdeaVim authors
 | 
			
		||||
 *
 | 
			
		||||
 * Use of this source code is governed by an MIT-style
 | 
			
		||||
 * license that can be found in the LICENSE.txt file or at
 | 
			
		||||
 * https://opensource.org/licenses/MIT.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.maddyhome.idea.vim.ui.ex
 | 
			
		||||
 | 
			
		||||
import com.intellij.openapi.diagnostic.logger
 | 
			
		||||
import com.intellij.openapi.editor.textarea.TextComponentEditorImpl
 | 
			
		||||
import com.maddyhome.idea.vim.KeyHandler
 | 
			
		||||
import com.maddyhome.idea.vim.api.LocalOptionInitialisationScenario
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.vim
 | 
			
		||||
import java.awt.event.ActionEvent
 | 
			
		||||
import java.awt.event.KeyEvent
 | 
			
		||||
import javax.swing.Action
 | 
			
		||||
import javax.swing.KeyStroke
 | 
			
		||||
import javax.swing.text.BadLocationException
 | 
			
		||||
import javax.swing.text.DefaultEditorKit
 | 
			
		||||
import javax.swing.text.Document
 | 
			
		||||
import javax.swing.text.TextAction
 | 
			
		||||
import kotlin.math.abs
 | 
			
		||||
import kotlin.math.min
 | 
			
		||||
 | 
			
		||||
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
 | 
			
		||||
internal interface MultiStepAction : Action {
 | 
			
		||||
  fun reset()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
 | 
			
		||||
internal class HistoryUpAction : TextAction(ExEditorKit.HistoryUp) {
 | 
			
		||||
  override fun actionPerformed(actionEvent: ActionEvent) {
 | 
			
		||||
    val target = getTextComponent(actionEvent) as ExTextField
 | 
			
		||||
    target.selectHistory(true, false)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
 | 
			
		||||
internal class HistoryDownAction : TextAction(ExEditorKit.HistoryDown) {
 | 
			
		||||
  override fun actionPerformed(actionEvent: ActionEvent) {
 | 
			
		||||
    val target = getTextComponent(actionEvent) as ExTextField
 | 
			
		||||
    target.selectHistory(false, false)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
 | 
			
		||||
internal class HistoryUpFilterAction : TextAction(ExEditorKit.HistoryUpFilter) {
 | 
			
		||||
  override fun actionPerformed(actionEvent: ActionEvent) {
 | 
			
		||||
    val target = getTextComponent(actionEvent) as ExTextField
 | 
			
		||||
    target.selectHistory(true, true)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
 | 
			
		||||
internal class HistoryDownFilterAction : TextAction(ExEditorKit.HistoryDownFilter) {
 | 
			
		||||
  override fun actionPerformed(actionEvent: ActionEvent) {
 | 
			
		||||
    val target = getTextComponent(actionEvent) as ExTextField
 | 
			
		||||
    target.selectHistory(false, true)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
 | 
			
		||||
internal class CompleteEntryAction : TextAction(ExEditorKit.CompleteEntry) {
 | 
			
		||||
  override fun actionPerformed(actionEvent: ActionEvent) {
 | 
			
		||||
    logger.debug("complete entry")
 | 
			
		||||
    val stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)
 | 
			
		||||
 | 
			
		||||
    // We send the <Enter> keystroke through the key handler rather than calling ProcessGroup#processExEntry directly.
 | 
			
		||||
    // We do this for a couple of reasons:
 | 
			
		||||
    // * The C mode mapping for ProcessExEntryAction handles the actual entry, and most importantly, it does so as a
 | 
			
		||||
    //   write action
 | 
			
		||||
    // * The key handler routines get the chance to clean up and reset state
 | 
			
		||||
    val entry = ExEntryPanel.getInstance().entry
 | 
			
		||||
    val keyHandler = KeyHandler.getInstance()
 | 
			
		||||
    keyHandler.handleKey(entry.editor!!.vim, stroke, entry.context.vim, keyHandler.keyHandlerState)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  companion object {
 | 
			
		||||
    private val logger = logger<CompleteEntryAction>()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
 | 
			
		||||
internal class CancelEntryAction : TextAction(ExEditorKit.CancelEntry) {
 | 
			
		||||
  override fun actionPerformed(e: ActionEvent) {
 | 
			
		||||
    val target = getTextComponent(e) as ExTextField
 | 
			
		||||
    target.cancel()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
 | 
			
		||||
internal class EscapeCharAction : TextAction(ExEditorKit.EscapeChar) {
 | 
			
		||||
  override fun actionPerformed(e: ActionEvent) {
 | 
			
		||||
    val target = getTextComponent(e) as ExTextField
 | 
			
		||||
    target.escape()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
 | 
			
		||||
internal abstract class DeleteCharAction internal constructor(name: String?) : TextAction(name) {
 | 
			
		||||
  @kotlin.jvm.Throws(BadLocationException::class)
 | 
			
		||||
  fun deleteSelection(doc: Document, dot: Int, mark: Int): Boolean {
 | 
			
		||||
    if (dot != mark) {
 | 
			
		||||
      doc.remove(min(dot, mark), abs(dot - mark))
 | 
			
		||||
      return true
 | 
			
		||||
    }
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @kotlin.jvm.Throws(BadLocationException::class)
 | 
			
		||||
  fun deleteNextChar(doc: Document, dot: Int): Boolean {
 | 
			
		||||
    if (dot < doc.length) {
 | 
			
		||||
      var delChars = 1
 | 
			
		||||
      if (dot < doc.length - 1) {
 | 
			
		||||
        val dotChars = doc.getText(dot, 2)
 | 
			
		||||
        val c0 = dotChars[0]
 | 
			
		||||
        val c1 = dotChars[1]
 | 
			
		||||
        if (c0 in '\uD800'..'\uDBFF' && c1 in '\uDC00'..'\uDFFF') {
 | 
			
		||||
          delChars = 2
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      doc.remove(dot, delChars)
 | 
			
		||||
      return true
 | 
			
		||||
    }
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @kotlin.jvm.Throws(BadLocationException::class)
 | 
			
		||||
  fun deletePrevChar(doc: Document, dot: Int): Boolean {
 | 
			
		||||
    if (dot > 0) {
 | 
			
		||||
      var delChars = 1
 | 
			
		||||
      if (dot > 1) {
 | 
			
		||||
        val dotChars = doc.getText(dot - 2, 2)
 | 
			
		||||
        val c0 = dotChars[0]
 | 
			
		||||
        val c1 = dotChars[1]
 | 
			
		||||
        if (c0 in '\uD800'..'\uDBFF' && c1 in '\uDC00'..'\uDFFF') {
 | 
			
		||||
          delChars = 2
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      doc.remove(dot - delChars, delChars)
 | 
			
		||||
      return true
 | 
			
		||||
    }
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
 | 
			
		||||
internal class DeleteNextCharAction : DeleteCharAction(DefaultEditorKit.deleteNextCharAction) {
 | 
			
		||||
  override fun actionPerformed(e: ActionEvent) {
 | 
			
		||||
    val target = getTextComponent(e) as ExTextField
 | 
			
		||||
    target.saveLastEntry()
 | 
			
		||||
    try {
 | 
			
		||||
      val doc = target.document
 | 
			
		||||
      val caret = target.caret
 | 
			
		||||
      val dot = caret.dot
 | 
			
		||||
      val mark = caret.mark
 | 
			
		||||
      if (!deleteSelection(doc, dot, mark) && !deleteNextChar(doc, dot) && !deletePrevChar(doc, dot)) {
 | 
			
		||||
        target.cancel()
 | 
			
		||||
      }
 | 
			
		||||
    } catch (ex: BadLocationException) {
 | 
			
		||||
      // ignore
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
 | 
			
		||||
internal class DeletePreviousCharAction : DeleteCharAction(DefaultEditorKit.deletePrevCharAction) {
 | 
			
		||||
  override fun actionPerformed(e: ActionEvent) {
 | 
			
		||||
    val target = getTextComponent(e) as ExTextField
 | 
			
		||||
    target.saveLastEntry()
 | 
			
		||||
    try {
 | 
			
		||||
      val doc = target.document
 | 
			
		||||
      val caret = target.caret
 | 
			
		||||
      val dot = caret.dot
 | 
			
		||||
      val mark = caret.mark
 | 
			
		||||
      if (!deleteSelection(doc, dot, mark) && !deletePrevChar(doc, dot)) {
 | 
			
		||||
        if (dot == 0 && doc.length == 0) {
 | 
			
		||||
          target.cancel()
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } catch (bl: BadLocationException) {
 | 
			
		||||
      // ignore
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
 | 
			
		||||
internal class DeletePreviousWordAction : TextAction(DefaultEditorKit.deletePrevWordAction) {
 | 
			
		||||
  /**
 | 
			
		||||
   * Invoked when an action occurs.
 | 
			
		||||
   */
 | 
			
		||||
  override fun actionPerformed(e: ActionEvent) {
 | 
			
		||||
    val target = getTextComponent(e) as ExTextField
 | 
			
		||||
    target.saveLastEntry()
 | 
			
		||||
    val doc = target.document
 | 
			
		||||
    val caret = target.caret
 | 
			
		||||
    val project = target.editor!!.project
 | 
			
		||||
 | 
			
		||||
    // Create a VimEditor instance on the Swing text field which we can pass to the search helpers. We need an editor
 | 
			
		||||
    // rather than just working on a buffer because the search helpers need local options (specifically the local to
 | 
			
		||||
    // buffer 'iskeyword'). We use the CMD_LINE scenario to initialise local options from the main editor. The options
 | 
			
		||||
    // service will copy all local-to-buffer and local-to-window options, effectively cloning the options.
 | 
			
		||||
    // TODO: Over time, we should migrate all ex actions to be based on VimEditor
 | 
			
		||||
    // This will mean we always have an editor that has been initialised for options, etc. But also means that we can
 | 
			
		||||
    // share the command line entry actions between IdeaVim implementations
 | 
			
		||||
    val editor = TextComponentEditorImpl(project, target).vim
 | 
			
		||||
    injector.optionGroup.initialiseLocalOptions(editor, target.editor!!.vim, LocalOptionInitialisationScenario.CMD_LINE)
 | 
			
		||||
 | 
			
		||||
    val offset = injector.searchHelper.findNextWord(editor, caret.dot, -1, bigWord = false, spaceWords = false)
 | 
			
		||||
    if (logger.isDebugEnabled) logger.debug("offset=$offset")
 | 
			
		||||
    try {
 | 
			
		||||
      val pos = caret.dot
 | 
			
		||||
      doc.remove(offset, pos - offset)
 | 
			
		||||
    } catch (ex: BadLocationException) {
 | 
			
		||||
      // ignore
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  companion object {
 | 
			
		||||
    private val logger = logger<DeletePreviousWordAction>()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
 | 
			
		||||
internal class DeleteToCursorAction : TextAction(ExEditorKit.DeleteToCursor) {
 | 
			
		||||
  /**
 | 
			
		||||
   * Invoked when an action occurs.
 | 
			
		||||
   */
 | 
			
		||||
  override fun actionPerformed(e: ActionEvent) {
 | 
			
		||||
    val target = getTextComponent(e) as ExTextField
 | 
			
		||||
    target.saveLastEntry()
 | 
			
		||||
    val doc = target.document
 | 
			
		||||
    val caret = target.caret
 | 
			
		||||
    try {
 | 
			
		||||
      doc.remove(0, caret.dot)
 | 
			
		||||
    } catch (ex: BadLocationException) {
 | 
			
		||||
      // ignore
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
 | 
			
		||||
internal class ToggleInsertReplaceAction : TextAction(ExEditorKit.ToggleInsertReplace) {
 | 
			
		||||
  /**
 | 
			
		||||
   * Invoked when an action occurs.
 | 
			
		||||
   */
 | 
			
		||||
  override fun actionPerformed(e: ActionEvent) {
 | 
			
		||||
    logger.debug("actionPerformed")
 | 
			
		||||
    val target = getTextComponent(e) as ExTextField
 | 
			
		||||
    target.toggleInsertReplace()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  init {
 | 
			
		||||
    logger.debug("ToggleInsertReplaceAction()")
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  companion object {
 | 
			
		||||
    private val logger = logger<ToggleInsertReplaceAction>()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -34,7 +34,7 @@ public class ExDocument extends PlainDocument {
 | 
			
		||||
  void toggleInsertReplace() {
 | 
			
		||||
    VimCommandLine commandLine = injector.getCommandLine().getActiveCommandLine();
 | 
			
		||||
    if (commandLine != null) {
 | 
			
		||||
      commandLine.toggleReplaceMode();
 | 
			
		||||
      ((ExEntryPanel) commandLine).isReplaceMode = !((ExEntryPanel)commandLine).isReplaceMode;
 | 
			
		||||
    }
 | 
			
		||||
    overwrite = !overwrite;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -7,57 +7,19 @@
 | 
			
		||||
 */
 | 
			
		||||
package com.maddyhome.idea.vim.ui.ex
 | 
			
		||||
 | 
			
		||||
import com.intellij.openapi.diagnostic.debug
 | 
			
		||||
import com.intellij.openapi.diagnostic.logger
 | 
			
		||||
import com.maddyhome.idea.vim.KeyHandler
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.vim
 | 
			
		||||
import org.jetbrains.annotations.NonNls
 | 
			
		||||
import java.awt.event.ActionEvent
 | 
			
		||||
import java.awt.event.KeyEvent
 | 
			
		||||
import javax.swing.Action
 | 
			
		||||
import javax.swing.KeyStroke
 | 
			
		||||
import javax.swing.text.DefaultEditorKit
 | 
			
		||||
import javax.swing.text.Document
 | 
			
		||||
import javax.swing.text.TextAction
 | 
			
		||||
 | 
			
		||||
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
 | 
			
		||||
internal object ExEditorKit : DefaultEditorKit() {
 | 
			
		||||
 | 
			
		||||
  @NonNls
 | 
			
		||||
  val CancelEntry: String = "cancel-entry"
 | 
			
		||||
 | 
			
		||||
  @NonNls
 | 
			
		||||
  val CompleteEntry: String = "complete-entry"
 | 
			
		||||
 | 
			
		||||
  @NonNls
 | 
			
		||||
  val EscapeChar: String = "escape"
 | 
			
		||||
 | 
			
		||||
  @NonNls
 | 
			
		||||
  val DeleteToCursor: String = "delete-to-cursor"
 | 
			
		||||
 | 
			
		||||
  @NonNls
 | 
			
		||||
  val ToggleInsertReplace: String = "toggle-insert"
 | 
			
		||||
 | 
			
		||||
  @NonNls
 | 
			
		||||
  val HistoryUp: String = "history-up"
 | 
			
		||||
 | 
			
		||||
  @NonNls
 | 
			
		||||
  val HistoryDown: String = "history-down"
 | 
			
		||||
 | 
			
		||||
  @NonNls
 | 
			
		||||
  val HistoryUpFilter: String = "history-up-filter"
 | 
			
		||||
 | 
			
		||||
  @NonNls
 | 
			
		||||
  val HistoryDownFilter: String = "history-down-filter"
 | 
			
		||||
 | 
			
		||||
  @NonNls
 | 
			
		||||
  val StartDigraph: String = "start-digraph"
 | 
			
		||||
 | 
			
		||||
  @NonNls
 | 
			
		||||
  val StartLiteral: String = "start-literal"
 | 
			
		||||
 | 
			
		||||
  private val logger = logger<ExEditorKit>()
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Gets the MIME type of the data that this
 | 
			
		||||
   * kit represents support for.
 | 
			
		||||
@@ -69,19 +31,6 @@ internal object ExEditorKit : DefaultEditorKit() {
 | 
			
		||||
    return "text/ideavim"
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Fetches the set of commands that can be used
 | 
			
		||||
   * on a text component that is using a model and
 | 
			
		||||
   * view produced by this kit.
 | 
			
		||||
   *
 | 
			
		||||
   * @return the set of actions
 | 
			
		||||
   */
 | 
			
		||||
  override fun getActions(): Array<Action> {
 | 
			
		||||
    val res = TextAction.augmentList(super.getActions(), exActions)
 | 
			
		||||
    logger.debug { "res.length=${res.size}" }
 | 
			
		||||
    return res
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates an uninitialized text storage model
 | 
			
		||||
   * that is appropriate for this type of editor.
 | 
			
		||||
@@ -92,34 +41,17 @@ internal object ExEditorKit : DefaultEditorKit() {
 | 
			
		||||
    return ExDocument()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private val exActions = arrayOf<Action>(
 | 
			
		||||
    CancelEntryAction(),
 | 
			
		||||
    CompleteEntryAction(),
 | 
			
		||||
    EscapeCharAction(),
 | 
			
		||||
    DeleteNextCharAction(),
 | 
			
		||||
    DeletePreviousCharAction(),
 | 
			
		||||
    DeletePreviousWordAction(),
 | 
			
		||||
    DeleteToCursorAction(),
 | 
			
		||||
    HistoryUpAction(),
 | 
			
		||||
    HistoryDownAction(),
 | 
			
		||||
    HistoryUpFilterAction(),
 | 
			
		||||
    HistoryDownFilterAction(),
 | 
			
		||||
    ToggleInsertReplaceAction(),
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  class DefaultExKeyHandler : DefaultKeyTypedAction() {
 | 
			
		||||
    override fun actionPerformed(e: ActionEvent) {
 | 
			
		||||
      val target = getTextComponent(e) as ExTextField
 | 
			
		||||
      val currentAction = target.currentAction
 | 
			
		||||
      if (currentAction != null) {
 | 
			
		||||
        currentAction.actionPerformed(e)
 | 
			
		||||
      } else {
 | 
			
		||||
 | 
			
		||||
      val key = convert(e)
 | 
			
		||||
      if (key != null) {
 | 
			
		||||
        val c = key.keyChar
 | 
			
		||||
        if (c.code > 0) {
 | 
			
		||||
          if (target.useHandleKeyFromEx) {
 | 
			
		||||
              val entry = ExEntryPanel.getInstance().entry
 | 
			
		||||
            val panel = ((injector.commandLine.getActiveCommandLine() as? ExEntryPanel) ?: (injector.modalInput.getCurrentModalInput() as? WrappedAsModalInputExEntryPanel)?.exEntryPanel) ?: return
 | 
			
		||||
            val entry = panel.entry
 | 
			
		||||
            val editor = entry.editor
 | 
			
		||||
            val keyHandler = KeyHandler.getInstance()
 | 
			
		||||
            keyHandler.handleKey(editor!!.vim, key, entry.context.vim, keyHandler.keyHandlerState)
 | 
			
		||||
@@ -135,7 +67,6 @@ internal object ExEditorKit : DefaultEditorKit() {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun convert(event: ActionEvent): KeyStroke? {
 | 
			
		||||
    val cmd = event.actionCommand
 | 
			
		||||
 
 | 
			
		||||
@@ -25,10 +25,12 @@ import com.maddyhome.idea.vim.VimPlugin;
 | 
			
		||||
import com.maddyhome.idea.vim.action.VimShortcutKeyAction;
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimCommandLine;
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimCommandLineCaret;
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor;
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimKeyGroupBase;
 | 
			
		||||
import com.maddyhome.idea.vim.ex.ranges.LineRange;
 | 
			
		||||
import com.maddyhome.idea.vim.helper.SearchHighlightsHelper;
 | 
			
		||||
import com.maddyhome.idea.vim.helper.UiHelper;
 | 
			
		||||
import com.maddyhome.idea.vim.key.interceptors.VimInputInterceptor;
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimCaret;
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
			
		||||
import com.maddyhome.idea.vim.ui.ExPanelBorder;
 | 
			
		||||
@@ -36,6 +38,8 @@ import com.maddyhome.idea.vim.vimscript.model.commands.Command;
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.commands.GlobalCommand;
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.commands.SubstituteCommand;
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.parser.VimscriptParser;
 | 
			
		||||
import kotlin.Unit;
 | 
			
		||||
import kotlin.jvm.functions.Function1;
 | 
			
		||||
import org.jetbrains.annotations.Contract;
 | 
			
		||||
import org.jetbrains.annotations.NotNull;
 | 
			
		||||
import org.jetbrains.annotations.Nullable;
 | 
			
		||||
@@ -59,9 +63,13 @@ import static com.maddyhome.idea.vim.group.KeyGroup.toShortcutSet;
 | 
			
		||||
public class ExEntryPanel extends JPanel implements VimCommandLine {
 | 
			
		||||
  public static ExEntryPanel instance;
 | 
			
		||||
  public static ExEntryPanel instanceWithoutShortcuts;
 | 
			
		||||
  private boolean isReplaceMode = false;
 | 
			
		||||
  public boolean isReplaceMode = false;
 | 
			
		||||
  private WeakReference<Editor> weakEditor = null;
 | 
			
		||||
 | 
			
		||||
  private VimInputInterceptor myInputInterceptor = null;
 | 
			
		||||
  public Function1<String, Unit> inputProcessing = null;
 | 
			
		||||
  public Character finishOn = null;
 | 
			
		||||
 | 
			
		||||
  private ExEntryPanel(boolean enableShortcuts) {
 | 
			
		||||
    label = new JLabel(" ");
 | 
			
		||||
    entry = new ExTextField();
 | 
			
		||||
@@ -79,7 +87,6 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
 | 
			
		||||
    layout.setConstraints(entry, gbc);
 | 
			
		||||
    add(entry);
 | 
			
		||||
 | 
			
		||||
    if (enableShortcuts) {
 | 
			
		||||
    // This does not need to be unregistered, it's registered as a custom UI property on this
 | 
			
		||||
    EventFacade.getInstance().registerCustomShortcutSet(
 | 
			
		||||
      VimShortcutKeyAction.getInstance(),
 | 
			
		||||
@@ -87,7 +94,6 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
 | 
			
		||||
      entry
 | 
			
		||||
    );
 | 
			
		||||
    new ExShortcutKeyAction(this).registerCustomShortcutSet();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    updateUI();
 | 
			
		||||
  }
 | 
			
		||||
@@ -127,10 +133,18 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public @Nullable Editor getEditor() {
 | 
			
		||||
  public @Nullable Editor getIjEditor() {
 | 
			
		||||
    return weakEditor != null ? weakEditor.get() : null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public @NotNull VimEditor getEditor() {
 | 
			
		||||
    Editor editor = getIjEditor();
 | 
			
		||||
    if (editor == null) {
 | 
			
		||||
      throw new RuntimeException("Editor was disposed for active command line");
 | 
			
		||||
    }
 | 
			
		||||
    return new IjVimEditor(editor);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void setEditor(@Nullable Editor editor) {
 | 
			
		||||
    if (editor == null) {
 | 
			
		||||
      weakEditor = null;
 | 
			
		||||
@@ -139,6 +153,14 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @deprecated use {@link ExEntryPanel#activate(Editor, DataContext, String, String)}
 | 
			
		||||
   */
 | 
			
		||||
  @Deprecated(forRemoval = true)
 | 
			
		||||
  public void activate(@NotNull Editor editor, DataContext context, @NotNull String label, String initText, int count) {
 | 
			
		||||
    activate(editor, context, label, initText);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Turns on the ex entry field for the given editor
 | 
			
		||||
   *
 | 
			
		||||
@@ -249,6 +271,9 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
 | 
			
		||||
 | 
			
		||||
    // We have this in the end, because `entry.deactivate()` communicates with active panel during deactivation
 | 
			
		||||
    active = false;
 | 
			
		||||
    finishOn = null;
 | 
			
		||||
    myInputInterceptor = null;
 | 
			
		||||
    inputProcessing = null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void reset() {
 | 
			
		||||
@@ -273,7 +298,7 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void textChanged(@NotNull DocumentEvent e) {
 | 
			
		||||
      String text = entry.getText();
 | 
			
		||||
      Font newFont = UiHelper.selectEditorFont(getEditor(), text);
 | 
			
		||||
      Font newFont = UiHelper.selectEditorFont(getIjEditor(), text);
 | 
			
		||||
      if (newFont != entry.getFont()) {
 | 
			
		||||
        entry.setFont(newFont);
 | 
			
		||||
      }
 | 
			
		||||
@@ -326,7 +351,7 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
 | 
			
		||||
        // coerced to at least 1.
 | 
			
		||||
        int count1 = KeyHandler.getInstance().getKeyHandlerState().getEditorCommandBuilder().getAggregatedUncommittedCount();
 | 
			
		||||
 | 
			
		||||
        if (labelText.equals("/") || labelText.equals("?") || searchCommand) {
 | 
			
		||||
        if ((labelText.equals("/") || labelText.equals("?") || searchCommand) && !injector.getMacro().isExecutingMacro()) {
 | 
			
		||||
          final boolean forwards = !labelText.equals("?");  // :s, :g, :v are treated as forwards
 | 
			
		||||
          int pattenEnd = injector.getSearchGroup().findEndOfPattern(searchText, separator, 0);
 | 
			
		||||
          final String pattern = searchText.substring(0, pattenEnd);
 | 
			
		||||
@@ -380,7 +405,7 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void toggleReplaceMode() {
 | 
			
		||||
    isReplaceMode = !isReplaceMode;
 | 
			
		||||
    entry.toggleInsertReplace();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@@ -392,6 +417,14 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
 | 
			
		||||
    return active;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @deprecated Use getVisibleText()
 | 
			
		||||
   */
 | 
			
		||||
  @Deprecated(forRemoval = true)
 | 
			
		||||
  public @NotNull String getText() {
 | 
			
		||||
    return entry.getText();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public @NotNull String getVisibleText() {
 | 
			
		||||
    return entry.getText();
 | 
			
		||||
@@ -409,6 +442,10 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
 | 
			
		||||
  @Override
 | 
			
		||||
  public void handleKey(@NotNull KeyStroke stroke) {
 | 
			
		||||
    entry.handleKey(stroke);
 | 
			
		||||
    if (finishOn != null && stroke.getKeyChar() == finishOn && inputProcessing != null) {
 | 
			
		||||
      inputProcessing.invoke(getActualText());
 | 
			
		||||
      close(true, true);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Called automatically when the LAF is changed and the component is visible, and manually by the LAF listener handler
 | 
			
		||||
@@ -446,8 +483,8 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void setFontForElements() {
 | 
			
		||||
    label.setFont(UiHelper.selectEditorFont(getEditor(), label.getText()));
 | 
			
		||||
    entry.setFont(UiHelper.selectEditorFont(getEditor(), getVisibleText()));
 | 
			
		||||
    label.setFont(UiHelper.selectEditorFont(getIjEditor(), label.getText()));
 | 
			
		||||
    entry.setFont(UiHelper.selectEditorFont(getIjEditor(), getVisibleText()));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void positionPanel() {
 | 
			
		||||
@@ -501,10 +538,11 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void setText(@NotNull String string) {
 | 
			
		||||
  public void setText(@NotNull String string, boolean updateLastEntry) {
 | 
			
		||||
    // It's a feature of Swing that caret is moved when we set new text. However, our API is Swing independent and we do not expect this
 | 
			
		||||
    int offset = getCaret().getOffset();
 | 
			
		||||
    entry.updateText(string);
 | 
			
		||||
    if (updateLastEntry) entry.saveLastEntry();
 | 
			
		||||
    getCaret().setOffset(Math.min(offset, getVisibleText().length()));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -535,6 +573,53 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
 | 
			
		||||
    IdeFocusManager.findInstance().requestFocus(entry, true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Nullable
 | 
			
		||||
  public VimInputInterceptor<?> getInputInterceptor() {
 | 
			
		||||
    return myInputInterceptor;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void insertText(int offset, @NotNull String string) {
 | 
			
		||||
    VimCommandLine.super.insertText(offset, string);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void setInputInterceptor(@Nullable VimInputInterceptor<?> vimInputInterceptor) {
 | 
			
		||||
    myInputInterceptor = vimInputInterceptor;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Nullable
 | 
			
		||||
  @Override
 | 
			
		||||
  public Function1<String, Unit> getInputProcessing() {
 | 
			
		||||
    return inputProcessing;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Nullable
 | 
			
		||||
  @Override
 | 
			
		||||
  public Character getFinishOn() {
 | 
			
		||||
    return finishOn;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public int getHistIndex() {
 | 
			
		||||
    return entry.histIndex;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void setHistIndex(int i) {
 | 
			
		||||
    entry.histIndex = i;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @NotNull
 | 
			
		||||
  @Override
 | 
			
		||||
  public String getLastEntry() {
 | 
			
		||||
    return entry.lastEntry;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void setLastEntry(@NotNull String s) {
 | 
			
		||||
    entry.lastEntry = s;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static class LafListener implements LafManagerListener {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void lookAndFeelChanged(@NotNull LafManager source) {
 | 
			
		||||
 
 | 
			
		||||
@@ -11,25 +11,33 @@ import com.intellij.openapi.application.ApplicationManager
 | 
			
		||||
import com.maddyhome.idea.vim.action.change.Extension
 | 
			
		||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimCommandLine
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimCommandLineService
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimCommandLineCaret
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimCommandLineServiceBase
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimModalInput
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimModalInputBase
 | 
			
		||||
import com.maddyhome.idea.vim.api.VimModalInputService
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.helper.TestInputModel
 | 
			
		||||
import com.maddyhome.idea.vim.helper.inRepeatMode
 | 
			
		||||
import com.maddyhome.idea.vim.helper.isCloseKeyStroke
 | 
			
		||||
import com.maddyhome.idea.vim.key.interceptors.VimInputInterceptor
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.ij
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.vim
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
			
		||||
import com.maddyhome.idea.vim.state.mode.ReturnableFromCmd
 | 
			
		||||
import com.maddyhome.idea.vim.ui.ModalEntry
 | 
			
		||||
import java.awt.event.KeyEvent
 | 
			
		||||
import javax.swing.KeyStroke
 | 
			
		||||
 | 
			
		||||
class ExEntryPanelService : VimCommandLineService {
 | 
			
		||||
class ExEntryPanelService : VimCommandLineServiceBase(), VimModalInputService {
 | 
			
		||||
  override fun getActiveCommandLine(): VimCommandLine? {
 | 
			
		||||
    val instance = ExEntryPanel.instance ?: ExEntryPanel.instanceWithoutShortcuts ?: return null
 | 
			
		||||
    val instance = ExEntryPanel.instance ?: return null
 | 
			
		||||
    return if (instance.isActive) instance else null
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun inputString(vimEditor: VimEditor, context: ExecutionContext, prompt: String, finishOn: Char?): String? {
 | 
			
		||||
  @Deprecated("Please use readInputAndProcess")
 | 
			
		||||
  fun inputString(vimEditor: VimEditor, context: ExecutionContext, prompt: String, finishOn: Char?): String? {
 | 
			
		||||
    val editor = vimEditor.ij
 | 
			
		||||
    if (vimEditor.inRepeatMode) {
 | 
			
		||||
      val input = Extension.consumeString()
 | 
			
		||||
@@ -58,7 +66,7 @@ class ExEntryPanelService : VimCommandLineService {
 | 
			
		||||
    } else {
 | 
			
		||||
      var text: String? = null
 | 
			
		||||
      // XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for input()
 | 
			
		||||
      val commandLine = injector.commandLine.create(vimEditor, context, prompt.ifEmpty { " " }, "")
 | 
			
		||||
      val commandLine = injector.commandLine.createSearchPrompt(vimEditor, context, prompt.ifEmpty { " " }, "")
 | 
			
		||||
      ModalEntry.activate(editor.vim) { key: KeyStroke ->
 | 
			
		||||
        return@activate when {
 | 
			
		||||
          key.isCloseKeyStroke() -> {
 | 
			
		||||
@@ -89,7 +97,40 @@ class ExEntryPanelService : VimCommandLineService {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun create(editor: VimEditor, context: ExecutionContext, label: String, initText: String): VimCommandLine {
 | 
			
		||||
  override fun readInputAndProcess(
 | 
			
		||||
    editor: VimEditor,
 | 
			
		||||
    context: ExecutionContext,
 | 
			
		||||
    prompt: String,
 | 
			
		||||
    finishOn: Char?,
 | 
			
		||||
    processing: (String) -> Unit,
 | 
			
		||||
  ) {
 | 
			
		||||
    val currentMode = editor.mode
 | 
			
		||||
    check(currentMode is ReturnableFromCmd) {
 | 
			
		||||
      "Cannot enable cmd mode from current mode $currentMode"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Make sure the Visual selection marks are up to date before we use them.
 | 
			
		||||
    injector.markService.setVisualSelectionMarks(editor)
 | 
			
		||||
 | 
			
		||||
    // Note that we should remove selection and reset caret offset before we switch back to Normal mode and then enter
 | 
			
		||||
    // Command-line mode. However, some IdeaVim commands can handle multiple carets, including multiple carets with
 | 
			
		||||
    // selection (which might or might not be a block selection). Unfortunately, because we're just entering
 | 
			
		||||
    // Command-line mode, we don't know which command is going to be entered, so we can't remove selection here.
 | 
			
		||||
    // Therefore, we switch to Normal and then Command-line even though we might still have a Visual selection...
 | 
			
		||||
    // On the plus side, it means we still show selection while editing the command line, which Vim also does
 | 
			
		||||
    // (Normal then Command-line is not strictly necessary, but done for completeness and autocmd)
 | 
			
		||||
    // Caret selection is finally handled in Command.execute
 | 
			
		||||
    editor.mode = Mode.NORMAL()
 | 
			
		||||
    editor.mode = Mode.CMD_LINE(currentMode)
 | 
			
		||||
 | 
			
		||||
    val panel = ExEntryPanel.getInstance()
 | 
			
		||||
    panel as ExEntryPanel
 | 
			
		||||
    panel.finishOn = finishOn
 | 
			
		||||
    panel.inputProcessing = processing
 | 
			
		||||
    panel.activate(editor.ij, context.ij, prompt, "")
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun createPanel(editor: VimEditor, context: ExecutionContext, label: String, initText: String): VimCommandLine {
 | 
			
		||||
    val panel = ExEntryPanel.getInstance()
 | 
			
		||||
    panel.activate(editor.ij, context.ij, label, initText)
 | 
			
		||||
    return panel
 | 
			
		||||
@@ -104,4 +145,31 @@ class ExEntryPanelService : VimCommandLineService {
 | 
			
		||||
  override fun fullReset() {
 | 
			
		||||
    ExEntryPanel.fullReset()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun getCurrentModalInput(): VimModalInput? {
 | 
			
		||||
    return ExEntryPanel.getInstanceWithoutShortcuts()?.takeIf { it.isActive && it.inputInterceptor != null }?.let { WrappedAsModalInputExEntryPanel(it) }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun create(editor: VimEditor, context: ExecutionContext, label: String, inputInterceptor: VimInputInterceptor<*>): VimModalInput {
 | 
			
		||||
    val panel = ExEntryPanel.getInstanceWithoutShortcuts()
 | 
			
		||||
    panel.inputInterceptor = inputInterceptor
 | 
			
		||||
    panel.activate(editor.ij, context.ij, label, "")
 | 
			
		||||
    return WrappedAsModalInputExEntryPanel(panel)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
internal class WrappedAsModalInputExEntryPanel(internal val exEntryPanel: ExEntryPanel) : VimModalInputBase() {
 | 
			
		||||
  override var inputInterceptor: VimInputInterceptor<*>
 | 
			
		||||
    get() = exEntryPanel.inputInterceptor!!
 | 
			
		||||
    set(value) { exEntryPanel.inputInterceptor = value }
 | 
			
		||||
  override val caret: VimCommandLineCaret = exEntryPanel.caret
 | 
			
		||||
  override val label: String = exEntryPanel.label
 | 
			
		||||
 | 
			
		||||
  override fun deactivate(refocusOwningEditor: Boolean, resetCaret: Boolean) {
 | 
			
		||||
    exEntryPanel.deactivate(refocusOwningEditor, resetCaret)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun focus() {
 | 
			
		||||
    exEntryPanel.focus()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -21,57 +21,6 @@ internal object ExKeyBindings {
 | 
			
		||||
 | 
			
		||||
  val bindings: Array<KeyBinding> by lazy {
 | 
			
		||||
    arrayOf(
 | 
			
		||||
      // Escape will cancel a pending insert digraph/register before cancelling
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), ExEditorKit.EscapeChar),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.EscapeChar),
 | 
			
		||||
 | 
			
		||||
      // Cancel entry, ignoring any pending actions such as digraph/registry entry
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.CancelEntry),
 | 
			
		||||
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), ExEditorKit.CompleteEntry),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_J, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.CompleteEntry),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_M, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.CompleteEntry),
 | 
			
		||||
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_B, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.beginLineAction),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(0x02.toChar().code, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.beginLineAction),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0), DefaultEditorKit.beginLineAction),
 | 
			
		||||
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_E, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.endLineAction),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(0x05.toChar().code, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.endLineAction),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0), DefaultEditorKit.endLineAction),
 | 
			
		||||
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), DefaultEditorKit.deletePrevCharAction),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.deletePrevCharAction),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(0x08.toChar().code, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.deletePrevCharAction),
 | 
			
		||||
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), DefaultEditorKit.deleteNextCharAction),
 | 
			
		||||
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.deletePrevWordAction),
 | 
			
		||||
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.DeleteToCursor),
 | 
			
		||||
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), ExEditorKit.HistoryUpFilter),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.SHIFT_DOWN_MASK), ExEditorKit.HistoryUp),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), ExEditorKit.HistoryUp),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.HistoryUp),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), ExEditorKit.HistoryDownFilter),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_DOWN_MASK), ExEditorKit.HistoryDown),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), ExEditorKit.HistoryDown),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.HistoryDown),
 | 
			
		||||
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0), ExEditorKit.ToggleInsertReplace),
 | 
			
		||||
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), DefaultEditorKit.backwardAction),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.SHIFT_DOWN_MASK), DefaultEditorKit.previousWordAction),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.previousWordAction),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), DefaultEditorKit.forwardAction),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.SHIFT_DOWN_MASK), DefaultEditorKit.nextWordAction),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.nextWordAction),
 | 
			
		||||
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_K, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.StartDigraph),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.StartLiteral),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_Q, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.StartLiteral),
 | 
			
		||||
 | 
			
		||||
      // These appear to be non-Vim shortcuts
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.META_DOWN_MASK), DefaultEditorKit.pasteAction),
 | 
			
		||||
      KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, KeyEvent.SHIFT_DOWN_MASK), DefaultEditorKit.pasteAction),
 | 
			
		||||
 
 | 
			
		||||
@@ -59,9 +59,45 @@ internal class ExShortcutKeyAction(private val exEntryPanel: ExEntryPanel) : Dum
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun registerCustomShortcutSet() {
 | 
			
		||||
    val shortcuts = ExKeyBindings.bindings.map {
 | 
			
		||||
      KeyboardShortcut(it.key, null)
 | 
			
		||||
    }.toTypedArray()
 | 
			
		||||
    val shortcuts = listOf(
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, KeyEvent.CTRL_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_J, KeyEvent.CTRL_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_M, KeyEvent.CTRL_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_B, KeyEvent.CTRL_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_E, KeyEvent.CTRL_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_END, 0),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.SHIFT_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.CTRL_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.SHIFT_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.CTRL_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.SHIFT_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.CTRL_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_K, KeyEvent.CTRL_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.CTRL_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_Q, KeyEvent.CTRL_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.META_DOWN_MASK),
 | 
			
		||||
        KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, KeyEvent.SHIFT_DOWN_MASK),
 | 
			
		||||
    )
 | 
			
		||||
      .map { KeyboardShortcut(it, null) }
 | 
			
		||||
      .toTypedArray()
 | 
			
		||||
 | 
			
		||||
    registerCustomShortcutSet({ shortcuts }, exEntryPanel)
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,6 @@ import com.maddyhome.idea.vim.api.VimCommandLineCaret;
 | 
			
		||||
import com.maddyhome.idea.vim.helper.UiHelper;
 | 
			
		||||
import com.maddyhome.idea.vim.history.HistoryConstants;
 | 
			
		||||
import com.maddyhome.idea.vim.history.HistoryEntry;
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
			
		||||
import com.maddyhome.idea.vim.options.helpers.GuiCursorAttributes;
 | 
			
		||||
import com.maddyhome.idea.vim.options.helpers.GuiCursorMode;
 | 
			
		||||
import com.maddyhome.idea.vim.options.helpers.GuiCursorOptionHelper;
 | 
			
		||||
@@ -66,9 +65,6 @@ public class ExTextField extends JTextField {
 | 
			
		||||
        // If we're in the middle of an action (e.g. entering a register to paste, or inserting a digraph), cancel it if
 | 
			
		||||
        // the mouse is clicked anywhere. Vim's behavior is to use the mouse click as an event, which can lead to
 | 
			
		||||
        // something like : !%!C, which I don't believe is documented, or useful
 | 
			
		||||
        if (currentAction != null) {
 | 
			
		||||
          clearCurrentAction();
 | 
			
		||||
        }
 | 
			
		||||
        super.mouseClicked(e);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
@@ -211,7 +207,7 @@ public class ExTextField extends JTextField {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public @Nullable Editor getEditor() {
 | 
			
		||||
    return ExEntryPanel.getInstance().getEditor();
 | 
			
		||||
    return ExEntryPanel.getInstance().getIjEditor();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public DataContext getContext() {
 | 
			
		||||
@@ -239,10 +235,6 @@ public class ExTextField extends JTextField {
 | 
			
		||||
    // This gets called for ALL events, before the IDE starts to process key events for the action system. We can add a
 | 
			
		||||
    // dispatcher that checks that the plugin is enabled, checks that the component with the focus is ExTextField,
 | 
			
		||||
    // dispatch to ExEntryPanel#handleKey and if it's processed, mark the event as consumed.
 | 
			
		||||
    if (currentAction != null) {
 | 
			
		||||
      currentAction.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, String.valueOf(c), modifiers));
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
    KeyEvent event = new KeyEvent(this, keyChar != KeyEvent.CHAR_UNDEFINED ? KeyEvent.KEY_TYPED :
 | 
			
		||||
                                        (stroke.isOnKeyRelease() ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED),
 | 
			
		||||
                                  (new Date()).getTime(), modifiers, keyCode, c);
 | 
			
		||||
@@ -254,7 +246,6 @@ public class ExTextField extends JTextField {
 | 
			
		||||
      useHandleKeyFromEx = true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  protected void processKeyEvent(KeyEvent e) {
 | 
			
		||||
@@ -278,39 +269,25 @@ public class ExTextField extends JTextField {
 | 
			
		||||
   * Cancels current action, if there is one. If not, cancels entry.
 | 
			
		||||
   */
 | 
			
		||||
  void escape() {
 | 
			
		||||
    if (currentAction != null) {
 | 
			
		||||
      clearCurrentAction();
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
    cancel();
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Cancels entry, including any current action.
 | 
			
		||||
   */
 | 
			
		||||
  void cancel() {
 | 
			
		||||
    clearCurrentAction();
 | 
			
		||||
    Editor editor = ExEntryPanel.instance.getEditor();
 | 
			
		||||
    if (editor != null) {
 | 
			
		||||
      VimPlugin.getProcess().cancelExEntry(new IjVimEditor(editor), true, true);
 | 
			
		||||
    VimCommandLine commandLine = injector.getCommandLine().getActiveCommandLine();
 | 
			
		||||
    if (commandLine != null) {
 | 
			
		||||
      commandLine.close(true, true);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void clearCurrentAction() {
 | 
			
		||||
    if (currentAction != null) {
 | 
			
		||||
      currentAction.reset();
 | 
			
		||||
    }
 | 
			
		||||
    currentAction = null;
 | 
			
		||||
    VimCommandLine commandLine = injector.getCommandLine().getActiveCommandLine();
 | 
			
		||||
    if (commandLine != null) commandLine.clearPromptCharacter();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Nullable
 | 
			
		||||
  Action getCurrentAction() {
 | 
			
		||||
    return currentAction;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void setInsertMode() {
 | 
			
		||||
    ExDocument doc = (ExDocument)getDocument();
 | 
			
		||||
    if (doc.isOverwrite()) {
 | 
			
		||||
@@ -541,10 +518,9 @@ public class ExTextField extends JTextField {
 | 
			
		||||
 | 
			
		||||
  private DataContext context;
 | 
			
		||||
  private final CommandLineCaret caret;
 | 
			
		||||
  private String lastEntry;
 | 
			
		||||
  String lastEntry;
 | 
			
		||||
  private List<HistoryEntry> history;
 | 
			
		||||
  private int histIndex = 0;
 | 
			
		||||
  private @Nullable MultiStepAction currentAction;
 | 
			
		||||
  int histIndex = 0;
 | 
			
		||||
  int currentActionPromptCharacterOffset = -1;
 | 
			
		||||
 | 
			
		||||
  private static final Logger logger = Logger.getInstance(ExTextField.class.getName());
 | 
			
		||||
 
 | 
			
		||||
@@ -352,7 +352,8 @@ class ModeWidgetPopup : AnAction() {
 | 
			
		||||
 | 
			
		||||
enum class ModeWidgetTheme(private var value: String) {
 | 
			
		||||
  TERM("Term"),
 | 
			
		||||
  COLORLESS("Colorless");
 | 
			
		||||
  COLORLESS("Colorless"),
 | 
			
		||||
  DRACULA("Dracula");
 | 
			
		||||
 | 
			
		||||
  override fun toString(): String {
 | 
			
		||||
    return value
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,16 @@ fun getModeBackground(mode: Mode?): Color {
 | 
			
		||||
          is Mode.OP_PENDING, null -> UIUtil.getPanelBackground()
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      ModeWidgetTheme.DRACULA -> {
 | 
			
		||||
        return when (mode) {
 | 
			
		||||
          Mode.INSERT -> Color.decode("#50FA7B")
 | 
			
		||||
          Mode.REPLACE -> Color.decode("#FF5555")
 | 
			
		||||
          is Mode.NORMAL -> Color.decode("#BD93F9")
 | 
			
		||||
          is Mode.CMD_LINE -> Color.decode("#FFB86C")
 | 
			
		||||
          is Mode.VISUAL, is Mode.SELECT -> Color.decode("#F1FA8C")
 | 
			
		||||
          is Mode.OP_PENDING, null -> UIUtil.getPanelBackground()
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      ModeWidgetTheme.COLORLESS -> {
 | 
			
		||||
        return UIUtil.getPanelBackground()
 | 
			
		||||
      }
 | 
			
		||||
@@ -83,6 +93,7 @@ fun getModeForeground(mode: Mode?): Color {
 | 
			
		||||
    val theme = ModeWidgetTheme.parseString(themeString) ?: ModeWidgetTheme.getDefaultTheme()
 | 
			
		||||
    return when (theme) {
 | 
			
		||||
      ModeWidgetTheme.TERM -> if (isLight) Color.WHITE else Color.BLACK
 | 
			
		||||
      ModeWidgetTheme.DRACULA -> Color.BLACK
 | 
			
		||||
      ModeWidgetTheme.COLORLESS -> UIUtil.getLabelForeground()
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -8,114 +8,20 @@
 | 
			
		||||
 | 
			
		||||
package com.maddyhome.idea.vim.vimscript
 | 
			
		||||
 | 
			
		||||
import com.intellij.openapi.actionSystem.DataContext
 | 
			
		||||
import com.intellij.openapi.components.Service
 | 
			
		||||
import com.intellij.openapi.diagnostic.logger
 | 
			
		||||
import com.intellij.openapi.fileEditor.FileDocumentManager
 | 
			
		||||
import com.intellij.openapi.vfs.VirtualFileManager
 | 
			
		||||
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.VimScriptExecutorBase
 | 
			
		||||
import com.maddyhome.idea.vim.api.injector
 | 
			
		||||
import com.maddyhome.idea.vim.ex.ExException
 | 
			
		||||
import com.maddyhome.idea.vim.ex.FinishException
 | 
			
		||||
import com.maddyhome.idea.vim.extension.VimExtensionRegistrar
 | 
			
		||||
import com.maddyhome.idea.vim.history.HistoryConstants
 | 
			
		||||
import com.maddyhome.idea.vim.newapi.vim
 | 
			
		||||
import com.maddyhome.idea.vim.register.RegisterConstants.LAST_COMMAND_REGISTER
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.VimLContext
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.commands.Command
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.model.commands.RepeatCommand
 | 
			
		||||
import com.maddyhome.idea.vim.vimscript.parser.VimscriptParser
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
 | 
			
		||||
@Service
 | 
			
		||||
internal class Executor : VimScriptExecutorBase() {
 | 
			
		||||
  private val logger = logger<Executor>()
 | 
			
		||||
  override var executingVimscript = false
 | 
			
		||||
  override var executingIdeaVimRcConfiguration = false
 | 
			
		||||
 | 
			
		||||
  @Throws(ExException::class)
 | 
			
		||||
  override fun execute(script: String, editor: VimEditor, context: ExecutionContext, skipHistory: Boolean, indicateErrors: Boolean, vimContext: VimLContext?): ExecutionResult {
 | 
			
		||||
    try {
 | 
			
		||||
      injector.vimscriptExecutor.executingVimscript = true
 | 
			
		||||
      var finalResult: ExecutionResult = ExecutionResult.Success
 | 
			
		||||
 | 
			
		||||
      val myScript = VimscriptParser.parse(script)
 | 
			
		||||
      myScript.units.forEach { it.vimContext = vimContext ?: myScript }
 | 
			
		||||
 | 
			
		||||
      for (unit in myScript.units) {
 | 
			
		||||
        try {
 | 
			
		||||
          val result = unit.execute(editor, context)
 | 
			
		||||
          if (result is ExecutionResult.Error) {
 | 
			
		||||
            finalResult = ExecutionResult.Error
 | 
			
		||||
            if (indicateErrors) {
 | 
			
		||||
              VimPlugin.indicateError()
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        } catch (e: ExException) {
 | 
			
		||||
          if (e is FinishException) {
 | 
			
		||||
            break
 | 
			
		||||
          }
 | 
			
		||||
          finalResult = ExecutionResult.Error
 | 
			
		||||
          if (indicateErrors) {
 | 
			
		||||
            VimPlugin.showMessage(e.message)
 | 
			
		||||
            VimPlugin.indicateError()
 | 
			
		||||
          } else {
 | 
			
		||||
            logger.warn("Failed while executing $unit. " + e.message)
 | 
			
		||||
          }
 | 
			
		||||
        } catch (e: NotImplementedError) {
 | 
			
		||||
          if (indicateErrors) {
 | 
			
		||||
            VimPlugin.showMessage("Not implemented yet :(")
 | 
			
		||||
            VimPlugin.indicateError()
 | 
			
		||||
          }
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
          logger.warn(e)
 | 
			
		||||
          if (injector.application.isUnitTest()) {
 | 
			
		||||
            throw e
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!skipHistory) {
 | 
			
		||||
        VimPlugin.getHistory().addEntry(HistoryConstants.COMMAND, script)
 | 
			
		||||
        if (myScript.units.size == 1 && myScript.units[0] is Command && myScript.units[0] !is RepeatCommand) {
 | 
			
		||||
          VimPlugin.getRegister().storeTextSpecial(LAST_COMMAND_REGISTER, script)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return finalResult
 | 
			
		||||
    } finally {
 | 
			
		||||
      injector.vimscriptExecutor.executingVimscript = false
 | 
			
		||||
 | 
			
		||||
      // Initialize any extensions that were enabled during execution of this vimscript
 | 
			
		||||
      // See the doc of this function for details
 | 
			
		||||
  override fun enableDelayedExtensions() {
 | 
			
		||||
    VimExtensionRegistrar.enableDelayedExtensions()
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun executeFile(file: File, editor: VimEditor, fileIsIdeaVimRcConfig: Boolean, indicateErrors: Boolean) {
 | 
			
		||||
    val context = DataContext.EMPTY_CONTEXT.vim
 | 
			
		||||
    try {
 | 
			
		||||
      if (fileIsIdeaVimRcConfig) {
 | 
			
		||||
        injector.vimscriptExecutor.executingIdeaVimRcConfiguration = true
 | 
			
		||||
      }
 | 
			
		||||
      ensureFileIsSaved(file)
 | 
			
		||||
      execute(file.readText(), editor, context, skipHistory = true, indicateErrors)
 | 
			
		||||
    } catch (ignored: IOException) {
 | 
			
		||||
      LOG.error(ignored)
 | 
			
		||||
    } finally {
 | 
			
		||||
      if (fileIsIdeaVimRcConfig) {
 | 
			
		||||
        injector.vimrcFileState.saveFileState(file.absolutePath)
 | 
			
		||||
        injector.vimscriptExecutor.executingIdeaVimRcConfiguration = false
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun ensureFileIsSaved(file: File) {
 | 
			
		||||
  override fun ensureFileIsSaved(file: File) {
 | 
			
		||||
    val documentManager = FileDocumentManager.getInstance()
 | 
			
		||||
 | 
			
		||||
    VirtualFileManager.getInstance().findFileByNioPath(file.toPath())
 | 
			
		||||
@@ -123,16 +29,4 @@ internal class Executor : VimScriptExecutorBase() {
 | 
			
		||||
      ?.takeIf(documentManager::isDocumentUnsaved)
 | 
			
		||||
      ?.let(documentManager::saveDocumentAsIs)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Throws(ExException::class)
 | 
			
		||||
  override fun executeLastCommand(editor: VimEditor, context: ExecutionContext): Boolean {
 | 
			
		||||
    val reg = VimPlugin.getRegister().getRegister(':') ?: return false
 | 
			
		||||
    val text = reg.text ?: return false
 | 
			
		||||
    execute(text, editor, context, skipHistory = false, indicateErrors = true, CommandLineVimLContext)
 | 
			
		||||
    return true
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  companion object {
 | 
			
		||||
    val LOG = logger<Executor>()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,9 @@ internal data class ActionListCommand(val range: Range, val argument: String) :
 | 
			
		||||
      .filter { line -> searchPattern.all { it in line.lowercase(Locale.getDefault()) } }
 | 
			
		||||
      .joinToString(lineSeparator)
 | 
			
		||||
 | 
			
		||||
    ExOutputModel.getInstance(editor.ij).output(MessageHelper.message("ex.show.all.actions.0.1", lineSeparator, actions))
 | 
			
		||||
    val outputPanel = injector.outputPanel.getOrCreate(editor, context)
 | 
			
		||||
    outputPanel.addText(MessageHelper.message("ex.show.all.actions.0.1", lineSeparator, actions))
 | 
			
		||||
    outputPanel.show()
 | 
			
		||||
    return ExecutionResult.Success
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user