1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-08-19 13:24:56 +02:00

Compare commits

..

112 Commits

Author SHA1 Message Date
822dad52f3 Set plugin version to chylex-41 2024-09-05 07:40:10 +02:00
bf12c98880 Exit insert mode after refactoring 2024-09-05 07:40:10 +02:00
c02bd15e4f Add action to run last macro in all opened files 2024-09-05 07:27:53 +02:00
5957d5d681 Stop macro execution after a failed search 2024-09-05 07:27:53 +02:00
9445afb555 Revert per-caret registers 2024-09-05 07:27:53 +02:00
6e115bdf64 Revert "Factor disposable objects on editor opening"
This reverts commit 1fa78935
2024-09-05 07:27:53 +02:00
6305c412b5 Fix(VIM-3364): Exception with mapped Generate action 2024-09-05 07:27:53 +02:00
be2eabe3b9 Apply scrolloff after executing native IDEA actions 2024-09-05 07:27:53 +02:00
de13e4348a Stay on same line after reindenting 2024-09-05 07:12:09 +02:00
1c154fdc8a Update search register when using f/t 2024-09-05 07:12:09 +02:00
9b75931736 Automatically add unambiguous imports after running a macro 2024-09-05 07:12:09 +02:00
3fe97aa767 Fix(VIM-3179): Respect virtual space below editor (imperfectly) 2024-09-05 07:12:09 +02:00
8eb2dbebcf Fix(VIM-3178): Workaround to support "Jump to Source" action mapping 2024-09-05 07:12:09 +02:00
cb781f9a0d Add support for count for visual and line motion surround 2024-09-05 07:12:09 +02:00
6693755823 Fix vim-surround not working with multiple cursors
Fixes multiple cursors with vim-surround commands `cs, ds, S` (but not `ys`).
2024-09-05 07:12:09 +02:00
9a9bd335d3 Fix(VIM-696) Restore visual mode after undo/redo, and disable incompatible actions 2024-09-05 07:12:09 +02:00
91e22482c6 Respect count with <Action> mappings 2024-09-05 07:08:52 +02:00
06dc2ed75c Change matchit plugin to use HTML patterns in unrecognized files 2024-09-05 07:02:47 +02:00
12da1ac969 Reset insert mode when switching active editor 2024-09-05 07:02:47 +02:00
5747ac0ebf Disable switching to insert mode for some editors 2024-09-05 07:02:44 +02:00
8a47bc1840 Remove update checker 2024-09-05 07:02:36 +02:00
da4deaf698 Set custom plugin version 2024-09-05 07:02:36 +02:00
IdeaVim Bot
4c09ab4766 Add Felix Wiemuth to contributors list 2024-08-31 09:01:59 +00:00
Alex Plate
ee447bce07 Add a note to replace the raw string after the changes will be available
https://github.com/JetBrains/intellij-community/pull/2825
2024-08-30 18:57:00 +03:00
Alex Plate
5fb4c10f88 Add 2024.2 IJ for testing on TC 2024-08-30 18:42:02 +03:00
Alex Plate
ed2fcb08b0 [VIM-3620] Add a link to the usage survey 2024-08-30 18:34:10 +03:00
dedd90ce13 Fix(VIM-3615): Escape closes dialog while waiting for more keys 2024-08-30 16:46:53 +03:00
Alex Plate
73326e623e Use the behavior form in docs 2024-08-30 16:42:03 +03:00
Alex Plate
a2bc34d6ec [VIM-3620] Show the uninstall survey only when uninstalling IdeaVim 2024-08-30 16:41:36 +03:00
Felix Wiemuth
b652c7726a Fix typo 2024-08-30 16:41:05 +03:00
Matt Ellis
fb7a2de07b Encapsulate the command builder's state flag
This also gets rid of BAD_COMMAND, which was set but never checked - the function that set the flag would immediately reset the command builder
2024-08-30 16:36:24 +03:00
Matt Ellis
def9ca479b Ensure builder resets to a root command trie node
Also refactors command nodes a bit for better debug/trace output
2024-08-30 16:36:24 +03:00
Matt Ellis
0936e0761f Reorder CommandBuilder methods
Try to keep related functions together: awaiting arguments, count, registers, adding action/argument, processing keystrokes, build, reset.
2024-08-30 16:36:24 +03:00
Matt Ellis
09a335bcfe Start to encapsulate setting command builder state
Also rename `pushCommandPart` and `completeCommandPart`
2024-08-30 16:36:24 +03:00
Matt Ellis
37b8d69bac Remove unused EMPTY_COMMAND 2024-08-30 16:36:24 +03:00
Matt Ellis
13308050a8 Remove unnecessary OperatorArguments parameters 2024-08-30 16:36:24 +03:00
Matt Ellis
a1a553ebc9 Deprecate OperatorArguments.mode 2024-08-30 16:36:24 +03:00
Matt Ellis
5bb0c4f7cb Use editor.mode instead of OperatorArguments.mode
`OperatorArguments.mode` is the mode *before* the command was completed, rather than the current mode, which is non-obvious. E.g. for a command in Insert mode, it will still be Insert, and for a (simple) command in Normal, it will still be Normal. The only difference is that a command such as `dw` would be in Operator-pending before the command is completed. That logic is not required for this method, so it's safe to use the current mode.

This allows us to start to deprecate `OperatorArguments.mode`.
2024-08-30 16:36:24 +03:00
Matt Ellis
da6736f24a Simplify the logic when yanking during delete 2024-08-30 16:36:24 +03:00
Matt Ellis
4df1ce2ae8 Remove OperatorArguments.mode usage in block insert
`OperatorArguments.mode` is the mode *before* the command is completed, so might be Visual, Operator-pending, Insert, etc. It's not immediately obvious this is the case, so we're going to deprecate `OperatorArguments.mode` to avoid confusion with `editor.mode`.

It's not required for this method because it's only called for Visual-block mode.
2024-08-30 16:36:24 +03:00
Matt Ellis
00fd4cd491 Handle op-pending for space and backspace 2024-08-30 16:36:24 +03:00
Matt Ellis
d185672e2f Deprecate OperatorArguments.isOperatorPending
Register specific handlers for Operator-pending mode instead of relying on a runtime flag for behaviour. Also refactors and renames some arrow motion handlers.
2024-08-30 16:36:24 +03:00
Matt Ellis
55fef03a4a Add 'whichwrap' to dictionary 2024-08-30 16:36:24 +03:00
Matt Ellis
69b3e4f782 Start to refactor OperatorArguments 2024-08-30 16:36:24 +03:00
Matt Ellis
6be29b0378 Remove KeyHandler.isOperatorPending
It's easier to just look at mode. We don't need the additional check on command builder, because we can't be in OP_PENDING without pushing an operator action to the command builder
2024-08-30 16:36:24 +03:00
Matt Ellis
9965c863a6 Encapsulate command node state in builder 2024-08-30 16:36:24 +03:00
Matt Ellis
3f11ae512c Move register pending state to command builder 2024-08-30 16:36:24 +03:00
Matt Ellis
1c842eb3d8 Avoid exposing misleading count on command builder 2024-08-30 16:36:24 +03:00
Matt Ellis
c7fbce675b Update comment 2024-08-30 16:36:24 +03:00
Matt Ellis
d7e68488c8 Make Command.rawCount immutable 2024-08-30 16:36:24 +03:00
Matt Ellis
69d13a74e6 Remove unnecessary copy method 2024-08-30 16:36:24 +03:00
Matt Ellis
5a83df34a7 Replace var properties with read only 2024-08-30 16:36:24 +03:00
Matt Ellis
0a18c388e0 Simplify CommandBuilder
Fixes selecting last register if multiple registers are used in a command
2024-08-30 16:36:24 +03:00
Matt Ellis
1a3409e7df Remove count from motion argument
Only Command has a count. The motion argument is now a sealed class hierarchy, and consists only of the motion action and optional argument. This is to reduce confusion over which count to use, and potential incorrect calculation of the count
2024-08-30 16:36:24 +03:00
Matt Ellis
e93db961a0 Wrap offsets argument as an external action 2024-08-30 16:36:24 +03:00
Matt Ellis
8fd76bd08f Refactor properties for sealed Argument classes 2024-08-30 16:36:24 +03:00
Matt Ellis
0eea4a5b2c Introduce sealed classes to represent an argument 2024-08-30 16:36:24 +03:00
Matt Ellis
18a0c533e2 Remove unused OperatedRange type 2024-08-30 16:36:24 +03:00
Matt Ellis
0bd8d8f4d2 Remove unused digraph command flag 2024-08-30 16:36:24 +03:00
Alex Plate
64a89c8863 [VIM-3620] Add the uninstall survey
We're actively working on understanding the users and what we can improve in the plugin
2024-08-28 18:57:29 +03:00
Filipp Vakhitov
5b17d7740e Update generated files after merging PRs 2024-08-25 21:51:14 +03:00
IdeaVim Bot
e406885ec6 Add Igor Babko to contributors list 2024-08-24 09:01:39 +00:00
filipp
9bbeab8062 VIM-2074 Backspace behaviour is incorrect in Replace mode 2024-08-23 14:22:48 +03:00
filipp
373bfc4eab Add endOffset to LiveRange 2024-08-23 14:22:48 +03:00
filipp
050f2f7b97 Respect emojis and don't assume that every character is a single offset 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
e30bc14843 Make closing command line safer 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
76d590be11 Support more flexible command line restrictions for Fleet 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
b005328b4a Exclude generated files from .gitignore
I've also added sorting to be more confident that the generated file will be persistent across different machines
2024-08-23 14:18:03 +03:00
Filipp Vakhitov
ad20021cee Remove currentAction and some unused code 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
126de5c218 Remove package-info.java 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
0f7aef3f15 Fix tests 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
f352b84922 Move some Executor logic to its base class 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
789faa7cb2 Add entry to history on <Esc> in COMMAND mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
a338f5768a Better reset 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
8205c74571 Remove some old bindings 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
def40eb409 Save last entry on every command line text update 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
010e8a7541 Support <C-U> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
46c6778b3a Support <Down>, <S-Down>, <C-N>, <PageDown> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
0977bd4400 Support <Up>, <S-Up>, <C-P>, <PageUp> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
db092e9b0a Support history in VimCommandLine 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
5cfb98e188 Remove some old bindings 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
3a95b62885 Support <Insert> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
93e6adf5a9 Support <C-W> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
37204398ff Support <C-Right>, <S-Right> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
b2f450d14d Support <C-Left>, <S-Left> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
61da888571 Fix failing tests 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
fcda97cfb8 Update methods to support searching in any text, not just Editor content 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
1dc7ea6363 Register all shortcuts without references on ExKeyBindings
All the shortcuts will be removed from ExKeyBindings, but they still need to be registered
2024-08-23 14:18:03 +03:00
Filipp Vakhitov
bb507db884 Remove swing bindings that are already implemented 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
7b0482ed94 Fix unwanted beep when can't perform an action 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
1c79b0d59a Support <Right> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
ff4eb31418 Support <Left> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
cb1078cf70 Support <C-H> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
da3e40eaf6 Support <C-E>, <End> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
17f77a9639 Support <C-B>, <Home> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
3d03494354 Support <DEL> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
642caddda7 Support <BS> in command mode 2024-08-23 14:18:03 +03:00
Filipp Vakhitov
1d97c43e30 Make ExEntryAction public
Fleet registers commands manually, so commands must be accessible
2024-08-23 14:18:03 +03:00
Igor Babko
7ed3e3b53c Update support-guide.md 2024-08-23 12:14:41 +03:00
IdeaVim Bot
aa39ca2006 Add shaun to contributors list 2024-08-19 09:01:43 +00:00
shaun
bb122903de remove Latest Fixes link 2024-08-19 10:23:23 +03:00
shaun
c84a3cf64d [changelog] add YouTrack link for version fixes 2024-08-19 10:23:23 +03:00
IdeaVim Bot
f4c9464b8a Add The1xDeveloper to contributors list 2024-08-17 09:01:38 +00:00
The1xDeveloper
3ba14d05b4 VIM-566: Add za motion support for toggling folds 2024-08-16 12:28:18 +03:00
Filipp Vakhitov
2189b70b87 Fix(VIM-3601): The escape characters in IdeaVim's configuration file are invalid 2024-08-14 18:18:55 +03:00
dependabot[bot]
ea2222f9d5 Bump org.junit.jupiter:junit-jupiter from 5.10.3 to 5.11.0
Bumps [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5) from 5.10.3 to 5.11.0.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.10.3...r5.11.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-14 18:53:54 +03:00
Filipp Vakhitov
7d68d41888 Fix(VIM-3580): New terminal switches editor to insert mode 2024-08-12 13:44:24 +03:00
Alex Pláte
44749b6d8c Update IdeaVim Plugins.md 2024-08-12 12:16:19 +03:00
Alex Pláte
1ce4dbc569 Update Home.md 2024-08-12 12:14:44 +03:00
Alex Pláte
db59513505 Update Home.md 2024-08-12 12:13:36 +03:00
Filipp Vakhitov
8c9ff9465f Fix(VIM-3584): com.maddyhome.idea.vim.KeyHandler requests com.maddyhome.idea.vim.api.VimKeyGroup instance. Class initialization must not depend on services 2024-08-11 01:12:59 +03:00
Filipp Vakhitov
1a2322ddec OutputPanel no longer ignores key events 2024-08-10 23:55:57 +03:00
194 changed files with 5029 additions and 2503 deletions

3
.gitignore vendored
View File

@@ -27,9 +27,6 @@
# Generated by gradle task "generateGrammarSource" # Generated by gradle task "generateGrammarSource"
vim-engine/src/main/java/com/maddyhome/idea/vim/parser/generated vim-engine/src/main/java/com/maddyhome/idea/vim/parser/generated
vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
# Generated JSONs for lazy classloading
/vim-engine/src/main/resources/ksp-generated
/src/main/resources/ksp-generated
# Created by github automation # Created by github automation
settings.xml settings.xml

View File

@@ -27,6 +27,7 @@ object Project : Project({
// Active tests // Active tests
buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT")) buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT"))
buildType(TestingBuildType("2024.1.1", "<default>")) buildType(TestingBuildType("2024.1.1", "<default>"))
buildType(TestingBuildType("2024.2", "<default>"))
buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT")) buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT"))
buildType(PropertyBased) buildType(PropertyBased)

View File

