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

Compare commits

..

200 Commits

Author SHA1 Message Date
0f0a73c139 Set plugin version to chylex-49 2025-06-10 02:58:25 +02:00
9eccf626db Make g0/g^/g$ work with soft wraps 2025-06-10 02:58:15 +02:00
f53f980a01 Make gj/gk jump over soft wraps 2025-06-10 01:43:13 +02:00
54e3f7053c Make camelCase motions adjust based on direction of visual selection 2025-06-10 01:43:13 +02:00
c7a8f6b4aa Make search highlights temporary 2025-06-10 01:43:13 +02:00
f680f0b2cd Exit insert mode after refactoring 2025-06-10 01:43:13 +02:00
10131a8d57 Add action to run last macro in all opened files 2025-06-10 01:43:13 +02:00
f5b194002c Stop macro execution after a failed search 2025-06-10 01:43:13 +02:00
41e4adbd6a Revert per-caret registers 2025-06-10 01:43:11 +02:00
191bf9967e Apply scrolloff after executing native IDEA actions 2025-06-10 01:42:50 +02:00
ed84d20d4a Stay on same line after reindenting 2025-06-10 01:42:50 +02:00
3e829b8949 Update search register when using f/t 2025-06-10 01:42:50 +02:00
28b511131b Automatically add unambiguous imports after running a macro 2025-06-10 01:42:50 +02:00
cc2a8d25e2 Fix(VIM-3179): Respect virtual space below editor (imperfectly) 2025-06-10 01:42:50 +02:00
fb9f83821b Fix(VIM-3178): Workaround to support "Jump to Source" action mapping 2025-06-10 01:42:50 +02:00
fcb9fd8169 Add support for count for visual and line motion surround 2025-06-10 01:42:50 +02:00
8793889f05 Fix vim-surround not working with multiple cursors
Fixes multiple cursors with vim-surround commands `cs, ds, S` (but not `ys`).
2025-06-10 01:42:50 +02:00
f294059e81 Fix(VIM-696) Restore visual mode after undo/redo, and disable incompatible actions 2025-06-10 01:42:50 +02:00
c7a9fbf374 Respect count with <Action> mappings 2025-06-10 01:42:50 +02:00
9b04739f15 Change matchit plugin to use HTML patterns in unrecognized files 2025-06-10 01:42:50 +02:00
f68c56b968 Reset insert mode when switching active editor 2025-06-10 01:42:50 +02:00
0282d99692 Remove notifications about configuration options 2025-06-10 01:42:49 +02:00
03be3e1be2 Remove update checker 2025-06-10 01:42:49 +02:00
7c233f5e1a Set custom plugin version 2025-06-10 01:42:49 +02:00
IdeaVim Bot
ef2d87ff6b Add Thomas Canava to contributors list 2025-06-07 09:02:03 +00:00
Alex Plate
75cd79312c Which-Key plugin don't use VimShortcutKeyAction anymore, so we can hide it
The change was here: 2a1191a260
2025-06-06 17:02:35 +03:00
Thomas Canava
b868e0cb81 test: Update tests to match new keys 2025-06-06 15:34:44 +03:00
Thomas Canava
30c972ee1e fix: Vim macro not working with arrows 2025-06-06 15:34:44 +03:00
761f6f5fb9 Implement pumvisible() function 2025-06-06 15:21:26 +03:00
Matt Ellis
1e3738314a Add support for := to print line number
Fixes VIM-3921
2025-06-06 15:15:13 +03:00
dependabot[bot]
f47388175b Bump org.junit.jupiter:junit-jupiter-api from 5.12.2 to 5.13.0
Bumps [org.junit.jupiter:junit-jupiter-api](https://github.com/junit-team/junit5) from 5.12.2 to 5.13.0.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.12.2...r5.13.0)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter-api
  dependency-version: 5.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-04 19:40:36 +03:00
dependabot[bot]
779de5e29c Bump org.junit.jupiter:junit-jupiter-params from 5.12.2 to 5.13.0
Bumps [org.junit.jupiter:junit-jupiter-params](https://github.com/junit-team/junit5) from 5.12.2 to 5.13.0.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.12.2...r5.13.0)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter-params
  dependency-version: 5.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-04 19:23:51 +03:00
dependabot[bot]
1f0cca17cf Bump org.junit.jupiter:junit-jupiter from 5.12.2 to 5.13.0
Bumps [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5) from 5.12.2 to 5.13.0.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.12.2...r5.13.0)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter
  dependency-version: 5.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-04 19:06:42 +03:00
dependabot[bot]
d09592824c Bump org.junit.jupiter:junit-jupiter-engine from 5.12.2 to 5.13.0
Bumps [org.junit.jupiter:junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.12.2 to 5.13.0.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.12.2...r5.13.0)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter-engine
  dependency-version: 5.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-04 19:06:27 +03:00
dependabot[bot]
725d60a56e Bump org.junit.vintage:junit-vintage-engine from 5.12.2 to 5.13.0
Bumps [org.junit.vintage:junit-vintage-engine](https://github.com/junit-team/junit5) from 5.12.2 to 5.13.0.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.12.2...r5.13.0)

---
updated-dependencies:
- dependency-name: org.junit.vintage:junit-vintage-engine
  dependency-version: 5.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-04 19:06:01 +03:00
Alex Plate
77a106f9c6 Throw ProcessCanceledException instead of silently ignoring it in VimTypedActionHandler
This is a requirement from the platform
2025-05-30 19:41:41 +03:00
Alex Plate
1f66bdf5ed Refactor tests in ChangeActionTest.kt for better readability by reformatting doTest calls and improving code style consistency. 2025-05-30 15:12:43 +03:00
Alex Plate
7c4cfe44ae Remove unused MutableBoolean data class from KeyHandler 2025-05-30 14:20:25 +03:00
Alex Plate
b58c1a42d2 Remove unused mappingCompleted parameter from handleKey methods
This updates method signatures and removes the redundant parameter throughout the codebase. Adds a deprecated overload for backward compatibility.
2025-05-30 14:16:40 +03:00
Alex Plate
02fab15aa9 Add tests that verify the behaviour with oldundo flag 2025-05-28 16:45:06 +03:00
Alex Plate
6c9d85cce0 Add a big number of undo tests for macros
This is a part of work for VIM-3935. Since we'll have to change the `CommandProcessor` execution, this may affect the "undo" command behavior. To ensure the stability, we add undo tests before making a refactoring.
2025-05-28 12:40:56 +03:00
Alex Plate
360080e8c0 Enable some disabled tests 2025-05-28 12:14:08 +03:00
Alex Plate
e41831798e Add a big number of undo tests for ex commands
This is a part of work for VIM-3935. Since we'll have to change the `CommandProcessor` execution, this may affect the "undo" command behavior. To ensure the stability, we add undo tests before making a refactoring.
2025-05-28 11:48:38 +03:00
Alex Plate
c93c9e539c Add a big number of undo tests
This is a part of work for VIM-3935. Since we'll have to change the `CommandProcessor` execution, this may affect the "undo" command behavior. To ensure the stability, we add undo tests before making a refactoring.
2025-05-28 09:46:05 +03:00
Alex Plate
149e3f92ae Update .gitignore to exclude local Claude settings file 2025-05-28 09:40:53 +03:00
Alex Plate
233bad0070 Turn off TeamCity emulation when running tests locally
IJ platform runs additional project leak checks when it detects teamcity run. It was quite complicated to understand why tests were failing on TC, but not locally, so I decided to enable TeamCity emulation to have these checks locally.

However, it turned out that in addition to that, an IJ platform also collects CPU statistics on TeamCity, which may take around a minute. This dramatically affects the performance of local execution. So, this property is turned off.
2025-05-28 09:38:54 +03:00
Alex Plate
b44308c9ef Remove skipping of command execution if it happens not on the EDT
This requirement was a fix for VIM-318 introduced in ac654d70fa.
However, now we always run IdeaVim on EDT. Also, this check prevents the migration of IdeaVim to the background thread: VIM-3935
2025-05-27 18:30:16 +03:00
Alex Plate
8c612afed6 VIM-3929: Re-enable IdeaVim in diff editors 2025-05-27 13:07:43 +03:00
Alex Plate
2c057e937a Fix(VIM-3929): IdeaVim is disabled in non-file based editors of IDE
This change should fix issues in Rider evaluate window and other places.

However, we may face small issues as IdeaVim will be disabled in more places than it used to be. However, this approach looks safer as before that we were disabling some random keys
2025-05-23 19:34:17 +03:00
Alex Plate
2548008116 Add missing switches to EDT during test 2025-05-23 17:14:26 +03:00
Alex Plate
c43eb1212e Bring back CommandState to fix the compatibility with the external plugins 2025-05-23 16:53:01 +03:00
Alex Plate
4c11399b43 Bring the compatibility with the latest EAP version
Removed a single test on JSON. There are always problems with the json plugin in IJ. In 2025.2 EAP it was unbundled.
2025-05-23 16:48:37 +03:00
Alex Plate
18f3ba6fe1 Rethrow ProcessCancelledException 2025-05-23 16:22:04 +03:00
Alex Plate
2435136734 Remove vimscript roadmap file 2025-05-23 16:00:37 +03:00
Alex Plate
4d2a7df6b4 Cleanup readme file 2025-05-23 13:19:46 +03:00
dependabot[bot]
b04e90e93c Bump org.jetbrains.intellij.platform from 2.5.0 to 2.6.0
Bumps org.jetbrains.intellij.platform from 2.5.0 to 2.6.0.

---
updated-dependencies:
- dependency-name: org.jetbrains.intellij.platform
  dependency-version: 2.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-21 19:02:43 +03:00
dependabot[bot]
4c88359f46 Bump org.eclipse.jgit:org.eclipse.jgit.ssh.apache
Bumps [org.eclipse.jgit:org.eclipse.jgit.ssh.apache](https://github.com/eclipse-jgit/jgit) from 7.2.0.202503040940-r to 7.2.1.202505142326-r.
- [Commits](https://github.com/eclipse-jgit/jgit/compare/v7.2.0.202503040940-r...v7.2.1.202505142326-r)

---
updated-dependencies:
- dependency-name: org.eclipse.jgit:org.eclipse.jgit.ssh.apache
  dependency-version: 7.2.1.202505142326-r
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-21 19:02:23 +03:00
Alex Plate
8e136bf769 Update list of supported IDEs 2025-05-21 12:31:29 +03:00
Alex Plate
be73e662bd Change wording to JetBrains IDEs on the readme
This is a new proper name for the ide family
2025-05-21 12:30:46 +03:00
dependabot[bot]
ee70ecd92e Bump com.google.devtools.ksp:symbol-processing-api
Bumps [com.google.devtools.ksp:symbol-processing-api](https://github.com/google/ksp) from 2.1.20-2.0.1 to 2.1.21-2.0.1.
- [Release notes](https://github.com/google/ksp/releases)
- [Commits](https://github.com/google/ksp/compare/2.1.20-2.0.1...2.1.21-2.0.1)

---
updated-dependencies:
- dependency-name: com.google.devtools.ksp:symbol-processing-api
  dependency-version: 2.1.21-2.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-14 18:28:17 +03:00
dependabot[bot]
f3cf3f03b1 Bump org.jetbrains.kotlin:kotlin-stdlib from 2.1.20 to 2.1.21
Bumps [org.jetbrains.kotlin:kotlin-stdlib](https://github.com/JetBrains/kotlin) from 2.1.20 to 2.1.21.
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v2.1.20...v2.1.21)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin:kotlin-stdlib
  dependency-version: 2.1.21
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-14 18:28:01 +03:00
dependabot[bot]
4c46d734cc Bump io.ktor:ktor-client-core from 3.1.2 to 3.1.3
Bumps [io.ktor:ktor-client-core](https://github.com/ktorio/ktor) from 3.1.2 to 3.1.3.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/compare/3.1.2...3.1.3)

---
updated-dependencies:
- dependency-name: io.ktor:ktor-client-core
  dependency-version: 3.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-14 18:27:52 +03:00
Alex Plate
e932fe2059 Add com.ugarosa.idea.edgemotion as a dependant plugin 2025-05-14 12:02:06 +03:00
Alex Plate
d3f248d2f9 Refactor reset to avoid service initialization during the dispose 2025-05-09 19:04:10 +02:00
Alex Plate
f623c1eef9 Fix(VIM-3784): Integrate a notification that will warn new GoLand users that they use IdeaVim 2025-05-09 18:39:17 +02:00
Alex Plate
f03734e235 Fix(VIM-3786): Disable IdeaVim in the AI assistant 2025-05-09 16:31:44 +02:00
Alex Plate
85acdc2e24 Fix(VIM-3881): Execute undo/redo directly for the remote dev scenarios 2025-05-09 16:20:27 +02:00
Alex Plate
afa70e20e1 Correct the rider test 2025-05-09 15:27:43 +02:00
Alex Plate
73f3d328c5 Try to wait longer till Rider will start 2025-05-09 13:51:40 +02:00
Alex Plate
df3224b02d Start Rider with printing the logs to the output 2025-05-09 13:34:51 +02:00
Alex Plate
0e56a4e86e Remove background Rider start to see the problem when loading 2025-05-09 13:32:22 +02:00
Alex Plate
66ce949cc5 Do not use installer for Rider tests 2025-05-09 13:00:50 +02:00
Alex Plate
ab635972cc Remove a lot of deprecated methods in IdeaVim 2025-05-09 12:58:47 +02:00
Alex Plate
7fbf321cbd Do not use installer for Rider tests 2025-05-09 12:17:13 +02:00
Alex Plate
d4c24fcc7f Remove the outdated file with implemented vimscript functions 2025-05-09 12:17:12 +02:00
Matt Ellis
fd5af31247 Report cannot track intention action
If an alt+enter intention is invoked from Search Everywhere, IdeaVim's Track Action ID shows "Cannot detect action ID" instead of explaining that there is no action ID.

Relates to VIM-2541
2025-05-09 12:11:11 +02:00
Matt Ellis
df74b75570 Stop switching fragment editors to Insert mode
Fixes VIM-1217
2025-05-09 12:10:10 +02:00
Vladimir Parfinenko
c74163e917 Fix case settings in replacement string, VIM-3510
^VIM-3510 fixed
2025-05-09 12:08:01 +02:00
Vladimir Parfinenko
d38b2885ba Fix missing backreferences in replacement string, VIM-3895
^VIM-3895 fixed
2025-05-09 12:08:01 +02:00
Vladimir Parfinenko
61666d1cfd Always print float numbers using period as a decimal separator, VIM-3894
^VIM-3894 fixed
2025-05-09 12:02:56 +02:00
Alex Plate
35ddb21fe0 Fix the deprecated method use 2025-05-09 11:09:11 +02:00
Alex Plate
ccdd708907 Get rid of the old way of action execution 2025-05-08 18:22:40 +02:00
Alex Plate
73e61e0955 Get rid of hacks for Rider regarding esc and enter 2025-05-08 17:49:15 +02:00
Alex Plate
c3fa093d32 Upgrade the code to use the 2025.1 functions 2025-05-08 17:47:35 +02:00
Alex Plate
a94d509441 Remove other code deprecations 2025-05-08 17:33:41 +02:00
Alex Plate
12fd5bc79a Remove deprecated functions 2025-05-08 17:20:58 +02:00
Alex Plate
235368c449 Remove old comments regarding vim-engine extraction 2025-05-08 17:20:00 +02:00
Alex Plate
57ecd25640 Refactor MacKeyRepeat.kt in order to remove the deprecated methods use 2025-05-08 17:18:17 +02:00
Alex Plate
09d37ebd38 Convert MacKeyRepeat to kotlin 2025-05-08 17:11:39 +02:00
Alex Plate
81bc3f1f1b Rename .java to .kt 2025-05-08 17:11:39 +02:00
Alex Plate
9e3058dace Code cleanup: remove some deprecations from the source code 2025-05-08 17:04:25 +02:00
Alex Plate
6819d4f96c Add Mia Vucinic to the contributors list 2025-05-08 13:41:11 +02:00
IdeaVim Bot
222e1471b4 Add vumi19 to contributors list 2025-05-08 09:01:43 +00:00
M. V
35b9eaae3e VIM-2263 Add a not-null assertion operator to getRegister function to since assertions won't be executed if register does not exist 2025-05-07 18:54:26 +02:00
M. V
4eee1d3192 VIM-2263 Add additional tests that check the content of the registers 2025-05-07 18:54:26 +02:00
M. V
3c2e2bfb68 VIM-3771 Add a smile command with python ascii art 2025-05-07 18:54:12 +02:00
M. V
3f75b6db6d VIM-3771 Add a smile command with java ascii art 2025-05-07 18:54:12 +02:00
M. V
5fd318bf88 VIM-3771 Add a smile command with kotlin and default ascii art 2025-05-07 18:54:12 +02:00
M. V
6d34c70a9d VIM-3771 Rename VirtualFile.kt to VimVirtualFile.kt and add a new property extension 2025-05-07 18:54:12 +02:00
dependabot[bot]
3ffe8b68f7 Bump io.ktor:ktor-serialization-kotlinx-json from 3.1.2 to 3.1.3
Bumps [io.ktor:ktor-serialization-kotlinx-json](https://github.com/ktorio/ktor) from 3.1.2 to 3.1.3.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/compare/3.1.2...3.1.3)

---
updated-dependencies:
- dependency-name: io.ktor:ktor-serialization-kotlinx-json
  dependency-version: 3.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-07 18:30:51 +02:00
dependabot[bot]
d09fc538f7 Bump io.ktor:ktor-client-cio from 3.1.2 to 3.1.3
Bumps [io.ktor:ktor-client-cio](https://github.com/ktorio/ktor) from 3.1.2 to 3.1.3.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/compare/3.1.2...3.1.3)

---
updated-dependencies:
- dependency-name: io.ktor:ktor-client-cio
  dependency-version: 3.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-07 18:30:20 +02:00
dependabot[bot]
8d4d6b0f27 Bump io.ktor:ktor-client-content-negotiation from 3.1.2 to 3.1.3
Bumps [io.ktor:ktor-client-content-negotiation](https://github.com/ktorio/ktor) from 3.1.2 to 3.1.3.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/compare/3.1.2...3.1.3)

---
updated-dependencies:
- dependency-name: io.ktor:ktor-client-content-negotiation
  dependency-version: 3.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-07 18:13:19 +02:00
dependabot[bot]
f9f5f039db Bump com.google.devtools.ksp:symbol-processing-api
Bumps [com.google.devtools.ksp:symbol-processing-api](https://github.com/google/ksp) from 2.1.20-2.0.0 to 2.1.20-2.0.1.
- [Release notes](https://github.com/google/ksp/releases)
- [Commits](https://github.com/google/ksp/compare/2.1.20-2.0.0...2.1.20-2.0.1)

---
updated-dependencies:
- dependency-name: com.google.devtools.ksp:symbol-processing-api
  dependency-version: 2.1.20-2.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-07 17:57:47 +02:00
dependabot[bot]
cacb63a525 Bump io.ktor:ktor-client-auth from 3.1.2 to 3.1.3
Bumps [io.ktor:ktor-client-auth](https://github.com/ktorio/ktor) from 3.1.2 to 3.1.3.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/compare/3.1.2...3.1.3)

---
updated-dependencies:
- dependency-name: io.ktor:ktor-client-auth
  dependency-version: 3.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-07 17:56:32 +02:00
Alex Plate
518eac1476 Fix the authors update script 2025-05-06 14:28:59 +02:00
IdeaVim Bot
ad18667520 Add Ivan Yarkov to contributors list 2025-05-06 09:01:46 +00:00
Alex Plate
5bf2b51c5d Add a comment about the change 2025-05-05 17:00:33 +02:00
Ivan.Yarkov
5959fc2824 RIDER-123506 don't insert line break on enter in insert mode for Rider 2025-05-05 16:59:57 +02:00
Alex Plate
a9f9ae3727 Fix(VIM-3786): Make a workaround to fix shift-enter in AI chat 2025-04-29 19:36:05 +03:00
Alex Plate
8bfcd13c33 Fix(VIM-3882): Register the VimShortcutAction to the editor component instead of its wrapper
It turned out the editor.getComponent returns not the editorComponent, but the parent of the component. This caused no problems until the AI plugin started to register enter/esc on the editor component directly. Since an editor component is more specific than the component with vim actions, the vim shortcuts were suppressed.

In this change, we start to register shortcuts on the editor component directly, allowing them to properly work on the same level as AI shortcuts. This is also the level where the shortcuts are supposed to be registered.
2025-04-28 17:55:18 +03:00
Alex Plate
2cd5c9db72 Add Lejia Chen to the Authors list
Lejia was providing exceptional support for the IdeaVim project for two-plus years starting from 2023.
2025-04-28 11:45:15 +03:00
Matt Ellis
70d662fe28 Remove obsolete internal options 2025-04-22 15:48:19 +03:00
Alex Plate
2f33d41713 Update the minimal version of IJ to 251 2025-04-22 08:56:07 +03:00
Alex Plate
8247392dc3 Fix some compilation warnings 2025-04-16 22:12:36 +03:00
Alex Plate
c8504e1138 Fix warning about synchronization on primitive 2025-04-16 21:35:01 +03:00
Alex Plate
443e50b55f Add tests for case changing
Mostly to highlight that commands in format `gugu` and `gUgU` don't work for the moment
2025-04-16 19:44:38 +03:00
Alex Plate
1891216182 Fix(VIM-3878): Support ROT13 command g? 2025-04-16 19:44:38 +03:00
dependabot[bot]
515f0ca568 Bump org.junit.jupiter:junit-jupiter-params from 5.12.1 to 5.12.2
Bumps [org.junit.jupiter:junit-jupiter-params](https://github.com/junit-team/junit5) from 5.12.1 to 5.12.2.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.12.1...r5.12.2)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter-params
  dependency-version: 5.12.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-16 19:41:45 +03:00
dependabot[bot]
2be0228c35 Bump org.junit.jupiter:junit-jupiter-api from 5.12.1 to 5.12.2
Bumps [org.junit.jupiter:junit-jupiter-api](https://github.com/junit-team/junit5) from 5.12.1 to 5.12.2.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.12.1...r5.12.2)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter-api
  dependency-version: 5.12.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-16 19:25:31 +03:00
dependabot[bot]
cf554e9ae2 Bump org.junit.vintage:junit-vintage-engine from 5.12.1 to 5.12.2
Bumps [org.junit.vintage:junit-vintage-engine](https://github.com/junit-team/junit5) from 5.12.1 to 5.12.2.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.12.1...r5.12.2)

---
updated-dependencies:
- dependency-name: org.junit.vintage:junit-vintage-engine
  dependency-version: 5.12.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-16 19:10:22 +03:00
dependabot[bot]
251cc638db Bump org.junit.jupiter:junit-jupiter from 5.12.1 to 5.12.2
Bumps [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5) from 5.12.1 to 5.12.2.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.12.1...r5.12.2)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter
  dependency-version: 5.12.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-16 19:10:13 +03:00
dependabot[bot]
5700738c61 Bump org.junit.jupiter:junit-jupiter-engine from 5.12.1 to 5.12.2
Bumps [org.junit.jupiter:junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.12.1 to 5.12.2.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.12.1...r5.12.2)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter-engine
  dependency-version: 5.12.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-16 19:09:53 +03:00
Alex Plate
916afd31b2 Fix(VIM-3870): Add missing write actions 2025-04-16 18:54:22 +03:00
Alex Plate
5d2852420a Remove the duplication method for existing the select mode 2025-04-15 09:20:00 +03:00
Matt Ellis
a5efa5f9f2 Rename SAVE_VISUAL to SAVE_SELECTION
IdeaVim will still leave Visual mode, but the IDE's selection remains.
2025-04-10 19:32:29 +03:00
Matt Ellis
e86503798a Remove special case handling for Plug mapping
While it might not make sense to replay an incorrect `<Plug>` or `<Action>` mapping as characters where they are likely to cause unexpected behaviour as Normal commands, this is standard Vim behaviour (at least for `<Plug>`).

Note that `<Plug>` (and `<Action>`) is a special key code that cannot be typed. In Insert mode, Vim expands it to the text "<Plug>".
2025-04-10 19:32:29 +03:00
Matt Ellis
0b6ac4a9f4 Rename variable and add comments 2025-04-10 19:32:29 +03:00
Matt Ellis
2a6f7cc907 Remove unused mappingComplete parameter
Also makes all KeyConsumers internal
2025-04-10 19:32:29 +03:00
Matt Ellis
9b4f114d61 Refactor MappingProcessor
Extract single implementation for replaying unhandled keys and update to match the longest mapping that fits in the unhandled keys.
2025-04-10 19:32:29 +03:00
Matt Ellis
3155556832 Reformat and update comments for MappingProcessor
Minor refactorings, should be no changes in logic
2025-04-10 19:32:29 +03:00
Matt Ellis
07190f38c9 Fix :normal command with multi-letter mapping 2025-04-10 19:32:29 +03:00
Matt Ellis
db116faa32 Fix handling of modes in NormalCommand
All commands are called in Normal, so there is no need to check mode at execution time. The SAVE_VISUAL flag is perhaps poorly named, as it still change to Normal mode, but will save the current selection for commands that need (usually because they interact with IDE features). The `:normal` command does not need the current selection.
2025-04-10 19:32:29 +03:00
Matt Ellis
58496fa1a1 Uncomment NormalCommandTest.kt 2025-04-10 19:32:29 +03:00
dependabot[bot]
0bd12af1f4 Bump com.google.devtools.ksp:symbol-processing-api
Bumps [com.google.devtools.ksp:symbol-processing-api](https://github.com/google/ksp) from 2.1.20-1.0.32 to 2.1.20-2.0.0.
- [Release notes](https://github.com/google/ksp/releases)
- [Commits](https://github.com/google/ksp/compare/2.1.20-1.0.32...2.1.20-2.0.0)

---
updated-dependencies:
- dependency-name: com.google.devtools.ksp:symbol-processing-api
  dependency-version: 2.1.20-2.0.0
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-09 18:44:36 +03:00
dependabot[bot]
9f6d697e30 Bump io.ktor:ktor-client-cio from 3.1.1 to 3.1.2
Bumps [io.ktor:ktor-client-cio](https://github.com/ktorio/ktor) from 3.1.1 to 3.1.2.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/compare/3.1.1...3.1.2)

---
updated-dependencies:
- dependency-name: io.ktor:ktor-client-cio
  dependency-version: 3.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-09 18:44:10 +03:00
IdeaVim Bot
8ed283f7ae Add Max Siryk to contributors list 2025-04-07 09:02:16 +00:00
erotourtes
5a3d35f216 fix: typo & consistency 2025-04-07 10:47:41 +03:00
Alex Plate
13850f059d Add a new plugin to the compatibility check 2025-04-04 18:43:03 +03:00
Alex Plate
3ddc75f5f0 Remove the call for the instrumentationTools as it's deprecated 2025-04-04 18:39:28 +03:00
Alex Plate
6c71698aae Do not use the installer for some of the tests because currently it doesn't work on the installer with the new gradle plugin
It's unclear if this is a bug in the gradle plugin or misconfiguration
2025-04-04 18:38:29 +03:00
dependabot[bot]
664895941d Bump io.ktor:ktor-serialization-kotlinx-json from 3.1.1 to 3.1.2
Bumps [io.ktor:ktor-serialization-kotlinx-json](https://github.com/ktorio/ktor) from 3.1.1 to 3.1.2.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/compare/3.1.1...3.1.2)

---
updated-dependencies:
- dependency-name: io.ktor:ktor-serialization-kotlinx-json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-02 18:45:35 +03:00
dependabot[bot]
c758ac16ec Bump io.ktor:ktor-client-core from 3.1.1 to 3.1.2
Bumps [io.ktor:ktor-client-core](https://github.com/ktorio/ktor) from 3.1.1 to 3.1.2.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/compare/3.1.1...3.1.2)

---
updated-dependencies:
- dependency-name: io.ktor:ktor-client-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-02 18:31:12 +03:00
dependabot[bot]
4c2b3b8011 Bump io.ktor:ktor-client-auth from 3.1.1 to 3.1.2
Bumps [io.ktor:ktor-client-auth](https://github.com/ktorio/ktor) from 3.1.1 to 3.1.2.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/compare/3.1.1...3.1.2)

---
updated-dependencies:
- dependency-name: io.ktor:ktor-client-auth
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-02 18:31:03 +03:00
dependabot[bot]
ee1c4914d4 Bump io.ktor:ktor-client-content-negotiation from 3.1.1 to 3.1.2
Bumps [io.ktor:ktor-client-content-negotiation](https://github.com/ktorio/ktor) from 3.1.1 to 3.1.2.
- [Release notes](https://github.com/ktorio/ktor/releases)
- [Changelog](https://github.com/ktorio/ktor/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ktorio/ktor/compare/3.1.1...3.1.2)

---
updated-dependencies:
- dependency-name: io.ktor:ktor-client-content-negotiation
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-02 18:16:18 +03:00
dependabot[bot]
a5f225394f Bump com.google.devtools.ksp:symbol-processing-api
Bumps [com.google.devtools.ksp:symbol-processing-api](https://github.com/google/ksp) from 2.1.20-1.0.31 to 2.1.20-1.0.32.
- [Release notes](https://github.com/google/ksp/releases)
- [Commits](https://github.com/google/ksp/compare/2.1.20-1.0.31...2.1.20-1.0.32)

---
updated-dependencies:
- dependency-name: com.google.devtools.ksp:symbol-processing-api
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-02 18:16:01 +03:00
Alex Plate
8af848cef6 Fix(VIM-3861): Fix the debug action execution 2025-03-31 12:33:18 +03:00
IdeaVim Bot
4c1d91cc37 Add Peter Hoburg to contributors list 2025-03-29 09:01:47 +00:00
Alex Plate
deca256e1c Add clear conditions for property tests 2025-03-28 19:23:47 +02:00
Matt Ellis
6d3bde3ad5 Fix incorrect implementation of DoesNotMatchHandler 2025-03-28 19:07:06 +02:00
Matt Ellis
3b2b863c88 Update truthy expressions 2025-03-28 19:07:06 +02:00
Matt Ellis
a7e1c08589 Simplify unary arithmetic operators 2025-03-28 19:07:06 +02:00
Matt Ellis
5aa5e82d59 Rename some operator handlers 2025-03-28 19:07:06 +02:00
Matt Ellis
0db9ad505b Remove unnecessary singleton classes 2025-03-28 19:07:06 +02:00
Matt Ellis
147eb99745 Simplify case sensitive binary operators 2025-03-28 19:07:06 +02:00
Matt Ellis
bb73bdfb4a Simplify case sensitive comparisons 2025-03-28 19:07:06 +02:00
Matt Ellis
9ab1044880 Migrate various string operators to toVimString
Make it explicit they only work with String values
2025-03-28 19:07:06 +02:00
Matt Ellis
e2cc9c648f Introduce toVimString to match toVimNumber 2025-03-28 19:07:06 +02:00
Matt Ellis
a322525c2a Be more explicit with is and isnot implementation 2025-03-28 19:07:06 +02:00
Matt Ellis
51ea947ccb Move comparison operator data rules to base class 2025-03-28 19:07:06 +02:00
Matt Ellis
f6810798ef Move binary operator conversion rules to base class 2025-03-28 19:07:06 +02:00
Matt Ellis
2fee525998 Refactor logical operators 2025-03-28 19:07:06 +02:00
Matt Ellis
9e3ab12451 Combine logical handlers into same file 2025-03-28 19:07:06 +02:00
Matt Ellis
5f8d552e8a Extract error message 2025-03-28 19:07:06 +02:00
Matt Ellis
4e847f8ef4 Merge arithmetic operators to same file 2025-03-28 19:07:06 +02:00
Matt Ellis
d5901fc2c9 Merge case-sensitive handlers to same file 2025-03-28 19:07:06 +02:00
Matt Ellis
3621b91321 Make operator handlers internal 2025-03-28 19:07:06 +02:00
Alex Plate
acbf1d7bd4 Update gradle wrapper 2025-03-28 19:05:55 +02:00
CCCC-L
f079edfb25 Modify CommentaryTextObject test case 2025-03-28 18:45:42 +02:00
CCCC-L
440cab1674 CommentaryTextObject no longer contains spaces to be consistent with neovim 2025-03-28 18:45:42 +02:00
Alex Plate
9322e3b81b Update intellij gradle plugin 2025-03-28 18:40:26 +02:00
Matt Ellis
021db84a21 Expand ~ to home directory in XDG_CONFIG_HOME
Addresses VIM-3844, VIM-2001
2025-03-28 18:18:32 +02:00
Matt Ellis
e232cb3ceb Support 32-bit Unicode codepoints in digraphs
Fixes VIM-3842
2025-03-28 18:13:14 +02:00
Peter Hoburg
174757cdb2 Removed summary from Mini.ai on the README.md 2025-03-28 18:00:26 +02:00
Peter Hoburg
ef0883bc0d finished adding summaries. 2025-03-28 18:00:26 +02:00
Peter Hoburg
0b4ad07b32 Moved more details around,
added more About sections.
2025-03-28 18:00:26 +02:00
Peter Hoburg
3d1a426566 Added back in a missing </details> tag. 2025-03-28 18:00:26 +02:00
Peter Hoburg
6f4eb838c3 Started alphabetizing and adding About: sections to the IdeaVim Plugins.md 2025-03-28 18:00:26 +02:00
Peter Hoburg
bc38ddc0f8 Alphabetized the IdeaVim Plugins section of the main README.md 2025-03-28 18:00:26 +02:00
Matt Ellis
a33b3980ab Fix unhandled key stroke after surround operation
Fixes VIM-3841
2025-03-28 17:51:14 +02:00
Matt Ellis
7917c83cb5 Add extra tests for line() function behaviour
It's not obvious that line('v') will return the opposite end of the current selection. Add some tests to highlight this.

Relates to VIM-3838
2025-03-28 17:23:41 +02:00
Matt Ellis
42229b285b Fix typo 2025-03-28 17:23:41 +02:00
Matt Ellis
421c3bbfb8 Support <C-U> in commandToKeys 2025-03-28 17:23:41 +02:00
Matt Ellis
dbd097a91a Fix caret position after :move command
Fixes VIM-3837
2025-03-28 17:23:41 +02:00
Alex Plate
30f019aa18 Fix enter for Clion Nova 2025-03-28 15:35:33 +02:00
Alex Plate
543d8dbb13 Fix(VIM-3856): Disable new way of action execution for CLion 2025-03-28 14:05:53 +02:00
Alex Plate
2800b2d5fc Fix(VIM-3857): Fix pasting for the client 2025-03-28 13:45:13 +02:00
Alex Plate
cd27ce8004 Fix(VIM-3852): Fix incorrect cast while checking the shortcuts 2025-03-28 13:27:59 +02:00
Alex Plate
6f3cf43bae Make a few tests for Rider
This should cover VIM-3826 issues
2025-03-27 17:34:24 +02:00
Alex Plate
b043296cde Fix(VIM-3826): Fix action execution in Rider 243 and older versions
It still doesn't work fine when executing in format `:action ReformatCode` in 243, but with 251 format updates from Rider, it should be fine.
2025-03-27 17:20:23 +02:00
Alex Plate
9beca20037 Click Activate during testing 2025-03-27 15:17:14 +02:00
Alex Plate
b882d60416 Trying to make Rider UI tests alive 2025-03-27 15:05:38 +02:00
Alex Plate
7144d73488 Mute a slow operation related to VIM-3648 2025-03-27 14:12:55 +02:00
dependabot[bot]
ae71075134 Bump org.jetbrains.kotlin:kotlin-stdlib from 2.1.10 to 2.1.20
Bumps [org.jetbrains.kotlin:kotlin-stdlib](https://github.com/JetBrains/kotlin) from 2.1.10 to 2.1.20.
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v2.1.10...v2.1.20)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin:kotlin-stdlib
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-26 17:43:25 +02:00
dependabot[bot]
a5d4bf1a57 Bump org.eclipse.jgit:org.eclipse.jgit.ssh.apache
Bumps [org.eclipse.jgit:org.eclipse.jgit.ssh.apache](https://github.com/eclipse-jgit/jgit) from 7.1.0.202411261347-r to 7.2.0.202503040940-r.
- [Commits](https://github.com/eclipse-jgit/jgit/compare/v7.1.0.202411261347-r...v7.2.0.202503040940-r)

---
updated-dependencies:
- dependency-name: org.eclipse.jgit:org.eclipse.jgit.ssh.apache
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-26 17:41:33 +02:00
dependabot[bot]
3b8a830622 Bump org.junit.jupiter:junit-jupiter-api from 5.12.0 to 5.12.1
Bumps [org.junit.jupiter:junit-jupiter-api](https://github.com/junit-team/junit5) from 5.12.0 to 5.12.1.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.12.0...r5.12.1)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter-api
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-26 17:41:17 +02:00
dependabot[bot]
71adcef1bf Bump com.google.devtools.ksp:symbol-processing-api
Bumps [com.google.devtools.ksp:symbol-processing-api](https://github.com/google/ksp) from 2.1.10-1.0.29 to 2.1.20-1.0.31.
- [Release notes](https://github.com/google/ksp/releases)
- [Commits](https://github.com/google/ksp/compare/2.1.10-1.0.29...2.1.20-1.0.31)

---
updated-dependencies:
- dependency-name: com.google.devtools.ksp:symbol-processing-api
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-26 17:40:41 +02:00
dependabot[bot]
d4d0212b04 Bump org.junit.jupiter:junit-jupiter-params from 5.12.0 to 5.12.1
Bumps [org.junit.jupiter:junit-jupiter-params](https://github.com/junit-team/junit5) from 5.12.0 to 5.12.1.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.12.0...r5.12.1)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter-params
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-19 18:51:22 +03:00
dependabot[bot]
4b3bba6a98 Bump org.junit.vintage:junit-vintage-engine from 5.12.0 to 5.12.1
Bumps [org.junit.vintage:junit-vintage-engine](https://github.com/junit-team/junit5) from 5.12.0 to 5.12.1.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.12.0...r5.12.1)

---
updated-dependencies:
- dependency-name: org.junit.vintage:junit-vintage-engine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-19 18:34:51 +03:00
dependabot[bot]
13edc1294c Bump org.junit.jupiter:junit-jupiter from 5.12.0 to 5.12.1
Bumps [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5) from 5.12.0 to 5.12.1.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.12.0...r5.12.1)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-19 18:34:22 +03:00
dependabot[bot]
f5269a56d6 Bump org.junit.jupiter:junit-jupiter-engine from 5.12.0 to 5.12.1
Bumps [org.junit.jupiter:junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.12.0 to 5.12.1.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.12.0...r5.12.1)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter-engine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-19 18:34:13 +03:00
276 changed files with 10899 additions and 3722 deletions

View File

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

4
.gitignore vendored
View File

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

View File

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

View File

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

View File

@@ -43,6 +43,8 @@ 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.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.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
""".trimIndent()
}
}

View File

@@ -24,6 +24,7 @@ object PropertyBased : IdeaVimBuildType({
steps {
gradle {
clearConditions()
tasks = "clean :tests:property-tests:testPropertyBased"
buildFile = ""
enableStacktrace = true

View File

@@ -12,7 +12,7 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.vcs
open class TestingBuildType(
private val testName: String,
private val branch: String,
private val branch: String = "<default>",
private val version: String = testName,
private val javaVersion: String? = null,
private val javaPlugin: Boolean = true,

View File

@@ -26,6 +26,13 @@ Previous maintainers:
&nbsp;
Andrey Vlasovskikh
Previous support members:
* [![icon][mail]](mailto:lejia.chen@jetbrains.com)
[![icon][github-off]](#)
&nbsp;
Lejia Chen
Contributors:
* [![icon][mail]](mailto:yole@jetbrains.com)
@@ -587,6 +594,26 @@ Contributors:
[![icon][github]](https://github.com/Iliya-usov)
&nbsp;
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
Previous contributors:

View File

@@ -29,8 +29,8 @@ IdeaVim is a Vim engine for JetBrains IDEs.
#### Compatibility
IntelliJ IDEA, PyCharm, CLion, PhpStorm, WebStorm, RubyMine, DataGrip, GoLand, Rider, Cursive,
Android Studio and other IntelliJ platform based IDEs.
IntelliJ IDEA, PyCharm, GoLand, CLion, PhpStorm, WebStorm, RubyMine, DataGrip, DataSpell, Rider, Cursive,
Android Studio, and other [JetBrains IDEs](https://www.jetbrains.com/ides/).
Setup
------------
@@ -89,29 +89,12 @@ Here are some examples of supported vim features and commands:
* Full Vim regexps for search and search/replace
* Vim web help
* `~/.ideavimrc` configuration file
[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
* Vim script
* IdeaVim plugins
See also:
* [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
-----
@@ -265,8 +248,7 @@ 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.
Additionally, you may be interested in the
[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).
[Vim Script Discussion](https://github.com/JetBrains/ideavim/discussions/357).
### IDE specific options

View File

@@ -21,7 +21,7 @@ repositories {
}
dependencies {
compileOnly("com.google.devtools.ksp:symbol-processing-api:2.1.10-1.0.29")
compileOnly("com.google.devtools.ksp:symbol-processing-api:2.1.21-2.0.1")
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
exclude("org.jetbrains.kotlin", "kotlin-stdlib")

View File

@@ -31,6 +31,7 @@ import kotlinx.serialization.json.putJsonObject
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.RepositoryBuilder
import org.intellij.markdown.ast.getTextInNode
import org.intellij.markdown.ast.impl.ListCompositeNode
import org.jetbrains.changelog.Changelog
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware
@@ -50,14 +51,14 @@ buildscript {
classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
// This is needed for jgit to connect to ssh
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.1.0.202411261347-r")
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.2.1.202505142326-r")
classpath("org.kohsuke:github-api:1.305")
classpath("io.ktor:ktor-client-core:3.1.1")
classpath("io.ktor:ktor-client-cio:3.1.1")
classpath("io.ktor:ktor-client-auth:3.1.1")
classpath("io.ktor:ktor-client-content-negotiation:3.1.1")
classpath("io.ktor:ktor-serialization-kotlinx-json:3.1.1")
classpath("io.ktor:ktor-client-core:3.1.3")
classpath("io.ktor:ktor-client-cio:3.1.3")
classpath("io.ktor:ktor-client-auth:3.1.3")
classpath("io.ktor:ktor-client-content-negotiation:3.1.3")
classpath("io.ktor:ktor-serialization-kotlinx-json:3.1.3")
// This comes from the changelog plugin
// classpath("org.jetbrains:markdown:0.3.1")
@@ -69,7 +70,12 @@ plugins {
kotlin("jvm") version "2.0.21"
application
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.kotlinx.kover") version "0.6.1"
id("com.dorongold.task-tree") version "4.0.1"
@@ -112,7 +118,11 @@ dependencies {
intellijPlatform {
// Snapshots don't use installers
// https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html#target-versions-installers
val useInstaller = "EAP-SNAPSHOT" !in ideaVersion
var 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
// E.g. local("/Users/{user}/Applications/IntelliJ IDEA Ultimate.app")
@@ -127,9 +137,9 @@ dependencies {
// AceJump is an optional dependency. We use their SessionManager class to check if it's active
plugin("AceJump", "3.8.19")
plugin("com.intellij.classic.ui", "243.21565.122")
plugin("com.intellij.classic.ui", "251.23774.318")
bundledPlugins("org.jetbrains.plugins.terminal", "com.intellij.modules.json")
bundledPlugins("org.jetbrains.plugins.terminal")
}
moduleSources(project(":vim-engine", "sourcesJarArtifacts"))
@@ -151,17 +161,17 @@ dependencies {
// https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin
testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.12.0")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.12.0")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.12.0")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.12.0")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.12.0")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.12.0")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.13.0")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.13.0")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.13.0")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.13.0")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.13.0")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.13.0")
// Temp workaround suggested in https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-faq.html#junit5-test-framework-refers-to-junit4
// Can be removed when IJPL-159134 is fixed
// testRuntimeOnly("junit:junit:4.13.2")
testImplementation("org.junit.vintage:junit-vintage-engine:5.12.0")
testImplementation("org.junit.vintage:junit-vintage-engine:5.13.0")
// testFixturesImplementation("org.junit.vintage:junit-vintage-engine:5.10.3")
}
@@ -189,12 +199,9 @@ tasks {
useJUnitPlatform()
// Set teamcity env variable locally to run additional tests for leaks.
// By default, this test runs on TC only, but this test doesn't take a lot of time,
// so we can turn it on for local development
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")
}
println("Project leak checks: If you experience project leaks on TeamCity that doesn't reproduce locally")
println("Uncomment the following line in build.gradle to enable leak checks (see build.gradle config)")
// environment("TEAMCITY_VERSION" to "X")
systemProperty("ideavim.nvim.test", System.getProperty("nvim") ?: false)
@@ -250,6 +257,7 @@ tasks {
// a custom task (see below)
runIde {
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
@@ -822,7 +830,9 @@ fun updateAuthors(uncheckedEmails: Set<String>) {
org.intellij.markdown.parser.MarkdownParser(org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor())
val tree = parser.buildMarkdownTreeFromString(authors)
val contributorsSection = tree.children[24]
val contributorsSection = tree.children
.filter { it is ListCompositeNode }
.single { it.getTextInNode(authors).contains("yole") }
val existingEmails = mutableSetOf<String>()
for (child in contributorsSection.children) {
if (child.children.size > 1) {

View File

@@ -16,10 +16,95 @@ in `~/.ideavimrc`. E.g. `set nosurround`.
Available plugins:
<details>
<summary><h2>easymotion</h2></summary>
<summary><h2>argtextobj: Provides a text-object 'a' argument</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).
### 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:
- Install [IdeaVim-EasyMotion](https://plugins.jetbrains.com/plugin/13360-ideavim-easymotion/)
and [AceJump](https://plugins.jetbrains.com/plugin/7086-acejump/) plugins.
@@ -41,80 +126,176 @@ All commands with the mappings are supported. See the [full list of supported co
</details>
<details>
<summary><h2>sneak</h2></summary>
<summary><h2>exchange: Easy text exchange operator</h2></summary>
<img src="images/sneakIcon.svg" width="80" height="80" alt="icon"/>
By [fan-tom](https://github.com/fan-tom)
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:
- 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'`
- Add the following command to `~/.ideavimrc`: `Plug 'tommcdo/vim-exchange'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'preservim/nerdtree'</code>
<code>Plugin 'tommcdo/vim-exchange'</code>
<br/>
<code>Plug 'https://github.com/preservim/nerdtree'</code>
<code>Plug 'https://github.com/tommcdo/vim-exchange'</code>
<br/>
<code>Plug 'nerdtree'</code>
<code>Plug 'vim-exchange'</code>
<br/>
<code>set NERDTree</code>
<code>set exchange</code>
</details>
### Instructions
[See here](NERDTree-support.md).
https://github.com/tommcdo/vim-exchange/blob/master/doc/exchange.txt
</details>
<details>
<summary><h2>surround</h2></summary>
Original plugin: [vim-surround](https://github.com/tpope/vim-surround).
<summary><h2>FunctionTextObj: Adds text objects for manipulating functions/methods</h2></summary>
By Julien Phalip
### 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:
- Add the following command to `~/.ideavimrc`: `Plug 'tpope/vim-surround'`
- Add the following command to `~/.ideavimrc`: `Plug 'machakann/vim-highlightedyank'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'tpope/vim-surround'</code>
<code>Plugin 'machakann/vim-highlightedyank'</code>
<br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=1697'</code>
<code>Plug 'https://github.com/machakann/vim-highlightedyank'</code>
<br/>
<code>Plug 'vim-surround'</code>
<code>Plug 'vim-highlightedyank'</code>
<br/>
<code>set surround</code>
<code>set highlightedyank</code>
</details>
### Instructions
https://github.com/tpope/vim-surround/blob/master/doc/surround.txt
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>multiple-cursors</h2></summary>
<summary><h2>indent-object: Adds text objects for manipulating sentences/paragraphs/etc...</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).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'terryma/vim-multiple-cursors'`
<details>
@@ -127,7 +308,7 @@ Original plugin: [vim-multiple-cursors](https://github.com/terryma/vim-multiple-
<br/>
<code>set multiple-cursors</code>
</details>
### 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...
@@ -153,38 +334,118 @@ xmap <leader>g<C-n> <Plug>AllOccurrences
</details>
<details>
<summary><h2>commentary</h2></summary>
<summary><h2>NERDTree: Adds NERDTree navigation to the project panel</h2></summary>
Original plugin: [NERDTree](https://github.com/preservim/nerdtree).
By [Daniel Leong](https://github.com/dhleong)
Original plugin: [commentary.vim](https://github.com/tpope/vim-commentary).
### Summary:
Adds NERDTree navigation to the project panel.
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'tpope/vim-commentary'`
- Add the following command to `~/.ideavimrc`: `Plug 'preservim/nerdtree'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'tpope/vim-commentary'</code>
<code>Plugin 'preservim/nerdtree'</code>
<br/>
<code>Plug 'https://github.com/tpope/vim-commentary'</code>
<code>Plug 'https://github.com/preservim/nerdtree'</code>
<br/>
<code>Plug 'vim-commentary'</code>
<code>Plug 'nerdtree'</code>
<br/>
<code>Plug 'tcomment_vim'</code>
<br/>
<code>set commentary</code>
<code>set NERDTree</code>
</details>
### Instructions
https://github.com/tpope/vim-commentary/blob/master/doc/commentary.txt
[See here](NERDTree-support.md).
</details>
<details>
<summary><h2>ReplaceWithRegister</h2></summary>
<summary><h2>paragraph-motion: Extends the { and } motions to ignore whitespace on otherwise empty lines</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)
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:
- Add the following command to `~/.ideavimrc`: `Plug 'vim-scripts/ReplaceWithRegister'`
<details>
@@ -203,78 +464,99 @@ Original plugin: [ReplaceWithRegister](https://github.com/vim-scripts/ReplaceWit
<br/>
<code>set ReplaceWithRegister</code>
</details>
### Instructions
https://github.com/vim-scripts/ReplaceWithRegister/blob/master/doc/ReplaceWithRegister.txt
</details>
<details>
<summary><h2>argtextobj</h2></summary>
<summary><h2>sneak: Jump to any location specified by two characters</h2></summary>
<img src="images/sneakIcon.svg" width="80" height="80" alt="icon"/>
By [Mikhail Levchenko](https://github.com/Mishkun)
Original repository with the plugin: https://github.com/Mishkun/ideavim-sneak
Original plugin: [vim-sneak](https://github.com/justinmk/vim-sneak).
### Summary:
Jump to any location specified by two characters.
Original plugin: [argtextobj.vim](https://www.vim.org/scripts/script.php?script_id=2699).
### Setup:
- Add the following command to `~/.ideavimrc`: `Plug 'vim-scripts/argtextobj.vim'`
- 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:
- Add the following command to `~/.ideavimrc`: `Plug 'tpope/vim-surround'`
<details>
<summary>Alternative syntax</summary>
<code>Plugin 'vim-scripts/argtextobj.vim'</code>
<code>Plugin 'tpope/vim-surround'</code>
<br/>
<code>Plug 'https://github.com/vim-scripts/argtextobj.vim'</code>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=1697'</code>
<br/>
<code>Plug 'argtextobj.vim'</code>
<code>Plug 'vim-surround'</code>
<br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=2699'</code>
<br/>
<code>set argtextobj</code>
<code>set surround</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
https://github.com/tpope/vim-surround/blob/master/doc/surround.txt
</details>
<details>
<summary><h2>exchange</h2></summary>
<summary><h2>Switch: Switch some text under the cursor based on regex patterns</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
https://github.com/tommcdo/vim-exchange/blob/master/doc/exchange.txt
https://plugins.jetbrains.com/plugin/25899-vim-switch
</details>
<details>
<summary><h2>textobj-entire</h2></summary>
<summary><h2>textobj-entire: Adds mapping for selecting entire contents of file regardless of cursor position</h2></summary>
By [Alexandre Grison](https://github.com/agrison)
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:
- Add the following command to `~/.ideavimrc`: `Plug 'kana/vim-textobj-entire'`
<details>
@@ -295,158 +577,13 @@ https://github.com/kana/vim-textobj-entire/blob/master/doc/textobj-entire.txt
</details>
<details>
<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>
<summary><h2>Which-Key: Displays available keybindings in popup</h2></summary>
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:
- Install [Which-Key](https://plugins.jetbrains.com/plugin/15976-which-key) plugin.
- Add the following command to `~/.ideavimrc`: `set which-key`
@@ -456,49 +593,3 @@ Original plugin: [vim-which-key](https://github.com/liuchengxu/vim-which-key).
https://github.com/TheBlob42/idea-which-key?tab=readme-ov-file#installation
</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
# Maven releases are here: https://www.jetbrains.com/intellij-repository/releases
# And snapshots: https://www.jetbrains.com/intellij-repository/snapshots
ideaVersion=2024.3
ideaVersion=2025.1
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
ideaType=IC
instrumentPluginCode=true
version=chylex-45
version=chylex-49
javaVersion=21
remoteRobotVersion=0.11.23
antlrVersion=4.10.1

View File

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

3
gradlew vendored
View File

@@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# 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
' "$PWD" ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum

View File

@@ -20,17 +20,17 @@ repositories {
}
dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:2.1.10")
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:2.1.21")
implementation("io.ktor:ktor-client-core:3.1.1")
implementation("io.ktor:ktor-client-cio:3.1.1")
implementation("io.ktor:ktor-client-content-negotiation:3.1.1")
implementation("io.ktor:ktor-serialization-kotlinx-json:3.1.1")
implementation("io.ktor:ktor-client-auth:3.1.1")
implementation("io.ktor:ktor-client-core:3.1.3")
implementation("io.ktor:ktor-client-cio:3.1.3")
implementation("io.ktor:ktor-client-content-negotiation:3.1.3")
implementation("io.ktor:ktor-serialization-kotlinx-json:3.1.3")
implementation("io.ktor:ktor-client-auth:3.1.3")
implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
// This is needed for jgit to connect to ssh
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.1.0.202411261347-r")
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.2.1.202505142326-r")
implementation("com.vdurmont:semver4j:3.1.0")
}

View File

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

View File

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

View File

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

View File

@@ -59,11 +59,8 @@ import javax.swing.KeyStroke
*
*
* 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
*/
class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
internal class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
init {
initInjector()
@@ -92,9 +89,10 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
val duration = System.currentTimeMillis() - start
LOG.info("VimShortcut execution '$keyStroke': $duration ms")
}
} catch (_: ProcessCanceledException) {
// Control-flow exceptions (like ProcessCanceledException) should never be logged
} catch (e: ProcessCanceledException) {
// Control-flow exceptions (like ProcessCanceledException) should never be logged and should be rethrown
// See {@link com.intellij.openapi.diagnostic.Logger.checkException}
throw e
} catch (throwable: Throwable) {
LOG.error(throwable)
}
@@ -182,9 +180,6 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
}
if (editor.inInsertMode) {
if (keyCode == KeyEvent.VK_ENTER) {
return ActionEnableStatus.no("Enter action in insert mode", LogLevel.INFO)
}
if (keyCode == KeyEvent.VK_TAB) {
// TODO: This stops VimEditorTab seeing <Tab> in insert mode and correctly scrolling the view
// There are multiple actions registered for VK_TAB. The important items, in order, are this, the Live
@@ -201,10 +196,6 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
VimPlugin.getChange().tabAction = true
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) {
@@ -352,14 +343,6 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
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>()
@JvmStatic

View File

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

View File

@@ -11,16 +11,17 @@ package com.maddyhome.idea.vim.customization.feature.terminal
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.key.IdeaVimDisablerExtensionPoint
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.isOutputModelEditor
import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isPromptEditor
/**
* The only implementation is defined right here.
*/
// [VERSION UPDATE] 2025.1+ Add 2 new predicates
internal class IdeaVimTerminalDisablerExtension : IdeaVimDisablerExtensionPoint {
override fun isDisabledForEditor(editor: Editor): Boolean {
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) {
val context = injector.executionContextManager.getEditorExecutionContext(editor.vim)
val keyHandler = KeyHandler.getInstance()
keys.forEach { keyHandler.handleKey(editor.vim, it, context, false, false, keyHandler.keyHandlerState) }
keys.forEach { keyHandler.handleKey(editor.vim, it, context, false, keyHandler.keyHandlerState) }
}
/** Returns a single key stroke from the user input similar to 'getchar()'. */
@@ -214,7 +214,13 @@ object VimExtensionFacade {
/** Set the current contents of the given register */
@JvmStatic
fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) {
fun setRegisterForCaret(
editor: VimEditor,
context: ExecutionContext,
register: Char,
caret: ImmutableVimCaret,
keys: List<KeyStroke?>?,
) {
caret.registerStorage.setKeys(register, keys?.filterNotNull() ?: emptyList())
}

View File

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

View File

@@ -11,6 +11,7 @@ import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret
@@ -141,6 +142,10 @@ internal class VimSurroundExtension : VimExtension {
runWriteAction {
// Leave visual mode
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)
}
}
}
@@ -169,8 +174,8 @@ internal class VimSurroundExtension : VimExtension {
val surroundings = editor.sortedCarets()
.map {
val oldValue: List<KeyStroke>? = getRegisterForCaret(editor, context, REGISTER, it)
setRegisterForCaret(REGISTER, it, null)
SurroundingInfo(it, null, oldValue, false)
setRegisterForCaret(editor, context, REGISTER, it, null)
SurroundingInfo(editor, context, it, null, oldValue, false)
}
// Delete surrounding's content
@@ -259,9 +264,16 @@ internal class VimSurroundExtension : VimExtension {
}
}
private data class SurroundingInfo(val caret: VimCaret, var innerText: List<KeyStroke>?, val oldRegisterContent: List<KeyStroke>?, var isValidSurrounding: Boolean) {
private data class SurroundingInfo(
val editor: VimEditor,
val context: ExecutionContext,
val caret: VimCaret,
var innerText: List<KeyStroke>?,
val oldRegisterContent: List<KeyStroke>?,
var isValidSurrounding: Boolean,
) {
fun restoreRegister() {
setRegisterForCaret(REGISTER, caret, oldRegisterContent)
setRegisterForCaret(editor, context, REGISTER, caret, oldRegisterContent)
}
}

View File

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

View File

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

View File

@@ -141,12 +141,8 @@ object IjOptions {
// Temporary feature flags during development, not really intended for external use
val closenotebooks: ToggleOption =
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 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
// derives from Option<VimInt>

View File

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

View File

@@ -89,6 +89,9 @@ internal class MotionGroup : VimMotionGroupBase() {
}
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)
return moveCaretToColumn(editor, caret, col, false)
}
@@ -97,6 +100,15 @@ internal class MotionGroup : VimMotionGroupBase() {
editor: VimEditor,
caret: ImmutableVimCaret,
): @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 bufferLine = caret.getLine()
return editor.getLeadingCharacterOffset(bufferLine, col)
@@ -107,6 +119,9 @@ internal class MotionGroup : VimMotionGroupBase() {
caret: ImmutableVimCaret,
allowEnd: Boolean,
): Motion {
if (editor.ij.softWrapModel.isSoftWrappingEnabled) {
return AbsoluteOffset(caret.ij.visualLineEnd - 1)
}
val col = EditorHelper.getVisualColumnAtRightOfDisplay(editor.ij, caret.getVisualPosition().line)
return moveCaretToColumn(editor, caret, col, allowEnd)
}

View File

@@ -12,7 +12,6 @@ import com.intellij.icons.AllIcons
import com.intellij.ide.BrowserUtil
import com.intellij.ide.actions.OpenFileAction
import com.intellij.ide.actions.RevealFileAction
import com.intellij.notification.ActionCenter
import com.intellij.notification.Notification
import com.intellij.notification.NotificationGroup
import com.intellij.notification.NotificationGroupManager
@@ -37,6 +36,7 @@ import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.handler.KeyMapIssue
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.ShortcutOwnerInfo
import com.maddyhome.idea.vim.newapi.globalIjOptions
@@ -135,8 +135,30 @@ internal class NotificationService(private val project: Project?) {
).notify(project)
}
fun notifyActionId(id: String?, candidates: List<String>? = null) {
ActionIdNotifier.notifyActionId(id, project, candidates)
/**
* Shows a notification that the user can reenable IdeaVim by clicking on the IdeaVim icon in the status bar.
*/
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>) {
@@ -214,12 +236,15 @@ internal class NotificationService(private val project: Project?) {
object ActionIdNotifier {
private var notification: Notification? = null
fun notifyActionId(id: String?, project: Project?, candidates: List<String>? = null) {
fun notifyActionId(id: String?, project: Project?, candidates: List<String>? = null, intentionName: String? = null) {
notification?.expire()
val possibleIDs = candidates?.distinct()?.sorted()
val content = when {
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.size == 1 -> "Possible action ID: <code>${possibleIDs[0]}</code><br><br>"
else -> {
@@ -229,7 +254,7 @@ internal class NotificationService(private val project: Project?) {
append("</ul></p>")
}
}
} + "<small>See the ${ActionCenter.getToolwindowName()} tool window for previous IDs</small>"
} + "<small>See the Notifications tool window for previous IDs</small>"
notification =
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? {
val bookmarksManager = BookmarksManager.getInstance(this) ?: return null
val lineBookmarkProvider = LineBookmarkProvider.find(this) ?: return null
val lineBookmarkProvider = LineBookmarkProvider.Util.find(this) ?: return null
val bookmark = lineBookmarkProvider.createBookmark(editor, line) as LineBookmark? ?: return null
val type = BookmarkType.get(mnemonic)
if (type == BookmarkType.DEFAULT) return null

View File

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

View File

@@ -96,7 +96,7 @@ internal object IdeaSelectionControl {
} else {
logger.debug("None of carets have selection. State before adjustment: ${editor.vim.mode}")
if (editor.vim.inVisualMode) editor.vim.exitVisualMode()
if (editor.vim.inSelectMode) editor.exitSelectMode(false)
if (editor.vim.inSelectMode) editor.vim.exitSelectMode(false)
if (editor.vim.inNormalMode) {
activateMode(editor, chooseNonSelectionMode(editor))

View File

@@ -17,7 +17,6 @@ import com.intellij.openapi.keymap.KeymapManagerListener
import com.intellij.openapi.keymap.ex.KeymapManagerEx
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity
import com.jetbrains.rd.util.ConcurrentHashMap
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.key
@@ -28,6 +27,8 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
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
@@ -67,11 +68,7 @@ internal class IdeaVimCorrectorKeymapChangedListener : KeymapManagerListener {
check(correctorRequester.tryEmit(Unit))
}
override fun shortcutChanged(keymap: Keymap, actionId: String) {
check(correctorRequester.tryEmit(Unit))
}
override fun shortcutChanged(keymap: Keymap, actionId: String, fromSettings: Boolean) {
override fun shortcutsChanged(keymap: Keymap, actionIds: @NonNls Collection<String>, fromSettings: Boolean) {
check(correctorRequester.tryEmit(Unit))
}
}

View File

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

View File

@@ -60,7 +60,7 @@ internal fun Editor.updateCaretsVisualAttributes() {
* Used when Vim emulation is disabled
*/
internal fun Editor.removeCaretsVisualAttributes() {
caretModel.allCarets.forEach { it.visualAttributes = CaretVisualAttributes.DEFAULT }
caretModel.allCarets.forEach { it.visualAttributes = CaretVisualAttributes.getDefault() }
}
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
// NOTE: At the moment, this causes project leak in tests
// IJPL-928 - this will be fixed in 2024.2
// [VERSION UPDATE] 2024.2 - remove if wrapping
// IJPL-928 - this will be fixed in 2025.2
// [VERSION UPDATE] 2025.2 - remove if wrapping
if (!ApplicationManager.getApplication().isUnitTestMode) {
(this as? EditorEx)?.setCaretVisible(true)
}

View File

@@ -1,67 +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.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,6 +8,7 @@
package com.maddyhome.idea.vim.helper;
import com.intellij.injected.editor.VirtualFileWindow;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.editor.impl.EditorImpl;
@@ -15,6 +16,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileUtil;
import com.intellij.testFramework.LightVirtualFile;
import com.maddyhome.idea.vim.api.EngineEditorHelperKt;
import com.maddyhome.idea.vim.api.VimEditor;
@@ -652,7 +654,21 @@ public class EditorHelper {
*/
public static boolean isFileEditor(@NotNull Editor editor) {
final VirtualFile virtualFile = getVirtualFile(editor);
return virtualFile != null && !(virtualFile instanceof LightVirtualFile);
if (virtualFile == null) return false;
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,6 +14,7 @@ import com.intellij.codeWithMe.ClientId
import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.CaretState
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.EditorKind
import com.intellij.openapi.editor.ex.util.EditorUtil
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
import com.intellij.util.ui.table.JBTableRowEditor
@@ -42,9 +43,33 @@ internal val Editor.isIdeaVimDisabledHere: Boolean
return (ideaVimDisabledInDialog(ideaVimSupportValue) && isInDialog()) ||
!ClientId.isCurrentlyUnderLocalId || // CWM-927
(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 {
return !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialog)
&& !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialoglegacy)
@@ -71,6 +96,19 @@ internal fun Editor.isPrimaryEditor(): Boolean {
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
private fun isTableCellEditor(c: Component): Boolean {
return (java.lang.Boolean.TRUE == (c as JComponent).getClientProperty("JComboBox.isTableCellEditor")) ||

View File

@@ -8,19 +8,15 @@
package com.maddyhome.idea.vim.helper
import com.intellij.openapi.actionSystem.ActionGroup
import com.intellij.execution.actions.StopAction
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.EmptyAction
import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.actionSystem.PlatformDataKeys
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.SimpleDataContext
import com.intellij.openapi.actionSystem.impl.Utils
import com.intellij.openapi.application.ex.ApplicationManagerEx
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.command.UndoConfirmationPolicy
@@ -28,9 +24,7 @@ import com.intellij.openapi.components.Service
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.editor.actionSystem.DocCommandGroupId
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.registry.Registry
import com.maddyhome.idea.vim.RegisterActions
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.NativeAction
@@ -38,14 +32,11 @@ import com.maddyhome.idea.vim.api.VimActionExecutor
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
import com.maddyhome.idea.vim.ide.isRider
import com.maddyhome.idea.vim.newapi.IjNativeAction
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.runFromVimKey
import org.jetbrains.annotations.NonNls
import java.awt.Component
import javax.swing.JComponent
import javax.swing.SwingUtilities
@Service
internal class IjActionExecutor : VimActionExecutor {
@@ -64,8 +55,11 @@ internal class IjActionExecutor : VimActionExecutor {
override val ACTION_EXPAND_REGION_RECURSIVELY: String
get() = IdeActions.ACTION_EXPAND_REGION_RECURSIVELY
override val ACTION_EXPAND_COLLAPSE_TOGGLE: String
// [VERSION UPDATE] 2024.3+ Replace raw "ExpandCollapseToggleAction" with IdeActions.ACTION_EXPAND_COLLAPSE_TOGGLE_REGION from the platform.
get() = "ExpandCollapseToggleAction"
get() = IdeActions.ACTION_EXPAND_COLLAPSE_TOGGLE_REGION
override val ACTION_UNDO: String
get() = IdeActions.ACTION_UNDO
override val ACTION_REDO: String
get() = IdeActions.ACTION_REDO
var isRunningActionFromVim: Boolean = false
@@ -77,78 +71,28 @@ internal class IjActionExecutor : VimActionExecutor {
}
val ijAction = (action as IjNativeAction).action
if (Registry.`is`("ideavim.old.action.execution", true) || isRider()) {
return manualActionExecution(context, ijAction)
} else {
try {
isRunningActionFromVim = true
// The context component should be editor. This is especially important when running the `:action` commands
// because at the moment of execution, the focused component is Ex Field, not editor.
val contextComponent = editor?.ij?.contentComponent
val res = ActionManager.getInstance().tryToExecute(ijAction, null, contextComponent, "IdeaVim", true)
res.waitFor(5_000)
return res.isDone
} finally {
isRunningActionFromVim = false
}
try {
isRunningActionFromVim = true
// The context component should be editor. This is especially important when running the `:action` commands
// because at the moment of execution, the focused component is Ex Field, not editor.
val contextComponent = editor?.ij?.contentComponent
val place = ijAction.choosePlace()
val res = ActionManager.getInstance().tryToExecute(ijAction, null, contextComponent, place, true)
res.waitFor(5_000)
return res.isDone
} finally {
isRunningActionFromVim = false
}
}
private fun manualActionExecution(
context: ExecutionContext,
ijAction: AnAction,
): Boolean {
/**
* Data context that defines that some action was started from IdeaVim.
* You can call use [runFromVimKey] key to define if intellij action was started from 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
}
// Note: We should find a proper place for the IdeaVim actions
// Currently, we use "IdeaVim" except a few actions
private fun AnAction.choosePlace(): String {
// StopAction works fine if `StopAction.isPlaceGlobal` returns true
// Or if there is a specific data stored in the context. This data, however, is stored
// only if the run window is in focus.
if (this is StopAction) return ActionPlaces.ACTION_SEARCH
return "IdeaVim"
}
/**

View File

@@ -1,75 +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 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

@@ -0,0 +1,54 @@
/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package 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,28 +24,6 @@ import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.vim
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 */
internal fun VimEditor.exitSelectMode(adjustCaretPosition: Boolean) {
if (!this.inSelectMode) return

View File

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

View File

@@ -19,6 +19,7 @@ import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.fileEditor.TextEditorWithPreview
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
import com.intellij.openapi.util.registry.Registry
import com.intellij.util.PlatformUtils
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret
@@ -43,6 +44,12 @@ internal class UndoRedoHelper : VimTimestampBasedUndoService {
}
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 project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false
val textEditor = getTextEditor(editor.ij)
@@ -106,6 +113,10 @@ internal class UndoRedoHelper : VimTimestampBasedUndoService {
}
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 project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false
val textEditor = getTextEditor(editor.ij)

View File

@@ -10,8 +10,6 @@ package com.maddyhome.idea.vim.listener
import com.intellij.execution.impl.ConsoleViewImpl
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.LastUsedEditorInfo
import com.maddyhome.idea.vim.VimPlugin
@@ -19,8 +17,8 @@ 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.common.EditorListener
import com.maddyhome.idea.vim.helper.EditorHelper
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.state.mode.Mode
@@ -57,7 +55,7 @@ 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.
val ijEditor = editor.ij
val isCurrentEditorTerminal = isTerminal(ijEditor)
val isCurrentEditorTerminal = ijEditor.isTerminalEditor()
KeyHandler.getInstance().lastUsedEditorInfo = LastUsedEditorInfo(currentEditorHashCode, false)
@@ -66,8 +64,10 @@ class IJEditorFocusListener : EditorListener {
VimPlugin.getChange().insertBeforeCursor(editor, context)
KeyHandler.getInstance().lastUsedEditorInfo = LastUsedEditorInfo(currentEditorHashCode, true)
}
if (isCurrentEditorTerminal && !ijEditor.inInsertMode) {
switchToInsertMode.run()
if (isCurrentEditorTerminal) {
if (!ijEditor.inInsertMode) {
switchToInsertMode.run()
}
} else if (ijEditor.isInsertMode && (oldEditorInfo.isInsertModeForced || !ijEditor.document.isWritable)) {
val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor)
val mode = injector.vimState.mode
@@ -87,12 +87,4 @@ class IJEditorFocusListener : EditorListener {
}
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,6 +20,7 @@ import com.intellij.codeInsight.template.impl.TemplateManagerImpl
import com.intellij.codeInsight.template.impl.TemplateState
import com.intellij.codeInsight.template.impl.actions.NextVariableAction
import com.intellij.find.FindModelListener
import com.intellij.ide.actions.ApplyIntentionAction
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnAction
@@ -99,10 +100,14 @@ internal object IdeaSpecifics {
} else {
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 could filter out action events with `place == ActionPlaces.TOOLWINDOW_TOOLBAR_BAR`
VimPlugin.getNotifications(event.dataContext.getData(CommonDataKeys.PROJECT)).notifyActionId(id, candidates)
VimPlugin.getNotifications(event.dataContext.getData(CommonDataKeys.PROJECT)).notifyActionId(id, candidates, intentionName)
}
}

View File

@@ -9,6 +9,7 @@
package com.maddyhome.idea.vim.listener
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.AnActionResult
@@ -16,6 +17,8 @@ import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.actionSystem.ex.AnActionListener
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.CaretListener
import com.maddyhome.idea.vim.VimPlugin
@@ -24,10 +27,16 @@ import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl
import com.maddyhome.idea.vim.group.visual.moveCaretOneCharLeftFromSelectionEnd
import com.maddyhome.idea.vim.helper.getTopLevelEditor
import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
import com.maddyhome.idea.vim.newapi.vim
internal class RiderActionListener : AnActionListener {
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) {
if (VimPlugin.isNotEnabled()) return
@@ -35,6 +44,12 @@ internal class RiderActionListener : AnActionListener {
if (hostEditor != null) {
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) {

View File

@@ -468,7 +468,9 @@ internal object VimListenerManager {
openingEditor == null -> LocalOptionInitialisationScenario.EDIT
else -> LocalOptionInitialisationScenario.NEW
}
EditorListeners.add(event.editor, openingEditor?.vim ?: injector.fallbackWindow, scenario)
SlowOperations.knownIssue("VIM-3648").use {
EditorListeners.add(event.editor, openingEditor?.vim ?: injector.fallbackWindow, scenario)
}
firstEditorInitialised = true
} else {
// We've got a virtual file, so FileOpenedSyncListener will be called. Save data
@@ -817,7 +819,7 @@ internal object VimListenerManager {
if (editor.inVisualMode) {
editor.vim.exitVisualMode()
} else if (editor.vim.inSelectMode) {
editor.exitSelectMode(false)
editor.vim.exitSelectMode(false)
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)
override val key = BookmarksManager.getInstance(project)?.getType(bookmark)?.mnemonic!!
override val key = BookmarksManager.getInstance(project)?.getType(bookmark)?.mnemonic ?: ' '
override val line: Int
get() = getMark()?.line ?: 0
override val filepath: String

View File

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

View File

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

View File

@@ -35,8 +35,8 @@ import com.maddyhome.idea.vim.api.VimFoldRegion
import com.maddyhome.idea.vim.api.VimIndentConfig
import com.maddyhome.idea.vim.api.VimScrollingModel
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.VirtualFile
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.IndentConfig
import com.maddyhome.idea.vim.common.LiveRange
@@ -301,12 +301,13 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor, VimEditorBase(
return editor.logicalPositionToOffset(logicalPosition)
}
override fun getVirtualFile(): VirtualFile? {
override fun getVirtualFile(): VimVirtualFile? {
val vf = EditorHelper.getVirtualFile(editor)
return vf?.let {
object : VirtualFile {
object : VimVirtualFile {
override val path: String = vf.path
override val protocol: String = vf.fileSystem.protocol
override val extension: String? = vf.extension
}
}
}
@@ -513,6 +514,10 @@ 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 {
return caret
}

View File

@@ -213,16 +213,6 @@ internal class IjVimInjector : VimInjectorBase() {
override val redrawService: VimRedrawService
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
get() = service<IjEditorHelper>()
override val editorGroup: VimEditorGroup

View File

@@ -0,0 +1,99 @@
/*
* 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,14 +428,6 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
return active;
}
/**
* @deprecated Use getVisibleText()
*/
@Deprecated(forRemoval = true)
public @NotNull String getText() {
return entry.getText();
}
@Override
public @NotNull String getVisibleText() {
return entry.getText();

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package 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,6 +132,9 @@
key="ideavim.only.in.editor.component"/>
<registryKey defaultValue="false" description="Old action execution mechanism" key="ideavim.old.action.execution"
restartRequired="false"/>
<editorNotificationProvider
implementation="com.maddyhome.idea.vim.troubleshooting.AccidentalInstallDetectorEditorNotificationProvider"/>
</extensions>
<xi:include href="/META-INF/includes/ApplicationServices.xml" xpointer="xpointer(/idea-plugin/*)"/>

View File

@@ -1,3 +1,4 @@
{
"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,11 +85,23 @@ E545=E545: Missing colon: {0}
E546=E546: Illegal mode: {0}
E548=E548: Digit expected: {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()
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
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
E804=E804: Cannot use '%' with Float
E805=E805: Using a Float as a Number
E806=E806: Using a Float as a String
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
E939=E939: Positive count required

View File

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

View File

@@ -0,0 +1,108 @@
/*
* 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

@@ -0,0 +1,102 @@
/*
* 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,4 +284,206 @@ class MacroActionTest : VimTestCase() {
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,15 +875,6 @@ class MotionActionTest : VimTestCase() {
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|
@TestWithoutNeovim(reason = SkipNeovimReason.UNCLEAR)
@Test

View File

@@ -18,7 +18,6 @@ import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.SelectionType
import org.jetbrains.plugins.ideavim.VimBehaviorDiffers
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import kotlin.test.assertNotNull
@@ -1822,7 +1821,6 @@ $c five six se${c}ven eight
)
}
@Disabled("Action execution in tests is broken for 2024.2")
@Test
fun testInsertNewLineAboveAction() {
typeTextInFile(
@@ -1849,7 +1847,6 @@ $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")
@Test
fun testInsertNewLineAboveActionWithMultipleCaretsInLine() {
@@ -1864,7 +1861,6 @@ $c five six se${c}ven eight
assertState("${c}\nabcde\n${c}\nabcde\n")
}
@Disabled("Action execution in tests is broken for 2024.2")
@Test
fun testInsertNewLineBelowAction() {
typeTextInFile(
@@ -2185,16 +2181,18 @@ rtyfg${c}hzxc"""
val vimEditor = fixture.editor.vim
val context = injector.executionContextManager.getEditorExecutionContext(vimEditor)
injector.registerGroup.storeText(vimEditor, context, '*', "fgh")
ApplicationManager.getApplication().runWriteAction {
VimPlugin.getRegister()
.storeText(
IjVimEditor(editor),
context,
editor.vim.primaryCaret(),
TextRange(16, 19),
SelectionType.CHARACTER_WISE,
false
)
ApplicationManager.getApplication().invokeAndWait {
ApplicationManager.getApplication().runWriteAction {
VimPlugin.getRegister()
.storeText(
IjVimEditor(editor),
context,
editor.vim.primaryCaret(),
TextRange(16, 19),
SelectionType.CHARACTER_WISE,
false
)
}
}
typeText(injector.parser.parseKeys("\"*P"))
val after = "fg${c}hqfg${c}hwe asd zxc rty fg${c}hfgh vbn"

View File

@@ -0,0 +1,557 @@
/*
* 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,7 +9,6 @@
package org.jetbrains.plugins.ideavim.action.change
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
class OperatorActionTest : VimTestCase() {
@@ -154,7 +153,6 @@ class OperatorActionTest : VimTestCase() {
}
@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:
// 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

View File

@@ -85,4 +85,239 @@ class ChangeCaseToggleCharacterActionTest : VimTestCase() {
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")
}
}
}

View File

@@ -12,7 +12,6 @@ package org.jetbrains.plugins.ideavim.action.change.change
import com.maddyhome.idea.vim.state.mode.Mode
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
class ChangeMotionActionTest : VimTestCase() {
@@ -150,9 +149,7 @@ class ChangeMotionActionTest : VimTestCase() {
}
// VIM-276 |c| |F|
@Suppress("unused")
@Test
@Disabled
fun testChangeLinesToBackwards() {
doTest(
"cFc",
@@ -208,4 +205,280 @@ class ChangeMotionActionTest : VimTestCase() {
""".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,4 +47,245 @@ class ChangeNumberDecActionTest : VimTestCase() {
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,4 +19,231 @@ class ChangeNumberIncActionTest : VimTestCase() {
fun `test inc fancy number`() {
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,4 +130,81 @@ class DeleteCharacterLeftActionTest : VimTestCase() {
assertVisualPosition(0, 55)
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,4 +113,31 @@ class DeleteCharacterRightActionTest : VimTestCase() {
// type annotation
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,4 +32,230 @@ class DeleteEndOfLineActionTest : VimTestCase() {
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,6 +16,7 @@ import org.jetbrains.plugins.ideavim.VimTestCase
import org.jetbrains.plugins.ideavim.impl.OptionTest
import org.jetbrains.plugins.ideavim.impl.TraceOptions
import org.jetbrains.plugins.ideavim.impl.VimOption
import org.junit.jupiter.api.Test
@TraceOptions
class DeleteJoinLinesSpacesActionTest : VimTestCase() {
@@ -88,4 +89,280 @@ class DeleteJoinLinesSpacesActionTest : VimTestCase() {
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,4 +257,298 @@ class DeleteMotionActionTest : VimTestCase() {
typeText("dd")
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,4 +52,76 @@ class InsertDeleteInsertedTextActionTest : VimTestCase() {
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,7 +15,6 @@ import com.maddyhome.idea.vim.state.mode.Mode
import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
@@ -85,7 +84,6 @@ class InsertNewLineAboveActionTest : VimTestCase() {
doTest("O", before, after, Mode.INSERT)
}
@Disabled("Action execution in tests is broken for 2024.2")
@Test
fun `test insert new line above with multiple carets`() {
val before = """ I fou${c}nd it in a legendary land

View File

@@ -12,7 +12,6 @@ import com.maddyhome.idea.vim.state.mode.Mode
import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
class InsertNewLineBelowActionTest : VimTestCase() {
@@ -80,7 +79,6 @@ class InsertNewLineBelowActionTest : VimTestCase() {
doTest("o", before, after, Mode.INSERT)
}
@Disabled("Action execution in tests is broken for 2024.2")
@Test
fun `test insert new line below with multiple carets`() {
val before = """ I fou${c}nd it in a legendary land

View File

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

View File

@@ -216,4 +216,294 @@ 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,4 +56,257 @@ class PutTextBeforeCursorActionTest : VimTestCase() {
""".trimIndent()
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,7 +136,6 @@ class MotionDownActionTest : VimTestCase() {
}
@Test
@Disabled
fun `test motion down in visual block mode with dollar motion2`() {
val keys = "i<C-O>d<ESC>"
val before = """

View File

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

View File

@@ -132,9 +132,18 @@ class MultipleCaretsTest : VimTestCase() {
val editor = configureByText(before)
val vimEditor = editor.vim
val context = injector.executionContextManager.getEditorExecutionContext(vimEditor)
ApplicationManager.getApplication().runWriteAction {
VimPlugin.getRegister()
.storeText(vimEditor, context, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false)
ApplicationManager.getApplication().invokeAndWait {
ApplicationManager.getApplication().runWriteAction {
VimPlugin.getRegister()
.storeText(
vimEditor,
context,
vimEditor.primaryCaret(),
TextRange(16, 19),
SelectionType.CHARACTER_WISE,
false
)
}
}
typeText(commandToKeys("pu"))
val after = """
@@ -170,9 +179,18 @@ class MultipleCaretsTest : VimTestCase() {
val editor = configureByText(before)
val vimEditor = editor.vim
val context = injector.executionContextManager.getEditorExecutionContext(vimEditor)
ApplicationManager.getApplication().runWriteAction {
VimPlugin.getRegister()
.storeText(vimEditor, context, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false)
ApplicationManager.getApplication().invokeAndWait {
ApplicationManager.getApplication().runWriteAction {
VimPlugin.getRegister()
.storeText(
vimEditor,
context,
vimEditor.primaryCaret(),
TextRange(16, 19),
SelectionType.CHARACTER_WISE,
false
)
}
}
typeText(commandToKeys("pu"))
val after = """
@@ -209,9 +227,18 @@ class MultipleCaretsTest : VimTestCase() {
val editor = configureByText(before)
val vimEditor = editor.vim
val context = injector.executionContextManager.getEditorExecutionContext(vimEditor)
ApplicationManager.getApplication().runWriteAction {
VimPlugin.getRegister()
.storeText(vimEditor, context, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false)
ApplicationManager.getApplication().invokeAndWait {
ApplicationManager.getApplication().runWriteAction {
VimPlugin.getRegister()
.storeText(
vimEditor,
context,
vimEditor.primaryCaret(),
TextRange(16, 19),
SelectionType.CHARACTER_WISE,
false
)
}
}
typeText(commandToKeys("4pu"))
val after = """
@@ -248,9 +275,18 @@ class MultipleCaretsTest : VimTestCase() {
val editor = configureByText(before)
val vimEditor = editor.vim
val context = injector.executionContextManager.getEditorExecutionContext(vimEditor)
ApplicationManager.getApplication().runWriteAction {
VimPlugin.getRegister()
.storeText(vimEditor, context, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false)
ApplicationManager.getApplication().invokeAndWait {
ApplicationManager.getApplication().runWriteAction {
VimPlugin.getRegister()
.storeText(
vimEditor,
context,
vimEditor.primaryCaret(),
TextRange(16, 19),
SelectionType.CHARACTER_WISE,
false
)
}
}
typeText(commandToKeys("4pu"))
val after = """

View File

@@ -77,4 +77,12 @@ class AsciiCommandTest : VimTestCase() {
enterCommand("ascii")
assertEquals("<⓪> 9450, Hex 24ea, Oct 22352, Digr (0", VimPlugin.getMessage())
}
@Test
fun `test shows custom digraph with 32-bit Unicode codepoint`() {
configureByText("🔴")
enterCommand("digraph cr 128308")
enterCommand("ascii")
assertEquals("<🔴> 128308, Hex 1f534, Oct 372464, Digr cr", VimPlugin.getMessage())
}
}

View File

@@ -202,4 +202,219 @@ class CopyCommandTest : VimTestCase() {
)
assertPluginError(false)
}
@Test
fun `test copy line and undo`() {
configureByText(
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Nunc tincidunt viverra ligula non ${c}scelerisque.
Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor.
""".trimIndent()
)
enterCommand("copy .")
assertState(
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Nunc tincidunt viverra ligula non scelerisque.
${c}Nunc tincidunt viverra ligula non scelerisque.
Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor.
""".trimIndent()
)
typeText("u")
assertState(
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Nunc tincidunt viverra ligula non ${c}scelerisque.
Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor.
""".trimIndent()
)
}
@Test
fun `test copy range and undo`() {
configureByText(
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Morbi nec luctus tortor, id venenatis lacus.
Nunc sit amet tellus vel ${c}purus cursus posuere et at purus.
Ut id dapibus augue.
Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
""".trimIndent()
)
enterCommand("2,3copy $")
assertState(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|Ut id dapibus augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|${c}Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|""".trimMargin()
)
typeText("u")
assertState(
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Morbi nec luctus tortor, id venenatis lacus.
Nunc sit amet tellus vel ${c}purus cursus posuere et at purus.
Ut id dapibus augue.
Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
""".trimIndent()
)
}
@Test
fun `test t command synonym and undo`() {
configureByText(
"""
Line 1
Line 2
Line ${c}3
Line 4
""".trimIndent()
)
enterCommand("t-1")
assertState(
"""
Line 1
Line 2
${c}Line 3
Line 3
Line 4
""".trimIndent()
)
typeText("u")
assertState(
"""
Line 1
Line 2
Line ${c}3
Line 4
""".trimIndent()
)
}
@Test
fun `test copy line and undo with oldundo`() {
configureByText(
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Nunc tincidunt viverra ligula non ${c}scelerisque.
Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor.
""".trimIndent()
)
try {
enterCommand("set oldundo")
enterCommand("copy .")
assertState(
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Nunc tincidunt viverra ligula non scelerisque.
${c}Nunc tincidunt viverra ligula non scelerisque.
Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor.
""".trimIndent()
)
typeText("u")
assertState(
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Nunc tincidunt viverra ligula non ${c}scelerisque.
Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor.
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test copy range and undo with oldundo`() {
configureByText(
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Morbi nec luctus tortor, id venenatis lacus.
Nunc sit amet tellus vel ${c}purus cursus posuere et at purus.
Ut id dapibus augue.
Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
""".trimIndent()
)
try {
enterCommand("set oldundo")
enterCommand("2,3copy $")
assertState(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|Ut id dapibus augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|${c}Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|""".trimMargin()
)
typeText("u")
assertState(
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Morbi nec luctus tortor, id venenatis lacus.
Nunc sit amet tellus vel ${c}purus cursus posuere et at purus.
Ut id dapibus augue.
Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test t command synonym and undo with oldundo`() {
configureByText(
"""
Line 1
Line 2
Line ${c}3
Line 4
""".trimIndent()
)
try {
enterCommand("set oldundo")
enterCommand("t-1")
assertState(
"""
Line 1
Line 2
${c}Line 3
Line 3
Line 4
""".trimIndent()
)
typeText("u")
assertState(
"""
Line 1
Line 2
Line ${c}3
Line 4
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
}

View File

@@ -441,4 +441,233 @@ class DeleteLinesCommandTest : VimTestCase() {
"Ut id dapibus augue.^J"
)
}
@Test
fun `test delete line and undo`() {
configureByText(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|Ut id dapibus${c} augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
enterCommand("d")
assertState(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|${c}Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
typeText("u")
assertState(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|Ut id dapibus${c} augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
}
@Test
fun `test delete multiple lines with range and undo`() {
configureByText(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel ${c}purus cursus posuere et at purus.
|Ut id dapibus augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
enterCommand("2,4d")
assertState(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|${c}Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
typeText("u")
assertState(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel ${c}purus cursus posuere et at purus.
|Ut id dapibus augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
}
@Test
fun `test delete with count and undo`() {
configureByText(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|${c}Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|Ut id dapibus augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
enterCommand("d 3")
assertState(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|${c}Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
typeText("u")
assertState(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|${c}Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|Ut id dapibus augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
}
@Test
fun `test delete line and undo with oldundo`() {
configureByText(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|Ut id dapibus${c} augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
try {
enterCommand("set oldundo")
enterCommand("d")
assertState(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|${c}Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
typeText("u")
assertState(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|Ut id dapibus${c} augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test delete multiple lines with range and undo with oldundo`() {
configureByText(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel ${c}purus cursus posuere et at purus.
|Ut id dapibus augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
try {
enterCommand("set oldundo")
enterCommand("2,4d")
assertState(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|${c}Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
typeText("u")
assertState(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel ${c}purus cursus posuere et at purus.
|Ut id dapibus augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test delete with count and undo with oldundo`() {
configureByText(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|${c}Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|Ut id dapibus augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
try {
enterCommand("set oldundo")
enterCommand("d 3")
assertState(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|${c}Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
typeText("u")
assertState(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|${c}Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|Ut id dapibus augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
} finally {
enterCommand("set nooldundo")
}
}
}

View File

@@ -33,20 +33,26 @@ class DigraphsCommandTest : VimTestCase() {
@Test
fun `test add custom digraph`() {
enterCommand("digraph (0 9450")
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('(', '0'))
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('(', '0').toChar())
}
@Test
fun `test add custom 32-bit digraph`() {
enterCommand("digraph cr 128308")
assertEquals("🔴", String(Character.toChars(injector.digraphGroup.getCharacterForDigraph('c', 'r'))))
}
@Test
fun `test add custom digraph matches reversed characters`() {
enterCommand("digraph (0 9450")
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('0', '('))
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('0', '(').toChar())
}
@Test
fun `test add multiple custom digraphs`() {
enterCommand("digraph (0 9450 (1 9312")
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('(', '0'))
assertEquals('①', injector.digraphGroup.getCharacterForDigraph('(', '1'))
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('(', '0').toChar())
assertEquals('①', injector.digraphGroup.getCharacterForDigraph('(', '1').toChar())
}
@Test
@@ -80,14 +86,14 @@ class DigraphsCommandTest : VimTestCase() {
@Test
fun `test add custom digraph with more than two characters add custom digraph with initial two characters`() {
enterCommand("digraph aaaa 9450")
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('a', 'a'))
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('a', 'a').toChar())
}
@Test
fun `test add custom digraphs until error`() {
enterCommand("digraph (0 9450 (1 9312 (2")
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('(', '0'))
assertEquals('①', injector.digraphGroup.getCharacterForDigraph('(', '1'))
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('(', '0').toChar())
assertEquals('①', injector.digraphGroup.getCharacterForDigraph('(', '1').toChar())
assertPluginError(true)
assertPluginErrorMessageContains("E39: Number expected")
}
@@ -95,15 +101,15 @@ class DigraphsCommandTest : VimTestCase() {
@Test
fun `test custom digraph overwrites existing custom digraph`() {
enterCommand("digraph (0 9450")
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('(', '0'))
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('(', '0').toChar())
enterCommand("digraph (0 10003")
assertEquals('✓', injector.digraphGroup.getCharacterForDigraph('(', '0'))
assertEquals('✓', injector.digraphGroup.getCharacterForDigraph('(', '0').toChar())
}
@Test
fun `test custom digraph overwrites existing default digraph`() {
enterCommand("digraph OK 9450")
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('O', 'K'))
assertEquals('⓪', injector.digraphGroup.getCharacterForDigraph('O', 'K').toChar())
}
@Test
@@ -865,6 +871,244 @@ class DigraphsCommandTest : VimTestCase() {
)
}
@Test
fun `test digraph output with 32-bit custom digraphs`() {
enterCommand("digraph cr 128308") // 🔴
assertCommandOutput(
"digraphs",
"""
|NU ^@ 10 SH ^A 1 SX ^B 2 EX ^C 3 ET ^D 4 EQ ^E 5
|AK ^F 6 BL ^G 7 BS ^H 8 HT ^I 9 LF ^J 10 VT ^K 11
|FF ^L 12 CR ^M 13 SO ^N 14 SI ^O 15 DL ^P 16 D1 ^Q 17
|D2 ^R 18 D3 ^S 19 D4 ^T 20 NK ^U 21 SY ^V 22 EB ^W 23
|CN ^X 24 EM ^Y 25 SB ^Z 26 EC ^[ 27 FS ^\ 28 GS ^] 29
|RS ^^ 30 US ^_ 31 SP 32 Nb # 35 DO $ 36 At @ 64
|<( [ 91 // \ 92 )> ] 93 '> ^ 94 '! ` 96 (! { 123
|!! | 124 !) } 125 '? ~ 126 DT ^? 127 PA <80> 128 HO <81> 129
|BH <82> 130 NH <83> 131 IN <84> 132 NL <85> 133 SA <86> 134 ES <87> 135
|HS <88> 136 HJ <89> 137 VS <8a> 138 PD <8b> 139 PU <8c> 140 RI <8d> 141
|S2 <8e> 142 S3 <8f> 143 DC <90> 144 P1 <91> 145 P2 <92> 146 TS <93> 147
|CC <94> 148 MW <95> 149 SG <96> 150 EG <97> 151 SS <98> 152 GC <99> 153
|SC <9a> 154 CI <9b> 155 ST <9c> 156 OC <9d> 157 PM <9e> 158 AC <9f> 159
|NS   160 !I ¡ 161 ~! ¡ 161 Ct ¢ 162 c| ¢ 162 Pd £ 163
|$$ £ 163 Cu ¤ 164 ox ¤ 164 Ye ¥ 165 Y- ¥ 165 BB ¦ 166
||| ¦ 166 SE § 167 ': ¨ 168 Co © 169 cO © 169 -a ª 170
|<< « 171 NO ¬ 172 -, ¬ 172 -- <ad> 173 Rg ® 174 'm ¯ 175
|-= ¯ 175 DG ° 176 ~o ° 176 +- ± 177 2S ² 178 22 ² 178
|3S ³ 179 33 ³ 179 '' ´ 180 My µ 181 PI ¶ 182 pp ¶ 182
|.M · 183 ~. · 183 ', ¸ 184 1S ¹ 185 11 ¹ 185 -o º 186
|>> » 187 14 ¼ 188 12 ½ 189 34 ¾ 190 ?I ¿ 191 ~? ¿ 191
|A! À 192 A` À 192 A' Á 193 A> Â 194 A^ Â 194 A? Ã 195
|A~ Ã 195 A: Ä 196 A" Ä 196 AA Å 197 A@ Å 197 AE Æ 198
|C, Ç 199 E! È 200 E` È 200 E' É 201 E> Ê 202 E^ Ê 202
|E: Ë 203 E" Ë 203 I! Ì 204 I` Ì 204 I' Í 205 I> Î 206
|I^ Î 206 I: Ï 207 I" Ï 207 D- Ð 208 N? Ñ 209 N~ Ñ 209
|O! Ò 210 O` Ò 210 O' Ó 211 O> Ô 212 O^ Ô 212 O? Õ 213
|O~ Õ 213 O: Ö 214 *X × 215 /\ × 215 O/ Ø 216 U! Ù 217
|U` Ù 217 U' Ú 218 U> Û 219 U^ Û 219 U: Ü 220 Y' Ý 221
|TH Þ 222 Ip Þ 222 ss ß 223 a! à 224 a` à 224 a' á 225
|a> â 226 a^ â 226 a? ã 227 a~ ã 227 a: ä 228 a" ä 228
|aa å 229 a@ å 229 ae æ 230 c, ç 231 e! è 232 e` è 232
|e' é 233 e> ê 234 e^ ê 234 e: ë 235 e" ë 235 i! ì 236
|i` ì 236 i' í 237 i> î 238 i^ î 238 i: ï 239 d- ð 240
|n? ñ 241 n~ ñ 241 o! ò 242 o` ò 242 o' ó 243 o> ô 244
|o^ ô 244 o? õ 245 o~ õ 245 o: ö 246 -: ÷ 247 o/ ø 248
|u! ù 249 u` ù 249 u' ú 250 u> û 251 u^ û 251 u: ü 252
|y' ý 253 th þ 254 y: ÿ 255 y" ÿ 255 A- Ā 256 a- ā 257
|A( Ă 258 a( ă 259 A; Ą 260 a; ą 261 C' Ć 262 c' ć 263
|C> Ĉ 264 c> ĉ 265 C. Ċ 266 c. ċ 267 C< Č 268 c< č 269
|D< Ď 270 d< ď 271 D/ Đ 272 d/ đ 273 E- Ē 274 e- ē 275
|E( Ĕ 276 e( ĕ 277 E. Ė 278 e. ė 279 E; Ę 280 e; ę 281
|E< Ě 282 e< ě 283 G> Ĝ 284 g> ĝ 285 G( Ğ 286 g( ğ 287
|G. Ġ 288 g. ġ 289 G, Ģ 290 g, ģ 291 H> Ĥ 292 h> ĥ 293
|H/ Ħ 294 h/ ħ 295 I? Ĩ 296 i? ĩ 297 I- Ī 298 i- ī 299
|I( Ĭ 300 i( ĭ 301 I; Į 302 i; į 303 I. İ 304 i. ı 305
|IJ IJ 306 ij ij 307 J> Ĵ 308 j> ĵ 309 K, Ķ 310 k, ķ 311
|kk ĸ 312 L' Ĺ 313 l' ĺ 314 L, Ļ 315 l, ļ 316 L< Ľ 317
|l< ľ 318 L. Ŀ 319 l. ŀ 320 L/ Ł 321 l/ ł 322 N' Ń 323
|n' ń 324 N, Ņ 325 n, ņ 326 N< Ň 327 n< ň 328 'n ʼn 329
|NG Ŋ 330 ng ŋ 331 O- Ō 332 o- ō 333 O( Ŏ 334 o( ŏ 335
|O" Ő 336 o" ő 337 OE Œ 338 oe œ 339 R' Ŕ 340 r' ŕ 341
|R, Ŗ 342 r, ŗ 343 R< Ř 344 r< ř 345 S' Ś 346 s' ś 347
|S> Ŝ 348 s> ŝ 349 S, Ş 350 s, ş 351 S< Š 352 s< š 353
|T, Ţ 354 t, ţ 355 T< Ť 356 t< ť 357 T/ Ŧ 358 t/ ŧ 359
|U? Ũ 360 u? ũ 361 U- Ū 362 u- ū 363 U( Ŭ 364 u( ŭ 365
|U0 Ů 366 u0 ů 367 U" Ű 368 u" ű 369 U; Ų 370 u; ų 371
|W> Ŵ 372 w> ŵ 373 Y> Ŷ 374 y> ŷ 375 Y: Ÿ 376 Z' Ź 377
|z' ź 378 Z. Ż 379 z. ż 380 Z< Ž 381 z< ž 382 O9 Ơ 416
|o9 ơ 417 OI Ƣ 418 oi ƣ 419 yr Ʀ 422 U9 Ư 431 u9 ư 432
|Z/ Ƶ 437 z/ ƶ 438 ED Ʒ 439 A< Ǎ 461 a< ǎ 462 I< Ǐ 463
|i< ǐ 464 O< Ǒ 465 o< ǒ 466 U< Ǔ 467 u< ǔ 468 A1 Ǟ 478
|a1 ǟ 479 A7 Ǡ 480 a7 ǡ 481 A3 Ǣ 482 a3 ǣ 483 G/ Ǥ 484
|g/ ǥ 485 G< Ǧ 486 g< ǧ 487 K< Ǩ 488 k< ǩ 489 O; Ǫ 490
|o; ǫ 491 O1 Ǭ 492 o1 ǭ 493 EZ Ǯ 494 ez ǯ 495 j< ǰ 496
|G' Ǵ 500 g' ǵ 501 ;S ʿ 703 '< ˇ 711 '( ˘ 728 '. ˙ 729
|'0 ˚ 730 '; ˛ 731 '" ˝ 733 A% Ά 902 E% Έ 904 Y% Ή 905
|I% Ί 906 O% Ό 908 U% Ύ 910 W% Ώ 911 i3 ΐ 912 A* Α 913
|B* Β 914 G* Γ 915 D* Δ 916 E* Ε 917 Z* Ζ 918 Y* Η 919
|H* Θ 920 I* Ι 921 K* Κ 922 L* Λ 923 M* Μ 924 N* Ν 925
|C* Ξ 926 O* Ο 927 P* Π 928 R* Ρ 929 S* Σ 931 T* Τ 932
|U* Υ 933 F* Φ 934 X* Χ 935 Q* Ψ 936 W* Ω 937 J* Ϊ 938
|V* Ϋ 939 a% ά 940 e% έ 941 y% ή 942 i% ί 943 u3 ΰ 944
|a* α 945 b* β 946 g* γ 947 d* δ 948 e* ε 949 z* ζ 950
|y* η 951 h* θ 952 i* ι 953 k* κ 954 l* λ 955 m* μ 956
|n* ν 957 c* ξ 958 o* ο 959 p* π 960 r* ρ 961 *s ς 962
|s* σ 963 t* τ 964 u* υ 965 f* φ 966 x* χ 967 q* ψ 968
|w* ω 969 j* ϊ 970 v* ϋ 971 o% ό 972 u% ύ 973 w% ώ 974
|'G Ϙ 984 ,G ϙ 985 T3 Ϛ 986 t3 ϛ 987 M3 Ϝ 988 m3 ϝ 989
|K3 Ϟ 990 k3 ϟ 991 P3 Ϡ 992 p3 ϡ 993 '% ϴ 1012 j3 ϵ 1013
|IO Ё 1025 D% Ђ 1026 G% Ѓ 1027 IE Є 1028 DS Ѕ 1029 II І 1030
|YI Ї 1031 J% Ј 1032 LJ Љ 1033 NJ Њ 1034 Ts Ћ 1035 KJ Ќ 1036
|V% Ў 1038 DZ Џ 1039 A= А 1040 B= Б 1041 V= В 1042 G= Г 1043
|D= Д 1044 E= Е 1045 Z% Ж 1046 Z= З 1047 I= И 1048 J= Й 1049
|K= К 1050 L= Л 1051 M= М 1052 N= Н 1053 O= О 1054 P= П 1055
|R= Р 1056 S= С 1057 T= Т 1058 U= У 1059 F= Ф 1060 H= Х 1061
|C= Ц 1062 C% Ч 1063 S% Ш 1064 Sc Щ 1065 =" Ъ 1066 Y= Ы 1067
|%" Ь 1068 JE Э 1069 JU Ю 1070 JA Я 1071 a= а 1072 b= б 1073
|v= в 1074 g= г 1075 d= д 1076 e= е 1077 z% ж 1078 z= з 1079
|i= и 1080 j= й 1081 k= к 1082 l= л 1083 m= м 1084 n= н 1085
|o= о 1086 p= п 1087 r= р 1088 s= с 1089 t= т 1090 u= у 1091
|f= ф 1092 h= х 1093 c= ц 1094 c% ч 1095 s% ш 1096 sc щ 1097
|=' ъ 1098 y= ы 1099 %' ь 1100 je э 1101 ju ю 1102 ja я 1103
|io ё 1105 d% ђ 1106 g% ѓ 1107 ie є 1108 ds ѕ 1109 ii і 1110
|yi ї 1111 j% ј 1112 lj љ 1113 nj њ 1114 ts ћ 1115 kj ќ 1116
|v% ў 1118 dz џ 1119 Y3 Ѣ 1122 y3 ѣ 1123 O3 Ѫ 1130 o3 ѫ 1131
|F3 Ѳ 1138 f3 ѳ 1139 V3 Ѵ 1140 v3 ѵ 1141 C3 Ҁ 1152 c3 ҁ 1153
|G3 Ґ 1168 g3 ґ 1169 A+ ⁧א⁩ 1488 B+ ⁧ב⁩ 1489 G+ ⁧ג⁩ 1490 D+ ⁧ד⁩ 1491
|H+ ⁧ה⁩ 1492 W+ ו 1493 Z+ ⁧ז⁩ 1494 X+ ⁧ח⁩ 1495 Tj ט 1496 J+ י 1497
|K% ⁧ך⁩ 1498 K+ ⁧כ⁩ 1499 L+ ⁧ל⁩ 1500 M% ⁧ם⁩ 1501 M+ ⁧מ⁩ 1502 N% ן 1503
|N+ ⁧נ⁩ 1504 S+ ס 1505 E+ ⁧ע⁩ 1506 P% ⁧ף⁩ 1507 P+ ⁧פ⁩ 1508 Zj ⁧ץ⁩ 1509
|ZJ ⁧צ⁩ 1510 Q+ ⁧ק⁩ 1511 R+ ⁧ר⁩ 1512 Sh ⁧ש⁩ 1513 T+ ⁧ת⁩ 1514 ,+ ، 1548
|;+ ⁧؛⁩ 1563 ?+ ⁧؟⁩ 1567 H' ⁧ء⁩ 1569 aM ⁧آ⁩ 1570 aH ⁧أ⁩ 1571 wH ⁧ؤ⁩ 1572
|ah ⁧إ⁩ 1573 yH ⁧ئ⁩ 1574 a+ ا 1575 b+ ⁧ب⁩ 1576 tm ⁧ة⁩ 1577 t+ ⁧ت⁩ 1578
|tk ⁧ث⁩ 1579 g+ ⁧ج⁩ 1580 hk ⁧ح⁩ 1581 x+ ⁧خ⁩ 1582 d+ ⁧د⁩ 1583 dk ⁧ذ⁩ 1584
|r+ ⁧ر⁩ 1585 z+ ⁧ز⁩ 1586 s+ ⁧س⁩ 1587 sn ⁧ش⁩ 1588 c+ ⁧ص⁩ 1589 dd ⁧ض⁩ 1590
|tj ⁧ط⁩ 1591 zH ⁧ظ⁩ 1592 e+ ⁧ع⁩ 1593 i+ ⁧غ⁩ 1594 ++ ⁧ـ⁩ 1600 f+ ⁧ف⁩ 1601
|q+ ⁧ق⁩ 1602 k+ ⁧ك⁩ 1603 l+ ⁧ل⁩ 1604 m+ ⁧م⁩ 1605 n+ ⁧ن⁩ 1606 h+ ه 1607
|w+ ⁧و⁩ 1608 j+ ⁧ى⁩ 1609 y+ ⁧ي⁩ 1610 :+ ً 1611 "+ ٌ 1612 =+ ٍ 1613
|/+ َ 1614 '+ ُ 1615 1+ ِ 1616 3+ ّ 1617 0+ ْ 1618 aS ٰ 1648
|p+ ⁧پ⁩ 1662 v+ ⁧ڤ⁩ 1700 gf ⁧گ⁩ 1711 0a ۰ 1776 1a ۱ 1777 2a ۲ 1778
|3a ۳ 1779 4a ۴ 1780 5a ۵ 1781 6a ۶ 1782 7a ۷ 1783 8a ۸ 1784
|9a ۹ 1785 B. Ḃ 7682 b. ḃ 7683 B_ Ḇ 7686 b_ ḇ 7687 D. Ḋ 7690
|d. ḋ 7691 D_ Ḏ 7694 d_ ḏ 7695 D, Ḑ 7696 d, ḑ 7697 F. Ḟ 7710
|f. ḟ 7711 G- Ḡ 7712 g- ḡ 7713 H. Ḣ 7714 h. ḣ 7715 H: Ḧ 7718
|h: ḧ 7719 H, Ḩ 7720 h, ḩ 7721 K' Ḱ 7728 k' ḱ 7729 K_ Ḵ 7732
|k_ ḵ 7733 L_ Ḻ 7738 l_ ḻ 7739 M' Ḿ 7742 m' ḿ 7743 M. Ṁ 7744
|m. ṁ 7745 N. Ṅ 7748 n. ṅ 7749 N_ Ṉ 7752 n_ ṉ 7753 P' Ṕ 7764
|p' ṕ 7765 P. Ṗ 7766 p. ṗ 7767 R. Ṙ 7768 r. ṙ 7769 R_ Ṟ 7774
|r_ ṟ 7775 S. Ṡ 7776 s. ṡ 7777 T. Ṫ 7786 t. ṫ 7787 T_ Ṯ 7790
|t_ ṯ 7791 V? Ṽ 7804 v? ṽ 7805 W! Ẁ 7808 W` Ẁ 7808 w! ẁ 7809
|w` ẁ 7809 W' Ẃ 7810 w' ẃ 7811 W: Ẅ 7812 w: ẅ 7813 W. Ẇ 7814
|w. ẇ 7815 X. Ẋ 7818 x. ẋ 7819 X: Ẍ 7820 x: ẍ 7821 Y. Ẏ 7822
|y. ẏ 7823 Z> Ẑ 7824 z> ẑ 7825 Z_ Ẕ 7828 z_ ẕ 7829 h_ ẖ 7830
|t: ẗ 7831 w0 ẘ 7832 y0 ẙ 7833 A2 Ả 7842 a2 ả 7843 E2 Ẻ 7866
|e2 ẻ 7867 E? Ẽ 7868 e? ẽ 7869 I2 Ỉ 7880 i2 ỉ 7881 O2 Ỏ 7886
|o2 ỏ 7887 U2 Ủ 7910 u2 ủ 7911 Y! Ỳ 7922 Y` Ỳ 7922 y! ỳ 7923
|y` ỳ 7923 Y2 Ỷ 7926 y2 ỷ 7927 Y? Ỹ 7928 y? ỹ 7929 ;' ἀ 7936
|,' ἁ 7937 ;! ἂ 7938 ,! ἃ 7939 ?; ἄ 7940 ?, ἅ 7941 !: ἆ 7942
|?: ἇ 7943 1N 8194 1M 8195 3M 8196 4M 8197 6M 8198
|1T 8201 1H 8202 -1 8208 -N 8211 -M — 8212 -3 ― 8213
|!2 ‖ 8214 =2 ‗ 8215 '6 8216 '9 8217 .9 8218 9' 8219
|"6 “ 8220 "9 ” 8221 :9 „ 8222 9" ‟ 8223 /- † 8224 /= ‡ 8225
|oo • 8226 .. ‥ 8229 ,. … 8230 %0 ‰ 8240 1' 8242 2' ″ 8243
|3' ‴ 8244 4' ⁗ 8279 1" 8245 2" ‶ 8246 3" ‷ 8247 Ca ‸ 8248
|<1 8249 >1 8250 :X ※ 8251 '- ‾ 8254 /f 8260 0S ⁰ 8304
|4S ⁴ 8308 5S ⁵ 8309 6S ⁶ 8310 7S ⁷ 8311 8S ⁸ 8312 9S ⁹ 8313
|+S ⁺ 8314 -S ⁻ 8315 =S ⁼ 8316 (S ⁽ 8317 )S ⁾ 8318 nS ⁿ 8319
|0s ₀ 8320 1s ₁ 8321 2s ₂ 8322 3s ₃ 8323 4s ₄ 8324 5s ₅ 8325
|6s ₆ 8326 7s ₇ 8327 8s ₈ 8328 9s ₉ 8329 +s ₊ 8330 -s ₋ 8331
|=s ₌ 8332 (s ₍ 8333 )s ₎ 8334 Li ₤ 8356 Pt ₧ 8359 W= ₩ 8361
|=e € 8364 Eu € 8364 =R ₽ 8381 =P ₽ 8381 oC ℃ 8451 co ℅ 8453
|oF ℉ 8457 N0 № 8470 PO ℗ 8471 Rx ℞ 8478 SM ℠ 8480 TM ™ 8482
|Om Ω 8486 AO Å 8491 13 ⅓ 8531 23 ⅔ 8532 15 ⅕ 8533 25 ⅖ 8534
|35 ⅗ 8535 45 ⅘ 8536 16 ⅙ 8537 56 ⅚ 8538 18 ⅛ 8539 38 ⅜ 8540
|58 ⅝ 8541 78 ⅞ 8542 1R 8544 2R Ⅱ 8545 3R Ⅲ 8546 4R Ⅳ 8547
|5R 8548 6R Ⅵ 8549 7R Ⅶ 8550 8R Ⅷ 8551 9R Ⅸ 8552 aR 8553
|bR Ⅺ 8554 cR Ⅻ 8555 1r 8560 2r ⅱ 8561 3r ⅲ 8562 4r ⅳ 8563
|5r 8564 6r ⅵ 8565 7r ⅶ 8566 8r ⅷ 8567 9r ⅸ 8568 ar 8569
|br ⅺ 8570 cr ⅻ 8571 <- ← 8592 -! ↑ 8593 -> → 8594 -v ↓ 8595
|<> ↔ 8596 UD ↕ 8597 <= ⇐ 8656 => ⇒ 8658 == ⇔ 8660 FA ∀ 8704
|dP ∂ 8706 TE ∃ 8707 /0 ∅ 8709 DE ∆ 8710 NB ∇ 8711 (- ∈ 8712
|-) ∋ 8715 *P ∏ 8719 +Z ∑ 8721 -2 8722 -+ ∓ 8723 *- 8727
|Ob ∘ 8728 Sb ∙ 8729 RT √ 8730 0( ∝ 8733 00 ∞ 8734 -L ∟ 8735
|-V ∠ 8736 PP ∥ 8741 AN ∧ 8743 OR 8744 (U ∩ 8745 )U 8746
|In ∫ 8747 DI ∬ 8748 Io ∮ 8750 .: ∴ 8756 :. ∵ 8757 :R 8758
|:: ∷ 8759 ?1 8764 CG ∾ 8766 ?- ≃ 8771 ?= ≅ 8773 ?2 ≈ 8776
|=? ≌ 8780 HI ≓ 8787 != ≠ 8800 =3 ≡ 8801 =< ≤ 8804 >= ≥ 8805
|<* ≪ 8810 *> ≫ 8811 !< ≮ 8814 !> ≯ 8815 (C ⊂ 8834 )C ⊃ 8835
|(_ ⊆ 8838 )_ ⊇ 8839 0. ⊙ 8857 02 ⊚ 8858 -T ⊥ 8869 .P ⋅ 8901
|:3 ⋮ 8942 .3 ⋯ 8943 Eh ⌂ 8962 <7 ⌈ 8968 >7 ⌉ 8969 7< ⌊ 8970
|7> ⌋ 8971 NI ⌐ 8976 (A ⌒ 8978 TR ⌕ 8981 Iu ⌠ 8992 Il ⌡ 8993
|</ 〈 9001 /> 〉 9002 Vs ␣ 9251 1h ⑀ 9280 3h ⑁ 9281 2h ⑂ 9282
|4h ⑃ 9283 1j ⑆ 9286 2j ⑇ 9287 3j ⑈ 9288 4j ⑉ 9289 1. ⒈ 9352
|2. ⒉ 9353 3. ⒊ 9354 4. ⒋ 9355 5. ⒌ 9356 6. ⒍ 9357 7. ⒎ 9358
|8. ⒏ 9359 9. ⒐ 9360 hh ─ 9472 HH ━ 9473 vv │ 9474 VV ┃ 9475
|3- ┄ 9476 3_ ┅ 9477 3! ┆ 9478 3/ ┇ 9479 4- ┈ 9480 4_ ┉ 9481
|4! ┊ 9482 4/ ┋ 9483 dr ┌ 9484 dR ┍ 9485 Dr ┎ 9486 DR ┏ 9487
|dl ┐ 9488 dL ┑ 9489 Dl ┒ 9490 LD ┓ 9491 ur └ 9492 uR ┕ 9493
|Ur ┖ 9494 UR ┗ 9495 ul ┘ 9496 uL ┙ 9497 Ul ┚ 9498 UL ┛ 9499
|vr ├ 9500 vR ┝ 9501 Vr ┠ 9504 VR ┣ 9507 vl ┤ 9508 vL ┥ 9509
|Vl ┨ 9512 VL ┫ 9515 dh ┬ 9516 dH ┯ 9519 Dh ┰ 9520 DH ┳ 9523
|uh ┴ 9524 uH ┷ 9527 Uh ┸ 9528 UH ┻ 9531 vh ┼ 9532 vH ┿ 9535
|Vh ╂ 9538 VH ╋ 9547 FD 9585 BD ╲ 9586 TB ▀ 9600 LB ▄ 9604
|FB █ 9608 lB ▌ 9612 RB ▐ 9616 .S ░ 9617 :S ▒ 9618 ?S ▓ 9619
|fS ■ 9632 OS □ 9633 RO ▢ 9634 Rr ▣ 9635 RF ▤ 9636 RY ▥ 9637
|RH ▦ 9638 RZ ▧ 9639 RK ▨ 9640 RX ▩ 9641 sB ▪ 9642 SR ▬ 9644
|Or ▭ 9645 UT ▲ 9650 uT △ 9651 PR ▶ 9654 Tr ▷ 9655 Dt ▼ 9660
|dT ▽ 9661 PL ◀ 9664 Tl ◁ 9665 Db ◆ 9670 Dw ◇ 9671 LZ ◊ 9674
|0m ○ 9675 0o ◎ 9678 0M ● 9679 0L ◐ 9680 0R ◑ 9681 Sn ◘ 9688
|Ic ◙ 9689 Fd ◢ 9698 Bd ◣ 9699 *2 ★ 9733 *1 ☆ 9734 <H ☜ 9756
|>H ☞ 9758 0u ☺ 9786 0U ☻ 9787 SU ☼ 9788 Fm ♀ 9792 Ml ♂ 9794
|cS ♠ 9824 cH ♡ 9825 cD ♢ 9826 cC ♣ 9827 Md ♩ 9833 M8 ♪ 9834
|M2 ♫ 9835 Mb ♭ 9837 Mx ♮ 9838 MX ♯ 9839 OK ✓ 10003 XX ✗ 10007
|-X ✠ 10016 IS   12288 ,_ 、 12289 ._ 。 12290 +" 〃 12291 +_ 〄 12292
|*_ 々 12293 ;_ 〆 12294 0_ 12295 <+ 《 12298 >+ 》 12299 <' 「 12300
|>' 」 12301 <" 『 12302 >" 』 12303 (" 【 12304 )" 】 12305 =T 〒 12306
|=_ 〓 12307 (' 12308 )' 12309 (I 〖 12310 )I 〗 12311 -? 〜 12316
|A5 ぁ 12353 a5 あ 12354 I5 ぃ 12355 i5 い 12356 U5 ぅ 12357 u5 う 12358
|E5 ぇ 12359 e5 え 12360 O5 ぉ 12361 o5 お 12362 ka か 12363 ga が 12364
|ki き 12365 gi ぎ 12366 ku く 12367 gu ぐ 12368 ke け 12369 ge げ 12370
|ko こ 12371 go ご 12372 sa さ 12373 za ざ 12374 si し 12375 zi じ 12376
|su す 12377 zu ず 12378 se せ 12379 ze ぜ 12380 so そ 12381 zo ぞ 12382
|ta た 12383 da だ 12384 ti ち 12385 di ぢ 12386 tU っ 12387 tu つ 12388
|du づ 12389 te て 12390 de で 12391 to と 12392 do ど 12393 na な 12394
|ni に 12395 nu ぬ 12396 ne ね 12397 no の 12398 ha は 12399 ba ば 12400
|pa ぱ 12401 hi ひ 12402 bi び 12403 pi ぴ 12404 hu ふ 12405 bu ぶ 12406
|pu ぷ 12407 he へ 12408 be べ 12409 pe ぺ 12410 ho ほ 12411 bo ぼ 12412
|po ぽ 12413 ma ま 12414 mi み 12415 mu む 12416 me め 12417 mo も 12418
|yA ゃ 12419 ya や 12420 yU ゅ 12421 yu ゆ 12422 yO ょ 12423 yo よ 12424
|ra ら 12425 ri り 12426 ru る 12427 re れ 12428 ro ろ 12429 wA ゎ 12430
|wa わ 12431 wi ゐ 12432 we ゑ 12433 wo を 12434 n5 ん 12435 vu ゔ 12436
|"5 ゛ 12443 05 ゜ 12444 *5 ゝ 12445 +5 ゞ 12446 a6 ァ 12449 A6 ア 12450
|i6 ィ 12451 I6 イ 12452 u6 ゥ 12453 U6 ウ 12454 e6 ェ 12455 E6 エ 12456
|o6 ォ 12457 O6 オ 12458 Ka カ 12459 Ga ガ 12460 Ki キ 12461 Gi ギ 12462
|Ku ク 12463 Gu グ 12464 Ke ケ 12465 Ge ゲ 12466 Ko コ 12467 Go ゴ 12468
|Sa サ 12469 Za ザ 12470 Si シ 12471 Zi ジ 12472 Su ス 12473 Zu ズ 12474
|Se セ 12475 Ze ゼ 12476 So ソ 12477 Zo ゾ 12478 Ta タ 12479 Da ダ 12480
|Ti チ 12481 Di ヂ 12482 TU ッ 12483 Tu ツ 12484 Du ヅ 12485 Te テ 12486
|De デ 12487 To ト 12488 Do ド 12489 Na ナ 12490 Ni ニ 12491 Nu ヌ 12492
|Ne ネ 12493 No 12494 Ha ハ 12495 Ba バ 12496 Pa パ 12497 Hi ヒ 12498
|Bi ビ 12499 Pi ピ 12500 Hu フ 12501 Bu ブ 12502 Pu プ 12503 He ヘ 12504
|Be ベ 12505 Pe ペ 12506 Ho ホ 12507 Bo ボ 12508 Po ポ 12509 Ma マ 12510
|Mi ミ 12511 Mu ム 12512 Me メ 12513 Mo モ 12514 YA ャ 12515 Ya ヤ 12516
|YU ュ 12517 Yu ユ 12518 YO ョ 12519 Yo ヨ 12520 Ra ラ 12521 Ri リ 12522
|Ru ル 12523 Re レ 12524 Ro ロ 12525 WA ヮ 12526 Wa ワ 12527 Wi ヰ 12528
|We ヱ 12529 Wo ヲ 12530 N6 ン 12531 Vu ヴ 12532 KA ヵ 12533 KE ヶ 12534
|Va ヷ 12535 Vi ヸ 12536 Ve ヹ 12537 Vo ヺ 12538 .6 ・ 12539 -6 ー 12540
|*6 ヽ 12541 +6 ヾ 12542 b4 ㄅ 12549 p4 ㄆ 12550 m4 ㄇ 12551 f4 ㄈ 12552
|d4 ㄉ 12553 t4 ㄊ 12554 n4 ㄋ 12555 l4 ㄌ 12556 g4 ㄍ 12557 k4 ㄎ 12558
|h4 ㄏ 12559 j4 ㄐ 12560 q4 ㄑ 12561 x4 ㄒ 12562 zh ㄓ 12563 ch ㄔ 12564
|sh ㄕ 12565 r4 ㄖ 12566 z4 ㄗ 12567 c4 ㄘ 12568 s4 ㄙ 12569 a4 ㄚ 12570
|o4 ㄛ 12571 e4 ㄜ 12572 ai ㄞ 12574 ei ㄟ 12575 au ㄠ 12576 ou ㄡ 12577
|an ㄢ 12578 en ㄣ 12579 aN ㄤ 12580 eN ㄥ 12581 er ㄦ 12582 i4 ㄧ 12583
|u4 ㄨ 12584 iu ㄩ 12585 v4 ㄪ 12586 nG ㄫ 12587 gn ㄬ 12588 1c ㈠ 12832
|2c ㈡ 12833 3c ㈢ 12834 4c ㈣ 12835 5c ㈤ 12836 6c ㈥ 12837 7c ㈦ 12838
|8c ㈧ 12839 9c ㈨ 12840 ff ff 64256 fi fi 64257 fl fl 64258 ft ſt 64261
|st st 64262 cr 🔴 128308
""".trimMargin()
)
}
@Test
fun `test digraph output with headers and custom digraphs`() {
enterCommand("digraphs (0 9450 (2 9313 (1 9312")

View File

@@ -13,7 +13,7 @@ import com.intellij.openapi.application.ApplicationManager
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.options
import com.maddyhome.idea.vim.history.HistoryConstants
import com.maddyhome.idea.vim.history.VimHistory
import com.maddyhome.idea.vim.newapi.vim
import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -240,7 +240,7 @@ class GlobalCommandTest : VimTestCase() {
@Test
fun `test check history`() {
VimPlugin.getHistory().clear()
val initialEntries = VimPlugin.getHistory().getEntries(HistoryConstants.COMMAND, 0, 0)
val initialEntries = VimPlugin.getHistory().getEntries(VimHistory.Type.Command, 0, 0)
doTest(
"g/found/d",
initialText,
@@ -252,7 +252,7 @@ class GlobalCommandTest : VimTestCase() {
hard by the torrent of a mountain pass.
""".trimIndent(),
)
val entries = VimPlugin.getHistory().getEntries(HistoryConstants.COMMAND, 0, 0)
val entries = VimPlugin.getHistory().getEntries(VimHistory.Type.Command, 0, 0)
kotlin.test.assertEquals(1, entries.size - initialEntries.size)
val element = entries.last()
kotlin.test.assertEquals("g/found/d", element.entry)

View File

@@ -251,4 +251,207 @@ class JoinLinesCommandTest : VimTestCase() {
""".trimIndent(),
)
}
@Test
fun `test join lines and undo`() {
configureByText(
"""
Line 1
Line ${c}2
Line 3
Line 4
""".trimIndent()
)
enterCommand("j")
assertState(
"""
Line 1
${c}Line 2 Line 3
Line 4
""".trimIndent()
)
typeText("u")
assertState(
"""
Line 1
Line ${c}2
Line 3
Line 4
""".trimIndent()
)
}
@Test
fun `test join range and undo`() {
configureByText(
"""
Line 1
Line ${c}2
Line 3
Line 4
Line 5
""".trimIndent()
)
enterCommand("2,4j")
assertState(
"""
Line 1
${c}Line 2 Line 3 Line 4
Line 5
""".trimIndent()
)
typeText("u")
assertState(
"""
Line 1
Line ${c}2
Line 3
Line 4
Line 5
""".trimIndent()
)
}
@Test
fun `test join with count and undo`() {
configureByText(
"""
${c}Lorem ipsum dolor sit amet,
consectetur adipiscing elit
Sed in orci mauris.
Cras id tellus in ex imperdiet egestas.
""".trimIndent()
)
enterCommand("j 3")
assertState(
"""
${c}Lorem ipsum dolor sit amet, consectetur adipiscing elit Sed in orci mauris.
Cras id tellus in ex imperdiet egestas.
""".trimIndent()
)
typeText("u")
assertState(
"""
${c}Lorem ipsum dolor sit amet,
consectetur adipiscing elit
Sed in orci mauris.
Cras id tellus in ex imperdiet egestas.
""".trimIndent()
)
}
@Test
fun `test join lines and undo with oldundo`() {
configureByText(
"""
Line 1
Line ${c}2
Line 3
Line 4
""".trimIndent()
)
try {
enterCommand("set oldundo")
enterCommand("j")
assertState(
"""
Line 1
${c}Line 2 Line 3
Line 4
""".trimIndent()
)
typeText("u")
assertState(
"""
Line 1
Line ${c}2
Line 3
Line 4
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test join range and undo with oldundo`() {
configureByText(
"""
Line 1
Line ${c}2
Line 3
Line 4
Line 5
""".trimIndent()
)
try {
enterCommand("set oldundo")
enterCommand("2,4j")
assertState(
"""
Line 1
${c}Line 2 Line 3 Line 4
Line 5
""".trimIndent()
)
typeText("u")
assertState(
"""
Line 1
Line ${c}2
Line 3
Line 4
Line 5
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test join with count and undo with oldundo`() {
configureByText(
"""
${c}Lorem ipsum dolor sit amet,
consectetur adipiscing elit
Sed in orci mauris.
Cras id tellus in ex imperdiet egestas.
""".trimIndent()
)
try {
enterCommand("set oldundo")
enterCommand("j 3")
assertState(
"""
${c}Lorem ipsum dolor sit amet, consectetur adipiscing elit Sed in orci mauris.
Cras id tellus in ex imperdiet egestas.
""".trimIndent()
)
typeText("u")
assertState(
"""
${c}Lorem ipsum dolor sit amet,
consectetur adipiscing elit
Sed in orci mauris.
Cras id tellus in ex imperdiet egestas.
""".trimIndent()
)
} finally {
enterCommand("set nooldundo")
}
}
}

View File

@@ -10,6 +10,7 @@ package org.jetbrains.plugins.ideavim.ex.implementation.commands
import com.intellij.idea.TestFor
import com.intellij.testFramework.LoggedErrorProcessor
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.keys
import com.maddyhome.idea.vim.command.MappingMode
@@ -21,6 +22,7 @@ import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
import org.jetbrains.plugins.ideavim.VimTestCase
import org.jetbrains.plugins.ideavim.exceptionMappingOwner
import org.jetbrains.plugins.ideavim.waitAndAssert
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
@@ -105,16 +107,16 @@ class MapCommandTest : VimTestCase() {
enterCommand("imap <C-Down> <C-O>gt")
enterCommand("nmap ,f <Plug>Foo")
enterCommand("nmap <Plug>Foo iHello<Esc>")
enterCommand("imap")
assertExOutput(
assertCommandOutput("imap",
"""
|i <C-Down> <C-O>gt
|i bar <Esc>
|i foo bar
""".trimMargin(),
)
enterCommand("map")
assertExOutput(
assertCommandOutput("map",
"""
| <C-Down> gt
|n <Plug>Foo iHello<Esc>
@@ -142,11 +144,9 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
enterCommand("map")
// Note that Vim doesn't appear to have an order. Items are kinda sorted, but also not. I.e. `m{something}` are
// grouped together, but followed later by `g{something}`. We'll sort by {lhs}, so we're at least consistent
assertExOutput(
assertCommandOutput("map",
"""
| all foo
|n normal foo
@@ -163,9 +163,7 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
enterCommand("nmap")
assertExOutput(
assertCommandOutput("nmap",
"""
| all foo
|n normal foo
@@ -178,9 +176,7 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
enterCommand("vmap")
assertExOutput(
assertCommandOutput("vmap",
"""
| all foo
|s select foo
@@ -195,9 +191,7 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
enterCommand("smap")
assertExOutput(
assertCommandOutput("smap",
"""
| all foo
|s select foo
@@ -211,9 +205,7 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
enterCommand("xmap")
assertExOutput(
assertCommandOutput("xmap",
"""
| all foo
|x visual foo
@@ -227,9 +219,7 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
enterCommand("omap")
assertExOutput(
assertCommandOutput("omap",
"""
| all foo
|o op-pending foo
@@ -242,9 +232,7 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
enterCommand("map!")
assertExOutput(
assertCommandOutput("map!",
"""
|c cmdline foo
|i insert foo
@@ -267,9 +255,7 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
enterCommand("imap")
assertExOutput(
assertCommandOutput("imap",
"""
|i insert foo
|! insert+cmdline foo
@@ -283,9 +269,7 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
enterCommand("lmap")
assertExOutput(
assertCommandOutput("lmap",
"""
|l lang foo
""".trimMargin()
@@ -297,9 +281,7 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
addTestMaps()
enterCommand("cmap")
assertExOutput(
assertCommandOutput("cmap",
"""
|c cmdline foo
|! insert+cmdline foo
@@ -313,10 +295,9 @@ class MapCommandTest : VimTestCase() {
addTestMaps() // Adds a mapping of all for NVO
enterCommand("sunmap all") // Removes Select from the NVO mapping for foo
enterCommand("map")
// Note that the formatting is exactly how Vim shows it. Messy, isn't it?
assertExOutput(
assertCommandOutput("map",
"""
|noxall foo
|n normal foo
@@ -334,9 +315,8 @@ class MapCommandTest : VimTestCase() {
addTestMaps() // Adds a mapping of all for NVO
enterCommand("vunmap all") // Removes Visual+Select from the NVO mapping for foo
enterCommand("map")
assertExOutput(
assertCommandOutput("map",
"""
|no all foo
|n normal foo
@@ -355,8 +335,7 @@ class MapCommandTest : VimTestCase() {
enterCommand("vmap foo baz") // Visual, Select
// Just to be sure we're set up correctly
enterCommand("map")
assertExOutput(
assertCommandOutput("map",
"""
|no foo bar
|v foo baz
@@ -366,8 +345,7 @@ class MapCommandTest : VimTestCase() {
enterCommand("sunmap foo")
enterCommand("ounmap foo")
enterCommand("map")
assertExOutput(
assertCommandOutput("map",
"""
|n foo bar
|x foo baz
@@ -383,8 +361,7 @@ class MapCommandTest : VimTestCase() {
enterCommand("nmap fee bap")
enterCommand("nmap zzz ppp")
enterCommand("map f")
assertExOutput(
assertCommandOutput("map f",
"""
|n fee bap
| foo bar
@@ -397,8 +374,7 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
enterCommand("map foo bar")
enterCommand("map ")
assertExOutput(
assertCommandOutput("map ",
"""
| foo bar
""".trimMargin()
@@ -410,8 +386,7 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
enterCommand("imap foo bar")
enterCommand("imap f ")
assertExOutput(
assertCommandOutput("imap f ",
"""
|i foo bar
""".trimMargin()
@@ -429,7 +404,7 @@ class MapCommandTest : VimTestCase() {
}
@Test
fun testddWithMapping() {
fun `test dd with mapping starting with d`() {
configureByText(
"""
Hello$c 1
@@ -460,8 +435,8 @@ class MapCommandTest : VimTestCase() {
configureByText("\n")
enterCommand("inoremap jj <Esc>")
enterCommand("imap foo bar")
enterCommand("imap")
assertExOutput(
assertCommandOutput("imap",
"""
|i foo bar
|i jj * <Esc>
@@ -491,8 +466,7 @@ class MapCommandTest : VimTestCase() {
""".trimIndent(),
)
assertOffset(1)
enterCommand("nmap")
assertExOutput("n <Right> * <Nop>")
assertCommandOutput("nmap", "n <Right> * <Nop>")
}
@Test
@@ -505,8 +479,8 @@ class MapCommandTest : VimTestCase() {
enterCommand("nmap <script> ,e /e<CR>")
enterCommand("nmap <expr> ,f '/f<CR>'")
enterCommand("nmap <unique> ,g /g<CR>")
enterCommand("nmap")
assertExOutput(
assertCommandOutput("nmap",
"""
|n ,a /a<CR>
|n ,b /b<CR>
@@ -613,8 +587,8 @@ class MapCommandTest : VimTestCase() {
typeText("i" + "#" + "<Esc>")
assertState("#\n")
assertMode(Mode.NORMAL())
enterCommand("imap")
assertExOutput("i # * X<C-H>#")
assertCommandOutput("imap", "i # * X<C-H>#")
}
// VIM-679 |:map|
@@ -638,8 +612,7 @@ class MapCommandTest : VimTestCase() {
""".trimIndent(),
)
assertMode(Mode.NORMAL())
enterCommand("map")
assertExOutput(" <C-X>i dd")
assertCommandOutput("map", " <C-X>i dd")
typeText("<C-X>i")
assertState("bar\n")
}
@@ -754,6 +727,59 @@ class MapCommandTest : VimTestCase() {
assertState("Bye\n")
}
@Test
fun `test map applies longest mapping`() {
configureByText("\n")
enterCommand("imap ab AB")
enterCommand("imap abcd ABCD")
typeText("i", "abcd", "<Esc>")
assertState("ABCD\n")
}
@Test
fun `test map falls back to previous longest mapping when abandoned`() {
configureByText("\n")
enterCommand("imap abc ABC")
enterCommand("imap abcd ABCD")
typeText("i", "abcg", "<Esc>")
assertState("ABCg\n")
}
@Test
fun `test map falls back to previous longest mapping when abandoned with shorter prefix`() {
configureByText("\n")
enterCommand("imap ab AB")
enterCommand("imap abcd ABCD")
typeText("i", "abcg", "<Esc>")
assertState("ABcg\n")
}
@Test
fun `test map falls back to previous longest mapping after timeout`() {
configureByText("\n")
enterCommand("imap ab AB")
enterCommand("imap abcd ABCD")
enterCommand("set timeoutlen=100")
typeText("i", "abc")
waitAndAssert(injector.globalOptions().timeoutlen + 100) {
fixture.editor.document.text == "ABc\n"
}
assertState("ABc\n")
}
@Test
fun `test map falls back to previous longest mapping after timeout with shorter prefix`() {
configureByText("\n")
enterCommand("imap ab AB")
enterCommand("imap abcde ABCDE")
enterCommand("set timeoutlen=100")
typeText("i", "abcd")
waitAndAssert(injector.globalOptions().timeoutlen + 100) {
fixture.editor.document.text == "ABcd\n"
}
assertState("ABcd\n")
}
@TestWithoutNeovim(SkipNeovimReason.PLUG)
@Test
fun testPlugMapping() {
@@ -773,16 +799,6 @@ class MapCommandTest : VimTestCase() {
assertState("123${c}7890")
}
@TestWithoutNeovim(SkipNeovimReason.PLUG)
@Test
fun testIncompleteMapping() {
configureByText("123${c}4567890")
enterCommand("map <Plug>(Hi)l lll")
enterCommand("map I <Plug>(Hi)")
typeText("Ih")
assertState("12${c}34567890")
}
@Test
fun testIntersectingCommands2() {
configureByText("123${c}4567890")
@@ -791,6 +807,86 @@ class MapCommandTest : VimTestCase() {
assertState("123${c}567890")
}
@Test
fun `test partial Plug mapping`() {
doTest(
listOf("i", "Xy"),
"Lorem $c ipsum dolor sit amet",
"Lorem Hello ipsum dolor sit amet",
Mode.INSERT
) {
enterCommand("imap <Plug>xy Hello")
enterCommand("imap X <Plug>x")
}
}
@Test
fun `test abandoned Plug mapping replays all keys as text`() {
// The default Vim behaviour for an abandoned mapping is to replay it, character by character, even if that makes no
// sense for the mode or mapping, and will move the caret or delete text or whatever. This is also true for <Plug>
// mappings, even though `<Plug>` is a special char that can't be typed by the user. When used in Insert mode, Vim
// expands the special char to the string "<Plug>".
doTest(
listOf("i", "Xz"),
"Lorem $c ipsum dolor sit amet",
"Lorem <Plug>xz ipsum dolor sit amet",
Mode.INSERT
) {
enterCommand("imap <Plug>xy Hello")
enterCommand("imap X <Plug>x")
}
}
@Test
fun `test partial Action mapping`() {
doTest(
listOf("i", "X(EditorToggleCase)"),
"Lorem ${c}ipsum dolor sit amet",
"Lorem IPSUM dolor sit amet",
Mode.INSERT
) {
enterCommand("imap X <Action>")
}
}
@Test
fun `test abandoned Action mapping replays all keys as text`() {
// The mapping is looking for `<Action>(...)`, so we need to feed it a key that is not part of this mapping. A space
// char will work
doTest(
listOf("i", "X( "),
"Lorem $c ipsum dolor sit amet",
"Lorem <Action>( ipsum dolor sit amet",
Mode.INSERT
) {
enterCommand("imap X <Action>")
}
}
@Test
fun `test abandoned Action mapping replays all keys as text 2`() {
doTest(
listOf("i", "Xz"),
"Lorem $c ipsum dolor sit amet",
"Lorem <Action>z ipsum dolor sit amet",
Mode.INSERT
) {
enterCommand("imap X <Action>")
}
}
@Test
fun `test timedout Action mapping replays all keys as text`() {
configureByText("Lorem $c ipsum dolor sit amet")
enterCommand("imap X <Action>")
enterCommand("set timeoutlen=100")
typeText("i", "X")
waitAndAssert(injector.globalOptions().timeoutlen + 100) {
fixture.editor.document.text == "Lorem <Action> ipsum dolor sit amet"
}
assertState("Lorem <Action> ipsum dolor sit amet")
}
@TestWithoutNeovim(reason = SkipNeovimReason.DIFFERENT)
@Test
fun testMapZero() {
@@ -903,7 +999,7 @@ class MapCommandTest : VimTestCase() {
@Test
fun `test rhc with triangle brackets`() {
fun `test rhs with triangle brackets`() {
configureByText("\n")
enterCommand("inoremap p <p>")
typeText("ip")
@@ -942,8 +1038,8 @@ class MapCommandTest : VimTestCase() {
enterCommand("nmap ,g :action Back<C-M>")
enterCommand("nmap ,h :action Back<C-m>")
enterCommand("nmap ,i :action Back<c-m>")
enterCommand("nmap")
assertExOutput(
assertCommandOutput("nmap",
"""
|n ,a <Action>(Back)
|n ,b <Action>(Back)
@@ -970,8 +1066,8 @@ class MapCommandTest : VimTestCase() {
enterCommand("nnoremap ,g :action Back<C-M>")
enterCommand("nnoremap ,h :action Back<C-m>")
enterCommand("nnoremap ,i :action Back<c-m>")
enterCommand("nnoremap")
assertExOutput(
assertCommandOutput("nnoremap",
"""
|n ,a <Action>(Back)
|n ,b <Action>(Back)

View File

@@ -14,6 +14,42 @@ import org.junit.jupiter.api.Test
@Suppress("SpellCheckingInspection")
class MoveCommandTest : VimTestCase() {
@Test
fun `test move line up and undo`() {
configureByText(
"""
====
My mother taught me this trick: if you repeat something over and over again it loses its meaning.
For example: homewor${c}k, homework, homework, homework, homework, homework, homework, homework, homework.
See, nothing.
""".trimIndent(),
)
enterCommand("m 0")
assertState(
"""
${c}For example: homework, homework, homework, homework, homework, homework, homework, homework, homework.
====
My mother taught me this trick: if you repeat something over and over again it loses its meaning.
See, nothing.
""".trimIndent(),
)
typeText("u")
assertState(
"""
====
My mother taught me this trick: if you repeat something over and over again it loses its meaning.
For example: homewor${c}k, homework, homework, homework, homework, homework, homework, homework, homework.
See, nothing.
""".trimIndent(),
)
}
@Test
fun `test selection marks after moving line up`() {
configureByText(
@@ -148,7 +184,7 @@ class MoveCommandTest : VimTestCase() {
enterCommand("m 0")
assertState(
"""
For example: homewor${c}k, homework, homework, homework, homework, homework, homework, homework, homework.
${c}For example: homework, homework, homework, homework, homework, homework, homework, homework, homework.
====
My mother taught me this trick: if you repeat something over and over again it loses its meaning.
See, nothing.
@@ -173,8 +209,8 @@ class MoveCommandTest : VimTestCase() {
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|Ut id dapibus augue.
|${c}Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
|${c}Ut id dapibus augue.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
}
@@ -194,7 +230,7 @@ class MoveCommandTest : VimTestCase() {
assertState(
"""
For example: homework, homework, homework, homework, homework, homework, homework, homework, homework.
See, nothing.
${c}See, nothing.
====
My mother taught me this trick: if you repeat something over and over again it loses its meaning.
""".trimIndent(),
@@ -216,7 +252,7 @@ class MoveCommandTest : VimTestCase() {
"""
====
My mother taught me this trick: if you repeat something over and over again it loses its meaning.
See, not${c}hing.
${c}See, nothing.
For example: homework, homework, homework, homework, homework, homework, homework, homework, homework.
""".trimIndent(),
)
@@ -238,8 +274,144 @@ class MoveCommandTest : VimTestCase() {
====
My mother taught me this trick: if you repeat something over and over again it loses its meaning.
See, nothing.
For example: homewor${c}k, homework, homework, homework, homework, homework, homework, homework, homework.
${c}For example: homework, homework, homework, homework, homework, homework, homework, homework, homework.
""".trimIndent(),
)
}
// VIM-3837
@Test
fun `test moving relative line positions caret correctly`() {
doTest(
exCommand("+2m."), // Move the line 2 lines below, to below the current line
"""
|2
|1
|${c}3
|1
|2
""".trimMargin(),
"""
|2
|1
|3
|${c}2
|1
""".trimMargin()
)
}
@Test
fun `test moving relative line positions caret correctly 2`() {
doTest(
exCommand("+2m."), // Move the line 2 lines below, to below the current line
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet ${c}tellus vel purus cursus posuere et at purus.
|Ut id dapibus augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin(),
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|${c}Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Ut id dapibus augue.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
}
@Test
fun `test moving lines positions caret correctly with nostartofline option`() {
doTest(
exCommand("+2m."), // Move the line 2 lines below, to below the current line
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet ${c}tellus vel purus cursus posuere et at purus.
|Ut id dapibus augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin(),
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|Orci varius na${c}toque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Ut id dapibus augue.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
) {
enterCommand("set nostartofline")
}
}
@Test
fun `test moving lines positions caret correctly with nostartofline option on shorter line`() {
doTest(
exCommand("+2m."), // Move the line 2 lines below, to below the current line
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id ${c}venenatis lacus.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|Ut id dapibus augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin(),
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|Morbi nec luctus tortor, id venenatis lacus.
|Ut id dapibus augue${c}.
|Nunc sit amet tellus vel purus cursus posuere et at purus.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
) {
enterCommand("set nostartofline")
}
}
@Test
fun `test move line up and undo with oldundo`() {
configureByText(
"""
====
My mother taught me this trick: if you repeat something over and over again it loses its meaning.
For example: homewor${c}k, homework, homework, homework, homework, homework, homework, homework, homework.
See, nothing.
""".trimIndent(),
)
try {
enterCommand("set oldundo")
enterCommand("m 0")
assertState(
"""
${c}For example: homework, homework, homework, homework, homework, homework, homework, homework, homework.
====
My mother taught me this trick: if you repeat something over and over again it loses its meaning.
See, nothing.
""".trimIndent(),
)
typeText("u")
assertState(
"""
====
My mother taught me this trick: if you repeat something over and over again it loses its meaning.
For example: homewor${c}k, homework, homework, homework, homework, homework, homework, homework, homework.
See, nothing.
""".trimIndent(),
)
} finally {
enterCommand("set nooldundo")
}
}
}

View File

@@ -8,33 +8,40 @@
package org.jetbrains.plugins.ideavim.ex.implementation.commands
/*
class NormalCommandTest : VimTestCase() {
import com.maddyhome.idea.vim.state.mode.Mode
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Test
class NormalCommandTest : VimTestCase() {
@Test
fun `test simple execution`() {
doTest("normal x", "123<caret>456", "123<caret>56")
doTest(exCommand("normal x"), "123<caret>456", "123<caret>56")
}
@Test
fun `test short command`() {
doTest("norm x", "123<caret>456", "123<caret>56")
doTest(exCommand("norm x"), "123<caret>456", "123<caret>56")
}
@Test
fun `test normal command automatically exits Insert mode`() {
doTest(exCommand("normal iFoo"), "123<caret>456", "123Fo<caret>o456", Mode.NORMAL())
}
@Test
fun `test multiple commands`() {
doTest("normal xiNewText", "123<caret>456", "123NewTex<caret>t56")
doTest(exCommand("normal xiNewText"), "123<caret>456", "123NewTex<caret>t56")
}
@Test
fun `test range single stroke`() {
doTest(".norm x", "123<caret>456", "<caret>23456")
fun `test normal command with current line range moves caret to start of line before executing command`() {
doTest(exCommand(".norm x"), "123<caret>456", "<caret>23456")
}
@Test
fun `test range multiple strokes`() {
fun `test normal command with multi-line range`() {
doTest(
"1,3norm x",
exCommand("1,3norm x"),
"""
123456
123456
@@ -53,7 +60,7 @@ class NormalCommandTest : VimTestCase() {
}
@Test
fun `test with mapping`() {
fun `test normal command with single letter mapping`() {
configureByText(
"""
<caret>123456
@@ -61,8 +68,8 @@ class NormalCommandTest : VimTestCase() {
123456
""".trimIndent()
)
typeText(commandToKeys("map G dd"))
typeText(commandToKeys("normal G"))
enterCommand("map G dd")
enterCommand("normal G")
assertState(
"""
<caret>123456
@@ -72,7 +79,28 @@ class NormalCommandTest : VimTestCase() {
}
@Test
fun `test with disabled mapping`() {
fun `test normal command with multi-letter mapping`() {
doTest(
exCommand("normal dd"),
"""
|${c}Lorem ipsum dolor sit amet,
|consectetur adipiscing elit
|Sed in orci mauris.
|Cras id tellus in ex imperdiet egestas.
""".trimMargin(),
"""
|Lorem ipsum dolor sit amet,
|consectetur adipiscing elit
|Sed in orci mauris.
|${c}Cras id tellus in ex imperdiet egestas.
""".trimMargin(),
) {
enterCommand("map dd G")
}
}
@Test
fun `test normal command with disabled mapping`() {
configureByText(
"""
<caret>123456
@@ -80,8 +108,8 @@ class NormalCommandTest : VimTestCase() {
123456
""".trimIndent()
)
typeText(commandToKeys("map G dd"))
typeText(commandToKeys("normal! G"))
enterCommand("map G dd")
enterCommand("normal! G")
assertState(
"""
123456
@@ -92,7 +120,7 @@ class NormalCommandTest : VimTestCase() {
}
@Test
fun `test from visual mode`() {
fun `test normal from Visual mode runs command on start of each line in range`() {
configureByText(
"""
<caret>123456
@@ -102,8 +130,8 @@ class NormalCommandTest : VimTestCase() {
123456
""".trimIndent()
)
typeText(parseKeys("Vjj"))
typeText(commandToKeys("normal x"))
typeText("Vjj")
enterCommand("normal x") // Will give `:'<,'>normal x`
assertState(
"""
23456
@@ -116,7 +144,7 @@ class NormalCommandTest : VimTestCase() {
}
@Test
fun `test execute visual mode`() {
fun `test normal command switches to Visual mode`() {
configureByText(
"""
<caret>123456
@@ -126,8 +154,8 @@ class NormalCommandTest : VimTestCase() {
123456
""".trimIndent()
)
typeText(commandToKeys("normal Vjj"))
typeText(parseKeys("x"))
enterCommand("normal Vjj")
typeText("x")
assertState(
"""
<caret>123456
@@ -148,8 +176,8 @@ class NormalCommandTest : VimTestCase() {
123456
""".trimIndent()
)
typeText(parseKeys("qqxq", "jVjjj"))
typeText(commandToKeys("norm @q"))
typeText("qqxq", "jVjjj")
enterCommand("norm @q")
assertState(
"""
23456
@@ -165,29 +193,268 @@ class NormalCommandTest : VimTestCase() {
@Test
fun `test command executes at selection start`() {
configureByText("hello <caret>world !")
typeText(parseKeys("vw"))
typeText(parseKeys(":<C-u>norm x<CR>"))
typeText("vw")
enterCommand("<C-u>norm x")
assertState("hello <caret>orld !")
}
@Test
fun `test false escape`() {
configureByText("hello <caret>world !")
typeText(commandToKeys("norm i<Esc>"))
enterCommand("norm i<Esc>")
assertState("hello <Esc<caret>>world !")
}
@Test
fun `test C-R`() {
configureByText("myprop: \"my value\"")
typeText(commandToKeys("exe \"norm ^dei-\\<C-R>\\\"-\""))
assertState("-myprop-: \"my value\"")
configureByText("""myprop: "my value"""")
enterCommand("""exe "norm ^dei-\<C-R>\"-"""")
assertState("""-myprop-: "my value"""")
}
private fun doTest(command: String, before: String, after: String) {
myFixture.configureByText("a.java", before)
typeText(commandToKeys(command))
myFixture.checkResult(after)
@Test
fun `test normal command delete and undo`() {
configureByText(
"""
|Line 1
|Line ${c}2
|Line 3
""".trimMargin()
)
enterCommand("normal x")
assertState(
"""
|Line 1
|Line
|Line 3
""".trimMargin()
)
typeText("u")
assertState(
"""
|Line 1
|Line ${c}2
|Line 3
""".trimMargin()
)
}
@Test
fun `test normal command with range and undo`() {
configureByText(
"""
|First ${c}line
|Second line
|Third line
|Fourth line
""".trimMargin()
)
enterCommand("2,3normal A!")
assertState(
"""
|First line
|Second line!
|Third line${c}!
|Fourth line
""".trimMargin()
)
typeText("u")
assertState(
"""
|First ${c}line
|Second line
|Third line
|Fourth line
""".trimMargin()
)
}
@Test
fun `test normal command insert and undo`() {
configureByText(
"""
|${c}Hello world
""".trimMargin()
)
enterCommand("normal iTest ")
assertState(
"""
|Test${c} Hello world
""".trimMargin()
)
typeText("u")
assertState(
"""
|${c}Hello world
""".trimMargin()
)
}
@Test
fun `test normal command complex operation and undo`() {
configureByText(
"""
|Line ${c}1
|Line 2
|Line 3
""".trimMargin()
)
enterCommand("normal ddp")
assertState(
"""
|Line 2
|${c}Line 1
|Line 3
""".trimMargin()
)
typeText("u")
assertState(
"""
|Line ${c}1
|Line 2
|Line 3
""".trimMargin()
)
}
@Test
fun `test normal command delete and undo with oldundo`() {
configureByText(
"""
|Line 1
|Line ${c}2
|Line 3
""".trimMargin()
)
try {
enterCommand("set oldundo")
enterCommand("normal x")
assertState(
"""
|Line 1
|Line
|Line 3
""".trimMargin()
)
typeText("u")
assertState(
"""
|Line 1
|Line ${c}2
|Line 3
""".trimMargin()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test normal command with range and undo with oldundo`() {
configureByText(
"""
|First ${c}line
|Second line
|Third line
|Fourth line
""".trimMargin()
)
try {
enterCommand("set oldundo")
enterCommand("2,3normal A!")
assertState(
"""
|First line
|Second line!
|Third line${c}!
|Fourth line
""".trimMargin()
)
typeText("u")
assertState(
"""
|First ${c}line
|Second line
|Third line
|Fourth line
""".trimMargin()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test normal command insert and undo with oldundo`() {
configureByText(
"""
|${c}Hello world
""".trimMargin()
)
try {
enterCommand("set oldundo")
enterCommand("normal iTest ")
assertState(
"""
|Test${c} Hello world
""".trimMargin()
)
typeText("u")
assertState(
"""
|${c}Hello world
""".trimMargin()
)
} finally {
enterCommand("set nooldundo")
}
}
@Test
fun `test normal command complex operation and undo with oldundo`() {
configureByText(
"""
|Line ${c}1
|Line 2
|Line 3
""".trimMargin()
)
try {
enterCommand("set oldundo")
enterCommand("normal ddp")
assertState(
"""
|Line 2
|${c}Line 1
|Line 3
""".trimMargin()
)
typeText("u")
assertState(
"""
|Line ${c}1
|Line 2
|Line 3
""".trimMargin()
)
} finally {
enterCommand("set nooldundo")
}
}
}
*/

View File

@@ -0,0 +1,106 @@
/*
* 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.ex.implementation.commands
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Test
class PrintLineNumberTest : VimTestCase() {
@Test
fun `test print last line number`() {
configureByLines(10, "Lorem ipsum dolor sit amet")
enterCommand("=")
assertStatusLineMessageContains("10")
}
@Test
fun `test print current line number`() {
configureByLines(10, "Lorem ipsum dolor sit amet")
typeText("4j")
enterCommand(".=")
assertStatusLineMessageContains("5")
}
@Test
fun `test print specific line number`() {
configureByLines(10, "Lorem ipsum dolor sit amet")
enterCommand("7=")
assertStatusLineMessageContains("7")
}
@Test
fun `test print line number of last part of range`() {
configureByLines(10, "Lorem ipsum dolor sit amet")
enterCommand("1,5=")
assertStatusLineMessageContains("5")
}
@Test
fun `test trailing characters raises an error`() {
configureByLines(10, "Lorem ipsum dolor sit amet")
enterCommand("=foo")
assertPluginError(true)
assertPluginErrorMessageContains("E488: Trailing characters: foo")
}
@Test
fun `test # flag prints line content and number`() {
configureByText("""
|Lorem ipsum dolor sit amet
|...consectetur adipiscing elit
|Maecenas efficitur nec odio vel malesuada
""".trimMargin().dotToTab())
enterCommand("2=#")
assertStatusLineMessageContains("2 \t\t\tconsectetur adipiscing elit")
}
@Test
fun `test l flag prints line content as printable string and number`() {
configureByText("""
|Lorem ipsum dolor sit amet
|...consectetur adipiscing elit
|Maecenas efficitur nec odio vel malesuada
""".trimMargin().dotToTab())
enterCommand("2=l")
assertStatusLineMessageContains("2 ^I^I^Iconsectetur adipiscing elit")
}
@Test
fun `test l and p flags print line content as printable string and number`() {
configureByText("""
|Lorem ipsum dolor sit amet
|...consectetur adipiscing elit
|Maecenas efficitur nec odio vel malesuada
""".trimMargin().dotToTab())
enterCommand("2=lp")
assertStatusLineMessageContains("2 ^I^I^Iconsectetur adipiscing elit")
}
@Test
fun `test p flag prints line content and number`() {
configureByText("""
|Lorem ipsum dolor sit amet
|...consectetur adipiscing elit
|Maecenas efficitur nec odio vel malesuada
""".trimMargin().dotToTab())
enterCommand("2=p")
assertStatusLineMessageContains("2 \t\t\tconsectetur adipiscing elit")
}
@Test
fun `test p and # flag prints line content and number`() {
configureByText("""
|Lorem ipsum dolor sit amet
|...consectetur adipiscing elit
|Maecenas efficitur nec odio vel malesuada
""".trimMargin().dotToTab())
enterCommand("2=p#")
assertStatusLineMessageContains("2 \t\t\tconsectetur adipiscing elit")
}
}

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