mirror of
				https://github.com/chylex/IntelliJ-IdeaVim.git
				synced 2025-11-04 01:40:12 +01:00 
			
		
		
		
	Compare commits
	
		
			18 Commits
		
	
	
		
			063ed0aa84
			...
			customized
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						dbf26be1f0
	
				 | 
					
					
						|||
| 
						
						
							
						
						500756c86f
	
				 | 
					
					
						|||
| 
						
						
							
						
						5967979113
	
				 | 
					
					
						|||
| 
						
						
							
						
						358a13c22c
	
				 | 
					
					
						|||
| 
						
						
							
						
						64229d327a
	
				 | 
					
					
						|||
| 
						
						
							
						
						62d38eb4df
	
				 | 
					
					
						|||
| 
						
						
							
						
						4e15b65cec
	
				 | 
					
					
						|||
| 
						
						
							
						
						e4d31379f0
	
				 | 
					
					
						|||
| 
						
						
							
						
						195910b3a5
	
				 | 
					
					
						|||
| 
						
						
							
						
						10d476340f
	
				 | 
					
					
						|||
| 
						
						
							
						
						2c4ccc77b9
	
				 | 
					
					
						|||
| 
						
						
							
						
						4b0c2bce18
	
				 | 
					
					
						|||
| 
						
						
							
						
						a2d5adbc6c
	
				 | 
					
					
						|||
| 
						
						
							
						
						3b5b4ed84c
	
				 | 
					
					
						|||
| 
						
						
							
						
						a33addb6ea
	
				 | 
					
					
						|||
| 
						
						
							
						
						626b6ee0af
	
				 | 
					
					
						|||
| 
						
						
							
						
						f353f47987
	
				 | 
					
					
						|||
| 
						
						
							
						
						5410187bd5
	
				 | 
					
					
						
							
								
								
									
										6
									
								
								.github/workflows/runUiOctopusTests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/runUiOctopusTests.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
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								.github/workflows/runUiPyTests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.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
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								.github/workflows/runUiTests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.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
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										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 }}
 | 
				
			||||||
							
								
								
									
										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)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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.23-1.0.19")
 | 
				
			||||||
  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")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,11 +51,11 @@ buildscript {
 | 
				
			|||||||
    classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.9.0.202403050737-r")
 | 
					    classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.9.0.202403050737-r")
 | 
				
			||||||
    classpath("org.kohsuke:github-api:1.305")
 | 
					    classpath("org.kohsuke:github-api:1.305")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    classpath("io.ktor:ktor-client-core:2.3.10")
 | 
					    classpath("io.ktor:ktor-client-core:2.3.9")
 | 
				
			||||||
    classpath("io.ktor:ktor-client-cio:2.3.10")
 | 
					    classpath("io.ktor:ktor-client-cio:2.3.9")
 | 
				
			||||||
    classpath("io.ktor:ktor-client-auth:2.3.10")
 | 
					    classpath("io.ktor:ktor-client-auth:2.3.9")
 | 
				
			||||||
    classpath("io.ktor:ktor-client-content-negotiation:2.3.10")
 | 
					    classpath("io.ktor:ktor-client-content-negotiation:2.3.9")
 | 
				
			||||||
    classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.10")
 | 
					    classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.9")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 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 +69,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.2"
 | 
				
			||||||
  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,7 +141,7 @@ 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.2")
 | 
				
			||||||
  testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
 | 
					  testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
 | 
				
			||||||
