1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-08-17 16:31:45 +02:00

Compare commits

..

22 Commits

Author SHA1 Message Date
7c7e7728f2 Set plugin version to chylex-44 2025-03-25 09:25:25 +01:00
de930ed57c Make search highlights temporary 2025-03-25 09:25:25 +01:00
590d5bd22d Exit insert mode after refactoring 2025-03-25 09:25:25 +01:00
69c748d881 Add action to run last macro in all opened files 2025-03-25 09:25:25 +01:00
144cc5c3fc Stop macro execution after a failed search 2025-03-25 09:25:24 +01:00
fb270cdbc5 Revert per-caret registers 2025-03-25 09:25:24 +01:00
ce6a296233 Fix(VIM-3364): Exception with mapped Generate action 2025-03-25 09:25:24 +01:00
949f359b98 Apply scrolloff after executing native IDEA actions 2025-03-25 09:25:24 +01:00
c922426e02 Stay on same line after reindenting 2025-03-25 09:25:24 +01:00
9240e82f2d Update search register when using f/t 2025-03-25 09:25:24 +01:00
a1639d80b0 Automatically add unambiguous imports after running a macro 2025-03-25 09:25:24 +01:00
7860b98107 Fix(VIM-3179): Respect virtual space below editor (imperfectly) 2025-03-25 09:25:24 +01:00
7157f9c5a5 Fix(VIM-3178): Workaround to support "Jump to Source" action mapping 2025-03-25 09:25:24 +01:00
1522618cd6 Add support for count for visual and line motion surround 2025-03-25 09:25:24 +01:00
09862c8356 Fix vim-surround not working with multiple cursors
Fixes multiple cursors with vim-surround commands `cs, ds, S` (but not `ys`).
2025-03-25 09:25:24 +01:00
c6ef3f286f Fix(VIM-696) Restore visual mode after undo/redo, and disable incompatible actions 2025-03-25 09:25:24 +01:00
b358e63444 Respect count with <Action> mappings 2025-03-25 09:25:24 +01:00
7ac743c604 Change matchit plugin to use HTML patterns in unrecognized files 2025-03-25 09:25:24 +01:00
db3d3fc608 Reset insert mode when switching active editor 2025-03-25 09:25:24 +01:00
14d313907b Remove notifications about configuration options 2025-03-25 09:25:24 +01:00
fe37a69544 Remove update checker 2025-03-25 09:25:24 +01:00
54de3dac25 Set custom plugin version 2025-03-25 09:25:24 +01:00
301 changed files with 3941 additions and 11321 deletions

View File

@@ -28,12 +28,10 @@ jobs:
uses: jtalk/url-health-check-action@v3 uses: jtalk/url-health-check-action@v3
with: with:
url: http://127.0.0.1:8082 url: http://127.0.0.1:8082
max-attempts: 100 max-attempts: 20
retry-delay: 10s retry-delay: 10s
- name: Tests - name: Tests
run: gradle :tests:ui-rd-tests:testUi run: gradle :tests:ui-rd-tests:testUi
env:
RIDER_LICENSE: ${{ secrets.RIDER_LICENSE }}
- name: Move video - name: Move video
if: always() if: always()
run: mv tests/ui-rd-tests/video build/reports run: mv tests/ui-rd-tests/video build/reports

4
.gitignore vendored
View File

@@ -32,6 +32,4 @@ vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
# Created by github automation # Created by github automation
settings.xml settings.xml
.kotlin .kotlin
.claude/settings.local.json

View File

@@ -5,7 +5,7 @@
<option name="executionName" /> <option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" /> <option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="-x :tests:property-tests:test -x :tests:long-running-tests:test" /> <option name="scriptParameters" value="" />
<option name="taskDescriptions"> <option name="taskDescriptions">
<list /> <list />
</option> </option>
@@ -19,7 +19,6 @@
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess> <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess> <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled> <DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" /> <method v="2" />
</configuration> </configuration>
</component> </component>

View File

@@ -5,11 +5,11 @@ object Constants {
const val EAP_CHANNEL = "eap" const val EAP_CHANNEL = "eap"
const val DEV_CHANNEL = "Dev" const val DEV_CHANNEL = "Dev"
const val NVIM_TESTS = "2025.1" const val NVIM_TESTS = "2024.3.3"
const val PROPERTY_TESTS = "2025.1" const val PROPERTY_TESTS = "2024.3.3"
const val LONG_RUNNING_TESTS = "2025.1" const val LONG_RUNNING_TESTS = "2024.3.3"
const val RELEASE = "2025.1" const val RELEASE = "2024.3.3"
const val RELEASE_DEV = "2025.1" const val RELEASE_DEV = "2024.3.3"
const val RELEASE_EAP = "2025.1" const val RELEASE_EAP = "2024.3.3"
} }

View File

@@ -23,8 +23,8 @@ object Project : Project({
vcsRoot(ReleasesVcsRoot) vcsRoot(ReleasesVcsRoot)
// Active tests // Active tests
buildType(TestingBuildType("Latest EAP", version = "LATEST-EAP-SNAPSHOT")) buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT"))
buildType(TestingBuildType("2025.1")) buildType(TestingBuildType("2024.3.3", "<default>"))
buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT")) buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT"))
buildType(PropertyBased) buildType(PropertyBased)

View File

@@ -43,9 +43,6 @@ object Compatibility : IdeaVimBuildType({
java -jar verifier1/verifier-cli-dev-all-2.jar check-plugin '${'$'}com.julienphalip.ideavim.peekaboo' [latest-IU] -team-city java -jar verifier1/verifier-cli-dev-all-2.jar check-plugin '${'$'}com.julienphalip.ideavim.peekaboo' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-2.jar check-plugin '${'$'}com.julienphalip.ideavim.switch' [latest-IU] -team-city java -jar verifier1/verifier-cli-dev-all-2.jar check-plugin '${'$'}com.julienphalip.ideavim.switch' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-2.jar check-plugin '${'$'}com.julienphalip.ideavim.functiontextobj' [latest-IU] -team-city java -jar verifier1/verifier-cli-dev-all-2.jar check-plugin '${'$'}com.julienphalip.ideavim.functiontextobj' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-2.jar check-plugin '${'$'}com.miksuki.HighlightCursor' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-2.jar check-plugin '${'$'}com.ugarosa.idea.edgemotion' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-2.jar check-plugin '${'$'}cn.mumukehao.plugin' [latest-IU] -team-city
""".trimIndent() """.trimIndent()
} }
} }

View File

