1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2024-10-19 13:42:45 +02:00

Compare commits

..

1 Commits

Author SHA1 Message Date
ea703bce69
VIM-3238 Fix recording a macro that replays another macro 2024-01-24 23:02:35 +01:00
289 changed files with 3677 additions and 13492 deletions

View File

@ -1,87 +0,0 @@
name: Run Non Octopus UI Tests
on:
workflow_dispatch:
schedule:
- cron: '0 12 * * *'
jobs:
build-for-ui-test-mac-os:
if: github.repository == 'JetBrains/ideavim'
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Apply Patch
run: |
git apply src/test/java/ui/octopus.patch
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: zulu
java-version: 11
- name: Setup 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
uses: gradle/gradle-build-action@v2.4.2
- name: Build Plugin
run: gradle :buildPlugin
- name: Run Idea
run: |
mkdir -p build/reports
gradle :runIdeForUiTests > build/reports/idea.log &
- name: Wait for Idea started
uses: jtalk/url-health-check-action@v3
with:
url: http://127.0.0.1:8082
max-attempts: 20
retry-delay: 10s
- name: Tests
run: gradle :testUi
- name: Move video
if: always()
run: mv video build/reports
- name: Move sandbox logs
if: always()
run: mv build/idea-sandbox/system/log sandbox-idea-log
- name: Save report
if: always()
uses: actions/upload-artifact@v4
with:
name: ui-test-fails-report-mac
path: |
build/reports
sandbox-idea-log
# build-for-ui-test-linux:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v2
# - name: Setup Java
# uses: actions/setup-java@v2.1.0
# with:
# distribution: zulu
# java-version: 11
# - name: Build Plugin
# run: gradle :buildPlugin
# - name: Run Idea
# run: |
# export DISPLAY=:99.0
# Xvfb -ac :99 -screen 0 1920x1080x16 &
# mkdir -p build/reports
# gradle :runIdeForUiTests #> build/reports/idea.log
# - name: Wait for Idea started
# uses: jtalk/url-health-check-action@1.5
# with:
# url: http://127.0.0.1:8082
# max-attempts: 15
# retry-delay: 30s
# - name: Tests
# run: gradle :testUi
# - name: Save fails report
# if: ${{ failure() }}
# uses: actions/upload-artifact@v2
# with:
# name: ui-test-fails-report-linux
# path: |
# ui-test-example/build/reports

1
.gitignore vendored
View File

@ -23,7 +23,6 @@
# Generated by gradle task "generateGrammarSource" # Generated by gradle task "generateGrammarSource"
src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated
vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
# Generated JSONs for lazy classloading # Generated JSONs for lazy classloading
/vim-engine/src/main/resources/ksp-generated /vim-engine/src/main/resources/ksp-generated
/src/main/resources/ksp-generated /src/main/resources/ksp-generated

View File

