mirror of
				https://github.com/chylex/IntelliJ-IdeaVim.git
				synced 2025-11-04 01:40:12 +01:00 
			
		
		
		
	Compare commits
	
		
			17 Commits
		
	
	
		
			f33589fbce
			...
			customized
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						57ddf2083e
	
				 | 
					
					
						|||
| 
						
						
							
						
						aad2287433
	
				 | 
					
					
						|||
| 
						
						
							
						
						a348428422
	
				 | 
					
					
						|||
| 
						
						
							
						
						c4c66c194a
	
				 | 
					
					
						|||
| 
						
						
							
						
						e065783486
	
				 | 
					
					
						|||
| 
						
						
							
						
						dae5e3a8fd
	
				 | 
					
					
						|||
| 
						
						
							
						
						bb3b67f611
	
				 | 
					
					
						|||
| 
						
						
							
						
						bf69c8b4a6
	
				 | 
					
					
						|||
| 
						
						
							
						
						49d1d2d270
	
				 | 
					
					
						|||
| 
						
						
							
						
						96cdf1c26d
	
				 | 
					
					
						|||
| 
						
						
							
						
						365bbce9a0
	
				 | 
					
					
						|||
| 
						
						
							
						
						1f8a580b7f
	
				 | 
					
					
						|||
| 
						
						
							
						
						fa9529eaa5
	
				 | 
					
					
						|||
| 
						
						
							
						
						0b29a4704b
	
				 | 
					
					
						|||
| 
						
						
							
						
						92b9046d6c
	
				 | 
					
					
						|||
| 
						
						
							
						
						f5b120ac01
	
				 | 
					
					
						|||
| 
						
						
							
						
						70ea63c0ba
	
				 | 
					
					
						
							
								
								
									
										9
									
								
								.github/workflows/closeYoutrackOnCommit.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.github/workflows/closeYoutrackOnCommit.yml
									
									
									
									
										vendored
									
									
								
							@@ -8,9 +8,6 @@ on:
 | 
				
			|||||||
  push:
 | 
					  push:
 | 
				
			||||||
    branches: [ master ]
 | 
					    branches: [ master ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
permissions:
 | 
					 | 
				
			||||||
  contents: write
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
jobs:
 | 
					jobs:
 | 
				
			||||||
  build:
 | 
					  build:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -23,10 +20,10 @@ jobs:
 | 
				
			|||||||
          fetch-depth: 300
 | 
					          fetch-depth: 300
 | 
				
			||||||
      - name: Get tags
 | 
					      - name: Get tags
 | 
				
			||||||
        run: git fetch --tags origin
 | 
					        run: git fetch --tags origin
 | 
				
			||||||
      - name: Set up JDK 17
 | 
					      - name: Set up JDK 11
 | 
				
			||||||
        uses: actions/setup-java@v2
 | 
					        uses: actions/setup-java@v2
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          java-version: '17'
 | 
					          java-version: '11'
 | 
				
			||||||
          distribution: 'adopt'
 | 
					          distribution: 'adopt'
 | 
				
			||||||
          server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
 | 
					          server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
 | 
				
			||||||
          settings-path: ${{ github.workspace }} # location for the settings.xml file
 | 
					          settings-path: ${{ github.workspace }} # location for the settings.xml file
 | 
				
			||||||
@@ -37,7 +34,7 @@ jobs:
 | 
				
			|||||||
          echo "LAST_COMMIT=$(git rev-list -n 1 tags/workflow-close-youtrack)" >> $GITHUB_ENV
 | 
					          echo "LAST_COMMIT=$(git rev-list -n 1 tags/workflow-close-youtrack)" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Update YouTrack
 | 
					      - name: Update YouTrack
 | 
				
			||||||
        run: ./gradlew --no-configuration-cache updateYoutrackOnCommit
 | 
					        run: ./gradlew updateYoutrackOnCommit
 | 
				
			||||||
        env:
 | 
					        env:
 | 
				
			||||||
          SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
 | 
					          SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
 | 
				
			||||||
          YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
 | 
					          YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							@@ -20,11 +20,6 @@ on:
 | 
				
			|||||||
  schedule:
 | 
					  schedule:
 | 
				
			||||||
    - cron: '44 12 * * 4'
 | 
					    - cron: '44 12 * * 4'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
permissions:
 | 
					 | 
				
			||||||
  actions: read
 | 
					 | 
				
			||||||
  contents: read
 | 
					 | 
				
			||||||
  security-events: write
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
jobs:
 | 
					jobs:
 | 
				
			||||||
  analyze:
 | 
					  analyze:
 | 
				
			||||||
    name: Analyze
 | 
					    name: Analyze
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								.github/workflows/integrationsTest.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/integrationsTest.yml
									
									
									
									
										vendored
									
									
								
							@@ -18,16 +18,16 @@ jobs:
 | 
				
			|||||||
      - uses: actions/checkout@v2
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          fetch-depth: 300
 | 
					          fetch-depth: 300
 | 
				
			||||||
      - name: Set up JDK 17
 | 
					      - name: Set up JDK 11
 | 
				
			||||||
        uses: actions/setup-java@v2
 | 
					        uses: actions/setup-java@v2
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          java-version: '17'
 | 
					          java-version: '11'
 | 
				
			||||||
          distribution: 'adopt'
 | 
					          distribution: 'adopt'
 | 
				
			||||||
          server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
 | 
					          server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
 | 
				
			||||||
          settings-path: ${{ github.workspace }} # location for the settings.xml file
 | 
					          settings-path: ${{ github.workspace }} # location for the settings.xml file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Run tests
 | 
					      - name: Run tests
 | 
				
			||||||
        run: ./gradlew --no-configuration-cache integrationsTest
 | 
					        run: ./gradlew integrationsTest
 | 
				
			||||||
        env:
 | 
					        env:
 | 
				
			||||||
          YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
 | 
					          YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
 | 
				
			||||||
          GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
 | 
					          GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								.github/workflows/kover.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/kover.yml
									
									
									
									
										vendored
									
									
								
							@@ -18,10 +18,10 @@ jobs:
 | 
				
			|||||||
      - uses: actions/checkout@v2
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          fetch-depth: 300
 | 
					          fetch-depth: 300
 | 
				
			||||||
      - name: Set up JDK 17
 | 
					      - name: Set up JDK 11
 | 
				
			||||||
        uses: actions/setup-java@v2
 | 
					        uses: actions/setup-java@v2
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          java-version: '17'
 | 
					          java-version: '11'
 | 
				
			||||||
          distribution: 'adopt'
 | 
					          distribution: 'adopt'
 | 
				
			||||||
          server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
 | 
					          server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
 | 
				
			||||||
          settings-path: ${{ github.workspace }} # location for the settings.xml file
 | 
					          settings-path: ${{ github.workspace }} # location for the settings.xml file
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								.github/workflows/mergePr.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/mergePr.yml
									
									
									
									
										vendored
									
									
								
							@@ -20,17 +20,17 @@ jobs:
 | 
				
			|||||||
          fetch-depth: 50
 | 
					          fetch-depth: 50
 | 
				
			||||||
          # See end of file updateChangeslog.yml for explanation of this secret
 | 
					          # See end of file updateChangeslog.yml for explanation of this secret
 | 
				
			||||||
          ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
 | 
					          ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
 | 
				
			||||||
      - name: Set up JDK 17
 | 
					      - name: Set up JDK 11
 | 
				
			||||||
        uses: actions/setup-java@v2
 | 
					        uses: actions/setup-java@v2
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          java-version: '17'
 | 
					          java-version: '11'
 | 
				
			||||||
          distribution: 'adopt'
 | 
					          distribution: 'adopt'
 | 
				
			||||||
          server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
 | 
					          server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
 | 
				
			||||||
          settings-path: ${{ github.workspace }} # location for the settings.xml file
 | 
					          settings-path: ${{ github.workspace }} # location for the settings.xml file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Update authors
 | 
					      - name: Update authors
 | 
				
			||||||
        id: update_authors
 | 
					        id: update_authors
 | 
				
			||||||
        run: ./gradlew --no-configuration-cache updateMergedPr -PprId=${{ github.event.number }}
 | 
					        run: ./gradlew updateMergedPr -PprId=${{ github.event.number }}
 | 
				
			||||||
        env:
 | 
					        env:
 | 
				
			||||||
          GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
 | 
					          GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								.github/workflows/runUiOctopusTests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/runUiOctopusTests.yml
									
									
									
									
										vendored
									
									
								
							@@ -16,14 +16,14 @@ jobs:
 | 
				
			|||||||
          java-version: 17
 | 
					          java-version: 17
 | 
				
			||||||
      - name: Setup FFmpeg
 | 
					      - name: Setup FFmpeg
 | 
				
			||||||
        run: brew install ffmpeg
 | 
					        run: brew install ffmpeg
 | 
				
			||||||
#      - name: Setup Gradle
 | 
					      - name: Setup Gradle
 | 
				
			||||||
#        uses: gradle/gradle-build-action@v2.4.2
 | 
					        uses: gradle/gradle-build-action@v2.4.2
 | 
				
			||||||
      - name: Build Plugin
 | 
					      - name: Build Plugin
 | 
				
			||||||
        run: gradle :buildPlugin
 | 
					        run: gradle :buildPlugin
 | 
				
			||||||
      - name: Run Idea
 | 
					      - name: Run Idea
 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
          mkdir -p build/reports
 | 
					          mkdir -p build/reports
 | 
				
			||||||
          gradle --no-configuration-cache runIdeForUiTests -Doctopus.handler=false > build/reports/idea.log &
 | 
					          gradle runIdeForUiTests -Doctopus.handler=false > build/reports/idea.log &
 | 
				
			||||||
      - name: Wait for Idea started
 | 
					      - name: Wait for Idea started
 | 
				
			||||||
        uses: jtalk/url-health-check-action@v3
 | 
					        uses: jtalk/url-health-check-action@v3
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
@@ -37,7 +37,7 @@ jobs:
 | 
				
			|||||||
        run: mv tests/ui-ij-tests/video build/reports
 | 
					        run: mv tests/ui-ij-tests/video build/reports
 | 
				
			||||||
      - name: Move sandbox logs
 | 
					      - name: Move sandbox logs
 | 
				
			||||||
        if: always()
 | 
					        if: always()
 | 
				
			||||||
        run: mv build/idea-sandbox/IC-2024.1.2/log_runIdeForUiTests idea-sandbox-log
 | 
					        run: mv build/idea-sandbox/system/log sandbox-idea-log
 | 
				
			||||||
      - name: Save report
 | 
					      - name: Save report
 | 
				
			||||||
        if: always()
 | 
					        if: always()
 | 
				
			||||||
        uses: actions/upload-artifact@v4
 | 
					        uses: actions/upload-artifact@v4
 | 
				
			||||||
@@ -46,7 +46,7 @@ jobs:
 | 
				
			|||||||
          path: |
 | 
					          path: |
 | 
				
			||||||
            build/reports
 | 
					            build/reports
 | 
				
			||||||
            tests/ui-ij-tests/build/reports
 | 
					            tests/ui-ij-tests/build/reports
 | 
				
			||||||
            idea-sandbox-log
 | 
					            sandbox-idea-log
 | 
				
			||||||
#  build-for-ui-test-linux:
 | 
					#  build-for-ui-test-linux:
 | 
				
			||||||
#    runs-on: ubuntu-latest
 | 
					#    runs-on: ubuntu-latest
 | 
				
			||||||
#    steps:
 | 
					#    steps:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								.github/workflows/runUiPyTests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/runUiPyTests.yml
									
									
									
									
										vendored
									
									
								
							@@ -19,14 +19,14 @@ jobs:
 | 
				
			|||||||
          python-version: '3.10'
 | 
					          python-version: '3.10'
 | 
				
			||||||
      - name: Setup FFmpeg
 | 
					      - name: Setup FFmpeg
 | 
				
			||||||
        run: brew install ffmpeg
 | 
					        run: brew install ffmpeg
 | 
				
			||||||
#      - name: Setup Gradle
 | 
					      - name: Setup Gradle
 | 
				
			||||||
#        uses: gradle/gradle-build-action@v2.4.2
 | 
					        uses: gradle/gradle-build-action@v2.4.2
 | 
				
			||||||
      - name: Build Plugin
 | 
					      - name: Build Plugin
 | 
				
			||||||
        run: gradle :buildPlugin
 | 
					        run: gradle :buildPlugin
 | 
				
			||||||
      - name: Run Idea
 | 
					      - name: Run Idea
 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
          mkdir -p build/reports
 | 
					          mkdir -p build/reports
 | 
				
			||||||
          gradle --no-configuration-cache :runIdeForUiTests -PideaType=PC > build/reports/idea.log &
 | 
					          gradle :runIdeForUiTests -PideaType=PC > build/reports/idea.log &
 | 
				
			||||||
      - name: Wait for Idea started
 | 
					      - name: Wait for Idea started
 | 
				
			||||||
        uses: jtalk/url-health-check-action@v3
 | 
					        uses: jtalk/url-health-check-action@v3
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
@@ -40,7 +40,7 @@ jobs:
 | 
				
			|||||||
        run: mv tests/ui-py-tests/video build/reports
 | 
					        run: mv tests/ui-py-tests/video build/reports
 | 
				
			||||||
      - name: Move sandbox logs
 | 
					      - name: Move sandbox logs
 | 
				
			||||||
        if: always()
 | 
					        if: always()
 | 
				
			||||||
        run: mv build/idea-sandbox/PC-2024.1.2/log_runIdeForUiTests idea-sandbox-log
 | 
					        run: mv build/idea-sandbox/system/log sandbox-idea-log
 | 
				
			||||||
      - name: Save report
 | 
					      - name: Save report
 | 
				
			||||||
        if: always()
 | 
					        if: always()
 | 
				
			||||||
        uses: actions/upload-artifact@v4
 | 
					        uses: actions/upload-artifact@v4
 | 
				
			||||||
@@ -49,4 +49,4 @@ jobs:
 | 
				
			|||||||
          path: |
 | 
					          path: |
 | 
				
			||||||
            build/reports
 | 
					            build/reports
 | 
				
			||||||
            tests/ui-py-tests/build/reports
 | 
					            tests/ui-py-tests/build/reports
 | 
				
			||||||
            idea-sandbox-log
 | 
					            sandbox-idea-log
 | 
				
			||||||
							
								
								
									
										10
									
								
								.github/workflows/runUiTests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/runUiTests.yml
									
									
									
									
										vendored
									
									
								
							@@ -16,14 +16,14 @@ jobs:
 | 
				
			|||||||
          java-version: 17
 | 
					          java-version: 17
 | 
				
			||||||
      - name: Setup FFmpeg
 | 
					      - name: Setup FFmpeg
 | 
				
			||||||
        run: brew install ffmpeg
 | 
					        run: brew install ffmpeg
 | 
				
			||||||
#      - name: Setup Gradle
 | 
					      - name: Setup Gradle
 | 
				
			||||||
#        uses: gradle/gradle-build-action@v2.4.2
 | 
					        uses: gradle/gradle-build-action@v2.4.2
 | 
				
			||||||
      - name: Build Plugin
 | 
					      - name: Build Plugin
 | 
				
			||||||
        run: gradle :buildPlugin
 | 
					        run: gradle :buildPlugin
 | 
				
			||||||
      - name: Run Idea
 | 
					      - name: Run Idea
 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
          mkdir -p build/reports
 | 
					          mkdir -p build/reports
 | 
				
			||||||
          gradle --no-configuration-cache runIdeForUiTests > build/reports/idea.log &
 | 
					          gradle runIdeForUiTests > build/reports/idea.log &
 | 
				
			||||||
      - name: Wait for Idea started
 | 
					      - name: Wait for Idea started
 | 
				
			||||||
        uses: jtalk/url-health-check-action@v3
 | 
					        uses: jtalk/url-health-check-action@v3
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
@@ -37,7 +37,7 @@ jobs:
 | 
				
			|||||||
        run: mv tests/ui-ij-tests/video build/reports
 | 
					        run: mv tests/ui-ij-tests/video build/reports
 | 
				
			||||||
      - name: Move sandbox logs
 | 
					      - name: Move sandbox logs
 | 
				
			||||||
        if: always()
 | 
					        if: always()
 | 
				
			||||||
        run: mv build/idea-sandbox/IC-2024.1.2/log_runIdeForUiTests idea-sandbox-log
 | 
					        run: mv build/idea-sandbox/system/log sandbox-idea-log
 | 
				
			||||||
      - name: Save report
 | 
					      - name: Save report
 | 
				
			||||||
        if: always()
 | 
					        if: always()
 | 
				
			||||||
        uses: actions/upload-artifact@v4
 | 
					        uses: actions/upload-artifact@v4
 | 
				
			||||||
@@ -46,7 +46,7 @@ jobs:
 | 
				
			|||||||
          path: |
 | 
					          path: |
 | 
				
			||||||
            build/reports
 | 
					            build/reports
 | 
				
			||||||
            tests/ui-ij-tests/build/reports
 | 
					            tests/ui-ij-tests/build/reports
 | 
				
			||||||
            idea-sandbox-log
 | 
					            sandbox-idea-log
 | 
				
			||||||
#  build-for-ui-test-linux:
 | 
					#  build-for-ui-test-linux:
 | 
				
			||||||
#    runs-on: ubuntu-latest
 | 
					#    runs-on: ubuntu-latest
 | 
				
			||||||
#    steps:
 | 
					#    steps:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										14
									
								
								.github/workflows/syncDoc.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.github/workflows/syncDoc.yml
									
									
									
									
										vendored
									
									
								
							@@ -10,9 +10,6 @@ on:
 | 
				
			|||||||
  push:
 | 
					  push:
 | 
				
			||||||
    branches: [ master ]
 | 
					    branches: [ master ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
permissions:
 | 
					 | 
				
			||||||
  contents: write
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
jobs:
 | 
					jobs:
 | 
				
			||||||
  build:
 | 
					  build:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -37,17 +34,6 @@ jobs:
 | 
				
			|||||||
        id: update_authors
 | 
					        id: update_authors
 | 
				
			||||||
        run: cp -a origin/doc/. docs
 | 
					        run: cp -a origin/doc/. docs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      # The Wiki for github should have no `.md` in references
 | 
					 | 
				
			||||||
      # Otherwise, such links will lead to the raw text.
 | 
					 | 
				
			||||||
      # However, the `.md` should exist to have a navigation in GitHub code.
 | 
					 | 
				
			||||||
      - name: Replace `.md)` with `)`
 | 
					 | 
				
			||||||
        run: |
 | 
					 | 
				
			||||||
          # Define the directory you want to process
 | 
					 | 
				
			||||||
          DIRECTORY="docs"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          # Find all files in the directory and perform the replacement
 | 
					 | 
				
			||||||
          find $DIRECTORY -type f -exec sed -i 's/\.md)/)/g' {} +
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      - name: Commit changes
 | 
					      - name: Commit changes
 | 
				
			||||||
        uses: stefanzweifel/git-auto-commit-action@v4
 | 
					        uses: stefanzweifel/git-auto-commit-action@v4
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								.github/workflows/updateAuthors.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/updateAuthors.yml
									
									
									
									
										vendored
									
									
								
							@@ -25,10 +25,10 @@ jobs:
 | 
				
			|||||||
          ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
 | 
					          ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
 | 
				
			||||||
      - name: Get tags
 | 
					      - name: Get tags
 | 
				
			||||||
        run: git fetch --tags origin
 | 
					        run: git fetch --tags origin
 | 
				
			||||||
      - name: Set up JDK 17
 | 
					      - name: Set up JDK 11
 | 
				
			||||||
        uses: actions/setup-java@v2
 | 
					        uses: actions/setup-java@v2
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          java-version: '17'
 | 
					          java-version: '11'
 | 
				
			||||||
          distribution: 'adopt'
 | 
					          distribution: 'adopt'
 | 
				
			||||||
          server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
 | 
					          server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
 | 
				
			||||||
          settings-path: ${{ github.workspace }} # location for the settings.xml file
 | 
					          settings-path: ${{ github.workspace }} # location for the settings.xml file
 | 
				
			||||||
@@ -40,7 +40,7 @@ jobs:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      - name: Update authors
 | 
					      - name: Update authors
 | 
				
			||||||
        id: update_authors
 | 
					        id: update_authors
 | 
				
			||||||
        run: ./gradlew --no-configuration-cache updateAuthors --stacktrace
 | 
					        run: ./gradlew updateAuthors --stacktrace
 | 
				
			||||||
        env:
 | 
					        env:
 | 
				
			||||||
          SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
 | 
					          SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
 | 
				
			||||||
          GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
 | 
					          GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								.github/workflows/updateChangelog.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/updateChangelog.yml
									
									
									
									
										vendored
									
									
								
							@@ -22,10 +22,10 @@ jobs:
 | 
				
			|||||||
          ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
 | 
					          ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
 | 
				
			||||||
      - name: Get tags
 | 
					      - name: Get tags
 | 
				
			||||||
        run: git fetch --tags origin
 | 
					        run: git fetch --tags origin
 | 
				
			||||||
      - name: Set up JDK 17
 | 
					      - name: Set up JDK 11
 | 
				
			||||||
        uses: actions/setup-java@v2
 | 
					        uses: actions/setup-java@v2
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          java-version: '17'
 | 
					          java-version: '11'
 | 
				
			||||||
          distribution: 'adopt'
 | 
					          distribution: 'adopt'
 | 
				
			||||||
          server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
 | 
					          server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
 | 
				
			||||||
          settings-path: ${{ github.workspace }} # location for the settings.xml file
 | 
					          settings-path: ${{ github.workspace }} # location for the settings.xml file
 | 
				
			||||||
@@ -36,7 +36,7 @@ jobs:
 | 
				
			|||||||
          echo "LAST_COMMIT=$(git rev-list -n 1 tags/workflow-changelog)" >> $GITHUB_ENV
 | 
					          echo "LAST_COMMIT=$(git rev-list -n 1 tags/workflow-changelog)" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Update changelog
 | 
					      - name: Update changelog
 | 
				
			||||||
        run: ./gradlew --no-configuration-cache updateChangelog
 | 
					        run: ./gradlew updateChangelog
 | 
				
			||||||
        env:
 | 
					        env:
 | 
				
			||||||
          SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
 | 
					          SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								.github/workflows/updateFormatting.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/updateFormatting.yml
									
									
									
									
										vendored
									
									
								
							@@ -20,10 +20,10 @@ jobs:
 | 
				
			|||||||
          fetch-depth: 50
 | 
					          fetch-depth: 50
 | 
				
			||||||
          # See end of file updateChangeslog.yml for explanation of this secret
 | 
					          # See end of file updateChangeslog.yml for explanation of this secret
 | 
				
			||||||
          ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
 | 
					          ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
 | 
				
			||||||
      - name: Set up JDK 17
 | 
					      - name: Set up JDK 11
 | 
				
			||||||
        uses: actions/setup-java@v2
 | 
					        uses: actions/setup-java@v2
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          java-version: '17'
 | 
					          java-version: '11'
 | 
				
			||||||
          distribution: 'adopt'
 | 
					          distribution: 'adopt'
 | 
				
			||||||
          server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
 | 
					          server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
 | 
				
			||||||
          settings-path: ${{ github.workspace }} # location for the settings.xml file
 | 
					          settings-path: ${{ github.workspace }} # location for the settings.xml file
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,5 @@
 | 
				
			|||||||
*.swp
 | 
					*.swp
 | 
				
			||||||
/.gradle/
 | 
					/.gradle/
 | 
				
			||||||
/.intellijPlatform/
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/.idea/
 | 
					/.idea/
 | 
				
			||||||
!/.idea/scopes
 | 
					!/.idea/scopes
 | 
				
			||||||
@@ -11,8 +10,6 @@
 | 
				
			|||||||
!/.idea/runConfigurations
 | 
					!/.idea/runConfigurations
 | 
				
			||||||
!/.idea/codeStyles
 | 
					!/.idea/codeStyles
 | 
				
			||||||
!/.idea/vcs.xml
 | 
					!/.idea/vcs.xml
 | 
				
			||||||
!/.idea/misc.xml
 | 
					 | 
				
			||||||
!/.idea/.name
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
**/build/
 | 
					**/build/
 | 
				
			||||||
**/out/
 | 
					**/out/
 | 
				
			||||||
@@ -25,10 +22,11 @@
 | 
				
			|||||||
.teamcity/*.iml
 | 
					.teamcity/*.iml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Generated by gradle task "generateGrammarSource"
 | 
					# Generated by gradle task "generateGrammarSource"
 | 
				
			||||||
vim-engine/src/main/java/com/maddyhome/idea/vim/parser/generated
 | 
					src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated
 | 
				
			||||||
vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/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
 | 
					# Created by github automation
 | 
				
			||||||
settings.xml
 | 
					settings.xml
 | 
				
			||||||
 | 
					 | 
				
			||||||
.kotlin
 | 
					 | 
				
			||||||
							
								
								
									
										1
									
								
								.idea/.name
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								.idea/.name
									
									
									
										generated
									
									
									
								
							@@ -1 +0,0 @@
 | 
				
			|||||||
IdeaVim
 | 
					 | 
				
			||||||
							
								
								
									
										22
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										22
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							@@ -1,22 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
					 | 
				
			||||||
<project version="4">
 | 
					 | 
				
			||||||
  <component name="EntryPointsManager">
 | 
					 | 
				
			||||||
    <list size="3">
 | 
					 | 
				
			||||||
      <item index="0" class="java.lang.String" itemvalue="com.intellij.vim.annotations.CommandOrMotion" />
 | 
					 | 
				
			||||||
      <item index="1" class="java.lang.String" itemvalue="com.intellij.vim.annotations.ExCommand" />
 | 
					 | 
				
			||||||
      <item index="2" class="java.lang.String" itemvalue="com.intellij.vim.annotations.VimscriptFunction" />
 | 
					 | 
				
			||||||
    </list>
 | 
					 | 
				
			||||||
  </component>
 | 
					 | 
				
			||||||
  <component name="ExternalStorageConfigurationManager" enabled="true" />
 | 
					 | 
				
			||||||
  <component name="FrameworkDetectionExcludesConfiguration">
 | 
					 | 
				
			||||||
    <file type="web" url="file://$PROJECT_DIR$" />
 | 
					 | 
				
			||||||
  </component>
 | 
					 | 
				
			||||||
  <component name="MavenProjectsManager">
 | 
					 | 
				
			||||||
    <option name="originalFiles">
 | 
					 | 
				
			||||||
      <list>
 | 
					 | 
				
			||||||
        <option value="$PROJECT_DIR$/.teamcity/pom.xml" />
 | 
					 | 
				
			||||||
      </list>
 | 
					 | 
				
			||||||
    </option>
 | 
					 | 
				
			||||||
  </component>
 | 
					 | 
				
			||||||
  <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="corretto-17" project-jdk-type="JavaSDK" />
 | 
					 | 
				
			||||||
</project>
 | 
					 | 
				
			||||||
@@ -12,7 +12,7 @@
 | 
				
			|||||||
      <option name="taskNames">
 | 
					      <option name="taskNames">
 | 
				
			||||||
        <list>
 | 
					        <list>
 | 
				
			||||||
          <option value="check" />
 | 
					          <option value="check" />
 | 
				
			||||||
          <option value="verifyPlugin" />
 | 
					          <option value="runPluginVerifier" />
 | 
				
			||||||
        </list>
 | 
					        </list>
 | 
				
			||||||
      </option>
 | 
					      </option>
 | 
				
			||||||
      <option name="vmOptions" value="" />
 | 
					      <option name="vmOptions" value="" />
 | 
				
			||||||
@@ -20,7 +20,6 @@
 | 
				
			|||||||
    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
 | 
					    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
 | 
				
			||||||
    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
 | 
					    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
 | 
				
			||||||
    <DebugAllEnabled>false</DebugAllEnabled>
 | 
					    <DebugAllEnabled>false</DebugAllEnabled>
 | 
				
			||||||
    <RunAsTest>false</RunAsTest>
 | 
					 | 
				
			||||||
    <method v="2" />
 | 
					    <method v="2" />
 | 
				
			||||||
  </configuration>
 | 
					  </configuration>
 | 
				
			||||||
</component>
 | 
					</component>
 | 
				
			||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
<component name="ProjectRunConfigurationManager">
 | 
					 | 
				
			||||||
  <configuration default="false" name="Start IJ with IdeaVim (Split Mode)" type="GradleRunConfiguration" factoryName="Gradle">
 | 
					 | 
				
			||||||
    <log_file alias="idea.log" path="$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log" />
 | 
					 | 
				
			||||||
    <ExternalSystemSettings>
 | 
					 | 
				
			||||||
      <option name="executionName" />
 | 
					 | 
				
			||||||
      <option name="externalProjectPath" value="$PROJECT_DIR$" />
 | 
					 | 
				
			||||||
      <option name="externalSystemIdString" value="GRADLE" />
 | 
					 | 
				
			||||||
      <option name="scriptParameters" value="" />
 | 
					 | 
				
			||||||
      <option name="taskDescriptions">
 | 
					 | 
				
			||||||
        <list />
 | 
					 | 
				
			||||||
      </option>
 | 
					 | 
				
			||||||
      <option name="taskNames">
 | 
					 | 
				
			||||||
        <list>
 | 
					 | 
				
			||||||
          <option value="runIdeSplitMode" />
 | 
					 | 
				
			||||||
        </list>
 | 
					 | 
				
			||||||
      </option>
 | 
					 | 
				
			||||||
      <option name="vmOptions" value="" />
 | 
					 | 
				
			||||||
    </ExternalSystemSettings>
 | 
					 | 
				
			||||||
    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
 | 
					 | 
				
			||||||
    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
 | 
					 | 
				
			||||||
    <DebugAllEnabled>false</DebugAllEnabled>
 | 
					 | 
				
			||||||
    <RunAsTest>false</RunAsTest>
 | 
					 | 
				
			||||||
    <method v="2" />
 | 
					 | 
				
			||||||
  </configuration>
 | 
					 | 
				
			||||||
</component>
 | 
					 | 
				
			||||||
							
								
								
									
										15
									
								
								.teamcity/_Self/Constants.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								.teamcity/_Self/Constants.kt
									
									
									
									
										vendored
									
									
								
							@@ -5,12 +5,13 @@ object Constants {
 | 
				
			|||||||
  const val EAP_CHANNEL = "eap"
 | 
					  const val EAP_CHANNEL = "eap"
 | 
				
			||||||
  const val DEV_CHANNEL = "Dev"
 | 
					  const val DEV_CHANNEL = "Dev"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const val NVIM_TESTS = "2024.2.1"
 | 
					  const val GITHUB_TESTS = "2024.1.1"
 | 
				
			||||||
  const val PROPERTY_TESTS = "2024.2.1"
 | 
					  const val NVIM_TESTS = "2024.1.1"
 | 
				
			||||||
  const val LONG_RUNNING_TESTS = "2024.2.1"
 | 
					  const val PROPERTY_TESTS = "2024.1.1"
 | 
				
			||||||
  const val QODANA_TESTS = "2024.2.1"
 | 
					  const val LONG_RUNNING_TESTS = "2024.1.1"
 | 
				
			||||||
  const val RELEASE = "2024.2.1"
 | 
					  const val QODANA_TESTS = "2024.1.1"
 | 
				
			||||||
 | 
					  const val RELEASE = "2024.1.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const val RELEASE_DEV = "2024.2.1"
 | 
					  const val RELEASE_DEV = "2024.1.1"
 | 
				
			||||||
  const val RELEASE_EAP = "2024.2.1"
 | 
					  const val RELEASE_EAP = "2024.1.1"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								.teamcity/_Self/Project.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.teamcity/_Self/Project.kt
									
									
									
									
										vendored
									
									
								
							@@ -8,6 +8,7 @@ import _Self.buildTypes.PropertyBased
 | 
				
			|||||||
import _Self.buildTypes.Qodana
 | 
					import _Self.buildTypes.Qodana
 | 
				
			||||||
import _Self.buildTypes.TestingBuildType
 | 
					import _Self.buildTypes.TestingBuildType
 | 
				
			||||||
import _Self.subprojects.GitHub
 | 
					import _Self.subprojects.GitHub
 | 
				
			||||||
 | 
					import _Self.subprojects.OldTests
 | 
				
			||||||
import _Self.subprojects.Releases
 | 
					import _Self.subprojects.Releases
 | 
				
			||||||
import _Self.vcsRoots.GitHubPullRequest
 | 
					import _Self.vcsRoots.GitHubPullRequest
 | 
				
			||||||
import _Self.vcsRoots.ReleasesVcsRoot
 | 
					import _Self.vcsRoots.ReleasesVcsRoot
 | 
				
			||||||
@@ -17,7 +18,7 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.Project
 | 
				
			|||||||
object Project : Project({
 | 
					object Project : Project({
 | 
				
			||||||
  description = "Vim engine for JetBrains IDEs"
 | 
					  description = "Vim engine for JetBrains IDEs"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  subProjects(Releases, GitHub)
 | 
					  subProjects(Releases, OldTests, GitHub)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // VCS roots
 | 
					  // VCS roots
 | 
				
			||||||
  vcsRoot(GitHubPullRequest)
 | 
					  vcsRoot(GitHubPullRequest)
 | 
				
			||||||
@@ -25,7 +26,7 @@ object Project : Project({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // Active tests
 | 
					  // Active tests
 | 
				
			||||||
  buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT"))
 | 
					  buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT"))
 | 
				
			||||||
  buildType(TestingBuildType("2024.2.1", "<default>"))
 | 
					  buildType(TestingBuildType("2024.1.1", "<default>"))
 | 
				
			||||||
  buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT"))
 | 
					  buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  buildType(PropertyBased)
 | 
					  buildType(PropertyBased)
 | 
				
			||||||
@@ -42,9 +43,6 @@ object Project : Project({
 | 
				
			|||||||
abstract class IdeaVimBuildType(init: BuildType.() -> Unit) : BuildType({
 | 
					abstract class IdeaVimBuildType(init: BuildType.() -> Unit) : BuildType({
 | 
				
			||||||
  artifactRules = """
 | 
					  artifactRules = """
 | 
				
			||||||
        +:build/reports => build/reports
 | 
					        +:build/reports => build/reports
 | 
				
			||||||
        +:tests/java-tests/build/reports => java-tests/build/reports
 | 
					 | 
				
			||||||
        +:tests/long-running-tests/build/reports => long-running-tests/build/reports
 | 
					 | 
				
			||||||
        +:tests/property-tests/build/reports => property-tests/build/reports
 | 
					 | 
				
			||||||
        +:/mnt/agent/temp/buildTmp/ => /mnt/agent/temp/buildTmp/
 | 
					        +:/mnt/agent/temp/buildTmp/ => /mnt/agent/temp/buildTmp/
 | 
				
			||||||
    """.trimIndent()
 | 
					    """.trimIndent()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -54,7 +52,7 @@ abstract class IdeaVimBuildType(init: BuildType.() -> Unit) : BuildType({
 | 
				
			|||||||
    // These requirements define Linux-Medium configuration.
 | 
					    // These requirements define Linux-Medium configuration.
 | 
				
			||||||
    // Unfortunately, requirement by name (teamcity.agent.name) doesn't work
 | 
					    // Unfortunately, requirement by name (teamcity.agent.name) doesn't work
 | 
				
			||||||
    //   IDK the reason for it, but on our agents this property is empty
 | 
					    //   IDK the reason for it, but on our agents this property is empty
 | 
				
			||||||
    equals("teamcity.agent.hardware.cpuCount", "16")
 | 
					    equals("teamcity.agent.hardware.cpuCount", "4")
 | 
				
			||||||
    equals("teamcity.agent.os.family", "Linux")
 | 
					    equals("teamcity.agent.os.family", "Linux")
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								.teamcity/_Self/buildTypes/Compatibility.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.teamcity/_Self/buildTypes/Compatibility.kt
									
									
									
									
										vendored
									
									
								
							@@ -37,12 +37,9 @@ 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 '${'$'}eu.theblob42.idea.whichkey' [latest-IU] -team-city
 | 
				
			||||||
              java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}IdeaVimExtension' [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
 | 
					              # 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.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
 | 
					              java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.joshestein.ideavim-quickscope' [latest-IU] -team-city
 | 
				
			||||||
              java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.julienphalip.ideavim.peekaboo' [latest-IU] -team-city
 | 
					 | 
				
			||||||
              java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.julienphalip.ideavim.switch' [latest-IU] -team-city
 | 
					 | 
				
			||||||
              java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.julienphalip.ideavim.functiontextobj' [latest-IU] -team-city
 | 
					 | 
				
			||||||
            """.trimIndent()
 | 
					            """.trimIndent()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								.teamcity/_Self/buildTypes/PluginVerifier.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.teamcity/_Self/buildTypes/PluginVerifier.kt
									
									
									
									
										vendored
									
									
								
							@@ -22,7 +22,7 @@ object PluginVerifier : IdeaVimBuildType({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  steps {
 | 
					  steps {
 | 
				
			||||||
    gradle {
 | 
					    gradle {
 | 
				
			||||||
      tasks = "clean verifyPlugin"
 | 
					      tasks = "clean runPluginVerifier"
 | 
				
			||||||
      buildFile = ""
 | 
					      buildFile = ""
 | 
				
			||||||
      enableStacktrace = true
 | 
					      enableStacktrace = true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,7 @@ object ReleaseEapFromBranch : IdeaVimBuildType({
 | 
				
			|||||||
  vcs {
 | 
					  vcs {
 | 
				
			||||||
    root(ReleasesVcsRoot)
 | 
					    root(ReleasesVcsRoot)
 | 
				
			||||||
    branchFilter = """
 | 
					    branchFilter = """
 | 
				
			||||||
      +:heads/releases/*
 | 
					      +:heads/(releases/*)
 | 
				
			||||||
      """.trimIndent()
 | 
					      """.trimIndent()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    checkoutMode = CheckoutMode.AUTO
 | 
					    checkoutMode = CheckoutMode.AUTO
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										15
									
								
								.teamcity/_Self/buildTypes/ReleasePlugin.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								.teamcity/_Self/buildTypes/ReleasePlugin.kt
									
									
									
									
										vendored
									
									
								
							@@ -19,6 +19,8 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.ParameterDisplay
 | 
				
			|||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent
 | 
					import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent
 | 
				
			||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
 | 
					import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
 | 
				
			||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script
 | 
					import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script
 | 
				
			||||||
 | 
					import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.BuildFailureOnMetric
 | 
				
			||||||
 | 
					import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.failOnMetricChange
 | 
				
			||||||
 | 
					
 | 
				
			||||||
object ReleaseMajor : ReleasePlugin("major")
 | 
					object ReleaseMajor : ReleasePlugin("major")
 | 
				
			||||||
object ReleaseMinor : ReleasePlugin("minor")
 | 
					object ReleaseMinor : ReleasePlugin("minor")
 | 
				
			||||||
@@ -144,7 +146,6 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
 | 
				
			|||||||
    gradle {
 | 
					    gradle {
 | 
				
			||||||
      name = "Run Integrations"
 | 
					      name = "Run Integrations"
 | 
				
			||||||
      tasks = "releaseActions"
 | 
					      tasks = "releaseActions"
 | 
				
			||||||
      gradleParams = "--no-configuration-cache"
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
//    gradle {
 | 
					//    gradle {
 | 
				
			||||||
//      name = "Slack Notification"
 | 
					//      name = "Slack Notification"
 | 
				
			||||||
@@ -157,4 +158,16 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
 | 
				
			|||||||
      teamcitySshKey = "IdeaVim ssh keys"
 | 
					      teamcitySshKey = "IdeaVim ssh keys"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  failureConditions {
 | 
				
			||||||
 | 
					    failOnMetricChange {
 | 
				
			||||||
 | 
					      metric = BuildFailureOnMetric.MetricType.ARTIFACT_SIZE
 | 
				
			||||||
 | 
					      threshold = 5
 | 
				
			||||||
 | 
					      units = BuildFailureOnMetric.MetricUnit.PERCENTS
 | 
				
			||||||
 | 
					      comparison = BuildFailureOnMetric.MetricComparison.DIFF
 | 
				
			||||||
 | 
					      compareTo = build {
 | 
				
			||||||
 | 
					        buildRule = lastSuccessful()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,11 +39,9 @@ open class TestingBuildType(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  steps {
 | 
					  steps {
 | 
				
			||||||
    gradle {
 | 
					    gradle {
 | 
				
			||||||
      clearConditions()
 | 
					 | 
				
			||||||
      tasks = "clean test"
 | 
					      tasks = "clean test"
 | 
				
			||||||
      buildFile = ""
 | 
					      buildFile = ""
 | 
				
			||||||
      enableStacktrace = true
 | 
					      enableStacktrace = true
 | 
				
			||||||
      jdkHome = "/usr/lib/jvm/java-17-amazon-corretto"
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								.teamcity/_Self/subprojects/GitHub.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.teamcity/_Self/subprojects/GitHub.kt
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,6 @@
 | 
				
			|||||||
package _Self.subprojects
 | 
					package _Self.subprojects
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import _Self.Constants
 | 
				
			||||||
import _Self.IdeaVimBuildType
 | 
					import _Self.IdeaVimBuildType
 | 
				
			||||||
import _Self.vcsRoots.GitHubPullRequest
 | 
					import _Self.vcsRoots.GitHubPullRequest
 | 
				
			||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode
 | 
					import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode
 | 
				
			||||||
@@ -24,6 +25,7 @@ class GithubBuildType(command: String, desc: String) : IdeaVimBuildType({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  params {
 | 
					  params {
 | 
				
			||||||
    param("env.ORG_GRADLE_PROJECT_downloadIdeaSources", "false")
 | 
					    param("env.ORG_GRADLE_PROJECT_downloadIdeaSources", "false")
 | 
				
			||||||
 | 
					    param("env.ORG_GRADLE_PROJECT_ideaVersion", Constants.GITHUB_TESTS)
 | 
				
			||||||
    param("env.ORG_GRADLE_PROJECT_instrumentPluginCode", "false")
 | 
					    param("env.ORG_GRADLE_PROJECT_instrumentPluginCode", "false")
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										25
									
								
								.teamcity/_Self/subprojects/OldTests.kt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								.teamcity/_Self/subprojects/OldTests.kt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					package _Self.subprojects
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import _Self.buildTypes.TestingBuildType
 | 
				
			||||||
 | 
					import jetbrains.buildServer.configs.kotlin.v2019_2.Project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					object OldTests : Project({
 | 
				
			||||||
 | 
					  name = "Old IdeaVim tests"
 | 
				
			||||||
 | 
					  description = "Tests for older versions of IJ"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  buildType(TestingBuildType("IC-2018.1", "181-182", javaVersion = "1.8", javaPlugin = false))
 | 
				
			||||||
 | 
					  buildType(TestingBuildType("IC-2018.2", "181-182", javaVersion = "1.8", javaPlugin = false))
 | 
				
			||||||
 | 
					  buildType(TestingBuildType("IC-2018.3", "183", javaVersion = "1.8", javaPlugin = false))
 | 
				
			||||||
 | 
					  buildType(TestingBuildType("IC-2019.1", "191-193", javaVersion = "1.8", javaPlugin = false))
 | 
				
			||||||
 | 
					  buildType(TestingBuildType("IC-2019.2", "191-193", javaVersion = "1.8", javaPlugin = false))
 | 
				
			||||||
 | 
					  buildType(TestingBuildType("IC-2019.3", "191-193", javaVersion = "1.8", javaPlugin = false))
 | 
				
			||||||
 | 
					  buildType(TestingBuildType("IC-2020.1", "201", javaVersion = "1.8", javaPlugin = false))
 | 
				
			||||||
 | 
					  buildType(TestingBuildType("IC-2020.2", "202", javaVersion = "1.8", javaPlugin = false))
 | 
				
			||||||
 | 
					  buildType(TestingBuildType("IC-2020.3", "203-212", javaVersion = "1.8", javaPlugin = false))
 | 
				
			||||||
 | 
					  buildType(TestingBuildType("IC-2021.1", "203-212", javaVersion = "1.8", javaPlugin = false))
 | 
				
			||||||
 | 
					  buildType(TestingBuildType("IC-2021.2.2", "203-212", javaVersion = "1.8", javaPlugin = false))
 | 
				
			||||||
 | 
					  buildType(TestingBuildType("IC-2021.3.2", "213-221", javaVersion = "1.8", javaPlugin = false))
 | 
				
			||||||
 | 
					  buildType(TestingBuildType("IC-2022.2.3", branch = "222", javaPlugin = false))
 | 
				
			||||||
 | 
					  buildType(TestingBuildType("IC-2023.1", "231-232", javaPlugin = false))
 | 
				
			||||||
 | 
					  buildType(TestingBuildType("IC-2023.2", "231-232", javaPlugin = false))
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
							
								
								
									
										39
									
								
								.teamcity/patches/buildTypes/IdeaVimTests_Latest_EAP.kts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								.teamcity/patches/buildTypes/IdeaVimTests_Latest_EAP.kts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					package patches.buildTypes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import jetbrains.buildServer.configs.kotlin.v2019_2.*
 | 
				
			||||||
 | 
					import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.GradleBuildStep
 | 
				
			||||||
 | 
					import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
 | 
				
			||||||
 | 
					import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					This patch script was generated by TeamCity on settings change in UI.
 | 
				
			||||||
 | 
					To apply the patch, change the buildType with id = 'IdeaVimTests_Latest_EAP'
 | 
				
			||||||
 | 
					accordingly, and delete the patch script.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					changeBuildType(RelativeId("IdeaVimTests_Latest_EAP")) {
 | 
				
			||||||
 | 
					    check(artifactRules == """
 | 
				
			||||||
 | 
					        +:build/reports => build/reports
 | 
				
			||||||
 | 
					        +:/mnt/agent/temp/buildTmp/ => /mnt/agent/temp/buildTmp/
 | 
				
			||||||
 | 
					    """.trimIndent()) {
 | 
				
			||||||
 | 
					        "Unexpected option value: artifactRules = $artifactRules"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    artifactRules = """
 | 
				
			||||||
 | 
					        +:build/reports => build/reports
 | 
				
			||||||
 | 
					        +:/mnt/agent/temp/buildTmp/ => /mnt/agent/temp/buildTmp/
 | 
				
			||||||
 | 
					        +:tests/java-tests/build/reports => tests/java-tests/build/reports
 | 
				
			||||||
 | 
					    """.trimIndent()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expectSteps {
 | 
				
			||||||
 | 
					        gradle {
 | 
				
			||||||
 | 
					            tasks = "clean test"
 | 
				
			||||||
 | 
					            buildFile = ""
 | 
				
			||||||
 | 
					            enableStacktrace = true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    steps {
 | 
				
			||||||
 | 
					        update<GradleBuildStep>(0) {
 | 
				
			||||||
 | 
					            clearConditions()
 | 
				
			||||||
 | 
					            jdkHome = "/usr/lib/jvm/java-17-amazon-corretto"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								.teamcity/patches/buildTypes/ReleaseEapFromBranch.kts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								.teamcity/patches/buildTypes/ReleaseEapFromBranch.kts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					package patches.buildTypes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import jetbrains.buildServer.configs.kotlin.v2019_2.*
 | 
				
			||||||
 | 
					import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					This patch script was generated by TeamCity on settings change in UI.
 | 
				
			||||||
 | 
					To apply the patch, change the buildType with id = 'ReleaseEapFromBranch'
 | 
				
			||||||
 | 
					accordingly, and delete the patch script.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					changeBuildType(RelativeId("ReleaseEapFromBranch")) {
 | 
				
			||||||
 | 
					    vcs {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        check(branchFilter == "+:heads/(releases/*)") {
 | 
				
			||||||
 | 
					            "Unexpected option value: branchFilter = $branchFilter"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        branchFilter = "heads/releases/*"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										56
									
								
								AUTHORS.md
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								AUTHORS.md
									
									
									
									
									
								
							@@ -511,58 +511,6 @@ Contributors:
 | 
				
			|||||||
  [![icon][github]](https://github.com/Aisper)
 | 
					  [![icon][github]](https://github.com/Aisper)
 | 
				
			||||||
   
 | 
					   
 | 
				
			||||||
  Egor Nikolaevsky
 | 
					  Egor Nikolaevsky
 | 
				
			||||||
* [![icon][mail]](mailto:77796630+throwaway69420-69420@users.noreply.github.com)
 | 
					 | 
				
			||||||
  [![icon][github]](https://github.com/kun-codes)
 | 
					 | 
				
			||||||
   
 | 
					 | 
				
			||||||
  Bishwa Saha, 
 | 
					 | 
				
			||||||
* [![icon][mail]](mailto:alexfu@fastmail.com)
 | 
					 | 
				
			||||||
  [![icon][github]](https://github.com/alexfu)
 | 
					 | 
				
			||||||
   
 | 
					 | 
				
			||||||
  Alex Fu
 | 
					 | 
				
			||||||
* [![icon][mail]](mailto:jakepeters199@hotmail.com)
 | 
					 | 
				
			||||||
  [![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
 | 
					 | 
				
			||||||
* [![icon][mail]](mailto:533601+felixwiemuth@users.noreply.github.com)
 | 
					 | 
				
			||||||
  [![icon][github]](https://github.com/felixwiemuth)
 | 
					 | 
				
			||||||
   
 | 
					 | 
				
			||||||
  Felix Wiemuth
 | 
					 | 
				
			||||||
* [![icon][mail]](mailto:kirill.karnaukhov@jetbrains.com)
 | 
					 | 
				
			||||||
  [![icon][github]](https://github.com/kkarnauk)
 | 
					 | 
				
			||||||
   
 | 
					 | 
				
			||||||
  Kirill Karnaukhov, 
 | 
					 | 
				
			||||||
* [![icon][mail]](mailto:sander.hestvik@gmail.com)
 | 
					 | 
				
			||||||
  [![icon][github]](https://github.com/SanderHestvik)
 | 
					 | 
				
			||||||
   
 | 
					 | 
				
			||||||
  SanderHestvik
 | 
					 | 
				
			||||||
* [![icon][mail]](mailto:gregory.shrago@jetbrains.com)
 | 
					 | 
				
			||||||
  [![icon][github]](https://github.com/gregsh)
 | 
					 | 
				
			||||||
   
 | 
					 | 
				
			||||||
  Greg Shrago
 | 
					 | 
				
			||||||
* [![icon][mail]](mailto:jphalip@gmail.com)
 | 
					 | 
				
			||||||
  [![icon][github]](https://github.com/jphalip)
 | 
					 | 
				
			||||||
   
 | 
					 | 
				
			||||||
  Julien Phalip
 | 
					 | 
				
			||||||
* [![icon][mail]](mailto:j.trimailovas@gmail.com)
 | 
					 | 
				
			||||||
  [![icon][github]](https://github.com/trimailov)
 | 
					 | 
				
			||||||
   
 | 
					 | 
				
			||||||
  Justas Trimailovas, 
 | 
					 | 
				
			||||||
* [![icon][mail]](mailto:justast@wix.com)
 | 
					 | 
				
			||||||
  [![icon][github]](https://github.com/justast-wix)
 | 
					 | 
				
			||||||
   
 | 
					 | 
				
			||||||
  Justas Trimailovas
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Previous contributors:
 | 
					Previous contributors:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -574,10 +522,6 @@ Previous contributors:
 | 
				
			|||||||
  [![icon][github]](https://github.com/kevin70)
 | 
					  [![icon][github]](https://github.com/kevin70)
 | 
				
			||||||
   
 | 
					   
 | 
				
			||||||
  kk
 | 
					  kk
 | 
				
			||||||
* [![icon][mail]](mailto:gregory.shrago@jetbrains.com)
 | 
					 | 
				
			||||||
  [![icon][github]](https://github.com/gregsh)
 | 
					 | 
				
			||||||
   
 | 
					 | 
				
			||||||
  Greg Shrago
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        
 | 
					                        
 | 
				
			||||||
If you are a contributor and your name is not listed here, feel free to
 | 
					If you are a contributor and your name is not listed here, feel free to
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,8 +27,8 @@ usual beta standards.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Since version 2.9.0, the changelog can be found on YouTrack
 | 
					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)
 | 
					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)
 | 
					Latest Fixes: https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20sort%20by:%20updated%20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 2.9.0, 2024-02-20
 | 
					## 2.9.0, 2024-02-20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,16 +62,12 @@ for a few days or send it to a friend for testing.
 | 
				
			|||||||
If you are looking for:
 | 
					If you are looking for:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Vim commands (`w`, `<C-O>`, `p`, etc.):
 | 
					- Vim commands (`w`, `<C-O>`, `p`, etc.):
 | 
				
			||||||
    - Any particular command:
 | 
					    - Any particular command: `package-info.java`.
 | 
				
			||||||
      - [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`.
 | 
					    - How commands are executed in common: `EditorActionHandlerBase`.
 | 
				
			||||||
    - Key mapping: `KeyHandler.handleKey()`.
 | 
					    - Key mapping: `KeyHandler.handleKey()`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Ex commands (`:set`, `:s`, `:nohlsearch`):
 | 
					- Ex commands (`:set`, `:s`, `:nohlsearch`):
 | 
				
			||||||
    - Any particular command:
 | 
					    - Any particular ex command: package `com.maddyhome.idea.vim.ex.handler`.
 | 
				
			||||||
        - [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 grammar: `Vimscript.g4`.
 | 
				
			||||||
    - Vim script parsing: package `com.maddyhome.idea.vim.vimscript.parser`.
 | 
					    - Vim script parsing: package `com.maddyhome.idea.vim.vimscript.parser`.
 | 
				
			||||||
    - Vim script executor: `Executor`.
 | 
					    - Vim script executor: `Executor`.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -109,6 +109,7 @@ etc
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
See also:
 | 
					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)
 | 
					* [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)
 | 
					* [Vimscript support roadmap](vimscript-info/VIMSCRIPT_ROADMAP.md)
 | 
				
			||||||
* [List of supported in-build functions](vimscript-info/FUNCTIONS_INFO.MD)
 | 
					* [List of supported in-build functions](vimscript-info/FUNCTIONS_INFO.MD)
 | 
				
			||||||
@@ -221,13 +222,13 @@ Ex commands or via `:map` command mappings:
 | 
				
			|||||||
    * Execute an action by `{action_id}`. Works from Ex command line.
 | 
					    * Execute an action by `{action_id}`. Works from Ex command line.
 | 
				
			||||||
    * Please don't use `:action` in mappings. Use `<Action>` instead.
 | 
					    * 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`).
 | 
					  This command can be found in "Search everywhere" (double `shift`).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <details>
 | 
					    <details>
 | 
				
			||||||
        <summary><strong>"Track action IDs" Details</strong> (click to see)</summary>
 | 
					        <summary><strong>"Track action Ids" Details</strong> (click to see)</summary>
 | 
				
			||||||
        <picture>
 | 
					        <picture>
 | 
				
			||||||
            <source media="(prefers-color-scheme: dark)" srcset="assets/readme/track_action_dark.gif">
 | 
					            <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"/>
 | 
					            <img src="assets/readme/track_action_light.gif" alt="track action ids"/>
 | 
				
			||||||
@@ -368,8 +369,6 @@ is the full list of synonyms.
 | 
				
			|||||||
- Fancy constants for [undolevels](https://vimhelp.org/options.txt.html#%27undolevels%27):
 | 
					- 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.
 | 
					  > 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
 | 
					License
 | 
				
			||||||
-------
 | 
					-------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
IdeaVim project is licensed under MIT license except the following parts of it:
 | 
					IdeaVim project is licensed under MIT license except the following parts of it:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* File [RegExp.kt](src/main/java/com/maddyhome/idea/vim/regexp/RegExp.kt) is licensed under Vim License.  
 | 
				
			||||||
* File [ScrollViewHelper.kt](com/maddyhome/idea/vim/helper/ScrollViewHelper.kt) is licensed under Vim License.
 | 
					* File [ScrollViewHelper.kt](com/maddyhome/idea/vim/helper/ScrollViewHelper.kt) is licensed under Vim License.
 | 
				
			||||||
* File [Tutor.kt](src/main/java/com/maddyhome/idea/vim/ui/Tutor.kt) is licensed under Vim License.
 | 
					* File [Tutor.kt](src/main/java/com/maddyhome/idea/vim/ui/Tutor.kt) is licensed under Vim License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
plugins {
 | 
					plugins {
 | 
				
			||||||
  kotlin("jvm")
 | 
					  kotlin("jvm")
 | 
				
			||||||
  kotlin("plugin.serialization") version "2.0.0"
 | 
					  kotlin("plugin.serialization") version "1.9.22"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
val kotlinxSerializationVersion: String by project
 | 
					val kotlinxSerializationVersion: String by project
 | 
				
			||||||
@@ -21,7 +21,7 @@ repositories {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
  compileOnly("com.google.devtools.ksp:symbol-processing-api:2.1.0-1.0.29")
 | 
					  compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.24-1.0.20")
 | 
				
			||||||
  implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:$kotlinxSerializationVersion") {
 | 
					  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
 | 
					    // kotlin stdlib is provided by IJ, so there is no need to include it into the distribution
 | 
				
			||||||
    exclude("org.jetbrains.kotlin", "kotlin-stdlib")
 | 
					    exclude("org.jetbrains.kotlin", "kotlin-stdlib")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,8 +37,7 @@ class CommandOrMotionProcessor(private val environment: SymbolProcessorEnvironme
 | 
				
			|||||||
    Files.createDirectories(generatedDirPath)
 | 
					    Files.createDirectories(generatedDirPath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val filePath = generatedDirPath.resolve(environment.options["commands_file"]!!)
 | 
					    val filePath = generatedDirPath.resolve(environment.options["commands_file"]!!)
 | 
				
			||||||
    val sortedCommands = commands.sortedWith(compareBy({ it.keys }, { it.`class` }))
 | 
					    val fileContent = json.encodeToString(commands)
 | 
				
			||||||
    val fileContent = json.encodeToString(sortedCommands)
 | 
					 | 
				
			||||||
    filePath.writeText(fileContent)
 | 
					    filePath.writeText(fileContent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return emptyList()
 | 
					    return emptyList()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,8 +37,7 @@ class ExCommandProcessor(private val environment: SymbolProcessorEnvironment): S
 | 
				
			|||||||
    Files.createDirectories(generatedDirPath)
 | 
					    Files.createDirectories(generatedDirPath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val filePath = generatedDirPath.resolve(environment.options["ex_commands_file"]!!)
 | 
					    val filePath = generatedDirPath.resolve(environment.options["ex_commands_file"]!!)
 | 
				
			||||||
    val sortedCommandToClass = commandToClass.toList().sortedWith(compareBy({ it.first }, { it.second })).toMap()
 | 
					    val fileContent = json.encodeToString(commandToClass)
 | 
				
			||||||
    val fileContent = json.encodeToString(sortedCommandToClass)
 | 
					 | 
				
			||||||
    filePath.writeText(fileContent)
 | 
					    filePath.writeText(fileContent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return emptyList()
 | 
					    return emptyList()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,8 +37,7 @@ class VimscriptFunctionProcessor(private val environment: SymbolProcessorEnviron
 | 
				
			|||||||
    Files.createDirectories(generatedDirPath)
 | 
					    Files.createDirectories(generatedDirPath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val filePath = generatedDirPath.resolve(environment.options["vimscript_functions_file"]!!)
 | 
					    val filePath = generatedDirPath.resolve(environment.options["vimscript_functions_file"]!!)
 | 
				
			||||||
    val sortedNameToClass = nameToClass.toList().sortedWith(compareBy({ it.first }, { it.second })).toMap()
 | 
					    val fileContent = json.encodeToString(nameToClass)
 | 
				
			||||||
    val fileContent = json.encodeToString(sortedNameToClass)
 | 
					 | 
				
			||||||
    filePath.writeText(fileContent)
 | 
					    filePath.writeText(fileContent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return emptyList()
 | 
					    return emptyList()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@ import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
 | 
				
			|||||||
import com.google.devtools.ksp.processing.SymbolProcessorProvider
 | 
					import com.google.devtools.ksp.processing.SymbolProcessorProvider
 | 
				
			||||||
import com.intellij.vim.processors.VimscriptFunctionProcessor
 | 
					import com.intellij.vim.processors.VimscriptFunctionProcessor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class VimscriptFunctionProcessorProvider : SymbolProcessorProvider {
 | 
					public class VimscriptFunctionProcessorProvider : SymbolProcessorProvider {
 | 
				
			||||||
  override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
 | 
					  override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
 | 
				
			||||||
    return VimscriptFunctionProcessor(environment)
 | 
					    return VimscriptFunctionProcessor(environment)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										316
									
								
								build.gradle.kts
									
									
									
									
									
								
							
							
						
						
									
										316
									
								
								build.gradle.kts
									
									
									
									
									
								
							@@ -32,8 +32,6 @@ import org.eclipse.jgit.api.Git
 | 
				
			|||||||
import org.eclipse.jgit.lib.RepositoryBuilder
 | 
					import org.eclipse.jgit.lib.RepositoryBuilder
 | 
				
			||||||
import org.intellij.markdown.ast.getTextInNode
 | 
					import org.intellij.markdown.ast.getTextInNode
 | 
				
			||||||
import org.jetbrains.changelog.Changelog
 | 
					import org.jetbrains.changelog.Changelog
 | 
				
			||||||
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
 | 
					 | 
				
			||||||
import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware
 | 
					 | 
				
			||||||
import org.kohsuke.github.GHUser
 | 
					import org.kohsuke.github.GHUser
 | 
				
			||||||
import java.net.HttpURLConnection
 | 
					import java.net.HttpURLConnection
 | 
				
			||||||
import java.net.URL
 | 
					import java.net.URL
 | 
				
			||||||
@@ -45,19 +43,19 @@ buildscript {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  dependencies {
 | 
					  dependencies {
 | 
				
			||||||
    classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0")
 | 
					    classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22")
 | 
				
			||||||
    classpath("com.github.AlexPl292:mark-down-to-slack:1.1.2")
 | 
					    classpath("com.github.AlexPl292:mark-down-to-slack:1.1.2")
 | 
				
			||||||
    classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
 | 
					    classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // This is needed for jgit to connect to ssh
 | 
					    // This is needed for jgit to connect to ssh
 | 
				
			||||||
    classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.1.0.202411261347-r")
 | 
					    classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.9.0.202403050737-r")
 | 
				
			||||||
    classpath("org.kohsuke:github-api:1.305")
 | 
					    classpath("org.kohsuke:github-api:1.305")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    classpath("io.ktor:ktor-client-core:3.0.2")
 | 
					    classpath("io.ktor:ktor-client-core:2.3.11")
 | 
				
			||||||
    classpath("io.ktor:ktor-client-cio:3.0.2")
 | 
					    classpath("io.ktor:ktor-client-cio:2.3.10")
 | 
				
			||||||
    classpath("io.ktor:ktor-client-auth:3.0.2")
 | 
					    classpath("io.ktor:ktor-client-auth:2.3.11")
 | 
				
			||||||
    classpath("io.ktor:ktor-client-content-negotiation:3.0.2")
 | 
					    classpath("io.ktor:ktor-client-content-negotiation:2.3.10")
 | 
				
			||||||
    classpath("io.ktor:ktor-serialization-kotlinx-json:3.0.2")
 | 
					    classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.11")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // This comes from the changelog plugin
 | 
					    // This comes from the changelog plugin
 | 
				
			||||||
//        classpath("org.jetbrains:markdown:0.3.1")
 | 
					//        classpath("org.jetbrains:markdown:0.3.1")
 | 
				
			||||||
@@ -65,25 +63,44 @@ buildscript {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
plugins {
 | 
					plugins {
 | 
				
			||||||
 | 
					  antlr
 | 
				
			||||||
  java
 | 
					  java
 | 
				
			||||||
  kotlin("jvm") version "2.0.0"
 | 
					  kotlin("jvm") version "1.9.22"
 | 
				
			||||||
  application
 | 
					  application
 | 
				
			||||||
  id("java-test-fixtures")
 | 
					  id("java-test-fixtures")
 | 
				
			||||||
  id("org.jetbrains.intellij.platform") version "2.2.0"
 | 
					
 | 
				
			||||||
  id("org.jetbrains.changelog") version "2.2.1"
 | 
					  id("org.jetbrains.intellij") version "1.17.3"
 | 
				
			||||||
 | 
					  id("org.jetbrains.changelog") version "2.2.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  id("org.jetbrains.kotlinx.kover") version "0.6.1"
 | 
					  id("org.jetbrains.kotlinx.kover") version "0.6.1"
 | 
				
			||||||
  id("com.dorongold.task-tree") version "4.0.0"
 | 
					  id("com.dorongold.task-tree") version "3.0.0"
 | 
				
			||||||
  id("com.google.devtools.ksp") version "2.0.0-1.0.23"
 | 
					
 | 
				
			||||||
 | 
					  id("com.google.devtools.ksp") version "1.9.22-1.0.17"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
val moduleSources by configurations.registering
 | 
					ksp {
 | 
				
			||||||
 | 
					  arg("generated_directory", "$projectDir/src/main/resources/ksp-generated")
 | 
				
			||||||
 | 
					  arg("vimscript_functions_file", "intellij_vimscript_functions.json")
 | 
				
			||||||
 | 
					  arg("ex_commands_file", "intellij_ex_commands.json")
 | 
				
			||||||
 | 
					  arg("commands_file", "intellij_commands.json")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					afterEvaluate {
 | 
				
			||||||
 | 
					//  tasks.named("kspKotlin").configure { dependsOn("clean") }
 | 
				
			||||||
 | 
					  tasks.named("kspKotlin").configure { dependsOn("generateGrammarSource") }
 | 
				
			||||||
 | 
					  tasks.named("kspTestFixturesKotlin").configure { enabled = false }
 | 
				
			||||||
 | 
					  tasks.named("kspTestFixturesKotlin").configure { enabled = false }
 | 
				
			||||||
 | 
					  tasks.named("kspTestKotlin").configure { enabled = false }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Import variables from gradle.properties file
 | 
					// Import variables from gradle.properties file
 | 
				
			||||||
val javaVersion: String by project
 | 
					val javaVersion: String by project
 | 
				
			||||||
val kotlinVersion: String by project
 | 
					val kotlinVersion: String by project
 | 
				
			||||||
val ideaVersion: String by project
 | 
					val ideaVersion: String by project
 | 
				
			||||||
val ideaType: String by project
 | 
					val ideaType: String by project
 | 
				
			||||||
 | 
					val downloadIdeaSources: String by project
 | 
				
			||||||
val instrumentPluginCode: String by project
 | 
					val instrumentPluginCode: String by project
 | 
				
			||||||
 | 
					val antlrVersion: String by project
 | 
				
			||||||
val remoteRobotVersion: String by project
 | 
					val remoteRobotVersion: String by project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
val publishChannels: String by project
 | 
					val publishChannels: String by project
 | 
				
			||||||
@@ -92,49 +109,25 @@ val publishToken: String by project
 | 
				
			|||||||
val slackUrl: String by project
 | 
					val slackUrl: String by project
 | 
				
			||||||
val youtrackToken: String by project
 | 
					val youtrackToken: String by project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
val releaseType: String? by project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
repositories {
 | 
					repositories {
 | 
				
			||||||
  mavenCentral()
 | 
					  mavenCentral()
 | 
				
			||||||
  intellijPlatform {
 | 
					  maven { url = uri("https://cache-redirector.jetbrains.com/intellij-dependencies") }
 | 
				
			||||||
    defaultRepositories()
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
  api(project(":vim-engine"))
 | 
					  api(project(":vim-engine"))
 | 
				
			||||||
  ksp(project(":annotation-processors"))
 | 
					  ksp(project(":annotation-processors"))
 | 
				
			||||||
  compileOnly(project(":annotation-processors"))
 | 
					  implementation(project(":annotation-processors"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
 | 
					  compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
 | 
				
			||||||
  compileOnly("org.jetbrains:annotations:26.0.1")
 | 
					  compileOnly("org.jetbrains:annotations:24.1.0")
 | 
				
			||||||
 | 
					  runtimeOnly("org.antlr:antlr4-runtime:$antlrVersion")
 | 
				
			||||||
  intellijPlatform {
 | 
					  antlr("org.antlr:antlr4:$antlrVersion")
 | 
				
			||||||
    // Snapshots don't use installers
 | 
					 | 
				
			||||||
    // https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html#target-versions-installers
 | 
					 | 
				
			||||||
    val useInstaller = "EAP-SNAPSHOT" !in ideaVersion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Note that it is also possible to use local("...") to compile against a locally installed IDE
 | 
					 | 
				
			||||||
    // E.g. local("/Users/{user}/Applications/IntelliJ IDEA Ultimate.app")
 | 
					 | 
				
			||||||
    // Or something like: intellijIdeaUltimate(ideaVersion)
 | 
					 | 
				
			||||||
    create(ideaType, ideaVersion, useInstaller)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pluginVerifier()
 | 
					 | 
				
			||||||
    zipSigner()
 | 
					 | 
				
			||||||
    instrumentationTools()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    testFramework(TestFrameworkType.Platform)
 | 
					 | 
				
			||||||
    testFramework(TestFrameworkType.JUnit5)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // AceJump is an optional dependency. We use their SessionManager class to check if it's active
 | 
					 | 
				
			||||||
    plugin("AceJump", "3.8.19")
 | 
					 | 
				
			||||||
    plugin("com.intellij.classic.ui", "242.20224.159")
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  moduleSources(project(":vim-engine", "sourcesJarArtifacts"))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // --------- Test dependencies ----------
 | 
					  // --------- Test dependencies ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  testImplementation(testFixtures(project(":")))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  testApi("com.squareup.okhttp3:okhttp:4.12.0")
 | 
					  testApi("com.squareup.okhttp3:okhttp:4.12.0")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // https://mvnrepository.com/artifact/com.ensarsarajcic.neovim.java/neovim-api
 | 
					  // https://mvnrepository.com/artifact/com.ensarsarajcic.neovim.java/neovim-api
 | 
				
			||||||
@@ -148,20 +141,14 @@ dependencies {
 | 
				
			|||||||
  testFixturesImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
 | 
					  testFixturesImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin
 | 
					  // https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin
 | 
				
			||||||
  testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
 | 
					  testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.5")
 | 
					  testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
 | 
				
			||||||
  testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.5")
 | 
					  testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
 | 
				
			||||||
  testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.5")
 | 
					  testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
 | 
				
			||||||
  testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.5")
 | 
					  testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
 | 
				
			||||||
  testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.5")
 | 
					  testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
 | 
				
			||||||
  testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.5")
 | 
					  testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
 | 
				
			||||||
 | 
					 | 
				
			||||||
  // 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.5")
 | 
					 | 
				
			||||||
//  testFixturesImplementation("org.junit.vintage:junit-vintage-engine:5.10.3")
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
configurations {
 | 
					configurations {
 | 
				
			||||||
@@ -172,8 +159,6 @@ configurations {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
tasks {
 | 
					tasks {
 | 
				
			||||||
  test {
 | 
					  test {
 | 
				
			||||||
    useJUnitPlatform()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Set teamcity env variable locally to run additional tests for leaks.
 | 
					    // Set teamcity env variable locally to run additional tests for leaks.
 | 
				
			||||||
    // By default, this test runs on TC only, but this test doesn't take a lot of time,
 | 
					    // By default, this test runs on TC only, but this test doesn't take a lot of time,
 | 
				
			||||||
    //   so we can turn it on for local development
 | 
					    //   so we can turn it on for local development
 | 
				
			||||||
@@ -186,9 +171,6 @@ tasks {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  compileJava {
 | 
					  compileJava {
 | 
				
			||||||
    // CodeQL can't resolve the 'by project' property, so we need to give it a hint. This is the minimum version we need
 | 
					 | 
				
			||||||
    // so doesn't have to match exactly
 | 
					 | 
				
			||||||
    // Hint for the CodeQL autobuilder: sourceCompatibility = 17
 | 
					 | 
				
			||||||
    sourceCompatibility = javaVersion
 | 
					    sourceCompatibility = javaVersion
 | 
				
			||||||
    targetCompatibility = javaVersion
 | 
					    targetCompatibility = javaVersion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -201,91 +183,34 @@ tasks {
 | 
				
			|||||||
      // See https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#kotlin-standard-library
 | 
					      // See https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#kotlin-standard-library
 | 
				
			||||||
      // For the list of bundled versions
 | 
					      // For the list of bundled versions
 | 
				
			||||||
      apiVersion = "1.9"
 | 
					      apiVersion = "1.9"
 | 
				
			||||||
      freeCompilerArgs = listOf(
 | 
					      freeCompilerArgs = listOf("-Xjvm-default=all-compatibility")
 | 
				
			||||||
        "-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
 | 
					//            allWarningsAsErrors = true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  compileTestKotlin {
 | 
					  compileTestKotlin {
 | 
				
			||||||
    enabled = false
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    kotlinOptions {
 | 
					    kotlinOptions {
 | 
				
			||||||
      jvmTarget = javaVersion
 | 
					      jvmTarget = javaVersion
 | 
				
			||||||
      apiVersion = "1.9"
 | 
					      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
 | 
					//            allWarningsAsErrors = true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Note that this will run the plugin installed in the IDE specified in dependencies. To run in a different IDE, use
 | 
					  downloadRobotServerPlugin {
 | 
				
			||||||
  // a custom task (see below)
 | 
					    version.set(remoteRobotVersion)
 | 
				
			||||||
  runIde {
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  runIdeForUiTests {
 | 
				
			||||||
 | 
					    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)
 | 
					    systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Uncomment to run the plugin in a custom IDE, rather than the IDE specified as a compile target in dependencies
 | 
					  runIde {
 | 
				
			||||||
  // Note that the version must be greater than the plugin's target version, for obvious reasons
 | 
					    systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
 | 
				
			||||||
  // You can also set splitMode and splitModeTarget here to test split mode in a custom IDE
 | 
					 | 
				
			||||||
//  val runIdeCustom by intellijPlatformTesting.runIde.registering {
 | 
					 | 
				
			||||||
//    type = IntelliJPlatformType.Rider
 | 
					 | 
				
			||||||
//    version = "2024.1.2"
 | 
					 | 
				
			||||||
//  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Uncomment to run the plugin in a locally installed IDE
 | 
					 | 
				
			||||||
//  val runIdeLocal by intellijPlatformTesting.runIde.registering {
 | 
					 | 
				
			||||||
//    localPath = file("/Users/{user}/Applications/WebStorm.app")
 | 
					 | 
				
			||||||
//  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  val 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
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Add plugin open API sources to the plugin ZIP
 | 
					 | 
				
			||||||
  val sourcesJar by registering(Jar::class) {
 | 
					 | 
				
			||||||
    dependsOn(moduleSources)
 | 
					 | 
				
			||||||
    destinationDirectory.set(layout.buildDirectory.dir("libs"))
 | 
					 | 
				
			||||||
    archiveClassifier.set(DocsType.SOURCES)
 | 
					 | 
				
			||||||
    from(sourceSets.main.map { it.kotlin })
 | 
					 | 
				
			||||||
    from(provider {
 | 
					 | 
				
			||||||
      moduleSources.map {
 | 
					 | 
				
			||||||
        it.map { jarFile -> zipTree(jarFile) }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  buildPlugin {
 | 
					 | 
				
			||||||
    dependsOn(sourcesJar)
 | 
					 | 
				
			||||||
    from(sourcesJar) { into("lib/src") }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -296,6 +221,7 @@ java {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
kotlin {
 | 
					kotlin {
 | 
				
			||||||
 | 
					  explicitApi()
 | 
				
			||||||
  jvmToolchain {
 | 
					  jvmToolchain {
 | 
				
			||||||
    languageVersion.set(JavaLanguageVersion.of(javaVersion))
 | 
					    languageVersion.set(JavaLanguageVersion.of(javaVersion))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -310,68 +236,96 @@ gradle.projectsEvaluated {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// --- Intellij plugin
 | 
					// --- Intellij plugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
intellijPlatform {
 | 
					intellij {
 | 
				
			||||||
  pluginConfiguration {
 | 
					  version.set(ideaVersion)
 | 
				
			||||||
    name = "IdeaVim"
 | 
					  type.set(ideaType)
 | 
				
			||||||
    changeNotes.set(
 | 
					  pluginName.set("IdeaVim")
 | 
				
			||||||
      """
 | 
					 | 
				
			||||||
        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 {
 | 
					  updateSinceUntilBuild.set(false)
 | 
				
			||||||
      // Let the Gradle plugin set the since-build version. It defaults to the version of the IDE we're building against
 | 
					 | 
				
			||||||
      // specified as two components, `{branch}.{build}` (e.g., "241.15989"). There is no third component specified.
 | 
					 | 
				
			||||||
      // The until-build version defaults to `{branch}.*`, but we want to support _all_ future versions, so we set it
 | 
					 | 
				
			||||||
      // with a null provider (the provider is important).
 | 
					 | 
				
			||||||
      // By letting the Gradle plugin handle this, the Plugin DevKit IntelliJ plugin cannot help us with the "Usage of
 | 
					 | 
				
			||||||
      // IntelliJ API not available in older IDEs" inspection. However, since our since-build is the version we compile
 | 
					 | 
				
			||||||
      // against, we can never get an API that's newer - it would be an unresolved symbol.
 | 
					 | 
				
			||||||
      untilBuild.set(provider { null })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  publishing {
 | 
					  downloadSources.set(downloadIdeaSources.toBoolean())
 | 
				
			||||||
 | 
					  instrumentCode.set(instrumentPluginCode.toBoolean())
 | 
				
			||||||
 | 
					  intellijRepository.set("https://www.jetbrains.com/intellij-repository")
 | 
				
			||||||
 | 
					  plugins.set(listOf("AceJump:3.8.11"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tasks {
 | 
				
			||||||
 | 
					  publishPlugin {
 | 
				
			||||||
    channels.set(publishChannels.split(","))
 | 
					    channels.set(publishChannels.split(","))
 | 
				
			||||||
    token.set(publishToken)
 | 
					    token.set(publishToken)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  signing {
 | 
					  signPlugin {
 | 
				
			||||||
    certificateChain.set(providers.environmentVariable("CERTIFICATE_CHAIN"))
 | 
					    certificateChain.set(providers.environmentVariable("CERTIFICATE_CHAIN"))
 | 
				
			||||||
    privateKey.set(providers.environmentVariable("PRIVATE_KEY"))
 | 
					    privateKey.set(providers.environmentVariable("PRIVATE_KEY"))
 | 
				
			||||||
    password.set(providers.environmentVariable("PRIVATE_KEY_PASSWORD"))
 | 
					    password.set(providers.environmentVariable("PRIVATE_KEY_PASSWORD"))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  verifyPlugin {
 | 
					  runPluginVerifier {
 | 
				
			||||||
    teamCityOutputFormat = true
 | 
					    downloadDir.set("${project.buildDir}/pluginVerifier/ides")
 | 
				
			||||||
    ides {
 | 
					    teamCityOutputFormat.set(true)
 | 
				
			||||||
      recommended()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  instrumentCode.set(instrumentPluginCode.toBoolean())
 | 
					  generateGrammarSource {
 | 
				
			||||||
 | 
					    maxHeapSize = "128m"
 | 
				
			||||||
 | 
					    arguments.addAll(listOf("-package", "com.maddyhome.idea.vim.vimscript.parser.generated", "-visitor"))
 | 
				
			||||||
 | 
					    outputDirectory = file("src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated")
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  named("compileKotlin") {
 | 
				
			||||||
 | 
					    dependsOn("generateGrammarSource")
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  named("compileTestKotlin") {
 | 
				
			||||||
 | 
					    dependsOn("generateTestGrammarSource")
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  named("compileTestFixturesKotlin") {
 | 
				
			||||||
 | 
					    dependsOn("generateTestFixturesGrammarSource")
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Add plugin open API sources to the plugin ZIP
 | 
				
			||||||
 | 
					  val createOpenApiSourceJar by registering(Jar::class) {
 | 
				
			||||||
 | 
					    dependsOn("generateGrammarSource")
 | 
				
			||||||
 | 
					    // Java sources
 | 
				
			||||||
 | 
					    from(sourceSets.main.get().java) {
 | 
				
			||||||
 | 
					      include("**/com/maddyhome/idea/vim/**/*.java")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    from(project(":vim-engine").sourceSets.main.get().java) {
 | 
				
			||||||
 | 
					      include("**/com/maddyhome/idea/vim/**/*.java")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // Kotlin sources
 | 
				
			||||||
 | 
					    from(kotlin.sourceSets.main.get().kotlin) {
 | 
				
			||||||
 | 
					      include("**/com/maddyhome/idea/vim/**/*.kt")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    from(project(":vim-engine").kotlin.sourceSets.main.get().kotlin) {
 | 
				
			||||||
 | 
					      include("**/com/maddyhome/idea/vim/**/*.kt")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    destinationDirectory.set(layout.buildDirectory.dir("libs"))
 | 
				
			||||||
 | 
					    archiveClassifier.set("src")
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  buildPlugin {
 | 
				
			||||||
 | 
					    dependsOn(createOpenApiSourceJar)
 | 
				
			||||||
 | 
					    from(createOpenApiSourceJar) { into("lib/src") }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  patchPluginXml {
 | 
				
			||||||
 | 
					    // Don't forget to update plugin.xml
 | 
				
			||||||
 | 
					    sinceBuild.set("241.15989.150")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    changeNotes.set(
 | 
				
			||||||
 | 
					      """<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>"""
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ksp {
 | 
					// --- Tests
 | 
				
			||||||
  arg("generated_directory", "$projectDir/src/main/resources/ksp-generated")
 | 
					 | 
				
			||||||
  arg("vimscript_functions_file", "intellij_vimscript_functions.json")
 | 
					 | 
				
			||||||
  arg("ex_commands_file", "intellij_ex_commands.json")
 | 
					 | 
				
			||||||
  arg("commands_file", "intellij_commands.json")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
afterEvaluate {
 | 
					tasks {
 | 
				
			||||||
//  tasks.named("kspKotlin").configure { dependsOn("clean") }
 | 
					  test {
 | 
				
			||||||
  tasks.named("kspTestFixturesKotlin").configure { enabled = false }
 | 
					    useJUnitPlatform()
 | 
				
			||||||
  tasks.named("kspTestFixturesKotlin").configure { enabled = false }
 | 
					  }
 | 
				
			||||||
  tasks.named("kspTestKotlin").configure { enabled = false }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
// --- Changelog
 | 
					// --- Changelog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
changelog {
 | 
					changelog {
 | 
				
			||||||
@@ -497,8 +451,6 @@ val fixVersionsElementType = "VersionBundleElement"
 | 
				
			|||||||
tasks.register("releaseActions") {
 | 
					tasks.register("releaseActions") {
 | 
				
			||||||
  group = "other"
 | 
					  group = "other"
 | 
				
			||||||
  doLast {
 | 
					  doLast {
 | 
				
			||||||
    if (releaseType == "patch") return@doLast
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val tickets = getYoutrackTicketsByQuery("%23%7BReady+To+Release%7D%20and%20tag:%20%7BIdeaVim%20Released%20In%20EAP%7D%20")
 | 
					    val tickets = getYoutrackTicketsByQuery("%23%7BReady+To+Release%7D%20and%20tag:%20%7BIdeaVim%20Released%20In%20EAP%7D%20")
 | 
				
			||||||
    if (tickets.isNotEmpty()) {
 | 
					    if (tickets.isNotEmpty()) {
 | 
				
			||||||
      println("Updating statuses for tickets: $tickets")
 | 
					      println("Updating statuses for tickets: $tickets")
 | 
				
			||||||
@@ -958,12 +910,12 @@ fun changes(): List<Change> {
 | 
				
			|||||||
  println("Start changes processing")
 | 
					  println("Start changes processing")
 | 
				
			||||||
  for (message in messages) {
 | 
					  for (message in messages) {
 | 
				
			||||||
    println("Processing '$message'...")
 | 
					    println("Processing '$message'...")
 | 
				
			||||||
    val lowercaseMessage = message.lowercase()
 | 
					    val lowercaseMessage = message.toLowerCase()
 | 
				
			||||||
    val regex = "^fix\\((vim-\\d+)\\):".toRegex()
 | 
					    val regex = "^fix\\((vim-\\d+)\\):".toRegex()
 | 
				
			||||||
    val findResult = regex.find(lowercaseMessage)
 | 
					    val findResult = regex.find(lowercaseMessage)
 | 
				
			||||||
    if (findResult != null) {
 | 
					    if (findResult != null) {
 | 
				
			||||||
      println("Message matches")
 | 
					      println("Message matches")
 | 
				
			||||||
      val value = findResult.groups[1]!!.value.uppercase()
 | 
					      val value = findResult.groups[1]!!.value.toUpperCase()
 | 
				
			||||||
      val shortMessage = message.drop(findResult.range.last + 1).trim()
 | 
					      val shortMessage = message.drop(findResult.range.last + 1).trim()
 | 
				
			||||||
      newFixes += Change(value, shortMessage)
 | 
					      newFixes += Change(value, shortMessage)
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -130,25 +130,7 @@ Original plugin: [vim-multiple-cursors](https://github.com/terryma/vim-multiple-
 | 
				
			|||||||
   
 | 
					   
 | 
				
			||||||
### Instructions
 | 
					### Instructions
 | 
				
			||||||
   
 | 
					   
 | 
				
			||||||
At the moment, the default key binds for this plugin do not get mapped correctly in IdeaVim (see [VIM-2178](https://youtrack.jetbrains.com/issue/VIM-2178)). To enable the default key binds, add the following to your `.ideavimrc` file...
 | 
					https://github.com/terryma/vim-multiple-cursors/blob/master/doc/multiple_cursors.txt
 | 
				
			||||||
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
" Remap multiple-cursors shortcuts to match terryma/vim-multiple-cursors
 | 
					 | 
				
			||||||
nmap <C-n> <Plug>NextWholeOccurrence
 | 
					 | 
				
			||||||
xmap <C-n> <Plug>NextWholeOccurrence
 | 
					 | 
				
			||||||
nmap g<C-n> <Plug>NextOccurrence
 | 
					 | 
				
			||||||
xmap g<C-n> <Plug>NextOccurrence
 | 
					 | 
				
			||||||
xmap <C-x> <Plug>SkipOccurrence
 | 
					 | 
				
			||||||
xmap <C-p> <Plug>RemoveOccurrence
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
" Note that the default <A-n> and g<A-n> shortcuts don't work on Mac due to dead keys.
 | 
					 | 
				
			||||||
" <A-n> is used to enter accented text e.g. ñ
 | 
					 | 
				
			||||||
" Feel free to pick your own mappings that are not affected. I like to use <leader>
 | 
					 | 
				
			||||||
nmap <leader><C-n> <Plug>AllWholeOccurrences
 | 
					 | 
				
			||||||
xmap <leader><C-n> <Plug>AllWholeOccurrences
 | 
					 | 
				
			||||||
nmap <leader>g<C-n> <Plug>AllOccurrences
 | 
					 | 
				
			||||||
xmap <leader>g<C-n> <Plug>AllOccurrences
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
</details>
 | 
					</details>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -322,9 +304,6 @@ If you want to optimize highlight duration, assign a time in milliseconds:
 | 
				
			|||||||
If you want to change background color of highlight you can provide the rgba of the color you want e.g.  
 | 
					If you want to change background color of highlight you can provide the rgba of the color you want e.g.  
 | 
				
			||||||
      `let g:highlightedyank_highlight_color = "rgba(160, 160, 160, 155)"`
 | 
					      `let g:highlightedyank_highlight_color = "rgba(160, 160, 160, 155)"`
 | 
				
			||||||
   
 | 
					   
 | 
				
			||||||
If you want to change text color of highlight you can provide the rgba of the color you want e.g.  
 | 
					 | 
				
			||||||
`let g:highlightedyank_highlight_foreground_color = "rgba(0, 0, 0, 255)"`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
https://github.com/machakann/vim-highlightedyank/blob/master/doc/highlightedyank.txt
 | 
					https://github.com/machakann/vim-highlightedyank/blob/master/doc/highlightedyank.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</details>
 | 
					</details>
 | 
				
			||||||
@@ -438,50 +417,3 @@ Original plugin: [vim-which-key](https://github.com/liuchengxu/vim-which-key).
 | 
				
			|||||||
https://github.com/TheBlob42/idea-which-key?tab=readme-ov-file#installation
 | 
					https://github.com/TheBlob42/idea-which-key?tab=readme-ov-file#installation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</details>
 | 
					</details>
 | 
				
			||||||
<details>
 | 
					 | 
				
			||||||
<summary><h2>Vim Peekaboo</h2></summary>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
By Julien Phalip  
 | 
					 | 
				
			||||||
Original plugin: [vim-peekaboo](https://github.com/junegunn/vim-peekaboo).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Setup
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add `set peekaboo` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
 | 
					 | 
				
			||||||
or restart the IDE.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Instructions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
https://plugins.jetbrains.com/plugin/25776-vim-peekaboo
 | 
					 | 
				
			||||||
</details>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<details>
 | 
					 | 
				
			||||||
<summary><h2>FunctionTextObj</h2></summary>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
By Julien Phalip  
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Setup
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add `set functiontextobj` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
 | 
					 | 
				
			||||||
or restart the IDE.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Instructions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
https://plugins.jetbrains.com/plugin/25897-vim-functiontextobj
 | 
					 | 
				
			||||||
</details>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<details>
 | 
					 | 
				
			||||||
<summary><h2>Switch</h2></summary>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
By Julien Phalip  
 | 
					 | 
				
			||||||
Original plugin: [switch.vim](https://github.com/AndrewRadev/switch.vim).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Setup
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add `set switch` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
 | 
					 | 
				
			||||||
or restart the IDE.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Instructions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
https://plugins.jetbrains.com/plugin/25899-vim-switch
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</details>
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,33 +40,30 @@ Plug 'nerdtree'
 | 
				
			|||||||
- `:NERDTreeFind`
 | 
					- `:NERDTreeFind`
 | 
				
			||||||
- `:NERDTreeRefreshRoot`
 | 
					- `:NERDTreeRefreshRoot`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| Key     | Description                                            | Map Setting                    |
 | 
					| Key     |  Description                                            |  Map Setting                   |
 | 
				
			||||||
|---------|--------------------------------------------------------|--------------------------------|
 | 
					|---------|---------------------------------------------------------|--------------------------------|
 | 
				
			||||||
| `o`     | Open files, directories and bookmarks                  | `g:NERDTreeMapActivateNode`    |
 | 
					| `o`     |  Open files, directories and bookmarks                  | `g:NERDTreeMapActivateNode`    |
 | 
				
			||||||
| `go`    | Open selected file, but leave cursor in the NERDTree   | `g:NERDTreeMapPreview`         |
 | 
					| `go`    |  Open selected file, but leave cursor in the NERDTree   | `g:NERDTreeMapPreview`         |
 | 
				
			||||||
| `t`     | Open selected node/bookmark in a new tab               | `g:NERDTreeMapOpenInTab`       |
 | 
					| `t`     |  Open selected node/bookmark in a new tab               | `g:NERDTreeMapOpenInTab`       |
 | 
				
			||||||
| `T`     | Same as 't' but keep the focus on the current tab      | `g:NERDTreeMapOpenInTabSilent` |
 | 
					| `T`     |  Same as 't' but keep the focus on the current tab      | `g:NERDTreeMapOpenInTabSilent` |
 | 
				
			||||||
| `i`     | Open selected file in a split window                   | `g:NERDTreeMapOpenSplit`       |
 | 
					| `i`     |  Open selected file in a split window                   | `g:NERDTreeMapOpenSplit`       |
 | 
				
			||||||
| `gi`    | Same as i, but leave the cursor on the NERDTree        | `g:NERDTreeMapPreviewSplit`    |
 | 
					| `gi`    |  Same as i, but leave the cursor on the NERDTree        | `g:NERDTreeMapPreviewSplit`    |
 | 
				
			||||||
| `s`     | Open selected file in a new vsplit                     | `g:NERDTreeMapOpenVSplit`      |
 | 
					| `s`     |  Open selected file in a new vsplit                     | `g:NERDTreeMapOpenVSplit`      |
 | 
				
			||||||
| `gs`    | Same as s, but leave the cursor on the NERDTree        | `g:NERDTreeMapPreviewVSplit`   |
 | 
					| `gs`    |  Same as s, but leave the cursor on the NERDTree        | `g:NERDTreeMapPreviewVSplit`   |
 | 
				
			||||||
| `O`     | Recursively open the selected directory                | `g:NERDTreeMapOpenRecursively` |
 | 
					| `O`     |  Recursively open the selected directory                | `g:NERDTreeMapOpenRecursively` |
 | 
				
			||||||
| `x`     | Close the current nodes parent                         | `g:NERDTreeMapCloseDir`        |
 | 
					| `x`     |  Close the current nodes parent                         | `g:NERDTreeMapCloseDir`        |
 | 
				
			||||||
| `X`     | Recursively close all children of the current node     | `g:NERDTreeMapCloseChildren`   |
 | 
					| `X`     |  Recursively close all children of the current node     | `g:NERDTreeMapCloseChildren`   |
 | 
				
			||||||
| `P`     | Jump to the root node                                  | `g:NERDTreeMapJumpRoot`        |
 | 
					| `P`     |  Jump to the root node                                  | `g:NERDTreeMapJumpRoot`        |
 | 
				
			||||||
| `p`     | Jump to current nodes parent                           | `g:NERDTreeMapJumpParent`      | 
 | 
					| `p`     |  Jump to current nodes parent                           | `g:NERDTreeMapJumpParent`      | 
 | 
				
			||||||
| `K`     | Jump up inside directories at the current tree depth   | `g:NERDTreeMapJumpFirstChild`  |
 | 
					| `K`     |  Jump up inside directories at the current tree depth   | `g:NERDTreeMapJumpFirstChild`  |
 | 
				
			||||||
| `J`     | Jump down inside directories at the current tree depth | `g:NERDTreeMapJumpLastChild`   |
 | 
					| `J`     |  Jump down inside directories at the current tree depth | `g:NERDTreeMapJumpLastChild`   |
 | 
				
			||||||
| `<C-J>` | Jump down to next sibling of the current directory     | `g:NERDTreeMapJumpNextSibling` |
 | 
					| `<C-J>` |  Jump down to next sibling of the current directory     | `g:NERDTreeMapJumpNextSibling` |
 | 
				
			||||||
| `<C-K>` | Jump up to previous sibling of the current directory   | `g:NERDTreeMapJumpPrevSibling` |
 | 
					| `<C-K>` |  Jump up to previous sibling of the current directory   | `g:NERDTreeMapJumpPrevSibling` |
 | 
				
			||||||
| `r`     | Recursively refresh the current directory              | `g:NERDTreeMapRefresh`         |
 | 
					| `r`     |  Recursively refresh the current directory              | `g:NERDTreeMapRefresh`         |
 | 
				
			||||||
| `R`     | Recursively refresh the current root                   | `g:NERDTreeMapRefreshRoot`     |
 | 
					| `R`     |  Recursively refresh the current root                   | `g:NERDTreeMapRefreshRoot`     |
 | 
				
			||||||
| `m`     | Display the NERDTree menu                              | `g:NERDTreeMapMenu`            |
 | 
					| `m`     |  Display the NERDTree menu                              | `g:NERDTreeMapMenu`            |
 | 
				
			||||||
| `q`     | Close the NERDTree window                              | `g:NERDTreeMapQuit`            |
 | 
					| `q`     |  Close the NERDTree window                              | `g:NERDTreeMapQuit`            |
 | 
				
			||||||
| `A`     | Zoom (maximize/minimize) the NERDTree window           | `g:NERDTreeMapToggleZoom`      |
 | 
					| `A`     |  Zoom (maximize/minimize) the NERDTree window           | `g:NERDTreeMapToggleZoom`      |
 | 
				
			||||||
| `d`     | Delete file or directory                               | `g:NERDTreeMapDelete`          |
 | 
					 | 
				
			||||||
| `n`     | Create File                                            | `g:NERDTreeMapNewFile`         |
 | 
					 | 
				
			||||||
| `N`     | Create Directory                                       | `g:NERDTreeMapNewDir`          |
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Troubleshooting
 | 
					### Troubleshooting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@ Using actions from external plugins is the same, as tracking and reusing any IDE
 | 
				
			|||||||
1. Install the plugin via Marketplace
 | 
					1. Install the plugin via Marketplace
 | 
				
			||||||
2. Enable action tracking. You can enable it by one of the following ways:
 | 
					2. Enable action tracking. You can enable it by one of the following ways:
 | 
				
			||||||
    * Execute `:set trackactionids` ex command or just `:set tai`
 | 
					    * 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:
 | 
					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"/>
 | 
					   <img alt="Notification" src="images/action-id-notification.png"/>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ Every effort is made to make these options compatible with Vim behaviour.
 | 
				
			|||||||
However, some differences are inevitable.
 | 
					However, some differences are inevitable.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
'clipboard'     'cb'    Defines clipboard behavior
 | 
					'clipboard'     'cb'    Defines clipboard behavioue
 | 
				
			||||||
        A comma-separated list of words to control clipboard behaviour:
 | 
					        A comma-separated list of words to control clipboard behaviour:
 | 
				
			||||||
           unnamed      The clipboard register '*' is used instead of the
 | 
					           unnamed      The clipboard register '*' is used instead of the
 | 
				
			||||||
                        unnamed register
 | 
					                        unnamed register
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
# Support Guide
 | 
					# Support Guide
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This document is created to help our support team.
 | 
					This document is created to help our support team.
 | 
				
			||||||
It's not intended to be read by the users as it brings no value to them.
 | 
					It's not intended to be read by the users as it brings to value to them.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Support channels
 | 
					## Support channels
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -25,7 +25,7 @@ It's not intended to be read by the users as it brings no value to them.
 | 
				
			|||||||
IdeaVim has multiple YouTrack statuses, main are:
 | 
					IdeaVim has multiple YouTrack statuses, main are:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Submitted: issue is created by user, but not processed by our team. This is the default status for new tickets.
 | 
					- Submitted: issue is created by user, but not processed by our team. This is the default status for new tickets.
 | 
				
			||||||
- Open: issues is processed by our team, what means that the issues is reproduced and accepted
 | 
					- Open: issues is processed by out team, what means that the issues is reproduced and accepted
 | 
				
			||||||
- Waiting For Reply: Waiting for further information from the user. These issues are automatically closed if the
 | 
					- Waiting For Reply: Waiting for further information from the user. These issues are automatically closed if the
 | 
				
			||||||
     user doesn't reply in 30 days.
 | 
					     user doesn't reply in 30 days.
 | 
				
			||||||
- Ready To Release: Bug is fixed, but not yet released
 | 
					- Ready To Release: Bug is fixed, but not yet released
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,27 +8,22 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# suppress inspection "UnusedProperty" for whole file
 | 
					# suppress inspection "UnusedProperty" for whole file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ideaVersion is the version of the IDE that will be added as a compile-time dependency. The format can be either
 | 
					#ideaVersion=LATEST-EAP-SNAPSHOT
 | 
				
			||||||
# product version (e.g. 2024.1, 2024.1.1) or build (e.g. 241.15989.150, 241-EAP-SNAPSHOT). The dependency will be
 | 
					ideaVersion=2024.1.1
 | 
				
			||||||
# resolved against the configured repositories, which by default includes Maven releases and snapshots, the CDN used to
 | 
					 | 
				
			||||||
# download consumer releases, the plugin marketplace and so on.
 | 
					 | 
				
			||||||
# You can find an example list of all CDN based versions for IDEA Community here:
 | 
					 | 
				
			||||||
# 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.2
 | 
					 | 
				
			||||||
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
 | 
					# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
 | 
				
			||||||
ideaType=IC
 | 
					ideaType=IC
 | 
				
			||||||
 | 
					downloadIdeaSources=true
 | 
				
			||||||
instrumentPluginCode=true
 | 
					instrumentPluginCode=true
 | 
				
			||||||
version=chylex-42
 | 
					version=chylex-35
 | 
				
			||||||
javaVersion=17
 | 
					javaVersion=17
 | 
				
			||||||
remoteRobotVersion=0.11.23
 | 
					remoteRobotVersion=0.11.22
 | 
				
			||||||
antlrVersion=4.10.1
 | 
					antlrVersion=4.10.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					kotlin.incremental.useClasspathSnapshot=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Please don't forget to update kotlin version in buildscript section
 | 
					# Please don't forget to update kotlin version in buildscript section
 | 
				
			||||||
# Also update kotlinxSerializationVersion version
 | 
					# Also update kotlinxSerializationVersion version
 | 
				
			||||||
kotlinVersion=2.0.0
 | 
					kotlinVersion=1.9.22
 | 
				
			||||||
publishToken=token
 | 
					publishToken=token
 | 
				
			||||||
publishChannels=eap
 | 
					publishChannels=eap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -41,7 +36,6 @@ youtrackToken=
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Gradle settings
 | 
					# Gradle settings
 | 
				
			||||||
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
 | 
					org.gradle.jvmargs='-Dfile.encoding=UTF-8'
 | 
				
			||||||
org.gradle.caching=true
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Disable warning from gradle-intellij-plugin. Kotlin stdlib is included as compileOnly, so the warning is unnecessary
 | 
					# Disable warning from gradle-intellij-plugin. Kotlin stdlib is included as compileOnly, so the warning is unnecessary
 | 
				
			||||||
kotlin.stdlib.default.dependency=false
 | 
					kotlin.stdlib.default.dependency=false
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
distributionBase=GRADLE_USER_HOME
 | 
					distributionBase=GRADLE_USER_HOME
 | 
				
			||||||
distributionPath=wrapper/dists
 | 
					distributionPath=wrapper/dists
 | 
				
			||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
 | 
					distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
 | 
				
			||||||
networkTimeout=10000
 | 
					networkTimeout=10000
 | 
				
			||||||
validateDistributionUrl=true
 | 
					validateDistributionUrl=true
 | 
				
			||||||
zipStoreBase=GRADLE_USER_HOME
 | 
					zipStoreBase=GRADLE_USER_HOME
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										22
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							@@ -15,8 +15,6 @@
 | 
				
			|||||||
# See the License for the specific language governing permissions and
 | 
					# See the License for the specific language governing permissions and
 | 
				
			||||||
# limitations under the License.
 | 
					# limitations under the License.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# SPDX-License-Identifier: Apache-2.0
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
##############################################################################
 | 
					##############################################################################
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
@@ -57,7 +55,7 @@
 | 
				
			|||||||
#       Darwin, MinGW, and NonStop.
 | 
					#       Darwin, MinGW, and NonStop.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#   (3) This script is generated from the Groovy template
 | 
					#   (3) This script is generated from the Groovy template
 | 
				
			||||||
#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
 | 
					#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
 | 
				
			||||||
#       within the Gradle project.
 | 
					#       within the Gradle project.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#       You can find Gradle at https://github.com/gradle/gradle/.
 | 
					#       You can find Gradle at https://github.com/gradle/gradle/.
 | 
				
			||||||
@@ -85,9 +83,7 @@ done
 | 
				
			|||||||
# This is normally unused
 | 
					# This is normally unused
 | 
				
			||||||
# shellcheck disable=SC2034
 | 
					# shellcheck disable=SC2034
 | 
				
			||||||
APP_BASE_NAME=${0##*/}
 | 
					APP_BASE_NAME=${0##*/}
 | 
				
			||||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
 | 
					APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
 | 
				
			||||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
 | 
					 | 
				
			||||||
' "$PWD" ) || exit
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
 | 
					# Use the maximum available, or set MAX_FD != -1 to use that value.
 | 
				
			||||||
MAX_FD=maximum
 | 
					MAX_FD=maximum
 | 
				
			||||||
@@ -148,7 +144,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
 | 
				
			|||||||
    case $MAX_FD in #(
 | 
					    case $MAX_FD in #(
 | 
				
			||||||
      max*)
 | 
					      max*)
 | 
				
			||||||
        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
 | 
					        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
 | 
				
			||||||
        # shellcheck disable=SC2039,SC3045
 | 
					        # shellcheck disable=SC3045
 | 
				
			||||||
        MAX_FD=$( ulimit -H -n ) ||
 | 
					        MAX_FD=$( ulimit -H -n ) ||
 | 
				
			||||||
            warn "Could not query maximum file descriptor limit"
 | 
					            warn "Could not query maximum file descriptor limit"
 | 
				
			||||||
    esac
 | 
					    esac
 | 
				
			||||||
@@ -156,7 +152,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
 | 
				
			|||||||
      '' | soft) :;; #(
 | 
					      '' | soft) :;; #(
 | 
				
			||||||
      *)
 | 
					      *)
 | 
				
			||||||
        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
 | 
					        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
 | 
				
			||||||
        # shellcheck disable=SC2039,SC3045
 | 
					        # shellcheck disable=SC3045
 | 
				
			||||||
        ulimit -n "$MAX_FD" ||
 | 
					        ulimit -n "$MAX_FD" ||
 | 
				
			||||||
            warn "Could not set maximum file descriptor limit to $MAX_FD"
 | 
					            warn "Could not set maximum file descriptor limit to $MAX_FD"
 | 
				
			||||||
    esac
 | 
					    esac
 | 
				
			||||||
@@ -205,11 +201,11 @@ fi
 | 
				
			|||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
 | 
					# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
 | 
				
			||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
 | 
					DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Collect all arguments for the java command:
 | 
					# Collect all arguments for the java command;
 | 
				
			||||||
#   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
 | 
					#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
 | 
				
			||||||
#     and any embedded shellness will be escaped.
 | 
					#     shell script including quotes and variable substitutions, so put them in
 | 
				
			||||||
#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
 | 
					#     double quotes to make sure that they get re-expanded; and
 | 
				
			||||||
#     treated as '${Hostname}' itself on the command line.
 | 
					#   * put everything else in single quotes, so that it's not re-expanded.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
set -- \
 | 
					set -- \
 | 
				
			||||||
        "-Dorg.gradle.appname=$APP_BASE_NAME" \
 | 
					        "-Dorg.gradle.appname=$APP_BASE_NAME" \
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										22
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
								
							@@ -13,8 +13,6 @@
 | 
				
			|||||||
@rem See the License for the specific language governing permissions and
 | 
					@rem See the License for the specific language governing permissions and
 | 
				
			||||||
@rem limitations under the License.
 | 
					@rem limitations under the License.
 | 
				
			||||||
@rem
 | 
					@rem
 | 
				
			||||||
@rem SPDX-License-Identifier: Apache-2.0
 | 
					 | 
				
			||||||
@rem
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@if "%DEBUG%"=="" @echo off
 | 
					@if "%DEBUG%"=="" @echo off
 | 
				
			||||||
@rem ##########################################################################
 | 
					@rem ##########################################################################
 | 
				
			||||||
@@ -45,11 +43,11 @@ set JAVA_EXE=java.exe
 | 
				
			|||||||
%JAVA_EXE% -version >NUL 2>&1
 | 
					%JAVA_EXE% -version >NUL 2>&1
 | 
				
			||||||
if %ERRORLEVEL% equ 0 goto execute
 | 
					if %ERRORLEVEL% equ 0 goto execute
 | 
				
			||||||
 | 
					
 | 
				
			||||||
echo. 1>&2
 | 
					echo.
 | 
				
			||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
 | 
					echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
 | 
				
			||||||
echo. 1>&2
 | 
					echo.
 | 
				
			||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
 | 
					echo Please set the JAVA_HOME variable in your environment to match the
 | 
				
			||||||
echo location of your Java installation. 1>&2
 | 
					echo location of your Java installation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
goto fail
 | 
					goto fail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -59,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
if exist "%JAVA_EXE%" goto execute
 | 
					if exist "%JAVA_EXE%" goto execute
 | 
				
			||||||
 | 
					
 | 
				
			||||||
echo. 1>&2
 | 
					echo.
 | 
				
			||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
 | 
					echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
 | 
				
			||||||
echo. 1>&2
 | 
					echo.
 | 
				
			||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
 | 
					echo Please set the JAVA_HOME variable in your environment to match the
 | 
				
			||||||
echo location of your Java installation. 1>&2
 | 
					echo location of your Java installation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
goto fail
 | 
					goto fail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,8 +19,9 @@ exclude:
 | 
				
			|||||||
      - src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/JavaText.kt
 | 
					      - src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/JavaText.kt
 | 
				
			||||||
      - src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/LoremText.kt
 | 
					      - src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/LoremText.kt
 | 
				
			||||||
      - src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/SimpleText.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/regexp/parser/generated
 | 
				
			||||||
      - vim-engine/src/main/java/com/maddyhome/idea/vim/parser/generated
 | 
					 | 
				
			||||||
      - src/main/java/com/maddyhome/idea/vim/group/SearchGroup.java
 | 
					      - src/main/java/com/maddyhome/idea/vim/group/SearchGroup.java
 | 
				
			||||||
      - tests/ui-fixtures
 | 
					      - tests/ui-fixtures
 | 
				
			||||||
dependencyIgnores:
 | 
					dependencyIgnores:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,17 +20,17 @@ repositories {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
  compileOnly("org.jetbrains.kotlin:kotlin-stdlib:2.1.0")
 | 
					  compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.24")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  implementation("io.ktor:ktor-client-core:3.0.2")
 | 
					  implementation("io.ktor:ktor-client-core:2.3.11")
 | 
				
			||||||
  implementation("io.ktor:ktor-client-cio:3.0.2")
 | 
					  implementation("io.ktor:ktor-client-cio:2.3.10")
 | 
				
			||||||
  implementation("io.ktor:ktor-client-content-negotiation:3.0.2")
 | 
					  implementation("io.ktor:ktor-client-content-negotiation:2.3.10")
 | 
				
			||||||
  implementation("io.ktor:ktor-serialization-kotlinx-json:3.0.2")
 | 
					  implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.11")
 | 
				
			||||||
  implementation("io.ktor:ktor-client-auth:3.0.2")
 | 
					  implementation("io.ktor:ktor-client-auth:2.3.11")
 | 
				
			||||||
  implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
 | 
					  implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // This is needed for jgit to connect to ssh
 | 
					  // This is needed for jgit to connect to ssh
 | 
				
			||||||
  implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.1.0.202411261347-r")
 | 
					  implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.9.0.202403050737-r")
 | 
				
			||||||
  implementation("com.vdurmont:semver4j:3.1.0")
 | 
					  implementation("com.vdurmont:semver4j:3.1.0")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,9 +40,6 @@ val knownPlugins = setOf(
 | 
				
			|||||||
  "com.protoseo.input-source-auto-converter",
 | 
					  "com.protoseo.input-source-auto-converter",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//   "cc.implicated.intellij.plugins.bunny", // I don't want to include this plugin in the list of IdeaVim plugins as I don't understand what this is for
 | 
					//   "cc.implicated.intellij.plugins.bunny", // I don't want to include this plugin in the list of IdeaVim plugins as I don't understand what this is for
 | 
				
			||||||
  "com.julienphalip.ideavim.peekaboo", // https://plugins.jetbrains.com/plugin/25776-vim-peekaboo
 | 
					 | 
				
			||||||
  "com.julienphalip.ideavim.switch", // https://plugins.jetbrains.com/plugin/25899-vim-switch
 | 
					 | 
				
			||||||
  "com.julienphalip.ideavim.functiontextobj", // https://plugins.jetbrains.com/plugin/25897-vim-functiontextobj
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
suspend fun main() {
 | 
					suspend fun main() {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										20
									
								
								settings.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								settings.gradle
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					// Set repository for snapshot versions of gradle plugin
 | 
				
			||||||
 | 
					pluginManagement {
 | 
				
			||||||
 | 
					  repositories {
 | 
				
			||||||
 | 
					    maven {
 | 
				
			||||||
 | 
					      url 'https://oss.sonatype.org/content/repositories/snapshots/'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    gradlePluginPortal()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					rootProject.name = 'IdeaVIM'
 | 
				
			||||||
 | 
					include 'vim-engine'
 | 
				
			||||||
 | 
					include 'scripts'
 | 
				
			||||||
 | 
					include 'annotation-processors'
 | 
				
			||||||
 | 
					include 'tests:java-tests'
 | 
				
			||||||
 | 
					include 'tests:property-tests'
 | 
				
			||||||
 | 
					include 'tests:long-running-tests'
 | 
				
			||||||
 | 
					include 'tests:ui-ij-tests'
 | 
				
			||||||
 | 
					include 'tests:ui-py-tests'
 | 
				
			||||||
 | 
					include 'tests:ui-fixtures'
 | 
				
			||||||
@@ -1,21 +0,0 @@
 | 
				
			|||||||
// Set repository for snapshot versions of gradle plugin
 | 
					 | 
				
			||||||
pluginManagement {
 | 
					 | 
				
			||||||
  repositories {
 | 
					 | 
				
			||||||
    maven {
 | 
					 | 
				
			||||||
      url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    gradlePluginPortal()
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rootProject.name = "IdeaVIM"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
include("vim-engine")
 | 
					 | 
				
			||||||
include("scripts")
 | 
					 | 
				
			||||||
include("annotation-processors")
 | 
					 | 
				
			||||||
include("tests:java-tests")
 | 
					 | 
				
			||||||
include("tests:property-tests")
 | 
					 | 
				
			||||||
include("tests:long-running-tests")
 | 
					 | 
				
			||||||
include("tests:ui-ij-tests")
 | 
					 | 
				
			||||||
include("tests:ui-py-tests")
 | 
					 | 
				
			||||||
include("tests:ui-fixtures")
 | 
					 | 
				
			||||||
@@ -64,7 +64,7 @@ finallyBlock:           (WS | COLON)* FINALLY WS* ((inline_comment NEW_LINE) | (
 | 
				
			|||||||
;
 | 
					;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
functionDefinition:
 | 
					functionDefinition:
 | 
				
			||||||
                        (WS | COLON)* FUNCTION (replace = BANG)? WS+ (SID | SNR)? (anyCaseNameWithDigitsAndUnderscores NUM)* (functionScope COLON)? (functionName | (literalDictionaryKey (DOT literalDictionaryKey)+)) WS* L_PAREN WS* argumentsDeclaration WS* R_PAREN WS* (functionFlag WS*)* ((inline_comment NEW_LINE) | (NEW_LINE | BAR)+)
 | 
					                        (WS | COLON)* FUNCTION (replace = EXCLAMATION)? WS+ (SID | SNR)? (anyCaseNameWithDigitsAndUnderscores NUM)* (functionScope COLON)? (functionName | (literalDictionaryKey (DOT literalDictionaryKey)+)) WS* L_PAREN WS* argumentsDeclaration WS* R_PAREN WS* (functionFlag WS*)* ((inline_comment NEW_LINE) | (NEW_LINE | BAR)+)
 | 
				
			||||||
                            blockMember*
 | 
					                            blockMember*
 | 
				
			||||||
                        (WS | COLON)* ENDFUNCTION WS* ((inline_comment NEW_LINE) | (NEW_LINE | BAR))
 | 
					                        (WS | COLON)* ENDFUNCTION WS* ((inline_comment NEW_LINE) | (NEW_LINE | BAR))
 | 
				
			||||||
;
 | 
					;
 | 
				
			||||||
@@ -92,7 +92,7 @@ command:
 | 
				
			|||||||
    (WS | COLON)* range? (WS | COLON)* ECHO (WS* expr)* WS* (NEW_LINE | BAR)+
 | 
					    (WS | COLON)* range? (WS | COLON)* ECHO (WS* expr)* WS* (NEW_LINE | BAR)+
 | 
				
			||||||
    #EchoCommand|
 | 
					    #EchoCommand|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    (WS | COLON)* range? (WS | COLON)* DELF (replace = BANG)? WS+ (functionScope COLON)? functionName ((inline_comment NEW_LINE+) | (NEW_LINE | BAR)+)
 | 
					    (WS | COLON)* range? (WS | COLON)* DELF (replace = EXCLAMATION)? WS+ (functionScope COLON)? functionName ((inline_comment NEW_LINE+) | (NEW_LINE | BAR)+)
 | 
				
			||||||
    #DelfunctionCommand|
 | 
					    #DelfunctionCommand|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    (WS | COLON)* range? (WS | COLON)* CALL WS+ expr WS* ((inline_comment NEW_LINE+) | (NEW_LINE | BAR)+)
 | 
					    (WS | COLON)* range? (WS | COLON)* CALL WS+ expr WS* ((inline_comment NEW_LINE+) | (NEW_LINE | BAR)+)
 | 
				
			||||||
@@ -119,12 +119,10 @@ command:
 | 
				
			|||||||
        | PROMPT_REPLACE | P_LOWERCASE | P_UPPERCASE | PRINT | PREVIOUS_TAB | N_UPPERCASE | PREVIOUS_FILE | PLUG
 | 
					        | PROMPT_REPLACE | P_LOWERCASE | P_UPPERCASE | PRINT | PREVIOUS_TAB | N_UPPERCASE | PREVIOUS_FILE | PLUG
 | 
				
			||||||
        | ONLY | NO_HL_SEARCH | NEXT_TAB | N_LOWERCASE | NEXT_FILE | M_LOWERCASE | MOVE_TEXT | MARKS | K_LOWERCASE
 | 
					        | ONLY | NO_HL_SEARCH | NEXT_TAB | N_LOWERCASE | NEXT_FILE | M_LOWERCASE | MOVE_TEXT | MARKS | K_LOWERCASE
 | 
				
			||||||
        | MARK_COMMAND | JUMPS | J_LOWERCASE | JOIN_LINES | HISTORY | GO_TO_CHAR | SYMBOL | FIND | CLASS | F_LOWERCASE
 | 
					        | MARK_COMMAND | JUMPS | J_LOWERCASE | JOIN_LINES | HISTORY | GO_TO_CHAR | SYMBOL | FIND | CLASS | F_LOWERCASE
 | 
				
			||||||
        | CLEARJUMPS
 | 
					 | 
				
			||||||
        | FILE | EXIT | E_LOWERCASE | EDIT_FILE | DUMP_LINE | DIGRAPH | DEL_MARKS | D_LOWERCASE | DEL_LINES | DELCMD
 | 
					        | FILE | EXIT | E_LOWERCASE | EDIT_FILE | DUMP_LINE | DIGRAPH | DEL_MARKS | D_LOWERCASE | DEL_LINES | DELCMD
 | 
				
			||||||
        | T_LOWERCASE | COPY | CMD_CLEAR | BUFFER_LIST | BUFFER_CLOSE | B_LOWERCASE | BUFFER | ASCII
 | 
					        | T_LOWERCASE | COPY | CMD_CLEAR | BUFFER_LIST | BUFFER_CLOSE | B_LOWERCASE | BUFFER | ASCII
 | 
				
			||||||
        | ACTIONLIST | ACTION | LOCKVAR | UNLOCKVAR | PACKADD | TABMOVE
 | 
					        | ACTIONLIST | ACTION | LOCKVAR | UNLOCKVAR | PACKADD | TABMOVE
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
      bangModifier = BANG?
 | 
					 | 
				
			||||||
    WS* ((commandArgumentWithoutBars? inline_comment NEW_LINE) | (commandArgumentWithoutBars? NEW_LINE) | (commandArgumentWithoutBars? BAR)) (NEW_LINE | BAR)*
 | 
					    WS* ((commandArgumentWithoutBars? inline_comment NEW_LINE) | (commandArgumentWithoutBars? NEW_LINE) | (commandArgumentWithoutBars? BAR)) (NEW_LINE | BAR)*
 | 
				
			||||||
    #CommandWithComment|
 | 
					    #CommandWithComment|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -132,19 +130,17 @@ command:
 | 
				
			|||||||
      name = (
 | 
					      name = (
 | 
				
			||||||
        MAP | MAP_CLEAR | UNMAP | SORT | REGISTERS | CMD | H_LOWERCASE | HELP | NORMAL
 | 
					        MAP | MAP_CLEAR | UNMAP | SORT | REGISTERS | CMD | H_LOWERCASE | HELP | NORMAL
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
      bangModifier = BANG?
 | 
					 | 
				
			||||||
    WS* commandArgumentWithoutBars? (NEW_LINE | BAR)+
 | 
					    WS* commandArgumentWithoutBars? (NEW_LINE | BAR)+
 | 
				
			||||||
    #CommandWithoutComments|
 | 
					    #CommandWithoutComments|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    (WS | COLON)* range? (WS | COLON)*
 | 
					    (WS | COLON)* range? (WS | COLON)*
 | 
				
			||||||
      name = (
 | 
					      name = (
 | 
				
			||||||
        G_LOWERCASE | GLOBAL | V_LOWERCASE | V_GLOBAL | S_LOWERCASE | SUBSTITUTE | TILDE | AMPERSAND | BANG
 | 
					        G_LOWERCASE | GLOBAL | V_LOWERCASE | V_GLOBAL | S_LOWERCASE | SUBSTITUTE | TILDE | AMPERSAND | EXCLAMATION
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
      bangModifier = BANG?
 | 
					 | 
				
			||||||
    WS* commandArgumentWithBars? NEW_LINE+
 | 
					    WS* commandArgumentWithBars? NEW_LINE+
 | 
				
			||||||
    #CommandWithBars|
 | 
					    #CommandWithBars|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    (WS | COLON)* range? (WS | COLON)* commandName (bangModifier = BANG?) WS* commandArgumentWithBars? (NEW_LINE | BAR)+
 | 
					    (WS | COLON)* range? (WS | COLON)* commandName WS* commandArgumentWithBars? (NEW_LINE | BAR)+
 | 
				
			||||||
    #OtherCommand
 | 
					    #OtherCommand
 | 
				
			||||||
;
 | 
					;
 | 
				
			||||||
commandArgumentWithBars: ~(NEW_LINE)+;
 | 
					commandArgumentWithBars: ~(NEW_LINE)+;
 | 
				
			||||||
@@ -211,7 +207,7 @@ commandName:
 | 
				
			|||||||
    (LESS)+
 | 
					    (LESS)+
 | 
				
			||||||
|   (GREATER)+
 | 
					|   (GREATER)+
 | 
				
			||||||
|   anyCaseNameWithDigitsAndUnderscoresExceptKeywords
 | 
					|   anyCaseNameWithDigitsAndUnderscoresExceptKeywords
 | 
				
			||||||
|   commandName (bang = BANG)
 | 
					|   commandName EXCLAMATION
 | 
				
			||||||
;
 | 
					;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -223,7 +219,7 @@ commandName:
 | 
				
			|||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
					////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
				
			||||||
expr:
 | 
					expr:
 | 
				
			||||||
                        expr L_BRACKET expr R_BRACKET                                                                   #OneElementSublistExpression
 | 
					                        expr L_BRACKET expr R_BRACKET                                                                   #OneElementSublistExpression
 | 
				
			||||||
                    |   WS* BANG WS* expr                                                                        #UnaryExpression
 | 
					                    |   WS* EXCLAMATION WS* expr                                                                        #UnaryExpression
 | 
				
			||||||
                    |   expr L_BRACKET WS* from = expr? WS* COLON WS* to = expr? WS* R_BRACKET                          #SublistExpression
 | 
					                    |   expr L_BRACKET WS* from = expr? WS* COLON WS* to = expr? WS* R_BRACKET                          #SublistExpression
 | 
				
			||||||
                    |   expr WS* binaryOperator1 WS* expr                                                               #BinExpression1
 | 
					                    |   expr WS* binaryOperator1 WS* expr                                                               #BinExpression1
 | 
				
			||||||
                    |   expr WS* binaryOperator2 WS* expr                                                               #BinExpression2
 | 
					                    |   expr WS* binaryOperator2 WS* expr                                                               #BinExpression2
 | 
				
			||||||
@@ -622,7 +618,6 @@ BUFFER_CLOSE:           'bd' | 'bde' | 'bdel' | 'bdele' | 'bdelet' | 'bdelete';
 | 
				
			|||||||
BUFFER_LIST:            'buffers' | 'ls' | 'files';
 | 
					BUFFER_LIST:            'buffers' | 'ls' | 'files';
 | 
				
			||||||
CALL:                   'cal' | 'call';
 | 
					CALL:                   'cal' | 'call';
 | 
				
			||||||
CLASS:                  'cla' | 'clas' | 'class';
 | 
					CLASS:                  'cla' | 'clas' | 'class';
 | 
				
			||||||
CLEARJUMPS:             'cle' | 'clea' | 'clear' | 'clearj' | 'clearju' | 'clearjum' | 'clearjump' | 'clearjumps';
 | 
					 | 
				
			||||||
CMD:                    'com' | 'comm' | 'comma' | 'comman' | 'command';
 | 
					CMD:                    'com' | 'comm' | 'comma' | 'comman' | 'command';
 | 
				
			||||||
CMD_CLEAR:              'comc' | 'comcl' | 'comcle' | 'comclea' | 'comclear';
 | 
					CMD_CLEAR:              'comc' | 'comcl' | 'comcle' | 'comclea' | 'comclear';
 | 
				
			||||||
COPY:                   'co' | 'cop' | 'copy';
 | 
					COPY:                   'co' | 'cop' | 'copy';
 | 
				
			||||||
@@ -692,7 +687,6 @@ MAP_CLEAR:              ('mapc' | 'nmapc' | 'vmapc' | 'xmapc' | 'smapc' | 'omapc
 | 
				
			|||||||
                    |   (('mapc' | 'nmapc' | 'vmapc' | 'xmapc' | 'smapc' | 'omapc' | 'imapc' | 'cmapc') 'lea')
 | 
					                    |   (('mapc' | 'nmapc' | 'vmapc' | 'xmapc' | 'smapc' | 'omapc' | 'imapc' | 'cmapc') 'lea')
 | 
				
			||||||
                    |   (('mapc' | 'nmapc' | 'vmapc' | 'xmapc' | 'smapc' | 'omapc' | 'imapc' | 'cmapc') 'lear');
 | 
					                    |   (('mapc' | 'nmapc' | 'vmapc' | 'xmapc' | 'smapc' | 'omapc' | 'imapc' | 'cmapc') 'lear');
 | 
				
			||||||
MAP:                    'map'
 | 
					MAP:                    'map'
 | 
				
			||||||
                    |   'smap'
 | 
					 | 
				
			||||||
                    |   'nm' | 'vm' | 'xm' | 'om' | 'im' | 'cm'
 | 
					                    |   'nm' | 'vm' | 'xm' | 'om' | 'im' | 'cm'
 | 
				
			||||||
                    |   (('nm' | 'vm' | 'xm' | 'om' | 'im' | 'cm') 'a')
 | 
					                    |   (('nm' | 'vm' | 'xm' | 'om' | 'im' | 'cm') 'a')
 | 
				
			||||||
                    |   (('nm' | 'vm' | 'xm' | 'om' | 'im' | 'cm') 'ap')
 | 
					                    |   (('nm' | 'vm' | 'xm' | 'om' | 'im' | 'cm') 'ap')
 | 
				
			||||||
@@ -702,8 +696,7 @@ MAP:                    'map'
 | 
				
			|||||||
                    |   (('no' | 'nno' | 'vno' | 'xno' | 'ono' | 'ino' | 'cno') 're')
 | 
					                    |   (('no' | 'nno' | 'vno' | 'xno' | 'ono' | 'ino' | 'cno') 're')
 | 
				
			||||||
                    |   (('no' | 'nno' | 'vno' | 'xno' | 'ono' | 'ino' | 'cno') 'rem')
 | 
					                    |   (('no' | 'nno' | 'vno' | 'xno' | 'ono' | 'ino' | 'cno') 'rem')
 | 
				
			||||||
                    |   (('no' | 'nno' | 'vno' | 'xno' | 'ono' | 'ino' | 'cno') 'rema')
 | 
					                    |   (('no' | 'nno' | 'vno' | 'xno' | 'ono' | 'ino' | 'cno') 'rema')
 | 
				
			||||||
                    |   (('no' | 'nno' | 'vno' | 'xno' | 'ono' | 'ino' | 'cno') 'remap')
 | 
					                    |   (('no' | 'nno' | 'vno' | 'xno' | 'ono' | 'ino' | 'cno') 'remap');
 | 
				
			||||||
                    |   ('snor' | 'snore' | 'snorem' | 'snorema' | 'snoremap');
 | 
					 | 
				
			||||||
UNMAP:                  'unm' | 'nun' | 'vu' | 'xu' | 'sunm' | 'ou' | 'iu' | 'cu'
 | 
					UNMAP:                  'unm' | 'nun' | 'vu' | 'xu' | 'sunm' | 'ou' | 'iu' | 'cu'
 | 
				
			||||||
                    |   'vun' | 'xun' | 'oun' | 'iun' | 'cun'
 | 
					                    |   'vun' | 'xun' | 'oun' | 'iun' | 'cun'
 | 
				
			||||||
                    |   'nunm' | 'vunm' | 'xunm' | 'ounm' | 'iunm' | 'cunm'
 | 
					                    |   'nunm' | 'vunm' | 'xunm' | 'ounm' | 'iunm' | 'cunm'
 | 
				
			||||||
@@ -733,7 +726,7 @@ IDENTIFIER_LOWERCASE:   [a-z]+;
 | 
				
			|||||||
IDENTIFIER_ANY_CASE:    [a-zA-Z]+;
 | 
					IDENTIFIER_ANY_CASE:    [a-zA-Z]+;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Unary operators
 | 
					// Unary operators
 | 
				
			||||||
BANG:                   '!';
 | 
					EXCLAMATION:            '!';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Punctuation
 | 
					// Punctuation
 | 
				
			||||||
L_PAREN:                '(';
 | 
					L_PAREN:                '(';
 | 
				
			||||||
@@ -8,23 +8,14 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.maddyhome.idea.vim
 | 
					package com.maddyhome.idea.vim
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.ide.BrowserUtil
 | 
					 | 
				
			||||||
import com.intellij.ide.plugins.IdeaPluginDescriptor
 | 
					 | 
				
			||||||
import com.intellij.ide.plugins.PluginStateListener
 | 
					 | 
				
			||||||
import com.intellij.ide.plugins.PluginStateManager
 | 
					 | 
				
			||||||
import com.intellij.openapi.application.ApplicationManager
 | 
					 | 
				
			||||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
 | 
					import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
 | 
				
			||||||
import com.intellij.openapi.project.Project
 | 
					import com.intellij.openapi.project.Project
 | 
				
			||||||
import com.intellij.openapi.project.ProjectManagerListener
 | 
					import com.intellij.openapi.project.ProjectManagerListener
 | 
				
			||||||
import com.intellij.openapi.startup.ProjectActivity
 | 
					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.api.injector
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.EditorHelper
 | 
					import com.maddyhome.idea.vim.helper.EditorHelper
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
					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
 | 
					 * @author Alex Plate
 | 
				
			||||||
@@ -33,39 +24,18 @@ internal class PluginStartup : ProjectActivity/*, LightEditCompatible*/ {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private var firstInitializationOccurred = false
 | 
					  private var firstInitializationOccurred = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // TODO
 | 
					 | 
				
			||||||
  // We should migrate to some solution from https://plugins.jetbrains.com/docs/intellij/plugin-components.html#application-startup
 | 
					 | 
				
			||||||
  // If you'd like to add a new code here, please consider using one of the things described there.
 | 
					 | 
				
			||||||
  override suspend fun execute(project: Project) {
 | 
					  override suspend fun execute(project: Project) {
 | 
				
			||||||
    if (firstInitializationOccurred) return
 | 
					    if (firstInitializationOccurred) return
 | 
				
			||||||
    firstInitializationOccurred = true
 | 
					    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
 | 
					    // This code should be executed once
 | 
				
			||||||
    VimPlugin.getInstance().initialize()
 | 
					    VimPlugin.getInstance().initialize()
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Uninstall survey. Should be registered once for all projects
 | 
					 | 
				
			||||||
    PluginStateManager.addStateListener(object : PluginStateListener {
 | 
					 | 
				
			||||||
      override fun install(p0: IdeaPluginDescriptor) {/*Nothing*/
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      override fun uninstall(descriptor: IdeaPluginDescriptor) {
 | 
					 | 
				
			||||||
        if (descriptor.pluginId == VimPlugin.getPluginId()) {
 | 
					 | 
				
			||||||
          BrowserUtil.open("https://surveys.jetbrains.com/s3/ideavim-uninstall-feedback")
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This is a temporal workaround for VIM-2487
 | 
					// This is a temporal workaround for VIM-2487
 | 
				
			||||||
internal class PyNotebooksCloseWorkaround : ProjectManagerListener {
 | 
					internal class PyNotebooksCloseWorkaround : ProjectManagerListener {
 | 
				
			||||||
  override fun projectClosingBeforeSave(project: Project) {
 | 
					  override fun projectClosingBeforeSave(project: Project) {
 | 
				
			||||||
    initInjector()
 | 
					 | 
				
			||||||
    // TODO: Confirm context in CWM scenario
 | 
					    // TODO: Confirm context in CWM scenario
 | 
				
			||||||
    if (injector.globalIjOptions().closenotebooks) {
 | 
					    if (injector.globalIjOptions().closenotebooks) {
 | 
				
			||||||
      injector.editorGroup.getEditors().forEach { vimEditor ->
 | 
					      injector.editorGroup.getEditors().forEach { vimEditor ->
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,36 +14,36 @@ import com.maddyhome.idea.vim.key.MappingOwner
 | 
				
			|||||||
import java.awt.event.KeyEvent
 | 
					import java.awt.event.KeyEvent
 | 
				
			||||||
import javax.swing.KeyStroke
 | 
					import javax.swing.KeyStroke
 | 
				
			||||||
 | 
					
 | 
				
			||||||
object RegisterActions {
 | 
					public object RegisterActions {
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Register all the key/action mappings for the plugin.
 | 
					   * Register all the key/action mappings for the plugin.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  fun registerActions() {
 | 
					  public fun registerActions() {
 | 
				
			||||||
    registerVimCommandActions()
 | 
					    registerVimCommandActions()
 | 
				
			||||||
    registerShortcutsWithoutActions()
 | 
					    registerShortcutsWithoutActions()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fun findAction(id: String): EditorActionHandlerBase? {
 | 
					  public fun findAction(id: String): EditorActionHandlerBase? {
 | 
				
			||||||
    val commandBean = IntellijCommandProvider.getCommands().firstOrNull { it.actionId == id }
 | 
					    val commandBean = EngineCommandProvider.getCommands().firstOrNull { it.actionId == id }
 | 
				
			||||||
      ?: EngineCommandProvider.getCommands().firstOrNull { it.actionId == id } ?: return null
 | 
					      ?: IntellijCommandProvider.getCommands().firstOrNull { it.actionId == id } ?: return null
 | 
				
			||||||
    return commandBean.instance
 | 
					    return commandBean.instance
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fun findActionOrDie(id: String): EditorActionHandlerBase {
 | 
					  public fun findActionOrDie(id: String): EditorActionHandlerBase {
 | 
				
			||||||
    return findAction(id) ?: throw RuntimeException("Action $id is not registered")
 | 
					    return findAction(id) ?: throw RuntimeException("Action $id is not registered")
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  fun unregisterActions() {
 | 
					  public fun unregisterActions() {
 | 
				
			||||||
    val keyGroup = VimPlugin.getKeyIfCreated()
 | 
					    val keyGroup = VimPlugin.getKeyIfCreated()
 | 
				
			||||||
    keyGroup?.unregisterCommandActions()
 | 
					    keyGroup?.unregisterCommandActions()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private fun registerVimCommandActions() {
 | 
					  private fun registerVimCommandActions() {
 | 
				
			||||||
    val parser = VimPlugin.getKey()
 | 
					    val parser = VimPlugin.getKey()
 | 
				
			||||||
    IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
 | 
					 | 
				
			||||||
    EngineCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
 | 
					    EngineCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
 | 
				
			||||||
 | 
					    IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private fun registerShortcutsWithoutActions() {
 | 
					  private fun registerShortcutsWithoutActions() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,6 @@ package com.maddyhome.idea.vim;
 | 
				
			|||||||
import com.intellij.ide.plugins.IdeaPluginDescriptor;
 | 
					import com.intellij.ide.plugins.IdeaPluginDescriptor;
 | 
				
			||||||
import com.intellij.ide.plugins.PluginManagerCore;
 | 
					import com.intellij.ide.plugins.PluginManagerCore;
 | 
				
			||||||
import com.intellij.openapi.Disposable;
 | 
					import com.intellij.openapi.Disposable;
 | 
				
			||||||
import com.intellij.openapi.application.AccessToken;
 | 
					 | 
				
			||||||
import com.intellij.openapi.application.Application;
 | 
					import com.intellij.openapi.application.Application;
 | 
				
			||||||
import com.intellij.openapi.application.ApplicationManager;
 | 
					import com.intellij.openapi.application.ApplicationManager;
 | 
				
			||||||
import com.intellij.openapi.components.PersistentStateComponent;
 | 
					import com.intellij.openapi.components.PersistentStateComponent;
 | 
				
			||||||
@@ -25,8 +24,10 @@ import com.intellij.openapi.project.Project;
 | 
				
			|||||||
import com.intellij.openapi.ui.Messages;
 | 
					import com.intellij.openapi.ui.Messages;
 | 
				
			||||||
import com.intellij.openapi.util.Disposer;
 | 
					import com.intellij.openapi.util.Disposer;
 | 
				
			||||||
import com.intellij.openapi.util.SystemInfo;
 | 
					import com.intellij.openapi.util.SystemInfo;
 | 
				
			||||||
import com.intellij.util.SlowOperations;
 | 
					import com.maddyhome.idea.vim.api.VimEditor;
 | 
				
			||||||
import com.maddyhome.idea.vim.api.*;
 | 
					import com.maddyhome.idea.vim.api.VimInjectorKt;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.VimKeyGroup;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.VimOptionGroup;
 | 
				
			||||||
import com.maddyhome.idea.vim.config.VimState;
 | 
					import com.maddyhome.idea.vim.config.VimState;
 | 
				
			||||||
import com.maddyhome.idea.vim.config.migration.ApplicationConfigurationMigrator;
 | 
					import com.maddyhome.idea.vim.config.migration.ApplicationConfigurationMigrator;
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionRegistrar;
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionRegistrar;
 | 
				
			||||||
@@ -35,9 +36,9 @@ import com.maddyhome.idea.vim.group.copy.PutGroup;
 | 
				
			|||||||
import com.maddyhome.idea.vim.group.visual.VisualMotionGroup;
 | 
					import com.maddyhome.idea.vim.group.visual.VisualMotionGroup;
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.MacKeyRepeat;
 | 
					import com.maddyhome.idea.vim.helper.MacKeyRepeat;
 | 
				
			||||||
import com.maddyhome.idea.vim.listener.VimListenerManager;
 | 
					import com.maddyhome.idea.vim.listener.VimListenerManager;
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimInjectorKt;
 | 
					import com.maddyhome.idea.vim.newapi.IjVimInjector;
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimSearchGroup;
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.ui.StatusBarIconFactory;
 | 
					import com.maddyhome.idea.vim.ui.StatusBarIconFactory;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.services.VariableService;
 | 
					import com.maddyhome.idea.vim.vimscript.services.VariableService;
 | 
				
			||||||
import com.maddyhome.idea.vim.yank.YankGroupBase;
 | 
					import com.maddyhome.idea.vim.yank.YankGroupBase;
 | 
				
			||||||
import org.jdom.Element;
 | 
					import org.jdom.Element;
 | 
				
			||||||
@@ -45,7 +46,6 @@ import org.jetbrains.annotations.Nls;
 | 
				
			|||||||
import org.jetbrains.annotations.NotNull;
 | 
					import org.jetbrains.annotations.NotNull;
 | 
				
			||||||
import org.jetbrains.annotations.Nullable;
 | 
					import org.jetbrains.annotations.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
 | 
					 | 
				
			||||||
import static com.maddyhome.idea.vim.group.EditorGroup.EDITOR_STORE_ELEMENT;
 | 
					import static com.maddyhome.idea.vim.group.EditorGroup.EDITOR_STORE_ELEMENT;
 | 
				
			||||||
import static com.maddyhome.idea.vim.group.KeyGroup.SHORTCUT_CONFLICTS_ELEMENT;
 | 
					import static com.maddyhome.idea.vim.group.KeyGroup.SHORTCUT_CONFLICTS_ELEMENT;
 | 
				
			||||||
import static com.maddyhome.idea.vim.vimscript.services.VimRcService.executeIdeaVimRc;
 | 
					import static com.maddyhome.idea.vim.vimscript.services.VimRcService.executeIdeaVimRc;
 | 
				
			||||||
@@ -66,7 +66,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
 | 
				
			|||||||
  private static final Logger LOG = Logger.getInstance(VimPlugin.class);
 | 
					  private static final Logger LOG = Logger.getInstance(VimPlugin.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static {
 | 
					  static {
 | 
				
			||||||
    IjVimInjectorKt.initInjector();
 | 
					    VimInjectorKt.setInjector(new IjVimInjector());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private final @NotNull VimState state = new VimState();
 | 
					  private final @NotNull VimState state = new VimState();
 | 
				
			||||||
@@ -123,12 +123,12 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
 | 
				
			|||||||
    return (FileGroup)VimInjectorKt.getInjector().getFile();
 | 
					    return (FileGroup)VimInjectorKt.getInjector().getFile();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static @NotNull IjVimSearchGroup getSearch() {
 | 
					  public static @NotNull SearchGroup getSearch() {
 | 
				
			||||||
    return ApplicationManager.getApplication().getService(IjVimSearchGroup.class);
 | 
					    return ApplicationManager.getApplication().getService(SearchGroup.class);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static @Nullable IjVimSearchGroup getSearchIfCreated() {
 | 
					  public static @Nullable SearchGroup getSearchIfCreated() {
 | 
				
			||||||
    return ApplicationManager.getApplication().getServiceIfCreated(IjVimSearchGroup.class);
 | 
					    return ApplicationManager.getApplication().getServiceIfCreated(SearchGroup.class);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static @NotNull ProcessGroup getProcess() {
 | 
					  public static @NotNull ProcessGroup getProcess() {
 | 
				
			||||||
@@ -139,8 +139,8 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
 | 
				
			|||||||
    return (MacroGroup)VimInjectorKt.getInjector().getMacro();
 | 
					    return (MacroGroup)VimInjectorKt.getInjector().getMacro();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static @NotNull VimDigraphGroup getDigraph() {
 | 
					  public static @NotNull DigraphGroup getDigraph() {
 | 
				
			||||||
    return VimInjectorKt.getInjector().getDigraphGroup();
 | 
					    return (DigraphGroup)VimInjectorKt.getInjector().getDigraphGroup();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static @NotNull HistoryGroup getHistory() {
 | 
					  public static @NotNull HistoryGroup getHistory() {
 | 
				
			||||||
@@ -283,11 +283,11 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (!ApplicationManager.getApplication().isUnitTestMode()) {
 | 
					    if (!ApplicationManager.getApplication().isUnitTestMode()) {
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
        injector.getOptionGroup().startInitVimRc();
 | 
					        VimInjectorKt.injector.getOptionGroup().startInitVimRc();
 | 
				
			||||||
        executeIdeaVimRc(editor);
 | 
					        executeIdeaVimRc(editor);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      finally {
 | 
					      finally {
 | 
				
			||||||
        injector.getOptionGroup().endInitVimRc();
 | 
					        VimInjectorKt.injector.getOptionGroup().endInitVimRc();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -337,9 +337,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // 4) ~/.ideavimrc execution
 | 
					    // 4) ~/.ideavimrc execution
 | 
				
			||||||
    // Evaluate in the context of the fallback window, to capture local option state, to copy to the first editor window
 | 
					    // Evaluate in the context of the fallback window, to capture local option state, to copy to the first editor window
 | 
				
			||||||
    try (AccessToken ignore = SlowOperations.knownIssue("VIM-3661")) {
 | 
					    registerIdeavimrc(VimInjectorKt.getInjector().getFallbackWindow());
 | 
				
			||||||
      registerIdeavimrc(VimInjectorKt.getInjector().getFallbackWindow());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Turing on should be performed after all commands registration
 | 
					    // Turing on should be performed after all commands registration
 | 
				
			||||||
    getSearch().turnOn();
 | 
					    getSearch().turnOn();
 | 
				
			||||||
@@ -347,14 +345,14 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private void turnOffPlugin(boolean unsubscribe) {
 | 
					  private void turnOffPlugin(boolean unsubscribe) {
 | 
				
			||||||
    IjVimSearchGroup searchGroup = getSearchIfCreated();
 | 
					    SearchGroup searchGroup = getSearchIfCreated();
 | 
				
			||||||
    if (searchGroup != null) {
 | 
					    if (searchGroup != null) {
 | 
				
			||||||
      searchGroup.turnOff();
 | 
					      searchGroup.turnOff();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (unsubscribe) {
 | 
					    if (unsubscribe) {
 | 
				
			||||||
      VimListenerManager.INSTANCE.turnOff();
 | 
					      VimListenerManager.INSTANCE.turnOff();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    injector.getCommandLine().fullReset();
 | 
					    ExEntryPanel.fullReset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Unregister vim actions in command mode
 | 
					    // Unregister vim actions in command mode
 | 
				
			||||||
    RegisterActions.unregisterActions();
 | 
					    RegisterActions.unregisterActions();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,13 +12,13 @@ import com.intellij.openapi.Disposable
 | 
				
			|||||||
import com.intellij.openapi.components.Service
 | 
					import com.intellij.openapi.components.Service
 | 
				
			||||||
import com.intellij.openapi.components.service
 | 
					import com.intellij.openapi.components.service
 | 
				
			||||||
import com.intellij.openapi.project.Project
 | 
					import com.intellij.openapi.project.Project
 | 
				
			||||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
 | 
					import com.maddyhome.idea.vim.group.EditorHolderService
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Service(Service.Level.PROJECT)
 | 
					@Service(Service.Level.PROJECT)
 | 
				
			||||||
internal class VimProjectService(val project: Project) : Disposable {
 | 
					internal class VimProjectService(val project: Project) : Disposable {
 | 
				
			||||||
  override fun dispose() {
 | 
					  override fun dispose() {
 | 
				
			||||||
    // Not sure if this is a best solution
 | 
					    // Not sure if this is a best solution
 | 
				
			||||||
    ExEntryPanel.getInstance().setEditor(null)
 | 
					    EditorHolderService.getInstance().editor = null
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  companion object {
 | 
					  companion object {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,7 +32,7 @@ import javax.swing.KeyStroke
 | 
				
			|||||||
 * This class is used in Which-Key plugin, so don't make it internal. Generally, we should provide a proper
 | 
					 * This class is used in Which-Key plugin, so don't make it internal. Generally, we should provide a proper
 | 
				
			||||||
 *   way to get ideavim keys for this plugin. See VIM-3085
 | 
					 *   way to get ideavim keys for this plugin. See VIM-3085
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandlerEx {
 | 
					public class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandlerEx {
 | 
				
			||||||
  private val handler = KeyHandler.getInstance()
 | 
					  private val handler = KeyHandler.getInstance()
 | 
				
			||||||
  private val traceTime = injector.globalOptions().ideatracetime
 | 
					  private val traceTime = injector.globalOptions().ideatracetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.maddyhome.idea.vim.action
 | 
					package com.maddyhome.idea.vim.action
 | 
				
			||||||
 | 
					
 | 
				
			||||||
object IntellijCommandProvider : CommandProvider {
 | 
					public object IntellijCommandProvider : CommandProvider {
 | 
				
			||||||
  override val commandListFileName: String = "intellij_commands.json"
 | 
					  override val commandListFileName: String = "intellij_commands.json"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,52 +0,0 @@
 | 
				
			|||||||
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)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -26,6 +26,7 @@ import com.maddyhome.idea.vim.KeyHandler
 | 
				
			|||||||
import com.maddyhome.idea.vim.VimPlugin
 | 
					import com.maddyhome.idea.vim.VimPlugin
 | 
				
			||||||
import com.maddyhome.idea.vim.api.globalOptions
 | 
					import com.maddyhome.idea.vim.api.globalOptions
 | 
				
			||||||
import com.maddyhome.idea.vim.api.injector
 | 
					import com.maddyhome.idea.vim.api.injector
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.group.EditorHolderService
 | 
				
			||||||
import com.maddyhome.idea.vim.group.IjOptionConstants
 | 
					import com.maddyhome.idea.vim.group.IjOptionConstants
 | 
				
			||||||
import com.maddyhome.idea.vim.group.IjOptions
 | 
					import com.maddyhome.idea.vim.group.IjOptions
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.enableOctopus
 | 
					import com.maddyhome.idea.vim.handler.enableOctopus
 | 
				
			||||||
@@ -43,9 +44,7 @@ import com.maddyhome.idea.vim.key.ShortcutOwnerInfo
 | 
				
			|||||||
import com.maddyhome.idea.vim.listener.AceJumpService
 | 
					import com.maddyhome.idea.vim.listener.AceJumpService
 | 
				
			||||||
import com.maddyhome.idea.vim.listener.AppCodeTemplates.appCodeTemplateCaptured
 | 
					import com.maddyhome.idea.vim.listener.AppCodeTemplates.appCodeTemplateCaptured
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
					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.newapi.vim
 | 
				
			||||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.ui.ex.ExTextField
 | 
					import com.maddyhome.idea.vim.ui.ex.ExTextField
 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
 | 
					import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
 | 
				
			||||||
import java.awt.event.InputEvent
 | 
					import java.awt.event.InputEvent
 | 
				
			||||||
@@ -61,14 +60,11 @@ import javax.swing.KeyStroke
 | 
				
			|||||||
 * This class is used in Which-Key plugin, so don't make it internal. Generally, we should provide a proper
 | 
					 * This class is used in Which-Key plugin, so don't make it internal. Generally, we should provide a proper
 | 
				
			||||||
 *   way to get ideavim keys for this plugin. See VIM-3085
 | 
					 *   way to get ideavim keys for this plugin. See VIM-3085
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
 | 
					public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  init {
 | 
					 | 
				
			||||||
    initInjector()
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private val traceTime: Boolean
 | 
					  private val traceTime: Boolean
 | 
				
			||||||
    get() {
 | 
					    get() {
 | 
				
			||||||
 | 
					      // Make sure the injector is initialized
 | 
				
			||||||
 | 
					      VimPlugin.getInstance()
 | 
				
			||||||
      return injector.globalOptions().ideatracetime
 | 
					      return injector.globalOptions().ideatracetime
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -261,7 +257,7 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
 | 
				
			|||||||
  private fun getEditor(e: AnActionEvent): Editor? {
 | 
					  private fun getEditor(e: AnActionEvent): Editor? {
 | 
				
			||||||
    return e.getData(PlatformDataKeys.EDITOR)
 | 
					    return e.getData(PlatformDataKeys.EDITOR)
 | 
				
			||||||
      ?: if (e.getData(PlatformDataKeys.CONTEXT_COMPONENT) is ExTextField) {
 | 
					      ?: if (e.getData(PlatformDataKeys.CONTEXT_COMPONENT) is ExTextField) {
 | 
				
			||||||
        ExEntryPanel.getInstance().ijEditor
 | 
					        EditorHolderService.getInstance().editor
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        null
 | 
					        null
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -298,10 +294,26 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
 | 
				
			|||||||
        .addAll(getKeyStrokes(KeyEvent.VK_BACK_SPACE, 0, InputEvent.CTRL_DOWN_MASK))
 | 
					        .addAll(getKeyStrokes(KeyEvent.VK_BACK_SPACE, 0, InputEvent.CTRL_DOWN_MASK))
 | 
				
			||||||
        .addAll(getKeyStrokes(KeyEvent.VK_INSERT, 0))
 | 
					        .addAll(getKeyStrokes(KeyEvent.VK_INSERT, 0))
 | 
				
			||||||
        .addAll(getKeyStrokes(KeyEvent.VK_DELETE, 0, InputEvent.CTRL_DOWN_MASK))
 | 
					        .addAll(getKeyStrokes(KeyEvent.VK_DELETE, 0, InputEvent.CTRL_DOWN_MASK))
 | 
				
			||||||
        .addAll(getKeyStrokes(KeyEvent.VK_UP, 0))
 | 
					        .addAll(getKeyStrokes(KeyEvent.VK_UP, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK))
 | 
				
			||||||
        .addAll(getKeyStrokes(KeyEvent.VK_DOWN, 0))
 | 
					        .addAll(getKeyStrokes(KeyEvent.VK_DOWN, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK))
 | 
				
			||||||
        .addAll(getKeyStrokes(KeyEvent.VK_LEFT, 0))
 | 
					        .addAll(
 | 
				
			||||||
        .addAll(getKeyStrokes(KeyEvent.VK_RIGHT, 0))
 | 
					          getKeyStrokes(
 | 
				
			||||||
 | 
					            KeyEvent.VK_LEFT,
 | 
				
			||||||
 | 
					            0,
 | 
				
			||||||
 | 
					            InputEvent.CTRL_DOWN_MASK,
 | 
				
			||||||
 | 
					            InputEvent.SHIFT_DOWN_MASK,
 | 
				
			||||||
 | 
					            InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK,
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .addAll(
 | 
				
			||||||
 | 
					          getKeyStrokes(
 | 
				
			||||||
 | 
					            KeyEvent.VK_RIGHT,
 | 
				
			||||||
 | 
					            0,
 | 
				
			||||||
 | 
					            InputEvent.CTRL_DOWN_MASK,
 | 
				
			||||||
 | 
					            InputEvent.SHIFT_DOWN_MASK,
 | 
				
			||||||
 | 
					            InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK,
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        .addAll(
 | 
					        .addAll(
 | 
				
			||||||
          getKeyStrokes(
 | 
					          getKeyStrokes(
 | 
				
			||||||
            KeyEvent.VK_HOME,
 | 
					            KeyEvent.VK_HOME,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,7 @@ import com.maddyhome.idea.vim.group.visual.VimSelection
 | 
				
			|||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
 | 
					import com.maddyhome.idea.vim.handler.VimActionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
 | 
					import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.MessageHelper
 | 
					import com.maddyhome.idea.vim.helper.MessageHelper
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.inRepeatMode
 | 
					import com.maddyhome.idea.vim.helper.vimStateMachine
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.ij
 | 
					import com.maddyhome.idea.vim.newapi.ij
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
					import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
 | 
					import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
 | 
				
			||||||
@@ -37,7 +37,7 @@ import com.maddyhome.idea.vim.vimscript.model.expressions.FunctionCallExpression
 | 
				
			|||||||
import com.maddyhome.idea.vim.vimscript.model.expressions.SimpleExpression
 | 
					import com.maddyhome.idea.vim.vimscript.model.expressions.SimpleExpression
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// todo make it multicaret
 | 
					// todo make it multicaret
 | 
				
			||||||
private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, motionType: SelectionType): Boolean {
 | 
					private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, selectionType: SelectionType): Boolean {
 | 
				
			||||||
  val func = injector.globalOptions().operatorfunc
 | 
					  val func = injector.globalOptions().operatorfunc
 | 
				
			||||||
  if (func.isEmpty()) {
 | 
					  if (func.isEmpty()) {
 | 
				
			||||||
    VimPlugin.showMessage(MessageHelper.message("E774"))
 | 
					    VimPlugin.showMessage(MessageHelper.message("E774"))
 | 
				
			||||||
@@ -57,9 +57,9 @@ private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textR
 | 
				
			|||||||
        if (value is VimFuncref) {
 | 
					        if (value is VimFuncref) {
 | 
				
			||||||
          handler = value.handler
 | 
					          handler = value.handler
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } catch (_: ExException) {
 | 
					      } catch (ex: ExException) {
 | 
				
			||||||
        // Get the argument for function('...') or funcref('...') for the error message
 | 
					        // Get the argument for function('...') or funcref('...') for the error message
 | 
				
			||||||
        val functionName = if (expression is FunctionCallExpression && expression.arguments.isNotEmpty()) {
 | 
					        val functionName = if (expression is FunctionCallExpression && expression.arguments.size > 0) {
 | 
				
			||||||
          expression.arguments[0].evaluate(editor, context, scriptContext).toString()
 | 
					          expression.arguments[0].evaluate(editor, context, scriptContext).toString()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else {
 | 
					        else {
 | 
				
			||||||
@@ -77,7 +77,7 @@ private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textR
 | 
				
			|||||||
    return false
 | 
					    return false
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  val arg = when (motionType) {
 | 
					  val arg = when (selectionType) {
 | 
				
			||||||
    SelectionType.LINE_WISE -> "line"
 | 
					    SelectionType.LINE_WISE -> "line"
 | 
				
			||||||
    SelectionType.CHARACTER_WISE -> "char"
 | 
					    SelectionType.CHARACTER_WISE -> "char"
 | 
				
			||||||
    SelectionType.BLOCK_WISE -> "block"
 | 
					    SelectionType.BLOCK_WISE -> "block"
 | 
				
			||||||
@@ -101,13 +101,19 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
 | 
				
			|||||||
  override val argumentType: Argument.Type = Argument.Type.MOTION
 | 
					  override val argumentType: Argument.Type = Argument.Type.MOTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
 | 
					  override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
 | 
				
			||||||
    val argument = cmd.argument as? Argument.Motion ?: return false
 | 
					    val argument = cmd.argument ?: return false
 | 
				
			||||||
    if (!editor.inRepeatMode) {
 | 
					    if (!editor.vimStateMachine.isDotRepeatInProgress) {
 | 
				
			||||||
      argumentCaptured = argument
 | 
					      argumentCaptured = argument
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    val range = getMotionRange(editor, context, argument, operatorArguments)
 | 
					    val range = getMotionRange(editor, context, argument, operatorArguments)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (range != null) {
 | 
					    if (range != null) {
 | 
				
			||||||
      return doOperatorAction(editor, context, range, argument.getMotionType())
 | 
					      val selectionType = if (argument.motion.isLinewiseMotion()) {
 | 
				
			||||||
 | 
					        SelectionType.LINE_WISE
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        SelectionType.CHARACTER_WISE
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return doOperatorAction(editor, context, range, selectionType)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return false
 | 
					    return false
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -115,7 +121,7 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
 | 
				
			|||||||
  private fun getMotionRange(
 | 
					  private fun getMotionRange(
 | 
				
			||||||
    editor: VimEditor,
 | 
					    editor: VimEditor,
 | 
				
			||||||
    context: ExecutionContext,
 | 
					    context: ExecutionContext,
 | 
				
			||||||
    argument: Argument.Motion,
 | 
					    argument: Argument,
 | 
				
			||||||
    operatorArguments: OperatorArguments,
 | 
					    operatorArguments: OperatorArguments,
 | 
				
			||||||
  ): TextRange? {
 | 
					  ): TextRange? {
 | 
				
			||||||
    // Note that we're using getMotionRange2 in order to avoid normalising the linewise range into line start
 | 
					    // Note that we're using getMotionRange2 in order to avoid normalising the linewise range into line start
 | 
				
			||||||
@@ -130,7 +136,7 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
 | 
				
			|||||||
      operatorArguments,
 | 
					      operatorArguments,
 | 
				
			||||||
    )?.normalize()?.let {
 | 
					    )?.normalize()?.let {
 | 
				
			||||||
      // If we're linewise, make sure the end offset isn't just the EOL char
 | 
					      // If we're linewise, make sure the end offset isn't just the EOL char
 | 
				
			||||||
      if (argument.getMotionType() == SelectionType.LINE_WISE && it.endOffset < editor.fileSize()) {
 | 
					      if (argument.motion.isLinewiseMotion() && it.endOffset < editor.fileSize()) {
 | 
				
			||||||
        TextRange(it.startOffset, it.endOffset + 1)
 | 
					        TextRange(it.startOffset, it.endOffset + 1)
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        it
 | 
					        it
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@ import com.maddyhome.idea.vim.api.injector
 | 
				
			|||||||
import com.maddyhome.idea.vim.command.Command
 | 
					import com.maddyhome.idea.vim.command.Command
 | 
				
			||||||
import com.maddyhome.idea.vim.command.OperatorArguments
 | 
					import com.maddyhome.idea.vim.command.OperatorArguments
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
 | 
					import com.maddyhome.idea.vim.handler.VimActionHandler
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.vimStateMachine
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.ij
 | 
					import com.maddyhome.idea.vim.newapi.ij
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@CommandOrMotion(keys = ["."], modes = [Mode.NORMAL])
 | 
					@CommandOrMotion(keys = ["."], modes = [Mode.NORMAL])
 | 
				
			||||||
@@ -24,8 +25,8 @@ internal class RepeatChangeAction : VimActionHandler.SingleExecution() {
 | 
				
			|||||||
  override val type: Command.Type = Command.Type.OTHER_WRITABLE
 | 
					  override val type: Command.Type = Command.Type.OTHER_WRITABLE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
 | 
					  override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
 | 
				
			||||||
    val state = injector.vimState
 | 
					    val state = editor.vimStateMachine
 | 
				
			||||||
    var lastCommand = VimRepeater.lastChangeCommand
 | 
					    val lastCommand = VimRepeater.lastChangeCommand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (lastCommand == null && Extension.lastExtensionHandler == null) return false
 | 
					    if (lastCommand == null && Extension.lastExtensionHandler == null) return false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -57,7 +58,12 @@ internal class RepeatChangeAction : VimActionHandler.SingleExecution() {
 | 
				
			|||||||
      )
 | 
					      )
 | 
				
			||||||
    } else if (!repeatHandler && lastCommand != null) {
 | 
					    } else if (!repeatHandler && lastCommand != null) {
 | 
				
			||||||
      if (cmd.rawCount > 0) {
 | 
					      if (cmd.rawCount > 0) {
 | 
				
			||||||
        lastCommand = lastCommand.copy(rawCount = cmd.rawCount)
 | 
					        lastCommand.rawCount = cmd.count
 | 
				
			||||||
 | 
					        val arg = lastCommand.argument
 | 
				
			||||||
 | 
					        if (arg != null) {
 | 
				
			||||||
 | 
					          val mot = arg.motion
 | 
				
			||||||
 | 
					          mot.rawCount = 0
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      state.executingCommand = lastCommand
 | 
					      state.executingCommand = lastCommand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,7 +20,7 @@ import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
 | 
				
			|||||||
import com.maddyhome.idea.vim.newapi.ijOptions
 | 
					import com.maddyhome.idea.vim.newapi.ijOptions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@CommandOrMotion(keys = ["gJ"], modes = [Mode.NORMAL])
 | 
					@CommandOrMotion(keys = ["gJ"], modes = [Mode.NORMAL])
 | 
				
			||||||
class DeleteJoinLinesAction : ChangeEditorActionHandler.ConditionalSingleExecution() {
 | 
					public class DeleteJoinLinesAction : ChangeEditorActionHandler.ConditionalSingleExecution() {
 | 
				
			||||||
  override val type: Command.Type = Command.Type.DELETE
 | 
					  override val type: Command.Type = Command.Type.DELETE
 | 
				
			||||||
  override fun runAsMulticaret(
 | 
					  override fun runAsMulticaret(
 | 
				
			||||||
    editor: VimEditor,
 | 
					    editor: VimEditor,
 | 
				
			||||||
@@ -40,7 +40,7 @@ class DeleteJoinLinesAction : ChangeEditorActionHandler.ConditionalSingleExecuti
 | 
				
			|||||||
  ): Boolean {
 | 
					  ): Boolean {
 | 
				
			||||||
    injector.editorGroup.notifyIdeaJoin(editor)
 | 
					    injector.editorGroup.notifyIdeaJoin(editor)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return injector.changeGroup.deleteJoinLines(editor, context, caret, operatorArguments.count1, false)
 | 
					    return injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, false, operatorArguments)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun execute(
 | 
					  override fun execute(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
 | 
				
			|||||||
import com.maddyhome.idea.vim.newapi.ijOptions
 | 
					import com.maddyhome.idea.vim.newapi.ijOptions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@CommandOrMotion(keys = ["J"], modes = [Mode.NORMAL])
 | 
					@CommandOrMotion(keys = ["J"], modes = [Mode.NORMAL])
 | 
				
			||||||
class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecution() {
 | 
					public class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecution() {
 | 
				
			||||||
  override val type: Command.Type = Command.Type.DELETE
 | 
					  override val type: Command.Type = Command.Type.DELETE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun execute(
 | 
					  override fun execute(
 | 
				
			||||||
@@ -35,7 +35,7 @@ class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecution()
 | 
				
			|||||||
    injector.editorGroup.notifyIdeaJoin(editor)
 | 
					    injector.editorGroup.notifyIdeaJoin(editor)
 | 
				
			||||||
    var res = true
 | 
					    var res = true
 | 
				
			||||||
    editor.nativeCarets().sortedByDescending { it.offset }.forEach { caret ->
 | 
					    editor.nativeCarets().sortedByDescending { it.offset }.forEach { caret ->
 | 
				
			||||||
      if (!injector.changeGroup.deleteJoinLines(editor, context, caret, operatorArguments.count1, true)) {
 | 
					      if (!injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, true, operatorArguments)) {
 | 
				
			||||||
        res = false
 | 
					        res = false
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,7 +23,7 @@ import com.maddyhome.idea.vim.newapi.ijOptions
 | 
				
			|||||||
 * @author vlan
 | 
					 * @author vlan
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@CommandOrMotion(keys = ["gJ"], modes = [Mode.VISUAL])
 | 
					@CommandOrMotion(keys = ["gJ"], modes = [Mode.VISUAL])
 | 
				
			||||||
class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExecution() {
 | 
					public class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExecution() {
 | 
				
			||||||
  override val type: Command.Type = Command.Type.DELETE
 | 
					  override val type: Command.Type = Command.Type.DELETE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun executeForAllCarets(
 | 
					  override fun executeForAllCarets(
 | 
				
			||||||
@@ -44,7 +44,6 @@ class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExecution(
 | 
				
			|||||||
      val range = caretsAndSelections[caret] ?: return@forEach
 | 
					      val range = caretsAndSelections[caret] ?: return@forEach
 | 
				
			||||||
      if (!injector.changeGroup.deleteJoinRange(
 | 
					      if (!injector.changeGroup.deleteJoinRange(
 | 
				
			||||||
          editor,
 | 
					          editor,
 | 
				
			||||||
          context,
 | 
					 | 
				
			||||||
          caret,
 | 
					          caret,
 | 
				
			||||||
          range.toVimTextRange(true).normalize(),
 | 
					          range.toVimTextRange(true).normalize(),
 | 
				
			||||||
          false,
 | 
					          false,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,7 +23,7 @@ import com.maddyhome.idea.vim.newapi.ijOptions
 | 
				
			|||||||
 * @author vlan
 | 
					 * @author vlan
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@CommandOrMotion(keys = ["J"], modes = [Mode.VISUAL])
 | 
					@CommandOrMotion(keys = ["J"], modes = [Mode.VISUAL])
 | 
				
			||||||
class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.SingleExecution() {
 | 
					public class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.SingleExecution() {
 | 
				
			||||||
  override val type: Command.Type = Command.Type.DELETE
 | 
					  override val type: Command.Type = Command.Type.DELETE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun executeForAllCarets(
 | 
					  override fun executeForAllCarets(
 | 
				
			||||||
@@ -44,7 +44,6 @@ class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.SingleExec
 | 
				
			|||||||
      val range = caretsAndSelections[caret] ?: return@forEach
 | 
					      val range = caretsAndSelections[caret] ?: return@forEach
 | 
				
			||||||
      if (!injector.changeGroup.deleteJoinRange(
 | 
					      if (!injector.changeGroup.deleteJoinRange(
 | 
				
			||||||
          editor,
 | 
					          editor,
 | 
				
			||||||
          context,
 | 
					 | 
				
			||||||
          caret,
 | 
					          caret,
 | 
				
			||||||
          range.toVimTextRange(true).normalize(),
 | 
					          range.toVimTextRange(true).normalize(),
 | 
				
			||||||
          true,
 | 
					          true,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,10 +20,13 @@ import com.maddyhome.idea.vim.command.OperatorArguments
 | 
				
			|||||||
import com.maddyhome.idea.vim.handler.IdeActionHandler
 | 
					import com.maddyhome.idea.vim.handler.IdeActionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
 | 
					import com.maddyhome.idea.vim.handler.VimActionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.enumSetOf
 | 
					import com.maddyhome.idea.vim.helper.enumSetOf
 | 
				
			||||||
import com.maddyhome.idea.vim.undo.VimKeyBasedUndoService
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.undo.VimTimestampBasedUndoService
 | 
					 | 
				
			||||||
import java.util.*
 | 
					import java.util.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@CommandOrMotion(keys = ["<C-H>", "<BS>"], modes = [Mode.INSERT])
 | 
				
			||||||
 | 
					internal class VimEditorBackSpace : IdeActionHandler(IdeActions.ACTION_EDITOR_BACKSPACE) {
 | 
				
			||||||
 | 
					  override val type: Command.Type = Command.Type.DELETE
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@CommandOrMotion(keys = ["<Del>"], modes = [Mode.INSERT])
 | 
					@CommandOrMotion(keys = ["<Del>"], modes = [Mode.INSERT])
 | 
				
			||||||
internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELETE) {
 | 
					internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELETE) {
 | 
				
			||||||
  override val type: Command.Type = Command.Type.DELETE
 | 
					  override val type: Command.Type = Command.Type.DELETE
 | 
				
			||||||
@@ -33,23 +36,6 @@ internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELET
 | 
				
			|||||||
internal class VimEditorDown : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN) {
 | 
					internal class VimEditorDown : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN) {
 | 
				
			||||||
  override val type: Command.Type = Command.Type.MOTION
 | 
					  override val type: Command.Type = Command.Type.MOTION
 | 
				
			||||||
  override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
 | 
					  override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
 | 
				
			||||||
 | 
					 | 
				
			||||||
  override fun execute(
 | 
					 | 
				
			||||||
    editor: VimEditor,
 | 
					 | 
				
			||||||
    context: ExecutionContext,
 | 
					 | 
				
			||||||
    cmd: Command,
 | 
					 | 
				
			||||||
    operatorArguments: OperatorArguments
 | 
					 | 
				
			||||||
  ): Boolean {
 | 
					 | 
				
			||||||
    val undo = injector.undo
 | 
					 | 
				
			||||||
    when (undo) {
 | 
					 | 
				
			||||||
      is VimKeyBasedUndoService -> undo.setMergeUndoKey()
 | 
					 | 
				
			||||||
      is VimTimestampBasedUndoService -> {
 | 
					 | 
				
			||||||
        val nanoTime = System.nanoTime()
 | 
					 | 
				
			||||||
        editor.forEachCaret { undo.endInsertSequence(it, it.offset, nanoTime) }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return super.execute(editor, context, cmd, operatorArguments)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@CommandOrMotion(keys = ["<Tab>", "<C-I>"], modes = [Mode.INSERT])
 | 
					@CommandOrMotion(keys = ["<Tab>", "<C-I>"], modes = [Mode.INSERT])
 | 
				
			||||||
@@ -62,23 +48,6 @@ internal class VimEditorTab : IdeActionHandler(IdeActions.ACTION_EDITOR_TAB) {
 | 
				
			|||||||
internal class VimEditorUp : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_UP) {
 | 
					internal class VimEditorUp : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_UP) {
 | 
				
			||||||
  override val type: Command.Type = Command.Type.MOTION
 | 
					  override val type: Command.Type = Command.Type.MOTION
 | 
				
			||||||
  override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
 | 
					  override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
 | 
				
			||||||
 | 
					 | 
				
			||||||
  override fun execute(
 | 
					 | 
				
			||||||
    editor: VimEditor,
 | 
					 | 
				
			||||||
    context: ExecutionContext,
 | 
					 | 
				
			||||||
    cmd: Command,
 | 
					 | 
				
			||||||
    operatorArguments: OperatorArguments
 | 
					 | 
				
			||||||
  ): Boolean {
 | 
					 | 
				
			||||||
    val undo = injector.undo
 | 
					 | 
				
			||||||
    when (undo) {
 | 
					 | 
				
			||||||
      is VimKeyBasedUndoService -> undo.setMergeUndoKey()
 | 
					 | 
				
			||||||
      is VimTimestampBasedUndoService -> {
 | 
					 | 
				
			||||||
        val nanoTime = System.nanoTime()
 | 
					 | 
				
			||||||
        editor.forEachCaret { undo.endInsertSequence(it, it.offset, nanoTime) }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return super.execute(editor, context, cmd, operatorArguments)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@CommandOrMotion(keys = ["K"], modes = [Mode.NORMAL])
 | 
					@CommandOrMotion(keys = ["K"], modes = [Mode.NORMAL])
 | 
				
			||||||
@@ -86,7 +55,7 @@ internal class VimQuickJavaDoc : VimActionHandler.SingleExecution() {
 | 
				
			|||||||
  override val type: Command.Type = Command.Type.OTHER_READONLY
 | 
					  override val type: Command.Type = Command.Type.OTHER_READONLY
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
 | 
					  override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
 | 
				
			||||||
    injector.actionExecutor.executeAction(editor, IdeActions.ACTION_QUICK_JAVADOC, context)
 | 
					    injector.actionExecutor.executeAction(IdeActions.ACTION_QUICK_JAVADOC, context)
 | 
				
			||||||
    return true
 | 
					    return true
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,18 +9,21 @@
 | 
				
			|||||||
package com.maddyhome.idea.vim.command
 | 
					package com.maddyhome.idea.vim.command
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.editor.Editor
 | 
					import com.intellij.openapi.editor.Editor
 | 
				
			||||||
import com.maddyhome.idea.vim.KeyHandler
 | 
					import com.maddyhome.idea.vim.helper.vimStateMachine
 | 
				
			||||||
import com.maddyhome.idea.vim.api.injector
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
import com.maddyhome.idea.vim.state.VimStateMachine
 | 
					import com.maddyhome.idea.vim.state.VimStateMachine
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * COMPATIBILITY-LAYER: Additional class
 | 
					 * COMPATIBILITY-LAYER: Additional class
 | 
				
			||||||
 * Please see: https://jb.gg/zo8n0r
 | 
					 * Please see: https://jb.gg/zo8n0r
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@Deprecated("Use `injector.vimState`")
 | 
					public class CommandState(private val machine: VimStateMachine) {
 | 
				
			||||||
class CommandState(private val machine: VimStateMachine) {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  val mode: Mode
 | 
					  public val isOperatorPending: Boolean
 | 
				
			||||||
 | 
					    get() = machine.isOperatorPending(machine.mode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public val mode: Mode
 | 
				
			||||||
    get() {
 | 
					    get() {
 | 
				
			||||||
      val myMode = machine.mode
 | 
					      val myMode = machine.mode
 | 
				
			||||||
      return when (myMode) {
 | 
					      return when (myMode) {
 | 
				
			||||||
@@ -34,23 +37,13 @@ class CommandState(private val machine: VimStateMachine) {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Deprecated("Use `KeyHandler.keyHandlerState.commandBuilder", ReplaceWith(
 | 
					  public val commandBuilder: CommandBuilder
 | 
				
			||||||
    "KeyHandler.getInstance().keyHandlerState.commandBuilder",
 | 
					    get() = machine.commandBuilder
 | 
				
			||||||
    "com.maddyhome.idea.vim.KeyHandler"
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
  val commandBuilder: CommandBuilder
 | 
					 | 
				
			||||||
    get() = KeyHandler.getInstance().keyHandlerState.commandBuilder
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Deprecated("Use `KeyHandler.keyHandlerState.mappingState", ReplaceWith(
 | 
					  public val mappingState: MappingState
 | 
				
			||||||
    "KeyHandler.getInstance().keyHandlerState.mappingState",
 | 
					    get() = machine.mappingState
 | 
				
			||||||
    "com.maddyhome.idea.vim.KeyHandler"
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
  val mappingState: MappingState
 | 
					 | 
				
			||||||
    get() = KeyHandler.getInstance().keyHandlerState.mappingState
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  enum class Mode {
 | 
					  public enum class Mode {
 | 
				
			||||||
    // Basic modes
 | 
					    // Basic modes
 | 
				
			||||||
    COMMAND, VISUAL, SELECT, INSERT, CMD_LINE, /*EX*/
 | 
					    COMMAND, VISUAL, SELECT, INSERT, CMD_LINE, /*EX*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -58,15 +51,22 @@ class CommandState(private val machine: VimStateMachine) {
 | 
				
			|||||||
    OP_PENDING, REPLACE /*, VISUAL_REPLACE*/, INSERT_NORMAL, INSERT_VISUAL, INSERT_SELECT
 | 
					    OP_PENDING, REPLACE /*, VISUAL_REPLACE*/, INSERT_NORMAL, INSERT_VISUAL, INSERT_SELECT
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  enum class SubMode {
 | 
					  public enum class SubMode {
 | 
				
			||||||
    NONE, VISUAL_CHARACTER, VISUAL_LINE, VISUAL_BLOCK
 | 
					    NONE, VISUAL_CHARACTER, VISUAL_LINE, VISUAL_BLOCK
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  companion object {
 | 
					  public companion object {
 | 
				
			||||||
    @JvmStatic
 | 
					    @JvmStatic
 | 
				
			||||||
    @Deprecated("Use `injector.vimState`")
 | 
					    public fun getInstance(editor: Editor): CommandState {
 | 
				
			||||||
    fun getInstance(editor: Editor): CommandState {
 | 
					      return CommandState(editor.vim.vimStateMachine)
 | 
				
			||||||
      return CommandState(injector.vimState)
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					internal val CommandState.SubMode.engine: SelectionType
 | 
				
			||||||
 | 
					  get() = when (this) {
 | 
				
			||||||
 | 
					    CommandState.SubMode.NONE -> error("Unexpected value")
 | 
				
			||||||
 | 
					    CommandState.SubMode.VISUAL_CHARACTER -> SelectionType.CHARACTER_WISE
 | 
				
			||||||
 | 
					    CommandState.SubMode.VISUAL_LINE -> SelectionType.LINE_WISE
 | 
				
			||||||
 | 
					    CommandState.SubMode.VISUAL_BLOCK -> SelectionType.BLOCK_WISE
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,18 +12,18 @@ import com.intellij.openapi.editor.Editor
 | 
				
			|||||||
import com.intellij.openapi.editor.LogicalPosition
 | 
					import com.intellij.openapi.editor.LogicalPosition
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CharacterPosition(line: Int, col: Int) : LogicalPosition(line, col) {
 | 
					public class CharacterPosition(line: Int, col: Int) : LogicalPosition(line, col) {
 | 
				
			||||||
  fun toOffset(editor: Editor): Int = editor.vim.getLineStartOffset(line) + column
 | 
					  public fun toOffset(editor: Editor): Int = editor.vim.getLineStartOffset(line) + column
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  companion object {
 | 
					  public companion object {
 | 
				
			||||||
    fun fromOffset(editor: Editor, offset: Int): CharacterPosition {
 | 
					    public fun fromOffset(editor: Editor, offset: Int): CharacterPosition {
 | 
				
			||||||
      // logical position "expands" tabs
 | 
					      // logical position "expands" tabs
 | 
				
			||||||
      val logicalPosition = editor.offsetToLogicalPosition(offset)
 | 
					      val logicalPosition = editor.offsetToLogicalPosition(offset)
 | 
				
			||||||
      val lineStartOffset = editor.vim.getLineStartOffset(logicalPosition.line)
 | 
					      val lineStartOffset = editor.vim.getLineStartOffset(logicalPosition.line)
 | 
				
			||||||
      return CharacterPosition(logicalPosition.line, offset - lineStartOffset)
 | 
					      return CharacterPosition(logicalPosition.line, offset - lineStartOffset)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun atCaret(editor: Editor): CharacterPosition {
 | 
					    public fun atCaret(editor: Editor): CharacterPosition {
 | 
				
			||||||
      return fromOffset(editor, editor.caretModel.offset)
 | 
					      return fromOffset(editor, editor.caretModel.offset)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,17 +12,17 @@ import com.intellij.application.options.CodeStyle
 | 
				
			|||||||
import com.intellij.openapi.editor.Editor
 | 
					import com.intellij.openapi.editor.Editor
 | 
				
			||||||
import com.intellij.openapi.project.Project
 | 
					import com.intellij.openapi.project.Project
 | 
				
			||||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings.IndentOptions
 | 
					import com.intellij.psi.codeStyle.CommonCodeStyleSettings.IndentOptions
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimIndentConfig
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal class IndentConfig private constructor(indentOptions: IndentOptions): VimIndentConfig {
 | 
					internal class IndentConfig private constructor(indentOptions: IndentOptions) {
 | 
				
			||||||
  private val indentSize = indentOptions.INDENT_SIZE
 | 
					  private val indentSize = indentOptions.INDENT_SIZE
 | 
				
			||||||
  private val tabSize = indentOptions.TAB_SIZE
 | 
					  private val tabSize = indentOptions.TAB_SIZE
 | 
				
			||||||
  private val isUseTabs = indentOptions.USE_TAB_CHARACTER
 | 
					  private val isUseTabs = indentOptions.USE_TAB_CHARACTER
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun getIndentSize(depth: Int): Int = indentSize * depth
 | 
					  fun getTotalIndent(count: Int): Int = indentSize * count
 | 
				
			||||||
  override fun createIndentByDepth(depth: Int): String = createIndentBySize(getIndentSize(depth))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun createIndentBySize(size: Int): String {
 | 
					  fun createIndentByCount(count: Int): String = createIndentBySize(getTotalIndent(count))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fun createIndentBySize(size: Int): String {
 | 
				
			||||||
    val tabCount: Int
 | 
					    val tabCount: Int
 | 
				
			||||||
    val spaceCount: Int
 | 
					    val spaceCount: Int
 | 
				
			||||||
    if (isUseTabs) {
 | 
					    if (isUseTabs) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,8 +18,6 @@ import kotlin.reflect.KProperty
 | 
				
			|||||||
internal class VimState {
 | 
					internal class VimState {
 | 
				
			||||||
  var isIdeaJoinNotified by StateProperty("idea-join")
 | 
					  var isIdeaJoinNotified by StateProperty("idea-join")
 | 
				
			||||||
  var isIdeaPutNotified by StateProperty("idea-put")
 | 
					  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) {
 | 
					  fun readData(element: Element) {
 | 
				
			||||||
    val notifications = element.getChild("notifications")
 | 
					    val notifications = element.getChild("notifications")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,142 +9,62 @@ package com.maddyhome.idea.vim.ex
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.application.ApplicationManager
 | 
					import com.intellij.openapi.application.ApplicationManager
 | 
				
			||||||
import com.intellij.openapi.editor.Editor
 | 
					import com.intellij.openapi.editor.Editor
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimOutputPanelBase
 | 
					import com.maddyhome.idea.vim.api.VimExOutputPanel
 | 
				
			||||||
import com.maddyhome.idea.vim.api.injector
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.helper.vimExOutput
 | 
					import com.maddyhome.idea.vim.helper.vimExOutput
 | 
				
			||||||
import com.maddyhome.idea.vim.ui.ExOutputPanel
 | 
					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
 | 
					// TODO: We need a nicer way to handle output, especially wrt testing, appending + clearing
 | 
				
			||||||
class ExOutputModel(private val myEditor: WeakReference<Editor>) : VimOutputPanelBase() {
 | 
					public class ExOutputModel private constructor(private val myEditor: Editor) : VimExOutputPanel {
 | 
				
			||||||
  private var isActiveInTestMode = false
 | 
					  private var isActiveInTestMode = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  val editor get() = myEditor.get()
 | 
					  override val isActive: Boolean
 | 
				
			||||||
 | 
					 | 
				
			||||||
  val isActive: Boolean
 | 
					 | 
				
			||||||
    get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
 | 
					    get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
 | 
				
			||||||
      editor?.let { ExOutputPanel.getNullablePanel(it) }?.myActive ?: false
 | 
					      ExOutputPanel.isPanelActive(myEditor)
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      isActiveInTestMode
 | 
					      isActiveInTestMode
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun addText(text: String, isNewLine: Boolean) {
 | 
					  override var text: String? = null
 | 
				
			||||||
    if (this.text.isNotEmpty() && isNewLine) this.text += "\n$text" else this.text += text
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  override fun show() {
 | 
					 | 
				
			||||||
    if (editor == null) return
 | 
					 | 
				
			||||||
    val currentPanel = injector.outputPanel.getCurrentOutputPanel()
 | 
					 | 
				
			||||||
    if (currentPanel != null && currentPanel != this) currentPanel.close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    editor!!.vimExOutput = this
 | 
					 | 
				
			||||||
    val exOutputPanel = ExOutputPanel.getInstance(editor!!)
 | 
					 | 
				
			||||||
    if (!exOutputPanel.myActive) {
 | 
					 | 
				
			||||||
      if (ApplicationManager.getApplication().isUnitTestMode) {
 | 
					 | 
				
			||||||
        isActiveInTestMode = true
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        exOutputPanel.activate()
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  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) {
 | 
					    get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
 | 
				
			||||||
      editor?.let { ExOutputPanel.getInstance(it).text } ?: ""
 | 
					      ExOutputPanel.getInstance(myEditor).text
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      // ExOutputPanel always returns a non-null string
 | 
					 | 
				
			||||||
      field
 | 
					      field
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    set(value) {
 | 
					    set(value) {
 | 
				
			||||||
      // ExOutputPanel will strip a trailing newline. We'll do it now so that tests have the same behaviour. We also
 | 
					 | 
				
			||||||
      // never pass null to ExOutputPanel, but we do store it for tests, so we know if we're active or not
 | 
					 | 
				
			||||||
      val newValue = value.removeSuffix("\n")
 | 
					 | 
				
			||||||
      if (!ApplicationManager.getApplication().isUnitTestMode) {
 | 
					      if (!ApplicationManager.getApplication().isUnitTestMode) {
 | 
				
			||||||
        editor?.let { ExOutputPanel.getInstance(it).setText(newValue) }
 | 
					        ExOutputPanel.getInstance(myEditor).setText(value ?: "")
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        field = newValue
 | 
					        field = value
 | 
				
			||||||
        isActiveInTestMode = newValue.isNotEmpty()
 | 
					        isActiveInTestMode = !value.isNullOrEmpty()
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  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) {
 | 
					  override fun output(text: String) {
 | 
				
			||||||
    this.text = text
 | 
					    this.text = text
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fun clear() {
 | 
					  override fun clear() {
 | 
				
			||||||
    text = ""
 | 
					    text = null
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  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() {
 | 
					  override fun close() {
 | 
				
			||||||
    if (!ApplicationManager.getApplication().isUnitTestMode) {
 | 
					    if (!ApplicationManager.getApplication().isUnitTestMode) {
 | 
				
			||||||
      editor?.let { ExOutputPanel.getInstance(it).close() }
 | 
					      ExOutputPanel.getInstance(myEditor).close()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else {
 | 
					    else {
 | 
				
			||||||
      isActiveInTestMode = false
 | 
					      isActiveInTestMode = false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  companion object {
 | 
					  public companion object {
 | 
				
			||||||
    @JvmStatic
 | 
					    @JvmStatic
 | 
				
			||||||
    fun getInstance(editor: Editor): ExOutputModel {
 | 
					    public fun getInstance(editor: Editor): ExOutputModel {
 | 
				
			||||||
      var model = editor.vimExOutput
 | 
					      var model = editor.vimExOutput
 | 
				
			||||||
      if (model == null) {
 | 
					      if (model == null) {
 | 
				
			||||||
        model = ExOutputModel(WeakReference(editor))
 | 
					        model = ExOutputModel(editor)
 | 
				
			||||||
        editor.vimExOutput = model
 | 
					        editor.vimExOutput = model
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return model
 | 
					      return model
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @JvmStatic
 | 
					 | 
				
			||||||
    fun tryGetInstance(editor: Editor) = editor.vimExOutput
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,30 +27,6 @@ public interface VimExtension {
 | 
				
			|||||||
    return MappingOwner.Plugin.Companion.get(getName());
 | 
					    return MappingOwner.Plugin.Companion.get(getName());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * This method is always called AFTER the full execution of the `.ideavimrc` file.
 | 
					 | 
				
			||||||
   * <p>
 | 
					 | 
				
			||||||
   * During vim initialization process, it firstly loads the .vimrc file, then executes scripts from the plugins folder.
 | 
					 | 
				
			||||||
   * This practically means that the .vimrc file is initialized first; then the plugins are loaded.
 | 
					 | 
				
			||||||
   * See `:h initialization`
 | 
					 | 
				
			||||||
   * <p>
 | 
					 | 
				
			||||||
   * Why does this matter? Because this affects the order of commands are executed. For example,
 | 
					 | 
				
			||||||
   * ```
 | 
					 | 
				
			||||||
   * plug 'tommcdo/vim-exchange'
 | 
					 | 
				
			||||||
   * let g:exchange_no_mappings=1
 | 
					 | 
				
			||||||
   * ```
 | 
					 | 
				
			||||||
   * Here the user will expect that the exchange plugin won't have default mappings. However, if we load vim-exchange
 | 
					 | 
				
			||||||
   *    immediately, this variable won't be initialized at the moment of plugin initialization.
 | 
					 | 
				
			||||||
   * <p>
 | 
					 | 
				
			||||||
   * There is also a tricky case for mappings override:
 | 
					 | 
				
			||||||
   * ```
 | 
					 | 
				
			||||||
   * plug 'tommcdo/vim-exchange'
 | 
					 | 
				
			||||||
   * map X <Plug>(ExchangeLine)
 | 
					 | 
				
			||||||
   * ```
 | 
					 | 
				
			||||||
   * For this case, a plugin with a good implementation detects that there is already a defined mapping for
 | 
					 | 
				
			||||||
   *   `<Plug>(ExchangeLine)` and doesn't register the default cxx mapping. However, such detection requires the mapping
 | 
					 | 
				
			||||||
   *   to be defined before the plugin initialization.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  void init();
 | 
					  void init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  default void dispose() {
 | 
					  default void dispose() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@ package com.maddyhome.idea.vim.extension
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.actionSystem.DataContext
 | 
					import com.intellij.openapi.actionSystem.DataContext
 | 
				
			||||||
import com.intellij.openapi.application.ApplicationManager
 | 
					import com.intellij.openapi.application.ApplicationManager
 | 
				
			||||||
 | 
					import com.intellij.openapi.components.service
 | 
				
			||||||
import com.intellij.openapi.diagnostic.logger
 | 
					import com.intellij.openapi.diagnostic.logger
 | 
				
			||||||
import com.intellij.openapi.editor.Editor
 | 
					import com.intellij.openapi.editor.Editor
 | 
				
			||||||
import com.maddyhome.idea.vim.KeyHandler
 | 
					import com.maddyhome.idea.vim.KeyHandler
 | 
				
			||||||
@@ -24,14 +25,13 @@ import com.maddyhome.idea.vim.common.CommandAlias
 | 
				
			|||||||
import com.maddyhome.idea.vim.common.CommandAliasHandler
 | 
					import com.maddyhome.idea.vim.common.CommandAliasHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.common.TextRange
 | 
					import com.maddyhome.idea.vim.common.TextRange
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.TestInputModel
 | 
					import com.maddyhome.idea.vim.helper.TestInputModel
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.inRepeatMode
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.helper.noneOfEnum
 | 
					import com.maddyhome.idea.vim.helper.noneOfEnum
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.vimStateMachine
 | 
				
			||||||
import com.maddyhome.idea.vim.key.MappingOwner
 | 
					import com.maddyhome.idea.vim.key.MappingOwner
 | 
				
			||||||
import com.maddyhome.idea.vim.key.OperatorFunction
 | 
					import com.maddyhome.idea.vim.key.OperatorFunction
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
					import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
				
			||||||
import com.maddyhome.idea.vim.ui.ModalEntry
 | 
					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.Executable
 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
 | 
					import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.VimLContext
 | 
					import com.maddyhome.idea.vim.vimscript.model.VimLContext
 | 
				
			||||||
@@ -51,13 +51,13 @@ import javax.swing.KeyStroke
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author vlan
 | 
					 * @author vlan
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
object VimExtensionFacade {
 | 
					public object VimExtensionFacade {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private val LOG = logger<VimExtensionFacade>()
 | 
					  private val LOG = logger<VimExtensionFacade>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** The 'map' command for mapping keys to handlers defined in extensions. */
 | 
					  /** The 'map' command for mapping keys to handlers defined in extensions. */
 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  fun putExtensionHandlerMapping(
 | 
					  public fun putExtensionHandlerMapping(
 | 
				
			||||||
    modes: Set<MappingMode>,
 | 
					    modes: Set<MappingMode>,
 | 
				
			||||||
    fromKeys: List<KeyStroke>,
 | 
					    fromKeys: List<KeyStroke>,
 | 
				
			||||||
    pluginOwner: MappingOwner,
 | 
					    pluginOwner: MappingOwner,
 | 
				
			||||||
@@ -68,14 +68,13 @@ object VimExtensionFacade {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * COMPATIBILITY-LAYER: Additional method
 | 
				
			||||||
 | 
					   * Please see: https://jb.gg/zo8n0r
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  /** The 'map' command for mapping keys to handlers defined in extensions. */
 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  @Deprecated("Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
 | 
					  public fun putExtensionHandlerMapping(
 | 
				
			||||||
    ReplaceWith(
 | 
					 | 
				
			||||||
      "VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
 | 
					 | 
				
			||||||
      "com.maddyhome.idea.vim.VimPlugin"
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
  fun putExtensionHandlerMapping(
 | 
					 | 
				
			||||||
    modes: Set<MappingMode>,
 | 
					    modes: Set<MappingMode>,
 | 
				
			||||||
    fromKeys: List<KeyStroke>,
 | 
					    fromKeys: List<KeyStroke>,
 | 
				
			||||||
    pluginOwner: MappingOwner,
 | 
					    pluginOwner: MappingOwner,
 | 
				
			||||||
@@ -87,7 +86,7 @@ object VimExtensionFacade {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /** The 'map' command for mapping keys to other keys. */
 | 
					  /** The 'map' command for mapping keys to other keys. */
 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  fun putKeyMapping(
 | 
					  public fun putKeyMapping(
 | 
				
			||||||
    modes: Set<MappingMode>,
 | 
					    modes: Set<MappingMode>,
 | 
				
			||||||
    fromKeys: List<KeyStroke>,
 | 
					    fromKeys: List<KeyStroke>,
 | 
				
			||||||
    pluginOwner: MappingOwner,
 | 
					    pluginOwner: MappingOwner,
 | 
				
			||||||
@@ -99,7 +98,7 @@ object VimExtensionFacade {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /** The 'map' command for mapping keys to other keys if there is no other mapping to these keys */
 | 
					  /** The 'map' command for mapping keys to other keys if there is no other mapping to these keys */
 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  fun putKeyMappingIfMissing(
 | 
					  public fun putKeyMappingIfMissing(
 | 
				
			||||||
    modes: Set<MappingMode>,
 | 
					    modes: Set<MappingMode>,
 | 
				
			||||||
    fromKeys: List<KeyStroke>,
 | 
					    fromKeys: List<KeyStroke>,
 | 
				
			||||||
    pluginOwner: MappingOwner,
 | 
					    pluginOwner: MappingOwner,
 | 
				
			||||||
@@ -113,7 +112,7 @@ object VimExtensionFacade {
 | 
				
			|||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Equivalent to calling 'command' to set up a user-defined command or alias
 | 
					   * Equivalent to calling 'command' to set up a user-defined command or alias
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  fun addCommand(
 | 
					  public fun addCommand(
 | 
				
			||||||
    name: String,
 | 
					    name: String,
 | 
				
			||||||
    handler: CommandAliasHandler,
 | 
					    handler: CommandAliasHandler,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
@@ -124,7 +123,7 @@ object VimExtensionFacade {
 | 
				
			|||||||
   * Equivalent to calling 'command' to set up a user-defined command or alias
 | 
					   * Equivalent to calling 'command' to set up a user-defined command or alias
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  fun addCommand(
 | 
					  public fun addCommand(
 | 
				
			||||||
    name: String,
 | 
					    name: String,
 | 
				
			||||||
    minimumNumberOfArguments: Int,
 | 
					    minimumNumberOfArguments: Int,
 | 
				
			||||||
    maximumNumberOfArguments: Int,
 | 
					    maximumNumberOfArguments: Int,
 | 
				
			||||||
@@ -142,7 +141,7 @@ object VimExtensionFacade {
 | 
				
			|||||||
   * leaves the editor in the insert mode if it's been activated.
 | 
					   * leaves the editor in the insert mode if it's been activated.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  fun executeNormalWithoutMapping(keys: List<KeyStroke>, editor: Editor) {
 | 
					  public fun executeNormalWithoutMapping(keys: List<KeyStroke>, editor: Editor) {
 | 
				
			||||||
    val context = injector.executionContextManager.getEditorExecutionContext(editor.vim)
 | 
					    val context = injector.executionContextManager.getEditorExecutionContext(editor.vim)
 | 
				
			||||||
    val keyHandler = KeyHandler.getInstance()
 | 
					    val keyHandler = KeyHandler.getInstance()
 | 
				
			||||||
    keys.forEach { keyHandler.handleKey(editor.vim, it, context, false, false, keyHandler.keyHandlerState) }
 | 
					    keys.forEach { keyHandler.handleKey(editor.vim, it, context, false, false, keyHandler.keyHandlerState) }
 | 
				
			||||||
@@ -150,8 +149,8 @@ object VimExtensionFacade {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /** Returns a single key stroke from the user input similar to 'getchar()'. */
 | 
					  /** Returns a single key stroke from the user input similar to 'getchar()'. */
 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  fun inputKeyStroke(editor: Editor): KeyStroke {
 | 
					  public fun inputKeyStroke(editor: Editor): KeyStroke {
 | 
				
			||||||
    if (editor.vim.inRepeatMode) {
 | 
					    if (editor.vim.vimStateMachine.isDotRepeatInProgress) {
 | 
				
			||||||
      val input = Extension.consumeKeystroke()
 | 
					      val input = Extension.consumeKeystroke()
 | 
				
			||||||
      LOG.trace("inputKeyStroke: dot repeat in progress. Input: $input")
 | 
					      LOG.trace("inputKeyStroke: dot repeat in progress. Input: $input")
 | 
				
			||||||
      return input ?: error("Not enough keystrokes saved: ${Extension.lastExtensionHandler}")
 | 
					      return input ?: error("Not enough keystrokes saved: ${Extension.lastExtensionHandler}")
 | 
				
			||||||
@@ -182,51 +181,43 @@ object VimExtensionFacade {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /** Returns a string typed in the input box similar to 'input()'. */
 | 
					  /** Returns a string typed in the input box similar to 'input()'. */
 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  fun inputString(editor: Editor, context: DataContext, prompt: String, finishOn: Char?): String {
 | 
					  public fun inputString(editor: Editor, context: DataContext, prompt: String, finishOn: Char?): String {
 | 
				
			||||||
    return (injector.commandLine as ExEntryPanelService).inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
 | 
					    return injector.commandLine.inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Get the current contents of the given register similar to 'getreg()'. */
 | 
					  /** Get the current contents of the given register similar to 'getreg()'. */
 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  @Deprecated("Please use com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister(com.maddyhome.idea.vim.api.VimEditor, char)")
 | 
					  public fun getRegister(register: Char): List<KeyStroke>? {
 | 
				
			||||||
  fun getRegister(register: Char): List<KeyStroke>? {
 | 
					 | 
				
			||||||
    val reg = VimPlugin.getRegister().getRegister(register) ?: return null
 | 
					    val reg = VimPlugin.getRegister().getRegister(register) ?: return null
 | 
				
			||||||
    return reg.keys
 | 
					    return reg.keys
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Get the current contents of the given register similar to 'getreg()'. */
 | 
					 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  fun getRegister(editor: VimEditor, register: Char): List<KeyStroke>? {
 | 
					  public fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
 | 
				
			||||||
    val reg = VimPlugin.getRegister().getRegister(editor, injector.executionContextManager.getEditorExecutionContext(editor), register) ?: return null
 | 
					 | 
				
			||||||
    return reg.keys
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @JvmStatic
 | 
					 | 
				
			||||||
  fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
 | 
					 | 
				
			||||||
    val reg = caret.registerStorage.getRegister(register) ?: return null
 | 
					    val reg = caret.registerStorage.getRegister(register) ?: return null
 | 
				
			||||||
    return reg.keys
 | 
					    return reg.keys
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Set the current contents of the given register */
 | 
					  /** Set the current contents of the given register */
 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  fun setRegister(register: Char, keys: List<KeyStroke?>?) {
 | 
					  public fun setRegister(register: Char, keys: List<KeyStroke?>?) {
 | 
				
			||||||
    VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList())
 | 
					    VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList())
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Set the current contents of the given register */
 | 
					  /** Set the current contents of the given register */
 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) {
 | 
					  public fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) {
 | 
				
			||||||
    caret.registerStorage.setKeys(register, keys?.filterNotNull() ?: emptyList())
 | 
					    caret.registerStorage.setKeys(register, keys?.filterNotNull() ?: emptyList())
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Set the current contents of the given register */
 | 
					  /** Set the current contents of the given register */
 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  fun setRegister(register: Char, keys: List<KeyStroke?>?, type: SelectionType) {
 | 
					  public fun setRegister(register: Char, keys: List<KeyStroke?>?, type: SelectionType) {
 | 
				
			||||||
    VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList(), type)
 | 
					    VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList(), type)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  fun exportScriptFunction(
 | 
					  public fun exportScriptFunction(
 | 
				
			||||||
    scope: Scope?,
 | 
					    scope: Scope?,
 | 
				
			||||||
    name: String,
 | 
					    name: String,
 | 
				
			||||||
    args: List<String>,
 | 
					    args: List<String>,
 | 
				
			||||||
@@ -262,7 +253,7 @@ object VimExtensionFacade {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFunction) {
 | 
					public fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFunction) {
 | 
				
			||||||
  exportScriptFunction(null, name, listOf("type"), emptyList(), false, noneOfEnum()) {
 | 
					  exportScriptFunction(null, name, listOf("type"), emptyList(), false, noneOfEnum()) {
 | 
				
			||||||
    editor, context, args ->
 | 
					    editor, context, args ->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -283,6 +274,6 @@ fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFu
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun interface ScriptFunction {
 | 
					public fun interface ScriptFunction {
 | 
				
			||||||
  fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult
 | 
					  public fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -19,12 +19,12 @@ import com.maddyhome.idea.vim.newapi.ij
 | 
				
			|||||||
 * COMPATIBILITY-LAYER: Created a class, renamed original class
 | 
					 * COMPATIBILITY-LAYER: Created a class, renamed original class
 | 
				
			||||||
 * Please see: https://jb.gg/zo8n0r
 | 
					 * Please see: https://jb.gg/zo8n0r
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
interface VimExtensionHandler : ExtensionHandler {
 | 
					public interface VimExtensionHandler : ExtensionHandler {
 | 
				
			||||||
  override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
 | 
					  override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
 | 
				
			||||||
    execute(editor.ij, context.ij)
 | 
					    execute(editor.ij, context.ij)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fun execute(editor: Editor, context: DataContext)
 | 
					  public fun execute(editor: Editor, context: DataContext)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  abstract class WithCallback : ExtensionHandler.WithCallback(), VimExtensionHandler
 | 
					  public abstract class WithCallback : ExtensionHandler.WithCallback(), VimExtensionHandler
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -88,10 +88,29 @@ internal object VimExtensionRegistrar : VimExtensionRegistrator {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * See the docs for [VimExtension.init]
 | 
					   * During vim initialization process, it firstly loads the .vimrc file, then executes scripts from the plugins folder.
 | 
				
			||||||
 | 
					   * This practically means that the .vimrc file is initialized first, then the plugins are loaded.
 | 
				
			||||||
 | 
					   * See `:h initialization`
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
   * In IdeaVim we don't have a separate plugins folder to load it after .ideavimrc load. However, we can collect
 | 
					   * In IdeaVim we don't have a separate plugins folder to load it after .ideavimrc load. However, we can collect
 | 
				
			||||||
   *   the list of plugins mentioned in the .ideavimrc and load them after .ideavimrc execution is finished.
 | 
					   *   the list of plugins mentioned in the .ideavimrc and load them after .ideavimrc execution is finished.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * Why this matters? Because this affects the order of commands are executed. For example:
 | 
				
			||||||
 | 
					   * ```
 | 
				
			||||||
 | 
					   * plug 'tommcdo/vim-exchange'
 | 
				
			||||||
 | 
					   * let g:exchange_no_mappings=1
 | 
				
			||||||
 | 
					   * ```
 | 
				
			||||||
 | 
					   * Here the user will expect that the exchange plugin won't have default mappings. However, if we load vim-exchange
 | 
				
			||||||
 | 
					   *    immediately, this variable won't be initialized at the moment of plugin initialization.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * There is also a tricky case for mappings override:
 | 
				
			||||||
 | 
					   * ```
 | 
				
			||||||
 | 
					   * plug 'tommcdo/vim-exchange'
 | 
				
			||||||
 | 
					   * map X <Plug>(ExchangeLine)
 | 
				
			||||||
 | 
					   * ```
 | 
				
			||||||
 | 
					   * For this case, a plugin with a good implementation detects that there is already a defined mapping for
 | 
				
			||||||
 | 
					   *   `<Plug>(ExchangeLine)` and doesn't register the default cxx mapping. However, such detection requires the mapping
 | 
				
			||||||
 | 
					   *   to be defined before the plugin initialization.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  fun enableDelayedExtensions() {
 | 
					  fun enableDelayedExtensions() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,6 @@
 | 
				
			|||||||
package com.maddyhome.idea.vim.extension.argtextobj;
 | 
					package com.maddyhome.idea.vim.extension.argtextobj;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.editor.Document;
 | 
					import com.intellij.openapi.editor.Document;
 | 
				
			||||||
import com.maddyhome.idea.vim.KeyHandler;
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.VimPlugin;
 | 
					import com.maddyhome.idea.vim.VimPlugin;
 | 
				
			||||||
import com.maddyhome.idea.vim.api.*;
 | 
					import com.maddyhome.idea.vim.api.*;
 | 
				
			||||||
import com.maddyhome.idea.vim.command.*;
 | 
					import com.maddyhome.idea.vim.command.*;
 | 
				
			||||||
@@ -24,7 +23,7 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
 | 
				
			|||||||
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
 | 
					import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimCaret;
 | 
					import com.maddyhome.idea.vim.newapi.IjVimCaret;
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
				
			||||||
import com.maddyhome.idea.vim.state.KeyHandlerState;
 | 
					import com.maddyhome.idea.vim.state.VimStateMachine;
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode;
 | 
					import com.maddyhome.idea.vim.state.mode.Mode;
 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString;
 | 
					import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString;
 | 
				
			||||||
import org.jetbrains.annotations.Nls;
 | 
					import org.jetbrains.annotations.Nls;
 | 
				
			||||||
@@ -33,6 +32,7 @@ import org.jetbrains.annotations.Nullable;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import java.util.ArrayDeque;
 | 
					import java.util.ArrayDeque;
 | 
				
			||||||
import java.util.Deque;
 | 
					import java.util.Deque;
 | 
				
			||||||
 | 
					import java.util.EnumSet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
 | 
					import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
 | 
				
			||||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing;
 | 
					import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing;
 | 
				
			||||||
@@ -63,8 +63,8 @@ public class VimArgTextObjExtension implements VimExtension {
 | 
				
			|||||||
   */
 | 
					   */
 | 
				
			||||||
  private static class BracketPairs {
 | 
					  private static class BracketPairs {
 | 
				
			||||||
    // NOTE: brackets must match by the position, and ordered by rank (highest to lowest).
 | 
					    // NOTE: brackets must match by the position, and ordered by rank (highest to lowest).
 | 
				
			||||||
    private final @NotNull String openBrackets;
 | 
					    @NotNull private final String openBrackets;
 | 
				
			||||||
    private final @NotNull String closeBrackets;
 | 
					    @NotNull private final String closeBrackets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static class ParseException extends Exception {
 | 
					    static class ParseException extends Exception {
 | 
				
			||||||
      public ParseException(@NotNull String message) {
 | 
					      public ParseException(@NotNull String message) {
 | 
				
			||||||
@@ -86,7 +86,8 @@ public class VimArgTextObjExtension implements VimExtension {
 | 
				
			|||||||
     * @param bracketPairs comma-separated list of colon-separated bracket pairs.
 | 
					     * @param bracketPairs comma-separated list of colon-separated bracket pairs.
 | 
				
			||||||
     * @throws ParseException if a syntax error is detected.
 | 
					     * @throws ParseException if a syntax error is detected.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    static @NotNull BracketPairs fromBracketPairList(final @NotNull String bracketPairs) throws ParseException {
 | 
					    @NotNull
 | 
				
			||||||
 | 
					    static BracketPairs fromBracketPairList(@NotNull final String bracketPairs) throws ParseException {
 | 
				
			||||||
      StringBuilder openBrackets = new StringBuilder();
 | 
					      StringBuilder openBrackets = new StringBuilder();
 | 
				
			||||||
      StringBuilder closeBrackets = new StringBuilder();
 | 
					      StringBuilder closeBrackets = new StringBuilder();
 | 
				
			||||||
      ParseState state = ParseState.OPEN;
 | 
					      ParseState state = ParseState.OPEN;
 | 
				
			||||||
@@ -126,7 +127,7 @@ public class VimArgTextObjExtension implements VimExtension {
 | 
				
			|||||||
      return new BracketPairs(openBrackets.toString(), closeBrackets.toString());
 | 
					      return new BracketPairs(openBrackets.toString(), closeBrackets.toString());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    BracketPairs(final @NotNull String openBrackets, final @NotNull String closeBrackets) {
 | 
					    BracketPairs(@NotNull final String openBrackets, @NotNull final String closeBrackets) {
 | 
				
			||||||
      assert openBrackets.length() == closeBrackets.length();
 | 
					      assert openBrackets.length() == closeBrackets.length();
 | 
				
			||||||
      this.openBrackets = openBrackets;
 | 
					      this.openBrackets = openBrackets;
 | 
				
			||||||
      this.closeBrackets = closeBrackets;
 | 
					      this.closeBrackets = closeBrackets;
 | 
				
			||||||
@@ -156,9 +157,10 @@ public class VimArgTextObjExtension implements VimExtension {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static final BracketPairs DEFAULT_BRACKET_PAIRS = new BracketPairs("(", ")");
 | 
					  public static final BracketPairs DEFAULT_BRACKET_PAIRS = new BracketPairs("(", ")");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static @Nullable String bracketPairsVariable() {
 | 
					  @Nullable
 | 
				
			||||||
 | 
					  private static String bracketPairsVariable() {
 | 
				
			||||||
    final Object value = VimPlugin.getVariableService().getGlobalVariableValue("argtextobj_pairs");
 | 
					    final Object value = VimPlugin.getVariableService().getGlobalVariableValue("argtextobj_pairs");
 | 
				
			||||||
    if (value instanceof VimString vimValue) {
 | 
					    if (value instanceof VimString vimValue) {
 | 
				
			||||||
      return vimValue.getValue();
 | 
					      return vimValue.getValue();
 | 
				
			||||||
@@ -189,12 +191,13 @@ public class VimArgTextObjExtension implements VimExtension {
 | 
				
			|||||||
        this.isInner = isInner;
 | 
					        this.isInner = isInner;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @Nullable
 | 
				
			||||||
      @Override
 | 
					      @Override
 | 
				
			||||||
      public @Nullable TextRange getRange(@NotNull VimEditor editor,
 | 
					      public TextRange getRange(@NotNull VimEditor editor,
 | 
				
			||||||
                                          @NotNull ImmutableVimCaret caret,
 | 
					                                @NotNull ImmutableVimCaret caret,
 | 
				
			||||||
                                          @NotNull ExecutionContext context,
 | 
					                                @NotNull ExecutionContext context,
 | 
				
			||||||
                                          int count,
 | 
					                                int count,
 | 
				
			||||||
                                          int rawCount) {
 | 
					                                int rawCount) {
 | 
				
			||||||
        BracketPairs bracketPairs = DEFAULT_BRACKET_PAIRS;
 | 
					        BracketPairs bracketPairs = DEFAULT_BRACKET_PAIRS;
 | 
				
			||||||
        final String bracketPairsVar = bracketPairsVariable();
 | 
					        final String bracketPairsVar = bracketPairsVariable();
 | 
				
			||||||
        if (bracketPairsVar != null) {
 | 
					        if (bracketPairsVar != null) {
 | 
				
			||||||
@@ -232,25 +235,28 @@ public class VimArgTextObjExtension implements VimExtension {
 | 
				
			|||||||
        return new TextRange(finder.getLeftBound(), finder.getRightBound());
 | 
					        return new TextRange(finder.getLeftBound(), finder.getRightBound());
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @NotNull
 | 
				
			||||||
      @Override
 | 
					      @Override
 | 
				
			||||||
      public @NotNull TextObjectVisualType getVisualType() {
 | 
					      public TextObjectVisualType getVisualType() {
 | 
				
			||||||
        return TextObjectVisualType.CHARACTER_WISE;
 | 
					        return TextObjectVisualType.CHARACTER_WISE;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
 | 
					    public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
 | 
				
			||||||
      @NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
 | 
					
 | 
				
			||||||
 | 
					      IjVimEditor vimEditor = (IjVimEditor) editor;
 | 
				
			||||||
 | 
					      @NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(vimEditor);
 | 
				
			||||||
 | 
					      int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner);
 | 
					      final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner);
 | 
				
			||||||
      //noinspection DuplicatedCode
 | 
					      //noinspection DuplicatedCode
 | 
				
			||||||
      if (!(editor.getMode() instanceof Mode.OP_PENDING)) {
 | 
					      if (!vimStateMachine.isOperatorPending(editor.getMode())) {
 | 
				
			||||||
        int count0 = operatorArguments.getCount0();
 | 
					 | 
				
			||||||
        editor.nativeCarets().forEach((VimCaret caret) -> {
 | 
					        editor.nativeCarets().forEach((VimCaret caret) -> {
 | 
				
			||||||
          final TextRange range = textObjectHandler.getRange(editor, caret, context, Math.max(1, count0), count0);
 | 
					          final TextRange range = textObjectHandler.getRange(editor, caret, context, count, 0);
 | 
				
			||||||
          if (range != null) {
 | 
					          if (range != null) {
 | 
				
			||||||
            try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
 | 
					            try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
 | 
				
			||||||
              if (editor.getMode() instanceof Mode.VISUAL) {
 | 
					              if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
 | 
				
			||||||
                com.maddyhome.idea.vim.group.visual.EngineVisualGroupKt.vimSetSelection(caret, range.getStartOffset(), range.getEndOffset() - 1, true);
 | 
					                com.maddyhome.idea.vim.group.visual.EngineVisualGroupKt.vimSetSelection(caret, range.getStartOffset(), range.getEndOffset() - 1, true);
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                InlayHelperKt.moveToInlayAwareOffset(((IjVimCaret)caret).getCaret(), range.getStartOffset());
 | 
					                InlayHelperKt.moveToInlayAwareOffset(((IjVimCaret)caret).getCaret(), range.getStartOffset());
 | 
				
			||||||
@@ -259,7 +265,8 @@ public class VimArgTextObjExtension implements VimExtension {
 | 
				
			|||||||
          }
 | 
					          }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        keyHandlerState.getCommandBuilder().addAction(textObjectHandler);
 | 
					        vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
 | 
				
			||||||
 | 
					                                                                                         textObjectHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags.class))));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -269,9 +276,9 @@ public class VimArgTextObjExtension implements VimExtension {
 | 
				
			|||||||
   * position
 | 
					   * position
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  private static class ArgBoundsFinder {
 | 
					  private static class ArgBoundsFinder {
 | 
				
			||||||
    private final @NotNull CharSequence text;
 | 
					    @NotNull private final CharSequence text;
 | 
				
			||||||
    private final @NotNull Document document;
 | 
					    @NotNull private final Document document;
 | 
				
			||||||
    private final @NotNull BracketPairs brackets;
 | 
					    @NotNull private final BracketPairs brackets;
 | 
				
			||||||
    private int leftBound = Integer.MAX_VALUE;
 | 
					    private int leftBound = Integer.MAX_VALUE;
 | 
				
			||||||
    private int rightBound = Integer.MIN_VALUE;
 | 
					    private int rightBound = Integer.MIN_VALUE;
 | 
				
			||||||
    private int leftBracket;
 | 
					    private int leftBracket;
 | 
				
			||||||
@@ -298,7 +305,7 @@ public class VimArgTextObjExtension implements VimExtension {
 | 
				
			|||||||
     * @param position starting position.
 | 
					     * @param position starting position.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    boolean findBoundsAt(int position) throws IllegalStateException {
 | 
					    boolean findBoundsAt(int position) throws IllegalStateException {
 | 
				
			||||||
      if (text.isEmpty()) {
 | 
					      if (text.length() == 0) {
 | 
				
			||||||
        error = "empty document";
 | 
					        error = "empty document";
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,13 +18,15 @@ import com.intellij.psi.PsiElement
 | 
				
			|||||||
import com.intellij.psi.PsiFile
 | 
					import com.intellij.psi.PsiFile
 | 
				
			||||||
import com.intellij.psi.PsiWhiteSpace
 | 
					import com.intellij.psi.PsiWhiteSpace
 | 
				
			||||||
import com.intellij.psi.util.PsiTreeUtil
 | 
					import com.intellij.psi.util.PsiTreeUtil
 | 
				
			||||||
import com.maddyhome.idea.vim.KeyHandler
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
					import com.maddyhome.idea.vim.api.ExecutionContext
 | 
				
			||||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
 | 
					import com.maddyhome.idea.vim.api.ImmutableVimCaret
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
					import com.maddyhome.idea.vim.api.VimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.api.getLineEndOffset
 | 
					import com.maddyhome.idea.vim.api.getLineEndOffset
 | 
				
			||||||
import com.maddyhome.idea.vim.api.globalOptions
 | 
					import com.maddyhome.idea.vim.api.globalOptions
 | 
				
			||||||
import com.maddyhome.idea.vim.api.injector
 | 
					import com.maddyhome.idea.vim.api.injector
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.command.Argument
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.command.Command
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.command.CommandFlags
 | 
				
			||||||
import com.maddyhome.idea.vim.command.MappingMode
 | 
					import com.maddyhome.idea.vim.command.MappingMode
 | 
				
			||||||
import com.maddyhome.idea.vim.command.OperatorArguments
 | 
					import com.maddyhome.idea.vim.command.OperatorArguments
 | 
				
			||||||
import com.maddyhome.idea.vim.command.TextObjectVisualType
 | 
					import com.maddyhome.idea.vim.command.TextObjectVisualType
 | 
				
			||||||
@@ -43,12 +45,14 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
 | 
				
			|||||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
 | 
					import com.maddyhome.idea.vim.extension.exportOperatorFunction
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
 | 
					import com.maddyhome.idea.vim.handler.TextObjectActionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.PsiHelper
 | 
					import com.maddyhome.idea.vim.helper.PsiHelper
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.vimStateMachine
 | 
				
			||||||
import com.maddyhome.idea.vim.key.OperatorFunction
 | 
					import com.maddyhome.idea.vim.key.OperatorFunction
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.ij
 | 
					import com.maddyhome.idea.vim.newapi.ij
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
					import com.maddyhome.idea.vim.state.mode.Mode
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
					import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
				
			||||||
 | 
					import java.util.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal class CommentaryExtension : VimExtension {
 | 
					internal class CommentaryExtension : VimExtension {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -60,7 +64,7 @@ internal class CommentaryExtension : VimExtension {
 | 
				
			|||||||
      selectionType: SelectionType,
 | 
					      selectionType: SelectionType,
 | 
				
			||||||
      resetCaret: Boolean = true,
 | 
					      resetCaret: Boolean = true,
 | 
				
			||||||
    ): Boolean {
 | 
					    ): Boolean {
 | 
				
			||||||
      val mode = editor.mode
 | 
					      val mode = editor.vimStateMachine.mode
 | 
				
			||||||
      if (mode !is Mode.VISUAL) {
 | 
					      if (mode !is Mode.VISUAL) {
 | 
				
			||||||
        editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset)
 | 
					        editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -75,12 +79,11 @@ internal class CommentaryExtension : VimExtension {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        val project = editor.ij.project!!
 | 
					        val project = editor.ij.project!!
 | 
				
			||||||
        val callback = { afterCommenting(mode, editor, resetCaret, range) }
 | 
					        val callback = { afterCommenting(mode, editor, resetCaret, range) }
 | 
				
			||||||
        actions.any { executeActionWithCallbackOnSuccess(editor, it, project, context, callback) }
 | 
					        actions.any { executeActionWithCallbackOnSuccess(it, project, context, callback) }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun executeActionWithCallbackOnSuccess(
 | 
					    private fun executeActionWithCallbackOnSuccess(
 | 
				
			||||||
      editor: VimEditor,
 | 
					 | 
				
			||||||
      action: String,
 | 
					      action: String,
 | 
				
			||||||
      project: Project,
 | 
					      project: Project,
 | 
				
			||||||
      context: ExecutionContext,
 | 
					      context: ExecutionContext,
 | 
				
			||||||
@@ -89,7 +92,7 @@ internal class CommentaryExtension : VimExtension {
 | 
				
			|||||||
      val res = Ref.create<Boolean>(false)
 | 
					      val res = Ref.create<Boolean>(false)
 | 
				
			||||||
      AsyncActionExecutionService.getInstance(project).withExecutionAfterAction(
 | 
					      AsyncActionExecutionService.getInstance(project).withExecutionAfterAction(
 | 
				
			||||||
        action,
 | 
					        action,
 | 
				
			||||||
        { res.set(injector.actionExecutor.executeAction(editor, name = action, context = context)) },
 | 
					        { res.set(injector.actionExecutor.executeAction(action, context)) },
 | 
				
			||||||
        { if (res.get()) callback() })
 | 
					        { if (res.get()) callback() })
 | 
				
			||||||
      return res.get()
 | 
					      return res.get()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -180,8 +183,10 @@ internal class CommentaryExtension : VimExtension {
 | 
				
			|||||||
    override val isRepeatable = true
 | 
					    override val isRepeatable = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
 | 
					    override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
 | 
				
			||||||
      val keyState = KeyHandler.getInstance().keyHandlerState
 | 
					      val commandState = editor.vimStateMachine
 | 
				
			||||||
      keyState.commandBuilder.addAction(CommentaryTextObjectMotionHandler)
 | 
					
 | 
				
			||||||
 | 
					      val command = Command(operatorArguments.count1, CommentaryTextObjectMotionHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags::class.java))
 | 
				
			||||||
 | 
					      commandState.commandBuilder.completeCommandPart(Argument(command))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -222,10 +222,10 @@ internal class VimExchangeExtension : VimExtension {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      val zRegText = getRegister(editor.vim, 'z')
 | 
					      val zRegText = getRegister('z')
 | 
				
			||||||
      val unnRegText = getRegister(editor.vim, '"')
 | 
					      val unnRegText = getRegister('"')
 | 
				
			||||||
      val startRegText = getRegister(editor.vim, '*')
 | 
					      val startRegText = getRegister('*')
 | 
				
			||||||
      val plusRegText = getRegister(editor.vim, '+')
 | 
					      val plusRegText = getRegister('+')
 | 
				
			||||||
      runWriteAction {
 | 
					      runWriteAction {
 | 
				
			||||||
        // TODO handle:
 | 
					        // TODO handle:
 | 
				
			||||||
        // 	" Compare using =~ because "'==' != 0" returns 0
 | 
					        // 	" Compare using =~ because "'==' != 0" returns 0
 | 
				
			||||||
@@ -299,7 +299,7 @@ internal class VimExchangeExtension : VimExtension {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private fun getExchange(editor: Editor, isVisual: Boolean, selectionType: SelectionType): Exchange {
 | 
					    private fun getExchange(editor: Editor, isVisual: Boolean, selectionType: SelectionType): Exchange {
 | 
				
			||||||
      // TODO: improve KeyStroke list to sting conversion
 | 
					      // TODO: improve KeyStroke list to sting conversion
 | 
				
			||||||
      fun getRegisterText(reg: Char): String = getRegister(editor.vim, reg)?.map { it.keyChar }?.joinToString("") ?: ""
 | 
					      fun getRegisterText(reg: Char): String = getRegister(reg)?.map { it.keyChar }?.joinToString("") ?: ""
 | 
				
			||||||
      fun getMarks(isVisual: Boolean): Pair<Mark, Mark> {
 | 
					      fun getMarks(isVisual: Boolean): Pair<Mark, Mark> {
 | 
				
			||||||
        val (startMark, endMark) =
 | 
					        val (startMark, endMark) =
 | 
				
			||||||
          if (isVisual) {
 | 
					          if (isVisual) {
 | 
				
			||||||
@@ -313,9 +313,9 @@ internal class VimExchangeExtension : VimExtension {
 | 
				
			|||||||
        return Pair(marks.getMark(vimEditor.primaryCaret(), startMark)!!, marks.getMark(vimEditor.primaryCaret(), endMark)!!)
 | 
					        return Pair(marks.getMark(vimEditor.primaryCaret(), startMark)!!, marks.getMark(vimEditor.primaryCaret(), endMark)!!)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      val unnRegText = getRegister(editor.vim, '"')
 | 
					      val unnRegText = getRegister('"')
 | 
				
			||||||
      val starRegText = getRegister(editor.vim, '*')
 | 
					      val starRegText = getRegister('*')
 | 
				
			||||||
      val plusRegText = getRegister(editor.vim, '+')
 | 
					      val plusRegText = getRegister('+')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      val (selectionStart, selectionEnd) = getMarks(isVisual)
 | 
					      val (selectionStart, selectionEnd) = getMarks(isVisual)
 | 
				
			||||||
      if (isVisual) {
 | 
					      if (isVisual) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,15 +23,13 @@ import com.intellij.util.Alarm
 | 
				
			|||||||
import com.intellij.util.Alarm.ThreadToUse
 | 
					import com.intellij.util.Alarm.ThreadToUse
 | 
				
			||||||
import com.maddyhome.idea.vim.VimPlugin
 | 
					import com.maddyhome.idea.vim.VimPlugin
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
					import com.maddyhome.idea.vim.api.VimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.api.injector
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.common.ModeChangeListener
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.common.TextRange
 | 
					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.extension.VimExtension
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.MessageHelper
 | 
					import com.maddyhome.idea.vim.helper.MessageHelper
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.VimNlsSafe
 | 
					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.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.VimDataType
 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
 | 
					import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
 | 
				
			||||||
import org.jetbrains.annotations.NonNls
 | 
					import org.jetbrains.annotations.NonNls
 | 
				
			||||||
@@ -45,10 +43,6 @@ private val HIGHLIGHT_DURATION_VARIABLE_NAME = "highlightedyank_highlight_durati
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@NonNls
 | 
					@NonNls
 | 
				
			||||||
private val HIGHLIGHT_COLOR_VARIABLE_NAME = "highlightedyank_highlight_color"
 | 
					private val HIGHLIGHT_COLOR_VARIABLE_NAME = "highlightedyank_highlight_color"
 | 
				
			||||||
 | 
					 | 
				
			||||||
@NonNls
 | 
					 | 
				
			||||||
private val HIGHLIGHT_FOREGROUND_COLOR_VARIABLE_NAME = "highlightedyank_highlight_foreground_color"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private var defaultHighlightTextColor: Color? = null
 | 
					private var defaultHighlightTextColor: Color? = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun getDefaultHighlightTextColor(): Color {
 | 
					private fun getDefaultHighlightTextColor(): Color {
 | 
				
			||||||
@@ -79,12 +73,9 @@ internal class HighlightColorResetter : LafManagerListener {
 | 
				
			|||||||
 * if you want to change background color of highlight you can provide the rgba of the color you want e.g.
 | 
					 * if you want to change background color of highlight you can provide the rgba of the color you want e.g.
 | 
				
			||||||
 * let g:highlightedyank_highlight_color = "rgba(160, 160, 160, 155)"
 | 
					 * let g:highlightedyank_highlight_color = "rgba(160, 160, 160, 155)"
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * if you want to change text color of highlight you can provide the rgba of the color you want e.g.
 | 
					 | 
				
			||||||
 * let g:highlightedyank_highlight_foreground_color = "rgba(0, 0, 0, 255)"
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * When a new text is yanked or user starts editing, the old highlighting would be deleted.
 | 
					 * When a new text is yanked or user starts editing, the old highlighting would be deleted.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeListener {
 | 
					internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertListener {
 | 
				
			||||||
  private val highlightHandler = HighlightHandler()
 | 
					  private val highlightHandler = HighlightHandler()
 | 
				
			||||||
  private var initialised = false
 | 
					  private var initialised = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -92,8 +83,8 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  override fun init() {
 | 
					  override fun init() {
 | 
				
			||||||
    // Note that these listeners will still be registered when IdeaVim is disabled. However, they'll never get called
 | 
					    // Note that these listeners will still be registered when IdeaVim is disabled. However, they'll never get called
 | 
				
			||||||
    injector.listenersNotifier.modeChangeListeners.add(this)
 | 
					    VimPlugin.getYank().addListener(this)
 | 
				
			||||||
    injector.listenersNotifier.yankListeners.add(this)
 | 
					    VimPlugin.getChange().addInsertListener(this)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Register our own disposable to remove highlights when IdeaVim is disabled. Somehow, we need to re-register when
 | 
					    // 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
 | 
					    // IdeaVim is re-enabled. We don't get a call back for that, but because the listeners are active until the
 | 
				
			||||||
@@ -114,8 +105,8 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  override fun dispose() {
 | 
					  override fun dispose() {
 | 
				
			||||||
    // Called when the extension is disabled with `:set no{extension}` or if the plugin owning the extension unloads
 | 
					    // Called when the extension is disabled with `:set no{extension}` or if the plugin owning the extension unloads
 | 
				
			||||||
    injector.listenersNotifier.modeChangeListeners.remove(this)
 | 
					    VimPlugin.getYank().removeListener(this)
 | 
				
			||||||
    injector.listenersNotifier.yankListeners.remove(this)
 | 
					    VimPlugin.getChange().removeInsertListener(this)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    highlightHandler.clearYankHighlighters()
 | 
					    highlightHandler.clearYankHighlighters()
 | 
				
			||||||
    initialised = false
 | 
					    initialised = false
 | 
				
			||||||
@@ -126,8 +117,7 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis
 | 
				
			|||||||
    highlightHandler.highlightYankRange(editor.ij, range)
 | 
					    highlightHandler.highlightYankRange(editor.ij, range)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun modeChanged(editor: VimEditor, oldMode: Mode) {
 | 
					  override fun insertModeStarted(editor: Editor) {
 | 
				
			||||||
    if (editor.mode !is Mode.INSERT) return
 | 
					 | 
				
			||||||
    ensureInitialised()
 | 
					    ensureInitialised()
 | 
				
			||||||
    highlightHandler.clearYankHighlighters()
 | 
					    highlightHandler.clearYankHighlighters()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -188,15 +178,13 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis
 | 
				
			|||||||
      highlighters.clear()
 | 
					      highlighters.clear()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun getHighlightTextAttributes(editor: Editor): TextAttributes {
 | 
					    private fun getHighlightTextAttributes(editor: Editor) = TextAttributes(
 | 
				
			||||||
      return TextAttributes(
 | 
					      null,
 | 
				
			||||||
        extractUserHighlightForegroundColor(),
 | 
					      extractUsersHighlightColor(),
 | 
				
			||||||
        extractUsersHighlightColor(),
 | 
					      editor.colorsScheme.getColor(EditorColors.CARET_COLOR),
 | 
				
			||||||
        editor.colorsScheme.getColor(EditorColors.CARET_COLOR),
 | 
					      EffectType.SEARCH_MATCH,
 | 
				
			||||||
        EffectType.SEARCH_MATCH,
 | 
					      Font.PLAIN,
 | 
				
			||||||
        Font.PLAIN,
 | 
					    )
 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun extractUsersHighlightDuration(): Int {
 | 
					    private fun extractUsersHighlightDuration(): Int {
 | 
				
			||||||
      return extractVariable(HIGHLIGHT_DURATION_VARIABLE_NAME, DEFAULT_HIGHLIGHT_DURATION) {
 | 
					      return extractVariable(HIGHLIGHT_DURATION_VARIABLE_NAME, DEFAULT_HIGHLIGHT_DURATION) {
 | 
				
			||||||
@@ -209,52 +197,15 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun extractUsersHighlightColor(): Color {
 | 
					    private fun extractUsersHighlightColor(): Color {
 | 
				
			||||||
      val value = VimPlugin.getVariableService().getGlobalVariableValue(HIGHLIGHT_COLOR_VARIABLE_NAME)
 | 
					      return extractVariable(HIGHLIGHT_COLOR_VARIABLE_NAME, getDefaultHighlightTextColor()) { value ->
 | 
				
			||||||
      if (value != null) {
 | 
					        val rgba = value.asString()
 | 
				
			||||||
        return try {
 | 
					          .substring(4)
 | 
				
			||||||
          parseRgbaColor(value.asString())
 | 
					          .filter { it != '(' && it != ')' && !it.isWhitespace() }
 | 
				
			||||||
        } catch (e: Exception) {
 | 
					          .split(',')
 | 
				
			||||||
          @VimNlsSafe val message = MessageHelper.message(
 | 
					          .map { it.toInt() }
 | 
				
			||||||
            "highlightedyank.invalid.value.of.0.1",
 | 
					
 | 
				
			||||||
            "g:$HIGHLIGHT_COLOR_VARIABLE_NAME",
 | 
					        Color(rgba[0], rgba[1], rgba[2], rgba[3])
 | 
				
			||||||
            e.message ?: "",
 | 
					 | 
				
			||||||
          )
 | 
					 | 
				
			||||||
          VimPlugin.showMessage(message)
 | 
					 | 
				
			||||||
          getDefaultHighlightTextColor()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return getDefaultHighlightTextColor()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private fun extractUserHighlightForegroundColor(): Color? {
 | 
					 | 
				
			||||||
      val value = VimPlugin.getVariableService().getGlobalVariableValue(HIGHLIGHT_FOREGROUND_COLOR_VARIABLE_NAME)
 | 
					 | 
				
			||||||
        ?: return null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return try {
 | 
					 | 
				
			||||||
        parseRgbaColor(value.asString())
 | 
					 | 
				
			||||||
      } catch (e: Exception) {
 | 
					 | 
				
			||||||
        @VimNlsSafe val message = MessageHelper.message(
 | 
					 | 
				
			||||||
          "highlightedyank.invalid.value.of.0.1",
 | 
					 | 
				
			||||||
          "g:$HIGHLIGHT_FOREGROUND_COLOR_VARIABLE_NAME",
 | 
					 | 
				
			||||||
          e.message ?: "",
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        VimPlugin.showMessage(message)
 | 
					 | 
				
			||||||
        null
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private fun parseRgbaColor(colorString: String): Color {
 | 
					 | 
				
			||||||
      val rgba = colorString
 | 
					 | 
				
			||||||
        .substring(4)
 | 
					 | 
				
			||||||
        .filter { it != '(' && it != ')' && !it.isWhitespace() }
 | 
					 | 
				
			||||||
        .split(',')
 | 
					 | 
				
			||||||
        .map { it.toInt() }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (rgba.size != 4 || rgba.any { it < 0 || it > 255 }) {
 | 
					 | 
				
			||||||
        throw IllegalArgumentException("Invalid RGBA values. Each component must be between 0 and 255")
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return Color(rgba[0], rgba[1], rgba[2], rgba[3])
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun <T> extractVariable(variable: String, default: T, extractFun: (value: VimDataType) -> T): T {
 | 
					    private fun <T> extractVariable(variable: String, default: T, extractFun: (value: VimDataType) -> T): T {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,6 @@ import com.intellij.openapi.vfs.VirtualFile
 | 
				
			|||||||
import com.intellij.psi.PsiComment
 | 
					import com.intellij.psi.PsiComment
 | 
				
			||||||
import com.intellij.psi.PsiElement
 | 
					import com.intellij.psi.PsiElement
 | 
				
			||||||
import com.intellij.psi.util.PsiTreeUtil
 | 
					import com.intellij.psi.util.PsiTreeUtil
 | 
				
			||||||
import com.maddyhome.idea.vim.KeyHandler
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.VimPlugin
 | 
					import com.maddyhome.idea.vim.VimPlugin
 | 
				
			||||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
					import com.maddyhome.idea.vim.api.ExecutionContext
 | 
				
			||||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
 | 
					import com.maddyhome.idea.vim.api.ImmutableVimCaret
 | 
				
			||||||
@@ -41,10 +40,10 @@ import com.maddyhome.idea.vim.handler.toMotionOrError
 | 
				
			|||||||
import com.maddyhome.idea.vim.helper.EditorHelper
 | 
					import com.maddyhome.idea.vim.helper.EditorHelper
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.PsiHelper
 | 
					import com.maddyhome.idea.vim.helper.PsiHelper
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.enumSetOf
 | 
					import com.maddyhome.idea.vim.helper.enumSetOf
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.vimStateMachine
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.ij
 | 
					import com.maddyhome.idea.vim.newapi.ij
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
					 | 
				
			||||||
import java.util.*
 | 
					import java.util.*
 | 
				
			||||||
import java.util.regex.Pattern
 | 
					import java.util.regex.Pattern
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -92,31 +91,35 @@ internal class Matchit : VimExtension {
 | 
				
			|||||||
  private class MatchitHandler(private val reverse: Boolean) : ExtensionHandler {
 | 
					  private class MatchitHandler(private val reverse: Boolean) : ExtensionHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
 | 
					    override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
 | 
				
			||||||
      val keyHandler = KeyHandler.getInstance()
 | 
					      val commandState = editor.vimStateMachine
 | 
				
			||||||
      val keyState = keyHandler.keyHandlerState
 | 
					      val count = commandState.commandBuilder.count
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Reset the command count so it doesn't transfer onto subsequent commands.
 | 
					      // Reset the command count so it doesn't transfer onto subsequent commands.
 | 
				
			||||||
      keyState.commandBuilder.resetCount()
 | 
					      editor.vimStateMachine.commandBuilder.resetCount()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Normally we want to jump to the start of the matching pair. But when moving forward in operator
 | 
					      // Normally we want to jump to the start of the matching pair. But when moving forward in operator
 | 
				
			||||||
      // pending mode, we want to include the entire match. isInOpPending makes that distinction.
 | 
					      // pending mode, we want to include the entire match. isInOpPending makes that distinction.
 | 
				
			||||||
      if (editor.mode is Mode.OP_PENDING) {
 | 
					      val isInOpPending = commandState.isOperatorPending(editor.mode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (isInOpPending) {
 | 
				
			||||||
        val matchitAction = MatchitAction()
 | 
					        val matchitAction = MatchitAction()
 | 
				
			||||||
        matchitAction.reverse = reverse
 | 
					        matchitAction.reverse = reverse
 | 
				
			||||||
        matchitAction.isInOpPending = true
 | 
					        matchitAction.isInOpPending = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        keyState.commandBuilder.addAction(matchitAction)
 | 
					        commandState.commandBuilder.completeCommandPart(
 | 
				
			||||||
 | 
					          Argument(
 | 
				
			||||||
 | 
					            Command(
 | 
				
			||||||
 | 
					              count,
 | 
				
			||||||
 | 
					              matchitAction,
 | 
				
			||||||
 | 
					              Command.Type.MOTION,
 | 
				
			||||||
 | 
					              EnumSet.noneOf(CommandFlags::class.java),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        editor.sortedCarets().forEach { caret ->
 | 
					        editor.sortedCarets().forEach { caret ->
 | 
				
			||||||
          injector.jumpService.saveJumpLocation(editor)
 | 
					          injector.jumpService.saveJumpLocation(editor)
 | 
				
			||||||
          caret.moveToOffset(
 | 
					          caret.moveToOffset(getMatchitOffset(editor.ij, caret.ij, count, isInOpPending, reverse))
 | 
				
			||||||
            getMatchitOffset(
 | 
					 | 
				
			||||||
              editor.ij,
 | 
					 | 
				
			||||||
              caret.ij,
 | 
					 | 
				
			||||||
              operatorArguments.count0,
 | 
					 | 
				
			||||||
              isInOpPending = false,
 | 
					 | 
				
			||||||
              reverse
 | 
					 | 
				
			||||||
            ))
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -350,7 +353,7 @@ private object FileTypePatterns {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
private val DEFAULT_PAIRS = setOf('(', ')', '[', ']', '{', '}')
 | 
					private val DEFAULT_PAIRS = setOf('(', ')', '[', ']', '{', '}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun getMatchitOffset(editor: Editor, caret: Caret, count0: Int, isInOpPending: Boolean, reverse: Boolean): Int {
 | 
					private fun getMatchitOffset(editor: Editor, caret: Caret, count: Int, isInOpPending: Boolean, reverse: Boolean): Int {
 | 
				
			||||||
  val virtualFile = EditorHelper.getVirtualFile(editor)
 | 
					  val virtualFile = EditorHelper.getVirtualFile(editor)
 | 
				
			||||||
  var caretOffset = caret.offset
 | 
					  var caretOffset = caret.offset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -363,9 +366,9 @@ private fun getMatchitOffset(editor: Editor, caret: Caret, count0: Int, isInOpPe
 | 
				
			|||||||
  val currentChar = editor.document.charsSequence[caretOffset]
 | 
					  val currentChar = editor.document.charsSequence[caretOffset]
 | 
				
			||||||
  var motionOffset: Int? = null
 | 
					  var motionOffset: Int? = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (count0 > 0) {
 | 
					  if (count > 0) {
 | 
				
			||||||
    // Matchit doesn't affect the percent motion, so we fall back to the default behavior.
 | 
					    // Matchit doesn't affect the percent motion, so we fall back to the default behavior.
 | 
				
			||||||
    motionOffset = VimPlugin.getMotion().moveCaretToLinePercent(editor.vim, caret.vim, count0)
 | 
					    motionOffset = VimPlugin.getMotion().moveCaretToLinePercent(editor.vim, caret.vim, count)
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    // Check the simplest case first.
 | 
					    // Check the simplest case first.
 | 
				
			||||||
    if (DEFAULT_PAIRS.contains(currentChar)) {
 | 
					    if (DEFAULT_PAIRS.contains(currentChar)) {
 | 
				
			||||||
@@ -396,7 +399,8 @@ private fun getMatchitOffset(editor: Editor, caret: Caret, count0: Int, isInOpPe
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
private fun getMotionOffset(motion: Motion): Int? {
 | 
					private fun getMotionOffset(motion: Motion): Int? {
 | 
				
			||||||
  return when (motion) {
 | 
					  return when (motion) {
 | 
				
			||||||
    is Motion.AdjustedOffset, is Motion.AbsoluteOffset -> motion.offset
 | 
					    is Motion.AbsoluteOffset -> motion.offset
 | 
				
			||||||
 | 
					    is Motion.AdjustedOffset -> motion.offset
 | 
				
			||||||
    is Motion.Error, is Motion.NoMotion -> null
 | 
					    is Motion.Error, is Motion.NoMotion -> null
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,11 +30,11 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMa
 | 
				
			|||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
 | 
				
			||||||
import com.maddyhome.idea.vim.group.visual.vimSetSelection
 | 
					import com.maddyhome.idea.vim.group.visual.vimSetSelection
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.MessageHelper
 | 
					import com.maddyhome.idea.vim.helper.MessageHelper
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.SearchHelper
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.SearchOptions
 | 
					import com.maddyhome.idea.vim.helper.SearchOptions
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.endOffsetInclusive
 | 
					import com.maddyhome.idea.vim.helper.endOffsetInclusive
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.enumSetOf
 | 
					import com.maddyhome.idea.vim.helper.enumSetOf
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.exitVisualMode
 | 
					import com.maddyhome.idea.vim.helper.exitVisualMode
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.findWordUnderCursor
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.helper.inVisualMode
 | 
					import com.maddyhome.idea.vim.helper.inVisualMode
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
 | 
					import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.userData
 | 
					import com.maddyhome.idea.vim.helper.userData
 | 
				
			||||||
@@ -235,7 +235,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
 | 
				
			|||||||
      val text = if (editor.inVisualMode) {
 | 
					      val text = if (editor.inVisualMode) {
 | 
				
			||||||
        primaryCaret.selectedText ?: return
 | 
					        primaryCaret.selectedText ?: return
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        val range = findWordUnderCursor(editor, primaryCaret) ?: return
 | 
					        val range = SearchHelper.findWordUnderCursor(editor, primaryCaret) ?: return
 | 
				
			||||||
        if (range.startOffset > primaryCaret.offset) return
 | 
					        if (range.startOffset > primaryCaret.offset) return
 | 
				
			||||||
        IjVimEditor(editor).getText(range)
 | 
					        IjVimEditor(editor).getText(range)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -300,7 +300,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private fun selectWordUnderCaret(editor: Editor, caret: Caret): TextRange? {
 | 
					  private fun selectWordUnderCaret(editor: Editor, caret: Caret): TextRange? {
 | 
				
			||||||
    val range = findWordUnderCursor(editor, caret) ?: return null
 | 
					    val range = SearchHelper.findWordUnderCursor(editor, caret) ?: return null
 | 
				
			||||||
    if (range.startOffset > caret.offset) return null
 | 
					    if (range.startOffset > caret.offset) return null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    enterVisualMode(editor.vim)
 | 
					    enterVisualMode(editor.vim)
 | 
				
			||||||
@@ -327,6 +327,6 @@ internal class VimMultipleCursorsExtension : VimExtension {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private fun makePattern(text: String, whole: Boolean): String {
 | 
					  private fun makePattern(text: String, whole: Boolean): String {
 | 
				
			||||||
    // Pattern is "very nomagic" (ignore regex chars) and "force case sensitive". This is vim-multiple-cursors behaviour
 | 
					    // Pattern is "very nomagic" (ignore regex chars) and "force case sensitive". This is vim-multiple-cursors behaviour
 | 
				
			||||||
    return "\\V\\C" + if (whole) "\\<$text\\>" else text
 | 
					    return "\\V\\C" + SearchHelper.makeSearchPattern(text, whole)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,10 +42,13 @@ import com.maddyhome.idea.vim.extension.VimExtension
 | 
				
			|||||||
import com.maddyhome.idea.vim.group.KeyGroup
 | 
					import com.maddyhome.idea.vim.group.KeyGroup
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.MessageHelper
 | 
					import com.maddyhome.idea.vim.helper.MessageHelper
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.runAfterGotFocus
 | 
					import com.maddyhome.idea.vim.helper.runAfterGotFocus
 | 
				
			||||||
import com.maddyhome.idea.vim.key.KeyStrokeTrie
 | 
					import com.maddyhome.idea.vim.key.CommandNode
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.key.CommandPartNode
 | 
				
			||||||
import com.maddyhome.idea.vim.key.MappingOwner
 | 
					import com.maddyhome.idea.vim.key.MappingOwner
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.key.Node
 | 
				
			||||||
import com.maddyhome.idea.vim.key.RequiredShortcut
 | 
					import com.maddyhome.idea.vim.key.RequiredShortcut
 | 
				
			||||||
import com.maddyhome.idea.vim.key.add
 | 
					import com.maddyhome.idea.vim.key.RootNode
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.key.addLeafs
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.ij
 | 
					import com.maddyhome.idea.vim.newapi.ij
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
 | 
					import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
 | 
				
			||||||
@@ -195,8 +198,6 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
    internal var waitForSearch = false
 | 
					    internal var waitForSearch = false
 | 
				
			||||||
    internal var speedSearchListenerInstalled = false
 | 
					    internal var speedSearchListenerInstalled = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val keys = mutableListOf<KeyStroke>()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun actionPerformed(e: AnActionEvent) {
 | 
					    override fun actionPerformed(e: AnActionEvent) {
 | 
				
			||||||
      var keyStroke = getKeyStroke(e) ?: return
 | 
					      var keyStroke = getKeyStroke(e) ?: return
 | 
				
			||||||
      val keyChar = keyStroke.keyChar
 | 
					      val keyChar = keyStroke.keyChar
 | 
				
			||||||
@@ -204,14 +205,20 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
        keyStroke = KeyStroke.getKeyStroke(keyChar)
 | 
					        keyStroke = KeyStroke.getKeyStroke(keyChar)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      keys.add(keyStroke)
 | 
					      val nextNode = currentNode[keyStroke]
 | 
				
			||||||
      actionsRoot.getData(keys)?.let { action ->
 | 
					 | 
				
			||||||
        when (action) {
 | 
					 | 
				
			||||||
          is NerdAction.ToIj -> Util.callAction(null, action.name, e.dataContext.vim)
 | 
					 | 
				
			||||||
          is NerdAction.Code -> e.project?.let { action.action(it, e.dataContext, e) }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        keys.clear()
 | 
					      when (nextNode) {
 | 
				
			||||||
 | 
					        null -> currentNode = actionsRoot
 | 
				
			||||||
 | 
					        is CommandNode<NerdAction> -> {
 | 
				
			||||||
 | 
					          currentNode = actionsRoot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          val action = nextNode.actionHolder
 | 
				
			||||||
 | 
					          when (action) {
 | 
				
			||||||
 | 
					            is NerdAction.ToIj -> Util.callAction(null, action.name, e.dataContext.vim)
 | 
				
			||||||
 | 
					            is NerdAction.Code -> e.project?.let { action.action(it, e.dataContext, e) }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        is CommandPartNode<NerdAction> -> currentNode = nextNode
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -471,12 +478,6 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
      NerdAction.ToIj("SynchronizeCurrentFile"),
 | 
					      NerdAction.ToIj("SynchronizeCurrentFile"),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    registerCommand("NERDTreeMapToggleHidden", "I", NerdAction.ToIj("ProjectView.ShowExcludedFiles"))
 | 
					    registerCommand("NERDTreeMapToggleHidden", "I", NerdAction.ToIj("ProjectView.ShowExcludedFiles"))
 | 
				
			||||||
    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("NERDTreeMapRefreshRoot", "R", NerdAction.ToIj("Synchronize"))
 | 
				
			||||||
    registerCommand("NERDTreeMapMenu", "m", NerdAction.ToIj("ShowPopupMenu"))
 | 
					    registerCommand("NERDTreeMapMenu", "m", NerdAction.ToIj("ShowPopupMenu"))
 | 
				
			||||||
    registerCommand("NERDTreeMapQuit", "q", NerdAction.ToIj("HideActiveWindow"))
 | 
					    registerCommand("NERDTreeMapQuit", "q", NerdAction.ToIj("HideActiveWindow"))
 | 
				
			||||||
@@ -533,29 +534,37 @@ private fun addCommand(alias: String, handler: CommandAliasHandler) {
 | 
				
			|||||||
  VimPlugin.getCommand().setAlias(alias, CommandAlias.Call(0, -1, alias, handler))
 | 
					  VimPlugin.getCommand().setAlias(alias, CommandAlias.Call(0, -1, alias, handler))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun registerCommand(variable: String, defaultMapping: String, action: NerdAction) {
 | 
					private fun registerCommand(variable: String, default: String, action: NerdAction) {
 | 
				
			||||||
  val variableValue = VimPlugin.getVariableService().getGlobalVariableValue(variable)
 | 
					  val variableValue = VimPlugin.getVariableService().getGlobalVariableValue(variable)
 | 
				
			||||||
  val mapping = if (variableValue is VimString) {
 | 
					  val mappings = if (variableValue is VimString) {
 | 
				
			||||||
    variableValue.value
 | 
					    variableValue.value
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    defaultMapping
 | 
					    default
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  registerCommand(mapping, action)
 | 
					  actionsRoot.addLeafs(mappings, action)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun registerCommand(mapping: String, action: NerdAction) {
 | 
					private fun registerCommand(default: String, action: NerdAction) {
 | 
				
			||||||
  actionsRoot.add(mapping, action)
 | 
					  actionsRoot.addLeafs(default, action)
 | 
				
			||||||
  injector.parser.parseKeys(mapping).forEach {
 | 
					 | 
				
			||||||
    distinctShortcuts.add(it)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private val actionsRoot: KeyStrokeTrie<NerdAction> = KeyStrokeTrie<NerdAction>("NERDTree")
 | 
					
 | 
				
			||||||
private val distinctShortcuts = mutableSetOf<KeyStroke>()
 | 
					private val actionsRoot: RootNode<NerdAction> = RootNode()
 | 
				
			||||||
 | 
					private var currentNode: CommandPartNode<NerdAction> = actionsRoot
 | 
				
			||||||
 | 
					private fun collectShortcuts(node: Node<NerdAction>): Set<KeyStroke> {
 | 
				
			||||||
 | 
					  return if (node is CommandPartNode<NerdAction>) {
 | 
				
			||||||
 | 
					    val res = node.keys.toMutableSet()
 | 
				
			||||||
 | 
					    res += node.values.map { collectShortcuts(it) }.flatten()
 | 
				
			||||||
 | 
					    res
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    emptySet()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun installDispatcher(project: Project) {
 | 
					private fun installDispatcher(project: Project) {
 | 
				
			||||||
  val dispatcher = NerdTree.NerdDispatcher.getInstance(project)
 | 
					  val dispatcher = NerdTree.NerdDispatcher.getInstance(project)
 | 
				
			||||||
  val shortcuts = distinctShortcuts.map { RequiredShortcut(it, MappingOwner.Plugin.get(NerdTree.pluginName)) }
 | 
					  val shortcuts =
 | 
				
			||||||
 | 
					    collectShortcuts(actionsRoot).map { RequiredShortcut(it, MappingOwner.Plugin.get(NerdTree.pluginName)) }
 | 
				
			||||||
  dispatcher.registerCustomShortcutSet(
 | 
					  dispatcher.registerCustomShortcutSet(
 | 
				
			||||||
    KeyGroup.toShortcutSet(shortcuts),
 | 
					    KeyGroup.toShortcutSet(shortcuts),
 | 
				
			||||||
    (ProjectView.getInstance(project) as ProjectViewImpl).component,
 | 
					    (ProjectView.getInstance(project) as ProjectViewImpl).component,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,7 +62,7 @@ internal class ParagraphMotion : VimExtension {
 | 
				
			|||||||
    toKeys: List<KeyStroke>,
 | 
					    toKeys: List<KeyStroke>,
 | 
				
			||||||
    recursive: Boolean,
 | 
					    recursive: Boolean,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    val filteredModes = modes.filterNotTo(HashSet()) { VimPlugin.getKey().getKeyMapping(it).getLayer(fromKeys) != null }
 | 
					    val filteredModes = modes.filterNotTo(HashSet()) { VimPlugin.getKey().hasmapfrom(it, fromKeys) }
 | 
				
			||||||
    putKeyMappingIfMissing(filteredModes, fromKeys, pluginOwner, toKeys, recursive)
 | 
					    putKeyMappingIfMissing(filteredModes, fromKeys, pluginOwner, toKeys, recursive)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,6 +28,7 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
 | 
				
			|||||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
 | 
					import com.maddyhome.idea.vim.extension.exportOperatorFunction
 | 
				
			||||||
import com.maddyhome.idea.vim.group.visual.VimSelection
 | 
					import com.maddyhome.idea.vim.group.visual.VimSelection
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.exitVisualMode
 | 
					import com.maddyhome.idea.vim.helper.exitVisualMode
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.vimStateMachine
 | 
				
			||||||
import com.maddyhome.idea.vim.key.OperatorFunction
 | 
					import com.maddyhome.idea.vim.key.OperatorFunction
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.ij
 | 
					import com.maddyhome.idea.vim.newapi.ij
 | 
				
			||||||
@@ -170,6 +171,11 @@ private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimC
 | 
				
			|||||||
      vimEditor,
 | 
					      vimEditor,
 | 
				
			||||||
      context.vim,
 | 
					      context.vim,
 | 
				
			||||||
      putData,
 | 
					      putData,
 | 
				
			||||||
 | 
					      operatorArguments = OperatorArguments(
 | 
				
			||||||
 | 
					        editor.vimStateMachine?.isOperatorPending(vimEditor.mode) ?: false,
 | 
				
			||||||
 | 
					        0,
 | 
				
			||||||
 | 
					        editor.vim.mode,
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
      saveToRegister = false
 | 
					      saveToRegister = false
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,7 +34,6 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
 | 
				
			|||||||
import com.maddyhome.idea.vim.extension.VimExtensionHandler
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.StrictMode
 | 
					import com.maddyhome.idea.vim.helper.StrictMode
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.ij
 | 
					import com.maddyhome.idea.vim.newapi.ij
 | 
				
			||||||
import org.jetbrains.annotations.TestOnly
 | 
					 | 
				
			||||||
import java.awt.Font
 | 
					import java.awt.Font
 | 
				
			||||||
import java.awt.event.KeyEvent
 | 
					import java.awt.event.KeyEvent
 | 
				
			||||||
import java.util.*
 | 
					import java.util.*
 | 
				
			||||||
@@ -46,26 +45,15 @@ private const val DEFAULT_HIGHLIGHT_DURATION_SNEAK = 300
 | 
				
			|||||||
// By [Mikhail Levchenko](https://github.com/Mishkun)
 | 
					// By [Mikhail Levchenko](https://github.com/Mishkun)
 | 
				
			||||||
// Original repository with the plugin: https://github.com/Mishkun/ideavim-sneak
 | 
					// Original repository with the plugin: https://github.com/Mishkun/ideavim-sneak
 | 
				
			||||||
internal class IdeaVimSneakExtension : VimExtension {
 | 
					internal class IdeaVimSneakExtension : VimExtension {
 | 
				
			||||||
  @Suppress("CompanionObjectInExtension")
 | 
					 | 
				
			||||||
  companion object {
 | 
					 | 
				
			||||||
    private var highlightHandler: HighlightHandler? = null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @TestOnly
 | 
					 | 
				
			||||||
    internal fun stopTimer() {
 | 
					 | 
				
			||||||
      highlightHandler?.stopExistingTimer()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  override fun getName(): String = "sneak"
 | 
					  override fun getName(): String = "sneak"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun init() {
 | 
					  override fun init() {
 | 
				
			||||||
    val _highlightHandler = HighlightHandler()
 | 
					    val highlightHandler = HighlightHandler()
 | 
				
			||||||
    highlightHandler = _highlightHandler
 | 
					    mapToFunctionAndProvideKeys("s", SneakHandler(highlightHandler, Direction.FORWARD), MappingMode.NXO)
 | 
				
			||||||
    mapToFunctionAndProvideKeys("s", SneakHandler(_highlightHandler, Direction.FORWARD), MappingMode.NXO)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // vim-sneak uses `Z` for visual mode because `S` conflict with vim-sneak plugin VIM-3330
 | 
					    // vim-sneak uses `Z` for visual mode because `S` conflict with vim-sneak plugin VIM-3330
 | 
				
			||||||
    mapToFunctionAndProvideKeys("S", SneakHandler(_highlightHandler, Direction.BACKWARD), MappingMode.NO)
 | 
					    mapToFunctionAndProvideKeys("S", SneakHandler(highlightHandler, Direction.BACKWARD), MappingMode.NO)
 | 
				
			||||||
    mapToFunctionAndProvideKeys("Z", SneakHandler(_highlightHandler, Direction.BACKWARD), MappingMode.X)
 | 
					    mapToFunctionAndProvideKeys("Z", SneakHandler(highlightHandler, Direction.BACKWARD), MappingMode.X)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // workaround to support ; and , commands
 | 
					    // workaround to support ; and , commands
 | 
				
			||||||
    mapToFunctionAndProvideKeys("f", SneakMemoryHandler("f"), MappingMode.NXO)
 | 
					    mapToFunctionAndProvideKeys("f", SneakMemoryHandler("f"), MappingMode.NXO)
 | 
				
			||||||
@@ -73,8 +61,8 @@ internal class IdeaVimSneakExtension : VimExtension {
 | 
				
			|||||||
    mapToFunctionAndProvideKeys("t", SneakMemoryHandler("t"), MappingMode.NXO)
 | 
					    mapToFunctionAndProvideKeys("t", SneakMemoryHandler("t"), MappingMode.NXO)
 | 
				
			||||||
    mapToFunctionAndProvideKeys("T", SneakMemoryHandler("T"), MappingMode.NXO)
 | 
					    mapToFunctionAndProvideKeys("T", SneakMemoryHandler("T"), MappingMode.NXO)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mapToFunctionAndProvideKeys(";", SneakRepeatHandler(_highlightHandler, RepeatDirection.IDENTICAL), MappingMode.NXO)
 | 
					    mapToFunctionAndProvideKeys(";", SneakRepeatHandler(highlightHandler, RepeatDirection.IDENTICAL), MappingMode.NXO)
 | 
				
			||||||
    mapToFunctionAndProvideKeys(",", SneakRepeatHandler(_highlightHandler, RepeatDirection.REVERSE), MappingMode.NXO)
 | 
					    mapToFunctionAndProvideKeys(",", SneakRepeatHandler(highlightHandler, RepeatDirection.REVERSE), MappingMode.NXO)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private class SneakHandler(
 | 
					  private class SneakHandler(
 | 
				
			||||||
@@ -221,7 +209,6 @@ internal class IdeaVimSneakExtension : VimExtension {
 | 
				
			|||||||
  private class HighlightHandler {
 | 
					  private class HighlightHandler {
 | 
				
			||||||
    private var editor: Editor? = null
 | 
					    private var editor: Editor? = null
 | 
				
			||||||
    private val sneakHighlighters: MutableSet<RangeHighlighter> = mutableSetOf()
 | 
					    private val sneakHighlighters: MutableSet<RangeHighlighter> = mutableSetOf()
 | 
				
			||||||
    private var timer: Timer? = null
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun highlightSneakRange(editor: Editor, range: TextRange) {
 | 
					    fun highlightSneakRange(editor: Editor, range: TextRange) {
 | 
				
			||||||
      clearAllSneakHighlighters()
 | 
					      clearAllSneakHighlighters()
 | 
				
			||||||
@@ -267,19 +254,13 @@ internal class IdeaVimSneakExtension : VimExtension {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun setClearHighlightRangeTimer(highlighter: RangeHighlighter) {
 | 
					    private fun setClearHighlightRangeTimer(highlighter: RangeHighlighter) {
 | 
				
			||||||
      stopExistingTimer()
 | 
					      val timer = Timer(DEFAULT_HIGHLIGHT_DURATION_SNEAK) {
 | 
				
			||||||
      timer = Timer(DEFAULT_HIGHLIGHT_DURATION_SNEAK) {
 | 
					 | 
				
			||||||
        if (editor?.isDisposed != true) {
 | 
					        if (editor?.isDisposed != true) {
 | 
				
			||||||
          editor?.markupModel?.removeHighlighter(highlighter)
 | 
					          editor?.markupModel?.removeHighlighter(highlighter)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      timer?.isRepeats = false
 | 
					      timer.isRepeats = false
 | 
				
			||||||
      timer?.start()
 | 
					      timer.start()
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun stopExistingTimer() {
 | 
					 | 
				
			||||||
      timer?.stop()
 | 
					 | 
				
			||||||
      timer?.actionListeners?.forEach { it.actionPerformed(null) }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun getHighlightTextAttributes() = TextAttributes(
 | 
					    private fun getHighlightTextAttributes() = TextAttributes(
 | 
				
			||||||
@@ -326,7 +307,7 @@ private fun VimExtension.mapToFunctionAndProvideKeys(
 | 
				
			|||||||
    VimPlugin.getKey().hasmapto(it, injector.parser.parseKeys(commandFromOriginalPlugin(keys)))
 | 
					    VimPlugin.getKey().hasmapto(it, injector.parser.parseKeys(commandFromOriginalPlugin(keys)))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  val filteredFromModes = mappingModes.filterNotTo(HashSet()) {
 | 
					  val filteredFromModes = mappingModes.filterNotTo(HashSet()) {
 | 
				
			||||||
    injector.keyGroup.getKeyMapping(it).getLayer(fromKeys) != null
 | 
					    injector.keyGroup.hasmapfrom(it, fromKeys)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  val doubleFiltered = mappingModes
 | 
					  val doubleFiltered = mappingModes
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,6 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
 | 
				
			|||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
 | 
					import com.maddyhome.idea.vim.extension.exportOperatorFunction
 | 
				
			||||||
import com.maddyhome.idea.vim.group.findBlockRange
 | 
					import com.maddyhome.idea.vim.group.findBlockRange
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.exitVisualMode
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore
 | 
					import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore
 | 
				
			||||||
import com.maddyhome.idea.vim.key.OperatorFunction
 | 
					import com.maddyhome.idea.vim.key.OperatorFunction
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimCaret
 | 
					import com.maddyhome.idea.vim.newapi.IjVimCaret
 | 
				
			||||||
@@ -47,7 +46,6 @@ import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper
 | 
				
			|||||||
import com.maddyhome.idea.vim.put.PutData
 | 
					import com.maddyhome.idea.vim.put.PutData
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
					import com.maddyhome.idea.vim.state.mode.Mode
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
					import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.returnTo
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.selectionType
 | 
					import com.maddyhome.idea.vim.state.mode.selectionType
 | 
				
			||||||
import org.jetbrains.annotations.NonNls
 | 
					import org.jetbrains.annotations.NonNls
 | 
				
			||||||
import java.awt.event.KeyEvent
 | 
					import java.awt.event.KeyEvent
 | 
				
			||||||
@@ -117,9 +115,6 @@ internal class VimSurroundExtension : VimExtension {
 | 
				
			|||||||
//        it.moveToOffset(lineStartOffset)
 | 
					//        it.moveToOffset(lineStartOffset)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      // Jump back to start
 | 
					      // Jump back to start
 | 
				
			||||||
      if (editor.mode !is Mode.NORMAL) {
 | 
					 | 
				
			||||||
        editor.mode = Mode.NORMAL()
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
 | 
					      executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -141,7 +136,7 @@ internal class VimSurroundExtension : VimExtension {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      runWriteAction {
 | 
					      runWriteAction {
 | 
				
			||||||
        // Leave visual mode
 | 
					        // Leave visual mode
 | 
				
			||||||
        editor.exitVisualMode()
 | 
					        executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -161,17 +156,17 @@ internal class VimSurroundExtension : VimExtension {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    companion object {
 | 
					    companion object {
 | 
				
			||||||
      fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: SurroundPair?) {
 | 
					      fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) {
 | 
				
			||||||
        editor.ij.runWithEveryCaretAndRestore { changeAtCaret(editor, context, charFrom, newSurround) }
 | 
					        editor.ij.runWithEveryCaretAndRestore { changeAtCaret(editor, context, charFrom, newSurround) }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      fun changeAtCaret(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: SurroundPair?) {
 | 
					      fun changeAtCaret(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) {
 | 
				
			||||||
        // Save old register values for carets
 | 
					        // Save old register values for carets
 | 
				
			||||||
        val surroundings = editor.sortedCarets()
 | 
					        val surroundings = editor.sortedCarets()
 | 
				
			||||||
          .map {
 | 
					          .map {
 | 
				
			||||||
            val oldValue: List<KeyStroke>? = getRegisterForCaret(REGISTER, it)
 | 
					            val oldValue: List<KeyStroke>? = getRegisterForCaret(REGISTER, it)
 | 
				
			||||||
            setRegisterForCaret(REGISTER, it, null)
 | 
					            setRegisterForCaret(REGISTER, it, null)
 | 
				
			||||||
            SurroundingInfo(it, null, oldValue, false)
 | 
					            SurroundingInfo(it, null, oldValue)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Delete surrounding's content
 | 
					        // Delete surrounding's content
 | 
				
			||||||
@@ -187,25 +182,21 @@ internal class VimSurroundExtension : VimExtension {
 | 
				
			|||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          val registerValue = getRegisterForCaret(REGISTER, it.caret)
 | 
					          val registerValue = getRegisterForCaret(REGISTER, it.caret)
 | 
				
			||||||
          val innerValue = if (registerValue.isNullOrEmpty()) emptyList() else registerValue
 | 
					          val innerValue = if (registerValue.isNullOrEmpty()) null else registerValue
 | 
				
			||||||
          it.innerText = innerValue
 | 
					          it.innerText = innerValue
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          // Valid surroundings are only those that:
 | 
					        surroundings.forEach {
 | 
				
			||||||
          // - are validly wrapping with surround characters (i.e. parenthesis, brackets, tags, quotes, etc.);
 | 
					          if (it.innerText == null && getRegisterForCaret(REGISTER, it.caret)?.isNotEmpty() == true) {
 | 
				
			||||||
          // - or have non-empty inner text (e.g. when we are surrounding words: `csw"`)
 | 
					            it.innerText = emptyList()
 | 
				
			||||||
          if (currentSurrounding != null || innerValue.isNotEmpty()) {
 | 
					 | 
				
			||||||
            it.isValidSurrounding = true
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        surroundings
 | 
					        surroundings
 | 
				
			||||||
          .filter { it.isValidSurrounding } // we do nothing with carets that are not inside the surrounding
 | 
					          .filter { it.innerText != null } // we do nothing with carets that are not inside the surrounding
 | 
				
			||||||
          .map { surrounding ->
 | 
					          .map { surrounding ->
 | 
				
			||||||
            val innerValue = injector.parser.toPrintableString(surrounding.innerText!!)
 | 
					            val innerValue = injector.parser.toPrintableString(surrounding.innerText!!)
 | 
				
			||||||
            val text = newSurround?.let {
 | 
					            val text = newSurround?.let { it.first + innerValue + it.second } ?: innerValue
 | 
				
			||||||
              val trimmedValue = if (newSurround.shouldTrim) innerValue.trim() else innerValue
 | 
					 | 
				
			||||||
              it.first + trimmedValue + it.second
 | 
					 | 
				
			||||||
            } ?: innerValue
 | 
					 | 
				
			||||||
            val textData = PutData.TextData(text, SelectionType.CHARACTER_WISE, emptyList(), null)
 | 
					            val textData = PutData.TextData(text, SelectionType.CHARACTER_WISE, emptyList(), null)
 | 
				
			||||||
            val putData = PutData(textData, null, 1, insertTextBeforeCaret = true, rawIndent = true, caretAfterInsertedText = false)
 | 
					            val putData = PutData(textData, null, 1, insertTextBeforeCaret = true, rawIndent = true, caretAfterInsertedText = false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -258,7 +249,7 @@ internal class VimSurroundExtension : VimExtension {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private data class SurroundingInfo(val caret: VimCaret, var innerText: List<KeyStroke>?, val oldRegisterContent: List<KeyStroke>?, var isValidSurrounding: Boolean) {
 | 
					  private data class SurroundingInfo(val caret: VimCaret, var innerText: List<KeyStroke>?, val oldRegisterContent: List<KeyStroke>?) {
 | 
				
			||||||
    fun restoreRegister() {
 | 
					    fun restoreRegister() {
 | 
				
			||||||
      setRegisterForCaret(REGISTER, caret, oldRegisterContent)
 | 
					      setRegisterForCaret(REGISTER, caret, oldRegisterContent)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -301,7 +292,7 @@ internal class VimSurroundExtension : VimExtension {
 | 
				
			|||||||
      return true
 | 
					      return true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    private fun applyOnce(editor: Editor, change: VimChangeGroup, pair: SurroundPair, count: Int) {
 | 
					    private fun applyOnce(editor: Editor, change: VimChangeGroup, pair: Pair<String, String>, count: Int) {
 | 
				
			||||||
      // XXX: Will it work with line-wise or block-wise selections?
 | 
					      // XXX: Will it work with line-wise or block-wise selections?
 | 
				
			||||||
      val primaryCaret = editor.caretModel.primaryCaret
 | 
					      val primaryCaret = editor.caretModel.primaryCaret
 | 
				
			||||||
      val range = getSurroundRange(primaryCaret.vim)
 | 
					      val range = getSurroundRange(primaryCaret.vim)
 | 
				
			||||||
@@ -315,10 +306,8 @@ internal class VimSurroundExtension : VimExtension {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private fun getSurroundRange(caret: VimCaret): TextRange? {
 | 
					    private fun getSurroundRange(caret: VimCaret): TextRange? {
 | 
				
			||||||
      val editor = caret.editor
 | 
					      val editor = caret.editor
 | 
				
			||||||
      if (editor.mode is Mode.CMD_LINE) {
 | 
					      val ijEditor = editor.ij
 | 
				
			||||||
        editor.mode = (editor.mode as Mode.CMD_LINE).returnTo()
 | 
					      return when (ijEditor.vim.mode) {
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      return when (editor.mode) {
 | 
					 | 
				
			||||||
        is Mode.NORMAL -> injector.markService.getChangeMarks(caret)
 | 
					        is Mode.NORMAL -> injector.markService.getChangeMarks(caret)
 | 
				
			||||||
        is Mode.VISUAL -> caret.run { TextRange(selectionStart, selectionEnd) }
 | 
					        is Mode.VISUAL -> caret.run { TextRange(selectionStart, selectionEnd) }
 | 
				
			||||||
        else -> null
 | 
					        else -> null
 | 
				
			||||||
@@ -333,44 +322,39 @@ private const val REGISTER = '"'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
private const val OPERATOR_FUNC = "SurroundOperatorFunc"
 | 
					private const val OPERATOR_FUNC = "SurroundOperatorFunc"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private val tagNameAndAttributesCapturePattern = "(\\S+)([^>]*)>".toPattern()
 | 
					    private val tagNameAndAttributesCapturePattern = "(\\S+)([^>]*)>".toPattern()
 | 
				
			||||||
 | 
					 | 
				
			||||||
private data class SurroundPair(val first: String, val second: String, val shouldTrim: Boolean)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
private val SURROUND_PAIRS = mapOf(
 | 
					private val SURROUND_PAIRS = mapOf(
 | 
				
			||||||
  'b' to SurroundPair("(", ")", false),
 | 
					  'b' to ("(" to ")"),
 | 
				
			||||||
  '(' to SurroundPair("( ", " )", false),
 | 
					  '(' to ("( " to " )"),
 | 
				
			||||||
  ')' to SurroundPair("(", ")", true),
 | 
					  ')' to ("(" to ")"),
 | 
				
			||||||
  'B' to SurroundPair("{", "}", false),
 | 
					  'B' to ("{" to "}"),
 | 
				
			||||||
  '{' to SurroundPair("{ ", " }", false),
 | 
					  '{' to ("{ " to " }"),
 | 
				
			||||||
  '}' to SurroundPair("{", "}", true),
 | 
					  '}' to ("{" to "}"),
 | 
				
			||||||
  'r' to SurroundPair("[", "]", false),
 | 
					  'r' to ("[" to "]"),
 | 
				
			||||||
  '[' to SurroundPair("[ ", " ]", false),
 | 
					  '[' to ("[ " to " ]"),
 | 
				
			||||||
  ']' to SurroundPair("[", "]", true),
 | 
					  ']' to ("[" to "]"),
 | 
				
			||||||
  'a' to SurroundPair("<", ">", false),
 | 
					  'a' to ("<" to ">"),
 | 
				
			||||||
  '>' to SurroundPair("<", ">", false),
 | 
					  '>' to ("<" to ">"),
 | 
				
			||||||
  's' to SurroundPair(" ", "", false),
 | 
					  's' to (" " to ""),
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun getSurroundPair(c: Char): SurroundPair? = if (c in SURROUND_PAIRS) {
 | 
					private fun getSurroundPair(c: Char): Pair<String, String>? = if (c in SURROUND_PAIRS) {
 | 
				
			||||||
  SURROUND_PAIRS[c]
 | 
					  SURROUND_PAIRS[c]
 | 
				
			||||||
} else if (!c.isLetter()) {
 | 
					} else if (!c.isLetter()) {
 | 
				
			||||||
  val s = c.toString()
 | 
					  val s = c.toString()
 | 
				
			||||||
  SurroundPair(s, s, false)
 | 
					  s to s
 | 
				
			||||||
} else {
 | 
					} else {
 | 
				
			||||||
  null
 | 
					  null
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun inputTagPair(editor: Editor, context: DataContext): SurroundPair? {
 | 
					private fun inputTagPair(editor: Editor, context: DataContext): Pair<String, String>? {
 | 
				
			||||||
  val tagInput = inputString(editor, context, "<", '>')
 | 
					  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)
 | 
					  val matcher = tagNameAndAttributesCapturePattern.matcher(tagInput)
 | 
				
			||||||
  return if (matcher.find()) {
 | 
					  return if (matcher.find()) {
 | 
				
			||||||
    val tagName = matcher.group(1)
 | 
					    val tagName = matcher.group(1)
 | 
				
			||||||
    val tagAttributes = matcher.group(2)
 | 
					    val tagAttributes = matcher.group(2)
 | 
				
			||||||
    SurroundPair("<$tagName$tagAttributes>", "</$tagName>", false)
 | 
					    "<$tagName$tagAttributes>" to "</$tagName>"
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    null
 | 
					    null
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -380,20 +364,13 @@ private fun inputFunctionName(
 | 
				
			|||||||
  editor: Editor,
 | 
					  editor: Editor,
 | 
				
			||||||
  context: DataContext,
 | 
					  context: DataContext,
 | 
				
			||||||
  withInternalSpaces: Boolean,
 | 
					  withInternalSpaces: Boolean,
 | 
				
			||||||
): SurroundPair? {
 | 
					): Pair<String, String>? {
 | 
				
			||||||
  val functionNameInput = inputString(editor, context, "function: ", null)
 | 
					  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
 | 
					  if (functionNameInput.isEmpty()) return null
 | 
				
			||||||
  return if (withInternalSpaces) {
 | 
					  return if (withInternalSpaces) "$functionNameInput( " to " )" else "$functionNameInput(" to ")"
 | 
				
			||||||
    SurroundPair("$functionNameInput( ", " )", false)
 | 
					 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
    SurroundPair("$functionNameInput(", ")", false)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun getOrInputPair(c: Char, editor: Editor, context: DataContext): SurroundPair? = when (c) {
 | 
					private fun getOrInputPair(c: Char, editor: Editor, context: DataContext): Pair<String, String>? = when (c) {
 | 
				
			||||||
  '<', 't' -> inputTagPair(editor, context)
 | 
					  '<', 't' -> inputTagPair(editor, context)
 | 
				
			||||||
  'f' -> inputFunctionName(editor, context, false)
 | 
					  'f' -> inputFunctionName(editor, context, false)
 | 
				
			||||||
  'F' -> inputFunctionName(editor, context, true)
 | 
					  'F' -> inputFunctionName(editor, context, true)
 | 
				
			||||||
@@ -412,7 +389,7 @@ private fun getChar(editor: Editor): Char {
 | 
				
			|||||||
  return res
 | 
					  return res
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun performSurround(pair: SurroundPair, range: TextRange, caret: VimCaret, count: Int, tagsOnNewLines: Boolean = false) {
 | 
					private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: VimCaret, count: Int, tagsOnNewLines: Boolean = false) {
 | 
				
			||||||
  runWriteAction {
 | 
					  runWriteAction {
 | 
				
			||||||
    val editor = caret.editor
 | 
					    val editor = caret.editor
 | 
				
			||||||
    val change = VimPlugin.getChange()
 | 
					    val change = VimPlugin.getChange()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,6 @@
 | 
				
			|||||||
package com.maddyhome.idea.vim.extension.textobjentire;
 | 
					package com.maddyhome.idea.vim.extension.textobjentire;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.editor.Caret;
 | 
					import com.intellij.openapi.editor.Caret;
 | 
				
			||||||
import com.maddyhome.idea.vim.KeyHandler;
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.api.ExecutionContext;
 | 
					import com.maddyhome.idea.vim.api.ExecutionContext;
 | 
				
			||||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret;
 | 
					import com.maddyhome.idea.vim.api.ImmutableVimCaret;
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimEditor;
 | 
					import com.maddyhome.idea.vim.api.VimEditor;
 | 
				
			||||||
@@ -24,17 +23,19 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
 | 
				
			|||||||
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
 | 
					import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimCaret;
 | 
					import com.maddyhome.idea.vim.newapi.IjVimCaret;
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
				
			||||||
import com.maddyhome.idea.vim.state.KeyHandlerState;
 | 
					import com.maddyhome.idea.vim.state.VimStateMachine;
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode;
 | 
					import com.maddyhome.idea.vim.state.mode.Mode;
 | 
				
			||||||
import org.jetbrains.annotations.NotNull;
 | 
					import org.jetbrains.annotations.NotNull;
 | 
				
			||||||
import org.jetbrains.annotations.Nullable;
 | 
					import org.jetbrains.annotations.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.EnumSet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
 | 
					import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
 | 
				
			||||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing;
 | 
					import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Port of vim-entire:
 | 
					 * Port of vim-entire:
 | 
				
			||||||
 * <a href="https://github.com/kana/vim-textobj-entire">vim-textobj-entire</a>
 | 
					 * https://github.com/kana/vim-textobj-entire
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * <p>
 | 
					 * <p>
 | 
				
			||||||
 * vim-textobj-entire provides two text objects:
 | 
					 * vim-textobj-entire provides two text objects:
 | 
				
			||||||
@@ -49,7 +50,7 @@ import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingI
 | 
				
			|||||||
 * </ul>
 | 
					 * </ul>
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * See also the reference manual for more details at:
 | 
					 * See also the reference manual for more details at:
 | 
				
			||||||
 * <a href="https://github.com/kana/vim-textobj-entire/blob/master/doc/textobj-entire.txt">text-obj-entire.txt</a>
 | 
					 * https://github.com/kana/vim-textobj-entire/blob/master/doc/textobj-entire.txt
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author Alexandre Grison (@agrison)
 | 
					 * @author Alexandre Grison (@agrison)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -92,12 +93,13 @@ public class VimTextObjEntireExtension implements VimExtension {
 | 
				
			|||||||
        this.ignoreLeadingAndTrailing = ignoreLeadingAndTrailing;
 | 
					        this.ignoreLeadingAndTrailing = ignoreLeadingAndTrailing;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @Nullable
 | 
				
			||||||
      @Override
 | 
					      @Override
 | 
				
			||||||
      public @Nullable TextRange getRange(@NotNull VimEditor editor,
 | 
					      public TextRange getRange(@NotNull VimEditor editor,
 | 
				
			||||||
                                          @NotNull ImmutableVimCaret caret,
 | 
					                                @NotNull ImmutableVimCaret caret,
 | 
				
			||||||
                                          @NotNull ExecutionContext context,
 | 
					                                @NotNull ExecutionContext context,
 | 
				
			||||||
                                          int count,
 | 
					                                int count,
 | 
				
			||||||
                                          int rawCount) {
 | 
					                                int rawCount) {
 | 
				
			||||||
        int start = 0, end = ((IjVimEditor)editor).getEditor().getDocument().getTextLength();
 | 
					        int start = 0, end = ((IjVimEditor)editor).getEditor().getDocument().getTextLength();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // for the `ie` text object we don't want leading an trailing spaces
 | 
					        // for the `ie` text object we don't want leading an trailing spaces
 | 
				
			||||||
@@ -122,25 +124,26 @@ public class VimTextObjEntireExtension implements VimExtension {
 | 
				
			|||||||
        return new TextRange(start, end);
 | 
					        return new TextRange(start, end);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @NotNull
 | 
				
			||||||
      @Override
 | 
					      @Override
 | 
				
			||||||
      public @NotNull TextObjectVisualType getVisualType() {
 | 
					      public TextObjectVisualType getVisualType() {
 | 
				
			||||||
        return TextObjectVisualType.CHARACTER_WISE;
 | 
					        return TextObjectVisualType.CHARACTER_WISE;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
 | 
					    public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
 | 
				
			||||||
      @NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
 | 
					      @NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(editor);
 | 
				
			||||||
 | 
					      int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
 | 
					      final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
 | 
				
			||||||
      //noinspection DuplicatedCode
 | 
					      //noinspection DuplicatedCode
 | 
				
			||||||
      if (!(editor.getMode() instanceof Mode.OP_PENDING)) {
 | 
					      if (!vimStateMachine.isOperatorPending(editor.getMode())) {
 | 
				
			||||||
        int count0 = operatorArguments.getCount0();
 | 
					 | 
				
			||||||
        ((IjVimEditor) editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
 | 
					        ((IjVimEditor) editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
 | 
				
			||||||
          final TextRange range = textObjectHandler.getRange(editor, new IjVimCaret(caret), context, Math.max(1, count0), count0);
 | 
					          final TextRange range = textObjectHandler.getRange(editor, new IjVimCaret(caret), context, count, 0);
 | 
				
			||||||
          if (range != null) {
 | 
					          if (range != null) {
 | 
				
			||||||
            try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
 | 
					            try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
 | 
				
			||||||
              if (editor.getMode() instanceof Mode.VISUAL) {
 | 
					              if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
 | 
				
			||||||
                com.maddyhome.idea.vim.group.visual.EngineVisualGroupKt.vimSetSelection(new IjVimCaret(caret), range.getStartOffset(), range.getEndOffset() - 1, true);
 | 
					                com.maddyhome.idea.vim.group.visual.EngineVisualGroupKt.vimSetSelection(new IjVimCaret(caret), range.getStartOffset(), range.getEndOffset() - 1, true);
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset());
 | 
					                InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset());
 | 
				
			||||||
@@ -150,7 +153,9 @@ public class VimTextObjEntireExtension implements VimExtension {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        keyHandlerState.getCommandBuilder().addAction(textObjectHandler);
 | 
					        vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
 | 
				
			||||||
 | 
					                                                                                         textObjectHandler, Command.Type.MOTION,
 | 
				
			||||||
 | 
					                                                                                         EnumSet.noneOf(CommandFlags.class))));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,6 @@
 | 
				
			|||||||
package com.maddyhome.idea.vim.extension.textobjindent;
 | 
					package com.maddyhome.idea.vim.extension.textobjindent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.editor.Caret;
 | 
					import com.intellij.openapi.editor.Caret;
 | 
				
			||||||
import com.maddyhome.idea.vim.KeyHandler;
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.api.ExecutionContext;
 | 
					import com.maddyhome.idea.vim.api.ExecutionContext;
 | 
				
			||||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret;
 | 
					import com.maddyhome.idea.vim.api.ImmutableVimCaret;
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimEditor;
 | 
					import com.maddyhome.idea.vim.api.VimEditor;
 | 
				
			||||||
@@ -25,17 +24,19 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
 | 
				
			|||||||
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
 | 
					import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimCaret;
 | 
					import com.maddyhome.idea.vim.newapi.IjVimCaret;
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
				
			||||||
import com.maddyhome.idea.vim.state.KeyHandlerState;
 | 
					import com.maddyhome.idea.vim.state.VimStateMachine;
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode;
 | 
					import com.maddyhome.idea.vim.state.mode.Mode;
 | 
				
			||||||
import org.jetbrains.annotations.NotNull;
 | 
					import org.jetbrains.annotations.NotNull;
 | 
				
			||||||
import org.jetbrains.annotations.Nullable;
 | 
					import org.jetbrains.annotations.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.EnumSet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
 | 
					import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
 | 
				
			||||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping;
 | 
					import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Port of vim-indent-object:
 | 
					 * Port of vim-indent-object:
 | 
				
			||||||
 * <a href="https://github.com/michaeljsmith/vim-indent-object">vim-indent-object</a>
 | 
					 * https://github.com/michaeljsmith/vim-indent-object
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * <p>
 | 
					 * <p>
 | 
				
			||||||
 * vim-indent-object provides these text objects based on the cursor line's indentation:
 | 
					 * vim-indent-object provides these text objects based on the cursor line's indentation:
 | 
				
			||||||
@@ -47,7 +48,7 @@ import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping;
 | 
				
			|||||||
 * </ul>
 | 
					 * </ul>
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * See also the reference manual for more details at:
 | 
					 * See also the reference manual for more details at:
 | 
				
			||||||
 * <a href="https://github.com/michaeljsmith/vim-indent-object/blob/master/doc/indent-object.txt">indent-object.txt</a>
 | 
					 * https://github.com/michaeljsmith/vim-indent-object/blob/master/doc/indent-object.txt
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author Shrikant Kandula (@sharat87)
 | 
					 * @author Shrikant Kandula (@sharat87)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -96,12 +97,13 @@ public class VimIndentObject implements VimExtension {
 | 
				
			|||||||
        this.includeBelow = includeBelow;
 | 
					        this.includeBelow = includeBelow;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @Nullable
 | 
				
			||||||
      @Override
 | 
					      @Override
 | 
				
			||||||
      public @Nullable TextRange getRange(@NotNull VimEditor editor,
 | 
					      public TextRange getRange(@NotNull VimEditor editor,
 | 
				
			||||||
                                          @NotNull ImmutableVimCaret caret,
 | 
					                                @NotNull ImmutableVimCaret caret,
 | 
				
			||||||
                                          @NotNull ExecutionContext context,
 | 
					                                @NotNull ExecutionContext context,
 | 
				
			||||||
                                          int count,
 | 
					                                int count,
 | 
				
			||||||
                                          int rawCount) {
 | 
					                                int rawCount) {
 | 
				
			||||||
        final CharSequence charSequence = ((IjVimEditor)editor).getEditor().getDocument().getCharsSequence();
 | 
					        final CharSequence charSequence = ((IjVimEditor)editor).getEditor().getDocument().getCharsSequence();
 | 
				
			||||||
        final int caretOffset = ((IjVimCaret)caret).getCaret().getOffset();
 | 
					        final int caretOffset = ((IjVimCaret)caret).getCaret().getOffset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -246,8 +248,9 @@ public class VimIndentObject implements VimExtension {
 | 
				
			|||||||
        return new TextRange(upperBoundaryOffset, lowerBoundaryOffset);
 | 
					        return new TextRange(upperBoundaryOffset, lowerBoundaryOffset);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @NotNull
 | 
				
			||||||
      @Override
 | 
					      @Override
 | 
				
			||||||
      public @NotNull TextObjectVisualType getVisualType() {
 | 
					      public TextObjectVisualType getVisualType() {
 | 
				
			||||||
        return TextObjectVisualType.LINE_WISE;
 | 
					        return TextObjectVisualType.LINE_WISE;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -260,17 +263,17 @@ public class VimIndentObject implements VimExtension {
 | 
				
			|||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
 | 
					    public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
 | 
				
			||||||
      IjVimEditor vimEditor = (IjVimEditor)editor;
 | 
					      IjVimEditor vimEditor = (IjVimEditor)editor;
 | 
				
			||||||
      @NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
 | 
					      @NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(vimEditor);
 | 
				
			||||||
 | 
					      int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      final IndentObjectHandler textObjectHandler = new IndentObjectHandler(includeAbove, includeBelow);
 | 
					      final IndentObjectHandler textObjectHandler = new IndentObjectHandler(includeAbove, includeBelow);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (!(editor.getMode() instanceof Mode.OP_PENDING)) {
 | 
					      if (!vimStateMachine.isOperatorPending(editor.getMode())) {
 | 
				
			||||||
        int count0 = operatorArguments.getCount0();
 | 
					 | 
				
			||||||
        ((IjVimEditor)editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
 | 
					        ((IjVimEditor)editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
 | 
				
			||||||
          final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, Math.max(1, count0), count0);
 | 
					          final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, count, 0);
 | 
				
			||||||
          if (range != null) {
 | 
					          if (range != null) {
 | 
				
			||||||
            try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
 | 
					            try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
 | 
				
			||||||
              if (editor.getMode() instanceof Mode.VISUAL) {
 | 
					              if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
 | 
				
			||||||
                EngineVisualGroupKt.vimSetSelection(new IjVimCaret(caret), range.getStartOffset(), range.getEndOffset() - 1, true);
 | 
					                EngineVisualGroupKt.vimSetSelection(new IjVimCaret(caret), range.getStartOffset(), range.getEndOffset() - 1, true);
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset());
 | 
					                InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset());
 | 
				
			||||||
@@ -280,7 +283,9 @@ public class VimIndentObject implements VimExtension {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        keyHandlerState.getCommandBuilder().addAction(textObjectHandler);
 | 
					        vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
 | 
				
			||||||
 | 
					                                                                                         textObjectHandler, Command.Type.MOTION,
 | 
				
			||||||
 | 
					                                                                                         EnumSet.noneOf(CommandFlags.class))));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,38 +15,76 @@ import com.intellij.openapi.command.CommandProcessor
 | 
				
			|||||||
import com.intellij.openapi.command.UndoConfirmationPolicy
 | 
					import com.intellij.openapi.command.UndoConfirmationPolicy
 | 
				
			||||||
import com.intellij.openapi.diagnostic.logger
 | 
					import com.intellij.openapi.diagnostic.logger
 | 
				
			||||||
import com.intellij.openapi.editor.Editor
 | 
					import com.intellij.openapi.editor.Editor
 | 
				
			||||||
 | 
					import com.intellij.openapi.editor.LogicalPosition
 | 
				
			||||||
import com.intellij.openapi.editor.actions.EnterAction
 | 
					import com.intellij.openapi.editor.actions.EnterAction
 | 
				
			||||||
import com.intellij.openapi.editor.event.EditorMouseEvent
 | 
					import com.intellij.openapi.editor.event.EditorMouseEvent
 | 
				
			||||||
import com.intellij.openapi.editor.event.EditorMouseListener
 | 
					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.UserDataHolder
 | 
				
			||||||
 | 
					import com.intellij.openapi.util.text.StringUtil
 | 
				
			||||||
import com.intellij.psi.codeStyle.CodeStyleManager
 | 
					import com.intellij.psi.codeStyle.CodeStyleManager
 | 
				
			||||||
import com.intellij.psi.util.PsiUtilBase
 | 
					import com.intellij.psi.util.PsiUtilBase
 | 
				
			||||||
 | 
					import com.intellij.util.containers.ContainerUtil
 | 
				
			||||||
import com.maddyhome.idea.vim.EventFacade
 | 
					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.ExecutionContext
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimCaret
 | 
					import com.maddyhome.idea.vim.api.VimCaret
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimChangeGroupBase
 | 
					import com.maddyhome.idea.vim.api.VimChangeGroupBase
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
					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.getLineEndForOffset
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.getLineEndOffset
 | 
				
			||||||
import com.maddyhome.idea.vim.api.getLineStartForOffset
 | 
					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.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.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.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.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.SearchHelper
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.endOffsetInclusive
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.inInsertMode
 | 
					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.key.KeyHandlerKeeper.Companion.getInstance
 | 
				
			||||||
import com.maddyhome.idea.vim.listener.VimInsertListener
 | 
					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.IjVimCaret
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimCopiedText
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.ij
 | 
					import com.maddyhome.idea.vim.newapi.ij
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.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
 | 
				
			||||||
import com.maddyhome.idea.vim.undo.VimKeyBasedUndoService
 | 
					import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
 | 
				
			||||||
import com.maddyhome.idea.vim.undo.VimTimestampBasedUndoService
 | 
					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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Provides all the insert/replace related functionality
 | 
					 * Provides all the insert/replace related functionality
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class ChangeGroup : VimChangeGroupBase() {
 | 
					public class ChangeGroup : VimChangeGroupBase() {
 | 
				
			||||||
 | 
					  private val insertListeners = ContainerUtil.createLockFreeCopyOnWriteList<VimInsertListener>()
 | 
				
			||||||
  private val listener: EditorMouseListener = object : EditorMouseListener {
 | 
					  private val listener: EditorMouseListener = object : EditorMouseListener {
 | 
				
			||||||
    override fun mouseClicked(event: EditorMouseEvent) {
 | 
					    override fun mouseClicked(event: EditorMouseEvent) {
 | 
				
			||||||
      val editor = event.editor
 | 
					      val editor = event.editor
 | 
				
			||||||
@@ -56,7 +94,7 @@ class ChangeGroup : VimChangeGroupBase() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fun editorCreated(editor: Editor?, disposable: Disposable) {
 | 
					  public fun editorCreated(editor: Editor?, disposable: Disposable) {
 | 
				
			||||||
    EventFacade.getInstance().addEditorMouseListener(editor!!, listener, disposable)
 | 
					    EventFacade.getInstance().addEditorMouseListener(editor!!, listener, disposable)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -64,15 +102,6 @@ class ChangeGroup : VimChangeGroupBase() {
 | 
				
			|||||||
    val editor = (vimEditor as IjVimEditor).editor
 | 
					    val editor = (vimEditor as IjVimEditor).editor
 | 
				
			||||||
    val ijContext = context.ij
 | 
					    val ijContext = context.ij
 | 
				
			||||||
    val doc = vimEditor.editor.document
 | 
					    val doc = vimEditor.editor.document
 | 
				
			||||||
 | 
					 | 
				
			||||||
    val undo = injector.undo
 | 
					 | 
				
			||||||
    when (undo) {
 | 
					 | 
				
			||||||
      is VimKeyBasedUndoService -> undo.setInsertNonMergeUndoKey()
 | 
					 | 
				
			||||||
      is VimTimestampBasedUndoService -> {
 | 
					 | 
				
			||||||
        val nanoTime = System.nanoTime()
 | 
					 | 
				
			||||||
        vimEditor.forEachCaret { undo.startInsertSequence(it, it.offset, nanoTime) }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    CommandProcessor.getInstance().executeCommand(
 | 
					    CommandProcessor.getInstance().executeCommand(
 | 
				
			||||||
      editor.project, {
 | 
					      editor.project, {
 | 
				
			||||||
        ApplicationManager.getApplication()
 | 
					        ApplicationManager.getApplication()
 | 
				
			||||||
@@ -112,9 +141,161 @@ class ChangeGroup : VimChangeGroupBase() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun processBackspace(editor: VimEditor, context: ExecutionContext) {
 | 
					  override fun getDeleteRangeAndType2(
 | 
				
			||||||
    injector.actionExecutor.executeAction(editor, name = IdeActions.ACTION_EDITOR_BACKSPACE, context = context)
 | 
					    editor: VimEditor,
 | 
				
			||||||
    injector.scroll.scrollCaretIntoView(editor)
 | 
					    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) {
 | 
					  private fun restoreCursor(editor: VimEditor, caret: VimCaret, startLine: Int) {
 | 
				
			||||||
@@ -125,13 +306,88 @@ class ChangeGroup : VimChangeGroupBase() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun reformatCode(editor: VimEditor, start: Int, end: Int) {
 | 
					  /**
 | 
				
			||||||
 | 
					   * 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) {
 | 
				
			||||||
    val project = (editor as IjVimEditor).editor.project ?: return
 | 
					    val project = (editor as IjVimEditor).editor.project ?: return
 | 
				
			||||||
    val file = PsiUtilBase.getPsiFileInEditor(editor.editor, project) ?: return
 | 
					    val file = PsiUtilBase.getPsiFileInEditor(editor.editor, project) ?: return
 | 
				
			||||||
    val textRange = com.intellij.openapi.util.TextRange.create(start, end)
 | 
					    val textRange = com.intellij.openapi.util.TextRange.create(start, end)
 | 
				
			||||||
    CodeStyleManager.getInstance(project).reformatText(file, listOf(textRange))
 | 
					    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(
 | 
					  override fun autoIndentRange(
 | 
				
			||||||
    editor: VimEditor,
 | 
					    editor: VimEditor,
 | 
				
			||||||
    caret: VimCaret,
 | 
					    caret: VimCaret,
 | 
				
			||||||
@@ -145,10 +401,10 @@ class ChangeGroup : VimChangeGroupBase() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // FIXME: Here we do selection, and it is not a good idea, because it updates primary selection in Linux
 | 
					    // FIXME: Here we do selection, and it is not a good idea, because it updates primary selection in Linux
 | 
				
			||||||
    // FIXME: I'll leave here a dirty fix that restores primary selection, but it would be better to rewrite this method
 | 
					    // FIXME: I'll leave here a dirty fix that restores primary selection, but it would be better to rewrite this method
 | 
				
			||||||
    var copiedText: IjVimCopiedText? = null
 | 
					    var primaryTextAndTransferableData: Pair<String, List<Any>?>? = null
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      if (injector.registerGroup.isPrimaryRegisterSupported()) {
 | 
					      if (injector.registerGroup.isPrimaryRegisterSupported()) {
 | 
				
			||||||
        copiedText = injector.clipboardManager.getPrimaryContent(editor, context) as IjVimCopiedText
 | 
					        primaryTextAndTransferableData = injector.clipboardManager.getPrimaryTextAndTransferableData()
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (e: Exception) {
 | 
					    } catch (e: Exception) {
 | 
				
			||||||
      // FIXME: [isPrimaryRegisterSupported()] is not implemented perfectly, so there might be thrown an exception after trying to access the primary selection
 | 
					      // FIXME: [isPrimaryRegisterSupported()] is not implemented perfectly, so there might be thrown an exception after trying to access the primary selection
 | 
				
			||||||
@@ -174,22 +430,374 @@ class ChangeGroup : VimChangeGroupBase() {
 | 
				
			|||||||
      afterAction.invoke()
 | 
					      afterAction.invoke()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      if (copiedText != null) {
 | 
					      if (primaryTextAndTransferableData != null) {
 | 
				
			||||||
        injector.clipboardManager.setPrimaryContent(editor, context, copiedText)
 | 
					        injector.clipboardManager.setPrimaryText(
 | 
				
			||||||
 | 
					          primaryTextAndTransferableData.first,
 | 
				
			||||||
 | 
					          primaryTextAndTransferableData.first,
 | 
				
			||||||
 | 
					          primaryTextAndTransferableData.second ?: emptyList()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (e: Exception) {
 | 
					    } catch (e: Exception) {
 | 
				
			||||||
      // FIXME: [isPrimaryRegisterSupported()] is not implemented perfectly, so there might be thrown an exception after trying to access the primary selection
 | 
					      // FIXME: [isPrimaryRegisterSupported()] is not implemented perfectly, so there might be thrown an exception after trying to access the primary selection
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Deprecated(message = "Please use listenersNotifier", replaceWith = ReplaceWith("injector.listenersNotifier.modeChangeListeners.add", imports = ["import com.maddyhome.idea.vim.api.injector"]))
 | 
					  override fun indentLines(
 | 
				
			||||||
  fun addInsertListener(listener: VimInsertListener) {
 | 
					    editor: VimEditor,
 | 
				
			||||||
    injector.listenersNotifier.modeChangeListeners.add(listener)
 | 
					    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)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Deprecated(message = "Please use listenersNotifier", replaceWith = ReplaceWith("injector.listenersNotifier.modeChangeListeners.remove", imports = ["import com.maddyhome.idea.vim.api.injector"]))
 | 
					  override fun indentMotion(
 | 
				
			||||||
  fun removeInsertListener(listener: VimInsertListener) {
 | 
					    editor: VimEditor,
 | 
				
			||||||
    injector.listenersNotifier.modeChangeListeners.remove(listener)
 | 
					    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 = SearchHelper.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 =
 | 
				
			||||||
 | 
					      SearchHelper.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
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public fun addInsertListener(listener: VimInsertListener) {
 | 
				
			||||||
 | 
					    insertListeners.add(listener)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public 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)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private companion object {
 | 
					  private companion object {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										78
									
								
								src/main/java/com/maddyhome/idea/vim/group/DigraphGroup.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/main/java/com/maddyhome/idea/vim/group/DigraphGroup.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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.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.ex.ExOutputModel;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.EditorHelper;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
				
			||||||
 | 
					import org.jetbrains.annotations.NotNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class DigraphGroup extends VimDigraphGroupBase {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void showDigraphs(@NotNull VimEditor editor) {
 | 
				
			||||||
 | 
					    int width = EditorHelper.getApproximateScreenWidth(((IjVimEditor) editor).getEditor());
 | 
				
			||||||
 | 
					    if (width < 10) {
 | 
				
			||||||
 | 
					      width = 80;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    int colCount = width / 12;
 | 
				
			||||||
 | 
					    int height = (int)Math.ceil((double) getDigraphs().size() / (double)colCount);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (logger.isDebugEnabled()) {
 | 
				
			||||||
 | 
					      logger.debug("width=" + width);
 | 
				
			||||||
 | 
					      logger.debug("colCount=" + colCount);
 | 
				
			||||||
 | 
					      logger.debug("height=" + height);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    StringBuilder res = new StringBuilder();
 | 
				
			||||||
 | 
					    int cnt = 0;
 | 
				
			||||||
 | 
					    for (Character code : getKeys().keySet()) {
 | 
				
			||||||
 | 
					      String key = getKeys().get(code);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      res.append(key);
 | 
				
			||||||
 | 
					      res.append(' ');
 | 
				
			||||||
 | 
					      if (code < 32) {
 | 
				
			||||||
 | 
					        res.append('^');
 | 
				
			||||||
 | 
					        res.append((char)(code + '@'));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else if (code >= 128 && code <= 159) {
 | 
				
			||||||
 | 
					        res.append('~');
 | 
				
			||||||
 | 
					        res.append((char)(code - 128 + '@'));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else {
 | 
				
			||||||
 | 
					        res.append(code);
 | 
				
			||||||
 | 
					        res.append(' ');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      res.append(' ');
 | 
				
			||||||
 | 
					      if (code < 0x1000) {
 | 
				
			||||||
 | 
					        res.append('0');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (code < 0x100) {
 | 
				
			||||||
 | 
					        res.append('0');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (code < 0x10) {
 | 
				
			||||||
 | 
					        res.append('0');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      res.append(Integer.toHexString(code));
 | 
				
			||||||
 | 
					      res.append("  ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      cnt++;
 | 
				
			||||||
 | 
					      if (cnt == colCount) {
 | 
				
			||||||
 | 
					        res.append('\n');
 | 
				
			||||||
 | 
					        cnt = 0;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ExOutputModel.getInstance(((IjVimEditor) editor).getEditor()).output(res.toString());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static final Logger logger = Logger.getInstance(DigraphGroup.class.getName());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -8,7 +8,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.maddyhome.idea.vim.group;
 | 
					package com.maddyhome.idea.vim.group;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.intellij.execution.impl.ConsoleViewImpl;
 | 
				
			||||||
import com.intellij.find.EditorSearchSession;
 | 
					import com.intellij.find.EditorSearchSession;
 | 
				
			||||||
 | 
					import com.intellij.openapi.application.ApplicationManager;
 | 
				
			||||||
import com.intellij.openapi.client.ClientAppSession;
 | 
					import com.intellij.openapi.client.ClientAppSession;
 | 
				
			||||||
import com.intellij.openapi.client.ClientKind;
 | 
					import com.intellij.openapi.client.ClientKind;
 | 
				
			||||||
import com.intellij.openapi.client.ClientSessionsManager;
 | 
					import com.intellij.openapi.client.ClientSessionsManager;
 | 
				
			||||||
@@ -18,13 +20,13 @@ import com.intellij.openapi.components.Storage;
 | 
				
			|||||||
import com.intellij.openapi.editor.*;
 | 
					import com.intellij.openapi.editor.*;
 | 
				
			||||||
import com.intellij.openapi.editor.event.CaretEvent;
 | 
					import com.intellij.openapi.editor.event.CaretEvent;
 | 
				
			||||||
import com.intellij.openapi.editor.event.CaretListener;
 | 
					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.editor.ex.EditorGutterComponentEx;
 | 
				
			||||||
import com.intellij.openapi.project.Project;
 | 
					import com.intellij.openapi.project.Project;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.KeyHandler;
 | 
				
			||||||
import com.maddyhome.idea.vim.VimPlugin;
 | 
					import com.maddyhome.idea.vim.VimPlugin;
 | 
				
			||||||
import com.maddyhome.idea.vim.api.*;
 | 
					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.CaretVisualAttributesHelperKt;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.CommandStateHelper;
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.EditorHelper;
 | 
					import com.maddyhome.idea.vim.helper.EditorHelper;
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.UserDataManager;
 | 
					import com.maddyhome.idea.vim.helper.UserDataManager;
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimDocument;
 | 
					import com.maddyhome.idea.vim.newapi.IjVimDocument;
 | 
				
			||||||
@@ -36,8 +38,6 @@ import org.jetbrains.annotations.NonNls;
 | 
				
			|||||||
import org.jetbrains.annotations.NotNull;
 | 
					import org.jetbrains.annotations.NotNull;
 | 
				
			||||||
import org.jetbrains.annotations.Nullable;
 | 
					import org.jetbrains.annotations.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.beans.PropertyChangeEvent;
 | 
					 | 
				
			||||||
import java.beans.PropertyChangeListener;
 | 
					 | 
				
			||||||
import java.util.Collection;
 | 
					import java.util.Collection;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
@@ -45,7 +45,6 @@ import java.util.stream.Stream;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import static com.intellij.openapi.editor.EditorSettings.*;
 | 
					import static com.intellij.openapi.editor.EditorSettings.*;
 | 
				
			||||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
 | 
					import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
 | 
				
			||||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.options;
 | 
					 | 
				
			||||||
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.ijOptions;
 | 
					import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.ijOptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -209,14 +208,44 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    initLineNumbers(editor);
 | 
					    initLineNumbers(editor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Listen for changes to the font size, so we can hide the ex text field/output panel
 | 
					    // We add Vim bindings to all opened editors, even read-only editors. We also add bindings to editors that are used
 | 
				
			||||||
    if (editor instanceof EditorEx editorEx) {
 | 
					    // elsewhere in the IDE, rather than just for editing project files. This includes editors used as part of the UI,
 | 
				
			||||||
      editorEx.addPropertyChangeListener(FontSizeChangeListener.INSTANCE);
 | 
					    // 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 interactive stdin/stdout for console-based run
 | 
				
			||||||
 | 
					    // configurations.
 | 
				
			||||||
    if (injector.getApplication().isUnitTest()) {
 | 
					    // We want to provide an intuitive experience for working with these additional editors, so we automatically switch
 | 
				
			||||||
      updateCaretsVisualAttributes(new IjVimEditor(editor));
 | 
					    // to INSERT mode for interactive editors. Recognising these can be a bit tricky.
 | 
				
			||||||
 | 
					    // These additional interactive editors are not file-based, but must have a writable document. However, log output
 | 
				
			||||||
 | 
					    // documents are also writable (the IDE is writing new content as it becomes available) just not user-editable. So
 | 
				
			||||||
 | 
					    // we must also check that the editor is not in read-only "viewer" mode (this includes "rendered" mode, which is
 | 
				
			||||||
 | 
					    // read-only and also hides the caret).
 | 
				
			||||||
 | 
					    // Furthermore, the interactive stdin/stdout console output is hosted in a read-only editor, but it can still be
 | 
				
			||||||
 | 
					    // edited. The `ConsoleViewImpl` class installs a typing handler that ignores the editor's `isViewer` property and
 | 
				
			||||||
 | 
					    // allows typing if the associated process (if any) is still running. We can get the editor's console view and check
 | 
				
			||||||
 | 
					    // this ourselves, but we have to wait until the editor has finished initialising before it's available in user
 | 
				
			||||||
 | 
					    // data.
 | 
				
			||||||
 | 
					    // 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.
 | 
				
			||||||
 | 
					    Runnable switchToInsertMode = () -> {
 | 
				
			||||||
 | 
					      ExecutionContext context = injector.getExecutionContextManager().getEditorExecutionContext(new IjVimEditor(editor));
 | 
				
			||||||
 | 
					      VimPlugin.getChange().insertBeforeCursor(new IjVimEditor(editor), context);
 | 
				
			||||||
 | 
					      KeyHandler.getInstance().reset(new IjVimEditor(editor));
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    if (!editor.isViewer() &&
 | 
				
			||||||
 | 
					        !EditorHelper.isFileEditor(editor) &&
 | 
				
			||||||
 | 
					        editor.getDocument().isWritable() &&
 | 
				
			||||||
 | 
					        !CommandStateHelper.inInsertMode(editor)) {
 | 
				
			||||||
 | 
					      switchToInsertMode.run();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    ApplicationManager.getApplication().invokeLater(
 | 
				
			||||||
 | 
					      () -> {
 | 
				
			||||||
 | 
					        if (editor.isDisposed()) return;
 | 
				
			||||||
 | 
					        ConsoleViewImpl consoleView = editor.getUserData(ConsoleViewImpl.CONSOLE_VIEW_IN_EDITOR_VIEW);
 | 
				
			||||||
 | 
					        if (consoleView != null && consoleView.isRunning() && !CommandStateHelper.inInsertMode(editor)) {
 | 
				
			||||||
 | 
					          switchToInsertMode.run();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    updateCaretsVisualAttributes(new IjVimEditor(editor));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void editorDeinit(@NotNull Editor editor) {
 | 
					  public void editorDeinit(@NotNull Editor editor) {
 | 
				
			||||||
@@ -224,9 +253,6 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
 | 
				
			|||||||
    UserDataManager.unInitializeEditor(editor);
 | 
					    UserDataManager.unInitializeEditor(editor);
 | 
				
			||||||
    VimPlugin.getKey().unregisterShortcutKeys(new IjVimEditor(editor));
 | 
					    VimPlugin.getKey().unregisterShortcutKeys(new IjVimEditor(editor));
 | 
				
			||||||
    CaretVisualAttributesHelperKt.removeCaretsVisualAttributes(editor);
 | 
					    CaretVisualAttributesHelperKt.removeCaretsVisualAttributes(editor);
 | 
				
			||||||
    if (editor instanceof EditorEx editorEx) {
 | 
					 | 
				
			||||||
      editorEx.removePropertyChangeListener(FontSizeChangeListener.INSTANCE);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void notifyIdeaJoin(@Nullable Project project, @NotNull VimEditor editor) {
 | 
					  public void notifyIdeaJoin(@Nullable Project project, @NotNull VimEditor editor) {
 | 
				
			||||||
@@ -238,8 +264,9 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
 | 
				
			|||||||
    VimPlugin.getNotifications(project).notifyAboutIdeaJoin(editor);
 | 
					    VimPlugin.getNotifications(project).notifyAboutIdeaJoin(editor);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Nullable
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public @Nullable Element getState() {
 | 
					  public Element getState() {
 | 
				
			||||||
    Element element = new Element("editor");
 | 
					    Element element = new Element("editor");
 | 
				
			||||||
    saveData(element);
 | 
					    saveData(element);
 | 
				
			||||||
    return element;
 | 
					    return element;
 | 
				
			||||||
@@ -288,7 +315,7 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
 | 
				
			|||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Integer convert(@NotNull Editor editor, int lineNumber) {
 | 
					    public Integer convert(@NotNull Editor editor, int lineNumber) {
 | 
				
			||||||
      final IjVimEditor ijVimEditor = new IjVimEditor(editor);
 | 
					      final IjVimEditor ijVimEditor = new IjVimEditor(editor);
 | 
				
			||||||
      final boolean number = options(injector, ijVimEditor).getNumber();
 | 
					      final boolean number = ijOptions(injector, ijVimEditor).getNumber();
 | 
				
			||||||
      final int caretLine = editor.getCaretModel().getLogicalPosition().line;
 | 
					      final int caretLine = editor.getCaretModel().getLogicalPosition().line;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // lineNumber is 1 based
 | 
					      // lineNumber is 1 based
 | 
				
			||||||
@@ -315,16 +342,18 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
 | 
				
			|||||||
      .collect(Collectors.toList());
 | 
					      .collect(Collectors.toList());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @NotNull
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public @NotNull Collection<VimEditor> getEditors() {
 | 
					  public Collection<VimEditor> getEditors() {
 | 
				
			||||||
    return getLocalEditors()
 | 
					    return getLocalEditors()
 | 
				
			||||||
      .filter(UserDataManager::getVimInitialised)
 | 
					      .filter(UserDataManager::getVimInitialised)
 | 
				
			||||||
      .map(IjVimEditor::new)
 | 
					      .map(IjVimEditor::new)
 | 
				
			||||||
      .collect(Collectors.toList());
 | 
					      .collect(Collectors.toList());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @NotNull
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public @NotNull Collection<VimEditor> getEditors(@NotNull VimDocument buffer) {
 | 
					  public Collection<VimEditor> getEditors(@NotNull VimDocument buffer) {
 | 
				
			||||||
    final Document document = ((IjVimDocument)buffer).getDocument();
 | 
					    final Document document = ((IjVimDocument)buffer).getDocument();
 | 
				
			||||||
    return getLocalEditors()
 | 
					    return getLocalEditors()
 | 
				
			||||||
      .filter(editor -> UserDataManager.getVimInitialised(editor) && editor.getDocument().equals(document))
 | 
					      .filter(editor -> UserDataManager.getVimInitialised(editor) && editor.getDocument().equals(document))
 | 
				
			||||||
@@ -344,7 +373,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
 | 
					    // events such as document change (to update search highlights), and these can come from CWM guests, and we'd get
 | 
				
			||||||
    // the remote editors.
 | 
					    // the remote editors.
 | 
				
			||||||
    // This invocation will always get local editors, regardless of the current context.
 | 
					    // This invocation will always get local editors, regardless of the current context.
 | 
				
			||||||
    List<ClientAppSession> appSessions = ClientSessionsManager.getAppSessions(ClientKind.LOCAL);
 | 
					    List<ClientAppSession> appSessions = ClientSessionsManager.getAppSessions(ClientKind.ALL);
 | 
				
			||||||
    if (!appSessions.isEmpty()) {
 | 
					    if (!appSessions.isEmpty()) {
 | 
				
			||||||
      ClientAppSession localSession = appSessions.get(0);
 | 
					      ClientAppSession localSession = appSessions.get(0);
 | 
				
			||||||
      return localSession.getService(ClientEditorManager.class).editors();
 | 
					      return localSession.getService(ClientEditorManager.class).editors();
 | 
				
			||||||
@@ -353,37 +382,4 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
 | 
				
			|||||||
      return Stream.empty();
 | 
					      return Stream.empty();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Listens to property changes from the editor to hide ex text field/output panel when the editor's font is zoomed
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  private static class FontSizeChangeListener implements PropertyChangeListener {
 | 
					 | 
				
			||||||
    public static FontSizeChangeListener INSTANCE = new FontSizeChangeListener();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void propertyChange(PropertyChangeEvent evt) {
 | 
					 | 
				
			||||||
      if (VimPlugin.isNotEnabled()) return;
 | 
					 | 
				
			||||||
      if (evt.getPropertyName().equals(EditorEx.PROP_FONT_SIZE)) {
 | 
					 | 
				
			||||||
        Object source = evt.getSource();
 | 
					 | 
				
			||||||
        if (source instanceof Editor editor) {
 | 
					 | 
				
			||||||
          // The editor is being zoomed, so hide the command line or output panel, if they're being shown. On the one
 | 
					 | 
				
			||||||
          // hand, it's a little rude to cancel a command line for the user, but on the other, the panels obscure the
 | 
					 | 
				
			||||||
          // zoom indicator, so it looks nicer if we hide them.
 | 
					 | 
				
			||||||
          // Note that IDE scale is handled by LafManager.lookAndFeelChanged
 | 
					 | 
				
			||||||
          VimCommandLine activeCommandLine = injector.getCommandLine().getActiveCommandLine();
 | 
					 | 
				
			||||||
          if (activeCommandLine != null) {
 | 
					 | 
				
			||||||
            activeCommandLine.close(true, false);
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          VimOutputPanel outputPanel = injector.getOutputPanel().getCurrentOutputPanel();
 | 
					 | 
				
			||||||
          if (outputPanel != null) {
 | 
					 | 
				
			||||||
            outputPanel.close();
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          VimModalInput modalInput = injector.getModalInput().getCurrentModalInput();
 | 
					 | 
				
			||||||
          if (modalInput != null) {
 | 
					 | 
				
			||||||
            modalInput.deactivate(true, false);
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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.group
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.intellij.openapi.components.Service
 | 
				
			||||||
 | 
					import com.intellij.openapi.components.service
 | 
				
			||||||
 | 
					import com.intellij.openapi.editor.Editor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Service
 | 
				
			||||||
 | 
					internal class EditorHolderService {
 | 
				
			||||||
 | 
					  var editor: Editor? = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  companion object {
 | 
				
			||||||
 | 
					    @JvmStatic
 | 
				
			||||||
 | 
					    fun getInstance(): EditorHolderService = service()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										476
									
								
								src/main/java/com/maddyhome/idea/vim/group/FileGroup.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										476
									
								
								src/main/java/com/maddyhome/idea/vim/group/FileGroup.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,476 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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.group;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.intellij.openapi.actionSystem.DataContext;
 | 
				
			||||||
 | 
					import com.intellij.openapi.actionSystem.PlatformDataKeys;
 | 
				
			||||||
 | 
					import com.intellij.openapi.application.ApplicationManager;
 | 
				
			||||||
 | 
					import com.intellij.openapi.diagnostic.Logger;
 | 
				
			||||||
 | 
					import com.intellij.openapi.editor.Document;
 | 
				
			||||||
 | 
					import com.intellij.openapi.editor.Editor;
 | 
				
			||||||
 | 
					import com.intellij.openapi.editor.LogicalPosition;
 | 
				
			||||||
 | 
					import com.intellij.openapi.fileEditor.*;
 | 
				
			||||||
 | 
					import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
 | 
				
			||||||
 | 
					import com.intellij.openapi.fileEditor.impl.EditorWindow;
 | 
				
			||||||
 | 
					import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
 | 
				
			||||||
 | 
					import com.intellij.openapi.fileTypes.FileType;
 | 
				
			||||||
 | 
					import com.intellij.openapi.fileTypes.FileTypeManager;
 | 
				
			||||||
 | 
					import com.intellij.openapi.project.Project;
 | 
				
			||||||
 | 
					import com.intellij.openapi.project.ProjectManager;
 | 
				
			||||||
 | 
					import com.intellij.openapi.roots.ProjectRootManager;
 | 
				
			||||||
 | 
					import com.intellij.openapi.vfs.LocalFileSystem;
 | 
				
			||||||
 | 
					import com.intellij.openapi.vfs.VirtualFile;
 | 
				
			||||||
 | 
					import com.intellij.openapi.vfs.VirtualFileManager;
 | 
				
			||||||
 | 
					import com.intellij.openapi.vfs.VirtualFileSystem;
 | 
				
			||||||
 | 
					import com.intellij.psi.search.FilenameIndex;
 | 
				
			||||||
 | 
					import com.intellij.psi.search.GlobalSearchScope;
 | 
				
			||||||
 | 
					import com.intellij.psi.search.ProjectScope;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.VimPlugin;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.*;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.common.TextRange;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.EditorHelper;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.EditorHelperRt;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.MessageHelper;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.SearchHelper;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.newapi.ExecuteExtensionKt;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.VimStateMachine;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.Mode;
 | 
				
			||||||
 | 
					import org.jetbrains.annotations.NotNull;
 | 
				
			||||||
 | 
					import org.jetbrains.annotations.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					import java.util.Collection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
 | 
				
			||||||
 | 
					import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.globalIjOptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class FileGroup extends VimFileBase {
 | 
				
			||||||
 | 
					  public boolean openFile(@NotNull String filename, @NotNull ExecutionContext context) {
 | 
				
			||||||
 | 
					    if (logger.isDebugEnabled()) {
 | 
				
			||||||
 | 
					      logger.debug("openFile(" + filename + ")");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    final Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext()); // API change - don't merge
 | 
				
			||||||
 | 
					    if (project == null) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    VirtualFile found = findFile(filename, project);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (found != null) {
 | 
				
			||||||
 | 
					      if (logger.isDebugEnabled()) {
 | 
				
			||||||
 | 
					        logger.debug("found file: " + found);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      // Can't open a file unless it has a known file type. The next call will return the known type.
 | 
				
			||||||
 | 
					      // If unknown, IDEA will prompt the user to pick a type.
 | 
				
			||||||
 | 
					      FileType type = FileTypeManager.getInstance().getKnownFileTypeOrAssociate(found, project);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      //noinspection IfStatementWithIdenticalBranches
 | 
				
			||||||
 | 
					      if (type != null) {
 | 
				
			||||||
 | 
					        FileEditorManager fem = FileEditorManager.getInstance(project);
 | 
				
			||||||
 | 
					        fem.openFile(found, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else {
 | 
				
			||||||
 | 
					        // There was no type and user didn't pick one. Don't open the file
 | 
				
			||||||
 | 
					        // Return true here because we found the file but the user canceled by not picking a type.
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					      VimPlugin.showMessage(MessageHelper.message("unable.to.find.0", filename));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Nullable VirtualFile findFile(@NotNull String filename, @NotNull Project project) {
 | 
				
			||||||
 | 
					    VirtualFile found;
 | 
				
			||||||
 | 
					    // Vim supports both ~/ and ~\ (tested on Mac and Windows). On Windows, it supports forward- and back-slashes, but
 | 
				
			||||||
 | 
					    // it only supports forward slash on Unix (tested on Mac)
 | 
				
			||||||
 | 
					    // VFS works with both directory separators (tested on Mac and Windows)
 | 
				
			||||||
 | 
					    if (filename.startsWith("~/") || filename.startsWith("~\\")) {
 | 
				
			||||||
 | 
					      String relativePath = filename.substring(2);
 | 
				
			||||||
 | 
					      String dir = System.getProperty("user.home");
 | 
				
			||||||
 | 
					      if (logger.isDebugEnabled()) {
 | 
				
			||||||
 | 
					        logger.debug("home dir file");
 | 
				
			||||||
 | 
					        logger.debug("looking for " + relativePath + " in " + dir);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      found = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(dir, relativePath));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					      found = LocalFileSystem.getInstance().findFileByIoFile(new File(filename));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (found == null) {
 | 
				
			||||||
 | 
					        found = findByNameInContentRoots(filename, project);
 | 
				
			||||||
 | 
					        if (found == null) {
 | 
				
			||||||
 | 
					          found = findByNameInProject(filename, project);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return found;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Nullable
 | 
				
			||||||
 | 
					  private VirtualFile findByNameInContentRoots(@NotNull String filename, @NotNull Project project) {
 | 
				
			||||||
 | 
					    VirtualFile found = null;
 | 
				
			||||||
 | 
					    ProjectRootManager prm = ProjectRootManager.getInstance(project);
 | 
				
			||||||
 | 
					    VirtualFile[] roots = prm.getContentRoots();
 | 
				
			||||||
 | 
					    for (int i = 0; i < roots.length; i++) {
 | 
				
			||||||
 | 
					      if (logger.isDebugEnabled()) {
 | 
				
			||||||
 | 
					        logger.debug("root[" + i + "] = " + roots[i].getPath());
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      found = roots[i].findFileByRelativePath(filename);
 | 
				
			||||||
 | 
					      if (found != null) {
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return found;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Nullable
 | 
				
			||||||
 | 
					  private static VirtualFile findByNameInProject(@NotNull String filename, @NotNull Project project) {
 | 
				
			||||||
 | 
					    GlobalSearchScope projectScope = ProjectScope.getProjectScope(project);
 | 
				
			||||||
 | 
					    Collection<VirtualFile> names = FilenameIndex.getVirtualFilesByName(filename, projectScope);
 | 
				
			||||||
 | 
					    if (!names.isEmpty()) {
 | 
				
			||||||
 | 
					      return names.stream().findFirst().get();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Closes the current editor.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void closeFile(@NotNull VimEditor editor, @NotNull ExecutionContext context) {
 | 
				
			||||||
 | 
					    final Project project = PlatformDataKeys.PROJECT.getData(((DataContext)context.getContext()));
 | 
				
			||||||
 | 
					    if (project != null) {
 | 
				
			||||||
 | 
					      final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(project);
 | 
				
			||||||
 | 
					      final EditorWindow window = fileEditorManager.getCurrentWindow();
 | 
				
			||||||
 | 
					      final VirtualFile virtualFile = fileEditorManager.getCurrentFile();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (virtualFile != null && window != null) {
 | 
				
			||||||
 | 
					        // During the work on VIM-2912 I've changed the close function to this one.
 | 
				
			||||||
 | 
					        //   However, the function with manager seems to work weirdly and it causes VIM-2953
 | 
				
			||||||
 | 
					        //window.getManager().closeFile(virtualFile, true, false);
 | 
				
			||||||
 | 
					        window.closeFile(virtualFile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get focus after closing tab
 | 
				
			||||||
 | 
					        window.requestFocus(true);
 | 
				
			||||||
 | 
					        if (!ApplicationManager.getApplication().isUnitTestMode()) {
 | 
				
			||||||
 | 
					          // This thing doesn't have an implementation in test mode
 | 
				
			||||||
 | 
					          EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Closes editor.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void closeFile(int number, @NotNull ExecutionContext context) {
 | 
				
			||||||
 | 
					    final Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext());
 | 
				
			||||||
 | 
					    if (project == null) return;
 | 
				
			||||||
 | 
					    final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(project);
 | 
				
			||||||
 | 
					    final EditorWindow window = fileEditorManager.getCurrentWindow();
 | 
				
			||||||
 | 
					    VirtualFile[] editors = fileEditorManager.getOpenFiles();
 | 
				
			||||||
 | 
					    if (window != null) {
 | 
				
			||||||
 | 
					      if (number >= 0 && number < editors.length) {
 | 
				
			||||||
 | 
					        fileEditorManager.closeFile(editors[number], window);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } if (!ApplicationManager.getApplication().isUnitTestMode()) {
 | 
				
			||||||
 | 
					      // This thing doesn't have an implementation in test mode
 | 
				
			||||||
 | 
					      EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Saves specific file in the project.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void saveFile(@NotNull ExecutionContext context) {
 | 
				
			||||||
 | 
					    NativeAction action;
 | 
				
			||||||
 | 
					    if (globalIjOptions(injector).getIdeawrite().contains(IjOptionConstants.ideawrite_all)) {
 | 
				
			||||||
 | 
					      action = injector.getNativeActionManager().getSaveAll();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					      action = injector.getNativeActionManager().getSaveCurrent();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ExecuteExtensionKt.execute(action, context);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Saves all files in the project.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public void saveFiles(@NotNull ExecutionContext context) {
 | 
				
			||||||
 | 
					    ExecuteExtensionKt.execute(VimInjectorKt.getInjector().getNativeActionManager().getSaveAll(), context);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Selects then next or previous editor.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public boolean selectFile(int count, @NotNull ExecutionContext context) {
 | 
				
			||||||
 | 
					    final Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext());
 | 
				
			||||||
 | 
					    if (project == null) return false;
 | 
				
			||||||
 | 
					    FileEditorManager fem = FileEditorManager.getInstance(project); // API change - don't merge
 | 
				
			||||||
 | 
					    VirtualFile[] editors = fem.getOpenFiles();
 | 
				
			||||||
 | 
					    if (count == 99) {
 | 
				
			||||||
 | 
					      count = editors.length - 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (count < 0 || count >= editors.length) {
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fem.openFile(editors[count], true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Selects then next or previous editor.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public void selectNextFile(int count, @NotNull ExecutionContext context) {
 | 
				
			||||||
 | 
					    Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext());
 | 
				
			||||||
 | 
					    if (project == null) return;
 | 
				
			||||||
 | 
					    FileEditorManager fem = FileEditorManager.getInstance(project); // API change - don't merge
 | 
				
			||||||
 | 
					    VirtualFile[] editors = fem.getOpenFiles();
 | 
				
			||||||
 | 
					    VirtualFile current = fem.getSelectedFiles()[0];
 | 
				
			||||||
 | 
					    for (int i = 0; i < editors.length; i++) {
 | 
				
			||||||
 | 
					      if (editors[i].equals(current)) {
 | 
				
			||||||
 | 
					        int pos = (i + (count % editors.length) + editors.length) % editors.length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fem.openFile(editors[pos], true);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Selects previous editor tab.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void selectPreviousTab(@NotNull ExecutionContext context) {
 | 
				
			||||||
 | 
					    Project project = PlatformDataKeys.PROJECT.getData(((DataContext)context.getContext()));
 | 
				
			||||||
 | 
					    if (project == null) return;
 | 
				
			||||||
 | 
					    VirtualFile vf = LastTabService.getInstance(project).getLastTab();
 | 
				
			||||||
 | 
					    if (vf != null && vf.isValid()) {
 | 
				
			||||||
 | 
					      FileEditorManager.getInstance(project).openFile(vf, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					      VimPlugin.indicateError();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Returns the previous tab.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public @Nullable VirtualFile getPreviousTab(@NotNull DataContext context) {
 | 
				
			||||||
 | 
					    Project project = PlatformDataKeys.PROJECT.getData(context);
 | 
				
			||||||
 | 
					    if (project == null) return null;
 | 
				
			||||||
 | 
					    VirtualFile vf = LastTabService.getInstance(project).getLastTab();
 | 
				
			||||||
 | 
					    if (vf != null && vf.isValid()) {
 | 
				
			||||||
 | 
					      return vf;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Nullable Editor selectEditor(Project project, @NotNull VirtualFile file) {
 | 
				
			||||||
 | 
					    FileEditorManager fMgr = FileEditorManager.getInstance(project);
 | 
				
			||||||
 | 
					    FileEditor[] feditors = fMgr.openFile(file, true);
 | 
				
			||||||
 | 
					    if (feditors.length > 0) {
 | 
				
			||||||
 | 
					      if (feditors[0] instanceof TextEditor) {
 | 
				
			||||||
 | 
					        Editor editor = ((TextEditor)feditors[0]).getEditor();
 | 
				
			||||||
 | 
					        if (!editor.isDisposed()) {
 | 
				
			||||||
 | 
					          return editor;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void displayLocationInfo(@NotNull VimEditor vimEditor) {
 | 
				
			||||||
 | 
					    Editor editor = ((IjVimEditor)vimEditor).getEditor();
 | 
				
			||||||
 | 
					    StringBuilder msg = new StringBuilder();
 | 
				
			||||||
 | 
					    Document doc = editor.getDocument();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!(VimStateMachine.Companion.getInstance(new IjVimEditor(editor)).getMode() instanceof Mode.VISUAL)) {
 | 
				
			||||||
 | 
					      LogicalPosition lp = editor.getCaretModel().getLogicalPosition();
 | 
				
			||||||
 | 
					      int col = editor.getCaretModel().getOffset() - doc.getLineStartOffset(lp.line);
 | 
				
			||||||
 | 
					      int endoff = doc.getLineEndOffset(lp.line);
 | 
				
			||||||
 | 
					      if (endoff < EditorHelperRt.getFileSize(editor) && doc.getCharsSequence().charAt(endoff) == '\n') {
 | 
				
			||||||
 | 
					        endoff--;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      int ecol = endoff - doc.getLineStartOffset(lp.line);
 | 
				
			||||||
 | 
					      LogicalPosition elp = editor.offsetToLogicalPosition(endoff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      msg.append("Col ").append(col + 1);
 | 
				
			||||||
 | 
					      if (col != lp.column) {
 | 
				
			||||||
 | 
					        msg.append("-").append(lp.column + 1);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      msg.append(" of ").append(ecol + 1);
 | 
				
			||||||
 | 
					      if (ecol != elp.column) {
 | 
				
			||||||
 | 
					        msg.append("-").append(elp.column + 1);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      int lline = editor.getCaretModel().getLogicalPosition().line;
 | 
				
			||||||
 | 
					      int total = new IjVimEditor(editor).lineCount();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      msg.append("; Line ").append(lline + 1).append(" of ").append(total);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      SearchHelper.CountPosition cp = SearchHelper.countWords(editor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      msg.append("; Word ").append(cp.getPosition()).append(" of ").append(cp.getCount());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      int offset = editor.getCaretModel().getOffset();
 | 
				
			||||||
 | 
					      int size = EditorHelperRt.getFileSize(editor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      msg.append("; Character ").append(offset + 1).append(" of ").append(size);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					      msg.append("Selected ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      TextRange vr = new TextRange(editor.getSelectionModel().getBlockSelectionStarts(),
 | 
				
			||||||
 | 
					                                   editor.getSelectionModel().getBlockSelectionEnds());
 | 
				
			||||||
 | 
					      vr.normalize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      int lines;
 | 
				
			||||||
 | 
					      SearchHelper.CountPosition cp = SearchHelper.countWords(editor);
 | 
				
			||||||
 | 
					      int words = cp.getCount();
 | 
				
			||||||
 | 
					      int word = 0;
 | 
				
			||||||
 | 
					      if (vr.isMultiple()) {
 | 
				
			||||||
 | 
					        lines = vr.size();
 | 
				
			||||||
 | 
					        int cols = vr.getMaxLength();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        msg.append(cols).append(" Cols; ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int i = 0; i < vr.size(); i++) {
 | 
				
			||||||
 | 
					          cp = SearchHelper.countWords(editor, vr.getStartOffsets()[i], vr.getEndOffsets()[i] - 1);
 | 
				
			||||||
 | 
					          word += cp.getCount();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else {
 | 
				
			||||||
 | 
					        LogicalPosition slp = editor.offsetToLogicalPosition(vr.getStartOffset());
 | 
				
			||||||
 | 
					        LogicalPosition elp = editor.offsetToLogicalPosition(vr.getEndOffset());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        lines = elp.line - slp.line + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cp = SearchHelper.countWords(editor, vr.getStartOffset(), vr.getEndOffset() - 1);
 | 
				
			||||||
 | 
					        word = cp.getCount();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      int total = new IjVimEditor(editor).lineCount();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      msg.append(lines).append(" of ").append(total).append(" Lines");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      msg.append("; ").append(word).append(" of ").append(words).append(" Words");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      int chars = vr.getSelectionCount();
 | 
				
			||||||
 | 
					      int size = EditorHelperRt.getFileSize(editor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      msg.append("; ").append(chars).append(" of ").append(size).append(" Characters");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    VimPlugin.showMessage(msg.toString());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void displayFileInfo(@NotNull VimEditor vimEditor, boolean fullPath) {
 | 
				
			||||||
 | 
					    Editor editor = ((IjVimEditor)vimEditor).getEditor();
 | 
				
			||||||
 | 
					    StringBuilder msg = new StringBuilder();
 | 
				
			||||||
 | 
					    VirtualFile vf = EditorHelper.getVirtualFile(editor);
 | 
				
			||||||
 | 
					    if (vf != null) {
 | 
				
			||||||
 | 
					      msg.append('"');
 | 
				
			||||||
 | 
					      if (fullPath) {
 | 
				
			||||||
 | 
					        msg.append(vf.getPath());
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else {
 | 
				
			||||||
 | 
					        Project project = editor.getProject();
 | 
				
			||||||
 | 
					        if (project != null) {
 | 
				
			||||||
 | 
					          VirtualFile root = ProjectRootManager.getInstance(project).getFileIndex().getContentRootForFile(vf);
 | 
				
			||||||
 | 
					          if (root != null) {
 | 
				
			||||||
 | 
					            msg.append(vf.getPath().substring(root.getPath().length() + 1));
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          else {
 | 
				
			||||||
 | 
					            msg.append(vf.getPath());
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      msg.append("\" ");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					      msg.append("\"[No File]\" ");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Document doc = editor.getDocument();
 | 
				
			||||||
 | 
					    if (!doc.isWritable()) {
 | 
				
			||||||
 | 
					      msg.append("[RO] ");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (FileDocumentManager.getInstance().isDocumentUnsaved(doc)) {
 | 
				
			||||||
 | 
					      msg.append("[+] ");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int lline = editor.getCaretModel().getLogicalPosition().line;
 | 
				
			||||||
 | 
					    int total = new IjVimEditor(editor).lineCount();
 | 
				
			||||||
 | 
					    int pct = (int)((float)lline / (float)total * 100f + 0.5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg.append("line ").append(lline + 1).append(" of ").append(total);
 | 
				
			||||||
 | 
					    msg.append(" --").append(pct).append("%-- ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LogicalPosition lp = editor.getCaretModel().getLogicalPosition();
 | 
				
			||||||
 | 
					    int col = editor.getCaretModel().getOffset() - doc.getLineStartOffset(lline);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg.append("col ").append(col + 1);
 | 
				
			||||||
 | 
					    if (col != lp.column) {
 | 
				
			||||||
 | 
					      msg.append("-").append(lp.column + 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    VimPlugin.showMessage(msg.toString());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static final @NotNull Logger logger = Logger.getInstance(FileGroup.class.getName());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Respond to editor tab selection and remember the last used tab
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public static void fileEditorManagerSelectionChangedCallback(@NotNull FileEditorManagerEvent event) {
 | 
				
			||||||
 | 
					    if (event.getOldFile() != null) {
 | 
				
			||||||
 | 
					      LastTabService.getInstance(event.getManager().getProject()).setLastTab(event.getOldFile());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Nullable
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public VimEditor selectEditor(@NotNull String projectId, @NotNull String documentPath, @Nullable String protocol) {
 | 
				
			||||||
 | 
					    VirtualFileSystem fileSystem = VirtualFileManager.getInstance().getFileSystem(protocol);
 | 
				
			||||||
 | 
					    if (fileSystem == null) return null;
 | 
				
			||||||
 | 
					    VirtualFile virtualFile = fileSystem.findFileByPath(documentPath);
 | 
				
			||||||
 | 
					    if (virtualFile == null) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Project project = Arrays.stream(ProjectManager.getInstance().getOpenProjects())
 | 
				
			||||||
 | 
					      .filter(p -> injector.getFile().getProjectId(p).equals(projectId))
 | 
				
			||||||
 | 
					      .findFirst().orElseThrow();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Editor editor = selectEditor(project, virtualFile);
 | 
				
			||||||
 | 
					    if (editor == null) return null;
 | 
				
			||||||
 | 
					    return new IjVimEditor(editor);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @NotNull
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public String getProjectId(@NotNull Object project) {
 | 
				
			||||||
 | 
					    if (!(project instanceof Project)) throw new IllegalArgumentException();
 | 
				
			||||||
 | 
					    return ((Project) project).getName();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,443 +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.group
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.intellij.openapi.actionSystem.DataContext
 | 
					 | 
				
			||||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
 | 
					 | 
				
			||||||
import com.intellij.openapi.application.ApplicationManager
 | 
					 | 
				
			||||||
import com.intellij.openapi.diagnostic.Logger
 | 
					 | 
				
			||||||
import com.intellij.openapi.editor.Editor
 | 
					 | 
				
			||||||
import com.intellij.openapi.fileEditor.FileDocumentManager
 | 
					 | 
				
			||||||
import com.intellij.openapi.fileEditor.FileEditorManager
 | 
					 | 
				
			||||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
 | 
					 | 
				
			||||||
import com.intellij.openapi.fileEditor.TextEditor
 | 
					 | 
				
			||||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
 | 
					 | 
				
			||||||
import com.intellij.openapi.fileEditor.impl.EditorsSplitters
 | 
					 | 
				
			||||||
import com.intellij.openapi.fileTypes.FileTypeManager
 | 
					 | 
				
			||||||
import com.intellij.openapi.project.Project
 | 
					 | 
				
			||||||
import com.intellij.openapi.project.ProjectManager
 | 
					 | 
				
			||||||
import com.intellij.openapi.roots.ProjectRootManager
 | 
					 | 
				
			||||||
import com.intellij.openapi.vfs.LocalFileSystem
 | 
					 | 
				
			||||||
import com.intellij.openapi.vfs.VirtualFile
 | 
					 | 
				
			||||||
import com.intellij.openapi.vfs.VirtualFileManager
 | 
					 | 
				
			||||||
import com.intellij.psi.search.FilenameIndex
 | 
					 | 
				
			||||||
import com.intellij.psi.search.ProjectScope
 | 
					 | 
				
			||||||
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.VimFileBase
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.api.injector
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.common.TextRange
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.group.LastTabService.Companion.getInstance
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.helper.EditorHelper
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.helper.MessageHelper.message
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.helper.countWords
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.helper.fileSize
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.execute
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
 | 
					 | 
				
			||||||
import java.io.File
 | 
					 | 
				
			||||||
import java.util.*
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class FileGroup : VimFileBase() {
 | 
					 | 
				
			||||||
  override fun openFile(filename: String, context: ExecutionContext): Boolean {
 | 
					 | 
				
			||||||
    if (logger.isDebugEnabled) {
 | 
					 | 
				
			||||||
      logger.debug("openFile($filename)")
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context)
 | 
					 | 
				
			||||||
      ?: return false // API change - don't merge
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val found = findFile(filename, project)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (found != null) {
 | 
					 | 
				
			||||||
      if (logger.isDebugEnabled) {
 | 
					 | 
				
			||||||
        logger.debug("found file: $found")
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      // Can't open a file unless it has a known file type. The next call will return the known type.
 | 
					 | 
				
			||||||
      // If unknown, IDEA will prompt the user to pick a type.
 | 
					 | 
				
			||||||
      val type = FileTypeManager.getInstance().getKnownFileTypeOrAssociate(found, project)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (type != null) {
 | 
					 | 
				
			||||||
        val fem = FileEditorManager.getInstance(project)
 | 
					 | 
				
			||||||
        fem.openFile(found, true)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return true
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        // There was no type and user didn't pick one. Don't open the file
 | 
					 | 
				
			||||||
        // Return true here because we found the file but the user canceled by not picking a type.
 | 
					 | 
				
			||||||
        return true
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      VimPlugin.showMessage(message("unable.to.find.0", filename))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return false
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  fun findFile(filename: String, project: Project): VirtualFile? {
 | 
					 | 
				
			||||||
    var found: VirtualFile?
 | 
					 | 
				
			||||||
    // Vim supports both ~/ and ~\ (tested on Mac and Windows). On Windows, it supports forward- and back-slashes, but
 | 
					 | 
				
			||||||
    // it only supports forward slash on Unix (tested on Mac)
 | 
					 | 
				
			||||||
    // VFS works with both directory separators (tested on Mac and Windows)
 | 
					 | 
				
			||||||
    if (filename.startsWith("~/") || filename.startsWith("~\\")) {
 | 
					 | 
				
			||||||
      val relativePath = filename.substring(2)
 | 
					 | 
				
			||||||
      val dir = System.getProperty("user.home")
 | 
					 | 
				
			||||||
      if (logger.isDebugEnabled) {
 | 
					 | 
				
			||||||
        logger.debug("home dir file")
 | 
					 | 
				
			||||||
        logger.debug("looking for $relativePath in $dir")
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      found = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(File(dir, relativePath))
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      found = LocalFileSystem.getInstance().findFileByIoFile(File(filename))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (found == null) {
 | 
					 | 
				
			||||||
        found = findByNameInContentRoots(filename, project)
 | 
					 | 
				
			||||||
        if (found == null) {
 | 
					 | 
				
			||||||
          found = findByNameInProject(filename, project)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return found
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private fun findByNameInContentRoots(filename: String, project: Project): VirtualFile? {
 | 
					 | 
				
			||||||
    var found: VirtualFile? = null
 | 
					 | 
				
			||||||
    val prm = ProjectRootManager.getInstance(project)
 | 
					 | 
				
			||||||
    val roots = prm.contentRoots
 | 
					 | 
				
			||||||
    for (i in roots.indices) {
 | 
					 | 
				
			||||||
      if (logger.isDebugEnabled) {
 | 
					 | 
				
			||||||
        logger.debug("root[" + i + "] = " + roots[i].path)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      found = roots[i].findFileByRelativePath(filename)
 | 
					 | 
				
			||||||
      if (found != null) {
 | 
					 | 
				
			||||||
        break
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return found
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Closes the current editor.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  override fun closeFile(editor: VimEditor, context: ExecutionContext) {
 | 
					 | 
				
			||||||
    val project = PlatformDataKeys.PROJECT.getData((context.context as DataContext))
 | 
					 | 
				
			||||||
    if (project != null) {
 | 
					 | 
				
			||||||
      val fileEditorManager = FileEditorManagerEx.getInstanceEx(project)
 | 
					 | 
				
			||||||
      val window = fileEditorManager.currentWindow
 | 
					 | 
				
			||||||
      val virtualFile = fileEditorManager.currentFile
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (virtualFile != null && window != null) {
 | 
					 | 
				
			||||||
        // During the work on VIM-2912 I've changed the close function to this one.
 | 
					 | 
				
			||||||
        //   However, the function with manager seems to work weirdly and it causes VIM-2953
 | 
					 | 
				
			||||||
        //window.getManager().closeFile(virtualFile, true, false);
 | 
					 | 
				
			||||||
        window.closeFile(virtualFile)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Get focus after closing tab
 | 
					 | 
				
			||||||
        window.requestFocus(true)
 | 
					 | 
				
			||||||
        if (!ApplicationManager.getApplication().isUnitTestMode) {
 | 
					 | 
				
			||||||
          // This thing doesn't have an implementation in test mode
 | 
					 | 
				
			||||||
          EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Closes editor.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  override fun closeFile(number: Int, context: ExecutionContext) {
 | 
					 | 
				
			||||||
    val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context) ?: return
 | 
					 | 
				
			||||||
    val fileEditorManager = FileEditorManagerEx.getInstanceEx(project)
 | 
					 | 
				
			||||||
    val window = fileEditorManager.currentWindow
 | 
					 | 
				
			||||||
    val editors = fileEditorManager.openFiles
 | 
					 | 
				
			||||||
    if (window != null) {
 | 
					 | 
				
			||||||
      if (number >= 0 && number < editors.size) {
 | 
					 | 
				
			||||||
        fileEditorManager.closeFile(editors[number], window)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (!ApplicationManager.getApplication().isUnitTestMode) {
 | 
					 | 
				
			||||||
      // This thing doesn't have an implementation in test mode
 | 
					 | 
				
			||||||
      EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Saves specific file in the project.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  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(editor, context)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Saves all files in the project.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  override fun saveFiles(editor: VimEditor, context: ExecutionContext) {
 | 
					 | 
				
			||||||
    injector.nativeActionManager.saveAll.execute(editor, context)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Selects then next or previous editor.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  override fun selectFile(count: Int, context: ExecutionContext): Boolean {
 | 
					 | 
				
			||||||
    var count = count
 | 
					 | 
				
			||||||
    val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context) ?: return false
 | 
					 | 
				
			||||||
    val fem = FileEditorManager.getInstance(project) // API change - don't merge
 | 
					 | 
				
			||||||
    val editors = fem.openFiles
 | 
					 | 
				
			||||||
    if (count == 99) {
 | 
					 | 
				
			||||||
      count = editors.size - 1
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (count < 0 || count >= editors.size) {
 | 
					 | 
				
			||||||
      return false
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fem.openFile(editors[count], true)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return true
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Selects then next or previous editor.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  override fun selectNextFile(count: Int, context: ExecutionContext) {
 | 
					 | 
				
			||||||
    val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context) ?: return
 | 
					 | 
				
			||||||
    val fem = FileEditorManager.getInstance(project) // API change - don't merge
 | 
					 | 
				
			||||||
    val editors = fem.openFiles
 | 
					 | 
				
			||||||
    val current = fem.selectedFiles[0]
 | 
					 | 
				
			||||||
    for (i in editors.indices) {
 | 
					 | 
				
			||||||
      if (editors[i] == current) {
 | 
					 | 
				
			||||||
        val pos = (i + (count % editors.size) + editors.size) % editors.size
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fem.openFile(editors[pos], true)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Selects previous editor tab.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  override fun selectPreviousTab(context: ExecutionContext) {
 | 
					 | 
				
			||||||
    val project = PlatformDataKeys.PROJECT.getData((context.context as DataContext)) ?: return
 | 
					 | 
				
			||||||
    val vf = getInstance(project).lastTab
 | 
					 | 
				
			||||||
    if (vf != null && vf.isValid) {
 | 
					 | 
				
			||||||
      FileEditorManager.getInstance(project).openFile(vf, true)
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      VimPlugin.indicateError()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Returns the previous tab.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  fun getPreviousTab(context: DataContext): VirtualFile? {
 | 
					 | 
				
			||||||
    val project = PlatformDataKeys.PROJECT.getData(context) ?: return null
 | 
					 | 
				
			||||||
    val vf = getInstance(project).lastTab
 | 
					 | 
				
			||||||
    if (vf != null && vf.isValid) {
 | 
					 | 
				
			||||||
      return vf
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return null
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  fun selectEditor(project: Project, file: VirtualFile): Editor? {
 | 
					 | 
				
			||||||
    val fMgr = FileEditorManager.getInstance(project)
 | 
					 | 
				
			||||||
    val feditors = fMgr.openFile(file, true)
 | 
					 | 
				
			||||||
    if (feditors.size > 0) {
 | 
					 | 
				
			||||||
      if (feditors[0] is TextEditor) {
 | 
					 | 
				
			||||||
        val editor = (feditors[0] as TextEditor).editor
 | 
					 | 
				
			||||||
        if (!editor.isDisposed) {
 | 
					 | 
				
			||||||
          return editor
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return null
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  override fun displayLocationInfo(vimEditor: VimEditor) {
 | 
					 | 
				
			||||||
    val editor = (vimEditor as IjVimEditor).editor
 | 
					 | 
				
			||||||
    val msg = StringBuilder()
 | 
					 | 
				
			||||||
    val doc = editor.document
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (injector.vimState.mode !is VISUAL) {
 | 
					 | 
				
			||||||
      val lp = editor.caretModel.logicalPosition
 | 
					 | 
				
			||||||
      val col = editor.caretModel.offset - doc.getLineStartOffset(lp.line)
 | 
					 | 
				
			||||||
      var endoff = doc.getLineEndOffset(lp.line)
 | 
					 | 
				
			||||||
      if (endoff < editor.fileSize && doc.charsSequence[endoff] == '\n') {
 | 
					 | 
				
			||||||
        endoff--
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      val ecol = endoff - doc.getLineStartOffset(lp.line)
 | 
					 | 
				
			||||||
      val elp = editor.offsetToLogicalPosition(endoff)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      msg.append("Col ").append(col + 1)
 | 
					 | 
				
			||||||
      if (col != lp.column) {
 | 
					 | 
				
			||||||
        msg.append("-").append(lp.column + 1)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      msg.append(" of ").append(ecol + 1)
 | 
					 | 
				
			||||||
      if (ecol != elp.column) {
 | 
					 | 
				
			||||||
        msg.append("-").append(elp.column + 1)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      val lline = editor.caretModel.logicalPosition.line
 | 
					 | 
				
			||||||
      val total = IjVimEditor(editor).lineCount()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      msg.append("; Line ").append(lline + 1).append(" of ").append(total)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      val cp = countWords(vimEditor)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      msg.append("; Word ").append(cp.position).append(" of ").append(cp.count)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      val offset = editor.caretModel.offset
 | 
					 | 
				
			||||||
      val size = editor.fileSize
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      msg.append("; Character ").append(offset + 1).append(" of ").append(size)
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      msg.append("Selected ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      val vr = TextRange(
 | 
					 | 
				
			||||||
        editor.selectionModel.blockSelectionStarts,
 | 
					 | 
				
			||||||
        editor.selectionModel.blockSelectionEnds
 | 
					 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
      vr.normalize()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      val lines: Int
 | 
					 | 
				
			||||||
      var cp = countWords(vimEditor)
 | 
					 | 
				
			||||||
      val words = cp.count
 | 
					 | 
				
			||||||
      var word = 0
 | 
					 | 
				
			||||||
      if (vr.isMultiple) {
 | 
					 | 
				
			||||||
        lines = vr.size()
 | 
					 | 
				
			||||||
        val cols = vr.maxLength
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        msg.append(cols).append(" Cols; ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (i in 0 until vr.size()) {
 | 
					 | 
				
			||||||
          cp = countWords(vimEditor, vr.startOffsets[i], (vr.endOffsets[i] - 1).toLong())
 | 
					 | 
				
			||||||
          word += cp.count
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        val slp = editor.offsetToLogicalPosition(vr.startOffset)
 | 
					 | 
				
			||||||
        val elp = editor.offsetToLogicalPosition(vr.endOffset)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        lines = elp.line - slp.line + 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        cp = countWords(vimEditor, vr.startOffset, (vr.endOffset - 1).toLong())
 | 
					 | 
				
			||||||
        word = cp.count
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      val total = IjVimEditor(editor).lineCount()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      msg.append(lines).append(" of ").append(total).append(" Lines")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      msg.append("; ").append(word).append(" of ").append(words).append(" Words")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      val chars = vr.selectionCount
 | 
					 | 
				
			||||||
      val size = editor.fileSize
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      msg.append("; ").append(chars).append(" of ").append(size).append(" Characters")
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    VimPlugin.showMessage(msg.toString())
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  override fun displayFileInfo(vimEditor: VimEditor, fullPath: Boolean) {
 | 
					 | 
				
			||||||
    val editor = (vimEditor as IjVimEditor).editor
 | 
					 | 
				
			||||||
    val msg = StringBuilder()
 | 
					 | 
				
			||||||
    val vf = EditorHelper.getVirtualFile(editor)
 | 
					 | 
				
			||||||
    if (vf != null) {
 | 
					 | 
				
			||||||
      msg.append('"')
 | 
					 | 
				
			||||||
      if (fullPath) {
 | 
					 | 
				
			||||||
        msg.append(vf.path)
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        val project = editor.project
 | 
					 | 
				
			||||||
        if (project != null) {
 | 
					 | 
				
			||||||
          val root = ProjectRootManager.getInstance(project).fileIndex.getContentRootForFile(vf)
 | 
					 | 
				
			||||||
          if (root != null) {
 | 
					 | 
				
			||||||
            msg.append(vf.path.substring(root.path.length + 1))
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            msg.append(vf.path)
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      msg.append("\" ")
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      msg.append("\"[No File]\" ")
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val doc = editor.document
 | 
					 | 
				
			||||||
    if (!doc.isWritable) {
 | 
					 | 
				
			||||||
      msg.append("[RO] ")
 | 
					 | 
				
			||||||
    } else if (FileDocumentManager.getInstance().isDocumentUnsaved(doc)) {
 | 
					 | 
				
			||||||
      msg.append("[+] ")
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val lline = editor.caretModel.logicalPosition.line
 | 
					 | 
				
			||||||
    val total = IjVimEditor(editor).lineCount()
 | 
					 | 
				
			||||||
    val pct = (lline.toFloat() / total.toFloat() * 100f + 0.5).toInt()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    msg.append("line ").append(lline + 1).append(" of ").append(total)
 | 
					 | 
				
			||||||
    msg.append(" --").append(pct).append("%-- ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val lp = editor.caretModel.logicalPosition
 | 
					 | 
				
			||||||
    val col = editor.caretModel.offset - doc.getLineStartOffset(lline)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    msg.append("col ").append(col + 1)
 | 
					 | 
				
			||||||
    if (col != lp.column) {
 | 
					 | 
				
			||||||
      msg.append("-").append(lp.column + 1)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    VimPlugin.showMessage(msg.toString())
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  override fun selectEditor(projectId: String, documentPath: String, protocol: String?): VimEditor? {
 | 
					 | 
				
			||||||
    val fileSystem = VirtualFileManager.getInstance().getFileSystem(protocol) ?: return null
 | 
					 | 
				
			||||||
    val virtualFile = fileSystem.findFileByPath(documentPath) ?: return null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val project = Arrays.stream(ProjectManager.getInstance().openProjects)
 | 
					 | 
				
			||||||
      .filter { p: Project? -> injector.file.getProjectId(p!!) == projectId }
 | 
					 | 
				
			||||||
      .findFirst().orElseThrow()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val editor = selectEditor(project, virtualFile) ?: return null
 | 
					 | 
				
			||||||
    return IjVimEditor(editor)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  override fun getProjectId(project: Any): String {
 | 
					 | 
				
			||||||
    require(project is Project)
 | 
					 | 
				
			||||||
    return project.name + "-" + project.locationHash
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  companion object {
 | 
					 | 
				
			||||||
    private fun findByNameInProject(filename: String, project: Project): VirtualFile? {
 | 
					 | 
				
			||||||
      val projectScope = ProjectScope.getProjectScope(project)
 | 
					 | 
				
			||||||
      val names = FilenameIndex.getVirtualFilesByName(filename, projectScope)
 | 
					 | 
				
			||||||
      if (!names.isEmpty()) {
 | 
					 | 
				
			||||||
        return names.stream().findFirst().get()
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      return null
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private val logger = Logger.getInstance(
 | 
					 | 
				
			||||||
      FileGroup::class.java.name
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Respond to editor tab selection and remember the last used tab
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    fun fileEditorManagerSelectionChangedCallback(event: FileEditorManagerEvent) {
 | 
					 | 
				
			||||||
      if (event.oldFile != null) {
 | 
					 | 
				
			||||||
        getInstance(event.manager.project).lastTab = event.oldFile
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -14,7 +14,9 @@ import com.intellij.openapi.components.State;
 | 
				
			|||||||
import com.intellij.openapi.components.Storage;
 | 
					import com.intellij.openapi.components.Storage;
 | 
				
			||||||
import com.intellij.openapi.diagnostic.Logger;
 | 
					import com.intellij.openapi.diagnostic.Logger;
 | 
				
			||||||
import com.maddyhome.idea.vim.VimPlugin;
 | 
					import com.maddyhome.idea.vim.VimPlugin;
 | 
				
			||||||
import com.maddyhome.idea.vim.history.*;
 | 
					import com.maddyhome.idea.vim.history.HistoryBlock;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.history.HistoryEntry;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.history.VimHistoryBase;
 | 
				
			||||||
import org.jdom.Element;
 | 
					import org.jdom.Element;
 | 
				
			||||||
import org.jetbrains.annotations.NotNull;
 | 
					import org.jetbrains.annotations.NotNull;
 | 
				
			||||||
import org.jetbrains.annotations.Nullable;
 | 
					import org.jetbrains.annotations.Nullable;
 | 
				
			||||||
@@ -33,20 +35,21 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
 | 
				
			|||||||
    logger.debug("saveData");
 | 
					    logger.debug("saveData");
 | 
				
			||||||
    Element hist = new Element("history");
 | 
					    Element hist = new Element("history");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (Type type : getHistories().keySet()) {
 | 
					    saveData(hist, SEARCH);
 | 
				
			||||||
      saveData(hist, type);
 | 
					    saveData(hist, COMMAND);
 | 
				
			||||||
    }
 | 
					    saveData(hist, EXPRESSION);
 | 
				
			||||||
 | 
					    saveData(hist, INPUT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    element.addContent(hist);
 | 
					    element.addContent(hist);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private void saveData(@NotNull Element element, VimHistory.Type type) {
 | 
					  private void saveData(@NotNull Element element, String key) {
 | 
				
			||||||
    final HistoryBlock block = getHistories().get(type);
 | 
					    final HistoryBlock block = getHistories().get(key);
 | 
				
			||||||
    if (block == null) {
 | 
					    if (block == null) {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final Element root = new Element("history-" + typeToKey(type));
 | 
					    final Element root = new Element("history-" + key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (HistoryEntry entry : block.getEntries()) {
 | 
					    for (HistoryEntry entry : block.getEntries()) {
 | 
				
			||||||
      final Element entryElement = new Element("entry");
 | 
					      final Element entryElement = new Element("entry");
 | 
				
			||||||
@@ -64,10 +67,10 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
 | 
				
			|||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (Element child : hist.getChildren()) {
 | 
					    readData(hist, SEARCH);
 | 
				
			||||||
      String key = child.getName().replace("history-", "");
 | 
					    readData(hist, COMMAND);
 | 
				
			||||||
      readData(hist, key);
 | 
					    readData(hist, EXPRESSION);
 | 
				
			||||||
    }
 | 
					    readData(hist, INPUT);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private void readData(@NotNull Element element, String key) {
 | 
					  private void readData(@NotNull Element element, String key) {
 | 
				
			||||||
@@ -77,7 +80,7 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    block = new HistoryBlock();
 | 
					    block = new HistoryBlock();
 | 
				
			||||||
    getHistories().put(getTypeForString(key), block);
 | 
					    getHistories().put(key, block);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final Element root = element.getChild("history-" + key);
 | 
					    final Element root = element.getChild("history-" + key);
 | 
				
			||||||
    if (root != null) {
 | 
					    if (root != null) {
 | 
				
			||||||
@@ -91,25 +94,6 @@ 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
 | 
					  @Nullable
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public Element getState() {
 | 
					  public Element getState() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,22 +19,24 @@ import com.maddyhome.idea.vim.options.OptionAccessScope
 | 
				
			|||||||
 * options
 | 
					 * options
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@Suppress("SpellCheckingInspection")
 | 
					@Suppress("SpellCheckingInspection")
 | 
				
			||||||
open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(scope) {
 | 
					public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(scope) {
 | 
				
			||||||
  var ide: String by optionProperty(IjOptions.ide)
 | 
					  public var ide: String by optionProperty(IjOptions.ide)
 | 
				
			||||||
  var ideamarks: Boolean by optionProperty(IjOptions.ideamarks)
 | 
					  public var ideamarks: Boolean by optionProperty(IjOptions.ideamarks)
 | 
				
			||||||
  var ideastatusicon: String by optionProperty(IjOptions.ideastatusicon)
 | 
					  public var ideastatusicon: String by optionProperty(IjOptions.ideastatusicon)
 | 
				
			||||||
  val ideavimsupport: StringListOptionValue by optionProperty(IjOptions.ideavimsupport)
 | 
					  public val ideavimsupport: StringListOptionValue by optionProperty(IjOptions.ideavimsupport)
 | 
				
			||||||
  var ideawrite: String by optionProperty(IjOptions.ideawrite)
 | 
					  public var ideawrite: String by optionProperty(IjOptions.ideawrite)
 | 
				
			||||||
  val lookupkeys: StringListOptionValue by optionProperty(IjOptions.lookupkeys)
 | 
					  public val lookupkeys: StringListOptionValue by optionProperty(IjOptions.lookupkeys)
 | 
				
			||||||
  var trackactionids: Boolean by optionProperty(IjOptions.trackactionids)
 | 
					  public var trackactionids: Boolean by optionProperty(IjOptions.trackactionids)
 | 
				
			||||||
  var visualdelay: Int by optionProperty(IjOptions.visualdelay)
 | 
					  public var visualdelay: Int by optionProperty(IjOptions.visualdelay)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Temporary options to control work-in-progress behaviour
 | 
					  // Temporary options to control work-in-progress behaviour
 | 
				
			||||||
  var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
 | 
					  public var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
 | 
				
			||||||
  var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
 | 
					  public var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
 | 
				
			||||||
  var oldundo: Boolean by optionProperty(IjOptions.oldundo)
 | 
					  public var exCommandAnnotation: Boolean by optionProperty(IjOptions.exCommandAnnotation)
 | 
				
			||||||
  var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
 | 
					  public var oldundo: Boolean by optionProperty(IjOptions.oldundo)
 | 
				
			||||||
  var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
 | 
					  public var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
 | 
				
			||||||
 | 
					  public var useNewRegex: Boolean by optionProperty(IjOptions.useNewRegex)
 | 
				
			||||||
 | 
					  public var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -42,19 +44,20 @@ open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(sco
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * As a convenience, this class also provides access to the IntelliJ specific global options, via inheritance.
 | 
					 * As a convenience, this class also provides access to the IntelliJ specific global options, via inheritance.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class EffectiveIjOptions(scope: OptionAccessScope.EFFECTIVE): GlobalIjOptions(scope) {
 | 
					public class EffectiveIjOptions(scope: OptionAccessScope.EFFECTIVE): GlobalIjOptions(scope) {
 | 
				
			||||||
  // Vim options that are implemented purely by existing IntelliJ features and not used by vim-engine
 | 
					  // Vim options that are implemented purely by existing IntelliJ features and not used by vim-engine
 | 
				
			||||||
  var breakindent: Boolean by optionProperty(IjOptions.breakindent)
 | 
					  public var breakindent: Boolean by optionProperty(IjOptions.breakindent)
 | 
				
			||||||
  val colorcolumn: StringListOptionValue by optionProperty(IjOptions.colorcolumn)
 | 
					  public val colorcolumn: StringListOptionValue by optionProperty(IjOptions.colorcolumn)
 | 
				
			||||||
  var cursorline: Boolean by optionProperty(IjOptions.cursorline)
 | 
					  public var cursorline: Boolean by optionProperty(IjOptions.cursorline)
 | 
				
			||||||
  var fileformat: String by optionProperty(IjOptions.fileformat)
 | 
					  public var fileformat: String by optionProperty(IjOptions.fileformat)
 | 
				
			||||||
  var list: Boolean by optionProperty(IjOptions.list)
 | 
					  public var list: Boolean by optionProperty(IjOptions.list)
 | 
				
			||||||
  var relativenumber: Boolean by optionProperty(IjOptions.relativenumber)
 | 
					  public var number: Boolean by optionProperty(IjOptions.number)
 | 
				
			||||||
  var textwidth: Int by optionProperty(IjOptions.textwidth)
 | 
					  public var relativenumber: Boolean by optionProperty(IjOptions.relativenumber)
 | 
				
			||||||
  var wrap: Boolean by optionProperty(IjOptions.wrap)
 | 
					  public var textwidth: Int by optionProperty(IjOptions.textwidth)
 | 
				
			||||||
 | 
					  public var wrap: Boolean by optionProperty(IjOptions.wrap)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // IntelliJ specific options
 | 
					  // IntelliJ specific options
 | 
				
			||||||
  var ideacopypreprocess: Boolean by optionProperty(IjOptions.ideacopypreprocess)
 | 
					  public var ideacopypreprocess: Boolean by optionProperty(IjOptions.ideacopypreprocess)
 | 
				
			||||||
  var ideajoin: Boolean by optionProperty(IjOptions.ideajoin)
 | 
					  public var ideajoin: Boolean by optionProperty(IjOptions.ideajoin)
 | 
				
			||||||
  var idearefactormode: String by optionProperty(IjOptions.idearefactormode)
 | 
					  public var idearefactormode: String by optionProperty(IjOptions.idearefactormode)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user