@@ -523,6 +523,22 @@ Contributors:
[![icon][github]](https://github.com/LazyScaper) [![icon][github]](https://github.com/LazyScaper)
&nbsp; &nbsp;
Jake Jake
* [![icon][mail]](mailto:the1xdeveloper@gmail.com)
[![icon][github]](https://github.com/The1xDeveloper)
&nbsp;
The1xDeveloper
* [![icon][mail]](mailto:shaunewilliams@gmail.com)
[![icon][github]](https://github.com/shaunlebron)
&nbsp;
shaun
* [![icon][mail]](mailto:i.i.babko@gmail.com)
[![icon][github]](https://github.com/igorbabko)
&nbsp;
Igor Babko
* [![icon][mail]](mailto:533601+felixwiemuth@users.noreply.github.com)
[![icon][github]](https://github.com/felixwiemuth)
&nbsp;
Felix Wiemuth
Previous contributors: Previous contributors:

View File

@@ -27,8 +27,8 @@ usual beta standards.
Since version 2.9.0, the changelog can be found on YouTrack Since version 2.9.0, the changelog can be found on YouTrack
To Be Released: https://youtrack.jetbrains.com/issues/VIM?q=%23%7BReady%20To%20Release%7D%20 * [To Be Released](https://youtrack.jetbrains.com/issues/VIM?q=%23%7BReady%20To%20Release%7D%20)
Latest Fixes: https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20sort%20by:%20updated%20 * [Version Fixes](https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20sort%20by:%20%7BFix%20versions%7D%20asc)
## 2.9.0, 2024-02-20 ## 2.9.0, 2024-02-20

View File

@@ -62,12 +62,16 @@ for a few days or send it to a friend for testing.
If you are looking for: If you are looking for:
- Vim commands (`w`, `<C-O>`, `p`, etc.): - Vim commands (`w`, `<C-O>`, `p`, etc.):
- Any particular command: `package-info.java`. - Any particular command:
- [Commands common for Fleet and IdeaVim](vim-engine/src/main/resources/ksp-generated/engine_commands.json)
- [IdeaVim only commands](src/main/resources/ksp-generated/intellij_commands.json)
- How commands are executed in common: `EditorActionHandlerBase`. - How commands are executed in common: `EditorActionHandlerBase`.
- Key mapping: `KeyHandler.handleKey()`. - Key mapping: `KeyHandler.handleKey()`.
- Ex commands (`:set`, `:s`, `:nohlsearch`): - Ex commands (`:set`, `:s`, `:nohlsearch`):
- Any particular ex command: package `com.maddyhome.idea.vim.ex.handler`. - Any particular command:
- [Commands common for Fleet and IdeaVim](vim-engine/src/main/resources/ksp-generated/engine_ex_commands.json)
- [IdeaVim only commands](src/main/resources/ksp-generated/intellij_ex_commands.json)
- Vim script grammar: `Vimscript.g4`. - Vim script grammar: `Vimscript.g4`.
- Vim script parsing: package `com.maddyhome.idea.vim.vimscript.parser`. - Vim script parsing: package `com.maddyhome.idea.vim.vimscript.parser`.
- Vim script executor: `Executor`. - Vim script executor: `Executor`.

View File

@@ -109,7 +109,6 @@ etc
See also: See also:
* [The list of all supported commands](src/main/java/com/maddyhome/idea/vim/package-info.java)
* [Top feature requests and bugs](https://youtrack.jetbrains.com/issues/VIM?q=%23Unresolved+sort+by%3A+votes) * [Top feature requests and bugs](https://youtrack.jetbrains.com/issues/VIM?q=%23Unresolved+sort+by%3A+votes)
* [Vimscript support roadmap](vimscript-info/VIMSCRIPT_ROADMAP.md) * [Vimscript support roadmap](vimscript-info/VIMSCRIPT_ROADMAP.md)
* [List of supported in-build functions](vimscript-info/FUNCTIONS_INFO.MD) * [List of supported in-build functions](vimscript-info/FUNCTIONS_INFO.MD)

View File

@@ -37,7 +37,8 @@ class CommandOrMotionProcessor(private val environment: SymbolProcessorEnvironme
Files.createDirectories(generatedDirPath) Files.createDirectories(generatedDirPath)
val filePath = generatedDirPath.resolve(environment.options["commands_file"]!!) val filePath = generatedDirPath.resolve(environment.options["commands_file"]!!)
val fileContent = json.encodeToString(commands) val sortedCommands = commands.sortedWith(compareBy({ it.keys }, { it.`class` }))
val fileContent = json.encodeToString(sortedCommands)
filePath.writeText(fileContent) filePath.writeText(fileContent)
return emptyList() return emptyList()

View File

@@ -37,7 +37,8 @@ class ExCommandProcessor(private val environment: SymbolProcessorEnvironment): S
Files.createDirectories(generatedDirPath) Files.createDirectories(generatedDirPath)
val filePath = generatedDirPath.resolve(environment.options["ex_commands_file"]!!) val filePath = generatedDirPath.resolve(environment.options["ex_commands_file"]!!)
val fileContent = json.encodeToString(commandToClass) val sortedCommandToClass = commandToClass.toList().sortedWith(compareBy({ it.first }, { it.second })).toMap()
val fileContent = json.encodeToString(sortedCommandToClass)
filePath.writeText(fileContent) filePath.writeText(fileContent)
return emptyList() return emptyList()

View File

@@ -37,7 +37,8 @@ class VimscriptFunctionProcessor(private val environment: SymbolProcessorEnviron
Files.createDirectories(generatedDirPath) Files.createDirectories(generatedDirPath)
val filePath = generatedDirPath.resolve(environment.options["vimscript_functions_file"]!!) val filePath = generatedDirPath.resolve(environment.options["vimscript_functions_file"]!!)
val fileContent = json.encodeToString(nameToClass) val sortedNameToClass = nameToClass.toList().sortedWith(compareBy({ it.first }, { it.second })).toMap()
val fileContent = json.encodeToString(sortedNameToClass)
filePath.writeText(fileContent) filePath.writeText(fileContent)
return emptyList() return emptyList()

View File

@@ -125,6 +125,7 @@ dependencies {
// AceJump is an optional dependency. We use their SessionManager class to check if it's active // AceJump is an optional dependency. We use their SessionManager class to check if it's active
plugin("AceJump", "3.8.19") plugin("AceJump", "3.8.19")
plugin("com.intellij.classic.ui", "242.20224.159")
} }
moduleSources(project(":vim-engine", "sourcesJarArtifacts")) moduleSources(project(":vim-engine", "sourcesJarArtifacts"))
@@ -210,6 +211,8 @@ tasks {
} }
compileTestKotlin { compileTestKotlin {
enabled = false
kotlinOptions { kotlinOptions {
jvmTarget = javaVersion jvmTarget = javaVersion
apiVersion = "1.9" apiVersion = "1.9"

View File

@@ -1,6 +1,6 @@
Welcome to the IdeaVim wiki! Welcome to the IdeaVim wiki!
- List of IdeaVim plugins: [plugins](IdeaVim%20Plugins.md) - List of IdeaVim plugins: [plugins](IdeaVim%20Plugins)
- Examples of `ideajoin` option (also known as "smart join"): ["ideajoin" examples](ideajoin-examples.md) - Examples of `ideajoin` option (also known as "smart join"): ["ideajoin" examples](ideajoin-examples)
- List of "set" commands: ["set" commands](set-commands.md) - List of "set" commands: ["set" commands](set-commands)
- Docs about "select" mode in vim: [select mode](Select-mode.md) - Docs about "select" mode in vim: [select mode](Select-mode)

View File

@@ -82,7 +82,7 @@ Original plugin: [NERDTree](https://github.com/preservim/nerdtree).
### Instructions ### Instructions
[See here](NERDTree-support.md). [See here](NERDTree-support).
</details> </details>

View File

@@ -8,7 +8,7 @@ Every effort is made to make these options compatible with Vim behaviour.
However, some differences are inevitable. However, some differences are inevitable.
``` ```
'clipboard' 'cb' Defines clipboard behavioue 'clipboard' 'cb' Defines clipboard behavior
A comma-separated list of words to control clipboard behaviour: A comma-separated list of words to control clipboard behaviour:
unnamed The clipboard register '*' is used instead of the unnamed The clipboard register '*' is used instead of the
unnamed register unnamed register

View File

@@ -1,7 +1,7 @@
# Support Guide # Support Guide
This document is created to help our support team. This document is created to help our support team.
It's not intended to be read by the users as it brings to value to them. It's not intended to be read by the users as it brings no value to them.
## Support channels ## Support channels
@@ -35,4 +35,4 @@ IdeaVim has multiple YouTrack statuses, main are:
# ~.ideavimrc file # ~.ideavimrc file
`~/.ideavimrc` is the file that is used for IdeaVim configuration. It may affect behaviour of the program, `~/.ideavimrc` is the file that is used for IdeaVim configuration. It may affect behaviour of the program,
so it makes sense to additionally request this file in case the issues can't be reproduced. so it makes sense to additionally request this file in case the issues can't be reproduced.

View File

@@ -20,7 +20,7 @@ ideaVersion=2024.2
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type # Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
ideaType=IC ideaType=IC
instrumentPluginCode=true instrumentPluginCode=true
version=chylex-38 version=chylex-41
javaVersion=17 javaVersion=17
remoteRobotVersion=0.11.23 remoteRobotVersion=0.11.23
antlrVersion=4.10.1 antlrVersion=4.10.1

View File

@@ -19,7 +19,6 @@ exclude:
- src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/JavaText.kt - src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/JavaText.kt
- src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/LoremText.kt - src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/LoremText.kt
- src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/SimpleText.kt - src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/SimpleText.kt
- src/main/java/com/maddyhome/idea/vim/package-info.java
- vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated - vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
- vim-engine/src/main/java/com/maddyhome/idea/vim/parser/generated - vim-engine/src/main/java/com/maddyhome/idea/vim/parser/generated
- src/main/java/com/maddyhome/idea/vim/group/SearchGroup.java - src/main/java/com/maddyhome/idea/vim/group/SearchGroup.java

View File

@@ -8,6 +8,10 @@
package com.maddyhome.idea.vim package com.maddyhome.idea.vim
import com.intellij.ide.BrowserUtil
import com.intellij.ide.plugins.IdeaPluginDescriptor
import com.intellij.ide.plugins.PluginStateListener
import com.intellij.ide.plugins.PluginStateManager
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
@@ -40,6 +44,18 @@ internal class PluginStartup : ProjectActivity/*, LightEditCompatible*/ {
// This code should be executed once // This code should be executed once
VimPlugin.getInstance().initialize() VimPlugin.getInstance().initialize()
// Uninstall survey. Should be registered once for all projects
PluginStateManager.addStateListener(object : PluginStateListener {
override fun install(p0: IdeaPluginDescriptor) {/*Nothing*/
}
override fun uninstall(descriptor: IdeaPluginDescriptor) {
if (descriptor.pluginId == VimPlugin.getPluginId()) {
BrowserUtil.open("https://surveys.jetbrains.com/s3/ideavim-uninstall-feedback")
}
}
})
} }
} }

View File

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

View File

@@ -37,7 +37,7 @@ import com.maddyhome.idea.vim.vimscript.model.expressions.FunctionCallExpression
import com.maddyhome.idea.vim.vimscript.model.expressions.SimpleExpression import com.maddyhome.idea.vim.vimscript.model.expressions.SimpleExpression
// todo make it multicaret // todo make it multicaret
private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, selectionType: SelectionType): Boolean { private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, motionType: SelectionType): Boolean {
val func = injector.globalOptions().operatorfunc val func = injector.globalOptions().operatorfunc
if (func.isEmpty()) { if (func.isEmpty()) {
VimPlugin.showMessage(MessageHelper.message("E774")) VimPlugin.showMessage(MessageHelper.message("E774"))
@@ -57,9 +57,9 @@ private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textR
if (value is VimFuncref) { if (value is VimFuncref) {
handler = value.handler handler = value.handler
} }
} catch (ex: ExException) { } catch (_: ExException) {
// Get the argument for function('...') or funcref('...') for the error message // Get the argument for function('...') or funcref('...') for the error message
val functionName = if (expression is FunctionCallExpression && expression.arguments.size > 0) { val functionName = if (expression is FunctionCallExpression && expression.arguments.isNotEmpty()) {
expression.arguments[0].evaluate(editor, context, scriptContext).toString() expression.arguments[0].evaluate(editor, context, scriptContext).toString()
} }
else { else {
@@ -77,7 +77,7 @@ private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textR
return false return false
} }
val arg = when (selectionType) { val arg = when (motionType) {
SelectionType.LINE_WISE -> "line" SelectionType.LINE_WISE -> "line"
SelectionType.CHARACTER_WISE -> "char" SelectionType.CHARACTER_WISE -> "char"
SelectionType.BLOCK_WISE -> "block" SelectionType.BLOCK_WISE -> "block"
@@ -101,19 +101,13 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
override val argumentType: Argument.Type = Argument.Type.MOTION override val argumentType: Argument.Type = Argument.Type.MOTION
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean { override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
val argument = cmd.argument ?: return false val argument = cmd.argument as? Argument.Motion ?: return false
if (!editor.inRepeatMode) { if (!editor.inRepeatMode) {
argumentCaptured = argument argumentCaptured = argument
} }
val range = getMotionRange(editor, context, argument, operatorArguments) val range = getMotionRange(editor, context, argument, operatorArguments)
if (range != null) { if (range != null) {
val selectionType = if (argument.motion.isLinewiseMotion()) { return doOperatorAction(editor, context, range, argument.getMotionType())
SelectionType.LINE_WISE
} else {
SelectionType.CHARACTER_WISE
}
return doOperatorAction(editor, context, range, selectionType)
} }
return false return false
} }
@@ -121,7 +115,7 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
private fun getMotionRange( private fun getMotionRange(
editor: VimEditor, editor: VimEditor,
context: ExecutionContext, context: ExecutionContext,
argument: Argument, argument: Argument.Motion,
operatorArguments: OperatorArguments, operatorArguments: OperatorArguments,
): TextRange? { ): TextRange? {
// Note that we're using getMotionRange2 in order to avoid normalising the linewise range into line start // Note that we're using getMotionRange2 in order to avoid normalising the linewise range into line start
@@ -136,7 +130,7 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
operatorArguments, operatorArguments,
)?.normalize()?.let { )?.normalize()?.let {
// If we're linewise, make sure the end offset isn't just the EOL char // If we're linewise, make sure the end offset isn't just the EOL char
if (argument.motion.isLinewiseMotion() && it.endOffset < editor.fileSize()) { if (argument.getMotionType() == SelectionType.LINE_WISE && it.endOffset < editor.fileSize()) {
TextRange(it.startOffset, it.endOffset + 1) TextRange(it.startOffset, it.endOffset + 1)
} else { } else {
it it

View File

@@ -25,7 +25,7 @@ internal class RepeatChangeAction : VimActionHandler.SingleExecution() {
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean { override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
val state = injector.vimState val state = injector.vimState
val lastCommand = VimRepeater.lastChangeCommand var lastCommand = VimRepeater.lastChangeCommand
if (lastCommand == null && Extension.lastExtensionHandler == null) return false if (lastCommand == null && Extension.lastExtensionHandler == null) return false
@@ -57,12 +57,7 @@ internal class RepeatChangeAction : VimActionHandler.SingleExecution() {
) )
} else if (!repeatHandler && lastCommand != null) { } else if (!repeatHandler && lastCommand != null) {
if (cmd.rawCount > 0) { if (cmd.rawCount > 0) {
lastCommand.rawCount = cmd.count lastCommand = lastCommand.copy(rawCount = cmd.rawCount)
val arg = lastCommand.argument
if (arg != null) {
val mot = arg.motion
mot.rawCount = 0
}
} }
state.executingCommand = lastCommand state.executingCommand = lastCommand

View File

@@ -40,7 +40,7 @@ class DeleteJoinLinesAction : ChangeEditorActionHandler.ConditionalSingleExecuti
): Boolean { ): Boolean {
injector.editorGroup.notifyIdeaJoin(editor) injector.editorGroup.notifyIdeaJoin(editor)
return injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, false, operatorArguments) return injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, false)
} }
override fun execute( override fun execute(

View File

@@ -35,7 +35,7 @@ class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecution()
injector.editorGroup.notifyIdeaJoin(editor) injector.editorGroup.notifyIdeaJoin(editor)
var res = true var res = true
editor.nativeCarets().sortedByDescending { it.offset }.forEach { caret -> editor.nativeCarets().sortedByDescending { it.offset }.forEach { caret ->
if (!injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, true, operatorArguments)) { if (!injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, true)) {
res = false res = false
} }
} }

View File

@@ -22,11 +22,6 @@ import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.enumSetOf import com.maddyhome.idea.vim.helper.enumSetOf
import java.util.* import java.util.*
@CommandOrMotion(keys = ["<C-H>", "<BS>"], modes = [Mode.INSERT])
internal class VimEditorBackSpace : IdeActionHandler(IdeActions.ACTION_EDITOR_BACKSPACE) {
override val type: Command.Type = Command.Type.DELETE
}
@CommandOrMotion(keys = ["<Del>"], modes = [Mode.INSERT]) @CommandOrMotion(keys = ["<Del>"], modes = [Mode.INSERT])
internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELETE) { internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELETE) {
override val type: Command.Type = Command.Type.DELETE override val type: Command.Type = Command.Type.DELETE

View File

@@ -33,7 +33,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Deque; import java.util.Deque;
import java.util.EnumSet;
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping; import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing; import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing;
@@ -64,8 +63,8 @@ public class VimArgTextObjExtension implements VimExtension {
*/ */
private static class BracketPairs { private static class BracketPairs {
// NOTE: brackets must match by the position, and ordered by rank (highest to lowest). // NOTE: brackets must match by the position, and ordered by rank (highest to lowest).
@NotNull private final String openBrackets; private final @NotNull String openBrackets;
@NotNull private final String closeBrackets; private final @NotNull String closeBrackets;
static class ParseException extends Exception { static class ParseException extends Exception {
public ParseException(@NotNull String message) { public ParseException(@NotNull String message) {
@@ -87,8 +86,7 @@ public class VimArgTextObjExtension implements VimExtension {
* @param bracketPairs comma-separated list of colon-separated bracket pairs. * @param bracketPairs comma-separated list of colon-separated bracket pairs.
* @throws ParseException if a syntax error is detected. * @throws ParseException if a syntax error is detected.
*/ */
@NotNull static @NotNull BracketPairs fromBracketPairList(final @NotNull String bracketPairs) throws ParseException {
static BracketPairs fromBracketPairList(@NotNull final String bracketPairs) throws ParseException {
StringBuilder openBrackets = new StringBuilder(); StringBuilder openBrackets = new StringBuilder();
StringBuilder closeBrackets = new StringBuilder(); StringBuilder closeBrackets = new StringBuilder();
ParseState state = ParseState.OPEN; ParseState state = ParseState.OPEN;
@@ -128,7 +126,7 @@ public class VimArgTextObjExtension implements VimExtension {
return new BracketPairs(openBrackets.toString(), closeBrackets.toString()); return new BracketPairs(openBrackets.toString(), closeBrackets.toString());
} }
BracketPairs(@NotNull final String openBrackets, @NotNull final String closeBrackets) { BracketPairs(final @NotNull String openBrackets, final @NotNull String closeBrackets) {
assert openBrackets.length() == closeBrackets.length(); assert openBrackets.length() == closeBrackets.length();
this.openBrackets = openBrackets; this.openBrackets = openBrackets;
this.closeBrackets = closeBrackets; this.closeBrackets = closeBrackets;
@@ -158,10 +156,9 @@ public class VimArgTextObjExtension implements VimExtension {
} }
} }
public static final BracketPairs DEFAULT_BRACKET_PAIRS = new BracketPairs("(", ")"); private static final BracketPairs DEFAULT_BRACKET_PAIRS = new BracketPairs("(", ")");
@Nullable private static @Nullable String bracketPairsVariable() {
private static String bracketPairsVariable() {
final Object value = VimPlugin.getVariableService().getGlobalVariableValue("argtextobj_pairs"); final Object value = VimPlugin.getVariableService().getGlobalVariableValue("argtextobj_pairs");
if (value instanceof VimString vimValue) { if (value instanceof VimString vimValue) {
return vimValue.getValue(); return vimValue.getValue();
@@ -192,13 +189,12 @@ public class VimArgTextObjExtension implements VimExtension {
this.isInner = isInner; this.isInner = isInner;
} }
@Nullable
@Override @Override
public TextRange getRange(@NotNull VimEditor editor, public @Nullable TextRange getRange(@NotNull VimEditor editor,
@NotNull ImmutableVimCaret caret, @NotNull ImmutableVimCaret caret,
@NotNull ExecutionContext context, @NotNull ExecutionContext context,
int count, int count,
int rawCount) { int rawCount) {
BracketPairs bracketPairs = DEFAULT_BRACKET_PAIRS; BracketPairs bracketPairs = DEFAULT_BRACKET_PAIRS;
final String bracketPairsVar = bracketPairsVariable(); final String bracketPairsVar = bracketPairsVariable();
if (bracketPairsVar != null) { if (bracketPairsVar != null) {
@@ -236,24 +232,22 @@ public class VimArgTextObjExtension implements VimExtension {
return new TextRange(finder.getLeftBound(), finder.getRightBound()); return new TextRange(finder.getLeftBound(), finder.getRightBound());
} }
@NotNull
@Override @Override
public TextObjectVisualType getVisualType() { public @NotNull TextObjectVisualType getVisualType() {
return TextObjectVisualType.CHARACTER_WISE; return TextObjectVisualType.CHARACTER_WISE;
} }
} }
@Override @Override
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) { public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState(); @NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner); final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner);
//noinspection DuplicatedCode //noinspection DuplicatedCode
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) { if (!(editor.getMode() instanceof Mode.OP_PENDING)) {
int count0 = operatorArguments.getCount0();
editor.nativeCarets().forEach((VimCaret caret) -> { editor.nativeCarets().forEach((VimCaret caret) -> {
final TextRange range = textObjectHandler.getRange(editor, caret, context, count, 0); final TextRange range = textObjectHandler.getRange(editor, caret, context, Math.max(1, count0), count0);
if (range != null) { if (range != null) {
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) { try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
if (editor.getMode() instanceof Mode.VISUAL) { if (editor.getMode() instanceof Mode.VISUAL) {
@@ -265,8 +259,7 @@ public class VimArgTextObjExtension implements VimExtension {
} }
}); });
} else { } else {
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count, keyHandlerState.getCommandBuilder().addAction(textObjectHandler);
textObjectHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags.class))));
} }
} }
} }
@@ -276,9 +269,9 @@ public class VimArgTextObjExtension implements VimExtension {
* position * position
*/ */
private static class ArgBoundsFinder { private static class ArgBoundsFinder {
@NotNull private final CharSequence text; private final @NotNull CharSequence text;
@NotNull private final Document document; private final @NotNull Document document;
@NotNull private final BracketPairs brackets; private final @NotNull BracketPairs brackets;
private int leftBound = Integer.MAX_VALUE; private int leftBound = Integer.MAX_VALUE;
private int rightBound = Integer.MIN_VALUE; private int rightBound = Integer.MIN_VALUE;
private int leftBracket; private int leftBracket;
@@ -305,7 +298,7 @@ public class VimArgTextObjExtension implements VimExtension {
* @param position starting position. * @param position starting position.
*/ */
boolean findBoundsAt(int position) throws IllegalStateException { boolean findBoundsAt(int position) throws IllegalStateException {
if (text.length() == 0) { if (text.isEmpty()) {
error = "empty document"; error = "empty document";
return false; return false;
} }

View File

@@ -25,9 +25,6 @@ import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.getLineEndOffset import com.maddyhome.idea.vim.api.getLineEndOffset
import com.maddyhome.idea.vim.api.globalOptions import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.MappingMode import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.command.TextObjectVisualType import com.maddyhome.idea.vim.command.TextObjectVisualType
@@ -52,7 +49,6 @@ import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.SelectionType import com.maddyhome.idea.vim.state.mode.SelectionType
import java.util.*
internal class CommentaryExtension : VimExtension { internal class CommentaryExtension : VimExtension {
@@ -184,10 +180,8 @@ internal class CommentaryExtension : VimExtension {
override val isRepeatable = true override val isRepeatable = true
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
val command = Command(operatorArguments.count1, CommentaryTextObjectMotionHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags::class.java))
val keyState = KeyHandler.getInstance().keyHandlerState val keyState = KeyHandler.getInstance().keyHandlerState
keyState.commandBuilder.completeCommandPart(Argument(command)) keyState.commandBuilder.addAction(CommentaryTextObjectMotionHandler)
} }
} }

View File

@@ -44,6 +44,7 @@ import com.maddyhome.idea.vim.helper.enumSetOf
import com.maddyhome.idea.vim.newapi.IjVimEditor import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.Mode
import java.util.* import java.util.*
import java.util.regex.Pattern import java.util.regex.Pattern
@@ -93,34 +94,29 @@ internal class Matchit : VimExtension {
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
val keyHandler = KeyHandler.getInstance() val keyHandler = KeyHandler.getInstance()
val keyState = keyHandler.keyHandlerState val keyState = keyHandler.keyHandlerState
val count = keyState.commandBuilder.count
// Reset the command count so it doesn't transfer onto subsequent commands. // Reset the command count so it doesn't transfer onto subsequent commands.
keyState.commandBuilder.resetCount() keyState.commandBuilder.resetCount()
// Normally we want to jump to the start of the matching pair. But when moving forward in operator // Normally we want to jump to the start of the matching pair. But when moving forward in operator
// pending mode, we want to include the entire match. isInOpPending makes that distinction. // pending mode, we want to include the entire match. isInOpPending makes that distinction.
val isInOpPending = keyHandler.isOperatorPending(editor.mode, keyState) if (editor.mode is Mode.OP_PENDING) {
if (isInOpPending) {
val matchitAction = MatchitAction() val matchitAction = MatchitAction()
matchitAction.reverse = reverse matchitAction.reverse = reverse
matchitAction.isInOpPending = true matchitAction.isInOpPending = true
keyState.commandBuilder.completeCommandPart( keyState.commandBuilder.addAction(matchitAction)
Argument(
Command(
count,
matchitAction,
Command.Type.MOTION,
EnumSet.noneOf(CommandFlags::class.java),
),
),
)
} else { } else {
editor.sortedCarets().forEach { caret -> editor.sortedCarets().forEach { caret ->
injector.jumpService.saveJumpLocation(editor) injector.jumpService.saveJumpLocation(editor)
caret.moveToOffset(getMatchitOffset(editor.ij, caret.ij, count, isInOpPending, reverse)) caret.moveToOffset(
getMatchitOffset(
editor.ij,
caret.ij,
operatorArguments.count0,
isInOpPending = false,
reverse
))
} }
} }
} }
@@ -354,7 +350,7 @@ private object FileTypePatterns {
private val DEFAULT_PAIRS = setOf('(', ')', '[', ']', '{', '}') private val DEFAULT_PAIRS = setOf('(', ')', '[', ']', '{', '}')
private fun getMatchitOffset(editor: Editor, caret: Caret, count: Int, isInOpPending: Boolean, reverse: Boolean): Int { private fun getMatchitOffset(editor: Editor, caret: Caret, count0: Int, isInOpPending: Boolean, reverse: Boolean): Int {
val virtualFile = EditorHelper.getVirtualFile(editor) val virtualFile = EditorHelper.getVirtualFile(editor)
var caretOffset = caret.offset var caretOffset = caret.offset
@@ -367,9 +363,9 @@ private fun getMatchitOffset(editor: Editor, caret: Caret, count: Int, isInOpPen
val currentChar = editor.document.charsSequence[caretOffset] val currentChar = editor.document.charsSequence[caretOffset]
var motionOffset: Int? = null var motionOffset: Int? = null
if (count > 0) { if (count0 > 0) {
// Matchit doesn't affect the percent motion, so we fall back to the default behavior. // Matchit doesn't affect the percent motion, so we fall back to the default behavior.
motionOffset = VimPlugin.getMotion().moveCaretToLinePercent(editor.vim, caret.vim, count) motionOffset = VimPlugin.getMotion().moveCaretToLinePercent(editor.vim, caret.vim, count0)
} else { } else {
// Check the simplest case first. // Check the simplest case first.
if (DEFAULT_PAIRS.contains(currentChar)) { if (DEFAULT_PAIRS.contains(currentChar)) {
@@ -400,8 +396,7 @@ private fun getMatchitOffset(editor: Editor, caret: Caret, count: Int, isInOpPen
private fun getMotionOffset(motion: Motion): Int? { private fun getMotionOffset(motion: Motion): Int? {
return when (motion) { return when (motion) {
is Motion.AbsoluteOffset -> motion.offset is Motion.AdjustedOffset, is Motion.AbsoluteOffset -> motion.offset
is Motion.AdjustedOffset -> motion.offset
is Motion.Error, is Motion.NoMotion -> null is Motion.Error, is Motion.NoMotion -> null
} }
} }

View File

@@ -555,12 +555,13 @@ private fun registerCommand(default: String, action: NerdAction) {
} }
private val actionsRoot: RootNode<NerdAction> = RootNode() private val actionsRoot: RootNode<NerdAction> = RootNode("NERDTree")
private var currentNode: CommandPartNode<NerdAction> = actionsRoot private var currentNode: CommandPartNode<NerdAction> = actionsRoot
private fun collectShortcuts(node: Node<NerdAction>): Set<KeyStroke> { private fun collectShortcuts(node: Node<NerdAction>): Set<KeyStroke> {
return if (node is CommandPartNode<NerdAction>) { return if (node is CommandPartNode<NerdAction>) {
val res = node.keys.toMutableSet() val res = node.children.keys.toMutableSet()
res += node.values.map { collectShortcuts(it) }.flatten() res += node.children.values.map { collectShortcuts(it) }.flatten()
res res
} else { } else {
emptySet() emptySet()

View File

@@ -10,7 +10,6 @@ package com.maddyhome.idea.vim.extension.replacewithregister
import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.ImmutableVimCaret import com.maddyhome.idea.vim.api.ImmutableVimCaret
@@ -166,17 +165,11 @@ private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimC
putToLine = -1, putToLine = -1,
) )
val vimEditor = editor.vim val vimEditor = editor.vim
val keyHandler = KeyHandler.getInstance()
ClipboardOptionHelper.IdeaputDisabler().use { ClipboardOptionHelper.IdeaputDisabler().use {
VimPlugin.getPut().putText( VimPlugin.getPut().putText(
vimEditor, vimEditor,
context.vim, context.vim,
putData, putData,
operatorArguments = OperatorArguments(
keyHandler.isOperatorPending(vimEditor.mode, keyHandler.keyHandlerState),
0,
editor.vim.mode,
),
saveToRegister = false saveToRegister = false
) )
} }

View File

@@ -29,14 +29,12 @@ import com.maddyhome.idea.vim.state.mode.Mode;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.EnumSet;
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping; import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing; import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing;
/** /**
* Port of vim-entire: * Port of vim-entire:
* https://github.com/kana/vim-textobj-entire * <a href="https://github.com/kana/vim-textobj-entire">vim-textobj-entire</a>
* *
* <p> * <p>
* vim-textobj-entire provides two text objects: * vim-textobj-entire provides two text objects:
@@ -51,7 +49,7 @@ import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingI
* </ul> * </ul>
* *
* See also the reference manual for more details at: * See also the reference manual for more details at:
* https://github.com/kana/vim-textobj-entire/blob/master/doc/textobj-entire.txt * <a href="https://github.com/kana/vim-textobj-entire/blob/master/doc/textobj-entire.txt">text-obj-entire.txt</a>
* *
* @author Alexandre Grison (@agrison) * @author Alexandre Grison (@agrison)
*/ */
@@ -94,13 +92,12 @@ public class VimTextObjEntireExtension implements VimExtension {
this.ignoreLeadingAndTrailing = ignoreLeadingAndTrailing; this.ignoreLeadingAndTrailing = ignoreLeadingAndTrailing;
} }
@Nullable
@Override @Override
public TextRange getRange(@NotNull VimEditor editor, public @Nullable TextRange getRange(@NotNull VimEditor editor,
@NotNull ImmutableVimCaret caret, @NotNull ImmutableVimCaret caret,
@NotNull ExecutionContext context, @NotNull ExecutionContext context,
int count, int count,
int rawCount) { int rawCount) {
int start = 0, end = ((IjVimEditor)editor).getEditor().getDocument().getTextLength(); int start = 0, end = ((IjVimEditor)editor).getEditor().getDocument().getTextLength();
// for the `ie` text object we don't want leading an trailing spaces // for the `ie` text object we don't want leading an trailing spaces
@@ -125,24 +122,22 @@ public class VimTextObjEntireExtension implements VimExtension {
return new TextRange(start, end); return new TextRange(start, end);
} }
@NotNull
@Override @Override
public TextObjectVisualType getVisualType() { public @NotNull TextObjectVisualType getVisualType() {
return TextObjectVisualType.CHARACTER_WISE; return TextObjectVisualType.CHARACTER_WISE;
} }
} }
@Override @Override
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) { public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState(); @NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing); final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
//noinspection DuplicatedCode //noinspection DuplicatedCode
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) { if (!(editor.getMode() instanceof Mode.OP_PENDING)) {
int count0 = operatorArguments.getCount0();
((IjVimEditor) editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> { ((IjVimEditor) editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
final TextRange range = textObjectHandler.getRange(editor, new IjVimCaret(caret), context, count, 0); final TextRange range = textObjectHandler.getRange(editor, new IjVimCaret(caret), context, Math.max(1, count0), count0);
if (range != null) { if (range != null) {
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) { try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
if (editor.getMode() instanceof Mode.VISUAL) { if (editor.getMode() instanceof Mode.VISUAL) {
@@ -155,9 +150,7 @@ public class VimTextObjEntireExtension implements VimExtension {
}); });
} else { } else {
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count, keyHandlerState.getCommandBuilder().addAction(textObjectHandler);
textObjectHandler, Command.Type.MOTION,
EnumSet.noneOf(CommandFlags.class))));
} }
} }
} }

View File

@@ -30,14 +30,12 @@ import com.maddyhome.idea.vim.state.mode.Mode;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.EnumSet;
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping; import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping; import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping;
/** /**
* Port of vim-indent-object: * Port of vim-indent-object:
* https://github.com/michaeljsmith/vim-indent-object * <a href="https://github.com/michaeljsmith/vim-indent-object">vim-indent-object</a>
* *
* <p> * <p>
* vim-indent-object provides these text objects based on the cursor line's indentation: * vim-indent-object provides these text objects based on the cursor line's indentation:
@@ -49,7 +47,7 @@ import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping;
* </ul> * </ul>
* *
* See also the reference manual for more details at: * See also the reference manual for more details at:
* https://github.com/michaeljsmith/vim-indent-object/blob/master/doc/indent-object.txt * <a href="https://github.com/michaeljsmith/vim-indent-object/blob/master/doc/indent-object.txt">indent-object.txt</a>
* *
* @author Shrikant Kandula (@sharat87) * @author Shrikant Kandula (@sharat87)
*/ */
@@ -98,13 +96,12 @@ public class VimIndentObject implements VimExtension {
this.includeBelow = includeBelow; this.includeBelow = includeBelow;
} }
@Nullable
@Override @Override
public TextRange getRange(@NotNull VimEditor editor, public @Nullable TextRange getRange(@NotNull VimEditor editor,
@NotNull ImmutableVimCaret caret, @NotNull ImmutableVimCaret caret,
@NotNull ExecutionContext context, @NotNull ExecutionContext context,
int count, int count,
int rawCount) { int rawCount) {
final CharSequence charSequence = ((IjVimEditor)editor).getEditor().getDocument().getCharsSequence(); final CharSequence charSequence = ((IjVimEditor)editor).getEditor().getDocument().getCharsSequence();
final int caretOffset = ((IjVimCaret)caret).getCaret().getOffset(); final int caretOffset = ((IjVimCaret)caret).getCaret().getOffset();
@@ -249,9 +246,8 @@ public class VimIndentObject implements VimExtension {
return new TextRange(upperBoundaryOffset, lowerBoundaryOffset); return new TextRange(upperBoundaryOffset, lowerBoundaryOffset);
} }
@NotNull
@Override @Override
public TextObjectVisualType getVisualType() { public @NotNull TextObjectVisualType getVisualType() {
return TextObjectVisualType.LINE_WISE; return TextObjectVisualType.LINE_WISE;
} }
@@ -264,15 +260,14 @@ public class VimIndentObject implements VimExtension {
@Override @Override
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) { public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
IjVimEditor vimEditor = (IjVimEditor)editor; IjVimEditor vimEditor = (IjVimEditor)editor;
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState(); @NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
final IndentObjectHandler textObjectHandler = new IndentObjectHandler(includeAbove, includeBelow); final IndentObjectHandler textObjectHandler = new IndentObjectHandler(includeAbove, includeBelow);
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) { if (!(editor.getMode() instanceof Mode.OP_PENDING)) {
int count0 = operatorArguments.getCount0();
((IjVimEditor)editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> { ((IjVimEditor)editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, count, 0); final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, Math.max(1, count0), count0);
if (range != null) { if (range != null) {
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) { try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
if (editor.getMode() instanceof Mode.VISUAL) { if (editor.getMode() instanceof Mode.VISUAL) {
@@ -285,9 +280,7 @@ public class VimIndentObject implements VimExtension {
}); });
} else { } else {
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count, keyHandlerState.getCommandBuilder().addAction(textObjectHandler);
textObjectHandler, Command.Type.MOTION,
EnumSet.noneOf(CommandFlags.class))));
} }
} }
} }

View File

@@ -103,6 +103,11 @@ class ChangeGroup : VimChangeGroupBase() {
} }
} }
override fun processBackspace(editor: VimEditor, context: ExecutionContext) {
injector.actionExecutor.executeAction(editor, name = IdeActions.ACTION_EDITOR_BACKSPACE, context = context)
injector.scroll.scrollCaretIntoView(editor)
}
private fun restoreCursor(editor: VimEditor, caret: VimCaret, startLine: Int) { private fun restoreCursor(editor: VimEditor, caret: VimCaret, startLine: Int) {
if (caret != editor.primaryCaret()) { if (caret != editor.primaryCaret()) {
(editor as IjVimEditor).editor.caretModel.addCaret( (editor as IjVimEditor).editor.caretModel.addCaret(

View File

@@ -11,11 +11,14 @@ package com.maddyhome.idea.vim.group;
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger;
import com.maddyhome.idea.vim.api.VimDigraphGroupBase; import com.maddyhome.idea.vim.api.VimDigraphGroupBase;
import com.maddyhome.idea.vim.api.VimEditor; import com.maddyhome.idea.vim.api.VimEditor;
import com.maddyhome.idea.vim.api.VimOutputPanel;
import com.maddyhome.idea.vim.ex.ExOutputModel; import com.maddyhome.idea.vim.ex.ExOutputModel;
import com.maddyhome.idea.vim.helper.EditorHelper; import com.maddyhome.idea.vim.helper.EditorHelper;
import com.maddyhome.idea.vim.newapi.IjVimEditor; import com.maddyhome.idea.vim.newapi.IjVimEditor;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
public class DigraphGroup extends VimDigraphGroupBase { public class DigraphGroup extends VimDigraphGroupBase {
public void showDigraphs(@NotNull VimEditor editor) { public void showDigraphs(@NotNull VimEditor editor) {
@@ -71,7 +74,9 @@ public class DigraphGroup extends VimDigraphGroupBase {
} }
} }
ExOutputModel.getInstance(((IjVimEditor) editor).getEditor()).output(res.toString()); VimOutputPanel output = injector.getOutputPanel().getOrCreate(editor, injector.getExecutionContextManager().getEditorExecutionContext(editor));
output.addText(res.toString(), true );
output.show();
} }
private static final Logger logger = Logger.getInstance(DigraphGroup.class.getName()); private static final Logger logger = Logger.getInstance(DigraphGroup.class.getName());

View File

@@ -374,9 +374,9 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
if (activeCommandLine != null) { if (activeCommandLine != null) {
activeCommandLine.close(true, false); activeCommandLine.close(true, false);
} }
ExOutputModel exOutputModel = ExOutputModel.tryGetInstance(editor); VimOutputPanel outputPanel = injector.getOutputPanel().getCurrentOutputPanel();
if (exOutputModel != null) { if (outputPanel != null) {
exOutputModel.close(); outputPanel.close();
} }
VimModalInput modalInput = injector.getModalInput().getCurrentModalInput(); VimModalInput modalInput = injector.getModalInput().getCurrentModalInput();
if (modalInput != null) { if (modalInput != null) {

View File

@@ -14,9 +14,7 @@ import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage; import com.intellij.openapi.components.Storage;
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger;
import com.maddyhome.idea.vim.VimPlugin; import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.history.HistoryBlock; import com.maddyhome.idea.vim.history.*;
import com.maddyhome.idea.vim.history.HistoryEntry;
import com.maddyhome.idea.vim.history.VimHistoryBase;
import org.jdom.Element; import org.jdom.Element;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -35,21 +33,20 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
logger.debug("saveData"); logger.debug("saveData");
Element hist = new Element("history"); Element hist = new Element("history");
saveData(hist, SEARCH); for (Type type : getHistories().keySet()) {
saveData(hist, COMMAND); saveData(hist, type);
saveData(hist, EXPRESSION); }
saveData(hist, INPUT);
element.addContent(hist); element.addContent(hist);
} }
private void saveData(@NotNull Element element, String key) { private void saveData(@NotNull Element element, VimHistory.Type type) {
final HistoryBlock block = getHistories().get(key); final HistoryBlock block = getHistories().get(type);
if (block == null) { if (block == null) {
return; return;
} }
final Element root = new Element("history-" + key); final Element root = new Element("history-" + typeToKey(type));
for (HistoryEntry entry : block.getEntries()) { for (HistoryEntry entry : block.getEntries()) {
final Element entryElement = new Element("entry"); final Element entryElement = new Element("entry");
@@ -67,10 +64,10 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
return; return;
} }
readData(hist, SEARCH); for (Element child : hist.getChildren()) {
readData(hist, COMMAND); String key = child.getName().replace("history-", "");
readData(hist, EXPRESSION); readData(hist, key);
readData(hist, INPUT); }
} }
private void readData(@NotNull Element element, String key) { private void readData(@NotNull Element element, String key) {
@@ -80,7 +77,7 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
} }
block = new HistoryBlock(); block = new HistoryBlock();
getHistories().put(key, block); getHistories().put(getTypeForString(key), block);
final Element root = element.getChild("history-" + key); final Element root = element.getChild("history-" + key);
if (root != null) { if (root != null) {
@@ -94,6 +91,25 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
} }
} }
private String typeToKey(VimHistory.Type type) {
if (type instanceof VimHistory.Type.Search) {
return SEARCH;
}
if (type instanceof VimHistory.Type.Command) {
return COMMAND;
}
if (type instanceof VimHistory.Type.Expression) {
return EXPRESSION;
}
if (type instanceof VimHistory.Type.Input) {
return INPUT;
}
if (type instanceof VimHistory.Type.Custom) {
return ((Type.Custom) type).getId();
}
return "unreachable";
}
@Nullable @Nullable
@Override @Override
public Element getState() { public Element getState() {

View File

@@ -26,10 +26,7 @@ import com.maddyhome.idea.vim.EventFacade;
import com.maddyhome.idea.vim.VimPlugin; import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.action.VimShortcutKeyAction; import com.maddyhome.idea.vim.action.VimShortcutKeyAction;
import com.maddyhome.idea.vim.action.change.LazyVimCommand; import com.maddyhome.idea.vim.action.change.LazyVimCommand;
import com.maddyhome.idea.vim.api.NativeAction; import com.maddyhome.idea.vim.api.*;
import com.maddyhome.idea.vim.api.VimEditor;
import com.maddyhome.idea.vim.api.VimInjectorKt;
import com.maddyhome.idea.vim.api.VimKeyGroupBase;
import com.maddyhome.idea.vim.command.MappingMode; import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.ex.ExOutputModel; import com.maddyhome.idea.vim.ex.ExOutputModel;
import com.maddyhome.idea.vim.key.*; import com.maddyhome.idea.vim.key.*;
@@ -80,25 +77,6 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
((IjVimEditor)editor).getEditor().getComponent()); ((IjVimEditor)editor).getEditor().getComponent());
} }
public boolean showKeyMappings(@NotNull Set<? extends MappingMode> modes, @NotNull Editor editor) {
List<Pair<EnumSet<MappingMode>, MappingInfo>> rows = getKeyMappingRows(modes);
final StringBuilder builder = new StringBuilder();
for (Pair<EnumSet<MappingMode>, MappingInfo> row : rows) {
MappingInfo mappingInfo = row.getSecond();
builder.append(StringsKt.padEnd(getModesStringCode(row.getFirst()), 2, ' '));
builder.append(" ");
builder.append(StringsKt.padEnd(VimInjectorKt.getInjector().getParser().toKeyNotation(mappingInfo.getFromKeys()), 11, ' '));
builder.append(" ");
builder.append(mappingInfo.isRecursive() ? " " : "*");
builder.append(" ");
builder.append(mappingInfo.getPresentableString());
builder.append("\n");
}
ExOutputModel.getInstance(editor).output(builder.toString());
return true;
}
@Override @Override
public void updateShortcutKeysRegistration() { public void updateShortcutKeysRegistration() {
for (VimEditor editor : injector.getEditorGroup().getEditors()) { for (VimEditor editor : injector.getEditorGroup().getEditors()) {
@@ -358,6 +336,22 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
@Override @Override
public boolean showKeyMappings(@NotNull Set<? extends MappingMode> modes, @NotNull VimEditor editor) { public boolean showKeyMappings(@NotNull Set<? extends MappingMode> modes, @NotNull VimEditor editor) {
return showKeyMappings(modes, ((IjVimEditor) editor).getEditor()); List<Pair<EnumSet<MappingMode>, MappingInfo>> rows = getKeyMappingRows(modes);
final StringBuilder builder = new StringBuilder();
for (Pair<EnumSet<MappingMode>, MappingInfo> row : rows) {
MappingInfo mappingInfo = row.getSecond();
builder.append(StringsKt.padEnd(getModesStringCode(row.getFirst()), 2, ' '));
builder.append(" ");
builder.append(StringsKt.padEnd(VimInjectorKt.getInjector().getParser().toKeyNotation(mappingInfo.getFromKeys()), 11, ' '));
builder.append(" ");
builder.append(mappingInfo.isRecursive() ? " " : "*");
builder.append(" ");
builder.append(mappingInfo.getPresentableString());
builder.append("\n");
}
VimOutputPanel outputPanel = injector.getOutputPanel().getOrCreate(editor, injector.getExecutionContextManager().getEditorExecutionContext(editor));
outputPanel.addText(builder.toString(), true);
outputPanel.show();
return true;
} }
} }

View File

@@ -35,7 +35,7 @@ import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.MotionType import com.maddyhome.idea.vim.command.MotionType
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.ex.ExOutputModel import com.maddyhome.idea.vim.handler.ExternalActionHandler
import com.maddyhome.idea.vim.handler.Motion import com.maddyhome.idea.vim.handler.Motion
import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
import com.maddyhome.idea.vim.handler.MotionActionHandler import com.maddyhome.idea.vim.handler.MotionActionHandler
@@ -193,21 +193,16 @@ internal class MotionGroup : VimMotionGroupBase() {
argument: Argument, argument: Argument,
operatorArguments: OperatorArguments, operatorArguments: OperatorArguments,
): TextRange? { ): TextRange? {
if (argument !is Argument.Motion) {
throw RuntimeException("Unexpected argument passed to getMotionRange2: $argument")
}
var start: Int var start: Int
var end: Int var end: Int
if (argument.type === Argument.Type.OFFSETS) {
val offsets = argument.offsets[caret.vim] ?: return null
val (first, second) = offsets.getNativeStartAndEnd()
start = first
end = second
} else {
val cmd = argument.motion
// Normalize the counts between the command and the motion argument
val cnt = cmd.count * operatorArguments.count1
val raw = if (operatorArguments.count0 == 0 && cmd.rawCount == 0) 0 else cnt
if (cmd.action is MotionActionHandler) {
val action = cmd.action as MotionActionHandler
val action = argument.motion
when (action) {
is MotionActionHandler -> {
// This is where we are now // This is where we are now
start = caret.offset start = caret.offset
@@ -216,8 +211,8 @@ internal class MotionGroup : VimMotionGroupBase() {
editor.vim, editor.vim,
caret.vim, caret.vim,
IjEditorExecutionContext(context!!), IjEditorExecutionContext(context!!),
cmd.argument, argument.argument,
operatorArguments.withCount0(raw), operatorArguments
) )
// Invalid motion // Invalid motion
@@ -233,22 +228,32 @@ internal class MotionGroup : VimMotionGroupBase() {
end++ end++
} }
} }
} else if (cmd.action is TextObjectActionHandler) { }
val action = cmd.action as TextObjectActionHandler
val range = is TextObjectActionHandler -> {
action.getRange(editor.vim, caret.vim, IjEditorExecutionContext(context!!), cnt, raw) ?: return null val range = action.getRange(
editor.vim,
caret.vim,
IjEditorExecutionContext(context!!),
operatorArguments.count1,
operatorArguments.count0
) ?: return null
start = range.startOffset start = range.startOffset
end = range.endOffset end = range.endOffset
if (cmd.isLinewiseMotion()) end-- if (argument.isLinewiseMotion()) end--
} else {
throw RuntimeException(
"Commands doesn't take " + cmd.action.javaClass.simpleName + " as an operator",
)
} }
is ExternalActionHandler -> {
val range = action.getRange(caret.vim) ?: return null
start = range.startOffset
end = range.endOffset
}
else -> throw RuntimeException("Commands doesn't take " + action.javaClass.simpleName + " as an operator")
} }
// This is a kludge for dw, dW, and d[w. Without this kludge, an extra newline is operated when it shouldn't be. // This is a kludge for dw, dW, and d[w. Without this kludge, an extra newline is operated when it shouldn't be.
val id = argument.motion.action.id val id = argument.motion.id
if (id == VimChangeGroupBase.VIM_MOTION_WORD_RIGHT || id == VimChangeGroupBase.VIM_MOTION_BIG_WORD_RIGHT || id == VimChangeGroupBase.VIM_MOTION_CAMEL_RIGHT) { if (id == VimChangeGroupBase.VIM_MOTION_WORD_RIGHT || id == VimChangeGroupBase.VIM_MOTION_BIG_WORD_RIGHT || id == VimChangeGroupBase.VIM_MOTION_CAMEL_RIGHT) {
val text = editor.document.charsSequence.subSequence(start, end).toString() val text = editor.document.charsSequence.subSequence(start, end).toString()
val lastNewLine = text.lastIndexOf('\n') val lastNewLine = text.lastIndexOf('\n')
@@ -258,6 +263,7 @@ internal class MotionGroup : VimMotionGroupBase() {
} }
} }
} }
return TextRange(start, end) return TextRange(start, end)
} }
@@ -317,7 +323,7 @@ internal class MotionGroup : VimMotionGroupBase() {
is Mode.CMD_LINE -> { is Mode.CMD_LINE -> {
val commandLine = injector.commandLine.getActiveCommandLine() ?: return val commandLine = injector.commandLine.getActiveCommandLine() ?: return
commandLine.close(refocusOwningEditor = false, resetCaret = false) commandLine.close(refocusOwningEditor = false, resetCaret = false)
ExOutputModel.tryGetInstance(editor)?.close() injector.outputPanel.getCurrentOutputPanel()?.close()
} }
else -> {} else -> {}
} }

View File

@@ -218,13 +218,17 @@ internal class VimEnterHandler(nextHandler: EditorActionHandler?) : VimKeyHandle
internal class VimEscHandler(nextHandler: EditorActionHandler) : VimKeyHandler(nextHandler) { internal class VimEscHandler(nextHandler: EditorActionHandler) : VimKeyHandler(nextHandler) {
override val key: String = "<Esc>" override val key: String = "<Esc>"
private val ideaVimSupportDialog
get() = injector.globalIjOptions().ideavimsupport.contains(IjOptionConstants.ideavimsupport_dialog)
override fun isHandlerEnabled(editor: Editor, dataContext: DataContext?): Boolean { override fun isHandlerEnabled(editor: Editor, dataContext: DataContext?): Boolean {
val ideaVimSupportDialog =
injector.globalIjOptions().ideavimsupport.contains(IjOptionConstants.ideavimsupport_dialog)
return editor.isPrimaryEditor() || return editor.isPrimaryEditor() ||
EditorHelper.isFileEditor(editor) && !editor.vim.mode.inNormalMode || EditorHelper.isFileEditor(editor) && vimStateNeedsToHandleEscape(editor) ||
ideaVimSupportDialog && !editor.vim.mode.inNormalMode ideaVimSupportDialog && vimStateNeedsToHandleEscape(editor)
}
private fun vimStateNeedsToHandleEscape(editor: Editor): Boolean {
return !editor.vim.mode.inNormalMode || KeyHandler.getInstance().keyHandlerState.mappingState.hasKeys
} }
} }

View File

@@ -61,6 +61,9 @@ internal class IjActionExecutor : VimActionExecutor {
get() = IdeActions.ACTION_EXPAND_REGION get() = IdeActions.ACTION_EXPAND_REGION
override val ACTION_EXPAND_REGION_RECURSIVELY: String override val ACTION_EXPAND_REGION_RECURSIVELY: String
get() = IdeActions.ACTION_EXPAND_REGION_RECURSIVELY get() = IdeActions.ACTION_EXPAND_REGION_RECURSIVELY
override val ACTION_EXPAND_COLLAPSE_TOGGLE: String
// [VERSION UPDATE] 2024.3+ Replace raw "ExpandCollapseToggleAction" with IdeActions.ACTION_EXPAND_COLLAPSE_TOGGLE_REGION from the platform.
get() = "ExpandCollapseToggleAction"
/** /**
* Execute an action * Execute an action

View File

@@ -16,7 +16,6 @@ import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.getLineEndForOffset import com.maddyhome.idea.vim.api.getLineEndForOffset
import com.maddyhome.idea.vim.api.getLineStartForOffset import com.maddyhome.idea.vim.api.getLineStartForOffset
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
import com.maddyhome.idea.vim.newapi.IjVimCaret import com.maddyhome.idea.vim.newapi.IjVimCaret
@@ -94,6 +93,6 @@ internal fun VimEditor.exitSelectMode(adjustCaretPosition: Boolean) {
} }
} }
internal fun Editor.exitInsertMode(context: DataContext, operatorArguments: OperatorArguments) { internal fun Editor.exitInsertMode(context: DataContext) {
VimPlugin.getChange().processEscape(IjVimEditor(this), IjEditorExecutionContext(context), operatorArguments) VimPlugin.getChange().processEscape(IjVimEditor(this), IjEditorExecutionContext(context))
} }

View File

@@ -24,6 +24,7 @@ import com.maddyhome.idea.vim.common.InsertSequence
import com.maddyhome.idea.vim.ex.ExOutputModel import com.maddyhome.idea.vim.ex.ExOutputModel
import com.maddyhome.idea.vim.group.visual.VisualChange import com.maddyhome.idea.vim.group.visual.VisualChange
import com.maddyhome.idea.vim.group.visual.vimLeadSelectionOffset import com.maddyhome.idea.vim.group.visual.vimLeadSelectionOffset
import com.maddyhome.idea.vim.common.VimEditorReplaceMask
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.VimStateMachine import com.maddyhome.idea.vim.state.VimStateMachine
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
@@ -124,6 +125,7 @@ internal var Editor.vimExOutput: ExOutputModel? by userData()
internal var Editor.vimTestInputModel: TestInputModel? by userData() internal var Editor.vimTestInputModel: TestInputModel? by userData()
internal var Editor.vimChangeActionSwitchMode: Mode? by userData() internal var Editor.vimChangeActionSwitchMode: Mode? by userData()
internal var Editor.replaceMask: VimEditorReplaceMask? by userData()
internal var Caret.currentInsert: InsertSequence? by userData() internal var Caret.currentInsert: InsertSequence? by userData()
internal val Caret.insertHistory: MutableList<InsertSequence> by userDataOr { mutableListOf() } internal val Caret.insertHistory: MutableList<InsertSequence> by userDataOr { mutableListOf() }

View File

@@ -15,7 +15,6 @@ import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.EditorListener import com.maddyhome.idea.vim.common.EditorListener
import com.maddyhome.idea.vim.helper.inInsertMode import com.maddyhome.idea.vim.helper.inInsertMode
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
@@ -65,7 +64,7 @@ class IJEditorFocusListener : EditorListener {
val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor) val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor)
val mode = injector.vimState.mode val mode = injector.vimState.mode
when (mode) { when (mode) {
is Mode.INSERT -> editor.exitInsertMode(context, OperatorArguments(false, 0, mode)) is Mode.INSERT -> editor.exitInsertMode(context)
else -> {} else -> {}
} }
} }
@@ -79,3 +78,4 @@ class IJEditorFocusListener : EditorListener {
KeyHandler.getInstance().reset(editor) KeyHandler.getInstance().reset(editor)
} }
} }

View File

@@ -16,7 +16,9 @@ import com.intellij.codeInsight.lookup.impl.actions.ChooseItemAction
import com.intellij.codeInsight.template.Template import com.intellij.codeInsight.template.Template
import com.intellij.codeInsight.template.TemplateEditingAdapter import com.intellij.codeInsight.template.TemplateEditingAdapter
import com.intellij.codeInsight.template.TemplateManagerListener import com.intellij.codeInsight.template.TemplateManagerListener
import com.intellij.codeInsight.template.impl.TemplateManagerImpl
import com.intellij.codeInsight.template.impl.TemplateState import com.intellij.codeInsight.template.impl.TemplateState
import com.intellij.codeInsight.template.impl.actions.NextVariableAction
import com.intellij.find.FindModelListener import com.intellij.find.FindModelListener
import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.ActionUpdateThread
@@ -152,6 +154,10 @@ internal object IdeaSpecifics {
KeyHandler.getInstance().reset(it.vim) KeyHandler.getInstance().reset(it.vim)
} }
} }
else if (action is NextVariableAction && TemplateManagerImpl.getTemplateState(editor) == null) {
editor.vim.exitInsertMode(event.dataContext.vim)
KeyHandler.getInstance().reset(editor.vim)
}
//endregion //endregion
if (caretOffset != -1 && caretOffset != editor.caretModel.offset) { if (caretOffset != -1 && caretOffset != editor.caretModel.offset) {

View File

@@ -66,7 +66,6 @@ import com.maddyhome.idea.vim.api.coerceOffset
import com.maddyhome.idea.vim.api.getLineEndForOffset import com.maddyhome.idea.vim.api.getLineEndForOffset
import com.maddyhome.idea.vim.api.getLineStartForOffset import com.maddyhome.idea.vim.api.getLineStartForOffset
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.ex.ExOutputModel
import com.maddyhome.idea.vim.group.EditorGroup import com.maddyhome.idea.vim.group.EditorGroup
import com.maddyhome.idea.vim.group.FileGroup import com.maddyhome.idea.vim.group.FileGroup
import com.maddyhome.idea.vim.group.IjOptions import com.maddyhome.idea.vim.group.IjOptions
@@ -395,7 +394,8 @@ internal object VimListenerManager {
editor.vim.mode = Mode.NORMAL() editor.vim.mode = Mode.NORMAL()
KeyHandler.getInstance().reset(editor.vim) KeyHandler.getInstance().reset(editor.vim)
} }
injector.scroll.scrollCaretIntoView(editor.vim) // Breaks relativenumber for some reason
// injector.scroll.scrollCaretIntoView(editor.vim)
} }
MotionGroup.fileEditorManagerSelectionChangedCallback(event) MotionGroup.fileEditorManagerSelectionChangedCallback(event)
@@ -748,7 +748,7 @@ internal object VimListenerManager {
injector.commandLine.getActiveCommandLine()?.close(refocusOwningEditor = true, resetCaret = false) injector.commandLine.getActiveCommandLine()?.close(refocusOwningEditor = true, resetCaret = false)
injector.modalInput.getCurrentModalInput()?.deactivate(refocusOwningEditor = true, resetCaret = false) injector.modalInput.getCurrentModalInput()?.deactivate(refocusOwningEditor = true, resetCaret = false)
ExOutputModel.tryGetInstance(editor)?.close() injector.outputPanel.getCurrentOutputPanel()?.close()
val caretModel = editor.caretModel val caretModel = editor.caretModel
if (editor.vim.mode.selectionType != null) { if (editor.vim.mode.selectionType != null) {
@@ -777,7 +777,7 @@ internal object VimListenerManager {
injector.commandLine.getActiveCommandLine()?.close(refocusOwningEditor = true, resetCaret = false) injector.commandLine.getActiveCommandLine()?.close(refocusOwningEditor = true, resetCaret = false)
injector.modalInput.getCurrentModalInput()?.deactivate(refocusOwningEditor = true, resetCaret = false) injector.modalInput.getCurrentModalInput()?.deactivate(refocusOwningEditor = true, resetCaret = false)
ExOutputModel.getInstance(event.editor).close() injector.outputPanel.getCurrentOutputPanel()?.close()
} }
} }
} }

View File

@@ -14,6 +14,27 @@ import com.maddyhome.idea.vim.common.LiveRange
internal class IjLiveRange(val marker: RangeMarker) : LiveRange { internal class IjLiveRange(val marker: RangeMarker) : LiveRange {
override val startOffset: Int override val startOffset: Int
get() = marker.startOffset get() = marker.startOffset
override val endOffset: Int
get() = marker.endOffset
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as IjLiveRange
if (startOffset != other.startOffset) return false
if (endOffset != other.endOffset) return false
return true
}
override fun hashCode(): Int {
var result = startOffset
result = 31 * result + endOffset
return result
}
} }
val RangeMarker.vim: LiveRange val RangeMarker.vim: LiveRange

View File

@@ -38,12 +38,12 @@ import com.maddyhome.idea.vim.api.VimSelectionModel
import com.maddyhome.idea.vim.api.VimVisualPosition import com.maddyhome.idea.vim.api.VimVisualPosition
import com.maddyhome.idea.vim.api.VirtualFile import com.maddyhome.idea.vim.api.VirtualFile
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.IndentConfig import com.maddyhome.idea.vim.common.IndentConfig
import com.maddyhome.idea.vim.common.IndentConfig.Companion.create
import com.maddyhome.idea.vim.common.LiveRange import com.maddyhome.idea.vim.common.LiveRange
import com.maddyhome.idea.vim.common.ModeChangeListener import com.maddyhome.idea.vim.common.ModeChangeListener
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.common.VimEditorReplaceMask
import com.maddyhome.idea.vim.common.forgetAllReplaceMasks
import com.maddyhome.idea.vim.group.visual.vimSetSystemBlockSelectionSilently import com.maddyhome.idea.vim.group.visual.vimSetSystemBlockSelectionSilently
import com.maddyhome.idea.vim.helper.EditorHelper import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.StrictMode import com.maddyhome.idea.vim.helper.StrictMode
@@ -53,6 +53,7 @@ import com.maddyhome.idea.vim.helper.fileSize
import com.maddyhome.idea.vim.helper.getTopLevelEditor import com.maddyhome.idea.vim.helper.getTopLevelEditor
import com.maddyhome.idea.vim.helper.inExMode import com.maddyhome.idea.vim.helper.inExMode
import com.maddyhome.idea.vim.helper.isTemplateActive import com.maddyhome.idea.vim.helper.isTemplateActive
import com.maddyhome.idea.vim.helper.replaceMask
import com.maddyhome.idea.vim.helper.vimChangeActionSwitchMode import com.maddyhome.idea.vim.helper.vimChangeActionSwitchMode
import com.maddyhome.idea.vim.helper.vimLastSelectionType import com.maddyhome.idea.vim.helper.vimLastSelectionType
import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
@@ -75,6 +76,11 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor, VimEditorBase(
// TBH, I don't like the names. Need to think a bit more about this // TBH, I don't like the names. Need to think a bit more about this
val editor = editor.getTopLevelEditor() val editor = editor.getTopLevelEditor()
val originalEditor = editor val originalEditor = editor
override var replaceMask: VimEditorReplaceMask?
get() = editor.replaceMask
set(value) {
editor.replaceMask = value
}
override fun updateMode(mode: Mode) { override fun updateMode(mode: Mode) {
(injector.vimState as VimStateMachineImpl).mode = mode (injector.vimState as VimStateMachineImpl).mode = mode
@@ -91,7 +97,7 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor, VimEditorBase(
editor.vimChangeActionSwitchMode = value editor.vimChangeActionSwitchMode = value
} }
override val indentConfig: VimIndentConfig override val indentConfig: VimIndentConfig
get() = create(editor) get() = IndentConfig.create(editor)
override fun fileSize(): Long = editor.fileSize.toLong() override fun fileSize(): Long = editor.fileSize.toLong()
@@ -398,8 +404,8 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor, VimEditorBase(
return editor.visualPositionToOffset(VisualPosition(position.line, position.column, position.leansRight)) return editor.visualPositionToOffset(VisualPosition(position.line, position.column, position.leansRight))
} }
override fun exitInsertMode(context: ExecutionContext, operatorArguments: OperatorArguments) { override fun exitInsertMode(context: ExecutionContext) {
editor.exitInsertMode(context.ij, operatorArguments) editor.exitInsertMode(context.ij)
} }
override fun exitSelectModeNative(adjustCaret: Boolean) { override fun exitSelectModeNative(adjustCaret: Boolean) {
@@ -471,6 +477,7 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor, VimEditorBase(
get() = (editor as? EditorEx)?.isInsertMode ?: false get() = (editor as? EditorEx)?.isInsertMode ?: false
set(value) { set(value) {
(editor as? EditorEx)?.isInsertMode = value (editor as? EditorEx)?.isInsertMode = value
forgetAllReplaceMasks()
} }
override val document: VimDocument override val document: VimDocument

View File

@@ -1,718 +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.
*/
/**
* IdeaVim command index.
*
*
* 1. Insert mode
*
* tag action
* -------------------------------------------------------------------------------------------------------------------
*
* |i_CTRL-@| {@link com.maddyhome.idea.vim.action.change.insert.InsertPreviousInsertExitAction}
* |i_CTRL-A| {@link com.maddyhome.idea.vim.action.change.insert.InsertPreviousInsertAction}
* |i_CTRL-C| {@link com.maddyhome.idea.vim.action.change.insert.InsertExitModeAction}
* |i_CTRL-D| {@link com.maddyhome.idea.vim.action.change.shift.ShiftLeftLinesAction}
* |i_CTRL-E| {@link com.maddyhome.idea.vim.action.change.insert.InsertCharacterBelowCursorAction}
* |i_CTRL-G_j| TO BE IMPLEMENTED
* |i_CTRL-G_k| TO BE IMPLEMENTED
* |i_CTRL-G_u| TO BE IMPLEMENTED
* |i_<BS>| {@link com.maddyhome.idea.vim.action.editor.VimEditorBackSpace}
* |i_digraph| IdeaVim enter digraph
* |i_CTRL-H| IntelliJ editor backspace
* |i_<Tab>| {@link com.maddyhome.idea.vim.action.editor.VimEditorTab}
* |i_CTRL-I| IntelliJ editor tab
* |i_<NL>| {@link com.maddyhome.idea.vim.action.change.insert.InsertEnterAction}
* |i_CTRL-J| TO BE IMPLEMENTED
* |i_CTRL-K| {@link com.maddyhome.idea.vim.action.change.insert.InsertCompletedDigraphAction}
* |i_CTRL-L| TO BE IMPLEMENTED
* |i_<CR>| {@link com.maddyhome.idea.vim.action.change.insert.InsertEnterAction}
* |i_CTRL-M| {@link com.maddyhome.idea.vim.action.change.insert.InsertEnterAction}
* |i_CTRL-N| {@link com.maddyhome.idea.vim.action.window.LookupDownAction}
* |i_CTRL-O| {@link com.maddyhome.idea.vim.action.change.insert.InsertSingleCommandAction}
* |i_CTRL-P| {@link com.maddyhome.idea.vim.action.window.LookupUpAction}
* |i_CTRL-Q| TO BE IMPLEMENTED
* |i_CTRL-R| {@link com.maddyhome.idea.vim.action.change.insert.InsertRegisterAction}
* |i_CTRL-R_CTRL-R| TO BE IMPLEMENTED
* |i_CTRL-R_CTRL-O| TO BE IMPLEMENTED
* |i_CTRL-R_CTRL-P| TO BE IMPLEMENTED
* |i_CTRL-T| {@link com.maddyhome.idea.vim.action.change.shift.ShiftRightLinesAction}
* |i_CTRL-U| {@link com.maddyhome.idea.vim.action.change.insert.InsertDeleteInsertedTextAction}
* |i_CTRL-V| {@link com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction}
* |i_CTRL-V_digit| {@link com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction}
* |i_CTRL-W| {@link com.maddyhome.idea.vim.action.change.insert.InsertDeletePreviousWordAction}
* |i_CTRL-X| TO BE IMPLEMENTED
* |i_CTRL-Y| {@link com.maddyhome.idea.vim.action.change.insert.InsertCharacterAboveCursorAction}
* |i_CTRL-Z| TO BE IMPLEMENTED
* |i_<Esc>| {@link com.maddyhome.idea.vim.action.change.insert.InsertExitModeAction}
* |i_CTRL-[| {@link com.maddyhome.idea.vim.action.change.insert.InsertExitModeAction}
* |i_CTRL-\_CTRL-N| {@link com.maddyhome.idea.vim.action.ResetModeAction}
* |i_CTRL-\_CTRL-G| TO BE IMPLEMENTED
* |i_CTRL-]} TO BE IMPLEMENTED
* |i_CTRL-^| TO BE IMPLEMENTED
* |i_CTRL-_| TO BE IMPLEMENTED
* |i_0_CTRL-D| TO BE IMPLEMENTED
* |i_^_CTRL-D| TO BE IMPLEMENTED
* |i_<Del>| {@link com.maddyhome.idea.vim.action.editor.VimEditorDelete}
* |i_<Left>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLeftInsertModeAction}
* |i_<S-Left>| {@link com.maddyhome.idea.vim.action.motion.text.MotionWordLeftInsertAction}
* |i_<C-Left>| {@link com.maddyhome.idea.vim.action.motion.text.MotionWordLeftInsertAction}
* |i_<Right>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionRightInsertAction}
* |i_<S-Right>| {@link com.maddyhome.idea.vim.action.motion.text.MotionWordRightInsertAction}
* |i_<C-Right>| {@link com.maddyhome.idea.vim.action.motion.text.MotionWordRightInsertAction}
* |i_<Up>| {@link com.maddyhome.idea.vim.action.editor.VimEditorUp}
* |i_<S-Up>| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageUpInsertModeAction}
* |i_<Down>| {@link com.maddyhome.idea.vim.action.editor.VimEditorDown}
* |i_<S-Down>| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageDownInsertModeAction}
* |i_<Home>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionFirstColumnInsertModeAction}
* |i_<C-Home>| {@link com.maddyhome.idea.vim.action.motion.updown.MotionGotoLineFirstInsertAction}
* |i_<End>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLastColumnInsertAction}
* |i_<C-End>| {@link com.maddyhome.idea.vim.action.motion.updown.MotionGotoLineLastEndInsertAction}
* |i_<Insert>| {@link com.maddyhome.idea.vim.action.change.insert.InsertInsertAction}
* |i_<PageUp>| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageUpInsertModeAction}
* |i_<PageDown>| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageDownInsertModeAction}
* |i_<F1>| IntelliJ help
* |i_<Insert>| IntelliJ editor toggle insert/replace
* |i_CTRL-X_index| TO BE IMPLEMENTED
*
*
* 2. Normal mode
*
* tag action
* -------------------------------------------------------------------------------------------------------------------
*
* |CTRL-A| {@link com.maddyhome.idea.vim.action.change.change.number.ChangeNumberIncAction}
* |CTRL-B| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageUpAction}
* |CTRL-C| TO BE IMPLEMENTED
* |CTRL-D| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollHalfPageDownAction}
* |CTRL-E| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLineDownAction}
* |CTRL-F| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageDownAction}
* |CTRL-G| {@link com.maddyhome.idea.vim.action.file.FileGetFileInfoAction}
* |<BS>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionBackspaceAction}
* |CTRL-H| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionBackspaceAction}
* |<Tab>| TO BE IMPLEMENTED
* |CTRL-I| {@link com.maddyhome.idea.vim.action.motion.mark.MotionJumpNextAction}
* |<NL>| {@link com.maddyhome.idea.vim.action.motion.updown.MotionDownNotLineWiseAction}
* |CTRL-J| TO BE IMPLEMENTED
* |CTRL-L| not applicable
* |<CR>| {@link com.maddyhome.idea.vim.action.motion.updown.EnterNormalAction}
* |CTRL-M| {@link com.maddyhome.idea.vim.action.motion.updown.MotionDownFirstNonSpaceAction}
* |CTRL-N| {@link com.maddyhome.idea.vim.action.motion.updown.MotionDownCtrlNAction}
* |CTRL-O| {@link com.maddyhome.idea.vim.action.motion.mark.MotionJumpPreviousAction}
* |CTRL-P| {@link com.maddyhome.idea.vim.action.motion.updown.MotionUpCtrlPAction}
* |CTRL-R| {@link com.maddyhome.idea.vim.action.change.RedoAction}
* |CTRL-T| TO BE IMPLEMENTED
* |CTRL-U| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollHalfPageUpAction}
* |CTRL-V| {@link com.maddyhome.idea.vim.action.motion.visual.VisualToggleBlockModeAction}
* |CTRL-W| see window commands
* |CTRL-X| {@link com.maddyhome.idea.vim.action.change.change.number.ChangeNumberDecAction}
* |CTRL-Y| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLineUpAction}
* |CTRL-Z| TO BE IMPLEMENTED
* |CTRL-]| {@link com.maddyhome.idea.vim.action.motion.search.GotoDeclarationAction}
* |CTRL-6| {@link com.maddyhome.idea.vim.action.file.FilePreviousAction}
* |CTRL-\CTRL-N| {@link com.maddyhome.idea.vim.action.ResetModeAction}
* |<Space>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionSpaceAction}
* |!| {@link com.maddyhome.idea.vim.action.change.change.FilterMotionAction}
* |!!| translated to !_
* |quote| handled by command key parser
* |#| {@link com.maddyhome.idea.vim.action.motion.search.SearchWholeWordBackwardAction}
* |$| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLastColumnAction}
* |%| {@link com.maddyhome.idea.vim.action.motion.updown.MotionPercentOrMatchAction}
* |&| {@link com.maddyhome.idea.vim.action.change.change.ChangeLastSearchReplaceAction}
* |'| {@link com.maddyhome.idea.vim.action.motion.mark.MotionGotoMarkLineAction}
* |''| ?
* ...
* |(| {@link com.maddyhome.idea.vim.action.motion.text.MotionSentencePreviousStartAction}
* |)| {@link com.maddyhome.idea.vim.action.motion.text.MotionSentenceNextStartAction}
* |star| {@link com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction}
* |+| {@link com.maddyhome.idea.vim.action.motion.updown.MotionDownFirstNonSpaceAction}
* |,| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLastMatchCharReverseAction}
* |-| {@link com.maddyhome.idea.vim.action.motion.updown.MotionUpFirstNonSpaceAction}
* |.| {@link com.maddyhome.idea.vim.action.change.RepeatChangeAction}
* |/| {@link com.maddyhome.idea.vim.action.motion.search.SearchEntryFwdAction}
* |:| {@link com.maddyhome.idea.vim.action.ex.ExEntryAction}
* |;| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLastMatchCharAction}
* |<| {@link com.maddyhome.idea.vim.action.change.shift.ShiftLeftMotionAction}
* |<<| translated to <_
* |=| {@link com.maddyhome.idea.vim.action.change.shift.AutoIndentMotionAction}
* |==| translated to =_
* |>| {@link com.maddyhome.idea.vim.action.change.shift.ShiftRightMotionAction}
* |>>| translated to >_
* |?| {@link com.maddyhome.idea.vim.action.motion.search.SearchEntryRevAction}
* |@| {@link com.maddyhome.idea.vim.action.macro.PlaybackRegisterAction}
* |A| {@link com.maddyhome.idea.vim.action.change.insert.InsertAfterLineEndAction}
* |B| {@link com.maddyhome.idea.vim.action.motion.text.MotionBigWordLeftAction}
* |C| {@link com.maddyhome.idea.vim.action.change.change.ChangeEndOfLineAction}
* |D| {@link com.maddyhome.idea.vim.action.change.delete.DeleteEndOfLineAction}
* |E| {@link com.maddyhome.idea.vim.action.motion.text.MotionBigWordEndRightAction}
* |F| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLeftMatchCharAction}
* |G| {@link com.maddyhome.idea.vim.action.motion.updown.MotionGotoLineLastAction}
* |H| {@link com.maddyhome.idea.vim.action.motion.screen.MotionFirstScreenLineAction}
* |H| {@link com.maddyhome.idea.vim.action.motion.screen.MotionOpPendingFirstScreenLineAction}
* |I| {@link com.maddyhome.idea.vim.action.change.insert.InsertBeforeFirstNonBlankAction}
* |J| {@link com.maddyhome.idea.vim.action.change.delete.DeleteJoinLinesSpacesAction}
* |K| {@link com.maddyhome.idea.vim.action.editor.VimQuickJavaDoc}
* |L| {@link com.maddyhome.idea.vim.action.motion.screen.MotionLastScreenLineAction}
* |L| {@link com.maddyhome.idea.vim.action.motion.screen.MotionOpPendingLastScreenLineAction}
* |M| {@link com.maddyhome.idea.vim.action.motion.screen.MotionMiddleScreenLineAction}
* |N| {@link com.maddyhome.idea.vim.action.motion.search.SearchAgainPreviousAction}
* |O| {@link com.maddyhome.idea.vim.action.change.insert.InsertNewLineAboveAction}
* |P| {@link com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorAction}
* |Q| TO BE IMPLEMENTED
* |R| {@link com.maddyhome.idea.vim.action.change.change.ChangeReplaceAction}
* |S| {@link com.maddyhome.idea.vim.action.change.change.ChangeLineAction}
* |T| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLeftTillMatchCharAction}
* |U| ?
* |V| {@link com.maddyhome.idea.vim.action.motion.visual.VisualToggleLineModeAction}
* |W| {@link com.maddyhome.idea.vim.action.motion.text.MotionBigWordRightAction}
* |X| {@link com.maddyhome.idea.vim.action.change.delete.DeleteCharacterLeftAction}
* |Y| {@link com.maddyhome.idea.vim.action.copy.YankLineAction}
* |ZZ| {@link com.maddyhome.idea.vim.action.file.FileSaveCloseAction}
* |ZQ| {@link com.maddyhome.idea.vim.action.file.FileSaveCloseAction}
* |[| see bracket commands
* |]| see bracket commands
* |^| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionFirstNonSpaceAction}
* |_| {@link com.maddyhome.idea.vim.action.motion.updown.MotionDownLess1FirstNonSpaceAction}
* |`| {@link com.maddyhome.idea.vim.action.motion.mark.MotionGotoMarkAction}
* |``| ?
* ...
* |0| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionFirstColumnAction}
* |a| {@link com.maddyhome.idea.vim.action.change.insert.InsertAfterCursorAction}
* |b| {@link com.maddyhome.idea.vim.action.motion.text.MotionWordLeftAction}
* |c| {@link com.maddyhome.idea.vim.action.change.change.ChangeMotionAction}
* |cc| translated to c_
* |d| {@link com.maddyhome.idea.vim.action.change.delete.DeleteMotionAction}
* |dd| translated to d_
* |do| TO BE IMPLEMENTED
* |dp| TO BE IMPLEMENTED
* |e| {@link com.maddyhome.idea.vim.action.motion.text.MotionWordEndRightAction}
* |f| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionRightMatchCharAction}
* |g| see commands starting with 'g'
* |h| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLeftAction}
* |i| {@link com.maddyhome.idea.vim.action.change.insert.InsertBeforeCursorAction}
* |j| {@link com.maddyhome.idea.vim.action.motion.updown.MotionDownAction}
* |k| {@link com.maddyhome.idea.vim.action.motion.updown.MotionUpAction}
* |l| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionRightAction}
* |m| {@link com.maddyhome.idea.vim.action.motion.mark.MotionMarkAction}
* |n| {@link com.maddyhome.idea.vim.action.motion.search.SearchAgainNextAction}
* |o| {@link com.maddyhome.idea.vim.action.change.insert.InsertNewLineBelowAction}
* |p| {@link com.maddyhome.idea.vim.action.copy.PutTextAfterCursorAction}
* |q| {@link com.maddyhome.idea.vim.action.macro.ToggleRecordingAction}
* |r| {@link com.maddyhome.idea.vim.action.change.change.ChangeCharacterAction}
* |s| {@link com.maddyhome.idea.vim.action.change.change.ChangeCharactersAction}
* |t| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionRightTillMatchCharAction}
* |u| {@link com.maddyhome.idea.vim.action.change.UndoAction}
* |v| {@link com.maddyhome.idea.vim.action.motion.visual.VisualToggleCharacterModeAction}
* |w| {@link com.maddyhome.idea.vim.action.motion.text.MotionWordRightAction}
* |x| {@link com.maddyhome.idea.vim.action.change.delete.DeleteCharacterRightAction}
* |y| {@link com.maddyhome.idea.vim.action.copy.YankMotionAction}
* |yy| translated to y_
* |z| see commands starting with 'z'
* |{| {@link com.maddyhome.idea.vim.action.motion.text.MotionParagraphPreviousAction}
* |bar| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionColumnAction}
* |}| {@link com.maddyhome.idea.vim.action.motion.text.MotionParagraphNextAction}
* |~| {@link com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleCharacterAction}
* |<C-End>| {@link com.maddyhome.idea.vim.action.motion.updown.MotionGotoLineLastEndAction}
* |<C-Home>| {@link com.maddyhome.idea.vim.action.motion.updown.MotionGotoLineFirstAction}
* |<C-Left>| {@link com.maddyhome.idea.vim.action.motion.text.MotionWordLeftAction}
* |<C-Right>| {@link com.maddyhome.idea.vim.action.motion.text.MotionWordRightAction}
* |<C-Down>| {@link com.maddyhome.idea.vim.action.motion.scroll.CtrlDownAction}
* |<C-Up>| {@link com.maddyhome.idea.vim.action.motion.scroll.CtrlUpAction}
* |<Del>| {@link com.maddyhome.idea.vim.action.change.delete.DeleteCharacterAction}
* |<Down>| {@link com.maddyhome.idea.vim.action.motion.updown.MotionArrowDownAction}
* |<End>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionEndAction}
* |<F1>| IntelliJ help
* |<Home>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionHomeAction}
* |<Insert>| {@link com.maddyhome.idea.vim.action.change.insert.InsertBeforeCursorAction}
* |<Left>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionArrowLeftAction}
* |<PageDown>| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageDownAction}
* |<PageUp>| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollPageUpAction}
* |<Right>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionArrowRightAction}
* |<S-Down>| {@link com.maddyhome.idea.vim.action.motion.updown.MotionShiftDownAction}
* |<S-Left>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionShiftLeftAction}
* |<S-Right>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionShiftRightAction}
* |<S-Up>| {@link com.maddyhome.idea.vim.action.motion.updown.MotionShiftUpAction}
* |<S-Home>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionShiftHomeAction}
* |<S-End>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionShiftEndAction}
* |<Up>| {@link com.maddyhome.idea.vim.action.motion.updown.MotionArrowUpAction}
*
*
* 2.1. Text objects
*
* Text object commands are listed in the visual mode section.
*
*
* 2.2. Window commands
*
* tag action
* -------------------------------------------------------------------------------------------------------------------
*
* |CTRL-W_+| TO BE IMPLEMENTED
* |CTRL-W_-| TO BE IMPLEMENTED
* |CTRL-W_<| TO BE IMPLEMENTED
* |CTRL-W_=| TO BE IMPLEMENTED
* |CTRL-W_>| TO BE IMPLEMENTED
* |CTRL-W_H| TO BE IMPLEMENTED
* |CTRL-W_J| TO BE IMPLEMENTED
* |CTRL-W_K| TO BE IMPLEMENTED
* |CTRL-W_L| TO BE IMPLEMENTED
* |CTRL-W_P| TO BE IMPLEMENTED
* |CTRL-W_R| TO BE IMPLEMENTED
* |CTRL-W_S| {@link com.maddyhome.idea.vim.action.window.HorizontalSplitAction}
* |CTRL-W_T| TO BE IMPLEMENTED
* |CTRL-W_W| {@link com.maddyhome.idea.vim.action.window.WindowPrevAction}
* |CTRL-W_]| TO BE IMPLEMENTED
* |CTRL-W_^| TO BE IMPLEMENTED
* |CTRL-W__| TO BE IMPLEMENTED
* |CTRL-W_b| TO BE IMPLEMENTED
* |CTRL-W_c| {@link com.maddyhome.idea.vim.action.window.CloseWindowAction}
* |CTRL-W_d| TO BE IMPLEMENTED
* |CTRL-W_f| TO BE IMPLEMENTED
* |CTRL-W-F| TO BE IMPLEMENTED
* |CTRL-W-g]| TO BE IMPLEMENTED
* |CTRL-W-g}| TO BE IMPLEMENTED
* |CTRL-W-gf| TO BE IMPLEMENTED
* |CTRL-W-gF| TO BE IMPLEMENTED
* |CTRL-W_h| {@link com.maddyhome.idea.vim.action.window.WindowLeftAction}
* |CTRL-W_i| TO BE IMPLEMENTED
* |CTRL-W_j| {@link com.maddyhome.idea.vim.action.window.WindowDownAction}
* |CTRL-W_k| {@link com.maddyhome.idea.vim.action.window.WindowUpAction}
* |CTRL-W_l| {@link com.maddyhome.idea.vim.action.window.WindowRightAction}
* |CTRL-W_n| TO BE IMPLEMENTED
* |CTRL-W_o| {@link com.maddyhome.idea.vim.action.window.WindowOnlyAction}
* |CTRL-W_p| TO BE IMPLEMENTED
* |CTRL-W_q| TO BE IMPLEMENTED
* |CTRL-W_r| TO BE IMPLEMENTED
* |CTRL-W_s| {@link com.maddyhome.idea.vim.action.window.HorizontalSplitAction}
* |CTRL-W_t| TO BE IMPLEMENTED
* |CTRL-W_v| {@link com.maddyhome.idea.vim.action.window.VerticalSplitAction}
* |CTRL-W_w| {@link com.maddyhome.idea.vim.action.window.WindowNextAction}
* |CTRL-W_x| TO BE IMPLEMENTED
* |CTRL-W_z| TO BE IMPLEMENTED
* |CTRL-W_bar| TO BE IMPLEMENTED
* |CTRL-W_}| TO BE IMPLEMENTED
* |CTRL-W_<Down>| {@link com.maddyhome.idea.vim.action.window.WindowDownAction}
* |CTRL-W_<Up>| {@link com.maddyhome.idea.vim.action.window.WindowUpAction}
* |CTRL-W_<Left>| {@link com.maddyhome.idea.vim.action.window.WindowLeftAction}
* |CTRL-W_<Right>| {@link com.maddyhome.idea.vim.action.window.WindowRightAction}
* |CTRL-W_CTRL-H| {@link com.maddyhome.idea.vim.action.window.WindowLeftAction}
* |CTRL-W_CTRL-J| {@link com.maddyhome.idea.vim.action.window.WindowDownAction}
* |CTRL-W_CTRL-K| {@link com.maddyhome.idea.vim.action.window.WindowUpAction}
* |CTRL-W_CTRL-L| {@link com.maddyhome.idea.vim.action.window.WindowRightAction}
*
*
* 2.3. Square bracket commands
*
* tag action
* -------------------------------------------------------------------------------------------------------------------
* |[_CTRL-D| TO BE IMPLEMENTED
* |[_CTRL-I| TO BE IMPLEMENTED
* |[#| TO BE IMPLEMENTED
* |['| TO BE IMPLEMENTED
* |[(| {@link com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedParenOpenAction}
* |[star| TO BE IMPLEMENTED
* |[`| TO BE IMPLEMENTED
* |[/| TO BE IMPLEMENTED
* |[D| TO BE IMPLEMENTED
* |[I| TO BE IMPLEMENTED
* |[M| {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodPreviousEndAction}
* |[P| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorNoIndentAction}
* |[P| {@link com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorNoIndentAction}
* |[[| {@link com.maddyhome.idea.vim.action.motion.text.MotionSectionBackwardStartAction}
* |[]| {@link com.maddyhome.idea.vim.action.motion.text.MotionSectionBackwardEndAction}
* |[c| TO BE IMPLEMENTED
* |[d| TO BE IMPLEMENTED
* |[f| TO BE IMPLEMENTED
* |[i| TO BE IMPLEMENTED
* |[m| {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodPreviousStartAction}
* |[p| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorNoIndentAction}
* |[p| {@link com.maddyhome.idea.vim.action.copy.PutTextAfterCursorNoIndentAction}
* |[s| {@link com.maddyhome.idea.vim.action.motion.text.MotionMisspelledWordPreviousAction}
* |[z| TO BE IMPLEMENTED
* |[{| {@link com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedBraceOpenAction}
* |]_CTRL-D| TO BE IMPLEMENTED
* |]_CTRL-I| TO BE IMPLEMENTED
* |]#| TO BE IMPLEMENTED
* |]'| TO BE IMPLEMENTED
* |])| {@link com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedParenCloseAction}
* |]star| TO BE IMPLEMENTED
* |]`| TO BE IMPLEMENTED
* |]/| TO BE IMPLEMENTED
* |]D| TO BE IMPLEMENTED
* |]I| TO BE IMPLEMENTED
* |]M| {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodNextEndAction}
* |]P| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorNoIndentAction}
* |]P| {@link com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorNoIndentAction}
* |][| {@link com.maddyhome.idea.vim.action.motion.text.MotionSectionForwardEndAction}
* |]]| {@link com.maddyhome.idea.vim.action.motion.text.MotionSectionForwardStartAction}
* |]c| TO BE IMPLEMENTED
* |]d| TO BE IMPLEMENTED
* |]f| TO BE IMPLEMENTED
* |]i| TO BE IMPLEMENTED
* |]m| {@link com.maddyhome.idea.vim.action.motion.text.MotionMethodNextStartAction}
* |]p| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorNoIndentAction}
* |]p| {@link com.maddyhome.idea.vim.action.copy.PutTextAfterCursorNoIndentAction}
* |]s| {@link com.maddyhome.idea.vim.action.motion.text.MotionMisspelledWordNextAction}
* |]z| TO BE IMPLEMENTED
* |]}| {@link com.maddyhome.idea.vim.action.motion.text.MotionUnmatchedBraceCloseAction}
*
*
* 2.4. Commands starting with 'g'
*
* tag action
* -------------------------------------------------------------------------------------------------------------------
*
* |g_CTRL-A| not applicable
* |g_CTRL-G| {@link com.maddyhome.idea.vim.action.file.FileGetLocationInfoAction}
* |g_CTRL-H| {@link com.maddyhome.idea.vim.action.motion.select.SelectEnableBlockModeAction}
* |g_CTRL-]| TO BE IMPLEMENTED
* |g#| {@link com.maddyhome.idea.vim.action.motion.search.SearchWordBackwardAction}
* |g$| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLastScreenColumnAction}
* |g&| {@link com.maddyhome.idea.vim.action.change.change.ChangeLastGlobalSearchReplaceAction}
* |v_g'| {@link com.maddyhome.idea.vim.action.motion.mark.MotionGotoFileMarkLineNoSaveJumpAction}
* |g'| {@link com.maddyhome.idea.vim.action.motion.mark.MotionGotoMarkLineNoSaveJumpAction}
* |g`| {@link com.maddyhome.idea.vim.action.motion.mark.MotionGotoMarkNoSaveJumpAction}
* |gstar| {@link com.maddyhome.idea.vim.action.motion.search.SearchWordForwardAction}
* |g+| TO BE IMPLEMENTED
* |g,| TO BE IMPLEMENTED
* |g-| TO BE IMPLEMENTED
* |g0| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionFirstScreenColumnAction}
* |g8| {@link com.maddyhome.idea.vim.action.file.FileGetHexAction}
* |g;| TO BE IMPLEMENTED
* |g<| TO BE IMPLEMENTED
* |g?| TO BE IMPLEMENTED
* |g?g?| TO BE IMPLEMENTED
* |gD| {@link com.maddyhome.idea.vim.action.motion.search.GotoDeclarationAction}
* |gE| {@link com.maddyhome.idea.vim.action.motion.text.MotionBigWordEndLeftAction}
* |gF| TO BE IMPLEMENTED
* |gH| {@link com.maddyhome.idea.vim.action.motion.select.SelectEnableLineModeAction}
* |gI| {@link com.maddyhome.idea.vim.action.change.insert.InsertLineStartAction}
* |gJ| {@link com.maddyhome.idea.vim.action.change.delete.DeleteJoinLinesAction}
* |gN| {@link com.maddyhome.idea.vim.action.motion.gn.VisualSelectPreviousSearch}
* |gN| {@link com.maddyhome.idea.vim.action.motion.gn.GnPreviousTextObject}
* |gP| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorMoveCursorAction}
* |gP| {@link com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorActionMoveCursor}
* |gQ| TO BE IMPLEMENTED
* |gR| TO BE IMPLEMENTED
* |gT| {@link com.maddyhome.idea.vim.action.window.tabs.PreviousTabAction}
* |gU| {@link com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperMotionAction}
* |gV| TO BE IMPLEMENTED
* |g]| TO BE IMPLEMENTED
* |g^| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionFirstScreenNonSpaceAction}
* |g_| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLastNonSpaceAction}
* |ga| {@link com.maddyhome.idea.vim.action.file.FileGetAsciiAction}
* |gd| {@link com.maddyhome.idea.vim.action.motion.search.GotoDeclarationAction}
* |ge| {@link com.maddyhome.idea.vim.action.motion.text.MotionWordEndLeftAction}
* |gf| TO BE IMPLEMENTED
* |gg| {@link com.maddyhome.idea.vim.action.motion.updown.MotionGotoLineFirstAction}
* |gh| {@link com.maddyhome.idea.vim.action.motion.select.SelectEnableCharacterModeAction}
* |gi| {@link com.maddyhome.idea.vim.action.change.insert.InsertAtPreviousInsertAction}
* |gj| TO BE IMPLEMENTED
* |gk| {@link com.maddyhome.idea.vim.action.motion.updown.MotionUpNotLineWiseAction}
* |gn| {@link com.maddyhome.idea.vim.action.motion.gn.VisualSelectNextSearch}
* |gn| {@link com.maddyhome.idea.vim.action.motion.gn.GnNextTextObject}
* |gm| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionMiddleColumnAction}
* |go| {@link com.maddyhome.idea.vim.action.motion.text.MotionNthCharacterAction}
* |gp| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorMoveCursorAction}
* |gp| {@link com.maddyhome.idea.vim.action.copy.PutTextAfterCursorActionMoveCursor}
* |gq| {@link com.maddyhome.idea.vim.action.change.change.ReformatCodeMotionAction}
* |gr| TO BE IMPLEMENTED
* |gs| TO BE IMPLEMENTED
* |gt| {@link com.maddyhome.idea.vim.action.window.tabs.NextTabAction}
* |gu| {@link com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerMotionAction}
* |gv| {@link com.maddyhome.idea.vim.action.motion.visual.VisualSelectPreviousAction}
* |gw| TO BE IMPLEMENTED
* |g@| {@link com.maddyhome.idea.vim.action.change.OperatorAction}
* |g~| {@link com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleMotionAction}
* |g<Down>| TO BE IMPLEMENTED
* |g<End>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionLastScreenColumnAction}
* |g<Home>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionFirstScreenColumnAction}
* |g<Up>| {@link com.maddyhome.idea.vim.action.motion.updown.MotionUpNotLineWiseAction}
*
*
* 2.5. Commands starting with 'z'
*
* tag action
* -------------------------------------------------------------------------------------------------------------------
* |z<CR>| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollFirstScreenLineStartAction}
* |z+| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollFirstScreenLinePageStartAction}
* |z-| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLastScreenLineStartAction}
* |z.| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollMiddleScreenLineStartAction}
* |z=| TO BE IMPLEMENTED
* |zA| TO BE IMPLEMENTED
* |zC| {@link com.maddyhome.idea.vim.action.fold.VimCollapseRegionRecursively}
* |zD| TO BE IMPLEMENTED
* |zE| TO BE IMPLEMENTED
* |zF| TO BE IMPLEMENTED
* |zG| TO BE IMPLEMENTED
* |zH| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollHalfWidthLeftAction}
* |zL| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollHalfWidthRightAction}
* |zM| {@link com.maddyhome.idea.vim.action.fold.VimCollapseAllRegions}
* |zN| TO BE IMPLEMENTED
* |zO| {@link com.maddyhome.idea.vim.action.fold.VimExpandRegionRecursively}
* |zR| {@link com.maddyhome.idea.vim.action.fold.VimExpandAllRegions}
* |zW| TO BE IMPLEMENTED
* |zX| TO BE IMPLEMENTED
* |z^| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLastScreenLinePageStartAction}
* |za| TO BE IMPLEMENTED
* |zb| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLastScreenLineAction}
* |zc| {@link com.maddyhome.idea.vim.action.fold.VimCollapseRegion}
* |zd| not applicable
* |ze| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollLastScreenColumnAction}
* |zf| not applicable
* |zg| TO BE IMPLEMENTED
* |zh| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollColumnRightAction}
* |zi| TO BE IMPLEMENTED
* |zj| TO BE IMPLEMENTED
* |zk| TO BE IMPLEMENTED
* |zl| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollColumnLeftAction}
* |zm| TO BE IMPLEMENTED
* |zn| TO BE IMPLEMENTED
* |zo| {@link com.maddyhome.idea.vim.action.fold.VimExpandRegion}
* |zr| TO BE IMPLEMENTED
* |zs| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollFirstScreenColumnAction}
* |zt| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollFirstScreenLineAction}
* |zv| TO BE IMPLEMENTED
* |zw| TO BE IMPLEMENTED
* |zx| TO BE IMPLEMENTED
* |zz| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollMiddleScreenLineAction}
* |z<Left>| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollColumnRightAction}
* |z<Right>| {@link com.maddyhome.idea.vim.action.motion.scroll.MotionScrollColumnLeftAction}
*
*
* 3. Visual mode
*
* tag action
* -------------------------------------------------------------------------------------------------------------------
*
* |v_CTRL-\_CTRL-N| {@link com.maddyhome.idea.vim.action.motion.visual.VisualExitModeAction}
* |v_CTRL-\_CTRL-G| TO BE IMPLEMENTED
* |v_CTRL-A| {@link com.maddyhome.idea.vim.action.change.change.number.ChangeVisualNumberIncAction}
* |v_CTRL-C| {@link com.maddyhome.idea.vim.action.motion.visual.VisualExitModeAction}
* |v_CTRL-G| {@link com.maddyhome.idea.vim.action.motion.select.SelectToggleVisualMode}
* |v_<BS>| NVO mapping
* |v_CTRL-H| NVO mapping
* |v_CTRL-O| TO BE IMPLEMENTED
* |v_CTRL-V| NVO mapping
* |v_<Esc>| {@link com.maddyhome.idea.vim.action.motion.visual.VisualExitModeAction}
* |v_CTRL-X| {@link com.maddyhome.idea.vim.action.change.change.number.ChangeVisualNumberDecAction}
* |v_CTRL-]| TO BE IMPLEMENTED
* |v_!| {@link com.maddyhome.idea.vim.action.change.change.FilterVisualLinesAction}
* |v_:| NVO mapping
* |v_<| {@link com.maddyhome.idea.vim.action.change.shift.ShiftLeftVisualAction}
* |v_=| {@link com.maddyhome.idea.vim.action.change.change.AutoIndentLinesVisualAction}
* |v_>| {@link com.maddyhome.idea.vim.action.change.shift.ShiftRightVisualAction}
* |v_b_A| {@link com.maddyhome.idea.vim.action.change.insert.VisualBlockAppendAction}
* |v_C| {@link com.maddyhome.idea.vim.action.change.change.ChangeVisualLinesEndAction}
* |v_D| {@link com.maddyhome.idea.vim.action.change.delete.DeleteVisualLinesEndAction}
* |v_b_I| {@link com.maddyhome.idea.vim.action.change.insert.VisualBlockInsertAction}
* |v_J| {@link com.maddyhome.idea.vim.action.change.delete.DeleteJoinVisualLinesSpacesAction}
* |v_K| TO BE IMPLEMENTED
* |v_O| {@link com.maddyhome.idea.vim.action.motion.visual.VisualSwapEndsBlockAction}
* |v_P| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorAction}
* |v_R| {@link com.maddyhome.idea.vim.action.change.change.ChangeVisualLinesAction}
* |v_S| {@link com.maddyhome.idea.vim.action.change.change.ChangeVisualLinesAction}
* |v_U| {@link com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperVisualAction}
* |v_V| NV mapping
* |v_X| {@link com.maddyhome.idea.vim.action.change.delete.DeleteVisualLinesAction}
* |v_Y| {@link com.maddyhome.idea.vim.action.copy.YankVisualLinesAction}
* |v_aquote| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockDoubleQuoteAction}
* |v_a'| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockSingleQuoteAction}
* |v_a(| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockParenAction}
* |v_a)| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockParenAction}
* |v_a<| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockAngleAction}
* |v_a>| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockAngleAction}
* |v_aB| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockBraceAction}
* |v_aW| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBigWordAction}
* |v_a[| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockBracketAction}
* |v_a]| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockBracketAction}
* |v_a`| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockBackQuoteAction}
* |v_ab| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockParenAction}
* |v_ap| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterParagraphAction}
* |v_as| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterSentenceAction}
* |v_at| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockTagAction}
* |v_aw| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterWordAction}
* |v_a{| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockBraceAction}
* |v_a}| {@link com.maddyhome.idea.vim.action.motion.object.MotionOuterBlockBraceAction}
* |v_c| {@link com.maddyhome.idea.vim.action.change.change.ChangeVisualAction}
* |v_d| {@link com.maddyhome.idea.vim.action.change.delete.DeleteVisualAction}
* |v_gCTRL-A| {@link com.maddyhome.idea.vim.action.change.change.number.ChangeVisualNumberAvalancheIncAction}
* |v_gCTRL-X| {@link com.maddyhome.idea.vim.action.change.change.number.ChangeVisualNumberAvalancheDecAction}
* |v_gJ| {@link com.maddyhome.idea.vim.action.change.delete.DeleteJoinVisualLinesAction}
* |v_gq| {@link com.maddyhome.idea.vim.action.change.change.ReformatCodeVisualAction}
* |v_gv| {@link com.maddyhome.idea.vim.action.motion.visual.VisualSwapSelectionsAction}
* |v_g`| {@link com.maddyhome.idea.vim.action.motion.mark.MotionGotoFileMarkNoSaveJumpAction}
* |v_g@| {@link com.maddyhome.idea.vim.action.change.VisualOperatorAction}
* |v_iquote| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockDoubleQuoteAction}
* |v_i'| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockSingleQuoteAction}
* |v_i(| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockParenAction}
* |v_i)| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockParenAction}
* |v_i<| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockAngleAction}
* |v_i>| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockAngleAction}
* |v_iB| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockBraceAction}
* |v_iW| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBigWordAction}
* |v_i[| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockBracketAction}
* |v_i]| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockBracketAction}
* |v_i`| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockBackQuoteAction}
* |v_ib| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockParenAction}
* |v_ip| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerParagraphAction}
* |v_is| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerSentenceAction}
* |v_it| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockTagAction}
* |v_iw| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerWordAction}
* |v_i{| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockBraceAction}
* |v_i}| {@link com.maddyhome.idea.vim.action.motion.object.MotionInnerBlockBraceAction}
* |v_o| {@link com.maddyhome.idea.vim.action.motion.visual.VisualSwapEndsAction}
* |v_p| {@link com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorAction}
* |v_r| {@link com.maddyhome.idea.vim.action.change.change.ChangeVisualCharacterAction}
* |v_s| {@link com.maddyhome.idea.vim.action.change.change.ChangeVisualAction}
* |v_u| {@link com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerVisualAction}
* |v_v| NV mapping
* |v_x| {@link com.maddyhome.idea.vim.action.change.delete.DeleteVisualAction}
* |v_y| {@link com.maddyhome.idea.vim.action.copy.YankVisualAction}
* |v_~| {@link com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleVisualAction}
* |v_`| {@link com.maddyhome.idea.vim.action.motion.mark.MotionGotoFileMarkAction}
* |v_'| {@link com.maddyhome.idea.vim.action.motion.mark.MotionGotoFileMarkLineAction}
*
*
* 4. Select mode
*
* tag action
* -------------------------------------------------------------------------------------------------------------------
* |<BS>| {@link com.maddyhome.idea.vim.action.motion.select.SelectDeleteAction}
* |<CR>| {@link com.maddyhome.idea.vim.action.motion.select.SelectEnterAction}
* |<DEL>| {@link com.maddyhome.idea.vim.action.motion.select.SelectDeleteAction}
* |<ESC>| {@link com.maddyhome.idea.vim.action.motion.select.SelectEscapeAction}
* |<C-G>| {@link com.maddyhome.idea.vim.action.motion.select.SelectToggleVisualMode}
* |<S-Down>| {@link com.maddyhome.idea.vim.action.motion.updown.MotionShiftDownAction}
* |<S-Left>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionShiftLeftAction}
* |<S-Right>| {@link com.maddyhome.idea.vim.action.motion.leftright.MotionShiftRightAction}
* |<S-Up>| {@link com.maddyhome.idea.vim.action.motion.updown.MotionShiftUpAction}
* |<Down>| {@link com.maddyhome.idea.vim.action.motion.updown.MotionArrowDownAction}
* |<Left>| {@link com.maddyhome.idea.vim.action.motion.select.motion.SelectMotionLeftAction}
* |<Right>| {@link com.maddyhome.idea.vim.action.motion.select.motion.SelectMotionRightAction}
* |<Up>| {@link com.maddyhome.idea.vim.action.motion.updown.MotionArrowUpAction}
*
* 5. Command line editing
*
* tag action
* -------------------------------------------------------------------------------------------------------------------
*
* |c_CTRL-A| TO BE IMPLEMENTED
* |c_CTRL-B| {@link javax.swing.text.DefaultEditorKit#beginLineAction}
* |c_CTRL-C| {@link com.maddyhome.idea.vim.ui.ex.CancelEntryAction}
* |c_CTRL-D| TO BE IMPLEMENTED
* |c_CTRL-E| {@link javax.swing.text.DefaultEditorKit#endLineAction}
* |c_CTRL-G| TO BE IMPLEMENTED
* |c_CTRL-H| {@link com.maddyhome.idea.vim.ui.ex.DeletePreviousCharAction}
* |c_CTRL-I| TO BE IMPLEMENTED
* |c_CTRL-J| {@link com.maddyhome.idea.vim.ui.ex.CompleteEntryAction}
* |c_CTRL-K| Handled by KeyHandler
* |c_CTRL-L| TO BE IMPLEMENTED
* |c_CTRL-M| {@link com.maddyhome.idea.vim.action.ex.ProcessExEntryAction}
* |c_CTRL-N| {@link com.maddyhome.idea.vim.ui.ex.HistoryDownAction}
* |c_CTRL-P| {@link com.maddyhome.idea.vim.ui.ex.HistoryUpAction}
* |c_CTRL-Q| Handled by KeyHandler
* |c_CTRL-R_CTRL-A| TO BE IMPLEMENTED
* |c_CTRL-R_CTRL-F| TO BE IMPLEMENTED
* |c_CTRL-R_CTRL-L| TO BE IMPLEMENTED
* |c_CTRL-R_CTRL-O| TO BE IMPLEMENTED
* |c_CTRL-R_CTRL-P| TO BE IMPLEMENTED
* |c_CTRL-R_CTRL-R| TO BE IMPLEMENTED
* |c_CTRL-R_CTRL-W| TO BE IMPLEMENTED
* |c_CTRL-T| TO BE IMPLEMENTED
* |c_CTRL-U| {@link com.maddyhome.idea.vim.ui.ex.DeleteToCursorAction}
* |c_CTRL-V| Handled by KeyHandler
* |c_CTRL-W| {@link com.maddyhome.idea.vim.ui.ex.DeletePreviousWordAction}
* |c_CTRL-Y| TO BE IMPLEMENTED
* |c_CTRL-\_e| TO BE IMPLEMENTED
* |c_CTRL-\_CTRL-G| TO BE IMPLEMENTED
* |c_CTRL-\_CTRL-N| TO BE IMPLEMENTED
* |c_CTRL-_| not applicable
* |c_CTRL-^| not applicable
* |c_CTRL-]| TO BE IMPLEMENTED
* |c_CTRL-[| {@link com.maddyhome.idea.vim.ui.ex.EscapeCharAction}
* |c_<BS>| {@link com.maddyhome.idea.vim.ui.ex.DeletePreviousCharAction}
* |c_<CR>| {@link com.maddyhome.idea.vim.ui.ex.CompleteEntryAction}
* |c_<C-Left>| {@link javax.swing.text.DefaultEditorKit#previousWordAction}
* |c_<C-Right>| {@link javax.swing.text.DefaultEditorKit#nextWordAction}
* |c_<Del>| {@link javax.swing.text.DefaultEditorKit#deleteNextCharAction}
* |c_<Down>| {@link com.maddyhome.idea.vim.ui.ex.HistoryDownFilterAction}
* |c_<End>| {@link javax.swing.text.DefaultEditorKit#endLineAction}
* |c_<Esc>| {@link com.maddyhome.idea.vim.ui.ex.EscapeCharAction}
* |c_<Home>| {@link javax.swing.text.DefaultEditorKit#beginLineAction}
* |c_<Insert>| {@link com.maddyhome.idea.vim.ui.ex.ToggleInsertReplaceAction}
* |c_<Left>| {@link javax.swing.text.DefaultEditorKit#backwardAction}
* |c_<LeftMouse>| not applicable
* |c_<MiddleMouse>| TO BE IMPLEMENTED
* |c_<NL>| {@link com.maddyhome.idea.vim.ui.ex.CompleteEntryAction}
* |c_<PageUp>| {@link com.maddyhome.idea.vim.ui.ex.HistoryUpAction}
* |c_<PageDown>| {@link com.maddyhome.idea.vim.ui.ex.HistoryDownAction}
* |c_<Right>| {@link javax.swing.text.DefaultEditorKit#forwardAction}
* |c_<S-Down>| {@link com.maddyhome.idea.vim.ui.ex.HistoryDownAction}
* |c_<S-Left>| {@link javax.swing.text.DefaultEditorKit#previousWordAction}
* |c_<S-Right>| {@link javax.swing.text.DefaultEditorKit#nextWordAction}
* |c_<S-Tab>| TO BE IMPLEMENTED
* |c_<S-Up>| {@link com.maddyhome.idea.vim.ui.ex.HistoryUpAction}
* |c_<Tab>| TO BE IMPLEMENTED
* |c_<Up>| {@link com.maddyhome.idea.vim.ui.ex.HistoryUpFilterAction}
* |c_digraph| {char1} <BS> {char2}
* |c_wildchar| TO BE IMPLEMENTED
* |'cedit'| TO BE IMPLEMENTED
*
*
* 6. Ex commands
*
* tag handler
* -------------------------------------------------------------------------------------------------------------------
*
* |:map| {@link com.maddyhome.idea.vim.vimscript.model.commands.mapping.MapCommand}
* |:nmap| ...
* |:vmap| ...
* |:omap| ...
* |:imap| ...
* |:cmap| ...
* |:noremap| ...
* |:nnoremap| ...
* |:vnoremap| ...
* |:onoremap| ...
* |:inoremap| ...
* |:cnoremap| ...
* |:shell| {@link com.maddyhome.idea.vim.vimscript.model.commands.ShellCommand}
* |:sort| {@link com.maddyhome.idea.vim.vimscript.model.commands.SortCommand}
* |:source| {@link com.maddyhome.idea.vim.vimscript.model.commands.SourceCommand}
* |:qall| {@link com.maddyhome.idea.vim.vimscript.model.commands.ExitCommand}
* |:quitall| {@link com.maddyhome.idea.vim.vimscript.model.commands.ExitCommand}
* |:quitall| {@link com.maddyhome.idea.vim.vimscript.model.commands.ExitCommand}
* |:wqall| {@link com.maddyhome.idea.vim.vimscript.model.commands.ExitCommand}
* |:xall| {@link com.maddyhome.idea.vim.vimscript.model.commands.ExitCommand}
* |:command| {@link com.maddyhome.idea.vim.vimscript.model.commands.CmdCommand}
* |:delcommand| {@link com.maddyhome.idea.vim.vimscript.model.commands.DelCmdCommand}
* |:comclear| {@link com.maddyhome.idea.vim.vimscript.model.commands.CmdClearCommand}
* ...
*
* The list of supported Ex commands is incomplete.
*
*
* A. Misc commands
*
* tag handler
* -------------------------------------------------------------------------------------------------------------------
* |]b| {@link com.maddyhome.idea.vim.action.motion.text.MotionCamelEndLeftAction}
* |]w| {@link com.maddyhome.idea.vim.action.motion.text.MotionCamelEndRightAction}
* |[b| {@link com.maddyhome.idea.vim.action.motion.text.MotionCamelLeftAction}
* |[w| {@link com.maddyhome.idea.vim.action.motion.text.MotionCamelRightAction}
* |g(| {@link com.maddyhome.idea.vim.action.motion.text.MotionSentencePreviousEndAction}
* |g)| {@link com.maddyhome.idea.vim.action.motion.text.MotionSentenceNextEndAction}
*
*
* See also :help index.
*
* @author vlan
*/
package com.maddyhome.idea.vim;

View File

@@ -192,6 +192,13 @@ private object VimActionsPopup {
null, null,
), ),
) )
actionGroup.add(
HelpLink(
"Take Survey ↗",
"https://surveys.jetbrains.com/s3/ideavim-usage-survey",
AllIcons.Actions.IntentionBulb,
),
)
actionGroup.addSeparator(MessageHelper.message("action.eap.choice.active.text")) actionGroup.addSeparator(MessageHelper.message("action.eap.choice.active.text"))
actionGroup.add(JoinEap) actionGroup.add(JoinEap)

View File

@@ -1,263 +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.ui.ex
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.editor.textarea.TextComponentEditorImpl
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.api.LocalOptionInitialisationScenario
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.newapi.vim
import java.awt.event.ActionEvent
import java.awt.event.KeyEvent
import javax.swing.Action
import javax.swing.KeyStroke
import javax.swing.text.BadLocationException
import javax.swing.text.DefaultEditorKit
import javax.swing.text.Document
import javax.swing.text.TextAction
import kotlin.math.abs
import kotlin.math.min
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
internal interface MultiStepAction : Action {
fun reset()
}
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
internal class HistoryUpAction : TextAction(ExEditorKit.HistoryUp) {
override fun actionPerformed(actionEvent: ActionEvent) {
val target = getTextComponent(actionEvent) as ExTextField
target.selectHistory(true, false)
}
}
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
internal class HistoryDownAction : TextAction(ExEditorKit.HistoryDown) {
override fun actionPerformed(actionEvent: ActionEvent) {
val target = getTextComponent(actionEvent) as ExTextField
target.selectHistory(false, false)
}
}
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
internal class HistoryUpFilterAction : TextAction(ExEditorKit.HistoryUpFilter) {
override fun actionPerformed(actionEvent: ActionEvent) {
val target = getTextComponent(actionEvent) as ExTextField
target.selectHistory(true, true)
}
}
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
internal class HistoryDownFilterAction : TextAction(ExEditorKit.HistoryDownFilter) {
override fun actionPerformed(actionEvent: ActionEvent) {
val target = getTextComponent(actionEvent) as ExTextField
target.selectHistory(false, true)
}
}
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
internal class CompleteEntryAction : TextAction(ExEditorKit.CompleteEntry) {
override fun actionPerformed(actionEvent: ActionEvent) {
logger.debug("complete entry")
val stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)
// We send the <Enter> keystroke through the key handler rather than calling ProcessGroup#processExEntry directly.
// We do this for a couple of reasons:
// * The C mode mapping for ProcessExEntryAction handles the actual entry, and most importantly, it does so as a
// write action
// * The key handler routines get the chance to clean up and reset state
val entry = ExEntryPanel.getInstance().entry
val keyHandler = KeyHandler.getInstance()
keyHandler.handleKey(entry.editor!!.vim, stroke, entry.context.vim, keyHandler.keyHandlerState)
}
companion object {
private val logger = logger<CompleteEntryAction>()
}
}
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
internal class CancelEntryAction : TextAction(ExEditorKit.CancelEntry) {
override fun actionPerformed(e: ActionEvent) {
val target = getTextComponent(e) as ExTextField
target.cancel()
}
}
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
internal class EscapeCharAction : TextAction(ExEditorKit.EscapeChar) {
override fun actionPerformed(e: ActionEvent) {
val target = getTextComponent(e) as ExTextField
target.escape()
}
}
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
internal abstract class DeleteCharAction internal constructor(name: String?) : TextAction(name) {
@kotlin.jvm.Throws(BadLocationException::class)
fun deleteSelection(doc: Document, dot: Int, mark: Int): Boolean {
if (dot != mark) {
doc.remove(min(dot, mark), abs(dot - mark))
return true
}
return false
}
@kotlin.jvm.Throws(BadLocationException::class)
fun deleteNextChar(doc: Document, dot: Int): Boolean {
if (dot < doc.length) {
var delChars = 1
if (dot < doc.length - 1) {
val dotChars = doc.getText(dot, 2)
val c0 = dotChars[0]
val c1 = dotChars[1]
if (c0 in '\uD800'..'\uDBFF' && c1 in '\uDC00'..'\uDFFF') {
delChars = 2
}
}
doc.remove(dot, delChars)
return true
}
return false
}
@kotlin.jvm.Throws(BadLocationException::class)
fun deletePrevChar(doc: Document, dot: Int): Boolean {
if (dot > 0) {
var delChars = 1
if (dot > 1) {
val dotChars = doc.getText(dot - 2, 2)
val c0 = dotChars[0]
val c1 = dotChars[1]
if (c0 in '\uD800'..'\uDBFF' && c1 in '\uDC00'..'\uDFFF') {
delChars = 2
}
}
doc.remove(dot - delChars, delChars)
return true
}
return false
}
}
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
internal class DeleteNextCharAction : DeleteCharAction(DefaultEditorKit.deleteNextCharAction) {
override fun actionPerformed(e: ActionEvent) {
val target = getTextComponent(e) as ExTextField
target.saveLastEntry()
try {
val doc = target.document
val caret = target.caret
val dot = caret.dot
val mark = caret.mark
if (!deleteSelection(doc, dot, mark) && !deleteNextChar(doc, dot) && !deletePrevChar(doc, dot)) {
target.cancel()
}
} catch (ex: BadLocationException) {
// ignore
}
}
}
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
internal class DeletePreviousCharAction : DeleteCharAction(DefaultEditorKit.deletePrevCharAction) {
override fun actionPerformed(e: ActionEvent) {
val target = getTextComponent(e) as ExTextField
target.saveLastEntry()
try {
val doc = target.document
val caret = target.caret
val dot = caret.dot
val mark = caret.mark
if (!deleteSelection(doc, dot, mark) && !deletePrevChar(doc, dot)) {
if (dot == 0 && doc.length == 0) {
target.cancel()
}
}
} catch (bl: BadLocationException) {
// ignore
}
}
}
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
internal class DeletePreviousWordAction : TextAction(DefaultEditorKit.deletePrevWordAction) {
/**
* Invoked when an action occurs.
*/
override fun actionPerformed(e: ActionEvent) {
val target = getTextComponent(e) as ExTextField
target.saveLastEntry()
val doc = target.document
val caret = target.caret
val project = target.editor!!.project
// Create a VimEditor instance on the Swing text field which we can pass to the search helpers. We need an editor
// rather than just working on a buffer because the search helpers need local options (specifically the local to
// buffer 'iskeyword'). We use the CMD_LINE scenario to initialise local options from the main editor. The options
// service will copy all local-to-buffer and local-to-window options, effectively cloning the options.
// TODO: Over time, we should migrate all ex actions to be based on VimEditor
// This will mean we always have an editor that has been initialised for options, etc. But also means that we can
// share the command line entry actions between IdeaVim implementations
val editor = TextComponentEditorImpl(project, target).vim
injector.optionGroup.initialiseLocalOptions(editor, target.editor!!.vim, LocalOptionInitialisationScenario.CMD_LINE)
val offset = injector.searchHelper.findNextWord(editor, caret.dot, -1, bigWord = false, spaceWords = false)
if (logger.isDebugEnabled) logger.debug("offset=$offset")
try {
val pos = caret.dot
doc.remove(offset, pos - offset)
} catch (ex: BadLocationException) {
// ignore
}
}
companion object {
private val logger = logger<DeletePreviousWordAction>()
}
}
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
internal class DeleteToCursorAction : TextAction(ExEditorKit.DeleteToCursor) {
/**
* Invoked when an action occurs.
*/
override fun actionPerformed(e: ActionEvent) {
val target = getTextComponent(e) as ExTextField
target.saveLastEntry()
val doc = target.document
val caret = target.caret
try {
doc.remove(0, caret.dot)
} catch (ex: BadLocationException) {
// ignore
}
}
}
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
internal class ToggleInsertReplaceAction : TextAction(ExEditorKit.ToggleInsertReplace) {
/**
* Invoked when an action occurs.
*/
override fun actionPerformed(e: ActionEvent) {
logger.debug("actionPerformed")
val target = getTextComponent(e) as ExTextField
target.toggleInsertReplace()
}
init {
logger.debug("ToggleInsertReplaceAction()")
}
companion object {
private val logger = logger<ToggleInsertReplaceAction>()
}
}

View File

@@ -34,7 +34,7 @@ public class ExDocument extends PlainDocument {
void toggleInsertReplace() { void toggleInsertReplace() {
VimCommandLine commandLine = injector.getCommandLine().getActiveCommandLine(); VimCommandLine commandLine = injector.getCommandLine().getActiveCommandLine();
if (commandLine != null) { if (commandLine != null) {
commandLine.toggleReplaceMode(); ((ExEntryPanel) commandLine).isReplaceMode = !((ExEntryPanel)commandLine).isReplaceMode;
} }
overwrite = !overwrite; overwrite = !overwrite;
} }

View File

@@ -7,7 +7,6 @@
*/ */
package com.maddyhome.idea.vim.ui.ex package com.maddyhome.idea.vim.ui.ex
import com.intellij.openapi.diagnostic.debug
import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.logger
import com.maddyhome.idea.vim.KeyHandler import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
@@ -15,50 +14,12 @@ import com.maddyhome.idea.vim.newapi.vim
import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.NonNls
import java.awt.event.ActionEvent import java.awt.event.ActionEvent
import java.awt.event.KeyEvent import java.awt.event.KeyEvent
import javax.swing.Action
import javax.swing.KeyStroke import javax.swing.KeyStroke
import javax.swing.text.DefaultEditorKit import javax.swing.text.DefaultEditorKit
import javax.swing.text.Document import javax.swing.text.Document
import javax.swing.text.TextAction
@Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes") @Deprecated("ExCommands should be migrated to KeyHandler like commands for other modes")
internal object ExEditorKit : DefaultEditorKit() { internal object ExEditorKit : DefaultEditorKit() {
@NonNls
val CancelEntry: String = "cancel-entry"
@NonNls
val CompleteEntry: String = "complete-entry"
@NonNls
val EscapeChar: String = "escape"
@NonNls
val DeleteToCursor: String = "delete-to-cursor"
@NonNls
val ToggleInsertReplace: String = "toggle-insert"
@NonNls
val HistoryUp: String = "history-up"
@NonNls
val HistoryDown: String = "history-down"
@NonNls
val HistoryUpFilter: String = "history-up-filter"
@NonNls
val HistoryDownFilter: String = "history-down-filter"
@NonNls
val StartDigraph: String = "start-digraph"
@NonNls
val StartLiteral: String = "start-literal"
private val logger = logger<ExEditorKit>()
/** /**
* Gets the MIME type of the data that this * Gets the MIME type of the data that this
* kit represents support for. * kit represents support for.
@@ -70,19 +31,6 @@ internal object ExEditorKit : DefaultEditorKit() {
return "text/ideavim" return "text/ideavim"
} }
/**
* Fetches the set of commands that can be used
* on a text component that is using a model and
* view produced by this kit.
*
* @return the set of actions
*/
override fun getActions(): Array<Action> {
val res = TextAction.augmentList(super.getActions(), exActions)
logger.debug { "res.length=${res.size}" }
return res
}
/** /**
* Creates an uninitialized text storage model * Creates an uninitialized text storage model
* that is appropriate for this type of editor. * that is appropriate for this type of editor.
@@ -93,48 +41,29 @@ internal object ExEditorKit : DefaultEditorKit() {
return ExDocument() return ExDocument()
} }
private val exActions = arrayOf<Action>(
CancelEntryAction(),
CompleteEntryAction(),
EscapeCharAction(),
DeleteNextCharAction(),
DeletePreviousCharAction(),
DeletePreviousWordAction(),
DeleteToCursorAction(),
HistoryUpAction(),
HistoryDownAction(),
HistoryUpFilterAction(),
HistoryDownFilterAction(),
ToggleInsertReplaceAction(),
)
class DefaultExKeyHandler : DefaultKeyTypedAction() { class DefaultExKeyHandler : DefaultKeyTypedAction() {
override fun actionPerformed(e: ActionEvent) { override fun actionPerformed(e: ActionEvent) {
val target = getTextComponent(e) as ExTextField val target = getTextComponent(e) as ExTextField
val currentAction = target.currentAction
if (currentAction != null) { val key = convert(e)
currentAction.actionPerformed(e) if (key != null) {
} else { val c = key.keyChar
val key = convert(e) if (c.code > 0) {
if (key != null) { if (target.useHandleKeyFromEx) {
val c = key.keyChar val panel = ((injector.commandLine.getActiveCommandLine() as? ExEntryPanel) ?: (injector.modalInput.getCurrentModalInput() as? WrappedAsModalInputExEntryPanel)?.exEntryPanel) ?: return
if (c.code > 0) { val entry = panel.entry
if (target.useHandleKeyFromEx) { val editor = entry.editor
val panel = ((injector.commandLine.getActiveCommandLine() as? ExEntryPanel) ?: (injector.modalInput.getCurrentModalInput() as? WrappedAsModalInputExEntryPanel)?.exEntryPanel) ?: return val keyHandler = KeyHandler.getInstance()
val entry = panel.entry keyHandler.handleKey(editor!!.vim, key, entry.context.vim, keyHandler.keyHandlerState)
val editor = entry.editor } else {
val keyHandler = KeyHandler.getInstance() val event = ActionEvent(e.source, e.id, c.toString(), e.getWhen(), e.modifiers)
keyHandler.handleKey(editor!!.vim, key, entry.context.vim, keyHandler.keyHandlerState) super.actionPerformed(event)
} else {
val event = ActionEvent(e.source, e.id, c.toString(), e.getWhen(), e.modifiers)
super.actionPerformed(event)
}
target.saveLastEntry()
} }
} else {
super.actionPerformed(e)
target.saveLastEntry() target.saveLastEntry()
} }
} else {
super.actionPerformed(e)
target.saveLastEntry()
} }
} }
} }

View File

@@ -23,7 +23,10 @@ import com.maddyhome.idea.vim.EventFacade;
import com.maddyhome.idea.vim.KeyHandler; import com.maddyhome.idea.vim.KeyHandler;
import com.maddyhome.idea.vim.VimPlugin; import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.action.VimShortcutKeyAction; import com.maddyhome.idea.vim.action.VimShortcutKeyAction;
import com.maddyhome.idea.vim.api.*; import com.maddyhome.idea.vim.api.VimCommandLine;
import com.maddyhome.idea.vim.api.VimCommandLineCaret;
import com.maddyhome.idea.vim.api.VimEditor;
import com.maddyhome.idea.vim.api.VimKeyGroupBase;
import com.maddyhome.idea.vim.ex.ranges.LineRange; import com.maddyhome.idea.vim.ex.ranges.LineRange;
import com.maddyhome.idea.vim.helper.SearchHighlightsHelper; import com.maddyhome.idea.vim.helper.SearchHighlightsHelper;
import com.maddyhome.idea.vim.helper.UiHelper; import com.maddyhome.idea.vim.helper.UiHelper;
@@ -60,7 +63,7 @@ import static com.maddyhome.idea.vim.group.KeyGroup.toShortcutSet;
public class ExEntryPanel extends JPanel implements VimCommandLine { public class ExEntryPanel extends JPanel implements VimCommandLine {
public static ExEntryPanel instance; public static ExEntryPanel instance;
public static ExEntryPanel instanceWithoutShortcuts; public static ExEntryPanel instanceWithoutShortcuts;
private boolean isReplaceMode = false; public boolean isReplaceMode = false;
private WeakReference<Editor> weakEditor = null; private WeakReference<Editor> weakEditor = null;
private VimInputInterceptor myInputInterceptor = null; private VimInputInterceptor myInputInterceptor = null;
@@ -341,14 +344,14 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
} }
} }
// Get the current count from the command builder. This value is coerced to at least 1, so will always be valid. // Get a snapshot of the count for the in progress command, and coerce it to 1. This value will include all
// The aggregated value includes any counts for operator and register selections, and the uncommitted count for // count components - selecting register(s), operator and motions. E.g. `2"a3"b4"c5d6/` will return 720.
// the search command (`/` or `?`). E.g., `2"a3"b4"c5d6/` would return 720. // If we're showing highlights for an Ex command like `:s`, the command builder will be empty, but we'll still
// If we're showing highlights for an ex command like `:s`, there won't be a command, but the value is already // get a valid value.
// coerced to at least 1. int count1 = Math.max(1, KeyHandler.getInstance().getKeyHandlerState().getEditorCommandBuilder()
int count1 = KeyHandler.getInstance().getKeyHandlerState().getEditorCommandBuilder().getAggregatedUncommittedCount(); .calculateCount0Snapshot());
if (labelText.equals("/") || labelText.equals("?") || searchCommand) { if ((labelText.equals("/") || labelText.equals("?") || searchCommand) && !injector.getMacro().isExecutingMacro()) {
final boolean forwards = !labelText.equals("?"); // :s, :g, :v are treated as forwards final boolean forwards = !labelText.equals("?"); // :s, :g, :v are treated as forwards
int pattenEnd = injector.getSearchGroup().findEndOfPattern(searchText, separator, 0); int pattenEnd = injector.getSearchGroup().findEndOfPattern(searchText, separator, 0);
final String pattern = searchText.substring(0, pattenEnd); final String pattern = searchText.substring(0, pattenEnd);
@@ -402,7 +405,7 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
@Override @Override
public void toggleReplaceMode() { public void toggleReplaceMode() {
isReplaceMode = !isReplaceMode; entry.toggleInsertReplace();
} }
/** /**
@@ -528,17 +531,17 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
private static final Logger logger = Logger.getInstance(ExEntryPanel.class.getName()); private static final Logger logger = Logger.getInstance(ExEntryPanel.class.getName());
@NotNull
@Override @Override
public VimCommandLineCaret getCaret() { public @NotNull VimCommandLineCaret getCaret() {
return (VimCommandLineCaret) entry.getCaret(); return (VimCommandLineCaret) entry.getCaret();
} }
@Override @Override
public void setText(@NotNull String string) { public void setText(@NotNull String string, boolean updateLastEntry) {
// It's a feature of Swing that caret is moved when we set new text. However, our API is Swing independent and we do not expect this // It's a feature of Swing that caret is moved when we set new text. However, our API is Swing independent and we do not expect this
int offset = getCaret().getOffset(); int offset = getCaret().getOffset();
entry.updateText(string); entry.updateText(string);
if (updateLastEntry) entry.saveLastEntry();
getCaret().setOffset(Math.min(offset, getVisibleText().length())); getCaret().setOffset(Math.min(offset, getVisibleText().length()));
} }
@@ -547,9 +550,8 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
entry.clearCurrentAction(); entry.clearCurrentAction();
} }
@Nullable
@Override @Override
public Integer getPromptCharacterOffset() { public @Nullable Integer getPromptCharacterOffset() {
int offset = entry.currentActionPromptCharacterOffset; int offset = entry.currentActionPromptCharacterOffset;
return offset == -1 ? null : offset; return offset == -1 ? null : offset;
} }
@@ -569,8 +571,7 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
IdeFocusManager.findInstance().requestFocus(entry, true); IdeFocusManager.findInstance().requestFocus(entry, true);
} }
@Nullable public @Nullable VimInputInterceptor<?> getInputInterceptor() {
public VimInputInterceptor<?> getInputInterceptor() {
return myInputInterceptor; return myInputInterceptor;
} }
@@ -583,18 +584,37 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
myInputInterceptor = vimInputInterceptor; myInputInterceptor = vimInputInterceptor;
} }
@Nullable
@Override @Override
public Function1<String, Unit> getInputProcessing() { public @Nullable Function1<String, Unit> getInputProcessing() {
return inputProcessing; return inputProcessing;
} }
@Nullable
@Override @Override
public Character getFinishOn() { public @Nullable Character getFinishOn() {
return finishOn; return finishOn;
} }
@Override
public int getHistIndex() {
return entry.histIndex;
}
@Override
public void setHistIndex(int i) {
entry.histIndex = i;
}
@NotNull
@Override
public String getLastEntry() {
return entry.lastEntry;
}
@Override
public void setLastEntry(@NotNull String s) {
entry.lastEntry = s;
}
public static class LafListener implements LafManagerListener { public static class LafListener implements LafManagerListener {
@Override @Override
public void lookAndFeelChanged(@NotNull LafManager source) { public void lookAndFeelChanged(@NotNull LafManager source) {

View File

@@ -21,57 +21,6 @@ internal object ExKeyBindings {
val bindings: Array<KeyBinding> by lazy { val bindings: Array<KeyBinding> by lazy {
arrayOf( arrayOf(
// Escape will cancel a pending insert digraph/register before cancelling
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), ExEditorKit.EscapeChar),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.EscapeChar),
// Cancel entry, ignoring any pending actions such as digraph/registry entry
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.CancelEntry),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), ExEditorKit.CompleteEntry),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_J, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.CompleteEntry),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_M, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.CompleteEntry),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_B, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.beginLineAction),
KeyBinding(KeyStroke.getKeyStroke(0x02.toChar().code, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.beginLineAction),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0), DefaultEditorKit.beginLineAction),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_E, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.endLineAction),
KeyBinding(KeyStroke.getKeyStroke(0x05.toChar().code, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.endLineAction),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0), DefaultEditorKit.endLineAction),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), DefaultEditorKit.deletePrevCharAction),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.deletePrevCharAction),
KeyBinding(KeyStroke.getKeyStroke(0x08.toChar().code, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.deletePrevCharAction),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), DefaultEditorKit.deleteNextCharAction),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.deletePrevWordAction),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.DeleteToCursor),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), ExEditorKit.HistoryUpFilter),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.SHIFT_DOWN_MASK), ExEditorKit.HistoryUp),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), ExEditorKit.HistoryUp),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.HistoryUp),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), ExEditorKit.HistoryDownFilter),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_DOWN_MASK), ExEditorKit.HistoryDown),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), ExEditorKit.HistoryDown),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.HistoryDown),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0), ExEditorKit.ToggleInsertReplace),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), DefaultEditorKit.backwardAction),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.SHIFT_DOWN_MASK), DefaultEditorKit.previousWordAction),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.previousWordAction),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), DefaultEditorKit.forwardAction),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.SHIFT_DOWN_MASK), DefaultEditorKit.nextWordAction),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.CTRL_DOWN_MASK), DefaultEditorKit.nextWordAction),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_K, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.StartDigraph),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.StartLiteral),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_Q, KeyEvent.CTRL_DOWN_MASK), ExEditorKit.StartLiteral),
// These appear to be non-Vim shortcuts // These appear to be non-Vim shortcuts
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.META_DOWN_MASK), DefaultEditorKit.pasteAction), KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.META_DOWN_MASK), DefaultEditorKit.pasteAction),
KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, KeyEvent.SHIFT_DOWN_MASK), DefaultEditorKit.pasteAction), KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, KeyEvent.SHIFT_DOWN_MASK), DefaultEditorKit.pasteAction),

View File

@@ -59,9 +59,45 @@ internal class ExShortcutKeyAction(private val exEntryPanel: ExEntryPanel) : Dum
} }
fun registerCustomShortcutSet() { fun registerCustomShortcutSet() {
val shortcuts = ExKeyBindings.bindings.map { val shortcuts = listOf(
KeyboardShortcut(it.key, null) KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
}.toTypedArray() KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, KeyEvent.CTRL_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
KeyStroke.getKeyStroke(KeyEvent.VK_J, KeyEvent.CTRL_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_M, KeyEvent.CTRL_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_B, KeyEvent.CTRL_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0),
KeyStroke.getKeyStroke(KeyEvent.VK_E, KeyEvent.CTRL_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_END, 0),
KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0),
KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0),
KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0),
KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0),
KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.CTRL_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),
KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0),
KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0),
KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0),
KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.CTRL_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0),
KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.CTRL_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_K, KeyEvent.CTRL_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.CTRL_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_Q, KeyEvent.CTRL_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.META_DOWN_MASK),
KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, KeyEvent.SHIFT_DOWN_MASK),
)
.map { KeyboardShortcut(it, null) }
.toTypedArray()
registerCustomShortcutSet({ shortcuts }, exEntryPanel) registerCustomShortcutSet({ shortcuts }, exEntryPanel)
} }

View File

@@ -16,11 +16,9 @@ import com.intellij.util.ui.JBUI;
import com.maddyhome.idea.vim.VimPlugin; import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.api.VimCommandLine; import com.maddyhome.idea.vim.api.VimCommandLine;
import com.maddyhome.idea.vim.api.VimCommandLineCaret; import com.maddyhome.idea.vim.api.VimCommandLineCaret;
import com.maddyhome.idea.vim.api.VimEditor;
import com.maddyhome.idea.vim.helper.UiHelper; import com.maddyhome.idea.vim.helper.UiHelper;
import com.maddyhome.idea.vim.history.HistoryConstants; import com.maddyhome.idea.vim.history.HistoryConstants;
import com.maddyhome.idea.vim.history.HistoryEntry; import com.maddyhome.idea.vim.history.HistoryEntry;
import com.maddyhome.idea.vim.newapi.IjVimEditor;
import com.maddyhome.idea.vim.options.helpers.GuiCursorAttributes; import com.maddyhome.idea.vim.options.helpers.GuiCursorAttributes;
import com.maddyhome.idea.vim.options.helpers.GuiCursorMode; import com.maddyhome.idea.vim.options.helpers.GuiCursorMode;
import com.maddyhome.idea.vim.options.helpers.GuiCursorOptionHelper; import com.maddyhome.idea.vim.options.helpers.GuiCursorOptionHelper;
@@ -67,9 +65,6 @@ public class ExTextField extends JTextField {
// If we're in the middle of an action (e.g. entering a register to paste, or inserting a digraph), cancel it if // If we're in the middle of an action (e.g. entering a register to paste, or inserting a digraph), cancel it if
// the mouse is clicked anywhere. Vim's behavior is to use the mouse click as an event, which can lead to // the mouse is clicked anywhere. Vim's behavior is to use the mouse click as an event, which can lead to
// something like : !%!C, which I don't believe is documented, or useful // something like : !%!C, which I don't believe is documented, or useful
if (currentAction != null) {
clearCurrentAction();
}
super.mouseClicked(e); super.mouseClicked(e);
} }
}); });
@@ -240,20 +235,15 @@ public class ExTextField extends JTextField {
// This gets called for ALL events, before the IDE starts to process key events for the action system. We can add a // This gets called for ALL events, before the IDE starts to process key events for the action system. We can add a
// dispatcher that checks that the plugin is enabled, checks that the component with the focus is ExTextField, // dispatcher that checks that the plugin is enabled, checks that the component with the focus is ExTextField,
// dispatch to ExEntryPanel#handleKey and if it's processed, mark the event as consumed. // dispatch to ExEntryPanel#handleKey and if it's processed, mark the event as consumed.
if (currentAction != null) { KeyEvent event = new KeyEvent(this, keyChar != KeyEvent.CHAR_UNDEFINED ? KeyEvent.KEY_TYPED :
currentAction.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, String.valueOf(c), modifiers)); (stroke.isOnKeyRelease() ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED),
} (new Date()).getTime(), modifiers, keyCode, c);
else {
KeyEvent event = new KeyEvent(this, keyChar != KeyEvent.CHAR_UNDEFINED ? KeyEvent.KEY_TYPED :
(stroke.isOnKeyRelease() ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED),
(new Date()).getTime(), modifiers, keyCode, c);
useHandleKeyFromEx = false; useHandleKeyFromEx = false;
try { try {
super.processKeyEvent(event); super.processKeyEvent(event);
}finally { } finally {
useHandleKeyFromEx = true; useHandleKeyFromEx = true;
}
} }
} }
@@ -279,12 +269,7 @@ public class ExTextField extends JTextField {
* Cancels current action, if there is one. If not, cancels entry. * Cancels current action, if there is one. If not, cancels entry.
*/ */
void escape() { void escape() {
if (currentAction != null) { cancel();
clearCurrentAction();
}
else {
cancel();
}
} }
/** /**
@@ -299,19 +284,10 @@ public class ExTextField extends JTextField {
} }
public void clearCurrentAction() { public void clearCurrentAction() {
if (currentAction != null) {
currentAction.reset();
}
currentAction = null;
VimCommandLine commandLine = injector.getCommandLine().getActiveCommandLine(); VimCommandLine commandLine = injector.getCommandLine().getActiveCommandLine();
if (commandLine != null) commandLine.clearPromptCharacter(); if (commandLine != null) commandLine.clearPromptCharacter();
} }
@Nullable
Action getCurrentAction() {
return currentAction;
}
private void setInsertMode() { private void setInsertMode() {
ExDocument doc = (ExDocument)getDocument(); ExDocument doc = (ExDocument)getDocument();
if (doc.isOverwrite()) { if (doc.isOverwrite()) {
@@ -542,10 +518,9 @@ public class ExTextField extends JTextField {
private DataContext context; private DataContext context;
private final CommandLineCaret caret; private final CommandLineCaret caret;
private String lastEntry; String lastEntry;
private List<HistoryEntry> history; private List<HistoryEntry> history;
private int histIndex = 0; int histIndex = 0;
private @Nullable MultiStepAction currentAction;
int currentActionPromptCharacterOffset = -1; int currentActionPromptCharacterOffset = -1;
private static final Logger logger = Logger.getInstance(ExTextField.class.getName()); private static final Logger logger = Logger.getInstance(ExTextField.class.getName());

View File

@@ -8,114 +8,20 @@
package com.maddyhome.idea.vim.vimscript package com.maddyhome.idea.vim.vimscript
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.components.Service import com.intellij.openapi.components.Service
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.VirtualFileManager
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.VimScriptExecutorBase import com.maddyhome.idea.vim.api.VimScriptExecutorBase
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.ex.ExException
import com.maddyhome.idea.vim.ex.FinishException
import com.maddyhome.idea.vim.extension.VimExtensionRegistrar import com.maddyhome.idea.vim.extension.VimExtensionRegistrar
import com.maddyhome.idea.vim.history.HistoryConstants
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.register.RegisterConstants.LAST_COMMAND_REGISTER
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
import com.maddyhome.idea.vim.vimscript.model.VimLContext
import com.maddyhome.idea.vim.vimscript.model.commands.Command
import com.maddyhome.idea.vim.vimscript.model.commands.RepeatCommand
import com.maddyhome.idea.vim.vimscript.parser.VimscriptParser
import java.io.File import java.io.File
import java.io.IOException
@Service @Service
internal class Executor : VimScriptExecutorBase() { internal class Executor : VimScriptExecutorBase() {
private val logger = logger<Executor>() override fun enableDelayedExtensions() {
override var executingVimscript = false VimExtensionRegistrar.enableDelayedExtensions()
override var executingIdeaVimRcConfiguration = false
@Throws(ExException::class)
override fun execute(script: String, editor: VimEditor, context: ExecutionContext, skipHistory: Boolean, indicateErrors: Boolean, vimContext: VimLContext?): ExecutionResult {
try {
injector.vimscriptExecutor.executingVimscript = true
var finalResult: ExecutionResult = ExecutionResult.Success
val myScript = VimscriptParser.parse(script)
myScript.units.forEach { it.vimContext = vimContext ?: myScript }
for (unit in myScript.units) {
try {
val result = unit.execute(editor, context)
if (result is ExecutionResult.Error) {
finalResult = ExecutionResult.Error
if (indicateErrors) {
VimPlugin.indicateError()
}
}
} catch (e: ExException) {
if (e is FinishException) {
break
}
finalResult = ExecutionResult.Error
if (indicateErrors) {
VimPlugin.showMessage(e.message)
VimPlugin.indicateError()
} else {
logger.warn("Failed while executing $unit. " + e.message)
}
} catch (e: NotImplementedError) {
if (indicateErrors) {
VimPlugin.showMessage("Not implemented yet :(")
VimPlugin.indicateError()
}
} catch (e: Exception) {
logger.warn(e)
if (injector.application.isUnitTest()) {
throw e
}
}
}
if (!skipHistory) {
VimPlugin.getHistory().addEntry(HistoryConstants.COMMAND, script)
if (myScript.units.size == 1 && myScript.units[0] is Command && myScript.units[0] !is RepeatCommand) {
VimPlugin.getRegister().storeTextSpecial(LAST_COMMAND_REGISTER, script)
}
}
return finalResult
} finally {
injector.vimscriptExecutor.executingVimscript = false
// Initialize any extensions that were enabled during execution of this vimscript
// See the doc of this function for details
VimExtensionRegistrar.enableDelayedExtensions()
}
} }
override fun executeFile(file: File, editor: VimEditor, fileIsIdeaVimRcConfig: Boolean, indicateErrors: Boolean) { override fun ensureFileIsSaved(file: File) {
val context = DataContext.EMPTY_CONTEXT.vim
try {
if (fileIsIdeaVimRcConfig) {
injector.vimscriptExecutor.executingIdeaVimRcConfiguration = true
}
ensureFileIsSaved(file)
execute(file.readText(), editor, context, skipHistory = true, indicateErrors)
} catch (ignored: IOException) {
LOG.error(ignored)
} finally {
if (fileIsIdeaVimRcConfig) {
injector.vimrcFileState.saveFileState(file.absolutePath)
injector.vimscriptExecutor.executingIdeaVimRcConfiguration = false
}
}
}
private fun ensureFileIsSaved(file: File) {
val documentManager = FileDocumentManager.getInstance() val documentManager = FileDocumentManager.getInstance()
VirtualFileManager.getInstance().findFileByNioPath(file.toPath()) VirtualFileManager.getInstance().findFileByNioPath(file.toPath())
@@ -123,16 +29,4 @@ internal class Executor : VimScriptExecutorBase() {
?.takeIf(documentManager::isDocumentUnsaved) ?.takeIf(documentManager::isDocumentUnsaved)
?.let(documentManager::saveDocumentAsIs) ?.let(documentManager::saveDocumentAsIs)
} }
@Throws(ExException::class)
override fun executeLastCommand(editor: VimEditor, context: ExecutionContext): Boolean {
val reg = VimPlugin.getRegister().getRegister(':') ?: return false
val text = reg.text ?: return false
execute(text, editor, context, skipHistory = false, indicateErrors = true, CommandLineVimLContext)
return true
}
companion object {
val LOG = logger<Executor>()
}
} }

View File

@@ -45,7 +45,9 @@ internal data class ActionListCommand(val range: Range, val argument: String) :
.filter { line -> searchPattern.all { it in line.lowercase(Locale.getDefault()) } } .filter { line -> searchPattern.all { it in line.lowercase(Locale.getDefault()) } }
.joinToString(lineSeparator) .joinToString(lineSeparator)
ExOutputModel.getInstance(editor.ij).output(MessageHelper.message("ex.show.all.actions.0.1", lineSeparator, actions)) val outputPanel = injector.outputPanel.getOrCreate(editor, context)
outputPanel.addText(MessageHelper.message("ex.show.all.actions.0.1", lineSeparator, actions))
outputPanel.show()
return ExecutionResult.Success return ExecutionResult.Success
} }
} }

View File

@@ -17,8 +17,8 @@ import com.intellij.vim.annotations.ExCommand
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.ex.ExOutputModel
import com.maddyhome.idea.vim.ex.ranges.Range import com.maddyhome.idea.vim.ex.ranges.Range
import com.maddyhome.idea.vim.helper.EditorHelper import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.vimLine import com.maddyhome.idea.vim.helper.vimLine
@@ -45,7 +45,9 @@ internal data class BufferListCommand(val range: Range, val argument: String) :
val filter = pruneUnsupportedFilters(arg) val filter = pruneUnsupportedFilters(arg)
val bufferList = getBufferList(context, filter) val bufferList = getBufferList(context, filter)
ExOutputModel.getInstance(editor.ij).output(bufferList.joinToString(separator = "\n")) val outputPanel = injector.outputPanel.getOrCreate(editor, context)
outputPanel.addText(bufferList.joinToString(separator = "\n"))
outputPanel.show()
return ExecutionResult.Success return ExecutionResult.Success
} }

View File

@@ -15,6 +15,7 @@ import com.intellij.vim.annotations.ExCommand
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.ex.ExException import com.maddyhome.idea.vim.ex.ExException
import com.maddyhome.idea.vim.ex.ExOutputModel import com.maddyhome.idea.vim.ex.ExOutputModel
@@ -74,7 +75,9 @@ internal data class CmdFilterCommand(val range: Range, val argument: String) : C
if (range.size() == 0) { if (range.size() == 0) {
// Show command output in a window // Show command output in a window
VimPlugin.getProcess().executeCommand(editor, command, null, workingDirectory)?.let { VimPlugin.getProcess().executeCommand(editor, command, null, workingDirectory)?.let {
ExOutputModel.getInstance(editor.ij).output(it) val outputPanel = injector.outputPanel.getOrCreate(editor, context)
outputPanel.addText(it)
outputPanel.show()
} }
lastCommand = command lastCommand = command
ExecutionResult.Success ExecutionResult.Success

View File

@@ -125,10 +125,12 @@
<xi:include href="/META-INF/includes/VimListeners.xml" xpointer="xpointer(/idea-plugin/*)"/> <xi:include href="/META-INF/includes/VimListeners.xml" xpointer="xpointer(/idea-plugin/*)"/>
<actions resource-bundle="messages.IdeaVimBundle"> <actions resource-bundle="messages.IdeaVimBundle">
<action id="VimPluginToggle" class="com.maddyhome.idea.vim.action.VimPluginToggleAction"> <group id="com.chylex.intellij.vim" text="Vim" popup="true">
<add-to-group group-id="ToolsMenu" anchor="last"/> <add-to-group group-id="ToolsMenu" anchor="last"/>
</action> <action id="VimPluginToggle" class="com.maddyhome.idea.vim.action.VimPluginToggleAction"/>
<action id="VimRunLastMacroInOpenFiles" class="com.maddyhome.idea.vim.action.VimRunLastMacroInOpenFiles"/>
</group>
<!-- Internal --> <!-- Internal -->
<!--suppress PluginXmlI18n --> <!--suppress PluginXmlI18n -->
<action id="VimInternalAddBlockInlays" class="com.maddyhome.idea.vim.action.internal.AddBlockInlaysAction" text="Add Test Block Inlays | IdeaVim Internal" internal="true"/> <action id="VimInternalAddBlockInlays" class="com.maddyhome.idea.vim.action.internal.AddBlockInlaysAction" text="Add Test Block Inlays | IdeaVim Internal" internal="true"/>

View File

@@ -44,6 +44,7 @@ viminfo
virtualedit virtualedit
visualbell visualbell
visualdelay visualdelay
whichwrap
wrapscan wrapscan
nobomb nobomb

View File

@@ -0,0 +1,82 @@
[
{
"keys": ".",
"class": "com.maddyhome.idea.vim.action.change.RepeatChangeAction",
"modes": "N"
},
{
"keys": "<C-I>",
"class": "com.maddyhome.idea.vim.action.editor.VimEditorTab",
"modes": "I"
},
{
"keys": "<C-L>",
"class": "com.maddyhome.idea.vim.action.RedrawAction",
"modes": "N"
},
{
"keys": "<Del>",
"class": "com.maddyhome.idea.vim.action.editor.VimEditorDelete",
"modes": "I"
},
{
"keys": "<Down>",
"class": "com.maddyhome.idea.vim.action.editor.VimEditorDown",
"modes": "I"
},
{
"keys": "<Tab>",
"class": "com.maddyhome.idea.vim.action.editor.VimEditorTab",
"modes": "I"
},
{
"keys": "<Up>",
"class": "com.maddyhome.idea.vim.action.editor.VimEditorUp",
"modes": "I"
},
{
"keys": "<kDown>",
"class": "com.maddyhome.idea.vim.action.editor.VimEditorDown",
"modes": "I"
},
{
"keys": "<kUp>",
"class": "com.maddyhome.idea.vim.action.editor.VimEditorUp",
"modes": "I"
},
{
"keys": "J",
"class": "com.maddyhome.idea.vim.action.change.delete.DeleteJoinLinesSpacesAction",
"modes": "N"
},
{
"keys": "J",
"class": "com.maddyhome.idea.vim.action.change.delete.DeleteJoinVisualLinesSpacesAction",
"modes": "X"
},
{
"keys": "K",
"class": "com.maddyhome.idea.vim.action.editor.VimQuickJavaDoc",
"modes": "N"
},
{
"keys": "g@",
"class": "com.maddyhome.idea.vim.action.change.OperatorAction",
"modes": "N"
},
{
"keys": "g@",
"class": "com.maddyhome.idea.vim.action.change.VisualOperatorAction",
"modes": "X"
},
{
"keys": "gJ",
"class": "com.maddyhome.idea.vim.action.change.delete.DeleteJoinLinesAction",
"modes": "N"
},
{
"keys": "gJ",
"class": "com.maddyhome.idea.vim.action.change.delete.DeleteJoinVisualLinesAction",
"modes": "X"
}
]

View File

@@ -0,0 +1,9 @@
{
"!": "com.maddyhome.idea.vim.vimscript.model.commands.CmdFilterCommand",
"actionl[ist]": "com.maddyhome.idea.vim.vimscript.model.commands.ActionListCommand",
"b[uffer]": "com.maddyhome.idea.vim.vimscript.model.commands.BufferCommand",
"buffers": "com.maddyhome.idea.vim.vimscript.model.commands.BufferListCommand",
"files": "com.maddyhome.idea.vim.vimscript.model.commands.BufferListCommand",
"h[elp]": "com.maddyhome.idea.vim.vimscript.model.commands.HelpCommand",
"ls": "com.maddyhome.idea.vim.vimscript.model.commands.BufferListCommand"
}

View File

@@ -0,0 +1,6 @@
{
"col": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.ColFunctionHandler",
"has": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.HasFunctionHandler",
"line": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.LineFunctionHandler",
"submatch": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.SubmatchFunctionHandler"
}

View File

@@ -7,6 +7,7 @@
*/ */
package org.jetbrains.plugins.ideavim.action package org.jetbrains.plugins.ideavim.action
import com.intellij.idea.TestFor
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.ReturnTo import com.maddyhome.idea.vim.state.mode.ReturnTo
@@ -1068,4 +1069,14 @@ foobaz
Mode.NORMAL(), Mode.NORMAL(),
) )
} }
@Test
@TestFor(issues = ["VIM-2074"])
fun `backspace with replace mode`() {
configureByText("${c}Hello world")
typeText("R1111")
assertState("1111o world")
typeText("<BS><BS><BS>")
assertState("1ello world")
}
} }

View File

@@ -109,7 +109,7 @@ class CopyActionTest : VimTestCase() {
@TestWithoutNeovim(reason = SkipNeovimReason.DIFFERENT) @TestWithoutNeovim(reason = SkipNeovimReason.DIFFERENT)
@Test @Test
fun testYankRegisterUsesLastEnteredRegister() { fun testYankRegisterUsesLastEnteredRegister() {
typeTextInFile("\"a\"byl" + "\"ap", "hel<caret>lo world\n") typeTextInFile("\"a\"byl" + "\"bp", "hel<caret>lo world\n")
assertState("helllo world\n") assertState("helllo world\n")
} }

View File

@@ -85,4 +85,33 @@ class MotionBackspaceActionTest : VimTestCase() {
enterCommand("set whichwrap=b") enterCommand("set whichwrap=b")
} }
} }
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@Test
fun `test backspace motion with operator`() {
doTest(
"d<BS>",
"""
lorem ${c}ipsum dolor sit amet
""".trimIndent(),
"""
lorem${c}ipsum dolor sit amet
""".trimIndent(),
)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@Test
fun `test backspace motion with operator at start of line`() {
doTest(
"d<BS>",
"""
lorem ipsum dolor sit amet
${c}lorem ipsum dolor sit amet
""".trimIndent(),
"""
lorem ipsum dolor sit amet${c}lorem ipsum dolor sit amet
""".trimIndent(),
)
}
} }