@ -15,7 +15,7 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType
import jetbrains.buildServer.configs.kotlin.v2019_2.Project import jetbrains.buildServer.configs.kotlin.v2019_2.Project
object Project : Project({ object Project : Project({
description = "Vim engine for JetBrains IDEs" description = "Vim engine for IDEs based on the IntelliJ platform"
subProjects(Releases, OldTests, GitHub) subProjects(Releases, OldTests, GitHub)

View File

@ -20,8 +20,8 @@ object PublishVimEngine : IdeaVimBuildType({
params { params {
param("env.ORG_GRADLE_PROJECT_engineVersion", "%build.number%") param("env.ORG_GRADLE_PROJECT_engineVersion", "%build.number%")
param("env.ORG_GRADLE_PROJECT_uploadUrl", "https://packages.jetbrains.team/maven/p/ij/intellij-dependencies") param("env.ORG_GRADLE_PROJECT_uploadUrl", "https://packages.jetbrains.team/maven/p/ij/intellij-dependencies")
password("env.ORG_GRADLE_PROJECT_spacePassword", "credentialsJSON:5ea56f8c-efe7-4e1e-83de-0c02bcc39d0b", display = ParameterDisplay.HIDDEN) password("env.ORG_GRADLE_PROJECT_spacePassword", "credentialsJSON:790b4e43-ee83-4184-b81b-678afab60409", display = ParameterDisplay.HIDDEN)
param("env.ORG_GRADLE_PROJECT_spaceUsername", "a121c67e-39ac-40e6-bf82-649855ec27b6") param("env.ORG_GRADLE_PROJECT_spaceUsername", "Aleksei.Plate")
} }
vcs { vcs {

View File

@ -5,7 +5,6 @@ import _Self.Constants.RELEASE_EAP
import _Self.IdeaVimBuildType import _Self.IdeaVimBuildType
import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode
import jetbrains.buildServer.configs.kotlin.v2019_2.DslContext import jetbrains.buildServer.configs.kotlin.v2019_2.DslContext
import jetbrains.buildServer.configs.kotlin.v2019_2.ParameterDisplay
import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script
@ -31,11 +30,6 @@ object ReleaseEap : IdeaVimBuildType({
"credentialsJSON:a8ab8150-e6f8-4eaf-987c-bcd65eac50b5", "credentialsJSON:a8ab8150-e6f8-4eaf-987c-bcd65eac50b5",
label = "Slack Token" label = "Slack Token"
) )
password(
"env.YOUTRACK_TOKEN",
"credentialsJSON:2479995b-7b60-4fbb-b095-f0bafae7f622",
display = ParameterDisplay.HIDDEN
)
} }
vcs { vcs {
@ -67,9 +61,13 @@ object ReleaseEap : IdeaVimBuildType({
tasks = "scripts:addReleaseTag" tasks = "scripts:addReleaseTag"
} }
gradle { gradle {
name = "Publish plugin"
tasks = "publishPlugin" tasks = "publishPlugin"
} }
// Same as the script below. However, jgit can't find ssh keys on TeamCity
// gradle {
// name = "Push changes to the repo"
// tasks = "scripts:pushChanges"
// }
script { script {
name = "Push changes to the repo" name = "Push changes to the repo"
scriptContent = """ scriptContent = """
@ -83,10 +81,6 @@ object ReleaseEap : IdeaVimBuildType({
git push origin %build.number% git push origin %build.number%
""".trimIndent() """.trimIndent()
} }
gradle {
name = "YouTrack post release actions"
tasks = "scripts:eapReleaseActions"
}
} }
features { features {

View File

@ -45,11 +45,7 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
"credentialsJSON:a8ab8150-e6f8-4eaf-987c-bcd65eac50b5", "credentialsJSON:a8ab8150-e6f8-4eaf-987c-bcd65eac50b5",
label = "Slack Token" label = "Slack Token"
) )
password( password("env.ORG_GRADLE_PROJECT_youtrackToken", "credentialsJSON:3cd3e867-282c-451f-b958-bc95d56a8450", display = ParameterDisplay.HIDDEN)
"env.ORG_GRADLE_PROJECT_youtrackToken",
"credentialsJSON:7bc0eb3a-b86a-4ebd-b622-d4ef12d7e1d3",
display = ParameterDisplay.HIDDEN
)
param("env.ORG_GRADLE_PROJECT_releaseType", releaseType) param("env.ORG_GRADLE_PROJECT_releaseType", releaseType)
} }
@ -69,25 +65,9 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
name = "Pull git history" name = "Pull git history"
scriptContent = "git fetch --unshallow" scriptContent = "git fetch --unshallow"
} }
script { gradle {
name = "Reset release branch" name = "Select branch"
scriptContent = """ tasks = "scripts:selectBranch"
if [ "major" = "$releaseType" ] || [ "minor" = "$releaseType" ]
then
echo Resetting the release branch because the release type is $releaseType
git checkout master
latest_eap=${'$'}(git describe --tags --match="[0-9].[0-9]*.[0-9]-eap.[0-9]*" --abbrev=0 HEAD)
echo Latest EAP: ${'$'}latest_eap
commit_of_latest_eap=${'$'}(git rev-list -n 1 ${'$'}latest_eap)
echo Commit of latest EAP: ${'$'}commit_of_latest_eap
git checkout release
git reset --hard ${'$'}commit_of_latest_eap
else
git checkout release
echo Do not reset the release branch because the release type is $releaseType
fi
echo Checked out release branch
""".trimIndent()
} }
gradle { gradle {
name = "Calculate new version" name = "Calculate new version"
@ -109,47 +89,41 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
name = "Add release tag" name = "Add release tag"
tasks = "scripts:addReleaseTag" tasks = "scripts:addReleaseTag"
} }
script { gradle {
name = "Run tests" name = "Reset release branch"
scriptContent = "./gradlew test" tasks = "scripts:resetReleaseBranch"
} }
gradle { gradle {
name = "Publish release" name = "Publish release"
tasks = "publishPlugin" tasks = "publishPlugin"
} }
script { // gradle {
name = "Checkout master branch" // name = "Push changes to the repo"
scriptContent = """ // tasks = "scripts:pushChangesWithReleaseBranch"
echo Checkout master // }
git checkout master
""".trimIndent()
}
gradle {
name = "Update change log in master"
tasks = "scripts:changelogUpdateUnreleased"
}
gradle {
name = "Commit preparation changes in master"
tasks = "scripts:commitChanges"
}
script { script {
name = "Push changes to the repo" name = "Push changes to the repo"
scriptContent = """ scriptContent = """
branch=$(git branch --show-current) branch=$(git branch --show-current)
echo Current branch is ${'$'}branch echo current branch is ${'$'}branch
if [ "master" != "${'$'}branch" ]; if [ "master" != "${'$'}branch" ];
then then
git checkout master git checkout master
fi fi
git push origin --tags
git push origin git push origin
if [ "patch" != $releaseType ];
then
git checkout release git checkout release
echo checkout release branch echo checkout release branch
git branch --set-upstream-to=origin/release release git branch --set-upstream-to=origin/release release
git push --tags
git push origin --force git push origin --force
# Push tag fi
git push origin %build.number%
git checkout ${'$'}branch
""".trimIndent() """.trimIndent()
} }
gradle { gradle {

View File

@ -0,0 +1,20 @@
package patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2019_2.*
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with id = 'ReleaseMinor'
accordingly, and delete the patch script.
*/
changeBuildType(RelativeId("ReleaseMinor")) {
params {
expect {
password("env.ORG_GRADLE_PROJECT_youtrackToken", "credentialsJSON:3cd3e867-282c-451f-b958-bc95d56a8450", display = ParameterDisplay.HIDDEN)
}
update {
password("env.ORG_GRADLE_PROJECT_youtrackToken", "credentialsJSON:7bc0eb3a-b86a-4ebd-b622-d4ef12d7e1d3", display = ParameterDisplay.HIDDEN)
}
}
}

View File

@ -0,0 +1,20 @@
package patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2019_2.*
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with id = 'ReleasePatch'
accordingly, and delete the patch script.
*/
changeBuildType(RelativeId("ReleasePatch")) {
params {
expect {
password("env.ORG_GRADLE_PROJECT_youtrackToken", "credentialsJSON:3cd3e867-282c-451f-b958-bc95d56a8450", display = ParameterDisplay.HIDDEN)
}
update {
password("env.ORG_GRADLE_PROJECT_youtrackToken", "credentialsJSON:7bc0eb3a-b86a-4ebd-b622-d4ef12d7e1d3", display = ParameterDisplay.HIDDEN)
}
}
}

17
.teamcity/patches/projects/_Self.kts vendored Normal file
View File

@ -0,0 +1,17 @@
package patches.projects
import jetbrains.buildServer.configs.kotlin.v2019_2.*
import jetbrains.buildServer.configs.kotlin.v2019_2.Project
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the root project
accordingly, and delete the patch script.
*/
changeProject(DslContext.projectId) {
check(description == "Vim engine for IDEs based on the IntelliJ platform") {
"Unexpected description: '$description'"
}
description = "Vim engine for JetBrains IDEs"
}

View File

@ -30,5 +30,5 @@ node (Plugins -> teamcity-configs -> teamcity-configs:generate),
the 'Debug' option is available in the context menu for the task. the 'Debug' option is available in the context menu for the task.
*/ */
version = "2023.11" version = "2023.05"
project(_Self.Project) project(_Self.Project)

View File

@ -491,10 +491,6 @@ Contributors:
[![icon][github]](https://github.com/Infonautica) [![icon][github]](https://github.com/Infonautica)
   
Leonid Danilov Leonid Danilov
* [![icon][mail]](mailto:emanuel-367@hotmail.com)
[![icon][github]](https://github.com/emanuelgestosa)
 
Emanuel Gestosa
Previous contributors: Previous contributors:

View File

@ -25,14 +25,6 @@ usual beta standards.
## To Be Released ## To Be Released
### Fixes:
* [VIM-3055](https://youtrack.jetbrains.com/issue/VIM-3055) Fix the issue with double deleting after dot
### Merged PRs:
* [725](https://github.com/JetBrains/ideavim/pull/725) by [Emanuel Gestosa](https://github.com/emanuelgestosa): Regex
## 2.8.0, 2024-01-30
### Fixes: ### Fixes:
* [VIM-3130](https://youtrack.jetbrains.com/issue/VIM-3130) Change the build version to 2023.1.2 * [VIM-3130](https://youtrack.jetbrains.com/issue/VIM-3130) Change the build version to 2023.1.2
* [VIM-3168](https://youtrack.jetbrains.com/issue/VIM-3168) Do not switch to block caret after enter if the IdeaVim is disabled * [VIM-3168](https://youtrack.jetbrains.com/issue/VIM-3168) Do not switch to block caret after enter if the IdeaVim is disabled
@ -52,8 +44,6 @@ usual beta standards.
* [VIM-3206](https://youtrack.jetbrains.com/issue/VIM-3206) Disable both copilot suggestion and insert mode on a single escape * [VIM-3206](https://youtrack.jetbrains.com/issue/VIM-3206) Disable both copilot suggestion and insert mode on a single escape
* [VIM-3090](https://youtrack.jetbrains.com/issue/VIM-3090) Cmd line mode saves the visual mode * [VIM-3090](https://youtrack.jetbrains.com/issue/VIM-3090) Cmd line mode saves the visual mode
* [VIM-3085](https://youtrack.jetbrains.com/issue/VIM-3085) Open access to VimTypedActionHandler and VimShortcutKeyAction * [VIM-3085](https://youtrack.jetbrains.com/issue/VIM-3085) Open access to VimTypedActionHandler and VimShortcutKeyAction
* [VIM-3260](https://youtrack.jetbrains.com/issue/VIM-3260) Processing the offsets at the file end
* [VIM-3183](https://youtrack.jetbrains.com/issue/VIM-3183) Execute .ideavimrc on pooled thread
### Merged PRs: ### Merged PRs:
* [763](https://github.com/JetBrains/ideavim/pull/763) by [Sam Ng](https://github.com/samabcde): Fix(VIM-3176) add test for restore selection after pasting in/below s… * [763](https://github.com/JetBrains/ideavim/pull/763) by [Sam Ng](https://github.com/samabcde): Fix(VIM-3176) add test for restore selection after pasting in/below s…

View File

@ -84,8 +84,3 @@ IV) It is not allowed to remove this license from the distribution of the Vim
license for previous Vim releases instead of the license that they came license for previous Vim releases instead of the license that they came
with, at your option. with, at your option.
``` ```
---
File [sneakIcon.png](doc/images/sneakIcon.svg), which is originally an icon of the ideavim-sneak plugin,
is merged icons of IdeaVim plugin and a random sneaker by FreePic from flaticon.com.

View File

@ -8,7 +8,7 @@
plugins { plugins {
kotlin("jvm") kotlin("jvm")
kotlin("plugin.serialization") version "1.9.22" kotlin("plugin.serialization") version "1.8.21"
} }
val kotlinxSerializationVersion: String by project val kotlinxSerializationVersion: String by project
@ -21,7 +21,7 @@ repositories {
} }
dependencies { dependencies {
compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.22-1.0.17") compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.22-1.0.16")
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")

View File

@ -44,7 +44,7 @@ buildscript {
} }
dependencies { dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.21")
classpath("com.github.AlexPl292:mark-down-to-slack:1.1.2") classpath("com.github.AlexPl292:mark-down-to-slack:1.1.2")
classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r") classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
@ -66,11 +66,10 @@ buildscript {
plugins { plugins {
antlr antlr
java java
kotlin("jvm") version "1.9.22" kotlin("jvm") version "1.8.21"
application application
id("java-test-fixtures")
id("org.jetbrains.intellij") version "1.17.0" id("org.jetbrains.intellij") version "1.16.1"
id("org.jetbrains.changelog") version "2.2.0" id("org.jetbrains.changelog") version "2.2.0"
// ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle // ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle
@ -79,7 +78,7 @@ plugins {
id("org.jetbrains.kotlinx.kover") version "0.6.1" id("org.jetbrains.kotlinx.kover") version "0.6.1"
id("com.dorongold.task-tree") version "2.1.1" 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.8.21-1.0.11"
} }
ksp { ksp {
@ -92,8 +91,6 @@ ksp {
afterEvaluate { afterEvaluate {
// tasks.named("kspKotlin").configure { dependsOn("clean") } // tasks.named("kspKotlin").configure { dependsOn("clean") }
tasks.named("kspKotlin").configure { dependsOn("generateGrammarSource") } tasks.named("kspKotlin").configure { dependsOn("generateGrammarSource") }
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
tasks.named("kspTestKotlin").configure { enabled = false } tasks.named("kspTestKotlin").configure { enabled = false }
} }
@ -101,7 +98,6 @@ afterEvaluate {
val javaVersion: String by project val javaVersion: String by project
val kotlinVersion: String by project val kotlinVersion: String by project
val ideaVersion: String by project val ideaVersion: String by project
val ideaType: String by project
val downloadIdeaSources: String by project val downloadIdeaSources: String by project
val instrumentPluginCode: String by project val instrumentPluginCode: String by project
val remoteRobotVersion: String by project val remoteRobotVersion: String by project
@ -119,45 +115,35 @@ repositories {
} }
dependencies { dependencies {
api(project(":vim-engine"))
ksp(project(":annotation-processors"))
implementation(project(":annotation-processors"))
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
compileOnly("org.jetbrains:annotations:24.1.0") compileOnly("org.jetbrains:annotations:24.1.0")
runtimeOnly("org.antlr:antlr4-runtime:$antlrVersion")
antlr("org.antlr:antlr4:$antlrVersion")
// --------- Test dependencies ----------
testImplementation(testFixtures(project(":")))
testApi("com.squareup.okhttp3:okhttp:4.12.0")
// https://mvnrepository.com/artifact/com.ensarsarajcic.neovim.java/neovim-api // https://mvnrepository.com/artifact/com.ensarsarajcic.neovim.java/neovim-api
testImplementation("com.ensarsarajcic.neovim.java:neovim-api:0.2.3") testImplementation("com.ensarsarajcic.neovim.java:neovim-api:0.2.3")
testImplementation("com.ensarsarajcic.neovim.java:core-rpc:0.2.3") testImplementation("com.ensarsarajcic.neovim.java:core-rpc:0.2.3")
testFixturesImplementation("com.ensarsarajcic.neovim.java:neovim-api:0.2.3")
testFixturesImplementation("com.ensarsarajcic.neovim.java:core-rpc:0.2.3")
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-test // https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-test
testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion") testImplementation("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.2.1") testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
testImplementation("com.intellij.remoterobot:remote-robot:$remoteRobotVersion") testImplementation("com.intellij.remoterobot:remote-robot:$remoteRobotVersion")
testImplementation("com.intellij.remoterobot:remote-fixtures:$remoteRobotVersion") testImplementation("com.intellij.remoterobot:remote-fixtures:$remoteRobotVersion")
testImplementation("com.intellij.remoterobot:ide-launcher:$remoteRobotVersion")
testImplementation("com.automation-remarks:video-recorder-junit5:2.0") testImplementation("com.automation-remarks:video-recorder-junit5:2.0")
runtimeOnly("org.antlr:antlr4-runtime:$antlrVersion")
antlr("org.antlr:antlr4:$antlrVersion")
api(project(":vim-engine"))
ksp(project(":annotation-processors"))
implementation(project(":annotation-processors"))
testApi("com.squareup.okhttp3:okhttp:4.12.0")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1") testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.1") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.1")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.1") testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.1")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.1")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.1")
} }
configurations { configurations {
@ -246,9 +232,7 @@ tasks {
compileKotlin { compileKotlin {
kotlinOptions { kotlinOptions {
jvmTarget = javaVersion jvmTarget = javaVersion
// See https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#kotlin-standard-library apiVersion = "1.6"
// For the list of bundled versions
apiVersion = "1.9"
freeCompilerArgs = listOf("-Xjvm-default=all-compatibility") freeCompilerArgs = listOf("-Xjvm-default=all-compatibility")
// allWarningsAsErrors = true // allWarningsAsErrors = true
} }
@ -256,7 +240,7 @@ tasks {
compileTestKotlin { compileTestKotlin {
kotlinOptions { kotlinOptions {
jvmTarget = javaVersion jvmTarget = javaVersion
apiVersion = "1.9" apiVersion = "1.6"
// allWarningsAsErrors = true // allWarningsAsErrors = true
} }
} }
@ -293,7 +277,8 @@ intellij {
downloadSources.set(downloadIdeaSources.toBoolean()) downloadSources.set(downloadIdeaSources.toBoolean())
instrumentCode.set(instrumentPluginCode.toBoolean()) instrumentCode.set(instrumentPluginCode.toBoolean())
intellijRepository.set("https://www.jetbrains.com/intellij-repository") intellijRepository.set("https://www.jetbrains.com/intellij-repository")
plugins.set(listOf("AceJump:3.8.11")) // Yaml is only used for testing. It's part of the IdeaIC distribution, but needs to be included as a reference
plugins.set(listOf("java", "AceJump:3.8.11", "yaml"/*, "Pythonid:231.8109.2", "com.intellij.clion-swift:231.8109.4"*/))
} }
tasks { tasks {
@ -338,9 +323,6 @@ tasks {
named("compileTestKotlin") { named("compileTestKotlin") {
dependsOn("generateTestGrammarSource") dependsOn("generateTestGrammarSource")
} }
named("compileTestFixturesKotlin") {
dependsOn("generateTestFixturesGrammarSource")
}
// Add plugin open API sources to the plugin ZIP // Add plugin open API sources to the plugin ZIP
val createOpenApiSourceJar by registering(Jar::class) { val createOpenApiSourceJar by registering(Jar::class) {
@ -447,7 +429,6 @@ kover {
tasks.register("slackNotification") { tasks.register("slackNotification") {
doLast { doLast {
if (version.toString().last() != '0') return@doLast
if (slackUrl.isBlank()) { if (slackUrl.isBlank()) {
println("Slack Url is not defined") println("Slack Url is not defined")
return@doLast return@doLast

View File

@ -45,20 +45,15 @@ All commands with the mappings are supported. See the [full list of supported co
<details> <details>
<summary><h2>sneak</h2></summary> <summary><h2>sneak</h2></summary>
<img src="images/sneakIcon.svg" width="80" height="80" alt="icon"/>
By [Mikhail Levchenko](https://github.com/Mishkun)
Original repository with the plugin: https://github.com/Mishkun/ideavim-sneak
Original plugin: [vim-sneak](https://github.com/justinmk/vim-sneak). Original plugin: [vim-sneak](https://github.com/justinmk/vim-sneak).
### Setup: ### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'justinmk/vim-sneak'` - Install [IdeaVim-sneak](https://plugins.jetbrains.com/plugin/15348-ideavim-sneak) plugin.
- Add the following command to `~/.ideavimrc`: `set sneak`
### Instructions ### Instructions
* Type `s` and two chars to start sneaking in forward direction See the [docs](https://github.com/Mishkun/ideavim-sneak#usage)
* Type `S` and two chars to start sneaking in backward direction
* Type `;` or `,` to proceed with sneaking just as if you were using `f` or `t` commands
</details> </details>

View File

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="386.498 234 32 32" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="ideavim_plugin-a" x1="-6.748%" x2="47.286%" y1="33.61%" y2="85.907%">
<stop offset="0" stop-color="#3BEA62"/>
<stop offset="1" stop-color="#087CFA"/>
</linearGradient>
</defs>
<g transform="matrix(1.238978, 0.90017, -0.90017, 1.238978, 131.776901, -422.953003)" style="">
<path d="M 399.962 247.648 C 399.207 246.894 399.147 246.318 399.692 245.453 C 400.237 244.588 401.955 245.886 401.955 245.886 L 401.955 250.737" style="fill: rgb(248, 245, 231);"/>
<path d="M 413.846 253.602 C 413.846 255.077 411.827 256.134 409.392 256.134 L 396.381 256.134 C 395.211 256.134 394.232 256.003 393.433 255.817 C 391.496 255.367 390.613 254.596 390.613 254.596 L 391.478 252.513 L 393.607 252.617 Z M 413.846 253.602" fill="#cce6f6" style=""/>
<path d="M 413.846 253.602 C 413.846 253.602 411.475 254.468 408.27 254.468 C 405.94 254.468 398.116 253.433 394.023 252.869 C 392.488 252.658 391.478 252.512 391.478 252.512 C 390.864 251.662 392.078 248.741 392.758 247.263 C 393.118 246.484 393.85 245.929 394.703 245.83 C 394.782 245.82 394.861 245.815 394.939 245.815 C 395.369 245.815 395.645 246.532 396.059 247.225 C 396.446 247.877 396.955 248.507 397.823 248.507 C 399.617 248.507 401.955 245.886 401.955 245.886 C 406.544 249.03 410.097 250.43 410.097 250.43 C 410.097 250.43 413.061 250.446 413.782 251.167 C 414.503 251.888 414.422 253.218 413.846 253.602 Z M 413.846 253.602" style="fill: rgb(8, 124, 250);"/>
<path d="M 394.023 252.869 L 393.433 255.817 C 391.496 255.367 390.613 254.596 390.613 254.596 L 391.478 252.513 L 393.607 252.617 Z M 394.023 252.869" fill="#9dcae0" style=""/>
<path d="M 396.059 247.225 C 395.073 245.986 393.193 250.255 394.023 252.869 C 392.488 252.658 391.478 252.513 391.478 252.513 C 390.864 251.662 392.078 248.741 392.758 247.263 C 393.118 246.484 393.85 245.929 394.703 245.83 C 394.782 245.82 394.861 245.815 394.939 245.815 C 395.369 245.815 395.645 246.532 396.059 247.225 Z M 396.059 247.225" style="fill: rgb(14, 112, 142);"/>
<path d="M 403.527 246.924 L 399.174 251.768 C 397.7 250.892 395.578 250.174 394.016 249.717 C 392.843 249.372 391.985 249.174 391.959 249.168 C 392.108 248.769 392.268 248.379 392.421 248.022 L 394.341 248.65 L 394.884 248.827 C 395.509 249.031 396.192 248.95 396.753 248.608 L 397.153 248.363 C 397.35 248.454 397.572 248.507 397.823 248.507 C 399.617 248.507 401.955 245.886 401.955 245.886 C 402.494 246.256 403.02 246.601 403.527 246.924 Z M 403.527 246.924" style="fill: rgb(59, 234, 98);"/>
<path d="M 413.847 253.602 C 413.847 253.602 411.475 254.468 408.27 254.468 C 407.586 254.468 406.426 254.378 405.025 254.238 L 405.025 254.237 C 405.025 253.495 406.924 251.743 408.366 251.616 C 408.623 252.865 410.097 252.512 411.219 252.128 C 412.341 251.743 413.783 251.167 413.783 251.167 C 414.503 251.888 414.422 253.218 413.847 253.602 Z M 413.847 253.602" style="fill: rgb(8, 124, 250);"/>
<path d="M 394.341 248.65 C 394.214 248.978 394.103 249.339 394.016 249.717 C 392.843 249.372 391.985 249.174 391.959 249.168 C 392.108 248.769 392.268 248.379 392.421 248.022 Z M 394.341 248.65" style="fill: rgb(37, 187, 163);"/>
<path d="M 408.366 251.616 C 406.924 251.743 405.025 253.495 405.025 254.237 L 405.025 254.238 C 403.784 254.113 402.355 253.948 400.899 253.77 C 400.899 253.051 400.191 252.372 399.174 251.768 L 399.174 251.768 L 403.528 246.924 C 407.331 249.34 410.097 250.43 410.097 250.43 C 410.77 251.102 409.809 251.487 408.366 251.616 Z M 408.366 251.616" style="fill: rgb(248, 245, 231);"/>
<polygon fill="url(#ideavim_plugin-a)" fill-rule="evenodd" points="406.356 248.463 403.261 252.616 402.183 248.212 400.984 249.899 401.999 254.236 403.927 254.176 407.639 249.062" style="" transform="matrix(0.994522, 0.104529, -0.104529, 0.994522, 28.475005, -40.88594)"/>
<g fill="#fb6572" transform="matrix(0.046265, 0, 0, 0.046265, 390.612823, 245.155533)" style="">
<path d="m288.839844 72.65625c-2.007813 0-4.011719-.761719-5.542969-2.292969-3.058594-3.0625-3.058594-8.023437 0-11.082031l20.089844-20.089844c3.0625-3.058594 8.023437-3.058594 11.082031 0 3.058594 3.0625 3.0625 8.023438 0 11.082032l-20.089844 20.089843c-1.527344 1.53125-3.535156 2.292969-5.539062 2.292969zm0 0" style="fill: rgb(56, 228, 105);"/>
<path d="m314.589844 87.082031c-2.007813 0-4.011719-.765625-5.542969-2.296875-3.0625-3.058594-3.0625-8.019531 0-11.082031l20.089844-20.085937c3.0625-3.058594 8.023437-3.058594 11.082031 0 3.058594 3.0625 3.058594 8.023437 0 11.082031l-20.089844 20.085937c-1.527344 1.53125-3.535156 2.296875-5.539062 2.296875zm0 0" style="fill: rgb(59, 233, 100);"/>
<path d="m340.339844 101.507812c-2.007813 0-4.011719-.765624-5.542969-2.296874-3.058594-3.058594-3.058594-8.023438 0-11.082032l20.089844-20.085937c3.0625-3.0625 8.023437-3.0625 11.082031 0 3.058594 3.058593 3.0625 8.023437 0 11.082031l-20.089844 20.085938c-1.527344 1.53125-3.535156 2.296874-5.539062 2.296874zm0 0" style="fill: rgb(59, 233, 100);"/>
<path d="m366.089844 115.929688c-2.003906 0-4.011719-.761719-5.539063-2.292969-3.0625-3.0625-3.0625-8.023438 0-11.082031l20.085938-20.089844c3.0625-3.058594 8.023437-3.058594 11.082031 0 3.0625 3.0625 3.0625 8.023437 0 11.082031l-20.085938 20.089844c-1.53125 1.53125-3.535156 2.292969-5.542968 2.292969zm0 0" style="fill: rgb(59, 233, 100);"/>
</g>
<path d="M 401.925 247.748 C 401.761 247.748 401.611 247.629 401.573 247.469 C 401.536 247.312 401.611 247.144 401.753 247.067 C 402 246.933 402.318 247.147 402.286 247.426 C 402.265 247.606 402.107 247.748 401.925 247.748 Z M 401.925 247.748" fill="#1e2628" style=""/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -9,8 +9,6 @@
# suppress inspection "UnusedProperty" for whole file # suppress inspection "UnusedProperty" for whole file
ideaVersion=2023.3.2 ideaVersion=2023.3.2
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
ideaType=IC
downloadIdeaSources=true downloadIdeaSources=true
instrumentPluginCode=true instrumentPluginCode=true
version=SNAPSHOT version=SNAPSHOT
@ -21,7 +19,7 @@ antlrVersion=4.10.1
# Please don't forget to update kotlin version in buildscript section # Please don't forget to update kotlin version in buildscript section
# Also update kotlinxSerializationVersion version # Also update kotlinxSerializationVersion version
kotlinVersion=1.9.22 kotlinVersion=1.8.21
publishToken=token publishToken=token
publishChannels=eap publishChannels=eap

View File

@ -93,16 +93,28 @@ tasks.register("addReleaseTag", JavaExec::class) {
args = listOf(project.version.toString(), rootProject.rootDir.toString(), releaseType ?: "") args = listOf(project.version.toString(), rootProject.rootDir.toString(), releaseType ?: "")
} }
tasks.register("selectBranch", JavaExec::class) { tasks.register("resetReleaseBranch", JavaExec::class) {
group = "release" group = "release"
mainClass.set("scripts.release.SelectBranchKt") mainClass.set("scripts.release.ResetReleaseBranchKt")
classpath = sourceSets["main"].runtimeClasspath classpath = sourceSets["main"].runtimeClasspath
args = listOf(project.version.toString(), rootProject.rootDir.toString(), releaseType ?: "") args = listOf(project.version.toString(), rootProject.rootDir.toString(), releaseType ?: "")
} }
tasks.register("eapReleaseActions", JavaExec::class) { tasks.register("pushChanges", JavaExec::class) {
mainClass.set("scripts.PushChangesKt")
classpath = sourceSets["main"].runtimeClasspath
args = listOf(rootProject.rootDir.toString())
}
tasks.register("pushChangesWithReleaseBranch", JavaExec::class) {
mainClass.set("scripts.PushChangesWithReleaseBranchKt")
classpath = sourceSets["main"].runtimeClasspath
args = listOf(rootProject.rootDir.toString(), releaseType ?: "")
}
tasks.register("selectBranch", JavaExec::class) {
group = "release" group = "release"
mainClass.set("scripts.releaseEap.EapReleaseActionsKt") mainClass.set("scripts.release.SelectBranchKt")
classpath = sourceSets["main"].runtimeClasspath classpath = sourceSets["main"].runtimeClasspath
args = listOf(project.version.toString(), rootProject.rootDir.toString(), releaseType ?: "") args = listOf(project.version.toString(), rootProject.rootDir.toString(), releaseType ?: "")
} }

View File

@ -32,7 +32,6 @@ val knownPlugins = listOf(
"com.github.dankinsoid.multicursor", "com.github.dankinsoid.multicursor",
"com.joshestein.ideavim-quickscope", "com.joshestein.ideavim-quickscope",
"ca.alexgirard.HarpoonIJ", "ca.alexgirard.HarpoonIJ",
"com.protoseo.input-source-auto-converter",
// "cc.implicated.intellij.plugins.bunny", // I don't want to include this plugin in the list of IdeaVim plugins as I don't understand what this is for // "cc.implicated.intellij.plugins.bunny", // I don't want to include this plugin in the list of IdeaVim plugins as I don't understand what this is for
) )

View File

@ -32,7 +32,7 @@ fun httpClient(): HttpClient {
install(Auth) { install(Auth) {
bearer { bearer {
loadTokens { loadTokens {
val accessToken = System.getenv("YOUTRACK_TOKEN") ?: error("Missing YOUTRACK_TOKEN") val accessToken = System.getenv("YOUTRACK_TOKEN")!!
BearerTokens(accessToken, "") BearerTokens(accessToken, "")
} }
} }

View File

@ -0,0 +1,37 @@
/*
* 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 scripts.release.checkoutBranch
import scripts.release.withGit
import scripts.release.withRepo
fun main(args: Array<String>) {
val rootDir = args[0]
println("root dir: $rootDir")
val currentBranch = withRepo(rootDir) { it.branch }
println("Current branch is $currentBranch")
withGit(rootDir) { git ->
if (currentBranch != "master") {
git.checkoutBranch("master")
println("Check out master branch")
}
git.push()
.setPushTags()
.call()
println("Master pushed with tags")
git.checkoutBranch(currentBranch)
println("Checked out $currentBranch branch")
}
}

View File

@ -0,0 +1,54 @@
/*
* 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 scripts.release.checkoutBranch
import scripts.release.withGit
import scripts.release.withRepo
fun main(args: Array<String>) {
val rootDir = args[0]
val releaseType = args[1]
println("root dir: $rootDir")
println("releaseType: $releaseType")
val currentBranch = withRepo(rootDir) { it.branch }
println("Current branch is $currentBranch")
withGit(rootDir) { git ->
if (currentBranch != "master") {
git.checkoutBranch("master")
println("Check out master branch")
}
git.push()
.setPushTags()
.call()
println("Master pushed with tags")
if (releaseType != "patch") {
git.checkoutBranch("release")
println("Checked out release")
git
.push()
.setForce(true)
.setPushTags()
.call()
println("Pushed release branch with tags")
}
else {
println("Do not push release branch because type of release is $releaseType")
}
git.checkoutBranch(currentBranch)
println("Checked out $currentBranch branch")
}
}

View File

@ -8,45 +8,28 @@
package scripts.release package scripts.release
import com.vdurmont.semver4j.Semver
import java.time.LocalDate import java.time.LocalDate
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import kotlin.io.path.Path import kotlin.io.path.Path
import kotlin.io.path.readText import kotlin.io.path.readText
import kotlin.io.path.writeText import kotlin.io.path.writeText
private const val toBeReleased = "## To Be Released"
fun main(args: Array<String>) { fun main(args: Array<String>) {
println("Start updating unreleased section") println("Start updating unreleased section")
val (newVersion, rootDir, releaseType) = readArgs(args) val (newVersion, rootDir, releaseType) = readArgs(args)
checkReleaseType(releaseType) checkReleaseType(releaseType)
if (releaseType == "patch") {
println("Skip updating the changelog because release type is 'patch'")
return
}
val currentDate = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE) val currentDate = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE)
val newItem = "## $newVersion, $currentDate" val newItem = "## $newVersion, $currentDate"
val changelogPath = Path("$rootDir/CHANGES.md") val changelogPath = Path("$rootDir/CHANGES.md")
val changelog = changelogPath.readText() val changelog = changelogPath.readText()
val newChangelog = if (releaseType == "patch") { val newChangelog = changelog.replace("## To Be Released", newItem)
val decreasedVersion = Semver(newVersion).withIncPatch(-1)
val firstEntry = changelog.indexOf("## $decreasedVersion")
if (firstEntry != -1) {
val newLog = StringBuilder(changelog)
newLog.insert(firstEntry, newItem + "\n")
newLog.toString()
} else {
changelog
}
} else {
if (toBeReleased in changelog) {
changelog.replace(toBeReleased, newItem)
} else {
val firstEntry = changelog.indexOf("##")
val newLog = StringBuilder(changelog)
newLog.insert(firstEntry, newItem + "\n")
newLog.toString()
}
}
changelogPath.writeText(newChangelog) changelogPath.writeText(newChangelog)
} }

View File

@ -13,8 +13,12 @@ fun main(args: Array<String>) {
checkReleaseType(releaseType) checkReleaseType(releaseType)
if (releaseType == "patch") {
println("Skip committing changes because release type is 'patch'")
return
}
withGit(rootDir) { git -> withGit(rootDir) { git ->
if (git.diff().call().isNotEmpty()) {
git git
.commit() .commit()
.setAll(true) .setAll(true)
@ -25,8 +29,5 @@ fun main(args: Array<String>) {
val lastGitMessage = git.log().call().first().shortMessage val lastGitMessage = git.log().call().first().shortMessage
println("Changes committed. Last gitlog message: $lastGitMessage") println("Changes committed. Last gitlog message: $lastGitMessage")
} else {
println("No Changes")
}
} }
} }

View File

@ -0,0 +1,38 @@
/*
* 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.release
fun main(args: Array<String>) {
val (_, rootDir, releaseType) = readArgs(args)
checkReleaseType(releaseType)
checkBranch(rootDir, releaseType)
if (releaseType == "patch") {
println("Skip release branch reset because release type is 'patch'")
return
}
withGit(rootDir) { git ->
val currentCommit = git.log().setMaxCount(1).call().first()
println("Current commit id: ${currentCommit.id.name}")
git.checkoutBranch("release")
println("Checked out release branch")
git.reset()
.setRef(currentCommit.id.name)
.call()
println("release branch reset")
git.checkoutBranch("master")
println("Checked out master branch")
}
}

View File

@ -15,7 +15,8 @@ fun main(args: Array<String>) {
withGit(rootDir) { git -> withGit(rootDir) { git ->
val branchName = when (releaseType) { val branchName = when (releaseType) {
"major", "minor", "patch" -> "release" "major", "minor" -> "master"
"patch" -> "release"
else -> error("Unsupported release type: $releaseType") else -> error("Unsupported release type: $releaseType")
} }

View File

@ -1,36 +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 scripts.releaseEap
import kotlinx.coroutines.runBlocking
import scripts.addComment
import scripts.getYoutrackTicketsByQuery
import scripts.release.readArgs
import scripts.releasedInEapTagId
import scripts.setTag
fun main(args: Array<String>) {
runBlocking {
val (newVersion, _, _) = readArgs(args)
// Search for Ready to release, but without "IdeaVim Released In EAP" tag
val ticketsToUpdate =
getYoutrackTicketsByQuery("%23%7BReady%20To%20Release%7D%20tag:%20-%7BIdeaVim%20Released%20In%20EAP%7D%20")
println("Have to update the following tickets: $ticketsToUpdate")
ticketsToUpdate.forEach { ticketId ->
setTag(ticketId, releasedInEapTagId)
addComment(
ticketId, """
The fix is available in the IdeaVim $newVersion. See https://jb.gg/ideavim-eap for the instructions on how to get EAP builds as updates within the IDE. You can also wait till the next stable release with this fix, youll get it automatically.
""".trimIndent()
)
}
}
}

View File

@ -11,8 +11,6 @@ package scripts
import io.ktor.client.call.* import io.ktor.client.call.*
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.http.* import io.ktor.http.*
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.addJsonObject import kotlinx.serialization.json.addJsonObject
import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.buildJsonObject
@ -23,10 +21,6 @@ import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonArray import kotlinx.serialization.json.putJsonArray
import kotlinx.serialization.json.putJsonObject import kotlinx.serialization.json.putJsonObject
// YouTrack tag "IdeaVim Released In EAP"
const val releasedInEapTagId = "68-385032"
suspend fun setYoutrackStatus(tickets: Collection<String>, status: String) { suspend fun setYoutrackStatus(tickets: Collection<String>, status: String) {
val client = httpClient() val client = httpClient()
@ -65,59 +59,3 @@ suspend fun setYoutrackStatus(tickets: Collection<String>, status: String) {
} }
} }
} }
fun getYoutrackTicketsByQuery(query: String): Set<String> {
val client = httpClient()
return runBlocking {
val response = client.get("https://youtrack.jetbrains.com/api/issues/?fields=idReadable&query=project:VIM+$query")
response.body<JsonArray>().mapTo(HashSet()) { it.jsonObject.getValue("idReadable").jsonPrimitive.content }
}
}
/**
* 68-385032
* [issueHumanId] is like VIM-123
* [tagId] is like "145-23"
*/
suspend fun setTag(issueHumanId: String, tagId: String) {
val client = httpClient()
println("Try to add tag $tagId to $issueHumanId")
val response =
// I've updated default url in client, so this may be broken now
client.post("https://youtrack.jetbrains.com/api/issues/$issueHumanId/tags?fields=customFields(id,name,value(id,name))") {
contentType(ContentType.Application.Json)
accept(ContentType.Application.Json)
val request = buildJsonObject {
put("id", tagId)
}
setBody(request)
}
println(response)
println(response.body<String>())
if (!response.status.isSuccess()) {
error("Request failed. $issueHumanId, ${response.body<String>()}")
}
}
suspend fun addComment(issueHumanId: String, text: String) {
val client = httpClient()
println("Try to add comment to $issueHumanId")
val response =
// I've updated default url in client, so this may be broken now
client.post("https://youtrack.jetbrains.com/api/issues/$issueHumanId/comments?fields=customFields(id,name,value(id,name))") {
contentType(ContentType.Application.Json)
accept(ContentType.Application.Json)
val request = buildJsonObject {
put("text", text)
}
setBody(request)
}
println(response)
println(response.body<String>())
if (!response.status.isSuccess()) {
error("Request failed. $issueHumanId, ${response.body<String>()}")
}
}

View File

@ -12,5 +12,4 @@ rootProject.name = 'IdeaVIM'
include 'vim-engine' include 'vim-engine'
include 'scripts' include 'scripts'
include 'annotation-processors' include 'annotation-processors'
include 'tests:java-tests'

View File

@ -7,27 +7,56 @@
*/ */
package com.maddyhome.idea.vim package com.maddyhome.idea.vim
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.extensions.ExtensionPointName
import com.maddyhome.idea.vim.action.EngineCommandProvider import com.maddyhome.idea.vim.action.EngineCommandProvider
import com.maddyhome.idea.vim.action.IntellijCommandProvider import com.maddyhome.idea.vim.action.IntellijCommandProvider
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.handler.ActionBeanClass
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
import com.maddyhome.idea.vim.key.MappingOwner import com.maddyhome.idea.vim.key.MappingOwner
import com.maddyhome.idea.vim.newapi.IjVimActionsInitiator
import com.maddyhome.idea.vim.newapi.globalIjOptions
import java.awt.event.KeyEvent import java.awt.event.KeyEvent
import javax.swing.KeyStroke import javax.swing.KeyStroke
public object RegisterActions { public object RegisterActions {
@Deprecated("Please use @CommandOrMotion annotation instead")
internal val VIM_ACTIONS_EP: ExtensionPointName<ActionBeanClass> = ExtensionPointName.create("IdeaVIM.vimAction")
/** /**
* Register all the key/action mappings for the plugin. * Register all the key/action mappings for the plugin.
*/ */
@JvmStatic @JvmStatic
public fun registerActions() { public fun registerActions() {
registerVimCommandActions() registerVimCommandActions()
registerEmptyShortcuts() // todo most likely it is not needed if (!injector.globalIjOptions().commandOrMotionAnnotation) {
registerEmptyShortcuts()
registerEpListener()
}
}
@Deprecated("Moving to annotations approach instead of xml")
private fun registerEpListener() {
// IdeaVim doesn't support contribution to VIM_ACTIONS_EP extension point, so technically we can skip this update,
// but let's support dynamic plugins in a more classic way and reload actions on every EP change.
VIM_ACTIONS_EP.addChangeListener({
unregisterActions()
registerActions()
}, VimPlugin.getInstance())
} }
public fun findAction(id: String): EditorActionHandlerBase? { public fun findAction(id: String): EditorActionHandlerBase? {
if (injector.globalIjOptions().commandOrMotionAnnotation) {
val commandBean = EngineCommandProvider.getCommands().firstOrNull { it.actionId == id } val commandBean = EngineCommandProvider.getCommands().firstOrNull { it.actionId == id }
?: IntellijCommandProvider.getCommands().firstOrNull { it.actionId == id } ?: return null ?: IntellijCommandProvider.getCommands().firstOrNull { it.actionId == id } ?: return null
return commandBean.instance return commandBean.instance
} else {
return VIM_ACTIONS_EP.getExtensionList(ApplicationManager.getApplication()).stream()
.filter { vimActionBean: ActionBeanClass -> vimActionBean.actionId == id }
.findFirst().map { obj: ActionBeanClass -> obj.instance }
.orElse(null)
}
} }
public fun findActionOrDie(id: String): EditorActionHandlerBase { public fun findActionOrDie(id: String): EditorActionHandlerBase {
@ -42,10 +71,24 @@ public object RegisterActions {
private fun registerVimCommandActions() { private fun registerVimCommandActions() {
val parser = VimPlugin.getKey() val parser = VimPlugin.getKey()
if (injector.globalIjOptions().commandOrMotionAnnotation) {
EngineCommandProvider.getCommands().forEach { parser.registerCommandAction(it) } EngineCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) } IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
} else {
VIM_ACTIONS_EP.getExtensionList(ApplicationManager.getApplication()).stream().map { bean: ActionBeanClass? ->
IjVimActionsInitiator(
bean!!
)
}
.forEach { actionHolder: IjVimActionsInitiator? ->
parser.registerCommandAction(
actionHolder!!
)
}
}
} }
// todo do we really need this?
private fun registerEmptyShortcuts() { private fun registerEmptyShortcuts() {
val parser = VimPlugin.getKey() val parser = VimPlugin.getKey()

View File

@ -39,9 +39,11 @@ import com.maddyhome.idea.vim.listener.VimListenerManager;
import com.maddyhome.idea.vim.newapi.IjVimInjector; import com.maddyhome.idea.vim.newapi.IjVimInjector;
import com.maddyhome.idea.vim.ui.StatusBarIconFactory; import com.maddyhome.idea.vim.ui.StatusBarIconFactory;
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel; import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
import com.maddyhome.idea.vim.vimscript.services.OptionService;
import com.maddyhome.idea.vim.vimscript.services.VariableService; import com.maddyhome.idea.vim.vimscript.services.VariableService;
import com.maddyhome.idea.vim.yank.YankGroupBase; import com.maddyhome.idea.vim.yank.YankGroupBase;
import org.jdom.Element; import org.jdom.Element;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -115,6 +117,12 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
return ApplicationManager.getApplication().getService(CommandGroup.class); return ApplicationManager.getApplication().getService(CommandGroup.class);
} }
@Deprecated // "Please use `injector.markService` instead"
@ApiStatus.ScheduledForRemoval(inVersion = "2.3")
public static @NotNull MarkGroup getMark() {
return ApplicationManager.getApplication().getService(MarkGroup.class);
}
public static @NotNull RegisterGroup getRegister() { public static @NotNull RegisterGroup getRegister() {
return ((RegisterGroup)VimInjectorKt.getInjector().getRegisterGroup()); return ((RegisterGroup)VimInjectorKt.getInjector().getRegisterGroup());
} }
@ -187,6 +195,13 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
return VimInjectorKt.getInjector().getOptionGroup(); return VimInjectorKt.getInjector().getOptionGroup();
} }
/** Deprecated: Use getOptionGroup */
@Deprecated
// Used by which-key 0.8.0, IdeaVimExtension 1.6.5 + 1.6.8
public static @NotNull OptionService getOptionService() {
return VimInjectorKt.getInjector().getOptionService();
}
private static @NotNull NotificationService getNotifications() { private static @NotNull NotificationService getNotifications() {
return getNotifications(null); return getNotifications(null);
} }

View File

@ -28,7 +28,6 @@ import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.group.IjOptionConstants import com.maddyhome.idea.vim.group.IjOptionConstants
import com.maddyhome.idea.vim.group.IjOptions import com.maddyhome.idea.vim.group.IjOptions
import com.maddyhome.idea.vim.handler.enableOctopus
import com.maddyhome.idea.vim.handler.isOctopusEnabled import com.maddyhome.idea.vim.handler.isOctopusEnabled
import com.maddyhome.idea.vim.helper.EditorHelper import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.HandlerInjector import com.maddyhome.idea.vim.helper.HandlerInjector
@ -117,14 +116,12 @@ public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible
if (VimPlugin.isNotEnabled()) return ActionEnableStatus.no("IdeaVim is disabled", LogLevel.DEBUG) if (VimPlugin.isNotEnabled()) return ActionEnableStatus.no("IdeaVim is disabled", LogLevel.DEBUG)
val editor = getEditor(e) val editor = getEditor(e)
if (editor != null && keyStroke != null) { if (editor != null && keyStroke != null) {
if (enableOctopus) {
if (isOctopusEnabled(keyStroke, editor)) { if (isOctopusEnabled(keyStroke, editor)) {
return ActionEnableStatus.no( return ActionEnableStatus.no(
"Processing VimShortcutKeyAction for the key that is used in the octopus handler", "Processing VimShortcutKeyAction for the key that is used in the octopus handler",
LogLevel.ERROR LogLevel.ERROR
) )
} }
}
if (editor.isIdeaVimDisabledHere) { if (editor.isIdeaVimDisabledHere) {
return ActionEnableStatus.no("IdeaVim is disabled in this place", LogLevel.INFO) return ActionEnableStatus.no("IdeaVim is disabled in this place", LogLevel.INFO)
} }

View File

@ -8,9 +8,10 @@
package com.maddyhome.idea.vim.action.editor package com.maddyhome.idea.vim.action.editor
import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.vim.annotations.CommandOrMotion import com.intellij.vim.annotations.CommandOrMotion
import com.intellij.vim.annotations.Mode import com.intellij.vim.annotations.Mode
import com.intellij.openapi.actionSystem.IdeActions
import com.maddyhome.idea.vim.action.ComplicatedKeysAction
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
@ -20,32 +21,54 @@ import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.IdeActionHandler import com.maddyhome.idea.vim.handler.IdeActionHandler
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.enumSetOf import com.maddyhome.idea.vim.helper.enumSetOf
import java.awt.event.KeyEvent
import java.util.* import java.util.*
import javax.swing.KeyStroke
@CommandOrMotion(keys = ["<C-H>", "<BS>"], modes = [Mode.INSERT]) @CommandOrMotion(keys = ["<C-H>", "<BS>"], modes = [Mode.INSERT])
internal class VimEditorBackSpace : IdeActionHandler(IdeActions.ACTION_EDITOR_BACKSPACE) { internal class VimEditorBackSpace : IdeActionHandler(IdeActions.ACTION_EDITOR_BACKSPACE), ComplicatedKeysAction {
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_DOWN_MASK)),
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0)),
)
override val type: Command.Type = Command.Type.DELETE override val type: Command.Type = Command.Type.DELETE
} }
@CommandOrMotion(keys = ["<Del>"], modes = [Mode.INSERT]) @CommandOrMotion(keys = ["<Del>"], modes = [Mode.INSERT])
internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELETE) { internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELETE), ComplicatedKeysAction {
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0)),
)
override val type: Command.Type = Command.Type.DELETE override val type: Command.Type = Command.Type.DELETE
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE)
} }
@CommandOrMotion(keys = ["<Down>", "<kDown>"], modes = [Mode.INSERT]) @CommandOrMotion(keys = ["<Down>", "<kDown>"], modes = [Mode.INSERT])
internal class VimEditorDown : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN) { internal class VimEditorDown : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN), ComplicatedKeysAction {
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0)),
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, 0)),
)
override val type: Command.Type = Command.Type.MOTION override val type: Command.Type = Command.Type.MOTION
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES) override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
} }
@CommandOrMotion(keys = ["<Tab>", "<C-I>"], modes = [Mode.INSERT]) @CommandOrMotion(keys = ["<Tab>", "<C-I>"], modes = [Mode.INSERT])
internal class VimEditorTab : IdeActionHandler(IdeActions.ACTION_EDITOR_TAB) { internal class VimEditorTab : IdeActionHandler(IdeActions.ACTION_EDITOR_TAB), ComplicatedKeysAction {
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_I, KeyEvent.CTRL_DOWN_MASK)),
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0)),
)
override val type: Command.Type = Command.Type.INSERT override val type: Command.Type = Command.Type.INSERT
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE) override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE)
} }
@CommandOrMotion(keys = ["<Up>", "<kUp>"], modes = [Mode.INSERT]) @CommandOrMotion(keys = ["<Up>", "<kUp>"], modes = [Mode.INSERT])
internal class VimEditorUp : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_UP) { internal class VimEditorUp : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_UP), ComplicatedKeysAction {
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0)),
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_UP, 0)),
)
override val type: Command.Type = Command.Type.MOTION override val type: Command.Type = Command.Type.MOTION
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES) override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
} }

View File

@ -10,6 +10,7 @@ package com.maddyhome.idea.vim.action.ex
import com.intellij.vim.annotations.CommandOrMotion import com.intellij.vim.annotations.CommandOrMotion
import com.intellij.vim.annotations.Mode import com.intellij.vim.annotations.Mode
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.action.ComplicatedKeysAction
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
@ -17,6 +18,7 @@ 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.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
import java.util.* import java.util.*
import javax.swing.KeyStroke
/** /**
* Called by KeyHandler to process the contents of the ex entry panel * Called by KeyHandler to process the contents of the ex entry panel
@ -24,7 +26,10 @@ import java.util.*
* The mapping for this action means that the ex command is executed as a write action * The mapping for this action means that the ex command is executed as a write action
*/ */
@CommandOrMotion(keys = ["<CR>", "<C-M>", "<C-J>"], modes = [Mode.CMD_LINE]) @CommandOrMotion(keys = ["<CR>", "<C-M>", "<C-J>"], modes = [Mode.CMD_LINE])
public class ProcessExEntryAction : VimActionHandler.SingleExecution() { public class ProcessExEntryAction : VimActionHandler.SingleExecution(), ComplicatedKeysAction {
override val keyStrokesSet: Set<List<KeyStroke>> =
parseKeysSet("<CR>", "<C-M>", 0x0a.toChar().toString(), 0x0d.toChar().toString())
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
override val flags: EnumSet<CommandFlags> = EnumSet.of(CommandFlags.FLAG_COMPLETE_EX) override val flags: EnumSet<CommandFlags> = EnumSet.of(CommandFlags.FLAG_COMPLETE_EX)

View File

@ -53,11 +53,6 @@ internal object VimExtensionRegistrar : VimExtensionRegistrator {
@Synchronized @Synchronized
private fun registerExtension(extensionBean: ExtensionBeanClass) { private fun registerExtension(extensionBean: ExtensionBeanClass) {
val name = extensionBean.name ?: extensionBean.instance.name val name = extensionBean.name ?: extensionBean.instance.name
if (name == "sneak" && extensionBean.name == null) {
// Filter out the old ideavim-sneak extension that used to be a separate plugin
// https://github.com/Mishkun/ideavim-sneak
return
}
if (name in registeredExtensions) return if (name in registeredExtensions) return
registeredExtensions.add(name) registeredExtensions.add(name)

View File

@ -246,7 +246,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
// Note that ignoreCase is not overridden by the `\C` in the pattern // Note that ignoreCase is not overridden by the `\C` in the pattern
val pattern = makePattern(text, whole) val pattern = makePattern(text, whole)
val matches = injector.searchHelper.findAll(IjVimEditor(editor), pattern, 0, -1, false) val matches = SearchHelper.findAll(editor, pattern, 0, -1, false)
for (match in matches) { for (match in matches) {
if (match.contains(primaryCaret.offset)) { if (match.contains(primaryCaret.offset)) {
primaryCaret.vim.moveToOffset(match.startOffset) primaryCaret.vim.moveToOffset(match.startOffset)
@ -322,7 +322,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
searchOptions.add(SearchOptions.WRAP) searchOptions.add(SearchOptions.WRAP)
} }
return injector.searchHelper.findPattern(IjVimEditor(editor), makePattern(text, whole), startOffset, 1, searchOptions)?.startOffset ?: -1 return SearchHelper.findPattern(editor, makePattern(text, whole), startOffset, 1, searchOptions)?.startOffset ?: -1
} }
private fun makePattern(text: String, whole: Boolean): String { private fun makePattern(text: String, whole: Boolean): String {

View File

@ -1,291 +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.extension.sneak
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ScrollType
import com.intellij.openapi.editor.colors.EditorColors
import com.intellij.openapi.editor.markup.EffectType
import com.intellij.openapi.editor.markup.HighlighterLayer
import com.intellij.openapi.editor.markup.HighlighterTargetArea
import com.intellij.openapi.editor.markup.RangeHighlighter
import com.intellij.openapi.editor.markup.TextAttributes
import com.intellij.openapi.util.Disposer
import com.maddyhome.idea.vim.VimProjectService
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.options
import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.extension.ExtensionHandler
import com.maddyhome.idea.vim.extension.VimExtension
import com.maddyhome.idea.vim.extension.VimExtensionFacade
import com.maddyhome.idea.vim.extension.VimExtensionHandler
import com.maddyhome.idea.vim.helper.StrictMode
import com.maddyhome.idea.vim.newapi.ij
import java.awt.Font
import java.awt.event.KeyEvent
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
private const val DEFAULT_HIGHLIGHT_DURATION_SNEAK: Long = 300
// By [Mikhail Levchenko](https://github.com/Mishkun)
// Original repository with the plugin: https://github.com/Mishkun/ideavim-sneak
internal class IdeaVimSneakExtension : VimExtension {
override fun getName(): String = "sneak"
override fun init() {
val highlightHandler = HighlightHandler()
mapToFunctionAndProvideKeys("s", SneakHandler(highlightHandler, Direction.FORWARD))
mapToFunctionAndProvideKeys("S", SneakHandler(highlightHandler, Direction.BACKWARD))
// workaround to support ; and , commands
mapToFunctionAndProvideKeys("f", SneakMemoryHandler("f"))
mapToFunctionAndProvideKeys("F", SneakMemoryHandler("F"))
mapToFunctionAndProvideKeys("t", SneakMemoryHandler("t"))
mapToFunctionAndProvideKeys("T", SneakMemoryHandler("T"))
mapToFunctionAndProvideKeys(";", SneakRepeatHandler(highlightHandler, RepeatDirection.IDENTICAL))
mapToFunctionAndProvideKeys(",", SneakRepeatHandler(highlightHandler, RepeatDirection.REVERSE))
}
private class SneakHandler(
private val highlightHandler: HighlightHandler,
private val direction: Direction,
) : ExtensionHandler {
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
val charone = getChar(editor) ?: return
val chartwo = getChar(editor) ?: return
val range = Util.jumpTo(editor, charone, chartwo, direction)
range?.let { highlightHandler.highlightSneakRange(editor.ij, range) }
Util.lastSymbols = "${charone}${chartwo}"
Util.lastSDirection = direction
}
private fun getChar(editor: VimEditor): Char? {
val key = VimExtensionFacade.inputKeyStroke(editor.ij)
return when {
key.keyChar == KeyEvent.CHAR_UNDEFINED || key.keyCode == KeyEvent.VK_ESCAPE -> null
else -> key.keyChar
}
}
}
/**
* This class acts as proxy for normal find commands because we need to update [Util.lastSDirection]
*/
private class SneakMemoryHandler(private val char: String) : VimExtensionHandler {
override fun execute(editor: Editor, context: DataContext) {
Util.lastSDirection = null
VimExtensionFacade.executeNormalWithoutMapping(injector.parser.parseKeys(char), editor)
}
}
private class SneakRepeatHandler(
private val highlightHandler: HighlightHandler,
private val direction: RepeatDirection,
) : ExtensionHandler {
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
val lastSDirection = Util.lastSDirection
if (lastSDirection != null) {
val (charone, chartwo) = Util.lastSymbols.toList()
val jumpRange = Util.jumpTo(editor, charone, chartwo, direction.map(lastSDirection))
jumpRange?.let { highlightHandler.highlightSneakRange(editor.ij, jumpRange) }
} else {
VimExtensionFacade.executeNormalWithoutMapping(injector.parser.parseKeys(direction.symb), editor.ij)
}
}
}
private object Util {
var lastSDirection: Direction? = null
var lastSymbols: String = ""
fun jumpTo(editor: VimEditor, charone: Char, chartwo: Char, sneakDirection: Direction): TextRange? {
val caret = editor.primaryCaret()
val position = caret.offset.point
val chars = editor.text()
val foundPosition = sneakDirection.findBiChar(editor, chars, position, charone, chartwo)
if (foundPosition != null) {
editor.primaryCaret().moveToOffset(foundPosition)
}
editor.ij.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE)
return foundPosition?.let { TextRange(foundPosition, foundPosition + 2) }
}
}
private enum class Direction(val offset: Int) {
FORWARD(1) {
override fun findBiChar(
editor: VimEditor,
charSequence: CharSequence,
position: Int,
charone: Char,
chartwo: Char
): Int? {
for (i in (position + offset) until charSequence.length - 1) {
if (matches(editor, charSequence, i, charone, chartwo)) {
return i
}
}
return null
}
},
BACKWARD(-1) {
override fun findBiChar(
editor: VimEditor,
charSequence: CharSequence,
position: Int,
charone: Char,
chartwo: Char
): Int? {
for (i in (position + offset) downTo 0) {
if (matches(editor, charSequence, i, charone, chartwo)) {
return i
}
}
return null
}
};
abstract fun findBiChar(
editor: VimEditor,
charSequence: CharSequence,
position: Int,
charone: Char,
chartwo: Char,
): Int?
fun matches(
editor: VimEditor,
charSequence: CharSequence,
charPosition: Int,
charOne: Char,
charTwo: Char,
): Boolean {
var match = charSequence[charPosition].equals(charOne, ignoreCase = injector.options(editor).ignorecase) &&
charSequence[charPosition + 1].equals(charTwo, ignoreCase = injector.options(editor).ignorecase)
if (injector.options(editor).ignorecase && injector.options(editor).smartcase) {
if (charOne.isUpperCase() || charTwo.isUpperCase()) {
match = charSequence[charPosition].equals(charOne, ignoreCase = false) &&
charSequence[charPosition + 1].equals(charTwo, ignoreCase = false)
}
}
return match
}
}
private enum class RepeatDirection(val symb: String) {
IDENTICAL(";") {
override fun map(direction: Direction): Direction = direction
},
REVERSE(",") {
override fun map(direction: Direction): Direction = when (direction) {
Direction.FORWARD -> Direction.BACKWARD
Direction.BACKWARD -> Direction.FORWARD
}
};
abstract fun map(direction: Direction): Direction
}
private class HighlightHandler {
private var editor: Editor? = null
private val sneakHighlighters: MutableSet<RangeHighlighter> = mutableSetOf()
fun highlightSneakRange(editor: Editor, range: TextRange) {
clearAllSneakHighlighters()
this.editor = editor
val project = editor.project
if (project != null) {
Disposer.register(VimProjectService.getInstance(project)) {
this.editor = null
sneakHighlighters.clear()
}
}
if (range.isMultiple) {
for (i in 0 until range.size()) {
highlightSingleRange(editor, range.startOffsets[i]..range.endOffsets[i])
}
} else {
highlightSingleRange(editor, range.startOffset..range.endOffset)
}
}
fun clearAllSneakHighlighters() {
sneakHighlighters.forEach { highlighter ->
editor?.markupModel?.removeHighlighter(highlighter) ?: StrictMode.fail("Highlighters without an editor")
}
sneakHighlighters.clear()
}
private fun highlightSingleRange(editor: Editor, range: ClosedRange<Int>) {
val highlighter = editor.markupModel.addRangeHighlighter(
range.start,
range.endInclusive,
HighlighterLayer.SELECTION,
getHighlightTextAttributes(),
HighlighterTargetArea.EXACT_RANGE
)
sneakHighlighters.add(highlighter)
setClearHighlightRangeTimer(highlighter)
}
private fun setClearHighlightRangeTimer(highlighter: RangeHighlighter) {
Executors.newSingleThreadScheduledExecutor().schedule({
ApplicationManager.getApplication().invokeLater {
editor?.markupModel?.removeHighlighter(highlighter) ?: StrictMode.fail("Highlighters without an editor")
}
}, DEFAULT_HIGHLIGHT_DURATION_SNEAK, TimeUnit.MILLISECONDS)
}
private fun getHighlightTextAttributes() = TextAttributes(
null,
EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES.defaultAttributes.backgroundColor,
editor?.colorsScheme?.getColor(EditorColors.CARET_COLOR),
EffectType.SEARCH_MATCH,
Font.PLAIN
)
}
}
/**
* Map some <Plug>(keys) command to given handler
* and create mapping to <Plug>(prefix)[keys]
*/
private fun VimExtension.mapToFunctionAndProvideKeys(keys: String, handler: ExtensionHandler) {
VimExtensionFacade.putExtensionHandlerMapping(
MappingMode.NXO,
injector.parser.parseKeys(command(keys)),
owner,
handler,
false
)
VimExtensionFacade.putKeyMapping(
MappingMode.NXO,
injector.parser.parseKeys(keys),
owner,
injector.parser.parseKeys(command(keys)),
true
)
}
private fun command(keys: String) = "<Plug>(sneak-$keys)"

View File

@ -30,6 +30,7 @@ public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesB
public var trackactionids: Boolean by optionProperty(IjOptions.trackactionids) public var trackactionids: Boolean by optionProperty(IjOptions.trackactionids)
public var visualdelay: Int by optionProperty(IjOptions.visualdelay) public var visualdelay: Int by optionProperty(IjOptions.visualdelay)
public var showmodewidget: Boolean by optionProperty(IjOptions.showmodewidget) public var showmodewidget: Boolean by optionProperty(IjOptions.showmodewidget)
public var colorfulmodewidget: Boolean by optionProperty(IjOptions.colorfulmodewidget)
// Temporary options to control work-in-progress behaviour // Temporary options to control work-in-progress behaviour
public var oldundo: Boolean by optionProperty(IjOptions.oldundo) public var oldundo: Boolean by optionProperty(IjOptions.oldundo)
@ -37,7 +38,6 @@ public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesB
public var exCommandAnnotation: Boolean by optionProperty(IjOptions.exCommandAnnotation) public var exCommandAnnotation: Boolean by optionProperty(IjOptions.exCommandAnnotation)
public var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation) public var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
public var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation) public var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
public var useNewRegex: Boolean by optionProperty(IjOptions.useNewRegex)
} }
/** /**

View File

@ -87,7 +87,7 @@ public object IjOptions {
public val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isTemporary = true)) public val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isTemporary = true))
public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isTemporary = true)) public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isTemporary = true))
public val showmodewidget: ToggleOption = addOption(ToggleOption("showmodewidget", GLOBAL, "showmodewidget", false, isTemporary = true)) public val showmodewidget: ToggleOption = addOption(ToggleOption("showmodewidget", GLOBAL, "showmodewidget", false, isTemporary = true))
public val useNewRegex: ToggleOption = addOption(ToggleOption("usenewregex", GLOBAL, "usenewregex", true, isTemporary = true)) public val colorfulmodewidget: ToggleOption = addOption(ToggleOption("colorfulmodewidget", GLOBAL, "colorfulmodewidget", false, isTemporary = true))
// This needs to be Option<out VimDataType> so that it can work with derived option types, such as NumberOption, which // This needs to be Option<out VimDataType> so that it can work with derived option types, such as NumberOption, which
// derives from Option<VimInt> // derives from Option<VimInt>

View File

@ -24,14 +24,17 @@ import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.keymap.ex.KeymapManagerEx; import com.intellij.openapi.keymap.ex.KeymapManagerEx;
import com.maddyhome.idea.vim.EventFacade; import com.maddyhome.idea.vim.EventFacade;
import com.maddyhome.idea.vim.VimPlugin; import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.action.ComplicatedKeysAction;
import com.maddyhome.idea.vim.action.VimShortcutKeyAction; import com.maddyhome.idea.vim.action.VimShortcutKeyAction;
import com.maddyhome.idea.vim.action.change.LazyVimCommand; import com.maddyhome.idea.vim.action.change.LazyVimCommand;
import com.maddyhome.idea.vim.api.*; import com.maddyhome.idea.vim.api.*;
import com.maddyhome.idea.vim.command.MappingMode; import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.ex.ExOutputModel; import com.maddyhome.idea.vim.ex.ExOutputModel;
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
import com.maddyhome.idea.vim.helper.HelperKt; import com.maddyhome.idea.vim.helper.HelperKt;
import com.maddyhome.idea.vim.key.*; import com.maddyhome.idea.vim.key.*;
import com.maddyhome.idea.vim.newapi.IjNativeAction; import com.maddyhome.idea.vim.newapi.IjNativeAction;
import com.maddyhome.idea.vim.newapi.IjVimActionsInitiator;
import com.maddyhome.idea.vim.newapi.IjVimEditor; import com.maddyhome.idea.vim.newapi.IjVimEditor;
import kotlin.Pair; import kotlin.Pair;
import kotlin.text.StringsKt; import kotlin.text.StringsKt;
@ -46,7 +49,6 @@ import java.awt.event.KeyEvent;
import java.util.List; import java.util.List;
import java.util.*; import java.util.*;
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toList;
/** /**
@ -219,23 +221,66 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
registerRequiredShortcut(keyStrokes, MappingOwner.IdeaVim.System.INSTANCE); registerRequiredShortcut(keyStrokes, MappingOwner.IdeaVim.System.INSTANCE);
for (MappingMode mappingMode : command.getModes()) { for (MappingMode mappingMode : command.getModes()) {
Node<LazyVimCommand> node = getKeyRoot(mappingMode); Node<VimActionsInitiator> node = getKeyRoot(mappingMode);
NodesKt.addLeafs(node, keyStrokes, command); NodesKt.addLeafs(node, keyStrokes, command);
} }
} }
} }
@Deprecated
public void registerCommandAction(@NotNull VimActionsInitiator actionHolder) {
IjVimActionsInitiator holder = (IjVimActionsInitiator)actionHolder;
if (!VimPlugin.getPluginId().equals(holder.getBean().getPluginDescriptor().getPluginId())) {
logger.error("IdeaVim doesn't accept contributions to `vimActions` extension points. " +
"Please create a plugin using `VimExtension`. " +
"Plugin to blame: " +
holder.getBean().getPluginDescriptor().getPluginId());
return;
}
Set<List<KeyStroke>> actionKeys = holder.getBean().getParsedKeys();
if (actionKeys == null) {
final EditorActionHandlerBase action = actionHolder.getInstance();
if (action instanceof ComplicatedKeysAction) {
actionKeys = ((ComplicatedKeysAction)action).getKeyStrokesSet();
}
else {
throw new RuntimeException("Cannot register action: " + action.getClass().getName());
}
}
Set<MappingMode> actionModes = holder.getBean().getParsedModes();
if (actionModes == null) {
throw new RuntimeException("Cannot register action: " + holder.getBean().getImplementation());
}
if (ApplicationManager.getApplication().isUnitTestMode()) {
initIdentityChecker();
for (List<KeyStroke> keys : actionKeys) {
checkCommand(actionModes, actionHolder.getInstance(), keys);
}
}
for (List<KeyStroke> keyStrokes : actionKeys) {
registerRequiredShortcut(keyStrokes, MappingOwner.IdeaVim.System.INSTANCE);
for (MappingMode mappingMode : actionModes) {
Node<VimActionsInitiator> node = getKeyRoot(mappingMode);
NodesKt.addLeafs(node, keyStrokes, actionHolder);
}
}
}
private void registerRequiredShortcut(@NotNull List<KeyStroke> keys, MappingOwner owner) { private void registerRequiredShortcut(@NotNull List<KeyStroke> keys, MappingOwner owner) {
for (KeyStroke key : keys) { for (KeyStroke key : keys) {
if (key.getKeyChar() == KeyEvent.CHAR_UNDEFINED) { if (key.getKeyChar() == KeyEvent.CHAR_UNDEFINED &&
if (!injector.getOptionGroup().getGlobalOptions().getOctopushandler() ||
!(key.getKeyCode() == KeyEvent.VK_ESCAPE && key.getModifiers() == 0) && !(key.getKeyCode() == KeyEvent.VK_ESCAPE && key.getModifiers() == 0) &&
!(key.getKeyCode() == KeyEvent.VK_ENTER && key.getModifiers() == 0)) { !(key.getKeyCode() == KeyEvent.VK_ENTER && key.getModifiers() == 0)) {
getRequiredShortcutKeys().add(new RequiredShortcut(key, owner)); getRequiredShortcutKeys().add(new RequiredShortcut(key, owner));
} }
} }
} }
}
public static @NotNull ShortcutSet toShortcutSet(@NotNull Collection<RequiredShortcut> requiredShortcuts) { public static @NotNull ShortcutSet toShortcutSet(@NotNull Collection<RequiredShortcut> requiredShortcuts) {
final List<Shortcut> shortcuts = new ArrayList<>(); final List<Shortcut> shortcuts = new ArrayList<>();

View File

@ -0,0 +1,138 @@
/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.group;
import com.intellij.ide.bookmark.LineBookmark;
import com.intellij.openapi.editor.Editor;
import com.maddyhome.idea.vim.api.*;
import com.maddyhome.idea.vim.common.TextRange;
import com.maddyhome.idea.vim.mark.IntellijMark;
import com.maddyhome.idea.vim.mark.Jump;
import com.maddyhome.idea.vim.mark.Mark;
import com.maddyhome.idea.vim.newapi.IjVimEditor;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
@Deprecated
@ApiStatus.ScheduledForRemoval(inVersion = "2.3")
public class MarkGroup {
public List<Jump> jumps = VimInjectorKt.injector.getJumpService().getJumps("");
public void saveJumpLocation(@NotNull Editor editor) {
VimInjectorKt.injector.getJumpService().saveJumpLocation(new IjVimEditor(editor));
}
public void saveJumpLocation(@NotNull VimEditor editor) {
VimInjectorKt.injector.getJumpService().saveJumpLocation(editor);
}
public void setChangeMarks(@NotNull VimEditor vimEditor, @NotNull TextRange range) {
VimMarkService markService = VimInjectorKt.injector.getMarkService();
VimMarkServiceKt.setChangeMarks(markService, vimEditor.primaryCaret(), range);
}
public void addJump(@NotNull VimEditor editor, boolean reset) {
VimJumpServiceKt.addJump(VimInjectorKt.injector.getJumpService(), editor, reset);
}
@Nullable
public Mark getMark(@NotNull VimEditor editor, char ch) {
return VimInjectorKt.injector.getMarkService().getMark(editor.primaryCaret(), ch);
}
@Nullable
public Jump getJump(int count) {
return VimInjectorKt.injector.getJumpService().getJump("", count);
}
@Nullable
public Mark createSystemMark(char ch, int line, int col, @NotNull VimEditor editor) {
Editor ijEditor = ((IjVimEditor)editor).getEditor();
@Nullable LineBookmark systemMark = SystemMarks.createOrGetSystemMark(ch, line, ijEditor);
if (systemMark == null) {
return null;
}
return new IntellijMark(systemMark, col, ijEditor.getProject());
}
public boolean setMark(@NotNull VimEditor editor, char ch, int offset) {
return VimInjectorKt.injector.getMarkService().setMark(editor.primaryCaret(), ch, offset);
}
public boolean setMark(@NotNull VimEditor editor, char ch) {
return VimInjectorKt.injector.getMarkService().setMark(editor, ch);
}
public void includeCurrentCommandAsNavigation(@NotNull VimEditor editor) {
VimInjectorKt.injector.getJumpService().includeCurrentCommandAsNavigation(editor);
}
@Nullable
public Mark getFileMark(@NotNull VimEditor editor, char ch) {
return VimInjectorKt.injector.getMarkService().getMark(editor.primaryCaret(), ch);
}
public void setVisualSelectionMarks(@NotNull VimEditor editor, @NotNull TextRange range) {
VimMarkService markService = VimInjectorKt.injector.getMarkService();
VimMarkServiceKt.setVisualSelectionMarks(markService, editor.primaryCaret(), range);
}
@Nullable
public TextRange getChangeMarks(@NotNull VimEditor editor) {
return VimInjectorKt.injector.getMarkService().getChangeMarks(editor.primaryCaret());
}
@Nullable
public TextRange getVisualSelectionMarks(@NotNull VimEditor editor) {
return VimInjectorKt.injector.getMarkService().getVisualSelectionMarks(editor.primaryCaret());
}
public void resetAllMarks() {
VimInjectorKt.injector.getMarkService().resetAllMarks();
}
public void removeMark(char ch, @NotNull Mark mark) {
VimInjectorKt.injector.getMarkService().removeMark(ch, mark);
}
@NotNull
public List<Mark> getMarks(@NotNull VimEditor editor) {
Set<Mark> marks = VimInjectorKt.injector.getMarkService().getAllLocalMarks(editor.primaryCaret());
marks.addAll(VimInjectorKt.injector.getMarkService().getGlobalMarks(editor));
return new ArrayList<>(marks);
}
public int getJumpSpot() {
return VimInjectorKt.injector.getJumpService().getJumpSpot("");
}
public void updateMarkFromDelete(@Nullable VimEditor editor,
@Nullable HashMap<Character, Mark> marks,
int delStartOff,
int delLength) {
VimInjectorKt.injector.getMarkService().updateMarksFromDelete(editor, delStartOff, delLength);
}
public void updateMarkFromInsert(@Nullable VimEditor editor,
@Nullable HashMap<Character, Mark> marks,
int insStartOff,
int insLength) {
VimInjectorKt.injector.getMarkService().updateMarksFromInsert(editor, insStartOff, insLength);
}
public void dropLastJump() {
VimInjectorKt.injector.getJumpService().dropLastJump("");
}
}

View File

@ -65,28 +65,28 @@ internal class OptionGroup : VimOptionGroupBase(), IjVimOptionGroup {
} }
} }
public class IjOptionConstants { internal class IjOptionConstants {
@Suppress("SpellCheckingInspection", "MemberVisibilityCanBePrivate", "ConstPropertyName") @Suppress("SpellCheckingInspection", "MemberVisibilityCanBePrivate", "ConstPropertyName")
public companion object { companion object {
public const val idearefactormode_keep: String = "keep" const val idearefactormode_keep = "keep"
public const val idearefactormode_select: String = "select" const val idearefactormode_select = "select"
public const val idearefactormode_visual: String = "visual" const val idearefactormode_visual = "visual"
public const val ideastatusicon_enabled: String = "enabled" const val ideastatusicon_enabled = "enabled"
public const val ideastatusicon_gray: String = "gray" const val ideastatusicon_gray = "gray"
public const val ideastatusicon_disabled: String = "disabled" const val ideastatusicon_disabled = "disabled"
public const val ideavimsupport_dialog: String = "dialog" const val ideavimsupport_dialog = "dialog"
public const val ideavimsupport_singleline: String = "singleline" const val ideavimsupport_singleline = "singleline"
public const val ideavimsupport_dialoglegacy: String = "dialoglegacy" const val ideavimsupport_dialoglegacy = "dialoglegacy"
public const val ideawrite_all: String = "all" const val ideawrite_all = "all"
public const val ideawrite_file: String = "file" const val ideawrite_file = "file"
public val ideaStatusIconValues: Set<String> = setOf(ideastatusicon_enabled, ideastatusicon_gray, ideastatusicon_disabled) val ideaStatusIconValues = setOf(ideastatusicon_enabled, ideastatusicon_gray, ideastatusicon_disabled)
public val ideaRefactorModeValues: Set<String> = setOf(idearefactormode_keep, idearefactormode_select, idearefactormode_visual) val ideaRefactorModeValues = setOf(idearefactormode_keep, idearefactormode_select, idearefactormode_visual)
public val ideaWriteValues: Set<String> = setOf(ideawrite_all, ideawrite_file) val ideaWriteValues = setOf(ideawrite_all, ideawrite_file)
public val ideavimsupportValues: Set<String> = setOf(ideavimsupport_dialog, ideavimsupport_singleline, ideavimsupport_dialoglegacy) val ideavimsupportValues = setOf(ideavimsupport_dialog, ideavimsupport_singleline, ideavimsupport_dialoglegacy)
} }
} }

View File

@ -51,8 +51,6 @@ import javax.swing.SwingUtilities
public class ProcessGroup : VimProcessGroupBase() { public class ProcessGroup : VimProcessGroupBase() {
override var lastCommand: String? = null override var lastCommand: String? = null
private set private set
override var isCommandProcessing: Boolean = false
override var modeBeforeCommandProcessing: Mode? = null
public override fun startSearchCommand(editor: VimEditor, context: ExecutionContext, count: Int, leader: Char) { public override fun startSearchCommand(editor: VimEditor, context: ExecutionContext, count: Int, leader: Char) {
// Don't allow searching in one line editors // Don't allow searching in one line editors
@ -81,8 +79,6 @@ public class ProcessGroup : VimProcessGroupBase() {
"Cannot enable cmd mode from current mode $currentMode" "Cannot enable cmd mode from current mode $currentMode"
} }
isCommandProcessing = true
modeBeforeCommandProcessing = currentMode
val initText = getRange(editor, cmd) val initText = getRange(editor, cmd)
injector.markService.setVisualSelectionMarks(editor) injector.markService.setVisualSelectionMarks(editor)
editor.vimStateMachine.mode = Mode.CMD_LINE(currentMode) editor.vimStateMachine.mode = Mode.CMD_LINE(currentMode)
@ -138,9 +134,6 @@ public class ProcessGroup : VimProcessGroupBase() {
logger.error(bad) logger.error(bad)
VimPlugin.indicateError() VimPlugin.indicateError()
res = false res = false
} finally {
isCommandProcessing = false
modeBeforeCommandProcessing = null
} }
return res return res

View File

@ -36,9 +36,10 @@ import com.maddyhome.idea.vim.history.HistoryConstants;
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.IjVimCaret;
import com.maddyhome.idea.vim.newapi.IjVimEditor; import com.maddyhome.idea.vim.newapi.IjVimEditor;
import com.maddyhome.idea.vim.newapi.IjVimSearchGroup;
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener; import com.maddyhome.idea.vim.options.GlobalOptionChangeListener;
import com.maddyhome.idea.vim.regexp.*; import com.maddyhome.idea.vim.regexp.CharPointer;
import com.maddyhome.idea.vim.regexp.CharacterClasses;
import com.maddyhome.idea.vim.regexp.RegExp;
import com.maddyhome.idea.vim.ui.ModalEntry; import com.maddyhome.idea.vim.ui.ModalEntry;
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel; import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
import com.maddyhome.idea.vim.vimscript.model.VimLContext; import com.maddyhome.idea.vim.vimscript.model.VimLContext;
@ -61,20 +62,13 @@ import java.util.*;
import static com.maddyhome.idea.vim.api.VimInjectorKt.*; import static com.maddyhome.idea.vim.api.VimInjectorKt.*;
import static com.maddyhome.idea.vim.helper.HelperKt.localEditors; import static com.maddyhome.idea.vim.helper.HelperKt.localEditors;
import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.shouldIgnoreCase; import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.shouldIgnoreCase;
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.globalIjOptions;
import static com.maddyhome.idea.vim.register.RegisterConstants.LAST_SEARCH_REGISTER; import static com.maddyhome.idea.vim.register.RegisterConstants.LAST_SEARCH_REGISTER;
@State(name = "VimSearchSettings", storages = { @State(name = "VimSearchSettings", storages = {
@Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED) @Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)
}) })
@Deprecated public class SearchGroup extends VimSearchGroupBase implements PersistentStateComponent<Element> {
/**
* @deprecated Replace with IjVimSearchGroup
*/
public class SearchGroup extends IjVimSearchGroup implements PersistentStateComponent<Element> {
public SearchGroup() { public SearchGroup() {
super();
if (!globalIjOptions(injector).getUseNewRegex()) {
// TODO: Investigate migrating these listeners to use the effective value change listener // TODO: Investigate migrating these listeners to use the effective value change listener
// This would allow us to update the editor we're told to update, rather than looping over all projects and updating // This would allow us to update the editor we're told to update, rather than looping over all projects and updating
// the highlights in that project's current document's open editors (see VIM-2779). // the highlights in that project's current document's open editors (see VIM-2779).
@ -94,13 +88,8 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.ignorecase, updateHighlightsIfVisible); VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.ignorecase, updateHighlightsIfVisible);
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.smartcase, updateHighlightsIfVisible); VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.smartcase, updateHighlightsIfVisible);
} }
}
public void turnOn() { public void turnOn() {
if (globalIjOptions(injector).getUseNewRegex()) {
super.updateSearchHighlights(false);
return;
}
updateSearchHighlights(); updateSearchHighlights();
} }
@ -111,12 +100,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
} }
@TestOnly @TestOnly
@Override
public void resetState() { public void resetState() {
if (globalIjOptions(injector).getUseNewRegex()) {
super.resetState();
return;
}
lastPatternIdx = RE_SEARCH; lastPatternIdx = RE_SEARCH;
lastSearch = lastSubstitute = lastReplace = null; lastSearch = lastSubstitute = lastReplace = null;
lastPatternOffset = ""; lastPatternOffset = "";
@ -130,9 +114,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
* *
* @return The pattern used for last search. Can be null * @return The pattern used for last search. Can be null
*/ */
@Override
public @Nullable String getLastSearchPattern() { public @Nullable String getLastSearchPattern() {
if (globalIjOptions(injector).getUseNewRegex()) return super.getLastSearchPattern();
return lastSearch; return lastSearch;
} }
@ -140,9 +122,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
* Get the last pattern used in substitution. * Get the last pattern used in substitution.
* @return The pattern used for the last substitute command. Can be null * @return The pattern used for the last substitute command. Can be null
*/ */
@Override
public @Nullable String getLastSubstitutePattern() { public @Nullable String getLastSubstitutePattern() {
if (globalIjOptions(injector).getUseNewRegex()) return super.getLastSubstitutePattern();
return lastSubstitute; return lastSubstitute;
} }
@ -151,9 +131,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
* *
* @return The pattern last used for either searching or substitution. Can be null * @return The pattern last used for either searching or substitution. Can be null
*/ */
@Override public @Nullable String getLastUsedPattern() {
protected @Nullable String getLastUsedPattern() {
if (globalIjOptions(injector).getUseNewRegex()) return super.getLastUsedPattern();
switch (lastPatternIdx) { switch (lastPatternIdx) {
case RE_SEARCH: return lastSearch; case RE_SEARCH: return lastSearch;
case RE_SUBST: return lastSubstitute; case RE_SUBST: return lastSubstitute;
@ -217,10 +195,6 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
@TestOnly @TestOnly
public void setLastSearchState(@SuppressWarnings("unused") @NotNull Editor editor, @NotNull String pattern, public void setLastSearchState(@SuppressWarnings("unused") @NotNull Editor editor, @NotNull String pattern,
@NotNull String patternOffset, Direction direction) { @NotNull String patternOffset, Direction direction) {
if (globalIjOptions(injector).getUseNewRegex()) {
super.setLastSearchState(pattern, patternOffset, direction);
return;
}
setLastUsedPattern(pattern, RE_SEARCH, true); setLastUsedPattern(pattern, RE_SEARCH, true);
lastIgnoreSmartCase = false; lastIgnoreSmartCase = false;
lastPatternOffset = patternOffset; lastPatternOffset = patternOffset;
@ -252,7 +226,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
int startLine, int startLine,
int endLine, int endLine,
boolean ignoreCase) { boolean ignoreCase) {
return injector.getSearchHelper().findAll(new IjVimEditor(editor), pattern, startLine, endLine, ignoreCase); return SearchHelper.findAll(editor, pattern, startLine, endLine, ignoreCase);
} }
/** /**
@ -280,8 +254,6 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
*/ */
@Override @Override
public int processSearchCommand(@NotNull VimEditor editor, @NotNull String command, int startOffset, @NotNull Direction dir) { public int processSearchCommand(@NotNull VimEditor editor, @NotNull String command, int startOffset, @NotNull Direction dir) {
if (globalIjOptions(injector).getUseNewRegex()) return super.processSearchCommand(editor, command, startOffset, dir);
boolean isNewPattern = false; boolean isNewPattern = false;
String pattern = null; String pattern = null;
String patternOffset = null; String patternOffset = null;
@ -442,7 +414,6 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
*/ */
@Override @Override
public int searchWord(@NotNull VimEditor editor, @NotNull ImmutableVimCaret caret, int count, boolean whole, @NotNull Direction dir) { public int searchWord(@NotNull VimEditor editor, @NotNull ImmutableVimCaret caret, int count, boolean whole, @NotNull Direction dir) {
if (globalIjOptions(injector).getUseNewRegex()) return super.searchWord(editor, caret, count, whole, dir);
TextRange range = SearchHelper.findWordUnderCursor(((IjVimEditor)editor).getEditor(), ((IjVimCaret)caret).getCaret()); TextRange range = SearchHelper.findWordUnderCursor(((IjVimEditor)editor).getEditor(), ((IjVimCaret)caret).getCaret());
if (range == null) { if (range == null) {
logger.warn("No range was found"); logger.warn("No range was found");
@ -484,7 +455,6 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
*/ */
@Override @Override
public int searchNext(@NotNull VimEditor editor, @NotNull ImmutableVimCaret caret, int count) { public int searchNext(@NotNull VimEditor editor, @NotNull ImmutableVimCaret caret, int count) {
if (globalIjOptions(injector).getUseNewRegex()) return super.searchNext(editor, caret, count);
return searchNextWithDirection(((IjVimEditor)editor).getEditor(), ((IjVimCaret)caret).getCaret(), count, lastDir); return searchNextWithDirection(((IjVimEditor)editor).getEditor(), ((IjVimCaret)caret).getCaret(), count, lastDir);
} }
@ -501,7 +471,6 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
*/ */
@Override @Override
public int searchPrevious(@NotNull VimEditor editor, @NotNull ImmutableVimCaret caret, int count) { public int searchPrevious(@NotNull VimEditor editor, @NotNull ImmutableVimCaret caret, int count) {
if (globalIjOptions(injector).getUseNewRegex()) return super.searchPrevious(editor, caret, count);
return searchNextWithDirection(((IjVimEditor)editor).getEditor(), ((IjVimCaret)caret).getCaret(), count, return searchNextWithDirection(((IjVimEditor)editor).getEditor(), ((IjVimCaret)caret).getCaret(), count,
lastDir.reverse()); lastDir.reverse());
} }
@ -555,8 +524,6 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
@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()) return super.processSubstituteCommand(editor, caret, 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<>();
if (CommandStateHelper.inVisualMode(((IjVimEditor) editor).getEditor())) { if (CommandStateHelper.inVisualMode(((IjVimEditor) editor).getEditor())) {
@ -720,7 +687,8 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
return false; return false;
} }
Pair<Boolean, Triple<Object, String, Object>> booleanregmmatch_tPair = search_regcomp(pat, which_pat, RE_SUBST); Pair<Boolean, Triple<Object, String, Object>> booleanregmmatch_tPair = search_regcomp(pat, which_pat,
RE_SUBST);
if (!booleanregmmatch_tPair.getFirst()) { if (!booleanregmmatch_tPair.getFirst()) {
if (do_error) { if (do_error) {
VimPlugin.showMessage(MessageHelper.message(Msg.e_invcmd)); VimPlugin.showMessage(MessageHelper.message(Msg.e_invcmd));
@ -787,6 +755,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
firstMatch = false; firstMatch = false;
} }
String match = sp.vim_regsub_multi(regmatch, lnum, sub, 1, false); String match = sp.vim_regsub_multi(regmatch, lnum, sub, 1, false);
if (sub.charAt(0) == '\\' && sub.charAt(1) == '=') { if (sub.charAt(0) == '\\' && sub.charAt(1) == '=') {
String exprString = sub.toString().substring(2); String exprString = sub.toString().substring(2);
@ -795,8 +764,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
exceptions.add(new ExException("E15: Invalid expression: " + exprString)); exceptions.add(new ExException("E15: Invalid expression: " + exprString));
expression = new SimpleExpression(new VimString("")); expression = new SimpleExpression(new VimString(""));
} }
} } else if (match == null) {
else if (match == null) {
return false; return false;
} }
@ -809,9 +777,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
if (do_all || line != lastLine) { if (do_all || line != lastLine) {
boolean doReplace = true; boolean doReplace = true;
if (do_ask) { if (do_ask) {
RangeHighlighter hl = RangeHighlighter hl = SearchHighlightsHelper.addSubstitutionConfirmationHighlight(((IjVimEditor) editor).getEditor(), startoff, endoff);
SearchHighlightsHelper.addSubstitutionConfirmationHighlight(((IjVimEditor)editor).getEditor(), startoff,
endoff);
final ReplaceConfirmationChoice choice = confirmChoice(((IjVimEditor) editor).getEditor(), 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) {
@ -836,23 +802,22 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
} }
} }
if (doReplace) { if (doReplace) {
SubmatchFunctionHandler.Companion.getInstance().setLatestMatch( SubmatchFunctionHandler.Companion.getInstance().setLatestMatch(((IjVimEditor) editor).getEditor().getDocument().getText(new com.intellij.openapi.util.TextRange(startoff, endoff)));
((IjVimEditor)editor).getEditor().getDocument().getText(new com.intellij.openapi.util.TextRange(startoff, endoff)));
caret.moveToOffset(startoff); caret.moveToOffset(startoff);
if (expression != null) { if (expression != null) {
try { try {
match = match = expression
expression.evaluate(editor, injector.getExecutionContextManager().onEditor(editor, null), parent).toInsertableString(); .evaluate(editor, injector.getExecutionContextManager().onEditor(editor, null), parent)
} .toInsertableString();
catch (Exception e) { } catch (Exception e) {
exceptions.add((ExException) e); exceptions.add((ExException) e);
match = ""; match = "";
} }
} }
String finalMatch = match; String finalMatch = match;
ApplicationManager.getApplication().runWriteAction( ApplicationManager.getApplication().runWriteAction(() -> ((IjVimEditor) editor).getEditor().getDocument().replaceString(startoff, endoff,
() -> ((IjVimEditor)editor).getEditor().getDocument().replaceString(startoff, endoff, finalMatch)); finalMatch));
lastMatch = startoff; lastMatch = startoff;
int newend = startoff + match.length(); int newend = startoff + match.length();
newpos = CharacterPosition.Companion.fromOffset(((IjVimEditor) editor).getEditor(), newend); newpos = CharacterPosition.Companion.fromOffset(((IjVimEditor) editor).getEditor(), newend);
@ -910,10 +875,6 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
@Override @Override
public void setLastSearchPattern(@Nullable String lastSearchPattern) { public void setLastSearchPattern(@Nullable String lastSearchPattern) {
if (globalIjOptions(injector).getUseNewRegex()) {
super.setLastSearchPattern(lastSearchPattern);
return;
}
this.lastSearch = lastSearchPattern; this.lastSearch = lastSearchPattern;
if (showSearchHighlight) { if (showSearchHighlight) {
resetIncsearchHighlights(); resetIncsearchHighlights();
@ -923,10 +884,6 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
@Override @Override
public void setLastSubstitutePattern(@Nullable String lastSubstitutePattern) { public void setLastSubstitutePattern(@Nullable String lastSubstitutePattern) {
if (globalIjOptions(injector).getUseNewRegex()) {
super.setLastSubstitutePattern(lastSubstitutePattern);
return;
}
this.lastSubstitute = lastSubstitutePattern; this.lastSubstitute = lastSubstitutePattern;
} }
@ -936,7 +893,6 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
int patternOffset, int patternOffset,
int startOffset, int startOffset,
@NotNull Direction direction) { @NotNull Direction direction) {
if (globalIjOptions(injector).getUseNewRegex()) return super.processSearchRange(editor, pattern, patternOffset, startOffset, direction);
return processSearchRange(((IjVimEditor) editor).getEditor(), pattern, patternOffset, startOffset, direction); return processSearchRange(((IjVimEditor) editor).getEditor(), pattern, patternOffset, startOffset, direction);
} }
@ -1059,7 +1015,6 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
*/ */
@Override @Override
public @Nullable TextRange getNextSearchRange(@NotNull VimEditor editor, int count, boolean forwards) { public @Nullable TextRange getNextSearchRange(@NotNull VimEditor editor, int count, boolean forwards) {
if (globalIjOptions(injector).getUseNewRegex()) return super.getNextSearchRange(editor, count, forwards);
editor.removeSecondaryCarets(); editor.removeSecondaryCarets();
TextRange current = findUnderCaret(editor); TextRange current = findUnderCaret(editor);
@ -1091,10 +1046,17 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
} }
} }
@Override
@Nullable
public TextRange findUnderCaret(@NotNull VimEditor editor) {
final TextRange backSearch = searchBackward(editor, editor.primaryCaret().getOffset().getPoint() + 1, 1);
if (backSearch == null) return null;
return backSearch.contains(editor.primaryCaret().getOffset().getPoint()) ? backSearch : null;
}
@Override @Override
@Nullable @Nullable
public TextRange searchBackward(@NotNull VimEditor editor, int offset, int count) { public TextRange searchBackward(@NotNull VimEditor editor, int offset, int count) {
if (globalIjOptions(injector).getUseNewRegex()) return super.searchBackward(editor, offset, count);
// Backward search returns wrongs end offset for some cases. That's why we should perform additional forward search // Backward search returns wrongs end offset for some cases. That's why we should perform additional forward search
final EnumSet<SearchOptions> searchOptions = EnumSet.of(SearchOptions.WRAP, SearchOptions.WHOLE_FILE, SearchOptions.BACKWARDS); final EnumSet<SearchOptions> searchOptions = EnumSet.of(SearchOptions.WRAP, SearchOptions.WHOLE_FILE, SearchOptions.BACKWARDS);
final TextRange foundBackward = VimInjectorKt.getInjector().getSearchHelper().findPattern(editor, getLastUsedPattern(), offset, count, searchOptions); final TextRange foundBackward = VimInjectorKt.getInjector().getSearchHelper().findPattern(editor, getLastUsedPattern(), offset, count, searchOptions);
@ -1112,12 +1074,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
// //
// ******************************************************************************************************************* // *******************************************************************************************************************
//region Search highlights //region Search highlights
@Override
public void clearSearchHighlight() { public void clearSearchHighlight() {
if (globalIjOptions(injector).getUseNewRegex()) {
super.clearSearchHighlight();
return;
}
showSearchHighlight = false; showSearchHighlight = false;
updateSearchHighlights(); updateSearchHighlights();
} }
@ -1137,12 +1094,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
/** /**
* Reset the search highlights to the last used pattern after highlighting incsearch results. * Reset the search highlights to the last used pattern after highlighting incsearch results.
*/ */
@Override
public void resetIncsearchHighlights() { public void resetIncsearchHighlights() {
if (globalIjOptions(injector).getUseNewRegex()) {
super.resetIncsearchHighlights();
return;
}
SearchHighlightsHelper.updateSearchHighlights(getLastUsedPattern(), lastIgnoreSmartCase, showSearchHighlight, true); SearchHighlightsHelper.updateSearchHighlights(getLastUsedPattern(), lastIgnoreSmartCase, showSearchHighlight, true);
} }
@ -1151,13 +1103,9 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
} }
private void highlightSearchLines(@NotNull Editor editor, int startLine, int endLine) { private void highlightSearchLines(@NotNull Editor editor, int startLine, int endLine) {
if (globalIjOptions(injector).getUseNewRegex()) {
super.highlightSearchLines(new IjVimEditor(editor), startLine, endLine);
return;
}
final String pattern = getLastUsedPattern(); final String pattern = getLastUsedPattern();
if (pattern != null) { if (pattern != null) {
final List<TextRange> results = injector.getSearchHelper().findAll(new IjVimEditor(editor), pattern, startLine, endLine, final List<TextRange> results = SearchHelper.findAll(editor, pattern, startLine, endLine,
shouldIgnoreCase(pattern, lastIgnoreSmartCase)); shouldIgnoreCase(pattern, lastIgnoreSmartCase));
SearchHighlightsHelper.highlightSearchResults(editor, pattern, results, -1); SearchHighlightsHelper.highlightSearchResults(editor, pattern, results, -1);
} }
@ -1166,17 +1114,12 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
/** /**
* Updates search highlights when the selected editor changes * Updates search highlights when the selected editor changes
*/ */
public void fileEditorManagerSelectionChangedCallback(@SuppressWarnings("unused") @NotNull FileEditorManagerEvent event) { public static void fileEditorManagerSelectionChangedCallback(@SuppressWarnings("unused") @NotNull FileEditorManagerEvent event) {
if (globalIjOptions(injector).getUseNewRegex()) {
super.updateSearchHighlights(false);
return;
}
VimPlugin.getSearch().updateSearchHighlights(); VimPlugin.getSearch().updateSearchHighlights();
} }
@Override @Override
public Integer findDecimalNumber(@NotNull String line) { public Integer findDecimalNumber(@NotNull String line) {
if (globalIjOptions(injector).getUseNewRegex()) return super.findDecimalNumber(line);
Pair<TextRange, NumberType> searchResult = SearchHelper.findNumberInText(line, 0, false, false, false); Pair<TextRange, NumberType> searchResult = SearchHelper.findNumberInText(line, 0, false, false, false);
if (searchResult != null) { if (searchResult != null) {
TextRange range = searchResult.component1(); TextRange range = searchResult.component1();
@ -1188,7 +1131,6 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
@NotNull @NotNull
@Override @Override
public Direction getLastSearchDirection() { public Direction getLastSearchDirection() {
if (globalIjOptions(injector).getUseNewRegex()) return super.getLastSearchDirection();
return lastDir; return lastDir;
} }
@ -1332,7 +1274,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
if (hasEndOffset) searchOptions.add(SearchOptions.WANT_ENDPOS); if (hasEndOffset) searchOptions.add(SearchOptions.WANT_ENDPOS);
// Uses RE_LAST. We know this is always set before being called // Uses RE_LAST. We know this is always set before being called
TextRange range = injector.getSearchHelper().findPattern(new IjVimEditor(editor), getLastUsedPattern(), startOffset, count, searchOptions); TextRange range = SearchHelper.findPattern(editor, getLastUsedPattern(), startOffset, count, searchOptions);
if (range == null) { if (range == null) {
logger.warn("No range is found"); logger.warn("No range is found");
return -1; return -1;

View File

@ -177,6 +177,15 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
return createOrGetSystemMark(char, line, col, editor) return createOrGetSystemMark(char, line, col, editor)
} }
@Deprecated("Please use removeMark with other signature")
override fun removeMark(ch: Char, mark: Mark) {
if (ch.isGlobalMark()) {
removeGlobalMark(ch)
} else if (ch.isLocalMark()) {
getLocalMarks(mark.filepath).remove(ch)
}
}
override fun removeGlobalMark(char: Char) { override fun removeGlobalMark(char: Char) {
val mark = getGlobalMark(char) val mark = getGlobalMark(char)
if (mark is IntellijMark) { if (mark is IntellijMark) {
@ -270,6 +279,16 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
} }
} }
/**
* COMPATIBILITY-LAYER: Method added
* Please see: [doc](https://jb.gg/zo8n0r)
*
*/
@Deprecated("Please use method with VimEditor")
fun saveJumpLocation(editor: Editor?) {
injector.jumpService.saveJumpLocation(IjVimEditor(editor!!))
}
companion object { companion object {
private const val SAVE_MARK_COUNT = 20 private const val SAVE_MARK_COUNT = 20
private val logger = Logger.getInstance( private val logger = Logger.getInstance(

View File

@ -52,13 +52,12 @@ import javax.swing.Timer
* editorHasSelection is false. Insert mode ([mode]) has no selection and editor also has no selection, so * editorHasSelection is false. Insert mode ([mode]) has no selection and editor also has no selection, so
* no adjustment gets performed and IdeaVim stays in insert mode. * no adjustment gets performed and IdeaVim stays in insert mode.
*/ */
// Do not remove until it's used in EasyMotion plugin in tests internal object VimVisualTimer {
public object VimVisualTimer {
public var swingTimer: Timer? = null var swingTimer: Timer? = null
public var mode: Mode? = null var mode: Mode? = null
public inline fun singleTask(currentMode: Mode, crossinline task: (initialMode: Mode?) -> Unit) { inline fun singleTask(currentMode: Mode, crossinline task: (initialMode: Mode?) -> Unit) {
swingTimer?.stop() swingTimer?.stop()
if (mode == null) mode = currentMode if (mode == null) mode = currentMode
@ -70,7 +69,7 @@ public object VimVisualTimer {
swingTimer = timer swingTimer = timer
} }
public fun doNow() { fun doNow() {
val swingTimer1 = swingTimer val swingTimer1 = swingTimer
if (swingTimer1 != null) { if (swingTimer1 != null) {
swingTimer1.stop() swingTimer1.stop()
@ -80,12 +79,12 @@ public object VimVisualTimer {
} }
} }
public fun drop() { fun drop() {
swingTimer?.stop() swingTimer?.stop()
swingTimer = null swingTimer = null
} }
public inline fun timerAction(task: (initialMode: Mode?) -> Unit) { inline fun timerAction(task: (initialMode: Mode?) -> Unit) {
task(mode) task(mode)
swingTimer = null swingTimer = null
mode = null mode = null

View File

@ -0,0 +1,102 @@
/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.handler
import com.intellij.serviceContainer.BaseKeyedLazyInstance
import com.intellij.util.SmartList
import com.intellij.util.xmlb.annotations.Attribute
import com.maddyhome.idea.vim.command.MappingMode
import org.jetbrains.annotations.ApiStatus.ScheduledForRemoval
import javax.swing.KeyStroke
/**
* Action holder for IdeaVim actions.
*
* [implementation] should be subclass of [EditorActionHandlerBase]
*
* [modes] ("mappingModes") defines the action modes. E.g. "NO" - action works in normal and op-pending modes.
* Warning: V - Visual and Select mode. X - Visual mode. (like vmap and xmap).
* Use "ALL" to enable action for all modes.
*
* [keys] comma-separated list of keys for the action. E.g. `gt,gT` - action gets executed on `gt` or `gT`
* Since xml doesn't allow using raw `<` character, use « and » symbols for mappings with modifiers.
* E.g. `«C-U»` - CTRL-U (<C-U> in vim notation)
* If you want to use exactly `<` character, replace it with `&lt;`. E.g. `i&lt;` - i<
* If you want to use comma in mapping, use `«COMMA»`
* Do not place a whitespace around the comma!
*
*
* !! IMPORTANT !!
* You may wonder why the extension points are used instead of any other approach to register actions.
* The reason is startup performance. Using the extension points you don't even have to load classes of actions.
* So, all actions are loaded on demand, including classes in classloader.
*/
@Deprecated(message = "Please use CommandOrMotion annotation")
@ScheduledForRemoval(inVersion = "2.9.0")
internal class ActionBeanClass : BaseKeyedLazyInstance<EditorActionHandlerBase>() {
@Attribute("implementation")
var implementation: String? = null
@Attribute("mappingModes")
var modes: String? = null
@Attribute("keys")
var keys: String? = null
val actionId: String get() = implementation?.let { EditorActionHandlerBase.getActionId(it) } ?: ""
fun getParsedKeys(): Set<List<KeyStroke>>? {
val myKeys = keys ?: return null
val escapedKeys = myKeys.splitByComma()
return EditorActionHandlerBase.parseKeysSet(escapedKeys)
}
override fun getImplementationClassName(): String? = implementation
fun getParsedModes(): Set<MappingMode>? {
val myModes = modes ?: return null
if ("ALL" == myModes) return MappingMode.ALL
val res = mutableListOf<MappingMode>()
for (c in myModes) {
when (c) {
'N' -> res += MappingMode.NORMAL
'X' -> res += MappingMode.VISUAL
'V' -> {
res += MappingMode.VISUAL
res += MappingMode.SELECT
}
'S' -> res += MappingMode.SELECT
'O' -> res += MappingMode.OP_PENDING
'I' -> res += MappingMode.INSERT
'C' -> res += MappingMode.CMD_LINE
else -> error("Wrong mapping mode: $c")
}
}
return res.toSet()
}
private fun String.splitByComma(): List<String> {
if (this.isEmpty()) return ArrayList()
val res = SmartList<String>()
var start = 0
var current = 0
while (current < this.length) {
if (this[current] == ',') {
res += this.substring(start, current)
current++
start = current
}
current++
}
res += this.substring(start, current)
return res
}
}

View File

@ -9,70 +9,42 @@
package com.maddyhome.idea.vim.handler package com.maddyhome.idea.vim.handler
import com.intellij.openapi.actionSystem.KeyboardShortcut import com.intellij.openapi.actionSystem.KeyboardShortcut
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.keymap.Keymap import com.intellij.openapi.keymap.Keymap
import com.intellij.openapi.keymap.KeymapManagerListener import com.intellij.openapi.keymap.KeymapManagerListener
import com.intellij.openapi.keymap.ex.KeymapManagerEx import com.intellij.openapi.keymap.ex.KeymapManagerEx
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity import com.intellij.openapi.startup.StartupActivity
import com.intellij.util.SingleAlarm
import com.jetbrains.rd.util.ConcurrentHashMap import com.jetbrains.rd.util.ConcurrentHashMap
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.key import com.maddyhome.idea.vim.api.key
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
// We use alarm with delay to avoid many actions in case many events are fired at the same time // We use alarm with delay to avoid many actions in case many events are fired at the same time
internal val correctorRequester = MutableSharedFlow<Unit>(replay=1, onBufferOverflow = BufferOverflow.DROP_OLDEST) // [VERSION UPDATE] 2023.3+ Replace SingleAlarm with coroutine flows https://youtrack.jetbrains.com/articles/IJPL-A-8/Alarm-Alternative
internal val correctorRequester = SingleAlarm({ correctCopilotKeymap() }, 1_000)
private val LOG = logger<CopilotKeymapCorrector>() private val LOG = logger<CopilotKeymapCorrector>()
internal class CopilotKeymapCorrector : ProjectActivity { internal class CopilotKeymapCorrector : StartupActivity {
override suspend fun execute(project: Project) { override fun runActivity(project: Project) {
project.service<CopilotKeymapCorrectorService>().start() correctorRequester.request()
correctorRequester.emit(Unit)
} }
} }
/**
* At the moment of release 2023.3 there is a problem that starting a coroutine like this
* right in the project activity will block this project activity in tests.
* To avoid that, there is an intermediate service that will allow to avoid this issue.
*
* However, in general we should start this coroutine right in the [CopilotKeymapCorrector]
*/
@OptIn(FlowPreview::class)
@Service(Service.Level.PROJECT)
internal class CopilotKeymapCorrectorService(private val cs: CoroutineScope) {
fun start() {
cs.launch {
correctorRequester
.debounce(5_000)
.collectLatest { correctCopilotKeymap() }
}
}
}
internal class IdeaVimCorrectorKeymapChangedListener : KeymapManagerListener { internal class IdeaVimCorrectorKeymapChangedListener : KeymapManagerListener {
override fun activeKeymapChanged(keymap: Keymap?) { override fun activeKeymapChanged(keymap: Keymap?) {
check(correctorRequester.tryEmit(Unit)) correctorRequester.request()
} }
override fun shortcutChanged(keymap: Keymap, actionId: String) { override fun shortcutChanged(keymap: Keymap, actionId: String) {
check(correctorRequester.tryEmit(Unit)) correctorRequester.request()
} }
override fun shortcutChanged(keymap: Keymap, actionId: String, fromSettings: Boolean) { override fun shortcutChanged(keymap: Keymap, actionId: String, fromSettings: Boolean) {
check(correctorRequester.tryEmit(Unit)) correctorRequester.request()
} }
} }
@ -95,7 +67,6 @@ private fun correctCopilotKeymap() {
// This is needed to initialize the injector in case this verification is called to fast // This is needed to initialize the injector in case this verification is called to fast
VimPlugin.getInstance() VimPlugin.getInstance()
if (!enableOctopus) return
if (injector.enabler.isEnabled()) { if (injector.enabler.isEnabled()) {
val keymap = KeymapManagerEx.getInstanceEx().activeKeymap val keymap = KeymapManagerEx.getInstanceEx().activeKeymap
val res = keymap.getShortcuts("copilot.disposeInlays") val res = keymap.getShortcuts("copilot.disposeInlays")

View File

@ -34,8 +34,6 @@ internal class EditorHandlersChainLogger : ProjectActivity {
private val editorHandlers = ExtensionPointName<EditorActionHandlerBean>("com.intellij.editorActionHandler") private val editorHandlers = ExtensionPointName<EditorActionHandlerBean>("com.intellij.editorActionHandler")
override suspend fun execute(project: Project) { override suspend fun execute(project: Project) {
if (!enableOctopus) return
val escHandlers = editorHandlers.extensionList val escHandlers = editorHandlers.extensionList
.filter { it.action == "EditorEscape" } .filter { it.action == "EditorEscape" }
.joinToString("\n") { it.implementationClass } .joinToString("\n") { it.implementationClass }

View File

@ -87,7 +87,6 @@ private fun verifyKeymap() {
// This is needed to initialize the injector in case this verification is called to fast // This is needed to initialize the injector in case this verification is called to fast
VimPlugin.getInstance() VimPlugin.getInstance()
if (!enableOctopus) return
if (!injector.enabler.isEnabled()) return if (!injector.enabler.isEnabled()) return
val keymap = KeymapManagerEx.getInstanceEx().activeKeymap val keymap = KeymapManagerEx.getInstanceEx().activeKeymap

View File

@ -27,7 +27,6 @@ import com.intellij.openapi.util.UserDataHolder
import com.intellij.openapi.util.removeUserData import com.intellij.openapi.util.removeUserData
import com.maddyhome.idea.vim.KeyHandler import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.key import com.maddyhome.idea.vim.api.key
import com.maddyhome.idea.vim.group.IjOptionConstants import com.maddyhome.idea.vim.group.IjOptionConstants
@ -53,7 +52,7 @@ internal val commandContinuation = Key.create<EditorActionHandler>("commandConti
*/ */
internal class CaretShapeEnterEditorHandler(private val nextHandler: EditorActionHandler) : EditorActionHandler() { internal class CaretShapeEnterEditorHandler(private val nextHandler: EditorActionHandler) : EditorActionHandler() {
override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) { override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) {
if (VimPlugin.isEnabled() && enableOctopus) { if (VimPlugin.isEnabled()) {
invokeLater { invokeLater {
editor.updateCaretsVisualAttributes() editor.updateCaretsVisualAttributes()
} }
@ -129,7 +128,6 @@ internal abstract class OctopusHandler(private val nextHandler: EditorActionHand
if (VimPlugin.isNotEnabled()) return false if (VimPlugin.isNotEnabled()) return false
if (!isHandlerEnabled(editor, dataContext)) return false if (!isHandlerEnabled(editor, dataContext)) return false
if (isNotActualKeyPress(dataContext)) return false if (isNotActualKeyPress(dataContext)) return false
if (!enableOctopus) return false
return true return true
} }
@ -244,7 +242,6 @@ internal class VimEscForRiderHandler(nextHandler: EditorActionHandler) : VimKeyH
override val key: String = "<Esc>" override val key: String = "<Esc>"
override fun isHandlerEnabled(editor: Editor, dataContext: DataContext?): Boolean { override fun isHandlerEnabled(editor: Editor, dataContext: DataContext?): Boolean {
if (!enableOctopus) return false
return LookupManager.getActiveLookup(editor) != null return LookupManager.getActiveLookup(editor) != null
} }
} }
@ -260,9 +257,7 @@ internal class VimEscForRiderHandler(nextHandler: EditorActionHandler) : VimKeyH
*/ */
internal class VimEscLoggerHandler(private val nextHandler: EditorActionHandler) : EditorActionHandler() { internal class VimEscLoggerHandler(private val nextHandler: EditorActionHandler) : EditorActionHandler() {
override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) { override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) {
if (enableOctopus) {
LOG.info("Esc pressed") LOG.info("Esc pressed")
}
nextHandler.execute(editor, caret, dataContext) nextHandler.execute(editor, caret, dataContext)
} }
@ -288,9 +283,7 @@ internal class StartNewLineBeforeCurrentDetector(nextHandler: EditorActionHandle
internal open class StartNewLineDetectorBase(private val nextHandler: EditorActionHandler) : EditorActionHandler() { internal open class StartNewLineDetectorBase(private val nextHandler: EditorActionHandler) : EditorActionHandler() {
override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) { override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) {
if (enableOctopus) {
DataManager.getInstance().saveInDataContext(dataContext, Util.key, true) DataManager.getInstance().saveInDataContext(dataContext, Util.key, true)
}
nextHandler.execute(editor, caret, dataContext) nextHandler.execute(editor, caret, dataContext)
} }
@ -318,9 +311,7 @@ internal open class StartNewLineDetectorBase(private val nextHandler: EditorActi
*/ */
internal class VimEnterLoggerHandler(private val nextHandler: EditorActionHandler) : EditorActionHandler() { internal class VimEnterLoggerHandler(private val nextHandler: EditorActionHandler) : EditorActionHandler() {
override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) { override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) {
if (enableOctopus) {
LOG.info("Enter pressed") LOG.info("Enter pressed")
}
nextHandler.execute(editor, caret, dataContext) nextHandler.execute(editor, caret, dataContext)
} }
@ -350,7 +341,6 @@ internal abstract class VimKeyHandler(nextHandler: EditorActionHandler?) : Octop
} }
internal fun isOctopusEnabled(s: KeyStroke, editor: Editor): Boolean { internal fun isOctopusEnabled(s: KeyStroke, editor: Editor): Boolean {
if (!enableOctopus) return false
// CMD line has a different processing mechanizm: the processing actions are registered // CMD line has a different processing mechanizm: the processing actions are registered
// for the input field component. These keys are not dispatched via the octopus handler. // for the input field component. These keys are not dispatched via the octopus handler.
if (editor.vim.mode is Mode.CMD_LINE) return false if (editor.vim.mode is Mode.CMD_LINE) return false
@ -360,6 +350,3 @@ internal fun isOctopusEnabled(s: KeyStroke, editor: Editor): Boolean {
} }
return false return false
} }
internal val enableOctopus: Boolean
get() = injector.globalOptions().octopushandler

View File

@ -46,6 +46,12 @@ public class EditorHelper {
return editor.getScrollingModel().getVisibleAreaOnScrollingFinished(); return editor.getScrollingModel().getVisibleAreaOnScrollingFinished();
} }
//("Use extension function with the same name on VimEditor")
@Deprecated
public static boolean isLineEmpty(final @NotNull Editor editor, final int line, final boolean allowBlanks) {
return EngineEditorHelperKt.isLineEmpty(new IjVimEditor(editor), line, allowBlanks);
}
public static boolean scrollVertically(@NotNull Editor editor, int verticalOffset) { public static boolean scrollVertically(@NotNull Editor editor, int verticalOffset) {
final ScrollingModel scrollingModel = editor.getScrollingModel(); final ScrollingModel scrollingModel = editor.getScrollingModel();
final Rectangle area = scrollingModel.getVisibleAreaOnScrollingFinished(); final Rectangle area = scrollingModel.getVisibleAreaOnScrollingFinished();

View File

@ -23,6 +23,43 @@ import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.inBlockSelection import com.maddyhome.idea.vim.state.mode.inBlockSelection
import java.util.stream.Collectors import java.util.stream.Collectors
/**
* This annotation is created for test functions (methods).
* It means that the original vim behavior has small differences from behavior of IdeaVim.
* [shouldBeFixed] flag indicates whether the given functionality should be fixed
* or the given behavior is normal for IdeaVim and should be leaved as is.
*
* E.g. after execution of some commands original vim has the following text:
* Hello1
* Hello2
* Hello3
*
* But IdeaVim gives you:
* Hello1
*
* Hello2
* Hello3
*
* In this case you should still create the test function and mark this function with [VimBehaviorDiffers] annotation.
*
* Why does this annotation exist?
* After creating some functionality you can understand that IdeaVim has a bit different behavior, but you
* cannot fix it right now because of any reason (bugs in IDE,
* the impossibility of this functionality in IDEA (*[shouldBeFixed] == false*), leak of time for fixing).
* In that case, you should NOT remove the corresponding test or leave it without any marks that this test
* not fully convenient with vim, but leave the test with IdeaVim's behavior and put this annotation
* with description of how original vim works.
*
* Note that using this annotation should be avoided as much as possible and behavior of IdeaVim should be as close
* to vim as possible.
*/
@Target(AnnotationTarget.FUNCTION)
internal annotation class VimBehaviorDiffers(
val originalVimAfter: String = "",
val description: String = "",
val shouldBeFixed: Boolean = true,
)
internal fun <T : Comparable<T>> sort(a: T, b: T) = if (a > b) b to a else a to b internal fun <T : Comparable<T>> sort(a: T, b: T) = if (a > b) b to a else a to b
// TODO Should be replaced with VimEditor.carets() // TODO Should be replaced with VimEditor.carets()

View File

@ -23,8 +23,6 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.maddyhome.idea.vim.VimPlugin; import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.api.EngineEditorHelperKt; import com.maddyhome.idea.vim.api.EngineEditorHelperKt;
import com.maddyhome.idea.vim.api.VimEditor; import com.maddyhome.idea.vim.api.VimEditor;
import com.maddyhome.idea.vim.regexp.*;
import com.maddyhome.idea.vim.regexp.match.VimMatchResult;
import com.maddyhome.idea.vim.state.mode.Mode; import com.maddyhome.idea.vim.state.mode.Mode;
import com.maddyhome.idea.vim.state.VimStateMachine; import com.maddyhome.idea.vim.state.VimStateMachine;
import com.maddyhome.idea.vim.common.CharacterPosition; import com.maddyhome.idea.vim.common.CharacterPosition;
@ -32,6 +30,8 @@ import com.maddyhome.idea.vim.common.Direction;
import com.maddyhome.idea.vim.common.TextRange; import com.maddyhome.idea.vim.common.TextRange;
import com.maddyhome.idea.vim.newapi.IjVimCaret; import com.maddyhome.idea.vim.newapi.IjVimCaret;
import com.maddyhome.idea.vim.newapi.IjVimEditor; import com.maddyhome.idea.vim.newapi.IjVimEditor;
import com.maddyhome.idea.vim.regexp.CharPointer;
import com.maddyhome.idea.vim.regexp.RegExp;
import kotlin.Pair; import kotlin.Pair;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -42,10 +42,10 @@ import java.util.function.Function;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static com.maddyhome.idea.vim.api.VimInjectorKt.*; import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
import static com.maddyhome.idea.vim.api.VimInjectorKt.options;
import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.checkInString; import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.checkInString;
import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.shouldIgnoreCase; import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.shouldIgnoreCase;
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.globalIjOptions;
/** /**
* Helper methods for searching text * Helper methods for searching text
@ -59,8 +59,6 @@ public class SearchHelper {
/** /**
* Find text matching the given pattern. * Find text matching the given pattern.
* *
* @deprecated Use IjVimSearchHelper.findPattern instead
*
* <p>See search.c:searchit</p> * <p>See search.c:searchit</p>
* *
* @param editor The editor to search in * @param editor The editor to search in
@ -71,13 +69,12 @@ public class SearchHelper {
* @return A TextRange representing the result, or null * @return A TextRange representing the result, or null
*/ */
@Nullable @Nullable
@Deprecated
public static TextRange findPattern(@NotNull Editor editor, public static TextRange findPattern(@NotNull Editor editor,
@Nullable String pattern, @Nullable String pattern,
int startOffset, int startOffset,
int count, int count,
EnumSet<SearchOptions> searchOptions) { EnumSet<SearchOptions> searchOptions) {
if (pattern == null || pattern.isEmpty()) { if (pattern == null || pattern.length() == 0) {
logger.warn("Pattern is null or empty. Cannot perform search"); logger.warn("Pattern is null or empty. Cannot perform search");
return null; return null;
} }
@ -350,8 +347,6 @@ public class SearchHelper {
/** /**
* Find all occurrences of the pattern. * Find all occurrences of the pattern.
* *
* @deprecated Use IjVimSearchHelper.findall instead
*
* @param editor The editor to search in * @param editor The editor to search in
* @param pattern The pattern to search for * @param pattern The pattern to search for
* @param startLine The start line of the range to search for * @param startLine The start line of the range to search for
@ -359,33 +354,12 @@ public class SearchHelper {
* @param ignoreCase Case sensitive or insensitive searching * @param ignoreCase Case sensitive or insensitive searching
* @return A list of TextRange objects representing the results * @return A list of TextRange objects representing the results
*/ */
@Deprecated
public static @NotNull List<TextRange> findAll(@NotNull Editor editor, public static @NotNull List<TextRange> findAll(@NotNull Editor editor,
@NotNull String pattern, @NotNull String pattern,
int startLine, int startLine,
int endLine, int endLine,
boolean ignoreCase) { boolean ignoreCase) {
final List<TextRange> results = Lists.newArrayList(); final List<TextRange> results = Lists.newArrayList();
if (globalIjOptions(injector).getUseNewRegex()) {
final List<VimRegexOptions> options = new ArrayList<>();
if (globalOptions(injector).getSmartcase()) options.add(VimRegexOptions.SMART_CASE);
if (globalOptions(injector).getIgnorecase()) options.add(VimRegexOptions.IGNORE_CASE);
VimEditor vimEditor = new IjVimEditor(editor);
try {
// TODO: we shouldn't care about the ignoreCase argument, and instead just look into the editor options.
// It would require a refactor, so for now prepend \c or \C to "force" ignoreCase
String newPattern = (ignoreCase ? "\\c" : "\\C") + pattern;
VimRegex regex = new VimRegex(newPattern);
List<VimMatchResult.Success> foundMatches = regex.findAll(vimEditor, vimEditor.getLineStartOffset(startLine), vimEditor.getLineEndOffset(endLine == -1 ? vimEditor.lineCount() - 1 : endLine) + 1, options);
for (VimMatchResult.Success match : foundMatches) results.add(match.getRange());
return results;
} catch (VimRegexException e) {
injector.getMessages().showStatusBarMessage(vimEditor, e.getMessage());
return results;
}
}
final int lineCount = new IjVimEditor(editor).lineCount(); final int lineCount = new IjVimEditor(editor).lineCount();
final int actualEndLine = endLine == -1 ? lineCount - 1 : endLine; final int actualEndLine = endLine == -1 ? lineCount - 1 : endLine;
@ -428,10 +402,6 @@ public class SearchHelper {
return results; return results;
} }
/**
* @deprecated Use IjVimSearchHelper.findSection instead
*/
@Deprecated
public static int findSection(@NotNull Editor editor, @NotNull Caret caret, char type, int dir, int count) { public static int findSection(@NotNull Editor editor, @NotNull Caret caret, char type, int dir, int count) {
CharSequence chars = editor.getDocument().getCharsSequence(); CharSequence chars = editor.getDocument().getCharsSequence();
int line = caret.getLogicalPosition().line + dir; int line = caret.getLogicalPosition().line + dir;
@ -458,10 +428,6 @@ public class SearchHelper {
return res; return res;
} }
/**
* @deprecated Use IjVimSearchHelper.findUnmatchedBlock instead
*/
@Deprecated
public static int findUnmatchedBlock(@NotNull Editor editor, @NotNull Caret caret, char type, int count) { public static int findUnmatchedBlock(@NotNull Editor editor, @NotNull Caret caret, char type, int count) {
CharSequence chars = editor.getDocument().getCharsSequence(); CharSequence chars = editor.getDocument().getCharsSequence();
int pos = caret.getOffset(); int pos = caret.getOffset();
@ -481,8 +447,6 @@ public class SearchHelper {
/** /**
* Find block enclosing the caret * Find block enclosing the caret
* *
* @deprecated Use IjVimSearchHelper.findBlockRange instead
*
* @param editor The editor to search in * @param editor The editor to search in
* @param caret The caret currently at * @param caret The caret currently at
* @param type The type of block, e.g. (, [, {, < * @param type The type of block, e.g. (, [, {, <
@ -834,10 +798,6 @@ public class SearchHelper {
} }
/**
* @deprecated Use IjVimSearchHelper.findBlockTagRange instead
*/
@Deprecated
public static @Nullable TextRange findBlockTagRange(@NotNull Editor editor, public static @Nullable TextRange findBlockTagRange(@NotNull Editor editor,
@NotNull Caret caret, @NotNull Caret caret,
int count, int count,
@ -1370,10 +1330,6 @@ public class SearchHelper {
return new TextRange(start, end); return new TextRange(start, end);
} }
/**
* @deprecated Use IjVimSearchHelper.findWordUnderCursor instead
*/
@Deprecated
@Contract("_, _, _, _, _, _, _ -> new") @Contract("_, _, _, _, _, _, _ -> new")
public static @NotNull TextRange findWordUnderCursor(@NotNull Editor editor, public static @NotNull TextRange findWordUnderCursor(@NotNull Editor editor,
@NotNull Caret caret, @NotNull Caret caret,
@ -1559,16 +1515,10 @@ public class SearchHelper {
} }
} }
/**
* @deprecated Use IjVimSearchHelper.findMethodStart instead
*/
public static int findMethodStart(@NotNull Editor editor, @NotNull Caret caret, int count) { public static int findMethodStart(@NotNull Editor editor, @NotNull Caret caret, int count) {
return PsiHelper.findMethodStart(editor, caret.getOffset(), count); return PsiHelper.findMethodStart(editor, caret.getOffset(), count);
} }
/**
* @deprecated Use IjVimSearchHelper.findMethodEnd instead
*/
public static int findMethodEnd(@NotNull Editor editor, @NotNull Caret caret, int count) { public static int findMethodEnd(@NotNull Editor editor, @NotNull Caret caret, int count) {
return PsiHelper.findMethodEnd(editor, caret.getOffset(), count); return PsiHelper.findMethodEnd(editor, caret.getOffset(), count);
} }

View File

@ -26,7 +26,6 @@ import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.options import com.maddyhome.idea.vim.api.options
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.ex.ranges.LineRange import com.maddyhome.idea.vim.ex.ranges.LineRange
import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import org.jetbrains.annotations.Contract import org.jetbrains.annotations.Contract
import java.awt.Color import java.awt.Color
@ -106,7 +105,7 @@ private fun updateSearchHighlights(
val startLine = searchRange?.startLine ?: 0 val startLine = searchRange?.startLine ?: 0
val endLine = searchRange?.endLine ?: -1 val endLine = searchRange?.endLine ?: -1
val results = val results =
injector.searchHelper.findAll(IjVimEditor(editor), pattern, startLine, endLine, shouldIgnoreCase(pattern, shouldIgnoreSmartCase)) SearchHelper.findAll(editor, pattern, startLine, endLine, shouldIgnoreCase(pattern, shouldIgnoreSmartCase))
if (results.isNotEmpty()) { if (results.isNotEmpty()) {
currentMatchOffset = findClosestMatch(editor, results, initialOffset, forwards) currentMatchOffset = findClosestMatch(editor, results, initialOffset, forwards)
highlightSearchResults(editor, pattern, results, currentMatchOffset) highlightSearchResults(editor, pattern, results, currentMatchOffset)
@ -120,7 +119,7 @@ private fun updateSearchHighlights(
} }
if (shouldIgnoreSmartCase) searchOptions.add(SearchOptions.IGNORE_SMARTCASE) if (shouldIgnoreSmartCase) searchOptions.add(SearchOptions.IGNORE_SMARTCASE)
if (!forwards) searchOptions.add(SearchOptions.BACKWARDS) if (!forwards) searchOptions.add(SearchOptions.BACKWARDS)
val result = injector.searchHelper.findPattern(IjVimEditor(editor), pattern, initialOffset, 1, searchOptions) val result = SearchHelper.findPattern(editor, pattern, initialOffset, 1, searchOptions)
if (result != null) { if (result != null) {
currentMatchOffset = result.startOffset currentMatchOffset = result.startOffset
val results = listOf(result) val results = listOf(result)

View File

@ -31,4 +31,10 @@ public object StringHelper {
return Arrays.stream(string).flatMap { o: String -> injector.parser.parseKeys(o).stream() } return Arrays.stream(string).flatMap { o: String -> injector.parser.parseKeys(o).stream() }
.collect(Collectors.toList()) .collect(Collectors.toList())
} }
@JvmStatic
@Deprecated("Use KeyStroke.isCloseKeyStroke", ReplaceWith("stroke.isCloseKeyStroke()"))
public fun isCloseKeyStroke(stroke: KeyStroke): Boolean {
return stroke.isCloseKeyStroke()
}
} }

View File

@ -11,15 +11,14 @@ import com.google.common.collect.Lists
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import javax.swing.KeyStroke import javax.swing.KeyStroke
// Do not remove until it's used in EasyMotion plugin in tests internal class TestInputModel private constructor() {
public class TestInputModel private constructor() {
private val myKeyStrokes: MutableList<KeyStroke> = Lists.newArrayList() private val myKeyStrokes: MutableList<KeyStroke> = Lists.newArrayList()
public fun setKeyStrokes(keyStrokes: List<KeyStroke>) { fun setKeyStrokes(keyStrokes: List<KeyStroke>) {
myKeyStrokes.clear() myKeyStrokes.clear()
myKeyStrokes.addAll(keyStrokes) myKeyStrokes.addAll(keyStrokes)
} }
public fun nextKeyStroke(): KeyStroke? { fun nextKeyStroke(): KeyStroke? {
// Return key from the unfinished mapping // Return key from the unfinished mapping
/* /*
MappingStack mappingStack = KeyHandler.getInstance().getMappingStack(); MappingStack mappingStack = KeyHandler.getInstance().getMappingStack();
@ -34,9 +33,9 @@ if (mappingStack.hasStroke()) {
} }
} }
public companion object { companion object {
@JvmStatic @JvmStatic
public fun getInstance(editor: Editor): TestInputModel { fun getInstance(editor: Editor): TestInputModel {
var model = editor.vimTestInputModel var model = editor.vimTestInputModel
if (model == null) { if (model == null) {
model = TestInputModel() model = TestInputModel()

View File

@ -55,7 +55,6 @@ import com.maddyhome.idea.vim.VimTypedActionHandler
import com.maddyhome.idea.vim.api.LocalOptionInitialisationScenario import com.maddyhome.idea.vim.api.LocalOptionInitialisationScenario
import com.maddyhome.idea.vim.api.Options import com.maddyhome.idea.vim.api.Options
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.coerceOffset
import com.maddyhome.idea.vim.api.getLineEndForOffset import com.maddyhome.idea.vim.api.getLineEndForOffset
import com.maddyhome.idea.vim.api.getLineStartForOffset import com.maddyhome.idea.vim.api.getLineStartForOffset
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
@ -134,14 +133,14 @@ internal object VimListenerManager {
fun turnOn() { fun turnOn() {
GlobalListeners.enable() GlobalListeners.enable()
EditorListeners.addAll() EditorListeners.addAll()
check(correctorRequester.tryEmit(Unit)) correctorRequester.request()
check(keyCheckRequests.tryEmit(Unit)) check(keyCheckRequests.tryEmit(Unit))
} }
fun turnOff() { fun turnOff() {
GlobalListeners.disable() GlobalListeners.disable()
EditorListeners.removeAll() EditorListeners.removeAll()
check(correctorRequester.tryEmit(Unit)) correctorRequester.request()
} }
object GlobalListeners { object GlobalListeners {
@ -307,7 +306,7 @@ internal object VimListenerManager {
if (VimPlugin.isNotEnabled()) return if (VimPlugin.isNotEnabled()) return
MotionGroup.fileEditorManagerSelectionChangedCallback(event) MotionGroup.fileEditorManagerSelectionChangedCallback(event)
FileGroup.fileEditorManagerSelectionChangedCallback(event) FileGroup.fileEditorManagerSelectionChangedCallback(event)
VimPlugin.getSearch().fileEditorManagerSelectionChangedCallback(event) SearchGroup.fileEditorManagerSelectionChangedCallback(event)
OptionGroup.fileEditorManagerSelectionChangedCallback(event) OptionGroup.fileEditorManagerSelectionChangedCallback(event)
} }
} }
@ -463,17 +462,11 @@ internal object VimListenerManager {
if (lineEnd == endOffset - 1) { if (lineEnd == endOffset - 1) {
// When starting on an empty line and dragging vertically upwards onto // When starting on an empty line and dragging vertically upwards onto
// another line, the selection should include the entirety of the empty line // another line, the selection should include the entirety of the empty line
caret.setSelection( caret.setSelection(endOffset + 1, startOffset)
ijVimEditor.coerceOffset(endOffset + 1).point,
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(lineEnd, lineEnd - 1)
ijVimEditor.coerceOffset(lineEnd).point,
ijVimEditor.coerceOffset(lineEnd - 1).point,
)
} }
} }
//endregion //endregion

View File

@ -0,0 +1,23 @@
/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.newapi
import com.maddyhome.idea.vim.api.VimActionsInitiator
import com.maddyhome.idea.vim.handler.ActionBeanClass
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
import org.jetbrains.annotations.ApiStatus
@Deprecated(message = "Please use CommandOrMotion annotation")
@ApiStatus.ScheduledForRemoval(inVersion = "2.9.0")
internal class IjVimActionsInitiator(val bean: ActionBeanClass) : VimActionsInitiator {
override fun getInstance(): EditorActionHandlerBase = bean.instance
}
internal val VimActionsInitiator.ij: ActionBeanClass
get() = (this as IjVimActionsInitiator).bean

View File

@ -1,181 +0,0 @@
/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.newapi
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.util.Ref
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.Options
import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.VimSearchGroupBase
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.helper.TestInputModel.Companion.getInstance
import com.maddyhome.idea.vim.helper.addSubstitutionConfirmationHighlight
import com.maddyhome.idea.vim.helper.highlightSearchResults
import com.maddyhome.idea.vim.helper.isCloseKeyStroke
import com.maddyhome.idea.vim.helper.shouldIgnoreCase
import com.maddyhome.idea.vim.helper.updateSearchHighlights
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener
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.parser.VimscriptParser.parseExpression
import org.jetbrains.annotations.TestOnly
import javax.swing.KeyStroke
public open class IjVimSearchGroup : VimSearchGroupBase() {
init {
// TODO: Investigate migrating these listeners to use the effective value change listener
// This would allow us to update the editor we're told to update, rather than looping over all projects and updating
// the highlights in that project's current document's open editors (see VIM-2779).
// However, we probably only want to update the editors associated with the current document, so maybe the whole
// code needs to be reworked. We're currently using the same update code for changes in the search term as well as
// changes in the search options.
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.hlsearch) {
resetSearchHighlight()
updateSearchHighlights(true)
}
val updateHighlightsIfVisible = GlobalOptionChangeListener {
if (showSearchHighlight) {
updateSearchHighlights(true)
}
}
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.ignorecase, updateHighlightsIfVisible)
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.smartcase, updateHighlightsIfVisible)
}
private var showSearchHighlight: Boolean = injector.globalOptions().hlsearch
override fun highlightSearchLines(
editor: VimEditor,
startLine: Int,
endLine: Int,
) {
val pattern = getLastUsedPattern()
if (pattern != null) {
val results = injector.searchHelper.findAll(
editor, pattern, startLine, endLine,
shouldIgnoreCase(pattern, lastIgnoreSmartCase)
)
highlightSearchResults(editor.ij, pattern, results, -1)
}
}
override fun updateSearchHighlights(force: Boolean) {
updateSearchHighlights(getLastUsedPattern(), lastIgnoreSmartCase, showSearchHighlight, force)
}
override fun resetIncsearchHighlights() {
updateSearchHighlights(getLastUsedPattern(), lastIgnoreSmartCase, showSearchHighlight, true)
}
override fun confirmChoice(
editor: VimEditor,
match: String,
caret: VimCaret,
startOffset: Int,
): ReplaceConfirmationChoice {
val result: Ref<ReplaceConfirmationChoice> = Ref.create(ReplaceConfirmationChoice.QUIT)
val keyStrokeProcessor: Function1<KeyStroke, Boolean> = label@{ key: KeyStroke ->
val choice: ReplaceConfirmationChoice
val c = key.keyChar
choice = if (key.isCloseKeyStroke() || c == 'q') {
ReplaceConfirmationChoice.QUIT
} else if (c == 'y') {
ReplaceConfirmationChoice.SUBSTITUTE_THIS
} else if (c == 'l') {
ReplaceConfirmationChoice.SUBSTITUTE_LAST
} else if (c == 'n') {
ReplaceConfirmationChoice.SKIP
} else if (c == 'a') {
ReplaceConfirmationChoice.SUBSTITUTE_ALL
} else {
return@label true
}
// TODO: Handle <C-E> and <C-Y>
result.set(choice)
false
}
if (ApplicationManager.getApplication().isUnitTestMode) {
caret.moveToOffset(startOffset)
val inputModel = getInstance(editor.ij)
var key = inputModel.nextKeyStroke()
while (key != null) {
if (!keyStrokeProcessor.invoke(key)) {
break
}
key = inputModel.nextKeyStroke()
}
} else {
// XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for this method
val exEntryPanel: com.maddyhome.idea.vim.ui.ex.ExEntryPanel =
com.maddyhome.idea.vim.ui.ex.ExEntryPanel.getInstanceWithoutShortcuts()
val context = injector.executionContextManager.onEditor(editor, null)
exEntryPanel.activate(
editor.ij,
(context as IjEditorExecutionContext).context,
MessageHelper.message("replace.with.0", match),
"",
1
)
caret.moveToOffset(startOffset)
ModalEntry.activate(editor, keyStrokeProcessor)
exEntryPanel.deactivate(true, false)
}
return result.get()
}
override fun parseVimScriptExpression(expressionString: String): Expression? {
return parseExpression(expressionString)
}
override fun addSubstitutionConfirmationHighlight(editor: VimEditor, startOffset: Int, endOffset: Int) {
val hl = addSubstitutionConfirmationHighlight(
(editor as IjVimEditor).editor,
startOffset,
endOffset
)
editor.editor.markupModel.removeHighlighter(hl)
}
override fun setLatestMatch(match: String) {
SubmatchFunctionHandler.getInstance().latestMatch = match
}
override fun replaceString(
editor: VimEditor,
startOffset: Int,
endOffset: Int,
newString: String,
) {
ApplicationManager.getApplication().runWriteAction {
(editor as IjVimEditor).editor.document.replaceString(startOffset, endOffset, newString)
}
}
@TestOnly
override fun resetState() {
super.resetState()
showSearchHighlight = injector.globalOptions().hlsearch
}
override fun resetSearchHighlight() {
showSearchHighlight = injector.globalOptions().hlsearch
}
override fun clearSearchHighlight() {
showSearchHighlight = false
updateSearchHighlights(false)
}
}

View File

@ -9,144 +9,49 @@
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.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.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.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 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() {
override fun findSection(editor: VimEditor, caret: ImmutableVimCaret, type: Char, dir: Int, count: Int): Int {
companion object { return SearchHelper.findSection(
private const val BLOCK_CHARS = "{}()[]<>" (editor as IjVimEditor).editor,
private val logger = Logger.getInstance(IjVimSearchHelper::class.java.name) (caret as IjVimCaret).caret,
} type,
override fun findSection( dir,
editor: VimEditor, count,
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 {
return PsiHelper.findMethodEnd(editor.ij, caret.ij.offset, count) return SearchHelper.findMethodEnd(
(editor as IjVimEditor).editor,
(caret as IjVimCaret).caret,
count,
)
} }
override fun findMethodStart(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int { override fun findMethodStart(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int {
return PsiHelper.findMethodStart(editor.ij, caret.ij.offset, count) return SearchHelper.findMethodStart(
(editor as IjVimEditor).editor,
(caret as IjVimCaret).caret,
count,
)
} }
override fun findUnmatchedBlock(editor: VimEditor, caret: ImmutableVimCaret, type: Char, count: Int): Int { override fun findUnmatchedBlock(editor: VimEditor, caret: ImmutableVimCaret, type: Char, count: Int): Int {
val chars: CharSequence = editor.ij.document.charsSequence return SearchHelper.findUnmatchedBlock(
var pos: Int = caret.ij.offset (editor as IjVimEditor).editor,
val loc = BLOCK_CHARS.indexOf(type) (caret as IjVimCaret).caret,
// What direction should we go now (-1 is backward, 1 is forward) type,
val dir = if (loc % 2 == 0) Direction.BACKWARDS else Direction.FORWARDS count,
// 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(
@ -156,44 +61,11 @@ internal class IjVimSearchHelper : VimSearchHelperBase() {
count: Int, count: Int,
searchOptions: EnumSet<SearchOptions>?, searchOptions: EnumSet<SearchOptions>?,
): TextRange? { ): TextRange? {
return if (injector.globalIjOptions().useNewRegex) super.findPattern(editor, pattern, startOffset, count, searchOptions) return SearchHelper.findPattern(editor.ij, pattern, startOffset, count, searchOptions)
else SearchHelper.findPattern(editor.ij, pattern, startOffset, count, searchOptions)
}
override fun findAll(
editor: VimEditor,
pattern: String,
startLine: Int,
endLine: Int,
ignoreCase: Boolean,
): List<TextRange> {
return if (injector.globalIjOptions().useNewRegex) super.findAll(editor, 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 { override fun findNextCharacterOnLine(editor: VimEditor, caret: ImmutableVimCaret, count: Int, ch: Char): Int {
val line: Int = caret.ij.logicalPosition.line return SearchHelper.findNextCharacterOnLine(editor.ij, caret.ij, count, ch)
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( override fun findWordUnderCursor(
@ -205,347 +77,11 @@ internal class IjVimSearchHelper : VimSearchHelperBase() {
isBig: Boolean, isBig: Boolean,
hasSelection: Boolean, hasSelection: Boolean,
): TextRange { ): TextRange {
if (logger.isDebugEnabled) { return SearchHelper.findWordUnderCursor(editor.ij, caret.ij, count, dir, isOuter, isBig, hasSelection)
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? { override fun findBlockTagRange(editor: VimEditor, caret: ImmutableVimCaret, count: Int, isOuter: Boolean): TextRange? {
var counter = count return SearchHelper.findBlockTagRange(editor.ij, caret.ij, count, isOuter)
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( override fun findBlockRange(
@ -555,138 +91,6 @@ internal class IjVimSearchHelper : VimSearchHelperBase() {
count: Int, count: Int,
isOuter: Boolean, isOuter: Boolean,
): TextRange? { ): TextRange? {
val chars: CharSequence = editor.ij.document.charsSequence return SearchHelper.findBlockRange(editor.ij, caret.ij, type, count, isOuter)
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)
} }
} }

View File

@ -0,0 +1,39 @@
/*
* 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.
*/
@file:Suppress("DEPRECATION", "unused")
package com.maddyhome.idea.vim.option
import com.maddyhome.idea.vim.api.Options
import com.maddyhome.idea.vim.options.helpers.KeywordOptionHelper
/**
* COMPATIBILITY-LAYER: Added a class and package
* Please see: https://jb.gg/zo8n0r
*/
@Deprecated("Use VimInjector.optionService")
// Used by:
// ideavim-sneak 1.2.0 which is current - ignorecase + smartcase, both just access isSet property
// IdeaVim-EasyMotion 1.9 + 1.10 - OptionsManger.iskeyword.toRegex() (plus more in tests, which are already broken)
// (which-key 0.6.2 uses timeout + timeoutlen, now removed. That plugin version is broken due to other changes)
public object OptionsManager {
public val ignorecase: ToggleOption
get() = ToggleOption(Options.ignorecase)
public val smartcase: ToggleOption
get() = ToggleOption(Options.smartcase)
public val iskeyword: KeywordOption
get() = KeywordOption(KeywordOptionHelper)
}
@Deprecated("No longer used. Use StringOption or KeywordOptionHelper")
public class KeywordOption(public val helper: KeywordOptionHelper) {
public fun toRegex(): List<String> {
return helper.toRegex()
}
}

View File

@ -8,7 +8,6 @@ import com.maddyhome.idea.vim.helper.Msg
import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.NonNls
import java.util.* import java.util.*
@Deprecated("Please use VimRegex class instead")
internal class RegExp { internal class RegExp {
/* /*
* The first byte of the regexp internal "program" is actually this magic * The first byte of the regexp internal "program" is actually this magic

View File

@ -37,7 +37,7 @@ internal object ShowCmd {
private const val SHOWCMD_COLS = 10 private const val SHOWCMD_COLS = 10
@NonNls @NonNls
internal const val ID = "IdeaVimShowCmd" internal const val ID = "IdeaVim::ShowCmd"
@NlsSafe @NlsSafe
internal const val displayName = "IdeaVim showcmd" internal const val displayName = "IdeaVim showcmd"

View File

@ -24,7 +24,7 @@ import com.maddyhome.idea.vim.ui.widgets.VimWidgetListener
import com.maddyhome.idea.vim.ui.widgets.mode.VimStatusBarWidget import com.maddyhome.idea.vim.ui.widgets.mode.VimStatusBarWidget
import java.awt.Component import java.awt.Component
private const val ID = "IdeaVimMacro" private const val ID = "IdeaVim::Macro"
internal class MacroWidgetFactory : StatusBarWidgetFactory, VimStatusBarWidget { internal class MacroWidgetFactory : StatusBarWidgetFactory, VimStatusBarWidget {
private var content: String = "" private var content: String = ""

View File

@ -19,7 +19,7 @@ import com.maddyhome.idea.vim.ui.widgets.VimWidgetListener
public class ModeWidgetFactory : StatusBarWidgetFactory { public class ModeWidgetFactory : StatusBarWidgetFactory {
public companion object { public companion object {
public const val ID: String = "IdeaVimMode" public const val ID: String = "IdeaVim::Mode"
} }
override fun getId(): String { override fun getId(): String {

View File

@ -351,7 +351,7 @@ public class ModeWidgetPopup : AnAction() {
} }
public enum class ModeWidgetTheme(private var value: String) { public enum class ModeWidgetTheme(private var value: String) {
TERM("Term"), TEST("Nord-Aurora (testing, will be removed)"),
COLORLESS("Colorless"); COLORLESS("Colorless");
override fun toString(): String { override fun toString(): String {
@ -363,6 +363,6 @@ public enum class ModeWidgetTheme(private var value: String) {
return ModeWidgetTheme.values().firstOrNull { it.value == string } return ModeWidgetTheme.values().firstOrNull { it.value == string }
} }
public fun getDefaultTheme(): ModeWidgetTheme = TERM public fun getDefaultTheme(): ModeWidgetTheme = TEST
} }
} }

View File

@ -22,12 +22,14 @@ public fun getModeBackground(mode: Mode?): Color {
val themeString = injector.variableService.getVimVariable("widget_mode_theme$keyPostfix")?.asString() ?: "" val themeString = injector.variableService.getVimVariable("widget_mode_theme$keyPostfix")?.asString() ?: ""
val theme = ModeWidgetTheme.parseString(themeString) ?: ModeWidgetTheme.getDefaultTheme() val theme = ModeWidgetTheme.parseString(themeString) ?: ModeWidgetTheme.getDefaultTheme()
when (theme) { when (theme) {
ModeWidgetTheme.TERM -> { ModeWidgetTheme.TEST -> {
return when (mode) { return when (mode) {
Mode.INSERT -> Color.decode("#F4BF75") Mode.INSERT -> Color.decode("#D08770")
Mode.REPLACE -> Color.decode("#AC4242") Mode.REPLACE -> Color.decode("#EBCB8B")
is Mode.NORMAL, is Mode.CMD_LINE -> Color.decode("#90A959") is Mode.NORMAL -> Color.decode("#BF616A")
is Mode.VISUAL, is Mode.SELECT -> Color.decode("#6A9FB5") is Mode.CMD_LINE -> Color.decode("#A3BE8C")
is Mode.VISUAL -> Color.decode("#B48EAD")
is Mode.SELECT -> Color.decode("#B48EAD")
is Mode.OP_PENDING, null -> UIUtil.getPanelBackground() is Mode.OP_PENDING, null -> UIUtil.getPanelBackground()
} }
} }
@ -82,7 +84,7 @@ public fun getModeForeground(mode: Mode?): Color {
val themeString = injector.variableService.getVimVariable("widget_mode_theme$keyPostfix")?.asString() ?: "" val themeString = injector.variableService.getVimVariable("widget_mode_theme$keyPostfix")?.asString() ?: ""
val theme = ModeWidgetTheme.parseString(themeString) ?: ModeWidgetTheme.getDefaultTheme() val theme = ModeWidgetTheme.parseString(themeString) ?: ModeWidgetTheme.getDefaultTheme()
return when (theme) { return when (theme) {
ModeWidgetTheme.TERM -> UIUtil.getPanelBackground() ModeWidgetTheme.TEST -> Color.decode("#2E3440")
ModeWidgetTheme.COLORLESS -> UIUtil.getLabelForeground() ModeWidgetTheme.COLORLESS -> UIUtil.getLabelForeground()
} }
} else { } else {

View File

@ -48,7 +48,10 @@ public class VimModeWidget(public val project: Project) : CustomStatusBarWidget,
private const val SELECT_LINE = "S-LINE" private const val SELECT_LINE = "S-LINE"
private const val SELECT_BLOCK = "S-BLOCK" private const val SELECT_BLOCK = "S-BLOCK"
} }
private val label = JBLabelWiderThan(setOf(REPLACE)).apply { isOpaque = true } private val useColors = injector.globalIjOptions().colorfulmodewidget
private val label = JBLabelWiderThan(setOf(REPLACE)).apply {
isOpaque = useColors
}
init { init {
val mode = getFocusedEditor(project)?.vim?.mode val mode = getFocusedEditor(project)?.vim?.mode
@ -93,9 +96,11 @@ public class VimModeWidget(public val project: Project) : CustomStatusBarWidget,
private fun updateLabel(mode: Mode?) { private fun updateLabel(mode: Mode?) {
label.text = getModeText(mode) label.text = getModeText(mode)
if (useColors) {
label.foreground = getModeForeground(mode) label.foreground = getModeForeground(mode)
label.background = getModeBackground(mode) label.background = getModeBackground(mode)
} }
}
private fun getFocusedEditor(project: Project): Editor? { private fun getFocusedEditor(project: Project): Editor? {
val fileEditorManager = FileEditorManager.getInstance(project) val fileEditorManager = FileEditorManager.getInstance(project)

View File

@ -11,7 +11,6 @@ package com.maddyhome.idea.vim.vimscript.model.commands
import com.intellij.openapi.editor.RangeMarker import com.intellij.openapi.editor.RangeMarker
import com.intellij.vim.annotations.ExCommand import com.intellij.vim.annotations.ExCommand
import com.maddyhome.idea.vim.VimPlugin 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.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.getLineStartForOffset import com.maddyhome.idea.vim.api.getLineStartForOffset
@ -19,20 +18,15 @@ 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.ex.ranges.LineRange import com.maddyhome.idea.vim.ex.ranges.LineRange
import com.maddyhome.idea.vim.ex.ranges.Ranges import com.maddyhome.idea.vim.ex.ranges.Ranges
import com.maddyhome.idea.vim.group.SearchGroup
import com.maddyhome.idea.vim.group.SearchGroup.RE_BOTH import com.maddyhome.idea.vim.group.SearchGroup.RE_BOTH
import com.maddyhome.idea.vim.group.SearchGroup.RE_LAST import com.maddyhome.idea.vim.group.SearchGroup.RE_LAST
import com.maddyhome.idea.vim.group.SearchGroup.RE_SEARCH import com.maddyhome.idea.vim.group.SearchGroup.RE_SEARCH
import com.maddyhome.idea.vim.group.SearchGroup.RE_SUBST import com.maddyhome.idea.vim.group.SearchGroup.RE_SUBST
import com.maddyhome.idea.vim.helper.MessageHelper.message import com.maddyhome.idea.vim.helper.MessageHelper.message
import com.maddyhome.idea.vim.helper.Msg import com.maddyhome.idea.vim.helper.Msg
import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.regexp.CharPointer import com.maddyhome.idea.vim.regexp.CharPointer
import com.maddyhome.idea.vim.regexp.RegExp import com.maddyhome.idea.vim.regexp.RegExp
import com.maddyhome.idea.vim.regexp.VimRegex
import com.maddyhome.idea.vim.regexp.VimRegexException
import com.maddyhome.idea.vim.regexp.match.VimMatchResult
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
/** /**
@ -105,53 +99,7 @@ internal data class GlobalCommand(val ranges: Ranges, val argument: String, val
} }
} }
if (injector.globalIjOptions().useNewRegex) { val (first, second) = injector.searchGroup.search_regcomp(pat, whichPat, RE_BOTH)
val regex = try {
VimRegex(pat.toString())
} catch (e: VimRegexException) {
injector.messages.showStatusBarMessage(editor, e.message)
return false
}
if (globalBusy) {
val match = regex.findInLine(editor, editor.currentCaret().getLine().line)
if (match is VimMatchResult.Success == !invert) {
globalExecuteOne(editor, context, editor.getLineStartOffset(editor.currentCaret().getLine().line), cmd.toString())
}
} else {
val line1 = range.startLine
val line2 = range.endLine
if (line1 < 0 || line2 < 0) {
return false
}
val matches = regex.findAll(
editor,
editor.getLineStartOffset(line1),
editor.getLineEndOffset(line2),
)
val marks = if (!invert) matches.map {
editor.ij.document.createRangeMarker(editor.getLineStartForOffset(it.range.startOffset), editor.getLineStartForOffset(it.range.startOffset))
// filter out lines that contain a match
} else (line1..line2).filterNot { line ->
matches.map { match ->
editor.offsetToBufferPosition(match.range.startOffset).line
}.contains(line)
}.map { editor.ij.document.createRangeMarker(editor.getLineStartOffset(it), editor.getLineStartOffset(it)) }
if (gotInt) {
VimPlugin.showMessage(message("e_interr"))
} else if (marks.isEmpty()) {
if (invert) {
VimPlugin.showMessage(message("global.command.not.found.v", pat.toString()))
} else {
VimPlugin.showMessage(message("global.command.not.found.g", pat.toString()))
}
} else {
globalExe(editor, context, marks, cmd.toString())
}
}
} else {
val (first, second) = (injector.searchGroup as SearchGroup).search_regcomp(pat, whichPat, RE_BOTH)
if (!first) { if (!first) {
VimPlugin.showMessage(message(Msg.e_invcmd)) VimPlugin.showMessage(message(Msg.e_invcmd))
VimPlugin.indicateError() VimPlugin.indicateError()
@ -210,7 +158,6 @@ internal data class GlobalCommand(val ranges: Ranges, val argument: String, val
globalExe(editor, context, marks, cmd.toString()) globalExe(editor, context, marks, cmd.toString())
} }
} }
}
return true return true
} }

View File

@ -0,0 +1,35 @@
/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.vimscript.model.functions
import com.intellij.openapi.diagnostic.logger
import com.intellij.serviceContainer.BaseKeyedLazyInstance
import com.intellij.util.xmlb.annotations.Attribute
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector
@Deprecated("Moved to annotation approach and lazy initialization")
internal class FunctionBeanClass : BaseKeyedLazyInstance<FunctionHandler>() {
@Attribute("implementation")
lateinit var implementation: String
@Attribute("name")
lateinit var name: String
override fun getImplementationClassName(): String = implementation
fun register() {
if (this.pluginDescriptor.pluginId != VimPlugin.getPluginId()) {
logger<FunctionHandler>().error("IdeaVim doesn't accept contributions to `vimActions` extension points. Please create a plugin using `VimExtension`. Plugin to blame: ${this.pluginDescriptor.pluginId}")
return
}
injector.functionService.addOldHandler(this)
}
}

View File

@ -8,15 +8,20 @@
package com.maddyhome.idea.vim.vimscript.services package com.maddyhome.idea.vim.vimscript.services
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.extensions.ExtensionPointName
import com.maddyhome.idea.vim.api.VimscriptFunctionService import com.maddyhome.idea.vim.api.VimscriptFunctionService
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.ex.ExException import com.maddyhome.idea.vim.ex.ExException
import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
import com.maddyhome.idea.vim.vimscript.model.Script import com.maddyhome.idea.vim.vimscript.model.Script
import com.maddyhome.idea.vim.vimscript.model.VimLContext import com.maddyhome.idea.vim.vimscript.model.VimLContext
import com.maddyhome.idea.vim.vimscript.model.expressions.Scope import com.maddyhome.idea.vim.vimscript.model.expressions.Scope
import com.maddyhome.idea.vim.vimscript.model.functions.DefinedFunctionHandler import com.maddyhome.idea.vim.vimscript.model.functions.DefinedFunctionHandler
import com.maddyhome.idea.vim.vimscript.model.functions.EngineFunctionProvider import com.maddyhome.idea.vim.vimscript.model.functions.EngineFunctionProvider
import com.maddyhome.idea.vim.vimscript.model.functions.FunctionBeanClass
import com.maddyhome.idea.vim.vimscript.model.functions.FunctionHandler import com.maddyhome.idea.vim.vimscript.model.functions.FunctionHandler
import com.maddyhome.idea.vim.vimscript.model.functions.IntellijFunctionProvider import com.maddyhome.idea.vim.vimscript.model.functions.IntellijFunctionProvider
import com.maddyhome.idea.vim.vimscript.model.functions.LazyVimscriptFunction import com.maddyhome.idea.vim.vimscript.model.functions.LazyVimscriptFunction
@ -29,6 +34,8 @@ internal class FunctionStorage : VimscriptFunctionService {
private val globalFunctions: MutableMap<String, FunctionDeclaration> = mutableMapOf() private val globalFunctions: MutableMap<String, FunctionDeclaration> = mutableMapOf()
private val builtInFunctions: MutableMap<String, LazyVimscriptFunction> = mutableMapOf() private val builtInFunctions: MutableMap<String, LazyVimscriptFunction> = mutableMapOf()
@Deprecated("Moved to annotation approach and lazy initialization")
private val oldBuiltInFunctions: MutableMap<String, FunctionHandler> = mutableMapOf()
override fun deleteFunction(name: String, scope: Scope?, vimContext: VimLContext) { override fun deleteFunction(name: String, scope: Scope?, vimContext: VimLContext) {
if (name[0].isLowerCase() && scope != Scope.SCRIPT_VARIABLE) { if (name[0].isLowerCase() && scope != Scope.SCRIPT_VARIABLE) {
@ -138,7 +145,11 @@ internal class FunctionStorage : VimscriptFunctionService {
} }
override fun getBuiltInFunction(name: String): FunctionHandler? { override fun getBuiltInFunction(name: String): FunctionHandler? {
return builtInFunctions[name]?.instance return if (injector.globalIjOptions().vimscriptFunctionAnnotation) {
builtInFunctions[name]?.instance
} else {
oldBuiltInFunctions[name]
}
} }
private fun storeScriptFunction(functionDeclaration: FunctionDeclaration) { private fun storeScriptFunction(functionDeclaration: FunctionDeclaration) {
@ -169,9 +180,27 @@ internal class FunctionStorage : VimscriptFunctionService {
val intellijFunctions = IntellijFunctionProvider.getFunctions() val intellijFunctions = IntellijFunctionProvider.getFunctions()
intellijFunctions.forEach { addHandler(it) } intellijFunctions.forEach { addHandler(it) }
extensionPoint.getExtensionList(ApplicationManager.getApplication()).forEach(FunctionBeanClass::register)
} }
override fun addHandler(handler: LazyVimscriptFunction) { override fun addHandler(handler: LazyVimscriptFunction) {
builtInFunctions[handler.name] = handler builtInFunctions[handler.name] = handler
} }
override fun addOldHandler(handler: Any) {
handler as FunctionBeanClass
oldBuiltInFunctions[handler.name] = handler.instance
}
@Deprecated("Moved to annotation approach and lazy initialization")
companion object {
private val extensionPoint = ExtensionPointName.create<FunctionBeanClass>("IdeaVIM.vimLibraryFunction")
inline fun <reified T : FunctionHandler> getFunctionOfType(): T {
val point = extensionPoint.getExtensionList(ApplicationManager.getApplication())
.single { it.implementation == T::class.java.name }
return point.instance as T
}
}
} }

View File

@ -16,6 +16,7 @@
<applicationService serviceImplementation="com.maddyhome.idea.vim.group.CommandGroup"/> <applicationService serviceImplementation="com.maddyhome.idea.vim.group.CommandGroup"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.group.VimMarkServiceImpl" <applicationService serviceImplementation="com.maddyhome.idea.vim.group.VimMarkServiceImpl"
serviceInterface="com.maddyhome.idea.vim.api.VimMarkService"/> serviceInterface="com.maddyhome.idea.vim.api.VimMarkService"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.group.MarkGroup"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.group.VimJumpServiceImpl" <applicationService serviceImplementation="com.maddyhome.idea.vim.group.VimJumpServiceImpl"
serviceInterface="com.maddyhome.idea.vim.api.VimJumpService"/> serviceInterface="com.maddyhome.idea.vim.api.VimJumpService"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.group.RegisterGroup" <applicationService serviceImplementation="com.maddyhome.idea.vim.group.RegisterGroup"

View File

@ -0,0 +1,364 @@
<!--
~ 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.
-->
<idea-plugin>
<extensions defaultExtensionNs="IdeaVIM">
<!-- Motions -->
<!-- Left/Right -->
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionColumnAction" mappingModes="NXO" keys="|"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionFirstColumnAction" mappingModes="NXO" keys="0"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionFirstColumnInsertModeAction" mappingModes="I" keys="«Home»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionFirstScreenColumnAction" mappingModes="NXO" keys="g0,g«Home»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionFirstNonSpaceAction" mappingModes="NXO" keys="^"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionFirstScreenNonSpaceAction" mappingModes="NXO" keys="g^"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionLastColumnAction" mappingModes="NXO" keys="$"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionLastColumnInsertAction" mappingModes="I" keys="«End»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionLastScreenColumnAction" mappingModes="NXO" keys="g$,g«End»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionLastMatchCharAction" mappingModes="NXO" keys=";"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionLastMatchCharReverseAction" mappingModes="NXO" keys="«COMMA»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionLastNonSpaceAction" mappingModes="NXO" keys="g_"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionLeftAction" mappingModes="NXO" keys="h"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionLeftInsertModeAction" mappingModes="I"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionBackspaceAction" mappingModes="NXO" keys="«BS»,«C-H»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionLeftMatchCharAction" mappingModes="NXO" keys="F"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionLeftTillMatchCharAction" mappingModes="NXO" keys="T"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionMiddleColumnAction" mappingModes="NXO" keys="gm"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionRightAction" mappingModes="NXO" keys="l"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionRightInsertAction" mappingModes="I"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionSpaceAction" mappingModes="NXO" keys=" "/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionRightMatchCharAction" mappingModes="NXO" keys="f"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionRightTillMatchCharAction" mappingModes="NXO" keys="t"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionHomeAction" mappingModes="NV" keys="«Home»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionShiftHomeAction" mappingModes="INV" keys="«S-Home»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionEndAction" mappingModes="NVO" keys="«End»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionShiftEndAction" mappingModes="INV" keys="«S-End»"/>
<!-- Up/Down -->
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionDownAction" mappingModes="NXO" keys="j"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionDownCtrlNAction" mappingModes="NXO" keys="«C-N»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionDownNotLineWiseAction" mappingModes="NXO" keys="gj,g«Down»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionDownFirstNonSpaceAction" mappingModes="NXO" keys="+,«C-M»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.EnterNormalAction" mappingModes="NXO" keys="«CR»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionDownLess1FirstNonSpaceAction" mappingModes="NXO" keys="_"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionGotoLineFirstAction" mappingModes="NXO" keys="gg,«C-Home»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionGotoLineFirstInsertAction" mappingModes="I" keys="«C-Home»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionGotoLineLastAction" mappingModes="NXO" keys="G"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionGotoLineLastEndAction" mappingModes="NXO" keys="«C-End»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionGotoLineLastEndInsertAction" mappingModes="I" keys="«C-End»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionPercentOrMatchAction" mappingModes="NXO" keys="%"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionUpAction" mappingModes="NXO" keys="k"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionUpCtrlPAction" mappingModes="NXO" keys="«C-P»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionUpNotLineWiseAction" mappingModes="NXO" keys="gk,g«Up»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionUpFirstNonSpaceAction" mappingModes="NXO" keys="-"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionArrowDownAction" mappingModes="NVO"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionArrowUpAction" mappingModes="NVO"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionArrowLeftAction" mappingModes="NXO"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionArrowRightAction" mappingModes="NXO"/>
<vimAction implementation="com.maddyhome.idea.vim.action.window.LookupDownAction" mappingModes="I" keys="«C-N»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.window.LookupUpAction" mappingModes="I" keys="«C-P»"/>
<!-- Text -->
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionCamelEndLeftAction" mappingModes="NXO" keys="]b"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionCamelEndRightAction" mappingModes="NXO" keys="]w"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionCamelLeftAction" mappingModes="NXO" keys="[b"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionCamelRightAction" mappingModes="NXO" keys="[w"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionNthCharacterAction" mappingModes="NXO" keys="go"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionWordEndLeftAction" mappingModes="NXO" keys="ge"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionBigWordEndLeftAction" mappingModes="NXO" keys="gE"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionWordEndRightAction" mappingModes="NXO" keys="e"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionBigWordEndRightAction" mappingModes="NXO" keys="E"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionWordLeftAction" mappingModes="NXO" keys="b"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionWordLeftInsertAction" mappingModes="I"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionBigWordLeftAction" mappingModes="NXO" keys="B,«C-Left»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionWordRightAction" mappingModes="NXO" keys="w"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionWordRightInsertAction" mappingModes="I"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionBigWordRightAction" mappingModes="NXO" keys="W,«C-Right»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionSentenceNextStartAction" mappingModes="NXO" keys=")"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionSentencePreviousStartAction" mappingModes="NXO" keys="("/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionSentenceNextEndAction" mappingModes="NXO" keys="g)"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionSentencePreviousEndAction" mappingModes="NXO" keys="g("/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionParagraphNextAction" mappingModes="NXO" keys="}"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionParagraphPreviousAction" mappingModes="NXO" keys="{"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedBraceCloseAction" mappingModes="NXO" keys="]}"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedBraceOpenAction" mappingModes="NXO" keys="[{"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedParenCloseAction" mappingModes="NXO" keys="])"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedParenOpenAction" mappingModes="NXO" keys="[("/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionSectionBackwardEndAction" mappingModes="NXO" keys="[]"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionSectionBackwardStartAction" mappingModes="NXO" keys="[["/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionSectionForwardStartAction" mappingModes="NXO" keys="]]"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionSectionForwardEndAction" mappingModes="NXO" keys="]["/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionMethodPreviousEndAction" mappingModes="NXO" keys="[M"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionMethodPreviousStartAction" mappingModes="NXO" keys="[m"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionMethodNextEndAction" mappingModes="NXO" keys="]M"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.text.MotionMethodNextStartAction" mappingModes="NXO" keys="]m"/>
<!-- Text Objects -->
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionOuterWordAction" mappingModes="XO" keys="aw"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionOuterBigWordAction" mappingModes="XO" keys="aW"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionInnerWordAction" mappingModes="XO" keys="iw"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionInnerBigWordAction" mappingModes="XO" keys="iW"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockAngleAction" mappingModes="XO" keys="i>,i&lt;"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockBraceAction" mappingModes="XO" keys="iB,i{,i}"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockBracketAction" mappingModes="XO" keys="i[,i]"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockParenAction" mappingModes="XO" keys="ib,i(,i)"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockDoubleQuoteAction" mappingModes="XO" keys='i"'/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockSingleQuoteAction" mappingModes="XO" keys="i'"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockBackQuoteAction" mappingModes="XO" keys="i`"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockTagAction" mappingModes="XO" keys="it"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockAngleAction" mappingModes="XO" keys="a&lt;,a>"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockBraceAction" mappingModes="XO" keys="aB,a{,a}"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockBracketAction" mappingModes="XO" keys="a[,a]"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockParenAction" mappingModes="XO" keys="ab,a(,a)"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockDoubleQuoteAction" mappingModes="XO" keys='a"'/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockSingleQuoteAction" mappingModes="XO" keys="a'"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockBackQuoteAction" mappingModes="XO" keys="a`"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockTagAction" mappingModes="XO" keys="at"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionInnerParagraphAction" mappingModes="XO" keys="ip"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionOuterParagraphAction" mappingModes="XO" keys="ap"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionInnerSentenceAction" mappingModes="XO" keys="is"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.object.MotionOuterSentenceAction" mappingModes="XO" keys="as"/>
<!-- Marks -->
<vimAction implementation="com.maddyhome.idea.vim.action.motion.mark.MotionMarkAction" mappingModes="NX" keys="m"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.mark.MotionGotoFileMarkAction" mappingModes="XO" keys="`"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.mark.MotionGotoFileMarkNoSaveJumpAction" mappingModes="XO" keys="g`"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.mark.MotionGotoFileMarkLineAction" mappingModes="XO" keys="'"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.mark.MotionGotoFileMarkLineNoSaveJumpAction" mappingModes="XO" keys="g'"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.mark.MotionGotoMarkAction" mappingModes="N" keys="`"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.mark.MotionGotoMarkNoSaveJumpAction" mappingModes="N" keys="g`"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.mark.MotionGotoMarkLineAction" mappingModes="N" keys="'"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.mark.MotionGotoMarkLineNoSaveJumpAction" mappingModes="N" keys="g'"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.mark.MotionJumpNextAction" mappingModes="N" keys="«C-I»,«Tab»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.mark.MotionJumpPreviousAction" mappingModes="N" keys="«C-O»,«C-T»"/>
<!-- Screen -->
<vimAction implementation="com.maddyhome.idea.vim.action.motion.screen.MotionFirstScreenLineAction" mappingModes="NX" keys="H"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.screen.MotionOpPendingFirstScreenLineAction" mappingModes="O" keys="H"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.screen.MotionLastScreenLineAction" mappingModes="NX" keys="L"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.screen.MotionOpPendingLastScreenLineAction" mappingModes="O" keys="L"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.screen.MotionMiddleScreenLineAction" mappingModes="NXO" keys="M"/>
<!-- Scroll -->
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollFirstScreenLinePageStartAction" mappingModes="NXO" keys="z+"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollFirstScreenLineStartAction" mappingModes="NXO" keys="z«CR»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollFirstScreenLineAction" mappingModes="NXO" keys="zt"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollHalfPageDownAction" mappingModes="NXO" keys="«C-D»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollHalfPageUpAction" mappingModes="NXO" keys="«C-U»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLastScreenLineAction" mappingModes="NXO" keys="zb"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLastScreenLinePageStartAction" mappingModes="NXO" keys="z^"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLastScreenLineStartAction" mappingModes="NXO" keys="z-"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLineDownAction" mappingModes="NXO" keys="«C-E»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLineUpAction" mappingModes="NXO" keys="«C-Y»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollMiddleScreenLineAction" mappingModes="NXO" keys="zz"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollMiddleScreenLineStartAction" mappingModes="NXO" keys="z."/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageDownAction" mappingModes="NXO" keys="«C-F»,«PageDown»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageDownInsertModeAction" mappingModes="I"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageUpAction" mappingModes="NXO" keys="«C-B»,«PageUp»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageUpInsertModeAction" mappingModes="I"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollFirstScreenColumnAction" mappingModes="NXO" keys="zs"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLastScreenColumnAction" mappingModes="NXO" keys="ze"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollColumnLeftAction" mappingModes="NXO" keys="zl,z«Right»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollColumnRightAction" mappingModes="NXO" keys="zh,z«Left»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollHalfWidthLeftAction" mappingModes="NXO" keys="zL"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollHalfWidthRightAction" mappingModes="NXO" keys="zH"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionShiftDownAction" mappingModes="INV" keys="«S-Down»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionShiftUpAction" mappingModes="INV" keys="«S-Up»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionShiftRightAction" mappingModes="INV" keys="«S-Right»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionShiftLeftAction" mappingModes="INV" keys="«S-Left»"/>
<!-- Visual -->
<vimAction implementation="com.maddyhome.idea.vim.action.motion.visual.VisualExitModeAction" mappingModes="X" keys="«Esc»,«C-[»,«C-C»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.visual.VisualToggleCharacterModeAction" mappingModes="NX" keys="v"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.visual.VisualToggleLineModeAction" mappingModes="NX" keys="V"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.visual.VisualToggleBlockModeAction" mappingModes="NX" keys="«C-q»,«C-v»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.visual.VisualSwapEndsAction" mappingModes="X" keys="o"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.visual.VisualSwapEndsBlockAction" mappingModes="X" keys="O"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.visual.VisualSelectPreviousAction" mappingModes="N" keys="gv"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.visual.VisualSwapSelectionsAction" mappingModes="X" keys="gv"/>
<!-- Select-->
<vimAction implementation="com.maddyhome.idea.vim.action.motion.select.SelectEnableCharacterModeAction" mappingModes="N" keys="gh"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.select.SelectEnableLineModeAction" mappingModes="N" keys="gH"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.select.SelectEnableBlockModeAction" mappingModes="N" keys="g«C-h»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.select.motion.SelectMotionRightAction" mappingModes="S" keys="«Right»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.select.motion.SelectMotionLeftAction" mappingModes="S" keys="«Left»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.select.SelectToggleVisualMode" mappingModes="V" keys="«C-G»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.select.SelectEnterAction" mappingModes="S" keys="«enter»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.select.SelectEscapeAction" mappingModes="S" keys="«esc»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.select.SelectDeleteAction" mappingModes="S" keys="«BS»,«DEL»"/>
<!-- Insert -->
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertAfterCursorAction" mappingModes="N" keys="a"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertAfterLineEndAction" mappingModes="N" keys="A"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertAtPreviousInsertAction" mappingModes="N" keys="gi"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertBeforeCursorAction" mappingModes="N" keys="i,«Insert»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertBeforeFirstNonBlankAction" mappingModes="N" keys="I"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertCharacterAboveCursorAction" mappingModes="I" keys="«C-Y»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertCharacterBelowCursorAction" mappingModes="I" keys="«C-E»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertDeleteInsertedTextAction" mappingModes="I" keys="«C-U»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertDeletePreviousWordAction" mappingModes="I" keys="«C-W»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertEnterAction" mappingModes="I" keys="«C-M»,«CR»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertExitModeAction" mappingModes="I" keys="«C-[»,«C-C»,«Esc»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertLineStartAction" mappingModes="N" keys="gI"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertNewLineAboveAction" mappingModes="N" keys="O"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertNewLineBelowAction" mappingModes="N" keys="o"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertPreviousInsertAction" mappingModes="I" keys="«C-A»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertPreviousInsertExitAction" mappingModes="I"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertRegisterAction" mappingModes="I" keys="«C-R»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertInsertAction" mappingModes="I" keys="«Ins»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertSingleCommandAction" mappingModes="I" keys="«C-O»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.VisualBlockInsertAction" mappingModes="X" keys="I"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.VisualBlockAppendAction" mappingModes="X" keys="A"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertCompletedDigraphAction" mappingModes="IC" keys="«C-K»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction" mappingModes="IC" keys="«C-V»,«C-Q»"/>
<!-- Delete -->
<vimAction implementation="com.maddyhome.idea.vim.action.change.delete.DeleteCharacterAction" mappingModes="N" keys="«DEL»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.delete.DeleteCharacterLeftAction" mappingModes="N" keys="X"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.delete.DeleteCharacterRightAction" mappingModes="N" keys="x"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.delete.DeleteEndOfLineAction" mappingModes="N" keys="D"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.delete.DeleteJoinLinesAction" mappingModes="N" keys="gJ"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.delete.DeleteJoinLinesSpacesAction" mappingModes="N" keys="J"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.delete.DeleteJoinVisualLinesAction" mappingModes="X" keys="gJ"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.delete.DeleteJoinVisualLinesSpacesAction" mappingModes="X" keys="J"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.delete.DeleteMotionAction" mappingModes="N" keys="d"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.delete.DeleteVisualAction" mappingModes="X" keys="d,x,«Del»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.delete.DeleteVisualLinesAction" mappingModes="X" keys="X"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.delete.DeleteVisualLinesEndAction" mappingModes="X" keys="D"/>
<!-- Change -->
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerMotionAction" mappingModes="N" keys="gu"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerVisualAction" mappingModes="X" keys="u"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleCharacterAction" mappingModes="N" keys="~"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleMotionAction" mappingModes="N" keys="g~"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleVisualAction" mappingModes="X" keys="~"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperMotionAction" mappingModes="N" keys="gU"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperVisualAction" mappingModes="X" keys="U"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCharacterAction" mappingModes="N" keys="r"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCharactersAction" mappingModes="N" keys="s"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeEndOfLineAction" mappingModes="N" keys="C"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeLineAction" mappingModes="N" keys="S"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeMotionAction" mappingModes="N" keys="c"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.number.ChangeNumberIncAction" mappingModes="N" keys="«C-A»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.number.ChangeNumberDecAction" mappingModes="N" keys="«C-X»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.number.ChangeVisualNumberIncAction" mappingModes="X" keys="«C-A»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.number.ChangeVisualNumberDecAction" mappingModes="X" keys="«C-X»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.number.ChangeVisualNumberAvalancheIncAction" mappingModes="X" keys="g«C-A»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.number.ChangeVisualNumberAvalancheDecAction" mappingModes="X" keys="g«C-X»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeReplaceAction" mappingModes="N" keys="R"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeVisualAction" mappingModes="X" keys="c,s"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeVisualCharacterAction" mappingModes="X" keys="r"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeVisualLinesAction" mappingModes="X" keys="R,S"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeVisualLinesEndAction" mappingModes="X" keys="C"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.FilterMotionAction" mappingModes="N" keys="!"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.FilterVisualLinesAction" mappingModes="X" keys="!"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.AutoIndentLinesVisualAction" mappingModes="X" keys="="/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ReformatCodeVisualAction" mappingModes="X" keys="gq"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ReformatCodeMotionAction" mappingModes="N" keys="gq"/>
<!-- Shift -->
<vimAction implementation="com.maddyhome.idea.vim.action.change.shift.AutoIndentMotionAction" mappingModes="N" keys="="/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.shift.ShiftLeftLinesAction" mappingModes="I" keys="«C-D»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.shift.ShiftLeftMotionAction" mappingModes="N" keys="&lt;"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.shift.ShiftLeftVisualAction" mappingModes="X" keys="&lt;"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.shift.ShiftRightLinesAction" mappingModes="I" keys="«C-T»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.shift.ShiftRightMotionAction" mappingModes="N" keys=">"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.shift.ShiftRightVisualAction" mappingModes="X" keys=">"/>
<!-- Copy -->
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutTextAfterCursorAction" mappingModes="N" keys="p"/>
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorAction" mappingModes="N" keys="P"/>
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutTextAfterCursorNoIndentAction" mappingModes="N" keys="]p"/>
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorNoIndentAction" mappingModes="N" keys="[P,]P,[p"/>
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutTextAfterCursorActionMoveCursor" mappingModes="N" keys="gp"/>
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorActionMoveCursor" mappingModes="N" keys="gP"/>
<vimAction implementation="com.maddyhome.idea.vim.action.copy.YankLineAction" mappingModes="N" keys="Y"/>
<vimAction implementation="com.maddyhome.idea.vim.action.copy.YankMotionAction" mappingModes="N" keys="y"/>
<vimAction implementation="com.maddyhome.idea.vim.action.copy.YankVisualAction" mappingModes="X" keys="y"/>
<vimAction implementation="com.maddyhome.idea.vim.action.copy.YankVisualLinesAction" mappingModes="X" keys="Y"/>
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorAction" mappingModes="X" keys="P"/>
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorAction" mappingModes="X" keys="p"/>
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorNoIndentAction" mappingModes="X" keys="]P,[P"/>
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorNoIndentAction" mappingModes="X" keys="[p,]p"/>
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorMoveCursorAction" mappingModes="X" keys="gP"/>
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorMoveCursorAction" mappingModes="X" keys="gp"/>
<!-- File -->
<vimAction implementation="com.maddyhome.idea.vim.action.file.FileSaveCloseAction" mappingModes="N" keys="ZQ,ZZ"/>
<vimAction implementation="com.maddyhome.idea.vim.action.file.FilePreviousAction" mappingModes="N"/>
<vimAction implementation="com.maddyhome.idea.vim.action.file.FileGetAsciiAction" mappingModes="N" keys="ga"/>
<vimAction implementation="com.maddyhome.idea.vim.action.file.FileGetHexAction" mappingModes="N" keys="g8"/>
<vimAction implementation="com.maddyhome.idea.vim.action.file.FileGetFileInfoAction" mappingModes="N" keys="«C-G»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.file.FileGetLocationInfoAction" mappingModes="NX" keys="g«C-G»"/>
<!-- Window -->
<vimAction implementation="com.maddyhome.idea.vim.action.window.VerticalSplitAction" mappingModes="N" keys="«C-W»v,«C-W»«C-V»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.window.HorizontalSplitAction" mappingModes="N" keys="«C-W»s,«C-W»S,«C-W»«C-S»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.window.CloseWindowAction" mappingModes="N" keys="«C-W»c"/>
<vimAction implementation="com.maddyhome.idea.vim.action.window.WindowOnlyAction" mappingModes="N" keys="«C-W»o,«C-W»«C-O»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.window.WindowNextAction" mappingModes="N" keys="«C-W»w,«C-W»«C-W»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.window.WindowPrevAction" mappingModes="N" keys="«C-W»W"/>
<vimAction implementation="com.maddyhome.idea.vim.action.window.WindowLeftAction" mappingModes="N" keys="«C-W»h,«C-W»«C-H»,«C-W»«Left»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.window.WindowRightAction" mappingModes="N" keys="«C-W»l,«C-W»«C-L»,«C-W»«Right»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.window.WindowUpAction" mappingModes="N" keys="«C-W»k,«C-W»«C-K»,«C-W»«Up»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.window.WindowDownAction" mappingModes="N" keys="«C-W»j,«C-W»«C-J»,«C-W»«Down»"/>
<!-- Tabs -->
<vimAction implementation="com.maddyhome.idea.vim.action.window.tabs.NextTabAction" mappingModes="NXO" keys="gt"/>
<vimAction implementation="com.maddyhome.idea.vim.action.window.tabs.PreviousTabAction" mappingModes="NXO" keys="gT"/>
<!-- Search -->
<vimAction implementation="com.maddyhome.idea.vim.action.motion.search.SearchEntryFwdAction" mappingModes="NXO" keys="/"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.search.SearchEntryRevAction" mappingModes="NXO" keys="?"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.search.SearchAgainNextAction" mappingModes="NXO" keys="n"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.search.SearchAgainPreviousAction" mappingModes="NXO" keys="N"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction" mappingModes="NXO" keys="*"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.search.SearchWholeWordBackwardAction" mappingModes="NXO" keys="#"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.search.SearchWordForwardAction" mappingModes="NXO" keys="g*"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.search.SearchWordBackwardAction" mappingModes="NXO" keys="g#"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.search.GotoDeclarationAction" mappingModes="NX" keys="gD,gd,«C-]»"/>
<!-- Macro -->
<vimAction implementation="com.maddyhome.idea.vim.action.macro.ToggleRecordingAction" mappingModes="NX" keys="q"/>
<vimAction implementation="com.maddyhome.idea.vim.action.macro.PlaybackRegisterAction" mappingModes="N" keys="@"/>
<!-- Command Line -->
<vimAction implementation="com.maddyhome.idea.vim.action.ex.ProcessExEntryAction" mappingModes="C"/>
<!-- Other -->
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeLastSearchReplaceAction" mappingModes="N" keys="&amp;"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeLastGlobalSearchReplaceAction" mappingModes="N" keys="g&amp;"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.RepeatChangeAction" mappingModes="N" keys="."/>
<vimAction implementation="com.maddyhome.idea.vim.action.ExEntryAction" mappingModes="NXO" keys=":"/>
<vimAction implementation="com.maddyhome.idea.vim.action.ResetModeAction" mappingModes="ALL" keys="«C-\»«C-N»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.RedoAction" mappingModes="N" keys="«C-R»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.UndoAction" mappingModes="N"/>
<!-- Keys -->
<vimAction implementation="com.maddyhome.idea.vim.action.change.OperatorAction" mappingModes="N" keys="g@"/>
<vimAction implementation="com.maddyhome.idea.vim.action.change.VisualOperatorAction" mappingModes="X" keys="g@"/>
<!-- Visual Selection of last Search Pattern (gn) -->
<vimAction implementation="com.maddyhome.idea.vim.action.motion.gn.VisualSelectNextSearch" mappingModes="NX" keys="gn"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.gn.VisualSelectPreviousSearch" mappingModes="NX" keys="gN"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.gn.GnNextTextObject" mappingModes="O" keys="gn"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.gn.GnPreviousTextObject" mappingModes="O" keys="gN"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.CtrlDownAction" mappingModes="N" keys="«C-Down»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.CtrlUpAction" mappingModes="N" keys="«C-Up»"/>
<!-- Folds -->
<vimAction implementation="com.maddyhome.idea.vim.action.fold.VimCollapseAllRegions" mappingModes="NX" keys="zM"/>
<vimAction implementation="com.maddyhome.idea.vim.action.fold.VimCollapseRegion" mappingModes="NX" keys="zc"/>
<vimAction implementation="com.maddyhome.idea.vim.action.fold.VimCollapseRegionRecursively" mappingModes="NX" keys="zC"/>
<vimAction implementation="com.maddyhome.idea.vim.action.fold.VimExpandAllRegions" mappingModes="NX" keys="zR"/>
<vimAction implementation="com.maddyhome.idea.vim.action.fold.VimExpandRegion" mappingModes="NX" keys="zo"/>
<vimAction implementation="com.maddyhome.idea.vim.action.fold.VimExpandRegionRecursively" mappingModes="NX" keys="zO"/>
<!-- Editor Actions -->
<vimAction implementation="com.maddyhome.idea.vim.action.editor.VimEditorBackSpace" mappingModes="I"/>
<vimAction implementation="com.maddyhome.idea.vim.action.editor.VimEditorDelete" mappingModes="I"/>
<vimAction implementation="com.maddyhome.idea.vim.action.editor.VimEditorDown" mappingModes="I"/>
<vimAction implementation="com.maddyhome.idea.vim.action.editor.VimEditorTab" mappingModes="I"/>
<vimAction implementation="com.maddyhome.idea.vim.action.editor.VimEditorUp" mappingModes="I"/>
<vimAction implementation="com.maddyhome.idea.vim.action.editor.VimQuickJavaDoc" mappingModes="N" keys="K"/>
</extensions>
</idea-plugin>

View File

@ -124,14 +124,6 @@
<alias name="chrisbra/matchit"/> <alias name="chrisbra/matchit"/>
</aliases> </aliases>
</vimExtension> </vimExtension>
<vimExtension implementation="com.maddyhome.idea.vim.extension.sneak.IdeaVimSneakExtension" name="sneak">
<aliases>
<alias name="https://github.com/justinmk/vim-sneak"/>
<alias name="justinmk/vim-sneak"/>
<alias name="vim-sneak"/>
</aliases>
</vimExtension>
</extensions> </extensions>
<!-- IdeaVim extensions--> <!-- IdeaVim extensions-->

View File

@ -0,0 +1,28 @@
<!--
~ 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.
-->
<idea-plugin>
<extensions defaultExtensionNs="IdeaVIM">
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.AbsFunctionHandler" name="abs"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.ColFunctionHandler" name="col"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.EmptyFunctionHandler" name="empty"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.LineFunctionHandler" name="line"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.SinFunctionHandler" name="sin"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.ExistsFunctionHandler" name="exists"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.LenFunctionHandler" name="len"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.FunctionFunctionHandler" name="function"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.FuncrefFunctionHandler" name="funcref"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.HasFunctionHandler" name="has"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.SubmatchFunctionHandler" name="submatch"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.TolowerFunctionHandler" name="tolower"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.ToupperFunctionHandler" name="toupper"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.JoinFunctionHandler" name="join"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.SplitFunctionHandler" name="split"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.GetFunctionHandler" name="get"/>
</extensions>
</idea-plugin>

View File

@ -57,6 +57,15 @@
<with attribute="implementation" implements="com.maddyhome.idea.vim.extension.VimExtension"/> <with attribute="implementation" implements="com.maddyhome.idea.vim.extension.VimExtension"/>
</extensionPoint> </extensionPoint>
<extensionPoint name="vimLibraryFunction"
beanClass="com.maddyhome.idea.vim.vimscript.model.functions.FunctionBeanClass" dynamic="true">
<with attribute="implementation" implements="com.maddyhome.idea.vim.vimscript.model.functions.FunctionHandler"/>
</extensionPoint>
<!-- For internal use only -->
<extensionPoint name="vimAction" beanClass="com.maddyhome.idea.vim.handler.ActionBeanClass" dynamic="true">
<with attribute="implementation" implements="com.maddyhome.idea.vim.handler.EditorActionHandlerBase"/>
</extensionPoint>
<extensionPoint interface="com.maddyhome.idea.vim.ide.ClionNovaProvider" dynamic="true" name="clionNovaProvider"/> <extensionPoint interface="com.maddyhome.idea.vim.ide.ClionNovaProvider" dynamic="true" name="clionNovaProvider"/>
</extensionPoints> </extensionPoints>
@ -64,10 +73,10 @@
<applicationConfigurable groupId="editor" instance="com.maddyhome.idea.vim.ui.VimEmulationConfigurable"/> <applicationConfigurable groupId="editor" instance="com.maddyhome.idea.vim.ui.VimEmulationConfigurable"/>
<projectService serviceImplementation="com.maddyhome.idea.vim.group.NotificationService"/> <projectService serviceImplementation="com.maddyhome.idea.vim.group.NotificationService"/>
<projectService serviceImplementation="com.maddyhome.idea.vim.group.LastTabService"/> <projectService serviceImplementation="com.maddyhome.idea.vim.group.LastTabService"/>
<statusBarWidgetFactory id="IdeaVimMode" implementation="com.maddyhome.idea.vim.ui.widgets.mode.ModeWidgetFactory" order="last, before Memory"/> <statusBarWidgetFactory id="IdeaVim-Icon" implementation="com.maddyhome.idea.vim.ui.StatusBarIconFactory"/>
<statusBarWidgetFactory id="IdeaVim-Icon" implementation="com.maddyhome.idea.vim.ui.StatusBarIconFactory" order="last, before IdeaVimMode"/> <statusBarWidgetFactory id="IdeaVim::Mode" implementation="com.maddyhome.idea.vim.ui.widgets.mode.ModeWidgetFactory" order="last"/>
<statusBarWidgetFactory id="IdeaVimShowCmd" implementation="com.maddyhome.idea.vim.ui.ShowCmdStatusBarWidgetFactory" order="first"/> <statusBarWidgetFactory id="IdeaVim::ShowCmd" implementation="com.maddyhome.idea.vim.ui.ShowCmdStatusBarWidgetFactory" order="first"/>
<statusBarWidgetFactory id="IdeaVimMacro" implementation="com.maddyhome.idea.vim.ui.widgets.macro.MacroWidgetFactory" order="first, after IdeaVimShowCmd"/> <statusBarWidgetFactory id="IdeaVim::Macro" implementation="com.maddyhome.idea.vim.ui.widgets.macro.MacroWidgetFactory"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.VimPlugin"/> <applicationService serviceImplementation="com.maddyhome.idea.vim.VimPlugin"/>
@ -133,8 +142,10 @@
</extensions> </extensions>
<xi:include href="/META-INF/includes/ApplicationServices.xml" xpointer="xpointer(/idea-plugin/*)"/> <xi:include href="/META-INF/includes/ApplicationServices.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="/META-INF/includes/VimActions.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="/META-INF/includes/VimExtensions.xml" xpointer="xpointer(/idea-plugin/*)"/> <xi:include href="/META-INF/includes/VimExtensions.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="/META-INF/includes/VimListeners.xml" xpointer="xpointer(/idea-plugin/*)"/> <xi:include href="/META-INF/includes/VimListeners.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="/META-INF/includes/VimLibraryFunctions.xml" xpointer="xpointer(/idea-plugin/*)"/>
<actions resource-bundle="messages.IdeaVimBundle"> <actions resource-bundle="messages.IdeaVimBundle">
<action id="VimPluginToggle" class="com.maddyhome.idea.vim.action.VimPluginToggleAction"> <action id="VimPluginToggle" class="com.maddyhome.idea.vim.action.VimPluginToggleAction">

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2003-2024 The IdeaVim authors * Copyright 2003-2023 The IdeaVim authors
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at * license that can be found in the LICENSE.txt file or at
@ -16,6 +16,7 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.LogicalPosition import com.intellij.openapi.editor.LogicalPosition
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.common.CharacterPosition import com.maddyhome.idea.vim.common.CharacterPosition
import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
import com.maddyhome.idea.vim.helper.vimStateMachine import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.register.RegisterConstants.ALTERNATE_BUFFER_REGISTER import com.maddyhome.idea.vim.register.RegisterConstants.ALTERNATE_BUFFER_REGISTER

View File

@ -1,10 +1,4 @@
/* // Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
* 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 org.jetbrains.plugins.ideavim package org.jetbrains.plugins.ideavim
import com.intellij.testFramework.LoggedErrorProcessor import com.intellij.testFramework.LoggedErrorProcessor
@ -22,7 +16,7 @@ import org.junit.jupiter.api.fail
* look like the exception is not processed. * look like the exception is not processed.
* I don't see a need for printing these caught exceptions, so we can use this processor to only rethrow them. * I don't see a need for printing these caught exceptions, so we can use this processor to only rethrow them.
*/ */
object OnlyThrowLoggedErrorProcessor : LoggedErrorProcessor() { internal object OnlyThrowLoggedErrorProcessor : LoggedErrorProcessor() {
override fun processError(category: String, message: String, details: Array<out String>, t: Throwable?): Set<Action> { override fun processError(category: String, message: String, details: Array<out String>, t: Throwable?): Set<Action> {
return setOf(Action.RETHROW) return setOf(Action.RETHROW)
} }
@ -31,7 +25,7 @@ object OnlyThrowLoggedErrorProcessor : LoggedErrorProcessor() {
/** /**
* Asserts that [T] was thrown via `LOG.error("message", e)` call where `e` has a type of [T]. * Asserts that [T] was thrown via `LOG.error("message", e)` call where `e` has a type of [T].
*/ */
inline fun <reified T: Throwable> assertThrowsLogError(crossinline action: () -> Unit): T { internal inline fun <reified T: Throwable> assertThrowsLogError(crossinline action: () -> Unit): T {
val exception = assertThrows<TestLoggerAssertionError> { val exception = assertThrows<TestLoggerAssertionError> {
LoggedErrorProcessor.executeWith<Throwable>(OnlyThrowLoggedErrorProcessor) { LoggedErrorProcessor.executeWith<Throwable>(OnlyThrowLoggedErrorProcessor) {
action() action()

View File

@ -8,11 +8,20 @@
package org.jetbrains.plugins.ideavim package org.jetbrains.plugins.ideavim
import com.maddyhome.idea.vim.RegisterActions.VIM_ACTIONS_EP
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.handler.ActionBeanClass
import com.maddyhome.idea.vim.key.CommandNode
import com.maddyhome.idea.vim.key.CommandPartNode
import com.maddyhome.idea.vim.newapi.globalIjOptions
import org.jetbrains.plugins.ideavim.impl.OptionTest import org.jetbrains.plugins.ideavim.impl.OptionTest
import org.jetbrains.plugins.ideavim.impl.VimOption import org.jetbrains.plugins.ideavim.impl.VimOption
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import javax.swing.KeyStroke
import kotlin.test.assertNotNull
class RegisterActionsTest : VimTestCase() { class RegisterActionsTest : VimTestCase() {
@OptionTest( @OptionTest(
@ -70,4 +79,35 @@ class RegisterActionsTest : VimTestCase() {
VimPlugin.setEnabled(true) VimPlugin.setEnabled(true)
} }
} }
@TestWithoutNeovim(reason = SkipNeovimReason.EDITOR_MODIFICATION)
@OptionTest(
VimOption(TestOptionConstants.virtualedit, doesntAffectTest = true),
VimOption(TestOptionConstants.whichwrap, doesntAffectTest = true),
)
fun `test unregister extension`() {
if (injector.globalIjOptions().commandOrMotionAnnotation) return
val before = "I ${c}found it in a legendary land"
val after = "I f${c}ound it in a legendary land"
var motionRightAction: ActionBeanClass? = null
doTest("l", before, after, Mode.NORMAL()) {
motionRightAction =
VIM_ACTIONS_EP.getExtensionList(null).first { it.actionId == "VimPreviousTabAction" }
assertNotNull<Any>(getCommandNode())
@Suppress("DEPRECATION")
VIM_ACTIONS_EP.getPoint(null).unregisterExtension(motionRightAction!!)
kotlin.test.assertNull(getCommandNode())
}
@Suppress("DEPRECATION")
VIM_ACTIONS_EP.getPoint(null).registerExtension(motionRightAction!!)
assertNotNull<Any>(getCommandNode())
}
private fun getCommandNode(): CommandNode<*>? {
// TODO: 08.02.2020 Sorry if your tests will fail because of this test
val node = VimPlugin.getKey().getKeyRoot(MappingMode.NORMAL)[KeyStroke.getKeyStroke('g')] as CommandPartNode
return node[KeyStroke.getKeyStroke('T')] as CommandNode<*>?
}
} }

View File

@ -8,15 +8,25 @@
package org.jetbrains.plugins.ideavim package org.jetbrains.plugins.ideavim
import com.intellij.ide.IdeEventQueue
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.LogicalPosition import com.intellij.openapi.editor.LogicalPosition
import com.intellij.testFramework.EditorTestUtil import com.intellij.testFramework.EditorTestUtil
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import com.intellij.util.containers.toArray
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.extension.ExtensionHandler import com.maddyhome.idea.vim.extension.ExtensionHandler
import com.maddyhome.idea.vim.key.MappingOwner import com.maddyhome.idea.vim.key.MappingOwner
import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.mode
import org.junit.jupiter.params.provider.Arguments
import kotlin.test.fail
/** /**
* @author Alex Plate * @author Alex Plate
@ -51,6 +61,43 @@ fun Editor.rangeOf(first: String, nLinesDown: Int): TextRange {
return TextRange(starts.toIntArray(), ends.toIntArray()) return TextRange(starts.toIntArray(), ends.toIntArray())
} }
inline fun waitAndAssert(timeInMillis: Int = 1000, condition: () -> Boolean) {
val end = System.currentTimeMillis() + timeInMillis
while (end > System.currentTimeMillis()) {
Thread.sleep(10)
IdeEventQueue.getInstance().flushQueue()
if (condition()) return
}
fail()
}
fun waitAndAssertMode(
fixture: CodeInsightTestFixture,
mode: Mode,
timeInMillis: Int? = null,
) {
val timeout = timeInMillis ?: (injector.globalIjOptions().visualdelay + 1000)
waitAndAssert(timeout) { fixture.editor.vim.mode == mode }
}
fun assertDoesntChange(timeInMillis: Int = 1000, condition: () -> Boolean) {
val end = System.currentTimeMillis() + timeInMillis
while (end > System.currentTimeMillis()) {
if (!condition()) {
fail()
}
Thread.sleep(10)
IdeEventQueue.getInstance().flushQueue()
}
}
fun assertHappened(timeInMillis: Int = 1000, precision: Int, condition: () -> Boolean) {
assertDoesntChange(timeInMillis - precision) { !condition() }
waitAndAssert(precision * 2) { condition() }
}
@Suppress("unused") @Suppress("unused")
fun waitCondition( fun waitCondition(
durationMillis: Long, durationMillis: Long,
@ -68,6 +115,26 @@ fun waitCondition(
return false return false
} }
internal fun <T, S, V> Collection<T>.cartesianProduct(other: Iterable<S>, transformer: (first: T, second: S) -> V): List<V> {
return this.flatMap { first -> other.map { second -> transformer.invoke(first, second) } }
}
// Cartesian product of multiple lists. Useful for making parameterized tests with all available combinations.
// Can be used instead of @Theory from JUnit 4
internal fun productForArguments(vararg elements: List<String>): List<Arguments> {
val res = product(*elements)
return res.map { Arguments.of(*it.toArray(emptyArray())) }
}
internal fun <T> product(vararg elements: List<T>): List<List<T>> {
val res = elements.fold(listOf<List<T>>(emptyList())) { acc, items ->
acc.cartesianProduct(items) { accItems, item ->
accItems + item
}
}
return res
}
internal class ExceptionHandler : ExtensionHandler { internal class ExceptionHandler : ExtensionHandler {
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
error(exceptionMessage) error(exceptionMessage)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2003-2024 The IdeaVim authors * Copyright 2003-2023 The IdeaVim authors
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at * license that can be found in the LICENSE.txt file or at
@ -9,6 +9,7 @@ package org.jetbrains.plugins.ideavim
import com.intellij.ide.ClipboardSynchronizer import com.intellij.ide.ClipboardSynchronizer
import com.intellij.ide.bookmark.BookmarksManager import com.intellij.ide.bookmark.BookmarksManager
import com.intellij.ide.highlighter.JavaFileType
import com.intellij.ide.highlighter.XmlFileType import com.intellij.ide.highlighter.XmlFileType
import com.intellij.json.JsonFileType import com.intellij.json.JsonFileType
import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.ActionManager
@ -78,7 +79,9 @@ import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimFuncref import com.maddyhome.idea.vim.vimscript.model.datatypes.VimFuncref
import com.maddyhome.idea.vim.vimscript.parser.errors.IdeavimErrorListener import com.maddyhome.idea.vim.vimscript.parser.errors.IdeavimErrorListener
import org.assertj.core.api.Assertions
import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.ApiStatus
import org.jetbrains.plugins.ideavim.impl.EmptyTransferable
import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.TestInfo import org.junit.jupiter.api.TestInfo
@ -87,7 +90,6 @@ import java.awt.event.KeyEvent
import java.util.* import java.util.*
import javax.swing.KeyStroke import javax.swing.KeyStroke
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.test.assertContains
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
import kotlin.test.assertNull import kotlin.test.assertNull
@ -215,6 +217,7 @@ abstract class VimTestCase {
} }
protected fun configureByText(content: String) = configureByText(PlainTextFileType.INSTANCE, content) protected fun configureByText(content: String) = configureByText(PlainTextFileType.INSTANCE, content)
protected fun configureByJavaText(content: String) = configureByText(JavaFileType.INSTANCE, content)
protected fun configureByXmlText(content: String) = configureByText(XmlFileType.INSTANCE, content) protected fun configureByXmlText(content: String) = configureByText(XmlFileType.INSTANCE, content)
protected fun configureByJsonText(@Suppress("SameParameterValue") content: String) = protected fun configureByJsonText(@Suppress("SameParameterValue") content: String) =
configureByText(JsonFileType.INSTANCE, content) configureByText(JsonFileType.INSTANCE, content)
@ -252,7 +255,7 @@ abstract class VimTestCase {
return ranges return ranges
} }
protected fun configureByText(fileType: FileType, content: String): Editor { private fun configureByText(fileType: FileType, content: String): Editor {
fixture.configureByText(fileType, content) fixture.configureByText(fileType, content)
NeovimTesting.setupEditor(fixture.editor, testInfo) NeovimTesting.setupEditor(fixture.editor, testInfo)
setEditorVisibleSize(screenWidth, screenHeight) setEditorVisibleSize(screenWidth, screenHeight)
@ -605,7 +608,7 @@ abstract class VimTestCase {
} }
fun assertPluginErrorMessageContains(message: String) { fun assertPluginErrorMessageContains(message: String) {
assertContains(VimPlugin.getMessage(), message) Assertions.assertThat(VimPlugin.getMessage()).contains(message)
} }
protected fun assertCaretsVisualAttributes() { protected fun assertCaretsVisualAttributes() {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2003-2024 The IdeaVim authors * Copyright 2003-2023 The IdeaVim authors
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at * license that can be found in the LICENSE.txt file or at
@ -10,10 +10,10 @@ package org.jetbrains.plugins.ideavim.action
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
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.VimTestCase
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
class AutoIndentTest : VimJavaTestCase() { class AutoIndentTest : VimTestCase() {
// VIM-256 |==| // VIM-256 |==|
@TestWithoutNeovim(reason = SkipNeovimReason.DIFFERENT) @TestWithoutNeovim(reason = SkipNeovimReason.DIFFERENT)
@Test @Test

View File

@ -7,13 +7,15 @@
*/ */
package org.jetbrains.plugins.ideavim.action package org.jetbrains.plugins.ideavim.action
import com.intellij.codeInsight.folding.CodeFoldingManager
import com.intellij.codeInsight.folding.impl.FoldingUtil
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.ReturnTo import com.maddyhome.idea.vim.state.mode.ReturnTo
import 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.Disabled import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@ -812,6 +814,48 @@ foobaz
) )
} }
// VIM-511 |.|
@TestWithoutNeovim(SkipNeovimReason.DIFFERENT)
@Test
fun testRepeatWithParensAndQuotesAutoInsertion() {
configureByJavaText(
"""
class C $c{
}
""".trimIndent(),
)
typeText(injector.parser.parseKeys("o" + "foo(\"<Right>, \"<Right><Right>;" + "<Esc>" + "."))
assertState(
"""class C {
foo("", "");
foo("", "");
}
""",
)
}
// VIM-511 |.|
@TestWithoutNeovim(SkipNeovimReason.DIFFERENT)
@Test
fun testDeleteBothParensAndStartAgain() {
configureByJavaText(
"""
class C $c{
}
""".trimIndent(),
)
typeText(injector.parser.parseKeys("o" + "C(" + "<BS>" + "(int i) {}" + "<Esc>" + "."))
assertState(
"""class C {
C(int i) {}
C(int i) {}
}
""",
)
}
// VIM-613 |.| // VIM-613 |.|
@Test @Test
fun testDeleteEndOfLineAndAgain() { fun testDeleteEndOfLineAndAgain() {
@ -834,6 +878,41 @@ foobaz
) )
} }
// VIM-511 |.|
@TestWithoutNeovim(SkipNeovimReason.DIFFERENT)
@Test
@VimBehaviorDiffers(originalVimAfter = """
class C {
C(int i) {
i = 3;
}
C(int i) {
i = 3;
}
}
""", description = """The bracket should be on the new line.
|This behaviour was explicitely broken as we migrate to the new handlers and I can't support it"""
)
fun testAutoCompleteCurlyBraceWithEnterWithinFunctionBody() {
configureByJavaText(
"""
class C $c{
}
""".trimIndent(),
)
typeText(injector.parser.parseKeys("o" + "C(" + "<BS>" + "(int i) {" + "<Enter>" + "i = 3;" + "<Esc>" + "<Down>" + "."))
assertState(
"""class C {
C(int i) {
i = 3;
}
C(int i) {
i = 3;}
}
""",
)
}
// VIM-1067 |.| // VIM-1067 |.|
@TestWithoutNeovim(SkipNeovimReason.DIFFERENT) @TestWithoutNeovim(SkipNeovimReason.DIFFERENT)
@ -897,6 +976,63 @@ foobaz
) )
} }
// VIM-287 |zc| |O|
@Test
fun testInsertAfterFold() {
configureByJavaText(
"""$c/**
* I should be fold
* a little more text
* and final fold
*/
and some text after""",
)
typeText(injector.parser.parseKeys("zc" + "G" + "O"))
assertState(
"""/**
* I should be fold
* a little more text
* and final fold
*/
$c
and some text after""",
)
}
// VIM-287 |zc| |o|
@TestWithoutNeovim(SkipNeovimReason.FOLDING)
@Test
fun testInsertBeforeFold() {
configureByJavaText(
"""
$c/**
* I should be fold
* a little more text
* and final fold
*/
and some text after
""".trimIndent(),
)
fixture.editor.foldingModel.runBatchFoldingOperation {
CodeFoldingManager.getInstance(fixture.project).updateFoldRegions(fixture.editor)
FoldingUtil.findFoldRegionStartingAtLine(fixture.editor, 0)!!.isExpanded = false
}
typeText(injector.parser.parseKeys("o"))
assertState(
"""
/**
* I should be fold
* a little more text
* and final fold
*/
$c
and some text after
""".trimIndent(),
)
}
@Test @Test
fun testRepeatChangeWordDoesNotBreakNextRepeatFind() { fun testRepeatChangeWordDoesNotBreakNextRepeatFind() {
doTest( doTest(

View File

@ -10,7 +10,7 @@ package org.jetbrains.plugins.ideavim.action
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import org.jetbrains.plugins.ideavim.VimBehaviorDiffers import com.maddyhome.idea.vim.helper.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

View File

@ -12,10 +12,10 @@ import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction
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.helper.VimBehaviorDiffers
import com.maddyhome.idea.vim.newapi.IjVimEditor import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.SelectionType import com.maddyhome.idea.vim.state.mode.SelectionType
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
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
@ -674,6 +674,106 @@ $c tw${c}o
) )
} }
@Test
fun testMotionMethodNextEndAction() {
configureByJavaText(
"""public class Foo {
private static void firstMethod(int argument) {
// Do som${c}ething...
}
${c}private static int x$c; private static void secondMethod(String argument) {
// Do something.$c..
}
}""",
)
typeText(injector.parser.parseKeys("]M"))
assertState(
"""public class Foo {
private static void firstMethod(int argument) {
// Do something...
$c}
private static int x$c; private static void secondMethod(String argument) {
// Do something...
$c}
}""",
)
}
@Test
fun testMotionMethodNextStartAction() {
configureByJavaText(
"""public class Foo {
$c private static void firstMethod(int argument) {
// Do som${c}ething...
}
${c}private static int x$c; private static void secondMethod(String argument) {
// Do something.$c..
}
}""",
)
typeText(injector.parser.parseKeys("]m"))
assertState(
"""public class Foo {
private static void firstMethod(int argument) $c{
// Do something...
}
${c}private static int x; private static void secondMethod(String argument) $c{
// Do something...
}
}""",
)
}
@Test
fun testMotionMethodPreviousEndAction() {
configureByJavaText(
"""public class Foo {
$c private static void firstMethod(int argument) {
// Do som${c}ething...
}
${c}private static int x$c; private static void secondMethod(String argument) {
// Do something.$c..
}
}""",
)
typeText(injector.parser.parseKeys("[M"))
assertState(
"""public class Foo {
private static void firstMethod(int argument) {
// Do something...
$c}
private static int x$c; private static void secondMethod(String argument) {
// Do something...
}
}""",
)
}
@Test
fun testMotionMethodPreviousStartAction() {
configureByJavaText(
"""public class Foo {
$c private static void firstMethod(int argument) {
// Do som${c}ething...
}
${c}private static int x$c; private static void secondMethod(String argument) {
// Do something.$c..
}
}""",
)
typeText(injector.parser.parseKeys("[m"))
assertState(
"""public class Foo $c{
private static void firstMethod(int argument) $c{
// Do something...
}
${c}private static int x; private static void secondMethod(String argument) $c{
// Do something...
}
}""",
)
}
@Test @Test
fun testMotionNthCharacterAction() { fun testMotionNthCharacterAction() {
typeTextInFile( typeTextInFile(
@ -1123,6 +1223,40 @@ $c tw${c}o
assertState("<selection>abcde\nabcde\n\nabcde\nab${c}cde\n</selection>") assertState("<selection>abcde\nabcde\n\nabcde\nab${c}cde\n</selection>")
} }
// com.maddyhome.idea.vim.action.change.change
@Test
fun testAutoIndentLinesVisualAction() {
configureByJavaText(
"""${c}public class Foo {
private boolean x;
private boolean y;
private boolean z;
${c}public void foo() {
x = true; // This will be indented
}
public void bar() {
y = true; // And this will not
}
}
""",
)
typeText(injector.parser.parseKeys("V2j="))
assertState(
"""${c}public class Foo {
private boolean x;
private boolean y;
private boolean z;
${c}public void foo() {
x = true; // This will be indented
}
public void bar() {
y = true; // And this will not
}
}
""",
)
}
@Test @Test
fun testChangeCaseLowerMotionAction() { fun testChangeCaseLowerMotionAction() {
typeTextInFile( typeTextInFile(
@ -2157,6 +2291,43 @@ rtyfg${c}hzxc"""
assertState(after) assertState(after)
} }
@Test
fun testAutoIndentRange() {
val before = "cl${c}ass C {\n C(int i) {\nmy${c}I = i;\n}\n private int myI;\n}"
configureByJavaText(before)
typeText(injector.parser.parseKeys("v2j="))
val after = """${c}class C {
C(int i) {
myI = i;
}
private int myI;
}"""
assertState(after)
}
@Test
fun testAutoIndentMotion() {
val before = "cl${c}ass C {\n C(int i) {\nmy${c}I = i;\n}\n private int myI;\n}"
configureByJavaText(before)
typeText(injector.parser.parseKeys("=3j"))
val after = """${c}class C {
C(int i) {
${c}myI = i;
}
private int myI;
}"""
assertState(after)
}
@Test
fun testAutoIndentLines() {
val before = "class C {\n C$c(int i) {\nmyI = i;\n}\n p${c}rivate int myI;\n}"
configureByJavaText(before)
typeText(injector.parser.parseKeys("=="))
val after = "class C {\n ${c}C(int i) {\nmyI = i;\n}\n ${c}private int myI;\n}"
assertState(after)
}
@Test @Test
fun testPutTextBeforeCursor() { fun testPutTextBeforeCursor() {
val before = "${c}qwe asd ${c}zxc rty ${c}fgh vbn" val before = "${c}qwe asd ${c}zxc rty ${c}fgh vbn"
@ -2436,6 +2607,68 @@ rtyfg${c}hzxc"""
assertState(after) assertState(after)
} }
@Test
fun testPutTextBeforeCursorWithIndention() {
val before = """class C {
C(int i) {
myI = i;
}
${c}private int myI = 0;
{
${c}private int myJ = 0;
}
${c}private int myK = 0;
}"""
configureByJavaText(before)
injector.registerGroup.storeText('*', "private int myK = 0;\n", SelectionType.LINE_WISE)
typeText(injector.parser.parseKeys("\"*P"))
val after = """class C {
C(int i) {
myI = i;
}
${c}private int myK = 0;
private int myI = 0;
{
${c}private int myK = 0;
private int myJ = 0;
}
${c}private int myK = 0;
private int myK = 0;
}"""
assertState(after)
}
@Test
fun testPutTextAfterCursorWithIndention() {
val before = """class C {
C(int i) {
myI = i;
}
${c}private int myI = 0;
{
${c}private int myJ = 0;
}
${c}private int myK = 0;
}"""
configureByJavaText(before)
injector.registerGroup.storeText('*', "private int myK = 0;", SelectionType.LINE_WISE)
typeText(injector.parser.parseKeys("\"*p"))
val after = """class C {
C(int i) {
myI = i;
}
private int myI = 0;
${c}private int myK = 0;
{
private int myJ = 0;
${c}private int myK = 0;
}
private int myK = 0;
${c}private int myK = 0;
}"""
assertState(after)
}
@Test @Test
fun testPutTextBeforeCursorBlockwise() { fun testPutTextBeforeCursorBlockwise() {
val before = """ *$c on${c}e val before = """ *$c on${c}e

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2003-2024 The IdeaVim authors * Copyright 2003-2023 The IdeaVim authors
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at * license that can be found in the LICENSE.txt file or at
@ -10,11 +10,16 @@ package org.jetbrains.plugins.ideavim.action
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
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.VimTestCase
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@Suppress("unused") @Suppress("unused")
class ReformatCodeTest : VimJavaTestCase() { class ReformatCodeTest : VimTestCase() {
@Test
fun testMark() {
kotlin.test.assertTrue(true)
}
@Test @Test
fun testEmpty() { fun testEmpty() {
configureByJavaText("<caret>") configureByJavaText("<caret>")

View File

@ -9,9 +9,6 @@
package org.jetbrains.plugins.ideavim.action package org.jetbrains.plugins.ideavim.action
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.key
import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.key.MappingOwner
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
@ -70,44 +67,4 @@ class RepeatActionTest : VimTestCase() {
"Good.", "Good.",
) )
} }
@Test
fun `repeat delete command`() {
doTest(
"i<del><esc>.",
"${c}1234567890",
"${c}34567890"
)
}
@Test
fun `map for the delete`() {
doTest(
"i<del><esc>.",
"""
Lorem Ipsum
${c}Lorem ipsum dolor sit amet,
consectetur adipiscing elit
Sed in orci mauris.
Cras id tellus in ex imperdiet egestas.
""".trimIndent(),
"""
Lorem Ipsum
${c}jjLorem ipsum dolor sit amet,
consectetur adipiscing elit
Sed in orci mauris.
Cras id tellus in ex imperdiet egestas.
""".trimIndent()
) {
injector.keyGroup.putKeyMapping(
MappingMode.I,
listOf(key("<DEL>")),
MappingOwner.IdeaVim.Other,
listOf(key("j")),
false
)
}
}
} }

View File

@ -10,7 +10,7 @@ package org.jetbrains.plugins.ideavim.action.change
import com.intellij.idea.TestFor 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.VimBehaviorDiffers import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
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

View File

@ -12,7 +12,7 @@ package org.jetbrains.plugins.ideavim.action.change.change
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.state.mode.Mode
import org.jetbrains.plugins.ideavim.VimBehaviorDiffers import com.maddyhome.idea.vim.helper.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

View File

@ -11,7 +11,7 @@
package org.jetbrains.plugins.ideavim.action.change.change package org.jetbrains.plugins.ideavim.action.change.change
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
import org.jetbrains.plugins.ideavim.VimBehaviorDiffers import com.maddyhome.idea.vim.helper.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

View File

@ -9,7 +9,7 @@
package org.jetbrains.plugins.ideavim.action.change.change.number package org.jetbrains.plugins.ideavim.action.change.change.number
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
import org.jetbrains.plugins.ideavim.VimBehaviorDiffers import com.maddyhome.idea.vim.helper.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

View File

@ -11,7 +11,7 @@
package org.jetbrains.plugins.ideavim.action.change.delete package org.jetbrains.plugins.ideavim.action.change.delete
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import org.jetbrains.plugins.ideavim.VimBehaviorDiffers import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
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

View File

@ -9,7 +9,7 @@
package org.jetbrains.plugins.ideavim.action.change.insert package org.jetbrains.plugins.ideavim.action.change.insert
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
import org.jetbrains.plugins.ideavim.VimBehaviorDiffers import com.maddyhome.idea.vim.helper.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

View File

@ -12,7 +12,7 @@ package org.jetbrains.plugins.ideavim.action.change.insert
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.state.mode.Mode
import org.jetbrains.plugins.ideavim.VimBehaviorDiffers import com.maddyhome.idea.vim.helper.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

View File

@ -17,8 +17,6 @@ import com.intellij.openapi.editor.actionSystem.EditorActionHandlerBean
import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.testFramework.ExtensionTestUtil import com.intellij.testFramework.ExtensionTestUtil
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.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
@ -47,7 +45,6 @@ class InsertEnterActionTest : VimTestCase() {
forEachBean.action = "EditorEnter" forEachBean.action = "EditorEnter"
forEachBean.setPluginDescriptor(PluginManagerCore.getPlugin(VimPlugin.getPluginId())!!) forEachBean.setPluginDescriptor(PluginManagerCore.getPlugin(VimPlugin.getPluginId())!!)
if (injector.globalOptions().octopushandler) {
if (repetitionInfo.currentRepetition == 1) { if (repetitionInfo.currentRepetition == 1) {
ExtensionTestUtil.maskExtensions( ExtensionTestUtil.maskExtensions(
ExtensionPointName("com.intellij.editorActionHandler"), ExtensionPointName("com.intellij.editorActionHandler"),
@ -68,7 +65,6 @@ class InsertEnterActionTest : VimTestCase() {
) )
} }
} }
}
@RepeatedTest(3) @RepeatedTest(3)
fun `test insert enter`() { fun `test insert enter`() {

View File

@ -79,6 +79,25 @@ class InsertNewLineAboveActionTest : VimTestCase() {
doTest("O", before, after, Mode.INSERT) doTest("O", before, after, Mode.INSERT)
} }
@TestWithoutNeovim(SkipNeovimReason.PLUGIN) // Java support would be a neovim plugin
@Test
fun `test insert new line above matches indent for java`() {
val before = """public class C {
| Integer a;
| ${c}Integer b;
|}
""".trimMargin()
val after = """public class C {
| Integer a;
| $c
| Integer b;
|}
""".trimMargin()
configureByJavaText(before)
typeText("O")
assertState(after)
}
@Test @Test
fun `test insert new line above with multiple carets`() { fun `test insert new line above with multiple carets`() {
val before = """ I fou${c}nd it in a legendary land val before = """ I fou${c}nd it in a legendary land

View File

@ -79,6 +79,44 @@ class InsertNewLineBelowActionTest : VimTestCase() {
doTest("o", before, after, Mode.INSERT) doTest("o", before, after, Mode.INSERT)
} }
@TestWithoutNeovim(SkipNeovimReason.PLUGIN) // Java support would be a neovim plugin
@Test
fun `test insert new line below matches indent for java`() {
val before = """public class C {
| ${c}Integer a;
| Integer b;
|}
""".trimMargin()
val after = """public class C {
| Integer a;
| $c
| Integer b;
|}
""".trimMargin()
configureByJavaText(before)
typeText("o")
assertState(after)
}
@TestWithoutNeovim(SkipNeovimReason.PLUGIN) // Java support would be a neovim plugin
@Test
fun `test insert new line below matches indent for java 1`() {
val before = """public class C {
|$c Integer a;
| Integer b;
|}
""".trimMargin()
val after = """public class C {
| Integer a;
| $c
| Integer b;
|}
""".trimMargin()
configureByJavaText(before)
typeText("o")
assertState(after)
}
@Test @Test
fun `test insert new line below with multiple carets`() { fun `test insert new line below with multiple carets`() {
val before = """ I fou${c}nd it in a legendary land val before = """ I fou${c}nd it in a legendary land

Some files were not shown because too many files have changed in this diff Show More