mirror of
				https://github.com/chylex/IntelliJ-IdeaVim.git
				synced 2025-11-03 16:40:12 +01:00 
			
		
		
		
	Compare commits
	
		
			21 Commits
		
	
	
		
			063ed0aa84
			...
			customized
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						8de7a9f741
	
				 | 
					
					
						|||
| 
						
						
							
						
						5977840cef
	
				 | 
					
					
						|||
| 
						
						
							
						
						d0fea229c4
	
				 | 
					
					
						|||
| 
						
						
							
						
						9eeac4a788
	
				 | 
					
					
						|||
| 
						
						
							
						
						8370661b0d
	
				 | 
					
					
						|||
| 
						
						
							
						
						3f88e64abf
	
				 | 
					
					
						|||
| 
						
						
							
						
						28e3a7c894
	
				 | 
					
					
						|||
| 
						
						
							
						
						bc859c65f5
	
				 | 
					
					
						|||
| 
						
						
							
						
						141fc8ed22
	
				 | 
					
					
						|||
| 
						
						
							
						
						dec494639c
	
				 | 
					
					
						|||
| 
						
						
							
						
						d06688033b
	
				 | 
					
					
						|||
| 
						
						
							
						
						98110b9132
	
				 | 
					
					
						|||
| 
						
						
							
						
						a0404b8664
	
				 | 
					
					
						|||
| 
						
						
							
						
						a2a30d545e
	
				 | 
					
					
						|||
| 
						
						
							
						
						4f3bf039c9
	
				 | 
					
					
						|||
| 
						
						
							
						
						1f4b97fa00
	
				 | 
					
					
						|||
| 
						
						
							
						
						61301fdbf5
	
				 | 
					
					
						|||
| 
						
						
							
						
						c980250529
	
				 | 
					
					
						|||
| 
						
						
							
						
						bdb4b02016
	
				 | 
					
					
						|||
| 
						
						
							
						
						62039a00d3
	
				 | 
					
					
						|||
| 
						
						
							
						
						95c66fd4a6
	
				 | 
					
					
						
							
								
								
									
										2
									
								
								.github/workflows/mergePr.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/mergePr.yml
									
									
									
									
										vendored
									
									
								
							@@ -11,7 +11,7 @@ on:
 | 
				
			|||||||
jobs:
 | 
					jobs:
 | 
				
			||||||
  build:
 | 
					  build:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if: false
 | 
					    if: github.event.pull_request.merged == true && github.repository == 'JetBrains/ideavim'
 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								.github/workflows/runUiOctopusTests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/workflows/runUiOctopusTests.yml
									
									
									
									
										vendored
									
									
								
							@@ -9,13 +9,20 @@ jobs:
 | 
				
			|||||||
    runs-on: macos-latest
 | 
					    runs-on: macos-latest
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - uses: actions/checkout@v4
 | 
					      - uses: actions/checkout@v4
 | 
				
			||||||
 | 
					      - name: Apply Patch
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          git apply tests/ui-ij-tests/src/test/kotlin/ui/octopus.patch
 | 
				
			||||||
      - name: Setup Java
 | 
					      - name: Setup Java
 | 
				
			||||||
        uses: actions/setup-java@v4
 | 
					        uses: actions/setup-java@v4
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          distribution: zulu
 | 
					          distribution: zulu
 | 
				
			||||||
          java-version: 17
 | 
					          java-version: 17
 | 
				
			||||||
      - name: Setup FFmpeg
 | 
					      - name: Setup FFmpeg
 | 
				
			||||||
        run: brew install ffmpeg
 | 
					        uses: FedericoCarboni/setup-ffmpeg@v3
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          # Not strictly necessary, but it may prevent rate limit
 | 
				
			||||||
 | 
					          # errors especially on GitHub-hosted macos machines.
 | 
				
			||||||
 | 
					          github-token: ${{ secrets.GITHUB_TOKEN }}
 | 
				
			||||||
      - 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
 | 
				
			||||||
@@ -23,7 +30,7 @@ jobs:
 | 
				
			|||||||
      - name: Run Idea
 | 
					      - name: Run Idea
 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
          mkdir -p build/reports
 | 
					          mkdir -p build/reports
 | 
				
			||||||
          gradle runIdeForUiTests -Doctopus.handler=false > 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:
 | 
				
			||||||
@@ -45,7 +52,6 @@ jobs:
 | 
				
			|||||||
          name: ui-test-fails-report-mac
 | 
					          name: ui-test-fails-report-mac
 | 
				
			||||||
          path: |
 | 
					          path: |
 | 
				
			||||||
            build/reports
 | 
					            build/reports
 | 
				
			||||||
            tests/ui-ij-tests/build/reports
 | 
					 | 
				
			||||||
            sandbox-idea-log
 | 
					            sandbox-idea-log
 | 
				
			||||||
#  build-for-ui-test-linux:
 | 
					#  build-for-ui-test-linux:
 | 
				
			||||||
#    runs-on: ubuntu-latest
 | 
					#    runs-on: ubuntu-latest
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										7
									
								
								.github/workflows/runUiPyTests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.github/workflows/runUiPyTests.yml
									
									
									
									
										vendored
									
									
								
							@@ -18,7 +18,11 @@ jobs:
 | 
				
			|||||||
        with:
 | 
					        with:
 | 
				
			||||||
          python-version: '3.10'
 | 
					          python-version: '3.10'
 | 
				
			||||||
      - name: Setup FFmpeg
 | 
					      - name: Setup FFmpeg
 | 
				
			||||||
        run: brew install ffmpeg
 | 
					        uses: FedericoCarboni/setup-ffmpeg@v3
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          # Not strictly necessary, but it may prevent rate limit
 | 
				
			||||||
 | 
					          # errors especially on GitHub-hosted macos machines.
 | 
				
			||||||
 | 
					          github-token: ${{ secrets.GITHUB_TOKEN }}
 | 
				
			||||||
      - 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
 | 
				
			||||||
@@ -48,5 +52,4 @@ jobs:
 | 
				
			|||||||
          name: ui-test-fails-report-mac
 | 
					          name: ui-test-fails-report-mac
 | 
				
			||||||
          path: |
 | 
					          path: |
 | 
				
			||||||
            build/reports
 | 
					            build/reports
 | 
				
			||||||
            tests/ui-py-tests/build/reports
 | 
					 | 
				
			||||||
            sandbox-idea-log
 | 
					            sandbox-idea-log
 | 
				
			||||||
							
								
								
									
										7
									
								
								.github/workflows/runUiTests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.github/workflows/runUiTests.yml
									
									
									
									
										vendored
									
									
								
							@@ -15,7 +15,11 @@ jobs:
 | 
				
			|||||||
          distribution: zulu
 | 
					          distribution: zulu
 | 
				
			||||||
          java-version: 17
 | 
					          java-version: 17
 | 
				
			||||||
      - name: Setup FFmpeg
 | 
					      - name: Setup FFmpeg
 | 
				
			||||||
        run: brew install ffmpeg
 | 
					        uses: FedericoCarboni/setup-ffmpeg@v3
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          # Not strictly necessary, but it may prevent rate limit
 | 
				
			||||||
 | 
					          # errors especially on GitHub-hosted macos machines.
 | 
				
			||||||
 | 
					          github-token: ${{ secrets.GITHUB_TOKEN }}
 | 
				
			||||||
      - 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
 | 
				
			||||||
@@ -45,7 +49,6 @@ jobs:
 | 
				
			|||||||
          name: ui-test-fails-report-mac
 | 
					          name: ui-test-fails-report-mac
 | 
				
			||||||
          path: |
 | 
					          path: |
 | 
				
			||||||
            build/reports
 | 
					            build/reports
 | 
				
			||||||
            tests/ui-ij-tests/build/reports
 | 
					 | 
				
			||||||
            sandbox-idea-log
 | 
					            sandbox-idea-log
 | 
				
			||||||
#  build-for-ui-test-linux:
 | 
					#  build-for-ui-test-linux:
 | 
				
			||||||
#    runs-on: ubuntu-latest
 | 
					#    runs-on: ubuntu-latest
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										34
									
								
								.github/workflows/updateAffectedRate.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								.github/workflows/updateAffectedRate.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created
 | 
				
			||||||
 | 
					# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# This workflow syncs changes from the docs folder of IdeaVim to the IdeaVim.wiki repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					name: Update Affected Rate field on YouTrack
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  workflow_dispatch:
 | 
				
			||||||
 | 
					  schedule:
 | 
				
			||||||
 | 
					    - cron: '0 8 * * *'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  build:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    if: github.repository == 'JetBrains/ideavim'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - name: Fetch origin repo
 | 
				
			||||||
 | 
					        uses: actions/checkout@v3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Set up JDK 17
 | 
				
			||||||
 | 
					        uses: actions/setup-java@v2
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          java-version: '17'
 | 
				
			||||||
 | 
					          distribution: 'adopt'
 | 
				
			||||||
 | 
					          server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
 | 
				
			||||||
 | 
					          settings-path: ${{ github.workspace }} # location for the settings.xml file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Update YouTrack
 | 
				
			||||||
 | 
					        run: ./gradlew scripts:updateAffectedRates
 | 
				
			||||||
 | 
					        env:
 | 
				
			||||||
 | 
					          YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
 | 
				
			||||||
							
								
								
									
										5
									
								
								.github/workflows/updateChangelog.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/workflows/updateChangelog.yml
									
									
									
									
										vendored
									
									
								
							@@ -7,12 +7,15 @@ on:
 | 
				
			|||||||
  workflow_dispatch:
 | 
					  workflow_dispatch:
 | 
				
			||||||
  schedule:
 | 
					  schedule:
 | 
				
			||||||
    - cron: '0 10 * * *'
 | 
					    - cron: '0 10 * * *'
 | 
				
			||||||
 | 
					# Workflow run on push is disabled to avoid conflicts when merging PR
 | 
				
			||||||
 | 
					#  push:
 | 
				
			||||||
 | 
					#    branches: [ master ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jobs:
 | 
					jobs:
 | 
				
			||||||
  build:
 | 
					  build:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
    if: false
 | 
					    if: github.repository == 'JetBrains/ideavim'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - uses: actions/checkout@v3
 | 
					      - uses: actions/checkout@v3
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								.teamcity/_Self/Project.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.teamcity/_Self/Project.kt
									
									
									
									
										vendored
									
									
								
							@@ -25,7 +25,6 @@ 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("2023.3", "<default>", version = "2023.3"))
 | 
					  buildType(TestingBuildType("2023.3", "<default>", version = "2023.3"))
 | 
				
			||||||
  buildType(TestingBuildType("2024.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)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								.teamcity/_Self/buildTypes/Qodana.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.teamcity/_Self/buildTypes/Qodana.kt
									
									
									
									
										vendored
									
									
								
							@@ -46,8 +46,8 @@ object Qodana : IdeaVimBuildType({
 | 
				
			|||||||
        version = Qodana.JVMVersion.LATEST
 | 
					        version = Qodana.JVMVersion.LATEST
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      reportAsTests = true
 | 
					      reportAsTests = true
 | 
				
			||||||
 | 
					      additionalDockerArguments = "-e QODANA_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJvcmdhbml6YXRpb24iOiIzUFZrQSIsInByb2plY3QiOiIzN1FlQSIsInRva2VuIjoiM0t2bXoifQ.uohp81tM7iAfvvB6k8faarfpV-OjusAaEbWQ8iNrOgs"
 | 
				
			||||||
      additionalQodanaArguments = "--baseline qodana.sarif.json"
 | 
					      additionalQodanaArguments = "--baseline qodana.sarif.json"
 | 
				
			||||||
      cloudToken = "credentialsJSON:6b79412e-9198-4862-9223-c5019488f903"
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -63,6 +63,7 @@ object Qodana : IdeaVimBuildType({
 | 
				
			|||||||
        timezone = "SERVER"
 | 
					        timezone = "SERVER"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      param("dayOfWeek", "Sunday")
 | 
					      param("dayOfWeek", "Sunday")
 | 
				
			||||||
 | 
					      enabled = false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										55
									
								
								.teamcity/_Self/buildTypes/ReleasePlugin.kt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										55
									
								
								.teamcity/_Self/buildTypes/ReleasePlugin.kt
									
									
									
									
										vendored
									
									
								
							@@ -97,14 +97,14 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
 | 
				
			|||||||
      name = "Set TeamCity build number"
 | 
					      name = "Set TeamCity build number"
 | 
				
			||||||
      tasks = "scripts:setTeamCityBuildNumber"
 | 
					      tasks = "scripts:setTeamCityBuildNumber"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
//    gradle {
 | 
					    gradle {
 | 
				
			||||||
//      name = "Update change log"
 | 
					      name = "Update change log"
 | 
				
			||||||
//      tasks = "scripts:changelogUpdateUnreleased"
 | 
					      tasks = "scripts:changelogUpdateUnreleased"
 | 
				
			||||||
//    }
 | 
					    }
 | 
				
			||||||
//    gradle {
 | 
					    gradle {
 | 
				
			||||||
//      name = "Commit preparation changes"
 | 
					      name = "Commit preparation changes"
 | 
				
			||||||
//      tasks = "scripts:commitChanges"
 | 
					      tasks = "scripts:commitChanges"
 | 
				
			||||||
//    }
 | 
					    }
 | 
				
			||||||
    gradle {
 | 
					    gradle {
 | 
				
			||||||
      name = "Add release tag"
 | 
					      name = "Add release tag"
 | 
				
			||||||
      tasks = "scripts:addReleaseTag"
 | 
					      tasks = "scripts:addReleaseTag"
 | 
				
			||||||
@@ -117,24 +117,33 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
 | 
				
			|||||||
      name = "Publish release"
 | 
					      name = "Publish release"
 | 
				
			||||||
      tasks = "publishPlugin"
 | 
					      tasks = "publishPlugin"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
//    script {
 | 
					    script {
 | 
				
			||||||
//      name = "Checkout master branch"
 | 
					      name = "Checkout master branch"
 | 
				
			||||||
//      scriptContent = """
 | 
					      scriptContent = """
 | 
				
			||||||
//        echo Checkout master
 | 
					        echo Checkout master
 | 
				
			||||||
//        git checkout master
 | 
					        git checkout master
 | 
				
			||||||
//      """.trimIndent()
 | 
					      """.trimIndent()
 | 
				
			||||||
//    }
 | 
					    }
 | 
				
			||||||
//    gradle {
 | 
					    gradle {
 | 
				
			||||||
//      name = "Update change log in master"
 | 
					      name = "Update change log in master"
 | 
				
			||||||
//      tasks = "scripts:changelogUpdateUnreleased"
 | 
					      tasks = "scripts:changelogUpdateUnreleased"
 | 
				
			||||||
//    }
 | 
					    }
 | 
				
			||||||
//    gradle {
 | 
					    gradle {
 | 
				
			||||||
//      name = "Commit preparation changes in master"
 | 
					      name = "Commit preparation changes in master"
 | 
				
			||||||
//      tasks = "scripts:commitChanges"
 | 
					      tasks = "scripts:commitChanges"
 | 
				
			||||||
//    }
 | 
					    }
 | 
				
			||||||
    script {
 | 
					    script {
 | 
				
			||||||
      name = "Push changes to the repo"
 | 
					      name = "Push changes to the repo"
 | 
				
			||||||
      scriptContent = """
 | 
					      scriptContent = """
 | 
				
			||||||
 | 
					      branch=$(git branch --show-current)  
 | 
				
			||||||
 | 
					      echo Current branch is ${'$'}branch
 | 
				
			||||||
 | 
					      if [ "master" != "${'$'}branch" ];
 | 
				
			||||||
 | 
					      then
 | 
				
			||||||
 | 
					        git checkout master
 | 
				
			||||||
 | 
					      fi
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      git push origin
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
      git checkout release
 | 
					      git checkout release
 | 
				
			||||||
      echo checkout release branch
 | 
					      echo checkout release branch
 | 
				
			||||||
      git branch --set-upstream-to=origin/release release
 | 
					      git branch --set-upstream-to=origin/release release
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,11 @@
 | 
				
			|||||||
package patches.buildTypes
 | 
					package patches.buildTypes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.*
 | 
					import jetbrains.buildServer.configs.kotlin.v2019_2.RelativeId
 | 
				
			||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.GradleBuildStep
 | 
					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.buildSteps.gradle
 | 
				
			||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
 | 
					import jetbrains.buildServer.configs.kotlin.v2019_2.ui.changeBuildType
 | 
				
			||||||
 | 
					import jetbrains.buildServer.configs.kotlin.v2019_2.ui.expectSteps
 | 
				
			||||||
 | 
					import jetbrains.buildServer.configs.kotlin.v2019_2.ui.update
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
This patch script was generated by TeamCity on settings change in UI.
 | 
					This patch script was generated by TeamCity on settings change in UI.
 | 
				
			||||||
@@ -11,18 +13,6 @@ To apply the patch, change the buildType with id = 'IdeaVimTests_Latest_EAP'
 | 
				
			|||||||
accordingly, and delete the patch script.
 | 
					accordingly, and delete the patch script.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
changeBuildType(RelativeId("IdeaVimTests_Latest_EAP")) {
 | 
					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 {
 | 
					    expectSteps {
 | 
				
			||||||
        gradle {
 | 
					        gradle {
 | 
				
			||||||
            tasks = "clean test"
 | 
					            tasks = "clean test"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -495,14 +495,6 @@ Contributors:
 | 
				
			|||||||
  [![icon][github]](https://github.com/emanuelgestosa)
 | 
					  [![icon][github]](https://github.com/emanuelgestosa)
 | 
				
			||||||
   
 | 
					   
 | 
				
			||||||
  Emanuel Gestosa
 | 
					  Emanuel Gestosa
 | 
				
			||||||
* [![icon][mail]](mailto:81118900+lippfi@users.noreply.github.com)
 | 
					 | 
				
			||||||
  [![icon][github]](https://github.com/lippfi)
 | 
					 | 
				
			||||||
   
 | 
					 | 
				
			||||||
  lippfi, 
 | 
					 | 
				
			||||||
* [![icon][mail]](mailto:fillipser143@gmail.com)
 | 
					 | 
				
			||||||
  [![icon][github]](https://github.com/Parker7123)
 | 
					 | 
				
			||||||
   
 | 
					 | 
				
			||||||
  FilipParker
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Previous contributors:
 | 
					Previous contributors:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								CHANGES.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								CHANGES.md
									
									
									
									
									
								
							@@ -23,19 +23,13 @@ It is important to distinguish EAP from traditional pre-release software.
 | 
				
			|||||||
Please note that the quality of EAP versions may at times be way below even
 | 
					Please note that the quality of EAP versions may at times be way below even
 | 
				
			||||||
usual beta standards.
 | 
					usual beta standards.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## End of changelog file maintenance
 | 
					## To Be Released
 | 
				
			||||||
 | 
					 | 
				
			||||||
Since version 2.9.0, the changelog can be found on YouTrack
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
To Be Released: https://youtrack.jetbrains.com/issues/VIM?q=%23%7BReady%20To%20Release%7D%20
 | 
					 | 
				
			||||||
Latest Fixes: https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20sort%20by:%20updated%20
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## 2.9.0, 2024-02-20
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Fixes:
 | 
					### Fixes:
 | 
				
			||||||
* [VIM-3055](https://youtrack.jetbrains.com/issue/VIM-3055) Fix the issue with double deleting after dot
 | 
					* [VIM-3055](https://youtrack.jetbrains.com/issue/VIM-3055) Fix the issue with double deleting after dot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Merged PRs:
 | 
					### Merged PRs:
 | 
				
			||||||
 | 
					* [725](https://github.com/JetBrains/ideavim/pull/725) by [Emanuel Gestosa](https://github.com/emanuelgestosa): Regex
 | 
				
			||||||
* [805](https://github.com/JetBrains/ideavim/pull/805) by [chylex](https://github.com/chylex): VIM-3238 Fix recording a macro that replays another macro
 | 
					* [805](https://github.com/JetBrains/ideavim/pull/805) by [chylex](https://github.com/chylex): VIM-3238 Fix recording a macro that replays another macro
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 2.8.0, 2024-01-30
 | 
					## 2.8.0, 2024-01-30
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ repositories {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
  compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.23-1.0.20")
 | 
					  compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.22-1.0.17")
 | 
				
			||||||
  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")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,6 +32,7 @@ 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.changelog.exceptions.MissingVersionException
 | 
				
			||||||
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
 | 
				
			||||||
@@ -48,14 +49,14 @@ buildscript {
 | 
				
			|||||||
    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:6.9.0.202403050737-r")
 | 
					    classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.8.0.202311291450-r")
 | 
				
			||||||
    classpath("org.kohsuke:github-api:1.305")
 | 
					    classpath("org.kohsuke:github-api:1.305")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    classpath("io.ktor:ktor-client-core:2.3.10")
 | 
					    classpath("io.ktor:ktor-client-core:2.3.7")
 | 
				
			||||||
    classpath("io.ktor:ktor-client-cio:2.3.10")
 | 
					    classpath("io.ktor:ktor-client-cio:2.3.7")
 | 
				
			||||||
    classpath("io.ktor:ktor-client-auth:2.3.10")
 | 
					    classpath("io.ktor:ktor-client-auth:2.3.7")
 | 
				
			||||||
    classpath("io.ktor:ktor-client-content-negotiation:2.3.10")
 | 
					    classpath("io.ktor:ktor-client-content-negotiation:2.3.7")
 | 
				
			||||||
    classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.10")
 | 
					    classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 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")
 | 
				
			||||||
@@ -69,11 +70,11 @@ plugins {
 | 
				
			|||||||
  application
 | 
					  application
 | 
				
			||||||
  id("java-test-fixtures")
 | 
					  id("java-test-fixtures")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  id("org.jetbrains.intellij") version "1.17.3"
 | 
					  id("org.jetbrains.intellij") version "1.17.1"
 | 
				
			||||||
  id("org.jetbrains.changelog") version "2.2.0"
 | 
					  id("org.jetbrains.changelog") version "2.2.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  id("org.jetbrains.kotlinx.kover") version "0.6.1"
 | 
					  id("org.jetbrains.kotlinx.kover") version "0.6.1"
 | 
				
			||||||
  id("com.dorongold.task-tree") version "3.0.0"
 | 
					  id("com.dorongold.task-tree") version "2.1.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  id("com.google.devtools.ksp") version "1.9.22-1.0.17"
 | 
					  id("com.google.devtools.ksp") version "1.9.22-1.0.17"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -141,14 +142,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.3.1")
 | 
					  testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
 | 
					  testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1")
 | 
				
			||||||
  testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
 | 
					  testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.1")
 | 
				
			||||||
  testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
 | 
					  testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.1")
 | 
				
			||||||
  testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
 | 
					  testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1")
 | 
				
			||||||
  testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
 | 
					  testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.1")
 | 
				
			||||||
  testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
 | 
					  testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.1")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
configurations {
 | 
					configurations {
 | 
				
			||||||
@@ -206,11 +207,6 @@ tasks {
 | 
				
			|||||||
    systemProperty("jb.privacy.policy.text", "<!--999.999-->")
 | 
					    systemProperty("jb.privacy.policy.text", "<!--999.999-->")
 | 
				
			||||||
    systemProperty("jb.consents.confirmation.enabled", "false")
 | 
					    systemProperty("jb.consents.confirmation.enabled", "false")
 | 
				
			||||||
    systemProperty("ide.show.tips.on.startup.default.value", "false")
 | 
					    systemProperty("ide.show.tips.on.startup.default.value", "false")
 | 
				
			||||||
    systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  runIde {
 | 
					 | 
				
			||||||
    systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -264,6 +260,7 @@ tasks {
 | 
				
			|||||||
  runPluginVerifier {
 | 
					  runPluginVerifier {
 | 
				
			||||||
    downloadDir.set("${project.buildDir}/pluginVerifier/ides")
 | 
					    downloadDir.set("${project.buildDir}/pluginVerifier/ides")
 | 
				
			||||||
    teamCityOutputFormat.set(true)
 | 
					    teamCityOutputFormat.set(true)
 | 
				
			||||||
 | 
					//        ideVersions.set(listOf("IC-2021.3.4"))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  generateGrammarSource {
 | 
					  generateGrammarSource {
 | 
				
			||||||
@@ -308,14 +305,26 @@ tasks {
 | 
				
			|||||||
    from(createOpenApiSourceJar) { into("lib/src") }
 | 
					    from(createOpenApiSourceJar) { into("lib/src") }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  patchPluginXml {
 | 
					    val pluginVersion = version
 | 
				
			||||||
    // Don't forget to update plugin.xml
 | 
					    // Don't forget to update plugin.xml
 | 
				
			||||||
    sinceBuild.set("233.11799.67")
 | 
					    patchPluginXml {
 | 
				
			||||||
 | 
					        // Get the latest available change notes from the changelog file
 | 
				
			||||||
    changeNotes.set(
 | 
					        changeNotes.set(
 | 
				
			||||||
      """<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>"""
 | 
					            provider {
 | 
				
			||||||
    )
 | 
					                with(changelog) {
 | 
				
			||||||
  }
 | 
					                    val log = try {
 | 
				
			||||||
 | 
					                        getUnreleased()
 | 
				
			||||||
 | 
					                    } catch (e: MissingVersionException) {
 | 
				
			||||||
 | 
					                        getOrNull(pluginVersion.toString()) ?: getLatest()
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    renderItem(
 | 
				
			||||||
 | 
					                        log,
 | 
				
			||||||
 | 
					                        org.jetbrains.changelog.Changelog.OutputType.HTML,
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// --- Tests
 | 
					// --- Tests
 | 
				
			||||||
@@ -420,14 +429,12 @@ val prId: String by project
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
tasks.register("updateMergedPr") {
 | 
					tasks.register("updateMergedPr") {
 | 
				
			||||||
  doLast {
 | 
					  doLast {
 | 
				
			||||||
    val x = changelog.getUnreleased()
 | 
					    if (project.hasProperty("prId")) {
 | 
				
			||||||
    println("x")
 | 
					      println("Got pr id: $prId")
 | 
				
			||||||
//    if (project.hasProperty("prId")) {
 | 
					      updateMergedPr(prId.toInt())
 | 
				
			||||||
//      println("Got pr id: $prId")
 | 
					    } else {
 | 
				
			||||||
//      updateMergedPr(prId.toInt())
 | 
					      error("Cannot get prId")
 | 
				
			||||||
//    } else {
 | 
					    }
 | 
				
			||||||
//      error("Cannot get prId")
 | 
					 | 
				
			||||||
//    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -451,7 +458,7 @@ val fixVersionsElementType = "VersionBundleElement"
 | 
				
			|||||||
tasks.register("releaseActions") {
 | 
					tasks.register("releaseActions") {
 | 
				
			||||||
  group = "other"
 | 
					  group = "other"
 | 
				
			||||||
  doLast {
 | 
					  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")
 | 
				
			||||||
    if (tickets.isNotEmpty()) {
 | 
					    if (tickets.isNotEmpty()) {
 | 
				
			||||||
      println("Updating statuses for tickets: $tickets")
 | 
					      println("Updating statuses for tickets: $tickets")
 | 
				
			||||||
      setYoutrackStatus(tickets, "Fixed")
 | 
					      setYoutrackStatus(tickets, "Fixed")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,13 +8,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# suppress inspection "UnusedProperty" for whole file
 | 
					# suppress inspection "UnusedProperty" for whole file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ideaVersion=LATEST-EAP-SNAPSHOT
 | 
					ideaVersion=2023.3.3
 | 
				
			||||||
ideaVersion=2024.1
 | 
					 | 
				
			||||||
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
 | 
					# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
 | 
				
			||||||
ideaType=IC
 | 
					ideaType=IC
 | 
				
			||||||
downloadIdeaSources=true
 | 
					downloadIdeaSources=true
 | 
				
			||||||
instrumentPluginCode=true
 | 
					instrumentPluginCode=true
 | 
				
			||||||
version=chylex-32
 | 
					version=chylex-27
 | 
				
			||||||
javaVersion=17
 | 
					javaVersion=17
 | 
				
			||||||
remoteRobotVersion=0.11.22
 | 
					remoteRobotVersion=0.11.22
 | 
				
			||||||
antlrVersion=4.10.1
 | 
					antlrVersion=4.10.1
 | 
				
			||||||
@@ -29,7 +28,7 @@ publishChannels=eap
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Kotlinx serialization also uses some version of kotlin stdlib under the hood. However,
 | 
					# Kotlinx serialization also uses some version of kotlin stdlib under the hood. However,
 | 
				
			||||||
#   we exclude this version from the dependency and use our own version of kotlin that is specified above
 | 
					#   we exclude this version from the dependency and use our own version of kotlin that is specified above
 | 
				
			||||||
kotlinxSerializationVersion=1.6.2
 | 
					kotlinxSerializationVersion=1.5.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
slackUrl=
 | 
					slackUrl=
 | 
				
			||||||
youtrackToken=
 | 
					youtrackToken=
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										135390
									
								
								qodana.sarif.json
									
									
									
									
									
								
							
							
						
						
									
										135390
									
								
								qodana.sarif.json
									
									
									
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -21,9 +21,6 @@ exclude:
 | 
				
			|||||||
      - 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/vimscript/parser/generated
 | 
				
			||||||
      - src/main/java/com/maddyhome/idea/vim/package-info.java
 | 
					      - src/main/java/com/maddyhome/idea/vim/package-info.java
 | 
				
			||||||
      - vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
 | 
					 | 
				
			||||||
      - src/main/java/com/maddyhome/idea/vim/group/SearchGroup.java
 | 
					 | 
				
			||||||
      - tests/ui-fixtures
 | 
					 | 
				
			||||||
dependencyIgnores:
 | 
					dependencyIgnores:
 | 
				
			||||||
  - name: "acejump"
 | 
					  - name: "acejump"
 | 
				
			||||||
  - name: "icu4j"
 | 
					  - name: "icu4j"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,17 +20,17 @@ repositories {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
  compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.23")
 | 
					  compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.22")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  implementation("io.ktor:ktor-client-core:2.3.10")
 | 
					  implementation("io.ktor:ktor-client-core:2.3.7")
 | 
				
			||||||
  implementation("io.ktor:ktor-client-cio:2.3.10")
 | 
					  implementation("io.ktor:ktor-client-cio:2.3.7")
 | 
				
			||||||
  implementation("io.ktor:ktor-client-content-negotiation:2.3.10")
 | 
					  implementation("io.ktor:ktor-client-content-negotiation:2.3.7")
 | 
				
			||||||
  implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.10")
 | 
					  implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
 | 
				
			||||||
  implementation("io.ktor:ktor-client-auth:2.3.10")
 | 
					  implementation("io.ktor:ktor-client-auth:2.3.7")
 | 
				
			||||||
  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:6.9.0.202403050737-r")
 | 
					  implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.8.0.202311291450-r")
 | 
				
			||||||
  implementation("com.vdurmont:semver4j:3.1.0")
 | 
					  implementation("com.vdurmont:semver4j:3.1.0")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -58,6 +58,13 @@ tasks.register("checkNewPluginDependencies", JavaExec::class) {
 | 
				
			|||||||
  classpath = sourceSets["main"].runtimeClasspath
 | 
					  classpath = sourceSets["main"].runtimeClasspath
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tasks.register("updateAffectedRates", JavaExec::class) {
 | 
				
			||||||
 | 
					  group = "verification"
 | 
				
			||||||
 | 
					  description = "This job updates Affected Rate field on YouTrack"
 | 
				
			||||||
 | 
					  mainClass.set("scripts.YouTrackUsersAffectedKt")
 | 
				
			||||||
 | 
					  classpath = sourceSets["main"].runtimeClasspath
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tasks.register("calculateNewVersion", JavaExec::class) {
 | 
					tasks.register("calculateNewVersion", JavaExec::class) {
 | 
				
			||||||
  group = "release"
 | 
					  group = "release"
 | 
				
			||||||
  mainClass.set("scripts.release.CalculateNewVersionKt")
 | 
					  mainClass.set("scripts.release.CalculateNewVersionKt")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,7 +22,7 @@ import kotlinx.serialization.json.jsonPrimitive
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Suppress("SpellCheckingInspection")
 | 
					@Suppress("SpellCheckingInspection")
 | 
				
			||||||
val knownPlugins = setOf(
 | 
					val knownPlugins = listOf(
 | 
				
			||||||
  "IdeaVimExtension",
 | 
					  "IdeaVimExtension",
 | 
				
			||||||
  "github.zgqq.intellij-enhance",
 | 
					  "github.zgqq.intellij-enhance",
 | 
				
			||||||
  "org.jetbrains.IdeaVim-EasyMotion",
 | 
					  "org.jetbrains.IdeaVim-EasyMotion",
 | 
				
			||||||
@@ -31,12 +31,7 @@ val knownPlugins = setOf(
 | 
				
			|||||||
  "com.github.copilot",
 | 
					  "com.github.copilot",
 | 
				
			||||||
  "com.github.dankinsoid.multicursor",
 | 
					  "com.github.dankinsoid.multicursor",
 | 
				
			||||||
  "com.joshestein.ideavim-quickscope",
 | 
					  "com.joshestein.ideavim-quickscope",
 | 
				
			||||||
 | 
					 | 
				
			||||||
  "ca.alexgirard.HarpoonIJ",
 | 
					  "ca.alexgirard.HarpoonIJ",
 | 
				
			||||||
  "me.kyren223.harpoonforjb", // https://plugins.jetbrains.com/plugin/23771-harpoonforjb
 | 
					 | 
				
			||||||
  "com.github.erotourtes.harpoon", // https://plugins.jetbrains.com/plugin/21796-harpooner
 | 
					 | 
				
			||||||
  "me.kyren223.trident", // https://plugins.jetbrains.com/plugin/23818-trident
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  "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
 | 
				
			||||||
@@ -47,15 +42,19 @@ suspend fun main() {
 | 
				
			|||||||
    parameter("dependency", "IdeaVIM")
 | 
					    parameter("dependency", "IdeaVIM")
 | 
				
			||||||
    parameter("includeOptional", true)
 | 
					    parameter("includeOptional", true)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  val output = response.body<List<String>>().toSet()
 | 
					  val output = response.body<List<String>>()
 | 
				
			||||||
  println(output)
 | 
					  println(output)
 | 
				
			||||||
  val newPlugins = (output - knownPlugins).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
 | 
					  if (knownPlugins != output) {
 | 
				
			||||||
  if (newPlugins.isNotEmpty()) {
 | 
					    val newPlugins = (output - knownPlugins).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
 | 
				
			||||||
//    val removedPlugins = (knownPlugins - output.toSet()).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
 | 
					    val removedPlugins = (knownPlugins - output.toSet()).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
 | 
				
			||||||
    error(
 | 
					    error(
 | 
				
			||||||
      """
 | 
					      """
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
      Unregistered plugins:
 | 
					      Unregistered plugins:
 | 
				
			||||||
      ${newPlugins.joinToString(separator = "\n") { it.first + "(" + it.second + ")" }}
 | 
					      ${if (newPlugins.isNotEmpty()) newPlugins.joinToString(separator = "\n") { it.first + "(" + it.second + ")" } else "No unregistered plugins"}
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      Removed plugins:
 | 
				
			||||||
 | 
					      ${if (removedPlugins.isNotEmpty()) removedPlugins.joinToString(separator = "\n") { it.first + "(" + it.second + ")" } else "No removed plugins"}
 | 
				
			||||||
    """.trimIndent()
 | 
					    """.trimIndent()
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,12 +8,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package scripts.release
 | 
					package scripts.release
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.eclipse.jgit.lib.ObjectId
 | 
					 | 
				
			||||||
import org.eclipse.jgit.revwalk.RevCommit
 | 
					 | 
				
			||||||
import org.eclipse.jgit.revwalk.RevWalk
 | 
					 | 
				
			||||||
import org.eclipse.jgit.revwalk.filter.RevFilter
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fun main(args: Array<String>) {
 | 
					fun main(args: Array<String>) {
 | 
				
			||||||
  println("HI!")
 | 
					  println("HI!")
 | 
				
			||||||
  val projectDir = args[0]
 | 
					  val projectDir = args[0]
 | 
				
			||||||
@@ -25,12 +19,10 @@ fun main(args: Array<String>) {
 | 
				
			|||||||
  check(branch == "master") {
 | 
					  check(branch == "master") {
 | 
				
			||||||
    "We should be on master branch"
 | 
					    "We should be on master branch"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  val mergeBaseCommit = getMergeBaseWithMaster(projectDir, objectId)
 | 
					 | 
				
			||||||
  println("Base commit $mergeBaseCommit")
 | 
					 | 
				
			||||||
  withGit(projectDir) { git ->
 | 
					  withGit(projectDir) { git ->
 | 
				
			||||||
    val log = git.log().setMaxCount(500).call().toList()
 | 
					    val log = git.log().setMaxCount(500).call().toList()
 | 
				
			||||||
    println("First commit hash in log: " + log.first().name + " log size: ${log.size}")
 | 
					    println("First commit hash in log: " + log.first().name + " log size: ${log.size}")
 | 
				
			||||||
    val logDiff = log.takeWhile { it.id.name != mergeBaseCommit }
 | 
					    val logDiff = log.takeWhile { it.id.name != objectId.name }
 | 
				
			||||||
    val numCommits = logDiff.size
 | 
					    val numCommits = logDiff.size
 | 
				
			||||||
    println("Log diff size is $numCommits")
 | 
					    println("Log diff size is $numCommits")
 | 
				
			||||||
    check(numCommits < 450) {
 | 
					    check(numCommits < 450) {
 | 
				
			||||||
@@ -43,18 +35,3 @@ fun main(args: Array<String>) {
 | 
				
			|||||||
    println("##teamcity[setParameter name='env.ORG_GRADLE_PROJECT_version' value='$nextVersion']")
 | 
					    println("##teamcity[setParameter name='env.ORG_GRADLE_PROJECT_version' value='$nextVersion']")
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
private fun getMergeBaseWithMaster(projectDir: String, tag: ObjectId): String {
 | 
					 | 
				
			||||||
  withRepo(projectDir) { repo ->
 | 
					 | 
				
			||||||
    val master = repo.resolve("master")
 | 
					 | 
				
			||||||
    RevWalk(repo).use { walk ->
 | 
					 | 
				
			||||||
      val tagRevCommit = walk.parseCommit(tag)
 | 
					 | 
				
			||||||
      val masterRevCommit = walk.parseCommit(master)
 | 
					 | 
				
			||||||
      walk.setRevFilter(RevFilter.MERGE_BASE)
 | 
					 | 
				
			||||||
      walk.markStart(tagRevCommit)
 | 
					 | 
				
			||||||
      walk.markStart(masterRevCommit)
 | 
					 | 
				
			||||||
      val mergeBase: RevCommit = walk.next()
 | 
					 | 
				
			||||||
      return mergeBase.name
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										62
									
								
								scripts/src/main/kotlin/scripts/youTrackUsersAffected.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								scripts/src/main/kotlin/scripts/youTrackUsersAffected.kt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 scripts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import io.ktor.client.call.*
 | 
				
			||||||
 | 
					import kotlinx.serialization.json.JsonArray
 | 
				
			||||||
 | 
					import kotlinx.serialization.json.jsonObject
 | 
				
			||||||
 | 
					import kotlinx.serialization.json.jsonPrimitive
 | 
				
			||||||
 | 
					import kotlinx.serialization.json.put
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					val areaWeights = setOf(
 | 
				
			||||||
 | 
					  Triple("118-53212", "Plugins", 50),
 | 
				
			||||||
 | 
					  Triple("118-53220", "Vim Script", 30),
 | 
				
			||||||
 | 
					  Triple("118-54084", "Esc", 100),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend fun updateRates() {
 | 
				
			||||||
 | 
					  println("Updating rates of the issues")
 | 
				
			||||||
 | 
					  areaWeights.forEach { (id, name, weight) ->
 | 
				
			||||||
 | 
					    val unmappedIssues = unmappedIssues(name)
 | 
				
			||||||
 | 
					    println("Got ${unmappedIssues.size} for $name area")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    unmappedIssues.forEach { issueId ->
 | 
				
			||||||
 | 
					      print("Trying to update issue $issueId: ")
 | 
				
			||||||
 | 
					      val response = updateCustomField(issueId) {
 | 
				
			||||||
 | 
					        put("name", "Affected Rate")
 | 
				
			||||||
 | 
					        put("\$type", "SimpleIssueCustomField")
 | 
				
			||||||
 | 
					        put("value", weight)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      println(response)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private suspend fun unmappedIssues(area: String): List<String> {
 | 
				
			||||||
 | 
					  val areaProcessed = if (" " in area) "{$area}" else area
 | 
				
			||||||
 | 
					  val res = issuesQuery(
 | 
				
			||||||
 | 
					    query = "project: VIM Affected Rate: {No affected rate} Area: $areaProcessed #Unresolved",
 | 
				
			||||||
 | 
					    fields = "id,idReadable"
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  return res.body<JsonArray>().map { it.jsonObject }.map { it["idReadable"]!!.jsonPrimitive.content }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend fun getAreasWithoutWeight(): Set<Pair<String, String>> {
 | 
				
			||||||
 | 
					  val allAreas = getAreaValues()
 | 
				
			||||||
 | 
					  return allAreas
 | 
				
			||||||
 | 
					    .filterNot { it.key in areaWeights.map { it.first }.toSet() }
 | 
				
			||||||
 | 
					    .entries
 | 
				
			||||||
 | 
					    .map { it.key to it.value }
 | 
				
			||||||
 | 
					    .toSet()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					suspend fun main() {
 | 
				
			||||||
 | 
					  updateRates()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -11,6 +11,7 @@ package com.maddyhome.idea.vim;
 | 
				
			|||||||
import com.intellij.openapi.Disposable;
 | 
					import com.intellij.openapi.Disposable;
 | 
				
			||||||
import com.intellij.openapi.actionSystem.AnAction;
 | 
					import com.intellij.openapi.actionSystem.AnAction;
 | 
				
			||||||
import com.intellij.openapi.actionSystem.ShortcutSet;
 | 
					import com.intellij.openapi.actionSystem.ShortcutSet;
 | 
				
			||||||
 | 
					import com.intellij.openapi.editor.Document;
 | 
				
			||||||
import com.intellij.openapi.editor.Editor;
 | 
					import com.intellij.openapi.editor.Editor;
 | 
				
			||||||
import com.intellij.openapi.editor.EditorFactory;
 | 
					import com.intellij.openapi.editor.EditorFactory;
 | 
				
			||||||
import com.intellij.openapi.editor.actionSystem.TypedAction;
 | 
					import com.intellij.openapi.editor.actionSystem.TypedAction;
 | 
				
			||||||
@@ -79,6 +80,14 @@ public class EventFacade {
 | 
				
			|||||||
    action.unregisterCustomShortcutSet(component);
 | 
					    action.unregisterCustomShortcutSet(component);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void addDocumentListener(@NotNull Document document, @NotNull DocumentListener listener) {
 | 
				
			||||||
 | 
					    document.addDocumentListener(listener);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void removeDocumentListener(@NotNull Document document, @NotNull DocumentListener listener) {
 | 
				
			||||||
 | 
					    document.removeDocumentListener(listener);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void addEditorFactoryListener(@NotNull EditorFactoryListener listener, @NotNull Disposable parentDisposable) {
 | 
					  public void addEditorFactoryListener(@NotNull EditorFactoryListener listener, @NotNull Disposable parentDisposable) {
 | 
				
			||||||
    EditorFactory.getInstance().addEditorFactoryListener(listener, parentDisposable);
 | 
					    EditorFactory.getInstance().addEditorFactoryListener(listener, parentDisposable);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -89,12 +98,20 @@ public class EventFacade {
 | 
				
			|||||||
    editor.getCaretModel().addCaretListener(listener, disposable);
 | 
					    editor.getCaretModel().addCaretListener(listener, disposable);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void removeCaretListener(@NotNull Editor editor, @NotNull CaretListener listener) {
 | 
				
			||||||
 | 
					    editor.getCaretModel().removeCaretListener(listener);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void addEditorMouseListener(@NotNull Editor editor,
 | 
					  public void addEditorMouseListener(@NotNull Editor editor,
 | 
				
			||||||
                                     @NotNull EditorMouseListener listener,
 | 
					                                     @NotNull EditorMouseListener listener,
 | 
				
			||||||
                                     @NotNull Disposable disposable) {
 | 
					                                     @NotNull Disposable disposable) {
 | 
				
			||||||
    editor.addEditorMouseListener(listener, disposable);
 | 
					    editor.addEditorMouseListener(listener, disposable);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void removeEditorMouseListener(@NotNull Editor editor, @NotNull EditorMouseListener listener) {
 | 
				
			||||||
 | 
					    editor.removeEditorMouseListener(listener);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void addComponentMouseListener(@NotNull Component component,
 | 
					  public void addComponentMouseListener(@NotNull Component component,
 | 
				
			||||||
                                        @NotNull MouseListener mouseListener,
 | 
					                                        @NotNull MouseListener mouseListener,
 | 
				
			||||||
                                        @NotNull Disposable disposable) {
 | 
					                                        @NotNull Disposable disposable) {
 | 
				
			||||||
@@ -102,18 +119,30 @@ public class EventFacade {
 | 
				
			|||||||
    Disposer.register(disposable, () -> component.removeMouseListener(mouseListener));
 | 
					    Disposer.register(disposable, () -> component.removeMouseListener(mouseListener));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void removeComponentMouseListener(@NotNull Component component, @NotNull MouseListener mouseListener) {
 | 
				
			||||||
 | 
					    component.removeMouseListener(mouseListener);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void addEditorMouseMotionListener(@NotNull Editor editor,
 | 
					  public void addEditorMouseMotionListener(@NotNull Editor editor,
 | 
				
			||||||
                                           @NotNull EditorMouseMotionListener listener,
 | 
					                                           @NotNull EditorMouseMotionListener listener,
 | 
				
			||||||
                                           @NotNull Disposable disposable) {
 | 
					                                           @NotNull Disposable disposable) {
 | 
				
			||||||
    editor.addEditorMouseMotionListener(listener, disposable);
 | 
					    editor.addEditorMouseMotionListener(listener, disposable);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void removeEditorMouseMotionListener(@NotNull Editor editor, @NotNull EditorMouseMotionListener listener) {
 | 
				
			||||||
 | 
					    editor.removeEditorMouseMotionListener(listener);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void addEditorSelectionListener(@NotNull Editor editor,
 | 
					  public void addEditorSelectionListener(@NotNull Editor editor,
 | 
				
			||||||
                                         @NotNull SelectionListener listener,
 | 
					                                         @NotNull SelectionListener listener,
 | 
				
			||||||
                                         @NotNull Disposable disposable) {
 | 
					                                         @NotNull Disposable disposable) {
 | 
				
			||||||
    editor.getSelectionModel().addSelectionListener(listener, disposable);
 | 
					    editor.getSelectionModel().addSelectionListener(listener, disposable);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void removeEditorSelectionListener(@NotNull Editor editor, @NotNull SelectionListener listener) {
 | 
				
			||||||
 | 
					    editor.getSelectionModel().removeSelectionListener(listener);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private @NotNull TypedAction getTypedAction() {
 | 
					  private @NotNull TypedAction getTypedAction() {
 | 
				
			||||||
    return TypedAction.getInstance();
 | 
					    return TypedAction.getInstance();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ import com.intellij.openapi.project.ProjectManagerListener
 | 
				
			|||||||
import com.intellij.openapi.startup.ProjectActivity
 | 
					import com.intellij.openapi.startup.ProjectActivity
 | 
				
			||||||
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.helper.localEditors
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
					import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -36,10 +36,8 @@ internal class PluginStartup : ProjectActivity/*, LightEditCompatible*/ {
 | 
				
			|||||||
// 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) {
 | 
				
			||||||
    // TODO: Confirm context in CWM scenario
 | 
					 | 
				
			||||||
    if (injector.globalIjOptions().closenotebooks) {
 | 
					    if (injector.globalIjOptions().closenotebooks) {
 | 
				
			||||||
      injector.editorGroup.getEditors().forEach { vimEditor ->
 | 
					      localEditors().forEach { editor ->
 | 
				
			||||||
        val editor = (vimEditor as IjVimEditor).editor
 | 
					 | 
				
			||||||
        val virtualFile = EditorHelper.getVirtualFile(editor)
 | 
					        val virtualFile = EditorHelper.getVirtualFile(editor)
 | 
				
			||||||
        if (virtualFile?.extension == "ipynb") {
 | 
					        if (virtualFile?.extension == "ipynb") {
 | 
				
			||||||
          val fileEditorManager = FileEditorManagerEx.getInstanceEx(project)
 | 
					          val fileEditorManager = FileEditorManagerEx.getInstanceEx(project)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ public object RegisterActions {
 | 
				
			|||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  public fun registerActions() {
 | 
					  public fun registerActions() {
 | 
				
			||||||
    registerVimCommandActions()
 | 
					    registerVimCommandActions()
 | 
				
			||||||
    registerShortcutsWithoutActions()
 | 
					    registerEmptyShortcuts() // todo most likely it is not needed
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public fun findAction(id: String): EditorActionHandlerBase? {
 | 
					  public fun findAction(id: String): EditorActionHandlerBase? {
 | 
				
			||||||
@@ -46,11 +46,12 @@ public object RegisterActions {
 | 
				
			|||||||
    IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
 | 
					    IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private fun registerShortcutsWithoutActions() {
 | 
					  private fun registerEmptyShortcuts() {
 | 
				
			||||||
    val parser = VimPlugin.getKey()
 | 
					    val parser = VimPlugin.getKey()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // The {char1} <BS> {char2} shortcut is handled directly by KeyHandler#handleKey, so doesn't have an action. But we
 | 
					    // The {char1} <BS> {char2} shortcut is handled directly by KeyHandler#handleKey, so doesn't have an action. But we
 | 
				
			||||||
    // still need to register the shortcut, to make sure the editor doesn't swallow it.
 | 
					    // still need to register the shortcut, to make sure the editor doesn't swallow it.
 | 
				
			||||||
    parser.registerShortcutWithoutAction(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), MappingOwner.IdeaVim.System)
 | 
					    parser
 | 
				
			||||||
 | 
					      .registerShortcutWithoutAction(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), MappingOwner.IdeaVim.System)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -211,22 +211,22 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
 | 
				
			|||||||
  public static void setEnabled(final boolean enabled) {
 | 
					  public static void setEnabled(final boolean enabled) {
 | 
				
			||||||
    if (isEnabled() == enabled) return;
 | 
					    if (isEnabled() == enabled) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!enabled) {
 | 
				
			||||||
 | 
					      getInstance().turnOffPlugin(true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getInstance().enabled = enabled;
 | 
					    getInstance().enabled = enabled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (enabled) {
 | 
				
			||||||
 | 
					      getInstance().turnOnPlugin();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (enabled) {
 | 
					    if (enabled) {
 | 
				
			||||||
      VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOn();
 | 
					      VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOn();
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOff();
 | 
					      VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOff();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!enabled) {
 | 
					 | 
				
			||||||
      getInstance().turnOffPlugin(true);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (enabled) {
 | 
					 | 
				
			||||||
      getInstance().turnOnPlugin();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    StatusBarIconFactory.Util.INSTANCE.updateIcon();
 | 
					    StatusBarIconFactory.Util.INSTANCE.updateIcon();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -353,7 +353,6 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (onOffDisposable != null) {
 | 
					    if (onOffDisposable != null) {
 | 
				
			||||||
      Disposer.dispose(onOffDisposable);
 | 
					      Disposer.dispose(onOffDisposable);
 | 
				
			||||||
      onOffDisposable = null;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ import com.intellij.openapi.components.service
 | 
				
			|||||||
import com.intellij.openapi.project.Project
 | 
					import com.intellij.openapi.project.Project
 | 
				
			||||||
import com.maddyhome.idea.vim.group.EditorHolderService
 | 
					import com.maddyhome.idea.vim.group.EditorHolderService
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Service(Service.Level.PROJECT)
 | 
					@Service
 | 
				
			||||||
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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -77,7 +77,7 @@ public class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActio
 | 
				
			|||||||
      val modifiers = if (charTyped == ' ' && VimKeyListener.isSpaceShift) KeyEvent.SHIFT_DOWN_MASK else 0
 | 
					      val modifiers = if (charTyped == ' ' && VimKeyListener.isSpaceShift) KeyEvent.SHIFT_DOWN_MASK else 0
 | 
				
			||||||
      val keyStroke = KeyStroke.getKeyStroke(charTyped, modifiers)
 | 
					      val keyStroke = KeyStroke.getKeyStroke(charTyped, modifiers)
 | 
				
			||||||
      val startTime = if (traceTime) System.currentTimeMillis() else null
 | 
					      val startTime = if (traceTime) System.currentTimeMillis() else null
 | 
				
			||||||
      handler.handleKey(editor.vim, keyStroke, context.vim, handler.keyHandlerState)
 | 
					      handler.handleKey(editor.vim, keyStroke, injector.executionContextManager.onEditor(editor.vim, context.vim))
 | 
				
			||||||
      if (startTime != null) {
 | 
					      if (startTime != null) {
 | 
				
			||||||
        val duration = System.currentTimeMillis() - startTime
 | 
					        val duration = System.currentTimeMillis() - startTime
 | 
				
			||||||
        LOG.info("VimTypedAction '$charTyped': $duration ms")
 | 
					        LOG.info("VimTypedAction '$charTyped': $duration ms")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,6 +44,7 @@ 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.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
 | 
					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 java.awt.event.InputEvent
 | 
					import java.awt.event.InputEvent
 | 
				
			||||||
import java.awt.event.KeyEvent
 | 
					import java.awt.event.KeyEvent
 | 
				
			||||||
@@ -78,8 +79,11 @@ public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible
 | 
				
			|||||||
      // Should we use HelperKt.getTopLevelEditor(editor) here, as we did in former EditorKeyHandler?
 | 
					      // Should we use HelperKt.getTopLevelEditor(editor) here, as we did in former EditorKeyHandler?
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
        val start = if (traceTime) System.currentTimeMillis() else null
 | 
					        val start = if (traceTime) System.currentTimeMillis() else null
 | 
				
			||||||
        val keyHandler = KeyHandler.getInstance()
 | 
					        KeyHandler.getInstance().handleKey(
 | 
				
			||||||
        keyHandler.handleKey(editor.vim, keyStroke, e.dataContext.vim, keyHandler.keyHandlerState)
 | 
					          editor.vim,
 | 
				
			||||||
 | 
					          keyStroke,
 | 
				
			||||||
 | 
					          injector.executionContextManager.onEditor(editor.vim, e.dataContext.vim),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        if (start != null) {
 | 
					        if (start != null) {
 | 
				
			||||||
          val duration = System.currentTimeMillis() - start
 | 
					          val duration = System.currentTimeMillis() - start
 | 
				
			||||||
          LOG.info("VimShortcut update '$keyStroke': $duration ms")
 | 
					          LOG.info("VimShortcut update '$keyStroke': $duration ms")
 | 
				
			||||||
@@ -376,10 +380,6 @@ private class ActionEnableStatus(
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun toString(): String {
 | 
					 | 
				
			||||||
    return "ActionEnableStatus(isEnabled=$isEnabled, message='$message', logLevel=$logLevel)"
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  companion object {
 | 
					  companion object {
 | 
				
			||||||
    private val LOG = logger<ActionEnableStatus>()
 | 
					    private val LOG = logger<ActionEnableStatus>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,6 @@ import com.maddyhome.idea.vim.VimPlugin
 | 
				
			|||||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
					import com.maddyhome.idea.vim.api.ExecutionContext
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimCaret
 | 
					import com.maddyhome.idea.vim.api.VimCaret
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
					import com.maddyhome.idea.vim.api.VimEditor
 | 
				
			||||||
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.api.setChangeMarks
 | 
					import com.maddyhome.idea.vim.api.setChangeMarks
 | 
				
			||||||
import com.maddyhome.idea.vim.command.Argument
 | 
					import com.maddyhome.idea.vim.command.Argument
 | 
				
			||||||
@@ -22,7 +21,6 @@ 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.common.TextRange
 | 
					import com.maddyhome.idea.vim.common.TextRange
 | 
				
			||||||
import com.maddyhome.idea.vim.common.argumentCaptured
 | 
					import com.maddyhome.idea.vim.common.argumentCaptured
 | 
				
			||||||
import com.maddyhome.idea.vim.ex.ExException
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.group.MotionGroup
 | 
					import com.maddyhome.idea.vim.group.MotionGroup
 | 
				
			||||||
import com.maddyhome.idea.vim.group.visual.VimSelection
 | 
					import com.maddyhome.idea.vim.group.visual.VimSelection
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
 | 
					import com.maddyhome.idea.vim.handler.VimActionHandler
 | 
				
			||||||
@@ -31,67 +29,21 @@ import com.maddyhome.idea.vim.helper.MessageHelper
 | 
				
			|||||||
import com.maddyhome.idea.vim.helper.vimStateMachine
 | 
					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.datatypes.VimFuncref
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.expressions.FunctionCallExpression
 | 
					 | 
				
			||||||
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, selectionType: SelectionType): Boolean {
 | 
					private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, selectionType: SelectionType): Boolean {
 | 
				
			||||||
  val func = injector.globalOptions().operatorfunc
 | 
					  val operatorFunction = injector.keyGroup.operatorFunction
 | 
				
			||||||
  if (func.isEmpty()) {
 | 
					  if (operatorFunction == null) {
 | 
				
			||||||
    VimPlugin.showMessage(MessageHelper.message("E774"))
 | 
					    VimPlugin.showMessage(MessageHelper.message("E774"))
 | 
				
			||||||
    return false
 | 
					    return false
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  val scriptContext = CommandLineVimLContext
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // The option value is either a function name, which should have a handler, or it might be a lambda expression, or a
 | 
					 | 
				
			||||||
  // `function` or `funcref` call expression, all of which will return a funcref (with a handler)
 | 
					 | 
				
			||||||
  var handler = injector.functionService.getFunctionHandlerOrNull(null, func, scriptContext)
 | 
					 | 
				
			||||||
  if (handler == null) {
 | 
					 | 
				
			||||||
    val expression = injector.vimscriptParser.parseExpression(func)
 | 
					 | 
				
			||||||
    if (expression != null) {
 | 
					 | 
				
			||||||
      try {
 | 
					 | 
				
			||||||
        val value = expression.evaluate(editor, context, scriptContext)
 | 
					 | 
				
			||||||
        if (value is VimFuncref) {
 | 
					 | 
				
			||||||
          handler = value.handler
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      } catch (ex: ExException) {
 | 
					 | 
				
			||||||
        // Get the argument for function('...') or funcref('...') for the error message
 | 
					 | 
				
			||||||
        val functionName = if (expression is FunctionCallExpression && expression.arguments.size > 0) {
 | 
					 | 
				
			||||||
          expression.arguments[0].evaluate(editor, context, scriptContext).toString()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else {
 | 
					 | 
				
			||||||
          func
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        VimPlugin.showMessage("E117: Unknown function: $functionName")
 | 
					 | 
				
			||||||
        return false
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (handler == null) {
 | 
					 | 
				
			||||||
    VimPlugin.showMessage("E117: Unknown function: $func")
 | 
					 | 
				
			||||||
    return false
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  val arg = when (selectionType) {
 | 
					 | 
				
			||||||
    SelectionType.LINE_WISE -> "line"
 | 
					 | 
				
			||||||
    SelectionType.CHARACTER_WISE -> "char"
 | 
					 | 
				
			||||||
    SelectionType.BLOCK_WISE -> "block"
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  val saveRepeatHandler = VimRepeater.repeatHandler
 | 
					  val saveRepeatHandler = VimRepeater.repeatHandler
 | 
				
			||||||
  injector.markService.setChangeMarks(editor.primaryCaret(), textRange)
 | 
					  injector.markService.setChangeMarks(editor.primaryCaret(), textRange)
 | 
				
			||||||
  KeyHandler.getInstance().reset(editor)
 | 
					  KeyHandler.getInstance().reset(editor)
 | 
				
			||||||
 | 
					  val result = operatorFunction.apply(editor, context, selectionType)
 | 
				
			||||||
  val arguments = listOf(SimpleExpression(arg))
 | 
					 | 
				
			||||||
  handler.executeFunction(arguments, editor, context, scriptContext)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  VimRepeater.repeatHandler = saveRepeatHandler
 | 
					  VimRepeater.repeatHandler = saveRepeatHandler
 | 
				
			||||||
  return true
 | 
					  return result
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@CommandOrMotion(keys = ["g@"], modes = [Mode.NORMAL])
 | 
					@CommandOrMotion(keys = ["g@"], modes = [Mode.NORMAL])
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,9 +7,9 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
package com.maddyhome.idea.vim.action.change
 | 
					package com.maddyhome.idea.vim.action.change
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.command.CommandProcessor
 | 
					 | 
				
			||||||
import com.intellij.vim.annotations.CommandOrMotion
 | 
					import com.intellij.vim.annotations.CommandOrMotion
 | 
				
			||||||
import com.intellij.vim.annotations.Mode
 | 
					import com.intellij.vim.annotations.Mode
 | 
				
			||||||
 | 
					import com.intellij.openapi.command.CommandProcessor
 | 
				
			||||||
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.VimEditor
 | 
					import com.maddyhome.idea.vim.api.VimEditor
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,7 +34,7 @@ public class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecu
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    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.point }.forEach { caret ->
 | 
				
			||||||
      if (!injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, true, operatorArguments)) {
 | 
					      if (!injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, true, operatorArguments)) {
 | 
				
			||||||
        res = false
 | 
					        res = false
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,7 +39,7 @@ public class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExe
 | 
				
			|||||||
      return true
 | 
					      return true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    var res = true
 | 
					    var res = true
 | 
				
			||||||
    editor.nativeCarets().sortedByDescending { it.offset }.forEach { caret ->
 | 
					    editor.nativeCarets().sortedByDescending { it.offset.point }.forEach { caret ->
 | 
				
			||||||
      if (!caret.isValid) return@forEach
 | 
					      if (!caret.isValid) return@forEach
 | 
				
			||||||
      val range = caretsAndSelections[caret] ?: return@forEach
 | 
					      val range = caretsAndSelections[caret] ?: return@forEach
 | 
				
			||||||
      if (!injector.changeGroup.deleteJoinRange(
 | 
					      if (!injector.changeGroup.deleteJoinRange(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,7 +39,7 @@ public class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.Sin
 | 
				
			|||||||
      return true
 | 
					      return true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    var res = true
 | 
					    var res = true
 | 
				
			||||||
    editor.carets().sortedByDescending { it.offset }.forEach { caret ->
 | 
					    editor.carets().sortedByDescending { it.offset.point }.forEach { caret ->
 | 
				
			||||||
      if (!caret.isValid) return@forEach
 | 
					      if (!caret.isValid) return@forEach
 | 
				
			||||||
      val range = caretsAndSelections[caret] ?: return@forEach
 | 
					      val range = caretsAndSelections[caret] ?: return@forEach
 | 
				
			||||||
      if (!injector.changeGroup.deleteJoinRange(
 | 
					      if (!injector.changeGroup.deleteJoinRange(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,19 +21,19 @@ import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
				
			|||||||
public class CommandState(private val machine: VimStateMachine) {
 | 
					public class CommandState(private val machine: VimStateMachine) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public val isOperatorPending: Boolean
 | 
					  public val isOperatorPending: Boolean
 | 
				
			||||||
    get() = machine.isOperatorPending(machine.mode)
 | 
					    get() = machine.isOperatorPending
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public val mode: Mode
 | 
					  public val mode: CommandState.Mode
 | 
				
			||||||
    get() {
 | 
					    get() {
 | 
				
			||||||
      val myMode = machine.mode
 | 
					      val myMode = machine.mode
 | 
				
			||||||
      return when (myMode) {
 | 
					      return when (myMode) {
 | 
				
			||||||
        is com.maddyhome.idea.vim.state.mode.Mode.CMD_LINE -> Mode.CMD_LINE
 | 
					        is com.maddyhome.idea.vim.state.mode.Mode.CMD_LINE -> CommandState.Mode.CMD_LINE
 | 
				
			||||||
        com.maddyhome.idea.vim.state.mode.Mode.INSERT -> Mode.INSERT
 | 
					        com.maddyhome.idea.vim.state.mode.Mode.INSERT -> CommandState.Mode.INSERT
 | 
				
			||||||
        is com.maddyhome.idea.vim.state.mode.Mode.NORMAL -> Mode.COMMAND
 | 
					        is com.maddyhome.idea.vim.state.mode.Mode.NORMAL -> CommandState.Mode.COMMAND
 | 
				
			||||||
        is com.maddyhome.idea.vim.state.mode.Mode.OP_PENDING -> Mode.OP_PENDING
 | 
					        is com.maddyhome.idea.vim.state.mode.Mode.OP_PENDING -> CommandState.Mode.OP_PENDING
 | 
				
			||||||
        com.maddyhome.idea.vim.state.mode.Mode.REPLACE -> Mode.REPLACE
 | 
					        com.maddyhome.idea.vim.state.mode.Mode.REPLACE -> CommandState.Mode.REPLACE
 | 
				
			||||||
        is com.maddyhome.idea.vim.state.mode.Mode.SELECT -> Mode.SELECT
 | 
					        is com.maddyhome.idea.vim.state.mode.Mode.SELECT -> CommandState.Mode.SELECT
 | 
				
			||||||
        is com.maddyhome.idea.vim.state.mode.Mode.VISUAL -> Mode.VISUAL
 | 
					        is com.maddyhome.idea.vim.state.mode.Mode.VISUAL -> CommandState.Mode.VISUAL
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,8 @@
 | 
				
			|||||||
package com.maddyhome.idea.vim.common
 | 
					package com.maddyhome.idea.vim.common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.application.options.CodeStyle
 | 
					import com.intellij.application.options.CodeStyle
 | 
				
			||||||
 | 
					import com.intellij.openapi.actionSystem.DataContext
 | 
				
			||||||
 | 
					import com.intellij.openapi.actionSystem.PlatformDataKeys
 | 
				
			||||||
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
 | 
				
			||||||
@@ -37,12 +39,13 @@ internal class IndentConfig private constructor(indentOptions: IndentOptions) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  companion object {
 | 
					  companion object {
 | 
				
			||||||
    @JvmStatic
 | 
					    @JvmStatic
 | 
				
			||||||
    fun create(editor: Editor): IndentConfig {
 | 
					    fun create(editor: Editor, context: DataContext): IndentConfig {
 | 
				
			||||||
      return create(editor, editor.project)
 | 
					      return create(editor, PlatformDataKeys.PROJECT.getData(context))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @JvmStatic
 | 
					    @JvmStatic
 | 
				
			||||||
    fun create(editor: Editor, project: Project?): IndentConfig {
 | 
					    @JvmOverloads
 | 
				
			||||||
 | 
					    fun create(editor: Editor, project: Project? = editor.project): IndentConfig {
 | 
				
			||||||
      val indentOptions = if (project != null) {
 | 
					      val indentOptions = if (project != null) {
 | 
				
			||||||
        CodeStyle.getIndentOptions(project, editor.document)
 | 
					        CodeStyle.getIndentOptions(project, editor.document)
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,6 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
package com.maddyhome.idea.vim.extension
 | 
					package com.maddyhome.idea.vim.extension
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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.components.service
 | 
				
			||||||
import com.intellij.openapi.diagnostic.logger
 | 
					import com.intellij.openapi.diagnostic.logger
 | 
				
			||||||
@@ -15,34 +14,21 @@ import com.intellij.openapi.editor.Editor
 | 
				
			|||||||
import com.maddyhome.idea.vim.KeyHandler
 | 
					import com.maddyhome.idea.vim.KeyHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.VimPlugin
 | 
					import com.maddyhome.idea.vim.VimPlugin
 | 
				
			||||||
import com.maddyhome.idea.vim.action.change.Extension
 | 
					import com.maddyhome.idea.vim.action.change.Extension
 | 
				
			||||||
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.VimCaret
 | 
					import com.maddyhome.idea.vim.api.VimCaret
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.api.injector
 | 
					import com.maddyhome.idea.vim.api.injector
 | 
				
			||||||
import com.maddyhome.idea.vim.command.MappingMode
 | 
					import com.maddyhome.idea.vim.command.MappingMode
 | 
				
			||||||
import com.maddyhome.idea.vim.common.CommandAlias
 | 
					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.helper.CommandLineHelper
 | 
					import com.maddyhome.idea.vim.helper.CommandLineHelper
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.TestInputModel
 | 
					import com.maddyhome.idea.vim.helper.TestInputModel
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.noneOfEnum
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.helper.vimStateMachine
 | 
					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.vimscript.model.Executable
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.VimLContext
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.expressions.Expression
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.expressions.Scope
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.statements.FunctionDeclaration
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.statements.FunctionFlag
 | 
					 | 
				
			||||||
import java.awt.event.KeyEvent
 | 
					import java.awt.event.KeyEvent
 | 
				
			||||||
import java.util.*
 | 
					 | 
				
			||||||
import javax.swing.KeyStroke
 | 
					import javax.swing.KeyStroke
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -134,6 +120,12 @@ public object VimExtensionFacade {
 | 
				
			|||||||
      .setAlias(name, CommandAlias.Call(minimumNumberOfArguments, maximumNumberOfArguments, name, handler))
 | 
					      .setAlias(name, CommandAlias.Call(minimumNumberOfArguments, maximumNumberOfArguments, name, handler))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Sets the value of 'operatorfunc' to be used as the operator function in 'g@'. */
 | 
				
			||||||
 | 
					  @JvmStatic
 | 
				
			||||||
 | 
					  public fun setOperatorFunction(function: OperatorFunction) {
 | 
				
			||||||
 | 
					    VimPlugin.getKey().operatorFunction = function
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Runs normal mode commands similar to ':normal! {commands}'.
 | 
					   * Runs normal mode commands similar to ':normal! {commands}'.
 | 
				
			||||||
   * Mappings doesn't work with this function
 | 
					   * Mappings doesn't work with this function
 | 
				
			||||||
@@ -143,9 +135,8 @@ public object VimExtensionFacade {
 | 
				
			|||||||
   */
 | 
					   */
 | 
				
			||||||
  @JvmStatic
 | 
					  @JvmStatic
 | 
				
			||||||
  public 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.onEditor(editor.vim)
 | 
				
			||||||
    val keyHandler = KeyHandler.getInstance()
 | 
					    keys.forEach { KeyHandler.getInstance().handleKey(editor.vim, it, context, false, false) }
 | 
				
			||||||
    keys.forEach { keyHandler.handleKey(editor.vim, it, context, false, false, keyHandler.keyHandlerState) }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Returns a single key stroke from the user input similar to 'getchar()'. */
 | 
					  /** Returns a single key stroke from the user input similar to 'getchar()'. */
 | 
				
			||||||
@@ -161,7 +152,7 @@ public object VimExtensionFacade {
 | 
				
			|||||||
      LOG.trace("Unit test mode is active")
 | 
					      LOG.trace("Unit test mode is active")
 | 
				
			||||||
      val mappingStack = KeyHandler.getInstance().keyStack
 | 
					      val mappingStack = KeyHandler.getInstance().keyStack
 | 
				
			||||||
      mappingStack.feedSomeStroke() ?: TestInputModel.getInstance(editor).nextKeyStroke()?.also {
 | 
					      mappingStack.feedSomeStroke() ?: TestInputModel.getInstance(editor).nextKeyStroke()?.also {
 | 
				
			||||||
        if (injector.registerGroup.isRecording) {
 | 
					        if (editor.vim.vimStateMachine.isRecording) {
 | 
				
			||||||
          KeyHandler.getInstance().modalEntryKeys += it
 | 
					          KeyHandler.getInstance().modalEntryKeys += it
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -182,8 +173,8 @@ public 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
 | 
				
			||||||
  public fun inputString(editor: Editor, context: DataContext, prompt: String, finishOn: Char?): String {
 | 
					  public fun inputString(editor: Editor, prompt: String, finishOn: Char?): String {
 | 
				
			||||||
    return service<CommandLineHelper>().inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
 | 
					    return service<CommandLineHelper>().inputString(editor.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()'. */
 | 
				
			||||||
@@ -216,65 +207,4 @@ public object VimExtensionFacade {
 | 
				
			|||||||
  public 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
 | 
					 | 
				
			||||||
  public fun exportScriptFunction(
 | 
					 | 
				
			||||||
    scope: Scope?,
 | 
					 | 
				
			||||||
    name: String,
 | 
					 | 
				
			||||||
    args: List<String>,
 | 
					 | 
				
			||||||
    defaultArgs: List<Pair<String, Expression>>,
 | 
					 | 
				
			||||||
    hasOptionalArguments: Boolean,
 | 
					 | 
				
			||||||
    flags: EnumSet<FunctionFlag>,
 | 
					 | 
				
			||||||
    function: ScriptFunction
 | 
					 | 
				
			||||||
  ) {
 | 
					 | 
				
			||||||
    var functionDeclaration: FunctionDeclaration? = null
 | 
					 | 
				
			||||||
    val body = listOf(object : Executable {
 | 
					 | 
				
			||||||
      // This context is set to the function declaration during initialisation and then set to the function execution
 | 
					 | 
				
			||||||
      // context during execution
 | 
					 | 
				
			||||||
      override lateinit var vimContext: VimLContext
 | 
					 | 
				
			||||||
      override var rangeInScript: TextRange = TextRange(0, 0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      override fun execute(editor: VimEditor, context: ExecutionContext): ExecutionResult {
 | 
					 | 
				
			||||||
        return function.execute(editor, context, functionDeclaration!!.functionVariables)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    functionDeclaration = FunctionDeclaration(
 | 
					 | 
				
			||||||
      scope,
 | 
					 | 
				
			||||||
      name,
 | 
					 | 
				
			||||||
      args,
 | 
					 | 
				
			||||||
      defaultArgs,
 | 
					 | 
				
			||||||
      body,
 | 
					 | 
				
			||||||
      replaceExisting = true,
 | 
					 | 
				
			||||||
      flags,
 | 
					 | 
				
			||||||
      hasOptionalArguments
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    functionDeclaration.rangeInScript = TextRange(0, 0)
 | 
					 | 
				
			||||||
    body.forEach { it.vimContext = functionDeclaration }
 | 
					 | 
				
			||||||
    injector.functionService.storeFunction(functionDeclaration)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
public fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFunction) {
 | 
					 | 
				
			||||||
  exportScriptFunction(null, name, listOf("type"), emptyList(), false, noneOfEnum()) {
 | 
					 | 
				
			||||||
    editor, context, args ->
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val type = args["type"]?.asString()
 | 
					 | 
				
			||||||
    val selectionType = when (type) {
 | 
					 | 
				
			||||||
      "line" -> SelectionType.LINE_WISE
 | 
					 | 
				
			||||||
      "block" -> SelectionType.BLOCK_WISE
 | 
					 | 
				
			||||||
      "char" -> SelectionType.CHARACTER_WISE
 | 
					 | 
				
			||||||
      else -> return@exportScriptFunction ExecutionResult.Error
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (function.apply(editor, context, selectionType)) {
 | 
					 | 
				
			||||||
      ExecutionResult.Success
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    else {
 | 
					 | 
				
			||||||
      ExecutionResult.Error
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public fun interface ScriptFunction {
 | 
					 | 
				
			||||||
  public fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -67,7 +67,7 @@ internal object VimExtensionRegistrar : VimExtensionRegistrator {
 | 
				
			|||||||
    VimPlugin.getOptionGroup().addGlobalOptionChangeListener(option) {
 | 
					    VimPlugin.getOptionGroup().addGlobalOptionChangeListener(option) {
 | 
				
			||||||
      if (injector.optionGroup.getOptionValue(option, OptionAccessScope.GLOBAL(null)).asBoolean()) {
 | 
					      if (injector.optionGroup.getOptionValue(option, OptionAccessScope.GLOBAL(null)).asBoolean()) {
 | 
				
			||||||
        initExtension(extensionBean, name)
 | 
					        initExtension(extensionBean, name)
 | 
				
			||||||
        PluginState.Util.enabledExtensions.add(name)
 | 
					        PluginState.enabledExtensions.add(name)
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        extensionBean.instance.dispose()
 | 
					        extensionBean.instance.dispose()
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -251,7 +251,7 @@ public class VimArgTextObjExtension implements VimExtension {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner);
 | 
					      final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner);
 | 
				
			||||||
      //noinspection DuplicatedCode
 | 
					      //noinspection DuplicatedCode
 | 
				
			||||||
      if (!vimStateMachine.isOperatorPending(editor.getMode())) {
 | 
					      if (!vimStateMachine.isOperatorPending()) {
 | 
				
			||||||
        editor.nativeCarets().forEach((VimCaret caret) -> {
 | 
					        editor.nativeCarets().forEach((VimCaret caret) -> {
 | 
				
			||||||
          final TextRange range = textObjectHandler.getRange(editor, caret, context, count, 0);
 | 
					          final TextRange range = textObjectHandler.getRange(editor, caret, context, count, 0);
 | 
				
			||||||
          if (range != null) {
 | 
					          if (range != null) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,26 +22,26 @@ 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.injector
 | 
					import com.maddyhome.idea.vim.api.injector
 | 
				
			||||||
import com.maddyhome.idea.vim.command.Argument
 | 
					import com.maddyhome.idea.vim.command.Argument
 | 
				
			||||||
import com.maddyhome.idea.vim.command.Command
 | 
					import com.maddyhome.idea.vim.command.Command
 | 
				
			||||||
import com.maddyhome.idea.vim.command.CommandFlags
 | 
					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.state.mode.Mode
 | 
				
			||||||
import com.maddyhome.idea.vim.command.OperatorArguments
 | 
					import com.maddyhome.idea.vim.command.OperatorArguments
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
				
			||||||
import com.maddyhome.idea.vim.command.TextObjectVisualType
 | 
					import com.maddyhome.idea.vim.command.TextObjectVisualType
 | 
				
			||||||
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.ex.ranges.Ranges
 | 
					import com.maddyhome.idea.vim.ex.ranges.Ranges
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.ExtensionHandler
 | 
					import com.maddyhome.idea.vim.extension.ExtensionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtension
 | 
					import com.maddyhome.idea.vim.extension.VimExtension
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.addCommand
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.addCommand
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
 | 
				
			||||||
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.helper.vimStateMachine
 | 
				
			||||||
@@ -49,19 +49,17 @@ 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.SelectionType
 | 
					 | 
				
			||||||
import java.util.*
 | 
					import java.util.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal class CommentaryExtension : VimExtension {
 | 
					internal class CommentaryExtension : VimExtension {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  object Util {
 | 
					  companion object {
 | 
				
			||||||
    fun doCommentary(
 | 
					    fun doCommentary(
 | 
				
			||||||
      editor: VimEditor,
 | 
					      editor: VimEditor,
 | 
				
			||||||
      context: ExecutionContext,
 | 
					      context: ExecutionContext,
 | 
				
			||||||
      range: TextRange,
 | 
					      range: TextRange,
 | 
				
			||||||
      selectionType: SelectionType,
 | 
					      selectionType: SelectionType,
 | 
				
			||||||
      resetCaret: Boolean = true,
 | 
					      resetCaret: Boolean,
 | 
				
			||||||
    ): Boolean {
 | 
					    ): Boolean {
 | 
				
			||||||
      val mode = editor.vimStateMachine.mode
 | 
					      val mode = editor.vimStateMachine.mode
 | 
				
			||||||
      if (mode !is Mode.VISUAL) {
 | 
					      if (mode !is Mode.VISUAL) {
 | 
				
			||||||
@@ -69,7 +67,8 @@ internal class CommentaryExtension : VimExtension {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return runWriteAction {
 | 
					      return runWriteAction {
 | 
				
			||||||
        // Treat block- and character-wise selections as block comments. Fall back if the first action isn't available
 | 
					        // Treat block- and character-wise selections as block comments. Be ready to fall back to if the first action
 | 
				
			||||||
 | 
					        // isn't available
 | 
				
			||||||
        val actions = if (selectionType === SelectionType.LINE_WISE) {
 | 
					        val actions = if (selectionType === SelectionType.LINE_WISE) {
 | 
				
			||||||
          listOf(IdeActions.ACTION_COMMENT_LINE, IdeActions.ACTION_COMMENT_BLOCK)
 | 
					          listOf(IdeActions.ACTION_COMMENT_LINE, IdeActions.ACTION_COMMENT_BLOCK)
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@@ -114,17 +113,12 @@ internal class CommentaryExtension : VimExtension {
 | 
				
			|||||||
      // first non-whitespace character, then the caret is in the right place. If it's inserted at the first column,
 | 
					      // first non-whitespace character, then the caret is in the right place. If it's inserted at the first column,
 | 
				
			||||||
      // then the caret is now in a bit of a weird place. We can't detect this scenario, so we just have to accept
 | 
					      // then the caret is now in a bit of a weird place. We can't detect this scenario, so we just have to accept
 | 
				
			||||||
      // the difference
 | 
					      // the difference
 | 
				
			||||||
      // TODO: If we don't move the caret to the start offset, we should maintain the current logical position
 | 
					 | 
				
			||||||
      if (resetCaret) {
 | 
					      if (resetCaret) {
 | 
				
			||||||
        editor.primaryCaret().moveToOffset(range.startOffset)
 | 
					        editor.primaryCaret().moveToOffset(range.startOffset)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  companion object {
 | 
					 | 
				
			||||||
    private const val OPERATOR_FUNC = "CommentaryOperatorFunc"
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  override fun getName() = "commentary"
 | 
					  override fun getName() = "commentary"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun init() {
 | 
					  override fun init() {
 | 
				
			||||||
@@ -151,16 +145,6 @@ internal class CommentaryExtension : VimExtension {
 | 
				
			|||||||
    putKeyMapping(MappingMode.N, injector.parser.parseKeys("<Plug>(CommentLine)"), owner, plugCommentaryLineKeys, true)
 | 
					    putKeyMapping(MappingMode.N, injector.parser.parseKeys("<Plug>(CommentLine)"), owner, plugCommentaryLineKeys, true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    addCommand("Commentary", CommentaryCommandAliasHandler())
 | 
					    addCommand("Commentary", CommentaryCommandAliasHandler())
 | 
				
			||||||
 | 
					 | 
				
			||||||
    VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, CommentaryOperatorFunction())
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private class CommentaryOperatorFunction : OperatorFunction {
 | 
					 | 
				
			||||||
    // todo make it multicaret
 | 
					 | 
				
			||||||
    override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
 | 
					 | 
				
			||||||
      val range = injector.markService.getChangeMarks(editor.primaryCaret()) ?: return false
 | 
					 | 
				
			||||||
      return Util.doCommentary(editor, context, range, selectionType ?: SelectionType.CHARACTER_WISE, true)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -169,13 +153,19 @@ internal class CommentaryExtension : VimExtension {
 | 
				
			|||||||
   * E.g. handles the `gc` in `gc_`, by setting the operator function, then invoking `g@` to receive the `_` motion to
 | 
					   * E.g. handles the `gc` in `gc_`, by setting the operator function, then invoking `g@` to receive the `_` motion to
 | 
				
			||||||
   * invoke the operator. This object is both the mapping handler and the operator function.
 | 
					   * invoke the operator. This object is both the mapping handler and the operator function.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  private class CommentaryOperatorHandler : ExtensionHandler {
 | 
					  private class CommentaryOperatorHandler : OperatorFunction, ExtensionHandler {
 | 
				
			||||||
    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) {
 | 
				
			||||||
      injector.globalOptions().operatorfunc = OPERATOR_FUNC
 | 
					      setOperatorFunction(this)
 | 
				
			||||||
      executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
 | 
					      executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // todo make it multicaret
 | 
				
			||||||
 | 
					    override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
 | 
				
			||||||
 | 
					      val range = injector.markService.getChangeMarks(editor.primaryCaret()) ?: return false
 | 
				
			||||||
 | 
					      return doCommentary(editor, context, range, selectionType ?: SelectionType.CHARACTER_WISE, true)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private class CommentaryMappingHandler : ExtensionHandler {
 | 
					  private class CommentaryMappingHandler : ExtensionHandler {
 | 
				
			||||||
@@ -249,7 +239,7 @@ internal class CommentaryExtension : VimExtension {
 | 
				
			|||||||
   */
 | 
					   */
 | 
				
			||||||
  private class CommentaryCommandAliasHandler : CommandAliasHandler {
 | 
					  private class CommentaryCommandAliasHandler : CommandAliasHandler {
 | 
				
			||||||
    override fun execute(command: String, ranges: Ranges, editor: VimEditor, context: ExecutionContext) {
 | 
					    override fun execute(command: String, ranges: Ranges, editor: VimEditor, context: ExecutionContext) {
 | 
				
			||||||
      Util.doCommentary(editor, context, ranges.getTextRange(editor, -1), SelectionType.LINE_WISE, false)
 | 
					      doCommentary(editor, context, ranges.getTextRange(editor, -1), SelectionType.LINE_WISE, false)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,22 +19,24 @@ import com.intellij.openapi.util.Key
 | 
				
			|||||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
					import com.maddyhome.idea.vim.api.ExecutionContext
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
					import com.maddyhome.idea.vim.api.VimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.api.getOffset
 | 
					import com.maddyhome.idea.vim.api.getOffset
 | 
				
			||||||
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.api.setChangeMarks
 | 
					import com.maddyhome.idea.vim.api.setChangeMarks
 | 
				
			||||||
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.state.mode.SelectionType
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.selectionType
 | 
				
			||||||
import com.maddyhome.idea.vim.common.TextRange
 | 
					import com.maddyhome.idea.vim.common.TextRange
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.ExtensionHandler
 | 
					import com.maddyhome.idea.vim.extension.ExtensionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtension
 | 
					import com.maddyhome.idea.vim.extension.VimExtension
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.helper.fileSize
 | 
					import com.maddyhome.idea.vim.helper.fileSize
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.mode
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition
 | 
					import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
 | 
					import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
 | 
				
			||||||
import com.maddyhome.idea.vim.key.OperatorFunction
 | 
					import com.maddyhome.idea.vim.key.OperatorFunction
 | 
				
			||||||
@@ -43,8 +45,6 @@ import com.maddyhome.idea.vim.mark.VimMarkConstants
 | 
				
			|||||||
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.SelectionType
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.selectionType
 | 
					 | 
				
			||||||
import org.jetbrains.annotations.NonNls
 | 
					import org.jetbrains.annotations.NonNls
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -72,13 +72,30 @@ internal class VimExchangeExtension : VimExtension {
 | 
				
			|||||||
    putKeyMappingIfMissing(MappingMode.X, injector.parser.parseKeys("X"), owner, injector.parser.parseKeys(EXCHANGE_CMD), true)
 | 
					    putKeyMappingIfMissing(MappingMode.X, injector.parser.parseKeys("X"), owner, injector.parser.parseKeys(EXCHANGE_CMD), true)
 | 
				
			||||||
    putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("cxc"), owner, injector.parser.parseKeys(EXCHANGE_CLEAR_CMD), true)
 | 
					    putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("cxc"), owner, injector.parser.parseKeys(EXCHANGE_CLEAR_CMD), true)
 | 
				
			||||||
    putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("cxx"), owner, injector.parser.parseKeys(EXCHANGE_LINE_CMD), true)
 | 
					    putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("cxx"), owner, injector.parser.parseKeys(EXCHANGE_LINE_CMD), true)
 | 
				
			||||||
 | 
					 | 
				
			||||||
    VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator())
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  object Util {
 | 
					  companion object {
 | 
				
			||||||
 | 
					    @NonNls
 | 
				
			||||||
 | 
					    const val EXCHANGE_CMD = "<Plug>(Exchange)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNls
 | 
				
			||||||
 | 
					    const val EXCHANGE_CLEAR_CMD = "<Plug>(ExchangeClear)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNls
 | 
				
			||||||
 | 
					    const val EXCHANGE_LINE_CMD = "<Plug>(ExchangeLine)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val EXCHANGE_KEY = Key<Exchange>("exchange")
 | 
					    val EXCHANGE_KEY = Key<Exchange>("exchange")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // End mark has always greater of eq offset than start mark
 | 
				
			||||||
 | 
					    class Exchange(val type: SelectionType, val start: Mark, val end: Mark, val text: String) {
 | 
				
			||||||
 | 
					      private var myHighlighter: RangeHighlighter? = null
 | 
				
			||||||
 | 
					      fun setHighlighter(highlighter: RangeHighlighter) {
 | 
				
			||||||
 | 
					        myHighlighter = highlighter
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      fun getHighlighter(): RangeHighlighter? = myHighlighter
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun clearExchange(editor: Editor) {
 | 
					    fun clearExchange(editor: Editor) {
 | 
				
			||||||
      editor.getUserData(EXCHANGE_KEY)?.getHighlighter()?.let {
 | 
					      editor.getUserData(EXCHANGE_KEY)?.getHighlighter()?.let {
 | 
				
			||||||
        editor.markupModel.removeHighlighter(it)
 | 
					        editor.markupModel.removeHighlighter(it)
 | 
				
			||||||
@@ -87,25 +104,18 @@ internal class VimExchangeExtension : VimExtension {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  companion object {
 | 
					 | 
				
			||||||
    @NonNls private const val EXCHANGE_CMD = "<Plug>(Exchange)"
 | 
					 | 
				
			||||||
    @NonNls private const val EXCHANGE_CLEAR_CMD = "<Plug>(ExchangeClear)"
 | 
					 | 
				
			||||||
    @NonNls private const val EXCHANGE_LINE_CMD = "<Plug>(ExchangeLine)"
 | 
					 | 
				
			||||||
    @NonNls private const val OPERATOR_FUNC = "ExchangeOperatorFunc"
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private class ExchangeHandler(private val isLine: Boolean) : ExtensionHandler {
 | 
					  private class ExchangeHandler(private val isLine: Boolean) : ExtensionHandler {
 | 
				
			||||||
    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) {
 | 
				
			||||||
      injector.globalOptions().operatorfunc = OPERATOR_FUNC
 | 
					      setOperatorFunction(Operator(false))
 | 
				
			||||||
      executeNormalWithoutMapping(injector.parser.parseKeys(if (isLine) "g@_" else "g@"), editor.ij)
 | 
					      executeNormalWithoutMapping(injector.parser.parseKeys(if (isLine) "g@_" else "g@"), editor.ij)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private class ExchangeClearHandler : ExtensionHandler {
 | 
					  private class ExchangeClearHandler : ExtensionHandler {
 | 
				
			||||||
    override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
 | 
					    override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
 | 
				
			||||||
      Util.clearExchange(editor.ij)
 | 
					      clearExchange(editor.ij)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -115,12 +125,12 @@ internal class VimExchangeExtension : VimExtension {
 | 
				
			|||||||
        val mode = editor.mode
 | 
					        val mode = editor.mode
 | 
				
			||||||
        // Leave visual mode to create selection marks
 | 
					        // Leave visual mode to create selection marks
 | 
				
			||||||
        executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
 | 
					        executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
 | 
				
			||||||
        Operator(true).apply(editor, context, mode.selectionType ?: SelectionType.CHARACTER_WISE)
 | 
					        Operator(true).apply(editor, context, mode.selectionType ?: CHARACTER_WISE)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private class Operator(private val isVisual: Boolean = false) : OperatorFunction {
 | 
					  private class Operator(private val isVisual: Boolean) : OperatorFunction {
 | 
				
			||||||
    fun Editor.getMarkOffset(mark: Mark) = IjVimEditor(this).getOffset(mark.line, mark.col)
 | 
					    fun Editor.getMarkOffset(mark: Mark) = IjVimEditor(this).getOffset(mark.line, mark.col)
 | 
				
			||||||
    fun SelectionType.getString() = when (this) {
 | 
					    fun SelectionType.getString() = when (this) {
 | 
				
			||||||
      SelectionType.CHARACTER_WISE -> "v"
 | 
					      SelectionType.CHARACTER_WISE -> "v"
 | 
				
			||||||
@@ -138,7 +148,7 @@ internal class VimExchangeExtension : VimExtension {
 | 
				
			|||||||
          else -> HighlighterTargetArea.EXACT_RANGE
 | 
					          else -> HighlighterTargetArea.EXACT_RANGE
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        val isVisualLine = ex.type == SelectionType.LINE_WISE
 | 
					        val isVisualLine = ex.type == SelectionType.LINE_WISE
 | 
				
			||||||
        val endAdj = if (!(isVisualLine) && (hlArea == HighlighterTargetArea.EXACT_RANGE || isVisual)) 1 else 0
 | 
					        val endAdj = if (!(isVisualLine) && (hlArea == HighlighterTargetArea.EXACT_RANGE || (isVisual))) 1 else 0
 | 
				
			||||||
        return ijEditor.markupModel.addRangeHighlighter(
 | 
					        return ijEditor.markupModel.addRangeHighlighter(
 | 
				
			||||||
          ijEditor.getMarkOffset(ex.start),
 | 
					          ijEditor.getMarkOffset(ex.start),
 | 
				
			||||||
          (ijEditor.getMarkOffset(ex.end) + endAdj).coerceAtMost(ijEditor.fileSize),
 | 
					          (ijEditor.getMarkOffset(ex.end) + endAdj).coerceAtMost(ijEditor.fileSize),
 | 
				
			||||||
@@ -148,12 +158,12 @@ internal class VimExchangeExtension : VimExtension {
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      val currentExchange = getExchange(ijEditor, isVisual, selectionType ?: SelectionType.CHARACTER_WISE)
 | 
					      val currentExchange = getExchange(ijEditor, isVisual, selectionType ?: CHARACTER_WISE)
 | 
				
			||||||
      val exchange1 = ijEditor.getUserData(Util.EXCHANGE_KEY)
 | 
					      val exchange1 = ijEditor.getUserData(EXCHANGE_KEY)
 | 
				
			||||||
      if (exchange1 == null) {
 | 
					      if (exchange1 == null) {
 | 
				
			||||||
        val highlighter = highlightExchange(currentExchange)
 | 
					        val highlighter = highlightExchange(currentExchange)
 | 
				
			||||||
        currentExchange.setHighlighter(highlighter)
 | 
					        currentExchange.setHighlighter(highlighter)
 | 
				
			||||||
        ijEditor.putUserData(Util.EXCHANGE_KEY, currentExchange)
 | 
					        ijEditor.putUserData(EXCHANGE_KEY, currentExchange)
 | 
				
			||||||
        return true
 | 
					        return true
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        val cmp = compareExchanges(exchange1, currentExchange)
 | 
					        val cmp = compareExchanges(exchange1, currentExchange)
 | 
				
			||||||
@@ -179,7 +189,7 @@ internal class VimExchangeExtension : VimExtension {
 | 
				
			|||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        exchange(ijEditor, ex1, ex2, reverse, expand)
 | 
					        exchange(ijEditor, ex1, ex2, reverse, expand)
 | 
				
			||||||
        Util.clearExchange(ijEditor)
 | 
					        clearExchange(ijEditor)
 | 
				
			||||||
        return true
 | 
					        return true
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -344,14 +354,4 @@ internal class VimExchangeExtension : VimExtension {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  // End mark has always greater of eq offset than start mark
 | 
					 | 
				
			||||||
  class Exchange(val type: SelectionType, val start: Mark, val end: Mark, val text: String) {
 | 
					 | 
				
			||||||
    private var myHighlighter: RangeHighlighter? = null
 | 
					 | 
				
			||||||
    fun setHighlighter(highlighter: RangeHighlighter) {
 | 
					 | 
				
			||||||
      myHighlighter = highlighter
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun getHighlighter(): RangeHighlighter? = myHighlighter
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -99,7 +99,7 @@ internal class Matchit : VimExtension {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      // 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.
 | 
				
			||||||
      val isInOpPending = commandState.isOperatorPending(editor.mode)
 | 
					      val isInOpPending = commandState.isOperatorPending
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (isInOpPending) {
 | 
					      if (isInOpPending) {
 | 
				
			||||||
        val matchitAction = MatchitAction()
 | 
					        val matchitAction = MatchitAction()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,21 +8,25 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.maddyhome.idea.vim.extension.nerdtree
 | 
					package com.maddyhome.idea.vim.extension.nerdtree
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.intellij.ide.projectView.ProjectView
 | 
				
			||||||
 | 
					import com.intellij.ide.projectView.impl.AbstractProjectViewPane
 | 
				
			||||||
 | 
					import com.intellij.ide.projectView.impl.ProjectViewImpl
 | 
				
			||||||
import com.intellij.openapi.actionSystem.ActionManager
 | 
					import com.intellij.openapi.actionSystem.ActionManager
 | 
				
			||||||
import com.intellij.openapi.actionSystem.ActionUpdateThread
 | 
					import com.intellij.openapi.actionSystem.ActionUpdateThread
 | 
				
			||||||
import com.intellij.openapi.actionSystem.AnActionEvent
 | 
					import com.intellij.openapi.actionSystem.AnActionEvent
 | 
				
			||||||
import com.intellij.openapi.actionSystem.CommonDataKeys
 | 
					import com.intellij.openapi.actionSystem.CommonDataKeys
 | 
				
			||||||
import com.intellij.openapi.actionSystem.PlatformCoreDataKeys
 | 
					 | 
				
			||||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
 | 
					import com.intellij.openapi.actionSystem.PlatformDataKeys
 | 
				
			||||||
import com.intellij.openapi.application.ApplicationManager
 | 
					import com.intellij.openapi.application.ApplicationManager
 | 
				
			||||||
import com.intellij.openapi.diagnostic.logger
 | 
					import com.intellij.openapi.diagnostic.logger
 | 
				
			||||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
 | 
					import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
 | 
				
			||||||
import com.intellij.openapi.project.DumbAwareAction
 | 
					import com.intellij.openapi.project.DumbAwareAction
 | 
				
			||||||
import com.intellij.openapi.ui.getUserData
 | 
					import com.intellij.openapi.project.Project
 | 
				
			||||||
import com.intellij.openapi.ui.putUserData
 | 
					import com.intellij.openapi.project.ProjectManager
 | 
				
			||||||
import com.intellij.openapi.util.Key
 | 
					import com.intellij.openapi.startup.ProjectActivity
 | 
				
			||||||
 | 
					import com.intellij.openapi.wm.ToolWindow
 | 
				
			||||||
import com.intellij.openapi.wm.ToolWindowId
 | 
					import com.intellij.openapi.wm.ToolWindowId
 | 
				
			||||||
import com.intellij.openapi.wm.ex.ToolWindowManagerEx
 | 
					import com.intellij.openapi.wm.ex.ToolWindowManagerEx
 | 
				
			||||||
 | 
					import com.intellij.openapi.wm.ex.ToolWindowManagerListener
 | 
				
			||||||
import com.intellij.ui.KeyStrokeAdapter
 | 
					import com.intellij.ui.KeyStrokeAdapter
 | 
				
			||||||
import com.intellij.ui.TreeExpandCollapse
 | 
					import com.intellij.ui.TreeExpandCollapse
 | 
				
			||||||
import com.intellij.ui.speedSearch.SpeedSearchSupply
 | 
					import com.intellij.ui.speedSearch.SpeedSearchSupply
 | 
				
			||||||
@@ -49,8 +53,6 @@ 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
 | 
				
			||||||
import java.awt.event.KeyEvent
 | 
					import java.awt.event.KeyEvent
 | 
				
			||||||
import javax.swing.JComponent
 | 
					 | 
				
			||||||
import javax.swing.JTree
 | 
					 | 
				
			||||||
import javax.swing.KeyStroke
 | 
					import javax.swing.KeyStroke
 | 
				
			||||||
import javax.swing.SwingConstants
 | 
					import javax.swing.SwingConstants
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -128,14 +130,15 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
    addCommand("NERDTreeFind", IjCommandHandler("SelectInProjectView"))
 | 
					    addCommand("NERDTreeFind", IjCommandHandler("SelectInProjectView"))
 | 
				
			||||||
    addCommand("NERDTreeRefreshRoot", IjCommandHandler("Synchronize"))
 | 
					    addCommand("NERDTreeRefreshRoot", IjCommandHandler("Synchronize"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    synchronized(Util.monitor) {
 | 
					    synchronized(monitor) {
 | 
				
			||||||
      Util.commandsRegistered = true
 | 
					      commandsRegistered = true
 | 
				
			||||||
 | 
					      ProjectManager.getInstance().openProjects.forEach { project -> installDispatcher(project) }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  class IjCommandHandler(private val actionId: String) : CommandAliasHandler {
 | 
					  class IjCommandHandler(private val actionId: String) : CommandAliasHandler {
 | 
				
			||||||
    override fun execute(command: String, ranges: Ranges, editor: VimEditor, context: ExecutionContext) {
 | 
					    override fun execute(command: String, ranges: Ranges, editor: VimEditor, context: ExecutionContext) {
 | 
				
			||||||
      Util.callAction(editor, actionId, context)
 | 
					      callAction(editor, actionId, context)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -146,7 +149,7 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
      if (toolWindow.isVisible) {
 | 
					      if (toolWindow.isVisible) {
 | 
				
			||||||
        toolWindow.hide()
 | 
					        toolWindow.hide()
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        Util.callAction(editor, "ActivateProjectToolWindow", context)
 | 
					        callAction(editor, "ActivateProjectToolWindow", context)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -161,8 +164,39 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  class ProjectViewListener(private val project: Project) : ToolWindowManagerListener {
 | 
				
			||||||
 | 
					    override fun toolWindowShown(toolWindow: ToolWindow) {
 | 
				
			||||||
 | 
					      if (ToolWindowId.PROJECT_VIEW != toolWindow.id) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      val dispatcher = NerdDispatcher.getInstance(project)
 | 
				
			||||||
 | 
					      if (dispatcher.speedSearchListenerInstalled) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // I specify nullability explicitly as we've got a lot of exceptions saying this property is null
 | 
				
			||||||
 | 
					      val currentProjectViewPane: AbstractProjectViewPane? = ProjectView.getInstance(project).currentProjectViewPane
 | 
				
			||||||
 | 
					      val tree = currentProjectViewPane?.tree ?: return
 | 
				
			||||||
 | 
					      val supply = SpeedSearchSupply.getSupply(tree, true) ?: return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // NB: Here might be some issues with concurrency, but it's not really bad, I think
 | 
				
			||||||
 | 
					      dispatcher.speedSearchListenerInstalled = true
 | 
				
			||||||
 | 
					      supply.addChangeListener {
 | 
				
			||||||
 | 
					        dispatcher.waitForSearch = false
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // TODO I'm not sure is this activity runs at all? Should we use [RunOnceUtil] instead?
 | 
				
			||||||
 | 
					  class NerdStartupActivity : ProjectActivity {
 | 
				
			||||||
 | 
					    override suspend fun execute(project: Project) {
 | 
				
			||||||
 | 
					      synchronized(monitor) {
 | 
				
			||||||
 | 
					        if (!commandsRegistered) return
 | 
				
			||||||
 | 
					        installDispatcher(project)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  class NerdDispatcher : DumbAwareAction() {
 | 
					  class NerdDispatcher : DumbAwareAction() {
 | 
				
			||||||
    internal var waitForSearch = false
 | 
					    internal var waitForSearch = false
 | 
				
			||||||
 | 
					    internal var speedSearchListenerInstalled = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun actionPerformed(e: AnActionEvent) {
 | 
					    override fun actionPerformed(e: AnActionEvent) {
 | 
				
			||||||
      var keyStroke = getKeyStroke(e) ?: return
 | 
					      var keyStroke = getKeyStroke(e) ?: return
 | 
				
			||||||
@@ -180,7 +214,7 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
          val action = nextNode.actionHolder
 | 
					          val action = nextNode.actionHolder
 | 
				
			||||||
          when (action) {
 | 
					          when (action) {
 | 
				
			||||||
            is NerdAction.ToIj -> Util.callAction(null, action.name, e.dataContext.vim)
 | 
					            is NerdAction.ToIj -> callAction(null, action.name, e.dataContext.vim)
 | 
				
			||||||
            is NerdAction.Code -> e.project?.let { action.action(it, e.dataContext, e) }
 | 
					            is NerdAction.Code -> e.project?.let { action.action(it, e.dataContext, e) }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -210,6 +244,10 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    companion object {
 | 
					    companion object {
 | 
				
			||||||
 | 
					      fun getInstance(project: Project): NerdDispatcher {
 | 
				
			||||||
 | 
					        return project.getService(NerdDispatcher::class.java)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      private const val ESCAPE_KEY_CODE = 27
 | 
					      private const val ESCAPE_KEY_CODE = 27
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -245,14 +283,19 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
    registerCommand(
 | 
					    registerCommand(
 | 
				
			||||||
      "NERDTreeMapActivateNode",
 | 
					      "NERDTreeMapActivateNode",
 | 
				
			||||||
      "o",
 | 
					      "o",
 | 
				
			||||||
      NerdAction.Code { _, dataContext, e ->
 | 
					      NerdAction.Code { project, dataContext, _ ->
 | 
				
			||||||
        val tree = getTree(e) ?: return@Code
 | 
					        val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        val row = tree.selectionRows?.getOrNull(0) ?: return@Code
 | 
					        val array = CommonDataKeys.NAVIGATABLE_ARRAY.getData(dataContext)?.filter { it.canNavigateToSource() }
 | 
				
			||||||
        if (tree.isExpanded(row)) {
 | 
					        if (array.isNullOrEmpty()) {
 | 
				
			||||||
          tree.collapseRow(row)
 | 
					          val row = tree.selectionRows?.getOrNull(0) ?: return@Code
 | 
				
			||||||
 | 
					          if (tree.isExpanded(row)) {
 | 
				
			||||||
 | 
					            tree.collapseRow(row)
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            tree.expandRow(row)
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          tree.expandRow(row)
 | 
					          array.forEach { it.navigate(true) }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
@@ -313,7 +356,7 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
        currentWindow?.split(SwingConstants.VERTICAL, true, file, true)
 | 
					        currentWindow?.split(SwingConstants.VERTICAL, true, file, true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // FIXME: 22.01.2021 This solution bouncing a bit
 | 
					        // FIXME: 22.01.2021 This solution bouncing a bit
 | 
				
			||||||
        Util.callAction(null, "ActivateProjectToolWindow", context.vim)
 | 
					        callAction(null, "ActivateProjectToolWindow", context.vim)
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    registerCommand(
 | 
					    registerCommand(
 | 
				
			||||||
@@ -325,14 +368,14 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
        val currentWindow = splitters.currentWindow
 | 
					        val currentWindow = splitters.currentWindow
 | 
				
			||||||
        currentWindow?.split(SwingConstants.HORIZONTAL, true, file, true)
 | 
					        currentWindow?.split(SwingConstants.HORIZONTAL, true, file, true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Util.callAction(null, "ActivateProjectToolWindow", context.vim)
 | 
					        callAction(null, "ActivateProjectToolWindow", context.vim)
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    registerCommand(
 | 
					    registerCommand(
 | 
				
			||||||
      "NERDTreeMapOpenRecursively",
 | 
					      "NERDTreeMapOpenRecursively",
 | 
				
			||||||
      "O",
 | 
					      "O",
 | 
				
			||||||
      NerdAction.Code { _, _, e ->
 | 
					      NerdAction.Code { project, _, _ ->
 | 
				
			||||||
        val tree = getTree(e) ?: return@Code
 | 
					        val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
 | 
				
			||||||
        TreeExpandCollapse.expandAll(tree)
 | 
					        TreeExpandCollapse.expandAll(tree)
 | 
				
			||||||
        tree.selectionPath?.let {
 | 
					        tree.selectionPath?.let {
 | 
				
			||||||
          TreeUtil.scrollToVisible(tree, it, false)
 | 
					          TreeUtil.scrollToVisible(tree, it, false)
 | 
				
			||||||
@@ -342,8 +385,8 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
    registerCommand(
 | 
					    registerCommand(
 | 
				
			||||||
      "NERDTreeMapCloseChildren",
 | 
					      "NERDTreeMapCloseChildren",
 | 
				
			||||||
      "X",
 | 
					      "X",
 | 
				
			||||||
      NerdAction.Code { _, _, e ->
 | 
					      NerdAction.Code { project, _, _ ->
 | 
				
			||||||
        val tree = getTree(e) ?: return@Code
 | 
					        val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
 | 
				
			||||||
        TreeExpandCollapse.collapse(tree)
 | 
					        TreeExpandCollapse.collapse(tree)
 | 
				
			||||||
        tree.selectionPath?.let {
 | 
					        tree.selectionPath?.let {
 | 
				
			||||||
          TreeUtil.scrollToVisible(tree, it, false)
 | 
					          TreeUtil.scrollToVisible(tree, it, false)
 | 
				
			||||||
@@ -353,8 +396,8 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
    registerCommand(
 | 
					    registerCommand(
 | 
				
			||||||
      "NERDTreeMapCloseDir",
 | 
					      "NERDTreeMapCloseDir",
 | 
				
			||||||
      "x",
 | 
					      "x",
 | 
				
			||||||
      NerdAction.Code { _, _, e ->
 | 
					      NerdAction.Code { project, _, _ ->
 | 
				
			||||||
        val tree = getTree(e) ?: return@Code
 | 
					        val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
 | 
				
			||||||
        val currentPath = tree.selectionPath ?: return@Code
 | 
					        val currentPath = tree.selectionPath ?: return@Code
 | 
				
			||||||
        if (tree.isExpanded(currentPath)) {
 | 
					        if (tree.isExpanded(currentPath)) {
 | 
				
			||||||
          tree.collapsePath(currentPath)
 | 
					          tree.collapsePath(currentPath)
 | 
				
			||||||
@@ -372,8 +415,8 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
    registerCommand(
 | 
					    registerCommand(
 | 
				
			||||||
      "NERDTreeMapJumpParent",
 | 
					      "NERDTreeMapJumpParent",
 | 
				
			||||||
      "p",
 | 
					      "p",
 | 
				
			||||||
      NerdAction.Code { _, _, e ->
 | 
					      NerdAction.Code { project, _, _ ->
 | 
				
			||||||
        val tree = getTree(e) ?: return@Code
 | 
					        val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
 | 
				
			||||||
        val currentPath = tree.selectionPath ?: return@Code
 | 
					        val currentPath = tree.selectionPath ?: return@Code
 | 
				
			||||||
        val parentPath = currentPath.parentPath ?: return@Code
 | 
					        val parentPath = currentPath.parentPath ?: return@Code
 | 
				
			||||||
        if (parentPath.parentPath != null) {
 | 
					        if (parentPath.parentPath != null) {
 | 
				
			||||||
@@ -386,8 +429,8 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
    registerCommand(
 | 
					    registerCommand(
 | 
				
			||||||
      "NERDTreeMapJumpFirstChild",
 | 
					      "NERDTreeMapJumpFirstChild",
 | 
				
			||||||
      "K",
 | 
					      "K",
 | 
				
			||||||
      NerdAction.Code { _, _, e ->
 | 
					      NerdAction.Code { project, _, _ ->
 | 
				
			||||||
        val tree = getTree(e) ?: return@Code
 | 
					        val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
 | 
				
			||||||
        val currentPath = tree.selectionPath ?: return@Code
 | 
					        val currentPath = tree.selectionPath ?: return@Code
 | 
				
			||||||
        val parent = currentPath.parentPath ?: return@Code
 | 
					        val parent = currentPath.parentPath ?: return@Code
 | 
				
			||||||
        val row = tree.getRowForPath(parent)
 | 
					        val row = tree.getRowForPath(parent)
 | 
				
			||||||
@@ -399,8 +442,8 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
    registerCommand(
 | 
					    registerCommand(
 | 
				
			||||||
      "NERDTreeMapJumpLastChild",
 | 
					      "NERDTreeMapJumpLastChild",
 | 
				
			||||||
      "J",
 | 
					      "J",
 | 
				
			||||||
      NerdAction.Code { _, _, e ->
 | 
					      NerdAction.Code { project, _, _ ->
 | 
				
			||||||
        val tree = getTree(e) ?: return@Code
 | 
					        val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
 | 
				
			||||||
        val currentPath = tree.selectionPath ?: return@Code
 | 
					        val currentPath = tree.selectionPath ?: return@Code
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        val currentPathCount = currentPath.pathCount
 | 
					        val currentPathCount = currentPath.pathCount
 | 
				
			||||||
@@ -445,17 +488,18 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    registerCommand(
 | 
					    registerCommand(
 | 
				
			||||||
      "/",
 | 
					      "/",
 | 
				
			||||||
      NerdAction.Code { _, _, e ->
 | 
					      NerdAction.Code { project, _, _ ->
 | 
				
			||||||
        val tree = getTree(e) ?: return@Code
 | 
					        NerdDispatcher.getInstance(project).waitForSearch = true
 | 
				
			||||||
        tree.getUserData(KEY)?.waitForSearch = true
 | 
					 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    registerCommand(
 | 
					    registerCommand(
 | 
				
			||||||
      "<ESC>",
 | 
					      "<ESC>",
 | 
				
			||||||
      NerdAction.Code { _, _, e ->
 | 
					      NerdAction.Code { project, _, _ ->
 | 
				
			||||||
        val tree = getTree(e) ?: return@Code
 | 
					        val instance = NerdDispatcher.getInstance(project)
 | 
				
			||||||
        tree.getUserData(KEY)?.waitForSearch = false
 | 
					        if (instance.waitForSearch) {
 | 
				
			||||||
 | 
					          instance.waitForSearch = false
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@@ -467,9 +511,14 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  object Util {
 | 
					  companion object {
 | 
				
			||||||
 | 
					    const val pluginName = "NERDTree"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    internal val monitor = Any()
 | 
					    internal val monitor = Any()
 | 
				
			||||||
    internal var commandsRegistered = false
 | 
					    internal var commandsRegistered = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val LOG = logger<NerdTree>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun callAction(editor: VimEditor?, name: String, context: ExecutionContext) {
 | 
					    fun callAction(editor: VimEditor?, name: String, context: ExecutionContext) {
 | 
				
			||||||
      val action = ActionManager.getInstance().getAction(name) ?: run {
 | 
					      val action = ActionManager.getInstance().getAction(name) ?: run {
 | 
				
			||||||
        VimPlugin.showMessage(MessageHelper.message("action.not.found.0", name))
 | 
					        VimPlugin.showMessage(MessageHelper.message("action.not.found.0", name))
 | 
				
			||||||
@@ -484,60 +533,45 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  companion object {
 | 
					    private fun addCommand(alias: String, handler: CommandAliasHandler) {
 | 
				
			||||||
    const val pluginName = "NERDTree"
 | 
					      VimPlugin.getCommand().setAlias(alias, CommandAlias.Call(0, -1, alias, handler))
 | 
				
			||||||
    private val LOG = logger<NerdTree>()
 | 
					    }
 | 
				
			||||||
    private val KEY = Key.create<NerdDispatcher>("IdeaVim-NerdTree-Dispatcher")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun installDispatcher(component: JComponent) {
 | 
					    private fun registerCommand(variable: String, default: String, action: NerdAction) {
 | 
				
			||||||
      if (component.getUserData(KEY) != null) return
 | 
					      val variableValue = VimPlugin.getVariableService().getGlobalVariableValue(variable)
 | 
				
			||||||
 | 
					      val mappings = if (variableValue is VimString) {
 | 
				
			||||||
      val dispatcher = NerdDispatcher()
 | 
					        variableValue.value
 | 
				
			||||||
      component.putUserData(KEY, dispatcher)
 | 
					      } else {
 | 
				
			||||||
 | 
					        default
 | 
				
			||||||
      val shortcuts = collectShortcuts(actionsRoot).map { RequiredShortcut(it, MappingOwner.Plugin.get(pluginName)) }
 | 
					 | 
				
			||||||
      dispatcher.registerCustomShortcutSet(KeyGroup.toShortcutSet(shortcuts), component)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      SpeedSearchSupply.getSupply(component, true)?.addChangeListener {
 | 
					 | 
				
			||||||
        dispatcher.waitForSearch = false
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      actionsRoot.addLeafs(mappings, action)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun registerCommand(default: String, action: NerdAction) {
 | 
				
			||||||
 | 
					      actionsRoot.addLeafs(default, action)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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) {
 | 
				
			||||||
 | 
					      val dispatcher = NerdDispatcher.getInstance(project)
 | 
				
			||||||
 | 
					      val shortcuts = collectShortcuts(actionsRoot).map { RequiredShortcut(it, MappingOwner.Plugin.get(pluginName)) }
 | 
				
			||||||
 | 
					      dispatcher.registerCustomShortcutSet(
 | 
				
			||||||
 | 
					        KeyGroup.toShortcutSet(shortcuts),
 | 
				
			||||||
 | 
					        (ProjectView.getInstance(project) as ProjectViewImpl).component,
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
private fun addCommand(alias: String, handler: CommandAliasHandler) {
 | 
					 | 
				
			||||||
  VimPlugin.getCommand().setAlias(alias, CommandAlias.Call(0, -1, alias, handler))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private fun registerCommand(variable: String, default: String, action: NerdAction) {
 | 
					 | 
				
			||||||
  val variableValue = VimPlugin.getVariableService().getGlobalVariableValue(variable)
 | 
					 | 
				
			||||||
  val mappings = if (variableValue is VimString) {
 | 
					 | 
				
			||||||
    variableValue.value
 | 
					 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
    default
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  actionsRoot.addLeafs(mappings, action)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private fun registerCommand(default: String, action: NerdAction) {
 | 
					 | 
				
			||||||
  actionsRoot.addLeafs(default, action)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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 getTree(e: AnActionEvent): JTree? {
 | 
					 | 
				
			||||||
  return e.dataContext.getData(PlatformCoreDataKeys.CONTEXT_COMPONENT) as? JTree
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
package com.maddyhome.idea.vim.extension.nerdtree
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.intellij.ide.ApplicationInitializedListener
 | 
					 | 
				
			||||||
import com.intellij.openapi.application.ApplicationManager
 | 
					 | 
				
			||||||
import com.intellij.util.ui.StartupUiUtil
 | 
					 | 
				
			||||||
import kotlinx.coroutines.CoroutineScope
 | 
					 | 
				
			||||||
import java.awt.AWTEvent
 | 
					 | 
				
			||||||
import java.awt.event.FocusEvent
 | 
					 | 
				
			||||||
import javax.swing.JTree
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Suppress("UnstableApiUsage")
 | 
					 | 
				
			||||||
internal class NerdTreeApplicationListener : ApplicationInitializedListener {
 | 
					 | 
				
			||||||
  override suspend fun execute(asyncScope: CoroutineScope) {
 | 
					 | 
				
			||||||
    StartupUiUtil.addAwtListener(::handleEvent, AWTEvent.FOCUS_EVENT_MASK, ApplicationManager.getApplication().getService(NerdTreeDisposableService::class.java))
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private fun handleEvent(event: AWTEvent) {
 | 
					 | 
				
			||||||
    if (event is FocusEvent && event.id == FocusEvent.FOCUS_GAINED) {
 | 
					 | 
				
			||||||
      val source = event.source
 | 
					 | 
				
			||||||
      if (source is JTree) {
 | 
					 | 
				
			||||||
        NerdTree.installDispatcher(source)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,9 +0,0 @@
 | 
				
			|||||||
package com.maddyhome.idea.vim.extension.nerdtree
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.intellij.openapi.Disposable
 | 
					 | 
				
			||||||
import com.intellij.openapi.components.Service
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Service
 | 
					 | 
				
			||||||
internal class NerdTreeDisposableService : Disposable {
 | 
					 | 
				
			||||||
  override fun dispose() {}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -9,11 +9,10 @@
 | 
				
			|||||||
package com.maddyhome.idea.vim.extension.paragraphmotion
 | 
					package com.maddyhome.idea.vim.extension.paragraphmotion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.editor.Caret
 | 
					import com.intellij.openapi.editor.Caret
 | 
				
			||||||
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.VimEditor
 | 
					import com.maddyhome.idea.vim.api.VimEditor
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.getLineEndForOffset
 | 
				
			||||||
import com.maddyhome.idea.vim.api.injector
 | 
					import com.maddyhome.idea.vim.api.injector
 | 
				
			||||||
import com.maddyhome.idea.vim.api.normalizeOffset
 | 
					 | 
				
			||||||
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.extension.ExtensionHandler
 | 
					import com.maddyhome.idea.vim.extension.ExtensionHandler
 | 
				
			||||||
@@ -21,10 +20,8 @@ import com.maddyhome.idea.vim.extension.VimExtension
 | 
				
			|||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.vimForEachCaret
 | 
					import com.maddyhome.idea.vim.helper.vimForEachCaret
 | 
				
			||||||
import com.maddyhome.idea.vim.key.MappingOwner
 | 
					 | 
				
			||||||
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 javax.swing.KeyStroke
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal class ParagraphMotion : VimExtension {
 | 
					internal class ParagraphMotion : VimExtension {
 | 
				
			||||||
  override fun getName(): String = "vim-paragraph-motion"
 | 
					  override fun getName(): String = "vim-paragraph-motion"
 | 
				
			||||||
@@ -33,8 +30,8 @@ internal class ParagraphMotion : VimExtension {
 | 
				
			|||||||
    VimExtensionFacade.putExtensionHandlerMapping(MappingMode.NXO, injector.parser.parseKeys("<Plug>(ParagraphNextMotion)"), owner, ParagraphMotionHandler(1), false)
 | 
					    VimExtensionFacade.putExtensionHandlerMapping(MappingMode.NXO, injector.parser.parseKeys("<Plug>(ParagraphNextMotion)"), owner, ParagraphMotionHandler(1), false)
 | 
				
			||||||
    VimExtensionFacade.putExtensionHandlerMapping(MappingMode.NXO, injector.parser.parseKeys("<Plug>(ParagraphPrevMotion)"), owner, ParagraphMotionHandler(-1), false)
 | 
					    VimExtensionFacade.putExtensionHandlerMapping(MappingMode.NXO, injector.parser.parseKeys("<Plug>(ParagraphPrevMotion)"), owner, ParagraphMotionHandler(-1), false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    putKeyMappingIfMissingFromAndToKeys(MappingMode.NXO, injector.parser.parseKeys("}"), owner, injector.parser.parseKeys("<Plug>(ParagraphNextMotion)"), true)
 | 
					    putKeyMappingIfMissing(MappingMode.NXO, injector.parser.parseKeys("}"), owner, injector.parser.parseKeys("<Plug>(ParagraphNextMotion)"), true)
 | 
				
			||||||
    putKeyMappingIfMissingFromAndToKeys(MappingMode.NXO, injector.parser.parseKeys("{"), owner, injector.parser.parseKeys("<Plug>(ParagraphPrevMotion)"), true)
 | 
					    putKeyMappingIfMissing(MappingMode.NXO, injector.parser.parseKeys("{"), owner, injector.parser.parseKeys("<Plug>(ParagraphPrevMotion)"), true)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private class ParagraphMotionHandler(private val count: Int) : ExtensionHandler {
 | 
					  private class ParagraphMotionHandler(private val count: Int) : ExtensionHandler {
 | 
				
			||||||
@@ -48,21 +45,7 @@ internal class ParagraphMotion : VimExtension {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun moveCaretToNextParagraph(editor: VimEditor, caret: Caret, count: Int): Int? {
 | 
					    fun moveCaretToNextParagraph(editor: VimEditor, caret: Caret, count: Int): Int? {
 | 
				
			||||||
      return injector.searchHelper.findNextParagraph(editor, caret.vim, count, true)
 | 
					      return injector.searchHelper.findNextParagraph(editor, caret.vim, count, true)?.let(editor::getLineEndForOffset)
 | 
				
			||||||
        ?.let { editor.normalizeOffset(it, true) }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  // For VIM-3306
 | 
					 | 
				
			||||||
  @Suppress("SameParameterValue")
 | 
					 | 
				
			||||||
  private fun putKeyMappingIfMissingFromAndToKeys(
 | 
					 | 
				
			||||||
    modes: Set<MappingMode>,
 | 
					 | 
				
			||||||
    fromKeys: List<KeyStroke>,
 | 
					 | 
				
			||||||
    pluginOwner: MappingOwner,
 | 
					 | 
				
			||||||
    toKeys: List<KeyStroke>,
 | 
					 | 
				
			||||||
    recursive: Boolean,
 | 
					 | 
				
			||||||
  ) {
 | 
					 | 
				
			||||||
    val filteredModes = modes.filterNotTo(HashSet()) { VimPlugin.getKey().hasmapfrom(it, fromKeys) }
 | 
					 | 
				
			||||||
    putKeyMappingIfMissing(filteredModes, fromKeys, pluginOwner, toKeys, recursive)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,26 +8,30 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.maddyhome.idea.vim.extension.replacewithregister
 | 
					package com.maddyhome.idea.vim.extension.replacewithregister
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.actionSystem.DataContext
 | 
					 | 
				
			||||||
import com.intellij.openapi.editor.Editor
 | 
					import com.intellij.openapi.editor.Editor
 | 
				
			||||||
import com.maddyhome.idea.vim.VimPlugin
 | 
					import com.maddyhome.idea.vim.VimPlugin
 | 
				
			||||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
					import com.maddyhome.idea.vim.api.ExecutionContext
 | 
				
			||||||
import com.maddyhome.idea.vim.api.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.injector
 | 
					import com.maddyhome.idea.vim.api.injector
 | 
				
			||||||
import com.maddyhome.idea.vim.command.MappingMode
 | 
					import com.maddyhome.idea.vim.command.MappingMode
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.Mode
 | 
				
			||||||
import com.maddyhome.idea.vim.command.OperatorArguments
 | 
					import com.maddyhome.idea.vim.command.OperatorArguments
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.isLine
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.selectionType
 | 
				
			||||||
import com.maddyhome.idea.vim.common.TextRange
 | 
					import com.maddyhome.idea.vim.common.TextRange
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.ExtensionHandler
 | 
					import com.maddyhome.idea.vim.extension.ExtensionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtension
 | 
					import com.maddyhome.idea.vim.extension.VimExtension
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
 | 
				
			||||||
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.state.mode.mode
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.vimStateMachine
 | 
					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
 | 
				
			||||||
@@ -35,10 +39,6 @@ import com.maddyhome.idea.vim.newapi.ij
 | 
				
			|||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper
 | 
					import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper
 | 
				
			||||||
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.SelectionType
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.isLine
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.selectionType
 | 
					 | 
				
			||||||
import org.jetbrains.annotations.NonNls
 | 
					import org.jetbrains.annotations.NonNls
 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal class ReplaceWithRegister : VimExtension {
 | 
					internal class ReplaceWithRegister : VimExtension {
 | 
				
			||||||
@@ -53,19 +53,17 @@ internal class ReplaceWithRegister : VimExtension {
 | 
				
			|||||||
    putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("gr"), owner, injector.parser.parseKeys(RWR_OPERATOR), true)
 | 
					    putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("gr"), owner, injector.parser.parseKeys(RWR_OPERATOR), true)
 | 
				
			||||||
    putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("grr"), owner, injector.parser.parseKeys(RWR_LINE), true)
 | 
					    putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("grr"), owner, injector.parser.parseKeys(RWR_LINE), true)
 | 
				
			||||||
    putKeyMappingIfMissing(MappingMode.X, injector.parser.parseKeys("gr"), owner, injector.parser.parseKeys(RWR_VISUAL), true)
 | 
					    putKeyMappingIfMissing(MappingMode.X, injector.parser.parseKeys("gr"), owner, injector.parser.parseKeys(RWR_VISUAL), true)
 | 
				
			||||||
 | 
					 | 
				
			||||||
    VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator())
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private class RwrVisual : ExtensionHandler {
 | 
					  private class RwrVisual : ExtensionHandler {
 | 
				
			||||||
    override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
 | 
					    override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
 | 
				
			||||||
      val typeInEditor = editor.mode.selectionType ?: SelectionType.CHARACTER_WISE
 | 
					      val typeInEditor = editor.mode.selectionType ?: CHARACTER_WISE
 | 
				
			||||||
      editor.sortedCarets().forEach { caret ->
 | 
					      editor.sortedCarets().forEach { caret ->
 | 
				
			||||||
        val selectionStart = caret.selectionStart
 | 
					        val selectionStart = caret.selectionStart
 | 
				
			||||||
        val selectionEnd = caret.selectionEnd
 | 
					        val selectionEnd = caret.selectionEnd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        val visualSelection = caret to VimSelection.create(selectionStart, selectionEnd - 1, typeInEditor, editor)
 | 
					        val visualSelection = caret to VimSelection.create(selectionStart, selectionEnd - 1, typeInEditor, editor)
 | 
				
			||||||
        doReplace(editor.ij, context.ij, caret, PutData.VisualSelection(mapOf(visualSelection), typeInEditor))
 | 
					        doReplace(editor.ij, caret, PutData.VisualSelection(mapOf(visualSelection), typeInEditor))
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      editor.exitVisualMode()
 | 
					      editor.exitVisualMode()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -75,7 +73,7 @@ internal class ReplaceWithRegister : VimExtension {
 | 
				
			|||||||
    override val isRepeatable: Boolean = true
 | 
					    override val isRepeatable: Boolean = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
 | 
					    override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
 | 
				
			||||||
      injector.globalOptions().operatorfunc = OPERATOR_FUNC
 | 
					      setOperatorFunction(Operator())
 | 
				
			||||||
      executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
 | 
					      executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -93,7 +91,7 @@ internal class ReplaceWithRegister : VimExtension {
 | 
				
			|||||||
        val visualSelection = caret to VimSelection.create(lineStart, lineEnd, SelectionType.LINE_WISE, editor)
 | 
					        val visualSelection = caret to VimSelection.create(lineStart, lineEnd, SelectionType.LINE_WISE, editor)
 | 
				
			||||||
        caretsAndSelections += visualSelection
 | 
					        caretsAndSelections += visualSelection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        doReplace(editor.ij, context.ij, caret, PutData.VisualSelection(mapOf(visualSelection), SelectionType.LINE_WISE))
 | 
					        doReplace(editor.ij, caret, PutData.VisualSelection(mapOf(visualSelection), SelectionType.LINE_WISE))
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      editor.sortedCarets().forEach { caret ->
 | 
					      editor.sortedCarets().forEach { caret ->
 | 
				
			||||||
@@ -114,14 +112,14 @@ internal class ReplaceWithRegister : VimExtension {
 | 
				
			|||||||
          editor.primaryCaret() to VimSelection.create(
 | 
					          editor.primaryCaret() to VimSelection.create(
 | 
				
			||||||
            range.startOffset,
 | 
					            range.startOffset,
 | 
				
			||||||
            range.endOffset - 1,
 | 
					            range.endOffset - 1,
 | 
				
			||||||
            selectionType ?: SelectionType.CHARACTER_WISE,
 | 
					            selectionType ?: CHARACTER_WISE,
 | 
				
			||||||
            editor,
 | 
					            editor,
 | 
				
			||||||
          ),
 | 
					          ),
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        selectionType ?: SelectionType.CHARACTER_WISE,
 | 
					        selectionType ?: CHARACTER_WISE,
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
      // todo multicaret
 | 
					      // todo multicaret
 | 
				
			||||||
      doReplace(ijEditor, context.ij, editor.primaryCaret(), visualSelection)
 | 
					      doReplace(ijEditor, editor.primaryCaret(), visualSelection)
 | 
				
			||||||
      return true
 | 
					      return true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -134,49 +132,52 @@ internal class ReplaceWithRegister : VimExtension {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  companion object {
 | 
					  companion object {
 | 
				
			||||||
    @NonNls private const val RWR_OPERATOR = "<Plug>ReplaceWithRegisterOperator"
 | 
					    @NonNls
 | 
				
			||||||
    @NonNls private const val RWR_LINE = "<Plug>ReplaceWithRegisterLine"
 | 
					    private const val RWR_OPERATOR = "<Plug>ReplaceWithRegisterOperator"
 | 
				
			||||||
    @NonNls private const val RWR_VISUAL = "<Plug>ReplaceWithRegisterVisual"
 | 
					
 | 
				
			||||||
    @NonNls private const val OPERATOR_FUNC = "ReplaceWithRegisterOperatorFunc"
 | 
					    @NonNls
 | 
				
			||||||
 | 
					    private const val RWR_LINE = "<Plug>ReplaceWithRegisterLine"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNls
 | 
				
			||||||
 | 
					    private const val RWR_VISUAL = "<Plug>ReplaceWithRegisterVisual"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun doReplace(editor: Editor, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
 | 
				
			||||||
 | 
					      val registerGroup = injector.registerGroup
 | 
				
			||||||
 | 
					      val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
 | 
				
			||||||
 | 
					      val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var usedType = savedRegister.type
 | 
				
			||||||
 | 
					      var usedText = savedRegister.text
 | 
				
			||||||
 | 
					      if (usedType.isLine && usedText?.endsWith('\n') == true) {
 | 
				
			||||||
 | 
					        // Code from original plugin implementation. Correct text for linewise selected text
 | 
				
			||||||
 | 
					        usedText = usedText.dropLast(1)
 | 
				
			||||||
 | 
					        usedType = SelectionType.CHARACTER_WISE
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      val textData = PutData.TextData(usedText, usedType, savedRegister.transferableData, savedRegister.name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      val putData = PutData(
 | 
				
			||||||
 | 
					        textData,
 | 
				
			||||||
 | 
					        visualSelection,
 | 
				
			||||||
 | 
					        1,
 | 
				
			||||||
 | 
					        insertTextBeforeCaret = true,
 | 
				
			||||||
 | 
					        rawIndent = true,
 | 
				
			||||||
 | 
					        caretAfterInsertedText = false,
 | 
				
			||||||
 | 
					        putToLine = -1,
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      ClipboardOptionHelper.IdeaputDisabler().use {
 | 
				
			||||||
 | 
					        VimPlugin.getPut().putText(
 | 
				
			||||||
 | 
					          IjVimEditor(editor),
 | 
				
			||||||
 | 
					          injector.executionContextManager.onEditor(editor.vim),
 | 
				
			||||||
 | 
					          putData,
 | 
				
			||||||
 | 
					          operatorArguments = OperatorArguments(
 | 
				
			||||||
 | 
					            editor.vimStateMachine?.isOperatorPending ?: false,
 | 
				
			||||||
 | 
					            0,
 | 
				
			||||||
 | 
					            editor.vim.mode,
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          saveToRegister = false
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
 | 
					 | 
				
			||||||
  val registerGroup = injector.registerGroup
 | 
					 | 
				
			||||||
  val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
 | 
					 | 
				
			||||||
  val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  var usedType = savedRegister.type
 | 
					 | 
				
			||||||
  var usedText = savedRegister.text
 | 
					 | 
				
			||||||
  if (usedType.isLine && usedText?.endsWith('\n') == true) {
 | 
					 | 
				
			||||||
    // Code from original plugin implementation. Correct text for linewise selected text
 | 
					 | 
				
			||||||
    usedText = usedText.dropLast(1)
 | 
					 | 
				
			||||||
    usedType = SelectionType.CHARACTER_WISE
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  val textData = PutData.TextData(usedText, usedType, savedRegister.transferableData, savedRegister.name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  val putData = PutData(
 | 
					 | 
				
			||||||
    textData,
 | 
					 | 
				
			||||||
    visualSelection,
 | 
					 | 
				
			||||||
    1,
 | 
					 | 
				
			||||||
    insertTextBeforeCaret = true,
 | 
					 | 
				
			||||||
    rawIndent = true,
 | 
					 | 
				
			||||||
    caretAfterInsertedText = false,
 | 
					 | 
				
			||||||
    putToLine = -1,
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
  val vimEditor = editor.vim
 | 
					 | 
				
			||||||
  ClipboardOptionHelper.IdeaputDisabler().use {
 | 
					 | 
				
			||||||
    VimPlugin.getPut().putText(
 | 
					 | 
				
			||||||
      vimEditor,
 | 
					 | 
				
			||||||
      context.vim,
 | 
					 | 
				
			||||||
      putData,
 | 
					 | 
				
			||||||
      operatorArguments = OperatorArguments(
 | 
					 | 
				
			||||||
        editor.vimStateMachine?.isOperatorPending(vimEditor.mode) ?: false,
 | 
					 | 
				
			||||||
        0,
 | 
					 | 
				
			||||||
        editor.vim.mode,
 | 
					 | 
				
			||||||
      ),
 | 
					 | 
				
			||||||
      saveToRegister = false
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -36,7 +36,6 @@ import com.maddyhome.idea.vim.helper.StrictMode
 | 
				
			|||||||
import com.maddyhome.idea.vim.newapi.ij
 | 
					import com.maddyhome.idea.vim.newapi.ij
 | 
				
			||||||
import java.awt.Font
 | 
					import java.awt.Font
 | 
				
			||||||
import java.awt.event.KeyEvent
 | 
					import java.awt.event.KeyEvent
 | 
				
			||||||
import java.util.*
 | 
					 | 
				
			||||||
import javax.swing.Timer
 | 
					import javax.swing.Timer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -49,20 +48,17 @@ internal class IdeaVimSneakExtension : VimExtension {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  override fun init() {
 | 
					  override fun init() {
 | 
				
			||||||
    val highlightHandler = HighlightHandler()
 | 
					    val highlightHandler = HighlightHandler()
 | 
				
			||||||
    mapToFunctionAndProvideKeys("s", SneakHandler(highlightHandler, Direction.FORWARD), MappingMode.NXO)
 | 
					    mapToFunctionAndProvideKeys("s", SneakHandler(highlightHandler, Direction.FORWARD))
 | 
				
			||||||
 | 
					    mapToFunctionAndProvideKeys("S", SneakHandler(highlightHandler, Direction.BACKWARD))
 | 
				
			||||||
    // 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("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"))
 | 
				
			||||||
    mapToFunctionAndProvideKeys("F", SneakMemoryHandler("F"), MappingMode.NXO)
 | 
					    mapToFunctionAndProvideKeys("F", SneakMemoryHandler("F"))
 | 
				
			||||||
    mapToFunctionAndProvideKeys("t", SneakMemoryHandler("t"), MappingMode.NXO)
 | 
					    mapToFunctionAndProvideKeys("t", SneakMemoryHandler("t"))
 | 
				
			||||||
    mapToFunctionAndProvideKeys("T", SneakMemoryHandler("T"), MappingMode.NXO)
 | 
					    mapToFunctionAndProvideKeys("T", SneakMemoryHandler("T"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mapToFunctionAndProvideKeys(";", SneakRepeatHandler(highlightHandler, RepeatDirection.IDENTICAL), MappingMode.NXO)
 | 
					    mapToFunctionAndProvideKeys(";", SneakRepeatHandler(highlightHandler, RepeatDirection.IDENTICAL))
 | 
				
			||||||
    mapToFunctionAndProvideKeys(",", SneakRepeatHandler(highlightHandler, RepeatDirection.REVERSE), MappingMode.NXO)
 | 
					    mapToFunctionAndProvideKeys(",", SneakRepeatHandler(highlightHandler, RepeatDirection.REVERSE))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private class SneakHandler(
 | 
					  private class SneakHandler(
 | 
				
			||||||
@@ -118,7 +114,7 @@ internal class IdeaVimSneakExtension : VimExtension {
 | 
				
			|||||||
    var lastSymbols: String = ""
 | 
					    var lastSymbols: String = ""
 | 
				
			||||||
    fun jumpTo(editor: VimEditor, charone: Char, chartwo: Char, sneakDirection: Direction): TextRange? {
 | 
					    fun jumpTo(editor: VimEditor, charone: Char, chartwo: Char, sneakDirection: Direction): TextRange? {
 | 
				
			||||||
      val caret = editor.primaryCaret()
 | 
					      val caret = editor.primaryCaret()
 | 
				
			||||||
      val position = caret.offset
 | 
					      val position = caret.offset.point
 | 
				
			||||||
      val chars = editor.text()
 | 
					      val chars = editor.text()
 | 
				
			||||||
      val foundPosition = sneakDirection.findBiChar(editor, chars, position, charone, chartwo)
 | 
					      val foundPosition = sneakDirection.findBiChar(editor, chars, position, charone, chartwo)
 | 
				
			||||||
      if (foundPosition != null) {
 | 
					      if (foundPosition != null) {
 | 
				
			||||||
@@ -277,18 +273,16 @@ internal class IdeaVimSneakExtension : VimExtension {
 | 
				
			|||||||
 * Map some <Plug>(keys) command to given handler
 | 
					 * Map some <Plug>(keys) command to given handler
 | 
				
			||||||
 *  and create mapping to <Plug>(prefix)[keys]
 | 
					 *  and create mapping to <Plug>(prefix)[keys]
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
private fun VimExtension.mapToFunctionAndProvideKeys(
 | 
					private fun VimExtension.mapToFunctionAndProvideKeys(keys: String, handler: ExtensionHandler) {
 | 
				
			||||||
  keys: String, handler: ExtensionHandler, mappingModes: EnumSet<MappingMode>
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
  VimExtensionFacade.putExtensionHandlerMapping(
 | 
					  VimExtensionFacade.putExtensionHandlerMapping(
 | 
				
			||||||
    mappingModes,
 | 
					    MappingMode.NXO,
 | 
				
			||||||
    injector.parser.parseKeys(command(keys)),
 | 
					    injector.parser.parseKeys(command(keys)),
 | 
				
			||||||
    owner,
 | 
					    owner,
 | 
				
			||||||
    handler,
 | 
					    handler,
 | 
				
			||||||
    false
 | 
					    false
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
  VimExtensionFacade.putExtensionHandlerMapping(
 | 
					  VimExtensionFacade.putExtensionHandlerMapping(
 | 
				
			||||||
    mappingModes,
 | 
					    MappingMode.NXO,
 | 
				
			||||||
    injector.parser.parseKeys(commandFromOriginalPlugin(keys)),
 | 
					    injector.parser.parseKeys(commandFromOriginalPlugin(keys)),
 | 
				
			||||||
    owner,
 | 
					    owner,
 | 
				
			||||||
    handler,
 | 
					    handler,
 | 
				
			||||||
@@ -300,17 +294,17 @@ private fun VimExtension.mapToFunctionAndProvideKeys(
 | 
				
			|||||||
  //  - The shortcut should not be registered if any of these mappings is overridden in .ideavimrc
 | 
					  //  - The shortcut should not be registered if any of these mappings is overridden in .ideavimrc
 | 
				
			||||||
  //  - The shortcut should not be registered if some other shortcut for this key exists
 | 
					  //  - The shortcut should not be registered if some other shortcut for this key exists
 | 
				
			||||||
  val fromKeys = injector.parser.parseKeys(keys)
 | 
					  val fromKeys = injector.parser.parseKeys(keys)
 | 
				
			||||||
  val filteredModes = mappingModes.filterNotTo(HashSet()) {
 | 
					  val filteredModes = MappingMode.NXO.filterNotTo(HashSet()) {
 | 
				
			||||||
    VimPlugin.getKey().hasmapto(it, injector.parser.parseKeys(command(keys)))
 | 
					    VimPlugin.getKey().hasmapto(it, injector.parser.parseKeys(command(keys)))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  val filteredModes2 = mappingModes.filterNotTo(HashSet()) {
 | 
					  val filteredModes2 = MappingMode.NXO.filterNotTo(HashSet()) {
 | 
				
			||||||
    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 = MappingMode.NXO.filterNotTo(HashSet()) {
 | 
				
			||||||
    injector.keyGroup.hasmapfrom(it, fromKeys)
 | 
					    injector.keyGroup.hasmapfrom(it, fromKeys)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  val doubleFiltered = mappingModes
 | 
					  val doubleFiltered = MappingMode.NXO
 | 
				
			||||||
    .filter { it in filteredModes2 && it in filteredModes && it in filteredFromModes }
 | 
					    .filter { it in filteredModes2 && it in filteredModes && it in filteredFromModes }
 | 
				
			||||||
    .toSet()
 | 
					    .toSet()
 | 
				
			||||||
  putKeyMapping(doubleFiltered, fromKeys, owner, injector.parser.parseKeys(command(keys)), true)
 | 
					  putKeyMapping(doubleFiltered, fromKeys, owner, injector.parser.parseKeys(command(keys)), true)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,6 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
package com.maddyhome.idea.vim.extension.surround
 | 
					package com.maddyhome.idea.vim.extension.surround
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.actionSystem.DataContext
 | 
					 | 
				
			||||||
import com.intellij.openapi.application.runWriteAction
 | 
					import com.intellij.openapi.application.runWriteAction
 | 
				
			||||||
import com.intellij.openapi.diagnostic.logger
 | 
					import com.intellij.openapi.diagnostic.logger
 | 
				
			||||||
import com.intellij.openapi.editor.Editor
 | 
					import com.intellij.openapi.editor.Editor
 | 
				
			||||||
@@ -18,7 +17,6 @@ import com.maddyhome.idea.vim.api.VimChangeGroup
 | 
				
			|||||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
					import com.maddyhome.idea.vim.api.VimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.api.endsWithNewLine
 | 
					import com.maddyhome.idea.vim.api.endsWithNewLine
 | 
				
			||||||
import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
 | 
					import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
 | 
				
			||||||
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.api.setChangeMarks
 | 
					import com.maddyhome.idea.vim.api.setChangeMarks
 | 
				
			||||||
import com.maddyhome.idea.vim.command.MappingMode
 | 
					import com.maddyhome.idea.vim.command.MappingMode
 | 
				
			||||||
@@ -26,16 +24,14 @@ import com.maddyhome.idea.vim.command.OperatorArguments
 | 
				
			|||||||
import com.maddyhome.idea.vim.common.TextRange
 | 
					import com.maddyhome.idea.vim.common.TextRange
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.ExtensionHandler
 | 
					import com.maddyhome.idea.vim.extension.ExtensionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtension
 | 
					import com.maddyhome.idea.vim.extension.VimExtension
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegisterForCaret
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegisterForCaret
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.inputKeyStroke
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.inputKeyStroke
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.inputString
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.inputString
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
 | 
				
			||||||
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.group.findBlockRange
 | 
					 | 
				
			||||||
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
 | 
				
			||||||
@@ -46,6 +42,7 @@ 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.mode
 | 
				
			||||||
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
 | 
				
			||||||
@@ -81,15 +78,13 @@ internal class VimSurroundExtension : VimExtension {
 | 
				
			|||||||
      putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("ds"), owner, injector.parser.parseKeys("<Plug>DSurround"), true)
 | 
					      putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("ds"), owner, injector.parser.parseKeys("<Plug>DSurround"), true)
 | 
				
			||||||
      putKeyMappingIfMissing(MappingMode.XO, injector.parser.parseKeys("S"), owner, injector.parser.parseKeys("<Plug>VSurround"), true)
 | 
					      putKeyMappingIfMissing(MappingMode.XO, injector.parser.parseKeys("S"), owner, injector.parser.parseKeys("<Plug>VSurround"), true)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator(supportsMultipleCursors = false, count = 1)) // TODO
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private class YSurroundHandler : ExtensionHandler {
 | 
					  private class YSurroundHandler : ExtensionHandler {
 | 
				
			||||||
    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) {
 | 
				
			||||||
      injector.globalOptions().operatorfunc = OPERATOR_FUNC
 | 
					      setOperatorFunction(Operator(supportsMultipleCursors = false, count = 1)) // TODO
 | 
				
			||||||
      executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
 | 
					      executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -101,7 +96,7 @@ internal class VimSurroundExtension : VimExtension {
 | 
				
			|||||||
      val ijEditor = editor.ij
 | 
					      val ijEditor = editor.ij
 | 
				
			||||||
      val c = getChar(ijEditor)
 | 
					      val c = getChar(ijEditor)
 | 
				
			||||||
      if (c.code == 0) return
 | 
					      if (c.code == 0) return
 | 
				
			||||||
      val pair = getOrInputPair(c, ijEditor, context.ij) ?: return
 | 
					      val pair = getOrInputPair(c, ijEditor) ?: return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      editor.forEachCaret {
 | 
					      editor.forEachCaret {
 | 
				
			||||||
        val line = it.getBufferPosition().line
 | 
					        val line = it.getBufferPosition().line
 | 
				
			||||||
@@ -151,7 +146,7 @@ internal class VimSurroundExtension : VimExtension {
 | 
				
			|||||||
      val charTo = getChar(editor.ij)
 | 
					      val charTo = getChar(editor.ij)
 | 
				
			||||||
      if (charTo.code == 0) return
 | 
					      if (charTo.code == 0) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      val newSurround = getOrInputPair(charTo, editor.ij, context.ij) ?: return
 | 
					      val newSurround = getOrInputPair(charTo, editor.ij) ?: return
 | 
				
			||||||
      runWriteAction { change(editor, context, charFrom, newSurround) }
 | 
					      runWriteAction { change(editor, context, charFrom, newSurround) }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -228,12 +223,12 @@ internal class VimSurroundExtension : VimExtension {
 | 
				
			|||||||
        val searchHelper = injector.searchHelper
 | 
					        val searchHelper = injector.searchHelper
 | 
				
			||||||
        return when (char) {
 | 
					        return when (char) {
 | 
				
			||||||
          't' -> searchHelper.findBlockTagRange(editor, caret, 1, true)
 | 
					          't' -> searchHelper.findBlockTagRange(editor, caret, 1, true)
 | 
				
			||||||
          '(', ')', 'b' -> findBlockRange(editor, caret, '(', 1, true)
 | 
					          '(', ')', 'b' -> searchHelper.findBlockRange(editor, caret, '(', 1, true)
 | 
				
			||||||
          '[', ']' -> findBlockRange(editor, caret, '[', 1, true)
 | 
					          '[', ']' -> searchHelper.findBlockRange(editor, caret, '[', 1, true)
 | 
				
			||||||
          '{', '}', 'B' -> findBlockRange(editor, caret, '{', 1, true)
 | 
					          '{', '}', 'B' -> searchHelper.findBlockRange(editor, caret, '{', 1, true)
 | 
				
			||||||
          '<', '>' -> findBlockRange(editor, caret, '<', 1, true)
 | 
					          '<', '>' -> searchHelper.findBlockRange(editor, caret, '<', 1, true)
 | 
				
			||||||
          '`', '\'', '"' -> {
 | 
					          '`', '\'', '"' -> {
 | 
				
			||||||
            val caretOffset = caret.offset
 | 
					            val caretOffset = caret.offset.point
 | 
				
			||||||
            val text = editor.text()
 | 
					            val text = editor.text()
 | 
				
			||||||
            if (text.getOrNull(caretOffset - 1) == char && text.getOrNull(caretOffset) == char) {
 | 
					            if (text.getOrNull(caretOffset - 1) == char && text.getOrNull(caretOffset) == char) {
 | 
				
			||||||
              TextRange(caretOffset - 1, caretOffset + 1)
 | 
					              TextRange(caretOffset - 1, caretOffset + 1)
 | 
				
			||||||
@@ -270,23 +265,23 @@ internal class VimSurroundExtension : VimExtension {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private class Operator(private val supportsMultipleCursors: Boolean, private val count: Int) : OperatorFunction {
 | 
					  private class Operator(private val supportsMultipleCursors: Boolean, private val count: Int) : OperatorFunction {
 | 
				
			||||||
    override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
 | 
					    override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
 | 
				
			||||||
      val ijEditor = vimEditor.ij
 | 
					      val editor = vimEditor.ij
 | 
				
			||||||
      val c = getChar(ijEditor)
 | 
					      val c = getChar(editor)
 | 
				
			||||||
      if (c.code == 0) return true
 | 
					      if (c.code == 0) return true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      val pair = getOrInputPair(c, ijEditor, context.ij) ?: return false
 | 
					      val pair = getOrInputPair(c, editor) ?: return false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      runWriteAction {
 | 
					      runWriteAction {
 | 
				
			||||||
        val change = VimPlugin.getChange()
 | 
					        val change = VimPlugin.getChange()
 | 
				
			||||||
        if (supportsMultipleCursors) {
 | 
					        if (supportsMultipleCursors) {
 | 
				
			||||||
          ijEditor.runWithEveryCaretAndRestore {
 | 
					          editor.runWithEveryCaretAndRestore {
 | 
				
			||||||
            applyOnce(ijEditor, change, pair, count)
 | 
					            applyOnce(editor, change, pair, count)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else {
 | 
					        else {
 | 
				
			||||||
          applyOnce(ijEditor, change, pair, count)
 | 
					          applyOnce(editor, change, pair, count)
 | 
				
			||||||
          // Jump back to start
 | 
					          // Jump back to start
 | 
				
			||||||
          executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
 | 
					          executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return true
 | 
					      return true
 | 
				
			||||||
@@ -320,9 +315,7 @@ private val LOG = logger<VimSurroundExtension>()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
private const val REGISTER = '"'
 | 
					private const val REGISTER = '"'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private const val OPERATOR_FUNC = "SurroundOperatorFunc"
 | 
					private val tagNameAndAttributesCapturePattern = "(\\S+)([^>]*)>".toPattern()
 | 
				
			||||||
 | 
					 | 
				
			||||||
    private val tagNameAndAttributesCapturePattern = "(\\S+)([^>]*)>".toPattern()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
private val SURROUND_PAIRS = mapOf(
 | 
					private val SURROUND_PAIRS = mapOf(
 | 
				
			||||||
  'b' to ("(" to ")"),
 | 
					  'b' to ("(" to ")"),
 | 
				
			||||||
@@ -348,8 +341,8 @@ private fun getSurroundPair(c: Char): Pair<String, String>? = if (c in SURROUND_
 | 
				
			|||||||
  null
 | 
					  null
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun inputTagPair(editor: Editor, context: DataContext): Pair<String, String>? {
 | 
					private fun inputTagPair(editor: Editor): Pair<String, String>? {
 | 
				
			||||||
  val tagInput = inputString(editor, context, "<", '>')
 | 
					  val tagInput = inputString(editor, "<", '>')
 | 
				
			||||||
  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)
 | 
				
			||||||
@@ -362,18 +355,17 @@ private fun inputTagPair(editor: Editor, context: DataContext): Pair<String, Str
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
private fun inputFunctionName(
 | 
					private fun inputFunctionName(
 | 
				
			||||||
  editor: Editor,
 | 
					  editor: Editor,
 | 
				
			||||||
  context: DataContext,
 | 
					 | 
				
			||||||
  withInternalSpaces: Boolean,
 | 
					  withInternalSpaces: Boolean,
 | 
				
			||||||
): Pair<String, String>? {
 | 
					): Pair<String, String>? {
 | 
				
			||||||
  val functionNameInput = inputString(editor, context, "function: ", null)
 | 
					  val functionNameInput = inputString(editor, "function: ", null)
 | 
				
			||||||
  if (functionNameInput.isEmpty()) return null
 | 
					  if (functionNameInput.isEmpty()) return null
 | 
				
			||||||
  return if (withInternalSpaces) "$functionNameInput( " to " )" else "$functionNameInput(" to ")"
 | 
					  return if (withInternalSpaces) "$functionNameInput( " to " )" else "$functionNameInput(" to ")"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun getOrInputPair(c: Char, editor: Editor, context: DataContext): Pair<String, String>? = when (c) {
 | 
					private fun getOrInputPair(c: Char, editor: Editor): Pair<String, String>? = when (c) {
 | 
				
			||||||
  '<', 't' -> inputTagPair(editor, context)
 | 
					  '<', 't' -> inputTagPair(editor)
 | 
				
			||||||
  'f' -> inputFunctionName(editor, context, false)
 | 
					  'f' -> inputFunctionName(editor, false)
 | 
				
			||||||
  'F' -> inputFunctionName(editor, context, true)
 | 
					  'F' -> inputFunctionName(editor, true)
 | 
				
			||||||
  else -> getSurroundPair(c)
 | 
					  else -> getSurroundPair(c)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -138,7 +138,7 @@ public class VimTextObjEntireExtension implements VimExtension {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
 | 
					      final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
 | 
				
			||||||
      //noinspection DuplicatedCode
 | 
					      //noinspection DuplicatedCode
 | 
				
			||||||
      if (!vimStateMachine.isOperatorPending(editor.getMode())) {
 | 
					      if (!vimStateMachine.isOperatorPending()) {
 | 
				
			||||||
        ((IjVimEditor) editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
 | 
					        ((IjVimEditor) editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
 | 
				
			||||||
          final TextRange range = textObjectHandler.getRange(editor, new IjVimCaret(caret), context, count, 0);
 | 
					          final TextRange range = textObjectHandler.getRange(editor, new IjVimCaret(caret), context, count, 0);
 | 
				
			||||||
          if (range != null) {
 | 
					          if (range != null) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -267,7 +267,7 @@ public class VimIndentObject implements VimExtension {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      final IndentObjectHandler textObjectHandler = new IndentObjectHandler(includeAbove, includeBelow);
 | 
					      final IndentObjectHandler textObjectHandler = new IndentObjectHandler(includeAbove, includeBelow);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (!vimStateMachine.isOperatorPending(editor.getMode())) {
 | 
					      if (!vimStateMachine.isOperatorPending()) {
 | 
				
			||||||
        ((IjVimEditor)editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
 | 
					        ((IjVimEditor)editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
 | 
				
			||||||
          final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, count, 0);
 | 
					          final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, count, 0);
 | 
				
			||||||
          if (range != null) {
 | 
					          if (range != null) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -68,11 +68,10 @@ 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.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.state.mode.Mode.VISUAL
 | 
					import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
					import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.mode
 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.commands.SortOption
 | 
					import com.maddyhome.idea.vim.vimscript.model.commands.SortOption
 | 
				
			||||||
import org.jetbrains.annotations.TestOnly
 | 
					import org.jetbrains.annotations.TestOnly
 | 
				
			||||||
import java.math.BigInteger
 | 
					import java.math.BigInteger
 | 
				
			||||||
@@ -197,7 +196,7 @@ public class ChangeGroup : VimChangeGroupBase() {
 | 
				
			|||||||
    val allowWrap = injector.options(editor).whichwrap.contains("~")
 | 
					    val allowWrap = injector.options(editor).whichwrap.contains("~")
 | 
				
			||||||
    var motion = injector.motion.getHorizontalMotion(editor, caret, count, true, allowWrap)
 | 
					    var motion = injector.motion.getHorizontalMotion(editor, caret, count, true, allowWrap)
 | 
				
			||||||
    if (motion is Motion.Error) return false
 | 
					    if (motion is Motion.Error) return false
 | 
				
			||||||
    changeCase(editor, caret, caret.offset, (motion as AbsoluteOffset).offset, CharacterHelper.CASE_TOGGLE)
 | 
					    changeCase(editor, caret, caret.offset.point, (motion as AbsoluteOffset).offset, CharacterHelper.CASE_TOGGLE)
 | 
				
			||||||
    motion = injector.motion.getHorizontalMotion(
 | 
					    motion = injector.motion.getHorizontalMotion(
 | 
				
			||||||
      editor,
 | 
					      editor,
 | 
				
			||||||
      caret,
 | 
					      caret,
 | 
				
			||||||
@@ -235,7 +234,8 @@ public class ChangeGroup : VimChangeGroupBase() {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      val lineLength = editor.lineLength(line)
 | 
					      val lineLength = editor.lineLength(line)
 | 
				
			||||||
      if (column < VimMotionGroupBase.LAST_COLUMN && lineLength < column) {
 | 
					      if (column < VimMotionGroupBase.LAST_COLUMN && lineLength < column) {
 | 
				
			||||||
        val pad = EditorHelper.pad((editor as IjVimEditor).editor, line, column)
 | 
					        val pad =
 | 
				
			||||||
 | 
					          EditorHelper.pad((editor as IjVimEditor).editor, (context as IjEditorExecutionContext).context, line, column)
 | 
				
			||||||
        val offset = editor.getLineEndOffset(line)
 | 
					        val offset = editor.getLineEndOffset(line)
 | 
				
			||||||
        insertText(editor, caret, offset, pad)
 | 
					        insertText(editor, caret, offset, pad)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -394,7 +394,7 @@ public class ChangeGroup : VimChangeGroupBase() {
 | 
				
			|||||||
    context: ExecutionContext,
 | 
					    context: ExecutionContext,
 | 
				
			||||||
    range: TextRange,
 | 
					    range: TextRange,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    val startPos = editor.offsetToBufferPosition(caret.offset)
 | 
					    val startPos = editor.offsetToBufferPosition(caret.offset.point)
 | 
				
			||||||
    val startOffset = editor.getLineStartForOffset(range.startOffset)
 | 
					    val startOffset = editor.getLineStartForOffset(range.startOffset)
 | 
				
			||||||
    val endOffset = editor.getLineEndForOffset(range.endOffset)
 | 
					    val endOffset = editor.getLineEndForOffset(range.endOffset)
 | 
				
			||||||
    val ijEditor = (editor as IjVimEditor).editor
 | 
					    val ijEditor = (editor as IjVimEditor).editor
 | 
				
			||||||
@@ -450,7 +450,7 @@ public class ChangeGroup : VimChangeGroupBase() {
 | 
				
			|||||||
    dir: Int,
 | 
					    dir: Int,
 | 
				
			||||||
    operatorArguments: OperatorArguments,
 | 
					    operatorArguments: OperatorArguments,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    val start = caret.offset
 | 
					    val start = caret.offset.point
 | 
				
			||||||
    val end = injector.motion.moveCaretToRelativeLineEnd(editor, caret, lines - 1, true)
 | 
					    val end = injector.motion.moveCaretToRelativeLineEnd(editor, caret, lines - 1, true)
 | 
				
			||||||
    indentRange(editor, caret, context, TextRange(start, end), 1, dir, operatorArguments)
 | 
					    indentRange(editor, caret, context, TextRange(start, end), 1, dir, operatorArguments)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -484,7 +484,7 @@ public class ChangeGroup : VimChangeGroupBase() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Remember the current caret column
 | 
					    // Remember the current caret column
 | 
				
			||||||
    val intendedColumn = caret.vimLastColumn
 | 
					    val intendedColumn = caret.vimLastColumn
 | 
				
			||||||
    val indentConfig = create((editor as IjVimEditor).editor)
 | 
					    val indentConfig = create((editor as IjVimEditor).editor, (context as IjEditorExecutionContext).context)
 | 
				
			||||||
    val sline = editor.offsetToBufferPosition(range.startOffset).line
 | 
					    val sline = editor.offsetToBufferPosition(range.startOffset).line
 | 
				
			||||||
    val endLogicalPosition = editor.offsetToBufferPosition(range.endOffset)
 | 
					    val endLogicalPosition = editor.offsetToBufferPosition(range.endOffset)
 | 
				
			||||||
    val eline = if (endLogicalPosition.column == 0) max((endLogicalPosition.line - 1).toDouble(), 0.0)
 | 
					    val eline = if (endLogicalPosition.column == 0) max((endLogicalPosition.line - 1).toDouble(), 0.0)
 | 
				
			||||||
@@ -531,7 +531,7 @@ public class ChangeGroup : VimChangeGroupBase() {
 | 
				
			|||||||
        val soff = editor.getLineStartOffset(l)
 | 
					        val soff = editor.getLineStartOffset(l)
 | 
				
			||||||
        val eoff = editor.getLineEndOffset(l, true)
 | 
					        val eoff = editor.getLineEndOffset(l, true)
 | 
				
			||||||
        val woff = injector.motion.moveCaretToLineStartSkipLeading(editor, l)
 | 
					        val woff = injector.motion.moveCaretToLineStartSkipLeading(editor, l)
 | 
				
			||||||
        val col = editor.offsetToBufferPosition(woff).column
 | 
					        val col = editor.offsetToVisualPosition(woff).column
 | 
				
			||||||
        val limit = max(0.0, (col + dir * indentConfig.getTotalIndent(count)).toDouble())
 | 
					        val limit = max(0.0, (col + dir * indentConfig.getTotalIndent(count)).toDouble())
 | 
				
			||||||
          .toInt()
 | 
					          .toInt()
 | 
				
			||||||
        if (col > 0 || soff != eoff) {
 | 
					        if (col > 0 || soff != eoff) {
 | 
				
			||||||
@@ -573,62 +573,48 @@ public class ChangeGroup : VimChangeGroupBase() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    val startOffset = editor.getLineStartOffset(startLine)
 | 
					    val startOffset = editor.getLineStartOffset(startLine)
 | 
				
			||||||
    val endOffset = editor.getLineEndOffset(endLine)
 | 
					    val endOffset = editor.getLineEndOffset(endLine)
 | 
				
			||||||
 | 
					    return sortTextRange(editor, caret, startOffset, endOffset, lineComparator, sortOptions)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val selectedText = (editor as IjVimEditor).editor.document.getText(TextRangeInterval(startOffset, endOffset))
 | 
					  /**
 | 
				
			||||||
    val lines = selectedText.split("\n")
 | 
					   * Sorts a text range with a comparator. Returns true if a replace was performed, false otherwise.
 | 
				
			||||||
    val modifiedLines = sortOptions.pattern?.let {
 | 
					   *
 | 
				
			||||||
      if (sortOptions.sortOnPattern) {
 | 
					   * @param editor         The editor to replace text in
 | 
				
			||||||
        extractPatternFromLines(editor, lines, startLine, it)
 | 
					   * @param start          The starting position for the sort
 | 
				
			||||||
      } else {
 | 
					   * @param end            The ending position for the sort
 | 
				
			||||||
        deletePatternFromLines(editor, lines, startLine, it)
 | 
					   * @param lineComparator The comparator to use to sort
 | 
				
			||||||
      }
 | 
					   * @param sortOption     The option to sort the range
 | 
				
			||||||
    } ?: lines
 | 
					   * @return true if able to sort the text, false if not
 | 
				
			||||||
    val sortedLines = lines.zip(modifiedLines)
 | 
					   */
 | 
				
			||||||
      .sortedWith { l1, l2 -> lineComparator.compare(l1.second, l2.second) }
 | 
					  private fun sortTextRange(
 | 
				
			||||||
      .map {it.first}
 | 
					    editor: VimEditor,
 | 
				
			||||||
      .toMutableList()
 | 
					    caret: VimCaret,
 | 
				
			||||||
 | 
					    start: Int,
 | 
				
			||||||
    if (sortOptions.unique) {
 | 
					    end: Int,
 | 
				
			||||||
      val iterator = sortedLines.iterator()
 | 
					    lineComparator: Comparator<String>,
 | 
				
			||||||
 | 
					    sortOption: SortOption,
 | 
				
			||||||
 | 
					  ): Boolean {
 | 
				
			||||||
 | 
					    val selectedText = (editor as IjVimEditor).editor.document.getText(TextRangeInterval(start, end))
 | 
				
			||||||
 | 
					    val lines: MutableList<String> = selectedText.split("\n").sortedWith(lineComparator).toMutableList()
 | 
				
			||||||
 | 
					    if (sortOption.unique) {
 | 
				
			||||||
 | 
					      val iterator = lines.iterator()
 | 
				
			||||||
      var previous: String? = null
 | 
					      var previous: String? = null
 | 
				
			||||||
      while (iterator.hasNext()) {
 | 
					      while (iterator.hasNext()) {
 | 
				
			||||||
        val current = iterator.next()
 | 
					        val current = iterator.next()
 | 
				
			||||||
        if (current == previous || sortOptions.ignoreCase && current.equals(previous, ignoreCase = true)) {
 | 
					        if (current == previous || sortOption.ignoreCase && current.equals(previous, ignoreCase = true)) {
 | 
				
			||||||
          iterator.remove()
 | 
					          iterator.remove()
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          previous = current
 | 
					          previous = current
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (sortedLines.isEmpty()) {
 | 
					    if (lines.size < 1) {
 | 
				
			||||||
      return false
 | 
					      return false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    replaceText(editor, caret, startOffset, endOffset, StringUtil.join(sortedLines, "\n"))
 | 
					    replaceText(editor, caret, start, end, StringUtil.join(lines, "\n"))
 | 
				
			||||||
    return true
 | 
					    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
 | 
					   * Perform increment and decrement for numbers in visual mode
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,11 +8,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.maddyhome.idea.vim.group
 | 
					package com.maddyhome.idea.vim.group
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.components.Service
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimCommandGroupBase
 | 
					import com.maddyhome.idea.vim.api.VimCommandGroupBase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @author Elliot Courant
 | 
					 * @author Elliot Courant
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@Service
 | 
					 | 
				
			||||||
internal class CommandGroup : VimCommandGroupBase()
 | 
					internal class CommandGroup : VimCommandGroupBase()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,9 +11,6 @@ package com.maddyhome.idea.vim.group;
 | 
				
			|||||||
import com.intellij.execution.impl.ConsoleViewImpl;
 | 
					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.application.ApplicationManager;
 | 
				
			||||||
import com.intellij.openapi.client.ClientAppSession;
 | 
					 | 
				
			||||||
import com.intellij.openapi.client.ClientKind;
 | 
					 | 
				
			||||||
import com.intellij.openapi.client.ClientSessionsManager;
 | 
					 | 
				
			||||||
import com.intellij.openapi.components.PersistentStateComponent;
 | 
					import com.intellij.openapi.components.PersistentStateComponent;
 | 
				
			||||||
import com.intellij.openapi.components.State;
 | 
					import com.intellij.openapi.components.State;
 | 
				
			||||||
import com.intellij.openapi.components.Storage;
 | 
					import com.intellij.openapi.components.Storage;
 | 
				
			||||||
@@ -25,10 +22,7 @@ import com.intellij.openapi.project.Project;
 | 
				
			|||||||
import com.maddyhome.idea.vim.KeyHandler;
 | 
					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.helper.CaretVisualAttributesHelperKt;
 | 
					import com.maddyhome.idea.vim.helper.*;
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.CommandStateHelper;
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.helper.EditorHelper;
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.helper.UserDataManager;
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimDocument;
 | 
					import com.maddyhome.idea.vim.newapi.IjVimDocument;
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
				
			||||||
import com.maddyhome.idea.vim.options.EffectiveOptionValueChangeListener;
 | 
					import com.maddyhome.idea.vim.options.EffectiveOptionValueChangeListener;
 | 
				
			||||||
@@ -40,10 +34,10 @@ import org.jetbrains.annotations.Nullable;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import java.util.Collection;
 | 
					import java.util.Collection;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
import java.util.stream.Stream;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
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.api.VimInjectorKt.options;
 | 
				
			||||||
 | 
					import static com.maddyhome.idea.vim.helper.CaretVisualAttributesHelperKt.updateCaretsVisualAttributes;
 | 
				
			||||||
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.ijOptions;
 | 
					import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.ijOptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -210,8 +204,7 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void editorCreated(@NotNull Editor editor) {
 | 
					  public void editorCreated(@NotNull Editor editor) {
 | 
				
			||||||
    UserDataManager.setVimInitialised(editor, true);
 | 
					    DocumentManager.INSTANCE.addListeners(editor.getDocument());
 | 
				
			||||||
 | 
					 | 
				
			||||||
    VimPlugin.getKey().registerRequiredShortcutKeys(new IjVimEditor(editor));
 | 
					    VimPlugin.getKey().registerRequiredShortcutKeys(new IjVimEditor(editor));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    initLineNumbers(editor);
 | 
					    initLineNumbers(editor);
 | 
				
			||||||
@@ -235,7 +228,7 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
 | 
				
			|||||||
    // Note that we need a similar check in `VimEditor.isWritable` to allow Escape to work to exit insert mode. We need
 | 
					    // 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.
 | 
					    // to know that a read-only editor that is hosting a console view with a running process can be treated as writable.
 | 
				
			||||||
    Runnable switchToInsertMode = () -> {
 | 
					    Runnable switchToInsertMode = () -> {
 | 
				
			||||||
      ExecutionContext context = injector.getExecutionContextManager().getEditorExecutionContext(new IjVimEditor(editor));
 | 
					      ExecutionContext.Editor context = injector.getExecutionContextManager().onEditor(new IjVimEditor(editor), null);
 | 
				
			||||||
      VimPlugin.getChange().insertBeforeCursor(new IjVimEditor(editor), context);
 | 
					      VimPlugin.getChange().insertBeforeCursor(new IjVimEditor(editor), context);
 | 
				
			||||||
      KeyHandler.getInstance().reset(new IjVimEditor(editor));
 | 
					      KeyHandler.getInstance().reset(new IjVimEditor(editor));
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
@@ -253,13 +246,14 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
 | 
				
			|||||||
          switchToInsertMode.run();
 | 
					          switchToInsertMode.run();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    updateCaretsVisualAttributes(new IjVimEditor(editor));
 | 
					    updateCaretsVisualAttributes(editor);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void editorDeinit(@NotNull Editor editor, boolean isReleased) {
 | 
					  public void editorDeinit(@NotNull Editor editor, boolean isReleased) {
 | 
				
			||||||
    deinitLineNumbers(editor, isReleased);
 | 
					    deinitLineNumbers(editor, isReleased);
 | 
				
			||||||
    UserDataManager.unInitializeEditor(editor);
 | 
					    UserDataManager.unInitializeEditor(editor);
 | 
				
			||||||
    VimPlugin.getKey().unregisterShortcutKeys(new IjVimEditor(editor));
 | 
					    VimPlugin.getKey().unregisterShortcutKeys(new IjVimEditor(editor));
 | 
				
			||||||
 | 
					    DocumentManager.INSTANCE.removeListeners(editor.getDocument());
 | 
				
			||||||
    CaretVisualAttributesHelperKt.removeCaretsVisualAttributes(editor);
 | 
					    CaretVisualAttributesHelperKt.removeCaretsVisualAttributes(editor);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -290,18 +284,6 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
 | 
				
			|||||||
    notifyIdeaJoin(((IjVimEditor) editor).getEditor().getProject(), editor);
 | 
					    notifyIdeaJoin(((IjVimEditor) editor).getEditor().getProject(), editor);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public void updateCaretsVisualAttributes(@NotNull VimEditor editor) {
 | 
					 | 
				
			||||||
    Editor ijEditor = ((IjVimEditor) editor).getEditor();
 | 
					 | 
				
			||||||
    CaretVisualAttributesHelperKt.updateCaretsVisualAttributes(ijEditor);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public void updateCaretsVisualPosition(@NotNull VimEditor editor) {
 | 
					 | 
				
			||||||
    Editor ijEditor = ((IjVimEditor) editor).getEditor();
 | 
					 | 
				
			||||||
    CaretVisualAttributesHelperKt.updateCaretsVisualAttributes(ijEditor);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public static class NumberChangeListener implements EffectiveOptionValueChangeListener {
 | 
					  public static class NumberChangeListener implements EffectiveOptionValueChangeListener {
 | 
				
			||||||
    public static NumberChangeListener INSTANCE = new NumberChangeListener();
 | 
					    public static NumberChangeListener INSTANCE = new NumberChangeListener();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -342,45 +324,20 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @NotNull
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public @NotNull Collection<VimEditor> getEditorsRaw() {
 | 
					  public Collection<VimEditor> localEditors() {
 | 
				
			||||||
    return getLocalEditors()
 | 
					    return HelperKt.localEditors().stream()
 | 
				
			||||||
      .map(IjVimEditor::new)
 | 
					      .map(IjVimEditor::new)
 | 
				
			||||||
      .collect(Collectors.toList());
 | 
					      .collect(Collectors.toList());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @NotNull
 | 
					  @NotNull
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public Collection<VimEditor> getEditors() {
 | 
					  public Collection<VimEditor> localEditors(@NotNull VimDocument buffer) {
 | 
				
			||||||
    return getLocalEditors()
 | 
					 | 
				
			||||||
      .filter(UserDataManager::getVimInitialised)
 | 
					 | 
				
			||||||
      .map(IjVimEditor::new)
 | 
					 | 
				
			||||||
      .collect(Collectors.toList());
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @NotNull
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public Collection<VimEditor> getEditors(@NotNull VimDocument buffer) {
 | 
					 | 
				
			||||||
    final Document document = ((IjVimDocument)buffer).getDocument();
 | 
					    final Document document = ((IjVimDocument)buffer).getDocument();
 | 
				
			||||||
    return getLocalEditors()
 | 
					    return HelperKt.localEditors(document).stream()
 | 
				
			||||||
      .filter(editor -> UserDataManager.getVimInitialised(editor) && editor.getDocument().equals(document))
 | 
					 | 
				
			||||||
      .map(IjVimEditor::new)
 | 
					      .map(IjVimEditor::new)
 | 
				
			||||||
      .collect(Collectors.toList());
 | 
					      .collect(Collectors.toList());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  private Stream<Editor> getLocalEditors() {
 | 
					 | 
				
			||||||
    // Always fetch local editors. If we're hosting a Code With Me session, any connected guests will create hidden
 | 
					 | 
				
			||||||
    // editors to handle syntax highlighting, completion requests, etc. We need to make sure that IdeaVim only makes
 | 
					 | 
				
			||||||
    // changes (e.g. adding search highlights) to local editors, so things don't incorrectly flow through to any Clients.
 | 
					 | 
				
			||||||
    // In non-CWM scenarios, or if IdeaVim is installed on the Client, there are only ever local editors, so this will
 | 
					 | 
				
			||||||
    // also work there. In Gateway remote development scenarios, IdeaVim should not be installed on the host, only the
 | 
					 | 
				
			||||||
    // Client, so all should work there too.
 | 
					 | 
				
			||||||
    // Note that most IdeaVim operations are in response to interactive keystrokes, which would mean that
 | 
					 | 
				
			||||||
    // ClientEditorManager.getCurrentInstance would return local editors. However, some operations are in response to
 | 
					 | 
				
			||||||
    // events such as document change (to update search highlights) and these can come from CWM guests, and we'd get the
 | 
					 | 
				
			||||||
    // remote editors.
 | 
					 | 
				
			||||||
    // This invocation will always get local editors, regardless of current context.
 | 
					 | 
				
			||||||
    final ClientAppSession localSession = ClientSessionsManager.getAppSessions(ClientKind.LOCAL).get(0);
 | 
					 | 
				
			||||||
    return localSession.getService(ClientEditorManager.class).editors();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,17 +22,16 @@ import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
 | 
				
			|||||||
import com.intellij.openapi.fileTypes.FileType;
 | 
					import com.intellij.openapi.fileTypes.FileType;
 | 
				
			||||||
import com.intellij.openapi.fileTypes.FileTypeManager;
 | 
					import com.intellij.openapi.fileTypes.FileTypeManager;
 | 
				
			||||||
import com.intellij.openapi.project.Project;
 | 
					import com.intellij.openapi.project.Project;
 | 
				
			||||||
import com.intellij.openapi.project.ProjectManager;
 | 
					 | 
				
			||||||
import com.intellij.openapi.roots.ProjectRootManager;
 | 
					import com.intellij.openapi.roots.ProjectRootManager;
 | 
				
			||||||
import com.intellij.openapi.vfs.LocalFileSystem;
 | 
					import com.intellij.openapi.vfs.LocalFileSystem;
 | 
				
			||||||
import com.intellij.openapi.vfs.VirtualFile;
 | 
					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.FilenameIndex;
 | 
				
			||||||
import com.intellij.psi.search.GlobalSearchScope;
 | 
					import com.intellij.psi.search.GlobalSearchScope;
 | 
				
			||||||
import com.intellij.psi.search.ProjectScope;
 | 
					import com.intellij.psi.search.ProjectScope;
 | 
				
			||||||
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.state.mode.Mode;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.VimStateMachine;
 | 
				
			||||||
import com.maddyhome.idea.vim.common.TextRange;
 | 
					import com.maddyhome.idea.vim.common.TextRange;
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.EditorHelper;
 | 
					import com.maddyhome.idea.vim.helper.EditorHelper;
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.EditorHelperRt;
 | 
					import com.maddyhome.idea.vim.helper.EditorHelperRt;
 | 
				
			||||||
@@ -41,13 +40,10 @@ import com.maddyhome.idea.vim.helper.SearchHelper;
 | 
				
			|||||||
import com.maddyhome.idea.vim.newapi.ExecuteExtensionKt;
 | 
					import com.maddyhome.idea.vim.newapi.ExecuteExtensionKt;
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext;
 | 
					import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext;
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
					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.NotNull;
 | 
				
			||||||
import org.jetbrains.annotations.Nullable;
 | 
					import org.jetbrains.annotations.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.File;
 | 
					import java.io.File;
 | 
				
			||||||
import java.util.Arrays;
 | 
					 | 
				
			||||||
import java.util.Collection;
 | 
					import java.util.Collection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
 | 
					import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
 | 
				
			||||||
@@ -442,35 +438,14 @@ public class FileGroup extends VimFileBase {
 | 
				
			|||||||
  private static final @NotNull Logger logger = Logger.getInstance(FileGroup.class.getName());
 | 
					  private static final @NotNull Logger logger = Logger.getInstance(FileGroup.class.getName());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Respond to editor tab selection and remember the last used tab
 | 
					   * This method listens for editor tab changes so any insert/replace modes that need to be reset can be.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public static void fileEditorManagerSelectionChangedCallback(@NotNull FileEditorManagerEvent event) {
 | 
					  public static void fileEditorManagerSelectionChangedCallback(@NotNull FileEditorManagerEvent event) {
 | 
				
			||||||
 | 
					    // The user has changed the editor they are working with - exit insert/replace mode, and complete any
 | 
				
			||||||
 | 
					    // appropriate repeat
 | 
				
			||||||
    if (event.getOldFile() != null) {
 | 
					    if (event.getOldFile() != null) {
 | 
				
			||||||
      LastTabService.getInstance(event.getManager().getProject()).setLastTab(event.getOldFile());
 | 
					      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();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,7 @@ import com.maddyhome.idea.vim.options.OptionAccessScope
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
@Suppress("SpellCheckingInspection")
 | 
					@Suppress("SpellCheckingInspection")
 | 
				
			||||||
public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(scope) {
 | 
					public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(scope) {
 | 
				
			||||||
 | 
					  public var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
 | 
				
			||||||
  public var ide: String by optionProperty(IjOptions.ide)
 | 
					  public var ide: String by optionProperty(IjOptions.ide)
 | 
				
			||||||
  public var ideamarks: Boolean by optionProperty(IjOptions.ideamarks)
 | 
					  public var ideamarks: Boolean by optionProperty(IjOptions.ideamarks)
 | 
				
			||||||
  public var ideastatusicon: String by optionProperty(IjOptions.ideastatusicon)
 | 
					  public var ideastatusicon: String by optionProperty(IjOptions.ideastatusicon)
 | 
				
			||||||
@@ -28,15 +29,15 @@ public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesB
 | 
				
			|||||||
  public val lookupkeys: StringListOptionValue by optionProperty(IjOptions.lookupkeys)
 | 
					  public val lookupkeys: StringListOptionValue by optionProperty(IjOptions.lookupkeys)
 | 
				
			||||||
  public var trackactionids: Boolean by optionProperty(IjOptions.trackactionids)
 | 
					  public var trackactionids: Boolean by optionProperty(IjOptions.trackactionids)
 | 
				
			||||||
  public var visualdelay: Int by optionProperty(IjOptions.visualdelay)
 | 
					  public var visualdelay: Int by optionProperty(IjOptions.visualdelay)
 | 
				
			||||||
 | 
					  public var showmodewidget: Boolean by optionProperty(IjOptions.showmodewidget)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Temporary options to control work-in-progress behaviour
 | 
					  // Temporary options to control work-in-progress behaviour
 | 
				
			||||||
  public var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
 | 
					 | 
				
			||||||
  public var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
 | 
					 | 
				
			||||||
  public var exCommandAnnotation: Boolean by optionProperty(IjOptions.exCommandAnnotation)
 | 
					 | 
				
			||||||
  public var oldundo: Boolean by optionProperty(IjOptions.oldundo)
 | 
					  public var oldundo: Boolean by optionProperty(IjOptions.oldundo)
 | 
				
			||||||
  public var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
 | 
					  public var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
 | 
				
			||||||
  public var useNewRegex: Boolean by optionProperty(IjOptions.useNewRegex)
 | 
					  public var exCommandAnnotation: Boolean by optionProperty(IjOptions.exCommandAnnotation)
 | 
				
			||||||
  public var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
 | 
					  public var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
 | 
				
			||||||
 | 
					  public var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
 | 
				
			||||||
 | 
					  public var useNewRegex: Boolean by optionProperty(IjOptions.useNewRegex)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,6 +33,8 @@ public object IjOptions {
 | 
				
			|||||||
    Options.overrideDefaultValue(Options.clipboard, VimString("ideaput,autoselect,exclude:cons\\|linux"))
 | 
					    Options.overrideDefaultValue(Options.clipboard, VimString("ideaput,autoselect,exclude:cons\\|linux"))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true))
 | 
				
			||||||
 | 
					  public val exCommandAnnotation: ToggleOption = addOption(ToggleOption("excommandannotation", GLOBAL, "excommandannotation", true))
 | 
				
			||||||
  public val ide: StringOption = addOption(
 | 
					  public val ide: StringOption = addOption(
 | 
				
			||||||
    StringOption("ide", GLOBAL, "ide", ApplicationNamesInfo.getInstance().fullProductNameWithEdition)
 | 
					    StringOption("ide", GLOBAL, "ide", ApplicationNamesInfo.getInstance().fullProductNameWithEdition)
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
@@ -79,16 +81,13 @@ public object IjOptions {
 | 
				
			|||||||
      "<Tab>,<Down>,<Up>,<Enter>,<Left>,<Right>,<C-Down>,<C-Up>,<PageUp>,<PageDown>,<C-J>,<C-Q>")
 | 
					      "<Tab>,<Down>,<Up>,<Enter>,<Left>,<Right>,<C-Down>,<C-Up>,<PageUp>,<PageDown>,<C-J>,<C-Q>")
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
  public val trackactionids: ToggleOption = addOption(ToggleOption("trackactionids", GLOBAL, "tai", false))
 | 
					  public val trackactionids: ToggleOption = addOption(ToggleOption("trackactionids", GLOBAL, "tai", false))
 | 
				
			||||||
 | 
					  public val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true))
 | 
				
			||||||
  public val visualdelay: UnsignedNumberOption = addOption(UnsignedNumberOption("visualdelay", GLOBAL, "visualdelay", 100))
 | 
					  public val visualdelay: UnsignedNumberOption = addOption(UnsignedNumberOption("visualdelay", GLOBAL, "visualdelay", 100))
 | 
				
			||||||
 | 
					  public val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isTemporary = true))
 | 
				
			||||||
  // Temporary feature flags during development, not really intended for external use
 | 
					  public val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isTemporary = true))
 | 
				
			||||||
  public val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true))
 | 
					  public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isTemporary = true))
 | 
				
			||||||
  public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true))
 | 
					  public val showmodewidget: ToggleOption = addOption(ToggleOption("showmodewidget", GLOBAL, "showmodewidget", false, isTemporary = true))
 | 
				
			||||||
  public val exCommandAnnotation: ToggleOption = addOption(ToggleOption("excommandannotation", GLOBAL, "excommandannotation", true, isHidden = true))
 | 
					  public val useNewRegex: ToggleOption = addOption(ToggleOption("usenewregex", GLOBAL, "usenewregex", true, isTemporary = true))
 | 
				
			||||||
  public val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isHidden = true))
 | 
					 | 
				
			||||||
  public val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true))
 | 
					 | 
				
			||||||
  public val useNewRegex: ToggleOption = addOption(ToggleOption("usenewregex", GLOBAL, "usenewregex", true, isHidden = true))
 | 
					 | 
				
			||||||
  public val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isHidden = true))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // This needs to be Option<out VimDataType> so that it can work with derived option types, such as NumberOption, which
 | 
					  // This needs to be Option<out VimDataType> so that it can work with derived option types, such as NumberOption, which
 | 
				
			||||||
  // derives from Option<VimInt>
 | 
					  // derives from Option<VimInt>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,38 +15,38 @@ import com.maddyhome.idea.vim.statistic.VimscriptState
 | 
				
			|||||||
internal class IjStatisticsService : VimStatistics {
 | 
					internal class IjStatisticsService : VimStatistics {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun logTrackedAction(actionId: String) {
 | 
					  override fun logTrackedAction(actionId: String) {
 | 
				
			||||||
    ActionTracker.Util.logTrackedAction(actionId)
 | 
					    ActionTracker.logTrackedAction(actionId)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun logCopiedAction(actionId: String) {
 | 
					  override fun logCopiedAction(actionId: String) {
 | 
				
			||||||
    ActionTracker.Util.logCopiedAction(actionId)
 | 
					    ActionTracker.logCopiedAction(actionId)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun setIfLoopUsed(value: Boolean) {
 | 
					  override fun setIfLoopUsed(value: Boolean) {
 | 
				
			||||||
    VimscriptState.Util.isLoopUsed = value
 | 
					    VimscriptState.isLoopUsed = value
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun setIfMapExprUsed(value: Boolean) {
 | 
					  override fun setIfMapExprUsed(value: Boolean) {
 | 
				
			||||||
    VimscriptState.Util.isMapExprUsed = value
 | 
					    VimscriptState.isMapExprUsed = value
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun setIfFunctionCallUsed(value: Boolean) {
 | 
					  override fun setIfFunctionCallUsed(value: Boolean) {
 | 
				
			||||||
    VimscriptState.Util.isFunctionCallUsed = value
 | 
					    VimscriptState.isFunctionCallUsed = value
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun setIfFunctionDeclarationUsed(value: Boolean) {
 | 
					  override fun setIfFunctionDeclarationUsed(value: Boolean) {
 | 
				
			||||||
    VimscriptState.Util.isFunctionDeclarationUsed = value
 | 
					    VimscriptState.isFunctionDeclarationUsed = value
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun setIfIfUsed(value: Boolean) {
 | 
					  override fun setIfIfUsed(value: Boolean) {
 | 
				
			||||||
    VimscriptState.Util.isIfUsed = value
 | 
					    VimscriptState.isIfUsed = value
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun addExtensionEnabledWithPlug(extension: String) {
 | 
					  override fun addExtensionEnabledWithPlug(extension: String) {
 | 
				
			||||||
    VimscriptState.Util.extensionsEnabledWithPlug.add(extension)
 | 
					    VimscriptState.extensionsEnabledWithPlug.add(extension)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun addSourcedFile(path: String) {
 | 
					  override fun addSourcedFile(path: String) {
 | 
				
			||||||
    VimscriptState.Util.sourcedFiles.add(path)
 | 
					    VimscriptState.sourcedFiles.add(path)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,61 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * Copyright 2003-2024 The IdeaVim authors
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Use of this source code is governed by an MIT-style
 | 
					 | 
				
			||||||
 * license that can be found in the LICENSE.txt file or at
 | 
					 | 
				
			||||||
 * https://opensource.org/licenses/MIT.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package com.maddyhome.idea.vim.group
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.intellij.lang.CodeDocumentationAwareCommenter
 | 
					 | 
				
			||||||
import com.intellij.lang.LanguageCommenters
 | 
					 | 
				
			||||||
import com.intellij.openapi.components.Service
 | 
					 | 
				
			||||||
import com.intellij.psi.PsiComment
 | 
					 | 
				
			||||||
import com.intellij.psi.util.PsiTreeUtil
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimPsiService
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.common.TextRange
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.helper.PsiHelper
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.ij
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Service
 | 
					 | 
				
			||||||
public class IjVimPsiService: VimPsiService {
 | 
					 | 
				
			||||||
  override fun getCommentAtPos(editor: VimEditor, pos: Int): Pair<TextRange, Pair<String, String>?>? {
 | 
					 | 
				
			||||||
    val psiFile = PsiHelper.getFile(editor.ij) ?: return null
 | 
					 | 
				
			||||||
    val psiElement = psiFile.findElementAt(pos) ?: return null
 | 
					 | 
				
			||||||
    val language = psiElement.language
 | 
					 | 
				
			||||||
    val commenter = LanguageCommenters.INSTANCE.forLanguage(language)
 | 
					 | 
				
			||||||
    val psiComment = PsiTreeUtil.getParentOfType(psiElement, PsiComment::class.java, false) ?: return null
 | 
					 | 
				
			||||||
    val commentText = psiComment.text
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val blockCommentPrefix = commenter.blockCommentPrefix
 | 
					 | 
				
			||||||
    val blockCommentSuffix = commenter.blockCommentSuffix
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val docCommentPrefix = (commenter as? CodeDocumentationAwareCommenter)?.documentationCommentPrefix
 | 
					 | 
				
			||||||
    val docCommentSuffix = (commenter as? CodeDocumentationAwareCommenter)?.documentationCommentSuffix
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val prefixToSuffix: Pair<String, String>? =
 | 
					 | 
				
			||||||
      if (docCommentPrefix != null && docCommentSuffix != null && commentText.startsWith(docCommentPrefix) && commentText.endsWith(docCommentSuffix)) {
 | 
					 | 
				
			||||||
        docCommentPrefix to docCommentSuffix
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      else if (blockCommentPrefix != null && blockCommentSuffix != null && commentText.startsWith(blockCommentPrefix) && commentText.endsWith(blockCommentSuffix)) {
 | 
					 | 
				
			||||||
        blockCommentPrefix to blockCommentSuffix
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      else {
 | 
					 | 
				
			||||||
        null
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    return Pair(psiComment.textRange.vim, prefixToSuffix)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  override fun getDoubleQuotedString(editor: VimEditor, pos: Int, isInner: Boolean): TextRange? {
 | 
					 | 
				
			||||||
    // TODO[ideavim] It wasn't implemented before, but implementing it will significantly improve % motion
 | 
					 | 
				
			||||||
    return getDoubleQuotesRangeNoPSI(editor.text(), pos, isInner)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  override fun getSingleQuotedString(editor: VimEditor, pos: Int, isInner: Boolean): TextRange? {
 | 
					 | 
				
			||||||
    // TODO[ideavim] It wasn't implemented before, but implementing it will significantly improve % motion
 | 
					 | 
				
			||||||
    return getSingleQuotesRangeNoPSI(editor.text(), pos, isInner)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -26,12 +26,10 @@ import com.maddyhome.idea.vim.EventFacade;
 | 
				
			|||||||
import com.maddyhome.idea.vim.VimPlugin;
 | 
					import com.maddyhome.idea.vim.VimPlugin;
 | 
				
			||||||
import com.maddyhome.idea.vim.action.VimShortcutKeyAction;
 | 
					import com.maddyhome.idea.vim.action.VimShortcutKeyAction;
 | 
				
			||||||
import com.maddyhome.idea.vim.action.change.LazyVimCommand;
 | 
					import com.maddyhome.idea.vim.action.change.LazyVimCommand;
 | 
				
			||||||
import com.maddyhome.idea.vim.api.NativeAction;
 | 
					import com.maddyhome.idea.vim.api.*;
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimEditor;
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimInjectorKt;
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimKeyGroupBase;
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.command.MappingMode;
 | 
					import com.maddyhome.idea.vim.command.MappingMode;
 | 
				
			||||||
import com.maddyhome.idea.vim.ex.ExOutputModel;
 | 
					import com.maddyhome.idea.vim.ex.ExOutputModel;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.HelperKt;
 | 
				
			||||||
import com.maddyhome.idea.vim.key.*;
 | 
					import com.maddyhome.idea.vim.key.*;
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjNativeAction;
 | 
					import com.maddyhome.idea.vim.newapi.IjNativeAction;
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
				
			||||||
@@ -101,9 +99,9 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public void updateShortcutKeysRegistration() {
 | 
					  public void updateShortcutKeysRegistration() {
 | 
				
			||||||
    for (VimEditor editor : injector.getEditorGroup().getEditors()) {
 | 
					    for (Editor editor : HelperKt.localEditors()) {
 | 
				
			||||||
      unregisterShortcutKeys(editor);
 | 
					      unregisterShortcutKeys(new IjVimEditor(editor));
 | 
				
			||||||
      registerRequiredShortcutKeys(editor);
 | 
					      registerRequiredShortcutKeys(new IjVimEditor(editor));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -230,7 +228,7 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
 | 
				
			|||||||
  private void registerRequiredShortcut(@NotNull List<KeyStroke> keys, MappingOwner owner) {
 | 
					  private void registerRequiredShortcut(@NotNull List<KeyStroke> keys, MappingOwner owner) {
 | 
				
			||||||
    for (KeyStroke key : keys) {
 | 
					    for (KeyStroke key : keys) {
 | 
				
			||||||
      if (key.getKeyChar() == KeyEvent.CHAR_UNDEFINED) {
 | 
					      if (key.getKeyChar() == KeyEvent.CHAR_UNDEFINED) {
 | 
				
			||||||
        if (!injector.getApplication().isOctopusEnabled() ||
 | 
					        if (!injector.getOptionGroup().getGlobalOptions().getOctopushandler() ||
 | 
				
			||||||
            !(key.getKeyCode() == KeyEvent.VK_ESCAPE && key.getModifiers() == 0) &&
 | 
					            !(key.getKeyCode() == KeyEvent.VK_ESCAPE && key.getModifiers() == 0) &&
 | 
				
			||||||
            !(key.getKeyCode() == KeyEvent.VK_ENTER && key.getModifiers() == 0)) {
 | 
					            !(key.getKeyCode() == KeyEvent.VK_ENTER && key.getModifiers() == 0)) {
 | 
				
			||||||
          getRequiredShortcutKeys().add(new RequiredShortcut(key, owner));
 | 
					          getRequiredShortcutKeys().add(new RequiredShortcut(key, owner));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,10 +7,7 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
package com.maddyhome.idea.vim.group
 | 
					package com.maddyhome.idea.vim.group
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.codeInsight.completion.CompletionPhase
 | 
					 | 
				
			||||||
import com.intellij.codeInsight.completion.impl.CompletionServiceImpl
 | 
					 | 
				
			||||||
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.progress.ProcessCanceledException
 | 
					import com.intellij.openapi.progress.ProcessCanceledException
 | 
				
			||||||
import com.intellij.openapi.progress.ProgressManager
 | 
					import com.intellij.openapi.progress.ProgressManager
 | 
				
			||||||
@@ -27,7 +24,6 @@ import com.maddyhome.idea.vim.newapi.ij
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * Used to handle playback of macros
 | 
					 * Used to handle playback of macros
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@Service
 | 
					 | 
				
			||||||
internal class MacroGroup : VimMacroBase() {
 | 
					internal class MacroGroup : VimMacroBase() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // If it's null, this is the top macro (as in most cases). If it's not null, this macro is executed from top macro
 | 
					  // If it's null, this is the top macro (as in most cases). If it's not null, this macro is executed from top macro
 | 
				
			||||||
@@ -78,12 +74,11 @@ internal class MacroGroup : VimMacroBase() {
 | 
				
			|||||||
                } catch (e: ProcessCanceledException) {
 | 
					                } catch (e: ProcessCanceledException) {
 | 
				
			||||||
                  return@runnable
 | 
					                  return@runnable
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                val keyHandler = getInstance()
 | 
					 | 
				
			||||||
                ProgressManager.getInstance().executeNonCancelableSection {
 | 
					                ProgressManager.getInstance().executeNonCancelableSection {
 | 
				
			||||||
                  // Prevent autocompletion during macros.
 | 
					                  // Prevent autocompletion during macros.
 | 
				
			||||||
                  // See https://github.com/JetBrains/ideavim/pull/772 for details
 | 
					                  // See https://github.com/JetBrains/ideavim/pull/772 for details
 | 
				
			||||||
                  CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion)
 | 
					//                  CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion)
 | 
				
			||||||
                  keyHandler.handleKey(editor, key, context, keyHandler.keyHandlerState)
 | 
					                  getInstance().handleKey(editor, key, context)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (injector.messages.isError()) return@runnable
 | 
					                if (injector.messages.isError()) return@runnable
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,27 +8,43 @@
 | 
				
			|||||||
package com.maddyhome.idea.vim.group
 | 
					package com.maddyhome.idea.vim.group
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.actionSystem.DataContext
 | 
					import com.intellij.openapi.actionSystem.DataContext
 | 
				
			||||||
import com.intellij.openapi.components.Service
 | 
					 | 
				
			||||||
import com.intellij.openapi.editor.Caret
 | 
					import com.intellij.openapi.editor.Caret
 | 
				
			||||||
import com.intellij.openapi.editor.Editor
 | 
					import com.intellij.openapi.editor.Editor
 | 
				
			||||||
 | 
					import com.intellij.openapi.editor.LogicalPosition
 | 
				
			||||||
import com.intellij.openapi.editor.VisualPosition
 | 
					import com.intellij.openapi.editor.VisualPosition
 | 
				
			||||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
 | 
					import com.intellij.openapi.fileEditor.FileEditorManagerEvent
 | 
				
			||||||
import com.intellij.openapi.fileEditor.TextEditor
 | 
					import com.intellij.openapi.fileEditor.TextEditor
 | 
				
			||||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
 | 
					import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
 | 
				
			||||||
import com.intellij.openapi.fileEditor.impl.EditorWindow
 | 
					import com.intellij.openapi.fileEditor.impl.EditorWindow
 | 
				
			||||||
 | 
					import com.intellij.openapi.project.Project
 | 
				
			||||||
 | 
					import com.intellij.openapi.vfs.LocalFileSystem
 | 
				
			||||||
 | 
					import com.intellij.openapi.vfs.VirtualFile
 | 
				
			||||||
 | 
					import com.intellij.openapi.vfs.VirtualFileManager
 | 
				
			||||||
 | 
					import com.intellij.openapi.vfs.VirtualFileSystem
 | 
				
			||||||
 | 
					import com.intellij.util.MathUtil.clamp
 | 
				
			||||||
import com.maddyhome.idea.vim.KeyHandler
 | 
					import com.maddyhome.idea.vim.KeyHandler
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.VimPlugin
 | 
				
			||||||
 | 
					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.ImmutableVimCaret
 | 
					import com.maddyhome.idea.vim.api.ImmutableVimCaret
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimCaret
 | 
					import com.maddyhome.idea.vim.api.VimCaret
 | 
				
			||||||
import com.maddyhome.idea.vim.api.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.VimMotionGroupBase
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.addJump
 | 
				
			||||||
import com.maddyhome.idea.vim.api.anyNonWhitespace
 | 
					import com.maddyhome.idea.vim.api.anyNonWhitespace
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.getJump
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.getJumpSpot
 | 
				
			||||||
import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
 | 
					import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
 | 
				
			||||||
import com.maddyhome.idea.vim.api.getVisualLineCount
 | 
					import com.maddyhome.idea.vim.api.getVisualLineCount
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.injector
 | 
				
			||||||
import com.maddyhome.idea.vim.api.lineLength
 | 
					import com.maddyhome.idea.vim.api.lineLength
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.normalizeColumn
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.normalizeLine
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.normalizeOffset
 | 
				
			||||||
import com.maddyhome.idea.vim.api.normalizeVisualColumn
 | 
					import com.maddyhome.idea.vim.api.normalizeVisualColumn
 | 
				
			||||||
import com.maddyhome.idea.vim.api.normalizeVisualLine
 | 
					import com.maddyhome.idea.vim.api.normalizeVisualLine
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.options
 | 
				
			||||||
import com.maddyhome.idea.vim.api.visualLineToBufferLine
 | 
					import com.maddyhome.idea.vim.api.visualLineToBufferLine
 | 
				
			||||||
import com.maddyhome.idea.vim.command.Argument
 | 
					import com.maddyhome.idea.vim.command.Argument
 | 
				
			||||||
import com.maddyhome.idea.vim.command.MotionType
 | 
					import com.maddyhome.idea.vim.command.MotionType
 | 
				
			||||||
@@ -37,9 +53,12 @@ import com.maddyhome.idea.vim.common.TextRange
 | 
				
			|||||||
import com.maddyhome.idea.vim.ex.ExOutputModel
 | 
					import com.maddyhome.idea.vim.ex.ExOutputModel
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.Motion
 | 
					import com.maddyhome.idea.vim.handler.Motion
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
 | 
					import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.handler.Motion.AdjustedOffset
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.MotionActionHandler
 | 
					import com.maddyhome.idea.vim.handler.MotionActionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
 | 
					import com.maddyhome.idea.vim.handler.TextObjectActionHandler
 | 
				
			||||||
 | 
					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.SearchHelper
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.exitVisualMode
 | 
					import com.maddyhome.idea.vim.helper.exitVisualMode
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.fileSize
 | 
					import com.maddyhome.idea.vim.helper.fileSize
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.getNormalizedScrollOffset
 | 
					import com.maddyhome.idea.vim.helper.getNormalizedScrollOffset
 | 
				
			||||||
@@ -47,25 +66,46 @@ import com.maddyhome.idea.vim.helper.getNormalizedSideScrollOffset
 | 
				
			|||||||
import com.maddyhome.idea.vim.helper.isEndAllowed
 | 
					import com.maddyhome.idea.vim.helper.isEndAllowed
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.vimLastColumn
 | 
					import com.maddyhome.idea.vim.helper.vimLastColumn
 | 
				
			||||||
import com.maddyhome.idea.vim.listener.AppCodeTemplates
 | 
					import com.maddyhome.idea.vim.listener.AppCodeTemplates
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.mark.Mark
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
 | 
					import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.newapi.IjVimCaret
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.ij
 | 
					import com.maddyhome.idea.vim.newapi.ij
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
import com.maddyhome.idea.vim.state.VimStateMachine
 | 
					import com.maddyhome.idea.vim.state.VimStateMachine
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
					import com.maddyhome.idea.vim.state.mode.Mode
 | 
				
			||||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
 | 
					import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
 | 
				
			||||||
import org.jetbrains.annotations.Range
 | 
					import org.jetbrains.annotations.Range
 | 
				
			||||||
 | 
					import java.io.File
 | 
				
			||||||
import kotlin.math.max
 | 
					import kotlin.math.max
 | 
				
			||||||
import kotlin.math.min
 | 
					import kotlin.math.min
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * This handles all motion related commands and marks
 | 
					 * This handles all motion related commands and marks
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@Service
 | 
					 | 
				
			||||||
internal class MotionGroup : VimMotionGroupBase() {
 | 
					internal class MotionGroup : VimMotionGroupBase() {
 | 
				
			||||||
  override fun onAppCodeMovement(editor: VimEditor, caret: VimCaret, offset: Int, oldOffset: Int) {
 | 
					  override fun onAppCodeMovement(editor: VimEditor, caret: VimCaret, offset: Int, oldOffset: Int) {
 | 
				
			||||||
    AppCodeTemplates.onMovement(editor.ij, caret.ij, oldOffset < offset)
 | 
					    AppCodeTemplates.onMovement(editor.ij, caret.ij, oldOffset < offset)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private fun selectEditor(project: Project, mark: Mark): Editor? {
 | 
				
			||||||
 | 
					    val virtualFile = markToVirtualFile(mark) ?: return null
 | 
				
			||||||
 | 
					    return selectEditor(project, virtualFile)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private fun markToVirtualFile(mark: Mark): VirtualFile? {
 | 
				
			||||||
 | 
					    val protocol = mark.protocol
 | 
				
			||||||
 | 
					    val fileSystem: VirtualFileSystem? = VirtualFileManager.getInstance().getFileSystem(protocol)
 | 
				
			||||||
 | 
					    return fileSystem?.findFileByPath(mark.filepath)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private fun selectEditor(project: Project?, file: VirtualFile) =
 | 
				
			||||||
 | 
					    VimPlugin.getFile().selectEditor(project, file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun moveCaretToMatchingPair(editor: VimEditor, caret: ImmutableVimCaret): Motion {
 | 
				
			||||||
 | 
					    return SearchHelper.findMatchingPairOnCurrentLine(editor.ij, caret.ij).toMotionOrError()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun moveCaretToFirstDisplayLine(
 | 
					  override fun moveCaretToFirstDisplayLine(
 | 
				
			||||||
    editor: VimEditor,
 | 
					    editor: VimEditor,
 | 
				
			||||||
    caret: ImmutableVimCaret,
 | 
					    caret: ImmutableVimCaret,
 | 
				
			||||||
@@ -88,12 +128,85 @@ internal class MotionGroup : VimMotionGroupBase() {
 | 
				
			|||||||
    return moveCaretToScreenLocation(editor.ij, caret.ij, ScreenLocation.MIDDLE, 0, false)
 | 
					    return moveCaretToScreenLocation(editor.ij, caret.ij, ScreenLocation.MIDDLE, 0, false)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun moveCaretToMark(caret: ImmutableVimCaret, ch: Char, toLineStart: Boolean): Motion {
 | 
				
			||||||
 | 
					    val markService = injector.markService
 | 
				
			||||||
 | 
					    val mark = markService.getMark(caret, ch) ?: return Motion.Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val caretEditor = caret.editor
 | 
				
			||||||
 | 
					    val caretVirtualFile = EditorHelper.getVirtualFile((caretEditor as IjVimEditor).editor)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val line = mark.line
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (caretVirtualFile!!.path == mark.filepath) {
 | 
				
			||||||
 | 
					      val offset = if (toLineStart) {
 | 
				
			||||||
 | 
					        moveCaretToLineStartSkipLeading(caretEditor, line)
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        caretEditor.bufferPositionToOffset(BufferPosition(line, mark.col, false))
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return offset.toMotionOrError()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val project = caretEditor.editor.project
 | 
				
			||||||
 | 
					    val markEditor = selectEditor(project!!, mark)
 | 
				
			||||||
 | 
					    if (markEditor != null) {
 | 
				
			||||||
 | 
					      // todo should we move all the carets or only one?
 | 
				
			||||||
 | 
					      for (carett in markEditor.caretModel.allCarets) {
 | 
				
			||||||
 | 
					        val offset = if (toLineStart) {
 | 
				
			||||||
 | 
					          moveCaretToLineStartSkipLeading(IjVimEditor(markEditor), line)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          // todo should it be the same as getting offset above?
 | 
				
			||||||
 | 
					          markEditor.logicalPositionToOffset(LogicalPosition(line, mark.col))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        IjVimCaret(carett!!).moveToOffset(offset)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return Motion.Error
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun moveCaretToJump(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Motion {
 | 
				
			||||||
 | 
					    val jumpService = injector.jumpService
 | 
				
			||||||
 | 
					    val spot = jumpService.getJumpSpot(editor)
 | 
				
			||||||
 | 
					    val (line, col, fileName) = jumpService.getJump(editor, count) ?: return Motion.Error
 | 
				
			||||||
 | 
					    val vf = EditorHelper.getVirtualFile(editor.ij) ?: return Motion.Error
 | 
				
			||||||
 | 
					    val lp = BufferPosition(line, col, false)
 | 
				
			||||||
 | 
					    val lpNative = LogicalPosition(line, col, false)
 | 
				
			||||||
 | 
					    return if (vf.path != fileName) {
 | 
				
			||||||
 | 
					      val newFile = LocalFileSystem.getInstance().findFileByPath(fileName.replace(File.separatorChar, '/'))
 | 
				
			||||||
 | 
					        ?: return Motion.Error
 | 
				
			||||||
 | 
					      selectEditor(editor.ij.project, newFile)?.let { newEditor ->
 | 
				
			||||||
 | 
					        if (spot == -1) {
 | 
				
			||||||
 | 
					          jumpService.addJump(editor, false)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        newEditor.vim.let {
 | 
				
			||||||
 | 
					          it.currentCaret().moveToOffset(it.normalizeOffset(newEditor.logicalPositionToOffset(lpNative), false))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      Motion.Error
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      if (spot == -1) {
 | 
				
			||||||
 | 
					        jumpService.addJump(editor, false)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      editor.bufferPositionToOffset(lp).toMotionOrError()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun moveCaretToCurrentDisplayLineMiddle(editor: VimEditor, caret: ImmutableVimCaret): Motion {
 | 
					  override fun moveCaretToCurrentDisplayLineMiddle(editor: VimEditor, caret: ImmutableVimCaret): Motion {
 | 
				
			||||||
    val width = EditorHelper.getApproximateScreenWidth(editor.ij) / 2
 | 
					    val width = EditorHelper.getApproximateScreenWidth(editor.ij) / 2
 | 
				
			||||||
    val len = editor.lineLength(editor.currentCaret().getBufferPosition().line)
 | 
					    val len = editor.lineLength(editor.currentCaret().getBufferPosition().line)
 | 
				
			||||||
    return moveCaretToColumn(editor, caret, max(0, min(len - 1, width)), false)
 | 
					    return moveCaretToColumn(editor, caret, max(0, min(len - 1, width)), false)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun moveCaretToColumn(editor: VimEditor, caret: ImmutableVimCaret, count: Int, allowEnd: Boolean): Motion {
 | 
				
			||||||
 | 
					    val line = caret.getLine().line
 | 
				
			||||||
 | 
					    val column = editor.normalizeColumn(line, count, allowEnd)
 | 
				
			||||||
 | 
					    val offset = editor.bufferPositionToOffset(BufferPosition(line, column, false))
 | 
				
			||||||
 | 
					    return if (column != count) {
 | 
				
			||||||
 | 
					      AdjustedOffset(offset, count)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      AbsoluteOffset(offset)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun moveCaretToCurrentDisplayLineStart(editor: VimEditor, caret: ImmutableVimCaret): Motion {
 | 
					  override fun moveCaretToCurrentDisplayLineStart(editor: VimEditor, caret: ImmutableVimCaret): Motion {
 | 
				
			||||||
    val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
 | 
					    val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
 | 
				
			||||||
    return moveCaretToColumn(editor, caret, col, false)
 | 
					    return moveCaretToColumn(editor, caret, col, false)
 | 
				
			||||||
@@ -104,7 +217,7 @@ internal class MotionGroup : VimMotionGroupBase() {
 | 
				
			|||||||
    caret: ImmutableVimCaret,
 | 
					    caret: ImmutableVimCaret,
 | 
				
			||||||
  ): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
 | 
					  ): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
 | 
				
			||||||
    val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
 | 
					    val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
 | 
				
			||||||
    val bufferLine = caret.getLine()
 | 
					    val bufferLine = caret.getLine().line
 | 
				
			||||||
    return editor.getLeadingCharacterOffset(bufferLine, col)
 | 
					    return editor.getLeadingCharacterOffset(bufferLine, col)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -117,6 +230,36 @@ internal class MotionGroup : VimMotionGroupBase() {
 | 
				
			|||||||
    return moveCaretToColumn(editor, caret, col, allowEnd)
 | 
					    return moveCaretToColumn(editor, caret, col, allowEnd)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun moveCaretToLineWithSameColumn(
 | 
				
			||||||
 | 
					    editor: VimEditor,
 | 
				
			||||||
 | 
					    line: Int,
 | 
				
			||||||
 | 
					    caret: ImmutableVimCaret,
 | 
				
			||||||
 | 
					  ): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
 | 
				
			||||||
 | 
					    var c = caret.vimLastColumn
 | 
				
			||||||
 | 
					    var l = line
 | 
				
			||||||
 | 
					    if (l < 0) {
 | 
				
			||||||
 | 
					      l = 0
 | 
				
			||||||
 | 
					      c = 0
 | 
				
			||||||
 | 
					    } else if (l >= editor.lineCount()) {
 | 
				
			||||||
 | 
					      l = editor.normalizeLine(editor.lineCount() - 1)
 | 
				
			||||||
 | 
					      c = editor.lineLength(l)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    val newPos = BufferPosition(l, editor.normalizeColumn(l, c, false))
 | 
				
			||||||
 | 
					    return editor.bufferPositionToOffset(newPos)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun moveCaretToLineWithStartOfLineOption(
 | 
				
			||||||
 | 
					    editor: VimEditor,
 | 
				
			||||||
 | 
					    line: Int,
 | 
				
			||||||
 | 
					    caret: ImmutableVimCaret,
 | 
				
			||||||
 | 
					  ): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
 | 
				
			||||||
 | 
					    return if (injector.options(editor).startofline) {
 | 
				
			||||||
 | 
					      moveCaretToLineStartSkipLeading(editor, line)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      moveCaretToLineWithSameColumn(editor, line, caret)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * If 'absolute' is true, then set tab index to 'value', otherwise add 'value' to tab index with wraparound.
 | 
					   * If 'absolute' is true, then set tab index to 'value', otherwise add 'value' to tab index with wraparound.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
@@ -134,18 +277,30 @@ internal class MotionGroup : VimMotionGroupBase() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun moveCaretGotoPreviousTab(editor: VimEditor, context: ExecutionContext, rawCount: Int): Int {
 | 
					  override fun moveCaretGotoPreviousTab(editor: VimEditor, context: ExecutionContext, rawCount: Int): Int {
 | 
				
			||||||
    val project = editor.ij.project ?: return editor.currentCaret().offset
 | 
					    val project = editor.ij.project ?: return editor.currentCaret().offset.point
 | 
				
			||||||
    val currentWindow = FileEditorManagerEx.getInstanceEx(project).splitters.currentWindow
 | 
					    val currentWindow = FileEditorManagerEx.getInstanceEx(project).splitters.currentWindow
 | 
				
			||||||
    switchEditorTab(currentWindow, if (rawCount >= 1) -rawCount else -1, false)
 | 
					    switchEditorTab(currentWindow, if (rawCount >= 1) -rawCount else -1, false)
 | 
				
			||||||
    return editor.currentCaret().offset
 | 
					    return editor.currentCaret().offset.point
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun moveCaretGotoNextTab(editor: VimEditor, context: ExecutionContext, rawCount: Int): Int {
 | 
					  override fun moveCaretGotoNextTab(editor: VimEditor, context: ExecutionContext, rawCount: Int): Int {
 | 
				
			||||||
    val absolute = rawCount >= 1
 | 
					    val absolute = rawCount >= 1
 | 
				
			||||||
    val project = editor.ij.project ?: return editor.currentCaret().offset
 | 
					    val project = editor.ij.project ?: return editor.currentCaret().offset.point
 | 
				
			||||||
    val currentWindow = FileEditorManagerEx.getInstanceEx(project).splitters.currentWindow
 | 
					    val currentWindow = FileEditorManagerEx.getInstanceEx(project).splitters.currentWindow
 | 
				
			||||||
    switchEditorTab(currentWindow, if (absolute) rawCount - 1 else 1, absolute)
 | 
					    switchEditorTab(currentWindow, if (absolute) rawCount - 1 else 1, absolute)
 | 
				
			||||||
    return editor.currentCaret().offset
 | 
					    return editor.currentCaret().offset.point
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun moveCaretToLinePercent(
 | 
				
			||||||
 | 
					    editor: VimEditor,
 | 
				
			||||||
 | 
					    caret: ImmutableVimCaret,
 | 
				
			||||||
 | 
					    count: Int,
 | 
				
			||||||
 | 
					  ): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
 | 
				
			||||||
 | 
					    return moveCaretToLineWithStartOfLineOption(
 | 
				
			||||||
 | 
					      editor,
 | 
				
			||||||
 | 
					      editor.normalizeLine((editor.lineCount() * clamp(count, 0, 100) + 99) / 100 - 1),
 | 
				
			||||||
 | 
					      caret,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private enum class ScreenLocation {
 | 
					  private enum class ScreenLocation {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,6 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread
 | 
				
			|||||||
import com.intellij.openapi.actionSystem.AnAction
 | 
					import com.intellij.openapi.actionSystem.AnAction
 | 
				
			||||||
import com.intellij.openapi.actionSystem.AnActionEvent
 | 
					import com.intellij.openapi.actionSystem.AnActionEvent
 | 
				
			||||||
import com.intellij.openapi.actionSystem.KeyboardShortcut
 | 
					import com.intellij.openapi.actionSystem.KeyboardShortcut
 | 
				
			||||||
import com.intellij.openapi.components.Service
 | 
					 | 
				
			||||||
import com.intellij.openapi.diagnostic.logger
 | 
					import com.intellij.openapi.diagnostic.logger
 | 
				
			||||||
import com.intellij.openapi.ide.CopyPasteManager
 | 
					import com.intellij.openapi.ide.CopyPasteManager
 | 
				
			||||||
import com.intellij.openapi.keymap.KeymapUtil
 | 
					import com.intellij.openapi.keymap.KeymapUtil
 | 
				
			||||||
@@ -56,7 +55,6 @@ import javax.swing.KeyStroke
 | 
				
			|||||||
 * This service is can be used as application level and as project level service.
 | 
					 * This service is can be used as application level and as project level service.
 | 
				
			||||||
 * If project is null, this means that this is an application level service and notification will be shown for all projects
 | 
					 * If project is null, this means that this is an application level service and notification will be shown for all projects
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@Service(Service.Level.PROJECT, Service.Level.APP)
 | 
					 | 
				
			||||||
internal class NotificationService(private val project: Project?) {
 | 
					internal class NotificationService(private val project: Project?) {
 | 
				
			||||||
  // This constructor is used to create an applicationService
 | 
					  // This constructor is used to create an applicationService
 | 
				
			||||||
  @Suppress("unused")
 | 
					  @Suppress("unused")
 | 
				
			||||||
@@ -278,7 +276,7 @@ internal class NotificationService(private val project: Project?) {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (id != null) {
 | 
					      if (id != null) {
 | 
				
			||||||
        ActionTracker.Util.logTrackedAction(id)
 | 
					        ActionTracker.logTrackedAction(id)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -286,7 +284,7 @@ internal class NotificationService(private val project: Project?) {
 | 
				
			|||||||
      override fun actionPerformed(e: AnActionEvent) {
 | 
					      override fun actionPerformed(e: AnActionEvent) {
 | 
				
			||||||
        CopyPasteManager.getInstance().setContents(StringSelection(id ?: ""))
 | 
					        CopyPasteManager.getInstance().setContents(StringSelection(id ?: ""))
 | 
				
			||||||
        if (id != null) {
 | 
					        if (id != null) {
 | 
				
			||||||
          ActionTracker.Util.logCopiedAction(id)
 | 
					          ActionTracker.logCopiedAction(id)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        notification?.expire()
 | 
					        notification?.expire()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,7 +20,6 @@ import com.intellij.openapi.progress.ProgressManager
 | 
				
			|||||||
import com.intellij.util.execution.ParametersListUtil
 | 
					import com.intellij.util.execution.ParametersListUtil
 | 
				
			||||||
import com.intellij.util.text.CharSequenceReader
 | 
					import com.intellij.util.text.CharSequenceReader
 | 
				
			||||||
import com.maddyhome.idea.vim.KeyHandler.Companion.getInstance
 | 
					import com.maddyhome.idea.vim.KeyHandler.Companion.getInstance
 | 
				
			||||||
import com.maddyhome.idea.vim.KeyProcessResult
 | 
					 | 
				
			||||||
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.VimEditor
 | 
					import com.maddyhome.idea.vim.api.VimEditor
 | 
				
			||||||
@@ -38,6 +37,7 @@ import com.maddyhome.idea.vim.state.mode.Mode
 | 
				
			|||||||
import com.maddyhome.idea.vim.state.mode.Mode.NORMAL
 | 
					import com.maddyhome.idea.vim.state.mode.Mode.NORMAL
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
 | 
					import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.ReturnableFromCmd
 | 
					import com.maddyhome.idea.vim.state.mode.ReturnableFromCmd
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.mode
 | 
				
			||||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
 | 
					import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
 | 
					import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
 | 
				
			||||||
import java.io.BufferedWriter
 | 
					import java.io.BufferedWriter
 | 
				
			||||||
@@ -85,27 +85,24 @@ public class ProcessGroup : VimProcessGroupBase() {
 | 
				
			|||||||
    modeBeforeCommandProcessing = currentMode
 | 
					    modeBeforeCommandProcessing = currentMode
 | 
				
			||||||
    val initText = getRange(editor, cmd)
 | 
					    val initText = getRange(editor, cmd)
 | 
				
			||||||
    injector.markService.setVisualSelectionMarks(editor)
 | 
					    injector.markService.setVisualSelectionMarks(editor)
 | 
				
			||||||
    editor.mode = Mode.CMD_LINE(currentMode)
 | 
					    editor.vimStateMachine.mode = Mode.CMD_LINE(currentMode)
 | 
				
			||||||
    val panel = ExEntryPanel.getInstance()
 | 
					    val panel = ExEntryPanel.getInstance()
 | 
				
			||||||
    panel.activate(editor.ij, context.ij, ":", initText, 1)
 | 
					    panel.activate(editor.ij, context.ij, ":", initText, 1)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public override fun processExKey(editor: VimEditor, stroke: KeyStroke, processResultBuilder: KeyProcessResult.KeyProcessResultBuilder): Boolean {
 | 
					  public override fun processExKey(editor: VimEditor, stroke: KeyStroke): Boolean {
 | 
				
			||||||
    // This will only get called if somehow the key focus ended up in the editor while the ex entry window
 | 
					    // This will only get called if somehow the key focus ended up in the editor while the ex entry window
 | 
				
			||||||
    // is open. So I'll put focus back in the editor and process the key.
 | 
					    // is open. So I'll put focus back in the editor and process the key.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val panel = ExEntryPanel.getInstance()
 | 
					    val panel = ExEntryPanel.getInstance()
 | 
				
			||||||
    if (panel.isActive) {
 | 
					    if (panel.isActive) {
 | 
				
			||||||
      processResultBuilder.addExecutionStep { _, _, _ ->
 | 
					      requestFocus(panel.entry)
 | 
				
			||||||
        requestFocus(panel.entry)
 | 
					      panel.handleKey(stroke)
 | 
				
			||||||
        panel.handleKey(stroke)
 | 
					
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      return true
 | 
					      return true
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      processResultBuilder.addExecutionStep { _, lambdaEditor, _ ->
 | 
					      getInstance(editor).mode = NORMAL()
 | 
				
			||||||
        lambdaEditor.mode = NORMAL()
 | 
					      getInstance().reset(editor)
 | 
				
			||||||
        getInstance().reset(lambdaEditor)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      return false
 | 
					      return false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -115,7 +112,7 @@ public class ProcessGroup : VimProcessGroupBase() {
 | 
				
			|||||||
    panel.deactivate(true)
 | 
					    panel.deactivate(true)
 | 
				
			||||||
    var res = true
 | 
					    var res = true
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      editor.mode = NORMAL()
 | 
					      getInstance(editor).mode = NORMAL()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      logger.debug("processing command")
 | 
					      logger.debug("processing command")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -155,7 +152,7 @@ public class ProcessGroup : VimProcessGroupBase() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public override fun cancelExEntry(editor: VimEditor, resetCaret: Boolean) {
 | 
					  public override fun cancelExEntry(editor: VimEditor, resetCaret: Boolean) {
 | 
				
			||||||
    editor.mode = NORMAL()
 | 
					    editor.vimStateMachine.mode = NORMAL()
 | 
				
			||||||
    getInstance().reset(editor)
 | 
					    getInstance().reset(editor)
 | 
				
			||||||
    val panel = ExEntryPanel.getInstance()
 | 
					    val panel = ExEntryPanel.getInstance()
 | 
				
			||||||
    panel.deactivate(true, resetCaret)
 | 
					    panel.deactivate(true, resetCaret)
 | 
				
			||||||
@@ -165,7 +162,7 @@ public class ProcessGroup : VimProcessGroupBase() {
 | 
				
			|||||||
    val initText = getRange(editor, cmd) + "!"
 | 
					    val initText = getRange(editor, cmd) + "!"
 | 
				
			||||||
    val currentMode = editor.mode
 | 
					    val currentMode = editor.mode
 | 
				
			||||||
    check(currentMode is ReturnableFromCmd) { "Cannot enable cmd mode from $currentMode" }
 | 
					    check(currentMode is ReturnableFromCmd) { "Cannot enable cmd mode from $currentMode" }
 | 
				
			||||||
    editor.mode = Mode.CMD_LINE(currentMode)
 | 
					    editor.vimStateMachine.mode = Mode.CMD_LINE(currentMode)
 | 
				
			||||||
    val panel = ExEntryPanel.getInstance()
 | 
					    val panel = ExEntryPanel.getInstance()
 | 
				
			||||||
    panel.activate(editor.ij, context.ij, ":", initText, 1)
 | 
					    panel.activate(editor.ij, context.ij, ":", initText, 1)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,9 +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.state.mode.SelectionType;
 | 
				
			||||||
import com.maddyhome.idea.vim.register.Register;
 | 
					import com.maddyhome.idea.vim.register.Register;
 | 
				
			||||||
import com.maddyhome.idea.vim.register.VimRegisterGroupBase;
 | 
					import com.maddyhome.idea.vim.register.VimRegisterGroupBase;
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.SelectionType;
 | 
					 | 
				
			||||||
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;
 | 
				
			||||||
@@ -37,10 +37,6 @@ public class RegisterGroup extends VimRegisterGroupBase implements PersistentSta
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private static final Logger logger = Logger.getInstance(RegisterGroup.class);
 | 
					  private static final Logger logger = Logger.getInstance(RegisterGroup.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public RegisterGroup() {
 | 
					 | 
				
			||||||
    this.initClipboardOptionListener();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public void saveData(final @NotNull Element element) {
 | 
					  public void saveData(final @NotNull Element element) {
 | 
				
			||||||
    logger.debug("Save registers data");
 | 
					    logger.debug("Save registers data");
 | 
				
			||||||
    final Element registersElement = new Element("registers");
 | 
					    final Element registersElement = new Element("registers");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,8 @@ import com.intellij.openapi.editor.event.DocumentEvent;
 | 
				
			|||||||
import com.intellij.openapi.editor.event.DocumentListener;
 | 
					import com.intellij.openapi.editor.event.DocumentListener;
 | 
				
			||||||
import com.intellij.openapi.editor.markup.RangeHighlighter;
 | 
					import com.intellij.openapi.editor.markup.RangeHighlighter;
 | 
				
			||||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
 | 
					import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
 | 
				
			||||||
 | 
					import com.intellij.openapi.project.Project;
 | 
				
			||||||
 | 
					import com.intellij.openapi.project.ProjectManager;
 | 
				
			||||||
import com.intellij.openapi.util.Ref;
 | 
					import com.intellij.openapi.util.Ref;
 | 
				
			||||||
import com.maddyhome.idea.vim.VimPlugin;
 | 
					import com.maddyhome.idea.vim.VimPlugin;
 | 
				
			||||||
import com.maddyhome.idea.vim.api.*;
 | 
					import com.maddyhome.idea.vim.api.*;
 | 
				
			||||||
@@ -31,11 +33,12 @@ import com.maddyhome.idea.vim.ex.ExException;
 | 
				
			|||||||
import com.maddyhome.idea.vim.ex.ranges.LineRange;
 | 
					import com.maddyhome.idea.vim.ex.ranges.LineRange;
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.*;
 | 
					import com.maddyhome.idea.vim.helper.*;
 | 
				
			||||||
import com.maddyhome.idea.vim.history.HistoryConstants;
 | 
					import com.maddyhome.idea.vim.history.HistoryConstants;
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.*;
 | 
					import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.newapi.IjVimCaret;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.newapi.IjVimSearchGroup;
 | 
				
			||||||
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener;
 | 
					import com.maddyhome.idea.vim.options.GlobalOptionChangeListener;
 | 
				
			||||||
import com.maddyhome.idea.vim.regexp.CharPointer;
 | 
					import com.maddyhome.idea.vim.regexp.*;
 | 
				
			||||||
import com.maddyhome.idea.vim.regexp.CharacterClasses;
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.regexp.RegExp;
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.ui.ModalEntry;
 | 
					import com.maddyhome.idea.vim.ui.ModalEntry;
 | 
				
			||||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
 | 
					import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.VimLContext;
 | 
					import com.maddyhome.idea.vim.vimscript.model.VimLContext;
 | 
				
			||||||
@@ -56,6 +59,7 @@ import java.text.ParsePosition;
 | 
				
			|||||||
import java.util.*;
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.*;
 | 
					import static com.maddyhome.idea.vim.api.VimInjectorKt.*;
 | 
				
			||||||
 | 
					import static com.maddyhome.idea.vim.helper.HelperKt.localEditors;
 | 
				
			||||||
import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.shouldIgnoreCase;
 | 
					import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.shouldIgnoreCase;
 | 
				
			||||||
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.globalIjOptions;
 | 
					import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.globalIjOptions;
 | 
				
			||||||
import static com.maddyhome.idea.vim.register.RegisterConstants.LAST_SEARCH_REGISTER;
 | 
					import static com.maddyhome.idea.vim.register.RegisterConstants.LAST_SEARCH_REGISTER;
 | 
				
			||||||
@@ -538,24 +542,20 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
 | 
				
			|||||||
   *
 | 
					   *
 | 
				
			||||||
   * @param editor  The editor to search in
 | 
					   * @param editor  The editor to search in
 | 
				
			||||||
   * @param caret   The caret to use for initial search offset, and to move for interactive substitution
 | 
					   * @param caret   The caret to use for initial search offset, and to move for interactive substitution
 | 
				
			||||||
   * @param context
 | 
					 | 
				
			||||||
   * @param range   Only search and substitute within the given line range. Must be valid
 | 
					   * @param range   Only search and substitute within the given line range. Must be valid
 | 
				
			||||||
   * @param excmd   The command part of the ex command line, e.g. `s` or `substitute`, or `~`
 | 
					   * @param excmd   The command part of the ex command line, e.g. `s` or `substitute`, or `~`
 | 
				
			||||||
   * @param exarg   The argument to the substitute command, such as `/{pattern}/{string}/[flags]`
 | 
					   * @param exarg   The argument to the substitute command, such as `/{pattern}/{string}/[flags]`
 | 
				
			||||||
   * @return True if the substitution succeeds, false on error. Will succeed even if nothing is modified
 | 
					   * @return        True if the substitution succeeds, false on error. Will succeed even if nothing is modified
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  @RWLockLabel.SelfSynchronized
 | 
					  @RWLockLabel.SelfSynchronized
 | 
				
			||||||
  public boolean processSubstituteCommand(@NotNull VimEditor editor,
 | 
					  public boolean processSubstituteCommand(@NotNull VimEditor editor,
 | 
				
			||||||
                                          @NotNull VimCaret caret,
 | 
					                                          @NotNull VimCaret caret,
 | 
				
			||||||
                                          @NotNull ExecutionContext context,
 | 
					 | 
				
			||||||
                                          @NotNull LineRange range,
 | 
					                                          @NotNull LineRange range,
 | 
				
			||||||
                                          @NotNull @NonNls String excmd,
 | 
					                                          @NotNull @NonNls String excmd,
 | 
				
			||||||
                                          @NotNull @NonNls String exarg,
 | 
					                                          @NotNull @NonNls String exarg,
 | 
				
			||||||
                                          @NotNull VimLContext parent) {
 | 
					                                          @NotNull VimLContext parent) {
 | 
				
			||||||
    if (globalIjOptions(injector).getUseNewRegex()) {
 | 
					    if (globalIjOptions(injector).getUseNewRegex()) return super.processSubstituteCommand(editor, caret, range, excmd, exarg, parent);
 | 
				
			||||||
      return super.processSubstituteCommand(editor, caret, context, range, excmd, exarg, parent);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Explicitly exit visual mode here, so that visual mode marks don't change when we move the cursor to a match.
 | 
					    // Explicitly exit visual mode here, so that visual mode marks don't change when we move the cursor to a match.
 | 
				
			||||||
    List<ExException> exceptions = new ArrayList<>();
 | 
					    List<ExException> exceptions = new ArrayList<>();
 | 
				
			||||||
@@ -812,7 +812,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
 | 
				
			|||||||
            RangeHighlighter hl =
 | 
					            RangeHighlighter hl =
 | 
				
			||||||
              SearchHighlightsHelper.addSubstitutionConfirmationHighlight(((IjVimEditor)editor).getEditor(), startoff,
 | 
					              SearchHighlightsHelper.addSubstitutionConfirmationHighlight(((IjVimEditor)editor).getEditor(), startoff,
 | 
				
			||||||
                                                                          endoff);
 | 
					                                                                          endoff);
 | 
				
			||||||
            final ReplaceConfirmationChoice choice = confirmChoice(((IjVimEditor)editor).getEditor(), context, match, ((IjVimCaret)caret).getCaret(), startoff);
 | 
					            final ReplaceConfirmationChoice choice = confirmChoice(((IjVimEditor)editor).getEditor(), match, ((IjVimCaret)caret).getCaret(), startoff);
 | 
				
			||||||
            ((IjVimEditor)editor).getEditor().getMarkupModel().removeHighlighter(hl);
 | 
					            ((IjVimEditor)editor).getEditor().getMarkupModel().removeHighlighter(hl);
 | 
				
			||||||
            switch (choice) {
 | 
					            switch (choice) {
 | 
				
			||||||
              case SUBSTITUTE_THIS:
 | 
					              case SUBSTITUTE_THIS:
 | 
				
			||||||
@@ -841,7 +841,8 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
 | 
				
			|||||||
            caret.moveToOffset(startoff);
 | 
					            caret.moveToOffset(startoff);
 | 
				
			||||||
            if (expression != null) {
 | 
					            if (expression != null) {
 | 
				
			||||||
              try {
 | 
					              try {
 | 
				
			||||||
                match = expression.evaluate(editor, context, parent).toInsertableString();
 | 
					                match =
 | 
				
			||||||
 | 
					                  expression.evaluate(editor, injector.getExecutionContextManager().onEditor(editor, null), parent).toInsertableString();
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
              catch (Exception e) {
 | 
					              catch (Exception e) {
 | 
				
			||||||
                exceptions.add((ExException)e);
 | 
					                exceptions.add((ExException)e);
 | 
				
			||||||
@@ -992,9 +993,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
 | 
				
			|||||||
    return new Pair<>(true, new Triple<>(regmatch, pattern, sp));
 | 
					    return new Pair<>(true, new Triple<>(regmatch, pattern, sp));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static @NotNull ReplaceConfirmationChoice confirmChoice(@NotNull Editor editor,
 | 
					  private static @NotNull ReplaceConfirmationChoice confirmChoice(@NotNull Editor editor, @NotNull String match, @NotNull Caret caret, int startoff) {
 | 
				
			||||||
                                                                  @NotNull ExecutionContext context,
 | 
					 | 
				
			||||||
                                                                  @NotNull String match, @NotNull Caret caret, int startoff) {
 | 
					 | 
				
			||||||
    final Ref<ReplaceConfirmationChoice> result = Ref.create(ReplaceConfirmationChoice.QUIT);
 | 
					    final Ref<ReplaceConfirmationChoice> result = Ref.create(ReplaceConfirmationChoice.QUIT);
 | 
				
			||||||
    final Function1<KeyStroke, Boolean> keyStrokeProcessor = key -> {
 | 
					    final Function1<KeyStroke, Boolean> keyStrokeProcessor = key -> {
 | 
				
			||||||
      final ReplaceConfirmationChoice choice;
 | 
					      final ReplaceConfirmationChoice choice;
 | 
				
			||||||
@@ -1028,6 +1027,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
 | 
				
			|||||||
    else {
 | 
					    else {
 | 
				
			||||||
      // XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for this method
 | 
					      // XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for this method
 | 
				
			||||||
      final ExEntryPanel exEntryPanel = ExEntryPanel.getInstanceWithoutShortcuts();
 | 
					      final ExEntryPanel exEntryPanel = ExEntryPanel.getInstanceWithoutShortcuts();
 | 
				
			||||||
 | 
					      ExecutionContext.Editor context = injector.getExecutionContextManager().onEditor(new IjVimEditor(editor), null);
 | 
				
			||||||
      exEntryPanel.activate(editor, ((IjEditorExecutionContext)context).getContext(), MessageHelper.message("replace.with.0", match), "", 1);
 | 
					      exEntryPanel.activate(editor, ((IjEditorExecutionContext)context).getContext(), MessageHelper.message("replace.with.0", match), "", 1);
 | 
				
			||||||
      new IjVimCaret(caret).moveToOffset(startoff);
 | 
					      new IjVimCaret(caret).moveToOffset(startoff);
 | 
				
			||||||
      ModalEntry.INSTANCE.activate(new IjVimEditor(editor), keyStrokeProcessor);
 | 
					      ModalEntry.INSTANCE.activate(new IjVimEditor(editor), keyStrokeProcessor);
 | 
				
			||||||
@@ -1085,9 +1085,9 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
 | 
				
			|||||||
  private @Nullable TextRange findNextSearchForGn(@NotNull VimEditor editor, int count, boolean forwards) {
 | 
					  private @Nullable TextRange findNextSearchForGn(@NotNull VimEditor editor, int count, boolean forwards) {
 | 
				
			||||||
    if (forwards) {
 | 
					    if (forwards) {
 | 
				
			||||||
      final EnumSet<SearchOptions> searchOptions = EnumSet.of(SearchOptions.WRAP, SearchOptions.WHOLE_FILE);
 | 
					      final EnumSet<SearchOptions> searchOptions = EnumSet.of(SearchOptions.WRAP, SearchOptions.WHOLE_FILE);
 | 
				
			||||||
      return VimInjectorKt.getInjector().getSearchHelper().findPattern(editor, getLastUsedPattern(), editor.primaryCaret().getOffset(), count, searchOptions);
 | 
					      return VimInjectorKt.getInjector().getSearchHelper().findPattern(editor, getLastUsedPattern(), editor.primaryCaret().getOffset().getPoint(), count, searchOptions);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      return searchBackward(editor, editor.primaryCaret().getOffset(), count);
 | 
					      return searchBackward(editor, editor.primaryCaret().getOffset().getPoint(), count);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1200,50 +1200,47 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
 | 
				
			|||||||
    public static DocumentSearchListener INSTANCE = new DocumentSearchListener();
 | 
					    public static DocumentSearchListener INSTANCE = new DocumentSearchListener();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Contract(pure = true)
 | 
					    @Contract(pure = true)
 | 
				
			||||||
    private DocumentSearchListener() {
 | 
					    private DocumentSearchListener () {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void documentChanged(@NotNull DocumentEvent event) {
 | 
					    public void documentChanged(@NotNull DocumentEvent event) {
 | 
				
			||||||
      // Loop over all local editors for the changed document, across all projects, and update search highlights.
 | 
					      for (Project project : ProjectManager.getInstance().getOpenProjects()) {
 | 
				
			||||||
      // Note that the change may have come from a remote guest in Code With Me scenarios (in which case
 | 
					        final Document document = event.getDocument();
 | 
				
			||||||
      // ClientId.current will be a guest ID), but we don't care - we still need to add/remove highlights for the
 | 
					 | 
				
			||||||
      // changed text. Make sure we only update local editors, though.
 | 
					 | 
				
			||||||
      final Document document = event.getDocument();
 | 
					 | 
				
			||||||
      for (VimEditor vimEditor : injector.getEditorGroup().getEditors(new IjVimDocument(document))) {
 | 
					 | 
				
			||||||
        final Editor editor = ((IjVimEditor)vimEditor).getEditor();
 | 
					 | 
				
			||||||
        Collection<RangeHighlighter> hls = UserDataManager.getVimLastHighlighters(editor);
 | 
					 | 
				
			||||||
        if (hls == null) {
 | 
					 | 
				
			||||||
          continue;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (logger.isDebugEnabled()) {
 | 
					        for (Editor editor : localEditors(document, project)) {
 | 
				
			||||||
          logger.debug("hls=" + hls);
 | 
					          Collection<RangeHighlighter> hls = UserDataManager.getVimLastHighlighters(editor);
 | 
				
			||||||
          logger.debug("event=" + event);
 | 
					          if (hls == null) {
 | 
				
			||||||
        }
 | 
					            continue;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // We can only re-highlight whole lines, so clear any highlights in the affected lines
 | 
					 | 
				
			||||||
        final LogicalPosition startPosition = editor.offsetToLogicalPosition(event.getOffset());
 | 
					 | 
				
			||||||
        final LogicalPosition endPosition = editor.offsetToLogicalPosition(event.getOffset() + event.getNewLength());
 | 
					 | 
				
			||||||
        final int startLineOffset = document.getLineStartOffset(startPosition.line);
 | 
					 | 
				
			||||||
        final int endLineOffset = document.getLineEndOffset(endPosition.line);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final Iterator<RangeHighlighter> iter = hls.iterator();
 | 
					 | 
				
			||||||
        while (iter.hasNext()) {
 | 
					 | 
				
			||||||
          final RangeHighlighter highlighter = iter.next();
 | 
					 | 
				
			||||||
          if (!highlighter.isValid() ||
 | 
					 | 
				
			||||||
              (highlighter.getStartOffset() >= startLineOffset && highlighter.getEndOffset() <= endLineOffset)) {
 | 
					 | 
				
			||||||
            iter.remove();
 | 
					 | 
				
			||||||
            editor.getMarkupModel().removeHighlighter(highlighter);
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        VimPlugin.getSearch().highlightSearchLines(editor, startPosition.line, endPosition.line);
 | 
					          if (logger.isDebugEnabled()) {
 | 
				
			||||||
 | 
					            logger.debug("hls=" + hls);
 | 
				
			||||||
 | 
					            logger.debug("event=" + event);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (logger.isDebugEnabled()) {
 | 
					          // We can only re-highlight whole lines, so clear any highlights in the affected lines
 | 
				
			||||||
          hls = UserDataManager.getVimLastHighlighters(editor);
 | 
					          final LogicalPosition startPosition = editor.offsetToLogicalPosition(event.getOffset());
 | 
				
			||||||
          logger.debug("sl=" + startPosition.line + ", el=" + endPosition.line);
 | 
					          final LogicalPosition endPosition = editor.offsetToLogicalPosition(event.getOffset() + event.getNewLength());
 | 
				
			||||||
          logger.debug("hls=" + hls);
 | 
					          final int startLineOffset = document.getLineStartOffset(startPosition.line);
 | 
				
			||||||
 | 
					          final int endLineOffset = document.getLineEndOffset(endPosition.line);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          final Iterator<RangeHighlighter> iter = hls.iterator();
 | 
				
			||||||
 | 
					          while (iter.hasNext()) {
 | 
				
			||||||
 | 
					            final RangeHighlighter highlighter = iter.next();
 | 
				
			||||||
 | 
					            if (!highlighter.isValid() || (highlighter.getStartOffset() >= startLineOffset && highlighter.getEndOffset() <= endLineOffset)) {
 | 
				
			||||||
 | 
					              iter.remove();
 | 
				
			||||||
 | 
					              editor.getMarkupModel().removeHighlighter(highlighter);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          VimPlugin.getSearch().highlightSearchLines(editor, startPosition.line, endPosition.line);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (logger.isDebugEnabled()) {
 | 
				
			||||||
 | 
					            hls = UserDataManager.getVimLastHighlighters(editor);
 | 
				
			||||||
 | 
					            logger.debug("sl=" + startPosition.line + ", el=" + endPosition.line);
 | 
				
			||||||
 | 
					            logger.debug("hls=" + hls);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -111,7 +111,7 @@ internal class JumpsListener(val project: Project) : RecentPlacesListener {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private fun buildJump(place: PlaceInfo): Jump? {
 | 
					  private fun buildJump(place: PlaceInfo): Jump? {
 | 
				
			||||||
    val editor = injector.editorGroup.getEditors().firstOrNull { it.ij.virtualFile == place.file } ?: return null
 | 
					    val editor = injector.editorGroup.localEditors().firstOrNull { it.ij.virtualFile == place.file } ?: return null
 | 
				
			||||||
    val offset = place.caretPosition?.startOffset ?: return null
 | 
					    val offset = place.caretPosition?.startOffset ?: return null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val bufferPosition = editor.offsetToBufferPosition(offset)
 | 
					    val bufferPosition = editor.offsetToBufferPosition(offset)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,6 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
package com.maddyhome.idea.vim.group
 | 
					package com.maddyhome.idea.vim.group
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.codeWithMe.ClientId
 | 
					 | 
				
			||||||
import com.intellij.ide.bookmark.Bookmark
 | 
					import com.intellij.ide.bookmark.Bookmark
 | 
				
			||||||
import com.intellij.ide.bookmark.BookmarkGroup
 | 
					import com.intellij.ide.bookmark.BookmarkGroup
 | 
				
			||||||
import com.intellij.ide.bookmark.BookmarksListener
 | 
					import com.intellij.ide.bookmark.BookmarksListener
 | 
				
			||||||
@@ -19,7 +18,7 @@ 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.intellij.openapi.editor.Document
 | 
					import com.intellij.openapi.editor.Document
 | 
				
			||||||
import com.intellij.openapi.editor.EditorFactory
 | 
					import com.intellij.openapi.editor.Editor
 | 
				
			||||||
import com.intellij.openapi.editor.event.DocumentEvent
 | 
					import com.intellij.openapi.editor.event.DocumentEvent
 | 
				
			||||||
import com.intellij.openapi.editor.event.DocumentListener
 | 
					import com.intellij.openapi.editor.event.DocumentListener
 | 
				
			||||||
import com.intellij.openapi.fileEditor.FileEditorManager
 | 
					import com.intellij.openapi.fileEditor.FileEditorManager
 | 
				
			||||||
@@ -29,11 +28,11 @@ import com.intellij.openapi.util.text.StringUtil
 | 
				
			|||||||
import com.intellij.util.asSafely
 | 
					import com.intellij.util.asSafely
 | 
				
			||||||
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.VimEditorGroup
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimMarkService
 | 
					import com.maddyhome.idea.vim.api.VimMarkService
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimMarkServiceBase
 | 
					import com.maddyhome.idea.vim.api.VimMarkServiceBase
 | 
				
			||||||
import com.maddyhome.idea.vim.api.injector
 | 
					import com.maddyhome.idea.vim.api.injector
 | 
				
			||||||
import com.maddyhome.idea.vim.group.SystemMarks.Companion.createOrGetSystemMark
 | 
					import com.maddyhome.idea.vim.group.SystemMarks.Companion.createOrGetSystemMark
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.localEditors
 | 
				
			||||||
import com.maddyhome.idea.vim.mark.IntellijMark
 | 
					import com.maddyhome.idea.vim.mark.IntellijMark
 | 
				
			||||||
import com.maddyhome.idea.vim.mark.Mark
 | 
					import com.maddyhome.idea.vim.mark.Mark
 | 
				
			||||||
import com.maddyhome.idea.vim.mark.VimMark.Companion.create
 | 
					import com.maddyhome.idea.vim.mark.VimMark.Companion.create
 | 
				
			||||||
@@ -194,10 +193,6 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
 | 
				
			|||||||
     * This event indicates that a document is about to be changed. We use this event to update all the
 | 
					     * This event indicates that a document is about to be changed. We use this event to update all the
 | 
				
			||||||
     * editor's marks if text is about to be deleted.
 | 
					     * editor's marks if text is about to be deleted.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * Note that the event is fired for both local changes and changes from remote guests in Code With Me scenarios (in
 | 
					 | 
				
			||||||
     * which case [ClientId.current] will be the remote client). We don't care who caused it, we just need to update the
 | 
					 | 
				
			||||||
     * stored marks.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param event The change event
 | 
					     * @param event The change event
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    override fun beforeDocumentChange(event: DocumentEvent) {
 | 
					    override fun beforeDocumentChange(event: DocumentEvent) {
 | 
				
			||||||
@@ -205,18 +200,15 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
 | 
				
			|||||||
      if (logger.isDebugEnabled) logger.debug("MarkUpdater before, event = $event")
 | 
					      if (logger.isDebugEnabled) logger.debug("MarkUpdater before, event = $event")
 | 
				
			||||||
      if (event.oldLength == 0) return
 | 
					      if (event.oldLength == 0) return
 | 
				
			||||||
      val doc = event.document
 | 
					      val doc = event.document
 | 
				
			||||||
      val anEditor = getAnyEditorForDocument(doc) ?: return
 | 
					      val anEditor = getAnEditor(doc) ?: return
 | 
				
			||||||
      injector.markService.updateMarksFromDelete(anEditor, event.offset, event.oldLength)
 | 
					      injector.markService
 | 
				
			||||||
 | 
					        .updateMarksFromDelete(IjVimEditor(anEditor), event.offset, event.oldLength)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * This event indicates that a document was just changed. We use this event to update all the editor's
 | 
					     * This event indicates that a document was just changed. We use this event to update all the editor's
 | 
				
			||||||
     * marks if text was just added.
 | 
					     * marks if text was just added.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * Note that the event is fired for both local changes and changes from remote guests in Code With Me scenarios (in
 | 
					 | 
				
			||||||
     * which case [ClientId.current] will be the remote client). We don't care who caused it, we just need to update the
 | 
					 | 
				
			||||||
     * stored marks.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param event The change event
 | 
					     * @param event The change event
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    override fun documentChanged(event: DocumentEvent) {
 | 
					    override fun documentChanged(event: DocumentEvent) {
 | 
				
			||||||
@@ -224,19 +216,19 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
 | 
				
			|||||||
      if (logger.isDebugEnabled) logger.debug("MarkUpdater after, event = $event")
 | 
					      if (logger.isDebugEnabled) logger.debug("MarkUpdater after, event = $event")
 | 
				
			||||||
      if (event.newLength == 0 || event.newLength == 1 && event.newFragment[0] != '\n') return
 | 
					      if (event.newLength == 0 || event.newLength == 1 && event.newFragment[0] != '\n') return
 | 
				
			||||||
      val doc = event.document
 | 
					      val doc = event.document
 | 
				
			||||||
      val anEditor = getAnyEditorForDocument(doc) ?: return
 | 
					      val anEditor = getAnEditor(doc) ?: return
 | 
				
			||||||
      injector.markService.updateMarksFromInsert(anEditor, event.offset, event.newLength)
 | 
					      injector.markService
 | 
				
			||||||
 | 
					        .updateMarksFromInsert(IjVimEditor(anEditor), event.offset, event.newLength)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    private fun getAnEditor(doc: Document): Editor? {
 | 
				
			||||||
     * Get any editor for the given document
 | 
					      val editors = localEditors(doc)
 | 
				
			||||||
     *
 | 
					      return if (editors.size > 0) {
 | 
				
			||||||
     * We need an editor to help calculate offsets for marks, and it doesn't matter which one we use, because they would
 | 
					        editors[0]
 | 
				
			||||||
     * all return the same results. However, we cannot use [VimEditorGroup.getEditors] because the change might have
 | 
					      } else {
 | 
				
			||||||
     * come from a remote guest and there might not be an open local editor.
 | 
					        null
 | 
				
			||||||
     */
 | 
					      }
 | 
				
			||||||
    private fun getAnyEditorForDocument(doc: Document) =
 | 
					    }
 | 
				
			||||||
      EditorFactory.getInstance().getEditors(doc).firstOrNull()?.let { IjVimEditor(it) }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  class VimBookmarksListener(private val myProject: Project) : BookmarksListener {
 | 
					  class VimBookmarksListener(private val myProject: Project) : BookmarksListener {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,11 +8,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.maddyhome.idea.vim.group
 | 
					package com.maddyhome.idea.vim.group
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.components.Service
 | 
					 | 
				
			||||||
import org.apache.commons.codec.binary.Base64
 | 
					import org.apache.commons.codec.binary.Base64
 | 
				
			||||||
import org.jdom.Element
 | 
					import org.jdom.Element
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Service
 | 
					 | 
				
			||||||
internal class XMLGroup {
 | 
					internal class XMLGroup {
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Set the text of an XML element, safely encode it if needed.
 | 
					   * Set the text of an XML element, safely encode it if needed.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,6 @@ import com.intellij.ide.DataManager
 | 
				
			|||||||
import com.intellij.ide.PasteProvider
 | 
					import com.intellij.ide.PasteProvider
 | 
				
			||||||
import com.intellij.openapi.actionSystem.DataContext
 | 
					import com.intellij.openapi.actionSystem.DataContext
 | 
				
			||||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
 | 
					import com.intellij.openapi.actionSystem.PlatformDataKeys
 | 
				
			||||||
import com.intellij.openapi.components.Service
 | 
					 | 
				
			||||||
import com.intellij.openapi.editor.Caret
 | 
					import com.intellij.openapi.editor.Caret
 | 
				
			||||||
import com.intellij.openapi.editor.RangeMarker
 | 
					import com.intellij.openapi.editor.RangeMarker
 | 
				
			||||||
import com.intellij.openapi.editor.ex.EditorEx
 | 
					import com.intellij.openapi.editor.ex.EditorEx
 | 
				
			||||||
@@ -52,7 +51,6 @@ import com.maddyhome.idea.vim.state.mode.isChar
 | 
				
			|||||||
import com.maddyhome.idea.vim.state.mode.isLine
 | 
					import com.maddyhome.idea.vim.state.mode.isLine
 | 
				
			||||||
import java.awt.datatransfer.DataFlavor
 | 
					import java.awt.datatransfer.DataFlavor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Service
 | 
					 | 
				
			||||||
internal class PutGroup : VimPutBase() {
 | 
					internal class PutGroup : VimPutBase() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun getProviderForPasteViaIde(
 | 
					  override fun getProviderForPasteViaIde(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,8 +20,9 @@ import com.maddyhome.idea.vim.helper.exitVisualMode
 | 
				
			|||||||
import com.maddyhome.idea.vim.helper.hasVisualSelection
 | 
					import com.maddyhome.idea.vim.helper.hasVisualSelection
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.inInsertMode
 | 
					import com.maddyhome.idea.vim.helper.inInsertMode
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.inNormalMode
 | 
					import com.maddyhome.idea.vim.helper.inNormalMode
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.isTemplateActive
 | 
					import com.maddyhome.idea.vim.helper.isTemplateActive
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.vimDisabled
 | 
					import com.maddyhome.idea.vim.helper.vimStateMachine
 | 
				
			||||||
import com.maddyhome.idea.vim.listener.VimListenerManager
 | 
					import com.maddyhome.idea.vim.listener.VimListenerManager
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
import com.maddyhome.idea.vim.options.OptionConstants
 | 
					import com.maddyhome.idea.vim.options.OptionConstants
 | 
				
			||||||
@@ -29,6 +30,7 @@ import com.maddyhome.idea.vim.state.mode.Mode
 | 
				
			|||||||
import com.maddyhome.idea.vim.state.mode.inNormalMode
 | 
					import com.maddyhome.idea.vim.state.mode.inNormalMode
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.inSelectMode
 | 
					import com.maddyhome.idea.vim.state.mode.inSelectMode
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.inVisualMode
 | 
					import com.maddyhome.idea.vim.state.mode.inVisualMode
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.mode
 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.options.helpers.IdeaRefactorModeHelper
 | 
					import com.maddyhome.idea.vim.vimscript.model.options.helpers.IdeaRefactorModeHelper
 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.options.helpers.isIdeaRefactorModeKeep
 | 
					import com.maddyhome.idea.vim.vimscript.model.options.helpers.isIdeaRefactorModeKeep
 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.options.helpers.isIdeaRefactorModeSelect
 | 
					import com.maddyhome.idea.vim.vimscript.model.options.helpers.isIdeaRefactorModeSelect
 | 
				
			||||||
@@ -53,7 +55,9 @@ internal object IdeaSelectionControl {
 | 
				
			|||||||
    selectionSource: VimListenerManager.SelectionSource = VimListenerManager.SelectionSource.OTHER,
 | 
					    selectionSource: VimListenerManager.SelectionSource = VimListenerManager.SelectionSource.OTHER,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    VimVisualTimer.singleTask(editor.vim.mode) { initialMode ->
 | 
					    VimVisualTimer.singleTask(editor.vim.mode) { initialMode ->
 | 
				
			||||||
      if (vimDisabled(editor)) return@singleTask
 | 
					
 | 
				
			||||||
 | 
					      if (VimPlugin.isNotEnabled()) return@singleTask
 | 
				
			||||||
 | 
					      if (editor.isIdeaVimDisabledHere) return@singleTask
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      logger.debug("Adjust non-vim selection. Source: $selectionSource, initialMode: $initialMode")
 | 
					      logger.debug("Adjust non-vim selection. Source: $selectionSource, initialMode: $initialMode")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -75,7 +79,7 @@ internal object IdeaSelectionControl {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        logger.debug("Some carets have selection. State before adjustment: ${editor.vim.mode}")
 | 
					        logger.debug("Some carets have selection. State before adjustment: ${editor.vim.mode}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        editor.vim.mode = Mode.NORMAL()
 | 
					        editor.vim.vimStateMachine.mode = Mode.NORMAL()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        activateMode(editor, chooseSelectionMode(editor, selectionSource, true))
 | 
					        activateMode(editor, chooseSelectionMode(editor, selectionSource, true))
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
@@ -117,7 +121,7 @@ internal object IdeaSelectionControl {
 | 
				
			|||||||
      is Mode.VISUAL -> VimPlugin.getVisualMotion().enterVisualMode(editor.vim, mode.selectionType)
 | 
					      is Mode.VISUAL -> VimPlugin.getVisualMotion().enterVisualMode(editor.vim, mode.selectionType)
 | 
				
			||||||
      is Mode.SELECT -> VimPlugin.getVisualMotion().enterSelectMode(editor.vim, mode.selectionType)
 | 
					      is Mode.SELECT -> VimPlugin.getVisualMotion().enterSelectMode(editor.vim, mode.selectionType)
 | 
				
			||||||
      is Mode.INSERT -> VimPlugin.getChange()
 | 
					      is Mode.INSERT -> VimPlugin.getChange()
 | 
				
			||||||
        .insertBeforeCursor(editor.vim, injector.executionContextManager.getEditorExecutionContext(editor.vim))
 | 
					        .insertBeforeCursor(editor.vim, injector.executionContextManager.onEditor(editor.vim))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      is Mode.NORMAL -> Unit
 | 
					      is Mode.NORMAL -> Unit
 | 
				
			||||||
      else -> error("Unexpected mode: $mode")
 | 
					      else -> error("Unexpected mode: $mode")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,13 +12,13 @@ import com.intellij.openapi.editor.Caret
 | 
				
			|||||||
import com.intellij.openapi.editor.Editor
 | 
					import com.intellij.openapi.editor.Editor
 | 
				
			||||||
import com.maddyhome.idea.vim.api.getLineEndForOffset
 | 
					import com.maddyhome.idea.vim.api.getLineEndForOffset
 | 
				
			||||||
import com.maddyhome.idea.vim.api.getLineStartForOffset
 | 
					import com.maddyhome.idea.vim.api.getLineStartForOffset
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.Mode
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.inBlockSelection
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.isEndAllowed
 | 
					import com.maddyhome.idea.vim.helper.isEndAllowed
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
 | 
					import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.vimSelectionStart
 | 
					import com.maddyhome.idea.vim.helper.vimSelectionStart
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.inBlockSelection
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal fun moveCaretOneCharLeftFromSelectionEnd(editor: Editor, predictedMode: Mode) {
 | 
					internal fun moveCaretOneCharLeftFromSelectionEnd(editor: Editor, predictedMode: Mode) {
 | 
				
			||||||
  if (predictedMode !is Mode.VISUAL) {
 | 
					  if (predictedMode !is Mode.VISUAL) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,10 +13,10 @@ import com.intellij.openapi.editor.Editor
 | 
				
			|||||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
					import com.maddyhome.idea.vim.api.VimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimVisualMotionGroupBase
 | 
					import com.maddyhome.idea.vim.api.VimVisualMotionGroupBase
 | 
				
			||||||
import com.maddyhome.idea.vim.command.CommandState
 | 
					import com.maddyhome.idea.vim.command.CommandState
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
				
			||||||
import com.maddyhome.idea.vim.command.engine
 | 
					import com.maddyhome.idea.vim.command.engine
 | 
				
			||||||
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.SelectionType
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @author Alex Plate
 | 
					 * @author Alex Plate
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,7 @@ import com.intellij.openapi.util.UserDataHolder
 | 
				
			|||||||
import com.intellij.openapi.util.removeUserData
 | 
					import com.intellij.openapi.util.removeUserData
 | 
				
			||||||
import com.maddyhome.idea.vim.KeyHandler
 | 
					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.injector
 | 
					import com.maddyhome.idea.vim.api.injector
 | 
				
			||||||
import com.maddyhome.idea.vim.api.key
 | 
					import com.maddyhome.idea.vim.api.key
 | 
				
			||||||
import com.maddyhome.idea.vim.group.IjOptionConstants
 | 
					import com.maddyhome.idea.vim.group.IjOptionConstants
 | 
				
			||||||
@@ -38,6 +39,7 @@ import com.maddyhome.idea.vim.newapi.actionStartedFromVim
 | 
				
			|||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
					import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.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.mode
 | 
				
			||||||
import java.awt.event.KeyEvent
 | 
					import java.awt.event.KeyEvent
 | 
				
			||||||
import javax.swing.KeyStroke
 | 
					import javax.swing.KeyStroke
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -337,9 +339,8 @@ internal abstract class VimKeyHandler(nextHandler: EditorActionHandler?) : Octop
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  override fun executeHandler(editor: Editor, caret: Caret?, dataContext: DataContext?) {
 | 
					  override fun executeHandler(editor: Editor, caret: Caret?, dataContext: DataContext?) {
 | 
				
			||||||
    val enterKey = key(key)
 | 
					    val enterKey = key(key)
 | 
				
			||||||
    val context = dataContext?.vim ?: injector.executionContextManager.getEditorExecutionContext(editor.vim)
 | 
					    val context = injector.executionContextManager.onEditor(editor.vim, dataContext?.vim)
 | 
				
			||||||
    val keyHandler = KeyHandler.getInstance()
 | 
					    KeyHandler.getInstance().handleKey(editor.vim, enterKey, context)
 | 
				
			||||||
    keyHandler.handleKey(editor.vim, enterKey, context, keyHandler.keyHandlerState)
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun isHandlerEnabled(editor: Editor, dataContext: DataContext?): Boolean {
 | 
					  override fun isHandlerEnabled(editor: Editor, dataContext: DataContext?): Boolean {
 | 
				
			||||||
@@ -361,4 +362,4 @@ internal fun isOctopusEnabled(s: KeyStroke, editor: Editor): Boolean {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal val enableOctopus: Boolean
 | 
					internal val enableOctopus: Boolean
 | 
				
			||||||
  get() = injector.application.isOctopusEnabled()
 | 
					  get() = injector.globalOptions().octopushandler
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.maddyhome.idea.vim.helper
 | 
					package com.maddyhome.idea.vim.helper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.application.ApplicationManager
 | 
					 | 
				
			||||||
import com.intellij.openapi.diagnostic.thisLogger
 | 
					import com.intellij.openapi.diagnostic.thisLogger
 | 
				
			||||||
import com.intellij.openapi.editor.Caret
 | 
					import com.intellij.openapi.editor.Caret
 | 
				
			||||||
import com.intellij.openapi.editor.CaretVisualAttributes
 | 
					import com.intellij.openapi.editor.CaretVisualAttributes
 | 
				
			||||||
@@ -19,17 +18,14 @@ 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.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.common.IsReplaceCharListener
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.common.ModeChangeListener
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.ij
 | 
					import com.maddyhome.idea.vim.newapi.ij
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
import com.maddyhome.idea.vim.options.EffectiveOptionValueChangeListener
 | 
					import com.maddyhome.idea.vim.options.EffectiveOptionValueChangeListener
 | 
				
			||||||
import com.maddyhome.idea.vim.options.helpers.GuiCursorMode
 | 
					import com.maddyhome.idea.vim.options.helpers.GuiCursorMode
 | 
				
			||||||
import com.maddyhome.idea.vim.options.helpers.GuiCursorOptionHelper
 | 
					import com.maddyhome.idea.vim.options.helpers.GuiCursorOptionHelper
 | 
				
			||||||
import com.maddyhome.idea.vim.options.helpers.GuiCursorType
 | 
					import com.maddyhome.idea.vim.options.helpers.GuiCursorType
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.inBlockSelection
 | 
					import com.maddyhome.idea.vim.state.mode.inBlockSelection
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.mode
 | 
				
			||||||
import org.jetbrains.annotations.TestOnly
 | 
					import org.jetbrains.annotations.TestOnly
 | 
				
			||||||
import java.awt.Color
 | 
					import java.awt.Color
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -89,12 +85,7 @@ private fun Editor.updatePrimaryCaretVisualAttributes() {
 | 
				
			|||||||
  caretModel.primaryCaret.visualAttributes = AttributesCache.getCaretVisualAttributes(this)
 | 
					  caretModel.primaryCaret.visualAttributes = AttributesCache.getCaretVisualAttributes(this)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Make sure the caret is visible as soon as it's set. It might be invisible while blinking
 | 
					  // Make sure the caret is visible as soon as it's set. It might be invisible while blinking
 | 
				
			||||||
  // NOTE: At the moment, this causes project leak in tests
 | 
					  (this as? EditorEx)?.setCaretVisible(true)
 | 
				
			||||||
  // IJPL-928 - this will be fixed in 2024.2
 | 
					 | 
				
			||||||
  // [VERSION UPDATE] 2024.2 - remove if wrapping
 | 
					 | 
				
			||||||
  if (!ApplicationManager.getApplication().isUnitTestMode) {
 | 
					 | 
				
			||||||
    (this as? EditorEx)?.setCaretVisible(true)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun Editor.updateSecondaryCaretsVisualAttributes() {
 | 
					private fun Editor.updateSecondaryCaretsVisualAttributes() {
 | 
				
			||||||
@@ -143,31 +134,3 @@ private object AttributesCache {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@TestOnly
 | 
					@TestOnly
 | 
				
			||||||
internal fun getGuiCursorMode(editor: Editor) = editor.guicursorMode()
 | 
					internal fun getGuiCursorMode(editor: Editor) = editor.guicursorMode()
 | 
				
			||||||
 | 
					 | 
				
			||||||
public class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeListener {
 | 
					 | 
				
			||||||
  override fun isReplaceCharChanged(editor: VimEditor) {
 | 
					 | 
				
			||||||
    updateCaretsVisual(editor)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  override fun modeChanged(editor: VimEditor, oldMode: Mode) {
 | 
					 | 
				
			||||||
    updateCaretsVisual(editor)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private fun updateCaretsVisual(editor: VimEditor) {
 | 
					 | 
				
			||||||
    if (injector.globalOptions().ideaglobalmode) {
 | 
					 | 
				
			||||||
      updateAllEditorsCaretsVisual()
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      val ijEditor = (editor as IjVimEditor).editor
 | 
					 | 
				
			||||||
      ijEditor.updateCaretsVisualAttributes()
 | 
					 | 
				
			||||||
      ijEditor.updateCaretsVisualPosition()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public fun updateAllEditorsCaretsVisual() {
 | 
					 | 
				
			||||||
    injector.editorGroup.getEditors().forEach { editor ->
 | 
					 | 
				
			||||||
      val ijEditor = (editor as IjVimEditor).editor
 | 
					 | 
				
			||||||
      ijEditor.updateCaretsVisualAttributes()
 | 
					 | 
				
			||||||
      ijEditor.updateCaretsVisualPosition()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -11,8 +11,8 @@ package com.maddyhome.idea.vim.helper
 | 
				
			|||||||
import com.intellij.openapi.application.ApplicationManager
 | 
					import com.intellij.openapi.application.ApplicationManager
 | 
				
			||||||
import com.intellij.openapi.components.Service
 | 
					import com.intellij.openapi.components.Service
 | 
				
			||||||
import com.maddyhome.idea.vim.action.change.Extension
 | 
					import com.maddyhome.idea.vim.action.change.Extension
 | 
				
			||||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
					 | 
				
			||||||
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.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.ui.ModalEntry
 | 
					import com.maddyhome.idea.vim.ui.ModalEntry
 | 
				
			||||||
@@ -23,7 +23,7 @@ import javax.swing.KeyStroke
 | 
				
			|||||||
@Service
 | 
					@Service
 | 
				
			||||||
internal class CommandLineHelper : VimCommandLineHelper {
 | 
					internal class CommandLineHelper : VimCommandLineHelper {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun inputString(vimEditor: VimEditor, context: ExecutionContext, prompt: String, finishOn: Char?): String? {
 | 
					  override fun inputString(vimEditor: VimEditor, prompt: String, finishOn: Char?): String? {
 | 
				
			||||||
    val editor = vimEditor.ij
 | 
					    val editor = vimEditor.ij
 | 
				
			||||||
    if (vimEditor.vimStateMachine.isDotRepeatInProgress) {
 | 
					    if (vimEditor.vimStateMachine.isDotRepeatInProgress) {
 | 
				
			||||||
      val input = Extension.consumeString()
 | 
					      val input = Extension.consumeString()
 | 
				
			||||||
@@ -53,7 +53,7 @@ internal class CommandLineHelper : VimCommandLineHelper {
 | 
				
			|||||||
      var text: String? = null
 | 
					      var text: String? = null
 | 
				
			||||||
      // XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for input()
 | 
					      // XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for input()
 | 
				
			||||||
      val exEntryPanel = ExEntryPanel.getInstanceWithoutShortcuts()
 | 
					      val exEntryPanel = ExEntryPanel.getInstanceWithoutShortcuts()
 | 
				
			||||||
      exEntryPanel.activate(editor, context.ij, prompt.ifEmpty { " " }, "", 1)
 | 
					      exEntryPanel.activate(editor, injector.executionContextManager.onEditor(editor.vim).ij, prompt.ifEmpty { " " }, "", 1)
 | 
				
			||||||
      ModalEntry.activate(editor.vim) { key: KeyStroke ->
 | 
					      ModalEntry.activate(editor.vim) { key: KeyStroke ->
 | 
				
			||||||
        return@activate when {
 | 
					        return@activate when {
 | 
				
			||||||
          key.isCloseKeyStroke() -> {
 | 
					          key.isCloseKeyStroke() -> {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,7 @@ import com.maddyhome.idea.vim.options.OptionAccessScope
 | 
				
			|||||||
import com.maddyhome.idea.vim.options.OptionConstants
 | 
					import com.maddyhome.idea.vim.options.OptionConstants
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
					import com.maddyhome.idea.vim.state.mode.Mode
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.inVisualMode
 | 
					import com.maddyhome.idea.vim.state.mode.inVisualMode
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.mode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal val Mode.hasVisualSelection
 | 
					internal val Mode.hasVisualSelection
 | 
				
			||||||
  get() = when (this) {
 | 
					  get() = when (this) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2003-2023 The IdeaVim authors
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Use of this source code is governed by an MIT-style
 | 
				
			||||||
 | 
					 * license that can be found in the LICENSE.txt file or at
 | 
				
			||||||
 | 
					 * https://opensource.org/licenses/MIT.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package com.maddyhome.idea.vim.helper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.intellij.openapi.editor.Document
 | 
				
			||||||
 | 
					import com.intellij.openapi.editor.event.DocumentListener
 | 
				
			||||||
 | 
					import com.intellij.openapi.util.Key
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.EventFacade
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.group.SearchGroup
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.group.VimMarkServiceImpl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					internal object DocumentManager {
 | 
				
			||||||
 | 
					  private val docListeners = mutableSetOf<DocumentListener>()
 | 
				
			||||||
 | 
					  private val LISTENER_MARKER = Key<String>("VimlistenerMarker")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  init {
 | 
				
			||||||
 | 
					    docListeners += VimMarkServiceImpl.MarkUpdater
 | 
				
			||||||
 | 
					    docListeners += SearchGroup.DocumentSearchListener.INSTANCE
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fun addListeners(doc: Document) {
 | 
				
			||||||
 | 
					    val marker = doc.getUserData(LISTENER_MARKER)
 | 
				
			||||||
 | 
					    if (marker != null) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    doc.putUserData(LISTENER_MARKER, "foo")
 | 
				
			||||||
 | 
					    for (docListener in docListeners) {
 | 
				
			||||||
 | 
					      EventFacade.getInstance().addDocumentListener(doc, docListener)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fun removeListeners(doc: Document) {
 | 
				
			||||||
 | 
					    doc.getUserData(LISTENER_MARKER) ?: return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    doc.putUserData(LISTENER_MARKER, null)
 | 
				
			||||||
 | 
					    for (docListener in docListeners) {
 | 
				
			||||||
 | 
					      EventFacade.getInstance().removeDocumentListener(doc, docListener)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -14,7 +14,6 @@ import com.intellij.openapi.editor.ex.util.EditorUtil
 | 
				
			|||||||
import com.intellij.openapi.util.Key
 | 
					import com.intellij.openapi.util.Key
 | 
				
			||||||
import com.intellij.openapi.util.UserDataHolder
 | 
					import com.intellij.openapi.util.UserDataHolder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Deprecated("Do not use context wrappers, use existing provided contexts. If no context available, use `injector.getExecutionContextManager().getEditorExecutionContext(editor)`")
 | 
					 | 
				
			||||||
internal class EditorDataContext @Deprecated("Please use `init` method") constructor(
 | 
					internal class EditorDataContext @Deprecated("Please use `init` method") constructor(
 | 
				
			||||||
  private val editor: Editor,
 | 
					  private val editor: Editor,
 | 
				
			||||||
  private val editorContext: DataContext,
 | 
					  private val editorContext: DataContext,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,6 @@ import com.intellij.testFramework.LightVirtualFile;
 | 
				
			|||||||
import com.maddyhome.idea.vim.api.EngineEditorHelperKt;
 | 
					import com.maddyhome.idea.vim.api.EngineEditorHelperKt;
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimEditor;
 | 
					import com.maddyhome.idea.vim.api.VimEditor;
 | 
				
			||||||
import com.maddyhome.idea.vim.common.IndentConfig;
 | 
					import com.maddyhome.idea.vim.common.IndentConfig;
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimDocument;
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor;
 | 
				
			||||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
 | 
					import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
 | 
				
			||||||
import kotlin.Pair;
 | 
					import kotlin.Pair;
 | 
				
			||||||
@@ -30,7 +29,6 @@ import java.util.Collections;
 | 
				
			|||||||
import java.util.Comparator;
 | 
					import java.util.Comparator;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
 | 
					 | 
				
			||||||
import static java.lang.Integer.max;
 | 
					import static java.lang.Integer.max;
 | 
				
			||||||
import static java.lang.Integer.min;
 | 
					import static java.lang.Integer.min;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -199,7 +197,7 @@ public class EditorHelper {
 | 
				
			|||||||
   * @param file The virtual file get the editor for
 | 
					   * @param file The virtual file get the editor for
 | 
				
			||||||
   * @return The matching editor or null if no match was found
 | 
					   * @return The matching editor or null if no match was found
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public static @Nullable VimEditor getEditor(final @Nullable VirtualFile file) {
 | 
					  public static @Nullable Editor getEditor(final @Nullable VirtualFile file) {
 | 
				
			||||||
    if (file == null) {
 | 
					    if (file == null) {
 | 
				
			||||||
      return null;
 | 
					      return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -208,15 +206,23 @@ public class EditorHelper {
 | 
				
			|||||||
    if (doc == null) {
 | 
					    if (doc == null) {
 | 
				
			||||||
      return null;
 | 
					      return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return injector.getEditorGroup().getEditors(new IjVimDocument(doc)).stream().findFirst().orElse(null);
 | 
					    final List<Editor> editors = HelperKt.localEditors(doc);
 | 
				
			||||||
 | 
					    if (editors.size() > 0) {
 | 
				
			||||||
 | 
					      return editors.get(0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static @NotNull String pad(final @NotNull Editor editor, int line, final int to) {
 | 
					  public static @NotNull String pad(final @NotNull Editor editor,
 | 
				
			||||||
 | 
					                                    @NotNull DataContext context,
 | 
				
			||||||
 | 
					                                    int line,
 | 
				
			||||||
 | 
					                                    final int to) {
 | 
				
			||||||
    final int len = EngineEditorHelperKt.lineLength(new IjVimEditor(editor), line);
 | 
					    final int len = EngineEditorHelperKt.lineLength(new IjVimEditor(editor), line);
 | 
				
			||||||
    if (len >= to) return "";
 | 
					    if (len >= to) return "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final int limit = to - len;
 | 
					    final int limit = to - len;
 | 
				
			||||||
    return IndentConfig.create(editor).createIndentBySize(limit);
 | 
					    return IndentConfig.create(editor, context).createIndentBySize(limit);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,6 @@ import com.intellij.openapi.editor.Editor
 | 
				
			|||||||
import com.intellij.openapi.editor.ex.util.EditorUtil
 | 
					import com.intellij.openapi.editor.ex.util.EditorUtil
 | 
				
			||||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
 | 
					import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
 | 
				
			||||||
import com.intellij.util.ui.table.JBTableRowEditor
 | 
					import com.intellij.util.ui.table.JBTableRowEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.api.StringListOptionValue
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.api.injector
 | 
					import com.maddyhome.idea.vim.api.injector
 | 
				
			||||||
import com.maddyhome.idea.vim.group.IjOptionConstants
 | 
					import com.maddyhome.idea.vim.group.IjOptionConstants
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
					import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
				
			||||||
@@ -38,27 +37,25 @@ public val Editor.fileSize: Int
 | 
				
			|||||||
internal val Editor.isIdeaVimDisabledHere: Boolean
 | 
					internal val Editor.isIdeaVimDisabledHere: Boolean
 | 
				
			||||||
  get() {
 | 
					  get() {
 | 
				
			||||||
    val ideaVimSupportValue = injector.globalIjOptions().ideavimsupport
 | 
					    val ideaVimSupportValue = injector.globalIjOptions().ideavimsupport
 | 
				
			||||||
    return (ideaVimDisabledInDialog(ideaVimSupportValue) && isInDialog()) ||
 | 
					    return disabledInDialog ||
 | 
				
			||||||
      !ClientId.isCurrentlyUnderLocalId || // CWM-927
 | 
					      (!ClientId.isCurrentlyUnderLocalId) || // CWM-927
 | 
				
			||||||
      (ideaVimDisabledForSingleLine(ideaVimSupportValue) && isSingleLine())
 | 
					      (!ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_singleline) && isDatabaseCell()) ||
 | 
				
			||||||
 | 
					      (!ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_singleline) && isOneLineMode)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun ideaVimDisabledInDialog(ideaVimSupportValue: StringListOptionValue): Boolean {
 | 
					private fun Editor.isDatabaseCell(): Boolean {
 | 
				
			||||||
  return !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialog)
 | 
					  return isTableCellEditor(this.component)
 | 
				
			||||||
    && !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialoglegacy)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun ideaVimDisabledForSingleLine(ideaVimSupportValue: StringListOptionValue): Boolean {
 | 
					private val Editor.disabledInDialog: Boolean
 | 
				
			||||||
  return !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_singleline)
 | 
					  get() {
 | 
				
			||||||
}
 | 
					    val ideaVimSupportValue = injector.globalIjOptions().ideavimsupport
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
private fun Editor.isInDialog(): Boolean {
 | 
					      !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialog) &&
 | 
				
			||||||
  return !this.isPrimaryEditor() && !EditorHelper.isFileEditor(this)
 | 
					        !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialoglegacy)
 | 
				
			||||||
}
 | 
					      ) &&
 | 
				
			||||||
 | 
					      (!this.isPrimaryEditor() && !EditorHelper.isFileEditor(this))
 | 
				
			||||||
private fun Editor.isSingleLine(): Boolean {
 | 
					  }
 | 
				
			||||||
  return isTableCellEditor(this.component) || isOneLineMode
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Checks if the editor is a primary editor in the main editing area.
 | 
					 * Checks if the editor is a primary editor in the main editing area.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,12 +9,19 @@
 | 
				
			|||||||
package com.maddyhome.idea.vim.helper
 | 
					package com.maddyhome.idea.vim.helper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.codeInsight.template.TemplateManager
 | 
					import com.intellij.codeInsight.template.TemplateManager
 | 
				
			||||||
 | 
					import com.intellij.codeWithMe.ClientId
 | 
				
			||||||
import com.intellij.injected.editor.EditorWindow
 | 
					import com.intellij.injected.editor.EditorWindow
 | 
				
			||||||
import com.intellij.openapi.editor.Caret
 | 
					import com.intellij.openapi.editor.Caret
 | 
				
			||||||
 | 
					import com.intellij.openapi.editor.ClientEditorManager
 | 
				
			||||||
 | 
					import com.intellij.openapi.editor.Document
 | 
				
			||||||
import com.intellij.openapi.editor.Editor
 | 
					import com.intellij.openapi.editor.Editor
 | 
				
			||||||
 | 
					import com.intellij.openapi.editor.EditorFactory
 | 
				
			||||||
 | 
					import com.intellij.openapi.project.Project
 | 
				
			||||||
 | 
					import com.intellij.openapi.util.Key
 | 
				
			||||||
import com.maddyhome.idea.vim.VimPlugin
 | 
					import com.maddyhome.idea.vim.VimPlugin
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.inBlockSelection
 | 
					import com.maddyhome.idea.vim.state.mode.inBlockSelection
 | 
				
			||||||
 | 
					import java.util.stream.Collectors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal fun <T : Comparable<T>> sort(a: T, b: T) = if (a > b) b to a else a to b
 | 
					internal fun <T : Comparable<T>> sort(a: T, b: T) = if (a > b) b to a else a to b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -29,6 +36,34 @@ internal inline fun Editor.vimForEachCaret(action: (caret: Caret) -> Unit) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
internal fun Editor.getTopLevelEditor() = if (this is EditorWindow) this.delegate else this
 | 
					internal fun Editor.getTopLevelEditor() = if (this is EditorWindow) this.delegate else this
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Return list of editors for local host (for code with me plugin)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public fun localEditors(): List<Editor> {
 | 
				
			||||||
 | 
					  return ClientEditorManager.getCurrentInstance().editors().collect(Collectors.toList())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public fun localEditors(doc: Document): List<Editor> {
 | 
				
			||||||
 | 
					  return EditorFactory.getInstance().getEditors(doc)
 | 
				
			||||||
 | 
					    .filter { editor -> editor.editorClientId.let { it == null || it == ClientId.currentOrNull } }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public fun localEditors(doc: Document, project: Project): List<Editor> {
 | 
				
			||||||
 | 
					  return EditorFactory.getInstance().getEditors(doc, project)
 | 
				
			||||||
 | 
					    .filter { editor -> editor.editorClientId.let { it == null || it == ClientId.currentOrNull } }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private val Editor.editorClientId: ClientId?
 | 
				
			||||||
 | 
					  get() {
 | 
				
			||||||
 | 
					    if (editorClientKey == null) {
 | 
				
			||||||
 | 
					      @Suppress("DEPRECATION")
 | 
				
			||||||
 | 
					      editorClientKey = Key.findKeyByName("editorClientIdby userData()") ?: return null
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return editorClientKey?.let { this.getUserData(it) as? ClientId }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private var editorClientKey: Key<*>? = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Suppress("IncorrectParentDisposable")
 | 
					@Suppress("IncorrectParentDisposable")
 | 
				
			||||||
internal fun Editor.isTemplateActive(): Boolean {
 | 
					internal fun Editor.isTemplateActive(): Boolean {
 | 
				
			||||||
  val project = this.project ?: return false
 | 
					  val project = this.project ?: return false
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,6 @@ import com.intellij.openapi.actionSystem.PlatformDataKeys
 | 
				
			|||||||
import com.intellij.openapi.actionSystem.ex.ActionManagerEx
 | 
					import com.intellij.openapi.actionSystem.ex.ActionManagerEx
 | 
				
			||||||
import com.intellij.openapi.actionSystem.ex.ActionUtil
 | 
					import com.intellij.openapi.actionSystem.ex.ActionUtil
 | 
				
			||||||
import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
 | 
					import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
 | 
				
			||||||
import com.intellij.openapi.actionSystem.impl.Utils
 | 
					 | 
				
			||||||
import com.intellij.openapi.command.CommandProcessor
 | 
					import com.intellij.openapi.command.CommandProcessor
 | 
				
			||||||
import com.intellij.openapi.command.UndoConfirmationPolicy
 | 
					import com.intellij.openapi.command.UndoConfirmationPolicy
 | 
				
			||||||
import com.intellij.openapi.components.Service
 | 
					import com.intellij.openapi.components.Service
 | 
				
			||||||
@@ -35,6 +34,7 @@ import com.maddyhome.idea.vim.api.ExecutionContext
 | 
				
			|||||||
import com.maddyhome.idea.vim.api.NativeAction
 | 
					import com.maddyhome.idea.vim.api.NativeAction
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimActionExecutor
 | 
					import com.maddyhome.idea.vim.api.VimActionExecutor
 | 
				
			||||||
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.command.OperatorArguments
 | 
					import com.maddyhome.idea.vim.command.OperatorArguments
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
 | 
					import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjNativeAction
 | 
					import com.maddyhome.idea.vim.newapi.IjNativeAction
 | 
				
			||||||
@@ -78,7 +78,6 @@ internal class IjActionExecutor : VimActionExecutor {
 | 
				
			|||||||
    val dataContext = DataContextWrapper(context.ij)
 | 
					    val dataContext = DataContextWrapper(context.ij)
 | 
				
			||||||
    dataContext.putUserData(runFromVimKey, true)
 | 
					    dataContext.putUserData(runFromVimKey, true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val actionId = ActionManager.getInstance().getId(ijAction)
 | 
					 | 
				
			||||||
    val event = AnActionEvent(
 | 
					    val event = AnActionEvent(
 | 
				
			||||||
      null,
 | 
					      null,
 | 
				
			||||||
      dataContext,
 | 
					      dataContext,
 | 
				
			||||||
@@ -87,20 +86,11 @@ internal class IjActionExecutor : VimActionExecutor {
 | 
				
			|||||||
      ActionManager.getInstance(),
 | 
					      ActionManager.getInstance(),
 | 
				
			||||||
      0,
 | 
					      0,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    Utils.initUpdateSession(event)
 | 
					 | 
				
			||||||
    // beforeActionPerformedUpdate should be called to update the action. It fixes some rider-specific problems.
 | 
					    // beforeActionPerformedUpdate should be called to update the action. It fixes some rider-specific problems.
 | 
				
			||||||
    //   because rider use async update method. See VIM-1819.
 | 
					    //   because rider use async update method. See VIM-1819.
 | 
				
			||||||
    // This method executes inside of lastUpdateAndCheckDumb
 | 
					    // This method executes inside of lastUpdateAndCheckDumb
 | 
				
			||||||
    // Another related issue: VIM-2604
 | 
					    // Another related issue: VIM-2604
 | 
				
			||||||
 | 
					    if (!ActionUtil.lastUpdateAndCheckDumb(ijAction, event, false)) return false
 | 
				
			||||||
    // This is a hack to fix the tests and fix VIM-3332
 | 
					 | 
				
			||||||
    // We should get rid of it in VIM-3376
 | 
					 | 
				
			||||||
    if (actionId == "RunClass" || actionId == IdeActions.ACTION_COMMENT_LINE || actionId == IdeActions.ACTION_COMMENT_BLOCK) {
 | 
					 | 
				
			||||||
      ijAction.beforeActionPerformedUpdate(event)
 | 
					 | 
				
			||||||
      if (!event.presentation.isEnabled) return false
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      if (!ActionUtil.lastUpdateAndCheckDumb(ijAction, event, false)) return false
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (ijAction is ActionGroup && !event.presentation.isPerformGroup) {
 | 
					    if (ijAction is ActionGroup && !event.presentation.isPerformGroup) {
 | 
				
			||||||
      // Some ActionGroups should not be performed, but shown as a popup
 | 
					      // Some ActionGroups should not be performed, but shown as a popup
 | 
				
			||||||
      val popup = JBPopupFactory.getInstance()
 | 
					      val popup = JBPopupFactory.getInstance()
 | 
				
			||||||
@@ -224,7 +214,7 @@ internal class IjActionExecutor : VimActionExecutor {
 | 
				
			|||||||
    CommandProcessor.getInstance()
 | 
					    CommandProcessor.getInstance()
 | 
				
			||||||
      .executeCommand(
 | 
					      .executeCommand(
 | 
				
			||||||
        editor.ij.project,
 | 
					        editor.ij.project,
 | 
				
			||||||
        { cmd.execute(editor, context, operatorArguments) },
 | 
					        { cmd.execute(editor, injector.executionContextManager.onEditor(editor, context), operatorArguments) },
 | 
				
			||||||
        cmd.id,
 | 
					        cmd.id,
 | 
				
			||||||
        DocCommandGroupId.noneGroupId(editor.ij.document),
 | 
					        DocCommandGroupId.noneGroupId(editor.ij.document),
 | 
				
			||||||
        UndoConfirmationPolicy.DEFAULT,
 | 
					        UndoConfirmationPolicy.DEFAULT,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ import com.intellij.openapi.editor.VisualPosition
 | 
				
			|||||||
import com.intellij.openapi.editor.actionSystem.EditorActionManager
 | 
					import com.intellij.openapi.editor.actionSystem.EditorActionManager
 | 
				
			||||||
import com.intellij.openapi.editor.ex.util.EditorUtil
 | 
					import com.intellij.openapi.editor.ex.util.EditorUtil
 | 
				
			||||||
import com.maddyhome.idea.vim.api.EngineEditorHelper
 | 
					import com.maddyhome.idea.vim.api.EngineEditorHelper
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.ExecutionContext
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
					import com.maddyhome.idea.vim.api.VimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimVisualPosition
 | 
					import com.maddyhome.idea.vim.api.VimVisualPosition
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
				
			||||||
@@ -50,8 +51,8 @@ internal class IjEditorHelper : EngineEditorHelper {
 | 
				
			|||||||
    return EditorHelper.getVisualLineAtBottomOfScreen(editor.ij)
 | 
					    return EditorHelper.getVisualLineAtBottomOfScreen(editor.ij)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun pad(editor: VimEditor, line: Int, to: Int): String {
 | 
					  override fun pad(editor: VimEditor, context: ExecutionContext, line: Int, to: Int): String {
 | 
				
			||||||
    return EditorHelper.pad(editor.ij, line, to)
 | 
					    return EditorHelper.pad(editor.ij, context.ij, line, to)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun inlayAwareOffsetToVisualPosition(editor: VimEditor, offset: Int): VimVisualPosition {
 | 
					  override fun inlayAwareOffsetToVisualPosition(editor: VimEditor, offset: Int): VimVisualPosition {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,15 +34,15 @@ internal fun Editor.exitSelectMode(adjustCaretPosition: Boolean) {
 | 
				
			|||||||
  val returnTo = this.vim.vimStateMachine.mode.returnTo
 | 
					  val returnTo = this.vim.vimStateMachine.mode.returnTo
 | 
				
			||||||
  when (returnTo) {
 | 
					  when (returnTo) {
 | 
				
			||||||
    ReturnTo.INSERT -> {
 | 
					    ReturnTo.INSERT -> {
 | 
				
			||||||
      this.vim.mode = Mode.INSERT
 | 
					      this.vim.vimStateMachine.mode = Mode.INSERT
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ReturnTo.REPLACE -> {
 | 
					    ReturnTo.REPLACE -> {
 | 
				
			||||||
      this.vim.mode = Mode.REPLACE
 | 
					      this.vim.vimStateMachine.mode = Mode.REPLACE
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    null -> {
 | 
					    null -> {
 | 
				
			||||||
      this.vim.mode = Mode.NORMAL()
 | 
					      this.vim.vimStateMachine.mode = Mode.NORMAL()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  SelectionVimListenerSuppressor.lock().use {
 | 
					  SelectionVimListenerSuppressor.lock().use {
 | 
				
			||||||
@@ -67,15 +67,15 @@ internal fun VimEditor.exitSelectMode(adjustCaretPosition: Boolean) {
 | 
				
			|||||||
  val returnTo = this.vimStateMachine.mode.returnTo
 | 
					  val returnTo = this.vimStateMachine.mode.returnTo
 | 
				
			||||||
  when (returnTo) {
 | 
					  when (returnTo) {
 | 
				
			||||||
    ReturnTo.INSERT -> {
 | 
					    ReturnTo.INSERT -> {
 | 
				
			||||||
      this.mode = Mode.INSERT
 | 
					      this.vimStateMachine.mode = Mode.INSERT
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ReturnTo.REPLACE -> {
 | 
					    ReturnTo.REPLACE -> {
 | 
				
			||||||
      this.mode = Mode.REPLACE
 | 
					      this.vimStateMachine.mode = Mode.REPLACE
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    null -> {
 | 
					    null -> {
 | 
				
			||||||
      this.mode = Mode.NORMAL()
 | 
					      this.vimStateMachine.mode = Mode.NORMAL()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  SelectionVimListenerSuppressor.lock().use {
 | 
					  SelectionVimListenerSuppressor.lock().use {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,13 +26,15 @@ import com.intellij.spellchecker.SpellCheckerSeveritiesProvider;
 | 
				
			|||||||
import com.maddyhome.idea.vim.VimPlugin;
 | 
					import com.maddyhome.idea.vim.VimPlugin;
 | 
				
			||||||
import com.maddyhome.idea.vim.api.EngineEditorHelperKt;
 | 
					import com.maddyhome.idea.vim.api.EngineEditorHelperKt;
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimEditor;
 | 
					import com.maddyhome.idea.vim.api.VimEditor;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.regexp.*;
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.regexp.match.VimMatchResult;
 | 
				
			||||||
import com.maddyhome.idea.vim.common.CharacterPosition;
 | 
					import com.maddyhome.idea.vim.common.CharacterPosition;
 | 
				
			||||||
import com.maddyhome.idea.vim.common.Direction;
 | 
					import com.maddyhome.idea.vim.common.Direction;
 | 
				
			||||||
import com.maddyhome.idea.vim.common.TextRange;
 | 
					import com.maddyhome.idea.vim.common.TextRange;
 | 
				
			||||||
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.regexp.*;
 | 
					import com.maddyhome.idea.vim.regexp.CharPointer;
 | 
				
			||||||
import com.maddyhome.idea.vim.regexp.match.VimMatchResult;
 | 
					import com.maddyhome.idea.vim.regexp.RegExp;
 | 
				
			||||||
import com.maddyhome.idea.vim.state.VimStateMachine;
 | 
					import com.maddyhome.idea.vim.state.VimStateMachine;
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode;
 | 
					import com.maddyhome.idea.vim.state.mode.Mode;
 | 
				
			||||||
import it.unimi.dsi.fastutil.ints.IntComparator;
 | 
					import it.unimi.dsi.fastutil.ints.IntComparator;
 | 
				
			||||||
@@ -632,6 +634,113 @@ public class SearchHelper {
 | 
				
			|||||||
    return new TextRange(bstart, bend + 1);
 | 
					    return new TextRange(bstart, bend + 1);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static int findMatchingBlockCommentPair(@NotNull PsiComment comment,
 | 
				
			||||||
 | 
					                                                  int pos,
 | 
				
			||||||
 | 
					                                                  @Nullable String prefix,
 | 
				
			||||||
 | 
					                                                  @Nullable String suffix) {
 | 
				
			||||||
 | 
					    if (prefix != null && suffix != null) {
 | 
				
			||||||
 | 
					      // TODO: Try to get rid of `getText()` because it takes a lot of time to calculate the string
 | 
				
			||||||
 | 
					      final String commentText = comment.getText();
 | 
				
			||||||
 | 
					      if (commentText.startsWith(prefix) && commentText.endsWith(suffix)) {
 | 
				
			||||||
 | 
					        final int endOffset = comment.getTextOffset() + comment.getTextLength();
 | 
				
			||||||
 | 
					        if (pos < comment.getTextOffset() + prefix.length()) {
 | 
				
			||||||
 | 
					          return endOffset;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (pos >= endOffset - suffix.length()) {
 | 
				
			||||||
 | 
					          return comment.getTextOffset();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return -1;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static int findMatchingBlockCommentPair(@NotNull PsiElement element, int pos) {
 | 
				
			||||||
 | 
					    final Language language = element.getLanguage();
 | 
				
			||||||
 | 
					    final Commenter commenter = LanguageCommenters.INSTANCE.forLanguage(language);
 | 
				
			||||||
 | 
					    final PsiComment comment = PsiTreeUtil.getParentOfType(element, PsiComment.class, false);
 | 
				
			||||||
 | 
					    if (comment != null) {
 | 
				
			||||||
 | 
					      final int ret = findMatchingBlockCommentPair(comment, pos, commenter.getBlockCommentPrefix(),
 | 
				
			||||||
 | 
					                                                   commenter.getBlockCommentSuffix());
 | 
				
			||||||
 | 
					      if (ret >= 0) {
 | 
				
			||||||
 | 
					        return ret;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (commenter instanceof CodeDocumentationAwareCommenter docCommenter) {
 | 
				
			||||||
 | 
					        return findMatchingBlockCommentPair(comment, pos, docCommenter.getDocumentationCommentPrefix(),
 | 
				
			||||||
 | 
					                                            docCommenter.getDocumentationCommentSuffix());
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return -1;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * This looks on the current line, starting at the cursor position for one of {, }, (, ), [, or ]. It then searches
 | 
				
			||||||
 | 
					   * forward or backward, as appropriate for the associated match pair. String in double quotes are skipped over.
 | 
				
			||||||
 | 
					   * Single characters in single quotes are skipped too.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param editor The editor to search in
 | 
				
			||||||
 | 
					   * @return The offset within the editor of the found character or -1 if no match was found or none of the characters
 | 
				
			||||||
 | 
					   * were found on the remainder of the current line.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public static int findMatchingPairOnCurrentLine(@NotNull Editor editor, @NotNull Caret caret) {
 | 
				
			||||||
 | 
					    int pos = caret.getOffset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final int commentPos = findMatchingComment(editor, pos);
 | 
				
			||||||
 | 
					    if (commentPos >= 0) {
 | 
				
			||||||
 | 
					      return commentPos;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int line = caret.getLogicalPosition().line;
 | 
				
			||||||
 | 
					    final IjVimEditor vimEditor = new IjVimEditor(editor);
 | 
				
			||||||
 | 
					    int end = EngineEditorHelperKt.getLineEndOffset(vimEditor, line, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // To handle the case where visual mode allows the user to go past the end of the line,
 | 
				
			||||||
 | 
					    // which will prevent loc from finding a pairable character below
 | 
				
			||||||
 | 
					    if (pos > 0 && pos == end) {
 | 
				
			||||||
 | 
					      pos = end - 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final String pairChars = parseMatchPairsOption(vimEditor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CharSequence chars = editor.getDocument().getCharsSequence();
 | 
				
			||||||
 | 
					    int loc = -1;
 | 
				
			||||||
 | 
					    // Search the remainder of the current line for one of the candidate characters
 | 
				
			||||||
 | 
					    while (pos < end) {
 | 
				
			||||||
 | 
					      loc = pairChars.indexOf(chars.charAt(pos));
 | 
				
			||||||
 | 
					      if (loc >= 0) {
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pos++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int res = -1;
 | 
				
			||||||
 | 
					    // If we found one ...
 | 
				
			||||||
 | 
					    if (loc >= 0) {
 | 
				
			||||||
 | 
					      // What direction should we go now (-1 is backward, 1 is forward)
 | 
				
			||||||
 | 
					      Direction dir = loc % 2 == 0 ? Direction.FORWARDS : Direction.BACKWARDS;
 | 
				
			||||||
 | 
					      // Which character did we find and which should we now search for
 | 
				
			||||||
 | 
					      char found = pairChars.charAt(loc);
 | 
				
			||||||
 | 
					      char match = pairChars.charAt(loc + dir.toInt());
 | 
				
			||||||
 | 
					      res = findBlockLocation(chars, found, match, dir, pos, 1, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * If on the start/end of a block comment, jump to the matching of that comment, or vice versa.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private static int findMatchingComment(@NotNull Editor editor, int pos) {
 | 
				
			||||||
 | 
					    final PsiFile psiFile = PsiHelper.getFile(editor);
 | 
				
			||||||
 | 
					    if (psiFile != null) {
 | 
				
			||||||
 | 
					      final PsiElement element = psiFile.findElementAt(pos);
 | 
				
			||||||
 | 
					      if (element != null) {
 | 
				
			||||||
 | 
					        return findMatchingBlockCommentPair(element, pos);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return -1;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static int findBlockLocation(@NotNull CharSequence chars,
 | 
					  private static int findBlockLocation(@NotNull CharSequence chars,
 | 
				
			||||||
                                       char found,
 | 
					                                       char found,
 | 
				
			||||||
                                       char match,
 | 
					                                       char match,
 | 
				
			||||||
@@ -1494,27 +1603,21 @@ public class SearchHelper {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    if (offsets.isEmpty()) {
 | 
					    if (offsets.isEmpty()) {
 | 
				
			||||||
      return -1;
 | 
					      return -1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    if (skipCount >= offsets.size()) {
 | 
					    if (skipCount >= offsets.size()) {
 | 
				
			||||||
      return offsets.lastInt();
 | 
					      return offsets.lastInt();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else {
 | 
					    else {
 | 
				
			||||||
      IntIterator offsetIterator = offsets.iterator();
 | 
					      IntIterator offsetIterator = offsets.iterator();
 | 
				
			||||||
      skip(offsetIterator, skipCount);
 | 
					      offsetIterator.skip(skipCount);
 | 
				
			||||||
      return offsetIterator.nextInt();
 | 
					      return offsetIterator.nextInt();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static void skip(IntIterator iterator, final int n) {
 | 
					 | 
				
			||||||
    if (n < 0) throw new IllegalArgumentException("Argument must be nonnegative: " + n);
 | 
					 | 
				
			||||||
    int i = n;
 | 
					 | 
				
			||||||
    while (i-- != 0 && iterator.hasNext()) iterator.nextInt();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private static @NotNull String parseMatchPairsOption(final VimEditor vimEditor) {
 | 
					  private static @NotNull String parseMatchPairsOption(final VimEditor vimEditor) {
 | 
				
			||||||
    List<String> pairs = options(injector, vimEditor).getMatchpairs();
 | 
					    List<String> pairs = options(injector, vimEditor).getMatchpairs();
 | 
				
			||||||
    StringBuilder res = new StringBuilder();
 | 
					    StringBuilder res = new StringBuilder();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,7 +26,6 @@ import com.maddyhome.idea.vim.api.injector
 | 
				
			|||||||
import com.maddyhome.idea.vim.api.options
 | 
					import com.maddyhome.idea.vim.api.options
 | 
				
			||||||
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.ex.ranges.LineRange
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimDocument
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
import org.jetbrains.annotations.Contract
 | 
					import org.jetbrains.annotations.Contract
 | 
				
			||||||
@@ -87,24 +86,11 @@ private fun updateSearchHighlights(
 | 
				
			|||||||
): Int {
 | 
					): Int {
 | 
				
			||||||
  var currentMatchOffset = -1
 | 
					  var currentMatchOffset = -1
 | 
				
			||||||
  val projectManager = ProjectManager.getInstanceIfCreated() ?: return currentMatchOffset
 | 
					  val projectManager = ProjectManager.getInstanceIfCreated() ?: return currentMatchOffset
 | 
				
			||||||
 | 
					 | 
				
			||||||
  // TODO: This implementation needs rethinking
 | 
					 | 
				
			||||||
  // It's a bit weird that we update search highlights across all open projects. It would make more sense to treat top
 | 
					 | 
				
			||||||
  // level project frame windows as separate applications, but we can't do this because IdeaVim does not maintain state
 | 
					 | 
				
			||||||
  // per-project.
 | 
					 | 
				
			||||||
  // So, to be clear, this will loop over each project, and therefore, for each project top-level frame, will update
 | 
					 | 
				
			||||||
  // search highlights in all editors for the document of the currently selected editor. It does not update highlights
 | 
					 | 
				
			||||||
  // for editors for the document that are in other projects.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (project in projectManager.openProjects) {
 | 
					  for (project in projectManager.openProjects) {
 | 
				
			||||||
    val current = FileEditorManager.getInstance(project).selectedTextEditor ?: continue
 | 
					    val current = FileEditorManager.getInstance(project).selectedTextEditor ?: continue
 | 
				
			||||||
    val editors = injector.editorGroup.getEditors(IjVimDocument(current.document))
 | 
					    // [VERSION UPDATE] 202+ Use editors
 | 
				
			||||||
    for (vimEditor in editors) {
 | 
					    val editors = localEditors(current.document, project)
 | 
				
			||||||
      val editor = (vimEditor as IjVimEditor).editor
 | 
					    for (editor in editors) {
 | 
				
			||||||
      if (editor.project != project) {
 | 
					 | 
				
			||||||
        continue
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // Try to keep existing highlights if possible. Update if hlsearch has changed or if the pattern has changed.
 | 
					      // Try to keep existing highlights if possible. Update if hlsearch has changed or if the pattern has changed.
 | 
				
			||||||
      // Force update for the situations where the text is the same, but the ignore case values have changed.
 | 
					      // Force update for the situations where the text is the same, but the ignore case values have changed.
 | 
				
			||||||
      // E.g. Use `*` to search for a word (which ignores smartcase), then use `/<Up>` to search for the same pattern,
 | 
					      // E.g. Use `*` to search for a word (which ignores smartcase), then use `/<Up>` to search for the same pattern,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,8 +31,4 @@ public object StringHelper {
 | 
				
			|||||||
    return Arrays.stream(string).flatMap { o: String -> injector.parser.parseKeys(o).stream() }
 | 
					    return Arrays.stream(string).flatMap { o: String -> injector.parser.parseKeys(o).stream() }
 | 
				
			||||||
      .collect(Collectors.toList())
 | 
					      .collect(Collectors.toList())
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  @JvmStatic
 | 
					 | 
				
			||||||
  @Deprecated("Use key.isCloseKeyStroke()", ReplaceWith("key.isCloseKeyStroke()"))
 | 
					 | 
				
			||||||
  public fun isCloseKeyStroke(key: KeyStroke): Boolean = key.isCloseKeyStroke()
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,13 +10,10 @@ package com.maddyhome.idea.vim.helper
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.actionSystem.DataContext
 | 
					import com.intellij.openapi.actionSystem.DataContext
 | 
				
			||||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
 | 
					import com.intellij.openapi.actionSystem.PlatformDataKeys
 | 
				
			||||||
import com.intellij.openapi.application.ApplicationInfo
 | 
					 | 
				
			||||||
import com.intellij.openapi.command.CommandProcessor
 | 
					import com.intellij.openapi.command.CommandProcessor
 | 
				
			||||||
import com.intellij.openapi.command.undo.UndoManager
 | 
					import com.intellij.openapi.command.undo.UndoManager
 | 
				
			||||||
import com.intellij.openapi.components.Service
 | 
					import com.intellij.openapi.components.Service
 | 
				
			||||||
import com.intellij.openapi.fileEditor.TextEditor
 | 
					 | 
				
			||||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
 | 
					import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
 | 
				
			||||||
import com.intellij.openapi.util.registry.Registry
 | 
					 | 
				
			||||||
import com.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.VimEditor
 | 
					import com.maddyhome.idea.vim.api.VimEditor
 | 
				
			||||||
@@ -43,25 +40,7 @@ internal class UndoRedoHelper : UndoRedoBase() {
 | 
				
			|||||||
      val scrollingModel = editor.getScrollingModel()
 | 
					      val scrollingModel = editor.getScrollingModel()
 | 
				
			||||||
      scrollingModel.accumulateViewportChanges()
 | 
					      scrollingModel.accumulateViewportChanges()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // [VERSION UPDATE] 241+ remove this if
 | 
					      if (injector.globalIjOptions().oldundo) {
 | 
				
			||||||
      if (ApplicationInfo.getInstance().build.baselineVersion >= 241) {
 | 
					 | 
				
			||||||
        undoFor241plus(editor, undoManager, fileEditor)
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        undoForLessThan241(undoManager, fileEditor, editor)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      scrollingModel.flushViewportChanges()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return true
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return false
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private fun undoForLessThan241(
 | 
					 | 
				
			||||||
    undoManager: UndoManager,
 | 
					 | 
				
			||||||
    fileEditor: TextEditor,
 | 
					 | 
				
			||||||
    editor: VimEditor,
 | 
					 | 
				
			||||||
  ) {if (injector.globalIjOptions().oldundo) {
 | 
					 | 
				
			||||||
        SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) }
 | 
					        SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) }
 | 
				
			||||||
        restoreVisualMode(editor)
 | 
					        restoreVisualMode(editor)
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
@@ -69,47 +48,22 @@ internal class UndoRedoHelper : UndoRedoBase() {
 | 
				
			|||||||
        editor.runWithChangeTracking {
 | 
					        editor.runWithChangeTracking {
 | 
				
			||||||
          undoManager.undo(fileEditor)
 | 
					          undoManager.undo(fileEditor)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // We execute undo one more time if the previous one just restored selection
 | 
					          // We execute undo one more time if the previous one just restored selection
 | 
				
			||||||
        if (!hasChanges && hasSelection(editor) && undoManager.isUndoAvailable(fileEditor)) {
 | 
					          if (!hasChanges && hasSelection(editor) && undoManager.isUndoAvailable(fileEditor)) {
 | 
				
			||||||
          undoManager.undo(fileEditor)
 | 
					            undoManager.undo(fileEditor)
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        CommandProcessor.getInstance().runUndoTransparentAction {
 | 
				
			||||||
 | 
					          removeSelections(editor)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      CommandProcessor.getInstance().runUndoTransparentAction {
 | 
					      scrollingModel.flushViewportChanges()
 | 
				
			||||||
        removeSelections(editor)
 | 
					
 | 
				
			||||||
      }
 | 
					      return true
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private fun undoFor241plus(
 | 
					 | 
				
			||||||
    editor: VimEditor,
 | 
					 | 
				
			||||||
    undoManager: UndoManager,
 | 
					 | 
				
			||||||
    fileEditor: TextEditor,
 | 
					 | 
				
			||||||
  ) {
 | 
					 | 
				
			||||||
    if (injector.globalIjOptions().oldundo) {
 | 
					 | 
				
			||||||
      // TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
 | 
					 | 
				
			||||||
      editor.runWithChangeTracking {
 | 
					 | 
				
			||||||
        undoManager.undo(fileEditor)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // We execute undo one more time if the previous one just restored selection
 | 
					 | 
				
			||||||
        if (!hasChanges && hasSelection(editor) && undoManager.isUndoAvailable(fileEditor)) {
 | 
					 | 
				
			||||||
          undoManager.undo(fileEditor)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      CommandProcessor.getInstance().runUndoTransparentAction {
 | 
					 | 
				
			||||||
        removeSelections(editor)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      runWithBooleanRegistryOption("ide.undo.transparent.caret.movement", true) {
 | 
					 | 
				
			||||||
        undoManager.undo(fileEditor)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      CommandProcessor.getInstance().runUndoTransparentAction {
 | 
					 | 
				
			||||||
        removeSelections(editor)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return false
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private fun hasSelection(editor: VimEditor): Boolean {
 | 
					  private fun hasSelection(editor: VimEditor): Boolean {
 | 
				
			||||||
@@ -122,23 +76,7 @@ internal class UndoRedoHelper : UndoRedoBase() {
 | 
				
			|||||||
    val fileEditor = TextEditorProvider.getInstance().getTextEditor(editor.ij)
 | 
					    val fileEditor = TextEditorProvider.getInstance().getTextEditor(editor.ij)
 | 
				
			||||||
    val undoManager = UndoManager.getInstance(project)
 | 
					    val undoManager = UndoManager.getInstance(project)
 | 
				
			||||||
    if (undoManager.isRedoAvailable(fileEditor)) {
 | 
					    if (undoManager.isRedoAvailable(fileEditor)) {
 | 
				
			||||||
      // [VERSION UPDATE] 241+ remove this if
 | 
					      if (injector.globalIjOptions().oldundo) {
 | 
				
			||||||
      if (ApplicationInfo.getInstance().build.baselineVersion >= 241) {
 | 
					 | 
				
			||||||
        redoFor241Plus(undoManager, fileEditor, editor)
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        redoForLessThan241(undoManager, fileEditor, editor)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return true
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return false
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private fun redoForLessThan241(
 | 
					 | 
				
			||||||
    undoManager: UndoManager,
 | 
					 | 
				
			||||||
    fileEditor: TextEditor,
 | 
					 | 
				
			||||||
    editor: VimEditor,
 | 
					 | 
				
			||||||
  ) {if (injector.globalIjOptions().oldundo) {
 | 
					 | 
				
			||||||
        SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) }
 | 
					        SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) }
 | 
				
			||||||
        restoreVisualMode(editor)
 | 
					        restoreVisualMode(editor)
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
@@ -150,50 +88,19 @@ internal class UndoRedoHelper : UndoRedoBase() {
 | 
				
			|||||||
        editor.runWithChangeTracking {
 | 
					        editor.runWithChangeTracking {
 | 
				
			||||||
          undoManager.redo(fileEditor)
 | 
					          undoManager.redo(fileEditor)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // We execute undo one more time if the previous one just restored selection
 | 
					          // We execute undo one more time if the previous one just restored selection
 | 
				
			||||||
        if (!hasChanges && hasSelection(editor) && undoManager.isRedoAvailable(fileEditor)) {
 | 
					          if (!hasChanges && hasSelection(editor) && undoManager.isRedoAvailable(fileEditor)) {
 | 
				
			||||||
          undoManager.redo(fileEditor)
 | 
					            undoManager.redo(fileEditor)
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        CommandProcessor.getInstance().runUndoTransparentAction {
 | 
				
			||||||
 | 
					          removeSelections(editor)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      return true
 | 
				
			||||||
      CommandProcessor.getInstance().runUndoTransparentAction {
 | 
					 | 
				
			||||||
        removeSelections(editor)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private fun redoFor241Plus(
 | 
					 | 
				
			||||||
    undoManager: UndoManager,
 | 
					 | 
				
			||||||
    fileEditor: TextEditor,
 | 
					 | 
				
			||||||
    editor: VimEditor,
 | 
					 | 
				
			||||||
  ) {
 | 
					 | 
				
			||||||
    if (injector.globalIjOptions().oldundo) {
 | 
					 | 
				
			||||||
      undoManager.redo(fileEditor)
 | 
					 | 
				
			||||||
      CommandProcessor.getInstance().runUndoTransparentAction {
 | 
					 | 
				
			||||||
        editor.carets().forEach { it.ij.removeSelection() }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      // TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
 | 
					 | 
				
			||||||
      editor.runWithChangeTracking {
 | 
					 | 
				
			||||||
        undoManager.redo(fileEditor)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // We execute undo one more time if the previous one just restored selection
 | 
					 | 
				
			||||||
        if (!hasChanges && hasSelection(editor) && undoManager.isRedoAvailable(fileEditor)) {
 | 
					 | 
				
			||||||
          undoManager.redo(fileEditor)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      CommandProcessor.getInstance().runUndoTransparentAction {
 | 
					 | 
				
			||||||
        removeSelections(editor)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      runWithBooleanRegistryOption("ide.undo.transparent.caret.movement", true) {
 | 
					 | 
				
			||||||
        undoManager.redo(fileEditor)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      CommandProcessor.getInstance().runUndoTransparentAction {
 | 
					 | 
				
			||||||
        removeSelections(editor)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return false
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private fun removeSelections(editor: VimEditor) {
 | 
					  private fun removeSelections(editor: VimEditor) {
 | 
				
			||||||
@@ -207,17 +114,6 @@ internal class UndoRedoHelper : UndoRedoBase() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private fun runWithBooleanRegistryOption(option: String, value: Boolean, block: () -> Unit) {
 | 
					 | 
				
			||||||
    val registry = Registry.get(option)
 | 
					 | 
				
			||||||
    val oldValue = registry.asBoolean()
 | 
					 | 
				
			||||||
    registry.setValue(value)
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      block()
 | 
					 | 
				
			||||||
    } finally {
 | 
					 | 
				
			||||||
      registry.setValue(oldValue)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private fun VimEditor.runWithChangeTracking(block: ChangeTracker.() -> Unit) {
 | 
					  private fun VimEditor.runWithChangeTracking(block: ChangeTracker.() -> Unit) {
 | 
				
			||||||
    val tracker = ChangeTracker(this)
 | 
					    val tracker = ChangeTracker(this)
 | 
				
			||||||
    tracker.block()
 | 
					    tracker.block()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,13 +21,13 @@ import com.intellij.openapi.util.UserDataHolder
 | 
				
			|||||||
import com.maddyhome.idea.vim.api.CaretRegisterStorageBase
 | 
					import com.maddyhome.idea.vim.api.CaretRegisterStorageBase
 | 
				
			||||||
import com.maddyhome.idea.vim.api.LocalMarkStorage
 | 
					import com.maddyhome.idea.vim.api.LocalMarkStorage
 | 
				
			||||||
import com.maddyhome.idea.vim.api.SelectionInfo
 | 
					import com.maddyhome.idea.vim.api.SelectionInfo
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.Mode
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.VimStateMachine
 | 
				
			||||||
import com.maddyhome.idea.vim.ex.ExOutputModel
 | 
					import com.maddyhome.idea.vim.ex.ExOutputModel
 | 
				
			||||||
import com.maddyhome.idea.vim.group.visual.VisualChange
 | 
					import com.maddyhome.idea.vim.group.visual.VisualChange
 | 
				
			||||||
import com.maddyhome.idea.vim.group.visual.vimLeadSelectionOffset
 | 
					import com.maddyhome.idea.vim.group.visual.vimLeadSelectionOffset
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
import com.maddyhome.idea.vim.state.VimStateMachine
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.ui.ExOutputPanel
 | 
					import com.maddyhome.idea.vim.ui.ExOutputPanel
 | 
				
			||||||
import kotlin.properties.ReadWriteProperty
 | 
					import kotlin.properties.ReadWriteProperty
 | 
				
			||||||
import kotlin.reflect.KProperty
 | 
					import kotlin.reflect.KProperty
 | 
				
			||||||
@@ -99,8 +99,6 @@ internal var Caret.registerStorage: CaretRegisterStorageBase? by userDataCaretTo
 | 
				
			|||||||
internal var Caret.markStorage: LocalMarkStorage? by userDataCaretToEditor()
 | 
					internal var Caret.markStorage: LocalMarkStorage? by userDataCaretToEditor()
 | 
				
			||||||
internal var Caret.lastSelectionInfo: SelectionInfo? by userDataCaretToEditor()
 | 
					internal var Caret.lastSelectionInfo: SelectionInfo? by userDataCaretToEditor()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal var Editor.vimInitialised: Boolean by userDataOr { false }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ------------------ Editor
 | 
					// ------------------ Editor
 | 
				
			||||||
internal fun unInitializeEditor(editor: Editor) {
 | 
					internal fun unInitializeEditor(editor: Editor) {
 | 
				
			||||||
  editor.vimLastSelectionType = null
 | 
					  editor.vimLastSelectionType = null
 | 
				
			||||||
@@ -108,7 +106,6 @@ internal fun unInitializeEditor(editor: Editor) {
 | 
				
			|||||||
  editor.vimMorePanel = null
 | 
					  editor.vimMorePanel = null
 | 
				
			||||||
  editor.vimExOutput = null
 | 
					  editor.vimExOutput = null
 | 
				
			||||||
  editor.vimLastHighlighters = null
 | 
					  editor.vimLastHighlighters = null
 | 
				
			||||||
  editor.vimInitialised = false
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal var Editor.vimLastSearch: String? by userData()
 | 
					internal var Editor.vimLastSearch: String? by userData()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,6 @@ public final class VimIcons {
 | 
				
			|||||||
  public static final @NotNull Icon YOUTRACK = load("/icons/youtrack.svg");
 | 
					  public static final @NotNull Icon YOUTRACK = load("/icons/youtrack.svg");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static @NotNull Icon load(@NotNull @NonNls String path) {
 | 
					  private static @NotNull Icon load(@NotNull @NonNls String path) {
 | 
				
			||||||
    return IconManager.getInstance().getIcon(path, VimIcons.class.getClassLoader());
 | 
					    return IconManager.getInstance().getIcon(path, VimIcons.class);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,19 +9,11 @@
 | 
				
			|||||||
package com.maddyhome.idea.vim.inspections
 | 
					package com.maddyhome.idea.vim.inspections
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.codeInspection.LocalInspectionTool
 | 
					import com.intellij.codeInspection.LocalInspectionTool
 | 
				
			||||||
import com.intellij.codeInspection.LocalQuickFix
 | 
					 | 
				
			||||||
import com.intellij.codeInspection.ProblemDescriptor
 | 
					 | 
				
			||||||
import com.intellij.codeInspection.ProblemsHolder
 | 
					import com.intellij.codeInspection.ProblemsHolder
 | 
				
			||||||
import com.intellij.openapi.project.Project
 | 
					 | 
				
			||||||
import com.intellij.openapi.util.TextRange
 | 
					import com.intellij.openapi.util.TextRange
 | 
				
			||||||
import com.intellij.psi.PsiElement
 | 
					import com.intellij.psi.PsiElement
 | 
				
			||||||
import com.intellij.psi.PsiElementVisitor
 | 
					import com.intellij.psi.PsiElementVisitor
 | 
				
			||||||
import com.intellij.psi.impl.source.tree.LeafPsiElement
 | 
					import com.intellij.psi.impl.source.tree.LeafPsiElement
 | 
				
			||||||
import com.intellij.psi.util.PsiEditorUtil
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.extension.ExtensionBeanClass
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtension
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.commands.SetCommand
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.parser.VimscriptParser
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal class UsePlugSyntaxInspection : LocalInspectionTool() {
 | 
					internal class UsePlugSyntaxInspection : LocalInspectionTool() {
 | 
				
			||||||
  override fun getGroupDisplayName(): String {
 | 
					  override fun getGroupDisplayName(): String {
 | 
				
			||||||
@@ -31,54 +23,11 @@ internal class UsePlugSyntaxInspection : LocalInspectionTool() {
 | 
				
			|||||||
  override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
 | 
					  override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
 | 
				
			||||||
    val file = holder.file
 | 
					    val file = holder.file
 | 
				
			||||||
    if (file.name != ".ideavimrc" && file.name != "_ideavimrc") return PsiElementVisitor.EMPTY_VISITOR
 | 
					    if (file.name != ".ideavimrc" && file.name != "_ideavimrc") return PsiElementVisitor.EMPTY_VISITOR
 | 
				
			||||||
    val plugins = buildPlugins()
 | 
					 | 
				
			||||||
    return object : PsiElementVisitor() {
 | 
					    return object : PsiElementVisitor() {
 | 
				
			||||||
      override fun visitElement(element: PsiElement) {
 | 
					      override fun visitElement(element: PsiElement) {
 | 
				
			||||||
        if (element !is LeafPsiElement) return
 | 
					        if (element !is LeafPsiElement) return
 | 
				
			||||||
        val myScript = VimscriptParser.parse(element.text)
 | 
					        holder.registerProblem(element, TextRange.create(10, 20), "Hi there")
 | 
				
			||||||
        myScript.units.forEach { unit ->
 | 
					 | 
				
			||||||
          if (unit is SetCommand) {
 | 
					 | 
				
			||||||
            val argument = unit.argument
 | 
					 | 
				
			||||||
            val alias = plugins[argument]
 | 
					 | 
				
			||||||
            if (alias != null) {
 | 
					 | 
				
			||||||
              holder.registerProblem(
 | 
					 | 
				
			||||||
                element,
 | 
					 | 
				
			||||||
                unit.rangeInScript.let { TextRange(it.startOffset, it.endOffset - 1) },
 | 
					 | 
				
			||||||
                """
 | 
					 | 
				
			||||||
                  Use `Plug` syntax for defining extensions
 | 
					 | 
				
			||||||
                """.trimIndent(),
 | 
					 | 
				
			||||||
                object : LocalQuickFix {
 | 
					 | 
				
			||||||
                  override fun getFamilyName(): String {
 | 
					 | 
				
			||||||
                    return "Use Plug syntax"
 | 
					 | 
				
			||||||
                  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                  override fun applyFix(p0: Project, p1: ProblemDescriptor) {
 | 
					 | 
				
			||||||
                    val editor = PsiEditorUtil.findEditor(file)
 | 
					 | 
				
			||||||
                    editor?.document?.replaceString(
 | 
					 | 
				
			||||||
                      unit.rangeInScript.startOffset,
 | 
					 | 
				
			||||||
                      unit.rangeInScript.endOffset - 1,
 | 
					 | 
				
			||||||
                      "Plug '$alias'"
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                  }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
              )
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  private fun buildPlugins(): HashMap<String, String> {
 | 
					 | 
				
			||||||
    val res = HashMap<String, String>()
 | 
					 | 
				
			||||||
    VimExtension.EP_NAME.extensions.forEach { extension: ExtensionBeanClass ->
 | 
					 | 
				
			||||||
      val alias = extension.aliases?.first { it.name?.count { it == '/' } == 1 }?.name
 | 
					 | 
				
			||||||
        ?: extension.aliases?.firstOrNull()?.name
 | 
					 | 
				
			||||||
      val name = extension.name
 | 
					 | 
				
			||||||
      if (alias != null && name != null) {
 | 
					 | 
				
			||||||
        res[name] = alias
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return res
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.maddyhome.idea.vim.listener
 | 
					package com.maddyhome.idea.vim.listener
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.application.ApplicationManager
 | 
					import com.intellij.openapi.components.ServiceManager
 | 
				
			||||||
import com.intellij.openapi.editor.Editor
 | 
					import com.intellij.openapi.editor.Editor
 | 
				
			||||||
import org.acejump.session.SessionManager
 | 
					import org.acejump.session.SessionManager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -16,11 +16,12 @@ import org.acejump.session.SessionManager
 | 
				
			|||||||
 * Key handling for IdeaVim should be updated to editorHandler usage. In this case this class can be safely removed.
 | 
					 * Key handling for IdeaVim should be updated to editorHandler usage. In this case this class can be safely removed.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Suppress("DEPRECATION")
 | 
				
			||||||
internal interface AceJumpService {
 | 
					internal interface AceJumpService {
 | 
				
			||||||
  fun isActive(editor: Editor): Boolean
 | 
					  fun isActive(editor: Editor): Boolean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  companion object {
 | 
					  companion object {
 | 
				
			||||||
    fun getInstance(): AceJumpService? = ApplicationManager.getApplication().getService(AceJumpService::class.java)
 | 
					    fun getInstance(): AceJumpService? = ServiceManager.getService(AceJumpService::class.java)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,6 @@ import com.intellij.codeInsight.template.TemplateManagerListener
 | 
				
			|||||||
import com.intellij.codeInsight.template.impl.TemplateState
 | 
					import com.intellij.codeInsight.template.impl.TemplateState
 | 
				
			||||||
import com.intellij.find.FindModelListener
 | 
					import com.intellij.find.FindModelListener
 | 
				
			||||||
import com.intellij.openapi.actionSystem.ActionManager
 | 
					import com.intellij.openapi.actionSystem.ActionManager
 | 
				
			||||||
import com.intellij.openapi.actionSystem.ActionUpdateThread
 | 
					 | 
				
			||||||
import com.intellij.openapi.actionSystem.AnAction
 | 
					import com.intellij.openapi.actionSystem.AnAction
 | 
				
			||||||
import com.intellij.openapi.actionSystem.AnActionEvent
 | 
					import com.intellij.openapi.actionSystem.AnActionEvent
 | 
				
			||||||
import com.intellij.openapi.actionSystem.AnActionResult
 | 
					import com.intellij.openapi.actionSystem.AnActionResult
 | 
				
			||||||
@@ -78,7 +77,7 @@ internal object IdeaSpecifics {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (hostEditor != null && action is ChooseItemAction && injector.registerGroup.isRecording) {
 | 
					      if (hostEditor != null && action is ChooseItemAction && hostEditor.vimStateMachine?.isRecording == true) {
 | 
				
			||||||
        val lookup = LookupManager.getActiveLookup(hostEditor)
 | 
					        val lookup = LookupManager.getActiveLookup(hostEditor)
 | 
				
			||||||
        if (lookup != null) {
 | 
					        if (lookup != null) {
 | 
				
			||||||
          val charsToRemove = hostEditor.caretModel.primaryCaret.offset - lookup.lookupStart
 | 
					          val charsToRemove = hostEditor.caretModel.primaryCaret.offset - lookup.lookupStart
 | 
				
			||||||
@@ -100,7 +99,7 @@ internal object IdeaSpecifics {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      val editor = editor
 | 
					      val editor = editor
 | 
				
			||||||
      if (editor != null) {
 | 
					      if (editor != null) {
 | 
				
			||||||
        if (action is ChooseItemAction && injector.registerGroup.isRecording) {
 | 
					        if (action is ChooseItemAction && editor.vimStateMachine?.isRecording == true) {
 | 
				
			||||||
          val prevDocumentLength = completionPrevDocumentLength
 | 
					          val prevDocumentLength = completionPrevDocumentLength
 | 
				
			||||||
          val prevDocumentOffset = completionPrevDocumentOffset
 | 
					          val prevDocumentOffset = completionPrevDocumentOffset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -127,12 +126,10 @@ internal object IdeaSpecifics {
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
          editor?.let {
 | 
					          val commandState = editor.vim.vimStateMachine
 | 
				
			||||||
            val commandState = it.vim.vimStateMachine
 | 
					          commandState.mode = Mode.NORMAL()
 | 
				
			||||||
            it.vim.mode = Mode.NORMAL()
 | 
					          VimPlugin.getChange().insertBeforeCursor(editor.vim, event.dataContext.vim)
 | 
				
			||||||
            VimPlugin.getChange().insertBeforeCursor(it.vim, event.dataContext.vim)
 | 
					          KeyHandler.getInstance().reset(editor.vim)
 | 
				
			||||||
            KeyHandler.getInstance().reset(it.vim)
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        //endregion
 | 
					        //endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -182,7 +179,7 @@ internal object IdeaSpecifics {
 | 
				
			|||||||
          if (editor.vim.inNormalMode) {
 | 
					          if (editor.vim.inNormalMode) {
 | 
				
			||||||
            VimPlugin.getChange().insertBeforeCursor(
 | 
					            VimPlugin.getChange().insertBeforeCursor(
 | 
				
			||||||
              editor.vim,
 | 
					              editor.vim,
 | 
				
			||||||
              injector.executionContextManager.getEditorExecutionContext(editor.vim),
 | 
					              injector.executionContextManager.onEditor(editor.vim),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            KeyHandler.getInstance().reset(editor.vim)
 | 
					            KeyHandler.getInstance().reset(editor.vim)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
@@ -232,7 +229,5 @@ internal class FindActionIdAction : DumbAwareToggleAction() {
 | 
				
			|||||||
  override fun setSelected(e: AnActionEvent, state: Boolean) {
 | 
					  override fun setSelected(e: AnActionEvent, state: Boolean) {
 | 
				
			||||||
    injector.globalIjOptions().trackactionids = !injector.globalIjOptions().trackactionids
 | 
					    injector.globalIjOptions().trackactionids = !injector.globalIjOptions().trackactionids
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
//endregion
 | 
					//endregion
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.maddyhome.idea.vim.listener
 | 
					package com.maddyhome.idea.vim.listener
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.codeWithMe.ClientId
 | 
					 | 
				
			||||||
import com.intellij.ide.ui.UISettings
 | 
					import com.intellij.ide.ui.UISettings
 | 
				
			||||||
import com.intellij.openapi.Disposable
 | 
					import com.intellij.openapi.Disposable
 | 
				
			||||||
import com.intellij.openapi.application.ApplicationManager
 | 
					import com.intellij.openapi.application.ApplicationManager
 | 
				
			||||||
@@ -21,8 +20,6 @@ import com.intellij.openapi.editor.EditorKind
 | 
				
			|||||||
import com.intellij.openapi.editor.actionSystem.TypedAction
 | 
					import com.intellij.openapi.editor.actionSystem.TypedAction
 | 
				
			||||||
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.event.DocumentEvent
 | 
					 | 
				
			||||||
import com.intellij.openapi.editor.event.DocumentListener
 | 
					 | 
				
			||||||
import com.intellij.openapi.editor.event.EditorFactoryEvent
 | 
					import com.intellij.openapi.editor.event.EditorFactoryEvent
 | 
				
			||||||
import com.intellij.openapi.editor.event.EditorFactoryListener
 | 
					import com.intellij.openapi.editor.event.EditorFactoryListener
 | 
				
			||||||
import com.intellij.openapi.editor.event.EditorMouseEvent
 | 
					import com.intellij.openapi.editor.event.EditorMouseEvent
 | 
				
			||||||
@@ -35,7 +32,6 @@ import com.intellij.openapi.editor.ex.DocumentEx
 | 
				
			|||||||
import com.intellij.openapi.editor.ex.EditorEventMulticasterEx
 | 
					import com.intellij.openapi.editor.ex.EditorEventMulticasterEx
 | 
				
			||||||
import com.intellij.openapi.editor.ex.FocusChangeListener
 | 
					import com.intellij.openapi.editor.ex.FocusChangeListener
 | 
				
			||||||
import com.intellij.openapi.editor.impl.EditorComponentImpl
 | 
					import com.intellij.openapi.editor.impl.EditorComponentImpl
 | 
				
			||||||
import com.intellij.openapi.editor.impl.EditorImpl
 | 
					 | 
				
			||||||
import com.intellij.openapi.fileEditor.FileEditorManager
 | 
					import com.intellij.openapi.fileEditor.FileEditorManager
 | 
				
			||||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
 | 
					import com.intellij.openapi.fileEditor.FileEditorManagerEvent
 | 
				
			||||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener
 | 
					import com.intellij.openapi.fileEditor.FileEditorManagerListener
 | 
				
			||||||
@@ -46,17 +42,13 @@ import com.intellij.openapi.fileEditor.ex.FileEditorWithProvider
 | 
				
			|||||||
import com.intellij.openapi.fileEditor.impl.EditorComposite
 | 
					import com.intellij.openapi.fileEditor.impl.EditorComposite
 | 
				
			||||||
import com.intellij.openapi.fileEditor.impl.EditorWindow
 | 
					import com.intellij.openapi.fileEditor.impl.EditorWindow
 | 
				
			||||||
import com.intellij.openapi.project.ProjectManager
 | 
					import com.intellij.openapi.project.ProjectManager
 | 
				
			||||||
import com.intellij.openapi.rd.createLifetime
 | 
					 | 
				
			||||||
import com.intellij.openapi.rd.createNestedDisposable
 | 
					 | 
				
			||||||
import com.intellij.openapi.util.Disposer
 | 
					import com.intellij.openapi.util.Disposer
 | 
				
			||||||
import com.intellij.openapi.util.Key
 | 
					import com.intellij.openapi.util.Key
 | 
				
			||||||
import com.intellij.openapi.util.removeUserData
 | 
					import com.intellij.openapi.util.removeUserData
 | 
				
			||||||
import com.intellij.openapi.vfs.VirtualFile
 | 
					import com.intellij.openapi.vfs.VirtualFile
 | 
				
			||||||
import com.intellij.util.ExceptionUtil
 | 
					import com.intellij.util.ExceptionUtil
 | 
				
			||||||
import com.jetbrains.rd.util.lifetime.Lifetime
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.EventFacade
 | 
					import com.maddyhome.idea.vim.EventFacade
 | 
				
			||||||
import com.maddyhome.idea.vim.KeyHandler
 | 
					import com.maddyhome.idea.vim.KeyHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.KeyHandlerStateResetter
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.VimKeyListener
 | 
					import com.maddyhome.idea.vim.VimKeyListener
 | 
				
			||||||
import com.maddyhome.idea.vim.VimPlugin
 | 
					import com.maddyhome.idea.vim.VimPlugin
 | 
				
			||||||
import com.maddyhome.idea.vim.VimTypedActionHandler
 | 
					import com.maddyhome.idea.vim.VimTypedActionHandler
 | 
				
			||||||
@@ -70,17 +62,17 @@ import com.maddyhome.idea.vim.api.injector
 | 
				
			|||||||
import com.maddyhome.idea.vim.ex.ExOutputModel
 | 
					import com.maddyhome.idea.vim.ex.ExOutputModel
 | 
				
			||||||
import com.maddyhome.idea.vim.group.EditorGroup
 | 
					import com.maddyhome.idea.vim.group.EditorGroup
 | 
				
			||||||
import com.maddyhome.idea.vim.group.FileGroup
 | 
					import com.maddyhome.idea.vim.group.FileGroup
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.group.IjOptions
 | 
				
			||||||
import com.maddyhome.idea.vim.group.MotionGroup
 | 
					import com.maddyhome.idea.vim.group.MotionGroup
 | 
				
			||||||
import com.maddyhome.idea.vim.group.OptionGroup
 | 
					import com.maddyhome.idea.vim.group.OptionGroup
 | 
				
			||||||
import com.maddyhome.idea.vim.group.ScrollGroup
 | 
					import com.maddyhome.idea.vim.group.ScrollGroup
 | 
				
			||||||
import com.maddyhome.idea.vim.group.SearchGroup
 | 
					import com.maddyhome.idea.vim.group.SearchGroup
 | 
				
			||||||
import com.maddyhome.idea.vim.group.VimMarkServiceImpl
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl
 | 
					import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl
 | 
				
			||||||
import com.maddyhome.idea.vim.group.visual.VimVisualTimer
 | 
					import com.maddyhome.idea.vim.group.visual.VimVisualTimer
 | 
				
			||||||
import com.maddyhome.idea.vim.group.visual.moveCaretOneCharLeftFromSelectionEnd
 | 
					import com.maddyhome.idea.vim.group.visual.moveCaretOneCharLeftFromSelectionEnd
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.correctorRequester
 | 
					import com.maddyhome.idea.vim.handler.correctorRequester
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.keyCheckRequests
 | 
					import com.maddyhome.idea.vim.handler.keyCheckRequests
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.CaretVisualAttributesListener
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.helper.GuicursorChangeListener
 | 
					import com.maddyhome.idea.vim.helper.GuicursorChangeListener
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.StrictMode
 | 
					import com.maddyhome.idea.vim.helper.StrictMode
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.exitSelectMode
 | 
					import com.maddyhome.idea.vim.helper.exitSelectMode
 | 
				
			||||||
@@ -88,23 +80,27 @@ import com.maddyhome.idea.vim.helper.exitVisualMode
 | 
				
			|||||||
import com.maddyhome.idea.vim.helper.forceBarCursor
 | 
					import com.maddyhome.idea.vim.helper.forceBarCursor
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.inVisualMode
 | 
					import com.maddyhome.idea.vim.helper.inVisualMode
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.isEndAllowed
 | 
					import com.maddyhome.idea.vim.helper.isEndAllowed
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.localEditors
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
 | 
					import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.resetVimLastColumn
 | 
					import com.maddyhome.idea.vim.helper.resetVimLastColumn
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
 | 
					import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.vimDisabled
 | 
					import com.maddyhome.idea.vim.helper.vimDisabled
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.listener.MouseEventsDataHolder.skipEvents
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.listener.MouseEventsDataHolder.skipNDragEvents
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.listener.VimListenerManager.EditorListeners.add
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.ij
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
 | 
					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.state.mode.inSelectMode
 | 
					import com.maddyhome.idea.vim.state.mode.inSelectMode
 | 
				
			||||||
 | 
					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.ui.ShowCmdOptionChangeListener
 | 
					import com.maddyhome.idea.vim.ui.ShowCmdOptionChangeListener
 | 
				
			||||||
import com.maddyhome.idea.vim.ui.ShowCmdWidgetUpdater
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
 | 
					import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
 | 
				
			||||||
import com.maddyhome.idea.vim.ui.widgets.macro.MacroWidgetListener
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.ui.widgets.macro.macroWidgetOptionListener
 | 
					import com.maddyhome.idea.vim.ui.widgets.macro.macroWidgetOptionListener
 | 
				
			||||||
import com.maddyhome.idea.vim.ui.widgets.mode.listeners.ModeWidgetListener
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.ui.widgets.mode.modeWidgetOptionListener
 | 
					import com.maddyhome.idea.vim.ui.widgets.mode.modeWidgetOptionListener
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.vimDisposable
 | 
				
			||||||
import java.awt.event.MouseAdapter
 | 
					import java.awt.event.MouseAdapter
 | 
				
			||||||
import java.awt.event.MouseEvent
 | 
					import java.awt.event.MouseEvent
 | 
				
			||||||
import javax.swing.SwingUtilities
 | 
					import javax.swing.SwingUtilities
 | 
				
			||||||
@@ -134,7 +130,6 @@ private fun getOpeningEditor(newEditor: Editor) = newEditor.project?.let { proje
 | 
				
			|||||||
internal object VimListenerManager {
 | 
					internal object VimListenerManager {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private val logger = Logger.getInstance(VimListenerManager::class.java)
 | 
					  private val logger = Logger.getInstance(VimListenerManager::class.java)
 | 
				
			||||||
  private val editorListenersDisposableKey = Key.create<Disposable>("IdeaVim listeners disposable")
 | 
					 | 
				
			||||||
  private var firstEditorInitialised = false
 | 
					  private var firstEditorInitialised = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fun turnOn() {
 | 
					  fun turnOn() {
 | 
				
			||||||
@@ -142,30 +137,11 @@ internal object VimListenerManager {
 | 
				
			|||||||
    EditorListeners.addAll()
 | 
					    EditorListeners.addAll()
 | 
				
			||||||
    check(correctorRequester.tryEmit(Unit))
 | 
					    check(correctorRequester.tryEmit(Unit))
 | 
				
			||||||
    check(keyCheckRequests.tryEmit(Unit))
 | 
					    check(keyCheckRequests.tryEmit(Unit))
 | 
				
			||||||
 | 
					 | 
				
			||||||
    val caretVisualAttributesListener = CaretVisualAttributesListener()
 | 
					 | 
				
			||||||
    injector.listenersNotifier.modeChangeListeners.add(caretVisualAttributesListener)
 | 
					 | 
				
			||||||
    injector.listenersNotifier.isReplaceCharListeners.add(caretVisualAttributesListener)
 | 
					 | 
				
			||||||
    caretVisualAttributesListener.updateAllEditorsCaretsVisual()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val modeWidgetListener = ModeWidgetListener()
 | 
					 | 
				
			||||||
    injector.listenersNotifier.modeChangeListeners.add(modeWidgetListener)
 | 
					 | 
				
			||||||
    injector.listenersNotifier.myEditorListeners.add(modeWidgetListener)
 | 
					 | 
				
			||||||
    injector.listenersNotifier.vimPluginListeners.add(modeWidgetListener)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val macroWidgetListener = MacroWidgetListener()
 | 
					 | 
				
			||||||
    injector.listenersNotifier.macroRecordingListeners.add(macroWidgetListener)
 | 
					 | 
				
			||||||
    injector.listenersNotifier.vimPluginListeners.add(macroWidgetListener)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    injector.listenersNotifier.myEditorListeners.add(KeyHandlerStateResetter())
 | 
					 | 
				
			||||||
    injector.listenersNotifier.myEditorListeners.add(ShowCmdWidgetUpdater())
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fun turnOff() {
 | 
					  fun turnOff() {
 | 
				
			||||||
    GlobalListeners.disable()
 | 
					    GlobalListeners.disable()
 | 
				
			||||||
    EditorListeners.removeAll()
 | 
					    EditorListeners.removeAll()
 | 
				
			||||||
    injector.listenersNotifier.reset()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    check(correctorRequester.tryEmit(Unit))
 | 
					    check(correctorRequester.tryEmit(Unit))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -183,29 +159,23 @@ internal object VimListenerManager {
 | 
				
			|||||||
      optionGroup.addEffectiveOptionValueChangeListener(Options.number, EditorGroup.NumberChangeListener.INSTANCE)
 | 
					      optionGroup.addEffectiveOptionValueChangeListener(Options.number, EditorGroup.NumberChangeListener.INSTANCE)
 | 
				
			||||||
      optionGroup.addEffectiveOptionValueChangeListener(Options.relativenumber, EditorGroup.NumberChangeListener.INSTANCE)
 | 
					      optionGroup.addEffectiveOptionValueChangeListener(Options.relativenumber, EditorGroup.NumberChangeListener.INSTANCE)
 | 
				
			||||||
      optionGroup.addEffectiveOptionValueChangeListener(Options.scrolloff, ScrollGroup.ScrollOptionsChangeListener)
 | 
					      optionGroup.addEffectiveOptionValueChangeListener(Options.scrolloff, ScrollGroup.ScrollOptionsChangeListener)
 | 
				
			||||||
      optionGroup.addEffectiveOptionValueChangeListener(Options.guicursor, GuicursorChangeListener)
 | 
					 | 
				
			||||||
      optionGroup.addGlobalOptionChangeListener(Options.showcmd, ShowCmdOptionChangeListener)
 | 
					      optionGroup.addGlobalOptionChangeListener(Options.showcmd, ShowCmdOptionChangeListener)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // This code is executed after ideavimrc execution, so we trigger onGlobalOptionChanged just in case
 | 
					      // This code is executed after ideavimrc execution, so we trigger onGlobalOptionChanged just in case
 | 
				
			||||||
      optionGroup.addGlobalOptionChangeListener(Options.showmode, modeWidgetOptionListener)
 | 
					      optionGroup.addGlobalOptionChangeListener(IjOptions.showmodewidget, modeWidgetOptionListener)
 | 
				
			||||||
      optionGroup.addGlobalOptionChangeListener(Options.showmode, macroWidgetOptionListener)
 | 
					      optionGroup.addGlobalOptionChangeListener(IjOptions.showmodewidget, macroWidgetOptionListener)
 | 
				
			||||||
      modeWidgetOptionListener.onGlobalOptionChanged()
 | 
					      modeWidgetOptionListener.onGlobalOptionChanged()
 | 
				
			||||||
      macroWidgetOptionListener.onGlobalOptionChanged()
 | 
					      macroWidgetOptionListener.onGlobalOptionChanged()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Listen for and initialise new editors
 | 
					      optionGroup.addEffectiveOptionValueChangeListener(Options.guicursor, GuicursorChangeListener)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      EventFacade.getInstance().addEditorFactoryListener(VimEditorFactoryListener, VimPlugin.getInstance().onOffDisposable)
 | 
					      EventFacade.getInstance().addEditorFactoryListener(VimEditorFactoryListener, VimPlugin.getInstance().onOffDisposable)
 | 
				
			||||||
      val busConnection = ApplicationManager.getApplication().messageBus.connect(VimPlugin.getInstance().onOffDisposable)
 | 
					      val busConnection = ApplicationManager.getApplication().messageBus.connect(VimPlugin.getInstance().onOffDisposable)
 | 
				
			||||||
      busConnection.subscribe(FileOpenedSyncListener.TOPIC, VimEditorFactoryListener)
 | 
					      busConnection.subscribe(FileOpenedSyncListener.TOPIC, VimEditorFactoryListener)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Listen for focus change to update various features such as mode widget
 | 
					      EditorFactory.getInstance().eventMulticaster.addCaretListener(VimCaretListener, VimPlugin.getInstance().onOffDisposable)
 | 
				
			||||||
      val eventMulticaster = EditorFactory.getInstance().eventMulticaster
 | 
					      val eventMulticaster = EditorFactory.getInstance().eventMulticaster as? EditorEventMulticasterEx
 | 
				
			||||||
      (eventMulticaster as? EditorEventMulticasterEx)?.addFocusChangeListener(
 | 
					      eventMulticaster?.addFocusChangeListener(VimFocusListener, VimPlugin.getInstance().onOffDisposable)
 | 
				
			||||||
        VimFocusListener,
 | 
					 | 
				
			||||||
        VimPlugin.getInstance().onOffDisposable
 | 
					 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // Listen for document changes to update document state such as marks
 | 
					 | 
				
			||||||
      eventMulticaster.addDocumentListener(VimDocumentListener, VimPlugin.getInstance().onOffDisposable)
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun disable() {
 | 
					    fun disable() {
 | 
				
			||||||
@@ -216,8 +186,8 @@ internal object VimListenerManager {
 | 
				
			|||||||
      optionGroup.removeEffectiveOptionValueChangeListener(Options.relativenumber, EditorGroup.NumberChangeListener.INSTANCE)
 | 
					      optionGroup.removeEffectiveOptionValueChangeListener(Options.relativenumber, EditorGroup.NumberChangeListener.INSTANCE)
 | 
				
			||||||
      optionGroup.removeEffectiveOptionValueChangeListener(Options.scrolloff, ScrollGroup.ScrollOptionsChangeListener)
 | 
					      optionGroup.removeEffectiveOptionValueChangeListener(Options.scrolloff, ScrollGroup.ScrollOptionsChangeListener)
 | 
				
			||||||
      optionGroup.removeGlobalOptionChangeListener(Options.showcmd, ShowCmdOptionChangeListener)
 | 
					      optionGroup.removeGlobalOptionChangeListener(Options.showcmd, ShowCmdOptionChangeListener)
 | 
				
			||||||
      optionGroup.removeGlobalOptionChangeListener(Options.showmode, modeWidgetOptionListener)
 | 
					      optionGroup.removeGlobalOptionChangeListener(IjOptions.showmodewidget, modeWidgetOptionListener)
 | 
				
			||||||
      optionGroup.removeGlobalOptionChangeListener(Options.showmode, macroWidgetOptionListener)
 | 
					      optionGroup.removeGlobalOptionChangeListener(IjOptions.showmodewidget, macroWidgetOptionListener)
 | 
				
			||||||
      optionGroup.removeEffectiveOptionValueChangeListener(Options.guicursor, GuicursorChangeListener)
 | 
					      optionGroup.removeEffectiveOptionValueChangeListener(Options.guicursor, GuicursorChangeListener)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -246,9 +216,7 @@ internal object VimListenerManager {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      // We could have a split window in this list, but since they're all being initialised from the same opening editor
 | 
					      // We could have a split window in this list, but since they're all being initialised from the same opening editor
 | 
				
			||||||
      // there's no need to use the SPLIT scenario
 | 
					      // there's no need to use the SPLIT scenario
 | 
				
			||||||
      // Make sure we get all editors, including uninitialised
 | 
					      localEditors().forEach { editor ->
 | 
				
			||||||
      injector.editorGroup.getEditorsRaw().forEach { vimEditor ->
 | 
					 | 
				
			||||||
        val editor = vimEditor.ij
 | 
					 | 
				
			||||||
        if (!initialisedEditors.contains(editor)) {
 | 
					        if (!initialisedEditors.contains(editor)) {
 | 
				
			||||||
          add(editor, getOpeningEditor(editor)?.vim ?: injector.fallbackWindow, LocalOptionInitialisationScenario.NEW)
 | 
					          add(editor, getOpeningEditor(editor)?.vim ?: injector.fallbackWindow, LocalOptionInitialisationScenario.NEW)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -256,24 +224,18 @@ internal object VimListenerManager {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun removeAll() {
 | 
					    fun removeAll() {
 | 
				
			||||||
      injector.editorGroup.getEditors().forEach { editor ->
 | 
					      localEditors().forEach { editor ->
 | 
				
			||||||
        remove(editor.ij)
 | 
					        remove(editor, false)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun add(editor: Editor, openingEditor: VimEditor, scenario: LocalOptionInitialisationScenario) {
 | 
					    fun add(editor: Editor, openingEditor: VimEditor, scenario: LocalOptionInitialisationScenario) {
 | 
				
			||||||
      // We shouldn't be called with anything other than local editors, but let's just be sure. This will prevent any
 | 
					      // As I understand, there is no need to pass a disposable that also disposes on editor close
 | 
				
			||||||
      // unsupported editor from incorrectly being initialised.
 | 
					      //   because all editor resources will be garbage collected anyway on editor close
 | 
				
			||||||
      // TODO: If the user changes the 'ideavimsupport' option, existing editors won't be initialised
 | 
					      val disposable = editor.project?.vimDisposable ?: return
 | 
				
			||||||
      if (vimDisabled(editor)) return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      val pluginLifetime = VimPlugin.getInstance().createLifetime()
 | 
					 | 
				
			||||||
      val editorLifetime = (editor as EditorImpl).disposable.createLifetime()
 | 
					 | 
				
			||||||
      val disposable =
 | 
					 | 
				
			||||||
        Lifetime.intersect(pluginLifetime, editorLifetime).createNestedDisposable("MyLifetimedDisposable")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      val listenersDisposable = Disposer.newDisposable(disposable)
 | 
					      val listenersDisposable = Disposer.newDisposable(disposable)
 | 
				
			||||||
      editor.putUserData(editorListenersDisposableKey, listenersDisposable)
 | 
					      editor.putUserData(editorListenersDisposable, listenersDisposable)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      Disposer.register(listenersDisposable) {
 | 
					      Disposer.register(listenersDisposable) {
 | 
				
			||||||
        if (VimListenerTestObject.enabled) {
 | 
					        if (VimListenerTestObject.enabled) {
 | 
				
			||||||
@@ -296,75 +258,60 @@ internal object VimListenerManager {
 | 
				
			|||||||
      eventFacade.addCaretListener(editor, EditorCaretHandler, listenersDisposable)
 | 
					      eventFacade.addCaretListener(editor, EditorCaretHandler, listenersDisposable)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      VimPlugin.getEditor().editorCreated(editor)
 | 
					      VimPlugin.getEditor().editorCreated(editor)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      VimPlugin.getChange().editorCreated(editor, listenersDisposable)
 | 
					      VimPlugin.getChange().editorCreated(editor, listenersDisposable)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      injector.listenersNotifier.notifyEditorCreated(vimEditor)
 | 
					      injector.listenersNotifier.notifyEditorCreated(vimEditor)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      Disposer.register(listenersDisposable) {
 | 
					      Disposer.register(listenersDisposable) {
 | 
				
			||||||
        VimPlugin.getEditor().editorDeinit(editor, true)
 | 
					        VimPlugin.getEditorIfCreated()?.editorDeinit(editor, true)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun remove(editor: Editor) {
 | 
					    fun remove(editor: Editor, isReleased: Boolean) {
 | 
				
			||||||
      val editorDisposable = editor.removeUserData(editorListenersDisposableKey)
 | 
					      val editorDisposable = editor.getUserData(editorListenersDisposable)
 | 
				
			||||||
      if (editorDisposable != null) {
 | 
					      if (editorDisposable != null) {
 | 
				
			||||||
        Disposer.dispose(editorDisposable)
 | 
					        Disposer.dispose(editorDisposable)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      else {
 | 
					      else StrictMode.fail("Editor doesn't have disposable attached. $editor")
 | 
				
			||||||
        // We definitely do not expect this to happen
 | 
					
 | 
				
			||||||
        StrictMode.fail("Editor doesn't have disposable attached. $editor")
 | 
					      VimPlugin.getEditorIfCreated()?.editorDeinit(editor, isReleased)
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Notifies other IdeaVim components of focus gain/loss, e.g. the mode widget. This will be called with non-local Code
 | 
					 | 
				
			||||||
   * With Me editors.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  private object VimFocusListener : FocusChangeListener {
 | 
					  private object VimFocusListener : FocusChangeListener {
 | 
				
			||||||
    override fun focusGained(editor: Editor) {
 | 
					    override fun focusGained(editor: Editor) {
 | 
				
			||||||
      if (vimDisabled(editor)) return
 | 
					 | 
				
			||||||
      injector.listenersNotifier.notifyEditorFocusGained(editor.vim)
 | 
					      injector.listenersNotifier.notifyEditorFocusGained(editor.vim)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun focusLost(editor: Editor) {
 | 
					    override fun focusLost(editor: Editor) {
 | 
				
			||||||
      if (vimDisabled(editor)) return
 | 
					 | 
				
			||||||
      injector.listenersNotifier.notifyEditorFocusLost(editor.vim)
 | 
					      injector.listenersNotifier.notifyEditorFocusLost(editor.vim)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  val editorListenersDisposable = Key.create<Disposable>("IdeaVim listeners disposable")
 | 
				
			||||||
   * Notifies other IdeaVim components of document changes. This will be called for all documents, even those only
 | 
					
 | 
				
			||||||
   * open in non-local Code With Me guest editors, which we still want to process (e.g. to update marks when a guest
 | 
					  object VimCaretListener : CaretListener {
 | 
				
			||||||
   * edits a file. Updating search highlights will be a no-op if there are no open local editors)
 | 
					    override fun caretAdded(event: CaretEvent) {
 | 
				
			||||||
   */
 | 
					      if (vimDisabled(event.editor)) return
 | 
				
			||||||
  private object VimDocumentListener : DocumentListener {
 | 
					      event.editor.updateCaretsVisualAttributes()
 | 
				
			||||||
    override fun beforeDocumentChange(event: DocumentEvent) {
 | 
					 | 
				
			||||||
      VimMarkServiceImpl.MarkUpdater.beforeDocumentChange(event)
 | 
					 | 
				
			||||||
      SearchGroup.DocumentSearchListener.INSTANCE.beforeDocumentChange(event)
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun documentChanged(event: DocumentEvent) {
 | 
					    override fun caretRemoved(event: CaretEvent) {
 | 
				
			||||||
      VimMarkServiceImpl.MarkUpdater.documentChanged(event)
 | 
					      if (vimDisabled(event.editor)) return
 | 
				
			||||||
      SearchGroup.DocumentSearchListener.INSTANCE.documentChanged(event)
 | 
					      event.editor.updateCaretsVisualAttributes()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Called when the selected file editor changes. In other words, when the user selects a new tab. Used to remember the
 | 
					 | 
				
			||||||
   * last selected file, update search highlights in the new tab, etc. This will be called with non-local Code With Me
 | 
					 | 
				
			||||||
   * guest editors.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  class VimFileEditorManagerListener : FileEditorManagerListener {
 | 
					  class VimFileEditorManagerListener : FileEditorManagerListener {
 | 
				
			||||||
    override fun selectionChanged(event: FileEditorManagerEvent) {
 | 
					    override fun selectionChanged(event: FileEditorManagerEvent) {
 | 
				
			||||||
      // We can't rely on being passed a non-null editor, so check for Code With Me scenarios explicitly
 | 
					      if (VimPlugin.isNotEnabled()) return
 | 
				
			||||||
      if (VimPlugin.isNotEnabled() || !ClientId.isCurrentlyUnderLocalId) return
 | 
					 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      val newEditor = event.newEditor
 | 
					      val newEditor = event.newEditor
 | 
				
			||||||
      if (newEditor is TextEditor) {
 | 
					      if (newEditor is TextEditor) {
 | 
				
			||||||
        val editor = newEditor.editor
 | 
					        val editor = newEditor.editor
 | 
				
			||||||
        if (editor.isInsertMode) {
 | 
					        if (editor.isInsertMode) {
 | 
				
			||||||
          editor.vim.mode = Mode.NORMAL()
 | 
					          VimStateMachine.getInstance(editor).mode = Mode.NORMAL()
 | 
				
			||||||
          KeyHandler.getInstance().reset(editor.vim)
 | 
					          KeyHandler.getInstance().reset(editor.vim)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -376,10 +323,6 @@ internal object VimListenerManager {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Listen to editor creation events in order to initialise IdeaVim compatible editors. This listener is called for all
 | 
					 | 
				
			||||||
   * editors, including non-local hidden Code With Me editors.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  private object VimEditorFactoryListener : EditorFactoryListener, FileOpenedSyncListener {
 | 
					  private object VimEditorFactoryListener : EditorFactoryListener, FileOpenedSyncListener {
 | 
				
			||||||
    private data class OpeningEditor(
 | 
					    private data class OpeningEditor(
 | 
				
			||||||
      val editor: Editor,
 | 
					      val editor: Editor,
 | 
				
			||||||
@@ -391,8 +334,6 @@ internal object VimListenerManager {
 | 
				
			|||||||
    private val openingEditorKey: Key<OpeningEditor> = Key("IdeaVim::OpeningEditor")
 | 
					    private val openingEditorKey: Key<OpeningEditor> = Key("IdeaVim::OpeningEditor")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun editorCreated(event: EditorFactoryEvent) {
 | 
					    override fun editorCreated(event: EditorFactoryEvent) {
 | 
				
			||||||
      if (vimDisabled(event.editor)) return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // This callback is called when an editor is created, but we cannot completely rely on it to initialise options.
 | 
					      // This callback is called when an editor is created, but we cannot completely rely on it to initialise options.
 | 
				
			||||||
      // We can find the currently selected editor, which we can use as the opening editor, and we're given the new
 | 
					      // We can find the currently selected editor, which we can use as the opening editor, and we're given the new
 | 
				
			||||||
      // editor, but we don't know enough about it - this function is called before the new editor is added to an
 | 
					      // editor, but we don't know enough about it - this function is called before the new editor is added to an
 | 
				
			||||||
@@ -413,7 +354,7 @@ internal object VimListenerManager {
 | 
				
			|||||||
          openingEditor == null -> LocalOptionInitialisationScenario.EDIT
 | 
					          openingEditor == null -> LocalOptionInitialisationScenario.EDIT
 | 
				
			||||||
          else -> LocalOptionInitialisationScenario.NEW
 | 
					          else -> LocalOptionInitialisationScenario.NEW
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        EditorListeners.add(event.editor, openingEditor?.vim ?: injector.fallbackWindow, scenario)
 | 
					        add(event.editor, openingEditor?.vim ?: injector.fallbackWindow, scenario)
 | 
				
			||||||
        firstEditorInitialised = true
 | 
					        firstEditorInitialised = true
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      else {
 | 
					      else {
 | 
				
			||||||
@@ -442,7 +383,6 @@ internal object VimListenerManager {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun editorReleased(event: EditorFactoryEvent) {
 | 
					    override fun editorReleased(event: EditorFactoryEvent) {
 | 
				
			||||||
      if (vimDisabled(event.editor)) return
 | 
					 | 
				
			||||||
      val vimEditor = event.editor.vim
 | 
					      val vimEditor = event.editor.vim
 | 
				
			||||||
      injector.listenersNotifier.notifyEditorReleased(vimEditor)
 | 
					      injector.listenersNotifier.notifyEditorReleased(vimEditor)
 | 
				
			||||||
      injector.markService.editorReleased(vimEditor)
 | 
					      injector.markService.editorReleased(vimEditor)
 | 
				
			||||||
@@ -466,8 +406,6 @@ internal object VimListenerManager {
 | 
				
			|||||||
      // editor is modified
 | 
					      // editor is modified
 | 
				
			||||||
      editorsWithProviders.forEach {
 | 
					      editorsWithProviders.forEach {
 | 
				
			||||||
        (it.fileEditor as? TextEditor)?.editor?.let { editor ->
 | 
					        (it.fileEditor as? TextEditor)?.editor?.let { editor ->
 | 
				
			||||||
          if (vimDisabled(editor)) return@let
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          val openingEditor = editor.removeUserData(openingEditorKey)
 | 
					          val openingEditor = editor.removeUserData(openingEditorKey)
 | 
				
			||||||
          val owningEditorWindow = getOwningEditorWindow(editor)
 | 
					          val owningEditorWindow = getOwningEditorWindow(editor)
 | 
				
			||||||
          val isInSameSplit = owningEditorWindow == openingEditor?.owningEditorWindow
 | 
					          val isInSameSplit = owningEditorWindow == openingEditor?.owningEditorWindow
 | 
				
			||||||
@@ -488,7 +426,7 @@ internal object VimListenerManager {
 | 
				
			|||||||
            (openingEditor.canBeReused || openingEditor.isPreview) && isInSameSplit && openingEditorIsClosed -> LocalOptionInitialisationScenario.EDIT
 | 
					            (openingEditor.canBeReused || openingEditor.isPreview) && isInSameSplit && openingEditorIsClosed -> LocalOptionInitialisationScenario.EDIT
 | 
				
			||||||
            else -> LocalOptionInitialisationScenario.NEW
 | 
					            else -> LocalOptionInitialisationScenario.NEW
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          EditorListeners.add(editor, openingEditor?.editor?.vim ?: injector.fallbackWindow, scenario)
 | 
					          add(editor, openingEditor?.editor?.vim ?: injector.fallbackWindow, scenario)
 | 
				
			||||||
          firstEditorInitialised = true
 | 
					          firstEditorInitialised = true
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -503,16 +441,14 @@ internal object VimListenerManager {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Callback for when an editor's text selection changes. Only registered for editors that we're interested in (so only
 | 
					 | 
				
			||||||
   * local editors). Fixes incorrect mouse selection at end of line, and synchronises selections across other editors.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  private object EditorSelectionHandler : SelectionListener {
 | 
					  private object EditorSelectionHandler : SelectionListener {
 | 
				
			||||||
 | 
					    private var myMakingChanges = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * This event is executed for each caret using [com.intellij.openapi.editor.CaretModel.runForEachCaret]
 | 
					     * This event is executed for each caret using [com.intellij.openapi.editor.CaretModel.runForEachCaret]
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    override fun selectionChanged(selectionEvent: SelectionEvent) {
 | 
					    override fun selectionChanged(selectionEvent: SelectionEvent) {
 | 
				
			||||||
 | 
					      if (selectionEvent.editor.isIdeaVimDisabledHere) return
 | 
				
			||||||
      VimVisualTimer.drop()
 | 
					      VimVisualTimer.drop()
 | 
				
			||||||
      val editor = selectionEvent.editor
 | 
					      val editor = selectionEvent.editor
 | 
				
			||||||
      val document = editor.document
 | 
					      val document = editor.document
 | 
				
			||||||
@@ -532,22 +468,20 @@ internal object VimListenerManager {
 | 
				
			|||||||
      val startOffset = selectionEvent.newRange.startOffset
 | 
					      val startOffset = selectionEvent.newRange.startOffset
 | 
				
			||||||
      val endOffset = selectionEvent.newRange.endOffset
 | 
					      val endOffset = selectionEvent.newRange.endOffset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // TODO: It is very confusing that this logic is split between EditorSelectionHandler and EditorMouseHandler
 | 
					      if (skipNDragEvents < skipEvents && lineStart != lineEnd && startOffset == caretOffset) {
 | 
				
			||||||
      if (MouseEventsDataHolder.dragEventCount < MouseEventsDataHolder.allowedSkippedDragEvents
 | 
					 | 
				
			||||||
        && lineStart != lineEnd && startOffset == caretOffset) {
 | 
					 | 
				
			||||||
        if (lineEnd == endOffset - 1) {
 | 
					        if (lineEnd == endOffset - 1) {
 | 
				
			||||||
          // When starting on an empty line and dragging vertically upwards onto
 | 
					          // When starting on an empty line and dragging vertically upwards onto
 | 
				
			||||||
          // another line, the selection should include the entirety of the empty line
 | 
					          // another line, the selection should include the entirety of the empty line
 | 
				
			||||||
          caret.setSelection(
 | 
					          caret.setSelection(
 | 
				
			||||||
            ijVimEditor.coerceOffset(endOffset + 1),
 | 
					            ijVimEditor.coerceOffset(endOffset + 1).point,
 | 
				
			||||||
            ijVimEditor.coerceOffset(startOffset),
 | 
					            ijVimEditor.coerceOffset(startOffset).point,
 | 
				
			||||||
          )
 | 
					          )
 | 
				
			||||||
        } else if (lineEnd == startOffset + 1 && startOffset == endOffset) {
 | 
					        } else if (lineEnd == startOffset + 1 && startOffset == endOffset) {
 | 
				
			||||||
          // When dragging left from EOL on a non-empty line, the selection
 | 
					          // When dragging left from EOL on a non-empty line, the selection
 | 
				
			||||||
          // should include the last character on the line
 | 
					          // should include the last character on the line
 | 
				
			||||||
          caret.setSelection(
 | 
					          caret.setSelection(
 | 
				
			||||||
            ijVimEditor.coerceOffset(lineEnd),
 | 
					            ijVimEditor.coerceOffset(lineEnd).point,
 | 
				
			||||||
            ijVimEditor.coerceOffset(lineEnd - 1),
 | 
					            ijVimEditor.coerceOffset(lineEnd - 1).point,
 | 
				
			||||||
          )
 | 
					          )
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -558,30 +492,32 @@ internal object VimListenerManager {
 | 
				
			|||||||
        IdeaSelectionControl.controlNonVimSelectionChange(editor)
 | 
					        IdeaSelectionControl.controlNonVimSelectionChange(editor)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (document is DocumentEx && document.isInEventsHandling) {
 | 
					      if (myMakingChanges || document is DocumentEx && document.isInEventsHandling) {
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      myMakingChanges = true
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        // Synchronize selections between editors
 | 
				
			||||||
 | 
					        val newRange = selectionEvent.newRange
 | 
				
			||||||
 | 
					        for (e in localEditors(document)) {
 | 
				
			||||||
 | 
					          if (e != editor) {
 | 
				
			||||||
 | 
					            e.selectionModel.vimSetSystemSelectionSilently(newRange.startOffset, newRange.endOffset)
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } finally {
 | 
				
			||||||
 | 
					        myMakingChanges = false
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Listener for mouse events registered with editors that we are interested (so only local editors). Responsible for:
 | 
					 | 
				
			||||||
   * * Hiding ex entry and output panels when clicking inside editor area (but not when right-clicking)
 | 
					 | 
				
			||||||
   * * Removing secondary carets on mouse click (such as visual block selection)
 | 
					 | 
				
			||||||
   * * Exiting visual or select mode on mouse click
 | 
					 | 
				
			||||||
   * * Resets the dragEventCount on mouse press + release
 | 
					 | 
				
			||||||
   * * Fix up Vim selected mode on mouse release, after dragging
 | 
					 | 
				
			||||||
   * * Force bar cursor while dragging, which looks better because IntelliJ selects a character once selection has got
 | 
					 | 
				
			||||||
   *   over halfway through the char, while Vim selects when it enters the character bounding box
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @see ComponentMouseListener
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  // TODO: Can we merge this with ComponentMouseListener to fully handle all mouse actions in one place?
 | 
					 | 
				
			||||||
  private object EditorMouseHandler : EditorMouseListener, EditorMouseMotionListener {
 | 
					  private object EditorMouseHandler : EditorMouseListener, EditorMouseMotionListener {
 | 
				
			||||||
    private var mouseDragging = false
 | 
					    private var mouseDragging = false
 | 
				
			||||||
    private var cutOffFixed = false
 | 
					    private var cutOffFixed = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun mouseDragged(e: EditorMouseEvent) {
 | 
					    override fun mouseDragged(e: EditorMouseEvent) {
 | 
				
			||||||
 | 
					      if (e.editor.isIdeaVimDisabledHere) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      val caret = e.editor.caretModel.primaryCaret
 | 
					      val caret = e.editor.caretModel.primaryCaret
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      clearFirstSelectionEvents(e)
 | 
					      clearFirstSelectionEvents(e)
 | 
				
			||||||
@@ -633,7 +569,7 @@ internal object VimListenerManager {
 | 
				
			|||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      MouseEventsDataHolder.dragEventCount -= 1
 | 
					      skipNDragEvents -= 1
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -648,7 +584,7 @@ internal object VimListenerManager {
 | 
				
			|||||||
     * (Both with mouse and with v$. IdeaVim treats v$ as an exclusive selection)
 | 
					     * (Both with mouse and with v$. IdeaVim treats v$ as an exclusive selection)
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private fun clearFirstSelectionEvents(e: EditorMouseEvent) {
 | 
					    private fun clearFirstSelectionEvents(e: EditorMouseEvent) {
 | 
				
			||||||
      if (MouseEventsDataHolder.dragEventCount > 0) {
 | 
					      if (skipNDragEvents > 0) {
 | 
				
			||||||
        logger.debug("Mouse dragging")
 | 
					        logger.debug("Mouse dragging")
 | 
				
			||||||
        VimVisualTimer.swingTimer?.stop()
 | 
					        VimVisualTimer.swingTimer?.stop()
 | 
				
			||||||
        if (!mouseDragging) {
 | 
					        if (!mouseDragging) {
 | 
				
			||||||
@@ -674,7 +610,9 @@ internal object VimListenerManager {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun mousePressed(event: EditorMouseEvent) {
 | 
					    override fun mousePressed(event: EditorMouseEvent) {
 | 
				
			||||||
      MouseEventsDataHolder.dragEventCount = MouseEventsDataHolder.allowedSkippedDragEvents
 | 
					      if (event.editor.isIdeaVimDisabledHere) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      skipNDragEvents = skipEvents
 | 
				
			||||||
      SelectionVimListenerSuppressor.reset()
 | 
					      SelectionVimListenerSuppressor.reset()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -685,10 +623,12 @@ internal object VimListenerManager {
 | 
				
			|||||||
     * - Click-hold and switch editor (ctrl-tab)
 | 
					     * - Click-hold and switch editor (ctrl-tab)
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    override fun mouseReleased(event: EditorMouseEvent) {
 | 
					    override fun mouseReleased(event: EditorMouseEvent) {
 | 
				
			||||||
 | 
					      if (event.editor.isIdeaVimDisabledHere) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      SelectionVimListenerSuppressor.unlock()
 | 
					      SelectionVimListenerSuppressor.unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      clearFirstSelectionEvents(event)
 | 
					      clearFirstSelectionEvents(event)
 | 
				
			||||||
      MouseEventsDataHolder.dragEventCount = MouseEventsDataHolder.allowedSkippedDragEvents
 | 
					      skipNDragEvents = skipEvents
 | 
				
			||||||
      if (mouseDragging) {
 | 
					      if (mouseDragging) {
 | 
				
			||||||
        logger.debug("Release mouse after dragging")
 | 
					        logger.debug("Release mouse after dragging")
 | 
				
			||||||
        val editor = event.editor
 | 
					        val editor = event.editor
 | 
				
			||||||
@@ -708,6 +648,7 @@ internal object VimListenerManager {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun mouseClicked(event: EditorMouseEvent) {
 | 
					    override fun mouseClicked(event: EditorMouseEvent) {
 | 
				
			||||||
 | 
					      if (event.editor.isIdeaVimDisabledHere) return
 | 
				
			||||||
      logger.debug("Mouse clicked")
 | 
					      logger.debug("Mouse clicked")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (event.area == EditorMouseEventArea.EDITING_AREA) {
 | 
					      if (event.area == EditorMouseEventArea.EDITING_AREA) {
 | 
				
			||||||
@@ -753,21 +694,13 @@ internal object VimListenerManager {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * A mouse listener registered to the editor component for editors that we are interested in (so only local editors).
 | 
					 | 
				
			||||||
   * Fixes some issues with mouse selection, namely:
 | 
					 | 
				
			||||||
   * * Clicking at the end of the line will place the caret on the last character rather than after it
 | 
					 | 
				
			||||||
   * * Double-clicking a word will place the caret on the last character rather than after it
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @see EditorMouseHandler
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  // TODO: Can we merge this with ComponentMouseListener to fully handle all mouse actions in one place?
 | 
					 | 
				
			||||||
  private object ComponentMouseListener : MouseAdapter() {
 | 
					  private object ComponentMouseListener : MouseAdapter() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var cutOffEnd = false
 | 
					    var cutOffEnd = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun mousePressed(e: MouseEvent?) {
 | 
					    override fun mousePressed(e: MouseEvent?) {
 | 
				
			||||||
      val editor = (e?.component as? EditorComponentImpl)?.editor ?: return
 | 
					      val editor = (e?.component as? EditorComponentImpl)?.editor ?: return
 | 
				
			||||||
 | 
					      if (editor.isIdeaVimDisabledHere) return
 | 
				
			||||||
      val predictedMode = IdeaSelectionControl.predictMode(editor, SelectionSource.MOUSE)
 | 
					      val predictedMode = IdeaSelectionControl.predictMode(editor, SelectionSource.MOUSE)
 | 
				
			||||||
      when (e.clickCount) {
 | 
					      when (e.clickCount) {
 | 
				
			||||||
        1 -> {
 | 
					        1 -> {
 | 
				
			||||||
@@ -804,22 +737,10 @@ internal object VimListenerManager {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Caret listener registered only for editors that we're interested in. Used to update caret shapes when carets are
 | 
					 | 
				
			||||||
   * added/removed. Also tracks the expected last column location of the caret.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  private object EditorCaretHandler : CaretListener {
 | 
					  private object EditorCaretHandler : CaretListener {
 | 
				
			||||||
    override fun caretPositionChanged(event: CaretEvent) {
 | 
					    override fun caretPositionChanged(event: CaretEvent) {
 | 
				
			||||||
      event.caret?.resetVimLastColumn()
 | 
					      event.caret?.resetVimLastColumn()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun caretAdded(event: CaretEvent) {
 | 
					 | 
				
			||||||
      event.editor.updateCaretsVisualAttributes()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun caretRemoved(event: CaretEvent) {
 | 
					 | 
				
			||||||
      event.editor.updateCaretsVisualAttributes()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  enum class SelectionSource {
 | 
					  enum class SelectionSource {
 | 
				
			||||||
@@ -834,6 +755,6 @@ internal object VimListenerTestObject {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private object MouseEventsDataHolder {
 | 
					private object MouseEventsDataHolder {
 | 
				
			||||||
  const val allowedSkippedDragEvents = 3
 | 
					  const val skipEvents = 3
 | 
				
			||||||
  var dragEventCount = allowedSkippedDragEvents
 | 
					  var skipNDragEvents = skipEvents
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,8 +12,16 @@ import com.intellij.openapi.actionSystem.DataContext
 | 
				
			|||||||
import com.intellij.openapi.util.Key
 | 
					import com.intellij.openapi.util.Key
 | 
				
			||||||
import com.intellij.openapi.util.UserDataHolder
 | 
					import com.intellij.openapi.util.UserDataHolder
 | 
				
			||||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
					import com.maddyhome.idea.vim.api.ExecutionContext
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.VimEditor
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.injector
 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal open class IjEditorExecutionContext(override val context: DataContext) : ExecutionContext
 | 
					internal open class IjEditorExecutionContext(override val context: DataContext) : ExecutionContext.Editor {
 | 
				
			||||||
 | 
					  override fun updateEditor(editor: VimEditor): ExecutionContext {
 | 
				
			||||||
 | 
					    return IjEditorExecutionContext(injector.executionContextManager.onEditor(editor, context.vim).ij)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					internal class IjCaretAndEditorExecutionContext(override val context: DataContext) : IjEditorExecutionContext(context), ExecutionContext.CaretAndEditor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This key is stored in data context when the action is started from vim
 | 
					// This key is stored in data context when the action is started from vim
 | 
				
			||||||
internal val runFromVimKey = Key.create<Boolean>("RunFromVim")
 | 
					internal val runFromVimKey = Key.create<Boolean>("RunFromVim")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,15 +9,23 @@
 | 
				
			|||||||
package com.maddyhome.idea.vim.newapi
 | 
					package com.maddyhome.idea.vim.newapi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.components.Service
 | 
					import com.intellij.openapi.components.Service
 | 
				
			||||||
import com.intellij.openapi.editor.ex.util.EditorUtil
 | 
					import com.intellij.openapi.editor.actionSystem.CaretSpecificDataContext
 | 
				
			||||||
import com.maddyhome.idea.vim.api.ExecutionContext
 | 
					import com.maddyhome.idea.vim.api.ExecutionContext
 | 
				
			||||||
import com.maddyhome.idea.vim.api.ExecutionContextManagerBase
 | 
					import com.maddyhome.idea.vim.api.ExecutionContextManagerBase
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.VimCaret
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
					import com.maddyhome.idea.vim.api.VimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.EditorDataContext
 | 
					import com.maddyhome.idea.vim.helper.EditorDataContext
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Service
 | 
					@Service
 | 
				
			||||||
internal class IjExecutionContextManager : ExecutionContextManagerBase() {
 | 
					internal class IjExecutionContextManager : ExecutionContextManagerBase() {
 | 
				
			||||||
  override fun getEditorExecutionContext(editor: VimEditor): ExecutionContext {
 | 
					  override fun onEditor(editor: VimEditor, prevContext: ExecutionContext?): ExecutionContext.Editor {
 | 
				
			||||||
    return EditorUtil.getEditorDataContext(editor.ij).vim
 | 
					    if (prevContext is ExecutionContext.CaretAndEditor) {
 | 
				
			||||||
 | 
					      return prevContext
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return IjEditorExecutionContext(EditorDataContext.init((editor as IjVimEditor).editor, prevContext?.ij))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun onCaret(caret: VimCaret, prevContext: ExecutionContext.Editor): ExecutionContext.CaretAndEditor {
 | 
				
			||||||
 | 
					    return IjCaretAndEditorExecutionContext(CaretSpecificDataContext.create(prevContext.ij, caret.ij))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,10 +10,12 @@ package com.maddyhome.idea.vim.newapi
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.editor.RangeMarker
 | 
					import com.intellij.openapi.editor.RangeMarker
 | 
				
			||||||
import com.maddyhome.idea.vim.common.LiveRange
 | 
					import com.maddyhome.idea.vim.common.LiveRange
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.common.Offset
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.common.offset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal class IjLiveRange(val marker: RangeMarker) : LiveRange {
 | 
					internal class IjLiveRange(val marker: RangeMarker) : LiveRange {
 | 
				
			||||||
  override val startOffset: Int
 | 
					  override val startOffset: Offset
 | 
				
			||||||
    get() = marker.startOffset
 | 
					    get() = marker.startOffset.offset
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public val RangeMarker.vim: LiveRange
 | 
					public val RangeMarker.vim: LiveRange
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,8 +34,4 @@ internal class IjNativeActionManager : NativeActionManager {
 | 
				
			|||||||
public val AnAction.vim: IjNativeAction
 | 
					public val AnAction.vim: IjNativeAction
 | 
				
			||||||
  get() = IjNativeAction(this)
 | 
					  get() = IjNativeAction(this)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class IjNativeAction(override val action: AnAction) : NativeAction {
 | 
					public class IjNativeAction(override val action: AnAction) : NativeAction
 | 
				
			||||||
  override fun toString(): String {
 | 
					 | 
				
			||||||
    return "IjNativeAction(action=$action)"
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,10 +43,6 @@ internal class IjVimApplication : VimApplicationBase() {
 | 
				
			|||||||
    return ApplicationManager.getApplication().isUnitTestMode
 | 
					    return ApplicationManager.getApplication().isUnitTestMode
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun isInternal(): Boolean {
 | 
					 | 
				
			||||||
    return ApplicationManager.getApplication().isInternal
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  override fun postKey(stroke: KeyStroke, editor: VimEditor) {
 | 
					  override fun postKey(stroke: KeyStroke, editor: VimEditor) {
 | 
				
			||||||
    val component: Component = SwingUtilities.getAncestorOfClass(Window::class.java, editor.ij.component)
 | 
					    val component: Component = SwingUtilities.getAncestorOfClass(Window::class.java, editor.ij.component)
 | 
				
			||||||
    val event = createKeyEvent(stroke, component)
 | 
					    val event = createKeyEvent(stroke, component)
 | 
				
			||||||
@@ -58,6 +54,10 @@ internal class IjVimApplication : VimApplicationBase() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun localEditors(): List<VimEditor> {
 | 
				
			||||||
 | 
					    return com.maddyhome.idea.vim.helper.localEditors().map { IjVimEditor(it) }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun runWriteCommand(editor: VimEditor, name: String?, groupId: Any?, command: Runnable) {
 | 
					  override fun runWriteCommand(editor: VimEditor, name: String?, groupId: Any?, command: Runnable) {
 | 
				
			||||||
    RunnableHelper.runWriteCommand((editor as IjVimEditor).editor.project, command, name, groupId)
 | 
					    RunnableHelper.runWriteCommand((editor as IjVimEditor).editor.project, command, name, groupId)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -82,12 +82,6 @@ internal class IjVimApplication : VimApplicationBase() {
 | 
				
			|||||||
    com.maddyhome.idea.vim.helper.runAfterGotFocus(runnable)
 | 
					    com.maddyhome.idea.vim.helper.runAfterGotFocus(runnable)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun isOctopusEnabled(): Boolean {
 | 
					 | 
				
			||||||
    val property = System.getProperty("octopus.handler") ?: "true"
 | 
					 | 
				
			||||||
    if (property.isBlank()) return true
 | 
					 | 
				
			||||||
    return property.toBoolean()
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private fun createKeyEvent(stroke: KeyStroke, component: Component): KeyEvent {
 | 
					  private fun createKeyEvent(stroke: KeyStroke, component: Component): KeyEvent {
 | 
				
			||||||
    return KeyEvent(
 | 
					    return KeyEvent(
 | 
				
			||||||
      component,
 | 
					      component,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ package com.maddyhome.idea.vim.newapi
 | 
				
			|||||||
import com.intellij.openapi.editor.Caret
 | 
					import com.intellij.openapi.editor.Caret
 | 
				
			||||||
import com.intellij.openapi.editor.LogicalPosition
 | 
					import com.intellij.openapi.editor.LogicalPosition
 | 
				
			||||||
import com.intellij.openapi.editor.VisualPosition
 | 
					import com.intellij.openapi.editor.VisualPosition
 | 
				
			||||||
 | 
					import com.intellij.openapi.util.Disposer
 | 
				
			||||||
import com.maddyhome.idea.vim.api.BufferPosition
 | 
					import com.maddyhome.idea.vim.api.BufferPosition
 | 
				
			||||||
import com.maddyhome.idea.vim.api.CaretRegisterStorage
 | 
					import com.maddyhome.idea.vim.api.CaretRegisterStorage
 | 
				
			||||||
import com.maddyhome.idea.vim.api.CaretRegisterStorageBase
 | 
					import com.maddyhome.idea.vim.api.CaretRegisterStorageBase
 | 
				
			||||||
@@ -21,7 +22,10 @@ import com.maddyhome.idea.vim.api.VimCaret
 | 
				
			|||||||
import com.maddyhome.idea.vim.api.VimCaretBase
 | 
					import com.maddyhome.idea.vim.api.VimCaretBase
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimEditor
 | 
					import com.maddyhome.idea.vim.api.VimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimVisualPosition
 | 
					import com.maddyhome.idea.vim.api.VimVisualPosition
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.common.EditorLine
 | 
				
			||||||
import com.maddyhome.idea.vim.common.LiveRange
 | 
					import com.maddyhome.idea.vim.common.LiveRange
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.common.Offset
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.common.offset
 | 
				
			||||||
import com.maddyhome.idea.vim.group.visual.VisualChange
 | 
					import com.maddyhome.idea.vim.group.visual.VisualChange
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.lastSelectionInfo
 | 
					import com.maddyhome.idea.vim.helper.lastSelectionInfo
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.markStorage
 | 
					import com.maddyhome.idea.vim.helper.markStorage
 | 
				
			||||||
@@ -38,6 +42,14 @@ import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
internal class IjVimCaret(val caret: Caret) : VimCaretBase() {
 | 
					internal class IjVimCaret(val caret: Caret) : VimCaretBase() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  init {
 | 
				
			||||||
 | 
					    if (caret.isValid) {
 | 
				
			||||||
 | 
					      Disposer.register(caret) {
 | 
				
			||||||
 | 
					        (registerStorage as CaretRegisterStorageBase).clearListener()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override val registerStorage: CaretRegisterStorage
 | 
					  override val registerStorage: CaretRegisterStorage
 | 
				
			||||||
    get() {
 | 
					    get() {
 | 
				
			||||||
      var storage = this.caret.registerStorage
 | 
					      var storage = this.caret.registerStorage
 | 
				
			||||||
@@ -75,8 +87,8 @@ internal class IjVimCaret(val caret: Caret) : VimCaretBase() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  override val editor: VimEditor
 | 
					  override val editor: VimEditor
 | 
				
			||||||
    get() = IjVimEditor(caret.editor)
 | 
					    get() = IjVimEditor(caret.editor)
 | 
				
			||||||
  override val offset: Int
 | 
					  override val offset: Offset
 | 
				
			||||||
    get() = caret.offset
 | 
					    get() = caret.offset.offset
 | 
				
			||||||
  override var vimLastColumn: Int
 | 
					  override var vimLastColumn: Int
 | 
				
			||||||
    get() = caret.vimLastColumn
 | 
					    get() = caret.vimLastColumn
 | 
				
			||||||
    set(value) {
 | 
					    set(value) {
 | 
				
			||||||
@@ -115,8 +127,8 @@ internal class IjVimCaret(val caret: Caret) : VimCaretBase() {
 | 
				
			|||||||
    this.caret.moveToLogicalPosition(LogicalPosition(position.line, position.column, position.leansForward))
 | 
					    this.caret.moveToLogicalPosition(LogicalPosition(position.line, position.column, position.leansForward))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun getLine(): Int {
 | 
					  override fun getLine(): EditorLine.Pointer {
 | 
				
			||||||
    return caret.logicalPosition.line
 | 
					    return EditorLine.Pointer.init(caret.logicalPosition.line, editor)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun hasSelection(): Boolean {
 | 
					  override fun hasSelection(): Boolean {
 | 
				
			||||||
@@ -161,8 +173,8 @@ internal class IjVimCaret(val caret: Caret) : VimCaretBase() {
 | 
				
			|||||||
    return this
 | 
					    return this
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun setSelection(start: Int, end: Int) {
 | 
					  override fun setSelection(start: Offset, end: Offset) {
 | 
				
			||||||
    caret.setSelection(start, end)
 | 
					    caret.setSelection(start.point, end.point)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun removeSelection() {
 | 
					  override fun removeSelection() {
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user