@@ -264,6 +264,9 @@ tasks {
 | 
				
			|||||||
  runPluginVerifier {
 | 
					  runPluginVerifier {
 | 
				
			||||||
    downloadDir.set("${project.buildDir}/pluginVerifier/ides")
 | 
					    downloadDir.set("${project.buildDir}/pluginVerifier/ides")
 | 
				
			||||||
    teamCityOutputFormat.set(true)
 | 
					    teamCityOutputFormat.set(true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // The latest version of the plugin verifier is broken, so temporally use the stable version
 | 
				
			||||||
 | 
					    verifierVersion = "1.307"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  generateGrammarSource {
 | 
					  generateGrammarSource {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,12 +9,12 @@
 | 
				
			|||||||
# suppress inspection "UnusedProperty" for whole file
 | 
					# suppress inspection "UnusedProperty" for whole file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ideaVersion=LATEST-EAP-SNAPSHOT
 | 
					#ideaVersion=LATEST-EAP-SNAPSHOT
 | 
				
			||||||
ideaVersion=2024.1
 | 
					ideaVersion=2023.3.3
 | 
				
			||||||
# 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-30
 | 
				
			||||||
javaVersion=17
 | 
					javaVersion=17
 | 
				
			||||||
remoteRobotVersion=0.11.22
 | 
					remoteRobotVersion=0.11.22
 | 
				
			||||||
antlrVersion=4.10.1
 | 
					antlrVersion=4.10.1
 | 
				
			||||||
@@ -42,3 +42,4 @@ kotlin.stdlib.default.dependency=false
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Disable incremental annotation processing
 | 
					# Disable incremental annotation processing
 | 
				
			||||||
ksp.incremental=false
 | 
					ksp.incremental=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,11 +22,11 @@ repositories {
 | 
				
			|||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
  compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.23")
 | 
					  compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.23")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  implementation("io.ktor:ktor-client-core:2.3.10")
 | 
					  implementation("io.ktor:ktor-client-core:2.3.9")
 | 
				
			||||||
  implementation("io.ktor:ktor-client-cio:2.3.10")
 | 
					  implementation("io.ktor:ktor-client-cio:2.3.9")
 | 
				
			||||||
  implementation("io.ktor:ktor-client-content-negotiation:2.3.10")
 | 
					  implementation("io.ktor:ktor-client-content-negotiation:2.3.9")
 | 
				
			||||||
  implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.10")
 | 
					  implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.9")
 | 
				
			||||||
  implementation("io.ktor:ktor-client-auth:2.3.10")
 | 
					  implementation("io.ktor:ktor-client-auth:2.3.9")
 | 
				
			||||||
  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
 | 
				
			||||||
@@ -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")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,13 +49,17 @@ suspend fun main() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  val output = response.body<List<String>>().toSet()
 | 
					  val output = response.body<List<String>>().toSet()
 | 
				
			||||||
  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()
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										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()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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), handler.keyHandlerState)
 | 
				
			||||||
      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")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -79,7 +79,12 @@ public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible
 | 
				
			|||||||
      try {
 | 
					      try {
 | 
				
			||||||
        val start = if (traceTime) System.currentTimeMillis() else null
 | 
					        val start = if (traceTime) System.currentTimeMillis() else null
 | 
				
			||||||
        val keyHandler = KeyHandler.getInstance()
 | 
					        val keyHandler = KeyHandler.getInstance()
 | 
				
			||||||
        keyHandler.handleKey(editor.vim, keyStroke, e.dataContext.vim, keyHandler.keyHandlerState)
 | 
					        keyHandler.handleKey(
 | 
				
			||||||
 | 
					          editor.vim,
 | 
				
			||||||
 | 
					          keyStroke,
 | 
				
			||||||
 | 
					          injector.executionContextManager.onEditor(editor.vim, e.dataContext.vim),
 | 
				
			||||||
 | 
					          keyHandler.keyHandlerState,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        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 +381,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>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
@@ -143,7 +142,7 @@ 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()
 | 
					    val keyHandler = KeyHandler.getInstance()
 | 
				
			||||||
    keys.forEach { keyHandler.handleKey(editor.vim, it, context, false, false, keyHandler.keyHandlerState) }
 | 
					    keys.forEach { keyHandler.handleKey(editor.vim, it, context, false, false, keyHandler.keyHandlerState) }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -182,8 +181,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()'. */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -130,6 +132,7 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    synchronized(Util.monitor) {
 | 
					    synchronized(Util.monitor) {
 | 
				
			||||||
      Util.commandsRegistered = true
 | 
					      Util.commandsRegistered = true
 | 
				
			||||||
 | 
					      ProjectManager.getInstance().openProjects.forEach { project -> installDispatcher(project) }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -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(Util.monitor) {
 | 
				
			||||||
 | 
					        if (!Util.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
 | 
				
			||||||
@@ -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) }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
@@ -331,8 +374,8 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
    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
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@@ -489,21 +533,6 @@ internal class NerdTree : VimExtension {
 | 
				
			|||||||
  companion object {
 | 
					  companion object {
 | 
				
			||||||
    const val pluginName = "NERDTree"
 | 
					    const val pluginName = "NERDTree"
 | 
				
			||||||
    private val LOG = logger<NerdTree>()
 | 
					    private val LOG = logger<NerdTree>()
 | 
				
			||||||
    private val KEY = Key.create<NerdDispatcher>("IdeaVim-NerdTree-Dispatcher")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun installDispatcher(component: JComponent) {
 | 
					 | 
				
			||||||
      if (component.getUserData(KEY) != null) return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      val dispatcher = NerdDispatcher()
 | 
					 | 
				
			||||||
      component.putUserData(KEY, dispatcher)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      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
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -538,6 +567,12 @@ private fun collectShortcuts(node: Node<NerdAction>): Set<KeyStroke> {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun getTree(e: AnActionEvent): JTree? {
 | 
					private fun installDispatcher(project: Project) {
 | 
				
			||||||
  return e.dataContext.getData(PlatformCoreDataKeys.CONTEXT_COMPONENT) as? JTree
 | 
					  val dispatcher = NerdTree.NerdDispatcher.getInstance(project)
 | 
				
			||||||
 | 
					  val shortcuts =
 | 
				
			||||||
 | 
					    collectShortcuts(actionsRoot).map { RequiredShortcut(it, MappingOwner.Plugin.get(NerdTree.pluginName)) }
 | 
				
			||||||
 | 
					  dispatcher.registerCustomShortcutSet(
 | 
				
			||||||
 | 
					    KeyGroup.toShortcutSet(shortcuts),
 | 
				
			||||||
 | 
					    (ProjectView.getInstance(project) as ProjectViewImpl).component,
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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() {}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -8,7 +8,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
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
 | 
				
			||||||
@@ -65,7 +64,7 @@ internal class ReplaceWithRegister : VimExtension {
 | 
				
			|||||||
        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()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -93,7 +92,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 ->
 | 
				
			||||||
@@ -121,7 +120,7 @@ internal class ReplaceWithRegister : VimExtension {
 | 
				
			|||||||
        selectionType ?: SelectionType.CHARACTER_WISE,
 | 
					        selectionType ?: SelectionType.CHARACTER_WISE,
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
      // todo multicaret
 | 
					      // todo multicaret
 | 
				
			||||||
      doReplace(ijEditor, context.ij, editor.primaryCaret(), visualSelection)
 | 
					      doReplace(ijEditor, editor.primaryCaret(), visualSelection)
 | 
				
			||||||
      return true
 | 
					      return true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -141,7 +140,7 @@ internal class ReplaceWithRegister : VimExtension {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
 | 
					private fun doReplace(editor: Editor, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
 | 
				
			||||||
  val registerGroup = injector.registerGroup
 | 
					  val registerGroup = injector.registerGroup
 | 
				
			||||||
  val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
 | 
					  val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
 | 
				
			||||||
  val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return
 | 
					  val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return
 | 
				
			||||||
@@ -169,7 +168,7 @@ private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimC
 | 
				
			|||||||
  ClipboardOptionHelper.IdeaputDisabler().use {
 | 
					  ClipboardOptionHelper.IdeaputDisabler().use {
 | 
				
			||||||
    VimPlugin.getPut().putText(
 | 
					    VimPlugin.getPut().putText(
 | 
				
			||||||
      vimEditor,
 | 
					      vimEditor,
 | 
				
			||||||
      context.vim,
 | 
					      injector.executionContextManager.onEditor(editor.vim),
 | 
				
			||||||
      putData,
 | 
					      putData,
 | 
				
			||||||
      operatorArguments = OperatorArguments(
 | 
					      operatorArguments = OperatorArguments(
 | 
				
			||||||
        editor.vimStateMachine?.isOperatorPending(vimEditor.mode) ?: false,
 | 
					        editor.vimStateMachine?.isOperatorPending(vimEditor.mode) ?: false,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -118,7 +118,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) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
@@ -35,7 +34,6 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMa
 | 
				
			|||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
 | 
					import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
 | 
				
			||||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
 | 
					import com.maddyhome.idea.vim.extension.exportOperatorFunction
 | 
				
			||||||
import com.maddyhome.idea.vim.group.findBlockRange
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.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 +44,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
 | 
				
			||||||
@@ -101,7 +100,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 +150,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 +227,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 +269,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
 | 
				
			||||||
@@ -348,8 +347,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 +361,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)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -197,7 +197,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 +235,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 +395,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 +451,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 +485,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)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -235,7 +235,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));
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,12 +22,9 @@ 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;
 | 
				
			||||||
@@ -47,7 +44,6 @@ 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;
 | 
				
			||||||
@@ -449,28 +445,4 @@ public class FileGroup extends VimFileBase {
 | 
				
			|||||||
      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();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -11,24 +11,41 @@ import com.intellij.openapi.actionSystem.DataContext
 | 
				
			|||||||
import com.intellij.openapi.components.Service
 | 
					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 +54,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,13 +67,17 @@ 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -66,6 +90,24 @@ internal class MotionGroup : VimMotionGroupBase() {
 | 
				
			|||||||
    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 +130,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 +219,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 +232,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 +279,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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -538,24 +538,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 +808,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 +837,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 +989,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 +1023,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 +1081,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);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -117,7 +117,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")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -337,7 +337,7 @@ 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()
 | 
					    val keyHandler = KeyHandler.getInstance()
 | 
				
			||||||
    keyHandler.handleKey(editor.vim, enterKey, context, keyHandler.keyHandlerState)
 | 
					    keyHandler.handleKey(editor.vim, enterKey, context, keyHandler.keyHandlerState)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -90,8 +90,6 @@ private fun Editor.updatePrimaryCaretVisualAttributes() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // 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
 | 
					  // NOTE: At the moment, this causes project leak in tests
 | 
				
			||||||
  // IJPL-928 - this will be fixed in 2024.2
 | 
					 | 
				
			||||||
  // [VERSION UPDATE] 2024.2 - remove if wrapping
 | 
					 | 
				
			||||||
  if (!ApplicationManager.getApplication().isUnitTestMode) {
 | 
					  if (!ApplicationManager.getApplication().isUnitTestMode) {
 | 
				
			||||||
    (this as? EditorEx)?.setCaretVisible(true)
 | 
					    (this as? EditorEx)?.setCaretVisible(true)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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() -> {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -211,12 +211,15 @@ public class EditorHelper {
 | 
				
			|||||||
    return injector.getEditorGroup().getEditors(new IjVimDocument(doc)).stream().findFirst().orElse(null);
 | 
					    return injector.getEditorGroup().getEditors(new IjVimDocument(doc)).stream().findFirst().orElse(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);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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,12 @@ 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
 | 
				
			||||||
 | 
					    ijAction.beforeActionPerformedUpdate(event)
 | 
				
			||||||
    // This is a hack to fix the tests and fix VIM-3332
 | 
					    if (!event.presentation.isEnabled) return false
 | 
				
			||||||
    // 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 +215,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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -632,6 +632,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,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
@@ -182,7 +181,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 +231,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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,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,14 +45,11 @@ 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.KeyHandlerStateResetter
 | 
				
			||||||
@@ -105,6 +101,7 @@ 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.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
 | 
				
			||||||
@@ -267,10 +264,12 @@ internal object VimListenerManager {
 | 
				
			|||||||
      // TODO: If the user changes the 'ideavimsupport' option, existing editors won't be initialised
 | 
					      // TODO: If the user changes the 'ideavimsupport' option, existing editors won't be initialised
 | 
				
			||||||
      if (vimDisabled(editor)) return
 | 
					      if (vimDisabled(editor)) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      val pluginLifetime = VimPlugin.getInstance().createLifetime()
 | 
					      // As I understand, there is no need to pass a disposable that also disposes on editor close
 | 
				
			||||||
      val editorLifetime = (editor as EditorImpl).disposable.createLifetime()
 | 
					      //   because all editor resources will be garbage collected anyway on editor close
 | 
				
			||||||
      val disposable =
 | 
					      // Note that this uses the plugin's main disposable, rather than VimPlugin.onOffDisposable, because we don't need
 | 
				
			||||||
        Lifetime.intersect(pluginLifetime, editorLifetime).createNestedDisposable("MyLifetimedDisposable")
 | 
					      // to - we explicitly call VimListenerManager.removeAll from VimPlugin.turnOffPlugin, and this disposes each
 | 
				
			||||||
 | 
					      // editor's disposable individually.
 | 
				
			||||||
 | 
					      val disposable = editor.project?.vimDisposable ?: return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      val listenersDisposable = Disposer.newDisposable(disposable)
 | 
					      val listenersDisposable = Disposer.newDisposable(disposable)
 | 
				
			||||||
      editor.putUserData(editorListenersDisposableKey, listenersDisposable)
 | 
					      editor.putUserData(editorListenersDisposableKey, listenersDisposable)
 | 
				
			||||||
@@ -539,15 +538,15 @@ internal object VimListenerManager {
 | 
				
			|||||||
          // 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,
 | 
				
			||||||
          )
 | 
					          )
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)"
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,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
 | 
				
			||||||
@@ -75,8 +78,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 +118,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 +164,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() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ import com.intellij.openapi.editor.event.DocumentListener
 | 
				
			|||||||
import com.maddyhome.idea.vim.api.VimDocument
 | 
					import com.maddyhome.idea.vim.api.VimDocument
 | 
				
			||||||
import com.maddyhome.idea.vim.common.ChangesListener
 | 
					import com.maddyhome.idea.vim.common.ChangesListener
 | 
				
			||||||
import com.maddyhome.idea.vim.common.LiveRange
 | 
					import com.maddyhome.idea.vim.common.LiveRange
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.common.Offset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal class IjVimDocument(val document: Document) : VimDocument {
 | 
					internal class IjVimDocument(val document: Document) : VimDocument {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -40,7 +41,7 @@ internal class IjVimDocument(val document: Document) : VimDocument {
 | 
				
			|||||||
    document.removeDocumentListener(nativeListener)
 | 
					    document.removeDocumentListener(nativeListener)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun getOffsetGuard(offset: Int): LiveRange? {
 | 
					  override fun getOffsetGuard(offset: Offset): LiveRange? {
 | 
				
			||||||
    return document.getOffsetGuard(offset)?.vim
 | 
					    return document.getOffsetGuard(offset.point)?.vim
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,11 +35,13 @@ import com.maddyhome.idea.vim.api.VimScrollingModel
 | 
				
			|||||||
import com.maddyhome.idea.vim.api.VimSelectionModel
 | 
					import com.maddyhome.idea.vim.api.VimSelectionModel
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimVisualPosition
 | 
					import com.maddyhome.idea.vim.api.VimVisualPosition
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VirtualFile
 | 
					import com.maddyhome.idea.vim.api.VirtualFile
 | 
				
			||||||
import com.maddyhome.idea.vim.api.injector
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.command.OperatorArguments
 | 
					import com.maddyhome.idea.vim.command.OperatorArguments
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.common.EditorLine
 | 
				
			||||||
import com.maddyhome.idea.vim.common.IndentConfig
 | 
					import com.maddyhome.idea.vim.common.IndentConfig
 | 
				
			||||||
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.TextRange
 | 
					import com.maddyhome.idea.vim.common.TextRange
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.common.offset
 | 
				
			||||||
import com.maddyhome.idea.vim.group.visual.vimSetSystemBlockSelectionSilently
 | 
					import com.maddyhome.idea.vim.group.visual.vimSetSystemBlockSelectionSilently
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.EditorHelper
 | 
					import com.maddyhome.idea.vim.helper.EditorHelper
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.StrictMode
 | 
					import com.maddyhome.idea.vim.helper.StrictMode
 | 
				
			||||||
@@ -88,18 +90,18 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
 | 
				
			|||||||
    return editor.document.lineCount
 | 
					    return editor.document.lineCount
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun deleteRange(leftOffset: Int, rightOffset: Int) {
 | 
					  override fun deleteRange(leftOffset: Offset, rightOffset: Offset) {
 | 
				
			||||||
    editor.document.deleteString(leftOffset, rightOffset)
 | 
					    editor.document.deleteString(leftOffset.point, rightOffset.point)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun addLine(atPosition: Int): Int {
 | 
					  override fun addLine(atPosition: EditorLine.Offset): EditorLine.Pointer {
 | 
				
			||||||
    val offset: Int = if (atPosition < lineCount()) {
 | 
					    val offset: Int = if (atPosition.line < lineCount()) {
 | 
				
			||||||
      // The new line character is inserted before the new line char of the previous line. So it works line an enter
 | 
					      // The new line character is inserted before the new line char of the previous line. So it works line an enter
 | 
				
			||||||
      //   on a line end. I believe that the correct implementation would be to insert the new line char after the
 | 
					      //   on a line end. I believe that the correct implementation would be to insert the new line char after the
 | 
				
			||||||
      //   \n of the previous line, however at the moment this won't update the mark on this line.
 | 
					      //   \n of the previous line, however at the moment this won't update the mark on this line.
 | 
				
			||||||
      //   https://youtrack.jetbrains.com/issue/IDEA-286587
 | 
					      //   https://youtrack.jetbrains.com/issue/IDEA-286587
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      val lineStart = (editor.document.getLineStartOffset(atPosition) - 1).coerceAtLeast(0)
 | 
					      val lineStart = (editor.document.getLineStartOffset(atPosition.line) - 1).coerceAtLeast(0)
 | 
				
			||||||
      val guard = editor.document.getOffsetGuard(lineStart)
 | 
					      val guard = editor.document.getOffsetGuard(lineStart)
 | 
				
			||||||
      if (guard != null && guard.endOffset == lineStart + 1) {
 | 
					      if (guard != null && guard.endOffset == lineStart + 1) {
 | 
				
			||||||
        // Dancing around guarded blocks. It may happen that this concrete position is locked, but the next
 | 
					        // Dancing around guarded blocks. It may happen that this concrete position is locked, but the next
 | 
				
			||||||
@@ -114,11 +116,11 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
 | 
				
			|||||||
      fileSize().toInt()
 | 
					      fileSize().toInt()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    editor.document.insertString(offset, "\n")
 | 
					    editor.document.insertString(offset, "\n")
 | 
				
			||||||
    return atPosition
 | 
					    return EditorLine.Pointer.init(atPosition.line, this)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun insertText(atPosition: Int, text: CharSequence) {
 | 
					  override fun insertText(atPosition: Offset, text: CharSequence) {
 | 
				
			||||||
    editor.document.insertString(atPosition, text)
 | 
					    editor.document.insertString(atPosition.point, text)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun replaceString(start: Int, end: Int, newString: String) {
 | 
					  override fun replaceString(start: Int, end: Int, newString: String) {
 | 
				
			||||||
@@ -126,13 +128,13 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // TODO: 30.12.2021 Is end offset inclusive?
 | 
					  // TODO: 30.12.2021 Is end offset inclusive?
 | 
				
			||||||
  override fun getLineRange(line: Int): Pair<Int, Int> {
 | 
					  override fun getLineRange(line: EditorLine.Pointer): Pair<Offset, Offset> {
 | 
				
			||||||
    // TODO: 30.12.2021 getLineEndOffset returns the same value for "xyz" and "xyz\n"
 | 
					    // TODO: 30.12.2021 getLineEndOffset returns the same value for "xyz" and "xyz\n"
 | 
				
			||||||
    return editor.document.getLineStartOffset(line) to editor.document.getLineEndOffset(line)
 | 
					    return editor.document.getLineStartOffset(line.line).offset to editor.document.getLineEndOffset(line.line).offset
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun getLine(offset: Int): Int {
 | 
					  override fun getLine(offset: Offset): EditorLine.Pointer {
 | 
				
			||||||
    return editor.offsetToLogicalPosition(offset).line
 | 
					    return EditorLine.Pointer.init(editor.offsetToLogicalPosition(offset.point).line, this)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun carets(): List<VimCaret> {
 | 
					  override fun carets(): List<VimCaret> {
 | 
				
			||||||
@@ -201,15 +203,15 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
 | 
				
			|||||||
    return editor.isOneLineMode
 | 
					    return editor.isOneLineMode
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun getText(left: Int, right: Int): CharSequence {
 | 
					  override fun getText(left: Offset, right: Offset): CharSequence {
 | 
				
			||||||
    return editor.document.charsSequence.subSequence(left, right)
 | 
					    return editor.document.charsSequence.subSequence(left.point, right.point)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun search(
 | 
					  override fun search(
 | 
				
			||||||
    pair: Pair<Int, Int>,
 | 
					    pair: Pair<Offset, Offset>,
 | 
				
			||||||
    editor: VimEditor,
 | 
					    editor: VimEditor,
 | 
				
			||||||
    shiftType: LineDeleteShift,
 | 
					    shiftType: LineDeleteShift,
 | 
				
			||||||
  ): Pair<Pair<Int, Int>, LineDeleteShift>? {
 | 
					  ): Pair<Pair<Offset, Offset>, LineDeleteShift>? {
 | 
				
			||||||
    val ijEditor = (editor as IjVimEditor).editor
 | 
					    val ijEditor = (editor as IjVimEditor).editor
 | 
				
			||||||
    return when (shiftType) {
 | 
					    return when (shiftType) {
 | 
				
			||||||
      LineDeleteShift.NO_NL -> if (pair.noGuard(ijEditor)) return pair to shiftType else null
 | 
					      LineDeleteShift.NO_NL -> if (pair.noGuard(ijEditor)) return pair to shiftType else null
 | 
				
			||||||
@@ -356,10 +358,10 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
 | 
				
			|||||||
    return EditorHelper.getVirtualFile(editor)?.getUrl()?.let { VirtualFileManager.extractProtocol(it) }
 | 
					    return EditorHelper.getVirtualFile(editor)?.getUrl()?.let { VirtualFileManager.extractProtocol(it) }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override val projectId = editor.project?.let { injector.file.getProjectId(it) } ?: DEFAULT_PROJECT_ID
 | 
					  override val projectId = editor.project?.basePath ?: DEFAULT_PROJECT_ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun visualPositionToOffset(position: VimVisualPosition): Int {
 | 
					  override fun visualPositionToOffset(position: VimVisualPosition): Offset {
 | 
				
			||||||
    return editor.visualPositionToOffset(VisualPosition(position.line, position.column, position.leansRight))
 | 
					    return editor.visualPositionToOffset(VisualPosition(position.line, position.column, position.leansRight)).offset
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun exitInsertMode(context: ExecutionContext, operatorArguments: OperatorArguments) {
 | 
					  override fun exitInsertMode(context: ExecutionContext, operatorArguments: OperatorArguments) {
 | 
				
			||||||
@@ -415,8 +417,8 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
 | 
				
			|||||||
    return visualPosition.run { VimVisualPosition(line, column, leansRight) }
 | 
					    return visualPosition.run { VimVisualPosition(line, column, leansRight) }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun createLiveMarker(start: Int, end: Int): LiveRange {
 | 
					  override fun createLiveMarker(start: Offset, end: Offset): LiveRange {
 | 
				
			||||||
    return editor.document.createRangeMarker(start, end).vim
 | 
					    return editor.document.createRangeMarker(start.point, end.point).vim
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -454,10 +456,10 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
 | 
				
			|||||||
            ijFoldRegion.isExpanded = value
 | 
					            ijFoldRegion.isExpanded = value
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      override val startOffset: Int
 | 
					      override val startOffset: Offset
 | 
				
			||||||
        get() = ijFoldRegion.startOffset
 | 
					        get() = Offset(ijFoldRegion.startOffset)
 | 
				
			||||||
      override val endOffset: Int
 | 
					      override val endOffset: Offset
 | 
				
			||||||
        get() = ijFoldRegion.endOffset
 | 
					        get() = Offset(ijFoldRegion.endOffset)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -466,17 +468,17 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
 | 
				
			|||||||
    return caret
 | 
					    return caret
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private fun Pair<Int, Int>.noGuard(editor: Editor): Boolean {
 | 
					  private fun Pair<Offset, Offset>.noGuard(editor: Editor): Boolean {
 | 
				
			||||||
    return editor.document.getRangeGuard(this.first, this.second) == null
 | 
					    return editor.document.getRangeGuard(this.first.point, this.second.point) == null
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private inline fun Pair<Int, Int>.shift(
 | 
					  private inline fun Pair<Offset, Offset>.shift(
 | 
				
			||||||
    shiftStart: Int = 0,
 | 
					    shiftStart: Int = 0,
 | 
				
			||||||
    shiftEnd: Int = 0,
 | 
					    shiftEnd: Int = 0,
 | 
				
			||||||
    action: Pair<Int, Int>.() -> Unit,
 | 
					    action: Pair<Offset, Offset>.() -> Unit,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    val data =
 | 
					    val data =
 | 
				
			||||||
      (this.first + shiftStart).coerceAtLeast(0) to (this.second + shiftEnd).coerceAtLeast(0)
 | 
					      (this.first.point + shiftStart).coerceAtLeast(0).offset to (this.second.point + shiftEnd).coerceAtLeast(0).offset
 | 
				
			||||||
    data.action()
 | 
					    data.action()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -498,6 +500,3 @@ public val Editor.vim: VimEditor
 | 
				
			|||||||
  get() = IjVimEditor(this)
 | 
					  get() = IjVimEditor(this)
 | 
				
			||||||
public val VimEditor.ij: Editor
 | 
					public val VimEditor.ij: Editor
 | 
				
			||||||
  get() = (this as IjVimEditor).editor
 | 
					  get() = (this as IjVimEditor).editor
 | 
				
			||||||
 | 
					 | 
				
			||||||
public val com.intellij.openapi.util.TextRange.vim: TextRange
 | 
					 | 
				
			||||||
  get() = TextRange(this.startOffset, this.endOffset)
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,7 +42,6 @@ import com.maddyhome.idea.vim.api.VimMessages
 | 
				
			|||||||
import com.maddyhome.idea.vim.api.VimMotionGroup
 | 
					import com.maddyhome.idea.vim.api.VimMotionGroup
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimOptionGroup
 | 
					import com.maddyhome.idea.vim.api.VimOptionGroup
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimProcessGroup
 | 
					import com.maddyhome.idea.vim.api.VimProcessGroup
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimPsiService
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimRegexpService
 | 
					import com.maddyhome.idea.vim.api.VimRegexpService
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimScrollGroup
 | 
					import com.maddyhome.idea.vim.api.VimScrollGroup
 | 
				
			||||||
import com.maddyhome.idea.vim.api.VimSearchGroup
 | 
					import com.maddyhome.idea.vim.api.VimSearchGroup
 | 
				
			||||||
@@ -66,7 +65,6 @@ import com.maddyhome.idea.vim.group.FileGroup
 | 
				
			|||||||
import com.maddyhome.idea.vim.group.GlobalIjOptions
 | 
					import com.maddyhome.idea.vim.group.GlobalIjOptions
 | 
				
			||||||
import com.maddyhome.idea.vim.group.HistoryGroup
 | 
					import com.maddyhome.idea.vim.group.HistoryGroup
 | 
				
			||||||
import com.maddyhome.idea.vim.group.IjVimOptionGroup
 | 
					import com.maddyhome.idea.vim.group.IjVimOptionGroup
 | 
				
			||||||
import com.maddyhome.idea.vim.group.IjVimPsiService
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.group.MacroGroup
 | 
					import com.maddyhome.idea.vim.group.MacroGroup
 | 
				
			||||||
import com.maddyhome.idea.vim.group.MotionGroup
 | 
					import com.maddyhome.idea.vim.group.MotionGroup
 | 
				
			||||||
import com.maddyhome.idea.vim.group.SearchGroup
 | 
					import com.maddyhome.idea.vim.group.SearchGroup
 | 
				
			||||||
@@ -149,8 +147,6 @@ internal class IjVimInjector : VimInjectorBase() {
 | 
				
			|||||||
    get() = service<MacroGroup>()
 | 
					    get() = service<MacroGroup>()
 | 
				
			||||||
  override val undo: VimUndoRedo
 | 
					  override val undo: VimUndoRedo
 | 
				
			||||||
    get() = service<UndoRedoHelper>()
 | 
					    get() = service<UndoRedoHelper>()
 | 
				
			||||||
  override val psiService: VimPsiService
 | 
					 | 
				
			||||||
    get() = service<IjVimPsiService>()
 | 
					 | 
				
			||||||
  override val commandLineHelper: VimCommandLineHelper
 | 
					  override val commandLineHelper: VimCommandLineHelper
 | 
				
			||||||
    get() = service<CommandLineHelper>()
 | 
					    get() = service<CommandLineHelper>()
 | 
				
			||||||
  override val nativeActionManager: NativeActionManager
 | 
					  override val nativeActionManager: NativeActionManager
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,6 @@ package com.maddyhome.idea.vim.newapi
 | 
				
			|||||||
import com.intellij.openapi.application.ApplicationManager
 | 
					import com.intellij.openapi.application.ApplicationManager
 | 
				
			||||||
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.ExecutionContext
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.api.Options
 | 
					import com.maddyhome.idea.vim.api.Options
 | 
				
			||||||
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
 | 
				
			||||||
@@ -27,7 +26,9 @@ import com.maddyhome.idea.vim.helper.shouldIgnoreCase
 | 
				
			|||||||
import com.maddyhome.idea.vim.helper.updateSearchHighlights
 | 
					import com.maddyhome.idea.vim.helper.updateSearchHighlights
 | 
				
			||||||
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener
 | 
					import com.maddyhome.idea.vim.options.GlobalOptionChangeListener
 | 
				
			||||||
import com.maddyhome.idea.vim.ui.ModalEntry
 | 
					import com.maddyhome.idea.vim.ui.ModalEntry
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.vimscript.model.expressions.Expression
 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.functions.handlers.SubmatchFunctionHandler
 | 
					import com.maddyhome.idea.vim.vimscript.model.functions.handlers.SubmatchFunctionHandler
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.vimscript.parser.VimscriptParser.parseExpression
 | 
				
			||||||
import org.jetbrains.annotations.TestOnly
 | 
					import org.jetbrains.annotations.TestOnly
 | 
				
			||||||
import javax.swing.KeyStroke
 | 
					import javax.swing.KeyStroke
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -81,7 +82,6 @@ public abstract class IjVimSearchGroup : VimSearchGroupBase() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  override fun confirmChoice(
 | 
					  override fun confirmChoice(
 | 
				
			||||||
    editor: VimEditor,
 | 
					    editor: VimEditor,
 | 
				
			||||||
    context: ExecutionContext,
 | 
					 | 
				
			||||||
    match: String,
 | 
					    match: String,
 | 
				
			||||||
    caret: VimCaret,
 | 
					    caret: VimCaret,
 | 
				
			||||||
    startOffset: Int,
 | 
					    startOffset: Int,
 | 
				
			||||||
@@ -121,6 +121,7 @@ public abstract class IjVimSearchGroup : VimSearchGroupBase() {
 | 
				
			|||||||
      // 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
 | 
				
			||||||
      val exEntryPanel: com.maddyhome.idea.vim.ui.ex.ExEntryPanel =
 | 
					      val exEntryPanel: com.maddyhome.idea.vim.ui.ex.ExEntryPanel =
 | 
				
			||||||
        com.maddyhome.idea.vim.ui.ex.ExEntryPanel.getInstanceWithoutShortcuts()
 | 
					        com.maddyhome.idea.vim.ui.ex.ExEntryPanel.getInstanceWithoutShortcuts()
 | 
				
			||||||
 | 
					      val context = injector.executionContextManager.onEditor(editor, null)
 | 
				
			||||||
      exEntryPanel.activate(
 | 
					      exEntryPanel.activate(
 | 
				
			||||||
        editor.ij,
 | 
					        editor.ij,
 | 
				
			||||||
        (context as IjEditorExecutionContext).context,
 | 
					        (context as IjEditorExecutionContext).context,
 | 
				
			||||||
@@ -135,6 +136,10 @@ public abstract class IjVimSearchGroup : VimSearchGroupBase() {
 | 
				
			|||||||
    return result.get()
 | 
					    return result.get()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun parseVimScriptExpression(expressionString: String): Expression? {
 | 
				
			||||||
 | 
					    return parseExpression(expressionString)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun addSubstitutionConfirmationHighlight(editor: VimEditor, startOffset: Int, endOffset: Int) {
 | 
					  override fun addSubstitutionConfirmationHighlight(editor: VimEditor, startOffset: Int, endOffset: Int) {
 | 
				
			||||||
    val hl = addSubstitutionConfirmationHighlight(
 | 
					    val hl = addSubstitutionConfirmationHighlight(
 | 
				
			||||||
      (editor as IjVimEditor).editor,
 | 
					      (editor as IjVimEditor).editor,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,31 +13,144 @@ import com.intellij.openapi.diagnostic.Logger
 | 
				
			|||||||
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.VimSearchHelperBase
 | 
					import com.maddyhome.idea.vim.api.VimSearchHelperBase
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.anyNonWhitespace
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.getLineEndOffset
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.getLineStartForOffset
 | 
				
			||||||
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.common.Direction
 | 
				
			||||||
import com.maddyhome.idea.vim.common.TextRange
 | 
					import com.maddyhome.idea.vim.common.TextRange
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.CharacterHelper
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.CharacterHelper.charType
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.PsiHelper
 | 
					import com.maddyhome.idea.vim.helper.PsiHelper
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.SearchHelper
 | 
					import com.maddyhome.idea.vim.helper.SearchHelper
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.SearchOptions
 | 
					import com.maddyhome.idea.vim.helper.SearchOptions
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.checkInString
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.helper.fileSize
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.VimStateMachine.Companion.getInstance
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
 | 
				
			||||||
import it.unimi.dsi.fastutil.ints.IntComparator
 | 
					import it.unimi.dsi.fastutil.ints.IntComparator
 | 
				
			||||||
import it.unimi.dsi.fastutil.ints.IntComparators
 | 
					import it.unimi.dsi.fastutil.ints.IntComparators
 | 
				
			||||||
import java.util.*
 | 
					import java.util.*
 | 
				
			||||||
 | 
					import java.util.function.Function
 | 
				
			||||||
 | 
					import java.util.regex.Pattern
 | 
				
			||||||
 | 
					import kotlin.math.abs
 | 
				
			||||||
 | 
					import kotlin.math.max
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Service
 | 
					@Service
 | 
				
			||||||
internal class IjVimSearchHelper : VimSearchHelperBase() {
 | 
					internal class IjVimSearchHelper : VimSearchHelperBase() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  companion object {
 | 
					  companion object {
 | 
				
			||||||
 | 
					    private const val BLOCK_CHARS = "{}()[]<>"
 | 
				
			||||||
    private val logger = Logger.getInstance(IjVimSearchHelper::class.java.name)
 | 
					    private val logger = Logger.getInstance(IjVimSearchHelper::class.java.name)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  override fun findSection(
 | 
				
			||||||
 | 
					    editor: VimEditor,
 | 
				
			||||||
 | 
					    caret: ImmutableVimCaret,
 | 
				
			||||||
 | 
					    type: Char,
 | 
				
			||||||
 | 
					    direction: Int,
 | 
				
			||||||
 | 
					    count: Int,
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  : Int {
 | 
				
			||||||
 | 
					    val documentText: CharSequence = editor.ij.document.charsSequence
 | 
				
			||||||
 | 
					    var currentLine: Int = caret.ij.logicalPosition.line + direction
 | 
				
			||||||
 | 
					    var resultOffset = -1
 | 
				
			||||||
 | 
					    var remainingTargets = count
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (currentLine in 1 until editor.lineCount() && remainingTargets > 0) {
 | 
				
			||||||
 | 
					      val lineStartOffset = editor.getLineStartOffset(currentLine)
 | 
				
			||||||
 | 
					      if (lineStartOffset < documentText.length) {
 | 
				
			||||||
 | 
					        val currentChar = documentText[lineStartOffset]
 | 
				
			||||||
 | 
					        if (currentChar == type || currentChar == '\u000C') {
 | 
				
			||||||
 | 
					          resultOffset = lineStartOffset
 | 
				
			||||||
 | 
					          remainingTargets--
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      currentLine += direction
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (resultOffset == -1) {
 | 
				
			||||||
 | 
					      resultOffset = if (direction < 0) 0 else documentText.length - 1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return resultOffset
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun findMethodEnd(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int {
 | 
					  override fun findMethodEnd(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int {
 | 
				
			||||||
    // TODO add it to PsiService
 | 
					 | 
				
			||||||
    return PsiHelper.findMethodEnd(editor.ij, caret.ij.offset, count)
 | 
					    return PsiHelper.findMethodEnd(editor.ij, caret.ij.offset, count)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun findMethodStart(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int {
 | 
					  override fun findMethodStart(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int {
 | 
				
			||||||
    // TODO add it to PsiService
 | 
					 | 
				
			||||||
    return PsiHelper.findMethodStart(editor.ij, caret.ij.offset, count)
 | 
					    return PsiHelper.findMethodStart(editor.ij, caret.ij.offset, count)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun findUnmatchedBlock(editor: VimEditor, caret: ImmutableVimCaret, type: Char, count: Int): Int {
 | 
				
			||||||
 | 
					    val chars: CharSequence = editor.ij.document.charsSequence
 | 
				
			||||||
 | 
					    var pos: Int = caret.ij.offset
 | 
				
			||||||
 | 
					    val loc = BLOCK_CHARS.indexOf(type)
 | 
				
			||||||
 | 
					    // What direction should we go now (-1 is backward, 1 is forward)
 | 
				
			||||||
 | 
					    val dir = if (loc % 2 == 0) Direction.BACKWARDS else Direction.FORWARDS
 | 
				
			||||||
 | 
					    // Which character did we find and which should we now search for
 | 
				
			||||||
 | 
					    val match = BLOCK_CHARS[loc]
 | 
				
			||||||
 | 
					    val found = BLOCK_CHARS[loc - dir.toInt()]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (pos < chars.length && chars[pos] == type) {
 | 
				
			||||||
 | 
					      pos += dir.toInt()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return findBlockLocation(chars, found, match, dir, pos, count)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private fun findBlockLocation(
 | 
				
			||||||
 | 
					    chars: CharSequence,
 | 
				
			||||||
 | 
					    found: Char,
 | 
				
			||||||
 | 
					    match: Char,
 | 
				
			||||||
 | 
					    dir: Direction,
 | 
				
			||||||
 | 
					    pos: Int,
 | 
				
			||||||
 | 
					    cnt: Int,
 | 
				
			||||||
 | 
					  ): Int {
 | 
				
			||||||
 | 
					    var position = pos
 | 
				
			||||||
 | 
					    var count = cnt
 | 
				
			||||||
 | 
					    var res = -1
 | 
				
			||||||
 | 
					    val initialPos = position
 | 
				
			||||||
 | 
					    val initialInString = checkInString(chars, position, true)
 | 
				
			||||||
 | 
					    val inCheckPosF =
 | 
				
			||||||
 | 
					      Function { x: Int -> if (dir === Direction.BACKWARDS && x > 0) x - 1 else x + 1 }
 | 
				
			||||||
 | 
					    val inCheckPos = inCheckPosF.apply(position)
 | 
				
			||||||
 | 
					    var inString = checkInString(chars, inCheckPos, true)
 | 
				
			||||||
 | 
					    var inChar = checkInString(chars, inCheckPos, false)
 | 
				
			||||||
 | 
					    var stack = 0
 | 
				
			||||||
 | 
					    // Search to start or end of file, as appropriate
 | 
				
			||||||
 | 
					    val charsToSearch: Set<Char> = HashSet(listOf('\'', '"', '\n', match, found))
 | 
				
			||||||
 | 
					    while (position >= 0 && position < chars.length && count > 0) {
 | 
				
			||||||
 | 
					      val (c, second) = SearchHelper.findPositionOfFirstCharacter(chars, position, charsToSearch, true, dir) ?: return -1
 | 
				
			||||||
 | 
					      position = second
 | 
				
			||||||
 | 
					      // If we found a match and we're not in a string...
 | 
				
			||||||
 | 
					      if (c == match && (!inString) && !inChar) {
 | 
				
			||||||
 | 
					        // We found our match
 | 
				
			||||||
 | 
					        if (stack == 0) {
 | 
				
			||||||
 | 
					          res = position
 | 
				
			||||||
 | 
					          count--
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          stack--
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } else if (c == '\n') {
 | 
				
			||||||
 | 
					        inString = false
 | 
				
			||||||
 | 
					        inChar = false
 | 
				
			||||||
 | 
					      } else if (position != initialPos) {
 | 
				
			||||||
 | 
					        // We found another character like our original - belongs to another pair
 | 
				
			||||||
 | 
					        if (!inString && !inChar && c == found) {
 | 
				
			||||||
 | 
					          stack++
 | 
				
			||||||
 | 
					        } else if (!inChar) {
 | 
				
			||||||
 | 
					          inString = checkInString(chars, inCheckPosF.apply(position), true)
 | 
				
			||||||
 | 
					        } else if (!inString) {
 | 
				
			||||||
 | 
					          inChar = checkInString(chars, inCheckPosF.apply(position), false)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      position += dir.toInt()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return res
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun findPattern(
 | 
					  override fun findPattern(
 | 
				
			||||||
    editor: VimEditor,
 | 
					    editor: VimEditor,
 | 
				
			||||||
    pattern: String?,
 | 
					    pattern: String?,
 | 
				
			||||||
@@ -60,6 +173,525 @@ internal class IjVimSearchHelper : VimSearchHelperBase() {
 | 
				
			|||||||
    else SearchHelper.findAll(editor.ij, pattern, startLine, endLine, ignoreCase)
 | 
					    else SearchHelper.findAll(editor.ij, pattern, startLine, endLine, ignoreCase)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun findNextCharacterOnLine(editor: VimEditor, caret: ImmutableVimCaret, count: Int, ch: Char): Int {
 | 
				
			||||||
 | 
					    val line: Int = caret.ij.logicalPosition.line
 | 
				
			||||||
 | 
					    val start = editor.getLineStartOffset(line)
 | 
				
			||||||
 | 
					    val end = editor.getLineEndOffset(line, true)
 | 
				
			||||||
 | 
					    val chars: CharSequence = editor.ij.document.charsSequence
 | 
				
			||||||
 | 
					    var found = 0
 | 
				
			||||||
 | 
					    val step = if (count >= 0) 1 else -1
 | 
				
			||||||
 | 
					    var pos: Int = caret.ij.offset + step
 | 
				
			||||||
 | 
					    while (pos in start until end && pos < chars.length) {
 | 
				
			||||||
 | 
					      if (chars[pos] == ch) {
 | 
				
			||||||
 | 
					        found++
 | 
				
			||||||
 | 
					        if (found == abs(count)) {
 | 
				
			||||||
 | 
					          break
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      pos += step
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return if (found == abs(count)) {
 | 
				
			||||||
 | 
					      pos
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      -1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun findWordUnderCursor(
 | 
				
			||||||
 | 
					    editor: VimEditor,
 | 
				
			||||||
 | 
					    caret: ImmutableVimCaret,
 | 
				
			||||||
 | 
					    count: Int,
 | 
				
			||||||
 | 
					    dir: Int,
 | 
				
			||||||
 | 
					    isOuter: Boolean,
 | 
				
			||||||
 | 
					    isBig: Boolean,
 | 
				
			||||||
 | 
					    hasSelection: Boolean,
 | 
				
			||||||
 | 
					  ): TextRange {
 | 
				
			||||||
 | 
					    if (logger.isDebugEnabled) {
 | 
				
			||||||
 | 
					      logger.debug("count=$count")
 | 
				
			||||||
 | 
					      logger.debug("dir=$dir")
 | 
				
			||||||
 | 
					      logger.debug("isOuter=$isOuter")
 | 
				
			||||||
 | 
					      logger.debug("isBig=$isBig")
 | 
				
			||||||
 | 
					      logger.debug("hasSelection=$hasSelection")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val chars: CharSequence = editor.ij.document.charsSequence
 | 
				
			||||||
 | 
					    //int min = EditorHelper.getLineStartOffset(editor, EditorHelper.getCurrentLogicalLine(editor));
 | 
				
			||||||
 | 
					    //int max = EditorHelper.getLineEndOffset(editor, EditorHelper.getCurrentLogicalLine(editor), true);
 | 
				
			||||||
 | 
					    val min = 0
 | 
				
			||||||
 | 
					    val max: Int = editor.ij.fileSize
 | 
				
			||||||
 | 
					    if (max == 0) return TextRange(0, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (logger.isDebugEnabled) {
 | 
				
			||||||
 | 
					      logger.debug("min=$min")
 | 
				
			||||||
 | 
					      logger.debug("max=$max")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val pos: Int = caret.ij.offset
 | 
				
			||||||
 | 
					    if (chars.length <= pos) return TextRange(chars.length - 1, chars.length - 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val startSpace = charType(editor, chars[pos], isBig) === CharacterHelper.CharacterType.WHITESPACE
 | 
				
			||||||
 | 
					    // Find word start
 | 
				
			||||||
 | 
					    val onWordStart = pos == min ||
 | 
				
			||||||
 | 
					      charType(editor, chars[pos - 1], isBig) !==
 | 
				
			||||||
 | 
					      charType(editor, chars[pos], isBig)
 | 
				
			||||||
 | 
					    var start = pos
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (logger.isDebugEnabled) {
 | 
				
			||||||
 | 
					      logger.debug("pos=$pos")
 | 
				
			||||||
 | 
					      logger.debug("onWordStart=$onWordStart")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!onWordStart && !(startSpace && isOuter) || hasSelection || count > 1 && dir == -1) {
 | 
				
			||||||
 | 
					      start = if (dir == 1) {
 | 
				
			||||||
 | 
					        findNextWord(editor, pos, -1, isBig, !isOuter)
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        findNextWord(
 | 
				
			||||||
 | 
					          editor,
 | 
				
			||||||
 | 
					          pos,
 | 
				
			||||||
 | 
					          -(count - if (onWordStart && !hasSelection) 1 else 0),
 | 
				
			||||||
 | 
					          isBig,
 | 
				
			||||||
 | 
					          !isOuter
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      start = editor.normalizeOffset(start, false)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (logger.isDebugEnabled) logger.debug("start=$start")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Find word end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Find word end
 | 
				
			||||||
 | 
					    val onWordEnd = pos >= max - 1 ||
 | 
				
			||||||
 | 
					      charType(editor, chars[pos + 1], isBig) !==
 | 
				
			||||||
 | 
					      charType(editor, chars[pos], isBig)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (logger.isDebugEnabled) logger.debug("onWordEnd=$onWordEnd")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var end = pos
 | 
				
			||||||
 | 
					    if (!onWordEnd || hasSelection || count > 1 && dir == 1 || startSpace && isOuter) {
 | 
				
			||||||
 | 
					      end = if (dir == 1) {
 | 
				
			||||||
 | 
					        val c = count - if (onWordEnd && !hasSelection && (!(startSpace && isOuter) || startSpace && !isOuter)) 1 else 0
 | 
				
			||||||
 | 
					        findNextWordEnd(editor, pos, c, isBig, !isOuter)
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        findNextWordEnd(editor, pos, 1, isBig, !isOuter)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (logger.isDebugEnabled) logger.debug("end=$end")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var goBack = startSpace && !hasSelection || !startSpace && hasSelection && !onWordStart
 | 
				
			||||||
 | 
					    if (dir == 1 && isOuter) {
 | 
				
			||||||
 | 
					      var firstEnd = end
 | 
				
			||||||
 | 
					      if (count > 1) {
 | 
				
			||||||
 | 
					        firstEnd = findNextWordEnd(editor, pos, 1, isBig, false)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (firstEnd < max - 1) {
 | 
				
			||||||
 | 
					        if (charType(editor, chars[firstEnd + 1], false) !== CharacterHelper.CharacterType.WHITESPACE) {
 | 
				
			||||||
 | 
					          goBack = true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (dir == -1 && isOuter && startSpace) {
 | 
				
			||||||
 | 
					      if (pos > min) {
 | 
				
			||||||
 | 
					        if (charType(editor, chars[pos - 1], false) !== CharacterHelper.CharacterType.WHITESPACE) {
 | 
				
			||||||
 | 
					          goBack = true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var goForward = dir == 1 && isOuter && (!startSpace && !onWordEnd || startSpace && onWordEnd && hasSelection)
 | 
				
			||||||
 | 
					    if (!goForward && dir == 1 && isOuter) {
 | 
				
			||||||
 | 
					      var firstEnd = end
 | 
				
			||||||
 | 
					      if (count > 1) {
 | 
				
			||||||
 | 
					        firstEnd = findNextWordEnd(editor, pos, 1, isBig, false)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (firstEnd < max - 1) {
 | 
				
			||||||
 | 
					        if (charType(editor, chars[firstEnd + 1], false) !== CharacterHelper.CharacterType.WHITESPACE) {
 | 
				
			||||||
 | 
					          goForward = true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!goForward && dir == 1 && isOuter && !startSpace && !hasSelection) {
 | 
				
			||||||
 | 
					      if (end < max - 1) {
 | 
				
			||||||
 | 
					        if (charType(editor, chars[end + 1], !isBig) !==
 | 
				
			||||||
 | 
					          charType(editor, chars[end], !isBig)
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          goForward = true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (logger.isDebugEnabled) {
 | 
				
			||||||
 | 
					      logger.debug("goBack=$goBack")
 | 
				
			||||||
 | 
					      logger.debug("goForward=$goForward")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (goForward) {
 | 
				
			||||||
 | 
					      if (editor.anyNonWhitespace(end, 1)) {
 | 
				
			||||||
 | 
					        while (end + 1 < max &&
 | 
				
			||||||
 | 
					          charType(editor, chars[end + 1], false) === CharacterHelper.CharacterType.WHITESPACE
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          end++
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (goBack) {
 | 
				
			||||||
 | 
					      if (editor.anyNonWhitespace(start, -1)) {
 | 
				
			||||||
 | 
					        while (start > min &&
 | 
				
			||||||
 | 
					          charType(editor, chars[start - 1], false) === CharacterHelper.CharacterType.WHITESPACE
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          start--
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (logger.isDebugEnabled) {
 | 
				
			||||||
 | 
					      logger.debug("start=$start")
 | 
				
			||||||
 | 
					      logger.debug("end=$end")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // End offset is exclusive
 | 
				
			||||||
 | 
					    return TextRange(start, end + 1)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun findBlockTagRange(editor: VimEditor, caret: ImmutableVimCaret, count: Int, isOuter: Boolean): TextRange? {
 | 
				
			||||||
 | 
					    var counter = count
 | 
				
			||||||
 | 
					    var isOuterVariable = isOuter
 | 
				
			||||||
 | 
					    val position: Int = caret.ij.offset
 | 
				
			||||||
 | 
					    val sequence: CharSequence = editor.ij.document.charsSequence
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val selectionStart: Int = caret.ij.selectionStart
 | 
				
			||||||
 | 
					    val selectionEnd: Int = caret.ij.selectionEnd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val isRangeSelection = selectionEnd - selectionStart > 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var searchStartPosition: Int
 | 
				
			||||||
 | 
					    searchStartPosition = if (!isRangeSelection) {
 | 
				
			||||||
 | 
					      val line: Int = caret.ij.logicalPosition.line
 | 
				
			||||||
 | 
					      val lineBegin: Int = editor.ij.document.getLineStartOffset(line)
 | 
				
			||||||
 | 
					      ignoreWhitespaceAtLineStart(sequence, lineBegin, position)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      selectionEnd
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (isInHTMLTag(sequence, searchStartPosition, false)) {
 | 
				
			||||||
 | 
					      // caret is inside opening tag. Move to closing '>'.
 | 
				
			||||||
 | 
					      while (searchStartPosition < sequence.length && sequence[searchStartPosition] != '>') {
 | 
				
			||||||
 | 
					        searchStartPosition++
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else if (isInHTMLTag(sequence, searchStartPosition, true)) {
 | 
				
			||||||
 | 
					      // caret is inside closing tag. Move to starting '<'.
 | 
				
			||||||
 | 
					      while (searchStartPosition > 0 && sequence[searchStartPosition] != '<') {
 | 
				
			||||||
 | 
					        searchStartPosition--
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (true) {
 | 
				
			||||||
 | 
					      val (closingTagTextRange, tagName) = findUnmatchedClosingTag(sequence, searchStartPosition, counter)
 | 
				
			||||||
 | 
					        ?: return null
 | 
				
			||||||
 | 
					      val openingTag = findUnmatchedOpeningTag(sequence, closingTagTextRange.startOffset, tagName)
 | 
				
			||||||
 | 
					        ?: return null
 | 
				
			||||||
 | 
					      if (isRangeSelection && openingTag.endOffset - 1 >= selectionStart) {
 | 
				
			||||||
 | 
					        // If there was already some text selected and the new selection would not extend further, we try again
 | 
				
			||||||
 | 
					        searchStartPosition = closingTagTextRange.endOffset
 | 
				
			||||||
 | 
					        counter = 1
 | 
				
			||||||
 | 
					        continue
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      var selectionEndWithoutNewline = selectionEnd
 | 
				
			||||||
 | 
					      while (selectionEndWithoutNewline < sequence.length && sequence[selectionEndWithoutNewline] == '\n') {
 | 
				
			||||||
 | 
					        selectionEndWithoutNewline++
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      val mode = getInstance(editor).mode
 | 
				
			||||||
 | 
					      if (mode is VISUAL) {
 | 
				
			||||||
 | 
					        if (closingTagTextRange.startOffset == selectionEndWithoutNewline &&
 | 
				
			||||||
 | 
					          openingTag.endOffset == selectionStart
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          // Special case: if the inner tag is already selected we should like isOuter is active
 | 
				
			||||||
 | 
					          // Note that we need to ignore newlines, because their selection is lost between multiple "it" invocations
 | 
				
			||||||
 | 
					          isOuterVariable = true
 | 
				
			||||||
 | 
					        } else if (openingTag.endOffset == closingTagTextRange.startOffset &&
 | 
				
			||||||
 | 
					          selectionStart == openingTag.endOffset
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          // Special case: for an empty tag pair (e.g. <a></a>) the whole tag is selected if the caret is in the middle.
 | 
				
			||||||
 | 
					          isOuterVariable = true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return if (isOuterVariable) {
 | 
				
			||||||
 | 
					        TextRange(openingTag.startOffset, closingTagTextRange.endOffset)
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        TextRange(openingTag.endOffset, closingTagTextRange.startOffset)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * returns new position which ignore whitespaces at beginning of the line
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private fun ignoreWhitespaceAtLineStart(seq: CharSequence, lineStart: Int, pos: Int): Int {
 | 
				
			||||||
 | 
					    var position = pos
 | 
				
			||||||
 | 
					    if (seq.subSequence(lineStart, position).chars().allMatch { codePoint: Int ->
 | 
				
			||||||
 | 
					        Character.isWhitespace(
 | 
				
			||||||
 | 
					          codePoint
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      }) {
 | 
				
			||||||
 | 
					      while (position < seq.length && seq[position] != '\n' && Character.isWhitespace(seq[position])) {
 | 
				
			||||||
 | 
					        position++
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return position
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Returns true if there is a html at the given position. Ignores tags with a trailing slash like <aaa></aaa>.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private fun isInHTMLTag(sequence: CharSequence, position: Int, isEndtag: Boolean): Boolean {
 | 
				
			||||||
 | 
					    var openingBracket = -1
 | 
				
			||||||
 | 
					    run {
 | 
				
			||||||
 | 
					      var i = position
 | 
				
			||||||
 | 
					      while (i >= 0 && i < sequence.length) {
 | 
				
			||||||
 | 
					        if (sequence[i] == '<') {
 | 
				
			||||||
 | 
					          openingBracket = i
 | 
				
			||||||
 | 
					          break
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (sequence[i] == '>' && i != position) {
 | 
				
			||||||
 | 
					          return false
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        i--
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (openingBracket == -1) {
 | 
				
			||||||
 | 
					      return false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    val hasSlashAfterOpening = openingBracket + 1 < sequence.length && sequence[openingBracket + 1] == '/'
 | 
				
			||||||
 | 
					    if (isEndtag && !hasSlashAfterOpening || !isEndtag && hasSlashAfterOpening) {
 | 
				
			||||||
 | 
					      return false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var closingBracket = -1
 | 
				
			||||||
 | 
					    for (i in openingBracket until sequence.length) {
 | 
				
			||||||
 | 
					      if (sequence[i] == '>') {
 | 
				
			||||||
 | 
					        closingBracket = i
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return closingBracket != -1 && sequence[closingBracket - 1] != '/'
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private fun findUnmatchedOpeningTag(
 | 
				
			||||||
 | 
					    sequence: CharSequence,
 | 
				
			||||||
 | 
					    position: Int,
 | 
				
			||||||
 | 
					    tagName: String,
 | 
				
			||||||
 | 
					  ): TextRange? {
 | 
				
			||||||
 | 
					    val quotedTagName = Pattern.quote(tagName)
 | 
				
			||||||
 | 
					    val patternString = ("(</%s>)" // match closing tags
 | 
				
			||||||
 | 
					      +
 | 
				
			||||||
 | 
					      "|(<%s" // or opening tags starting with tagName
 | 
				
			||||||
 | 
					      +
 | 
				
			||||||
 | 
					      "(\\s([^>]*" // After at least one whitespace there might be additional text in the tag. E.g. <html lang="en">
 | 
				
			||||||
 | 
					      +
 | 
				
			||||||
 | 
					      "[^/])?)?>)") // Slash is not allowed as last character (this would be a self closing tag).
 | 
				
			||||||
 | 
					    val tagPattern =
 | 
				
			||||||
 | 
					      Pattern.compile(String.format(patternString, quotedTagName, quotedTagName), Pattern.CASE_INSENSITIVE)
 | 
				
			||||||
 | 
					    val matcher = tagPattern.matcher(sequence.subSequence(0, position + 1))
 | 
				
			||||||
 | 
					    val openTags: Deque<TextRange> = ArrayDeque()
 | 
				
			||||||
 | 
					    while (matcher.find()) {
 | 
				
			||||||
 | 
					      val match = TextRange(matcher.start(), matcher.end())
 | 
				
			||||||
 | 
					      if (sequence[matcher.start() + 1] == '/') {
 | 
				
			||||||
 | 
					        if (!openTags.isEmpty()) {
 | 
				
			||||||
 | 
					          openTags.pop()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        openTags.push(match)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return if (openTags.isEmpty()) {
 | 
				
			||||||
 | 
					      null
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      openTags.pop()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private fun findUnmatchedClosingTag(
 | 
				
			||||||
 | 
					    sequence: CharSequence,
 | 
				
			||||||
 | 
					    position: Int,
 | 
				
			||||||
 | 
					    count: Int,
 | 
				
			||||||
 | 
					  ): Pair<TextRange, String>? {
 | 
				
			||||||
 | 
					    // The tag name may contain any characters except slashes, whitespace and '>'
 | 
				
			||||||
 | 
					    var counter = count
 | 
				
			||||||
 | 
					    val tagNamePattern = "([^/\\s>]+)"
 | 
				
			||||||
 | 
					    // An opening tag consists of '<' followed by a tag name, optionally some additional text after whitespace and a '>'
 | 
				
			||||||
 | 
					    val openingTagPattern = String.format("<%s(?:\\s[^>]*)?>", tagNamePattern)
 | 
				
			||||||
 | 
					    val closingTagPattern = String.format("</%s>", tagNamePattern)
 | 
				
			||||||
 | 
					    val tagPattern = Pattern.compile(String.format("(?:%s)|(?:%s)", openingTagPattern, closingTagPattern))
 | 
				
			||||||
 | 
					    val matcher = tagPattern.matcher(sequence.subSequence(position, sequence.length))
 | 
				
			||||||
 | 
					    val openTags: Deque<String> = ArrayDeque()
 | 
				
			||||||
 | 
					    while (matcher.find()) {
 | 
				
			||||||
 | 
					      val isClosingTag = matcher.group(1) == null
 | 
				
			||||||
 | 
					      if (isClosingTag) {
 | 
				
			||||||
 | 
					        val tagName = matcher.group(2)
 | 
				
			||||||
 | 
					        // Ignore unmatched open tags. Either the file is malformed or it might be a tag like <br> that does not need to be closed.
 | 
				
			||||||
 | 
					        while (!openTags.isEmpty() && !openTags.peek().equals(tagName, ignoreCase = true)) {
 | 
				
			||||||
 | 
					          openTags.pop()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (openTags.isEmpty()) {
 | 
				
			||||||
 | 
					          if (counter <= 1) {
 | 
				
			||||||
 | 
					            return Pair(TextRange(position + matcher.start(), position + matcher.end()), tagName)
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            counter--
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          openTags.pop()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        val tagName = matcher.group(1)
 | 
				
			||||||
 | 
					        openTags.push(tagName)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun findBlockRange(
 | 
				
			||||||
 | 
					    editor: VimEditor,
 | 
				
			||||||
 | 
					    caret: ImmutableVimCaret,
 | 
				
			||||||
 | 
					    type: Char,
 | 
				
			||||||
 | 
					    count: Int,
 | 
				
			||||||
 | 
					    isOuter: Boolean,
 | 
				
			||||||
 | 
					  ): TextRange? {
 | 
				
			||||||
 | 
					    val chars: CharSequence = editor.ij.document.charsSequence
 | 
				
			||||||
 | 
					    var pos: Int = caret.ij.offset
 | 
				
			||||||
 | 
					    var start: Int = caret.ij.selectionStart
 | 
				
			||||||
 | 
					    var end: Int = caret.ij.selectionEnd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val loc = BLOCK_CHARS.indexOf(type)
 | 
				
			||||||
 | 
					    val close = BLOCK_CHARS[loc + 1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // extend the range for blank line after type and before close, as they are excluded when inner match
 | 
				
			||||||
 | 
					    if (!isOuter) {
 | 
				
			||||||
 | 
					      if (start > 1 && chars[start - 2] == type && chars[start - 1] == '\n') {
 | 
				
			||||||
 | 
					        start--
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (end < chars.length && chars[end] == '\n') {
 | 
				
			||||||
 | 
					        var isSingleLineAllWhiteSpaceUntilClose = false
 | 
				
			||||||
 | 
					        var countWhiteSpaceCharacter = 1
 | 
				
			||||||
 | 
					        while (end + countWhiteSpaceCharacter < chars.length) {
 | 
				
			||||||
 | 
					          if (Character.isWhitespace(chars[end + countWhiteSpaceCharacter]) &&
 | 
				
			||||||
 | 
					            chars[end + countWhiteSpaceCharacter] != '\n'
 | 
				
			||||||
 | 
					          ) {
 | 
				
			||||||
 | 
					            countWhiteSpaceCharacter++
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (chars[end + countWhiteSpaceCharacter] == close) {
 | 
				
			||||||
 | 
					            isSingleLineAllWhiteSpaceUntilClose = true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          break
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (isSingleLineAllWhiteSpaceUntilClose) {
 | 
				
			||||||
 | 
					          end += countWhiteSpaceCharacter
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var rangeSelection = end - start > 1
 | 
				
			||||||
 | 
					    if (rangeSelection && start == 0) // early return not only for optimization
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      return null // but also not to break the interval semantic on this edge case (see below)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* In case of successive inner selection. We want to break out of
 | 
				
			||||||
 | 
					     * the block delimiter of the current inner selection.
 | 
				
			||||||
 | 
					     * In other terms, for the rest of the algorithm, a previous inner selection of a block
 | 
				
			||||||
 | 
					     * if equivalent to an outer one. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* In case of successive inner selection. We want to break out of
 | 
				
			||||||
 | 
					     * the block delimiter of the current inner selection.
 | 
				
			||||||
 | 
					     * In other terms, for the rest of the algorithm, a previous inner selection of a block
 | 
				
			||||||
 | 
					     * if equivalent to an outer one. */if (!isOuter && start - 1 >= 0 && type == chars[start - 1] && end < chars.length && close == chars[end]) {
 | 
				
			||||||
 | 
					      start -= 1
 | 
				
			||||||
 | 
					      pos = start
 | 
				
			||||||
 | 
					      rangeSelection = true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* when one char is selected, we want to find the enclosing block of (start,end]
 | 
				
			||||||
 | 
					     * although when a range of characters is selected, we want the enclosing block of [start, end]
 | 
				
			||||||
 | 
					     * shifting the position allow to express which kind of interval we work on */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* when one char is selected, we want to find the enclosing block of (start,end]
 | 
				
			||||||
 | 
					     * although when a range of characters is selected, we want the enclosing block of [start, end]
 | 
				
			||||||
 | 
					     * shifting the position allow to express which kind of interval we work on */if (rangeSelection) pos =
 | 
				
			||||||
 | 
					      max(0.0, (start - 1).toDouble()).toInt()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val initialPosIsInString = checkInString(chars, pos, true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var bstart = -1
 | 
				
			||||||
 | 
					    var bend = -1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var startPosInStringFound = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (initialPosIsInString) {
 | 
				
			||||||
 | 
					      val quoteRange = injector.searchHelper
 | 
				
			||||||
 | 
					        .findBlockQuoteInLineRange(editor, caret, '"', false)
 | 
				
			||||||
 | 
					      if (quoteRange != null) {
 | 
				
			||||||
 | 
					        val startOffset = quoteRange.startOffset
 | 
				
			||||||
 | 
					        val endOffset = quoteRange.endOffset
 | 
				
			||||||
 | 
					        val subSequence = chars.subSequence(startOffset, endOffset)
 | 
				
			||||||
 | 
					        val inQuotePos = pos - startOffset
 | 
				
			||||||
 | 
					        var inQuoteStart =
 | 
				
			||||||
 | 
					          findBlockLocation(subSequence, close, type, Direction.BACKWARDS, inQuotePos, count)
 | 
				
			||||||
 | 
					        if (inQuoteStart == -1) {
 | 
				
			||||||
 | 
					          inQuoteStart =
 | 
				
			||||||
 | 
					            findBlockLocation(subSequence, close, type, Direction.FORWARDS, inQuotePos, count)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (inQuoteStart != -1) {
 | 
				
			||||||
 | 
					          startPosInStringFound = true
 | 
				
			||||||
 | 
					          val inQuoteEnd =
 | 
				
			||||||
 | 
					            findBlockLocation(subSequence, type, close, Direction.FORWARDS, inQuoteStart, 1)
 | 
				
			||||||
 | 
					          if (inQuoteEnd != -1) {
 | 
				
			||||||
 | 
					            bstart = inQuoteStart + startOffset
 | 
				
			||||||
 | 
					            bend = inQuoteEnd + startOffset
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!startPosInStringFound) {
 | 
				
			||||||
 | 
					      bstart = findBlockLocation(chars, close, type, Direction.BACKWARDS, pos, count)
 | 
				
			||||||
 | 
					      if (bstart == -1) {
 | 
				
			||||||
 | 
					        bstart = findBlockLocation(chars, close, type, Direction.FORWARDS, pos, count)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (bstart != -1) {
 | 
				
			||||||
 | 
					        bend = findBlockLocation(chars, type, close, Direction.FORWARDS, bstart, 1)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (bstart == -1 || bend == -1) {
 | 
				
			||||||
 | 
					      return null
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!isOuter) {
 | 
				
			||||||
 | 
					      bstart++
 | 
				
			||||||
 | 
					      // exclude first line break after start for inner match
 | 
				
			||||||
 | 
					      if (chars[bstart] == '\n') {
 | 
				
			||||||
 | 
					        bstart++
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      val o = editor.getLineStartForOffset(bend)
 | 
				
			||||||
 | 
					      var allWhite = true
 | 
				
			||||||
 | 
					      for (i in o until bend) {
 | 
				
			||||||
 | 
					        if (!Character.isWhitespace(chars[i])) {
 | 
				
			||||||
 | 
					          allWhite = false
 | 
				
			||||||
 | 
					          break
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (allWhite) {
 | 
				
			||||||
 | 
					        bend = o - 2
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        bend--
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // End offset exclusive
 | 
				
			||||||
 | 
					    return TextRange(bstart, bend + 1)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun findMisspelledWord(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int {
 | 
					  override fun findMisspelledWord(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int {
 | 
				
			||||||
    val startOffset: Int
 | 
					    val startOffset: Int
 | 
				
			||||||
    val endOffset: Int
 | 
					    val endOffset: Int
 | 
				
			||||||
@@ -68,18 +700,17 @@ internal class IjVimSearchHelper : VimSearchHelperBase() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (count < 0) {
 | 
					    if (count < 0) {
 | 
				
			||||||
      startOffset = 0
 | 
					      startOffset = 0
 | 
				
			||||||
      endOffset = caret.offset - 1
 | 
					      endOffset = caret.offset.point - 1
 | 
				
			||||||
      skipCount = -count - 1
 | 
					      skipCount = -count - 1
 | 
				
			||||||
      offsetOrdering = IntComparators.OPPOSITE_COMPARATOR
 | 
					      offsetOrdering = IntComparators.OPPOSITE_COMPARATOR
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else {
 | 
					    else {
 | 
				
			||||||
      startOffset = caret.offset + 1
 | 
					      startOffset = caret.offset.point + 1
 | 
				
			||||||
      endOffset = editor.ij.document.textLength
 | 
					      endOffset = editor.ij.document.textLength
 | 
				
			||||||
      skipCount = count - 1
 | 
					      skipCount = count - 1
 | 
				
			||||||
      offsetOrdering = IntComparators.NATURAL_COMPARATOR
 | 
					      offsetOrdering = IntComparators.NATURAL_COMPARATOR
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO add it to PsiService
 | 
					 | 
				
			||||||
    return SearchHelper.findMisspelledWords(editor.ij, startOffset, endOffset, skipCount, offsetOrdering)
 | 
					    return SearchHelper.findMisspelledWords(editor.ij, startOffset, endOffset, skipCount, offsetOrdering)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -307,7 +307,7 @@ public class ExOutputPanel extends JPanel {
 | 
				
			|||||||
                    KeyHandler.getInstance().getKeyStack().dump());
 | 
					                    KeyHandler.getInstance().getKeyStack().dump());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        KeyHandler.getInstance().getKeyStack().addKeys(keys);
 | 
					        KeyHandler.getInstance().getKeyStack().addKeys(keys);
 | 
				
			||||||
        ExecutionContext context = injector.getExecutionContextManager().getEditorExecutionContext(new IjVimEditor(myEditor));
 | 
					        ExecutionContext.Editor context = injector.getExecutionContextManager().onEditor(new IjVimEditor(myEditor), null);
 | 
				
			||||||
        VimPlugin.getMacro().playbackKeys(new IjVimEditor(myEditor), context, 1);
 | 
					        VimPlugin.getMacro().playbackKeys(new IjVimEditor(myEditor), context, 1);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ package com.maddyhome.idea.vim.ui.ex
 | 
				
			|||||||
import com.intellij.openapi.diagnostic.debug
 | 
					import com.intellij.openapi.diagnostic.debug
 | 
				
			||||||
import com.intellij.openapi.diagnostic.logger
 | 
					import com.intellij.openapi.diagnostic.logger
 | 
				
			||||||
import com.maddyhome.idea.vim.KeyHandler
 | 
					import com.maddyhome.idea.vim.KeyHandler
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.injector
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
import org.jetbrains.annotations.NonNls
 | 
					import org.jetbrains.annotations.NonNls
 | 
				
			||||||
import java.awt.event.ActionEvent
 | 
					import java.awt.event.ActionEvent
 | 
				
			||||||
@@ -125,7 +126,12 @@ internal object ExEditorKit : DefaultEditorKit() {
 | 
				
			|||||||
              val entry = ExEntryPanel.getInstance().entry
 | 
					              val entry = ExEntryPanel.getInstance().entry
 | 
				
			||||||
              val editor = entry.editor
 | 
					              val editor = entry.editor
 | 
				
			||||||
              val keyHandler = KeyHandler.getInstance()
 | 
					              val keyHandler = KeyHandler.getInstance()
 | 
				
			||||||
              keyHandler.handleKey(editor.vim, key, entry.context.vim, keyHandler.keyHandlerState)
 | 
					              keyHandler.handleKey(
 | 
				
			||||||
 | 
					                editor.vim,
 | 
				
			||||||
 | 
					                key,
 | 
				
			||||||
 | 
					                injector.executionContextManager.onEditor(editor.vim, entry.context.vim),
 | 
				
			||||||
 | 
					                keyHandler.keyHandlerState,
 | 
				
			||||||
 | 
					              )
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
              val event = ActionEvent(e.source, e.id, c.toString(), e.getWhen(), e.modifiers)
 | 
					              val event = ActionEvent(e.source, e.id, c.toString(), e.getWhen(), e.modifiers)
 | 
				
			||||||
              super.actionPerformed(event)
 | 
					              super.actionPerformed(event)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ import com.intellij.openapi.actionSystem.AnActionEvent
 | 
				
			|||||||
import com.intellij.openapi.actionSystem.KeyboardShortcut
 | 
					import com.intellij.openapi.actionSystem.KeyboardShortcut
 | 
				
			||||||
import com.intellij.openapi.project.DumbAwareAction
 | 
					import com.intellij.openapi.project.DumbAwareAction
 | 
				
			||||||
import com.maddyhome.idea.vim.KeyHandler
 | 
					import com.maddyhome.idea.vim.KeyHandler
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.api.injector
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
import java.awt.event.KeyEvent
 | 
					import java.awt.event.KeyEvent
 | 
				
			||||||
import javax.swing.KeyStroke
 | 
					import javax.swing.KeyStroke
 | 
				
			||||||
@@ -36,12 +37,12 @@ internal class ExShortcutKeyAction(private val exEntryPanel: ExEntryPanel) : Dum
 | 
				
			|||||||
    if (keyStroke != null) {
 | 
					    if (keyStroke != null) {
 | 
				
			||||||
      val editor = exEntryPanel.entry.editor
 | 
					      val editor = exEntryPanel.entry.editor
 | 
				
			||||||
      val keyHandler = KeyHandler.getInstance()
 | 
					      val keyHandler = KeyHandler.getInstance()
 | 
				
			||||||
 | 
					      keyHandler.handleKey(
 | 
				
			||||||
      // About the context: we use the context of the main editor to execute actions on it.
 | 
					        editor.vim,
 | 
				
			||||||
      //   e.dataContext will refer to the ex-entry editor and commands will be executed on it,
 | 
					        keyStroke,
 | 
				
			||||||
      //   thus it should not be used. For example, `:action EditorSelectWord` will not work with this context
 | 
					        injector.executionContextManager.onEditor(editor.vim, e.dataContext.vim),
 | 
				
			||||||
      val mainEditorContext = exEntryPanel.entry.context.vim
 | 
					        keyHandler.keyHandlerState
 | 
				
			||||||
      keyHandler.handleKey(editor.vim, keyStroke, mainEditorContext, keyHandler.keyHandlerState)
 | 
					      )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,7 +74,8 @@ internal class Executor : VimScriptExecutorBase() {
 | 
				
			|||||||
            VimPlugin.indicateError()
 | 
					            VimPlugin.indicateError()
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        } catch (e: Exception) {
 | 
					        } catch (e: Exception) {
 | 
				
			||||||
          logger.warn(e)
 | 
					          logger.warn("Caught: ${e.message}")
 | 
				
			||||||
 | 
					          logger.warn(e.stackTrace.toString())
 | 
				
			||||||
          if (injector.application.isUnitTest()) {
 | 
					          if (injector.application.isUnitTest()) {
 | 
				
			||||||
            throw e
 | 
					            throw e
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -113,9 +113,9 @@ internal data class GlobalCommand(val ranges: Ranges, val argument: String, val
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (globalBusy) {
 | 
					      if (globalBusy) {
 | 
				
			||||||
        val match = regex.findInLine(editor, editor.currentCaret().getLine())
 | 
					        val match = regex.findInLine(editor, editor.currentCaret().getLine().line)
 | 
				
			||||||
        if (match is VimMatchResult.Success == !invert) {
 | 
					        if (match is VimMatchResult.Success == !invert) {
 | 
				
			||||||
          globalExecuteOne(editor, context, editor.getLineStartOffset(editor.currentCaret().getLine()), cmd.toString())
 | 
					          globalExecuteOne(editor, context, editor.getLineStartOffset(editor.currentCaret().getLine().line), cmd.toString())
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        val line1 = range.startLine
 | 
					        val line1 = range.startLine
 | 
				
			||||||
@@ -164,8 +164,8 @@ internal data class GlobalCommand(val ranges: Ranges, val argument: String, val
 | 
				
			|||||||
      val searchcol = 0
 | 
					      val searchcol = 0
 | 
				
			||||||
      if (globalBusy) {
 | 
					      if (globalBusy) {
 | 
				
			||||||
        val offset = editor.currentCaret().offset
 | 
					        val offset = editor.currentCaret().offset
 | 
				
			||||||
        val lineStartOffset = editor.getLineStartForOffset(offset)
 | 
					        val lineStartOffset = editor.getLineStartForOffset(offset.point)
 | 
				
			||||||
        match = sp.vim_regexec_multi(regmatch, editor, lcount, editor.currentCaret().getLine(), searchcol)
 | 
					        match = sp.vim_regexec_multi(regmatch, editor, lcount, editor.currentCaret().getLine().line, searchcol)
 | 
				
			||||||
        if ((!invert && match > 0) || (invert && match <= 0)) {
 | 
					        if ((!invert && match > 0) || (invert && match <= 0)) {
 | 
				
			||||||
          globalExecuteOne(editor, context, lineStartOffset, cmd.toString())
 | 
					          globalExecuteOne(editor, context, lineStartOffset, cmd.toString())
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -136,7 +136,11 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  <!--  IdeaVim extensions-->
 | 
					  <!--  IdeaVim extensions-->
 | 
				
			||||||
  <extensions defaultExtensionNs="com.intellij">
 | 
					  <extensions defaultExtensionNs="com.intellij">
 | 
				
			||||||
    <applicationService serviceImplementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$NerdDispatcher"/>
 | 
					    <projectService serviceImplementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$NerdDispatcher"/>
 | 
				
			||||||
    <applicationInitializedListener implementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTreeApplicationListener"/>
 | 
					    <postStartupActivity implementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$NerdStartupActivity"/>
 | 
				
			||||||
  </extensions>
 | 
					  </extensions>
 | 
				
			||||||
 | 
					  <projectListeners>
 | 
				
			||||||
 | 
					    <listener class="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$ProjectViewListener"
 | 
				
			||||||
 | 
					              topic="com.intellij.openapi.wm.ex.ToolWindowManagerListener"/>
 | 
				
			||||||
 | 
					  </projectListeners>
 | 
				
			||||||
</idea-plugin>
 | 
					</idea-plugin>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,6 @@ 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.plugins.ideavim.SkipNeovimReason
 | 
					import org.jetbrains.plugins.ideavim.SkipNeovimReason
 | 
				
			||||||
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 | 
					import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 | 
				
			||||||
import org.jetbrains.plugins.ideavim.VimBehaviorDiffers
 | 
					 | 
				
			||||||
import org.jetbrains.plugins.ideavim.VimTestCase
 | 
					import org.jetbrains.plugins.ideavim.VimTestCase
 | 
				
			||||||
import org.junit.jupiter.api.Test
 | 
					import org.junit.jupiter.api.Test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -382,14 +381,10 @@ class MotionActionTest : VimTestCase() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // VIM-1287 |d| |v_i{|
 | 
					  // VIM-1287 |d| |v_i{|
 | 
				
			||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
  @VimBehaviorDiffers(
 | 
					 | 
				
			||||||
    originalVimAfter = "{\"{foo, ${c}bar\", baz}}",
 | 
					 | 
				
			||||||
    description = "We have PSI and can resolve this case correctly. I'm not sure if it should be fixed"
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
  fun testBadlyNestedBlockInsideString() {
 | 
					  fun testBadlyNestedBlockInsideString() {
 | 
				
			||||||
    val before = "{\"{foo, ${c}bar\", baz}}"
 | 
					    val before = "{\"{foo, ${c}bar\", baz}}"
 | 
				
			||||||
    val keys = listOf("di{")
 | 
					    val keys = listOf("di{")
 | 
				
			||||||
    val after = "{}}"
 | 
					    val after = "{\"{foo, ${c}bar\", baz}}"
 | 
				
			||||||
    doTest(keys, before, after, Mode.NORMAL())
 | 
					    doTest(keys, before, after, Mode.NORMAL())
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -411,14 +406,6 @@ class MotionActionTest : VimTestCase() {
 | 
				
			|||||||
    doTest(keys, before, after, Mode.INSERT)
 | 
					    doTest(keys, before, after, Mode.INSERT)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Test
 | 
					 | 
				
			||||||
  fun testDeletingInnerBlockWhenItIsPresentInString() {
 | 
					 | 
				
			||||||
    val before = "let variable = ('abc' .. \"br${c}aces ( with content )\")"
 | 
					 | 
				
			||||||
    val keys = listOf("di(")
 | 
					 | 
				
			||||||
    val after = "let variable = ()"
 | 
					 | 
				
			||||||
    doTest(keys, before, after, Mode.NORMAL())
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // VIM-1008 |c| |v_i{|
 | 
					  // VIM-1008 |c| |v_i{|
 | 
				
			||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
  fun testDeleteInsideSingleQuotesSurroundedBlock() {
 | 
					  fun testDeleteInsideSingleQuotesSurroundedBlock() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,10 +42,10 @@ class YankMotionActionTest : VimTestCase() {
 | 
				
			|||||||
    """.trimIndent()
 | 
					    """.trimIndent()
 | 
				
			||||||
    configureByText(file)
 | 
					    configureByText(file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val initialOffset = fixture.editor.caretModel
 | 
					    val initialOffset = fixture.editor.caretModel.offset
 | 
				
			||||||
    typeText("yy")
 | 
					    typeText("yy")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    kotlin.test.assertEquals(initialOffset, fixture.editor.caretModel)
 | 
					    kotlin.test.assertEquals(initialOffset, fixture.editor.caretModel.offset)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Suppress("DANGEROUS_CHARACTERS")
 | 
					  @Suppress("DANGEROUS_CHARACTERS")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,7 @@ class SelectKeyHandlerTest : VimTestCase() {
 | 
				
			|||||||
                where it was settled on some sodden sand
 | 
					                where it was settled on some sodden sand
 | 
				
			||||||
                hard by the torrent of a mountain pass.
 | 
					                hard by the torrent of a mountain pass.
 | 
				
			||||||
      """.trimIndent(),
 | 
					      """.trimIndent(),
 | 
				
			||||||
      Mode.INSERT,
 | 
					Mode.INSERT,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -67,7 +67,7 @@ class SelectKeyHandlerTest : VimTestCase() {
 | 
				
			|||||||
                where it was settled on some sodden sand
 | 
					                where it was settled on some sodden sand
 | 
				
			||||||
                hard by the torrent of a mountain pass.
 | 
					                hard by the torrent of a mountain pass.
 | 
				
			||||||
      """.trimIndent(),
 | 
					      """.trimIndent(),
 | 
				
			||||||
      Mode.INSERT,
 | 
					Mode.INSERT,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -92,7 +92,7 @@ class SelectKeyHandlerTest : VimTestCase() {
 | 
				
			|||||||
                where it was settled on some sodden sand
 | 
					                where it was settled on some sodden sand
 | 
				
			||||||
                hard by the torrent of a mountain pass.
 | 
					                hard by the torrent of a mountain pass.
 | 
				
			||||||
      """.trimIndent(),
 | 
					      """.trimIndent(),
 | 
				
			||||||
      Mode.INSERT,
 | 
					Mode.INSERT,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -117,7 +117,7 @@ class SelectKeyHandlerTest : VimTestCase() {
 | 
				
			|||||||
                where it was settled on some sodden sand
 | 
					                where it was settled on some sodden sand
 | 
				
			||||||
                hard by the torrent of a mountain pass.
 | 
					                hard by the torrent of a mountain pass.
 | 
				
			||||||
      """.trimIndent(),
 | 
					      """.trimIndent(),
 | 
				
			||||||
      Mode.INSERT,
 | 
					Mode.INSERT,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -143,7 +143,7 @@ class SelectKeyHandlerTest : VimTestCase() {
 | 
				
			|||||||
                where it was settled on some sodden sand
 | 
					                where it was settled on some sodden sand
 | 
				
			||||||
                hard by the torrent of a mountain pass.
 | 
					                hard by the torrent of a mountain pass.
 | 
				
			||||||
      """.trimIndent(),
 | 
					      """.trimIndent(),
 | 
				
			||||||
      Mode.INSERT,
 | 
					Mode.INSERT,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -169,7 +169,7 @@ class SelectKeyHandlerTest : VimTestCase() {
 | 
				
			|||||||
                where it was settled on some sodden sand
 | 
					                where it was settled on some sodden sand
 | 
				
			||||||
                hard by the torrent of a mountain pass.
 | 
					                hard by the torrent of a mountain pass.
 | 
				
			||||||
      """.trimIndent(),
 | 
					      """.trimIndent(),
 | 
				
			||||||
      Mode.INSERT,
 | 
					Mode.INSERT,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -195,7 +195,7 @@ class SelectKeyHandlerTest : VimTestCase() {
 | 
				
			|||||||
                where it was settled on some sodden sand
 | 
					                where it was settled on some sodden sand
 | 
				
			||||||
                hard by the torrent of a mountain pass.
 | 
					                hard by the torrent of a mountain pass.
 | 
				
			||||||
      """.trimIndent(),
 | 
					      """.trimIndent(),
 | 
				
			||||||
      Mode.INSERT,
 | 
					Mode.INSERT,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -221,7 +221,7 @@ class SelectKeyHandlerTest : VimTestCase() {
 | 
				
			|||||||
                where it was settled on some sodden sand
 | 
					                where it was settled on some sodden sand
 | 
				
			||||||
                hard by the torrent of a mountain pass.
 | 
					                hard by the torrent of a mountain pass.
 | 
				
			||||||
      """.trimIndent(),
 | 
					      """.trimIndent(),
 | 
				
			||||||
      Mode.INSERT,
 | 
					Mode.INSERT,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -247,7 +247,7 @@ class SelectKeyHandlerTest : VimTestCase() {
 | 
				
			|||||||
                where it was settled on some sodden sand
 | 
					                where it was settled on some sodden sand
 | 
				
			||||||
                hard by the torrent of a mountain pass.
 | 
					                hard by the torrent of a mountain pass.
 | 
				
			||||||
      """.trimIndent(),
 | 
					      """.trimIndent(),
 | 
				
			||||||
      Mode.INSERT,
 | 
					Mode.INSERT,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -283,7 +283,7 @@ class SelectKeyHandlerTest : VimTestCase() {
 | 
				
			|||||||
                where it was settled on some sodden sand
 | 
					                where it was settled on some sodden sand
 | 
				
			||||||
                hard by the torrent of a mountain pass.
 | 
					                hard by the torrent of a mountain pass.
 | 
				
			||||||
      """.trimIndent(),
 | 
					      """.trimIndent(),
 | 
				
			||||||
      Mode.INSERT,
 | 
					Mode.INSERT,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -309,7 +309,7 @@ class SelectKeyHandlerTest : VimTestCase() {
 | 
				
			|||||||
                where it was settled on some sodden sand
 | 
					                where it was settled on some sodden sand
 | 
				
			||||||
                hard by the torrent of a mountain pass.
 | 
					                hard by the torrent of a mountain pass.
 | 
				
			||||||
      """.trimIndent(),
 | 
					      """.trimIndent(),
 | 
				
			||||||
      Mode.INSERT,
 | 
					Mode.INSERT,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -98,6 +98,15 @@ class MotionUnmatchedBraceOpenActionTest : VimTestCase() {
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @VimBehaviorDiffers(
 | 
				
			||||||
 | 
					    originalVimAfter = """
 | 
				
			||||||
 | 
					      class Xxx $c{
 | 
				
			||||||
 | 
					        int main() {
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					  """,
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
  fun `test go to next next bracket with great count`() {
 | 
					  fun `test go to next next bracket with great count`() {
 | 
				
			||||||
    doTest(
 | 
					    doTest(
 | 
				
			||||||
@@ -110,9 +119,9 @@ class MotionUnmatchedBraceOpenActionTest : VimTestCase() {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      """.trimIndent(),
 | 
					      """.trimIndent(),
 | 
				
			||||||
      """
 | 
					      """
 | 
				
			||||||
      class Xxx $c{
 | 
					      class Xxx {
 | 
				
			||||||
        int main() {
 | 
					        int main() {
 | 
				
			||||||
          
 | 
					          $c
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      """.trimIndent(),
 | 
					      """.trimIndent(),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,12 +8,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package org.jetbrains.plugins.ideavim.action.motion.updown
 | 
					package org.jetbrains.plugins.ideavim.action.motion.updown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.idea.TestFor
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
					import com.maddyhome.idea.vim.state.mode.Mode
 | 
				
			||||||
import org.jetbrains.plugins.ideavim.SkipNeovimReason
 | 
					import org.jetbrains.plugins.ideavim.SkipNeovimReason
 | 
				
			||||||
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 | 
					import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 | 
				
			||||||
import org.jetbrains.plugins.ideavim.VimTestCase
 | 
					import org.jetbrains.plugins.ideavim.VimTestCase
 | 
				
			||||||
import org.junit.jupiter.api.Disabled
 | 
					 | 
				
			||||||
import org.junit.jupiter.api.Test
 | 
					import org.junit.jupiter.api.Test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -145,7 +143,6 @@ class MotionPercentOrMatchActionTest : VimTestCase() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
  @Disabled("It will work after implementing all of the methods in VimPsiService")
 | 
					 | 
				
			||||||
  fun `test motion outside text`() {
 | 
					  fun `test motion outside text`() {
 | 
				
			||||||
    doTest(
 | 
					    doTest(
 | 
				
			||||||
      "%",
 | 
					      "%",
 | 
				
			||||||
@@ -210,45 +207,41 @@ class MotionPercentOrMatchActionTest : VimTestCase() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
  @TestWithoutNeovim(SkipNeovimReason.BUG_IN_NEOVIM)
 | 
					 | 
				
			||||||
  fun `test motion in text with escape (outer forward)`() {
 | 
					  fun `test motion in text with escape (outer forward)`() {
 | 
				
			||||||
    doTest(
 | 
					    doTest(
 | 
				
			||||||
      "%",
 | 
					      "%",
 | 
				
			||||||
      """ debugPrint$c(\(var)) """,
 | 
					      """ debugPrint$c(\(var)) """,
 | 
				
			||||||
 | 
					      """ debugPrint(\(var)$c) """,
 | 
				
			||||||
 | 
					      Mode.NORMAL(),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Test
 | 
				
			||||||
 | 
					  fun `test motion in text with escape (outer backward)`() {
 | 
				
			||||||
 | 
					    doTest(
 | 
				
			||||||
 | 
					      "%",
 | 
				
			||||||
 | 
					      """ debugPrint(\(var)$c) """,
 | 
				
			||||||
 | 
					      """ debugPrint$c(\(var)) """,
 | 
				
			||||||
 | 
					      Mode.NORMAL(),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Test
 | 
				
			||||||
 | 
					  fun `test motion in text with escape (inner forward)`() {
 | 
				
			||||||
 | 
					    doTest(
 | 
				
			||||||
 | 
					      "%",
 | 
				
			||||||
 | 
					      """ debugPrint(\$c(var)) """,
 | 
				
			||||||
      """ debugPrint(\(var$c)) """,
 | 
					      """ debugPrint(\(var$c)) """,
 | 
				
			||||||
      Mode.NORMAL(),
 | 
					      Mode.NORMAL(),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
  @TestWithoutNeovim(SkipNeovimReason.BUG_IN_NEOVIM)
 | 
					 | 
				
			||||||
  fun `test motion in text with escape (outer backward)`() {
 | 
					 | 
				
			||||||
    doTest(
 | 
					 | 
				
			||||||
      "%",
 | 
					 | 
				
			||||||
      """ debugPrint(\(var)$c) """,
 | 
					 | 
				
			||||||
      """ debugPrint(\(var)$c) """,
 | 
					 | 
				
			||||||
      Mode.NORMAL(),
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Test
 | 
					 | 
				
			||||||
  @TestWithoutNeovim(SkipNeovimReason.BUG_IN_NEOVIM)
 | 
					 | 
				
			||||||
  fun `test motion in text with escape (inner forward)`() {
 | 
					 | 
				
			||||||
    doTest(
 | 
					 | 
				
			||||||
      "%",
 | 
					 | 
				
			||||||
      """ debugPrint(\$c(var)) """,
 | 
					 | 
				
			||||||
      """ debugPrint(\$c(var)) """,
 | 
					 | 
				
			||||||
      Mode.NORMAL(),
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Test
 | 
					 | 
				
			||||||
  @TestWithoutNeovim(SkipNeovimReason.BUG_IN_NEOVIM)
 | 
					 | 
				
			||||||
  fun `test motion in text with escape (inner backward)`() {
 | 
					  fun `test motion in text with escape (inner backward)`() {
 | 
				
			||||||
    doTest(
 | 
					    doTest(
 | 
				
			||||||
      "%",
 | 
					      "%",
 | 
				
			||||||
      """ debugPrint(\$c(var)) """,
 | 
					      """ debugPrint(\$c(var)) """,
 | 
				
			||||||
      """ debugPrint(\$c(var)) """,
 | 
					      """ debugPrint(\(var$c)) """,
 | 
				
			||||||
      Mode.NORMAL(),
 | 
					      Mode.NORMAL(),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -339,28 +332,4 @@ class MotionPercentOrMatchActionTest : VimTestCase() {
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
    assertOffset(10)
 | 
					    assertOffset(10)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Test
 | 
					 | 
				
			||||||
  @TestFor(issues = ["VIM-3294"])
 | 
					 | 
				
			||||||
  fun `test matching with braces inside of string`() {
 | 
					 | 
				
			||||||
    configureByText("""
 | 
					 | 
				
			||||||
$c("("")")
 | 
					 | 
				
			||||||
    """.trimIndent())
 | 
					 | 
				
			||||||
    typeText("%")
 | 
					 | 
				
			||||||
    assertState("""
 | 
					 | 
				
			||||||
("("")"$c)
 | 
					 | 
				
			||||||
    """.trimIndent())
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Test
 | 
					 | 
				
			||||||
  @TestFor(issues = ["VIM-3294"])
 | 
					 | 
				
			||||||
  fun `test matching with braces inside of string 2`() {
 | 
					 | 
				
			||||||
    configureByText("""
 | 
					 | 
				
			||||||
("("")"$c)
 | 
					 | 
				
			||||||
    """.trimIndent())
 | 
					 | 
				
			||||||
    typeText("%")
 | 
					 | 
				
			||||||
    assertState("""
 | 
					 | 
				
			||||||
$c("("")")
 | 
					 | 
				
			||||||
    """.trimIndent())
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ package org.jetbrains.plugins.ideavim.common.editor
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.intellij.openapi.application.runWriteAction
 | 
					import com.intellij.openapi.application.runWriteAction
 | 
				
			||||||
import com.intellij.openapi.command.WriteCommandAction
 | 
					import com.intellij.openapi.command.WriteCommandAction
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.common.offset
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
				
			||||||
import org.jetbrains.plugins.ideavim.SkipNeovimReason
 | 
					import org.jetbrains.plugins.ideavim.SkipNeovimReason
 | 
				
			||||||
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 | 
					import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 | 
				
			||||||
@@ -24,7 +25,7 @@ class VimEditorTest : VimTestCase() {
 | 
				
			|||||||
    val vimEditor = IjVimEditor(fixture.editor)
 | 
					    val vimEditor = IjVimEditor(fixture.editor)
 | 
				
			||||||
    WriteCommandAction.runWriteCommandAction(fixture.project) {
 | 
					    WriteCommandAction.runWriteCommandAction(fixture.project) {
 | 
				
			||||||
      runWriteAction {
 | 
					      runWriteAction {
 | 
				
			||||||
        vimEditor.deleteRange(0, 5)
 | 
					        vimEditor.deleteRange(0.offset, 5.offset)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    assertState("567890")
 | 
					    assertState("567890")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package org.jetbrains.plugins.ideavim.extension.entiretextobj
 | 
					package org.jetbrains.plugins.ideavim.extension.entiretextobj
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.maddyhome.idea.vim.api.injector
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
					import com.maddyhome.idea.vim.state.mode.Mode
 | 
				
			||||||
import org.jetbrains.plugins.ideavim.SkipNeovimReason
 | 
					import org.jetbrains.plugins.ideavim.SkipNeovimReason
 | 
				
			||||||
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 | 
					import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 | 
				
			||||||
@@ -58,14 +57,6 @@ class VimTextObjEntireExtensionTest : VimTestCase() {
 | 
				
			|||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
  fun testYankEntireBuffer() {
 | 
					  fun testYankEntireBuffer() {
 | 
				
			||||||
    doTest("yae", poem, "<caret>$poemNoCaret")
 | 
					    doTest("yae", poem, "<caret>$poemNoCaret")
 | 
				
			||||||
    assertRegisterString(injector.registerGroup.defaultRegister, poemNoCaret)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // |y| |ae|
 | 
					 | 
				
			||||||
  @Test
 | 
					 | 
				
			||||||
  fun testYankEntireBufferWithCustomRegister() {
 | 
					 | 
				
			||||||
    doTest("\"kyae", poem, "<caret>$poemNoCaret")
 | 
					 | 
				
			||||||
    assertRegisterString('k', poemNoCaret)
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // |gU| |ie|
 | 
					  // |gU| |ie|
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -203,36 +203,6 @@ class ReplaceWithRegisterTest : VimTestCase() {
 | 
				
			|||||||
    assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
 | 
					    assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Test
 | 
					 | 
				
			||||||
  fun `with specific register`() {
 | 
					 | 
				
			||||||
    val text = "one ${c}two three four"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    configureByText(text)
 | 
					 | 
				
			||||||
    VimPlugin.getRegister().setKeys('k', injector.parser.parseKeys("one"))
 | 
					 | 
				
			||||||
    typeText(injector.parser.parseKeys("\"kgriw"))
 | 
					 | 
				
			||||||
    assertState("one on${c}e three four")
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Test
 | 
					 | 
				
			||||||
  fun `with specific register in visual mode`() {
 | 
					 | 
				
			||||||
    val text = "one ${c}two three four"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    configureByText(text)
 | 
					 | 
				
			||||||
    VimPlugin.getRegister().setKeys('k', injector.parser.parseKeys("one"))
 | 
					 | 
				
			||||||
    typeText(injector.parser.parseKeys("ve\"kgr"))
 | 
					 | 
				
			||||||
    assertState("one on${c}e three four")
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Test
 | 
					 | 
				
			||||||
  fun `with specific register in line mode`() {
 | 
					 | 
				
			||||||
    val text = "one ${c}two three four"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    configureByText(text)
 | 
					 | 
				
			||||||
    VimPlugin.getRegister().setKeys('k', injector.parser.parseKeys("one"))
 | 
					 | 
				
			||||||
    typeText(injector.parser.parseKeys("\"kgrr"))
 | 
					 | 
				
			||||||
    assertState("${c}one\n")
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // --------------------------------------- grr --------------------------
 | 
					  // --------------------------------------- grr --------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -322,7 +322,7 @@ class CaretVisualAttributesHelperTest : VimTestCase() {
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
    injector.actionExecutor.executeAction(
 | 
					    injector.actionExecutor.executeAction(
 | 
				
			||||||
      "EditorCloneCaretBelow",
 | 
					      "EditorCloneCaretBelow",
 | 
				
			||||||
      injector.executionContextManager.getEditorExecutionContext(fixture.editor.vim),
 | 
					      injector.executionContextManager.onEditor(fixture.editor.vim),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    kotlin.test.assertEquals(2, fixture.editor.caretModel.caretCount)
 | 
					    kotlin.test.assertEquals(2, fixture.editor.caretModel.caretCount)
 | 
				
			||||||
    assertCaretVisualAttributes(CaretVisualAttributes.Shape.BLOCK, 0f)
 | 
					    assertCaretVisualAttributes(CaretVisualAttributes.Shape.BLOCK, 0f)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,6 @@ package org.jetbrains.plugins.ideavim.helper
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.maddyhome.idea.vim.api.injector
 | 
					import com.maddyhome.idea.vim.api.injector
 | 
				
			||||||
import com.maddyhome.idea.vim.common.TextRange
 | 
					import com.maddyhome.idea.vim.common.TextRange
 | 
				
			||||||
import com.maddyhome.idea.vim.group.findBlockRange
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.helper.checkInString
 | 
					import com.maddyhome.idea.vim.helper.checkInString
 | 
				
			||||||
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
 | 
				
			||||||
@@ -247,7 +246,8 @@ class SearchHelperTest : VimTestCase() {
 | 
				
			|||||||
  fun findBlockRange(testCase: FindBlockRangeTestCase) {
 | 
					  fun findBlockRange(testCase: FindBlockRangeTestCase) {
 | 
				
			||||||
    val (_, text, type, count, isOuter, expected) = (testCase)
 | 
					    val (_, text, type, count, isOuter, expected) = (testCase)
 | 
				
			||||||
    configureByText(text)
 | 
					    configureByText(text)
 | 
				
			||||||
    val actual = findBlockRange(fixture.editor.vim, fixture.editor.vim.currentCaret(), type, count, isOuter)
 | 
					    val actual =
 | 
				
			||||||
 | 
					      injector.searchHelper.findBlockRange(fixture.editor.vim, fixture.editor.vim.currentCaret(), type, count, isOuter)
 | 
				
			||||||
    kotlin.test.assertEquals(expected, actual)
 | 
					    kotlin.test.assertEquals(expected, actual)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -294,8 +294,8 @@ class SearchHelperTest : VimTestCase() {
 | 
				
			|||||||
        FindBlockRangeTestCase("outer match exclude start paren in string when caret at start of quote", "(${c}\"(aa\")", '(', 1, isOuter = true, expected = TextRange(0, 7)),
 | 
					        FindBlockRangeTestCase("outer match exclude start paren in string when caret at start of quote", "(${c}\"(aa\")", '(', 1, isOuter = true, expected = TextRange(0, 7)),
 | 
				
			||||||
        FindBlockRangeTestCase("inner match exclude start paren in string when caret at end of quote", "(\"(aa${c}\")", '(', 1, isOuter = false, expected = TextRange(1, 6)),
 | 
					        FindBlockRangeTestCase("inner match exclude start paren in string when caret at end of quote", "(\"(aa${c}\")", '(', 1, isOuter = false, expected = TextRange(1, 6)),
 | 
				
			||||||
        FindBlockRangeTestCase("outer match exclude start paren in string when caret at end of quote", "(\"(aa${c}\")", '(', 1, isOuter = true, expected = TextRange(0, 7)),
 | 
					        FindBlockRangeTestCase("outer match exclude start paren in string when caret at end of quote", "(\"(aa${c}\")", '(', 1, isOuter = true, expected = TextRange(0, 7)),
 | 
				
			||||||
        FindBlockRangeTestCase("inner match not exclude start paren in string when caret in between quote", "(\"(a${c}a\")", '(', 1, isOuter = false, expected = TextRange(1, 6)), // Vim behavior differs, but we have some PSI magic and can resolve such cases
 | 
					        FindBlockRangeTestCase("inner match not exclude start paren in string when caret in between quote", "(\"(a${c}a\")", '(', 1, isOuter = false, expected = null),
 | 
				
			||||||
        FindBlockRangeTestCase("outer match not exclude start paren in string when caret in between quote", "(\"(a${c}a\")", '(', 1, isOuter = true, expected = TextRange(0, 7)), // Vim behavior differs, but we have some PSI magic and can resolve such cases
 | 
					        FindBlockRangeTestCase("outer match not exclude start paren in string when caret in between quote", "(\"(a${c}a\")", '(', 1, isOuter = true, expected = null),
 | 
				
			||||||
        FindBlockRangeTestCase("inner match exclude end paren in string when caret at start of quote", "(${c}\"aa)\")", '(', 1, isOuter = false, expected = TextRange(1, 6)),
 | 
					        FindBlockRangeTestCase("inner match exclude end paren in string when caret at start of quote", "(${c}\"aa)\")", '(', 1, isOuter = false, expected = TextRange(1, 6)),
 | 
				
			||||||
        FindBlockRangeTestCase("outer match exclude end paren in string when caret at start of quote", "(${c}\"aa)\")", '(', 1, isOuter = true, expected = TextRange(0, 7)),
 | 
					        FindBlockRangeTestCase("outer match exclude end paren in string when caret at start of quote", "(${c}\"aa)\")", '(', 1, isOuter = true, expected = TextRange(0, 7)),
 | 
				
			||||||
        FindBlockRangeTestCase("inner match exclude end paren in string when caret at end of quote", "(\"aa)${c}\")", '(', 1, isOuter = false, expected = TextRange(1, 6)),
 | 
					        FindBlockRangeTestCase("inner match exclude end paren in string when caret at end of quote", "(\"aa)${c}\")", '(', 1, isOuter = false, expected = TextRange(1, 6)),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -79,7 +79,7 @@ class VimRegexEngineTest : VimTestCase() {
 | 
				
			|||||||
    configureByText("Lor${c}em ${c}Ipsum")
 | 
					    configureByText("Lor${c}em ${c}Ipsum")
 | 
				
			||||||
    val editor = fixture.editor.vim
 | 
					    val editor = fixture.editor.vim
 | 
				
			||||||
    val mark = VimMark.create('m', 0, 0, editor.getPath(), editor.extractProtocol())!!
 | 
					    val mark = VimMark.create('m', 0, 0, editor.getPath(), editor.extractProtocol())!!
 | 
				
			||||||
    val secondCaret = editor.carets().maxByOrNull { it.offset }!!
 | 
					    val secondCaret = editor.carets().maxByOrNull { it.offset.point }!!
 | 
				
			||||||
    secondCaret.markStorage.setMark(mark)
 | 
					    secondCaret.markStorage.setMark(mark)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val result = findAll("\\%>'m\\%#.")
 | 
					    val result = findAll("\\%>'m\\%#.")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -245,9 +245,6 @@ enum class SkipNeovimReason {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  GUARDED_BLOCKS,
 | 
					  GUARDED_BLOCKS,
 | 
				
			||||||
  CTRL_CODES,
 | 
					  CTRL_CODES,
 | 
				
			||||||
 | 
					 | 
				
			||||||
  BUG_IN_NEOVIM,
 | 
					 | 
				
			||||||
  PSI,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun LogicalPosition.toVimCoords(): VimCoords {
 | 
					fun LogicalPosition.toVimCoords(): VimCoords {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,9 +15,7 @@ import com.intellij.openapi.actionSystem.ActionManager
 | 
				
			|||||||
import com.intellij.openapi.actionSystem.ActionPlaces
 | 
					import com.intellij.openapi.actionSystem.ActionPlaces
 | 
				
			||||||
import com.intellij.openapi.actionSystem.AnActionEvent
 | 
					import com.intellij.openapi.actionSystem.AnActionEvent
 | 
				
			||||||
import com.intellij.openapi.actionSystem.DataContext
 | 
					import com.intellij.openapi.actionSystem.DataContext
 | 
				
			||||||
import com.intellij.openapi.actionSystem.PlatformCoreDataKeys
 | 
					 | 
				
			||||||
import com.intellij.openapi.actionSystem.ex.ActionUtil
 | 
					import com.intellij.openapi.actionSystem.ex.ActionUtil
 | 
				
			||||||
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
 | 
					 | 
				
			||||||
import com.intellij.openapi.application.PathManager
 | 
					import com.intellij.openapi.application.PathManager
 | 
				
			||||||
import com.intellij.openapi.application.WriteAction
 | 
					import com.intellij.openapi.application.WriteAction
 | 
				
			||||||
import com.intellij.openapi.editor.CaretVisualAttributes
 | 
					import com.intellij.openapi.editor.CaretVisualAttributes
 | 
				
			||||||
@@ -27,7 +25,6 @@ import com.intellij.openapi.editor.LogicalPosition
 | 
				
			|||||||
import com.intellij.openapi.editor.VisualPosition
 | 
					import com.intellij.openapi.editor.VisualPosition
 | 
				
			||||||
import com.intellij.openapi.editor.colors.EditorColors
 | 
					import com.intellij.openapi.editor.colors.EditorColors
 | 
				
			||||||
import com.intellij.openapi.editor.ex.EditorEx
 | 
					import com.intellij.openapi.editor.ex.EditorEx
 | 
				
			||||||
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.openapi.fileTypes.FileType
 | 
					import com.intellij.openapi.fileTypes.FileType
 | 
				
			||||||
import com.intellij.openapi.fileTypes.PlainTextFileType
 | 
					import com.intellij.openapi.fileTypes.PlainTextFileType
 | 
				
			||||||
@@ -67,6 +64,7 @@ import com.maddyhome.idea.vim.key.ToKeysMappingInfo
 | 
				
			|||||||
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
 | 
					import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
					import com.maddyhome.idea.vim.newapi.IjVimEditor
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
					import com.maddyhome.idea.vim.newapi.globalIjOptions
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.newapi.ij
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.ijOptions
 | 
					import com.maddyhome.idea.vim.newapi.ijOptions
 | 
				
			||||||
import com.maddyhome.idea.vim.newapi.vim
 | 
					import com.maddyhome.idea.vim.newapi.vim
 | 
				
			||||||
import com.maddyhome.idea.vim.options.OptionAccessScope
 | 
					import com.maddyhome.idea.vim.options.OptionAccessScope
 | 
				
			||||||
@@ -453,12 +451,7 @@ abstract class VimTestCase {
 | 
				
			|||||||
    val actual = injector.registerGroup.getRegister(char)?.keys?.let(injector.parser::toKeyNotation)
 | 
					    val actual = injector.registerGroup.getRegister(char)?.keys?.let(injector.parser::toKeyNotation)
 | 
				
			||||||
    assertEquals(expected, actual, "Wrong register contents")
 | 
					    assertEquals(expected, actual, "Wrong register contents")
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
  protected fun assertRegisterString(char: Char, expected: String?) {
 | 
					 | 
				
			||||||
    val actual = injector.registerGroup.getRegister(char)?.keys?.let(injector.parser::toPrintableString)
 | 
					 | 
				
			||||||
    assertEquals(expected, actual, "Wrong register contents")
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  protected fun assertState(modeAfter: Mode) {
 | 
					  protected fun assertState(modeAfter: Mode) {
 | 
				
			||||||
    assertMode(modeAfter)
 | 
					    assertMode(modeAfter)
 | 
				
			||||||
    assertCaretsVisualAttributes()
 | 
					    assertCaretsVisualAttributes()
 | 
				
			||||||
@@ -762,13 +755,9 @@ abstract class VimTestCase {
 | 
				
			|||||||
          val event =
 | 
					          val event =
 | 
				
			||||||
            KeyEvent(editor.component, KeyEvent.KEY_PRESSED, Date().time, key.modifiers, key.keyCode, key.keyChar)
 | 
					            KeyEvent(editor.component, KeyEvent.KEY_PRESSED, Date().time, key.modifiers, key.keyCode, key.keyChar)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          val context = SimpleDataContext.builder()
 | 
					 | 
				
			||||||
            .setParent(EditorUtil.getEditorDataContext(editor))
 | 
					 | 
				
			||||||
            .add(PlatformCoreDataKeys.CONTEXT_COMPONENT, editor.component)
 | 
					 | 
				
			||||||
            .build()
 | 
					 | 
				
			||||||
          val e = AnActionEvent(
 | 
					          val e = AnActionEvent(
 | 
				
			||||||
            event,
 | 
					            event,
 | 
				
			||||||
            context,
 | 
					            injector.executionContextManager.onEditor(editor.vim).ij,
 | 
				
			||||||
            ActionPlaces.KEYBOARD_SHORTCUT,
 | 
					            ActionPlaces.KEYBOARD_SHORTCUT,
 | 
				
			||||||
            VimShortcutKeyAction.instance.templatePresentation.clone(),
 | 
					            VimShortcutKeyAction.instance.templatePresentation.clone(),
 | 
				
			||||||
            ActionManager.getInstance(),
 | 
					            ActionManager.getInstance(),
 | 
				
			||||||
@@ -821,7 +810,7 @@ abstract class VimTestCase {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    fun typeText(keys: List<KeyStroke?>, editor: Editor, project: Project?) {
 | 
					    fun typeText(keys: List<KeyStroke?>, editor: Editor, project: Project?) {
 | 
				
			||||||
      val keyHandler = KeyHandler.getInstance()
 | 
					      val keyHandler = KeyHandler.getInstance()
 | 
				
			||||||
      val dataContext = injector.executionContextManager.getEditorExecutionContext(editor.vim)
 | 
					      val dataContext = injector.executionContextManager.onEditor(editor.vim)
 | 
				
			||||||
      TestInputModel.getInstance(editor).setKeyStrokes(keys.filterNotNull())
 | 
					      TestInputModel.getInstance(editor).setKeyStrokes(keys.filterNotNull())
 | 
				
			||||||
      runWriteCommand(
 | 
					      runWriteCommand(
 | 
				
			||||||
        project,
 | 
					        project,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package org.jetbrains.plugins.ideavim.action.motion.updown
 | 
					package org.jetbrains.plugins.ideavim.action.motion.updown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.intellij.idea.TestFor
 | 
					 | 
				
			||||||
import org.jetbrains.plugins.ideavim.SkipNeovimReason
 | 
					import org.jetbrains.plugins.ideavim.SkipNeovimReason
 | 
				
			||||||
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 | 
					import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 | 
				
			||||||
import org.jetbrains.plugins.ideavim.VimJavaTestCase
 | 
					import org.jetbrains.plugins.ideavim.VimJavaTestCase
 | 
				
			||||||
@@ -67,98 +66,4 @@ class MotionPercentOrMatchActionJavaTest : VimJavaTestCase() {
 | 
				
			|||||||
    typeText("%")
 | 
					    typeText("%")
 | 
				
			||||||
    assertState("/* foo $c */")
 | 
					    assertState("/* foo $c */")
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Test
 | 
					 | 
				
			||||||
  @TestFor(issues = ["VIM-1399"])
 | 
					 | 
				
			||||||
  @TestWithoutNeovim(SkipNeovimReason.PSI)
 | 
					 | 
				
			||||||
  fun `test percent ignores brace inside comment`() {
 | 
					 | 
				
			||||||
    configureByJavaText("""
 | 
					 | 
				
			||||||
      protected TokenStream normalize(String fieldName, TokenStream in) {
 | 
					 | 
				
			||||||
      TokenStream result = new EmptyTokenFilter(in); /* $c{
 | 
					 | 
				
			||||||
              * some text
 | 
					 | 
				
			||||||
              */
 | 
					 | 
				
			||||||
      result = new LowerCaseFilter(result);
 | 
					 | 
				
			||||||
      return result;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    """.trimIndent())
 | 
					 | 
				
			||||||
    typeText("%")
 | 
					 | 
				
			||||||
    assertState("""
 | 
					 | 
				
			||||||
      protected TokenStream normalize(String fieldName, TokenStream in) {
 | 
					 | 
				
			||||||
      TokenStream result = new EmptyTokenFilter(in); /* $c{
 | 
					 | 
				
			||||||
              * some text
 | 
					 | 
				
			||||||
              */
 | 
					 | 
				
			||||||
      result = new LowerCaseFilter(result);
 | 
					 | 
				
			||||||
      return result;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    """.trimIndent())
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Test
 | 
					 | 
				
			||||||
  @TestFor(issues = ["VIM-1399"])
 | 
					 | 
				
			||||||
  @TestWithoutNeovim(SkipNeovimReason.PSI)
 | 
					 | 
				
			||||||
  fun `test percent doesnt match brace inside comment`() {
 | 
					 | 
				
			||||||
    configureByJavaText("""
 | 
					 | 
				
			||||||
      protected TokenStream normalize(String fieldName, TokenStream in) $c{
 | 
					 | 
				
			||||||
      TokenStream result = new EmptyTokenFilter(in); /* {
 | 
					 | 
				
			||||||
              * some text
 | 
					 | 
				
			||||||
              */
 | 
					 | 
				
			||||||
      result = new LowerCaseFilter(result);
 | 
					 | 
				
			||||||
      return result;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    """.trimIndent())
 | 
					 | 
				
			||||||
    typeText("%")
 | 
					 | 
				
			||||||
    assertState("""
 | 
					 | 
				
			||||||
      protected TokenStream normalize(String fieldName, TokenStream in) {
 | 
					 | 
				
			||||||
      TokenStream result = new EmptyTokenFilter(in); /* {
 | 
					 | 
				
			||||||
              * some text
 | 
					 | 
				
			||||||
              */
 | 
					 | 
				
			||||||
      result = new LowerCaseFilter(result);
 | 
					 | 
				
			||||||
      return result;
 | 
					 | 
				
			||||||
    $c}
 | 
					 | 
				
			||||||
    """.trimIndent())
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Test
 | 
					 | 
				
			||||||
  @TestWithoutNeovim(SkipNeovimReason.PSI)
 | 
					 | 
				
			||||||
  fun `test matching works with a sequence of single-line comments`() {
 | 
					 | 
				
			||||||
    configureByJavaText("""
 | 
					 | 
				
			||||||
      protected TokenStream normalize(String fieldName, TokenStream in) {
 | 
					 | 
				
			||||||
      // $c{
 | 
					 | 
				
			||||||
      // result = new LowerCaseFilter(result);
 | 
					 | 
				
			||||||
      // }
 | 
					 | 
				
			||||||
      return result;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    """.trimIndent())
 | 
					 | 
				
			||||||
    typeText("%")
 | 
					 | 
				
			||||||
    assertState("""
 | 
					 | 
				
			||||||
      protected TokenStream normalize(String fieldName, TokenStream in) {
 | 
					 | 
				
			||||||
      // {
 | 
					 | 
				
			||||||
      // result = new LowerCaseFilter(result);
 | 
					 | 
				
			||||||
      // $c}
 | 
					 | 
				
			||||||
      return result;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    """.trimIndent())
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Test
 | 
					 | 
				
			||||||
  @TestWithoutNeovim(SkipNeovimReason.PSI)
 | 
					 | 
				
			||||||
  fun `test matching doesn't work if a sequence of single-line comments is broken`() {
 | 
					 | 
				
			||||||
    configureByJavaText("""
 | 
					 | 
				
			||||||
      protected TokenStream normalize(String fieldName, TokenStream in) {
 | 
					 | 
				
			||||||
      // $c{
 | 
					 | 
				
			||||||
        result = new LowerCaseFilter(result);
 | 
					 | 
				
			||||||
      // }
 | 
					 | 
				
			||||||
      return result;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    """.trimIndent())
 | 
					 | 
				
			||||||
    typeText("%")
 | 
					 | 
				
			||||||
    assertState("""
 | 
					 | 
				
			||||||
      protected TokenStream normalize(String fieldName, TokenStream in) {
 | 
					 | 
				
			||||||
      // $c{
 | 
					 | 
				
			||||||
        result = new LowerCaseFilter(result);
 | 
					 | 
				
			||||||
      // }
 | 
					 | 
				
			||||||
      return result;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    """.trimIndent())
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -10,9 +10,7 @@ package ui
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.automation.remarks.junit5.Video
 | 
					import com.automation.remarks.junit5.Video
 | 
				
			||||||
import com.intellij.remoterobot.RemoteRobot
 | 
					import com.intellij.remoterobot.RemoteRobot
 | 
				
			||||||
import com.intellij.remoterobot.fixtures.ComponentFixture
 | 
					 | 
				
			||||||
import com.intellij.remoterobot.fixtures.ContainerFixture
 | 
					import com.intellij.remoterobot.fixtures.ContainerFixture
 | 
				
			||||||
import com.intellij.remoterobot.search.locators.byXpath
 | 
					 | 
				
			||||||
import com.intellij.remoterobot.steps.CommonSteps
 | 
					import com.intellij.remoterobot.steps.CommonSteps
 | 
				
			||||||
import com.intellij.remoterobot.stepsProcessing.step
 | 
					import com.intellij.remoterobot.stepsProcessing.step
 | 
				
			||||||
import com.intellij.remoterobot.utils.keyboard
 | 
					import com.intellij.remoterobot.utils.keyboard
 | 
				
			||||||
@@ -122,9 +120,6 @@ class UiTests {
 | 
				
			|||||||
      Thread.sleep(5000)
 | 
					      Thread.sleep(5000)
 | 
				
			||||||
      wrapWithIf(javaEditor)
 | 
					      wrapWithIf(javaEditor)
 | 
				
			||||||
      testTrackActionId(javaEditor)
 | 
					      testTrackActionId(javaEditor)
 | 
				
			||||||
      testActionGenerate(javaEditor)
 | 
					 | 
				
			||||||
      testActionNewElementSamePlace(javaEditor)
 | 
					 | 
				
			||||||
      testActionCopy(javaEditor)
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -243,66 +238,6 @@ class UiTests {
 | 
				
			|||||||
    vimExit()
 | 
					    vimExit()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private fun IdeaFrame.testActionGenerate(editor: Editor) {
 | 
					 | 
				
			||||||
    val label = findAll<ComponentFixture>(byXpath("//div[@class='EngravedLabel']"))
 | 
					 | 
				
			||||||
    assertTrue(label.isEmpty())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    keyboard {
 | 
					 | 
				
			||||||
      enterText(":action Generate")
 | 
					 | 
				
			||||||
      enter()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    waitFor {
 | 
					 | 
				
			||||||
      val generateDialog = findAll<ComponentFixture>(byXpath("//div[@class='EngravedLabel']"))
 | 
					 | 
				
			||||||
      if (generateDialog.size == 1) {
 | 
					 | 
				
			||||||
        return@waitFor generateDialog.single().hasText("Generate")
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      return@waitFor false
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    keyboard { escape() }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private fun IdeaFrame.testActionNewElementSamePlace(editor: Editor) {
 | 
					 | 
				
			||||||
    val label = findAll<ComponentFixture>(byXpath("//div[@class='EngravedLabel']"))
 | 
					 | 
				
			||||||
    assertTrue(label.isEmpty())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    keyboard {
 | 
					 | 
				
			||||||
      enterText(":action NewElementSamePlace")
 | 
					 | 
				
			||||||
      enter()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    waitFor {
 | 
					 | 
				
			||||||
      val generateDialog = findAll<ComponentFixture>(byXpath("//div[@class='EngravedLabel']"))
 | 
					 | 
				
			||||||
      if (generateDialog.size == 1) {
 | 
					 | 
				
			||||||
        return@waitFor generateDialog.single().hasText("New in This Directory")
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      return@waitFor false
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    keyboard { escape() }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private fun IdeaFrame.testActionCopy(editor: Editor) {
 | 
					 | 
				
			||||||
    val label = findAll<ComponentFixture>(byXpath("//div[@class='EngravedLabel']"))
 | 
					 | 
				
			||||||
    assertTrue(label.isEmpty())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    keyboard {
 | 
					 | 
				
			||||||
      enterText(":action CopyReferencePopupGroup")
 | 
					 | 
				
			||||||
      enter()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    waitFor {
 | 
					 | 
				
			||||||
      val generateDialog = findAll<ComponentFixture>(byXpath("//div[@class='EngravedLabel']"))
 | 
					 | 
				
			||||||
      if (generateDialog.size == 1) {
 | 
					 | 
				
			||||||
        return@waitFor generateDialog.single().hasText("Copy Path/Reference…")
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      return@waitFor false
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    keyboard { escape() }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private fun IdeaFrame.createFile(fileName: String, remoteRobot: RemoteRobot) {
 | 
					  private fun IdeaFrame.createFile(fileName: String, remoteRobot: RemoteRobot) {
 | 
				
			||||||
    step("Create $fileName file") {
 | 
					    step("Create $fileName file") {
 | 
				
			||||||
      with(projectViewTree) {
 | 
					      with(projectViewTree) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,11 +48,7 @@ class PyCharmTest {
 | 
				
			|||||||
        findAllText("Python Packages").isNotEmpty() &&
 | 
					        findAllText("Python Packages").isNotEmpty() &&
 | 
				
			||||||
        isSmartMode()
 | 
					        isSmartMode()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    findText("Python Console").click()
 | 
				
			||||||
    // Open tool window by id.
 | 
					 | 
				
			||||||
    // id taken from PythonConsoleToolWindowFactory.ID but it's not resolved in robot by some reason
 | 
					 | 
				
			||||||
    // the last 'x' is just to return some serializable value
 | 
					 | 
				
			||||||
    callJs<String>("com.intellij.openapi.wm.ToolWindowManager.getInstance(component.project).getToolWindow('Python Console').activate(null, true); 'x'", true)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Thread.sleep(10_000)
 | 
					    Thread.sleep(10_000)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,7 +55,7 @@ dependencies {
 | 
				
			|||||||
    compileOnly(project(":annotation-processors"))
 | 
					    compileOnly(project(":annotation-processors"))
 | 
				
			||||||
    compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:$kotlinxSerializationVersion")
 | 
					    compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:$kotlinxSerializationVersion")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1")
 | 
					    testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tasks {
 | 
					tasks {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,7 +59,7 @@ private fun changeCharacter(editor: VimEditor, caret: VimCaret, count: Int, ch:
 | 
				
			|||||||
  val col = caret.getBufferPosition().column
 | 
					  val col = caret.getBufferPosition().column
 | 
				
			||||||
  // TODO: Is this correct? Should we really use only current caret? We have a caret as an argument
 | 
					  // TODO: Is this correct? Should we really use only current caret? We have a caret as an argument
 | 
				
			||||||
  val len = editor.lineLength(editor.currentCaret().getBufferPosition().line)
 | 
					  val len = editor.lineLength(editor.currentCaret().getBufferPosition().line)
 | 
				
			||||||
  val offset = caret.offset
 | 
					  val offset = caret.offset.point
 | 
				
			||||||
  if (len - col < count) {
 | 
					  if (len - col < count) {
 | 
				
			||||||
    return false
 | 
					    return false
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,6 @@ public class ChangeLastGlobalSearchReplaceAction : ChangeEditorActionHandler.Sin
 | 
				
			|||||||
  ): Boolean {
 | 
					  ): Boolean {
 | 
				
			||||||
    val range = LineRange(0, editor.lineCount() - 1)
 | 
					    val range = LineRange(0, editor.lineCount() - 1)
 | 
				
			||||||
    return injector.searchGroup
 | 
					    return injector.searchGroup
 | 
				
			||||||
      .processSubstituteCommand(editor, editor.primaryCaret(), context, range, "s", "//~/&", Script(listOf()))
 | 
					      .processSubstituteCommand(editor, editor.primaryCaret(), range, "s", "//~/&", Script(listOf()))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,7 +33,7 @@ public class ChangeLastSearchReplaceAction : ChangeEditorActionHandler.SingleExe
 | 
				
			|||||||
    for (caret in editor.carets()) {
 | 
					    for (caret in editor.carets()) {
 | 
				
			||||||
      val line = caret.getBufferPosition().line
 | 
					      val line = caret.getBufferPosition().line
 | 
				
			||||||
      if (!injector.searchGroup
 | 
					      if (!injector.searchGroup
 | 
				
			||||||
          .processSubstituteCommand(editor, caret, context, LineRange(line, line), "s", "//~/", Script(listOf()))
 | 
					          .processSubstituteCommand(editor, caret, LineRange(line, line), "s", "//~/", Script(listOf()))
 | 
				
			||||||
      ) {
 | 
					      ) {
 | 
				
			||||||
        result = false
 | 
					        result = false
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,7 +75,7 @@ private fun insertCharacterAroundCursor(editor: VimEditor, caret: VimCaret, dir:
 | 
				
			|||||||
  vp = VimVisualPosition(vp.line + dir, vp.column, false)
 | 
					  vp = VimVisualPosition(vp.line + dir, vp.column, false)
 | 
				
			||||||
  val len = editor.lineLength(editor.visualLineToBufferLine(vp.line))
 | 
					  val len = editor.lineLength(editor.visualLineToBufferLine(vp.line))
 | 
				
			||||||
  if (vp.column < len) {
 | 
					  if (vp.column < len) {
 | 
				
			||||||
    val offset = editor.visualPositionToOffset(VimVisualPosition(vp.line, vp.column, false))
 | 
					    val offset = editor.visualPositionToOffset(VimVisualPosition(vp.line, vp.column, false)).point
 | 
				
			||||||
    val charsSequence = editor.text()
 | 
					    val charsSequence = editor.text()
 | 
				
			||||||
    if (offset < charsSequence.length) {
 | 
					    if (offset < charsSequence.length) {
 | 
				
			||||||
      val ch = charsSequence[offset]
 | 
					      val ch = charsSequence[offset]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,10 +17,11 @@ 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.OperatorArguments
 | 
					import com.maddyhome.idea.vim.command.OperatorArguments
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.common.Offset
 | 
				
			||||||
import com.maddyhome.idea.vim.common.TextRange
 | 
					import com.maddyhome.idea.vim.common.TextRange
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
 | 
					import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.enumSetOf
 | 
					import com.maddyhome.idea.vim.helper.enumSetOf
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
					 | 
				
			||||||
import java.util.*
 | 
					import java.util.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@CommandOrMotion(keys = ["<C-U>"], modes = [Mode.INSERT])
 | 
					@CommandOrMotion(keys = ["<C-U>"], modes = [Mode.INSERT])
 | 
				
			||||||
@@ -56,13 +57,13 @@ private fun insertDeleteInsertedText(
 | 
				
			|||||||
  var deleteTo = caret.vimInsertStart.startOffset
 | 
					  var deleteTo = caret.vimInsertStart.startOffset
 | 
				
			||||||
  val offset = caret.offset
 | 
					  val offset = caret.offset
 | 
				
			||||||
  if (offset == deleteTo) {
 | 
					  if (offset == deleteTo) {
 | 
				
			||||||
    deleteTo = injector.motion.moveCaretToCurrentLineStartSkipLeading(editor, caret)
 | 
					    deleteTo = Offset(injector.motion.moveCaretToCurrentLineStartSkipLeading(editor, caret))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (deleteTo != -1) {
 | 
					  if (deleteTo.point != -1) {
 | 
				
			||||||
    injector.changeGroup.deleteRange(
 | 
					    injector.changeGroup.deleteRange(
 | 
				
			||||||
      editor,
 | 
					      editor,
 | 
				
			||||||
      caret,
 | 
					      caret,
 | 
				
			||||||
      TextRange(deleteTo, offset),
 | 
					      TextRange(deleteTo.point, offset.point),
 | 
				
			||||||
      SelectionType.CHARACTER_WISE,
 | 
					      SelectionType.CHARACTER_WISE,
 | 
				
			||||||
      false,
 | 
					      false,
 | 
				
			||||||
      operatorArguments,
 | 
					      operatorArguments,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,11 +17,11 @@ 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.OperatorArguments
 | 
					import com.maddyhome.idea.vim.command.OperatorArguments
 | 
				
			||||||
 | 
					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.handler.ChangeEditorActionHandler
 | 
					import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
 | 
					import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.enumSetOf
 | 
					import com.maddyhome.idea.vim.helper.enumSetOf
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
					 | 
				
			||||||
import java.util.*
 | 
					import java.util.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@CommandOrMotion(keys = ["<C-W>"], modes = [Mode.INSERT])
 | 
					@CommandOrMotion(keys = ["<C-W>"], modes = [Mode.INSERT])
 | 
				
			||||||
@@ -52,9 +52,9 @@ public class InsertDeletePreviousWordAction : ChangeEditorActionHandler.ForEachC
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
private fun insertDeletePreviousWord(editor: VimEditor, caret: VimCaret, operatorArguments: OperatorArguments): Boolean {
 | 
					private fun insertDeletePreviousWord(editor: VimEditor, caret: VimCaret, operatorArguments: OperatorArguments): Boolean {
 | 
				
			||||||
  val deleteTo: Int = if (caret.getBufferPosition().column == 0) {
 | 
					  val deleteTo: Int = if (caret.getBufferPosition().column == 0) {
 | 
				
			||||||
    caret.offset - 1
 | 
					    caret.offset.point - 1
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    var pointer = caret.offset - 1
 | 
					    var pointer = caret.offset.point - 1
 | 
				
			||||||
    val chars = editor.text()
 | 
					    val chars = editor.text()
 | 
				
			||||||
    while (pointer >= 0 && chars[pointer] == ' ' && chars[pointer] != '\n') {
 | 
					    while (pointer >= 0 && chars[pointer] == ' ' && chars[pointer] != '\n') {
 | 
				
			||||||
      pointer--
 | 
					      pointer--
 | 
				
			||||||
@@ -73,7 +73,7 @@ private fun insertDeletePreviousWord(editor: VimEditor, caret: VimCaret, operato
 | 
				
			|||||||
  if (deleteTo < 0) {
 | 
					  if (deleteTo < 0) {
 | 
				
			||||||
    return false
 | 
					    return false
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  val range = TextRange(deleteTo, caret.offset)
 | 
					  val range = TextRange(deleteTo, caret.offset.point)
 | 
				
			||||||
  injector.changeGroup.deleteRange(editor, caret, range, SelectionType.CHARACTER_WISE, true, operatorArguments)
 | 
					  injector.changeGroup.deleteRange(editor, caret, range, SelectionType.CHARACTER_WISE, true, operatorArguments)
 | 
				
			||||||
  return true
 | 
					  return true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,7 @@ 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.OperatorArguments
 | 
					import com.maddyhome.idea.vim.command.OperatorArguments
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.common.Offset
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
 | 
					import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
					import com.maddyhome.idea.vim.state.mode.Mode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -78,7 +79,11 @@ private fun insertNewLineAbove(editor: VimEditor, context: ExecutionContext) {
 | 
				
			|||||||
  // Check if the "last character on previous line" has a guard
 | 
					  // Check if the "last character on previous line" has a guard
 | 
				
			||||||
  // This is actively used in pycharm notebooks https://youtrack.jetbrains.com/issue/VIM-2495
 | 
					  // This is actively used in pycharm notebooks https://youtrack.jetbrains.com/issue/VIM-2495
 | 
				
			||||||
  val hasGuards = moves.stream().anyMatch { (_, second): Pair<VimCaret?, Int?> ->
 | 
					  val hasGuards = moves.stream().anyMatch { (_, second): Pair<VimCaret?, Int?> ->
 | 
				
			||||||
    editor.document.getOffsetGuard(second!!) != null
 | 
					    editor.document.getOffsetGuard(
 | 
				
			||||||
 | 
					      Offset(
 | 
				
			||||||
 | 
					        second!!,
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    ) != null
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (!hasGuards) {
 | 
					  if (!hasGuards) {
 | 
				
			||||||
    for ((first, second) in moves) {
 | 
					    for ((first, second) in moves) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,12 +15,12 @@ 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.OperatorArguments
 | 
					import com.maddyhome.idea.vim.command.OperatorArguments
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
				
			||||||
import com.maddyhome.idea.vim.ex.ExException
 | 
					import com.maddyhome.idea.vim.ex.ExException
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
 | 
					import com.maddyhome.idea.vim.handler.VimActionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.RWLockLabel
 | 
					import com.maddyhome.idea.vim.helper.RWLockLabel
 | 
				
			||||||
import com.maddyhome.idea.vim.put.PutData
 | 
					import com.maddyhome.idea.vim.put.PutData
 | 
				
			||||||
import com.maddyhome.idea.vim.register.Register
 | 
					import com.maddyhome.idea.vim.register.Register
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.vimscript.model.Script
 | 
					import com.maddyhome.idea.vim.vimscript.model.Script
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@CommandOrMotion(keys = ["<C-R>"], modes = [Mode.INSERT])
 | 
					@CommandOrMotion(keys = ["<C-R>"], modes = [Mode.INSERT])
 | 
				
			||||||
@@ -40,7 +40,7 @@ public class InsertRegisterAction : VimActionHandler.SingleExecution() {
 | 
				
			|||||||
    if (argument?.character == '=') {
 | 
					    if (argument?.character == '=') {
 | 
				
			||||||
      injector.application.invokeLater {
 | 
					      injector.application.invokeLater {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
          val expression = readExpression(editor, context)
 | 
					          val expression = readExpression(editor)
 | 
				
			||||||
          if (expression != null) {
 | 
					          if (expression != null) {
 | 
				
			||||||
            if (expression.isNotEmpty()) {
 | 
					            if (expression.isNotEmpty()) {
 | 
				
			||||||
              val expressionValue =
 | 
					              val expressionValue =
 | 
				
			||||||
@@ -62,8 +62,8 @@ public class InsertRegisterAction : VimActionHandler.SingleExecution() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private fun readExpression(editor: VimEditor, context: ExecutionContext): String? {
 | 
					  private fun readExpression(editor: VimEditor): String? {
 | 
				
			||||||
    return injector.commandLineHelper.inputString(editor, context, "=", null)
 | 
					    return injector.commandLineHelper.inputString(editor, "=", null)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,8 +20,8 @@ import com.maddyhome.idea.vim.command.MotionType
 | 
				
			|||||||
import com.maddyhome.idea.vim.command.OperatorArguments
 | 
					import com.maddyhome.idea.vim.command.OperatorArguments
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.Motion
 | 
					import com.maddyhome.idea.vim.handler.Motion
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.MotionActionHandler
 | 
					import com.maddyhome.idea.vim.handler.MotionActionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.inVisualMode
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.isInsertionAllowed
 | 
					import com.maddyhome.idea.vim.state.mode.isInsertionAllowed
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.inVisualMode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@CommandOrMotion(keys = ["g$", "g<End>"], modes = [Mode.NORMAL, Mode.VISUAL, Mode.OP_PENDING])
 | 
					@CommandOrMotion(keys = ["g$", "g<End>"], modes = [Mode.NORMAL, Mode.VISUAL, Mode.OP_PENDING])
 | 
				
			||||||
public class MotionLastScreenColumnAction : MotionActionHandler.ForEachCaret() {
 | 
					public class MotionLastScreenColumnAction : MotionActionHandler.ForEachCaret() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,7 +34,7 @@ public class MotionShiftLeftAction : ShiftedArrowKeyHandler(true) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) {
 | 
					  override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) {
 | 
				
			||||||
    val caret = editor.currentCaret()
 | 
					    val caret = editor.currentCaret()
 | 
				
			||||||
    val newOffset = injector.motion.findOffsetOfNextWord(editor, caret.offset, -cmd.count, false)
 | 
					    val newOffset = injector.motion.findOffsetOfNextWord(editor, caret.offset.point, -cmd.count, false)
 | 
				
			||||||
    caret.moveToMotion(newOffset)
 | 
					    caret.moveToMotion(newOffset)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,7 @@ public class MotionShiftRightAction : ShiftedArrowKeyHandler(true) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) {
 | 
					  override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) {
 | 
				
			||||||
    val caret = editor.currentCaret()
 | 
					    val caret = editor.currentCaret()
 | 
				
			||||||
    val newOffset = injector.motion.findOffsetOfNextWord(editor, caret.offset, cmd.count, false)
 | 
					    val newOffset = injector.motion.findOffsetOfNextWord(editor, caret.offset.point, cmd.count, false)
 | 
				
			||||||
    if (newOffset is Motion.AbsoluteOffset) {
 | 
					    if (newOffset is Motion.AbsoluteOffset) {
 | 
				
			||||||
      caret.moveToOffset(newOffset.offset)
 | 
					      caret.moveToOffset(newOffset.offset)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,10 +13,10 @@ import com.intellij.vim.annotations.Mode
 | 
				
			|||||||
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.injector
 | 
				
			||||||
import com.maddyhome.idea.vim.command.CommandFlags
 | 
					import com.maddyhome.idea.vim.command.CommandFlags
 | 
				
			||||||
import com.maddyhome.idea.vim.command.TextObjectVisualType
 | 
					import com.maddyhome.idea.vim.command.TextObjectVisualType
 | 
				
			||||||
import com.maddyhome.idea.vim.common.TextRange
 | 
					import com.maddyhome.idea.vim.common.TextRange
 | 
				
			||||||
import com.maddyhome.idea.vim.group.findBlockRange
 | 
					 | 
				
			||||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
 | 
					import com.maddyhome.idea.vim.handler.TextObjectActionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.helper.enumSetOf
 | 
					import com.maddyhome.idea.vim.helper.enumSetOf
 | 
				
			||||||
import java.util.*
 | 
					import java.util.*
 | 
				
			||||||
@@ -35,7 +35,7 @@ public class MotionInnerBlockAngleAction : TextObjectActionHandler() {
 | 
				
			|||||||
    count: Int,
 | 
					    count: Int,
 | 
				
			||||||
    rawCount: Int,
 | 
					    rawCount: Int,
 | 
				
			||||||
  ): TextRange? {
 | 
					  ): TextRange? {
 | 
				
			||||||
    return findBlockRange(editor, caret, '<', count, false)
 | 
					    return injector.searchHelper.findBlockRange(editor, caret, '<', count, false)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -53,7 +53,7 @@ public class MotionInnerBlockBraceAction : TextObjectActionHandler() {
 | 
				
			|||||||
    count: Int,
 | 
					    count: Int,
 | 
				
			||||||
    rawCount: Int,
 | 
					    rawCount: Int,
 | 
				
			||||||
  ): TextRange? {
 | 
					  ): TextRange? {
 | 
				
			||||||
    return findBlockRange(editor, caret, '{', count, false)
 | 
					    return injector.searchHelper.findBlockRange(editor, caret, '{', count, false)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -71,7 +71,7 @@ public class MotionInnerBlockBracketAction : TextObjectActionHandler() {
 | 
				
			|||||||
    count: Int,
 | 
					    count: Int,
 | 
				
			||||||
    rawCount: Int,
 | 
					    rawCount: Int,
 | 
				
			||||||
  ): TextRange? {
 | 
					  ): TextRange? {
 | 
				
			||||||
    return findBlockRange(editor, caret, '[', count, false)
 | 
					    return injector.searchHelper.findBlockRange(editor, caret, '[', count, false)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -89,7 +89,7 @@ public class MotionInnerBlockParenAction : TextObjectActionHandler() {
 | 
				
			|||||||
    count: Int,
 | 
					    count: Int,
 | 
				
			||||||
    rawCount: Int,
 | 
					    rawCount: Int,
 | 
				
			||||||
  ): TextRange? {
 | 
					  ): TextRange? {
 | 
				
			||||||
    return findBlockRange(editor, caret, '(', count, false)
 | 
					    return injector.searchHelper.findBlockRange(editor, caret, '(', count, false)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -107,7 +107,7 @@ public class MotionOuterBlockAngleAction : TextObjectActionHandler() {
 | 
				
			|||||||
    count: Int,
 | 
					    count: Int,
 | 
				
			||||||
    rawCount: Int,
 | 
					    rawCount: Int,
 | 
				
			||||||
  ): TextRange? {
 | 
					  ): TextRange? {
 | 
				
			||||||
    return findBlockRange(editor, caret, '<', count, true)
 | 
					    return injector.searchHelper.findBlockRange(editor, caret, '<', count, true)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -125,7 +125,7 @@ public class MotionOuterBlockBraceAction : TextObjectActionHandler() {
 | 
				
			|||||||
    count: Int,
 | 
					    count: Int,
 | 
				
			||||||
    rawCount: Int,
 | 
					    rawCount: Int,
 | 
				
			||||||
  ): TextRange? {
 | 
					  ): TextRange? {
 | 
				
			||||||
    return findBlockRange(editor, caret, '{', count, true)
 | 
					    return injector.searchHelper.findBlockRange(editor, caret, '{', count, true)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -143,7 +143,7 @@ public class MotionOuterBlockBracketAction : TextObjectActionHandler() {
 | 
				
			|||||||
    count: Int,
 | 
					    count: Int,
 | 
				
			||||||
    rawCount: Int,
 | 
					    rawCount: Int,
 | 
				
			||||||
  ): TextRange? {
 | 
					  ): TextRange? {
 | 
				
			||||||
    return findBlockRange(editor, caret, '[', count, true)
 | 
					    return injector.searchHelper.findBlockRange(editor, caret, '[', count, true)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -161,6 +161,6 @@ public class MotionOuterBlockParenAction : TextObjectActionHandler() {
 | 
				
			|||||||
    count: Int,
 | 
					    count: Int,
 | 
				
			||||||
    rawCount: Int,
 | 
					    rawCount: Int,
 | 
				
			||||||
  ): TextRange? {
 | 
					  ): TextRange? {
 | 
				
			||||||
    return findBlockRange(editor, caret, '(', count, true)
 | 
					    return injector.searchHelper.findBlockRange(editor, caret, '(', count, true)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,10 +13,11 @@ 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.injector
 | 
					import com.maddyhome.idea.vim.api.injector
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.Mode
 | 
				
			||||||
import com.maddyhome.idea.vim.command.TextObjectVisualType
 | 
					import com.maddyhome.idea.vim.command.TextObjectVisualType
 | 
				
			||||||
import com.maddyhome.idea.vim.common.TextRange
 | 
					import com.maddyhome.idea.vim.common.TextRange
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
 | 
					import com.maddyhome.idea.vim.handler.TextObjectActionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.Mode
 | 
					import com.maddyhome.idea.vim.state.mode.mode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@CommandOrMotion(keys = ["iW"], modes = [com.intellij.vim.annotations.Mode.VISUAL, com.intellij.vim.annotations.Mode.OP_PENDING])
 | 
					@CommandOrMotion(keys = ["iW"], modes = [com.intellij.vim.annotations.Mode.VISUAL, com.intellij.vim.annotations.Mode.OP_PENDING])
 | 
				
			||||||
public class MotionInnerBigWordAction : TextObjectActionHandler() {
 | 
					public class MotionInnerBigWordAction : TextObjectActionHandler() {
 | 
				
			||||||
@@ -92,10 +93,10 @@ private fun getWordRange(
 | 
				
			|||||||
  var dir = 1
 | 
					  var dir = 1
 | 
				
			||||||
  var selection = false
 | 
					  var selection = false
 | 
				
			||||||
  if (editor.mode is Mode.VISUAL) {
 | 
					  if (editor.mode is Mode.VISUAL) {
 | 
				
			||||||
    if (caret.vimSelectionStart > caret.offset) {
 | 
					    if (caret.vimSelectionStart > caret.offset.point) {
 | 
				
			||||||
      dir = -1
 | 
					      dir = -1
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (caret.vimSelectionStart != caret.offset) {
 | 
					    if (caret.vimSelectionStart != caret.offset.point) {
 | 
				
			||||||
      selection = true
 | 
					      selection = true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,7 +39,7 @@ public class SearchEntryFwdAction : MotionActionHandler.ForEachCaret() {
 | 
				
			|||||||
  ): Motion {
 | 
					  ): Motion {
 | 
				
			||||||
    if (argument == null) return Motion.Error
 | 
					    if (argument == null) return Motion.Error
 | 
				
			||||||
    return injector.searchGroup
 | 
					    return injector.searchGroup
 | 
				
			||||||
      .processSearchCommand(editor, argument.string, caret.offset, Direction.FORWARDS).toMotionOrError()
 | 
					      .processSearchCommand(editor, argument.string, caret.offset.point, Direction.FORWARDS).toMotionOrError()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override val motionType: MotionType = MotionType.EXCLUSIVE
 | 
					  override val motionType: MotionType = MotionType.EXCLUSIVE
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,7 +39,7 @@ public class SearchEntryRevAction : MotionActionHandler.ForEachCaret() {
 | 
				
			|||||||
  ): Motion {
 | 
					  ): Motion {
 | 
				
			||||||
    if (argument == null) return Motion.Error
 | 
					    if (argument == null) return Motion.Error
 | 
				
			||||||
    return injector.searchGroup
 | 
					    return injector.searchGroup
 | 
				
			||||||
      .processSearchCommand(editor, argument.string, caret.offset, Direction.BACKWARDS).toMotionOrError()
 | 
					      .processSearchCommand(editor, argument.string, caret.offset.point, Direction.BACKWARDS).toMotionOrError()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override val motionType: MotionType = MotionType.EXCLUSIVE
 | 
					  override val motionType: MotionType = MotionType.EXCLUSIVE
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,9 +16,9 @@ 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.command.Command
 | 
					import com.maddyhome.idea.vim.command.Command
 | 
				
			||||||
import com.maddyhome.idea.vim.command.OperatorArguments
 | 
					import com.maddyhome.idea.vim.command.OperatorArguments
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
				
			||||||
import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
 | 
					import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
 | 
					import com.maddyhome.idea.vim.handler.VimActionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @author Alex Plate
 | 
					 * @author Alex Plate
 | 
				
			||||||
@@ -36,10 +36,10 @@ public class SelectEnableBlockModeAction : VimActionHandler.SingleExecution() {
 | 
				
			|||||||
    operatorArguments: OperatorArguments,
 | 
					    operatorArguments: OperatorArguments,
 | 
				
			||||||
  ): Boolean {
 | 
					  ): Boolean {
 | 
				
			||||||
    editor.removeSecondaryCarets()
 | 
					    editor.removeSecondaryCarets()
 | 
				
			||||||
    val lineEnd = editor.getLineEndForOffset(editor.primaryCaret().offset)
 | 
					    val lineEnd = editor.getLineEndForOffset(editor.primaryCaret().offset.point)
 | 
				
			||||||
    editor.primaryCaret().run {
 | 
					    editor.primaryCaret().run {
 | 
				
			||||||
      vimSetSystemSelectionSilently(offset, (offset + 1).coerceAtMost(lineEnd))
 | 
					      vimSetSystemSelectionSilently(offset.point, (offset.point + 1).coerceAtMost(lineEnd))
 | 
				
			||||||
      moveToInlayAwareOffset((offset + 1).coerceAtMost(lineEnd))
 | 
					      moveToInlayAwareOffset((offset.point + 1).coerceAtMost(lineEnd))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return injector.visualMotionGroup.enterSelectMode(editor, SelectionType.BLOCK_WISE)
 | 
					    return injector.visualMotionGroup.enterSelectMode(editor, SelectionType.BLOCK_WISE)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,9 +16,9 @@ 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.command.Command
 | 
					import com.maddyhome.idea.vim.command.Command
 | 
				
			||||||
import com.maddyhome.idea.vim.command.OperatorArguments
 | 
					import com.maddyhome.idea.vim.command.OperatorArguments
 | 
				
			||||||
 | 
					import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
				
			||||||
import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
 | 
					import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
 | 
				
			||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
 | 
					import com.maddyhome.idea.vim.handler.VimActionHandler
 | 
				
			||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @author Alex Plate
 | 
					 * @author Alex Plate
 | 
				
			||||||
@@ -35,11 +35,11 @@ public class SelectEnableCharacterModeAction : VimActionHandler.SingleExecution(
 | 
				
			|||||||
    cmd: Command,
 | 
					    cmd: Command,
 | 
				
			||||||
    operatorArguments: OperatorArguments,
 | 
					    operatorArguments: OperatorArguments,
 | 
				
			||||||
  ): Boolean {
 | 
					  ): Boolean {
 | 
				
			||||||
    editor.nativeCarets().sortedByDescending { it.offset }.forEach { caret ->
 | 
					    editor.nativeCarets().sortedByDescending { it.offset.point }.forEach { caret ->
 | 
				
			||||||
      val lineEnd = editor.getLineEndForOffset(caret.offset)
 | 
					      val lineEnd = editor.getLineEndForOffset(caret.offset.point)
 | 
				
			||||||
      caret.run {
 | 
					      caret.run {
 | 
				
			||||||
        vimSetSystemSelectionSilently(offset, (offset + 1).coerceAtMost(lineEnd))
 | 
					        vimSetSystemSelectionSilently(offset.point, (offset.point + 1).coerceAtMost(lineEnd))
 | 
				
			||||||
        moveToInlayAwareOffset((offset + 1).coerceAtMost(lineEnd))
 | 
					        moveToInlayAwareOffset((offset.point + 1).coerceAtMost(lineEnd))
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return injector.visualMotionGroup.enterSelectMode(editor, SelectionType.CHARACTER_WISE)
 | 
					    return injector.visualMotionGroup.enterSelectMode(editor, SelectionType.CHARACTER_WISE)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,8 +37,8 @@ public class SelectEnableLineModeAction : VimActionHandler.SingleExecution() {
 | 
				
			|||||||
    operatorArguments: OperatorArguments,
 | 
					    operatorArguments: OperatorArguments,
 | 
				
			||||||
  ): Boolean {
 | 
					  ): Boolean {
 | 
				
			||||||
    editor.nativeCarets().forEach { caret ->
 | 
					    editor.nativeCarets().forEach { caret ->
 | 
				
			||||||
      val lineEnd = editor.getLineEndForOffset(caret.offset)
 | 
					      val lineEnd = editor.getLineEndForOffset(caret.offset.point)
 | 
				
			||||||
      val lineStart = editor.getLineStartForOffset(caret.offset)
 | 
					      val lineStart = editor.getLineStartForOffset(caret.offset.point)
 | 
				
			||||||
      caret.vimSetSystemSelectionSilently(lineStart, lineEnd)
 | 
					      caret.vimSetSystemSelectionSilently(lineStart, lineEnd)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return injector.visualMotionGroup.enterSelectMode(editor, SelectionType.LINE_WISE)
 | 
					    return injector.visualMotionGroup.enterSelectMode(editor, SelectionType.LINE_WISE)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,8 +48,8 @@ public class SelectToggleVisualMode : VimActionHandler.SingleExecution() {
 | 
				
			|||||||
        editor.setSelectMode(myMode.selectionType)
 | 
					        editor.setSelectMode(myMode.selectionType)
 | 
				
			||||||
        if (myMode.selectionType != SelectionType.LINE_WISE) {
 | 
					        if (myMode.selectionType != SelectionType.LINE_WISE) {
 | 
				
			||||||
          editor.nativeCarets().forEach {
 | 
					          editor.nativeCarets().forEach {
 | 
				
			||||||
            if (it.offset + injector.visualMotionGroup.selectionAdj == it.selectionEnd) {
 | 
					            if (it.offset.point + injector.visualMotionGroup.selectionAdj == it.selectionEnd) {
 | 
				
			||||||
              it.moveToInlayAwareOffset(it.offset + injector.visualMotionGroup.selectionAdj)
 | 
					              it.moveToInlayAwareOffset(it.offset.point + injector.visualMotionGroup.selectionAdj)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -57,8 +57,8 @@ public class SelectToggleVisualMode : VimActionHandler.SingleExecution() {
 | 
				
			|||||||
        editor.pushVisualMode(myMode.selectionType)
 | 
					        editor.pushVisualMode(myMode.selectionType)
 | 
				
			||||||
        if (myMode.selectionType != SelectionType.LINE_WISE) {
 | 
					        if (myMode.selectionType != SelectionType.LINE_WISE) {
 | 
				
			||||||
          editor.nativeCarets().forEach {
 | 
					          editor.nativeCarets().forEach {
 | 
				
			||||||
            if (it.offset == it.selectionEnd && it.visualLineStart <= it.offset - injector.visualMotionGroup.selectionAdj) {
 | 
					            if (it.offset.point == it.selectionEnd && it.visualLineStart <= it.offset.point - injector.visualMotionGroup.selectionAdj) {
 | 
				
			||||||
              it.moveToInlayAwareOffset(it.offset - injector.visualMotionGroup.selectionAdj)
 | 
					              it.moveToInlayAwareOffset(it.offset.point - injector.visualMotionGroup.selectionAdj)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,7 +48,7 @@ public class SelectMotionLeftAction : MotionActionHandler.ForEachCaret() {
 | 
				
			|||||||
      if (editor.isTemplateActive()) {
 | 
					      if (editor.isTemplateActive()) {
 | 
				
			||||||
        logger.debug("Template is active. Activate insert mode")
 | 
					        logger.debug("Template is active. Activate insert mode")
 | 
				
			||||||
        injector.changeGroup.insertBeforeCursor(editor, context)
 | 
					        injector.changeGroup.insertBeforeCursor(editor, context)
 | 
				
			||||||
        if (caret.offset in startSelection..endSelection) {
 | 
					        if (caret.offset.point in startSelection..endSelection) {
 | 
				
			||||||
          return startSelection.toMotion()
 | 
					          return startSelection.toMotion()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,11 +48,11 @@ public class SelectMotionRightAction : MotionActionHandler.ForEachCaret() {
 | 
				
			|||||||
      if (editor.isTemplateActive()) {
 | 
					      if (editor.isTemplateActive()) {
 | 
				
			||||||
        logger.debug("Template is active. Activate insert mode")
 | 
					        logger.debug("Template is active. Activate insert mode")
 | 
				
			||||||
        injector.changeGroup.insertBeforeCursor(editor, context)
 | 
					        injector.changeGroup.insertBeforeCursor(editor, context)
 | 
				
			||||||
        if (caret.offset in startSelection..endSelection) {
 | 
					        if (caret.offset.point in startSelection..endSelection) {
 | 
				
			||||||
          return endSelection.toMotion()
 | 
					          return endSelection.toMotion()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return caret.offset.toMotion()
 | 
					      return caret.offset.point.toMotion()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return injector.motion.getHorizontalMotion(editor, caret, operatorArguments.count1, false)
 | 
					    return injector.motion.getHorizontalMotion(editor, caret, operatorArguments.count1, false)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,14 +48,14 @@ public sealed class WordEndAction(public val direction: Direction, public val bi
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun moveCaretToNextWordEnd(editor: VimEditor, caret: ImmutableVimCaret, count: Int, bigWord: Boolean): Motion {
 | 
					private fun moveCaretToNextWordEnd(editor: VimEditor, caret: ImmutableVimCaret, count: Int, bigWord: Boolean): Motion {
 | 
				
			||||||
  if (caret.offset == 0 && count < 0 || caret.offset >= editor.fileSize() - 1 && count > 0) {
 | 
					  if (caret.offset.point == 0 && count < 0 || caret.offset.point >= editor.fileSize() - 1 && count > 0) {
 | 
				
			||||||
    return Motion.Error
 | 
					    return Motion.Error
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // If we are doing this move as part of a change command (e.q. cw), we need to count the current end of
 | 
					  // If we are doing this move as part of a change command (e.q. cw), we need to count the current end of
 | 
				
			||||||
  // word if the cursor happens to be on the end of a word already. If this is a normal move, we don't count
 | 
					  // word if the cursor happens to be on the end of a word already. If this is a normal move, we don't count
 | 
				
			||||||
  // the current word.
 | 
					  // the current word.
 | 
				
			||||||
  val pos = injector.searchHelper.findNextWordEnd(editor, caret.offset, count, bigWord, false)
 | 
					  val pos = injector.searchHelper.findNextWordEnd(editor, caret.offset.point, count, bigWord, false)
 | 
				
			||||||
  return if (pos == -1) {
 | 
					  return if (pos == -1) {
 | 
				
			||||||
    if (count < 0) {
 | 
					    if (count < 0) {
 | 
				
			||||||
      AbsoluteOffset(injector.motion.moveCaretToLineStart(editor, 0))
 | 
					      AbsoluteOffset(injector.motion.moveCaretToLineStart(editor, 0))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,7 @@ public class MotionBigWordLeftAction : MotionActionHandler.ForEachCaret() {
 | 
				
			|||||||
    argument: Argument?,
 | 
					    argument: Argument?,
 | 
				
			||||||
    operatorArguments: OperatorArguments,
 | 
					    operatorArguments: OperatorArguments,
 | 
				
			||||||
  ): Motion {
 | 
					  ): Motion {
 | 
				
			||||||
    return injector.motion.findOffsetOfNextWord(editor, caret.offset, -operatorArguments.count1, true)
 | 
					    return injector.motion.findOffsetOfNextWord(editor, caret.offset.point, -operatorArguments.count1, true)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override val motionType: MotionType = MotionType.EXCLUSIVE
 | 
					  override val motionType: MotionType = MotionType.EXCLUSIVE
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,7 @@ public class MotionBigWordRightAction : MotionActionHandler.ForEachCaret() {
 | 
				
			|||||||
    argument: Argument?,
 | 
					    argument: Argument?,
 | 
				
			||||||
    operatorArguments: OperatorArguments,
 | 
					    operatorArguments: OperatorArguments,
 | 
				
			||||||
  ): Motion {
 | 
					  ): Motion {
 | 
				
			||||||
    return injector.motion.findOffsetOfNextWord(editor, caret.offset, operatorArguments.count1, true)
 | 
					    return injector.motion.findOffsetOfNextWord(editor, caret.offset.point, operatorArguments.count1, true)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override val motionType: MotionType = MotionType.EXCLUSIVE
 | 
					  override val motionType: MotionType = MotionType.EXCLUSIVE
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,7 +31,7 @@ public class MotionCamelEndLeftAction : MotionActionHandler.ForEachCaret() {
 | 
				
			|||||||
    argument: Argument?,
 | 
					    argument: Argument?,
 | 
				
			||||||
    operatorArguments: OperatorArguments,
 | 
					    operatorArguments: OperatorArguments,
 | 
				
			||||||
  ): Motion {
 | 
					  ): Motion {
 | 
				
			||||||
    return injector.searchHelper.findPreviousCamelEnd(editor.text(), caret.offset, operatorArguments.count1)
 | 
					    return injector.searchHelper.findPreviousCamelEnd(editor.text(), caret.offset.point, operatorArguments.count1)
 | 
				
			||||||
      ?.toMotionOrError() ?: Motion.Error
 | 
					      ?.toMotionOrError() ?: Motion.Error
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -47,7 +47,7 @@ public class MotionCamelEndRightAction : MotionActionHandler.ForEachCaret() {
 | 
				
			|||||||
    argument: Argument?,
 | 
					    argument: Argument?,
 | 
				
			||||||
    operatorArguments: OperatorArguments,
 | 
					    operatorArguments: OperatorArguments,
 | 
				
			||||||
  ): Motion {
 | 
					  ): Motion {
 | 
				
			||||||
    return injector.searchHelper.findNextCamelEnd(editor.text(), caret.offset + 1, operatorArguments.count1)
 | 
					    return injector.searchHelper.findNextCamelEnd(editor.text(), caret.offset.point + 1, operatorArguments.count1)
 | 
				
			||||||
      ?.toMotionOrError() ?: Motion.Error
 | 
					      ?.toMotionOrError() ?: Motion.Error
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user