@@ -25,7 +25,7 @@ object LongRunning : IdeaVimBuildType({
steps { steps {
gradle { gradle {
tasks = "clean :tests:long-running-tests:test" tasks = "clean :tests:long-running-tests:testLongRunning"
buildFile = "" buildFile = ""
enableStacktrace = true enableStacktrace = true
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto" jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"

View File

@@ -39,7 +39,7 @@ object Nvim : IdeaVimBuildType({
""".trimIndent() """.trimIndent()
} }
gradle { gradle {
tasks = "clean test -x :tests:property-tests:test -x :tests:long-running-tests:test -Dnvim" tasks = "clean test -Dnvim"
buildFile = "" buildFile = ""
enableStacktrace = true enableStacktrace = true
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto" jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"

View File

@@ -24,8 +24,7 @@ object PropertyBased : IdeaVimBuildType({
steps { steps {
gradle { gradle {
clearConditions() tasks = "clean :tests:property-tests:testPropertyBased"
tasks = "clean :tests:property-tests:test"
buildFile = "" buildFile = ""
enableStacktrace = true enableStacktrace = true
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto" jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"

View File

@@ -115,7 +115,7 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
} }
script { script {
name = "Run tests" name = "Run tests"
scriptContent = "./gradlew test -x :tests:property-tests:test -x :tests:long-running-tests:test" scriptContent = "./gradlew test"
} }
gradle { gradle {
name = "Publish release" name = "Publish release"

View File

@@ -12,7 +12,7 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.vcs
open class TestingBuildType( open class TestingBuildType(
private val testName: String, private val testName: String,
private val branch: String = "<default>", private val branch: String,
private val version: String = testName, private val version: String = testName,
private val javaVersion: String? = null, private val javaVersion: String? = null,
private val javaPlugin: Boolean = true, private val javaPlugin: Boolean = true,
@@ -40,7 +40,7 @@ open class TestingBuildType(
steps { steps {
gradle { gradle {
clearConditions() clearConditions()
tasks = "clean test -x :tests:property-tests:test -x :tests:long-running-tests:test" tasks = "clean test"
buildFile = "" buildFile = ""
enableStacktrace = true enableStacktrace = true
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto" jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"

View File

@@ -15,7 +15,7 @@ object GitHub : Project({
name = "Pull Requests checks" name = "Pull Requests checks"
description = "Automatic checking of GitHub Pull Requests" description = "Automatic checking of GitHub Pull Requests"
buildType(GithubBuildType("clean test -x :tests:property-tests:test -x :tests:long-running-tests:test", "Tests")) buildType(GithubBuildType("clean test", "Tests"))
}) })
class GithubBuildType(command: String, desc: String) : IdeaVimBuildType({ class GithubBuildType(command: String, desc: String) : IdeaVimBuildType({

View File

@@ -26,13 +26,6 @@ Previous maintainers:
&nbsp; &nbsp;
Andrey Vlasovskikh Andrey Vlasovskikh
Previous support members:
* [![icon][mail]](mailto:lejia.chen@jetbrains.com)
[![icon][github-off]](#)
&nbsp;
Lejia Chen
Contributors: Contributors:
* [![icon][mail]](mailto:yole@jetbrains.com) * [![icon][mail]](mailto:yole@jetbrains.com)
@@ -594,34 +587,6 @@ Contributors:
[![icon][github]](https://github.com/Iliya-usov) [![icon][github]](https://github.com/Iliya-usov)
&nbsp; &nbsp;
Ilya Usov Ilya Usov
* [![icon][mail]](mailto:peterHoburg@users.noreply.github.com)
[![icon][github]](https://github.com/peterHoburg)
&nbsp;
Peter Hoburg
* [![icon][mail]](mailto:erotourtes@gmail.com)
[![icon][github]](https://github.com/erotourtes)
&nbsp;
Max Siryk
* [![icon][mail]](mailto:ivan.yarkov@jetbrains.com)
[![icon][github]](https://github.com/MToolMakerJB)
&nbsp;
Ivan Yarkov
* [![icon][mail]](mailto:mia.vucinic@jetbrains.com)
[![icon][github]](https://github.com/vumi19)
&nbsp;
Mia Vucinic
* [![icon][mail]](mailto:canava.thomas@gmail.com)
[![icon][github]](https://github.com/Malandril)
&nbsp;
Thomas Canava
* [![icon][mail]](mailto:xinhe.wang@jetbrains.com)
[![icon][github]](https://github.com/wxh06)
&nbsp;
Xinhe Wang
* [![icon][mail]](mailto:zuber.kuba@gmail.com)
[![icon][github]](https://github.com/zuberol)
&nbsp;
Jakub Zuber
Previous contributors: Previous contributors:

View File

@@ -65,7 +65,7 @@ We've prepared some useful configurations for you:
And here are useful gradle commands: And here are useful gradle commands:
* `./gradlew runIde` — start the dev version of IntelliJ IDEA with IdeaVim installed. * `./gradlew runIde` — start the dev version of IntelliJ IDEA with IdeaVim installed.
* `./gradlew test -x :tests:property-tests:test -x :tests:long-running-tests:test` — run tests. * `./gradlew test` — run tests.
* `./gradlew buildPlugin` — build the plugin. The result will be located in `build/distributions`. This file can be * `./gradlew buildPlugin` — build the plugin. The result will be located in `build/distributions`. This file can be
installed by using `Settings | Plugin | >Gear Icon< | Install Plugin from Disk...`. You can stay with your personal build installed by using `Settings | Plugin | >Gear Icon< | Install Plugin from Disk...`. You can stay with your personal build
for a few days or send it to a friend for testing. for a few days or send it to a friend for testing.

View File

@@ -29,8 +29,8 @@ IdeaVim is a Vim engine for JetBrains IDEs.
#### Compatibility #### Compatibility
IntelliJ IDEA, PyCharm, GoLand, CLion, PhpStorm, WebStorm, RubyMine, DataGrip, DataSpell, Rider, Cursive, IntelliJ IDEA, PyCharm, CLion, PhpStorm, WebStorm, RubyMine, DataGrip, GoLand, Rider, Cursive,
Android Studio, and other [JetBrains IDEs](https://www.jetbrains.com/ides/). Android Studio and other IntelliJ platform based IDEs.
Setup Setup
------------ ------------
@@ -89,12 +89,29 @@ Here are some examples of supported vim features and commands:
* Full Vim regexps for search and search/replace * Full Vim regexps for search and search/replace
* Vim web help * Vim web help
* `~/.ideavimrc` configuration file * `~/.ideavimrc` configuration file
* Vim script
* IdeaVim plugins [IdeaVim plugins](https://github.com/JetBrains/ideavim/wiki/IdeaVim-Plugins):
* vim-easymotion
* NERDTree
* vim-surround
* vim-multiple-cursors
* vim-commentary
* argtextobj.vim
* vim-textobj-entire
* ReplaceWithRegister
* vim-exchange
* vim-highlightedyank
* vim-paragraph-motion
* vim-indent-object
* match.it
etc
See also: See also:
* [Top feature requests and bugs](https://youtrack.jetbrains.com/issues/VIM?q=%23Unresolved+sort+by%3A+votes) * [Top feature requests and bugs](https://youtrack.jetbrains.com/issues/VIM?q=%23Unresolved+sort+by%3A+votes)
* [Vimscript support roadmap](vimscript-info/VIMSCRIPT_ROADMAP.md)
* [List of supported in-build functions](vimscript-info/FUNCTIONS_INFO.MD)
Files Files
----- -----
@@ -248,7 +265,8 @@ IdeaVim can execute custom scripts that are written with Vim Script.
At the moment we support all language features, but not all of the built-in functions and options are supported. At the moment we support all language features, but not all of the built-in functions and options are supported.
Additionally, you may be interested in the Additionally, you may be interested in the
[Vim Script Discussion](https://github.com/JetBrains/ideavim/discussions/357). [Vim Script Discussion](https://github.com/JetBrains/ideavim/discussions/357) or
[Vim Script Roadmap](https://github.com/JetBrains/ideavim/blob/master/vimscript-info/VIMSCRIPT_ROADMAP.md).
### IDE specific options ### IDE specific options

View File

@@ -8,7 +8,7 @@
plugins { plugins {
kotlin("jvm") kotlin("jvm")
kotlin("plugin.serialization") version "2.2.0" kotlin("plugin.serialization") version "2.0.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:2.1.21-2.0.2") compileOnly("com.google.devtools.ksp:symbol-processing-api:2.1.10-1.0.29")
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

@@ -31,12 +31,9 @@ import kotlinx.serialization.json.putJsonObject
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.RepositoryBuilder import org.eclipse.jgit.lib.RepositoryBuilder
import org.intellij.markdown.ast.getTextInNode import org.intellij.markdown.ast.getTextInNode
import org.intellij.markdown.ast.impl.ListCompositeNode
import org.jetbrains.changelog.Changelog import org.jetbrains.changelog.Changelog
import org.jetbrains.intellij.platform.gradle.TestFrameworkType import org.jetbrains.intellij.platform.gradle.TestFrameworkType
import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.kohsuke.github.GHUser import org.kohsuke.github.GHUser
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL
@@ -48,19 +45,19 @@ buildscript {
} }
dependencies { dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.2.0") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.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")
// This is needed for jgit to connect to ssh // This is needed for jgit to connect to ssh
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.3.0.202506031305-r") classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.1.0.202411261347-r")
classpath("org.kohsuke:github-api:1.305") classpath("org.kohsuke:github-api:1.305")
classpath("io.ktor:ktor-client-core:3.2.2") classpath("io.ktor:ktor-client-core:3.1.1")
classpath("io.ktor:ktor-client-cio:3.2.2") classpath("io.ktor:ktor-client-cio:3.1.1")
classpath("io.ktor:ktor-client-auth:3.2.2") classpath("io.ktor:ktor-client-auth:3.1.1")
classpath("io.ktor:ktor-client-content-negotiation:3.2.2") classpath("io.ktor:ktor-client-content-negotiation:3.1.1")
classpath("io.ktor:ktor-serialization-kotlinx-json:3.2.2") classpath("io.ktor:ktor-serialization-kotlinx-json:3.1.1")
// This comes from the changelog plugin // This comes from the changelog plugin
// classpath("org.jetbrains:markdown:0.3.1") // classpath("org.jetbrains:markdown:0.3.1")
@@ -69,19 +66,14 @@ buildscript {
plugins { plugins {
java java
kotlin("jvm") version "2.2.0" kotlin("jvm") version "2.0.21"
application application
id("java-test-fixtures") id("java-test-fixtures")
id("org.jetbrains.intellij.platform") version "2.3.0"
// NOTE: Unignore "test block comment falls back to line comment when not available" test
// After changing this version. It supposed to work on the next version of the gradle plugin
// Or go report to the devs that this test still fails.
id("org.jetbrains.intellij.platform") version "2.6.0"
id("org.jetbrains.changelog") version "2.2.1" id("org.jetbrains.changelog") version "2.2.1"
id("org.jetbrains.kotlinx.kover") version "0.6.1" id("org.jetbrains.kotlinx.kover") version "0.6.1"
id("com.dorongold.task-tree") version "4.0.1" id("com.dorongold.task-tree") version "4.0.1"
id("com.google.devtools.ksp") version "2.2.0-2.0.2" id("com.google.devtools.ksp") version "2.0.21-1.0.25"
} }
val moduleSources by configurations.registering val moduleSources by configurations.registering
@@ -120,11 +112,7 @@ dependencies {
intellijPlatform { intellijPlatform {
// Snapshots don't use installers // Snapshots don't use installers
// https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html#target-versions-installers // https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html#target-versions-installers
var useInstaller = "EAP-SNAPSHOT" !in ideaVersion val useInstaller = "EAP-SNAPSHOT" !in ideaVersion
if (ideaType == "RD") {
// Using Rider as a target IntelliJ Platform with `useInstaller = true` is currently not supported, please set `useInstaller = false` instead. See: https://github.com/JetBrains/intellij-platform-gradle-plugin/issues/1852
useInstaller = false
}
// Note that it is also possible to use local("...") to compile against a locally installed IDE // Note that it is also possible to use local("...") to compile against a locally installed IDE
// E.g. local("/Users/{user}/Applications/IntelliJ IDEA Ultimate.app") // E.g. local("/Users/{user}/Applications/IntelliJ IDEA Ultimate.app")
@@ -139,14 +127,9 @@ dependencies {
// AceJump is an optional dependency. We use their SessionManager class to check if it's active // AceJump is an optional dependency. We use their SessionManager class to check if it's active
plugin("AceJump", "3.8.19") plugin("AceJump", "3.8.19")
plugin("com.intellij.classic.ui", "251.23774.318") plugin("com.intellij.classic.ui", "243.21565.122")
bundledPlugins("org.jetbrains.plugins.terminal") bundledPlugins("org.jetbrains.plugins.terminal", "com.intellij.modules.json")
// VERSION UPDATE: This module is required since 2025.2
if (ideaVersion == "LATEST-EAP-SNAPSHOT") {
bundledModule("intellij.spellchecker")
}
} }
moduleSources(project(":vim-engine", "sourcesJarArtifacts")) moduleSources(project(":vim-engine", "sourcesJarArtifacts"))
@@ -166,19 +149,19 @@ dependencies {
testFixturesImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion") testFixturesImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
// https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin // https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin
testImplementation("org.mockito.kotlin:mockito-kotlin:6.0.0") testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.13.3") testImplementation("org.junit.jupiter:junit-jupiter-api:5.12.0")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.13.3") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.12.0")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.13.3") testImplementation("org.junit.jupiter:junit-jupiter-params:5.12.0")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.13.3") testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.12.0")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.13.3") testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.12.0")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.13.3") testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.12.0")
// Temp workaround suggested in https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-faq.html#junit5-test-framework-refers-to-junit4 // Temp workaround suggested in https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-faq.html#junit5-test-framework-refers-to-junit4
// Can be removed when IJPL-159134 is fixed // Can be removed when IJPL-159134 is fixed
// testRuntimeOnly("junit:junit:4.13.2") // testRuntimeOnly("junit:junit:4.13.2")
testImplementation("org.junit.vintage:junit-vintage-engine:5.13.2") testImplementation("org.junit.vintage:junit-vintage-engine:5.12.0")
// testFixturesImplementation("org.junit.vintage:junit-vintage-engine:5.10.3") // testFixturesImplementation("org.junit.vintage:junit-vintage-engine:5.10.3")
} }
@@ -206,9 +189,12 @@ tasks {
useJUnitPlatform() useJUnitPlatform()
// Set teamcity env variable locally to run additional tests for leaks. // Set teamcity env variable locally to run additional tests for leaks.
println("Project leak checks: If you experience project leaks on TeamCity that doesn't reproduce locally") // By default, this test runs on TC only, but this test doesn't take a lot of time,
println("Uncomment the following line in build.gradle to enable leak checks (see build.gradle config)") // so we can turn it on for local development
// environment("TEAMCITY_VERSION" to "X") if (environment["TEAMCITY_VERSION"] == null) {
println("Set env TEAMCITY_VERSION to X to enable project leak checks from the platform")
environment("TEAMCITY_VERSION" to "X")
}
systemProperty("ideavim.nvim.test", System.getProperty("nvim") ?: false) systemProperty("ideavim.nvim.test", System.getProperty("nvim") ?: false)
@@ -228,11 +214,42 @@ tasks {
options.encoding = "UTF-8" options.encoding = "UTF-8"
} }
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
// See https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#kotlin-standard-library
// For the list of bundled versions
apiVersion = "2.0"
freeCompilerArgs = listOf(
"-Xjvm-default=all-compatibility",
// Needed to compile the AceJump which uses kotlin beta
// Without these two option compilation fails
"-Xskip-prerelease-check",
"-Xallow-unstable-dependencies",
)
// allWarningsAsErrors = true
}
}
compileTestKotlin {
enabled = false
kotlinOptions {
jvmTarget = javaVersion
apiVersion = "2.0"
// Needed to compile the AceJump which uses kotlin beta
// Without these two option compilation fails
freeCompilerArgs += listOf("-Xskip-prerelease-check", "-Xallow-unstable-dependencies")
// allWarningsAsErrors = true
}
}
// Note that this will run the plugin installed in the IDE specified in dependencies. To run in a different IDE, use // Note that this will run the plugin installed in the IDE specified in dependencies. To run in a different IDE, use
// a custom task (see below) // a custom task (see below)
runIde { runIde {
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true) systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
systemProperty("idea.trust.all.projects", "true")
} }
// Uncomment to run the plugin in a custom IDE, rather than the IDE specified as a compile target in dependencies // Uncomment to run the plugin in a custom IDE, rather than the IDE specified as a compile target in dependencies
@@ -301,23 +318,6 @@ kotlin {
jvmToolchain { jvmToolchain {
languageVersion.set(JavaLanguageVersion.of(javaVersion)) languageVersion.set(JavaLanguageVersion.of(javaVersion))
} }
compilerOptions {
jvmTarget.set(JvmTarget.fromTarget(javaVersion))
// See https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#kotlin-standard-library
// For the list of bundled versions
apiVersion.set(KotlinVersion.KOTLIN_2_0)
freeCompilerArgs = listOf(
"-Xjvm-default=all-compatibility",
// Needed to compile the AceJump which uses kotlin beta
// Without these two option compilation fails
"-Xskip-prerelease-check",
"-Xallow-unstable-dependencies",
)
// allWarningsAsErrors = true
}
} }
gradle.projectsEvaluated { gradle.projectsEvaluated {
@@ -822,9 +822,7 @@ fun updateAuthors(uncheckedEmails: Set<String>) {
org.intellij.markdown.parser.MarkdownParser(org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor()) org.intellij.markdown.parser.MarkdownParser(org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor())
val tree = parser.buildMarkdownTreeFromString(authors) val tree = parser.buildMarkdownTreeFromString(authors)
val contributorsSection = tree.children val contributorsSection = tree.children[24]
.filter { it is ListCompositeNode }
.single { it.getTextInNode(authors).contains("yole") }
val existingEmails = mutableSetOf<String>() val existingEmails = mutableSetOf<String>()
for (child in contributorsSection.children) { for (child in contributorsSection.children) {
if (child.children.size > 1) { if (child.children.size > 1) {

View File

@@ -16,95 +16,10 @@ in `~/.ideavimrc`. E.g. `set nosurround`.
Available plugins: Available plugins:
<details> <details>
<summary><h2>argtextobj: Provides a text-object 'a' argument</h2></summary> <summary><h2>easymotion</h2></summary>
Original plugin: [argtextobj.vim](https://www.vim.org/scripts/script.php?script_id=2699).
### Summary:
This plugin provides a text-object 'a' (argument).
You can d(elete), c(hange), v(select)... an argument or inner argument in familiar ways.
That is, such as 'daa'(delete-an-argument) 'cia'(change-inner-argument) 'via'(select-inner-argument).
What this script does is more than just typing
F,dt,
because it recognizes inclusion relationship of parentheses.
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'vim-scripts/argtextobj.vim'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'vim-scripts/argtextobj.vim'</code>
<br/>
<code>Plug 'https://github.com/vim-scripts/argtextobj.vim'</code>
<br/>
<code>Plug 'argtextobj.vim'</code>
<br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=2699'</code>
<br/>
<code>set argtextobj</code>
</details>
### Instructions
By default, only the arguments inside parenthesis are considered. To extend the functionality
to other types of brackets, set `g:argtextobj_pairs` variable to a comma-separated
list of colon-separated pairs (same as VIM's `matchpairs` option), like
`let g:argtextobj_pairs="(:),{:},<:>"`. The order of pairs matters when
handling symbols that can also be operators: `func(x << 5, 20) >> 17`. To handle
this syntax parenthesis, must come before angle brackets in the list.
https://www.vim.org/scripts/script.php?script_id=2699
</details>
<details>
<summary><h2>commentary: Adds mapping for quickly commenting stuff out</h2></summary>
By [Daniel Leong](https://github.com/dhleong)
Original plugin: [commentary.vim](https://github.com/tpope/vim-commentary).
### Summary:
Comment stuff out.
Use gcc to comment out a line (takes a count), gc to comment out the target of a motion
(for example, gcap to comment out a paragraph), gc in visual mode to comment out the selection,
and gc in operator pending mode to target a comment.
You can also use it as a command, either with a range like :7,17Commentary,
or as part of a :global invocation like with :g/TODO/Commentary.
That's it.
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'tpope/vim-commentary'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'tpope/vim-commentary'</code>
<br/>
<code>Plug 'https://github.com/tpope/vim-commentary'</code>
<br/>
<code>Plug 'vim-commentary'</code>
<br/>
<code>Plug 'tcomment_vim'</code>
<br/>
<code>set commentary</code>
</details>
### Instructions
https://github.com/tpope/vim-commentary/blob/master/doc/commentary.txt
</details>
<details>
<summary><h2>easymotion: Simplifies some motions</h2></summary>
Original plugin: [vim-easymotion](https://github.com/easymotion/vim-easymotion). Original plugin: [vim-easymotion](https://github.com/easymotion/vim-easymotion).
### Summary:
EasyMotion provides a much simpler way to use some motions in vim.
It takes the \<number> out of \<number>w or \<number>f{char} by highlighting all possible choices
and allowing you to press one key to jump directly to the target.
### Setup: ### Setup:
- Install [IdeaVim-EasyMotion](https://plugins.jetbrains.com/plugin/13360-ideavim-easymotion/) - Install [IdeaVim-EasyMotion](https://plugins.jetbrains.com/plugin/13360-ideavim-easymotion/)
and [AceJump](https://plugins.jetbrains.com/plugin/7086-acejump/) plugins. and [AceJump](https://plugins.jetbrains.com/plugin/7086-acejump/) plugins.
@@ -126,176 +41,80 @@ All commands with the mappings are supported. See the [full list of supported co
</details> </details>
<details> <details>
<summary><h2>exchange: Easy text exchange operator</h2></summary> <summary><h2>sneak</h2></summary>
By [fan-tom](https://github.com/fan-tom) <img src="images/sneakIcon.svg" width="80" height="80" alt="icon"/>
Original plugin: [vim-exchange](https://github.com/tommcdo/vim-exchange).
### Summary:
Easy text exchange operator for Vim.
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).
### Setup: ### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'tommcdo/vim-exchange'` - Add the following command to `~/.ideavimrc`: `Plug 'justinmk/vim-sneak'`
### Instructions
* Type `s` and two chars to start sneaking in forward direction
* 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>
<summary><h2>NERDTree</h2></summary>
Original plugin: [NERDTree](https://github.com/preservim/nerdtree).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'preservim/nerdtree'`
<details> <details>
<summary>Alternative syntax</summary> <summary>Alternative syntax</summary>
<code>Plugin 'tommcdo/vim-exchange'</code> <code>Plugin 'preservim/nerdtree'</code>
<br/> <br/>
<code>Plug 'https://github.com/tommcdo/vim-exchange'</code> <code>Plug 'https://github.com/preservim/nerdtree'</code>
<br/> <br/>
<code>Plug 'vim-exchange'</code> <code>Plug 'nerdtree'</code>
<br/> <br/>
<code>set exchange</code> <code>set NERDTree</code>
</details> </details>
### Instructions ### Instructions
https://github.com/tommcdo/vim-exchange/blob/master/doc/exchange.txt [See here](NERDTree-support.md).
</details> </details>
<details> <details>
<summary><h2>FunctionTextObj: Adds text objects for manipulating functions/methods</h2></summary> <summary><h2>surround</h2></summary>
By Julien Phalip Original plugin: [vim-surround](https://github.com/tpope/vim-surround).
### Summary:
An extension for IdeaVim that adds text objects for manipulating functions/methods in your code.
Similar to how iw operates on words or i" operates on quoted strings,
this plugin provides if and af to operate on functions
### Setup
Add `set functiontextobj` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
or restart the IDE.
### Instructions
https://plugins.jetbrains.com/plugin/25897-vim-functiontextobj
</details>
<details>
<summary><h2>highlightedyank: Highlights the yanked region</h2></summary>
By [KostkaBrukowa](https://github.com/KostkaBrukowa)
Original plugin: [vim-highlightedyank](https://github.com/machakann/vim-highlightedyank).
### Summary:
Make the yanked region apparent!
### Setup: ### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'machakann/vim-highlightedyank'` - Add the following command to `~/.ideavimrc`: `Plug 'tpope/vim-surround'`
<details> <details>
<summary>Alternative syntax</summary> <summary>Alternative syntax</summary>
<code>Plugin 'machakann/vim-highlightedyank'</code> <code>Plugin 'tpope/vim-surround'</code>
<br/> <br/>
<code>Plug 'https://github.com/machakann/vim-highlightedyank'</code> <code>Plug 'https://www.vim.org/scripts/script.php?script_id=1697'</code>
<br/> <br/>
<code>Plug 'vim-highlightedyank'</code> <code>Plug 'vim-surround'</code>
<br/> <br/>
<code>set highlightedyank</code> <code>set surround</code>
</details> </details>
### Instructions ### Instructions
If you want to optimize highlight duration, assign a time in milliseconds: https://github.com/tpope/vim-surround/blob/master/doc/surround.txt
`let g:highlightedyank_highlight_duration = "1000"`
A negative number makes the highlight persistent.
If you want to change background color of highlight you can provide the rgba of the color you want e.g.
`let g:highlightedyank_highlight_color = "rgba(160, 160, 160, 155)"`
If you want to change text color of highlight you can provide the rgba of the color you want e.g.
`let g:highlightedyank_highlight_foreground_color = "rgba(0, 0, 0, 255)"`
https://github.com/machakann/vim-highlightedyank/blob/master/doc/highlightedyank.txt
</details> </details>
<details> <details>
<summary><h2>indent-object: Adds text objects for manipulating sentences/paragraphs/etc...</h2></summary> <summary><h2>multiple-cursors</h2></summary>
By [Shrikant Sharat Kandula](https://github.com/sharat87)
Original plugin: [vim-indent-object](https://github.com/michaeljsmith/vim-indent-object).
### Summary:
Vim text objects provide a convenient way to select and operate on various types of objects.
These objects include regions surrounded by various types of brackets and various parts of language
(ie sentences, paragraphs, etc).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'michaeljsmith/vim-indent-object'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'michaeljsmith/vim-indent-object'</code>
<br/>
<code>Plug 'https://github.com/michaeljsmith/vim-indent-object'</code>
<br/>
<code>Plug 'vim-indent-object'</code>
<br/>
<code>set textobj-indent</code>
</details>
### Instructions
https://github.com/michaeljsmith/vim-indent-object/blob/master/doc/indent-object.txt
</details>
<details>
<summary><h2>matchit.vim: Extends the % key functionality</h2></summary>
By [Martin Yzeiri](https://github.com/myzeiri)
Original plugin: [matchit.vim](https://github.com/chrisbra/matchit).
### Summary:
In Vim, as in plain vi, the percent key, |%|, jumps the cursor from a brace, bracket, or paren to its match.
This can be configured with the 'matchpairs' option.
The matchit plugin extends this in several ways...
### Setup:
- Add the following command to `~/.ideavimrc`: `packadd matchit`
<details>
<summary>Alternative syntax</summary>
<code>Plug 'vim-matchit'</code>
<br/>
<code>Plug 'chrisbra/matchit'</code>
<br/>
<code>set matchit</code>
</details>
### Instructions
https://github.com/adelarsq/vim-matchit/blob/master/doc/matchit.txt
</details>
<details>
<summary><h2>Mini.ai: Extend and create a/i textobjects (IMPORTANT: The plugin is not related with artificial intelligence)</h2></summary>
### Summary:
Extend and create a/i textobjects
### Features:
Provides additional text object motions for handling quotes and brackets. The following motions are included:
- aq: Around any quotes.
- iq: Inside any quotes.
- ab: Around any parentheses, curly braces, and square brackets.
- ib: Inside any parentheses, curly braces, and square brackets.
Original plugin: [mini.ai](https://github.com/echasnovski/mini.ai).
### Setup:
- Add the following command to `~/.ideavimrc`: `set mini-ai`
</details>
<details>
<summary><h2>multiple-cursors: Extends multicursor support</h2></summary>
Original plugin: [vim-multiple-cursors](https://github.com/terryma/vim-multiple-cursors). Original plugin: [vim-multiple-cursors](https://github.com/terryma/vim-multiple-cursors).
### Setup: ### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'terryma/vim-multiple-cursors'` - Add the following command to `~/.ideavimrc`: `Plug 'terryma/vim-multiple-cursors'`
<details> <details>
@@ -308,7 +127,7 @@ Original plugin: [vim-multiple-cursors](https://github.com/terryma/vim-multiple-
<br/> <br/>
<code>set multiple-cursors</code> <code>set multiple-cursors</code>
</details> </details>
### Instructions ### Instructions
At the moment, the default key binds for this plugin do not get mapped correctly in IdeaVim (see [VIM-2178](https://youtrack.jetbrains.com/issue/VIM-2178)). To enable the default key binds, add the following to your `.ideavimrc` file... At the moment, the default key binds for this plugin do not get mapped correctly in IdeaVim (see [VIM-2178](https://youtrack.jetbrains.com/issue/VIM-2178)). To enable the default key binds, add the following to your `.ideavimrc` file...
@@ -334,118 +153,38 @@ xmap <leader>g<C-n> <Plug>AllOccurrences
</details> </details>
<details> <details>
<summary><h2>NERDTree: Adds NERDTree navigation to the project panel</h2></summary> <summary><h2>commentary</h2></summary>
Original plugin: [NERDTree](https://github.com/preservim/nerdtree).
### Summary: By [Daniel Leong](https://github.com/dhleong)
Adds NERDTree navigation to the project panel. Original plugin: [commentary.vim](https://github.com/tpope/vim-commentary).
### Setup: ### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'preservim/nerdtree'` - Add the following command to `~/.ideavimrc`: `Plug 'tpope/vim-commentary'`
<details> <details>
<summary>Alternative syntax</summary> <summary>Alternative syntax</summary>
<code>Plugin 'preservim/nerdtree'</code> <code>Plugin 'tpope/vim-commentary'</code>
<br/> <br/>
<code>Plug 'https://github.com/preservim/nerdtree'</code> <code>Plug 'https://github.com/tpope/vim-commentary'</code>
<br/> <br/>
<code>Plug 'nerdtree'</code> <code>Plug 'vim-commentary'</code>
<br/> <br/>
<code>set NERDTree</code> <code>Plug 'tcomment_vim'</code>
<br/>
<code>set commentary</code>
</details> </details>
### Instructions ### Instructions
[See here](NERDTree-support.md). https://github.com/tpope/vim-commentary/blob/master/doc/commentary.txt
</details> </details>
<details> <details>
<summary><h2>paragraph-motion: Extends the { and } motions to ignore whitespace on otherwise empty lines</h2></summary> <summary><h2>ReplaceWithRegister</h2></summary>
Original plugin: [vim-paragraph-motion](https://github.com/dbakker/vim-paragraph-motion).
### Summary:
Normally the { and } motions only match completely empty lines.
With this plugin lines that only contain whitespace are also matched.
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'dbakker/vim-paragraph-motion'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'dbakker/vim-paragraph-motion'</code>
<br/>
<code>Plug 'https://github.com/dbakker/vim-paragraph-motion'</code>
<br/>
<code>Plug 'vim-paragraph-motion'</code>
<br/>
<code>Plug 'https://github.com/vim-scripts/Improved-paragraph-motion'</code>
<br/>
<code>Plug 'vim-scripts/Improved-paragraph-motion'</code>
<br/>
<code>Plug 'Improved-paragraph-motion'</code>
<br/>
<code>set vim-paragraph-motion</code>
</details>
### Instructions
https://github.com/dbakker/vim-paragraph-motion#vim-paragraph-motion
</details>
<details>
<summary><h2>Peekaboo: Extends " @ CTRL-r to show a popup of the register contents</h2></summary>
By Julien Phalip
Original plugin: [vim-peekaboo](https://github.com/junegunn/vim-peekaboo).
### Summary:
Peekaboo extends " and @ in normal mode and <CTRL-R> in insert mode so you can see the contents of the registers.
### Setup
Add `set peekaboo` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
or restart the IDE.
### Instructions
https://plugins.jetbrains.com/plugin/25776-vim-peekaboo
</details>
<details>
<summary><h2>quick-scope: Always-on highlight for a unique character in every word on a line to help use f, F, etc.</h2></summary>
Original plugin: [quick-scope](https://github.com/unblevable/quick-scope).
### Summary:
An always-on highlight for a unique character in every word on a line to help you use f, F and family.
This plugin should help you get to any word on a line in two or three keystrokes with Vim's built-in f<char>
(which moves your cursor to <char>).
### Setup:
- Install [IdeaVim-Quickscope](https://plugins.jetbrains.com/plugin/19417-ideavim-quickscope) plugin.
- Add the following command to `~/.ideavimrc`: `set quickscope`
### Instructions
https://plugins.jetbrains.com/plugin/19417-ideavim-quickscope
</details>
<details>
<summary><h2>ReplaceWithRegister: Adds two-in-one command that replaces text with the contents of a register.</h2></summary>
By [igrekster](https://github.com/igrekster) By [igrekster](https://github.com/igrekster)
Original plugin: [ReplaceWithRegister](https://github.com/vim-scripts/ReplaceWithRegister). Original plugin: [ReplaceWithRegister](https://github.com/vim-scripts/ReplaceWithRegister).
### Summary:
This plugin offers a two-in-one command that replaces text covered by a
{motion}, entire line(s) or the current selection with the contents of a
register; the old text is deleted into the black-hole register, i.e. it's
gone. (But of course, the command can be easily undone.)
### Setup: ### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'vim-scripts/ReplaceWithRegister'` - Add the following command to `~/.ideavimrc`: `Plug 'vim-scripts/ReplaceWithRegister'`
<details> <details>
@@ -464,99 +203,78 @@ gone. (But of course, the command can be easily undone.)
<br/> <br/>
<code>set ReplaceWithRegister</code> <code>set ReplaceWithRegister</code>
</details> </details>
### Instructions ### Instructions
https://github.com/vim-scripts/ReplaceWithRegister/blob/master/doc/ReplaceWithRegister.txt https://github.com/vim-scripts/ReplaceWithRegister/blob/master/doc/ReplaceWithRegister.txt
</details> </details>
<details> <details>
<summary><h2>sneak: Jump to any location specified by two characters</h2></summary> <summary><h2>argtextobj</h2></summary>
<img src="images/sneakIcon.svg" width="80" height="80" alt="icon"/> Original plugin: [argtextobj.vim](https://www.vim.org/scripts/script.php?script_id=2699).
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).
### Summary:
Jump to any location specified by two characters.
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'justinmk/vim-sneak'`
### Instructions
* Type `s` and two chars to start sneaking in forward direction
* 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>
<summary><h2>surround: Adds provides mappings to easily delete, change, and add surroundings in pairs</h2></summary>
Original plugin: [vim-surround](https://github.com/tpope/vim-surround).
### Summary:
Surround.vim is all about "surroundings": parentheses, brackets, quotes, XML tags, and more.
The plugin provides mappings to easily delete, change and add such surroundings in pairs.
### Setup: ### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'tpope/vim-surround'` - Add the following command to `~/.ideavimrc`: `Plug 'vim-scripts/argtextobj.vim'`
<details> <details>
<summary>Alternative syntax</summary> <summary>Alternative syntax</summary>
<code>Plugin 'tpope/vim-surround'</code> <code>Plugin 'vim-scripts/argtextobj.vim'</code>
<br/> <br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=1697'</code> <code>Plug 'https://github.com/vim-scripts/argtextobj.vim'</code>
<br/> <br/>
<code>Plug 'vim-surround'</code> <code>Plug 'argtextobj.vim'</code>
<br/> <br/>
<code>set surround</code> <code>Plug 'https://www.vim.org/scripts/script.php?script_id=2699'</code>
<br/>
<code>set argtextobj</code>
</details> </details>
### Instructions ### Instructions
https://github.com/tpope/vim-surround/blob/master/doc/surround.txt By default, only the arguments inside parenthesis are considered. To extend the functionality
to other types of brackets, set `g:argtextobj_pairs` variable to a comma-separated
list of colon-separated pairs (same as VIM's `matchpairs` option), like
`let g:argtextobj_pairs="(:),{:},<:>"`. The order of pairs matters when
handling symbols that can also be operators: `func(x << 5, 20) >> 17`. To handle
this syntax parenthesis, must come before angle brackets in the list.
https://www.vim.org/scripts/script.php?script_id=2699
</details> </details>
<details> <details>
<summary><h2>Switch: Switch some text under the cursor based on regex patterns</h2></summary> <summary><h2>exchange</h2></summary>
By Julien Phalip
Original plugin: [switch.vim](https://github.com/AndrewRadev/switch.vim).
### Summary:
The purpose of the plugin is to switch some text under the cursor based on regex patterns.
The main entry point is a single command, :Switch.
When the command is executed,
the plugin looks for one of a few specific patterns under the cursor and performs a substitution depending on it.
### Setup
Add `set switch` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
or restart the IDE.
By [fan-tom](https://github.com/fan-tom)
Original plugin: [vim-exchange](https://github.com/tommcdo/vim-exchange).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'tommcdo/vim-exchange'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'tommcdo/vim-exchange'</code>
<br/>
<code>Plug 'https://github.com/tommcdo/vim-exchange'</code>
<br/>
<code>Plug 'vim-exchange'</code>
<br/>
<code>set exchange</code>
</details>
### Instructions ### Instructions
https://plugins.jetbrains.com/plugin/25899-vim-switch https://github.com/tommcdo/vim-exchange/blob/master/doc/exchange.txt
</details> </details>
<details> <details>
<summary><h2>textobj-entire: Adds mapping for selecting entire contents of file regardless of cursor position</h2></summary> <summary><h2>textobj-entire</h2></summary>
By [Alexandre Grison](https://github.com/agrison) By [Alexandre Grison](https://github.com/agrison)
Original plugin: [vim-textobj-entire](https://github.com/kana/vim-textobj-entire). Original plugin: [vim-textobj-entire](https://github.com/kana/vim-textobj-entire).
### Summary:
vim-textobj-entire is a Vim plugin to provide text objects
(ae and ie by default) to select the entire content of a buffer.
Though these are trivial operations (e.g. ggVG), text object versions are more handy,
because you do not have to be conscious of the cursor position (e.g. vae).
### Setup: ### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'kana/vim-textobj-entire'` - Add the following command to `~/.ideavimrc`: `Plug 'kana/vim-textobj-entire'`
<details> <details>
@@ -577,13 +295,158 @@ https://github.com/kana/vim-textobj-entire/blob/master/doc/textobj-entire.txt
</details> </details>
<details> <details>
<summary><h2>Which-Key: Displays available keybindings in popup</h2></summary> <summary><h2>highlightedyank</h2></summary>
By [KostkaBrukowa](https://github.com/KostkaBrukowa)
Original plugin: [vim-highlightedyank](https://github.com/machakann/vim-highlightedyank).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'machakann/vim-highlightedyank'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'machakann/vim-highlightedyank'</code>
<br/>
<code>Plug 'https://github.com/machakann/vim-highlightedyank'</code>
<br/>
<code>Plug 'vim-highlightedyank'</code>
<br/>
<code>set highlightedyank</code>
</details>
### Instructions
If you want to optimize highlight duration, assign a time in milliseconds:
`let g:highlightedyank_highlight_duration = "1000"`
A negative number makes the highlight persistent.
If you want to change background color of highlight you can provide the rgba of the color you want e.g.
`let g:highlightedyank_highlight_color = "rgba(160, 160, 160, 155)"`
If you want to change text color of highlight you can provide the rgba of the color you want e.g.
`let g:highlightedyank_highlight_foreground_color = "rgba(0, 0, 0, 255)"`
https://github.com/machakann/vim-highlightedyank/blob/master/doc/highlightedyank.txt
</details>
<details>
<summary><h2>vim-paragraph-motion</h2></summary>
Original plugin: [vim-paragraph-motion](https://github.com/dbakker/vim-paragraph-motion).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'dbakker/vim-paragraph-motion'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'dbakker/vim-paragraph-motion'</code>
<br/>
<code>Plug 'https://github.com/dbakker/vim-paragraph-motion'</code>
<br/>
<code>Plug 'vim-paragraph-motion'</code>
<br/>
<code>Plug 'https://github.com/vim-scripts/Improved-paragraph-motion'</code>
<br/>
<code>Plug 'vim-scripts/Improved-paragraph-motion'</code>
<br/>
<code>Plug 'Improved-paragraph-motion'</code>
<br/>
<code>set vim-paragraph-motion</code>
</details>
### Instructions
https://github.com/dbakker/vim-paragraph-motion#vim-paragraph-motion
</details>
<details>
<summary><h2>vim-indent-object</h2></summary>
By [Shrikant Sharat Kandula](https://github.com/sharat87)
Original plugin: [vim-indent-object](https://github.com/michaeljsmith/vim-indent-object).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'michaeljsmith/vim-indent-object'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'michaeljsmith/vim-indent-object'</code>
<br/>
<code>Plug 'https://github.com/michaeljsmith/vim-indent-object'</code>
<br/>
<code>Plug 'vim-indent-object'</code>
<br/>
<code>set textobj-indent</code>
</details>
### Instructions
https://github.com/michaeljsmith/vim-indent-object/blob/master/doc/indent-object.txt
</details>
<details>
<summary><h2>matchit.vim</h2></summary>
By [Martin Yzeiri](https://github.com/myzeiri)
Original plugin: [matchit.vim](https://github.com/chrisbra/matchit).
### Setup:
- Add the following command to `~/.ideavimrc`: `packadd matchit`
<details>
<summary>Alternative syntax</summary>
<code>Plug 'vim-matchit'</code>
<br/>
<code>Plug 'chrisbra/matchit'</code>
<br/>
<code>set matchit</code>
</details>
### Instructions
https://github.com/adelarsq/vim-matchit/blob/master/doc/matchit.txt
</details>
<details>
<summary><h2>IdeaVim-Quickscope</h2></summary>
Original plugin: [quick-scope](https://github.com/unblevable/quick-scope).
### Setup:
- Install [IdeaVim-Quickscope](https://plugins.jetbrains.com/plugin/19417-ideavim-quickscope) plugin.
- Add the following command to `~/.ideavimrc`: `set quickscope`
### Instructions
https://plugins.jetbrains.com/plugin/19417-ideavim-quickscope
</details>
<details>
<summary><h2>Mini.ai: Extend and create a/i textobjects (IMPORTANT: The plugin is not related with artificial intelligence)</h2></summary>
### Features:
Provides additional text object motions for handling quotes and brackets. The following motions are included:
- aq: Around any quotes.
- iq: Inside any quotes.
- ab: Around any parentheses, curly braces, and square brackets.
- ib: Inside any parentheses, curly braces, and square brackets.
Original plugin: [mini.ai](https://github.com/echasnovski/mini.ai).
### Setup:
- Add the following command to `~/.ideavimrc`: `set mini-ai`
</details>
<details>
<summary><h2>Which-Key</h2></summary>
Original plugin: [vim-which-key](https://github.com/liuchengxu/vim-which-key). Original plugin: [vim-which-key](https://github.com/liuchengxu/vim-which-key).
### Summary:
vim-which-key is vim port of emacs-which-key that displays available keybindings in popup.
### Setup: ### Setup:
- Install [Which-Key](https://plugins.jetbrains.com/plugin/15976-which-key) plugin. - Install [Which-Key](https://plugins.jetbrains.com/plugin/15976-which-key) plugin.
- Add the following command to `~/.ideavimrc`: `set which-key` - Add the following command to `~/.ideavimrc`: `set which-key`
@@ -593,3 +456,49 @@ vim-which-key is vim port of emacs-which-key that displays available keybindings
https://github.com/TheBlob42/idea-which-key?tab=readme-ov-file#installation https://github.com/TheBlob42/idea-which-key?tab=readme-ov-file#installation
</details> </details>
<details>
<summary><h2>Vim Peekaboo</h2></summary>
By Julien Phalip
Original plugin: [vim-peekaboo](https://github.com/junegunn/vim-peekaboo).
### Setup
Add `set peekaboo` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
or restart the IDE.
### Instructions
https://plugins.jetbrains.com/plugin/25776-vim-peekaboo
</details>
<details>
<summary><h2>FunctionTextObj</h2></summary>
By Julien Phalip
### Setup
Add `set functiontextobj` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
or restart the IDE.
### Instructions
https://plugins.jetbrains.com/plugin/25897-vim-functiontextobj
</details>
<details>
<summary><h2>Switch</h2></summary>
By Julien Phalip
Original plugin: [switch.vim](https://github.com/AndrewRadev/switch.vim).
### Setup
Add `set switch` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
or restart the IDE.
### Instructions
https://plugins.jetbrains.com/plugin/25899-vim-switch

View File

@@ -16,11 +16,11 @@
# https://data.services.jetbrains.com/products?code=IC # https://data.services.jetbrains.com/products?code=IC
# Maven releases are here: https://www.jetbrains.com/intellij-repository/releases # Maven releases are here: https://www.jetbrains.com/intellij-repository/releases
# And snapshots: https://www.jetbrains.com/intellij-repository/snapshots # And snapshots: https://www.jetbrains.com/intellij-repository/snapshots
ideaVersion=2025.1 ideaVersion=2024.3
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type # Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
ideaType=IC ideaType=IC
instrumentPluginCode=true instrumentPluginCode=true
version=chylex-50 version=chylex-44
javaVersion=21 javaVersion=21
remoteRobotVersion=0.11.23 remoteRobotVersion=0.11.23
antlrVersion=4.10.1 antlrVersion=4.10.1
@@ -28,7 +28,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=2.2.0 kotlinVersion=2.0.21
publishToken=token publishToken=token
publishChannels=eap publishChannels=eap

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

3
gradlew vendored
View File

@@ -86,7 +86,8 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum

View File

@@ -20,25 +20,27 @@ repositories {
} }
dependencies { dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:2.2.0") compileOnly("org.jetbrains.kotlin:kotlin-stdlib:2.1.10")
implementation("io.ktor:ktor-client-core:3.2.2") implementation("io.ktor:ktor-client-core:3.1.1")
implementation("io.ktor:ktor-client-cio:3.2.2") implementation("io.ktor:ktor-client-cio:3.1.1")
implementation("io.ktor:ktor-client-content-negotiation:3.2.2") implementation("io.ktor:ktor-client-content-negotiation:3.1.1")
implementation("io.ktor:ktor-serialization-kotlinx-json:3.2.2") implementation("io.ktor:ktor-serialization-kotlinx-json:3.1.1")
implementation("io.ktor:ktor-client-auth:3.2.2") implementation("io.ktor:ktor-client-auth:3.1.1")
implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r") implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
// This is needed for jgit to connect to ssh // This is needed for jgit to connect to ssh
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.3.0.202506031305-r") implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.1.0.202411261347-r")
implementation("com.vdurmont:semver4j:3.1.0") implementation("com.vdurmont:semver4j:3.1.0")
} }
val releaseType: String? by project val releaseType: String? by project
kotlin { tasks {
compilerOptions { compileKotlin {
freeCompilerArgs = listOf("-Xjvm-default=all-compatibility") kotlinOptions {
freeCompilerArgs = listOf("-Xjvm-default=all-compatibility")
}
} }
} }

View File

@@ -43,10 +43,6 @@ val knownPlugins = setOf(
"com.julienphalip.ideavim.peekaboo", // https://plugins.jetbrains.com/plugin/25776-vim-peekaboo "com.julienphalip.ideavim.peekaboo", // https://plugins.jetbrains.com/plugin/25776-vim-peekaboo
"com.julienphalip.ideavim.switch", // https://plugins.jetbrains.com/plugin/25899-vim-switch "com.julienphalip.ideavim.switch", // https://plugins.jetbrains.com/plugin/25899-vim-switch
"com.julienphalip.ideavim.functiontextobj", // https://plugins.jetbrains.com/plugin/25897-vim-functiontextobj "com.julienphalip.ideavim.functiontextobj", // https://plugins.jetbrains.com/plugin/25897-vim-functiontextobj
"com.miksuki.HighlightCursor", // https://plugins.jetbrains.com/plugin/26743-highlightcursor
"com.ugarosa.idea.edgemotion", // https://plugins.jetbrains.com/plugin/27211-edgemotion
"cn.mumukehao.plugin",
) )
suspend fun main() { suspend fun main() {

View File

@@ -355,12 +355,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
if (unsubscribe) { if (unsubscribe) {
VimListenerManager.INSTANCE.turnOff(); VimListenerManager.INSTANCE.turnOff();
} }
// Use getServiceIfCreated to avoid creating the service during the dispose (this is prohibited by the platform) injector.getCommandLine().fullReset();
@Nullable VimCommandLineService service =
ApplicationManager.getApplication().getServiceIfCreated(VimCommandLineService.class);
if (service != null) {
service.fullReset();
}
// Unregister vim actions in command mode // Unregister vim actions in command mode
RegisterActions.unregisterActions(); RegisterActions.unregisterActions();
@@ -376,7 +371,8 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
if (isEnabled() && !ApplicationManager.getApplication().isUnitTestMode()) { if (isEnabled() && !ApplicationManager.getApplication().isUnitTestMode()) {
stateUpdated = true; stateUpdated = true;
if (SystemInfo.isMac) { if (SystemInfo.isMac) {
final Boolean enabled = MacKeyRepeat.INSTANCE.isEnabled(); final MacKeyRepeat keyRepeat = MacKeyRepeat.getInstance();
final Boolean enabled = keyRepeat.isEnabled();
final Boolean isKeyRepeat = getEditor().isKeyRepeat(); final Boolean isKeyRepeat = getEditor().isKeyRepeat();
if ((enabled == null || !enabled) && (isKeyRepeat == null || isKeyRepeat)) { if ((enabled == null || !enabled) && (isKeyRepeat == null || isKeyRepeat)) {
// This system property is used in IJ ui robot to hide the startup tips // This system property is used in IJ ui robot to hide the startup tips
@@ -386,7 +382,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
if (showNotification) { if (showNotification) {
if (VimPlugin.getNotifications().enableRepeatingMode() == Messages.YES) { if (VimPlugin.getNotifications().enableRepeatingMode() == Messages.YES) {
getEditor().setKeyRepeat(true); getEditor().setKeyRepeat(true);
MacKeyRepeat.INSTANCE.setEnabled(true); keyRepeat.setEnabled(true);
} }
else { else {
getEditor().setKeyRepeat(false); getEditor().setKeyRepeat(false);

View File

@@ -88,7 +88,7 @@ class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandle
LOG.info("VimTypedAction '$charTyped': $duration ms") LOG.info("VimTypedAction '$charTyped': $duration ms")
} }
} catch (e: ProcessCanceledException) { } catch (e: ProcessCanceledException) {
throw e // Nothing
} catch (e: Throwable) { } catch (e: Throwable) {
LOG.error(e) LOG.error(e)
} }

View File

@@ -0,0 +1,52 @@
package com.maddyhome.idea.vim.action
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.command.UndoConfirmationPolicy
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
import com.intellij.openapi.project.DumbAwareAction
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.Mode
class VimRunLastMacroInOpenFiles : DumbAwareAction() {
override fun update(e: AnActionEvent) {
val lastRegister = injector.macro.lastRegister
val isEnabled = lastRegister != 0.toChar()
e.presentation.isEnabled = isEnabled
e.presentation.text = if (isEnabled) "Run Macro '${lastRegister}' in Open Files" else "Run Last Macro in Open Files"
}
override fun getActionUpdateThread(): ActionUpdateThread {
return ActionUpdateThread.EDT
}
override fun actionPerformed(e: AnActionEvent) {
val project = e.project ?: return
val fileEditorManager = FileEditorManagerEx.getInstanceExIfCreated(project) ?: return
val editors = fileEditorManager.allEditors.filterIsInstance<TextEditor>()
WriteCommandAction.writeCommandAction(project)
.withName(e.presentation.text)
.withGlobalUndo()
.withUndoConfirmationPolicy(UndoConfirmationPolicy.REQUEST_CONFIRMATION)
.run<RuntimeException> {
val reg = injector.macro.lastRegister
for (editor in editors) {
fileEditorManager.openFile(editor.file, true)
val vimEditor = editor.editor.vim
vimEditor.mode = Mode.NORMAL()
KeyHandler.getInstance().reset(vimEditor)
injector.macro.playbackRegister(vimEditor, IjEditorExecutionContext(e.dataContext), reg, 1)
}
}
}
}

View File

@@ -59,8 +59,11 @@ import javax.swing.KeyStroke
* *
* *
* These keys are not passed to [com.maddyhome.idea.vim.VimTypedActionHandler] and should be handled by actions. * These keys are not passed to [com.maddyhome.idea.vim.VimTypedActionHandler] and should be handled by actions.
*
* This class is used in Which-Key plugin, so don't make it internal. Generally, we should provide a proper
* way to get ideavim keys for this plugin. See VIM-3085
*/ */
internal class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ { class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
init { init {
initInjector() initInjector()
@@ -89,10 +92,9 @@ internal class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatib
val duration = System.currentTimeMillis() - start val duration = System.currentTimeMillis() - start
LOG.info("VimShortcut execution '$keyStroke': $duration ms") LOG.info("VimShortcut execution '$keyStroke': $duration ms")
} }
} catch (e: ProcessCanceledException) { } catch (_: ProcessCanceledException) {
// Control-flow exceptions (like ProcessCanceledException) should never be logged and should be rethrown // Control-flow exceptions (like ProcessCanceledException) should never be logged
// See {@link com.intellij.openapi.diagnostic.Logger.checkException} // See {@link com.intellij.openapi.diagnostic.Logger.checkException}
throw e
} catch (throwable: Throwable) { } catch (throwable: Throwable) {
LOG.error(throwable) LOG.error(throwable)
} }
@@ -196,6 +198,10 @@ internal class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatib
VimPlugin.getChange().tabAction = true VimPlugin.getChange().tabAction = true
return ActionEnableStatus.no("Tab action in insert mode", LogLevel.INFO) return ActionEnableStatus.no("Tab action in insert mode", LogLevel.INFO)
} }
// Debug watch, Python console, etc.
if (keyStroke in NON_FILE_EDITOR_KEYS && !EditorHelper.isFileEditor(editor)) {
return ActionEnableStatus.no("Non file editor keys", LogLevel.INFO)
}
} }
if (keyStroke in VIM_ONLY_EDITOR_KEYS) { if (keyStroke in VIM_ONLY_EDITOR_KEYS) {
@@ -343,6 +349,14 @@ internal class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatib
private const val ACTION_ID = "VimShortcutKeyAction" private const val ACTION_ID = "VimShortcutKeyAction"
private val NON_FILE_EDITOR_KEYS: Set<KeyStroke> = ImmutableSet.builder<KeyStroke>()
.addAll(getKeyStrokes(KeyEvent.VK_ENTER, 0))
.addAll(getKeyStrokes(KeyEvent.VK_ESCAPE, 0))
.addAll(getKeyStrokes(KeyEvent.VK_TAB, 0))
.addAll(getKeyStrokes(KeyEvent.VK_UP, 0))
.addAll(getKeyStrokes(KeyEvent.VK_DOWN, 0))
.build()
private val LOG = logger<VimShortcutKeyAction>() private val LOG = logger<VimShortcutKeyAction>()
@JvmStatic @JvmStatic

View File

@@ -1,67 +0,0 @@
package com.maddyhome.idea.vim.action.macro
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.command.UndoConfirmationPolicy
import com.intellij.openapi.command.impl.FinishMarkAction
import com.intellij.openapi.command.impl.StartMarkAction
import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
import com.intellij.vim.annotations.CommandOrMotion
import com.intellij.vim.annotations.Mode
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim
@CommandOrMotion(keys = ["z@"], modes = [Mode.NORMAL])
class PlaybackRegisterInOpenFilesAction : VimActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
override val argumentType: Argument.Type = Argument.Type.CHARACTER
private val playbackRegisterAction = PlaybackRegisterAction()
override fun execute(
editor: VimEditor,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments,
): Boolean {
val argument = cmd.argument as? Argument.Character ?: return false
val project = editor.ij.project ?: return false
val fileEditorManager = FileEditorManagerEx.getInstanceExIfCreated(project) ?: return false
val register = argument.character.let { if (it == '@') injector.macro.lastRegister else it }
val commandName = "Execute Macro '$register' in All Open Files"
val action = Runnable {
CommandProcessor.getInstance().markCurrentCommandAsGlobal(project)
for (textEditor in fileEditorManager.allEditors.filterIsInstance<TextEditor>()) {
fileEditorManager.openFile(textEditor.file, true)
val editor = textEditor.editor
val vimEditor = editor.vim
vimEditor.mode = com.maddyhome.idea.vim.state.mode.Mode.NORMAL()
KeyHandler.Companion.getInstance().reset(vimEditor)
val startMarkAction = StartMarkAction.start(editor, project, commandName)
playbackRegisterAction.execute(vimEditor, context, cmd, operatorArguments)
FinishMarkAction.finish(project, editor, startMarkAction)
}
}
CommandProcessor.getInstance()
.executeCommand(project, action, commandName, null, UndoConfirmationPolicy.REQUEST_CONFIRMATION)
return true
}
}

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2003-2025 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
@@ -14,6 +14,10 @@ import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.state.VimStateMachine import com.maddyhome.idea.vim.state.VimStateMachine
import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.ApiStatus
/**
* COMPATIBILITY-LAYER: Additional class
* Please see: https://jb.gg/zo8n0r
*/
@Deprecated("Use `injector.vimState`") @Deprecated("Use `injector.vimState`")
@ApiStatus.ScheduledForRemoval @ApiStatus.ScheduledForRemoval
class CommandState(private val machine: VimStateMachine) { class CommandState(private val machine: VimStateMachine) {

View File

@@ -11,17 +11,16 @@ package com.maddyhome.idea.vim.customization.feature.terminal
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.key.IdeaVimDisablerExtensionPoint import com.maddyhome.idea.vim.key.IdeaVimDisablerExtensionPoint
import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isAlternateBufferEditor import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isAlternateBufferEditor
import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isAlternateBufferModelEditor
import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isOutputEditor import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isOutputEditor
import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isOutputModelEditor
import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isPromptEditor import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isPromptEditor
/** /**
* The only implementation is defined right here. * The only implementation is defined right here.
*/ */
// [VERSION UPDATE] 2025.1+ Add 2 new predicates
internal class IdeaVimTerminalDisablerExtension : IdeaVimDisablerExtensionPoint { internal class IdeaVimTerminalDisablerExtension : IdeaVimDisablerExtensionPoint {
override fun isDisabledForEditor(editor: Editor): Boolean { override fun isDisabledForEditor(editor: Editor): Boolean {
return editor.isPromptEditor || editor.isOutputEditor || editor.isAlternateBufferEditor return editor.isPromptEditor || editor.isOutputEditor || editor.isAlternateBufferEditor
|| editor.isOutputModelEditor || editor.isAlternateBufferModelEditor // || editor.isOutputModelEditor || editor.isAlternateBufferModelEditor
} }
} }

View File

@@ -146,7 +146,7 @@ object VimExtensionFacade {
fun executeNormalWithoutMapping(keys: List<KeyStroke>, editor: Editor) { fun executeNormalWithoutMapping(keys: List<KeyStroke>, editor: Editor) {
val context = injector.executionContextManager.getEditorExecutionContext(editor.vim) val context = injector.executionContextManager.getEditorExecutionContext(editor.vim)
val keyHandler = KeyHandler.getInstance() val keyHandler = KeyHandler.getInstance()
keys.forEach { keyHandler.handleKey(editor.vim, it, context, false, keyHandler.keyHandlerState) } keys.forEach { keyHandler.handleKey(editor.vim, it, context, false, false, keyHandler.keyHandlerState) }
} }
/** Returns a single key stroke from the user input similar to 'getchar()'. */ /** Returns a single key stroke from the user input similar to 'getchar()'. */
@@ -214,13 +214,7 @@ object VimExtensionFacade {
/** Set the current contents of the given register */ /** Set the current contents of the given register */
@JvmStatic @JvmStatic
fun setRegisterForCaret( fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) {
editor: VimEditor,
context: ExecutionContext,
register: Char,
caret: ImmutableVimCaret,
keys: List<KeyStroke?>?,
) {
caret.registerStorage.setKeys(register, keys?.filterNotNull() ?: emptyList()) caret.registerStorage.setKeys(register, keys?.filterNotNull() ?: emptyList())
} }

View File

@@ -221,16 +221,14 @@ internal class CommentaryExtension : VimExtension {
val endOffset = editor.vim.getLineEndOffset(logicalLine, true) val endOffset = editor.vim.getLineEndOffset(logicalLine, true)
val startElement = file.findElementAt(startOffset) ?: return false val startElement = file.findElementAt(startOffset) ?: return false
var next: PsiElement? = startElement var next: PsiElement? = startElement
var hasComment = false
while (next != null && next.textRange.startOffset <= endOffset) { while (next != null && next.textRange.startOffset <= endOffset) {
when { if (next !is PsiWhiteSpace && !isComment(next)) {
next is PsiWhiteSpace -> {} // Skip whitespace elementl return false
isComment(next) -> hasComment = true // Mark when we find a comment
else -> return false // Non-comment content found, exit early
} }
next = PsiTreeUtil.nextLeaf(next, true) next = PsiTreeUtil.nextLeaf(next, true)
} }
return hasComment
return true
} }
private fun isComment(element: PsiElement) = private fun isComment(element: PsiElement) =

View File

@@ -455,17 +455,6 @@ internal class NerdTree : VimExtension {
tree.scrollRowToVisible(expectedRow) tree.scrollRowToVisible(expectedRow)
}, },
) )
registerCommand("gg", NerdAction.Code { project, _, _ ->
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
tree.setSelectionRow(0)
tree.scrollRowToVisible(0)
})
registerCommand("G", NerdAction.Code { project, _, _ ->
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
val lastRowIndex = tree.rowCount -1
tree.setSelectionRow(lastRowIndex)
tree.scrollRowToVisible(lastRowIndex)
})
registerCommand( registerCommand(
"NERDTreeMapJumpNextSibling", "NERDTreeMapJumpNextSibling",
"<C-J>", "<C-J>",

View File

@@ -11,7 +11,6 @@ import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
@@ -142,10 +141,6 @@ internal class VimSurroundExtension : VimExtension {
runWriteAction { runWriteAction {
// Leave visual mode // Leave visual mode
editor.exitVisualMode() editor.exitVisualMode()
// Reset the key handler so that the command trie is updated for the new mode (Normal)
// TODO: This should probably be handled by ToHandlerMapping.execute
KeyHandler.getInstance().reset(editor)
} }
} }
} }
@@ -174,8 +169,8 @@ internal class VimSurroundExtension : VimExtension {
val surroundings = editor.sortedCarets() val surroundings = editor.sortedCarets()
.map { .map {
val oldValue: List<KeyStroke>? = getRegisterForCaret(editor, context, REGISTER, it) val oldValue: List<KeyStroke>? = getRegisterForCaret(editor, context, REGISTER, it)
setRegisterForCaret(editor, context, REGISTER, it, null) setRegisterForCaret(REGISTER, it, null)
SurroundingInfo(editor, context, it, null, oldValue, false) SurroundingInfo(it, null, oldValue, false)
} }
// Delete surrounding's content // Delete surrounding's content
@@ -264,16 +259,9 @@ internal class VimSurroundExtension : VimExtension {
} }
} }
private data class SurroundingInfo( private data class SurroundingInfo(val caret: VimCaret, var innerText: List<KeyStroke>?, val oldRegisterContent: List<KeyStroke>?, var isValidSurrounding: Boolean) {
val editor: VimEditor,
val context: ExecutionContext,
val caret: VimCaret,
var innerText: List<KeyStroke>?,
val oldRegisterContent: List<KeyStroke>?,
var isValidSurrounding: Boolean,
) {
fun restoreRegister() { fun restoreRegister() {
setRegisterForCaret(editor, context, REGISTER, caret, oldRegisterContent) setRegisterForCaret(REGISTER, caret, oldRegisterContent)
} }
} }

View File

@@ -7,7 +7,7 @@
*/ */
package com.maddyhome.idea.vim.group package com.maddyhome.idea.vim.group
import com.intellij.codeInsight.actions.AsyncActionExecutionService import com.intellij.codeInsight.actions.AsyncActionExecutionService.Companion.getInstance
import com.intellij.openapi.Disposable import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.IdeActions import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
@@ -15,7 +15,6 @@ import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.command.UndoConfirmationPolicy import com.intellij.openapi.command.UndoConfirmationPolicy
import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.actionSystem.TypedActionHandler
import com.intellij.openapi.editor.actions.EnterAction import com.intellij.openapi.editor.actions.EnterAction
import com.intellij.openapi.editor.event.EditorMouseEvent import com.intellij.openapi.editor.event.EditorMouseEvent
import com.intellij.openapi.editor.event.EditorMouseListener import com.intellij.openapi.editor.event.EditorMouseListener
@@ -34,7 +33,7 @@ import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
import com.maddyhome.idea.vim.handler.commandContinuation import com.maddyhome.idea.vim.handler.commandContinuation
import com.maddyhome.idea.vim.helper.inInsertMode import com.maddyhome.idea.vim.helper.inInsertMode
import com.maddyhome.idea.vim.key.KeyHandlerKeeper import com.maddyhome.idea.vim.key.KeyHandlerKeeper.Companion.getInstance
import com.maddyhome.idea.vim.listener.VimInsertListener import com.maddyhome.idea.vim.listener.VimInsertListener
import com.maddyhome.idea.vim.newapi.IjVimCaret import com.maddyhome.idea.vim.newapi.IjVimCaret
import com.maddyhome.idea.vim.newapi.IjVimCopiedText import com.maddyhome.idea.vim.newapi.IjVimCopiedText
@@ -62,21 +61,8 @@ class ChangeGroup : VimChangeGroupBase() {
} }
override fun type(vimEditor: VimEditor, context: ExecutionContext, key: Char) { override fun type(vimEditor: VimEditor, context: ExecutionContext, key: Char) {
doType(vimEditor, context) {
it.execute(vimEditor.ij, key, context.ij)
}
}
override fun type(vimEditor: VimEditor, context: ExecutionContext, string: String) {
doType(vimEditor, context) { handler ->
string.forEach { char ->
handler.execute(vimEditor.ij, char, context.ij)
}
}
}
private fun doType(vimEditor: VimEditor, context: ExecutionContext, action: (TypedActionHandler) -> Unit) {
val editor = (vimEditor as IjVimEditor).editor val editor = (vimEditor as IjVimEditor).editor
val ijContext = context.ij
val doc = vimEditor.editor.document val doc = vimEditor.editor.document
val undo = injector.undo val undo = injector.undo
@@ -89,9 +75,8 @@ class ChangeGroup : VimChangeGroupBase() {
} }
CommandProcessor.getInstance().executeCommand( CommandProcessor.getInstance().executeCommand(
editor.project, { editor.project, {
ApplicationManager.getApplication().runWriteAction { ApplicationManager.getApplication()
action(KeyHandlerKeeper.getInstance().originalHandler) .runWriteAction { getInstance().originalHandler.execute(editor, key, ijContext) }
}
}, "", doc, }, "", doc,
UndoConfirmationPolicy.DEFAULT, doc UndoConfirmationPolicy.DEFAULT, doc
) )
@@ -165,7 +150,7 @@ class ChangeGroup : VimChangeGroupBase() {
var copiedText: IjVimCopiedText? = null var copiedText: IjVimCopiedText? = null
try { try {
if (injector.registerGroup.isPrimaryRegisterSupported()) { if (injector.registerGroup.isPrimaryRegisterSupported()) {
copiedText = injector.clipboardManager.getPrimaryContent() as IjVimCopiedText copiedText = injector.clipboardManager.getPrimaryContent(editor, context) as IjVimCopiedText
} }
} catch (e: Exception) { } catch (e: Exception) {
// FIXME: [isPrimaryRegisterSupported()] is not implemented perfectly, so there might be thrown an exception after trying to access the primary selection // FIXME: [isPrimaryRegisterSupported()] is not implemented perfectly, so there might be thrown an exception after trying to access the primary selection
@@ -184,7 +169,7 @@ class ChangeGroup : VimChangeGroupBase() {
restoreCursor(editor, caret, (caret as IjVimCaret).caret.logicalPosition.line) restoreCursor(editor, caret, (caret as IjVimCaret).caret.logicalPosition.line)
} }
if (project != null) { if (project != null) {
AsyncActionExecutionService.getInstance(project) getInstance(project)
.withExecutionAfterAction(IdeActions.ACTION_EDITOR_AUTO_INDENT_LINES, actionExecution, afterAction) .withExecutionAfterAction(IdeActions.ACTION_EDITOR_AUTO_INDENT_LINES, actionExecution, afterAction)
} else { } else {
actionExecution.invoke() actionExecution.invoke()

View File

@@ -31,8 +31,10 @@ open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(sco
// Temporary options to control work-in-progress behaviour // Temporary options to control work-in-progress behaviour
var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks) var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
var oldundo: Boolean by optionProperty(IjOptions.oldundo) var oldundo: Boolean by optionProperty(IjOptions.oldundo)
var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps) var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
} }
/** /**

View File

@@ -141,8 +141,12 @@ object IjOptions {
// Temporary feature flags during development, not really intended for external use // Temporary feature flags during development, not really intended for external use
val closenotebooks: ToggleOption = val closenotebooks: ToggleOption =
addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true)) addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true))
val commandOrMotionAnnotation: ToggleOption =
addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true))
val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isHidden = true)) val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isHidden = true))
val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true)) val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true))
val vimscriptFunctionAnnotation: ToggleOption =
addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isHidden = true))
// This needs to be Option<out VimDataType> so that it can work with derived option types, such as NumberOption, which // This needs to be Option<out VimDataType> so that it can work with derived option types, such as NumberOption, which
// derives from Option<VimInt> // derives from Option<VimInt>

View File

@@ -58,7 +58,7 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
public void registerRequiredShortcutKeys(@NotNull VimEditor editor) { public void registerRequiredShortcutKeys(@NotNull VimEditor editor) {
EventFacade.getInstance() EventFacade.getInstance()
.registerCustomShortcutSet(VimShortcutKeyAction.getInstance(), toShortcutSet(getRequiredShortcutKeys()), .registerCustomShortcutSet(VimShortcutKeyAction.getInstance(), toShortcutSet(getRequiredShortcutKeys()),
((IjVimEditor)editor).getEditor().getContentComponent()); ((IjVimEditor)editor).getEditor().getComponent());
} }
public void registerShortcutsForLookup(@NotNull LookupImpl lookup) { public void registerShortcutsForLookup(@NotNull LookupImpl lookup) {
@@ -69,7 +69,7 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
void unregisterShortcutKeys(@NotNull VimEditor editor) { void unregisterShortcutKeys(@NotNull VimEditor editor) {
EventFacade.getInstance().unregisterCustomShortcutSet(VimShortcutKeyAction.getInstance(), EventFacade.getInstance().unregisterCustomShortcutSet(VimShortcutKeyAction.getInstance(),
((IjVimEditor)editor).getEditor().getContentComponent()); ((IjVimEditor)editor).getEditor().getComponent());
} }
@Override @Override

View File

@@ -89,9 +89,6 @@ internal class MotionGroup : VimMotionGroupBase() {
} }
override fun moveCaretToCurrentDisplayLineStart(editor: VimEditor, caret: ImmutableVimCaret): Motion { override fun moveCaretToCurrentDisplayLineStart(editor: VimEditor, caret: ImmutableVimCaret): Motion {
if (editor.ij.softWrapModel.isSoftWrappingEnabled) {
return AbsoluteOffset(caret.ij.visualLineStart)
}
val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line) val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
return moveCaretToColumn(editor, caret, col, false) return moveCaretToColumn(editor, caret, col, false)
} }
@@ -100,15 +97,6 @@ internal class MotionGroup : VimMotionGroupBase() {
editor: VimEditor, editor: VimEditor,
caret: ImmutableVimCaret, caret: ImmutableVimCaret,
): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int { ): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
if (editor.ij.softWrapModel.isSoftWrappingEnabled) {
val offset = caret.ij.visualLineStart
val line = editor.offsetToBufferPosition(offset).line
return if (offset == editor.getLineStartOffset(line)) {
editor.getLeadingCharacterOffset(line, 0)
} else {
offset
}
}
val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line) val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
val bufferLine = caret.getLine() val bufferLine = caret.getLine()
return editor.getLeadingCharacterOffset(bufferLine, col) return editor.getLeadingCharacterOffset(bufferLine, col)
@@ -119,9 +107,6 @@ internal class MotionGroup : VimMotionGroupBase() {
caret: ImmutableVimCaret, caret: ImmutableVimCaret,
allowEnd: Boolean, allowEnd: Boolean,
): Motion { ): Motion {
if (editor.ij.softWrapModel.isSoftWrappingEnabled) {
return AbsoluteOffset(caret.ij.visualLineEnd - 1)
}
val col = EditorHelper.getVisualColumnAtRightOfDisplay(editor.ij, caret.getVisualPosition().line) val col = EditorHelper.getVisualColumnAtRightOfDisplay(editor.ij, caret.getVisualPosition().line)
return moveCaretToColumn(editor, caret, col, allowEnd) return moveCaretToColumn(editor, caret, col, allowEnd)
} }

View File

@@ -12,6 +12,7 @@ import com.intellij.icons.AllIcons
import com.intellij.ide.BrowserUtil import com.intellij.ide.BrowserUtil
import com.intellij.ide.actions.OpenFileAction import com.intellij.ide.actions.OpenFileAction
import com.intellij.ide.actions.RevealFileAction import com.intellij.ide.actions.RevealFileAction
import com.intellij.notification.ActionCenter
import com.intellij.notification.Notification import com.intellij.notification.Notification
import com.intellij.notification.NotificationGroup import com.intellij.notification.NotificationGroup
import com.intellij.notification.NotificationGroupManager import com.intellij.notification.NotificationGroupManager
@@ -36,7 +37,6 @@ import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.handler.KeyMapIssue import com.maddyhome.idea.vim.handler.KeyMapIssue
import com.maddyhome.idea.vim.helper.MessageHelper import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.icons.VimIcons
import com.maddyhome.idea.vim.key.ShortcutOwner import com.maddyhome.idea.vim.key.ShortcutOwner
import com.maddyhome.idea.vim.key.ShortcutOwnerInfo import com.maddyhome.idea.vim.key.ShortcutOwnerInfo
import com.maddyhome.idea.vim.newapi.globalIjOptions import com.maddyhome.idea.vim.newapi.globalIjOptions
@@ -135,30 +135,8 @@ internal class NotificationService(private val project: Project?) {
).notify(project) ).notify(project)
} }
/** fun notifyActionId(id: String?, candidates: List<String>? = null) {
* Shows a notification that the user can reenable IdeaVim by clicking on the IdeaVim icon in the status bar. ActionIdNotifier.notifyActionId(id, project, candidates)
*/
fun showReenableNotification(project: Project) {
val notification = Notification(
IDEAVIM_NOTIFICATION_ID,
IDEAVIM_NOTIFICATION_TITLE,
"IdeaVim has been disabled. You can reenable it by clicking on the gray IdeaVim icon in the status bar.",
NotificationType.INFORMATION,
)
notification.icon = VimIcons.IDEAVIM_DISABLED
notification.addAction(object : DumbAwareAction("Reenable IdeaVim") {
override fun actionPerformed(e: AnActionEvent) {
VimPlugin.setEnabled(true)
notification.expire()
}
})
notification.notify(project)
}
fun notifyActionId(id: String?, candidates: List<String>? = null, intentionName: String?) {
ActionIdNotifier.notifyActionId(id, project, candidates, intentionName)
} }
fun notifyKeymapIssues(issues: ArrayList<KeyMapIssue>) { fun notifyKeymapIssues(issues: ArrayList<KeyMapIssue>) {
@@ -236,15 +214,12 @@ internal class NotificationService(private val project: Project?) {
object ActionIdNotifier { object ActionIdNotifier {
private var notification: Notification? = null private var notification: Notification? = null
fun notifyActionId(id: String?, project: Project?, candidates: List<String>? = null, intentionName: String? = null) { fun notifyActionId(id: String?, project: Project?, candidates: List<String>? = null) {
notification?.expire() notification?.expire()
val possibleIDs = candidates?.distinct()?.sorted() val possibleIDs = candidates?.distinct()?.sorted()
val content = when { val content = when {
id != null -> "Action ID: <code>$id</code><br><br>" id != null -> "Action ID: <code>$id</code><br><br>"
possibleIDs.isNullOrEmpty() && !intentionName.isNullOrEmpty() -> {
"Intention \"$intentionName\" does not have an action ID.<br><br>"
}
possibleIDs.isNullOrEmpty() -> "<i>Cannot detect action ID</i><br><br>" possibleIDs.isNullOrEmpty() -> "<i>Cannot detect action ID</i><br><br>"
possibleIDs.size == 1 -> "Possible action ID: <code>${possibleIDs[0]}</code><br><br>" possibleIDs.size == 1 -> "Possible action ID: <code>${possibleIDs[0]}</code><br><br>"
else -> { else -> {
@@ -254,7 +229,7 @@ internal class NotificationService(private val project: Project?) {
append("</ul></p>") append("</ul></p>")
} }
} }
} + "<small>See the Notifications tool window for previous IDs</small>" } + "<small>See the ${ActionCenter.getToolwindowName()} tool window for previous IDs</small>"
notification = notification =
Notification(IDEAVIM_NOTIFICATION_ID, IDEAVIM_NOTIFICATION_TITLE, content, NotificationType.INFORMATION).also { Notification(IDEAVIM_NOTIFICATION_ID, IDEAVIM_NOTIFICATION_TITLE, content, NotificationType.INFORMATION).also {

View File

@@ -44,7 +44,7 @@ internal class SystemMarks {
internal fun Project.createLineBookmark(editor: Editor, line: Int, mnemonic: Char): LineBookmark? { internal fun Project.createLineBookmark(editor: Editor, line: Int, mnemonic: Char): LineBookmark? {
val bookmarksManager = BookmarksManager.getInstance(this) ?: return null val bookmarksManager = BookmarksManager.getInstance(this) ?: return null
val lineBookmarkProvider = LineBookmarkProvider.Util.find(this) ?: return null val lineBookmarkProvider = LineBookmarkProvider.find(this) ?: return null
val bookmark = lineBookmarkProvider.createBookmark(editor, line) as LineBookmark? ?: return null val bookmark = lineBookmarkProvider.createBookmark(editor, line) as LineBookmark? ?: return null
val type = BookmarkType.get(mnemonic) val type = BookmarkType.get(mnemonic)
if (type == BookmarkType.DEFAULT) return null if (type == BookmarkType.DEFAULT) return null

View File

@@ -19,7 +19,6 @@ import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.RangeMarker import com.intellij.openapi.editor.RangeMarker
import com.intellij.openapi.editor.ex.EditorEx import com.intellij.openapi.editor.ex.EditorEx
import com.intellij.openapi.ide.CopyPasteManager import com.intellij.openapi.ide.CopyPasteManager
import com.intellij.util.PlatformUtils
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
@@ -207,9 +206,8 @@ internal class PutGroup : VimPutBase() {
startOffset: Int, startOffset: Int,
endOffset: Int, endOffset: Int,
): Int { ): Int {
// Temp fix for VIM-2808 for Rider and Clion. Should be removed after rider will fix it's issues // Temp fix for VIM-2808. Should be removed after rider will fix it's issues
// Disable for client due to VIM-3857 if (isRider() || isClionNova()) return endOffset
if (isRider() || isClionNova() || PlatformUtils.isJetBrainsClient()) return endOffset
val startLine = editor.offsetToBufferPosition(startOffset).line val startLine = editor.offsetToBufferPosition(startOffset).line
val endLine = editor.offsetToBufferPosition(endOffset - 1).line val endLine = editor.offsetToBufferPosition(endOffset - 1).line

View File

@@ -96,7 +96,7 @@ internal object IdeaSelectionControl {
} else { } else {
logger.debug("None of carets have selection. State before adjustment: ${editor.vim.mode}") logger.debug("None of carets have selection. State before adjustment: ${editor.vim.mode}")
if (editor.vim.inVisualMode) editor.vim.exitVisualMode() if (editor.vim.inVisualMode) editor.vim.exitVisualMode()
if (editor.vim.inSelectMode) editor.vim.exitSelectMode(false) if (editor.vim.inSelectMode) editor.exitSelectMode(false)
if (editor.vim.inNormalMode) { if (editor.vim.inNormalMode) {
activateMode(editor, chooseNonSelectionMode(editor)) activateMode(editor, chooseNonSelectionMode(editor))
@@ -134,7 +134,7 @@ internal object IdeaSelectionControl {
is Mode.VISUAL -> VimPlugin.getVisualMotion().enterVisualMode(editor.vim, mode.selectionType) is Mode.VISUAL -> VimPlugin.getVisualMotion().enterVisualMode(editor.vim, mode.selectionType)
is Mode.SELECT -> VimPlugin.getVisualMotion().enterSelectMode(editor.vim, mode.selectionType) is Mode.SELECT -> VimPlugin.getVisualMotion().enterSelectMode(editor.vim, mode.selectionType)
is Mode.INSERT -> VimPlugin.getChange() is Mode.INSERT -> VimPlugin.getChange()
.insertBeforeCaret(editor.vim, injector.executionContextManager.getEditorExecutionContext(editor.vim)) .insertBeforeCursor(editor.vim, injector.executionContextManager.getEditorExecutionContext(editor.vim))
is Mode.NORMAL -> Unit is Mode.NORMAL -> Unit
else -> error("Unexpected mode: $mode") else -> error("Unexpected mode: $mode")

View File

@@ -17,6 +17,7 @@ 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.ProjectActivity
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
@@ -27,8 +28,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jetbrains.annotations.NonNls
import java.util.concurrent.ConcurrentHashMap
// 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
@@ -68,7 +67,11 @@ internal class IdeaVimCorrectorKeymapChangedListener : KeymapManagerListener {
check(correctorRequester.tryEmit(Unit)) check(correctorRequester.tryEmit(Unit))
} }
override fun shortcutsChanged(keymap: Keymap, actionIds: @NonNls Collection<String>, fromSettings: Boolean) { override fun shortcutChanged(keymap: Keymap, actionId: String) {
check(correctorRequester.tryEmit(Unit))
}
override fun shortcutChanged(keymap: Keymap, actionId: String, fromSettings: Boolean) {
check(correctorRequester.tryEmit(Unit)) check(correctorRequester.tryEmit(Unit))
} }
} }

View File

@@ -28,7 +28,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jetbrains.annotations.NonNls
import javax.swing.KeyStroke import javax.swing.KeyStroke
// We use alarm with delay to avoid many notifications in case many events are fired at the same time // We use alarm with delay to avoid many notifications in case many events are fired at the same time
@@ -68,7 +67,11 @@ internal class IdeaVimKeymapChangedListener : KeymapManagerListener {
check(keyCheckRequests.tryEmit(Unit)) check(keyCheckRequests.tryEmit(Unit))
} }
override fun shortcutsChanged(keymap: Keymap, actionIds: @NonNls Collection<String>, fromSettings: Boolean) { override fun shortcutChanged(keymap: Keymap, actionId: String) {
check(keyCheckRequests.tryEmit(Unit))
}
override fun shortcutChanged(keymap: Keymap, actionId: String, fromSettings: Boolean) {
check(keyCheckRequests.tryEmit(Unit)) check(keyCheckRequests.tryEmit(Unit))
} }
} }

View File

@@ -60,7 +60,7 @@ internal fun Editor.updateCaretsVisualAttributes() {
* Used when Vim emulation is disabled * Used when Vim emulation is disabled
*/ */
internal fun Editor.removeCaretsVisualAttributes() { internal fun Editor.removeCaretsVisualAttributes() {
caretModel.allCarets.forEach { it.visualAttributes = CaretVisualAttributes.getDefault() } caretModel.allCarets.forEach { it.visualAttributes = CaretVisualAttributes.DEFAULT }
} }
internal fun Editor.hasBlockOrUnderscoreCaret() = isBlockCursorOverride() || internal fun Editor.hasBlockOrUnderscoreCaret() = isBlockCursorOverride() ||
@@ -95,8 +95,8 @@ private fun Editor.updatePrimaryCaretVisualAttributes() {
// Make sure the caret is visible as soon as it's set. It might be invisible while blinking // Make sure the caret is visible as soon as it's set. It might be invisible while blinking
// NOTE: At the moment, this causes project leak in tests // NOTE: At the moment, this causes project leak in tests
// IJPL-928 - this will be fixed in 2025.2 // IJPL-928 - this will be fixed in 2024.2
// [VERSION UPDATE] 2025.2 - remove if wrapping // [VERSION UPDATE] 2024.2 - remove if wrapping
if (!ApplicationManager.getApplication().isUnitTestMode) { if (!ApplicationManager.getApplication().isUnitTestMode) {
(this as? EditorEx)?.setCaretVisible(true) (this as? EditorEx)?.setCaretVisible(true)
} }

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.helper
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ex.util.EditorUtil
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.UserDataHolder
@Deprecated("Do not use context wrappers, use existing provided contexts. If no context available, use `injector.getExecutionContextManager().getEditorExecutionContext(editor)`")
internal class EditorDataContext @Deprecated("Please use `init` method") constructor(
private val editor: Editor,
private val editorContext: DataContext,
private val contextDelegate: DataContext? = null,
) : DataContext, UserDataHolder {
/**
* Returns the object corresponding to the specified data identifier. Some of the supported data identifiers are
* defined in the [PlatformDataKeys] class.
*
* @param dataId the data identifier for which the value is requested.
* @return the value, or null if no value is available in the current context for this identifier.
*/
override fun getData(dataId: String): Any? = when {
PlatformDataKeys.EDITOR.name == dataId -> editor
PlatformDataKeys.PROJECT.name == dataId -> editor.project
PlatformDataKeys.VIRTUAL_FILE.name == dataId -> EditorHelper.getVirtualFile(editor)
else -> editorContext.getData(dataId) ?: contextDelegate?.getData(dataId)
}
override fun <T : Any?> getUserData(key: Key<T>): T? {
return if (contextDelegate is UserDataHolder) {
contextDelegate.getUserData(key)
} else {
null
}
}
override fun <T : Any?> putUserData(key: Key<T>, value: T?) {
if (contextDelegate is UserDataHolder) {
contextDelegate.putUserData(key, value)
}
}
companion object {
@Suppress("DEPRECATION")
@JvmStatic
fun init(editor: Editor, contextDelegate: DataContext? = null): EditorDataContext {
val editorContext = EditorUtil.getEditorDataContext(editor)
return if (contextDelegate is EditorDataContext) {
if (editor === contextDelegate.editor) {
contextDelegate
} else {
EditorDataContext(editor, editorContext, contextDelegate.contextDelegate)
}
} else {
EditorDataContext(editor, editorContext, contextDelegate)
}
}
}
}

View File

@@ -8,7 +8,6 @@
package com.maddyhome.idea.vim.helper; package com.maddyhome.idea.vim.helper;
import com.intellij.injected.editor.VirtualFileWindow;
import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.editor.impl.EditorImpl; import com.intellij.openapi.editor.impl.EditorImpl;
@@ -16,7 +15,6 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileUtil;
import com.intellij.testFramework.LightVirtualFile; import com.intellij.testFramework.LightVirtualFile;
import com.maddyhome.idea.vim.api.EngineEditorHelperKt; import com.maddyhome.idea.vim.api.EngineEditorHelperKt;
import com.maddyhome.idea.vim.api.VimEditor; import com.maddyhome.idea.vim.api.VimEditor;
@@ -654,21 +652,7 @@ public class EditorHelper {
*/ */
public static boolean isFileEditor(@NotNull Editor editor) { public static boolean isFileEditor(@NotNull Editor editor) {
final VirtualFile virtualFile = getVirtualFile(editor); final VirtualFile virtualFile = getVirtualFile(editor);
if (virtualFile == null) return false; return virtualFile != null && !(virtualFile instanceof LightVirtualFile);
if (virtualFile instanceof LightVirtualFile) {
var hostVirtualFile = getHostFileFromInjectedFile(virtualFile);
if (hostVirtualFile == null) return false;
return !(hostVirtualFile instanceof LightVirtualFile);
}
return true;
}
private static @Nullable VirtualFile getHostFileFromInjectedFile(@NotNull VirtualFile virtualFile) {
final var vf = VirtualFileUtil.originalFileOrSelf(virtualFile);
if (vf instanceof VirtualFileWindow) {
return ((VirtualFileWindow)vf).getDelegate();
}
return null;
} }
/** /**

View File

@@ -14,7 +14,6 @@ import com.intellij.codeWithMe.ClientId
import com.intellij.openapi.editor.Caret import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.CaretState import com.intellij.openapi.editor.CaretState
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.EditorKind
import com.intellij.openapi.editor.ex.util.EditorUtil import com.intellij.openapi.editor.ex.util.EditorUtil
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
import com.intellij.util.ui.table.JBTableRowEditor import com.intellij.util.ui.table.JBTableRowEditor
@@ -43,33 +42,9 @@ internal val Editor.isIdeaVimDisabledHere: Boolean
return (ideaVimDisabledInDialog(ideaVimSupportValue) && isInDialog()) || return (ideaVimDisabledInDialog(ideaVimSupportValue) && isInDialog()) ||
!ClientId.isCurrentlyUnderLocalId || // CWM-927 !ClientId.isCurrentlyUnderLocalId || // CWM-927
(ideaVimDisabledForSingleLine(ideaVimSupportValue) && isSingleLine()) || (ideaVimDisabledForSingleLine(ideaVimSupportValue) && isSingleLine()) ||
IdeaVimDisablerExtensionPoint.isDisabledForEditor(this) || IdeaVimDisablerExtensionPoint.isDisabledForEditor(this)
isNotFileEditorExceptAllowed()
} }
/**
* Almost every non-file-based editor should not use Vim mode. These editors are debug watch, Python console, AI chats,
* and other fields that are smart.
*
* We may support IdeaVim in these editors, but this will require a focused work and a lot of testing.
*
* Here are issues when non-file editors were supported:
* AI Chat VIM-3786
* Debug evaluate console VIM-3929
*
* However, we still support IdeaVim in a commit window because it works fine there, and removing vim from this place will
* be quite a visible change for users.
* We detect the commit window by the name of the editor (Dummy.txt). If this causes issues, let's disable IdeaVim
* in the commit window as well.
*
* Also, we support IdeaVim in diff viewers.
*/
private fun Editor.isNotFileEditorExceptAllowed(): Boolean {
if (EditorHelper.getVirtualFile(this)?.name?.contains("Dummy.txt") == true) return false
if (EditorHelper.isDiffEditor(this)) return false
return !EditorHelper.isFileEditor(this)
}
private fun ideaVimDisabledInDialog(ideaVimSupportValue: StringListOptionValue): Boolean { private fun ideaVimDisabledInDialog(ideaVimSupportValue: StringListOptionValue): Boolean {
return !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialog) return !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialog)
&& !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialoglegacy) && !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialoglegacy)
@@ -96,19 +71,6 @@ internal fun Editor.isPrimaryEditor(): Boolean {
return fileEditorManager.allEditors.any { fileEditor -> this == EditorUtil.getEditorEx(fileEditor) } return fileEditorManager.allEditors.any { fileEditor -> this == EditorUtil.getEditorEx(fileEditor) }
} }
/**
* Checks if the editor should be treated like a terminal. I.e. switch to Insert mode automatically
*
* A "terminal" editor is an editor used for purposes other than mainstream editing, such as a terminal, console, log
* viewer, etc. In this instance, the editor is writable, the document is writable, but it's not backed by a real file
* and it's not the diff viewer. We also check that if it's an injected language fragment backed by a real file.
*/
internal fun Editor.isTerminalEditor(): Boolean {
return !isViewer
&& document.isWritable
&& this.editorKind == EditorKind.CONSOLE
}
// Optimized clone of com.intellij.ide.ui.laf.darcula.DarculaUIUtil.isTableCellEditor // Optimized clone of com.intellij.ide.ui.laf.darcula.DarculaUIUtil.isTableCellEditor
private fun isTableCellEditor(c: Component): Boolean { private fun isTableCellEditor(c: Component): Boolean {
return (java.lang.Boolean.TRUE == (c as JComponent).getClientProperty("JComboBox.isTableCellEditor")) || return (java.lang.Boolean.TRUE == (c as JComponent).getClientProperty("JComboBox.isTableCellEditor")) ||

View File

@@ -8,15 +8,19 @@
package com.maddyhome.idea.vim.helper package com.maddyhome.idea.vim.helper
import com.intellij.execution.actions.StopAction import com.intellij.openapi.actionSystem.ActionGroup
import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.ActionPlaces import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.EmptyAction import com.intellij.openapi.actionSystem.EmptyAction
import com.intellij.openapi.actionSystem.IdeActions import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.actionSystem.PlatformDataKeys import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.actionSystem.ex.ActionUtil import com.intellij.openapi.actionSystem.ex.ActionUtil
import com.intellij.openapi.actionSystem.ex.ActionUtil.performDumbAwareWithCallbacks
import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
import com.intellij.openapi.actionSystem.impl.Utils
import com.intellij.openapi.application.ex.ApplicationManagerEx import com.intellij.openapi.application.ex.ApplicationManagerEx
import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.command.UndoConfirmationPolicy import com.intellij.openapi.command.UndoConfirmationPolicy
@@ -24,7 +28,9 @@ import com.intellij.openapi.components.Service
import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.editor.actionSystem.DocCommandGroupId import com.intellij.openapi.editor.actionSystem.DocCommandGroupId
import com.intellij.openapi.progress.util.ProgressIndicatorUtils import com.intellij.openapi.progress.util.ProgressIndicatorUtils
import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.util.NlsContexts import com.intellij.openapi.util.NlsContexts
import com.intellij.openapi.util.registry.Registry
import com.maddyhome.idea.vim.RegisterActions import com.maddyhome.idea.vim.RegisterActions
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.NativeAction import com.maddyhome.idea.vim.api.NativeAction
@@ -34,9 +40,11 @@ import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
import com.maddyhome.idea.vim.newapi.IjNativeAction import com.maddyhome.idea.vim.newapi.IjNativeAction
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.runFromVimKey
import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.NonNls
import java.awt.Component import java.awt.Component
import javax.swing.JComponent import javax.swing.JComponent
import javax.swing.SwingUtilities
@Service @Service
internal class IjActionExecutor : VimActionExecutor { internal class IjActionExecutor : VimActionExecutor {
@@ -55,11 +63,8 @@ internal class IjActionExecutor : VimActionExecutor {
override val ACTION_EXPAND_REGION_RECURSIVELY: String override val ACTION_EXPAND_REGION_RECURSIVELY: String
get() = IdeActions.ACTION_EXPAND_REGION_RECURSIVELY get() = IdeActions.ACTION_EXPAND_REGION_RECURSIVELY
override val ACTION_EXPAND_COLLAPSE_TOGGLE: String override val ACTION_EXPAND_COLLAPSE_TOGGLE: String
get() = IdeActions.ACTION_EXPAND_COLLAPSE_TOGGLE_REGION // [VERSION UPDATE] 2024.3+ Replace raw "ExpandCollapseToggleAction" with IdeActions.ACTION_EXPAND_COLLAPSE_TOGGLE_REGION from the platform.
override val ACTION_UNDO: String get() = "ExpandCollapseToggleAction"
get() = IdeActions.ACTION_UNDO
override val ACTION_REDO: String
get() = IdeActions.ACTION_REDO
var isRunningActionFromVim: Boolean = false var isRunningActionFromVim: Boolean = false
@@ -71,28 +76,78 @@ internal class IjActionExecutor : VimActionExecutor {
} }
val ijAction = (action as IjNativeAction).action val ijAction = (action as IjNativeAction).action
try { if (Registry.`is`("ideavim.old.action.execution", true)) {
isRunningActionFromVim = true return manualActionExecution(context, ijAction)
// The context component should be editor. This is especially important when running the `:action` commands } else {
// because at the moment of execution, the focused component is Ex Field, not editor. try {
val contextComponent = editor?.ij?.contentComponent isRunningActionFromVim = true
val place = ijAction.choosePlace() // The context component should be editor. This is especially important when running the `:action` commands
val res = ActionManager.getInstance().tryToExecute(ijAction, null, contextComponent, place, true) // because at the moment of execution, the focused component is Ex Field, not editor.
res.waitFor(5_000) val contextComponent = editor?.ij?.contentComponent
return res.isDone val res = ActionManager.getInstance().tryToExecute(ijAction, null, contextComponent, "IdeaVim", true)
} finally { res.waitFor(5_000)
isRunningActionFromVim = false return res.isDone
} finally {
isRunningActionFromVim = false
}
} }
} }
// Note: We should find a proper place for the IdeaVim actions private fun manualActionExecution(
// Currently, we use "IdeaVim" except a few actions context: ExecutionContext,
private fun AnAction.choosePlace(): String { ijAction: AnAction,
// StopAction works fine if `StopAction.isPlaceGlobal` returns true ): Boolean {
// Or if there is a specific data stored in the context. This data, however, is stored /**
// only if the run window is in focus. * Data context that defines that some action was started from IdeaVim.
if (this is StopAction) return ActionPlaces.ACTION_SEARCH * You can call use [runFromVimKey] key to define if intellij action was started from IdeaVim
return "IdeaVim" */
val dataContext = SimpleDataContext.getSimpleContext(runFromVimKey, true, context.ij)
val actionId = ActionManager.getInstance().getId(ijAction)
@Suppress("removal", "DEPRECATION") val event = AnActionEvent(
null,
dataContext,
ActionPlaces.KEYBOARD_SHORTCUT,
ijAction.templatePresentation.clone(),
ActionManager.getInstance(),
0,
)
Utils.initUpdateSession(event)
// beforeActionPerformedUpdate should be called to update the action. It fixes some rider-specific problems
// because rider uses an async update method. See VIM-1819.
// This method executes inside lastUpdateAndCheckDumb
// Another related issue: VIM-2604
// This is a hack to fix the tests and fix VIM-3332
// We should get rid of it in VIM-3376
if (actionId == "RunClass" || actionId == IdeActions.ACTION_COMMENT_LINE || actionId == IdeActions.ACTION_COMMENT_BLOCK) {
@Suppress("removal", "OverrideOnly", "DEPRECATION")
ijAction.beforeActionPerformedUpdate(event)
if (!event.presentation.isEnabled) return false
} else {
if (!ActionUtil.lastUpdateAndCheckDumb(ijAction, event, false)) return false
}
if (ijAction is ActionGroup && !event.presentation.isPerformGroup) {
// Some ActionGroups should not be performed but shown as a popup
val popup = JBPopupFactory.getInstance()
.createActionGroupPopup(event.presentation.text, ijAction, dataContext, false, null, -1)
val component = dataContext.getData(PlatformDataKeys.CONTEXT_COMPONENT)
if (component != null) {
val window = SwingUtilities.getWindowAncestor(component)
if (window != null) {
popup.showInCenterOf(window)
}
return true
}
popup.showInFocusCenter()
return true
} else {
performDumbAwareWithCallbacks(ijAction, event) {
@Suppress("OverrideOnly")
ijAction.actionPerformed(event)
}
return true
}
} }
/** /**

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.helper;
import com.google.common.io.CharStreams;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* @author vlan
*/
public class MacKeyRepeat {
@VimNlsSafe public static final String FMT = "defaults %s -globalDomain ApplePressAndHoldEnabled";
@NotNull private static final MacKeyRepeat INSTANCE = new MacKeyRepeat();
@NonNls private static final String EXEC_COMMAND = "launchctl stop com.apple.SystemUIServer.agent";
@NonNls private static final String delete = "delete";
@NonNls private static final String write = "write";
@NonNls private static final String read = "read";
public static @NotNull MacKeyRepeat getInstance() {
return INSTANCE;
}
private static @NotNull String read(@NotNull InputStream stream) throws IOException {
return CharStreams.toString(new InputStreamReader(stream));
}
public @Nullable Boolean isEnabled() {
final String command = String.format(FMT, read);
try {
final Process process = Runtime.getRuntime().exec(command);
final String data = read(process.getInputStream()).trim();
try {
return Integer.parseInt(data) == 0;
}
catch (NumberFormatException e) {
return null;
}
}
catch (IOException e) {
return null;
}
}
public void setEnabled(@Nullable Boolean value) {
final String command;
if (value == null) {
command = String.format(FMT, delete);
}
else {
final String arg = value ? "0" : "1";
command = String.format(FMT, write) + " " + arg;
}
try {
final Runtime runtime = Runtime.getRuntime();
final Process defaults = runtime.exec(command);
defaults.waitFor();
final Process restartSystemUI = runtime.exec(EXEC_COMMAND);
restartSystemUI.waitFor();
}
catch (IOException | InterruptedException ignored) {
}
}
}

View File

@@ -1,54 +0,0 @@
/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.helper
import com.google.common.io.CharStreams
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
object MacKeyRepeat {
var isEnabled: Boolean?
get() {
return try {
val process = Runtime.getRuntime().exec(READ_COMMAND)
val data = read(process.inputStream).trim().toIntOrNull() ?: return null
data == 0
} catch (_: IOException) {
null
}
}
set(value) {
val command: Array<String>
if (value == null) {
command = DELETE_COMMAND
} else {
val arg = if (value) "0" else "1"
command = WRITE_COMMAND + arg
}
try {
val runtime = Runtime.getRuntime()
val defaults = runtime.exec(command)
defaults.waitFor()
val restartSystemUI: Process = runtime.exec(EXEC_COMMAND)
restartSystemUI.waitFor()
} catch (_: IOException) {
} catch (_: InterruptedException) {
}
}
private val EXEC_COMMAND = arrayOf("launchctl", "stop", "com.apple.SystemUIServer.agent")
private val READ_COMMAND = arrayOf("defaults", "read", "-globalDomain", "ApplePressAndHoldEnabled")
private val WRITE_COMMAND = arrayOf("defaults", "write", "-globalDomain", "ApplePressAndHoldEnabled")
private val DELETE_COMMAND = arrayOf("defaults", "delete", "-globalDomain", "ApplePressAndHoldEnabled")
@Throws(IOException::class)
private fun read(stream: InputStream): String {
return CharStreams.toString(InputStreamReader(stream))
}
}

View File

@@ -24,6 +24,28 @@ 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.inSelectMode import com.maddyhome.idea.vim.state.mode.inSelectMode
/** [adjustCaretPosition] - if true, caret will be moved one char left if it's on the line end */
internal fun Editor.exitSelectMode(adjustCaretPosition: Boolean) {
val vimEditor = this.vim
if (!vimEditor.inSelectMode) return
vimEditor.mode = vimEditor.mode.returnTo
SelectionVimListenerSuppressor.lock().use {
this.caretModel.allCarets.forEach {
// NOTE: I think it should be write action, but the exception shows only an absence of the read action
injector.application.runReadAction { it.removeSelection() }
it.vim.vimSelectionStartClear()
if (adjustCaretPosition && !vimEditor.isEndAllowed) {
val lineEnd = IjVimEditor(this).getLineEndForOffset(it.offset)
val lineStart = IjVimEditor(this).getLineStartForOffset(it.offset)
if (it.offset == lineEnd && it.offset != lineStart) {
it.moveToInlayAwareOffset(it.offset - 1)
}
}
}
}
}
/** [adjustCaretPosition] - if true, caret will be moved one char left if it's on the line end */ /** [adjustCaretPosition] - if true, caret will be moved one char left if it's on the line end */
internal fun VimEditor.exitSelectMode(adjustCaretPosition: Boolean) { internal fun VimEditor.exitSelectMode(adjustCaretPosition: Boolean) {
if (!this.inSelectMode) return if (!this.inSelectMode) return

View File

@@ -42,7 +42,7 @@ public class PsiHelper {
if (file == null) { if (file == null) {
return -1; return -1;
} }
StructureViewBuilder structureViewBuilder = LanguageStructureViewBuilder.getInstance().getStructureViewBuilder(file); StructureViewBuilder structureViewBuilder = LanguageStructureViewBuilder.INSTANCE.getStructureViewBuilder(file);
if (!(structureViewBuilder instanceof TreeBasedStructureViewBuilder builder)) return -1; if (!(structureViewBuilder instanceof TreeBasedStructureViewBuilder builder)) return -1;
StructureViewModel model = builder.createStructureViewModel(editor); StructureViewModel model = builder.createStructureViewModel(editor);

View File

@@ -19,7 +19,6 @@ import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.fileEditor.TextEditorWithPreview import com.intellij.openapi.fileEditor.TextEditorWithPreview
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.util.registry.Registry
import com.intellij.util.PlatformUtils
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
@@ -44,12 +43,6 @@ internal class UndoRedoHelper : VimTimestampBasedUndoService {
} }
override fun undo(editor: VimEditor, context: ExecutionContext): Boolean { override fun undo(editor: VimEditor, context: ExecutionContext): Boolean {
if (PlatformUtils.isJetBrainsClient()) {
// Note: Remote Dev has special hacks for undo/redo, so we don't use the manager.
// The action is sent directly to the backend using the internal API.
return injector.actionExecutor.executeAction(editor, injector.actionExecutor.ACTION_UNDO, context)
}
val ijContext = context.context as DataContext val ijContext = context.context as DataContext
val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false
val textEditor = getTextEditor(editor.ij) val textEditor = getTextEditor(editor.ij)
@@ -113,10 +106,6 @@ internal class UndoRedoHelper : VimTimestampBasedUndoService {
} }
override fun redo(editor: VimEditor, context: ExecutionContext): Boolean { override fun redo(editor: VimEditor, context: ExecutionContext): Boolean {
if (PlatformUtils.isJetBrainsClient()) {
return injector.actionExecutor.executeAction(editor, injector.actionExecutor.ACTION_REDO, context)
}
val ijContext = context.context as DataContext val ijContext = context.context as DataContext
val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false
val textEditor = getTextEditor(editor.ij) val textEditor = getTextEditor(editor.ij)

View File

@@ -10,6 +10,8 @@ package com.maddyhome.idea.vim.listener
import com.intellij.execution.impl.ConsoleViewImpl import com.intellij.execution.impl.ConsoleViewImpl
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.EditorKind
import com.maddyhome.idea.vim.KeyHandler import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.LastUsedEditorInfo import com.maddyhome.idea.vim.LastUsedEditorInfo
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
@@ -17,8 +19,8 @@ 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
import com.maddyhome.idea.vim.common.EditorListener import com.maddyhome.idea.vim.common.EditorListener
import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.inInsertMode import com.maddyhome.idea.vim.helper.inInsertMode
import com.maddyhome.idea.vim.helper.isTerminalEditor
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
@@ -55,19 +57,17 @@ class IJEditorFocusListener : EditorListener {
// to know that a read-only editor that is hosting a console view with a running process can be treated as writable. // to know that a read-only editor that is hosting a console view with a running process can be treated as writable.
val ijEditor = editor.ij val ijEditor = editor.ij
val isCurrentEditorTerminal = ijEditor.isTerminalEditor() val isCurrentEditorTerminal = isTerminal(ijEditor)
KeyHandler.getInstance().lastUsedEditorInfo = LastUsedEditorInfo(currentEditorHashCode, false) KeyHandler.getInstance().lastUsedEditorInfo = LastUsedEditorInfo(currentEditorHashCode, false)
val switchToInsertMode = Runnable { val switchToInsertMode = Runnable {
val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor) val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor)
VimPlugin.getChange().insertBeforeCaret(editor, context) VimPlugin.getChange().insertBeforeCursor(editor, context)
KeyHandler.getInstance().lastUsedEditorInfo = LastUsedEditorInfo(currentEditorHashCode, true) KeyHandler.getInstance().lastUsedEditorInfo = LastUsedEditorInfo(currentEditorHashCode, true)
} }
if (isCurrentEditorTerminal) { if (isCurrentEditorTerminal && !ijEditor.inInsertMode) {
if (!ijEditor.inInsertMode) { switchToInsertMode.run()
switchToInsertMode.run()
}
} else if (ijEditor.isInsertMode && (oldEditorInfo.isInsertModeForced || !ijEditor.document.isWritable)) { } else if (ijEditor.isInsertMode && (oldEditorInfo.isInsertModeForced || !ijEditor.document.isWritable)) {
val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor) val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor)
val mode = injector.vimState.mode val mode = injector.vimState.mode
@@ -87,4 +87,12 @@ class IJEditorFocusListener : EditorListener {
} }
KeyHandler.getInstance().reset(editor) KeyHandler.getInstance().reset(editor)
} }
// By "terminal" we refer to some editor that should switch to INSERT mode on focus
private fun isTerminal(ijEditor: Editor): Boolean {
return !ijEditor.isViewer &&
!EditorHelper.isFileEditor(ijEditor) &&
ijEditor.document.isWritable &&
ijEditor.editorKind != EditorKind.DIFF
}
} }

View File

@@ -20,7 +20,6 @@ import com.intellij.codeInsight.template.impl.TemplateManagerImpl
import com.intellij.codeInsight.template.impl.TemplateState import com.intellij.codeInsight.template.impl.TemplateState
import com.intellij.codeInsight.template.impl.actions.NextVariableAction import com.intellij.codeInsight.template.impl.actions.NextVariableAction
import com.intellij.find.FindModelListener import com.intellij.find.FindModelListener
import com.intellij.ide.actions.ApplyIntentionAction
import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnAction
@@ -100,14 +99,10 @@ internal object IdeaSpecifics {
} else { } else {
emptyList() emptyList()
} }
val intentionName = if (action is ApplyIntentionAction) {
action.name
}
else null
// We can still get empty ID and empty candidates. Notably, for the tool window toggle buttons on the new UI. // We can still get empty ID and empty candidates. Notably, for the tool window toggle buttons on the new UI.
// We could filter out action events with `place == ActionPlaces.TOOLWINDOW_TOOLBAR_BAR` // We could filter out action events with `place == ActionPlaces.TOOLWINDOW_TOOLBAR_BAR`
VimPlugin.getNotifications(event.dataContext.getData(CommonDataKeys.PROJECT)).notifyActionId(id, candidates, intentionName) VimPlugin.getNotifications(event.dataContext.getData(CommonDataKeys.PROJECT)).notifyActionId(id, candidates)
} }
} }
@@ -169,7 +164,7 @@ internal object IdeaSpecifics {
) { ) {
editor?.let { editor?.let {
it.vim.mode = Mode.NORMAL() it.vim.mode = Mode.NORMAL()
VimPlugin.getChange().insertBeforeCaret(it.vim, event.dataContext.vim) VimPlugin.getChange().insertBeforeCursor(it.vim, event.dataContext.vim)
KeyHandler.getInstance().reset(it.vim) KeyHandler.getInstance().reset(it.vim)
} }
} }
@@ -223,7 +218,7 @@ internal object IdeaSpecifics {
// Enable insert mode if there is no selection in template // Enable insert mode if there is no selection in template
// Template with selection is handled by [com.maddyhome.idea.vim.group.visual.VisualMotionGroup.controlNonVimSelectionChange] // Template with selection is handled by [com.maddyhome.idea.vim.group.visual.VisualMotionGroup.controlNonVimSelectionChange]
if (editor.vim.inNormalMode) { if (editor.vim.inNormalMode) {
VimPlugin.getChange().insertBeforeCaret( VimPlugin.getChange().insertBeforeCursor(
editor.vim, editor.vim,
injector.executionContextManager.getEditorExecutionContext(editor.vim), injector.executionContextManager.getEditorExecutionContext(editor.vim),
) )

View File

@@ -9,7 +9,6 @@
package com.maddyhome.idea.vim.listener package com.maddyhome.idea.vim.listener
import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.AnActionResult import com.intellij.openapi.actionSystem.AnActionResult
@@ -17,8 +16,6 @@ import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.IdeActions import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.actionSystem.ex.AnActionListener import com.intellij.openapi.actionSystem.ex.AnActionListener
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.actionSystem.EditorAction
import com.intellij.openapi.editor.actions.EnterAction
import com.intellij.openapi.editor.event.CaretEvent import com.intellij.openapi.editor.event.CaretEvent
import com.intellij.openapi.editor.event.CaretListener import com.intellij.openapi.editor.event.CaretListener
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
@@ -27,16 +24,10 @@ import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl
import com.maddyhome.idea.vim.group.visual.moveCaretOneCharLeftFromSelectionEnd import com.maddyhome.idea.vim.group.visual.moveCaretOneCharLeftFromSelectionEnd
import com.maddyhome.idea.vim.helper.getTopLevelEditor import com.maddyhome.idea.vim.helper.getTopLevelEditor
import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
import com.maddyhome.idea.vim.newapi.vim
internal class RiderActionListener : AnActionListener { internal class RiderActionListener : AnActionListener {
private var editor: Editor? = null private var editor: Editor? = null
private fun shouldExecuteOnFrontend(action: EditorAction): Boolean {
val isInsertMode = editor?.vim?.insertMode
return isInsertMode == false && action is EnterAction
}
override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) { override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) {
if (VimPlugin.isNotEnabled()) return if (VimPlugin.isNotEnabled()) return
@@ -44,12 +35,6 @@ internal class RiderActionListener : AnActionListener {
if (hostEditor != null) { if (hostEditor != null) {
editor = hostEditor editor = hostEditor
} }
// Fixes RIDER-123506
if (action is EditorAction) {
val key = ActionPlaces.EXECUTE_EDITOR_ACTION_ON_FRONTEND
editor?.putUserData(key, shouldExecuteOnFrontend(action))
}
} }
override fun afterActionPerformed(action: AnAction, event: AnActionEvent, result: AnActionResult) { override fun afterActionPerformed(action: AnAction, event: AnActionEvent, result: AnActionResult) {

View File

@@ -468,9 +468,7 @@ internal object VimListenerManager {
openingEditor == null -> LocalOptionInitialisationScenario.EDIT openingEditor == null -> LocalOptionInitialisationScenario.EDIT
else -> LocalOptionInitialisationScenario.NEW else -> LocalOptionInitialisationScenario.NEW
} }
SlowOperations.knownIssue("VIM-3648").use { EditorListeners.add(event.editor, openingEditor?.vim ?: injector.fallbackWindow, scenario)
EditorListeners.add(event.editor, openingEditor?.vim ?: injector.fallbackWindow, scenario)
}
firstEditorInitialised = true firstEditorInitialised = true
} else { } else {
// We've got a virtual file, so FileOpenedSyncListener will be called. Save data // We've got a virtual file, so FileOpenedSyncListener will be called. Save data
@@ -819,7 +817,7 @@ internal object VimListenerManager {
if (editor.inVisualMode) { if (editor.inVisualMode) {
editor.vim.exitVisualMode() editor.vim.exitVisualMode()
} else if (editor.vim.inSelectMode) { } else if (editor.vim.inSelectMode) {
editor.vim.exitSelectMode(false) editor.exitSelectMode(false)
KeyHandler.getInstance().reset(editor.vim) KeyHandler.getInstance().reset(editor.vim)
} }
} }

View File

@@ -18,7 +18,7 @@ internal class IntellijMark(bookmark: LineBookmark, override val col: Int, proje
private val project: WeakReference<Project?> = WeakReference(project) private val project: WeakReference<Project?> = WeakReference(project)
override val key = BookmarksManager.getInstance(project)?.getType(bookmark)?.mnemonic ?: ' ' override val key = BookmarksManager.getInstance(project)?.getType(bookmark)?.mnemonic!!
override val line: Int override val line: Int
get() = getMark()?.line ?: 0 get() = getMark()?.line ?: 0
override val filepath: String override val filepath: String

View File

@@ -39,16 +39,26 @@ import java.io.IOException
@Service @Service
internal class IjClipboardManager : VimClipboardManager { internal class IjClipboardManager : VimClipboardManager {
override fun getPrimaryContent(): IjVimCopiedText? { @Deprecated("Please use com.maddyhome.idea.vim.api.VimClipboardManager#getPrimaryTextAndTransferableData")
override fun getPrimaryTextAndTransferableData(): Pair<String, List<Any>?>? {
val clipboard = Toolkit.getDefaultToolkit()?.systemSelection ?: return null val clipboard = Toolkit.getDefaultToolkit()?.systemSelection ?: return null
val contents = clipboard.getContents(null) ?: return null val contents = clipboard.getContents(null) ?: return null
val (text, transferableData) = getTextAndTransferableData(contents) ?: return null return getTextAndTransferableData(contents)
}
override fun getPrimaryContent(editor: VimEditor, context: ExecutionContext): IjVimCopiedText? {
val (text, transferableData) = getPrimaryTextAndTransferableData() ?: return null
return IjVimCopiedText(text, transferableData ?: emptyList()) return IjVimCopiedText(text, transferableData ?: emptyList())
} }
override fun getClipboardContent(editor: VimEditor, context: ExecutionContext): VimCopiedText? { @Deprecated("Please use com.maddyhome.idea.vim.api.VimClipboardManager#getClipboardTextAndTransferableData")
override fun getClipboardTextAndTransferableData(): Pair<String, List<Any>?>? {
val contents = getContents() ?: return null val contents = getContents() ?: return null
val (text, transferableData) = getTextAndTransferableData(contents) ?: return null return getTextAndTransferableData(contents)
}
override fun getClipboardContent(editor: VimEditor, context: ExecutionContext): VimCopiedText? {
val (text, transferableData) = getClipboardTextAndTransferableData() ?: return null
return IjVimCopiedText(text, transferableData ?: emptyList()) return IjVimCopiedText(text, transferableData ?: emptyList())
} }
@@ -115,6 +125,14 @@ internal class IjClipboardManager : VimClipboardManager {
// return setPrimaryText(entry.text, entry.rawText, entry.transferableData) != null // return setPrimaryText(entry.text, entry.rawText, entry.transferableData) != null
// } // }
@Deprecated("Please use com.maddyhome.idea.vim.api.VimClipboardManager#setPrimaryText")
override fun setPrimaryText(text: String, rawText: String, transferableData: List<Any>): Transferable? {
return handleTextSetting(text, rawText, transferableData) { content ->
val clipboard = Toolkit.getDefaultToolkit()?.systemSelection ?: return@handleTextSetting null
clipboard.setContents(content, EmptyClipboardOwner.INSTANCE)
}
}
override fun collectCopiedText( override fun collectCopiedText(
editor: VimEditor, editor: VimEditor,
context: ExecutionContext, context: ExecutionContext,
@@ -242,6 +260,6 @@ internal class IjClipboardManager : VimClipboardManager {
} }
} }
data class IjVimCopiedText(override val text: String, override val transferableData: List<Any>) : VimCopiedText { data class IjVimCopiedText(override val text: String, val transferableData: List<Any>) : VimCopiedText {
override fun updateText(newText: String): VimCopiedText = IjVimCopiedText(newText, transferableData) override fun updateText(newText: String): VimCopiedText = IjVimCopiedText(newText, transferableData)
} }

View File

@@ -32,7 +32,7 @@ internal class IjVimApplication : VimApplicationBase() {
return ApplicationManager.getApplication().isDispatchThread return ApplicationManager.getApplication().isDispatchThread
} }
override fun invokeLater(editor: VimEditor, action: () -> Unit) { override fun invokeLater(action: () -> Unit, editor: VimEditor) {
ApplicationManager.getApplication() ApplicationManager.getApplication()
.invokeLater(action, ModalityState.stateForComponent((editor as IjVimEditor).editor.component)) .invokeLater(action, ModalityState.stateForComponent((editor as IjVimEditor).editor.component))
} }

View File

@@ -20,7 +20,6 @@ import com.intellij.openapi.editor.ex.ScrollingModelEx
import com.intellij.openapi.editor.ex.util.EditorUtil import com.intellij.openapi.editor.ex.util.EditorUtil
import com.intellij.openapi.editor.impl.CaretModelImpl import com.intellij.openapi.editor.impl.CaretModelImpl
import com.intellij.openapi.editor.impl.EditorImpl import com.intellij.openapi.editor.impl.EditorImpl
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.VirtualFileManager
import com.maddyhome.idea.vim.api.BufferPosition import com.maddyhome.idea.vim.api.BufferPosition
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
@@ -36,8 +35,8 @@ import com.maddyhome.idea.vim.api.VimFoldRegion
import com.maddyhome.idea.vim.api.VimIndentConfig import com.maddyhome.idea.vim.api.VimIndentConfig
import com.maddyhome.idea.vim.api.VimScrollingModel import com.maddyhome.idea.vim.api.VimScrollingModel
import com.maddyhome.idea.vim.api.VimSelectionModel import com.maddyhome.idea.vim.api.VimSelectionModel
import com.maddyhome.idea.vim.api.VimVirtualFile
import com.maddyhome.idea.vim.api.VimVisualPosition import com.maddyhome.idea.vim.api.VimVisualPosition
import com.maddyhome.idea.vim.api.VirtualFile
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.IndentConfig import com.maddyhome.idea.vim.common.IndentConfig
import com.maddyhome.idea.vim.common.LiveRange import com.maddyhome.idea.vim.common.LiveRange
@@ -151,7 +150,7 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor, VimEditorBase(
} }
} }
} }
editor.document.insertString(atPosition, StringUtil.convertLineSeparators(text, "\n")) editor.document.insertString(atPosition, text)
} }
override fun replaceString(start: Int, end: Int, newString: String) { override fun replaceString(start: Int, end: Int, newString: String) {
@@ -302,13 +301,12 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor, VimEditorBase(
return editor.logicalPositionToOffset(logicalPosition) return editor.logicalPositionToOffset(logicalPosition)
} }
override fun getVirtualFile(): VimVirtualFile? { override fun getVirtualFile(): VirtualFile? {
val vf = EditorHelper.getVirtualFile(editor) val vf = EditorHelper.getVirtualFile(editor)
return vf?.let { return vf?.let {
object : VimVirtualFile { object : VirtualFile {
override val path: String = vf.path override val path: String = vf.path
override val protocol: String = vf.fileSystem.protocol override val protocol: String = vf.fileSystem.protocol
override val extension: String? = vf.extension
} }
} }
} }
@@ -515,10 +513,6 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor, VimEditorBase(
} }
} }
override fun getSoftWrapStartAtOffset(offset: Int): Int? {
return editor.softWrapModel.getSoftWrap(offset)?.start
}
override fun <T : ImmutableVimCaret> findLastVersionOfCaret(caret: T): T { override fun <T : ImmutableVimCaret> findLastVersionOfCaret(caret: T): T {
return caret return caret
} }

View File

@@ -213,6 +213,16 @@ internal class IjVimInjector : VimInjectorBase() {
override val redrawService: VimRedrawService override val redrawService: VimRedrawService
get() = service() get() = service()
@Deprecated("Please use VimInjector.vimState", replaceWith = ReplaceWith("vimState"))
override fun commandStateFor(editor: VimEditor): VimStateMachine {
return vimState
}
@Deprecated("Please use VimInjector.vimState", replaceWith = ReplaceWith("vimState"))
override fun commandStateFor(editor: Any): VimStateMachine {
return vimState
}
override val engineEditorHelper: EngineEditorHelper override val engineEditorHelper: EngineEditorHelper
get() = service<IjEditorHelper>() get() = service<IjEditorHelper>()
override val editorGroup: VimEditorGroup override val editorGroup: VimEditorGroup

View File

@@ -1,99 +0,0 @@
/*
* Copyright 2003-2025 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.troubleshooting
import com.intellij.openapi.fileEditor.FileEditor
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.ui.EditorNotificationPanel
import com.intellij.ui.EditorNotificationProvider
import com.intellij.ui.EditorNotifications
import com.intellij.util.PlatformUtils
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.icons.VimIcons
import com.maddyhome.idea.vim.vimscript.services.VimRcService
import java.util.function.Function
import javax.swing.JComponent
private var warningExplicitlyDisabled = false
private object CommandsCounter {
var commandsBeforeAutoDisable = 10
var initialized = false
@Synchronized
fun init() {
if (initialized) return
initialized = true
KeyHandler.getInstance().addCommandListener {
commandsBeforeAutoDisable -= 1
if (commandsBeforeAutoDisable <= 0) {
warningExplicitlyDisabled = true
}
}
}
}
/**
* Warning for the new users who may install IdeaVim plugin accidentally.
*/
internal class AccidentalInstallDetectorEditorNotificationProvider : EditorNotificationProvider, DumbAware {
override fun collectNotificationData(
project: Project,
file: VirtualFile,
): Function<in FileEditor, out JComponent?>? {
CommandsCounter.init()
// Note: Currently, enable this only for GoLand as it was a request from this IDE (VIM-3784).
// However, we can enable it for other IDEs if needed.
if (!PlatformUtils.isGoIde()) return null
if (warningExplicitlyDisabled) return null
if (VimPlugin.isNotEnabled()) return null
if (VimRcService.findIdeaVimRc() != null) return null
if (!injector.enabler.isNewIdeaVimUser()) return null
return Function { fileEditor: FileEditor ->
val panel = EditorNotificationPanel(fileEditor, EditorNotificationPanel.Status.Info)
panel.text = getText()
panel.icon(VimIcons.IDEAVIM)
KeyHandler.getInstance().addCommandListener {
if (CommandsCounter.commandsBeforeAutoDisable <= 0) {
KeyHandler.getInstance().removeAllCommandListeners()
EditorNotifications.getInstance(project).removeNotificationsForProvider(this)
}
panel.text = getText()
panel.invalidate()
panel.repaint()
}
@Suppress("DialogTitleCapitalization")
panel.createActionLabel("Disable IdeaVim") {
VimPlugin.setEnabled(false)
VimPlugin.getNotifications(project).showReenableNotification(project)
EditorNotifications.getInstance(project).removeNotificationsForProvider(this)
warningExplicitlyDisabled = true
}
panel.createActionLabel("Dismiss") {
EditorNotifications.getInstance(project).removeNotificationsForProvider(this)
warningExplicitlyDisabled = true
}
panel
}
}
private fun getText(): String {
return "<html>Youre using the IdeaVim plugin. If youre not familiar with Vim, consider disabling it. This message will disappear after ${CommandsCounter.commandsBeforeAutoDisable} commands.</html>"
}
}

View File

@@ -428,6 +428,14 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
return active; return active;
} }
/**
* @deprecated Use getVisibleText()
*/
@Deprecated(forRemoval = true)
public @NotNull String getText() {
return entry.getText();
}
@Override @Override
public @NotNull String getVisibleText() { public @NotNull String getVisibleText() {
return entry.getText(); return entry.getText();

View File

@@ -1,37 +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.vimscript.model.functions.handlers
import com.intellij.codeInsight.completion.CompletionService
import com.intellij.vim.annotations.VimscriptFunction
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.vimscript.model.VimLContext
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimInt
import com.maddyhome.idea.vim.vimscript.model.expressions.Expression
import com.maddyhome.idea.vim.vimscript.model.functions.FunctionHandler
@VimscriptFunction(name = "pumvisible")
internal class PopupMenuVisibleFunctionHandler : FunctionHandler() {
override val minimumNumberOfArguments = 0
override val maximumNumberOfArguments = 0
override fun doFunction(
argumentValues: List<Expression>,
editor: VimEditor,
context: ExecutionContext,
vimContext: VimLContext,
): VimDataType {
return if (CompletionService.getCompletionService().currentCompletion == null)
VimInt.ZERO
else
VimInt.ONE
}
}

View File

@@ -132,11 +132,6 @@
key="ideavim.only.in.editor.component"/> key="ideavim.only.in.editor.component"/>
<registryKey defaultValue="false" description="Old action execution mechanism" key="ideavim.old.action.execution" <registryKey defaultValue="false" description="Old action execution mechanism" key="ideavim.old.action.execution"
restartRequired="false"/> restartRequired="false"/>
<editorNotificationProvider
implementation="com.maddyhome.idea.vim.troubleshooting.AccidentalInstallDetectorEditorNotificationProvider"/>
<dependencySupport coordinate="configuration" kind="vim" displayName="IdeaVim"/>
</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/*)"/>
@@ -144,10 +139,12 @@
<xi:include href="/META-INF/includes/VimListeners.xml" xpointer="xpointer(/idea-plugin/*)"/> <xi:include href="/META-INF/includes/VimListeners.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"> <group id="com.chylex.intellij.vim" text="Vim" popup="true">
<add-to-group group-id="ToolsMenu" anchor="last"/> <add-to-group group-id="ToolsMenu" anchor="last"/>
</action> <action id="VimPluginToggle" class="com.maddyhome.idea.vim.action.VimPluginToggleAction"/>
<action id="VimRunLastMacroInOpenFiles" class="com.maddyhome.idea.vim.action.VimRunLastMacroInOpenFiles"/>
</group>
<!-- Internal --> <!-- Internal -->
<!--suppress PluginXmlI18n --> <!--suppress PluginXmlI18n -->
<action id="VimInternalAddBlockInlays" class="com.maddyhome.idea.vim.action.internal.AddBlockInlaysAction" text="Add Test Block Inlays | IdeaVim Internal" internal="true"/> <action id="VimInternalAddBlockInlays" class="com.maddyhome.idea.vim.action.internal.AddBlockInlaysAction" text="Add Test Block Inlays | IdeaVim Internal" internal="true"/>

View File

@@ -78,10 +78,5 @@
"keys": "gJ", "keys": "gJ",
"class": "com.maddyhome.idea.vim.action.change.delete.DeleteJoinVisualLinesAction", "class": "com.maddyhome.idea.vim.action.change.delete.DeleteJoinVisualLinesAction",
"modes": "X" "modes": "X"
},
{
"keys": "z@",
"class": "com.maddyhome.idea.vim.action.macro.PlaybackRegisterInOpenFilesAction",
"modes": "N"
} }
] ]

View File

@@ -1,4 +1,3 @@
{ {
"has": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.HasFunctionHandler", "has": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.HasFunctionHandler"
"pumvisible": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.PopupMenuVisibleFunctionHandler"
} }

View File

@@ -85,23 +85,11 @@ E545=E545: Missing colon: {0}
E546=E546: Illegal mode: {0} E546=E546: Illegal mode: {0}
E548=E548: Digit expected: {0} E548=E548: Digit expected: {0}
E549=E549: Illegal percentage: {0} E549=E549: Illegal percentage: {0}
E691=E691: Can only compare List with List
E692=E692: Invalid operation for List
E694=E694: Invalid operation for Funcrefs
E695=E695: Cannot index a Funcref
E701=E701: Invalid type for len() E701=E701: Invalid type for len()
E703=E703: Using a Funcref as a Number
E728=E728: Using a Dictionary as a Number
E729=E729: Using a Funcref as a String
E730=E730: Using a List as a String E730=E730: Using a List as a String
E731=E731: Using a Dictionary as a String E731=E731: Using a Dictionary as a String
E735=E735: Can only compare Dictionary with Dictionary
E736=E736: Invalid operation for Dictionary
E745=E745: Using a List as a Number
E774=E774: 'operatorfunc' is empty E774=E774: 'operatorfunc' is empty
E804=E804: Cannot use '%' with Float
E805=E805: Using a Float as a Number E805=E805: Using a Float as a Number
E806=E806: Using a Float as a String
E808=E808: Number or Float required E808=E808: Number or Float required
e841.reserved.name.cannot.be.used.for.user.defined.command=E841: Reserved name, cannot be used for user defined command e841.reserved.name.cannot.be.used.for.user.defined.command=E841: Reserved name, cannot be used for user defined command
E939=E939: Positive count required E939=E939: Positive count required

View File

@@ -15,6 +15,7 @@ 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.VimBehaviorDiffers
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
/** /**
@@ -113,8 +114,10 @@ class ChangeActionTest : VimTestCase() {
) )
} }
// Turn it on after typing via handlers are implemented for tests
// VIM-620 |i_CTRL-O| // VIM-620 |i_CTRL-O|
@Test @Test
@Disabled
fun testInsertSingleCommandAndNewLineInserting8() { fun testInsertSingleCommandAndNewLineInserting8() {
doTest( doTest(
listOf("i", "<C-O>", "gh", "d"), listOf("i", "<C-O>", "gh", "d"),
@@ -139,44 +142,24 @@ class ChangeActionTest : VimTestCase() {
// VIM-321 |d| |count| // VIM-321 |d| |count|
@Test @Test
fun testDeleteEmptyRange() { fun testDeleteEmptyRange() {
doTest( doTest("d0", "${c}hello\n", "hello\n", Mode.NORMAL())
"d0",
"${c}hello\n",
"hello\n",
Mode.NORMAL(),
)
} }
// VIM-157 |~| // VIM-157 |~|
@Test @Test
fun testToggleCharCase() { fun testToggleCharCase() {
doTest( doTest("~~", "${c}hello world\n", "HEllo world\n", Mode.NORMAL())
"~~",
"${c}hello world\n",
"HEllo world\n",
Mode.NORMAL(),
)
} }
// VIM-157 |~| // VIM-157 |~|
@Test @Test
fun testToggleCharCaseLineEnd() { fun testToggleCharCaseLineEnd() {
doTest( doTest("~~", "hello wor${c}ld\n", "hello worLD\n", Mode.NORMAL())
"~~",
"hello wor${c}ld\n",
"hello worLD\n",
Mode.NORMAL(),
)
} }
@Test @Test
fun testToggleCaseMotion() { fun testToggleCaseMotion() {
doTest( doTest("g~w", "${c}FooBar Baz\n", "fOObAR Baz\n", Mode.NORMAL())
"g~w",
"${c}FooBar Baz\n",
"fOObAR Baz\n",
Mode.NORMAL(),
)
} }
@Test @Test
@@ -191,12 +174,7 @@ class ChangeActionTest : VimTestCase() {
@Test @Test
fun testChangeLowerCase() { fun testChangeLowerCase() {
doTest( doTest("guw", "${c}FooBar Baz\n", "foobar Baz\n", Mode.NORMAL())
"guw",
"${c}FooBar Baz\n",
"foobar Baz\n",
Mode.NORMAL(),
)
} }
@Test @Test
@@ -238,13 +216,13 @@ class ChangeActionTest : VimTestCase() {
one one
two ${c}three two ${c}three
four four
""".trimIndent(), """.trimIndent(),
""" """
one one
two hello world! three two hello world! three
four four
""".trimIndent(), """.trimIndent(),
Mode.INSERT, Mode.INSERT,
) )
@@ -258,12 +236,12 @@ class ChangeActionTest : VimTestCase() {
""" """
one one
${c}two ${c}two
""".trimIndent(), """.trimIndent(),
""" """
one one
""".trimIndent(), """.trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )
@@ -278,12 +256,12 @@ class ChangeActionTest : VimTestCase() {
""" """
one ${c}two one ${c}two
three three
""".trimIndent(), """.trimIndent(),
""" """
one one
three three
""".trimIndent(), """.trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )
@@ -296,15 +274,15 @@ class ChangeActionTest : VimTestCase() {
"dw", "dw",
""" """
one ${c}two one ${c}two
three three
""".trimIndent(), """.trimIndent(),
""" """
one one
three three
""".trimIndent(), """.trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )
@@ -315,14 +293,12 @@ class ChangeActionTest : VimTestCase() {
fun testDeleteLastWordBeforeEOLAndWhitespace() { fun testDeleteLastWordBeforeEOLAndWhitespace() {
doTest( doTest(
"dw", "dw",
""" """one ${c}two
one ${c}two three
three """,
""".trimIndent(), """one
""" three
one """,
three
""".trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )
assertOffset(3) assertOffset(3)
@@ -336,7 +312,7 @@ class ChangeActionTest : VimTestCase() {
""" """
one ${c}two one ${c}two
three four three four
""".trimIndent(), """.trimIndent(),
"one four\n", "one four\n",
Mode.NORMAL(), Mode.NORMAL(),
@@ -377,12 +353,12 @@ class ChangeActionTest : VimTestCase() {
""" """
foo(${c}bar foo(${c}bar
baz baz
""".trimIndent(), """.trimIndent(),
""" """
foo( foo(
baz baz
""".trimIndent(), """.trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )
@@ -396,12 +372,12 @@ class ChangeActionTest : VimTestCase() {
""" """
fo${c}o fo${c}o
bar bar
""".trimIndent(), """.trimIndent(),
""" """
fo fo
bar bar
""".trimIndent(), """.trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )
@@ -416,7 +392,7 @@ class ChangeActionTest : VimTestCase() {
""" """
one one
two two
""".trimIndent(), """.trimIndent(),
"two\n", "two\n",
Mode.NORMAL(), Mode.NORMAL(),
@@ -467,14 +443,14 @@ class ChangeActionTest : VimTestCase() {
bar bar
baz baz
quux quux
""".trimIndent(), """.trimIndent(),
""" """
${c}o ${c}o
r r
z z
quux quux
""".trimIndent(), """.trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )
@@ -489,14 +465,14 @@ class ChangeActionTest : VimTestCase() {
bar bar
baz baz
quux quux
""".trimIndent(), """.trimIndent(),
""" """
${c}o ${c}o
r r
z z
quux quux
""".trimIndent(), """.trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )
@@ -541,7 +517,7 @@ quux
"3J", "3J",
""" """
foo. foo.
bar bar
""".dotToSpace().trimIndent(), """.dotToSpace().trimIndent(),
"foo bar", "foo bar",
@@ -566,16 +542,14 @@ quux
fun testDeleteJoinVisualLinesSpaces() { fun testDeleteJoinVisualLinesSpaces() {
doTest( doTest(
"v2jJ", "v2jJ",
""" """ a$c 1
a$c 1 b 2
b 2 c 3
c 3 quux
quux """,
""".trimIndent(), """ a 1 b 2 c 3
""" quux
a 1 b 2 c 3 """,
quux
""".trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )
} }
@@ -626,15 +600,15 @@ quux
listOf("l", "<C-V>", "jj", "x"), listOf("l", "<C-V>", "jj", "x"),
""" """
foo foo
bar bar
""".trimIndent(), """.trimIndent(),
""" """
fo fo
br br
""".trimIndent(), """.trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )
@@ -650,13 +624,13 @@ quux
foo foo
x x
bar bar
""".trimIndent(), """.trimIndent(),
""" """
fo fo
x x
br br
""".trimIndent(), """.trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )
@@ -669,7 +643,7 @@ quux
""" """
foo foo
bar bar
""".trimIndent(), """.trimIndent(),
) )
typeText(injector.parser.parseKeys("<C-V>" + "j" + "x")) typeText(injector.parser.parseKeys("<C-V>" + "j" + "x"))
@@ -677,7 +651,7 @@ quux
""" """
oo oo
ar ar
""".trimIndent(), """.trimIndent(),
) )
} }
@@ -685,35 +659,20 @@ quux
// |r| // |r|
@Test @Test
fun testReplaceOneChar() { fun testReplaceOneChar() {
doTest( doTest("rx", "b${c}ar\n", "b${c}xr\n", Mode.NORMAL())
"rx",
"b${c}ar\n",
"b${c}xr\n",
Mode.NORMAL(),
)
} }
// |r| // |r|
@VimBehaviorDiffers(originalVimAfter = "foXX${c}Xr\n") @VimBehaviorDiffers(originalVimAfter = "foXX${c}Xr\n")
@Test @Test
fun testReplaceMultipleCharsWithCount() { fun testReplaceMultipleCharsWithCount() {
doTest( doTest("3rX", "fo${c}obar\n", "fo${c}XXXr\n", Mode.NORMAL())
"3rX",
"fo${c}obar\n",
"fo${c}XXXr\n",
Mode.NORMAL(),
)
} }
// |r| // |r|
@Test @Test
fun testReplaceMultipleCharsWithCountPastEndOfLine() { fun testReplaceMultipleCharsWithCountPastEndOfLine() {
doTest( doTest("6rX", "fo${c}obar\n", "fo${c}obar\n", Mode.NORMAL())
"6rX",
"fo${c}obar\n",
"fo${c}obar\n",
Mode.NORMAL(),
)
} }
// |r| // |r|
@@ -725,12 +684,12 @@ quux
""" """
fo${c}obar fo${c}obar
foobaz foobaz
""".trimIndent(), """.trimIndent(),
""" """
foZZZZ foZZZZ
ZZZZZz ZZZZZz
""".trimIndent(), """.trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )
@@ -741,15 +700,13 @@ quux
fun testReplaceOneCharWithNewline() { fun testReplaceOneCharWithNewline() {
doTest( doTest(
"r<Enter>", "r<Enter>",
""" """ fo${c}obar
fo${c}obar foobaz
foobaz """,
""".trimIndent(), """ fo
""" bar
fo foobaz
bar """,
foobaz
""".trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )
} }
@@ -760,15 +717,13 @@ quux
fun testReplaceCharWithNewlineAndCountAddsOnlySingleNewline() { fun testReplaceCharWithNewlineAndCountAddsOnlySingleNewline() {
doTest( doTest(
"3r<Enter>", "3r<Enter>",
""" """ fo${c}obar
fo${c}obar foobaz
foobaz """,
""".trimIndent(), """ fo
""" r
fo foobaz
r """,
foobaz
""".trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )
} }
@@ -776,12 +731,7 @@ quux
// |s| // |s|
@Test @Test
fun testReplaceOneCharWithText() { fun testReplaceOneCharWithText() {
doTest( doTest("sxy<Esc>", "b${c}ar\n", "bx${c}yr\n", Mode.NORMAL())
"sxy<Esc>",
"b${c}ar\n",
"bx${c}yr\n",
Mode.NORMAL(),
)
} }
// |s| // |s|
@@ -803,12 +753,12 @@ quux
""" """
foo${c}bar foo${c}bar
biff biff
""".trimIndent(), """.trimIndent(),
""" """
fooxy${c}z fooxy${c}z
biff biff
""".trimIndent(), """.trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )
@@ -817,12 +767,7 @@ quux
// |R| // |R|
@Test @Test
fun testReplaceMode() { fun testReplaceMode() {
doTest( doTest("Rbaz<Esc>", "foo${c}bar\n", "fooba${c}z\n", Mode.NORMAL())
"Rbaz<Esc>",
"foo${c}bar\n",
"fooba${c}z\n",
Mode.NORMAL(),
)
} }
// |R| |i_<Insert>| // |R| |i_<Insert>|
@@ -858,12 +803,12 @@ quux
""" """
${c}foo baz ${c}foo baz
baz quux baz quux
""".trimIndent(), """.trimIndent(),
""" """
foo baz foo baz
fo${c}o quux fo${c}o quux
""".trimIndent(), """.trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )
@@ -877,16 +822,16 @@ quux
$c- 1 $c- 1
- 2 - 2
- 3 - 3
""".trimIndent(), """.trimIndent(),
) )
typeText(injector.parser.parseKeys("d$" + "j" + ".")) typeText(injector.parser.parseKeys("d$" + "j" + "."))
assertState( assertState(
""" """
- 3 - 3
""".trimIndent(), """.trimIndent(),
) )
} }
@@ -902,7 +847,7 @@ quux
$c- 1 $c- 1
- 2 - 2
- 3 - 3
""".trimIndent(), """.trimIndent(),
) )
typeText(injector.parser.parseKeys("A" + "<BS>" + "<Esc>" + "j" + ".")) typeText(injector.parser.parseKeys("A" + "<BS>" + "<Esc>" + "j" + "."))
@@ -911,7 +856,7 @@ quux
- -
- -
- 3 - 3
""".trimIndent(), """.trimIndent(),
) )
@@ -921,7 +866,7 @@ quux
$c- 1 $c- 1
- 2 - 2
- 3 - 3
""".trimIndent(), """.trimIndent(),
) )
typeText(injector.parser.parseKeys("A" + "4" + "<BS>" + "<Esc>" + "j" + ".")) typeText(injector.parser.parseKeys("A" + "4" + "<BS>" + "<Esc>" + "j" + "."))
@@ -930,7 +875,7 @@ quux
- 1 - 1
- 2 - 2
- 3 - 3
""".trimIndent(), """.trimIndent(),
) )
@@ -940,7 +885,7 @@ quux
$c- 1 $c- 1
- 2 - 2
- 3 - 3
""".trimIndent(), """.trimIndent(),
) )
typeText(injector.parser.parseKeys("A" + "<BS>" + "4" + "<Esc>" + "j" + ".")) typeText(injector.parser.parseKeys("A" + "<BS>" + "4" + "<Esc>" + "j" + "."))
@@ -949,7 +894,7 @@ quux
- 4 - 4
- 4 - 4
- 3 - 3
""".trimIndent(), """.trimIndent(),
) )
} }
@@ -1078,12 +1023,12 @@ quux
""" """
${c}lorem ipsum dolor sit amet ${c}lorem ipsum dolor sit amet
lorem ipsum dolor sit amet lorem ipsum dolor sit amet
""".trimIndent(), """.trimIndent(),
""" """
psum dolor sit amet psum dolor sit amet
${c}lorem ipsum dolor sit amet ${c}lorem ipsum dolor sit amet
""".trimIndent(), """.trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )
@@ -1098,12 +1043,12 @@ quux
gaganis ${c}gaganis gaganis gaganis ${c}gaganis gaganis
gaganis gaganis gaganis gaganis gaganis gaganis
gaganis gaganis gaganis gaganis gaganis gaganis
""".trimIndent(), """.trimIndent(),
""" """
gaganis s gaganis gaganis s gaganis
gaganis ${c}gaganis gaganis gaganis ${c}gaganis gaganis
""".trimIndent(), """.trimIndent(),
Mode.NORMAL(), Mode.NORMAL(),
) )

View File

@@ -1,108 +0,0 @@
/*
* Copyright 2003-2025 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.action
import com.maddyhome.idea.vim.api.injector
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
class ChangeCaseTest : VimTestCase() {
/**
* Note: The tests for duplicated commands (gugu and gUgU) might fail due to issues with the test environment,
* specifically related to file refresh operations. This is a known issue with the test infrastructure
* and not with the actual functionality being tested.
*
* The tests for guu and gUU test the same functionality and should pass.
*/
@Test
fun testChangeCaseLowerLineAction() {
typeTextInFile(
injector.parser.parseKeys("guu"),
"""
H${c}ELLO WORLD
${c}THIS IS A TEST
${c}FOR CASE CONVERSION
""".trimIndent(),
)
assertState(
"""
${c}hello world
${c}this is a test
${c}for case conversion
""".trimIndent()
)
}
@Disabled("Not yet supported")
@Test
fun testChangeCaseLowerLineActionDuplicated() {
typeTextInFile(
injector.parser.parseKeys("gugu"),
"""
H${c}ELLO WORLD
${c}THIS IS A TEST
${c}FOR CASE CONVERSION
""".trimIndent(),
)
assertState(
"""
${c}hello world
${c}this is a test
${c}for case conversion
""".trimIndent()
)
}
@Test
fun testChangeCaseUpperLineAction() {
typeTextInFile(
injector.parser.parseKeys("gUU"),
"""
h${c}ello world
${c}this is a test
${c}for case conversion
""".trimIndent(),
)
assertState(
"""
${c}HELLO WORLD
${c}THIS IS A TEST
${c}FOR CASE CONVERSION
""".trimIndent()
)
}
@Disabled("Not yet supported")
@Test
fun testChangeCaseUpperLineActionDuplicated() {
typeTextInFile(
injector.parser.parseKeys("gUgU"),
"""
h${c}ello world
${c}this is a test
${c}for case conversion
""".trimIndent(),
)
assertState(
"""
${c}HELLO WORLD
${c}THIS IS A TEST
${c}FOR CASE CONVERSION
""".trimIndent()
)
}
}

View File

@@ -1,102 +0,0 @@
/*
* Copyright 2003-2025 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.action
import com.maddyhome.idea.vim.api.injector
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
class ChangeRot13Test : VimTestCase() {
@Test
fun testChangeRot13MotionAction() {
typeTextInFile(
injector.parser.parseKeys("g?2w"),
"H${c}ello World ${c}This is a ${c}test for ROT13 ${c}encoding\n",
)
assertState("H${c}ryyb Jbeyq ${c}Guvf vf a ${c}grfg sbe ROT13 ${c}rapbqvat\n")
}
@Test
fun testChangeRot13VisualAction() {
typeTextInFile(
injector.parser.parseKeys("v2wg?"),
"H${c}ello World ${c}This is a ${c}test for ROT13 ${c}encoding\n",
)
assertState("H${c}ryyb Jbeyq Guvf vf n ${c}grfg sbe EOT13 ${c}rapbqvat\n")
}
@Test
fun testChangeRot13LineAction() {
typeTextInFile(
injector.parser.parseKeys("g??"),
"""
H${c}ello World
${c}This is a test
${c}for ROT13 encoding
""".trimIndent(),
)
assertState(
"""
${c}Uryyb Jbeyq
${c}Guvf vf n grfg
${c}sbe EBG13 rapbqvat
""".trimIndent()
)
}
@Disabled("Not yet supported")
@Test
fun testChangeRot13LineActionDuplicated() {
typeTextInFile(
injector.parser.parseKeys("g?g?"),
"""
H${c}ello World
${c}This is a test
${c}for ROT13 encoding
""".trimIndent(),
)
assertState(
"""
${c}Uryyb Jbeyq
${c}Guvf vf n grfg
${c}sbe EBG13 rapbqvat
""".trimIndent()
)
}
@Test
fun testChangeRot13NonEnglishLetters() {
typeTextInFile(
injector.parser.parseKeys("g?$"),
"${c}Привет мир! Hello world!\n",
)
assertState("${c}Привет мир! Uryyb jbeyq!\n")
}
@Test
fun testChangeRot13FullAlphabet() {
typeTextInFile(
injector.parser.parseKeys("g?$"),
"${c}abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\n",
)
assertState("${c}nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM\n")
}
@Test
fun testChangeRot13Symbols() {
typeTextInFile(
injector.parser.parseKeys("g?$"),
"${c}!@#$%^&*()_+-=[]{}|;:'\",.<>/?\n",
)
assertState("${c}!@#$%^&*()_+-=[]{}|;:'\",.<>/?\n")
}
}

View File

@@ -284,206 +284,4 @@ class MacroActionTest : VimTestCase() {
assertTrue(KeyHandler.getInstance().keyStack.isEmpty()) assertTrue(KeyHandler.getInstance().keyStack.isEmpty())
} }
// Undo tests for macros
@Test
fun `test undo after simple macro execution`() {
configureByText("${c}Hello world")
typeText(injector.parser.parseKeys("qa" + "dw" + "q"))
typeText(injector.parser.parseKeys("@a"))
assertState(c)
// Single undo should restore the state after a simple macro
typeText("u")
assertState("${c}world")
}
@Test
fun `test undo after macro with insert mode`() {
val initialText = "${c}test"
configureByText(initialText)
typeText(injector.parser.parseKeys("qa" + "A" + " added" + "<Esc>" + "q"))
typeText(injector.parser.parseKeys("@a"))
assertState("test added adde${c}d")
// Verify that undo can restore the original state
var undoCount = 0
while (fixture.editor.document.text != "test" && undoCount < 10) {
typeText("u")
undoCount++
}
assertState("test$c")
}
@Test
fun `test undo after executing macro multiple times`() {
val initialText = "${c}one two three four"
configureByText(initialText)
typeText(injector.parser.parseKeys("qa" + "dw" + "q"))
typeText(injector.parser.parseKeys("3@a"))
assertState(c)
// Verify that undo can restore the original state
var undoCount = 0
while (fixture.editor.document.text != "one two three four" && undoCount < 10) {
typeText("u")
undoCount++
}
assertState(initialText)
// Verify that multiple undos were needed (one for each macro execution)
assertTrue(undoCount == 2, "Expected 2 undos for 2 macro executions, but needed $undoCount")
}
@Test
fun `test undo after nested macro execution`() {
val initialText = "${c}line1\nline2\nline3"
configureByText(initialText)
typeText(injector.parser.parseKeys("qa" + "dd" + "q"))
typeText(injector.parser.parseKeys("qb" + "j@a" + "q"))
typeText(injector.parser.parseKeys("@b"))
assertState("${c}line2")
// Verify that undo can restore the original state
var undoCount = 0
while (fixture.editor.document.text != "line1\nline2\nline3" && undoCount < 10) {
typeText("u")
undoCount++
}
assertState(initialText)
}
@Test
fun `test undo macro with multiple operations`() {
val initialText = "${c}foo bar baz"
configureByText(initialText)
typeText(injector.parser.parseKeys("qa" + "dw" + "A" + "!" + "<Esc>" + "q"))
typeText(injector.parser.parseKeys("@a"))
assertState("bar baz${c}!")
// Verify that undo can restore the original state
var undoCount = 0
while (fixture.editor.document.text != "foo bar baz" && undoCount < 10) {
typeText("u")
undoCount++
}
assertState(initialText)
// Verify that multiple undos were needed for the multiple operations in the macro
assertTrue(undoCount > 1, "Expected multiple undos for macro with multiple operations")
}
@Test
fun `test undo macro with visual mode operations`() {
val initialText = "${c}select this text"
configureByText(initialText)
typeText(injector.parser.parseKeys("qa" + "viw" + "d" + "q"))
typeText(injector.parser.parseKeys("@a"))
assertState("${c}this text")
// Verify that undo can restore the original state
var undoCount = 0
while (fixture.editor.document.text != "select this text" && undoCount < 10) {
typeText("u")
undoCount++
}
assertState(initialText)
}
@Test
fun `test undo macro with change operation`() {
val initialText = "${c}change(this)"
configureByText(initialText)
typeText(injector.parser.parseKeys("qa" + "ci(" + "that" + "<Esc>" + "q"))
typeText(injector.parser.parseKeys("@a"))
assertState("change(tha${c}t)")
// Verify that undo can restore the original state
var undoCount = 0
while (fixture.editor.document.text != "change(this)" && undoCount < 10) {
typeText("u")
undoCount++
}
assertState(initialText)
}
@Test
fun `test undo after repeating last macro`() {
val initialText = "${c}word1 word2 word3"
configureByText(initialText)
typeText(injector.parser.parseKeys("qa" + "dw" + "q"))
typeText(injector.parser.parseKeys("@a"))
assertState("${c}word3")
typeText(injector.parser.parseKeys("@@"))
assertState(c)
// Verify that undo can restore the original state
var undoCount = 0
while (fixture.editor.document.text != "word1 word2 word3" && undoCount < 10) {
typeText("u")
undoCount++
}
assertState(initialText)
// Each macro execution should create its own undo entry
assertTrue(undoCount == 3, "Expected 3 undos for 3 macro executions, but was $undoCount")
}
@Test
fun `test undo macro with ex command`() {
val initialText = "${c}line1\nline2\nline3"
configureByText(initialText)
typeText(injector.parser.parseKeys("qa" + ":d<CR>" + "q"))
typeText(injector.parser.parseKeys("@a"))
assertState("${c}line3")
// Verify that undo can restore the original state
var undoCount = 0
while (fixture.editor.document.text != "line1\nline2\nline3" && undoCount < 10) {
typeText("u")
undoCount++
}
assertState(initialText)
}
@Test
fun `test undo macro with substitute command`() {
val initialText = "${c}hello world hello"
configureByText(initialText)
typeText(injector.parser.parseKeys("qa" + ":s/hello/goodbye<CR>" + "q"))
typeText(injector.parser.parseKeys("@a"))
assertState("${c}goodbye world goodbye")
// Verify that undo can restore the original state
var undoCount = 0
while (fixture.editor.document.text != "hello world hello" && undoCount < 10) {
typeText("u")
undoCount++
}
assertState(initialText)
}
@Test
fun `test multiple undo after macro`() {
val initialText = "${c}one two three"
configureByText(initialText)
typeText(injector.parser.parseKeys("qa" + "dw" + "i" + "first " + "<Esc>" + "q"))
typeText(injector.parser.parseKeys("@a"))
assertState("firstfirst${c} two three")
typeText(injector.parser.parseKeys("w"))
typeText(injector.parser.parseKeys("@a"))
assertState("firstfirst first${c} three")
// Verify that undo can restore the original state
var undoCount = 0
while (fixture.editor.document.text != "one two three" && undoCount < 10) {
typeText("u")
undoCount++
}
assertState(initialText)
// Should need multiple undos since we executed the macro twice
assertTrue(undoCount >= 2, "Expected at least 2 undos for 2 macro executions")
}
@Test
fun `test undo restores cursor position after macro`() {
val initialText = "${c}start middle end"
configureByText(initialText)
typeText(injector.parser.parseKeys("qa" + "w" + "dw" + "q"))
typeText(injector.parser.parseKeys("@a"))
assertState("start en${c}d")
// Verify that undo can restore both text and cursor position
var undoCount = 0
while (fixture.editor.document.text != "start middle end" && undoCount < 10) {
typeText("u")
undoCount++
}
assertState("start ${c}middle end")
}
} }

View File

@@ -875,6 +875,15 @@ class MotionActionTest : VimTestCase() {
assertOffset(9) assertOffset(9)
} }
// VIM-965 |[m|
@TestWithoutNeovim(reason = SkipNeovimReason.DIFFERENT, "File type specific")
@Test
fun testMethodMovingInNonJavaFile() {
configureByJsonText("{\"foo\": \"${c}bar\"}\n")
typeText(injector.parser.parseKeys("[m"))
assertState("{\"foo\": \"${c}bar\"}\n")
}
// VIM-331 |w| // VIM-331 |w|
@TestWithoutNeovim(reason = SkipNeovimReason.UNCLEAR) @TestWithoutNeovim(reason = SkipNeovimReason.UNCLEAR)
@Test @Test

View File

@@ -18,6 +18,7 @@ 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.VimBehaviorDiffers
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
@@ -1144,12 +1145,6 @@ $c tw${c}o
"O${c}NcE thIs ${c}TEXt wIlL n${c}Ot lOoK s${c}O rIdIcuLoUs\n", "O${c}NcE thIs ${c}TEXt wIlL n${c}Ot lOoK s${c}O rIdIcuLoUs\n",
) )
assertState("O${c}nce this text will n${c}ot look s${c}o ridiculous\n") assertState("O${c}nce this text will n${c}ot look s${c}o ridiculous\n")
typeTextInFile(
injector.parser.parseKeys("v2wgu"),
"O${c}NcE thIs ${c}TEXt wIlL n${c}Ot lOoK s${c}O rIdIcuLoUs\n",
)
assertState("O${c}nce this text will n${c}ot look s${c}o ridiculous\n")
} }
@Test @Test
@@ -1186,12 +1181,6 @@ $c tw${c}o
"O${c}NcE thIs ${c}TEXt wIlL N${c}Ot lOoK S${c}O rIdIcuLoUs\n", "O${c}NcE thIs ${c}TEXt wIlL N${c}Ot lOoK S${c}O rIdIcuLoUs\n",
) )
assertState("O${c}NCE THIS TEXT WILL N${c}OT LOOK S${c}O RIDICULOUS\n") assertState("O${c}NCE THIS TEXT WILL N${c}OT LOOK S${c}O RIDICULOUS\n")
typeTextInFile(
injector.parser.parseKeys("v2wgU"),
"O${c}NcE thIs ${c}TEXt wIlL N${c}Ot lOoK S${c}O rIdIcuLoUs\n",
)
assertState("O${c}NCE THIS TEXT WILL N${c}OT LOOK S${c}O RIDICULOUS\n")
} }
@Test @Test
@@ -1833,6 +1822,7 @@ $c five six se${c}ven eight
) )
} }
@Disabled("Action execution in tests is broken for 2024.2")
@Test @Test
fun testInsertNewLineAboveAction() { fun testInsertNewLineAboveAction() {
typeTextInFile( typeTextInFile(
@@ -1859,6 +1849,7 @@ $c five six se${c}ven eight
) )
} }
@Disabled("Action execution in tests is broken for 2024.2")
@VimBehaviorDiffers(originalVimAfter = "${c}\n${c}\nabcde\n${c}\n${c}\nabcde\n") @VimBehaviorDiffers(originalVimAfter = "${c}\n${c}\nabcde\n${c}\n${c}\nabcde\n")
@Test @Test
fun testInsertNewLineAboveActionWithMultipleCaretsInLine() { fun testInsertNewLineAboveActionWithMultipleCaretsInLine() {
@@ -1873,6 +1864,7 @@ $c five six se${c}ven eight
assertState("${c}\nabcde\n${c}\nabcde\n") assertState("${c}\nabcde\n${c}\nabcde\n")
} }
@Disabled("Action execution in tests is broken for 2024.2")
@Test @Test
fun testInsertNewLineBelowAction() { fun testInsertNewLineBelowAction() {
typeTextInFile( typeTextInFile(
@@ -2193,18 +2185,16 @@ rtyfg${c}hzxc"""
val vimEditor = fixture.editor.vim val vimEditor = fixture.editor.vim
val context = injector.executionContextManager.getEditorExecutionContext(vimEditor) val context = injector.executionContextManager.getEditorExecutionContext(vimEditor)
injector.registerGroup.storeText(vimEditor, context, '*', "fgh") injector.registerGroup.storeText(vimEditor, context, '*', "fgh")
ApplicationManager.getApplication().invokeAndWait { ApplicationManager.getApplication().runWriteAction {
ApplicationManager.getApplication().runWriteAction { VimPlugin.getRegister()
VimPlugin.getRegister() .storeText(
.storeText( IjVimEditor(editor),
IjVimEditor(editor), context,
context, editor.vim.primaryCaret(),
editor.vim.primaryCaret(), TextRange(16, 19),
TextRange(16, 19), SelectionType.CHARACTER_WISE,
SelectionType.CHARACTER_WISE, false
false )
)
}
} }
typeText(injector.parser.parseKeys("\"*P")) typeText(injector.parser.parseKeys("\"*P"))
val after = "fg${c}hqfg${c}hwe asd zxc rty fg${c}hfgh vbn" val after = "fg${c}hqfg${c}hwe asd zxc rty fg${c}hfgh vbn"

View File

@@ -1,557 +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 org.jetbrains.plugins.ideavim.action.change
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Test
/**
* Tests for complex undo scenarios involving motion and change operations
* These tests verify that undo correctly restores both text and cursor position
*/
class ComplexUndoTest : VimTestCase() {
@Test
fun `test undo change inside parentheses with cursor movement`() {
// This is the example from the user's request
configureByText("a${c}bc(xxx)def")
typeText("ci(")
typeText("yyy")
typeText("<Esc>")
assertState("abc(yy${c}y)def")
typeText("u")
assertState("a${c}bc(xxx)def")
}
@Test
fun `test undo change inside parentheses with cursor movement with oldundo`() {
// This is the example from the user's request
configureByText("a${c}bc(xxx)def")
try {
enterCommand("set oldundo")
typeText("ci(")
typeText("yyy")
typeText("<Esc>")
assertState("abc(yy${c}y)def")
typeText("u")
assertState("abc(yyy${c})def")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo delete inside brackets with cursor movement`() {
configureByText("fo${c}o[bar]baz")
typeText("di[")
assertState("foo[${c}]baz")
typeText("u")
assertState("fo${c}o[bar]baz")
}
@Test
fun `test undo delete inside brackets with cursor movement with oldundo`() {
configureByText("fo${c}o[bar]baz")
try {
enterCommand("set oldundo")
typeText("di[")
assertState("foo[${c}]baz")
typeText("u")
assertState("fo${c}o[bar]baz")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo change inside quotes with cursor movement`() {
configureByText("Say ${c}hello \"world\" today")
typeText("ci\"")
typeText("universe")
typeText("<Esc>")
assertState("Say hello \"univers${c}e\" today")
typeText("u")
assertState("Say ${c}hello \"world\" today")
}
@Test
fun `test undo change inside quotes with cursor movement with oldundo`() {
configureByText("Say ${c}hello \"world\" today")
try {
enterCommand("set oldundo")
typeText("ci\"")
typeText("universe")
typeText("<Esc>")
assertState("Say hello \"univers${c}e\" today")
typeText("u")
assertState("Say hello \"universe${c}\" today")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo delete word with cursor at different position`() {
configureByText("The qu${c}ick brown fox")
typeText("daw") // Delete a word (including surrounding spaces)
assertState("The ${c}brown fox")
typeText("u")
assertState("The qu${c}ick brown fox")
}
@Test
fun `test undo delete word with cursor at different position with oldundo`() {
configureByText("The qu${c}ick brown fox")
try {
enterCommand("set oldundo")
typeText("daw") // Delete a word (including surrounding spaces)
assertState("The ${c}brown fox")
typeText("u")
assertState("The qu${c}ick brown fox")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo change paragraph with cursor movement`() {
configureByText(
"""
First paragraph.
Sec${c}ond paragraph
with multiple lines.
Third paragraph.
""".trimIndent()
)
typeText("cip")
typeText("New content")
typeText("<Esc>")
assertState(
"""
First paragraph.
New conten${c}t
Third paragraph.
""".trimIndent()
)
typeText("u")
assertState(
"""
First paragraph.
Sec${c}ond paragraph
with multiple lines.
Third paragraph.
""".trimIndent()
)
}
@Test
fun `test undo change paragraph with cursor movement with oldundo`() {
configureByText(
"""
First paragraph.
Sec${c}ond paragraph
with multiple lines.
Third paragraph.
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("cip")
typeText("New content")
typeText("<Esc>")
assertState(
"""
First paragraph.
New conten${c}t
Third paragraph.
""".trimIndent()
)
typeText("u")
assertState(
"""
First paragraph.
New content${c}
Third paragraph.
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo delete to search result`() {
configureByText("abc${c}defghijklmnop")
typeText("d/jkl<CR>") // Delete to search result
assertState("abc${c}jklmnop")
typeText("u")
assertState("abc${c}defghijklmnop")
}
@Test
fun `test undo delete to search result with oldundo`() {
configureByText("abc${c}defghijklmnop")
try {
enterCommand("set oldundo")
typeText("d/jkl<CR>") // Delete to search result
assertState("abc${c}jklmnop")
typeText("u")
assertState("abc${c}defghijklmnop")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo change to mark with cursor movement`() {
configureByText(
"""
Li${c}ne 1
Line 2
Line 3
Line 4
""".trimIndent()
)
typeText("ma") // Set mark 'a'
typeText("2j") // Move down 2 lines
assertState(
"""
Line 1
Line 2
Li${c}ne 3
Line 4
""".trimIndent()
)
typeText("c'a") // Change to mark 'a'
typeText("Changed")
typeText("<Esc>")
assertState(
"""
Change${c}d
Line 4
""".trimIndent()
)
typeText("u")
assertState(
"""
Line 1
Line 2
Li${c}ne 3
Line 4
""".trimIndent()
)
}
@Test
fun `test undo change to mark with cursor movement with oldundo`() {
configureByText(
"""
Li${c}ne 1
Line 2
Line 3
Line 4
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("ma") // Set mark 'a'
typeText("2j") // Move down 2 lines
assertState(
"""
Line 1
Line 2
Li${c}ne 3
Line 4
""".trimIndent()
)
typeText("c'a") // Change to mark 'a'
typeText("Changed")
typeText("<Esc>")
assertState(
"""
Change${c}d
Line 4
""".trimIndent()
)
typeText("u")
assertState(
"""
Changed${c}
Line 4
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo substitute with cursor movement`() {
configureByText("${c}Hello world hello")
typeText(":s/hello/goodbye/gi<CR>") // Substitute with flags
assertState("${c}goodbye world goodbye")
typeText("u")
assertState("${c}Hello world hello")
}
@Test
fun `test undo substitute with cursor movement with oldundo`() {
configureByText("${c}Hello world hello")
try {
enterCommand("set oldundo")
typeText(":s/hello/goodbye/gi<CR>") // Substitute with flags
assertState("${c}goodbye world goodbye")
typeText("u")
assertState("${c}Hello world hello")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo multiple operations in sequence`() {
configureByText("${c}abc def ghi")
// First operation: delete word
typeText("dw")
assertState("${c}def ghi")
// Second operation: change word
typeText("cw")
typeText("xyz")
typeText("<Esc>")
assertState("xy${c}z ghi")
// Third operation: append at end
typeText("A")
typeText(" jkl")
typeText("<Esc>")
assertState("xyz ghi jk${c}l")
// Undo all operations
typeText("u")
assertState("xyz ghi${c}")
typeText("u")
assertState("${c}def ghi")
typeText("u")
assertState("${c}abc def ghi")
}
@Test
fun `test undo multiple operations in sequence with oldundo`() {
configureByText("${c}abc def ghi")
try {
enterCommand("set oldundo")
// First operation: delete word
typeText("dw")
assertState("${c}def ghi")
// Second operation: change word
typeText("cw")
typeText("xyz")
typeText("<Esc>")
assertState("xy${c}z ghi")
// Third operation: append at end
typeText("A")
typeText(" jkl")
typeText("<Esc>")
assertState("xyz ghi jk${c}l")
// Undo all operations
typeText("u")
assertState("xyz ghi jkl${c}")
typeText("u")
assertState("xyz ghi${c}")
typeText("u")
assertState("xyz${c} ghi")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo with text objects and counts`() {
configureByText("function(${c}arg1, arg2, arg3)")
typeText("d2f,") // Delete to 2nd comma
assertState("function(${c} arg3)")
typeText("u")
assertState("function(${c}arg1, arg2, arg3)")
}
@Test
fun `test undo with text objects and counts with oldundo`() {
configureByText("function(${c}arg1, arg2, arg3)")
try {
enterCommand("set oldundo")
typeText("d2f,") // Delete to 2nd comma
assertState("function(${c} arg3)")
typeText("u")
assertState("function(${c}arg1, arg2, arg3)")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo inner word at end of word`() {
configureByText("The quic${c}k brown fox")
typeText("diw")
assertState("The ${c} brown fox")
typeText("u")
assertState("The quic${c}k brown fox")
}
@Test
fun `test undo inner word at end of word with oldundo`() {
configureByText("The quic${c}k brown fox")
try {
enterCommand("set oldundo")
typeText("diw")
assertState("The ${c} brown fox")
typeText("u")
assertState("The quic${c}k brown fox")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo change with register and motion`() {
configureByText("${c}Hello world")
typeText("\"aciw") // Change inner word into register 'a'
typeText("Goodbye")
typeText("<Esc>")
assertState("Goodby${c}e world")
typeText("u")
assertState("${c}Hello world")
}
@Test
fun `test undo change with register and motion with oldundo`() {
configureByText("${c}Hello world")
try {
enterCommand("set oldundo")
typeText("\"aciw") // Change inner word into register 'a'
typeText("Goodbye")
typeText("<Esc>")
assertState("Goodby${c}e world")
typeText("u")
assertState("Goodbye${c} world")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo join with indentation handling`() {
configureByText(
"""
if (condition) {
${c}doSomething();
doMore();
}
""".trimIndent()
)
typeText("J")
assertState(
"""
if (condition) {
doSomething();${c} doMore();
}
""".trimIndent()
)
typeText("u")
assertState(
"""
if (condition) {
${c}doSomething();
doMore();
}
""".trimIndent()
)
}
@Test
fun `test undo join with indentation handling with oldundo`() {
configureByText(
"""
if (condition) {
${c}doSomething();
doMore();
}
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("J")
assertState(
"""
if (condition) {
doSomething();${c} doMore();
}
""".trimIndent()
)
typeText("u")
assertState(
"""
if (condition) {
${c}doSomething();
doMore();
}
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo replace mode changes`() {
configureByText("${c}Hello world")
typeText("R")
typeText("Goodbye")
typeText("<Esc>")
assertState("Goodby${c}eorld")
typeText("u")
assertState("${c}Hello world")
}
@Test
fun `test undo replace mode changes with oldundo`() {
configureByText("${c}Hello world")
try {
enterCommand("set oldundo")
typeText("R")
typeText("Goodbye")
typeText("<Esc>")
assertState("Goodby${c}eorld")
typeText("u")
assertState("Goodbye${c}orld")
} finally {
enterCommand("set nooldundo")
}
}
}

View File

@@ -9,6 +9,7 @@
package org.jetbrains.plugins.ideavim.action.change package org.jetbrains.plugins.ideavim.action.change
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
class OperatorActionTest : VimTestCase() { class OperatorActionTest : VimTestCase() {
@@ -153,6 +154,7 @@ class OperatorActionTest : VimTestCase() {
} }
@Test @Test
@Disabled(":set does not correctly parse the quotes in the lambda syntax")
// The parser is treating the second double-quote char as a comment. The argument to the command is parsed as: // The parser is treating the second double-quote char as a comment. The argument to the command is parsed as:
// opfunc={ arg -> execute "`[v`]rx // opfunc={ arg -> execute "`[v`]rx
// The map command is properly handled - the `<CR>g@` is correctly understood, and the full lambda is passed to the // The map command is properly handled - the `<CR>g@` is correctly understood, and the full lambda is passed to the

View File

@@ -85,296 +85,4 @@ class ChangeCaseToggleCharacterActionTest : VimTestCase() {
this.enterCommand("set whichwrap=~") this.enterCommand("set whichwrap=~")
} }
} }
@Test
fun `test undo after toggle case single character`() {
configureByText("Hello ${c}World")
typeText("~")
assertState("Hello w${c}orld")
typeText("u")
assertState("Hello ${c}World")
}
@Test
fun `test undo after toggle case single character with oldundo`() {
configureByText("Hello ${c}World")
try {
enterCommand("set oldundo")
typeText("~")
assertState("Hello w${c}orld")
typeText("u")
assertState("Hello ${c}World")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after toggle case multiple characters`() {
configureByText("${c}hello WORLD")
typeText("5~")
assertState("HELLO${c} WORLD")
typeText("u")
assertState("${c}hello WORLD")
}
@Test
fun `test undo after toggle case multiple characters with oldundo`() {
configureByText("${c}hello WORLD")
try {
enterCommand("set oldundo")
typeText("5~")
assertState("HELLO${c} WORLD")
typeText("u")
assertState("${c}hello WORLD")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test multiple undo after sequential toggle case`() {
configureByText("${c}aBc")
typeText("~")
assertState("A${c}Bc")
typeText("~")
assertState("Ab${c}c")
typeText("~")
assertState("Ab${c}C")
// Undo third toggle
typeText("u")
assertState("Ab${c}c")
// Undo second toggle
typeText("u")
assertState("A${c}Bc")
// Undo first toggle
typeText("u")
assertState("${c}aBc")
}
@Test
fun `test multiple undo after sequential toggle case with oldundo`() {
configureByText("${c}aBc")
try {
enterCommand("set oldundo")
typeText("~")
assertState("A${c}Bc")
typeText("~")
assertState("Ab${c}c")
typeText("~")
assertState("Ab${c}C")
// Undo third toggle
typeText("u")
assertState("Ab${c}c")
// Undo second toggle
typeText("u")
assertState("A${c}Bc")
// Undo first toggle
typeText("u")
assertState("${c}aBc")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo toggle case motion`() {
configureByText("${c}hello world")
typeText("g~w")
assertState("${c}HELLO world")
typeText("u")
assertState("${c}hello world")
}
@Test
fun `test undo toggle case motion with oldundo`() {
configureByText("${c}hello world")
try {
enterCommand("set oldundo")
typeText("g~w")
assertState("${c}HELLO world")
typeText("u")
assertState("${c}hello world")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo uppercase motion`() {
configureByText("${c}hello world")
typeText("gUw")
assertState("${c}HELLO world")
typeText("u")
assertState("${c}hello world")
}
@Test
fun `test undo uppercase motion with oldundo`() {
configureByText("${c}hello world")
try {
enterCommand("set oldundo")
typeText("gUw")
assertState("${c}HELLO world")
typeText("u")
assertState("${c}hello world")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo lowercase motion`() {
configureByText("${c}HELLO WORLD")
typeText("guw")
assertState("${c}hello WORLD")
typeText("u")
assertState("${c}HELLO WORLD")
}
@Test
fun `test undo lowercase motion with oldundo`() {
configureByText("${c}HELLO WORLD")
try {
enterCommand("set oldundo")
typeText("guw")
assertState("${c}hello WORLD")
typeText("u")
assertState("${c}HELLO WORLD")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo toggle case line`() {
configureByText("${c}Hello World")
typeText("g~~")
assertState("${c}hELLO wORLD")
typeText("u")
assertState("${c}Hello World")
}
@Test
fun `test undo toggle case line with oldundo`() {
configureByText("${c}Hello World")
try {
enterCommand("set oldundo")
typeText("g~~")
assertState("${c}hELLO wORLD")
typeText("u")
assertState("${c}Hello World")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo uppercase line`() {
configureByText("${c}Hello World")
typeText("gUU")
assertState("${c}HELLO WORLD")
typeText("u")
assertState("${c}Hello World")
}
@Test
fun `test undo uppercase line with oldundo`() {
configureByText("${c}Hello World")
try {
enterCommand("set oldundo")
typeText("gUU")
assertState("${c}HELLO WORLD")
typeText("u")
assertState("${c}Hello World")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo lowercase line`() {
configureByText("${c}HELLO WORLD")
typeText("guu")
assertState("${c}hello world")
typeText("u")
assertState("${c}HELLO WORLD")
}
@Test
fun `test undo lowercase line with oldundo`() {
configureByText("${c}HELLO WORLD")
try {
enterCommand("set oldundo")
typeText("guu")
assertState("${c}hello world")
typeText("u")
assertState("${c}HELLO WORLD")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test toggle case line caret position`() {
configureByText(" Hello ${c}World")
typeText("g~~")
assertState(" ${c}hELLO wORLD")
typeText("u")
assertState(" Hello ${c}World")
typeText("^g~~")
assertState(" ${c}hELLO wORLD")
typeText("u")
assertState(" ${c}Hello World")
typeText("hg~~")
assertState(" $c hELLO wORLD")
typeText("u")
assertState(" $c Hello World")
}
@Test
fun `test uppercase line caret position`() {
configureByText(" Hello ${c}World")
typeText("gUU")
assertState(" ${c}HELLO WORLD")
typeText("u")
assertState(" Hello ${c}World")
typeText("^gUU")
assertState(" ${c}HELLO WORLD")
typeText("u")
assertState(" ${c}Hello World")
typeText("hgUU")
assertState(" $c HELLO WORLD")
typeText("u")
assertState(" $c Hello World")
}
@Test
fun `test lowercase line caret position`() {
configureByText(" Hello ${c}World")
typeText("guu")
assertState(" ${c}hello world")
typeText("u")
assertState(" Hello ${c}World")
typeText("^guu")
assertState(" ${c}hello world")
typeText("u")
assertState(" ${c}Hello World")
typeText("hguu")
assertState(" $c hello world")
typeText("u")
assertState(" $c Hello World")
}
} }

View File

@@ -12,6 +12,7 @@ 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.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
class ChangeMotionActionTest : VimTestCase() { class ChangeMotionActionTest : VimTestCase() {
@@ -149,7 +150,9 @@ class ChangeMotionActionTest : VimTestCase() {
} }
// VIM-276 |c| |F| // VIM-276 |c| |F|
@Suppress("unused")
@Test @Test
@Disabled
fun testChangeLinesToBackwards() { fun testChangeLinesToBackwards() {
doTest( doTest(
"cFc", "cFc",
@@ -205,280 +208,4 @@ class ChangeMotionActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
) )
} }
@Test
fun `test undo after change word`() {
configureByText("Hello ${c}world and more")
typeText("cw")
typeText("Vim")
typeText("<Esc>")
assertState("Hello Vi${c}m and more")
typeText("u")
assertState("Hello ${c}world and more")
}
@Test
fun `test undo after change word with oldundo`() {
configureByText("Hello ${c}world and more")
try {
enterCommand("set oldundo")
typeText("cw")
typeText("Vim")
typeText("<Esc>")
assertState("Hello Vi${c}m and more")
typeText("u")
assertState("Hello Vim${c} and more")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after change line`() {
configureByText(
"""
First line
${c}Second line with text
Third line
""".trimIndent()
)
typeText("cc")
typeText("Changed line")
typeText("<Esc>")
assertState(
"""
First line
Changed lin${c}e
Third line
""".trimIndent()
)
typeText("u")
assertState(
"""
First line
${c}Second line with text
Third line
""".trimIndent()
)
}
@Test
fun `test undo after change line with oldundo`() {
configureByText(
"""
First line
${c}Second line with text
Third line
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("cc")
typeText("Changed line")
typeText("<Esc>")
assertState(
"""
First line
Changed lin${c}e
Third line
""".trimIndent()
)
typeText("u")
assertState(
"""
First line
Changed line${c}
Third line
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after change to end of line`() {
configureByText("Start ${c}middle end")
typeText("C")
typeText("new ending")
typeText("<Esc>")
assertState("Start new endin${c}g")
typeText("u")
assertState("Start ${c}middle end")
}
@Test
fun `test undo after change to end of line with oldundo`() {
configureByText("Start ${c}middle end")
try {
enterCommand("set oldundo")
typeText("C")
typeText("new ending")
typeText("<Esc>")
assertState("Start new endin${c}g")
typeText("u")
assertState("Start new ending${c}")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after change with motion`() {
configureByText("The ${c}quick brown fox")
typeText("c3w")
typeText("slow")
typeText("<Esc>")
assertState("The slo${c}w")
typeText("u")
assertState("The ${c}quick brown fox")
}
@Test
fun `test undo after change with motion with oldundo`() {
configureByText("The ${c}quick brown fox")
try {
enterCommand("set oldundo")
typeText("c3w")
typeText("slow")
typeText("<Esc>")
assertState("The slo${c}w")
typeText("u")
assertState("The slow${c}")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo change with motion and caret movement`() {
configureByText("a${c}bc(xxx)def")
typeText("ci(")
typeText("yyy")
typeText("<Esc>")
assertState("abc(yy${c}y)def")
typeText("u")
assertState("a${c}bc(xxx)def")
}
@Test
fun `test undo change with motion and caret movement with oldundo`() {
configureByText("a${c}bc(xxx)def")
try {
enterCommand("set oldundo")
typeText("ci(")
typeText("yyy")
typeText("<Esc>")
assertState("abc(yy${c}y)def")
typeText("u")
assertState("abc(yyy${c})def")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test multiple undo after sequential changes`() {
configureByText("${c}one two three")
typeText("cw")
typeText("ONE")
typeText("<Esc>")
assertState("ON${c}E two three")
typeText("w")
typeText("cw")
typeText("TWO")
typeText("<Esc>")
assertState("ONE TW${c}O three")
// Undo second change
typeText("u")
assertState("ONE ${c}two three")
// Undo first change
typeText("u")
assertState("${c}one two three")
}
@Test
fun `test multiple undo after sequential changes with oldundo`() {
configureByText("${c}one two three")
try {
enterCommand("set oldundo")
typeText("cw")
typeText("ONE")
typeText("<Esc>")
assertState("ON${c}E two three")
typeText("w")
typeText("cw")
typeText("TWO")
typeText("<Esc>")
assertState("ONE TW${c}O three")
// Undo second change
typeText("u")
assertState("ONE TWO${c} three")
// Undo first change
typeText("u")
assertState("ONE ${c} three")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo change character`() {
configureByText("a${c}bcdef")
typeText("s")
typeText("X")
typeText("<Esc>")
assertState("a${c}Xcdef")
typeText("u")
assertState("a${c}bcdef")
}
@Test
fun `test undo change character with oldundo`() {
configureByText("a${c}bcdef")
try {
enterCommand("set oldundo")
typeText("s")
typeText("X")
typeText("<Esc>")
assertState("a${c}Xcdef")
typeText("u")
assertState("aX${c}cdef")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo change multiple characters`() {
configureByText("abc${c}defghijk")
typeText("3s")
typeText("XXX")
typeText("<Esc>")
assertState("abcXX${c}Xghijk")
typeText("u")
assertState("abc${c}defghijk")
}
@Test
fun `test undo change multiple characters with oldundo`() {
configureByText("abc${c}defghijk")
try {
enterCommand("set oldundo")
typeText("3s")
typeText("XXX")
typeText("<Esc>")
assertState("abcXX${c}Xghijk")
typeText("u")
assertState("abcXXX${c}ghijk")
} finally {
enterCommand("set nooldundo")
}
}
} }

View File

@@ -47,245 +47,4 @@ class ChangeNumberDecActionTest : VimTestCase() {
Mode.NORMAL(), Mode.NORMAL(),
) )
} }
@Test
fun `test undo after decrement number`() {
configureByText("The answer is ${c}42")
typeText("<C-X>")
assertState("The answer is 4${c}1")
typeText("u")
assertState("The answer is ${c}42")
}
@Test
fun `test undo after decrement number with oldundo`() {
configureByText("The answer is ${c}42")
try {
enterCommand("set oldundo")
typeText("<C-X>")
assertState("The answer is 4${c}1")
typeText("u")
assertState("The answer is ${c}42")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after decrement number with caret move`() {
configureByText("The answer ${c}is 42")
typeText("<C-X>")
assertState("The answer is 4${c}1")
typeText("u")
assertState("The answer ${c}is 42")
}
@Test
fun `test undo after decrement number with caret move with oldundo`() {
configureByText("The answer ${c}is 42")
try {
enterCommand("set oldundo")
typeText("<C-X>")
assertState("The answer is 4${c}1")
typeText("u")
assertState("The answer ${c}is 42")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after decrement with count`() {
configureByText("Count: ${c}20")
typeText("5<C-X>")
assertState("Count: 1${c}5")
typeText("u")
assertState("Count: ${c}20")
}
@Test
fun `test undo after decrement with count with oldundo`() {
configureByText("Count: ${c}20")
try {
enterCommand("set oldundo")
typeText("5<C-X>")
assertState("Count: 1${c}5")
typeText("u")
assertState("Count: ${c}20")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after decrement negative number`() {
configureByText("Temperature: ${c}-5 degrees")
typeText("<C-X>")
assertState("Temperature: -${c}6 degrees")
typeText("u")
assertState("Temperature: ${c}-5 degrees")
}
@Test
fun `test undo after decrement negative number with oldundo`() {
configureByText("Temperature: ${c}-5 degrees")
try {
enterCommand("set oldundo")
typeText("<C-X>")
assertState("Temperature: -${c}6 degrees")
typeText("u")
assertState("Temperature: ${c}-5 degrees")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test multiple undo after sequential decrements`() {
configureByText("Value: ${c}100")
typeText("<C-X>")
assertState("Value: 9${c}9")
typeText("<C-X>")
assertState("Value: 9${c}8")
typeText("<C-X>")
assertState("Value: 9${c}7")
// Undo third decrement
typeText("u")
assertState("Value: 9${c}8")
// Undo second decrement
typeText("u")
assertState("Value: 9${c}9")
// Undo first decrement
typeText("u")
assertState("Value: ${c}100")
}
@Test
fun `test multiple undo after sequential decrements with oldundo`() {
configureByText("Value: ${c}100")
try {
enterCommand("set oldundo")
typeText("<C-X>")
assertState("Value: 9${c}9")
typeText("<C-X>")
assertState("Value: 9${c}8")
typeText("<C-X>")
assertState("Value: 9${c}7")
// Undo third decrement
typeText("u")
assertState("Value: 9${c}8")
// Undo second decrement
typeText("u")
assertState("Value: 9${c}9")
// Undo first decrement
typeText("u")
assertState("Value: ${c}100")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo decrement with visual selection`() {
configureByText("""
${c}10
20
30
""".trimIndent())
typeText("Vj<C-X>") // Visual select first two lines and decrement
assertState("""
${c}9
19
30
""".trimIndent())
typeText("u")
assertState("""
${c}10
20
30
""".trimIndent())
}
@Test
fun `test undo decrement with visual selection with oldundo`() {
configureByText("""
${c}10
20
30
""".trimIndent())
try {
enterCommand("set oldundo")
typeText("Vj<C-X>") // Visual select first two lines and decrement
assertState("""
${c}9
19
30
""".trimIndent())
typeText("u")
assertState("""
${c}10
20
30
""".trimIndent())
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo increment and decrement combination`() {
configureByText("Number: ${c}50")
typeText("<C-A>")
assertState("Number: 5${c}1")
typeText("<C-X>")
assertState("Number: 5${c}0")
typeText("<C-X>")
assertState("Number: 4${c}9")
// Undo second decrement
typeText("u")
assertState("Number: 5${c}0")
// Undo first decrement
typeText("u")
assertState("Number: 5${c}1")
// Undo increment
typeText("u")
assertState("Number: ${c}50")
}
@Test
fun `test undo increment and decrement combination with oldundo`() {
configureByText("Number: ${c}50")
try {
enterCommand("set oldundo")
typeText("<C-A>")
assertState("Number: 5${c}1")
typeText("<C-X>")
assertState("Number: 5${c}0")
typeText("<C-X>")
assertState("Number: 4${c}9")
// Undo second decrement
typeText("u")
assertState("Number: 5${c}0")
// Undo first decrement
typeText("u")
assertState("Number: 5${c}1")
// Undo increment
typeText("u")
assertState("Number: ${c}50")
} finally {
enterCommand("set nooldundo")
}
}
} }

View File

@@ -19,231 +19,4 @@ class ChangeNumberIncActionTest : VimTestCase() {
fun `test inc fancy number`() { fun `test inc fancy number`() {
doTest("<C-A>", "1${c}0X0", "10X1", Mode.NORMAL()) doTest("<C-A>", "1${c}0X0", "10X1", Mode.NORMAL())
} }
@Test
fun `test undo after increment number`() {
configureByText("The answer is ${c}42")
typeText("<C-A>")
assertState("The answer is 4${c}3")
typeText("u")
assertState("The answer is ${c}42")
}
@Test
fun `test undo after increment number with oldundo`() {
configureByText("The answer is ${c}42")
try {
enterCommand("set oldundo")
typeText("<C-A>")
assertState("The answer is 4${c}3")
typeText("u")
assertState("The answer is ${c}42")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after increment with count`() {
configureByText("Count: ${c}10")
typeText("5<C-A>")
assertState("Count: 1${c}5")
typeText("u")
assertState("Count: ${c}10")
}
@Test
fun `test undo after increment with count with oldundo`() {
configureByText("Count: ${c}10")
try {
enterCommand("set oldundo")
typeText("5<C-A>")
assertState("Count: 1${c}5")
typeText("u")
assertState("Count: ${c}10")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after increment negative number`() {
configureByText("Temperature: ${c}-5 degrees")
typeText("<C-A>")
assertState("Temperature: -${c}4 degrees")
typeText("u")
assertState("Temperature: ${c}-5 degrees")
}
@Test
fun `test undo after increment negative number with oldundo`() {
configureByText("Temperature: ${c}-5 degrees")
try {
enterCommand("set oldundo")
typeText("<C-A>")
assertState("Temperature: -${c}4 degrees")
typeText("u")
assertState("Temperature: ${c}-5 degrees")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test multiple undo after sequential increments`() {
configureByText("Value: ${c}100")
typeText("<C-A>")
assertState("Value: 10${c}1")
typeText("<C-A>")
assertState("Value: 10${c}2")
typeText("<C-A>")
assertState("Value: 10${c}3")
// Undo third increment
typeText("u")
assertState("Value: 10${c}2")
// Undo second increment
typeText("u")
assertState("Value: 10${c}1")
// Undo first increment
typeText("u")
assertState("Value: ${c}100")
}
@Test
fun `test multiple undo after sequential increments with oldundo`() {
configureByText("Value: ${c}100")
try {
enterCommand("set oldundo")
typeText("<C-A>")
assertState("Value: 10${c}1")
typeText("<C-A>")
assertState("Value: 10${c}2")
typeText("<C-A>")
assertState("Value: 10${c}3")
// Undo third increment
typeText("u")
assertState("Value: 10${c}2")
// Undo second increment
typeText("u")
assertState("Value: 10${c}1")
// Undo first increment
typeText("u")
assertState("Value: ${c}100")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo increment with visual selection`() {
configureByText(
"""
${c}10
20
30
""".trimIndent()
)
typeText("Vj<C-A>") // Visual select first two lines and increment
assertState(
"""
${c}11
21
30
""".trimIndent()
)
typeText("u")
assertState(
"""
${c}10
20
30
""".trimIndent()
)
}
@Test
fun `test undo increment with visual selection with oldundo`() {
configureByText(
"""
${c}10
20
30
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("Vj<C-A>") // Visual select first two lines and increment
assertState(
"""
${c}11
21
30
""".trimIndent()
)
typeText("u")
assertState(
"""
${c}10
20
30
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo increment octal number`() {
// OCT is disabled by default
configureByText("Octal: ${c}0777")
typeText("<C-A>")
assertState("Octal: 077${c}8")
typeText("u")
assertState("Octal: ${c}0777")
}
@Test
fun `test undo increment octal number with oldundo`() {
// OCT is disabled by default
configureByText("Octal: ${c}0777")
try {
enterCommand("set oldundo")
typeText("<C-A>")
assertState("Octal: 077${c}8")
typeText("u")
assertState("Octal: ${c}0777")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo increment hex number`() {
configureByText("Hex: ${c}0xff")
typeText("<C-A>")
assertState("Hex: 0x10${c}0")
typeText("u")
assertState("Hex: ${c}0xff")
}
@Test
fun `test undo increment hex number with oldundo`() {
configureByText("Hex: ${c}0xff")
try {
enterCommand("set oldundo")
typeText("<C-A>")
assertState("Hex: 0x10${c}0")
typeText("u")
assertState("Hex: ${c}0xff")
} finally {
enterCommand("set nooldundo")
}
}
} }

View File

@@ -130,81 +130,4 @@ class DeleteCharacterLeftActionTest : VimTestCase() {
assertVisualPosition(0, 55) assertVisualPosition(0, 55)
assertVisibleLineBounds(0, 15, 94) assertVisibleLineBounds(0, 15, 94)
} }
@Test
fun `test undo after deleting character left`() {
configureByText("foo f${c}oo")
typeText("X")
assertState("foo ${c}oo")
typeText("u")
assertState("foo f${c}oo")
}
@Test
fun `test undo after deleting character left with oldundo`() {
configureByText("foo f${c}oo")
try {
enterCommand("set oldundo")
typeText("X")
assertState("foo ${c}oo")
typeText("u")
assertState("foo f${c}oo")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after deleting multiple characters left`() {
configureByText("abcdef${c}ghijk")
typeText("3X")
assertState("abc${c}ghijk")
typeText("u")
assertState("abcdef${c}ghijk")
}
@Test
fun `test undo after deleting multiple characters left with oldundo`() {
configureByText("abcdef${c}ghijk")
try {
enterCommand("set oldundo")
typeText("3X")
assertState("abc${c}ghijk")
typeText("u")
assertState("abcdef${c}ghijk")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test multiple undo after sequential deletes`() {
configureByText("foo bar ${c}baz")
typeText("XXX")
assertState("foo b${c}baz")
typeText("u")
assertState("foo ba${c}baz")
typeText("u")
assertState("foo bar${c}baz")
typeText("u")
assertState("foo bar ${c}baz")
}
@Test
fun `test multiple undo after sequential deletes with oldundo`() {
configureByText("foo bar ${c}baz")
try {
enterCommand("set oldundo")
typeText("XXX")
assertState("foo b${c}baz")
typeText("u")
assertState("foo ba${c}baz")
typeText("u")
assertState("foo bar${c}baz")
typeText("u")
assertState("foo bar ${c}baz")
} finally {
enterCommand("set nooldundo")
}
}
} }

View File

@@ -113,31 +113,4 @@ class DeleteCharacterRightActionTest : VimTestCase() {
// type annotation // type annotation
assertVisualPosition(0, 4) assertVisualPosition(0, 4)
} }
@Test
fun `undo after deleting character`() {
configureByText("foo ${c}foo")
typeText("xx")
assertState("foo ${c}o")
typeText("u")
assertState("foo ${c}oo")
typeText("u")
assertState("foo ${c}foo")
}
@Test
fun `undo after deleting character with oldundo`() {
configureByText("foo ${c}foo")
try {
enterCommand("set oldundo")
typeText("xx")
assertState("foo ${c}o")
typeText("u")
assertState("foo ${c}oo")
typeText("u")
assertState("foo ${c}foo")
} finally {
enterCommand("set nooldundo")
}
}
} }

View File

@@ -32,230 +32,4 @@ class DeleteEndOfLineActionTest : VimTestCase() {
Mode.NORMAL(), Mode.NORMAL(),
) )
} }
@Test
fun `test undo after delete to end of line`() {
configureByText("Hello ${c}world and more text")
typeText("D")
assertState("Hello$c ")
typeText("u")
assertState("Hello ${c}world and more text")
}
@Test
fun `test undo after delete to end of line with count`() {
configureByText(
"""
First ${c}line with text
Second line
Third line
Fourth line
""".trimIndent()
)
typeText("2D")
assertState(
"""
First$c
Third line
Fourth line
""".trimIndent()
)
typeText("u")
assertState(
"""
First ${c}line with text
Second line
Third line
Fourth line
""".trimIndent()
)
}
@Test
fun `test undo after delete to end of line at different positions`() {
configureByText("abc${c}defghijk")
typeText("D")
assertState("ab${c}c")
typeText("u")
assertState("abc${c}defghijk")
// Move to different position and delete again
typeText("0")
assertState("${c}abcdefghijk")
typeText("D")
assertState("$c")
typeText("u")
assertState("${c}abcdefghijk")
}
@Test
fun `test multiple undo after sequential delete to end of line`() {
configureByText(
"""
${c}First line
Second line
Third line
""".trimIndent()
)
typeText("D")
assertState(
"""
$c
Second line
Third line
""".trimIndent()
)
typeText("j")
typeText("D")
assertState(
"""
$c
Third line
""".trimIndent()
)
// Undo second delete
typeText("u")
assertState(
"""
${c}Second line
Third line
""".trimIndent()
)
// Undo first delete
typeText("u")
assertState(
"""
${c}First line
Second line
Third line
""".trimIndent()
)
}
@Test
fun `test undo after delete to end of line with oldundo`() {
configureByText("Hello ${c}world and more text")
try {
enterCommand("set oldundo")
typeText("D")
assertState("Hello$c ")
typeText("u")
assertState("Hello ${c}world and more text")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after delete to end of line with count with oldundo`() {
configureByText(
"""
First ${c}line with text
Second line
Third line
Fourth line
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("2D")
assertState(
"""
First$c
Third line
Fourth line
""".trimIndent()
)
typeText("u")
assertState(
"""
First ${c}line with text
Second line
Third line
Fourth line
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after delete to end of line at different positions with oldundo`() {
configureByText("abc${c}defghijk")
try {
enterCommand("set oldundo")
typeText("D")
assertState("ab${c}c")
typeText("u")
assertState("abc${c}defghijk")
// Move to different position and delete again
typeText("0")
assertState("${c}abcdefghijk")
typeText("D")
assertState("$c")
typeText("u")
assertState("${c}abcdefghijk")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test multiple undo after sequential delete to end of line with oldundo`() {
configureByText(
"""
${c}First line
Second line
Third line
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("D")
assertState(
"""
$c
Second line
Third line
""".trimIndent()
)
typeText("j")
typeText("D")
assertState(
"""
$c
Third line
""".trimIndent()
)
// Undo second delete
typeText("u")
assertState(
"""
${c}Second line
Third line
""".trimIndent()
)
// Undo first delete
typeText("u")
assertState(
"""
${c}
Second line
Third line
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
} }

View File

@@ -16,7 +16,6 @@ import org.jetbrains.plugins.ideavim.VimTestCase
import org.jetbrains.plugins.ideavim.impl.OptionTest import org.jetbrains.plugins.ideavim.impl.OptionTest
import org.jetbrains.plugins.ideavim.impl.TraceOptions import org.jetbrains.plugins.ideavim.impl.TraceOptions
import org.jetbrains.plugins.ideavim.impl.VimOption import org.jetbrains.plugins.ideavim.impl.VimOption
import org.junit.jupiter.api.Test
@TraceOptions @TraceOptions
class DeleteJoinLinesSpacesActionTest : VimTestCase() { class DeleteJoinLinesSpacesActionTest : VimTestCase() {
@@ -89,280 +88,4 @@ class DeleteJoinLinesSpacesActionTest : VimTestCase() {
Mode.NORMAL(), Mode.NORMAL(),
) )
} }
@Test
fun `test undo after join lines`() {
configureByText(
"""
First line
${c}Second line
Third line
""".trimIndent()
)
typeText("J")
assertState(
"""
First line
Second line${c} Third line
""".trimIndent()
)
typeText("u")
assertState(
"""
First line
${c}Second line
Third line
""".trimIndent()
)
}
@Test
fun `test undo after join multiple lines`() {
configureByText(
"""
${c}Line 1
Line 2
Line 3
Line 4
""".trimIndent()
)
typeText("3J")
assertState(
"""
Line 1 Line 2$c Line 3
Line 4
""".trimIndent()
)
typeText("u")
assertState(
"""
${c}Line 1
Line 2
Line 3
Line 4
""".trimIndent()
)
}
@Test
fun `test undo after multiple sequential joins`() {
configureByText(
"""
${c}One
Two
Three
Four
""".trimIndent()
)
typeText("J")
assertState(
"""
One${c} Two
Three
Four
""".trimIndent()
)
typeText("J")
assertState(
"""
One Two${c} Three
Four
""".trimIndent()
)
// Undo second join
typeText("u")
assertState(
"""
One${c} Two
Three
Four
""".trimIndent()
)
// Undo first join
typeText("u")
assertState(
"""
${c}One
Two
Three
Four
""".trimIndent()
)
}
@Test
fun `test undo join with special whitespace handling`() {
configureByText(
"""
${c}foo {
bar
}
""".trimIndent()
)
typeText("J")
assertState(
"""
foo {${c} bar
}
""".trimIndent()
)
typeText("u")
assertState(
"""
${c}foo {
bar
}
""".trimIndent()
)
}
@Test
fun `test undo after join lines with oldundo`() {
configureByText(
"""
First line
${c}Second line
Third line
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("J")
assertState(
"""
First line
Second line${c} Third line
""".trimIndent()
)
typeText("u")
assertState(
"""
First line
${c}Second line
Third line
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after join multiple lines with oldundo`() {
configureByText(
"""
${c}Line 1
Line 2
Line 3
Line 4
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("3J")
assertState(
"""
Line 1 Line 2$c Line 3
Line 4
""".trimIndent()
)
typeText("u")
assertState(
"""
${c}Line 1
Line 2
Line 3
Line 4
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after multiple sequential joins with oldundo`() {
configureByText(
"""
${c}One
Two
Three
Four
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("J")
assertState(
"""
One${c} Two
Three
Four
""".trimIndent()
)
typeText("J")
assertState(
"""
One Two${c} Three
Four
""".trimIndent()
)
// Undo second join
typeText("u")
assertState(
"""
One${c} Two
Three
Four
""".trimIndent()
)
// Undo first join
typeText("u")
assertState(
"""
${c}One
Two
Three
Four
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo join with special whitespace handling with oldundo`() {
configureByText(
"""
${c}foo {
bar
}
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("J")
assertState(
"""
foo {${c} bar
}
""".trimIndent()
)
typeText("u")
assertState(
"""
${c}foo {
bar
}
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
} }

View File

@@ -257,298 +257,4 @@ class DeleteMotionActionTest : VimTestCase() {
typeText("dd") typeText("dd")
assertStatusLineCleared() assertStatusLineCleared()
} }
@Test
fun `test undo after delete motion with word`() {
configureByText("Hello ${c}world and more text")
typeText("dw")
assertState("Hello ${c}and more text")
typeText("u")
assertState("Hello ${c}world and more text")
}
@Test
fun `test undo after delete motion with word with oldundo`() {
configureByText("Hello ${c}world and more text")
try {
enterCommand("set oldundo")
typeText("dw")
assertState("Hello ${c}and more text")
typeText("u")
assertState("Hello ${c}world and more text")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after delete line`() {
configureByText(
"""
First line
${c}Second line
Third line
""".trimIndent()
)
typeText("dd")
assertState(
"""
First line
${c}Third line
""".trimIndent()
)
typeText("u")
assertState(
"""
First line
${c}Second line
Third line
""".trimIndent()
)
}
@Test
fun `test undo after delete line with oldundo`() {
configureByText(
"""
First line
${c}Second line
Third line
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("dd")
assertState(
"""
First line
${c}Third line
""".trimIndent()
)
typeText("u")
assertState(
"""
First line
${c}Second line
Third line
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after delete multiple lines`() {
configureByText(
"""
First line
${c}Second line
Third line
Fourth line
Fifth line
""".trimIndent()
)
typeText("3dd")
assertState(
"""
First line
${c}Fifth line
""".trimIndent()
)
typeText("u")
assertState(
"""
First line
${c}Second line
Third line
Fourth line
Fifth line
""".trimIndent()
)
}
@Test
fun `test undo after delete multiple lines with oldundo`() {
configureByText(
"""
First line
${c}Second line
Third line
Fourth line
Fifth line
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("3dd")
assertState(
"""
First line
${c}Fifth line
""".trimIndent()
)
typeText("u")
assertState(
"""
First line
${c}Second line
Third line
Fourth line
Fifth line
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after delete with different motions`() {
configureByText("The ${c}quick brown fox jumps")
typeText("d3w")
assertState("The ${c}jumps")
typeText("u")
assertState("The ${c}quick brown fox jumps")
// Test with $ motion
typeText("d$")
assertState("The$c ")
typeText("u")
assertState("The ${c}quick brown fox jumps")
// Test with 0 motion
typeText("d0")
assertState("${c}quick brown fox jumps")
typeText("u")
assertState("The ${c}quick brown fox jumps")
}
@Test
fun `test undo after delete with different motions with oldundo`() {
configureByText("The ${c}quick brown fox jumps")
try {
enterCommand("set oldundo")
typeText("d3w")
assertState("The ${c}jumps")
typeText("u")
assertState("The ${c}quick brown fox jumps")
// Test with $ motion
typeText("d$")
assertState("The$c ")
typeText("u")
assertState("The ${c}quick brown fox jumps")
// Test with 0 motion
typeText("d0")
assertState("${c}quick brown fox jumps")
typeText("u")
assertState("The ${c}quick brown fox jumps")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo delete with motion that includes caret movement`() {
configureByText("a${c}bc(xxx)def")
typeText("di(")
assertState("abc(${c})def")
typeText("u")
assertState("a${c}bc(xxx)def")
}
@Test
fun `test undo delete with motion that includes caret movement with oldundo`() {
configureByText("a${c}bc(xxx)def")
try {
enterCommand("set oldundo")
typeText("di(")
assertState("abc(${c})def")
typeText("u")
assertState("a${c}bc(xxx)def")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after delete to mark`() {
configureByText(
"""
Line 1
Li${c}ne 2
Line 3
Line 4
""".trimIndent()
)
typeText("ma") // Set mark a
typeText("jj")
assertState(
"""
Line 1
Line 2
Line 3
Li${c}ne 4
""".trimIndent()
)
typeText("d'a") // Delete to mark a
assertState(
"""
${c}Line 1
""".trimIndent()
)
typeText("u")
assertState(
"""
Line 1
Line 2
Line 3
Li${c}ne 4
""".trimIndent()
)
}
@Test
fun `test undo after delete to mark with oldundo`() {
configureByText(
"""
Line 1
Li${c}ne 2
Line 3
Line 4
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("ma") // Set mark a
typeText("jj")
assertState(
"""
Line 1
Line 2
Line 3
Li${c}ne 4
""".trimIndent()
)
typeText("d'a") // Delete to mark a
assertState(
"""
${c}Line 1
""".trimIndent()
)
typeText("u")
assertState(
"""
Line 1
Line 2
Line 3
Li${c}ne 4
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
} }

View File

@@ -52,76 +52,4 @@ class InsertDeleteInsertedTextActionTest : VimTestCase() {
Mode.NORMAL(), Mode.NORMAL(),
) )
} }
@Test
fun `test undo after delete inserted text in insert mode`() {
configureByText("Hello ${c}world")
typeText("i")
typeText("beautiful ")
assertState("Hello beautiful ${c}world")
assertMode(Mode.INSERT)
typeText("<C-U>")
assertState("Hello ${c}world")
assertMode(Mode.INSERT)
typeText("<Esc>")
typeText("u")
assertState("Hello ${c}world")
}
@Test
fun `test undo after delete inserted text in insert mode with oldundo`() {
configureByText("Hello ${c}world")
try {
enterCommand("set oldundo")
typeText("i")
typeText("beautiful ")
assertState("Hello beautiful ${c}world")
assertMode(Mode.INSERT)
typeText("<C-U>")
assertState("Hello ${c}world")
assertMode(Mode.INSERT)
typeText("<Esc>")
typeText("u")
assertState("Hello ${c}world")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo preserves text before insertion point`() {
configureByText("Start${c}End")
typeText("i")
typeText(" Middle ")
assertState("Start Middle ${c}End")
assertMode(Mode.INSERT)
typeText("<C-U>")
assertState("Start${c}End")
assertMode(Mode.INSERT)
typeText("<Esc>")
assertState("Star${c}tEnd")
typeText("u")
assertState("Start${c}End")
}
@Test
fun `test undo preserves text before insertion point with oldundo`() {
configureByText("Start${c}End")
try {
enterCommand("set oldundo")
typeText("i")
typeText(" Middle ")
assertState("Start Middle ${c}End")
assertMode(Mode.INSERT)
typeText("<C-U>")
assertState("Start${c}End")
assertMode(Mode.INSERT)
typeText("<Esc>")
assertState("Star${c}tEnd")
typeText("u")
assertState("Start${c}End")
} finally {
enterCommand("set nooldundo")
}
}
} }

View File

@@ -15,6 +15,7 @@ import com.maddyhome.idea.vim.state.mode.Mode
import org.jetbrains.plugins.ideavim.SkipNeovimReason import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestWithoutNeovim import org.jetbrains.plugins.ideavim.TestWithoutNeovim
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue import kotlin.test.assertTrue
@@ -84,6 +85,7 @@ class InsertNewLineAboveActionTest : VimTestCase() {
doTest("O", before, after, Mode.INSERT) doTest("O", before, after, Mode.INSERT)
} }
@Disabled("Action execution in tests is broken for 2024.2")
@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

@@ -12,6 +12,7 @@ import com.maddyhome.idea.vim.state.mode.Mode
import org.jetbrains.plugins.ideavim.SkipNeovimReason import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestWithoutNeovim import org.jetbrains.plugins.ideavim.TestWithoutNeovim
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
class InsertNewLineBelowActionTest : VimTestCase() { class InsertNewLineBelowActionTest : VimTestCase() {
@@ -79,6 +80,7 @@ class InsertNewLineBelowActionTest : VimTestCase() {
doTest("o", before, after, Mode.INSERT) doTest("o", before, after, Mode.INSERT)
} }
@Disabled("Action execution in tests is broken for 2024.2")
@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

View File

@@ -17,7 +17,7 @@ import org.jetbrains.plugins.ideavim.TestWithoutNeovim
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
class VisualInsertActionTest : VimTestCase() { class VisualBlockInsertActionTest : VimTestCase() {
// VIM-1379 |CTRL-V| |j| |v_b_I| // VIM-1379 |CTRL-V| |j| |v_b_I|
@TestWithoutNeovim(SkipNeovimReason.VISUAL_BLOCK_MODE) @TestWithoutNeovim(SkipNeovimReason.VISUAL_BLOCK_MODE)
@Test @Test
@@ -101,70 +101,28 @@ class VisualInsertActionTest : VimTestCase() {
) )
} }
@TestWithoutNeovim(SkipNeovimReason.VISUAL_BLOCK_MODE)
@Test @Test
fun `test insert in non-block visual within single line`() { fun `test insert in non block mode`() {
val before = """ doTest(
| A ${c}Discovery listOf("vwIHello<esc>"),
"""
${c}A Discovery
| I ${c}found it in a legendary land ${c}I found it in a legendary land
| all rocks and lavender and tufted grass, all rocks and ${c}lavender and tufted grass,
| where it was settled on some sodden sand where it was settled on some sodden sand
| hard by the torrent of a mountain pass. hard by the torrent of a mountain pass.
""".trimMargin() """.trimIndent(),
val after = """ """
|Hell${c}o A Discovery Hell${c}oA Discovery
|Hell${c}o I found it in a legendary land Hell${c}oI found it in a legendary land
| all rocks and lavender and tufted grass, Hell${c}oall rocks and lavender and tufted grass,
| where it was settled on some sodden sand where it was settled on some sodden sand
| hard by the torrent of a mountain pass. hard by the torrent of a mountain pass.
""".trimMargin() """.trimIndent(),
doTest(listOf($$"v$IHello<esc>"), before, after) )
doTest(listOf("VIHello<esc>"), before, after)
}
@Test
fun `test insert in non-block visual spanning multiple lines down`() {
val before = """
| A ${c}Discovery
| I ${c}found it in a legendary land
| all rocks and lavender and tufted grass,
| where it was settled on some sodden sand
| hard by the torrent of a mountain pass.
""".trimMargin()
val after = """
|Hell${c}o A Discovery
|Hell${c}o I found it in a legendary land
| all rocks and lavender and tufted grass,
| where it was settled on some sodden sand
| hard by the torrent of a mountain pass.
""".trimMargin()
doTest(listOf("vjIHello<esc>"), before, after)
doTest(listOf("VjIHello<esc>"), before, after)
}
@Test
fun `test insert in non-block visual spanning multiple lines up`() {
val before = """
| A Discovery
| I found it in a legendary land
| all rocks and lavender and tufted grass${c},
| where it was settled on some sodden sand
| hard ${c}by the torrent of a mountain pass.
""".trimMargin()
val after = """
| A Discovery
| I found it in a legendary landHell${c}o
| all rocks and lavender and tufted grass,
| whereHell${c}o it was settled on some sodden sand
| hard by the torrent of a mountain pass.
""".trimMargin()
doTest(listOf("vkIHello<esc>"), before, after)
doTest(listOf("VkIHello<esc>"), before, after)
} }
@TestWithoutNeovim(SkipNeovimReason.VISUAL_BLOCK_MODE) @TestWithoutNeovim(SkipNeovimReason.VISUAL_BLOCK_MODE)

View File

@@ -136,266 +136,4 @@ class ShiftLeftTest : VimTestCase() {
""".trimIndent(), """.trimIndent(),
) )
} }
@Test
fun `test undo after shift left single line`() {
configureByText(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
typeText("<<")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
typeText("u")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
}
@Test
fun `test undo after shift left with motion`() {
configureByText(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
typeText("<2j") // Shift left 3 lines
assertState(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
typeText("u")
assertState(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
}
@Test
fun `test undo after shift left visual mode`() {
configureByText(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
typeText("Vj<") // Visual select 2 lines and shift left
assertState(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
typeText("u")
assertState(
"""
func main() {
${c} line1()
line2()
line3()
}
""".trimIndent()
)
}
@Test
fun `test undo shift left in insert mode`() {
configureByText(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
typeText("i<C-D>")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
typeText("<Esc>")
typeText("u")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
}
@Test
fun `test undo after shift left single line with oldundo`() {
configureByText(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("<<")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
typeText("u")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after shift left with motion with oldundo`() {
configureByText(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("<2j") // Shift left 3 lines
assertState(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
typeText("u")
assertState(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after shift left visual mode with oldundo`() {
configureByText(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("Vj<") // Visual select 2 lines and shift left
assertState(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
typeText("u")
assertState(
"""
func main() {
${c} line1()
line2()
line3()
}
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo shift left in insert mode with oldundo`() {
configureByText(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("i<C-D>")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
typeText("<Esc>")
typeText("u")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
} }

View File

@@ -208,416 +208,4 @@ class ShiftRightTest : VimTestCase() {
""".trimIndent(), """.trimIndent(),
) )
} }
@Test
fun `test undo after shift right single line`() {
configureByText(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
typeText(">>")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
typeText("u")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
}
@Test
fun `test undo after shift right with motion`() {
configureByText(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
typeText(">2j") // Shift right 3 lines
assertState(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
typeText("u")
assertState(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
}
@Test
fun `test undo after shift right visual mode`() {
configureByText(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
typeText("Vj>") // Visual select 2 lines and shift right
assertState(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
typeText("u")
assertState(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
}
@Test
fun `test multiple undo after sequential shifts right`() {
configureByText("${c}unindented line")
typeText(">>")
assertState(" ${c}unindented line")
typeText(">>")
assertState(" ${c}unindented line")
typeText(">>")
assertState(" ${c}unindented line")
// Undo third shift
typeText("u")
assertState(" ${c}unindented line")
// Undo second shift
typeText("u")
assertState(" ${c}unindented line")
// Undo first shift
typeText("u")
assertState("${c}unindented line")
}
@Test
fun `test undo shift right in insert mode`() {
configureByText(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
typeText("i<C-T>")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
typeText("<Esc>")
typeText("u")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
}
@Test
fun `test undo shift right and left combination`() {
configureByText(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
typeText(">>") // Shift right
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
typeText("<<") // Shift left
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
// Undo shift left
typeText("u")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
// Undo shift right
typeText("u")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
}
@Test
fun `test undo after shift right single line with oldundo`() {
configureByText(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText(">>")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
typeText("u")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after shift right with motion with oldundo`() {
configureByText(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText(">2j") // Shift right 3 lines
assertState(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
typeText("u")
assertState(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after shift right visual mode with oldundo`() {
configureByText(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("Vj>") // Visual select 2 lines and shift right
assertState(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
typeText("u")
assertState(
"""
func main() {
${c}line1()
line2()
line3()
}
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test multiple undo after sequential shifts right with oldundo`() {
configureByText("${c}unindented line")
try {
enterCommand("set oldundo")
typeText(">>")
assertState(" ${c}unindented line")
typeText(">>")
assertState(" ${c}unindented line")
typeText(">>")
assertState(" ${c}unindented line")
// Undo third shift
typeText("u")
assertState(" ${c}unindented line")
// Undo second shift
typeText("u")
assertState(" ${c}unindented line")
// Undo first shift
typeText("u")
assertState("${c}unindented line")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo shift right in insert mode with oldundo`() {
configureByText(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("i<C-T>")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
typeText("<Esc>")
typeText("u")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo shift right and left combination with oldundo`() {
configureByText(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText(">>") // Shift right
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
typeText("<<") // Shift left
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
// Undo shift left
typeText("u")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
// Undo shift right
typeText("u")
assertState(
"""
func main() {
${c}println("Hello")
}
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
} }

View File

@@ -9,6 +9,7 @@
package org.jetbrains.plugins.ideavim.action.copy package org.jetbrains.plugins.ideavim.action.copy
import com.intellij.notification.ActionCenter import com.intellij.notification.ActionCenter
import com.intellij.notification.EventLog
import com.intellij.notification.Notification import com.intellij.notification.Notification
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
@@ -80,7 +81,7 @@ class IdeaPutNotificationsTest : VimTestCase() {
kotlin.test.assertTrue(notifications.isEmpty() || notifications.last().isExpired || OptionConstants.clipboard_ideaput !in notifications.last().content) kotlin.test.assertTrue(notifications.isEmpty() || notifications.last().isExpired || OptionConstants.clipboard_ideaput !in notifications.last().content)
} }
private fun notifications(): List<Notification> { private fun notifications(): MutableList<Notification> {
return ActionCenter.getNotifications(fixture.project) return ActionCenter.getNotifications(fixture.project)
} }
@@ -104,7 +105,7 @@ class IdeaPutNotificationsTest : VimTestCase() {
} }
typeText(injector.parser.parseKeys("p")) typeText(injector.parser.parseKeys("p"))
val notifications = ActionCenter.getNotifications(fixture.project) val notifications = EventLog.getLogModel(fixture.project).notifications
kotlin.test.assertTrue(notifications.isEmpty() || notifications.last().isExpired || OptionConstants.clipboard_ideaput !in notifications.last().content) kotlin.test.assertTrue(notifications.isEmpty() || notifications.last().isExpired || OptionConstants.clipboard_ideaput !in notifications.last().content)
} }

View File

@@ -216,294 +216,4 @@ class PutTestAfterCursorActionTest : VimTestCase() {
) )
} }
} }
@Test
fun `test undo after put after cursor`() {
configureByText("Hello ${c}world")
typeText("yy")
typeText("p")
assertState(
"""
Hello world
${c}Hello world
""".trimIndent()
)
typeText("u")
assertState("Hello ${c}world")
}
@Test
fun `test undo after put character after cursor`() {
configureByText("abc${c}def")
typeText("yl") // Yank 'd'
typeText("h") // Move left
assertState("ab${c}cdef")
typeText("p")
assertState("abc${c}ddef")
typeText("u")
assertState("ab${c}cdef")
}
@Test
fun `test undo after put word after cursor`() {
configureByText("The ${c}quick brown fox")
typeText("yiw") // Yank "quick"
typeText("w") // Move to "brown"
assertState("The quick ${c}brown fox")
typeText("p")
assertState("The quick bquic${c}krown fox")
typeText("u")
assertState("The quick ${c}brown fox")
}
@Test
fun `test multiple undo after sequential puts after cursor`() {
configureByText("${c}Hello")
typeText("yy")
typeText("p")
assertState(
"""
Hello
${c}Hello
""".trimIndent()
)
typeText("p")
assertState(
"""
Hello
Hello
${c}Hello
""".trimIndent()
)
// Undo second put
typeText("u")
assertState(
"""
Hello
${c}Hello
""".trimIndent()
)
// Undo first put
typeText("u")
assertState(
"""
${c}Hello
""".trimIndent()
)
}
@Test
fun `test undo put and move cursor`() {
configureByText("${c}abc def")
typeText("yiw") // Yank "abc"
typeText("w") // Move to "def"
assertState("abc ${c}def")
typeText("gp") // Put and move cursor after pasted text
assertState("abc dabc${c}ef")
typeText("u")
assertState("abc ${c}def")
}
@Test
fun `test undo put visual block after cursor`() {
configureByText(
"""
${c}abc
def
ghi
""".trimIndent()
)
typeText("<C-V>jjl") // Visual block select first 2 columns of all lines
typeText("y")
typeText("$")
assertState(
"""
ab${c}c
def
ghi
""".trimIndent()
)
typeText("p")
assertState(
"""
abc${c}ab
defde
ghigh
""".trimIndent()
)
typeText("u")
assertState(
"""
ab${c}c
def
ghi
""".trimIndent()
)
}
@Test
fun `test undo after put after cursor with oldundo`() {
configureByText("Hello ${c}world")
try {
enterCommand("set oldundo")
typeText("yy")
typeText("p")
assertState(
"""
Hello world
${c}Hello world
""".trimIndent()
)
typeText("u")
assertState("Hello ${c}world")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after put character after cursor with oldundo`() {
configureByText("abc${c}def")
try {
enterCommand("set oldundo")
typeText("yl") // Yank 'd'
typeText("h") // Move left
assertState("ab${c}cdef")
typeText("p")
assertState("abc${c}ddef")
typeText("u")
assertState("ab${c}cdef")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after put word after cursor with oldundo`() {
configureByText("The ${c}quick brown fox")
try {
enterCommand("set oldundo")
typeText("yiw") // Yank "quick"
typeText("w") // Move to "brown"
assertState("The quick ${c}brown fox")
typeText("p")
assertState("The quick bquic${c}krown fox")
typeText("u")
assertState("The quick ${c}brown fox")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test multiple undo after sequential puts after cursor with oldundo`() {
configureByText("${c}Hello")
try {
enterCommand("set oldundo")
typeText("yy")
typeText("p")
assertState(
"""
Hello
${c}Hello
""".trimIndent()
)
typeText("p")
assertState(
"""
Hello
Hello
${c}Hello
""".trimIndent()
)
// Undo second put
typeText("u")
assertState(
"""
Hello
${c}Hello
""".trimIndent()
)
// Undo first put
typeText("u")
assertState(
"""
${c}Hello
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo put and move cursor with oldundo`() {
configureByText("${c}abc def")
try {
enterCommand("set oldundo")
typeText("yiw") // Yank "abc"
typeText("w") // Move to "def"
assertState("abc ${c}def")
typeText("gp") // Put and move cursor after pasted text
assertState("abc dabc${c}ef")
typeText("u")
assertState("abc ${c}def")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo put visual block after cursor with oldundo`() {
configureByText(
"""
${c}abc
def
ghi
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("<C-V>jjl") // Visual block select first 2 columns of all lines
typeText("y")
typeText("$")
assertState(
"""
ab${c}c
def
ghi
""".trimIndent()
)
typeText("p")
assertState(
"""
abc${c}ab
defde
ghigh
""".trimIndent()
)
typeText("u")
assertState(
"""
ab${c}c
def
ghi
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
} }

View File

@@ -56,257 +56,4 @@ class PutTextBeforeCursorActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
assertState(after) assertState(after)
} }
@Test
fun `test undo after put before cursor`() {
configureByText("Hello ${c}world")
typeText("yy")
typeText("P")
assertState(
"""
${c}Hello world
Hello world
""".trimIndent()
)
typeText("u")
assertState("Hello ${c}world")
}
@Test
fun `test undo after put character before cursor`() {
configureByText("abc${c}def")
typeText("yl") // Yank 'd'
typeText("h") // Move left
assertState("ab${c}cdef")
typeText("P")
assertState("ab${c}dcdef")
typeText("u")
assertState("ab${c}cdef")
}
@Test
fun `test undo after put word before cursor`() {
configureByText("The ${c}quick brown fox")
typeText("yiw") // Yank "quick"
typeText("w") // Move to "brown"
assertState("The quick ${c}brown fox")
typeText("P")
assertState("The quick quic${c}kbrown fox")
typeText("u")
assertState("The quick ${c}brown fox")
}
@Test
fun `test multiple undo after sequential puts`() {
configureByText("${c}Hello")
typeText("yy")
typeText("P")
assertState(
"""
${c}Hello
Hello
""".trimIndent()
)
typeText("P")
assertState(
"""
${c}Hello
Hello
Hello
""".trimIndent()
)
// Undo second put
typeText("u")
assertState(
"""
${c}Hello
Hello
""".trimIndent()
)
// Undo first put
typeText("u")
assertState(
"""
${c}Hello
""".trimIndent()
)
}
@Test
fun `test undo put visual block`() {
configureByText(
"""
${c}abc
def
ghi
""".trimIndent()
)
typeText("<C-V>jjl") // Visual block select first 2 columns of all lines
typeText("y")
typeText("$")
assertState(
"""
ab${c}c
def
ghi
""".trimIndent()
)
typeText("P")
assertState(
"""
ab${c}abc
dedef
ghghi
""".trimIndent()
)
typeText("u")
assertState(
"""
ab${c}c
def
ghi
""".trimIndent()
)
}
@Test
fun `test undo after put before cursor with oldundo`() {
configureByText("Hello ${c}world")
try {
enterCommand("set oldundo")
typeText("yy")
typeText("P")
assertState(
"""
${c}Hello world
Hello world
""".trimIndent()
)
typeText("u")
assertState("Hello ${c}world")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after put character before cursor with oldundo`() {
configureByText("abc${c}def")
try {
enterCommand("set oldundo")
typeText("yl") // Yank 'd'
typeText("h") // Move left
assertState("ab${c}cdef")
typeText("P")
assertState("ab${c}dcdef")
typeText("u")
assertState("ab${c}cdef")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo after put word before cursor with oldundo`() {
configureByText("The ${c}quick brown fox")
try {
enterCommand("set oldundo")
typeText("yiw") // Yank "quick"
typeText("w") // Move to "brown"
assertState("The quick ${c}brown fox")
typeText("P")
assertState("The quick quic${c}kbrown fox")
typeText("u")
assertState("The quick ${c}brown fox")
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test multiple undo after sequential puts with oldundo`() {
configureByText("${c}Hello")
try {
enterCommand("set oldundo")
typeText("yy")
typeText("P")
assertState(
"""
${c}Hello
Hello
""".trimIndent()
)
typeText("P")
assertState(
"""
${c}Hello
Hello
Hello
""".trimIndent()
)
// Undo second put
typeText("u")
assertState(
"""
${c}Hello
Hello
""".trimIndent()
)
// Undo first put
typeText("u")
assertState(
"""
${c}Hello
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test undo put visual block with oldundo`() {
configureByText(
"""
${c}abc
def
ghi
""".trimIndent()
)
try {
enterCommand("set oldundo")
typeText("<C-V>jjl") // Visual block select first 2 columns of all lines
typeText("y")
typeText("$")
assertState(
"""
ab${c}c
def
ghi
""".trimIndent()
)
typeText("P")
assertState(
"""
ab${c}abc
dedef
ghghi
""".trimIndent()
)
typeText("u")
assertState(
"""
ab${c}c
def
ghi
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
} }

View File

@@ -136,6 +136,7 @@ class MotionDownActionTest : VimTestCase() {
} }
@Test @Test
@Disabled
fun `test motion down in visual block mode with dollar motion2`() { fun `test motion down in visual block mode with dollar motion2`() {
val keys = "i<C-O>d<ESC>" val keys = "i<C-O>d<ESC>"
val before = """ val before = """

View File

@@ -9,10 +9,12 @@
package org.jetbrains.plugins.ideavim.action.motion.updown package org.jetbrains.plugins.ideavim.action.motion.updown
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
class MotionUpCtrlPAction : VimTestCase() { class MotionUpCtrlPAction : VimTestCase() {
@Test @Test
@Disabled("This one should be fixed")
fun `test last column empty`() { fun `test last column empty`() {
val keys = "o<Esc><End><C-P>" val keys = "o<Esc><End><C-P>"
val before = """ val before = """

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