View File

@@ -85,4 +85,35 @@ class MotionSpaceActionTest : VimTestCase() {
enterCommand("set whichwrap=s") enterCommand("set whichwrap=s")
} }
} }
@Suppress("SpellCheckingInspection")
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@Test
fun `test space motion with operator`() {
doTest(
"d<Space>",
"""
lorem ${c}ipsum dolor sit amet
""".trimIndent(),
"""
lorem ${c}psum dolor sit amet
""".trimIndent(),
)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@Test
fun `test space motion with operator at end of line`() {
doTest(
"d<Space>",
"""
lorem ipsum dolor sit ame${c}t
lorem ipsum dolor sit amet
""".trimIndent(),
"""
lorem ipsum dolor sit am${c}e
lorem ipsum dolor sit amet
""".trimIndent(),
)
}
} }

View File

@@ -294,20 +294,20 @@ class ExEntryTest : VimTestCase() {
// assertExText("set incsearch") // assertExText("set incsearch")
typeExInput(":<S-Up>") typeExInput(":<S-Up>")
assertExText("set digraph")
typeText("<Up>")
assertExText("set incsearch") assertExText("set incsearch")
typeText("<Up>") typeText("<Up>")
assertExText("digraph") assertExText("digraph")
typeText("<Up>")
assertExText("set digraph")
deactivateExEntry() deactivateExEntry()
typeExInput(":<PageUp>") typeExInput(":<PageUp>")
assertExText("set incsearch")
typeText("<PageUp>")
assertExText("digraph") assertExText("digraph")
typeText("<PageUp>") typeText("<PageUp>")
assertExText("set digraph") assertExText("set digraph")
typeText("<PageUp>")
assertExText("set incsearch")
} }
@TestWithoutNeovim(SkipNeovimReason.CMD) @TestWithoutNeovim(SkipNeovimReason.CMD)
@@ -325,20 +325,20 @@ class ExEntryTest : VimTestCase() {
deactivateExEntry() deactivateExEntry()
typeExInput(":set<S-Up>") typeExInput(":set<S-Up>")
assertExText("set digraph")
typeText("<S-Up>")
assertExText("set incsearch") assertExText("set incsearch")
typeText("<S-Up>") typeText("<S-Up>")
assertExText("digraph") assertExText("digraph")
typeText("<S-Up>")
assertExText("set digraph")
deactivateExEntry() deactivateExEntry()
typeExInput(":set<PageUp>") typeExInput(":set<PageUp>")
assertExText("set incsearch")
typeText("<PageUp>")
assertExText("digraph") assertExText("digraph")
typeText("<PageUp>") typeText("<PageUp>")
assertExText("set digraph") assertExText("set digraph")
typeText("<PageUp>")
assertExText("set incsearch")
} }
@Test @Test
@@ -357,20 +357,20 @@ class ExEntryTest : VimTestCase() {
deactivateExEntry() deactivateExEntry()
typeExInput("/<S-Up>") typeExInput("/<S-Up>")
assertExText("something cool")
typeText("<S-Up>")
assertExText("so cool") assertExText("so cool")
typeText("<S-Up>") typeText("<S-Up>")
assertExText("not cool") assertExText("not cool")
typeText("<S-Up>")
assertExText("something cool")
deactivateExEntry() deactivateExEntry()
typeExInput("/<PageUp>") typeExInput("/<PageUp>")
assertExText("so cool")
typeText("<PageUp>")
assertExText("not cool") assertExText("not cool")
typeText("<PageUp>") typeText("<PageUp>")
assertExText("something cool") assertExText("something cool")
typeText("<PageUp>")
assertExText("so cool")
} }
@VimBehaviorDiffers(description = "Vim reorders history even when cancelling entry") @VimBehaviorDiffers(description = "Vim reorders history even when cancelling entry")
@@ -394,20 +394,20 @@ class ExEntryTest : VimTestCase() {
// assertEquals("set incsearch", exEntryPanel.text) // assertEquals("set incsearch", exEntryPanel.text)
typeExInput("/so<S-Up>") typeExInput("/so<S-Up>")
assertExText("something cool")
typeText("<S-Up>")
assertExText("so cool") assertExText("so cool")
typeText("<S-Up>") typeText("<S-Up>")
assertExText("not cool") assertExText("not cool")
typeText("<S-Up>")
assertExText("something cool")
deactivateExEntry() deactivateExEntry()
typeExInput("/so<PageUp>") typeExInput("/so<PageUp>")
assertExText("so cool")
typeText("<PageUp>")
assertExText("not cool") assertExText("not cool")
typeText("<PageUp>") typeText("<PageUp>")
assertExText("something cool") assertExText("something cool")
typeText("<PageUp>")
assertExText("so cool")
} }
@Test @Test

View File

@@ -26,6 +26,7 @@ import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNull
import kotlin.test.assertTrue import kotlin.test.assertTrue
/** /**
@@ -267,7 +268,7 @@ class MapCommandTest : VimTestCase() {
configureByText("${c}foo\n") configureByText("${c}foo\n")
typeText(commandToKeys("imap a b \\| c")) typeText(commandToKeys("imap a b \\| c"))
typeText(injector.parser.parseKeys("ia")) typeText(injector.parser.parseKeys("ia"))
assertState("b \\| cfoo\n") assertState("b | cfoo\n")
} }
// VIM-666 |:imap| // VIM-666 |:imap|
@@ -277,7 +278,7 @@ class MapCommandTest : VimTestCase() {
configureByText("${c}foo\n") configureByText("${c}foo\n")
typeText(commandToKeys("imap a b \\| c |")) typeText(commandToKeys("imap a b \\| c |"))
typeText(injector.parser.parseKeys("ia")) typeText(injector.parser.parseKeys("ia"))
assertState("b \\| c foo\n") assertState("b | c foo\n")
} }
// VIM-670 |:map| // VIM-670 |:map|
@@ -754,4 +755,23 @@ class MapCommandTest : VimTestCase() {
assertTrue(KeyHandler.getInstance().keyStack.isEmpty()) assertTrue(KeyHandler.getInstance().keyStack.isEmpty())
} }
@TestFor(issues = ["VIM-3601"])
@Test
fun `mapping to something with bars`() {
configureByText(
"""
Lorem Ipsum
Lorem ipsum dolor sit amet,
${c}consectetur adipiscing elit
Sed in orci mauris.
Cras id tellus in ex imperdiet egestas.
""".trimIndent()
)
typeText(commandToKeys("map k :echo 4<CR> \\| :echo 42<CR>"))
assertNull(injector.outputPanel.getCurrentOutputPanel())
typeText("k")
assertEquals("4\n42", injector.outputPanel.getCurrentOutputPanel()!!.text)
}
} }

View File

@@ -32,7 +32,7 @@ class PrintCommandTest : VimTestCase() {
// We should be waiting for a keypress now, such as <Enter> or <Esc> to close the output panel. But that's handled // We should be waiting for a keypress now, such as <Enter> or <Esc> to close the output panel. But that's handled
// by a separate key event loop which doesn't operate in tests. // by a separate key event loop which doesn't operate in tests.
// Simulate closing the output panel in the same way as if we'd entered the right key // Simulate closing the output panel in the same way as if we'd entered the right key
ExOutputModel.getInstance(fixture.editor).close() injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("p")) typeText(commandToKeys("p"))
assertExOutput(" Lorem Ipsum") assertExOutput(" Lorem Ipsum")
} }

View File

@@ -15,7 +15,7 @@ class VimVariableServiceTest : VimTestCase() {
@Test @Test
fun `test v count variable without count specified`() { fun `test v count variable without count specified`() {
configureByText("\n") configureByText("\n")
enterCommand("nnoremap <expr> n ':echo ' .. v:count .. \"\\<CR>\"") enterCommand("""nnoremap <expr> n ':echo ' .. v:count .. "\<CR>"""")
typeText("n") typeText("n")
assertExOutput("0") assertExOutput("0")
} }
@@ -23,15 +23,31 @@ class VimVariableServiceTest : VimTestCase() {
@Test @Test
fun `test v count variable`() { fun `test v count variable`() {
configureByText("\n") configureByText("\n")
enterCommand("nnoremap <expr> n ':' .. \"\\<C-u>\" .. 'echo ' .. v:count .. \"\\<CR>\"") enterCommand("""nnoremap <expr> n ':' .. "\<C-u>" .. 'echo ' .. v:count .. "\<CR>"""")
typeText("5n") typeText("5n")
assertExOutput("5") assertExOutput("5")
} }
@Test
fun `test v count variable with additional count during select register`() {
configureByText("\n")
enterCommand("""nnoremap <expr> n ':' .. "\<C-u>" .. 'echo ' .. v:count .. "\<CR>"""")
typeText("2\"a5n")
assertExOutput("10")
}
@Test
fun `test v count variable with additional pathological count during select register`() {
configureByText("\n")
enterCommand("""nnoremap <expr> n ':' .. "\<C-u>" .. 'echo ' .. v:count .. "\<CR>"""")
typeText("2\"a3\"b4\"c5n")
assertExOutput("120")
}
@Test @Test
fun `test v count1 variable without count specified`() { fun `test v count1 variable without count specified`() {
configureByText("\n") configureByText("\n")
enterCommand("nnoremap <expr> n ':echo ' .. v:count1 .. \"\\<CR>\"") enterCommand("""nnoremap <expr> n ':echo ' .. v:count1 .. "\<CR>"""")
typeText("n") typeText("n")
assertExOutput("1") assertExOutput("1")
} }
@@ -39,11 +55,27 @@ class VimVariableServiceTest : VimTestCase() {
@Test @Test
fun `test v count1 variable`() { fun `test v count1 variable`() {
configureByText("\n") configureByText("\n")
enterCommand("nnoremap <expr> n ':' .. \"\\<C-u>\" .. 'echo ' .. v:count1 .. \"\\<CR>\"") enterCommand("""nnoremap <expr> n ':' .. "\<C-u>" .. 'echo ' .. v:count1 .. "\<CR>"""")
typeText("5n") typeText("5n")
assertExOutput("5") assertExOutput("5")
} }
@Test
fun `test v count1 variable with additional count during select register`() {
configureByText("\n")
enterCommand("""nnoremap <expr> n ':' .. "\<C-u>" .. 'echo ' .. v:count1 .. "\<CR>"""")
typeText("2\"a5n")
assertExOutput("10")
}
@Test
fun `test v count1 variable with additional pathological count during select register`() {
configureByText("\n")
enterCommand("""nnoremap <expr> n ':' .. "\<C-u>" .. 'echo ' .. v:count1 .. "\<CR>"""")
typeText("2\"a3\"b4\"c5n")
assertExOutput("120")
}
@Test @Test
fun `test mapping with updating jumplist`() { fun `test mapping with updating jumplist`() {
configureByText("${c}1\n2\n3\n4\n5\n6\n7\n8\n9\n") configureByText("${c}1\n2\n3\n4\n5\n6\n7\n8\n9\n")

View File

@@ -141,6 +141,16 @@ Mode.INSERT,
) )
} }
@Test
fun testDeleteWithMultipleCounts() {
doTest(
"2d2aa",
"function(int <caret>arg1, char* arg<caret>2=\"a,b,c(d,e)\", bool arg3, string arg4, int arg5)",
"function(<caret>)",
Mode.NORMAL(),
)
}
@Test @Test
fun testSelectTwoArguments() { fun testSelectTwoArguments() {
doTest( doTest(

View File

@@ -660,7 +660,7 @@ abstract class VimTestCase {
} }
fun assertExOutput(expected: String, clear: Boolean = true) { fun assertExOutput(expected: String, clear: Boolean = true) {
val actual = ExOutputModel.getInstance(fixture.editor).text val actual = injector.outputPanel.getCurrentOutputPanel()?.text
assertNotNull(actual, "No Ex output") assertNotNull(actual, "No Ex output")
assertEquals(expected, actual) assertEquals(expected, actual)
NeovimTesting.typeCommand("<esc>", testInfo, fixture.editor) NeovimTesting.typeCommand("<esc>", testInfo, fixture.editor)

View File

@@ -16,6 +16,7 @@ import org.jetbrains.plugins.ideavim.TestWithoutNeovim
import org.jetbrains.plugins.ideavim.VimBehaviorDiffers import org.jetbrains.plugins.ideavim.VimBehaviorDiffers
import org.jetbrains.plugins.ideavim.VimJavaTestCase import org.jetbrains.plugins.ideavim.VimJavaTestCase
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
class ChangeActionJavaTest : VimJavaTestCase() { class ChangeActionJavaTest : VimJavaTestCase() {
// VIM-511 |.| // VIM-511 |.|
@@ -120,6 +121,28 @@ and some text after""",
) )
} }
// VIM-566
@TestWithoutNeovim(SkipNeovimReason.FOLDING)
@Test
fun testInsertAfterToggleFold() {
configureByJavaText(
"""
$c/**
* I should be fold
* a little more text
* and final fold
*/
and some text after
""".trimIndent(),
)
CodeFoldingManager.getInstance(fixture.project).updateFoldRegions(fixture.editor)
assertEquals(FoldingUtil.findFoldRegionStartingAtLine(fixture.editor, 0)!!.isExpanded, true)
typeText(injector.parser.parseKeys("za"))
assertEquals(FoldingUtil.findFoldRegionStartingAtLine(fixture.editor, 0)!!.isExpanded, false)
typeText(injector.parser.parseKeys("za"))
assertEquals(FoldingUtil.findFoldRegionStartingAtLine(fixture.editor, 0)!!.isExpanded, true)
}
// VIM-287 |zc| |o| // VIM-287 |zc| |o|
@TestWithoutNeovim(SkipNeovimReason.FOLDING) @TestWithoutNeovim(SkipNeovimReason.FOLDING)
@Test @Test

View File

@@ -95,7 +95,7 @@ private class AvailableActions(private val editor: Editor) : ImperativeCommand {
val currentNode = KeyHandler.getInstance().keyHandlerState.commandBuilder.getCurrentTrie() val currentNode = KeyHandler.getInstance().keyHandlerState.commandBuilder.getCurrentTrie()
// Note: esc is always an option // Note: esc is always an option
val possibleKeys = (currentNode.keys.toList() + esc).sortedBy { injector.parser.toKeyNotation(it) } val possibleKeys = (currentNode.children.keys.toList() + esc).sortedBy { injector.parser.toKeyNotation(it) }
println("Keys: ${possibleKeys.joinToString(", ")}") println("Keys: ${possibleKeys.joinToString(", ")}")
val keyGenerator = Generator.integers(0, possibleKeys.lastIndex) val keyGenerator = Generator.integers(0, possibleKeys.lastIndex)
.suchThat { injector.parser.toKeyNotation(possibleKeys[it]) !in stinkyKeysList } .suchThat { injector.parser.toKeyNotation(possibleKeys[it]) !in stinkyKeysList }

View File

@@ -15,7 +15,7 @@ val javaVersion: String by project
val remoteRobotVersion: String by project val remoteRobotVersion: String by project
dependencies { dependencies {
testFixturesImplementation("org.junit.jupiter:junit-jupiter:5.10.3") testFixturesImplementation("org.junit.jupiter:junit-jupiter:5.11.0")
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
testFixturesImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion") testFixturesImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
testFixturesImplementation(testFixtures(project(":"))) // The root project testFixturesImplementation(testFixtures(project(":"))) // The root project

View File

@@ -17,14 +17,13 @@ import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.MappingMode import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.command.MappingProcessor import com.maddyhome.idea.vim.command.MappingProcessor
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.CurrentCommandState
import com.maddyhome.idea.vim.diagnostic.VimLogger import com.maddyhome.idea.vim.diagnostic.VimLogger
import com.maddyhome.idea.vim.diagnostic.trace import com.maddyhome.idea.vim.diagnostic.trace
import com.maddyhome.idea.vim.diagnostic.vimLogger import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.impl.state.toMappingMode import com.maddyhome.idea.vim.impl.state.toMappingMode
import com.maddyhome.idea.vim.key.CommandPartNode
import com.maddyhome.idea.vim.key.KeyConsumer import com.maddyhome.idea.vim.key.KeyConsumer
import com.maddyhome.idea.vim.key.KeyStack import com.maddyhome.idea.vim.key.KeyStack
import com.maddyhome.idea.vim.key.RootNode
import com.maddyhome.idea.vim.key.consumers.CharArgumentConsumer import com.maddyhome.idea.vim.key.consumers.CharArgumentConsumer
import com.maddyhome.idea.vim.key.consumers.CommandConsumer import com.maddyhome.idea.vim.key.consumers.CommandConsumer
import com.maddyhome.idea.vim.key.consumers.CommandCountConsumer import com.maddyhome.idea.vim.key.consumers.CommandCountConsumer
@@ -55,8 +54,15 @@ class KeyHandler {
private val keyConsumers: List<KeyConsumer> = listOf(ModalInputConsumer(), MappingProcessor, CommandCountConsumer(), DeleteCommandConsumer(), EditorResetConsumer(), CharArgumentConsumer(), RegisterConsumer(), DigraphConsumer(), CommandConsumer(), SelectRegisterConsumer(), ModeInputConsumer()) private val keyConsumers: List<KeyConsumer> = listOf(ModalInputConsumer(), MappingProcessor, CommandCountConsumer(), DeleteCommandConsumer(), EditorResetConsumer(), CharArgumentConsumer(), RegisterConsumer(), DigraphConsumer(), CommandConsumer(), SelectRegisterConsumer(), ModeInputConsumer())
private var handleKeyRecursionCount = 0 private var handleKeyRecursionCount = 0
var keyHandlerState: KeyHandlerState = KeyHandlerState() // KeyHandlerState requires injector.keyGroup to be initialized and that's why we don't create it immediately and have this here
private set // TODO figure out a better solution
private val defaultKeyHandlerState by lazy { KeyHandlerState() }
private var mutableKeyHandlerState: KeyHandlerState? = null
var keyHandlerState: KeyHandlerState
get() = mutableKeyHandlerState ?: defaultKeyHandlerState
private set(value) {
mutableKeyHandlerState = value
}
val keyStack: KeyStack = KeyStack() val keyStack: KeyStack = KeyStack()
val modalEntryKeys: MutableList<KeyStroke> = ArrayList() val modalEntryKeys: MutableList<KeyStroke> = ArrayList()
@@ -190,11 +196,9 @@ class KeyHandler {
} }
private fun onUnknownKey(editor: VimEditor, keyState: KeyHandlerState) { private fun onUnknownKey(editor: VimEditor, keyState: KeyHandlerState) {
logger.trace("Command builder is set to BAD")
keyState.commandBuilder.commandState = CurrentCommandState.BAD_COMMAND
editor.resetOpPending() editor.resetOpPending()
injector.vimState.resetRegisterPending()
editor.isReplaceCharacter = false editor.isReplaceCharacter = false
// Note that this will also reset the CommandBuilder to NEW_COMMAND
reset(keyState, editor.mode) reset(keyState, editor.mode)
} }
@@ -203,14 +207,6 @@ class KeyHandler {
injector.messages.indicateError() injector.messages.indicateError()
} }
fun isDuplicateOperatorKeyStroke(key: KeyStroke, mode: Mode, keyState: KeyHandlerState): Boolean {
return isOperatorPending(mode, keyState) && keyState.commandBuilder.isDuplicateOperatorKeyStroke(key)
}
fun isOperatorPending(mode: Mode, keyState: KeyHandlerState): Boolean {
return mode is Mode.OP_PENDING && !keyState.commandBuilder.isEmpty
}
private fun executeCommand( private fun executeCommand(
editor: VimEditor, editor: VimEditor,
context: ExecutionContext, context: ExecutionContext,
@@ -219,11 +215,7 @@ class KeyHandler {
) { ) {
logger.trace("Command execution") logger.trace("Command execution")
val command = keyState.commandBuilder.buildCommand() val command = keyState.commandBuilder.buildCommand()
val operatorArguments = OperatorArguments( val operatorArguments = OperatorArguments(command.rawCount, editorState.mode)
editor.mode is Mode.OP_PENDING,
command.rawCount,
editorState.mode,
)
// If we were in "operator pending" mode, reset back to normal mode. // If we were in "operator pending" mode, reset back to normal mode.
// But opening command line should not reset operator pending mode (e.g. `d/foo` // But opening command line should not reset operator pending mode (e.g. `d/foo`
@@ -283,11 +275,12 @@ class KeyHandler {
// TODO we should have a single reset method // TODO we should have a single reset method
fun reset(keyState: KeyHandlerState, mode: Mode) { fun reset(keyState: KeyHandlerState, mode: Mode) {
logger.trace { "Reset is executed" } logger.trace { "Reset is executed" }
injector.commandLine.getActiveCommandLine()?.clearCurrentAction()
keyHandlerState.partialReset(mode) keyHandlerState.partialReset(mode)
keyState.commandBuilder.resetAll(getKeyRoot(mode.toMappingMode())) keyState.commandBuilder.resetAll(getKeyRoot(mode.toMappingMode()))
} }
private fun getKeyRoot(mappingMode: MappingMode): CommandPartNode<LazyVimCommand> { private fun getKeyRoot(mappingMode: MappingMode): RootNode<LazyVimCommand> {
return injector.keyGroup.getKeyRoot(mappingMode) return injector.keyGroup.getKeyRoot(mappingMode)
} }
@@ -333,7 +326,7 @@ class KeyHandler {
) : Runnable { ) : Runnable {
override fun run() { override fun run() {
val editorState = injector.vimState val editorState = injector.vimState
keyState.commandBuilder.commandState = CurrentCommandState.NEW_COMMAND
val register = cmd.register val register = cmd.register
if (register != null) { if (register != null) {
injector.registerGroup.selectRegister(register) injector.registerGroup.selectRegister(register)
@@ -353,22 +346,15 @@ class KeyHandler {
// mode we were in. This handles commands in those modes that temporarily allow us to execute normal // mode we were in. This handles commands in those modes that temporarily allow us to execute normal
// mode commands. An exception is if this command should leave us in the temporary mode such as // mode commands. An exception is if this command should leave us in the temporary mode such as
// "select register" // "select register"
val myMode = editorState.mode if (editorState.mode is Mode.NORMAL && !cmd.flags.contains(CommandFlags.FLAG_EXPECT_MORE)) {
val returnTo = myMode.returnTo when (editorState.mode.returnTo) {
if (myMode is Mode.NORMAL && returnTo != null && !cmd.flags.contains(CommandFlags.FLAG_EXPECT_MORE)) { ReturnTo.INSERT -> editor.mode = Mode.INSERT
when (returnTo) { ReturnTo.REPLACE -> editor.mode = Mode.REPLACE
ReturnTo.INSERT -> { null -> {}
editor.mode = Mode.INSERT
}
ReturnTo.REPLACE -> {
editor.mode = Mode.REPLACE
}
} }
} }
if (keyState.commandBuilder.isDone()) {
getInstance().reset(keyState, editorState.mode) instance.reset(keyState, editorState.mode)
}
} }
} }

View File

@@ -17,23 +17,17 @@ import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.lineLength import com.maddyhome.idea.vim.api.lineLength
import com.maddyhome.idea.vim.command.Argument import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.diagnostic.debug import com.maddyhome.idea.vim.diagnostic.debug
import com.maddyhome.idea.vim.diagnostic.vimLogger import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
import com.maddyhome.idea.vim.helper.enumSetOf
import com.maddyhome.idea.vim.state.KeyHandlerState import com.maddyhome.idea.vim.state.KeyHandlerState
import java.util.*
@CommandOrMotion(keys = ["r"], modes = [Mode.NORMAL]) @CommandOrMotion(keys = ["r"], modes = [Mode.NORMAL])
class ChangeCharacterAction : ChangeEditorActionHandler.ForEachCaret() { class ChangeCharacterAction : ChangeEditorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.CHANGE override val type: Command.Type = Command.Type.CHANGE
override val argumentType: Argument.Type = Argument.Type.DIGRAPH override val argumentType: Argument.Type = Argument.Type.DIGRAPH
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_ALLOW_DIGRAPH)
override fun onStartWaitingForArgument(editor: VimEditor, context: ExecutionContext, keyState: KeyHandlerState) { override fun onStartWaitingForArgument(editor: VimEditor, context: ExecutionContext, keyState: KeyHandlerState) {
editor.isReplaceCharacter = true editor.isReplaceCharacter = true
} }
@@ -45,7 +39,7 @@ class ChangeCharacterAction : ChangeEditorActionHandler.ForEachCaret() {
argument: Argument?, argument: Argument?,
operatorArguments: OperatorArguments, operatorArguments: OperatorArguments,
): Boolean { ): Boolean {
return argument != null && changeCharacter(editor, caret, operatorArguments.count1, argument.character) return argument is Argument.Character && changeCharacter(editor, caret, operatorArguments.count1, argument.character)
} }
} }

View File

@@ -37,12 +37,11 @@ class ChangeLineAction : ChangeInInsertSequenceAction() {
): Boolean { ): Boolean {
// `S` command is a synonym of `cc` // `S` command is a synonym of `cc`
val motion = MotionDownLess1FirstNonSpaceAction() val motion = MotionDownLess1FirstNonSpaceAction()
val command = Command(1, motion, motion.type, motion.flags)
return injector.changeGroup.changeMotion( return injector.changeGroup.changeMotion(
editor, editor,
caret, caret,
context, context,
Argument(command), Argument.Motion(motion, null),
operatorArguments, operatorArguments,
) )
} }

View File

@@ -15,6 +15,7 @@ import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Argument import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.VimEditorReplaceMask
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
@CommandOrMotion(keys = ["R"], modes = [Mode.NORMAL]) @CommandOrMotion(keys = ["R"], modes = [Mode.NORMAL])
@@ -39,4 +40,5 @@ class ChangeReplaceAction : ChangeEditorActionHandler.SingleExecution() {
*/ */
private fun changeReplace(editor: VimEditor, context: ExecutionContext) { private fun changeReplace(editor: VimEditor, context: ExecutionContext) {
injector.changeGroup.initInsert(editor, context, com.maddyhome.idea.vim.state.mode.Mode.REPLACE) injector.changeGroup.initInsert(editor, context, com.maddyhome.idea.vim.state.mode.Mode.REPLACE)
editor.replaceMask = VimEditorReplaceMask()
} }

View File

@@ -39,7 +39,6 @@ class ChangeVisualAction : VisualOperatorActionHandler.ForEachCaret() {
range.toVimTextRange(false), range.toVimTextRange(false),
range.type, range.type,
context, context,
operatorArguments,
) )
} }
} }

View File

@@ -15,16 +15,13 @@ import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Argument import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.diagnostic.debug import com.maddyhome.idea.vim.diagnostic.debug
import com.maddyhome.idea.vim.diagnostic.vimLogger import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.group.visual.VimSelection import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
import com.maddyhome.idea.vim.helper.enumSetOf
import com.maddyhome.idea.vim.state.KeyHandlerState import com.maddyhome.idea.vim.state.KeyHandlerState
import java.util.*
/** /**
* @author vlan * @author vlan
@@ -32,11 +29,8 @@ import java.util.*
@CommandOrMotion(keys = ["r"], modes = [Mode.VISUAL]) @CommandOrMotion(keys = ["r"], modes = [Mode.VISUAL])
class ChangeVisualCharacterAction : VisualOperatorActionHandler.ForEachCaret() { class ChangeVisualCharacterAction : VisualOperatorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.CHANGE override val type: Command.Type = Command.Type.CHANGE
override val argumentType: Argument.Type = Argument.Type.DIGRAPH override val argumentType: Argument.Type = Argument.Type.DIGRAPH
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_ALLOW_DIGRAPH)
override fun onStartWaitingForArgument(editor: VimEditor, context: ExecutionContext, keyState: KeyHandlerState) { override fun onStartWaitingForArgument(editor: VimEditor, context: ExecutionContext, keyState: KeyHandlerState) {
editor.isReplaceCharacter = true editor.isReplaceCharacter = true
} }
@@ -50,7 +44,7 @@ class ChangeVisualCharacterAction : VisualOperatorActionHandler.ForEachCaret() {
operatorArguments: OperatorArguments, operatorArguments: OperatorArguments,
): Boolean { ): Boolean {
val argument = cmd.argument val argument = cmd.argument
return argument != null && return argument is Argument.Character &&
changeCharacterRange(editor, caret, range.toVimTextRange(false), argument.character) changeCharacterRange(editor, caret, range.toVimTextRange(false), argument.character)
} }
} }

View File

@@ -56,7 +56,6 @@ class ChangeVisualLinesAction : VisualOperatorActionHandler.ForEachCaret() {
lineRange, lineRange,
SelectionType.LINE_WISE, SelectionType.LINE_WISE,
context, context,
operatorArguments,
) )
} }
} }

View File

@@ -53,7 +53,7 @@ class ChangeVisualLinesEndAction : VisualOperatorActionHandler.ForEachCaret() {
} }
} }
val blockRange = TextRange(starts, ends) val blockRange = TextRange(starts, ends)
injector.changeGroup.changeRange(editor, caret, blockRange, SelectionType.BLOCK_WISE, context, operatorArguments) injector.changeGroup.changeRange(editor, caret, blockRange, SelectionType.BLOCK_WISE, context)
} else { } else {
val lineEndForOffset = editor.getLineEndForOffset(vimTextRange.endOffset) val lineEndForOffset = editor.getLineEndForOffset(vimTextRange.endOffset)
val endsWithNewLine = if (lineEndForOffset.toLong() == editor.fileSize()) 0 else 1 val endsWithNewLine = if (lineEndForOffset.toLong() == editor.fileSize()) 0 else 1
@@ -61,7 +61,7 @@ class ChangeVisualLinesEndAction : VisualOperatorActionHandler.ForEachCaret() {
editor.getLineStartForOffset(vimTextRange.startOffset), editor.getLineStartForOffset(vimTextRange.startOffset),
lineEndForOffset + endsWithNewLine, lineEndForOffset + endsWithNewLine,
) )
injector.changeGroup.changeRange(editor, caret, lineRange, SelectionType.LINE_WISE, context, operatorArguments) injector.changeGroup.changeRange(editor, caret, lineRange, SelectionType.LINE_WISE, context)
} }
} }
} }

View File

@@ -31,7 +31,7 @@ class FilterVisualLinesAction : VimActionHandler.SingleExecution(), FilterComman
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean { override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
// Start ex entry with the initial text set to the calculated range and `!` // Start ex entry with the initial text set to the calculated range and `!`
startFilterCommand(editor, context, cmd) startFilterCommand(editor, context, cmd.rawCount)
return true return true
} }
} }
@@ -63,13 +63,13 @@ class FilterMotionAction : VimActionHandler.SingleExecution(), FilterCommand, Du
// Start ex entry with the initial text set to the calculated range and `!` // Start ex entry with the initial text set to the calculated range and `!`
val count = if (start.line < end.line) end.line - start.line + 1 else 1 val count = if (start.line < end.line) end.line - start.line + 1 else 1
startFilterCommand(editor, context, Argument.EMPTY_COMMAND.copy(rawCount = count)) startFilterCommand(editor, context, count)
return true return true
} }
} }
interface FilterCommand { interface FilterCommand {
fun startFilterCommand(editor: VimEditor, context: ExecutionContext, cmd: Command) { fun startFilterCommand(editor: VimEditor, context: ExecutionContext, count0: Int) {
injector.commandLine.createCommandPrompt(editor, context, cmd, initialText = "!") injector.commandLine.createCommandPrompt(editor, context, count0, initialText = "!")
} }
} }

View File

@@ -38,6 +38,6 @@ class DeleteMotionAction : ChangeEditorActionHandler.ForEachCaret(), DuplicableO
val (range, selectionType) = injector.changeGroup val (range, selectionType) = injector.changeGroup
.getDeleteRangeAndType(editor, caret, context, argument, false, operatorArguments) .getDeleteRangeAndType(editor, caret, context, argument, false, operatorArguments)
?: return false ?: return false
return injector.changeGroup.deleteRange(editor, caret, range, selectionType, false, operatorArguments) return injector.changeGroup.deleteRange(editor, caret, range, selectionType, false)
} }
} }

View File

@@ -40,7 +40,6 @@ class DeleteVisualAction : VisualOperatorActionHandler.ForEachCaret() {
range.toVimTextRange(false), range.toVimTextRange(false),
selectionType, selectionType,
false, false,
operatorArguments,
) )
} }
} }

View File

@@ -56,6 +56,6 @@ class DeleteVisualLinesAction : VisualOperatorActionHandler.ForEachCaret() {
Triple(caret, lineRange, SelectionType.LINE_WISE) Triple(caret, lineRange, SelectionType.LINE_WISE)
} }
} }
return injector.changeGroup.deleteRange(editor, usedCaret, usedRange, usedType, false, operatorArguments) return injector.changeGroup.deleteRange(editor, usedCaret, usedRange, usedType, false)
} }
} }

View File

@@ -58,7 +58,6 @@ class DeleteVisualLinesEndAction : VisualOperatorActionHandler.ForEachCaret() {
blockRange, blockRange,
SelectionType.BLOCK_WISE, SelectionType.BLOCK_WISE,
false, false,
operatorArguments,
) )
} else { } else {
val lineEndForOffset = editor.getLineEndForOffset(vimTextRange.endOffset) val lineEndForOffset = editor.getLineEndForOffset(vimTextRange.endOffset)
@@ -67,7 +66,7 @@ class DeleteVisualLinesEndAction : VisualOperatorActionHandler.ForEachCaret() {
editor.getLineStartForOffset(vimTextRange.startOffset), editor.getLineStartForOffset(vimTextRange.startOffset),
lineEndForOffset + endsWithNewLine, lineEndForOffset + endsWithNewLine,
) )
injector.changeGroup.deleteRange(editor, caret, lineRange, SelectionType.LINE_WISE, false, operatorArguments) injector.changeGroup.deleteRange(editor, caret, lineRange, SelectionType.LINE_WISE, false)
} }
} }
} }

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2003-2024 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.action.change.insert
import com.intellij.vim.annotations.CommandOrMotion
import com.intellij.vim.annotations.Mode
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.getLineStartForOffset
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.VimActionHandler
@CommandOrMotion(keys = ["<C-H>", "<BS>"], modes = [Mode.INSERT])
internal class InsertBackspaceAction : VimActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.OTHER_WRITABLE
override fun execute( editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments, ): Boolean {
if (editor.insertMode) {
injector.changeGroup.processBackspace(editor, context)
} else {
for (caret in editor.carets()) {
val offset = (caret.offset - 1).takeIf { it > 0 } ?: continue
val oldChar = editor.replaceMask?.popChange(editor, offset)
if (oldChar != null) {
injector.changeGroup.replaceText(editor, caret, offset, offset + 1, oldChar.toString())
}
caret.moveToOffset(offset)
}
}
return true
}
}

View File

@@ -52,7 +52,8 @@ class InsertCompletedDigraphAction : VimActionHandler.SingleExecution() {
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean { override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
// The converted digraph character has been captured as an argument, push it back through key handler // The converted digraph character has been captured as an argument, push it back through key handler
val keyStroke = KeyStroke.getKeyStroke(cmd.argument!!.character) val argument = cmd.argument as? Argument.Character ?: return false
val keyStroke = KeyStroke.getKeyStroke(argument.character)
val keyHandler = KeyHandler.getInstance() val keyHandler = KeyHandler.getInstance()
keyHandler.handleKey(editor, keyStroke, context, keyHandler.keyHandlerState) keyHandler.handleKey(editor, keyStroke, context, keyHandler.keyHandlerState)
return true return true

View File

@@ -52,7 +52,8 @@ class InsertCompletedLiteralAction : VimActionHandler.SingleExecution() {
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean { override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
// The converted literal character has been captured as an argument, push it back through key handler // The converted literal character has been captured as an argument, push it back through key handler
val keyStroke = KeyStroke.getKeyStroke(cmd.argument!!.character) val argument = cmd.argument as? Argument.Character ?: return false
val keyStroke = KeyStroke.getKeyStroke(argument.character)
val keyHandler = KeyHandler.getInstance() val keyHandler = KeyHandler.getInstance()
keyHandler.handleKey(editor, keyStroke, context, keyHandler.keyHandlerState) keyHandler.handleKey(editor, keyStroke, context, keyHandler.keyHandlerState)
return true return true

View File

@@ -36,7 +36,7 @@ class InsertDeleteInsertedTextAction : ChangeEditorActionHandler.ForEachCaret()
argument: Argument?, argument: Argument?,
operatorArguments: OperatorArguments, operatorArguments: OperatorArguments,
): Boolean { ): Boolean {
return insertDeleteInsertedText(editor, caret, operatorArguments) return insertDeleteInsertedText(editor, caret)
} }
} }
@@ -48,11 +48,7 @@ class InsertDeleteInsertedTextAction : ChangeEditorActionHandler.ForEachCaret()
* @param caret The caret on which the action is performed * @param caret The caret on which the action is performed
* @return true if able to delete the text, false if not * @return true if able to delete the text, false if not
*/ */
private fun insertDeleteInsertedText( private fun insertDeleteInsertedText(editor: VimEditor, caret: VimCaret): Boolean {
editor: VimEditor,
caret: VimCaret,
operatorArguments: OperatorArguments,
): Boolean {
var deleteTo = caret.vimInsertStart.startOffset var deleteTo = caret.vimInsertStart.startOffset
val offset = caret.offset val offset = caret.offset
if (offset == deleteTo) { if (offset == deleteTo) {
@@ -65,7 +61,6 @@ private fun insertDeleteInsertedText(
TextRange(deleteTo, offset), TextRange(deleteTo, offset),
SelectionType.CHARACTER_WISE, SelectionType.CHARACTER_WISE,
false, false,
operatorArguments,
) )
return true return true
} }

View File

@@ -37,7 +37,7 @@ class InsertDeletePreviousWordAction : ChangeEditorActionHandler.ForEachCaret()
argument: Argument?, argument: Argument?,
operatorArguments: OperatorArguments, operatorArguments: OperatorArguments,
): Boolean { ): Boolean {
return insertDeletePreviousWord(editor, caret, operatorArguments) return insertDeletePreviousWord(editor, caret)
} }
} }
@@ -50,7 +50,7 @@ class InsertDeletePreviousWordAction : ChangeEditorActionHandler.ForEachCaret()
* @param editor The editor to delete the text from * @param editor The editor to delete the text from
* @return true if able to delete text, false if not * @return true if able to delete text, false if not
*/ */
private fun insertDeletePreviousWord(editor: VimEditor, caret: VimCaret, operatorArguments: OperatorArguments): Boolean { private fun insertDeletePreviousWord(editor: VimEditor, caret: VimCaret): Boolean {
val deleteTo: Int = if (caret.getBufferPosition().column == 0) { val deleteTo: Int = if (caret.getBufferPosition().column == 0) {
caret.offset - 1 caret.offset - 1
} else { } else {
@@ -74,6 +74,6 @@ private fun insertDeletePreviousWord(editor: VimEditor, caret: VimCaret, operato
return false return false
} }
val range = TextRange(deleteTo, caret.offset) val range = TextRange(deleteTo, caret.offset)
injector.changeGroup.deleteRange(editor, caret, range, SelectionType.CHARACTER_WISE, true, operatorArguments) injector.changeGroup.deleteRange(editor, caret, range, SelectionType.CHARACTER_WISE, true)
return true return true
} }

View File

@@ -18,14 +18,10 @@ import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.ex.ExException import com.maddyhome.idea.vim.ex.ExException
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.RWLockLabel import com.maddyhome.idea.vim.helper.RWLockLabel
import com.maddyhome.idea.vim.helper.isCloseKeyStroke
import com.maddyhome.idea.vim.key.interceptors.VimInputInterceptorBase
import com.maddyhome.idea.vim.put.PutData import com.maddyhome.idea.vim.put.PutData
import com.maddyhome.idea.vim.register.Register import com.maddyhome.idea.vim.register.Register
import com.maddyhome.idea.vim.state.mode.SelectionType import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.vimscript.model.Script import com.maddyhome.idea.vim.vimscript.model.Script
import java.awt.event.KeyEvent
import javax.swing.KeyStroke
@CommandOrMotion(keys = ["<C-R>"], modes = [Mode.INSERT]) @CommandOrMotion(keys = ["<C-R>"], modes = [Mode.INSERT])
class InsertRegisterAction : VimActionHandler.SingleExecution() { class InsertRegisterAction : VimActionHandler.SingleExecution() {
@@ -39,9 +35,8 @@ class InsertRegisterAction : VimActionHandler.SingleExecution() {
cmd: Command, cmd: Command,
operatorArguments: OperatorArguments, operatorArguments: OperatorArguments,
): Boolean { ): Boolean {
val argument = cmd.argument val argument = cmd.argument as? Argument.Character ?: return false
if (argument.character == '=') {
if (argument?.character == '=') {
injector.commandLine.readInputAndProcess(editor, context, "=", finishOn = null) { input -> injector.commandLine.readInputAndProcess(editor, context, "=", finishOn = null) { input ->
try { try {
if (input.isNotEmpty()) { if (input.isNotEmpty()) {
@@ -50,7 +45,7 @@ class InsertRegisterAction : VimActionHandler.SingleExecution() {
val textToStore = expression.toInsertableString() val textToStore = expression.toInsertableString()
injector.registerGroup.storeTextSpecial('=', textToStore) injector.registerGroup.storeTextSpecial('=', textToStore)
} }
insertRegister(editor, context, '=', operatorArguments) insertRegister(editor, context, '=')
} catch (e: ExException) { } catch (e: ExException) {
injector.messages.indicateError() injector.messages.indicateError()
injector.messages.showStatusBarMessage(editor, e.message) injector.messages.showStatusBarMessage(editor, e.message)
@@ -58,7 +53,7 @@ class InsertRegisterAction : VimActionHandler.SingleExecution() {
} }
return true return true
} else { } else {
return argument != null && insertRegister(editor, context, argument.character, operatorArguments) return insertRegister(editor, context, argument.character)
} }
} }
} }
@@ -72,18 +67,13 @@ class InsertRegisterAction : VimActionHandler.SingleExecution() {
* @return true if able to insert the register contents, false if not * @return true if able to insert the register contents, false if not
*/ */
@RWLockLabel.SelfSynchronized @RWLockLabel.SelfSynchronized
private fun insertRegister( private fun insertRegister(editor: VimEditor, context: ExecutionContext, key: Char): Boolean {
editor: VimEditor,
context: ExecutionContext,
key: Char,
operatorArguments: OperatorArguments,
): Boolean {
val register: Register? = injector.registerGroup.getRegister(key) val register: Register? = injector.registerGroup.getRegister(key)
if (register != null) { if (register != null) {
val text = register.rawText ?: injector.parser.toPrintableString(register.keys) val text = register.rawText ?: injector.parser.toPrintableString(register.keys)
val textData = PutData.TextData(text, SelectionType.CHARACTER_WISE, emptyList(), register.name) val textData = PutData.TextData(text, SelectionType.CHARACTER_WISE, emptyList(), register.name)
val putData = PutData(textData, null, 1, insertTextBeforeCaret = true, rawIndent = true, caretAfterInsertedText = true) val putData = PutData(textData, null, 1, insertTextBeforeCaret = true, rawIndent = true, caretAfterInsertedText = true)
injector.put.putText(editor, context, putData, operatorArguments = operatorArguments) injector.put.putText(editor, context, putData)
return true return true
} }
return false return false

View File

@@ -36,7 +36,7 @@ class VisualBlockAppendAction : VisualOperatorActionHandler.SingleExecution() {
if (editor.isOneLineMode()) return false if (editor.isOneLineMode()) return false
val range = caretsAndSelections.values.stream().findFirst().orElse(null) ?: return false val range = caretsAndSelections.values.stream().findFirst().orElse(null) ?: return false
return if (range.type == SelectionType.BLOCK_WISE) { return if (range.type == SelectionType.BLOCK_WISE) {
injector.changeGroup.blockInsert(editor, context, range.toVimTextRange(false), true, operatorArguments) injector.changeGroup.initBlockInsert(editor, context, range.toVimTextRange(false), true)
} else { } else {
injector.changeGroup.insertAfterLineEnd(editor, context) injector.changeGroup.insertAfterLineEnd(editor, context)
true true

View File

@@ -36,7 +36,7 @@ class VisualBlockInsertAction : VisualOperatorActionHandler.SingleExecution() {
if (editor.isOneLineMode()) return false if (editor.isOneLineMode()) return false
val vimSelection = caretsAndSelections.values.stream().findFirst().orElse(null) ?: return false val vimSelection = caretsAndSelections.values.stream().findFirst().orElse(null) ?: return false
return if (vimSelection.type == SelectionType.BLOCK_WISE) { return if (vimSelection.type == SelectionType.BLOCK_WISE) {
injector.changeGroup.blockInsert(editor, context, vimSelection.toVimTextRange(false), false, operatorArguments) injector.changeGroup.initBlockInsert(editor, context, vimSelection.toVimTextRange(false), false)
} else { } else {
injector.changeGroup.insertBeforeFirstNonBlank(editor, context) injector.changeGroup.insertBeforeFirstNonBlank(editor, context)
true true

View File

@@ -52,7 +52,7 @@ sealed class PutTextBaseAction(
} }
result result
} else { } else {
injector.put.putText(editor, context, getPutData(count), operatorArguments) injector.put.putText(editor, context, getPutData(count))
} }
} }

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2003-2024 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.action.ex
import com.intellij.vim.annotations.CommandOrMotion
import com.intellij.vim.annotations.Mode
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.Graphemes
import com.maddyhome.idea.vim.handler.VimActionHandler
@CommandOrMotion(keys = ["<DEL>"], modes = [Mode.CMD_LINE])
class DeleteNextCharAction : VimActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
val commandLine = injector.commandLine.getActiveCommandLine() ?: return false
val caretOffset = commandLine.caret.offset
val oldText = commandLine.actualText
if (oldText.isEmpty()) {
commandLine.close(refocusOwningEditor = true, resetCaret = false)
return true
}
val newText = if (caretOffset == oldText.length) {
val preEndOffset = Graphemes.prev(oldText, oldText.length) ?: return true
oldText.substring(0, preEndOffset)
} else {
val nextOffset = Graphemes.next(oldText, caretOffset) ?: return true
oldText.substring(0, caretOffset) + oldText.substring(nextOffset)
}
commandLine.setText(newText)
return true
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2003-2024 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.action.ex
import com.intellij.vim.annotations.CommandOrMotion
import com.intellij.vim.annotations.Mode
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.Motion
import com.maddyhome.idea.vim.handler.VimActionHandler
@CommandOrMotion(keys = ["<C-W>"], modes = [Mode.CMD_LINE])
class DeletePrevWordAction : VimActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
val commandLine = injector.commandLine.getActiveCommandLine() ?: return false
val caretOffset = commandLine.caret.offset
if (caretOffset == 0) return true
val oldText = commandLine.actualText
val motion = injector.motion.findOffsetOfNextWord(oldText, oldText.length, commandLine.caret.offset, -1, true, editor)
when (motion) {
is Motion.AbsoluteOffset -> {
val newText = oldText.substring(0, motion.offset) + oldText.substring(caretOffset)
commandLine.caret.offset = motion.offset
commandLine.setText(newText)
}
else -> {}
}
return true
}
}

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