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

Compare commits

..

69 Commits

Author SHA1 Message Date
9f469d0eb2 Set plugin version to chylex-13 2022-10-24 02:18:35 +02:00
f59d2f769c Stop IdeaVIM blocking editor scrolling 2022-10-24 02:18:35 +02:00
dc00b59733 Change matchit plugin to use HTML patterns in unrecognized files 2022-10-24 02:11:31 +02:00
bc20e6af9c Fix vim-surround not working with multiple cursors
Fixes multiple cursors with vim-surround commands `cs, ds, S` (but not `ys`).

 Conflicts:
	src/main/java/com/maddyhome/idea/vim/extension/surround/VimSurroundExtension.kt
2022-10-24 02:09:43 +02:00
c40f07714a Add VimScript 'renaming()' function 2022-10-24 02:04:59 +02:00
fa68842c2d Add support for repeatable actions with ':raction' 2022-10-24 02:04:59 +02:00
eca9258607 Disable taking over arrow keys and Home/End 2022-10-24 02:04:26 +02:00
46fb030977 Set custom plugin version 2022-10-24 02:04:25 +02:00
Alex Plate
da3d83ecc6 Update formatting 2022-10-23 00:26:59 +00:00
filipp
4af8e574c4 Log keystrokes only in ASCII 2022-10-19 14:59:09 +03:00
Alex Plate
bdcb5c4f33 Update colors 2022-10-18 16:56:43 +03:00
Alex Plate
013f7a42c2 [VIM-2774] Move visual toggle mode to another handler 2022-10-18 16:38:40 +03:00
Alex Plate
d03398f3e8 [VIM-2774] Move reset mode to another handler 2022-10-18 16:38:40 +03:00
Alex Plate
7a26307a2b [VIM-2774] Move caret swap to runForEachCaret 2022-10-18 16:38:40 +03:00
Alex Plate
fa6a0369b8 [VIM-2774] Remove runforEachCaret for other method 2022-10-18 16:38:40 +03:00
Alex Plate
ad8cb0ba09 [VIM-2774] Add conditional multicaret handler 2022-10-18 16:38:39 +03:00
Alex Plate
8125ce5072 Update changelog 2022-10-18 08:34:00 +00:00
Alex Plate
6c0cc7285f Fix(VIM-2766): Move NERDTree update to background thread 2022-10-18 11:31:15 +03:00
Alex Plate
d3424021c8 Fix(VIM-2768): Refactor listeners 2022-10-17 17:39:40 +03:00
Alex Plate
623aa40acd Update to java 17 2022-10-17 15:45:15 +03:00
Alex Plate
c131cb338e Update description in plugin.xml 2022-10-17 15:45:14 +03:00
filipp
14d242a233 Disable logging undefined handlers 2022-10-17 14:56:16 +03:00
Alex Plate
a131b7d29a Update IdeaVim motto 2022-10-17 12:35:05 +03:00
Alex Plate
85a1fbe89e Update links to github accounts 2022-10-11 09:23:07 +03:00
Alex Plate
142550a1f8 Use 2022.2.2 version of IJ by default 2022-10-04 09:48:35 +03:00
Alex Plate
e3d3b73903 Fix incompatiility with futur versions of IJ 2022-10-04 09:48:18 +03:00
Alex Plate
45f18ff91c Update formatting 2022-10-02 00:26:29 +00:00
Alex Pláte
2103163207 Update AUTHORS information 2022-09-29 09:10:19 +03:00
filipp
19dd49670c Statistic 2022-09-26 11:51:56 +03:00
Alex Plate
e738a1a821 Update formatting 2022-09-25 00:20:49 +00:00
filipp
6e0f301fb8 Fix(VIM-2757) Wrong gv behavior 2022-09-22 04:08:39 +03:00
filipp
c76b8db293 Fix(VIM-2718) Copied value is not inserted 2022-09-21 03:00:53 +03:00
Alex Plate
9fa4ca8fb3 Update formatting 2022-09-18 00:20:25 +00:00
Alex Plate
871b60fe8d Revert "[New Typing Handler]: Switch j command to new typing handler"
This reverts commit 43a79dba
2022-09-12 09:30:22 +03:00
Alex Plate
6715a5b61f Revert "[New Typing Handler]: Support d"
This reverts commit c32c62ea
2022-09-12 09:29:46 +03:00
Alex Plate
d7d91f1cc5 Add some tests for dd 2022-09-12 09:28:27 +03:00
Alex Plate
9f00dbd6f4 Update formatting 2022-09-11 00:19:01 +00:00
Alex Plate
f95cf3d671 Update changelog 2022-09-10 21:26:01 +00:00
filipp
7fbc17624f Fix(VIM-2718): Fixed case where the primary caret was changed 2022-09-11 00:24:06 +03:00
Alex Plate
b9c2ea37cb Update changelog 2022-09-09 14:21:51 +00:00
Alex Plate
ca0db15e01 Fix(VIM-2749): Fix :tabn and :tabN commands 2022-09-09 17:20:09 +03:00
Alex Plate
c32c62eacc [New Typing Handler]: Support d 2022-09-06 13:13:31 +03:00
Alex Plate
43a79dbad4 [New Typing Handler]: Switch j command to new typing handler 2022-09-05 15:22:10 +03:00
Alex Plate
2829a13187 Update gradle plugin 2022-09-05 12:32:08 +03:00
Alex Plate
efc8c9207d Update changelog 2022-08-29 06:37:12 +00:00
Alex Plate
183ed10592 Fix(VIM-2744): Fix undo from ex line 2022-08-29 09:31:55 +03:00
Alex Plate
926b47a31e Update gradle plugin 2022-08-29 09:31:34 +03:00
Alex Plate
d272c919ea Fill missing authors 2022-08-25 11:59:16 +03:00
Alex Plate
f6e7d04fd5 Fix accessing bookmarks
Tests were failing because IdeaVim group is created by default by project name
2022-08-25 11:59:05 +03:00
Alex Plate
ccdff4f087 TC: Fix missing import 2022-08-25 11:23:22 +03:00
Alex Plate
ff14303e88 TC: add tests for stable version of idea 2022-08-25 11:12:50 +03:00
Alex Plate
48a592340b Update test fixture 2022-08-23 15:27:00 +03:00
Alex Plate
da8f5f3231 Fix handler call from put command 2022-08-23 15:26:58 +03:00
Alex Plate
f8fa8b73fa Move some methods to vim engine 2022-08-23 15:26:55 +03:00
Alex Plate
aee126b625 Formatting 2022-08-23 15:26:53 +03:00
Alex Plate
396ac86939 Fix strange issue on ideavimrc reload 2022-08-23 15:26:50 +03:00
Alex Plate
81816f903f Update formatting 2022-08-21 00:17:52 +00:00
Alex Plate
06a85b784b Update changelog 2022-08-17 07:14:42 +00:00
Alex Plate
7f1e3bb155 Fix(VIM-1758): Commentary plugin in rider 2022-08-17 10:13:04 +03:00
Alex Plate
241f554133 Fix(VIM-1903): Autoindent now works in rider 2022-08-17 10:11:19 +03:00
Alex Plate
9498d0779c Revert "Refactoring IdeaVim to use editor actions instead of registering shortcuts in the editor"
This reverts commit b12fd5100f.
2022-08-15 18:59:35 +03:00
Alex Plate
b12fd5100f Refactoring IdeaVim to use editor actions instead of registering shortcuts in the editor 2022-08-15 18:42:32 +03:00
Alex Plate
92f622430d Revert "Add a hidden gem"
This reverts commit 362b9a5c3a.
2022-08-15 17:22:31 +03:00
Alex Plate
ef518f5b23 Revert "Rename hidden gem to neovim"
This reverts commit 4fd1a25557.
2022-08-15 17:22:31 +03:00
Alex Plate
7acb17ebdb Small update 2022-08-15 17:22:31 +03:00
Alex Plate
479a7dbbaf Update formatting 2022-08-14 00:17:52 +00:00
Alex Plate
4fd1a25557 Rename hidden gem to neovim 2022-08-12 12:14:17 +03:00
Alex Plate
f32d42e625 Nice knowledge popup 2022-08-12 12:10:57 +03:00
Alex Plate
362b9a5c3a Add a hidden gem 2022-08-12 10:49:55 +03:00
78 changed files with 1113 additions and 742 deletions

View File

@@ -6,6 +6,7 @@ import _Self.buildTypes.Nvim
import _Self.buildTypes.PluginVerifier import _Self.buildTypes.PluginVerifier
import _Self.buildTypes.PropertyBased import _Self.buildTypes.PropertyBased
import _Self.buildTypes.Qodana import _Self.buildTypes.Qodana
import _Self.buildTypes.TestsForIntelliJ20222
import _Self.buildTypes.TestsForIntelliJEAP import _Self.buildTypes.TestsForIntelliJEAP
import _Self.subprojects.GitHub import _Self.subprojects.GitHub
import _Self.subprojects.OldTests import _Self.subprojects.OldTests
@@ -39,6 +40,7 @@ object Project : Project({
// Builds // Builds
buildType(TestsForIntelliJEAP) buildType(TestsForIntelliJEAP)
buildType(TestsForIntelliJ20222)
buildType(PropertyBased) buildType(PropertyBased)
buildType(LongRunning) buildType(LongRunning)

View File

@@ -55,4 +55,4 @@ sealed class ActiveTests(buildName: String, ijVersion: String) : BuildType({
}) })
object TestsForIntelliJEAP : ActiveTests("Tests for IntelliJ Latest EAP", "LATEST-EAP-SNAPSHOT") object TestsForIntelliJEAP : ActiveTests("Tests for IntelliJ Latest EAP", "LATEST-EAP-SNAPSHOT")
//object TestsForIntelliJ20213 : ActiveTests("Tests for IntelliJ 2021.3", "2021.3.2") object TestsForIntelliJ20222 : ActiveTests("Tests for IntelliJ 2022.2", "2022.2.1")

View File

@@ -32,16 +32,48 @@ Contributors:
[![icon][github]](https://github.com/yole) [![icon][github]](https://github.com/yole)
   
Dmitry Jemerov Dmitry Jemerov
* [![icon][mail]](mailto:tony.kay@gmail.com)
[![icon][github]](https://github.com/awkay)
 
Tony Kay
* [![icon][mail]](mailto:jamescmartinez@gmail.com)
[![icon][github]](https://github.com/jamescmartinez)
 
James Martinez
* [![icon][mail]](mailto:almas337519@gmail.com)
[![icon][github]](https://github.com/strogiyotec)
 
strogiyotec
* [![icon][mail]](mailto:raimon49@hotmail.com)
[![icon][github]](https://github.com/raimon49)
 
raimon
* [![icon][mail]](mailto:agrsbm@gmail.com)
[![icon][github-off]](#)
 
Alexander Griesbaum
* [![icon][mail]](mailto:manwe64@gmail.com)
[![icon][github]](https://github.com/baldrs)
 
Baldrs
* [![icon][mail]](mailto:yury@shurup.com)
[![icon][github]](https://github.com/zyv)
 
Yury V. Zaytsev
* [![icon][mail]](mailto:jflorian@doubledog.org)
[![icon][github]](https://github.com/jflorian)
 
John Florian
* [![icon][mail]](mailto:marquis@marquiswang.com) * [![icon][mail]](mailto:marquis@marquiswang.com)
[![icon][github]](https://github.com/marquiswang) [![icon][github]](https://github.com/marquiswang)
   
Marquis Wang Marquis Wang
* [![icon][mail]](mailto:madgnome@gmail.com) * [![icon][mail]](mailto:madgnome@gmail.com)
[![icon][github-off]](#) [![icon][github]](https://github.com/madgnome)
   
Julien Hoarau Julien Hoarau
* [![icon][mail]](mailto:masanobu.imai@gmail.com) * [![icon][mail]](mailto:masanobu.imai@gmail.com)
[![icon][github-off]](#) [![icon][github]](https://github.com/masanobuimai)
   
Masanobu Imai Masanobu Imai
* [![icon][mail]](mailto:poxvuibr@gmail.com) * [![icon][mail]](mailto:poxvuibr@gmail.com)
@@ -57,7 +89,7 @@ Contributors:
   
John Lindquist John Lindquist
* [![icon][mail]](mailto:iklotzko@ltech.com) * [![icon][mail]](mailto:iklotzko@ltech.com)
[![icon][github-off]](#) [![icon][github]](https://github.com/iklotzko)
   
Ira Klotzko Ira Klotzko
* [![icon][mail]](mailto:alex@selesse.com) * [![icon][mail]](mailto:alex@selesse.com)
@@ -65,7 +97,7 @@ Contributors:
   
Alex Selesse Alex Selesse
* [![icon][mail]](mailto:dbennett@palantir.com) * [![icon][mail]](mailto:dbennett@palantir.com)
[![icon][github-off]](#) [![icon][github]](https://github.com/dathanb)
   
Dathan Bennett Dathan Bennett
* [![icon][mail]](mailto:kphayen@gmail.com) * [![icon][mail]](mailto:kphayen@gmail.com)
@@ -77,11 +109,11 @@ Contributors:
   
Alexey Shmalko Alexey Shmalko
* [![icon][mail]](mailto:a.m.brookins@gmail.com) * [![icon][mail]](mailto:a.m.brookins@gmail.com)
[![icon][github-off]](#) [![icon][github]](https://github.com/abrookins)
   
Andrew Brookins Andrew Brookins
* [![icon][mail]](mailto:changwang83@gmail.com) * [![icon][mail]](mailto:changwang83@gmail.com)
[![icon][github-off]](#) [![icon][github]](https://github.com/changwang)
   
Chang Wang Chang Wang
* [![icon][mail]](mailto:josejaime.sanchez@gmail.com) * [![icon][mail]](mailto:josejaime.sanchez@gmail.com)
@@ -89,19 +121,19 @@ Contributors:
   
Jaime Sanchez Jaime Sanchez
* [![icon][mail]](mailto:thomas@homburg.dk) * [![icon][mail]](mailto:thomas@homburg.dk)
[![icon][github-off]](#) [![icon][github]](https://github.com/homburg)
   
Thomas B Homburg Thomas B Homburg
* [![icon][mail]](mailto:smartbomb@server.fake) * [![icon][mail]](mailto:smartbomb@server.fake)
[![icon][github-off]](#) [![icon][github]](https://github.com/smartbomb)
   
smartbomb smartbomb
* [![icon][mail]](mailto:tuomas.tynkkynen@iki.fi) * [![icon][mail]](mailto:tuomas.tynkkynen@iki.fi)
[![icon][github-off]](#) [![icon][github]](https://github.com/dezgeg)
   
Tuomas Tynkkynen Tuomas Tynkkynen
* [![icon][mail]](mailto:jackson@donorschoose.org) * [![icon][mail]](mailto:jackson@donorschoose.org)
[![icon][github-off]](#) [![icon][github]](https://github.com/jdpopkin)
   
Jackson Popkin Jackson Popkin
* [![icon][mail]](mailto:yuyuyu1999@gmail.com) * [![icon][mail]](mailto:yuyuyu1999@gmail.com)
@@ -109,7 +141,7 @@ Contributors:
   
Teruo Kunihiro Teruo Kunihiro
* [![icon][mail]](mailto:lubashka.994@mail.ru) * [![icon][mail]](mailto:lubashka.994@mail.ru)
[![icon][github-off]](#) [![icon][github]](https://github.com/lubba)
   
Liubov Paina Liubov Paina
* [![icon][mail]](mailto:me@dhleong.net) * [![icon][mail]](mailto:me@dhleong.net)
@@ -137,7 +169,7 @@ Contributors:
   
tieTYT tieTYT
* [![icon][mail]](mailto:nickgieschen@gmail.com) * [![icon][mail]](mailto:nickgieschen@gmail.com)
[![icon][github-off]](#) [![icon][github]](https://github.com/nickgieschen)
   
Nick Gieschen Nick Gieschen
* [![icon][mail]](mailto:ikenox@gmail.com) * [![icon][mail]](mailto:ikenox@gmail.com)
@@ -149,7 +181,7 @@ Contributors:
   
Maximilian Luz Maximilian Luz
* [![icon][mail]](mailto:vparfinenko@excelsior-usa.com) * [![icon][mail]](mailto:vparfinenko@excelsior-usa.com)
[![icon][github-off]](#) [![icon][github]](https://github.com/cypok)
   
Vladimir Parfinenko Vladimir Parfinenko
* [![icon][mail]](mailto:hassmann@hwdev.de) * [![icon][mail]](mailto:hassmann@hwdev.de)
@@ -193,7 +225,7 @@ Contributors:
   
Marcel Hild Marcel Hild
* [![icon][mail]](mailto:vedranb@gmail.com) * [![icon][mail]](mailto:vedranb@gmail.com)
[![icon][github-off]](#) [![icon][github]](https://github.com/vedran)
   
Vedran Budimcic Vedran Budimcic
* [![icon][mail]](mailto:andreigasparovici1@gmail.com) * [![icon][mail]](mailto:andreigasparovici1@gmail.com)
@@ -208,10 +240,13 @@ Contributors:
[![icon][github]](https://github.com/TonyArra) [![icon][github]](https://github.com/TonyArra)
   
Tony Arra Tony Arra
* [![icon][mail]](mailto:bradziolko@gmail.com) * [![icon][mail]](mailto:mj@ziolko.dev)
[![icon][github]](https://github.com/bradziolko) [![icon][github]](https://github.com/mjziolko)
   
Brad Ziolko Madeline Ziolko
[Original contribution from:
[![icon][mail]](mailto:bradziolko@gmail.com)
[![icon][github]](https://github.com/bradziolko)]
* [![icon][mail]](mailto:sumoooru2@gmail.com) * [![icon][mail]](mailto:sumoooru2@gmail.com)
[![icon][github]](https://github.com/sumoooru2) [![icon][github]](https://github.com/sumoooru2)
   

View File

@@ -23,6 +23,17 @@ It is important to distinguish EAP from traditional pre-release software.
Please note that the quality of EAP versions may at times be way below even Please note that the quality of EAP versions may at times be way below even
usual beta standards. usual beta standards.
## To Be Released
### Fixes:
* [VIM-1758](https://youtrack.jetbrains.com/issue/VIM-1758) Commentary plugin in rider
* [VIM-1903](https://youtrack.jetbrains.com/issue/VIM-1903) Autoindent now works in rider
* [VIM-2744](https://youtrack.jetbrains.com/issue/VIM-2744) Fix undo from ex line
* [VIM-2749](https://youtrack.jetbrains.com/issue/VIM-2749) Fix :tabn and :tabN commands
* [VIM-2718](https://youtrack.jetbrains.com/issue/VIM-2718) Fixed case where the primary caret was changed
* [VIM-2766](https://youtrack.jetbrains.com/issue/VIM-2766) Move NERDTree update to background thread
* [VIM-2768](https://youtrack.jetbrains.com/issue/VIM-2768) Refactor listeners
## 1.11.0, 2022-08-09 ## 1.11.0, 2022-08-09
### Features: ### Features:

View File

@@ -11,7 +11,7 @@ IdeaVim
[![Gitter][gitter-svg]][gitter] [![Gitter][gitter-svg]][gitter]
[![Twitter][twitter-svg]][twitter] [![Twitter][twitter-svg]][twitter]
IdeaVim is a Vim emulation plugin for IntelliJ Platform-based IDEs. IdeaVim is a Vim engine for JetBrains IDEs.
##### Contact maintainers: ##### Contact maintainers:
* [Bug tracker](https://youtrack.jetbrains.com/issues/VIM) * [Bug tracker](https://youtrack.jetbrains.com/issues/VIM)
@@ -36,7 +36,7 @@ Setup
- IdeaVim can be installed via `Settings | Plugins`. - IdeaVim can be installed via `Settings | Plugins`.
See the [detailed instructions](https://www.jetbrains.com/help/idea/managing-plugins.html#). See the [detailed instructions](https://www.jetbrains.com/help/idea/managing-plugins.html#).
- Use `Tools | Vim Emulator` in the menu to enable or disable emulation. - Use `Tools | Vim` in the menu to enable or disable vim.
- Use the `~/.ideavimrc` file as an analog of `~/.vimrc` ([learn more](#Files)). The XDG standard is supported, as well. - Use the `~/.ideavimrc` file as an analog of `~/.vimrc` ([learn more](#Files)). The XDG standard is supported, as well.
@@ -88,7 +88,7 @@ Here are some examples of supported vim features and commands:
* Vim web help * Vim web help
* `~/.ideavimrc` configuration file * `~/.ideavimrc` configuration file
[Emulated Vim plugins](https://github.com/JetBrains/ideavim/wiki/Emulated-plugins): [IdeaVim plugins](https://github.com/JetBrains/ideavim/wiki/Emulated-plugins):
* vim-easymotion * vim-easymotion
* NERDTree * NERDTree
@@ -198,7 +198,7 @@ Alternatively, you can set up initialization commands using [XDG](https://specif
Put your settings to `$XDG_CONFIG_HOME/ideavim/ideavimrc` file. Put your settings to `$XDG_CONFIG_HOME/ideavim/ideavimrc` file.
Emulated Vim Plugins IdeaVim Plugins
-------------------- --------------------
See [doc/emulated-plugins.md](https://github.com/JetBrains/ideavim/wiki/Emulated-plugins) See [doc/emulated-plugins.md](https://github.com/JetBrains/ideavim/wiki/Emulated-plugins)

View File

@@ -26,7 +26,7 @@ plugins {
java java
kotlin("jvm") version "1.6.21" kotlin("jvm") version "1.6.21"
id("org.jetbrains.intellij") version "1.8.0" id("org.jetbrains.intellij") version "1.10.0-SNAPSHOT"
id("org.jetbrains.changelog") version "1.3.1" id("org.jetbrains.changelog") version "1.3.1"
// ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle // ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle
@@ -166,6 +166,18 @@ tasks {
} }
} }
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(javaVersion))
}
}
kotlin {
jvmToolchain {
(this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(javaVersion))
}
}
gradle.projectsEvaluated { gradle.projectsEvaluated {
tasks.compileJava { tasks.compileJava {
// options.compilerArgs.add("-Werror") // options.compilerArgs.add("-Werror")
@@ -335,8 +347,15 @@ tasks.register("slackNotification") {
} }
} }
// --- Update authors // Uncomment to enable FUS testing mode
// tasks {
// withType<org.jetbrains.intellij.tasks.RunIdeTask> {
// jvmArgs("-Didea.is.internal=true")
// jvmArgs("-Dfus.internal.test.mode=true")
// }
// }
// --- Update authors
tasks.register("updateAuthors") { tasks.register("updateAuthors") {
doLast { doLast {
val uncheckedEmails = setOf( val uncheckedEmails = setOf(

View File

@@ -1,14 +1,13 @@
# suppress inspection "UnusedProperty" for whole file # suppress inspection "UnusedProperty" for whole file
ideaVersion=LATEST-EAP-SNAPSHOT ideaVersion=2022.2.2
downloadIdeaSources=true downloadIdeaSources=true
instrumentPluginCode=true instrumentPluginCode=true
version=SNAPSHOT version=chylex-13
javaVersion=11 javaVersion=17
remoteRobotVersion=0.11.15 remoteRobotVersion=0.11.15
antlrVersion=4.10.1 antlrVersion=4.10.1
# Please don't forget to update kotlin version in buildscript section # Please don't forget to update kotlin version in buildscript section
kotlinVersion=1.6.21 kotlinVersion=1.6.21
publishToken=token publishToken=token

View File

@@ -27,6 +27,7 @@ import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.actionSystem.TypedAction; import com.intellij.openapi.editor.actionSystem.TypedAction;
import com.intellij.openapi.editor.actionSystem.TypedActionHandler; import com.intellij.openapi.editor.actionSystem.TypedActionHandler;
import com.intellij.openapi.editor.event.*; import com.intellij.openapi.editor.event.*;
import com.intellij.openapi.util.Disposer;
import com.maddyhome.idea.vim.helper.HandlerInjector; import com.maddyhome.idea.vim.helper.HandlerInjector;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -101,32 +102,41 @@ public class EventFacade {
EditorFactory.getInstance().addEditorFactoryListener(listener, parentDisposable); EditorFactory.getInstance().addEditorFactoryListener(listener, parentDisposable);
} }
public void addEditorMouseListener(@NotNull Editor editor, @NotNull EditorMouseListener listener) { public void addEditorMouseListener(@NotNull Editor editor,
editor.addEditorMouseListener(listener); @NotNull EditorMouseListener listener,
@NotNull Disposable disposable) {
editor.addEditorMouseListener(listener, disposable);
} }
public void removeEditorMouseListener(@NotNull Editor editor, @NotNull EditorMouseListener listener) { public void removeEditorMouseListener(@NotNull Editor editor, @NotNull EditorMouseListener listener) {
editor.removeEditorMouseListener(listener); editor.removeEditorMouseListener(listener);
} }
public void addComponentMouseListener(@NotNull Component component, @NotNull MouseListener mouseListener) { public void addComponentMouseListener(@NotNull Component component,
@NotNull MouseListener mouseListener,
@NotNull Disposable disposable) {
component.addMouseListener(mouseListener); component.addMouseListener(mouseListener);
Disposer.register(disposable, () -> component.removeMouseListener(mouseListener));
} }
public void removeComponentMouseListener(@NotNull Component component, @NotNull MouseListener mouseListener) { public void removeComponentMouseListener(@NotNull Component component, @NotNull MouseListener mouseListener) {
component.removeMouseListener(mouseListener); component.removeMouseListener(mouseListener);
} }
public void addEditorMouseMotionListener(@NotNull Editor editor, @NotNull EditorMouseMotionListener listener) { public void addEditorMouseMotionListener(@NotNull Editor editor,
editor.addEditorMouseMotionListener(listener); @NotNull EditorMouseMotionListener listener,
@NotNull Disposable disposable) {
editor.addEditorMouseMotionListener(listener, disposable);
} }
public void removeEditorMouseMotionListener(@NotNull Editor editor, @NotNull EditorMouseMotionListener listener) { public void removeEditorMouseMotionListener(@NotNull Editor editor, @NotNull EditorMouseMotionListener listener) {
editor.removeEditorMouseMotionListener(listener); editor.removeEditorMouseMotionListener(listener);
} }
public void addEditorSelectionListener(@NotNull Editor editor, @NotNull SelectionListener listener) { public void addEditorSelectionListener(@NotNull Editor editor,
editor.getSelectionModel().addSelectionListener(listener); @NotNull SelectionListener listener,
@NotNull Disposable disposable) {
editor.getSelectionModel().addSelectionListener(listener, disposable);
} }
public void removeEditorSelectionListener(@NotNull Editor editor, @NotNull SelectionListener listener) { public void removeEditorSelectionListener(@NotNull Editor editor, @NotNull SelectionListener listener) {

View File

@@ -114,7 +114,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
@Override @Override
public void dispose() { public void dispose() {
LOG.debug("disposeComponent"); LOG.debug("disposeComponent");
turnOffPlugin(); turnOffPlugin(false);
LOG.debug("done"); LOG.debug("done");
} }
@@ -272,7 +272,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
if (isEnabled() == enabled) return; if (isEnabled() == enabled) return;
if (!enabled) { if (!enabled) {
getInstance().turnOffPlugin(); getInstance().turnOffPlugin(true);
} }
getInstance().enabled = enabled; getInstance().enabled = enabled;
@@ -365,12 +365,14 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
VimListenerManager.INSTANCE.turnOn(); VimListenerManager.INSTANCE.turnOn();
} }
private void turnOffPlugin() { private void turnOffPlugin(boolean unsubscribe) {
SearchGroup searchGroup = getSearchIfCreated(); SearchGroup searchGroup = getSearchIfCreated();
if (searchGroup != null) { if (searchGroup != null) {
searchGroup.turnOff(); searchGroup.turnOff();
} }
VimListenerManager.INSTANCE.turnOff(); if (unsubscribe) {
VimListenerManager.INSTANCE.turnOff();
}
ExEntryPanel.fullReset(); ExEntryPanel.fullReset();
// Unregister vim actions in command mode // Unregister vim actions in command mode

View File

@@ -153,6 +153,10 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
if (keyCode == KeyEvent.VK_TAB && editor.isTemplateActive()) return false if (keyCode == KeyEvent.VK_TAB && editor.isTemplateActive()) return false
if ((keyCode == KeyEvent.VK_TAB || keyCode == KeyEvent.VK_ENTER) && editor.appCodeTemplateCaptured()) return false if ((keyCode == KeyEvent.VK_TAB || keyCode == KeyEvent.VK_ENTER) && editor.appCodeTemplateCaptured()) return false
if (keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_RIGHT) return false
if (keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN) return false
if (keyCode == KeyEvent.VK_HOME || keyCode == KeyEvent.VK_END) return false
if (editor.inInsertMode) { if (editor.inInsertMode) {
if (keyCode == KeyEvent.VK_TAB) { if (keyCode == KeyEvent.VK_TAB) {

View File

@@ -22,6 +22,7 @@ import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
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.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.CommandFlags
@@ -40,7 +41,7 @@ import com.maddyhome.idea.vim.newapi.ij
import java.util.* import java.util.*
private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, selectionType: SelectionType): Boolean { private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, selectionType: SelectionType): Boolean {
val operatorFunction = VimPlugin.getKey().operatorFunction val operatorFunction = injector.keyGroup.operatorFunction
if (operatorFunction == null) { if (operatorFunction == null) {
VimPlugin.showMessage(MessageHelper.message("E774")) VimPlugin.showMessage(MessageHelper.message("E774"))
return false return false
@@ -49,7 +50,7 @@ private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textR
val saveRepeatHandler = VimRepeater.repeatHandler val saveRepeatHandler = VimRepeater.repeatHandler
VimPlugin.getMark().setChangeMarks(editor, textRange) VimPlugin.getMark().setChangeMarks(editor, textRange)
KeyHandler.getInstance().reset(editor) KeyHandler.getInstance().reset(editor)
val result = operatorFunction.apply(editor.ij, context.ij, selectionType) val result = operatorFunction.apply(editor, context, selectionType)
VimRepeater.repeatHandler = saveRepeatHandler VimRepeater.repeatHandler = saveRepeatHandler
return result return result
} }

View File

@@ -127,7 +127,7 @@ object VimExtensionFacade {
/** Sets the value of 'operatorfunc' to be used as the operator function in 'g@'. */ /** Sets the value of 'operatorfunc' to be used as the operator function in 'g@'. */
@JvmStatic @JvmStatic
fun setOperatorFunction(function: OperatorFunction) { fun setOperatorFunction(function: OperatorFunction) {
VimPlugin.getKey().setOperatorFunction(function) VimPlugin.getKey().operatorFunction = function
} }
/** /**
@@ -186,7 +186,7 @@ object VimExtensionFacade {
@JvmStatic @JvmStatic
fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? { fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
val reg = caret.registerStorage.getRegister(register) ?: return null val reg = caret.registerStorage.getRegister(caret, register) ?: return null
return reg.keys return reg.keys
} }
@@ -199,7 +199,7 @@ object VimExtensionFacade {
/** Set the current contents of the given register */ /** Set the current contents of the given register */
@JvmStatic @JvmStatic
fun setRegisterForCaret(register: Char, caret: VimCaret, keys: List<KeyStroke?>?) { fun setRegisterForCaret(register: Char, caret: VimCaret, keys: List<KeyStroke?>?) {
caret.registerStorage.setKeys(register, keys?.filterNotNull() ?: emptyList()) caret.registerStorage.setKeys(caret, register, keys?.filterNotNull() ?: emptyList())
} }
/** Set the current contents of the given register */ /** Set the current contents of the given register */

View File

@@ -17,10 +17,11 @@
*/ */
package com.maddyhome.idea.vim.extension.commentary package com.maddyhome.idea.vim.extension.commentary
import com.intellij.openapi.actionSystem.DataContext import com.intellij.codeInsight.actions.AsyncActionExecutionService
import com.intellij.openapi.actionSystem.IdeActions import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiComment import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
@@ -56,47 +57,65 @@ import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.key.OperatorFunction import com.maddyhome.idea.vim.key.OperatorFunction
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 java.util.* import java.util.*
class CommentaryExtension : VimExtension { class CommentaryExtension : VimExtension {
companion object { companion object {
fun doCommentary(editor: VimEditor, context: ExecutionContext, range: TextRange, selectionType: SelectionType, resetCaret: Boolean): Boolean { fun doCommentary(
editor: VimEditor,
context: ExecutionContext,
range: TextRange,
selectionType: SelectionType,
resetCaret: Boolean,
): Boolean {
val mode = editor.vimStateMachine.mode val mode = editor.vimStateMachine.mode
if (mode !== VimStateMachine.Mode.VISUAL) { if (mode !== VimStateMachine.Mode.VISUAL) {
editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset) editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset)
} }
return runWriteAction { return runWriteAction {
try { // Treat block- and character-wise selections as block comments. Be ready to fall back to if the first action
// Treat block- and character-wise selections as block comments. Be ready to fall back to if the first action // isn't available
// isn't available val actions = if (selectionType === SelectionType.LINE_WISE) {
val actions = if (selectionType === SelectionType.LINE_WISE) { listOf(IdeActions.ACTION_COMMENT_LINE, IdeActions.ACTION_COMMENT_BLOCK)
listOf(IdeActions.ACTION_COMMENT_LINE, IdeActions.ACTION_COMMENT_BLOCK) } else {
} else { listOf(IdeActions.ACTION_COMMENT_BLOCK, IdeActions.ACTION_COMMENT_LINE)
listOf(IdeActions.ACTION_COMMENT_BLOCK, IdeActions.ACTION_COMMENT_LINE)
}
injector.actionExecutor.executeAction(actions[0], context) ||
injector.actionExecutor.executeAction(actions[1], context)
} finally {
// Remove the selection, if we added it
if (mode !== VimStateMachine.Mode.VISUAL) {
editor.removeSelection()
}
// Put the caret back at the start of the range, as though it was moved by the operator's motion argument.
// This is what Vim does. If IntelliJ is configured to add comments at the start of the line, this might put
// the caret in the "wrong" place. E.g. gc_ should put the caret on the first non-whitespace character. This
// is calculated by the motion, saved in the marks, and then we insert the comment. If it's inserted at the
// first non-whitespace character, then the caret is in the right place. If it's inserted at the first column,
// then the caret is now in a bit of a weird place. We can't detect this scenario, so we just have to accept
// the difference
if (resetCaret) {
editor.primaryCaret().moveToOffset(range.startOffset)
}
} }
val res = Ref.create<Boolean>(true)
AsyncActionExecutionService.getInstance(editor.ij.project!!).withExecutionAfterAction(actions[0], {
res.set(injector.actionExecutor.executeAction(actions[0], context))
}, { afterCommenting(mode, editor, resetCaret, range) })
if (!res.get()) {
AsyncActionExecutionService.getInstance(editor.ij.project!!).withExecutionAfterAction(actions[1], {
res.set(injector.actionExecutor.executeAction(actions[1], context))
}, { afterCommenting(mode, editor, resetCaret, range) })
}
res.get()
}
}
private fun afterCommenting(
mode: VimStateMachine.Mode,
editor: VimEditor,
resetCaret: Boolean,
range: TextRange,
) {
// Remove the selection, if we added it
if (mode !== VimStateMachine.Mode.VISUAL) {
editor.removeSelection()
}
// Put the caret back at the start of the range, as though it was moved by the operator's motion argument.
// This is what Vim does. If IntelliJ is configured to add comments at the start of the line, this might put
// the caret in the "wrong" place. E.g. gc_ should put the caret on the first non-whitespace character. This
// is calculated by the motion, saved in the marks, and then we insert the comment. If it's inserted at the
// first non-whitespace character, then the caret is in the right place. If it's inserted at the first column,
// then the caret is now in a bit of a weird place. We can't detect this scenario, so we just have to accept
// the difference
if (resetCaret) {
editor.primaryCaret().moveToOffset(range.startOffset)
} }
} }
} }
@@ -112,7 +131,13 @@ class CommentaryExtension : VimExtension {
putKeyMappingIfMissing(MappingMode.NXO, injector.parser.parseKeys("gc"), owner, plugCommentaryKeys, true) putKeyMappingIfMissing(MappingMode.NXO, injector.parser.parseKeys("gc"), owner, plugCommentaryKeys, true)
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("gcc"), owner, plugCommentaryLineKeys, true) putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("gcc"), owner, plugCommentaryLineKeys, true)
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("gcu"), owner, injector.parser.parseKeys("<Plug>Commentary<Plug>Commentary"), true) putKeyMappingIfMissing(
MappingMode.N,
injector.parser.parseKeys("gcu"),
owner,
injector.parser.parseKeys("<Plug>Commentary<Plug>Commentary"),
true
)
// Previous versions of IdeaVim used different mappings to Vim's Commentary. Make sure everything works if someone // Previous versions of IdeaVim used different mappings to Vim's Commentary. Make sure everything works if someone
// is still using the old mapping // is still using the old mapping
@@ -132,14 +157,19 @@ class CommentaryExtension : VimExtension {
private class CommentaryOperatorHandler : OperatorFunction, ExtensionHandler { private class CommentaryOperatorHandler : OperatorFunction, ExtensionHandler {
override val isRepeatable = true override val isRepeatable = true
// In this operator we process selection by ourselves. This is necessary for rider, VIM-1758
override fun postProcessSelection(): Boolean {
return false
}
override fun execute(editor: VimEditor, context: ExecutionContext) { override fun execute(editor: VimEditor, context: ExecutionContext) {
setOperatorFunction(this) setOperatorFunction(this)
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij) executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
} }
override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean { override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean {
val range = VimPlugin.getMark().getChangeMarks(editor.vim) ?: return false val range = VimPlugin.getMark().getChangeMarks(editor) ?: return false
return doCommentary(editor.vim, context.vim, range, selectionType, true) return doCommentary(editor, context, range, selectionType, true)
} }
} }
@@ -174,7 +204,7 @@ class CommentaryExtension : VimExtension {
context: ExecutionContext, context: ExecutionContext,
count: Int, count: Int,
rawCount: Int, rawCount: Int,
argument: Argument? argument: Argument?,
): TextRange? { ): TextRange? {
val nativeEditor = (editor as IjVimEditor).editor val nativeEditor = (editor as IjVimEditor).editor

View File

@@ -18,7 +18,6 @@
package com.maddyhome.idea.vim.extension.exchange package com.maddyhome.idea.vim.extension.exchange
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.LogicalPosition import com.intellij.openapi.editor.LogicalPosition
@@ -133,7 +132,7 @@ class VimExchangeExtension : VimExtension {
val subMode = editor.subMode val subMode = editor.subMode
// Leave visual mode to create selection marks // Leave visual mode to create selection marks
executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij) executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
Operator(true).apply(editor.ij, context.ij, SelectionType.fromSubMode(subMode)) Operator(true).apply(editor, context, SelectionType.fromSubMode(subMode))
} }
} }
} }
@@ -147,7 +146,8 @@ class VimExchangeExtension : VimExtension {
else -> error("Invalid SubMode: $this") else -> error("Invalid SubMode: $this")
} }
override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean { override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean {
val editor = vimEditor.ij
fun highlightExchange(ex: Exchange): RangeHighlighter { fun highlightExchange(ex: Exchange): RangeHighlighter {
val attributes = editor.colorsScheme.getAttributes(EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES) val attributes = editor.colorsScheme.getAttributes(EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES)
val hlArea = when (ex.type) { val hlArea = when (ex.type) {

View File

@@ -233,7 +233,7 @@ private object FileTypePatterns {
} else if (fileTypeName == "CMakeLists.txt" || fileName == "CMakeLists") { } else if (fileTypeName == "CMakeLists.txt" || fileName == "CMakeLists") {
this.cMakePatterns this.cMakePatterns
} else { } else {
return null this.htmlPatterns
} }
} }

View File

@@ -243,7 +243,7 @@ class NerdTree : VimExtension {
e.presentation.isEnabled = !speedSearchIsHere(project) e.presentation.isEnabled = !speedSearchIsHere(project)
} }
override fun getActionUpdateThread() = ActionUpdateThread.BGT override fun getActionUpdateThread() = ActionUpdateThread.EDT
private fun speedSearchIsHere(project: Project): Boolean { private fun speedSearchIsHere(project: Project): Boolean {
val component = ProjectView.getInstance(project).currentProjectViewPane.tree ?: return false val component = ProjectView.getInstance(project).currentProjectViewPane.tree ?: return false
@@ -345,7 +345,7 @@ class NerdTree : VimExtension {
if (file.isDirectory) return@Code if (file.isDirectory) return@Code
val splitters = FileEditorManagerEx.getInstanceEx(project).splitters val splitters = FileEditorManagerEx.getInstanceEx(project).splitters
val currentWindow = splitters.currentWindow val currentWindow = splitters.currentWindow
currentWindow.split(SwingConstants.HORIZONTAL, true, file, true) currentWindow?.split(SwingConstants.HORIZONTAL, true, file, true)
} }
) )
registerCommand( registerCommand(
@@ -354,7 +354,7 @@ class NerdTree : VimExtension {
val file = event.getData(CommonDataKeys.VIRTUAL_FILE) ?: return@Code val file = event.getData(CommonDataKeys.VIRTUAL_FILE) ?: return@Code
val splitters = FileEditorManagerEx.getInstanceEx(project).splitters val splitters = FileEditorManagerEx.getInstanceEx(project).splitters
val currentWindow = splitters.currentWindow val currentWindow = splitters.currentWindow
currentWindow.split(SwingConstants.VERTICAL, true, file, true) currentWindow?.split(SwingConstants.VERTICAL, true, file, true)
// FIXME: 22.01.2021 This solution bouncing a bit // FIXME: 22.01.2021 This solution bouncing a bit
callAction("ActivateProjectToolWindow", context.vim) callAction("ActivateProjectToolWindow", context.vim)
@@ -366,7 +366,7 @@ class NerdTree : VimExtension {
val file = event.getData(CommonDataKeys.VIRTUAL_FILE) ?: return@Code val file = event.getData(CommonDataKeys.VIRTUAL_FILE) ?: return@Code
val splitters = FileEditorManagerEx.getInstanceEx(project).splitters val splitters = FileEditorManagerEx.getInstanceEx(project).splitters
val currentWindow = splitters.currentWindow val currentWindow = splitters.currentWindow
currentWindow.split(SwingConstants.HORIZONTAL, true, file, true) currentWindow?.split(SwingConstants.HORIZONTAL, true, file, true)
callAction("ActivateProjectToolWindow", context.vim) callAction("ActivateProjectToolWindow", context.vim)
} }

View File

@@ -18,7 +18,6 @@
package com.maddyhome.idea.vim.extension.replacewithregister package com.maddyhome.idea.vim.extension.replacewithregister
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
@@ -115,21 +114,22 @@ class ReplaceWithRegister : VimExtension {
} }
private class Operator : OperatorFunction { private class Operator : OperatorFunction {
override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean { override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean {
val editor = (vimEditor as IjVimEditor).editor
val range = getRange(editor) ?: return false val range = getRange(editor) ?: return false
val visualSelection = PutData.VisualSelection( val visualSelection = PutData.VisualSelection(
mapOf( mapOf(
editor.caretModel.primaryCaret.vim to VimSelection.create( vimEditor.primaryCaret() to VimSelection.create(
range.startOffset, range.startOffset,
range.endOffset - 1, range.endOffset - 1,
selectionType, selectionType,
IjVimEditor(editor) vimEditor
) )
), ),
selectionType selectionType
) )
// todo multicaret // todo multicaret
doReplace(editor, editor.vim.primaryCaret(), visualSelection) doReplace(editor, vimEditor.primaryCaret(), visualSelection)
return true return true
} }
@@ -152,7 +152,7 @@ class ReplaceWithRegister : VimExtension {
private fun doReplace(editor: Editor, caret: VimCaret, visualSelection: PutData.VisualSelection) { private fun doReplace(editor: Editor, caret: VimCaret, visualSelection: PutData.VisualSelection) {
val lastRegisterChar = injector.registerGroup.lastRegisterChar val lastRegisterChar = injector.registerGroup.lastRegisterChar
val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return val savedRegister = caret.registerStorage.getRegister(caret, lastRegisterChar) ?: return
var usedType = savedRegister.type var usedType = savedRegister.type
var usedText = savedRegister.text var usedText = savedRegister.text
@@ -185,8 +185,8 @@ class ReplaceWithRegister : VimExtension {
) )
} }
caret.registerStorage.saveRegister(savedRegister.name, savedRegister) caret.registerStorage.saveRegister(caret, savedRegister.name, savedRegister)
caret.registerStorage.saveRegister(VimPlugin.getRegister().defaultRegister, savedRegister) caret.registerStorage.saveRegister(caret, VimPlugin.getRegister().defaultRegister, savedRegister)
} }
} }
} }

View File

@@ -17,12 +17,12 @@
*/ */
package com.maddyhome.idea.vim.extension.surround package com.maddyhome.idea.vim.extension.surround
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimChangeGroup
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.MappingMode import com.maddyhome.idea.vim.command.MappingMode
@@ -40,6 +40,7 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
import com.maddyhome.idea.vim.helper.editorMode import com.maddyhome.idea.vim.helper.editorMode
import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore
import com.maddyhome.idea.vim.key.OperatorFunction import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.newapi.IjVimCaret import com.maddyhome.idea.vim.newapi.IjVimCaret
import com.maddyhome.idea.vim.newapi.IjVimEditor import com.maddyhome.idea.vim.newapi.IjVimEditor
@@ -85,22 +86,20 @@ class VimSurroundExtension : VimExtension {
override val isRepeatable = true override val isRepeatable = true
override fun execute(editor: VimEditor, context: ExecutionContext) { override fun execute(editor: VimEditor, context: ExecutionContext) {
setOperatorFunction(Operator()) setOperatorFunction(Operator(supportsMultipleCursors = false)) // TODO
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij) executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
} }
} }
private class VSurroundHandler : ExtensionHandler { private class VSurroundHandler : ExtensionHandler {
override fun execute(editor: VimEditor, context: ExecutionContext) { override fun execute(editor: VimEditor, context: ExecutionContext) {
val selectionStart = editor.ij.caretModel.primaryCaret.selectionStart
// NB: Operator ignores SelectionType anyway // NB: Operator ignores SelectionType anyway
if (!Operator().apply(editor.ij, context.ij, SelectionType.CHARACTER_WISE)) { if (!Operator(supportsMultipleCursors = true).apply(editor, context, SelectionType.CHARACTER_WISE)) {
return return
} }
runWriteAction { runWriteAction {
// Leave visual mode // Leave visual mode
executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij) executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
editor.ij.caretModel.moveToOffset(selectionStart)
} }
} }
} }
@@ -121,6 +120,10 @@ class VimSurroundExtension : VimExtension {
companion object { companion object {
fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) { fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) {
editor.ij.runWithEveryCaretAndRestore { changeAtCaret(editor, context, charFrom, newSurround) }
}
fun changeAtCaret(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) {
// Save old register values for carets // Save old register values for carets
val surroundings = editor.sortedCarets() val surroundings = editor.sortedCarets()
.map { .map {
@@ -219,25 +222,44 @@ class VimSurroundExtension : VimExtension {
} }
} }
private class Operator : OperatorFunction { private class Operator(private val supportsMultipleCursors: Boolean) : OperatorFunction {
override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean { override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean {
val editor = vimEditor.ij
val c = getChar(editor) val c = getChar(editor)
if (c.code == 0) return true if (c.code == 0) return true
val pair = getOrInputPair(c, editor) ?: return false val pair = getOrInputPair(c, editor) ?: return false
// XXX: Will it work with line-wise or block-wise selections?
val range = getSurroundRange(editor) ?: return false
runWriteAction { runWriteAction {
val change = VimPlugin.getChange() val change = VimPlugin.getChange()
val leftSurround = pair.first if (supportsMultipleCursors) {
val primaryCaret = editor.caretModel.primaryCaret editor.runWithEveryCaretAndRestore {
change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, leftSurround) applyOnce(editor, change, pair)
change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.endOffset + leftSurround.length, pair.second) }
// Jump back to start }
executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor) else {
applyOnce(editor, change, pair)
// Jump back to start
executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor)
}
} }
return true return true
} }
private fun applyOnce(editor: Editor, change: VimChangeGroup, pair: Pair<String, String>) {
// XXX: Will it work with line-wise or block-wise selections?
val range = getSurroundRange(editor)
if (range != null) {
val primaryCaret = editor.caretModel.primaryCaret
change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, pair.first)
change.insertText(
IjVimEditor(editor),
IjVimCaret(primaryCaret),
range.endOffset + pair.first.length,
pair.second
)
}
}
private fun getSurroundRange(editor: Editor): TextRange? = when (editor.editorMode) { private fun getSurroundRange(editor: Editor): TextRange? = when (editor.editorMode) {
VimStateMachine.Mode.COMMAND -> VimPlugin.getMark().getChangeMarks(editor.vim) VimStateMachine.Mode.COMMAND -> VimPlugin.getMark().getChangeMarks(editor.vim)

View File

@@ -20,7 +20,10 @@ package com.maddyhome.idea.vim.group;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.intellij.codeInsight.actions.AsyncActionExecutionService;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.UndoConfirmationPolicy; import com.intellij.openapi.command.UndoConfirmationPolicy;
@@ -33,6 +36,9 @@ import com.intellij.openapi.editor.event.EditorMouseEvent;
import com.intellij.openapi.editor.event.EditorMouseListener; import com.intellij.openapi.editor.event.EditorMouseListener;
import com.intellij.openapi.editor.impl.TextRangeInterval; import com.intellij.openapi.editor.impl.TextRangeInterval;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.CodeStyleManager;
@@ -48,13 +54,19 @@ import com.maddyhome.idea.vim.ex.ranges.LineRange;
import com.maddyhome.idea.vim.group.visual.VimSelection; import com.maddyhome.idea.vim.group.visual.VimSelection;
import com.maddyhome.idea.vim.group.visual.VisualModeHelperKt; import com.maddyhome.idea.vim.group.visual.VisualModeHelperKt;
import com.maddyhome.idea.vim.helper.*; import com.maddyhome.idea.vim.helper.*;
import com.maddyhome.idea.vim.icons.VimIcons;
import com.maddyhome.idea.vim.key.KeyHandlerKeeper; import com.maddyhome.idea.vim.key.KeyHandlerKeeper;
import com.maddyhome.idea.vim.listener.VimInsertListener; import com.maddyhome.idea.vim.listener.VimInsertListener;
import com.maddyhome.idea.vim.newapi.*; import com.maddyhome.idea.vim.newapi.IjExecutionContext;
import com.maddyhome.idea.vim.newapi.IjExecutionContextKt;
import com.maddyhome.idea.vim.newapi.IjVimCaret;
import com.maddyhome.idea.vim.newapi.IjVimEditor;
import com.maddyhome.idea.vim.options.OptionConstants; import com.maddyhome.idea.vim.options.OptionConstants;
import com.maddyhome.idea.vim.options.OptionScope; import com.maddyhome.idea.vim.options.OptionScope;
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString; import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString;
import kotlin.Pair; import kotlin.Pair;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.text.StringsKt; import kotlin.text.StringsKt;
import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -85,6 +97,8 @@ public class ChangeGroup extends VimChangeGroupBase {
private final List<VimInsertListener> insertListeners = ContainerUtil.createLockFreeCopyOnWriteList(); private final List<VimInsertListener> insertListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private long lastShownTime = 0L;
/** /**
* Inserts a new line above the caret position * Inserts a new line above the caret position
* *
@@ -142,14 +156,12 @@ public class ChangeGroup extends VimChangeGroupBase {
} }
}; };
@Override public void editorCreated(Editor editor, @NotNull Disposable disposable) {
public void editorCreated(VimEditor editor) { EventFacade.getInstance().addEditorMouseListener(editor, listener, disposable);
EventFacade.getInstance().addEditorMouseListener(((IjVimEditor) editor).getEditor(), listener);
} }
@Override public void editorReleased(Editor editor) {
public void editorReleased(VimEditor editor) { EventFacade.getInstance().removeEditorMouseListener(editor, listener);
EventFacade.getInstance().removeEditorMouseListener(((IjVimEditor) editor).getEditor(), listener);
} }
@Override @Override
@@ -571,17 +583,31 @@ public class ChangeGroup extends VimChangeGroupBase {
final int startOffset = injector.getEngineEditorHelper().getLineStartForOffset(editor, range.getStartOffset()); final int startOffset = injector.getEngineEditorHelper().getLineStartForOffset(editor, range.getStartOffset());
final int endOffset = injector.getEngineEditorHelper().getLineEndForOffset(editor, range.getEndOffset()); final int endOffset = injector.getEngineEditorHelper().getLineEndForOffset(editor, range.getEndOffset());
VisualModeHelperKt.vimSetSystemSelectionSilently(((IjVimEditor) editor).getEditor().getSelectionModel(), startOffset, endOffset); Editor ijEditor = ((IjVimEditor)editor).getEditor();
VisualModeHelperKt.vimSetSystemSelectionSilently(ijEditor.getSelectionModel(), startOffset, endOffset);
NativeAction joinLinesAction = VimInjectorKt.getInjector().getNativeActionManager().getIndentLines(); Project project = ijEditor.getProject();
if (joinLinesAction != null) { Function0<Unit> actionExecution = () -> {
VimInjectorKt.getInjector().getActionExecutor().executeAction(joinLinesAction, context); NativeAction joinLinesAction = VimInjectorKt.getInjector().getNativeActionManager().getIndentLines();
if (joinLinesAction != null) {
VimInjectorKt.getInjector().getActionExecutor().executeAction(joinLinesAction, context);
}
return null;
};
Function0<Unit> afterAction = () -> {
final int firstLine = editor.offsetToLogicalPosition(Math.min(startOffset, endOffset)).getLine();
final int newOffset = VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, firstLine);
injector.getMotion().moveCaret(editor, caret, newOffset);
restoreCursor(editor, caret, ((IjVimCaret)caret).getCaret().getLogicalPosition().line);
return null;
};
if (project != null) {
AsyncActionExecutionService.Companion.getInstance(project)
.withExecutionAfterAction(IdeActions.ACTION_EDITOR_AUTO_INDENT_LINES, actionExecution, afterAction);
} else {
actionExecution.invoke();
afterAction.invoke();
} }
final int firstLine = editor.offsetToLogicalPosition(Math.min(startOffset, endOffset)).getLine();
final int newOffset = VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, firstLine);
injector.getMotion().moveCaret(editor, caret, newOffset);
restoreCursor(editor, caret, ((IjVimCaret) caret).getCaret().getLogicalPosition().line);
} }
@Override @Override
@@ -773,6 +799,23 @@ public class ChangeGroup extends VimChangeGroupBase {
@NotNull TextRange selectedRange, @NotNull TextRange selectedRange,
final int count, final int count,
boolean avalanche) { boolean avalanche) {
// Just an easter egg
if (avalanche) {
long currentTime = System.currentTimeMillis();
if (currentTime - lastShownTime > 60_000) {
lastShownTime = currentTime;
ApplicationManager.getApplication().invokeLater(() -> {
final Balloon balloon = JBPopupFactory.getInstance()
.createHtmlTextBalloonBuilder("Wow, nice vim skills!", VimIcons.IDEAVIM,
MessageType.INFO.getTitleForeground(), MessageType.INFO.getPopupBackground(),
null).createBalloon();
balloon.show(JBPopupFactory.getInstance().guessBestPopupLocation(((IjVimEditor)editor).getEditor()),
Balloon.Position.below);
});
}
}
String nf = ((VimString) VimPlugin.getOptionService().getOptionValue(new OptionScope.LOCAL(editor), OptionConstants.nrformatsName, OptionConstants.nrformatsName)).getValue(); String nf = ((VimString) VimPlugin.getOptionService().getOptionValue(new OptionScope.LOCAL(editor), OptionConstants.nrformatsName, OptionConstants.nrformatsName)).getValue();
boolean alpha = nf.contains("alpha"); boolean alpha = nf.contains("alpha");
boolean hex = nf.contains("hex"); boolean hex = nf.contains("hex");

View File

@@ -232,7 +232,7 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
KeyHandler.getInstance().reset(new IjVimEditor(editor)); KeyHandler.getInstance().reset(new IjVimEditor(editor));
} }
updateCaretsVisualAttributes(editor); updateCaretsVisualAttributes(editor);
editor.getSettings().setRefrainFromScrolling(REFRAIN_FROM_SCROLLING_VIM_VALUE); //editor.getSettings().setRefrainFromScrolling(REFRAIN_FROM_SCROLLING_VIM_VALUE);
} }
public void editorDeinit(@NotNull Editor editor, boolean isReleased) { public void editorDeinit(@NotNull Editor editor, boolean isReleased) {

View File

@@ -73,8 +73,6 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
private static final Logger logger = Logger.getInstance(KeyGroup.class); private static final Logger logger = Logger.getInstance(KeyGroup.class);
private @Nullable OperatorFunction operatorFunction = null;
public void registerRequiredShortcutKeys(@NotNull VimEditor editor) { public void registerRequiredShortcutKeys(@NotNull VimEditor editor) {
EventFacade.getInstance() EventFacade.getInstance()
.registerCustomShortcutSet(VimShortcutKeyAction.getInstance(), toShortcutSet(getRequiredShortcutKeys()), .registerCustomShortcutSet(VimShortcutKeyAction.getInstance(), toShortcutSet(getRequiredShortcutKeys()),
@@ -119,14 +117,6 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
} }
} }
public @Nullable OperatorFunction getOperatorFunction() {
return operatorFunction;
}
public void setOperatorFunction(@NotNull OperatorFunction function) {
operatorFunction = function;
}
public void saveData(@NotNull Element element) { public void saveData(@NotNull Element element) {
final Element conflictsElement = new Element(SHORTCUT_CONFLICTS_ELEMENT); final Element conflictsElement = new Element(SHORTCUT_CONFLICTS_ELEMENT);
for (Map.Entry<KeyStroke, ShortcutOwnerInfo> entry : myShortcutConflicts.entrySet()) { for (Map.Entry<KeyStroke, ShortcutOwnerInfo> entry : myShortcutConflicts.entrySet()) {

View File

@@ -23,8 +23,10 @@ import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManagerEvent; import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
import com.intellij.openapi.fileEditor.TextEditor; import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.fileEditor.impl.EditorTabbedContainer; import com.intellij.openapi.fileEditor.impl.EditorTabbedContainer;
import com.intellij.openapi.fileEditor.impl.EditorWindow; import com.intellij.openapi.fileEditor.impl.EditorWindow;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.VirtualFileManager;
@@ -1168,15 +1170,25 @@ public class MotionGroup extends VimMotionGroupBase {
@Override @Override
public int moveCaretGotoPreviousTab(@NotNull VimEditor editor, @NotNull ExecutionContext context, int rawCount) { public int moveCaretGotoPreviousTab(@NotNull VimEditor editor, @NotNull ExecutionContext context, int rawCount) {
switchEditorTab(EditorWindow.DATA_KEY.getData((DataContext)context.getContext()), rawCount >= 1 ? -rawCount : -1, false); Project project = ((IjVimEditor)editor).getEditor().getProject();
if (project == null) {
return editor.currentCaret().getOffset().getPoint();
}
EditorWindow currentWindow = FileEditorManagerEx.getInstanceEx(project).getSplitters().getCurrentWindow();
switchEditorTab(currentWindow, rawCount >= 1 ? -rawCount : -1, false);
return editor.currentCaret().getOffset().getPoint(); return editor.currentCaret().getOffset().getPoint();
} }
@Override @Override
public int moveCaretGotoNextTab(@NotNull VimEditor editor, @NotNull ExecutionContext context, int rawCount) { public int moveCaretGotoNextTab(@NotNull VimEditor editor, @NotNull ExecutionContext context, int rawCount) {
final boolean absolute = rawCount >= 1; final boolean absolute = rawCount >= 1;
switchEditorTab(EditorWindow.DATA_KEY.getData((DataContext)context.getContext()), absolute ? rawCount - 1 : 1,
absolute); Project project = ((IjVimEditor)editor).getEditor().getProject();
if (project == null) {
return editor.currentCaret().getOffset().getPoint();
}
EditorWindow currentWindow = FileEditorManagerEx.getInstanceEx(project).getSplitters().getCurrentWindow();
switchEditorTab(currentWindow, absolute ? rawCount - 1 : 1, absolute);
return editor.currentCaret().getOffset().getPoint(); return editor.currentCaret().getOffset().getPoint();
} }

View File

@@ -92,7 +92,7 @@ public class ProcessGroup extends VimProcessGroupBase {
String initText = getRange(((IjVimEditor) editor).getEditor(), cmd); String initText = getRange(((IjVimEditor) editor).getEditor(), cmd);
VimStateMachine.getInstance(editor).pushModes(VimStateMachine.Mode.CMD_LINE, VimStateMachine.SubMode.NONE); VimStateMachine.getInstance(editor).pushModes(VimStateMachine.Mode.CMD_LINE, VimStateMachine.SubMode.NONE);
ExEntryPanel panel = ExEntryPanel.getInstance(); ExEntryPanel panel = ExEntryPanel.getInstance();
panel.activate(((IjVimEditor) editor).getEditor(), ((IjExecutionContext) context).getContext(), ":", initText, 1); panel.activate(((IjVimEditor) editor).getEditor(), ((IjExecutionContext) context).getContext(), ":", initText, cmd.getCount());
} }
@Override @Override
@@ -123,7 +123,7 @@ public class ProcessGroup extends VimProcessGroupBase {
logger.debug("processing command"); logger.debug("processing command");
final String text = panel.getText(); String text = panel.getText();
if (!panel.getLabel().equals(":")) { if (!panel.getLabel().equals(":")) {
// Search is handled via Argument.Type.EX_STRING. Although ProcessExEntryAction is registered as the handler for // Search is handled via Argument.Type.EX_STRING. Although ProcessExEntryAction is registered as the handler for
@@ -134,7 +134,15 @@ public class ProcessGroup extends VimProcessGroupBase {
if (logger.isDebugEnabled()) logger.debug("swing=" + SwingUtilities.isEventDispatchThread()); if (logger.isDebugEnabled()) logger.debug("swing=" + SwingUtilities.isEventDispatchThread());
VimInjectorKt.getInjector().getVimscriptExecutor().execute(text, editor, context, skipHistory(editor), true, CommandLineVimLContext.INSTANCE); int repeat = 1;
if (text.contains("raction ")) {
text = text.replace("raction ", "action ");
repeat = panel.getCount();
}
for (int i = 0; i < repeat; i++) {
VimInjectorKt.getInjector().getVimscriptExecutor().execute(text, editor, context, skipHistory(editor), true, CommandLineVimLContext.INSTANCE);
}
} }
catch (ExException e) { catch (ExException e) {
VimPlugin.showMessage(e.getMessage()); VimPlugin.showMessage(e.getMessage());

View File

@@ -61,7 +61,10 @@ internal fun Project.createLineBookmark(editor: Editor, line: Int, mnemonic: Cha
val type = BookmarkType.get(mnemonic) val type = BookmarkType.get(mnemonic)
if (type == BookmarkType.DEFAULT) return null if (type == BookmarkType.DEFAULT) return null
val group = bookmarksManager.defaultGroup ?: bookmarksManager.addGroup("IdeaVim", true) ?: return null val group = bookmarksManager.defaultGroup
?: bookmarksManager.getGroup("IdeaVim")
?: bookmarksManager.addGroup("IdeaVim", true)
?: return null
if (group.canAdd(bookmark)) { if (group.canAdd(bookmark)) {
group.add(bookmark, type) group.add(bookmark, type)
return bookmark return bookmark

View File

@@ -190,7 +190,12 @@ class PutGroup : VimPutBase() {
} }
} }
visualSelection.typeInEditor.isLine -> { visualSelection.typeInEditor.isLine -> {
if (caret.offset == editor.fileSize && editor.fileSize != 0) { val lastChar = if (editor.fileSize > 0) {
editor.document.getText(com.intellij.openapi.util.TextRange(editor.fileSize - 1, editor.fileSize))[0]
} else {
null
}
if (caret.offset == editor.fileSize && editor.fileSize != 0 && lastChar != '\n') {
application.runWriteAction { editor.document.insertString(caret.offset, "\n") } application.runWriteAction { editor.document.insertString(caret.offset, "\n") }
listOf(caret.offset + 1) listOf(caret.offset + 1)
} else listOf(caret.offset) } else listOf(caret.offset)

View File

@@ -211,7 +211,7 @@ class YankGroup : YankGroupBase() {
var result = true var result = true
for ((caret, myRange) in caretToRange) { for ((caret, myRange) in caretToRange) {
result = caret.registerStorage.storeText(editor, myRange, type, false) && result result = caret.registerStorage.storeText(caret, editor, myRange, type, false) && result
} }
return result return result
} }

View File

@@ -22,6 +22,7 @@ package com.maddyhome.idea.vim.helper
import com.intellij.codeWithMe.ClientId import com.intellij.codeWithMe.ClientId
import com.intellij.openapi.editor.Caret import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.CaretState
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ex.util.EditorUtil import com.intellij.openapi.editor.ex.util.EditorUtil
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
@@ -106,3 +107,41 @@ val Caret.vimLine: Int
*/ */
val Editor.vimLine: Int val Editor.vimLine: Int
get() = this.caretModel.currentCaret.vimLine get() = this.caretModel.currentCaret.vimLine
inline fun Editor.runWithEveryCaretAndRestore(action: () -> Unit) {
val caretModel = this.caretModel
val carets = if (this.inBlockSubMode) null else caretModel.allCarets
if (carets == null || carets.size == 1) {
action()
}
else {
var initialDocumentSize = this.document.textLength
var documentSizeDifference = 0
val caretOffsets = carets.map { it.selectionStart to it.selectionEnd }
val restoredCarets = mutableListOf<CaretState>()
caretModel.removeSecondaryCarets()
for ((selectionStart, selectionEnd) in caretOffsets) {
if (selectionStart == selectionEnd) {
caretModel.primaryCaret.moveToOffset(selectionStart + documentSizeDifference)
}
else {
caretModel.primaryCaret.setSelection(
selectionStart + documentSizeDifference,
selectionEnd + documentSizeDifference
)
}
action()
restoredCarets.add(caretModel.caretsAndSelections.single())
val documentLength = this.document.textLength
documentSizeDifference += documentLength - initialDocumentSize
initialDocumentSize = documentLength
}
caretModel.caretsAndSelections = restoredCarets
}
}

View File

@@ -31,6 +31,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
import com.maddyhome.idea.vim.VimPlugin; import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.api.VimSearchHelperBase;
import com.maddyhome.idea.vim.command.VimStateMachine; import com.maddyhome.idea.vim.command.VimStateMachine;
import com.maddyhome.idea.vim.common.CharacterPosition; import com.maddyhome.idea.vim.common.CharacterPosition;
import com.maddyhome.idea.vim.common.Direction; import com.maddyhome.idea.vim.common.Direction;
@@ -1201,7 +1202,7 @@ public class SearchHelper {
int last = -1; int last = -1;
int res = start; int res = start;
while (true) { while (true) {
res = findNextWordOne(chars, res, end, 1, true, false); res = (int)VimSearchHelperBase.Companion.findNextWordOne(chars, res, end, 1, true, false);
if (res == start || res == 0 || res > end || res == last) { if (res == start || res == 0 || res > end || res == last) {
break; break;
} }
@@ -1230,105 +1231,6 @@ public class SearchHelper {
return new CountPosition(count, position); return new CountPosition(count, position);
} }
public static int findNextWord(@NotNull Editor editor, int searchFrom, int count, boolean bigWord) {
CharSequence chars = editor.getDocument().getCharsSequence();
final int size = EditorHelperRt.getFileSize(editor);
return findNextWord(chars, searchFrom, size, count, bigWord, false);
}
public static int findNextWord(@NotNull CharSequence chars,
int pos,
int size,
int count,
boolean bigWord,
boolean spaceWords) {
int step = count >= 0 ? 1 : -1;
count = Math.abs(count);
int res = pos;
for (int i = 0; i < count; i++) {
res = findNextWordOne(chars, res, size, step, bigWord, spaceWords);
if (res == pos || res == 0 || res == size - 1) {
break;
}
}
return res;
}
private static int findNextWordOne(@NotNull CharSequence chars,
int pos,
int size,
int step,
boolean bigWord,
boolean spaceWords) {
boolean found = false;
pos = pos < size ? pos : Math.min(size, chars.length() - 1);
// For back searches, skip any current whitespace so we start at the end of a word
if (step < 0 && pos > 0) {
if (CharacterHelper.charType(chars.charAt(pos - 1), bigWord) == CharacterHelper.CharacterType.WHITESPACE &&
!spaceWords) {
pos = skipSpace(chars, pos - 1, step, size) + 1;
}
if (pos > 0 &&
CharacterHelper.charType(chars.charAt(pos), bigWord) !=
CharacterHelper.charType(chars.charAt(pos - 1), bigWord)) {
pos += step;
}
}
int res = pos;
if (pos < 0 || pos >= size) {
return pos;
}
CharacterHelper.CharacterType type = CharacterHelper.charType(chars.charAt(pos), bigWord);
if (type == CharacterHelper.CharacterType.WHITESPACE && step < 0 && pos > 0 && !spaceWords) {
type = CharacterHelper.charType(chars.charAt(pos - 1), bigWord);
}
pos += step;
while (pos >= 0 && pos < size && !found) {
CharacterHelper.CharacterType newType = CharacterHelper.charType(chars.charAt(pos), bigWord);
if (newType != type) {
if (newType == CharacterHelper.CharacterType.WHITESPACE && step >= 0 && !spaceWords) {
pos = skipSpace(chars, pos, step, size);
res = pos;
}
else if (step < 0) {
res = pos + 1;
}
else {
res = pos;
}
type = CharacterHelper.charType(chars.charAt(res), bigWord);
found = true;
}
pos += step;
}
if (found) {
if (res < 0) //(pos <= 0)
{
res = 0;
}
else if (res >= size) //(pos >= size)
{
res = size - 1;
}
}
else if (pos <= 0) {
res = 0;
}
else if (pos >= size) {
res = size;
}
return res;
}
public static @NotNull List<Pair<TextRange, NumberType>> findNumbersInRange(final @NotNull Editor editor, public static @NotNull List<Pair<TextRange, NumberType>> findNumbersInRange(final @NotNull Editor editor,
@NotNull TextRange textRange, @NotNull TextRange textRange,
final boolean alpha, final boolean alpha,
@@ -1654,10 +1556,10 @@ public class SearchHelper {
if ((!onWordStart && !(startSpace && isOuter)) || hasSelection || (count > 1 && dir == -1)) { if ((!onWordStart && !(startSpace && isOuter)) || hasSelection || (count > 1 && dir == -1)) {
if (dir == 1) { if (dir == 1) {
start = findNextWord(chars, pos, max, -1, isBig, !isOuter); start = (int)VimSearchHelperBase.Companion.findNextWord(chars, pos, max, -1, isBig, !isOuter);
} }
else { else {
start = findNextWord(chars, pos, max, -(count - (onWordStart && !hasSelection ? 1 : 0)), isBig, !isOuter); start = (int)VimSearchHelperBase.Companion.findNextWord(chars, pos, max, -(count - (onWordStart && !hasSelection ? 1 : 0)), isBig, !isOuter);
} }
start = EditorHelper.normalizeOffset(editor, start, false); start = EditorHelper.normalizeOffset(editor, start, false);
@@ -1805,7 +1707,7 @@ public class SearchHelper {
if (step > 0 && pos < size - 1) { if (step > 0 && pos < size - 1) {
if (CharacterHelper.charType(chars.charAt(pos + 1), bigWord) == CharacterHelper.CharacterType.WHITESPACE && if (CharacterHelper.charType(chars.charAt(pos + 1), bigWord) == CharacterHelper.CharacterType.WHITESPACE &&
!spaceWords) { !spaceWords) {
pos = skipSpace(chars, pos + 1, step, size) - 1; pos = (int)(VimSearchHelperBase.Companion.skipSpace(chars, pos + 1, step, size) - 1);
} }
if (pos < size - 1 && if (pos < size - 1 &&
CharacterHelper.charType(chars.charAt(pos), bigWord) != CharacterHelper.charType(chars.charAt(pos), bigWord) !=
@@ -1830,7 +1732,7 @@ public class SearchHelper {
res = pos - 1; res = pos - 1;
} }
else if (newType == CharacterHelper.CharacterType.WHITESPACE && step < 0 && !spaceWords) { else if (newType == CharacterHelper.CharacterType.WHITESPACE && step < 0 && !spaceWords) {
pos = skipSpace(chars, pos, step, size); pos = (int)VimSearchHelperBase.Companion.skipSpace(chars, pos, step, size);
res = pos; res = pos;
} }
else { else {
@@ -1858,34 +1760,6 @@ public class SearchHelper {
return res; return res;
} }
/**
* Skip whitespace starting with the supplied position.
* <p>
* An empty line is considered a whitespace break.
*
* @param chars The text as a character array
* @param offset The starting position
* @param step The direction to move
* @param size The size of the document
* @return The new position. This will be the first non-whitespace character found or an empty line
*/
private static int skipSpace(@NotNull CharSequence chars, int offset, int step, int size) {
char prev = 0;
while (offset >= 0 && offset < size) {
final char c = chars.charAt(offset);
if (c == '\n' && c == prev) {
break;
}
if (CharacterHelper.charType(c, false) != CharacterHelper.CharacterType.WHITESPACE) {
break;
}
prev = c;
offset += step;
}
return offset < size ? offset : size - 1;
}
/** /**
* This locates the position with the document of the count-th occurrence of ch on the current line * This locates the position with the document of the count-th occurrence of ch on the current line
* *

View File

@@ -25,6 +25,7 @@ import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.command.impl.UndoManagerImpl import com.intellij.openapi.command.impl.UndoManagerImpl
import com.intellij.openapi.command.undo.UndoManager import com.intellij.openapi.command.undo.UndoManager
import com.intellij.openapi.components.Service import com.intellij.openapi.components.Service
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.ChangesListener import com.maddyhome.idea.vim.common.ChangesListener
@@ -48,16 +49,17 @@ class UndoRedoHelper : UndoRedoBase() {
override fun undo(context: ExecutionContext): Boolean { override fun undo(context: ExecutionContext): Boolean {
val ijContext = context.context as DataContext val ijContext = context.context as DataContext
val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false
val fileEditor = PlatformDataKeys.FILE_EDITOR.getData(ijContext) val editor = CommonDataKeys.EDITOR.getData(context.ij) ?: return false
val vimEditor = editor.vim
val fileEditor = TextEditorProvider.getInstance().getTextEditor(editor)
val undoManager = UndoManager.getInstance(project) val undoManager = UndoManager.getInstance(project)
if (fileEditor != null && undoManager.isUndoAvailable(fileEditor)) { if (undoManager.isUndoAvailable(fileEditor)) {
val editor = CommonDataKeys.EDITOR.getData(context.ij)?.vim
if (injector.optionService.isSet(OptionScope.GLOBAL, IjVimOptionService.oldUndo)) { if (injector.optionService.isSet(OptionScope.GLOBAL, IjVimOptionService.oldUndo)) {
SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) } SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) }
} else { } else {
performUntilFileChanges(editor, { undoManager.isUndoAvailable(fileEditor) }, { undoManager.undo(fileEditor) }) performUntilFileChanges(vimEditor, { undoManager.isUndoAvailable(fileEditor) }, { undoManager.undo(fileEditor) })
editor?.carets()?.forEach { vimEditor.carets().forEach {
val ijCaret = it.ij val ijCaret = it.ij
val hasSelection = ijCaret.hasSelection() val hasSelection = ijCaret.hasSelection()
if (hasSelection) { if (hasSelection) {
@@ -78,16 +80,17 @@ class UndoRedoHelper : UndoRedoBase() {
override fun redo(context: ExecutionContext): Boolean { override fun redo(context: ExecutionContext): Boolean {
val ijContext = context.context as DataContext val ijContext = context.context as DataContext
val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false
val fileEditor = PlatformDataKeys.FILE_EDITOR.getData(ijContext) val editor = CommonDataKeys.EDITOR.getData(context.ij) ?: return false
val vimEditor = editor.vim
val fileEditor = TextEditorProvider.getInstance().getTextEditor(editor)
val undoManager = UndoManager.getInstance(project) val undoManager = UndoManager.getInstance(project)
val editor = CommonDataKeys.EDITOR.getData(context.ij)?.vim if (undoManager.isRedoAvailable(fileEditor)) {
if (fileEditor != null && undoManager.isRedoAvailable(fileEditor)) {
if (injector.optionService.isSet(OptionScope.GLOBAL, IjVimOptionService.oldUndo)) { if (injector.optionService.isSet(OptionScope.GLOBAL, IjVimOptionService.oldUndo)) {
SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) } SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) }
} else { } else {
performUntilFileChanges(editor, { undoManager.isRedoAvailable(fileEditor) }, { undoManager.redo(fileEditor) }) performUntilFileChanges(vimEditor, { undoManager.isRedoAvailable(fileEditor) }, { undoManager.redo(fileEditor) })
CommandProcessor.getInstance().runUndoTransparentAction { CommandProcessor.getInstance().runUndoTransparentAction {
editor?.carets()?.forEach { it.ij.removeSelection() } vimEditor.carets().forEach { it.ij.removeSelection() }
} }
} }
return true return true

View File

@@ -36,9 +36,14 @@ import com.intellij.openapi.editor.event.SelectionEvent
import com.intellij.openapi.editor.event.SelectionListener import com.intellij.openapi.editor.event.SelectionListener
import com.intellij.openapi.editor.ex.DocumentEx import com.intellij.openapi.editor.ex.DocumentEx
import com.intellij.openapi.editor.impl.EditorComponentImpl import com.intellij.openapi.editor.impl.EditorComponentImpl
import com.intellij.openapi.editor.impl.EditorImpl
import com.intellij.openapi.fileEditor.FileEditorManagerEvent import com.intellij.openapi.fileEditor.FileEditorManagerEvent
import com.intellij.openapi.fileEditor.FileEditorManagerListener import com.intellij.openapi.fileEditor.FileEditorManagerListener
import com.intellij.openapi.rd.createLifetime
import com.intellij.openapi.rd.createNestedDisposable
import com.intellij.openapi.util.Disposer
import com.intellij.util.ExceptionUtil import com.intellij.util.ExceptionUtil
import com.jetbrains.rd.util.lifetime.intersect
import com.maddyhome.idea.vim.EventFacade import com.maddyhome.idea.vim.EventFacade
import com.maddyhome.idea.vim.KeyHandler import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimKeyListener import com.maddyhome.idea.vim.VimKeyListener
@@ -73,8 +78,6 @@ import com.maddyhome.idea.vim.helper.vimLastColumn
import com.maddyhome.idea.vim.listener.MouseEventsDataHolder.skipEvents import com.maddyhome.idea.vim.listener.MouseEventsDataHolder.skipEvents
import com.maddyhome.idea.vim.listener.MouseEventsDataHolder.skipNDragEvents import com.maddyhome.idea.vim.listener.MouseEventsDataHolder.skipNDragEvents
import com.maddyhome.idea.vim.listener.VimListenerManager.EditorListeners.add import com.maddyhome.idea.vim.listener.VimListenerManager.EditorListeners.add
import com.maddyhome.idea.vim.listener.VimListenerManager.EditorListeners.remove
import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.options.OptionConstants import com.maddyhome.idea.vim.options.OptionConstants
import com.maddyhome.idea.vim.options.OptionScope import com.maddyhome.idea.vim.options.OptionScope
@@ -153,17 +156,26 @@ object VimListenerManager {
} }
fun add(editor: Editor) { fun add(editor: Editor) {
val pluginLifetime = VimPlugin.getInstance().createLifetime()
val editorLifetime = (editor as EditorImpl).disposable.createLifetime()
val disposable = editorLifetime.intersect(pluginLifetime).createNestedDisposable("MyLifetimedDisposable")
editor.contentComponent.addKeyListener(VimKeyListener) editor.contentComponent.addKeyListener(VimKeyListener)
Disposer.register(disposable) { editor.contentComponent.removeKeyListener(VimKeyListener) }
val eventFacade = EventFacade.getInstance() val eventFacade = EventFacade.getInstance()
eventFacade.addEditorMouseListener(editor, EditorMouseHandler) eventFacade.addEditorMouseListener(editor, EditorMouseHandler, disposable)
eventFacade.addEditorMouseMotionListener(editor, EditorMouseHandler) eventFacade.addEditorMouseMotionListener(editor, EditorMouseHandler, disposable)
eventFacade.addEditorSelectionListener(editor, EditorSelectionHandler) eventFacade.addEditorSelectionListener(editor, EditorSelectionHandler, disposable)
eventFacade.addComponentMouseListener(editor.contentComponent, ComponentMouseListener) eventFacade.addComponentMouseListener(editor.contentComponent, ComponentMouseListener, disposable)
VimPlugin.getEditor().editorCreated(editor) VimPlugin.getEditor().editorCreated(editor)
VimPlugin.getChange().editorCreated(IjVimEditor(editor)) VimPlugin.getChange().editorCreated(editor, disposable)
Disposer.register(disposable) {
VimPlugin.getEditorIfCreated()?.editorDeinit(editor, true)
}
} }
fun remove(editor: Editor, isReleased: Boolean) { fun remove(editor: Editor, isReleased: Boolean) {
@@ -177,7 +189,7 @@ object VimListenerManager {
VimPlugin.getEditorIfCreated()?.editorDeinit(editor, isReleased) VimPlugin.getEditorIfCreated()?.editorDeinit(editor, isReleased)
VimPlugin.getChange().editorReleased(IjVimEditor(editor)) VimPlugin.getChange().editorReleased(editor)
} }
} }
@@ -209,7 +221,6 @@ object VimListenerManager {
} }
override fun editorReleased(event: EditorFactoryEvent) { override fun editorReleased(event: EditorFactoryEvent) {
remove(event.editor, true)
VimPlugin.getMark().editorReleased(event) VimPlugin.getMark().editorReleased(event)
} }
} }

View File

@@ -53,7 +53,7 @@ class IjVimCaret(val caret: Caret) : VimCaretBase() {
get() { get() {
var storage = this.caret.registerStorage var storage = this.caret.registerStorage
if (storage == null) { if (storage == null) {
storage = CaretRegisterStorageBase(editor.primaryCaret().ij == caret) storage = CaretRegisterStorageBase()
this.caret.registerStorage = storage this.caret.registerStorage = storage
} }
return storage return storage
@@ -87,6 +87,8 @@ class IjVimCaret(val caret: Caret) : VimCaretBase() {
} }
override val vimLine: Int override val vimLine: Int
get() = this.caret.vimLine get() = this.caret.vimLine
override val isPrimary: Boolean
get() = editor.primaryCaret().ij == this.caret
override fun moveToOffset(offset: Int) { override fun moveToOffset(offset: Int) {
// TODO: 17.12.2021 Unpack internal actions // TODO: 17.12.2021 Unpack internal actions

View File

@@ -3,14 +3,14 @@ package com.maddyhome.idea.vim.newapi
import com.intellij.openapi.components.Service import com.intellij.openapi.components.Service
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.VimSearchHelper import com.maddyhome.idea.vim.api.VimSearchHelperBase
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.helper.SearchHelper import com.maddyhome.idea.vim.helper.SearchHelper
import com.maddyhome.idea.vim.helper.SearchOptions import com.maddyhome.idea.vim.helper.SearchOptions
import java.util.* import java.util.*
@Service @Service
class IjVimSearchHelper : VimSearchHelper { class IjVimSearchHelper : VimSearchHelperBase() {
override fun findNextParagraph(editor: VimEditor, caret: VimCaret, count: Int, allowBlanks: Boolean): Int { override fun findNextParagraph(editor: VimEditor, caret: VimCaret, count: Int, allowBlanks: Boolean): Int {
return SearchHelper.findNextParagraph( return SearchHelper.findNextParagraph(
(editor as IjVimEditor).editor, (editor as IjVimEditor).editor,
@@ -121,15 +121,6 @@ class IjVimSearchHelper : VimSearchHelper {
return SearchHelper.findNextWordEnd(chars, pos, size, count, bigWord, spaceWords) return SearchHelper.findNextWordEnd(chars, pos, size, count, bigWord, spaceWords)
} }
override fun findNextWord(editor: VimEditor, searchFrom: Int, count: Int, bigWord: Boolean): Int {
return SearchHelper.findNextWord(
(editor as IjVimEditor).editor,
searchFrom,
count,
bigWord
)
}
override fun findPattern( override fun findPattern(
editor: VimEditor, editor: VimEditor,
pattern: String?, pattern: String?,

View File

@@ -21,15 +21,14 @@ package com.maddyhome.idea.vim.statistic
import com.intellij.internal.statistic.beans.MetricEvent import com.intellij.internal.statistic.beans.MetricEvent
import com.intellij.internal.statistic.eventLog.EventLogGroup import com.intellij.internal.statistic.eventLog.EventLogGroup
import com.intellij.internal.statistic.eventLog.events.EventFields import com.intellij.internal.statistic.eventLog.events.EventFields
import com.intellij.internal.statistic.eventLog.events.EventPair
import com.intellij.internal.statistic.eventLog.events.StringListEventField
import com.intellij.internal.statistic.eventLog.events.VarargEventId
import com.intellij.internal.statistic.service.fus.collectors.ApplicationUsagesCollector import com.intellij.internal.statistic.service.fus.collectors.ApplicationUsagesCollector
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.key.ShortcutOwner import com.maddyhome.idea.vim.key.ShortcutOwner
import com.maddyhome.idea.vim.key.ShortcutOwnerInfo import com.maddyhome.idea.vim.key.ShortcutOwnerInfo
import java.awt.event.InputEvent
import java.awt.event.InputEvent.CTRL_DOWN_MASK import java.awt.event.InputEvent.CTRL_DOWN_MASK
import java.awt.event.InputEvent.SHIFT_DOWN_MASK import java.awt.event.InputEvent.SHIFT_DOWN_MASK
import java.awt.event.KeyEvent
import javax.swing.KeyStroke import javax.swing.KeyStroke
internal class ShortcutConflictState : ApplicationUsagesCollector() { internal class ShortcutConflictState : ApplicationUsagesCollector() {
@@ -37,93 +36,15 @@ internal class ShortcutConflictState : ApplicationUsagesCollector() {
override fun getGroup(): EventLogGroup = GROUP override fun getGroup(): EventLogGroup = GROUP
override fun getMetrics(): Set<MetricEvent> { override fun getMetrics(): Set<MetricEvent> {
return setOf( val metrics = mutableSetOf<MetricEvent>()
HANDLERS.metric( keyStrokes.forEach { keystroke ->
CTRL_1 withKeyStroke KeyStroke.getKeyStroke('1'.code, CTRL_DOWN_MASK), getHandlersForShortcut(keystroke)
CTRL_2 withKeyStroke KeyStroke.getKeyStroke('2'.code, CTRL_DOWN_MASK), .filter { !setOf(HandledModes.INSERT_UNDEFINED, HandledModes.NORMAL_UNDEFINED, HandledModes.VISUAL_AND_SELECT_UNDEFINED).contains(it) }
CTRL_3 withKeyStroke KeyStroke.getKeyStroke('3'.code, CTRL_DOWN_MASK), .forEach { mode ->
CTRL_4 withKeyStroke KeyStroke.getKeyStroke('4'.code, CTRL_DOWN_MASK), metrics += HANDLER.metric(keystroke.toReadableString(), mode)
CTRL_5 withKeyStroke KeyStroke.getKeyStroke('5'.code, CTRL_DOWN_MASK), }
CTRL_6 withKeyStroke KeyStroke.getKeyStroke('6'.code, CTRL_DOWN_MASK), }
CTRL_7 withKeyStroke KeyStroke.getKeyStroke('7'.code, CTRL_DOWN_MASK), return metrics
CTRL_8 withKeyStroke KeyStroke.getKeyStroke('8'.code, CTRL_DOWN_MASK),
CTRL_9 withKeyStroke KeyStroke.getKeyStroke('9'.code, CTRL_DOWN_MASK),
CTRL_0 withKeyStroke KeyStroke.getKeyStroke('0'.code, CTRL_DOWN_MASK),
CTRL_SHIFT_1 withKeyStroke KeyStroke.getKeyStroke('1'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_2 withKeyStroke KeyStroke.getKeyStroke('2'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_3 withKeyStroke KeyStroke.getKeyStroke('3'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_4 withKeyStroke KeyStroke.getKeyStroke('4'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_5 withKeyStroke KeyStroke.getKeyStroke('5'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_6 withKeyStroke KeyStroke.getKeyStroke('6'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_7 withKeyStroke KeyStroke.getKeyStroke('7'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_8 withKeyStroke KeyStroke.getKeyStroke('8'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_9 withKeyStroke KeyStroke.getKeyStroke('9'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_0 withKeyStroke KeyStroke.getKeyStroke('0'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_A withKeyStroke KeyStroke.getKeyStroke('A'.code, CTRL_DOWN_MASK),
CTRL_B withKeyStroke KeyStroke.getKeyStroke('B'.code, CTRL_DOWN_MASK),
CTRL_C withKeyStroke KeyStroke.getKeyStroke('C'.code, CTRL_DOWN_MASK),
CTRL_D withKeyStroke KeyStroke.getKeyStroke('D'.code, CTRL_DOWN_MASK),
CTRL_E withKeyStroke KeyStroke.getKeyStroke('E'.code, CTRL_DOWN_MASK),
CTRL_F withKeyStroke KeyStroke.getKeyStroke('F'.code, CTRL_DOWN_MASK),
CTRL_G withKeyStroke KeyStroke.getKeyStroke('G'.code, CTRL_DOWN_MASK),
CTRL_H withKeyStroke KeyStroke.getKeyStroke('H'.code, CTRL_DOWN_MASK),
CTRL_I withKeyStroke KeyStroke.getKeyStroke('I'.code, CTRL_DOWN_MASK),
CTRL_J withKeyStroke KeyStroke.getKeyStroke('J'.code, CTRL_DOWN_MASK),
CTRL_K withKeyStroke KeyStroke.getKeyStroke('K'.code, CTRL_DOWN_MASK),
CTRL_L withKeyStroke KeyStroke.getKeyStroke('L'.code, CTRL_DOWN_MASK),
CTRL_M withKeyStroke KeyStroke.getKeyStroke('M'.code, CTRL_DOWN_MASK),
CTRL_N withKeyStroke KeyStroke.getKeyStroke('N'.code, CTRL_DOWN_MASK),
CTRL_O withKeyStroke KeyStroke.getKeyStroke('O'.code, CTRL_DOWN_MASK),
CTRL_P withKeyStroke KeyStroke.getKeyStroke('P'.code, CTRL_DOWN_MASK),
CTRL_Q withKeyStroke KeyStroke.getKeyStroke('Q'.code, CTRL_DOWN_MASK),
CTRL_R withKeyStroke KeyStroke.getKeyStroke('R'.code, CTRL_DOWN_MASK),
CTRL_S withKeyStroke KeyStroke.getKeyStroke('S'.code, CTRL_DOWN_MASK),
CTRL_T withKeyStroke KeyStroke.getKeyStroke('T'.code, CTRL_DOWN_MASK),
CTRL_U withKeyStroke KeyStroke.getKeyStroke('U'.code, CTRL_DOWN_MASK),
CTRL_V withKeyStroke KeyStroke.getKeyStroke('V'.code, CTRL_DOWN_MASK),
CTRL_W withKeyStroke KeyStroke.getKeyStroke('W'.code, CTRL_DOWN_MASK),
CTRL_X withKeyStroke KeyStroke.getKeyStroke('X'.code, CTRL_DOWN_MASK),
CTRL_Y withKeyStroke KeyStroke.getKeyStroke('Y'.code, CTRL_DOWN_MASK),
CTRL_Z withKeyStroke KeyStroke.getKeyStroke('Z'.code, CTRL_DOWN_MASK),
CTRL_BR1 withKeyStroke KeyStroke.getKeyStroke('['.code, CTRL_DOWN_MASK),
CTRL_BR2 withKeyStroke KeyStroke.getKeyStroke(']'.code, CTRL_DOWN_MASK),
CTRL_SHIFT_A withKeyStroke KeyStroke.getKeyStroke('A'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_B withKeyStroke KeyStroke.getKeyStroke('B'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_C withKeyStroke KeyStroke.getKeyStroke('C'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_D withKeyStroke KeyStroke.getKeyStroke('D'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_E withKeyStroke KeyStroke.getKeyStroke('E'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_F withKeyStroke KeyStroke.getKeyStroke('F'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_G withKeyStroke KeyStroke.getKeyStroke('G'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_H withKeyStroke KeyStroke.getKeyStroke('H'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_I withKeyStroke KeyStroke.getKeyStroke('I'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_J withKeyStroke KeyStroke.getKeyStroke('J'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_K withKeyStroke KeyStroke.getKeyStroke('K'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_L withKeyStroke KeyStroke.getKeyStroke('L'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_M withKeyStroke KeyStroke.getKeyStroke('M'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_N withKeyStroke KeyStroke.getKeyStroke('N'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_O withKeyStroke KeyStroke.getKeyStroke('O'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_P withKeyStroke KeyStroke.getKeyStroke('P'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_Q withKeyStroke KeyStroke.getKeyStroke('Q'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_R withKeyStroke KeyStroke.getKeyStroke('R'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_S withKeyStroke KeyStroke.getKeyStroke('S'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_T withKeyStroke KeyStroke.getKeyStroke('T'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_U withKeyStroke KeyStroke.getKeyStroke('U'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_V withKeyStroke KeyStroke.getKeyStroke('V'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_W withKeyStroke KeyStroke.getKeyStroke('W'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_X withKeyStroke KeyStroke.getKeyStroke('X'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_Y withKeyStroke KeyStroke.getKeyStroke('Y'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_Z withKeyStroke KeyStroke.getKeyStroke('Z'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_BR1 withKeyStroke KeyStroke.getKeyStroke('['.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
CTRL_SHIFT_BR2 withKeyStroke KeyStroke.getKeyStroke(']'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
)
)
}
private infix fun StringListEventField.withKeyStroke(ks: KeyStroke): EventPair<List<String>> {
return this.with(getHandlersForShortcut(ks).map { it.name })
} }
private fun getHandlersForShortcut(shortcut: KeyStroke): List<HandledModes> { private fun getHandlersForShortcut(shortcut: KeyStroke): List<HandledModes> {
@@ -162,173 +83,105 @@ internal class ShortcutConflictState : ApplicationUsagesCollector() {
companion object { companion object {
private val GROUP = EventLogGroup("vim.handlers", 1) private val GROUP = EventLogGroup("vim.handlers", 1)
private val values = HandledModes.values().map { it.name }
private val CTRL_1 = EventFields.StringList("Ctrl-1", values) private val keyStrokes = listOf(
private val CTRL_2 = EventFields.StringList("Ctrl-2", values) KeyStroke.getKeyStroke('1'.code, CTRL_DOWN_MASK),
private val CTRL_3 = EventFields.StringList("Ctrl-3", values) KeyStroke.getKeyStroke('2'.code, CTRL_DOWN_MASK),
private val CTRL_4 = EventFields.StringList("Ctrl-4", values) KeyStroke.getKeyStroke('3'.code, CTRL_DOWN_MASK),
private val CTRL_5 = EventFields.StringList("Ctrl-5", values) KeyStroke.getKeyStroke('4'.code, CTRL_DOWN_MASK),
private val CTRL_6 = EventFields.StringList("Ctrl-6", values) KeyStroke.getKeyStroke('5'.code, CTRL_DOWN_MASK),
private val CTRL_7 = EventFields.StringList("Ctrl-7", values) KeyStroke.getKeyStroke('6'.code, CTRL_DOWN_MASK),
private val CTRL_8 = EventFields.StringList("Ctrl-8", values) KeyStroke.getKeyStroke('7'.code, CTRL_DOWN_MASK),
private val CTRL_9 = EventFields.StringList("Ctrl-9", values) KeyStroke.getKeyStroke('8'.code, CTRL_DOWN_MASK),
private val CTRL_0 = EventFields.StringList("Ctrl-0", values) KeyStroke.getKeyStroke('9'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('0'.code, CTRL_DOWN_MASK),
private val CTRL_SHIFT_1 = EventFields.StringList("Ctrl-Shift-1", values) KeyStroke.getKeyStroke('1'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_2 = EventFields.StringList("Ctrl-Shift-2", values) KeyStroke.getKeyStroke('2'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_3 = EventFields.StringList("Ctrl-Shift-3", values) KeyStroke.getKeyStroke('3'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_4 = EventFields.StringList("Ctrl-Shift-4", values) KeyStroke.getKeyStroke('4'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_5 = EventFields.StringList("Ctrl-Shift-5", values) KeyStroke.getKeyStroke('5'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_6 = EventFields.StringList("Ctrl-Shift-6", values) KeyStroke.getKeyStroke('6'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_7 = EventFields.StringList("Ctrl-Shift-7", values) KeyStroke.getKeyStroke('7'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_8 = EventFields.StringList("Ctrl-Shift-8", values) KeyStroke.getKeyStroke('8'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_9 = EventFields.StringList("Ctrl-Shift-9", values) KeyStroke.getKeyStroke('9'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_0 = EventFields.StringList("Ctrl-Shift-0", values) KeyStroke.getKeyStroke('0'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_A = EventFields.StringList("Ctrl-A", values) KeyStroke.getKeyStroke('A'.code, CTRL_DOWN_MASK),
private val CTRL_B = EventFields.StringList("Ctrl-B", values) KeyStroke.getKeyStroke('B'.code, CTRL_DOWN_MASK),
private val CTRL_C = EventFields.StringList("Ctrl-C", values) KeyStroke.getKeyStroke('C'.code, CTRL_DOWN_MASK),
private val CTRL_D = EventFields.StringList("Ctrl-D", values) KeyStroke.getKeyStroke('D'.code, CTRL_DOWN_MASK),
private val CTRL_E = EventFields.StringList("Ctrl-E", values) KeyStroke.getKeyStroke('E'.code, CTRL_DOWN_MASK),
private val CTRL_F = EventFields.StringList("Ctrl-F", values) KeyStroke.getKeyStroke('F'.code, CTRL_DOWN_MASK),
private val CTRL_G = EventFields.StringList("Ctrl-G", values) KeyStroke.getKeyStroke('G'.code, CTRL_DOWN_MASK),
private val CTRL_H = EventFields.StringList("Ctrl-H", values) KeyStroke.getKeyStroke('H'.code, CTRL_DOWN_MASK),
private val CTRL_I = EventFields.StringList("Ctrl-I", values) KeyStroke.getKeyStroke('I'.code, CTRL_DOWN_MASK),
private val CTRL_J = EventFields.StringList("Ctrl-J", values) KeyStroke.getKeyStroke('J'.code, CTRL_DOWN_MASK),
private val CTRL_K = EventFields.StringList("Ctrl-K", values) KeyStroke.getKeyStroke('K'.code, CTRL_DOWN_MASK),
private val CTRL_L = EventFields.StringList("Ctrl-L", values) KeyStroke.getKeyStroke('L'.code, CTRL_DOWN_MASK),
private val CTRL_M = EventFields.StringList("Ctrl-M", values) KeyStroke.getKeyStroke('M'.code, CTRL_DOWN_MASK),
private val CTRL_N = EventFields.StringList("Ctrl-N", values) KeyStroke.getKeyStroke('N'.code, CTRL_DOWN_MASK),
private val CTRL_O = EventFields.StringList("Ctrl-O", values) KeyStroke.getKeyStroke('O'.code, CTRL_DOWN_MASK),
private val CTRL_P = EventFields.StringList("Ctrl-P", values) KeyStroke.getKeyStroke('P'.code, CTRL_DOWN_MASK),
private val CTRL_Q = EventFields.StringList("Ctrl-Q", values) KeyStroke.getKeyStroke('Q'.code, CTRL_DOWN_MASK),
private val CTRL_R = EventFields.StringList("Ctrl-R", values) KeyStroke.getKeyStroke('R'.code, CTRL_DOWN_MASK),
private val CTRL_S = EventFields.StringList("Ctrl-S", values) KeyStroke.getKeyStroke('S'.code, CTRL_DOWN_MASK),
private val CTRL_T = EventFields.StringList("Ctrl-T", values) KeyStroke.getKeyStroke('T'.code, CTRL_DOWN_MASK),
private val CTRL_U = EventFields.StringList("Ctrl-U", values) KeyStroke.getKeyStroke('U'.code, CTRL_DOWN_MASK),
private val CTRL_V = EventFields.StringList("Ctrl-V", values) KeyStroke.getKeyStroke('V'.code, CTRL_DOWN_MASK),
private val CTRL_W = EventFields.StringList("Ctrl-W", values) KeyStroke.getKeyStroke('W'.code, CTRL_DOWN_MASK),
private val CTRL_X = EventFields.StringList("Ctrl-X", values) KeyStroke.getKeyStroke('X'.code, CTRL_DOWN_MASK),
private val CTRL_Y = EventFields.StringList("Ctrl-Y", values) KeyStroke.getKeyStroke('Y'.code, CTRL_DOWN_MASK),
private val CTRL_Z = EventFields.StringList("Ctrl-Z", values) KeyStroke.getKeyStroke('Z'.code, CTRL_DOWN_MASK),
private val CTRL_BR1 = EventFields.StringList("Ctrl-[", values) KeyStroke.getKeyStroke('['.code, CTRL_DOWN_MASK),
private val CTRL_BR2 = EventFields.StringList("Ctrl-]", values) KeyStroke.getKeyStroke(']'.code, CTRL_DOWN_MASK),
private val CTRL_SHIFT_A = EventFields.StringList("Ctrl-Shift-A", values) KeyStroke.getKeyStroke('A'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_B = EventFields.StringList("Ctrl-Shift-B", values) KeyStroke.getKeyStroke('B'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_C = EventFields.StringList("Ctrl-Shift-C", values) KeyStroke.getKeyStroke('C'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_D = EventFields.StringList("Ctrl-Shift-D", values) KeyStroke.getKeyStroke('D'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_E = EventFields.StringList("Ctrl-Shift-E", values) KeyStroke.getKeyStroke('E'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_F = EventFields.StringList("Ctrl-Shift-F", values) KeyStroke.getKeyStroke('F'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_G = EventFields.StringList("Ctrl-Shift-G", values) KeyStroke.getKeyStroke('G'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_H = EventFields.StringList("Ctrl-Shift-H", values) KeyStroke.getKeyStroke('H'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_I = EventFields.StringList("Ctrl-Shift-I", values) KeyStroke.getKeyStroke('I'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_J = EventFields.StringList("Ctrl-Shift-J", values) KeyStroke.getKeyStroke('J'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_K = EventFields.StringList("Ctrl-Shift-K", values) KeyStroke.getKeyStroke('K'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_L = EventFields.StringList("Ctrl-Shift-L", values) KeyStroke.getKeyStroke('L'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_M = EventFields.StringList("Ctrl-Shift-M", values) KeyStroke.getKeyStroke('M'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_N = EventFields.StringList("Ctrl-Shift-N", values) KeyStroke.getKeyStroke('N'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_O = EventFields.StringList("Ctrl-Shift-O", values) KeyStroke.getKeyStroke('O'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_P = EventFields.StringList("Ctrl-Shift-P", values) KeyStroke.getKeyStroke('P'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_Q = EventFields.StringList("Ctrl-Shift-Q", values) KeyStroke.getKeyStroke('Q'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_R = EventFields.StringList("Ctrl-Shift-R", values) KeyStroke.getKeyStroke('R'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_S = EventFields.StringList("Ctrl-Shift-S", values) KeyStroke.getKeyStroke('S'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_T = EventFields.StringList("Ctrl-Shift-T", values) KeyStroke.getKeyStroke('T'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_U = EventFields.StringList("Ctrl-Shift-U", values) KeyStroke.getKeyStroke('U'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_V = EventFields.StringList("Ctrl-Shift-V", values) KeyStroke.getKeyStroke('V'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_W = EventFields.StringList("Ctrl-Shift-W", values) KeyStroke.getKeyStroke('W'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_X = EventFields.StringList("Ctrl-Shift-X", values) KeyStroke.getKeyStroke('X'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_Y = EventFields.StringList("Ctrl-Shift-Y", values) KeyStroke.getKeyStroke('Y'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_Z = EventFields.StringList("Ctrl-Shift-Z", values) KeyStroke.getKeyStroke('Z'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_BR1 = EventFields.StringList("Ctrl-Shift-[", values) KeyStroke.getKeyStroke('['.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_SHIFT_BR2 = EventFields.StringList("Ctrl-Shift-]", values) KeyStroke.getKeyStroke(']'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val HANDLERS: VarargEventId = GROUP.registerVarargEvent(
"vim.handlers",
CTRL_1,
CTRL_2,
CTRL_3,
CTRL_4,
CTRL_5,
CTRL_6,
CTRL_7,
CTRL_8,
CTRL_9,
CTRL_0,
CTRL_SHIFT_1,
CTRL_SHIFT_2,
CTRL_SHIFT_3,
CTRL_SHIFT_4,
CTRL_SHIFT_5,
CTRL_SHIFT_6,
CTRL_SHIFT_7,
CTRL_SHIFT_8,
CTRL_SHIFT_9,
CTRL_SHIFT_0,
CTRL_A,
CTRL_B,
CTRL_C,
CTRL_D,
CTRL_E,
CTRL_F,
CTRL_G,
CTRL_H,
CTRL_I,
CTRL_J,
CTRL_K,
CTRL_L,
CTRL_M,
CTRL_N,
CTRL_O,
CTRL_P,
CTRL_Q,
CTRL_R,
CTRL_S,
CTRL_T,
CTRL_U,
CTRL_V,
CTRL_W,
CTRL_X,
CTRL_Y,
CTRL_Z,
CTRL_BR1,
CTRL_BR2,
CTRL_SHIFT_A,
CTRL_SHIFT_B,
CTRL_SHIFT_C,
CTRL_SHIFT_D,
CTRL_SHIFT_E,
CTRL_SHIFT_F,
CTRL_SHIFT_G,
CTRL_SHIFT_H,
CTRL_SHIFT_I,
CTRL_SHIFT_J,
CTRL_SHIFT_K,
CTRL_SHIFT_L,
CTRL_SHIFT_M,
CTRL_SHIFT_N,
CTRL_SHIFT_O,
CTRL_SHIFT_P,
CTRL_SHIFT_Q,
CTRL_SHIFT_R,
CTRL_SHIFT_S,
CTRL_SHIFT_T,
CTRL_SHIFT_U,
CTRL_SHIFT_V,
CTRL_SHIFT_W,
CTRL_SHIFT_X,
CTRL_SHIFT_Y,
CTRL_SHIFT_Z,
CTRL_SHIFT_BR1,
CTRL_SHIFT_BR2,
) )
private val KEY_STROKE = EventFields.String("key_stroke", keyStrokes.map { it.toReadableString() })
private val HANDLER_MODE = EventFields.Enum<HandledModes>("handler")
private val HANDLER = GROUP.registerEvent("vim.handler", KEY_STROKE, HANDLER_MODE)
} }
} }
private fun KeyStroke.toReadableString(): String {
val result = StringBuilder()
val modifiers = this.modifiers
if (modifiers > 0) {
result.append(InputEvent.getModifiersExText(modifiers))
.append("+")
}
result.append(KeyEvent.getKeyText(this.keyCode))
return result.toString()
}
private enum class HandledModes { private enum class HandledModes {
NORMAL_UNDEFINED, NORMAL_UNDEFINED,
NORMAL_IDE, NORMAL_IDE,

View File

@@ -59,8 +59,8 @@ internal class VimscriptState : ApplicationUsagesCollector() {
var isFunctionDeclarationUsed = false var isFunctionDeclarationUsed = false
var isFunctionCallUsed = false var isFunctionCallUsed = false
private val SOURCED_FILES = EventFields.Int("number_of_sourced_files") private val SOURCED_FILES = EventFields.RoundedInt("number_of_sourced_files")
private val IDEAVIMRC_SIZE = EventFields.Int("ideavimrc_size") private val IDEAVIMRC_SIZE = EventFields.RoundedInt("ideavimrc_size")
private val EXTENSIONS_ENABLED_BY_SET = EventFields.StringList("extensions_enabled_by_set", PluginState.extensionNames) private val EXTENSIONS_ENABLED_BY_SET = EventFields.StringList("extensions_enabled_by_set", PluginState.extensionNames)
private val EXTENSIONS_ENABLED_BY_PLUG = EventFields.StringList("extensions_enabled_by_plug", PluginState.extensionNames) private val EXTENSIONS_ENABLED_BY_PLUG = EventFields.StringList("extensions_enabled_by_plug", PluginState.extensionNames)
private val IS_IDE_SPECIFIC_CONFIGURATION_USED = EventFields.Boolean("is_IDE-specific_configuration_used") private val IS_IDE_SPECIFIC_CONFIGURATION_USED = EventFields.Boolean("is_IDE-specific_configuration_used")

View File

@@ -24,7 +24,6 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.DefaultActionGroup import com.intellij.openapi.actionSystem.DefaultActionGroup
import com.intellij.openapi.actionSystem.PlatformDataKeys import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Document import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.toolbar.floating.AbstractFloatingToolbarProvider import com.intellij.openapi.editor.toolbar.floating.AbstractFloatingToolbarProvider
import com.intellij.openapi.editor.toolbar.floating.FloatingToolbarComponent import com.intellij.openapi.editor.toolbar.floating.FloatingToolbarComponent
@@ -151,7 +150,7 @@ class ReloadVimRc : DumbAwareAction() {
val editor = e.getData(PlatformDataKeys.EDITOR) ?: return val editor = e.getData(PlatformDataKeys.EDITOR) ?: return
FileDocumentManager.getInstance().saveDocumentAsIs(editor.document) FileDocumentManager.getInstance().saveDocumentAsIs(editor.document)
injector.keyGroup.removeKeyMapping(MappingOwner.IdeaVim.InitScript) injector.keyGroup.removeKeyMapping(MappingOwner.IdeaVim.InitScript)
service<Troubleshooter>().removeByType("old-action-notation-in-mappings") Troubleshooter.instance.removeByType("old-action-notation-in-mappings")
executeIdeaVimRc() executeIdeaVimRc()
} }
} }

View File

@@ -21,7 +21,7 @@ package com.maddyhome.idea.vim.ui.ex
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.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.helper.SearchHelper import com.maddyhome.idea.vim.api.VimSearchHelperBase
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import java.awt.event.ActionEvent import java.awt.event.ActionEvent
import java.awt.event.KeyEvent import java.awt.event.KeyEvent
@@ -238,14 +238,14 @@ class DeletePreviousWordAction : TextAction(DefaultEditorKit.deletePrevWordActio
target.saveLastEntry() target.saveLastEntry()
val doc = target.document val doc = target.document
val caret = target.caret val caret = target.caret
val offset = SearchHelper.findNextWord( val offset = VimSearchHelperBase.Companion.findNextWord(
target.actualText, caret.dot, target.actualText.length, target.actualText, caret.dot.toLong(), target.actualText.length.toLong(),
-1, false, false -1, false, false
) )
if (logger.isDebugEnabled) logger.debug("offset=$offset") if (logger.isDebugEnabled) logger.debug("offset=$offset")
try { try {
val pos = caret.dot val pos = caret.dot
doc.remove(offset, pos - offset) doc.remove(offset.toInt(), (pos - offset).toInt())
} catch (ex: BadLocationException) { } catch (ex: BadLocationException) {
// ignore // ignore
} }

View File

@@ -0,0 +1,48 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2021 The IdeaVim authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.maddyhome.idea.vim.vimscript.model.functions.handlers
import com.intellij.refactoring.rename.inplace.InplaceRefactoring
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.vimscript.model.VimLContext
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimInt
import com.maddyhome.idea.vim.vimscript.model.expressions.Expression
import com.maddyhome.idea.vim.vimscript.model.functions.FunctionHandler
object RenamingFunctionHandler : FunctionHandler() {
override val name = "renaming"
override val minimumNumberOfArguments = 0
override val maximumNumberOfArguments = 0
override fun doFunction(
argumentValues: List<Expression>,
editor: VimEditor,
context: ExecutionContext,
vimContext: VimLContext,
): VimDataType {
return if (InplaceRefactoring.getActiveInplaceRenamer(editor.ij) == null)
VimInt.ZERO
else
VimInt.ONE
}
}

View File

@@ -14,5 +14,6 @@
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.TolowerFunctionHandler" name="tolower"/> <vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.TolowerFunctionHandler" name="tolower"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.ToupperFunctionHandler" name="toupper"/> <vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.ToupperFunctionHandler" name="toupper"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.JoinFunctionHandler" name="join"/> <vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.JoinFunctionHandler" name="join"/>
<vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.RenamingFunctionHandler" name="renaming"/>
</extensions> </extensions>
</idea-plugin> </idea-plugin>

View File

@@ -1,4 +1,4 @@
<idea-plugin url="https://plugins.jetbrains.com/plugin/164" xmlns:xi="http://www.w3.org/2001/XInclude"> <idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
<name>IdeaVim</name> <name>IdeaVim</name>
<id>IdeaVIM</id> <id>IdeaVIM</id>
<change-notes><![CDATA[ <change-notes><![CDATA[
@@ -109,7 +109,7 @@
]]> ]]>
</change-notes> </change-notes>
<description><![CDATA[ <description><![CDATA[
<p>Vim emulation plugin for IntelliJ Platform-based IDEs.</p> <p>Vim engine for JetBrains IDEs</p>
<br/> <br/>
<p>IdeaVim supports many Vim features including normal/insert/visual modes, motion keys, deletion/changing, <p>IdeaVim supports many Vim features including normal/insert/visual modes, motion keys, deletion/changing,
marks, registers, some Ex commands, Vim regexps, configuration via ~/.ideavimrc, macros, Vim plugins, etc.</p> marks, registers, some Ex commands, Vim regexps, configuration via ~/.ideavimrc, macros, Vim plugins, etc.</p>
@@ -120,7 +120,7 @@
<li><a href="https://youtrack.jetbrains.com/issues/VIM">Issue tracker</a>: feature requests and bug reports</li> <li><a href="https://youtrack.jetbrains.com/issues/VIM">Issue tracker</a>: feature requests and bug reports</li>
</ul> </ul>
]]></description> ]]></description>
<version>SNAPSHOT</version> <version>chylex</version>
<vendor>JetBrains</vendor> <vendor>JetBrains</vendor>
<!-- Please search for "[VERSION UPDATE]" in project in case you update the since-build version --> <!-- Please search for "[VERSION UPDATE]" in project in case you update the since-build version -->

View File

@@ -63,7 +63,7 @@ E548=E548: Digit expected: {0}
E549=E549: Illegal percentage: {0} E549=E549: Illegal percentage: {0}
E774=E774: 'operatorfunc' is empty E774=E774: 'operatorfunc' is empty
action.VimPluginToggle.text=Vim Emulator action.VimPluginToggle.text=Vim
action.VimPluginToggle.description=Toggle the vim plugin On/Off action.VimPluginToggle.description=Toggle the vim plugin On/Off
action.VimPluginToggle.enabled=Enabled action.VimPluginToggle.enabled=Enabled
action.VimPluginToggle.enable=Enable action.VimPluginToggle.enable=Enable

View File

@@ -94,7 +94,7 @@ abstract class VimTestCase : UsefulTestCase() {
super.setUp() super.setUp()
val factory = IdeaTestFixtureFactory.getFixtureFactory() val factory = IdeaTestFixtureFactory.getFixtureFactory()
val projectDescriptor = LightProjectDescriptor.EMPTY_PROJECT_DESCRIPTOR val projectDescriptor = LightProjectDescriptor.EMPTY_PROJECT_DESCRIPTOR
val fixtureBuilder = factory.createLightFixtureBuilder(projectDescriptor) val fixtureBuilder = factory.createLightFixtureBuilder(projectDescriptor, "IdeaVim")
val fixture = fixtureBuilder.fixture val fixture = fixtureBuilder.fixture
myFixture = IdeaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture( myFixture = IdeaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(
fixture, fixture,

View File

@@ -20,6 +20,7 @@ package org.jetbrains.plugins.ideavim.action
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.VimStateMachine import com.maddyhome.idea.vim.command.VimStateMachine
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
import com.maddyhome.idea.vim.helper.vimStateMachine import com.maddyhome.idea.vim.helper.vimStateMachine
import org.jetbrains.plugins.ideavim.SkipNeovimReason import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestWithoutNeovim import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -1039,4 +1040,16 @@ two
) )
assertOffset(4) assertOffset(4)
} }
fun `test gv after backwards selection`() {
configureByText("${c}Oh, hi Mark\n")
typeText(parseKeys("yw" + "$" + "vb" + "p" + "gv"))
assertSelection("Oh")
}
fun `test gv after linewise selection`() {
configureByText("${c}Oh, hi Mark\nOh, hi Markus\n")
typeText(parseKeys("V" + "y" + "j" + "V" + "p" + "gv"))
assertSelection("Oh, hi Mark")
}
} }

View File

@@ -18,8 +18,8 @@
package org.jetbrains.plugins.ideavim.action.change.change package org.jetbrains.plugins.ideavim.action.change.change
//class InsertRegisterTest : VimTestCase() { // class InsertRegisterTest : VimTestCase() {
// todo test cursor position VIM-2732 // todo test cursor position VIM-2732
// fun `test multiline insert from expression register`() { // fun `test multiline insert from expression register`() {
// val keys = "VjyGo<C-r>=@\"<CR>" // val keys = "VjyGo<C-r>=@\"<CR>"
// val before = """ // val before = """
@@ -43,4 +43,4 @@ package org.jetbrains.plugins.ideavim.action.change.change
// """.trimIndent() // """.trimIndent()
// doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE) // doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
// } // }
//} // }

View File

@@ -49,6 +49,23 @@ class DeleteMotionActionTest : VimTestCase() {
) )
} }
fun `test on line in middle`() {
typeTextInFile(
injector.parser.parseKeys("dd"),
"""
def xxx():
expression${c} one
expression two
""".trimIndent()
)
assertState(
"""
def xxx():
${c}expression two
""".trimIndent()
)
}
fun `test delete single line`() { fun `test delete single line`() {
typeTextInFile( typeTextInFile(
injector.parser.parseKeys("dd"), injector.parser.parseKeys("dd"),

View File

@@ -18,6 +18,13 @@
package org.jetbrains.plugins.ideavim.action.copy package org.jetbrains.plugins.ideavim.action.copy
import com.intellij.codeInsight.editorActions.CopyPastePostProcessor
import com.intellij.codeInsight.editorActions.CopyPastePreProcessor
import com.intellij.codeInsight.editorActions.TextBlockTransferableData
import com.intellij.openapi.editor.CaretStateTransferableData
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiFile
import com.intellij.testFramework.ExtensionTestUtil
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.SelectionType import com.maddyhome.idea.vim.command.SelectionType
@@ -27,8 +34,32 @@ import com.maddyhome.idea.vim.newapi.vim
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
import org.jetbrains.plugins.ideavim.rangeOf import org.jetbrains.plugins.ideavim.rangeOf
import org.junit.Test import org.junit.Test
import java.awt.datatransfer.Transferable
class PutTestAfterCursorActionTest : VimTestCase() { class PutTestAfterCursorActionTest : VimTestCase() {
fun `test platform handlers are called`() {
val extension = TestExtension()
ExtensionTestUtil.maskExtensions(
CopyPastePostProcessor.EP_NAME,
listOf(extension),
myFixture.testRootDisposable
)
ExtensionTestUtil.maskExtensions(
CopyPastePreProcessor.EP_NAME,
listOf(),
myFixture.testRootDisposable
)
setRegister('4', "XXX ")
doTest(
"\"4p",
"This is my$c text",
"This is my XXX$c text",
VimStateMachine.Mode.COMMAND,
VimStateMachine.SubMode.NONE
)
assertEquals(1, extension.calledExtractTransferableData)
}
fun `test put from number register`() { fun `test put from number register`() {
setRegister('4', "XXX ") setRegister('4', "XXX ")
doTest( doTest(
@@ -99,4 +130,24 @@ class PutTestAfterCursorActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
assertState(after) assertState(after)
} }
private class TestExtension : CopyPastePostProcessor<TextBlockTransferableData>() {
var calledExtractTransferableData = 0
override fun collectTransferableData(
file: PsiFile,
editor: Editor,
startOffsets: IntArray?,
endOffsets: IntArray?,
): List<TextBlockTransferableData> {
return emptyList()
}
override fun extractTransferableData(content: Transferable): List<TextBlockTransferableData> {
calledExtractTransferableData += 1
return listOf(
// Just some random data
CaretStateTransferableData(intArrayOf(), intArrayOf())
)
}
}
} }

View File

@@ -91,7 +91,7 @@ class YankVisualActionTest : VimTestCase() {
typeText(injector.parser.parseKeys("viw" + "y")) typeText(injector.parser.parseKeys("viw" + "y"))
val editor = myFixture.editor.vim val editor = myFixture.editor.vim
val lastRegister = injector.registerGroup.lastRegisterChar val lastRegister = injector.registerGroup.lastRegisterChar
val registers = editor.carets().map { it.registerStorage.getRegister(lastRegister)?.rawText } val registers = editor.carets().map { it.registerStorage.getRegister(it, lastRegister)?.rawText }
assertEquals(listOf("found", "was"), registers) assertEquals(listOf("found", "was"), registers)
} }
@@ -165,7 +165,7 @@ class YankVisualActionTest : VimTestCase() {
typeText(injector.parser.parseKeys("V" + "y")) typeText(injector.parser.parseKeys("V" + "y"))
val editor = myFixture.editor.vim val editor = myFixture.editor.vim
val lastRegister = injector.registerGroup.lastRegisterChar val lastRegister = injector.registerGroup.lastRegisterChar
val registers = editor.carets().map { it.registerStorage.getRegister(lastRegister)?.rawText } val registers = editor.carets().map { it.registerStorage.getRegister(it, lastRegister)?.rawText }
assertEquals( assertEquals(
listOf("all rocks and lavender and tufted grass,\n", "hard by the torrent of a mountain pass.\n"), listOf("all rocks and lavender and tufted grass,\n", "hard by the torrent of a mountain pass.\n"),
registers registers

View File

@@ -121,6 +121,22 @@ n ,f <Plug>Foo
assertState("quux\n") assertState("quux\n")
} }
fun testddWithMapping() {
configureByText(
"""
Hello$c 1
Hello 2
""".trimIndent()
)
typeText(commandToKeys("nmap dc k"))
typeText(injector.parser.parseKeys("dd"))
assertState(
"""
Hello 2
""".trimIndent()
)
}
fun testNonRecursiveMapping() { fun testNonRecursiveMapping() {
configureByText("\n") configureByText("\n")
typeText(commandToKeys("inoremap a b")) typeText(commandToKeys("inoremap a b"))

View File

@@ -38,8 +38,8 @@ class MarksCommandTest : VimTestCase() {
assertState( assertState(
"""I found it in a legendary land """I found it in a legendary land
|all rocks and lavender and tufted grass, |all rocks and lavender and tufted grass,
|${s}all rocks and lavender and tufted grass, |${s}all rocks and lavender and tufted grass,$se
|${se}hard by the torrent of a mountain pass. |hard by the torrent of a mountain pass.
""".trimMargin() """.trimMargin()
) )
} }

View File

@@ -23,11 +23,11 @@ import com.maddyhome.idea.vim.vimscript.model.expressions.Register
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
import org.jetbrains.plugins.ideavim.ex.evaluate import org.jetbrains.plugins.ideavim.ex.evaluate
class ExpressionTest: VimTestCase() { class ExpressionTest : VimTestCase() {
fun `test multiline register content`() { fun `test multiline register content`() {
configureByText("${c}Oh\nHi\nMark\n") configureByText("${c}Oh\nHi\nMark\n")
typeText(parseKeys("VGy")) typeText(parseKeys("VGy"))
assertEquals("Oh\nHi\nMark\n", Register('"').evaluate().toString()) assertEquals("Oh\nHi\nMark\n", Register('"').evaluate().toString())
} }
} }

View File

@@ -18,8 +18,8 @@
package org.jetbrains.plugins.ideavim.helper; package org.jetbrains.plugins.ideavim.helper;
import com.maddyhome.idea.vim.api.VimSearchHelperBase;
import com.maddyhome.idea.vim.command.VimStateMachine; import com.maddyhome.idea.vim.command.VimStateMachine;
import com.maddyhome.idea.vim.helper.SearchHelper;
import com.maddyhome.idea.vim.helper.SearchHelperKtKt; import com.maddyhome.idea.vim.helper.SearchHelperKtKt;
import org.jetbrains.plugins.ideavim.SkipNeovimReason; import org.jetbrains.plugins.ideavim.SkipNeovimReason;
import org.jetbrains.plugins.ideavim.TestWithoutNeovim; import org.jetbrains.plugins.ideavim.TestWithoutNeovim;
@@ -29,7 +29,7 @@ public class SearchHelperTest extends VimTestCase {
@TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
public void testFindNextWord() { public void testFindNextWord() {
String text = "first second"; String text = "first second";
int nextWordPosition = SearchHelper.findNextWord(text, 0, text.length(), 1, true, false); int nextWordPosition = (int)VimSearchHelperBase.Companion.findNextWord(text, 0, text.length(), 1, true, false);
assertEquals(nextWordPosition, text.indexOf("second")); assertEquals(nextWordPosition, text.indexOf("second"));
} }
@@ -37,7 +37,7 @@ public class SearchHelperTest extends VimTestCase {
@TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
public void testFindSecondNextWord() { public void testFindSecondNextWord() {
String text = "first second third"; String text = "first second third";
int nextWordPosition = SearchHelper.findNextWord(text, 0, text.length(), 2, true, false); int nextWordPosition = (int)VimSearchHelperBase.Companion.findNextWord(text, 0, text.length(), 2, true, false);
assertEquals(nextWordPosition, text.indexOf("third")); assertEquals(nextWordPosition, text.indexOf("third"));
} }
@@ -45,7 +45,7 @@ public class SearchHelperTest extends VimTestCase {
@TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
public void testFindAfterLastWord() { public void testFindAfterLastWord() {
String text = "first second"; String text = "first second";
int nextWordPosition = SearchHelper.findNextWord(text, 0, text.length(), 3, true, false); int nextWordPosition = (int)VimSearchHelperBase.Companion.findNextWord(text, 0, text.length(), 3, true, false);
assertEquals(nextWordPosition, text.length()); assertEquals(nextWordPosition, text.length());
} }
@@ -53,7 +53,8 @@ public class SearchHelperTest extends VimTestCase {
@TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
public void testFindPreviousWord() { public void testFindPreviousWord() {
String text = "first second"; String text = "first second";
int previousWordPosition = SearchHelper.findNextWord(text, text.indexOf("second"), text.length(), -1, true, false); int previousWordPosition =
(int)VimSearchHelperBase.Companion.findNextWord(text, text.indexOf("second"), text.length(), -1, true, false);
//noinspection ConstantConditions //noinspection ConstantConditions
assertEquals(previousWordPosition, text.indexOf("first")); assertEquals(previousWordPosition, text.indexOf("first"));
@@ -62,7 +63,8 @@ public class SearchHelperTest extends VimTestCase {
@TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
public void testFindSecondPreviousWord() { public void testFindSecondPreviousWord() {
String text = "first second third"; String text = "first second third";
int previousWordPosition = SearchHelper.findNextWord(text, text.indexOf("third"), text.length(), -2, true, false); int previousWordPosition =
(int)VimSearchHelperBase.Companion.findNextWord(text, text.indexOf("third"), text.length(), -2, true, false);
//noinspection ConstantConditions //noinspection ConstantConditions
assertEquals(previousWordPosition, text.indexOf("first")); assertEquals(previousWordPosition, text.indexOf("first"));
@@ -71,7 +73,8 @@ public class SearchHelperTest extends VimTestCase {
@TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
public void testFindBeforeFirstWord() { public void testFindBeforeFirstWord() {
String text = "first second"; String text = "first second";
int previousWordPosition = SearchHelper.findNextWord(text, text.indexOf("second"), text.length(), -3, true, false); int previousWordPosition =
(int)VimSearchHelperBase.Companion.findNextWord(text, text.indexOf("second"), text.length(), -3, true, false);
//noinspection ConstantConditions //noinspection ConstantConditions
assertEquals(previousWordPosition, text.indexOf("first")); assertEquals(previousWordPosition, text.indexOf("first"));
@@ -80,7 +83,8 @@ public class SearchHelperTest extends VimTestCase {
@TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING) @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
public void testFindPreviousWordWhenCursorOutOfBound() { public void testFindPreviousWordWhenCursorOutOfBound() {
String text = "first second"; String text = "first second";
int previousWordPosition = SearchHelper.findNextWord(text, text.length(), text.length(), -1, true, false); int previousWordPosition =
(int)VimSearchHelperBase.Companion.findNextWord(text, text.length(), text.length(), -1, true, false);
assertEquals(previousWordPosition, text.indexOf("second")); assertEquals(previousWordPosition, text.indexOf("second"));
} }

View File

@@ -19,6 +19,7 @@ package com.maddyhome.idea.vim.action
import com.maddyhome.idea.vim.KeyHandler import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.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.Command import com.maddyhome.idea.vim.command.Command
@@ -27,8 +28,33 @@ import com.maddyhome.idea.vim.command.VimStateMachine
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.mode import com.maddyhome.idea.vim.helper.mode
class ResetModeAction : VimActionHandler.SingleExecution() { class ResetModeAction : VimActionHandler.ConditionalMulticaret() {
private lateinit var modeBeforeReset: VimStateMachine.Mode
override val type: Command.Type = Command.Type.OTHER_WRITABLE override val type: Command.Type = Command.Type.OTHER_WRITABLE
override fun runAsMulticaret(
editor: VimEditor,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments,
): Boolean {
modeBeforeReset = editor.mode
KeyHandler.getInstance().fullReset(editor)
return true
}
override fun execute(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments,
): Boolean {
if (modeBeforeReset == VimStateMachine.Mode.INSERT) {
val position = injector.motion.getOffsetOfHorizontalMotion(editor, caret, -1, false)
caret.moveToOffset(position)
}
return true
}
override fun execute( override fun execute(
editor: VimEditor, editor: VimEditor,
@@ -36,16 +62,6 @@ class ResetModeAction : VimActionHandler.SingleExecution() {
cmd: Command, cmd: Command,
operatorArguments: OperatorArguments, operatorArguments: OperatorArguments,
): Boolean { ): Boolean {
val modeBeforeReset = editor.mode error("This method should not be used")
KeyHandler.getInstance().fullReset(editor)
if (modeBeforeReset == VimStateMachine.Mode.INSERT) {
editor.forEachCaret { caret ->
val position = injector.motion.getOffsetOfHorizontalMotion(editor, caret, -1, false)
caret.moveToOffset(position)
}
}
return true
} }
} }

View File

@@ -42,19 +42,25 @@ sealed class PutTextBaseAction(
operatorArguments: OperatorArguments operatorArguments: OperatorArguments
): Boolean { ): Boolean {
val count = operatorArguments.count1 val count = operatorArguments.count1
val caretToPutData = editor.sortedCarets().associateWith { getPutDataForCaret(it, count) } val sortedCarets = editor.sortedCarets()
var result = true return if (sortedCarets.size > 1) {
injector.application.runWriteAction { val caretToPutData = sortedCarets.associateWith { getPutDataForCaret(it, count) }
caretToPutData.forEach { var result = true
result = injector.put.putTextForCaret(editor, it.key, context, it.value) && result injector.application.runWriteAction {
caretToPutData.forEach {
result = injector.put.putTextForCaret(editor, it.key, context, it.value) && result
}
} }
result
} else {
val putData = getPutDataForCaret(sortedCarets.single(), count)
injector.put.putText(editor, context, putData, operatorArguments)
} }
return result
} }
private fun getPutDataForCaret(caret: VimCaret, count: Int): PutData { private fun getPutDataForCaret(caret: VimCaret, count: Int): PutData {
val lastRegisterChar = injector.registerGroup.lastRegisterChar val lastRegisterChar = injector.registerGroup.lastRegisterChar
val register = caret.registerStorage.getRegister(lastRegisterChar) val register = caret.registerStorage.getRegister(caret, lastRegisterChar)
val textData = register?.let { val textData = register?.let {
TextData( TextData(
register.text ?: injector.parser.toPrintableString(register.keys), register.text ?: injector.parser.toPrintableString(register.keys),

View File

@@ -66,7 +66,7 @@ sealed class PutVisualTextBaseAction(
private fun getPutDataForCaret(caret: VimCaret, selection: VimSelection?, count: Int): PutData { private fun getPutDataForCaret(caret: VimCaret, selection: VimSelection?, count: Int): PutData {
val lastRegisterChar = injector.registerGroup.lastRegisterChar val lastRegisterChar = injector.registerGroup.lastRegisterChar
val register = caret.registerStorage.getRegister(lastRegisterChar) val register = caret.registerStorage.getRegister(caret, lastRegisterChar)
val textData = register?.let { val textData = register?.let {
PutData.TextData( PutData.TextData(
register.text ?: injector.parser.toPrintableString(register.keys), register.text ?: injector.parser.toPrintableString(register.keys),

View File

@@ -19,6 +19,7 @@
package com.maddyhome.idea.vim.action.motion.leftright package com.maddyhome.idea.vim.action.motion.leftright
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.VimMotionGroupBase import com.maddyhome.idea.vim.api.VimMotionGroupBase
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
@@ -35,27 +36,25 @@ class MotionShiftEndAction : ShiftedSpecialKeyHandler() {
override val type: Command.Type = Command.Type.OTHER_READONLY override val type: Command.Type = Command.Type.OTHER_READONLY
override fun motion(editor: VimEditor, context: ExecutionContext, cmd: Command) { override fun motion(editor: VimEditor, context: ExecutionContext, cmd: Command, caret: VimCaret) {
editor.forEachCaret { caret -> var allow = false
var allow = false if (editor.inInsertMode) {
if (editor.inInsertMode) { allow = true
} else if (editor.inVisualMode || editor.inSelectMode) {
val opt = (
injector.optionService.getOptionValue(
OptionScope.LOCAL(editor),
OptionConstants.selectionName
) as VimString
).value
if (opt != "old") {
allow = true allow = true
} else if (editor.inVisualMode || editor.inSelectMode) {
val opt = (
injector.optionService.getOptionValue(
OptionScope.LOCAL(editor),
OptionConstants.selectionName
) as VimString
).value
if (opt != "old") {
allow = true
}
} }
val newOffset = injector.motion.moveCaretToLineEndOffset(editor, caret, cmd.count - 1, allow)
caret.vimLastColumn = VimMotionGroupBase.LAST_COLUMN
caret.moveToOffset(newOffset)
caret.vimLastColumn = VimMotionGroupBase.LAST_COLUMN
} }
val newOffset = injector.motion.moveCaretToLineEndOffset(editor, caret, cmd.count - 1, allow)
caret.vimLastColumn = VimMotionGroupBase.LAST_COLUMN
caret.moveToOffset(newOffset)
caret.vimLastColumn = VimMotionGroupBase.LAST_COLUMN
} }
} }

View File

@@ -19,6 +19,7 @@
package com.maddyhome.idea.vim.action.motion.leftright package com.maddyhome.idea.vim.action.motion.leftright
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.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.Command import com.maddyhome.idea.vim.command.Command
@@ -31,10 +32,8 @@ class MotionShiftHomeAction : ShiftedSpecialKeyHandler() {
override val type: Command.Type = Command.Type.OTHER_READONLY override val type: Command.Type = Command.Type.OTHER_READONLY
override fun motion(editor: VimEditor, context: ExecutionContext, cmd: Command) { override fun motion(editor: VimEditor, context: ExecutionContext, cmd: Command, caret: VimCaret) {
editor.forEachCaret { caret -> val newOffset = injector.motion.moveCaretToLineStart(editor, caret)
val newOffset = injector.motion.moveCaretToLineStart(editor, caret) caret.moveToOffset(newOffset)
caret.moveToOffset(newOffset)
}
} }
} }

View File

@@ -19,6 +19,7 @@
package com.maddyhome.idea.vim.action.motion.leftright package com.maddyhome.idea.vim.action.motion.leftright
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.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.Command import com.maddyhome.idea.vim.command.Command
@@ -29,23 +30,20 @@ import com.maddyhome.idea.vim.handler.ShiftedArrowKeyHandler
* @author Alex Plate * @author Alex Plate
*/ */
class MotionShiftLeftAction : ShiftedArrowKeyHandler() { class MotionShiftLeftAction : ShiftedArrowKeyHandler(true) {
override val type: Command.Type = Command.Type.OTHER_READONLY override val type: Command.Type = Command.Type.OTHER_READONLY
override fun motionWithKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) { override fun motionWithKeyModel(editor: VimEditor, caret: VimCaret, context: ExecutionContext, cmd: Command) {
editor.forEachCaret { caret -> val vertical = injector.motion.getOffsetOfHorizontalMotion(editor, caret, -cmd.count, true)
val vertical = injector.motion.getOffsetOfHorizontalMotion(editor, caret, -cmd.count, true) caret.moveToOffset(vertical)
caret.moveToOffset(vertical)
}
} }
override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) { override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) {
editor.forEachCaret { caret -> val caret = editor.currentCaret()
val newOffset = injector.motion.findOffsetOfNextWord(editor, caret.offset.point, -cmd.count, false) val newOffset = injector.motion.findOffsetOfNextWord(editor, caret.offset.point, -cmd.count, false)
if (newOffset is Motion.AbsoluteOffset) { if (newOffset is Motion.AbsoluteOffset) {
caret.moveToOffset(newOffset.offset) caret.moveToOffset(newOffset.offset)
}
} }
} }
} }

View File

@@ -19,6 +19,7 @@
package com.maddyhome.idea.vim.action.motion.leftright package com.maddyhome.idea.vim.action.motion.leftright
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.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.Command import com.maddyhome.idea.vim.command.Command
@@ -29,23 +30,20 @@ import com.maddyhome.idea.vim.handler.ShiftedArrowKeyHandler
* @author Alex Plate * @author Alex Plate
*/ */
class MotionShiftRightAction : ShiftedArrowKeyHandler() { class MotionShiftRightAction : ShiftedArrowKeyHandler(true) {
override val type: Command.Type = Command.Type.OTHER_READONLY override val type: Command.Type = Command.Type.OTHER_READONLY
override fun motionWithKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) { override fun motionWithKeyModel(editor: VimEditor, caret: VimCaret, context: ExecutionContext, cmd: Command) {
editor.forEachCaret { caret -> val vertical = injector.motion.getOffsetOfHorizontalMotion(editor, caret, cmd.count, true)
val vertical = injector.motion.getOffsetOfHorizontalMotion(editor, caret, cmd.count, true) caret.moveToOffset(vertical)
caret.moveToOffset(vertical)
}
} }
override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) { override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) {
editor.forEachCaret { caret -> val caret = editor.currentCaret()
val newOffset = injector.motion.findOffsetOfNextWord(editor, caret.offset.point, cmd.count, false) val newOffset = injector.motion.findOffsetOfNextWord(editor, caret.offset.point, cmd.count, false)
if (newOffset is Motion.AbsoluteOffset) { if (newOffset is Motion.AbsoluteOffset) {
caret.moveToOffset(newOffset.offset) caret.moveToOffset(newOffset.offset)
}
} }
} }
} }

View File

@@ -19,6 +19,7 @@
package com.maddyhome.idea.vim.action.motion.updown package com.maddyhome.idea.vim.action.motion.updown
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.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.Command import com.maddyhome.idea.vim.command.Command
@@ -28,18 +29,16 @@ import com.maddyhome.idea.vim.handler.ShiftedArrowKeyHandler
* @author Alex Plate * @author Alex Plate
*/ */
class MotionShiftDownAction : ShiftedArrowKeyHandler() { class MotionShiftDownAction : ShiftedArrowKeyHandler(false) {
override val type: Command.Type = Command.Type.OTHER_READONLY override val type: Command.Type = Command.Type.OTHER_READONLY
override fun motionWithKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) { override fun motionWithKeyModel(editor: VimEditor, caret: VimCaret, context: ExecutionContext, cmd: Command) {
editor.forEachCaret { caret -> val vertical = injector.motion.getVerticalMotionOffset(editor, caret, cmd.count)
val vertical = injector.motion.getVerticalMotionOffset(editor, caret, cmd.count) val col = injector.engineEditorHelper.prepareLastColumn(caret)
val col = injector.engineEditorHelper.prepareLastColumn(caret) caret.moveToOffset(vertical)
caret.moveToOffset(vertical)
injector.engineEditorHelper.updateLastColumn(caret, col) injector.engineEditorHelper.updateLastColumn(caret, col)
}
} }
override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) { override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) {

View File

@@ -19,6 +19,7 @@
package com.maddyhome.idea.vim.action.motion.updown package com.maddyhome.idea.vim.action.motion.updown
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.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.Command import com.maddyhome.idea.vim.command.Command
@@ -28,18 +29,16 @@ import com.maddyhome.idea.vim.handler.ShiftedArrowKeyHandler
* @author Alex Plate * @author Alex Plate
*/ */
class MotionShiftUpAction : ShiftedArrowKeyHandler() { class MotionShiftUpAction : ShiftedArrowKeyHandler(false) {
override val type: Command.Type = Command.Type.OTHER_READONLY override val type: Command.Type = Command.Type.OTHER_READONLY
override fun motionWithKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) { override fun motionWithKeyModel(editor: VimEditor, caret: VimCaret, context: ExecutionContext, cmd: Command) {
editor.forEachCaret { caret -> val vertical = injector.motion.getVerticalMotionOffset(editor, caret, -cmd.count)
val vertical = injector.motion.getVerticalMotionOffset(editor, caret, -cmd.count) val col = injector.engineEditorHelper.prepareLastColumn(caret)
val col = injector.engineEditorHelper.prepareLastColumn(caret) caret.moveToOffset(vertical)
caret.moveToOffset(vertical)
injector.engineEditorHelper.updateLastColumn(caret, col) injector.engineEditorHelper.updateLastColumn(caret, col)
}
} }
override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) { override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) {

View File

@@ -30,20 +30,17 @@ import com.maddyhome.idea.vim.helper.inBlockSubMode
/** /**
* @author vlan * @author vlan
*/ */
class VisualSwapEndsAction : VimActionHandler.SingleExecution() { class VisualSwapEndsAction : VimActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.OTHER_READONLY override val type: Command.Type = Command.Type.OTHER_READONLY
override fun execute( override fun execute(
editor: VimEditor, editor: VimEditor,
caret: VimCaret,
context: ExecutionContext, context: ExecutionContext,
cmd: Command, cmd: Command,
operatorArguments: OperatorArguments, operatorArguments: OperatorArguments
): Boolean { ): Boolean = swapVisualEnds(caret)
var ret = true
editor.forEachCaret { ret = ret and swapVisualEnds(it) }
return ret
}
} }
/** /**

View File

@@ -19,6 +19,7 @@
package com.maddyhome.idea.vim.action.motion.visual package com.maddyhome.idea.vim.action.motion.visual
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.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.Command import com.maddyhome.idea.vim.command.Command
@@ -29,11 +30,10 @@ import com.maddyhome.idea.vim.options.OptionConstants
import com.maddyhome.idea.vim.options.OptionScope import com.maddyhome.idea.vim.options.OptionScope
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
class VisualToggleLineModeAction : VimActionHandler.SingleExecution() { class VisualToggleLineModeAction : VimActionHandler.ConditionalMulticaret() {
override val type: Command.Type = Command.Type.OTHER_READONLY override val type: Command.Type = Command.Type.OTHER_READONLY
override fun runAsMulticaret(
override fun execute(
editor: VimEditor, editor: VimEditor,
context: ExecutionContext, context: ExecutionContext,
cmd: Command, cmd: Command,
@@ -46,10 +46,29 @@ class VisualToggleLineModeAction : VimActionHandler.SingleExecution() {
) as VimString ) as VimString
).value ).value
return if ("cmd" in listOption) { return if ("cmd" in listOption) {
injector.visualMotionGroup.enterSelectMode(editor, VimStateMachine.SubMode.VISUAL_LINE).also { injector.visualMotionGroup.enterSelectMode(editor, VimStateMachine.SubMode.VISUAL_LINE)
editor.forEachCaret { it.vimSetSelection(it.offset.point) } true
} } else false
} else injector.visualMotionGroup }
override fun execute(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments,
): Boolean {
caret.vimSetSelection(caret.offset.point)
return true
}
override fun execute(
editor: VimEditor,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments,
): Boolean {
return injector.visualMotionGroup
.toggleVisual(editor, cmd.count, cmd.rawCount, VimStateMachine.SubMode.VISUAL_LINE) .toggleVisual(editor, cmd.count, cmd.rawCount, VimStateMachine.SubMode.VISUAL_LINE)
} }
} }

View File

@@ -22,6 +22,7 @@ interface VimCaret {
val vimLeadSelectionOffset: Int val vimLeadSelectionOffset: Int
var vimLastVisualOperatorRange: VisualChange? var vimLastVisualOperatorRange: VisualChange?
val vimLine: Int val vimLine: Int
val isPrimary: Boolean
fun moveToOffset(offset: Int) fun moveToOffset(offset: Int)
fun moveToOffsetNative(offset: Int) fun moveToOffsetNative(offset: Int)
fun moveToLogicalPosition(logicalPosition: VimLogicalPosition) fun moveToLogicalPosition(logicalPosition: VimLogicalPosition)
@@ -43,17 +44,18 @@ interface VimCaret {
} }
interface CaretRegisterStorage { interface CaretRegisterStorage {
// todo methods shouldn't have caret in signature
/** /**
* Stores text to caret's recordable (named/numbered/unnamed) register * Stores text to caret's recordable (named/numbered/unnamed) register
*/ */
fun storeText(editor: VimEditor, range: TextRange, type: SelectionType, isDelete: Boolean): Boolean fun storeText(caret: VimCaret, editor: VimEditor, range: TextRange, type: SelectionType, isDelete: Boolean): Boolean
/** /**
* Gets text from caret's recordable register * Gets text from caret's recordable register
* If the register is not recordable - global text state will be returned * If the register is not recordable - global text state will be returned
*/ */
fun getRegister(r: Char): Register? fun getRegister(caret: VimCaret, r: Char): Register?
fun setKeys(register: Char, keys: List<KeyStroke>) fun setKeys(caret: VimCaret, register: Char, keys: List<KeyStroke>)
fun saveRegister(r: Char, register: Register) fun saveRegister(caret: VimCaret, r: Char, register: Register)
} }

View File

@@ -9,15 +9,15 @@ import javax.swing.KeyStroke
abstract class VimCaretBase : VimCaret abstract class VimCaretBase : VimCaret
open class CaretRegisterStorageBase(private val isCaretPrimary: Boolean) : CaretRegisterStorage, VimRegisterGroupBase() { open class CaretRegisterStorageBase : CaretRegisterStorage, VimRegisterGroupBase() {
override var lastRegisterChar: Char override var lastRegisterChar: Char
get() { get() {
return injector.registerGroup.lastRegisterChar return injector.registerGroup.lastRegisterChar
} }
set(_) {} set(_) {}
override fun storeText(editor: VimEditor, range: TextRange, type: SelectionType, isDelete: Boolean): Boolean { override fun storeText(caret: VimCaret, editor: VimEditor, range: TextRange, type: SelectionType, isDelete: Boolean): Boolean {
if (isCaretPrimary) { if (caret.isPrimary) {
return injector.registerGroup.storeText(editor, range, type, isDelete) return injector.registerGroup.storeText(editor, range, type, isDelete)
} }
val register = lastRegisterChar val register = lastRegisterChar
@@ -27,15 +27,15 @@ open class CaretRegisterStorageBase(private val isCaretPrimary: Boolean) : Caret
return super.storeText(editor, range, type, isDelete) return super.storeText(editor, range, type, isDelete)
} }
override fun getRegister(r: Char): Register? { override fun getRegister(caret: VimCaret, r: Char): Register? {
if (isCaretPrimary || !RegisterConstants.RECORDABLE_REGISTERS.contains(r)) { if (caret.isPrimary || !RegisterConstants.RECORDABLE_REGISTERS.contains(r)) {
return injector.registerGroup.getRegister(r) return injector.registerGroup.getRegister(r)
} }
return super.getRegister(r) ?: injector.registerGroup.getRegister(r) return super.getRegister(r) ?: injector.registerGroup.getRegister(r)
} }
override fun setKeys(register: Char, keys: List<KeyStroke>) { override fun setKeys(caret: VimCaret, register: Char, keys: List<KeyStroke>) {
if (isCaretPrimary) { if (caret.isPrimary) {
injector.registerGroup.setKeys(register, keys) injector.registerGroup.setKeys(register, keys)
} }
if (!RegisterConstants.RECORDABLE_REGISTERS.contains(register)) { if (!RegisterConstants.RECORDABLE_REGISTERS.contains(register)) {
@@ -44,8 +44,8 @@ open class CaretRegisterStorageBase(private val isCaretPrimary: Boolean) : Caret
return super.setKeys(register, keys) return super.setKeys(register, keys)
} }
override fun saveRegister(r: Char, register: Register) { override fun saveRegister(caret: VimCaret, r: Char, register: Register) {
if (isCaretPrimary) { if (caret.isPrimary) {
injector.registerGroup.saveRegister(r, register) injector.registerGroup.saveRegister(r, register)
} }
if (!RegisterConstants.RECORDABLE_REGISTERS.contains(r)) { if (!RegisterConstants.RECORDABLE_REGISTERS.contains(r)) {

View File

@@ -47,10 +47,6 @@ interface VimChangeGroup {
fun initInsert(editor: VimEditor, context: ExecutionContext, mode: VimStateMachine.Mode) fun initInsert(editor: VimEditor, context: ExecutionContext, mode: VimStateMachine.Mode)
fun editorCreated(editor: VimEditor?)
fun editorReleased(editor: VimEditor?)
fun processEscape(editor: VimEditor, context: ExecutionContext?, operatorArguments: OperatorArguments) fun processEscape(editor: VimEditor, context: ExecutionContext?, operatorArguments: OperatorArguments)
fun processEnter(editor: VimEditor, context: ExecutionContext) fun processEnter(editor: VimEditor, context: ExecutionContext)

View File

@@ -158,7 +158,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
} }
} }
if (type == null || if (type == null ||
operatorArguments.mode.inInsertMode || caret.registerStorage.storeText(editor, updatedRange, type, true) || operatorArguments.mode.inInsertMode || caret.registerStorage.storeText(caret, editor, updatedRange, type, true) ||
caret != editor.primaryCaret() // sticky tape for VIM-2703 todo remove in the next release caret != editor.primaryCaret() // sticky tape for VIM-2703 todo remove in the next release
) { ) {
val startOffsets = updatedRange.startOffsets val startOffsets = updatedRange.startOffsets

View File

@@ -24,6 +24,7 @@ import com.maddyhome.idea.vim.key.KeyMapping
import com.maddyhome.idea.vim.key.KeyMappingLayer import com.maddyhome.idea.vim.key.KeyMappingLayer
import com.maddyhome.idea.vim.key.MappingInfo import com.maddyhome.idea.vim.key.MappingInfo
import com.maddyhome.idea.vim.key.MappingOwner import com.maddyhome.idea.vim.key.MappingOwner
import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.key.ShortcutOwnerInfo import com.maddyhome.idea.vim.key.ShortcutOwnerInfo
import com.maddyhome.idea.vim.vimscript.model.expressions.Expression import com.maddyhome.idea.vim.vimscript.model.expressions.Expression
import javax.swing.KeyStroke import javax.swing.KeyStroke
@@ -73,4 +74,5 @@ interface VimKeyGroup {
val shortcutConflicts: MutableMap<KeyStroke, ShortcutOwnerInfo> val shortcutConflicts: MutableMap<KeyStroke, ShortcutOwnerInfo>
val savedShortcutConflicts: MutableMap<KeyStroke, ShortcutOwnerInfo> val savedShortcutConflicts: MutableMap<KeyStroke, ShortcutOwnerInfo>
var operatorFunction: OperatorFunction?
} }

View File

@@ -8,6 +8,7 @@ import com.maddyhome.idea.vim.key.KeyMapping
import com.maddyhome.idea.vim.key.KeyMappingLayer import com.maddyhome.idea.vim.key.KeyMappingLayer
import com.maddyhome.idea.vim.key.MappingInfo import com.maddyhome.idea.vim.key.MappingInfo
import com.maddyhome.idea.vim.key.MappingOwner import com.maddyhome.idea.vim.key.MappingOwner
import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.key.RequiredShortcut import com.maddyhome.idea.vim.key.RequiredShortcut
import com.maddyhome.idea.vim.key.RootNode import com.maddyhome.idea.vim.key.RootNode
import com.maddyhome.idea.vim.key.ShortcutOwnerInfo import com.maddyhome.idea.vim.key.ShortcutOwnerInfo
@@ -24,6 +25,8 @@ abstract class VimKeyGroupBase : VimKeyGroup {
val keyRoots: MutableMap<MappingMode, CommandPartNode<VimActionsInitiator>> = EnumMap(MappingMode::class.java) val keyRoots: MutableMap<MappingMode, CommandPartNode<VimActionsInitiator>> = EnumMap(MappingMode::class.java)
val keyMappings: MutableMap<MappingMode, KeyMapping> = EnumMap(MappingMode::class.java) val keyMappings: MutableMap<MappingMode, KeyMapping> = EnumMap(MappingMode::class.java)
override var operatorFunction: OperatorFunction? = null
override fun removeKeyMapping(modes: Set<MappingMode>, keys: List<KeyStroke>) { override fun removeKeyMapping(modes: Set<MappingMode>, keys: List<KeyStroke>) {
modes.map { getKeyMapping(it) }.forEach { it.delete(keys) } modes.map { getKeyMapping(it) }.forEach { it.delete(keys) }
} }

View File

@@ -83,7 +83,7 @@ interface VimSearchHelper {
spaceWords: Boolean, spaceWords: Boolean,
): Int ): Int
fun findNextWord(editor: VimEditor, searchFrom: Int, count: Int, bigWord: Boolean): Int fun findNextWord(editor: VimEditor, searchFrom: Int, count: Int, bigWord: Boolean): Long
fun findPattern( fun findPattern(
editor: VimEditor, editor: VimEditor,

View File

@@ -0,0 +1,117 @@
package com.maddyhome.idea.vim.api
import com.maddyhome.idea.vim.helper.CharacterHelper
import com.maddyhome.idea.vim.helper.CharacterHelper.charType
import kotlin.math.abs
import kotlin.math.min
abstract class VimSearchHelperBase : VimSearchHelper {
override fun findNextWord(editor: VimEditor, searchFrom: Int, count: Int, bigWord: Boolean): Long {
return findNextWord(editor.charsSequence(), searchFrom.toLong(), editor.fileSize(), count, bigWord, false)
}
companion object {
fun findNextWord(
chars: CharSequence,
pos: Long,
size: Long,
count: Int,
bigWord: Boolean,
spaceWords: Boolean,
): Long {
var _count = count
val step = if (_count >= 0) 1 else -1
_count = abs(_count)
var res = pos
for (i in 0 until _count) {
res = findNextWordOne(chars, res, size, step, bigWord, spaceWords)
if (res == pos || res == 0L || res == size - 1) {
break
}
}
return res
}
// TODO: 18.08.2022 Make private
fun findNextWordOne(
chars: CharSequence,
pos: Long,
size: Long,
step: Int,
bigWord: Boolean,
spaceWords: Boolean,
): Long {
var found = false
var _pos = if (pos < size) pos else min(size, (chars.length - 1).toLong())
// For back searches, skip any current whitespace so we start at the end of a word
if (step < 0 && _pos > 0) {
if (charType(chars[_pos - 1], bigWord) === CharacterHelper.CharacterType.WHITESPACE && !spaceWords) {
_pos = skipSpace(chars, _pos - 1, step, size) + 1
}
if (_pos > 0 && charType(chars[_pos], bigWord) !== charType(chars[_pos - 1], bigWord)) {
_pos += step
}
}
var res = _pos
if (_pos < 0 || _pos >= size) {
return _pos
}
var type = charType(chars[_pos], bigWord)
if (type === CharacterHelper.CharacterType.WHITESPACE && step < 0 && _pos > 0 && !spaceWords) {
type = charType(chars[_pos - 1], bigWord)
}
_pos += step
while (_pos in 0 until size && !found) {
val newType = charType(chars[_pos], bigWord)
if (newType !== type) {
if (newType === CharacterHelper.CharacterType.WHITESPACE && step >= 0 && !spaceWords) {
_pos = skipSpace(chars, _pos, step, size)
res = _pos
} else if (step < 0) {
res = _pos + 1
} else {
res = _pos
}
type = charType(chars[res], bigWord)
found = true
}
_pos += step
}
if (found) {
if (res < 0) { // (pos <= 0)
res = 0
} else if (res >= size) { // (pos >= size)
res = size - 1
}
} else if (_pos <= 0) {
res = 0
} else if (_pos >= size) {
res = size
}
return res
}
/**
* Skip whitespace starting with the supplied position.
*
* An empty line is considered a whitespace break.
*/
// TODO: 18.08.2022 Make private
fun skipSpace(chars: CharSequence, offset: Long, step: Int, size: Long): Long {
var _offset = offset
var prev = 0.toChar()
while (_offset in 0 until size) {
val c = chars[_offset.toInt()]
if (c == '\n' && c == prev) break
if (charType(c, false) !== CharacterHelper.CharacterType.WHITESPACE) break
prev = c
_offset += step
}
return if (_offset < size) _offset else size - 1
}
operator fun CharSequence.get(index: Long): Char {
return this[index.toInt()]
}
}
}

View File

@@ -95,7 +95,7 @@ abstract class VimMachineBase : VimMachine {
val operatedText = editor.deleteDryRun(range) ?: return null val operatedText = editor.deleteDryRun(range) ?: return null
val normalizedRange = operatedText.toNormalizedTextRange(editor) val normalizedRange = operatedText.toNormalizedTextRange(editor)
caret.registerStorage.storeText(editor, normalizedRange, operatedText.toType(), true) caret.registerStorage.storeText(caret, editor, normalizedRange, operatedText.toType(), true)
(editor as MutableVimEditor).delete(range) (editor as MutableVimEditor).delete(range)
val start = normalizedRange.startOffset val start = normalizedRange.startOffset

View File

@@ -12,4 +12,5 @@ fun Int.toMotion(): Motion.AbsoluteOffset {
} }
fun Int.toMotionOrError(): Motion = if (this < 0) Motion.Error else Motion.AbsoluteOffset(this) fun Int.toMotionOrError(): Motion = if (this < 0) Motion.Error else Motion.AbsoluteOffset(this)
fun Long.toMotionOrError(): Motion = if (this < 0) Motion.Error else Motion.AbsoluteOffset(this.toInt())
fun Int.toMotionOrNoMotion(): Motion = if (this < 0) Motion.NoMotion else Motion.AbsoluteOffset(this) fun Int.toMotionOrNoMotion(): Motion = if (this < 0) Motion.NoMotion else Motion.AbsoluteOffset(this)

View File

@@ -42,8 +42,28 @@ import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
* *
* Handler is called once for all carets * Handler is called once for all carets
*/ */
abstract class ShiftedSpecialKeyHandler : VimActionHandler.SingleExecution() { abstract class ShiftedSpecialKeyHandler : VimActionHandler.ConditionalMulticaret() {
final override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean { final override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
error("This method should not be executed")
}
override fun execute(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments,
): Boolean {
motion(editor, context, cmd, caret)
return true
}
override fun runAsMulticaret(
editor: VimEditor,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments,
): Boolean {
val startSel = OptionConstants.keymodel_startsel in (injector.optionService.getOptionValue(OptionScope.GLOBAL, OptionConstants.keymodelName) as VimString).value val startSel = OptionConstants.keymodel_startsel in (injector.optionService.getOptionValue(OptionScope.GLOBAL, OptionConstants.keymodelName) as VimString).value
if (startSel && !editor.inVisualMode && !editor.inSelectMode) { if (startSel && !editor.inVisualMode && !editor.inSelectMode) {
if (OptionConstants.selectmode_key in (injector.optionService.getOptionValue(OptionScope.GLOBAL, OptionConstants.selectmodeName) as VimString).value) { if (OptionConstants.selectmode_key in (injector.optionService.getOptionValue(OptionScope.GLOBAL, OptionConstants.selectmodeName) as VimString).value) {
@@ -53,7 +73,6 @@ abstract class ShiftedSpecialKeyHandler : VimActionHandler.SingleExecution() {
.toggleVisual(editor, 1, 0, VimStateMachine.SubMode.VISUAL_CHARACTER) .toggleVisual(editor, 1, 0, VimStateMachine.SubMode.VISUAL_CHARACTER)
} }
} }
motion(editor, context, cmd)
return true return true
} }
@@ -61,7 +80,7 @@ abstract class ShiftedSpecialKeyHandler : VimActionHandler.SingleExecution() {
* This method is called when `keymodel` doesn't contain `startsel`, * This method is called when `keymodel` doesn't contain `startsel`,
* or contains one of `continue*` values but in different mode. * or contains one of `continue*` values but in different mode.
*/ */
abstract fun motion(editor: VimEditor, context: ExecutionContext, cmd: Command) abstract fun motion(editor: VimEditor, context: ExecutionContext, cmd: Command, caret: VimCaret)
} }
/** /**
@@ -71,16 +90,16 @@ abstract class ShiftedSpecialKeyHandler : VimActionHandler.SingleExecution() {
* *
* Handler is called once for all carets * Handler is called once for all carets
*/ */
abstract class ShiftedArrowKeyHandler : VimActionHandler.SingleExecution() { abstract class ShiftedArrowKeyHandler(private val runBothCommandsAsMulticaret: Boolean) : VimActionHandler.ConditionalMulticaret() {
final override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
val keymodelOption = (injector.optionService.getOptionValue(OptionScope.GLOBAL, OptionConstants.keymodelName) as VimString).value
val startSel = OptionConstants.keymodel_startsel in keymodelOption
val inVisualMode = editor.inVisualMode
val inSelectMode = editor.inSelectMode
val continueSelectSelection = OptionConstants.keymodel_continueselect in keymodelOption && inSelectMode override fun runAsMulticaret(
val continueVisualSelection = OptionConstants.keymodel_continuevisual in keymodelOption && inVisualMode editor: VimEditor,
if (startSel || continueSelectSelection || continueVisualSelection) { context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments,
): Boolean {
val (inVisualMode, inSelectMode, withKey) = withKeyOrNot(editor)
if (withKey) {
if (!inVisualMode && !inSelectMode) { if (!inVisualMode && !inSelectMode) {
if (OptionConstants.selectmode_key in (injector.optionService.getOptionValue(OptionScope.GLOBAL, OptionConstants.selectmodeName) as VimString).value) { if (OptionConstants.selectmode_key in (injector.optionService.getOptionValue(OptionScope.GLOBAL, OptionConstants.selectmodeName) as VimString).value) {
injector.visualMotionGroup.enterSelectMode(editor, VimStateMachine.SubMode.VISUAL_CHARACTER) injector.visualMotionGroup.enterSelectMode(editor, VimStateMachine.SubMode.VISUAL_CHARACTER)
@@ -89,17 +108,54 @@ abstract class ShiftedArrowKeyHandler : VimActionHandler.SingleExecution() {
.toggleVisual(editor, 1, 0, VimStateMachine.SubMode.VISUAL_CHARACTER) .toggleVisual(editor, 1, 0, VimStateMachine.SubMode.VISUAL_CHARACTER)
} }
} }
motionWithKeyModel(editor, context, cmd) return true
} else { } else {
motionWithoutKeyModel(editor, context, cmd) return runBothCommandsAsMulticaret
} }
}
private fun withKeyOrNot(editor: VimEditor): Triple<Boolean, Boolean, Boolean> {
val keymodelOption =
(injector.optionService.getOptionValue(OptionScope.GLOBAL, OptionConstants.keymodelName) as VimString).value
val startSel = OptionConstants.keymodel_startsel in keymodelOption
val inVisualMode = editor.inVisualMode
val inSelectMode = editor.inSelectMode
val continueSelectSelection = OptionConstants.keymodel_continueselect in keymodelOption && inSelectMode
val continueVisualSelection = OptionConstants.keymodel_continuevisual in keymodelOption && inVisualMode
val withKey = startSel || continueSelectSelection || continueVisualSelection
return Triple(inVisualMode, inSelectMode, withKey)
}
override fun execute(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments,
): Boolean {
if (runBothCommandsAsMulticaret) {
val (_, _, withKey) = withKeyOrNot(editor)
if (withKey) {
motionWithKeyModel(editor, caret, context, cmd)
} else {
motionWithoutKeyModel(editor, context, cmd)
}
} else {
motionWithKeyModel(editor, caret, context, cmd)
}
return true
}
final override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
motionWithoutKeyModel(editor, context, cmd)
return true return true
} }
/** /**
* This method is called when `keymodel` contains `startsel`, or one of `continue*` values in corresponding mode * This method is called when `keymodel` contains `startsel`, or one of `continue*` values in corresponding mode
*/ */
abstract fun motionWithKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) abstract fun motionWithKeyModel(editor: VimEditor, caret: VimCaret, context: ExecutionContext, cmd: Command)
/** /**
* This method is called when `keymodel` doesn't contain `startsel`, * This method is called when `keymodel` doesn't contain `startsel`,

View File

@@ -61,6 +61,30 @@ sealed class VimActionHandler(myRunForEachCaret: Boolean) : EditorActionHandlerB
): Boolean ): Boolean
} }
abstract class ConditionalMulticaret : VimActionHandler(false) {
abstract fun runAsMulticaret(
editor: VimEditor,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments,
): Boolean
abstract fun execute(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments,
): Boolean
abstract fun execute(
editor: VimEditor,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments,
): Boolean
}
final override fun baseExecute( final override fun baseExecute(
editor: VimEditor, editor: VimEditor,
caret: VimCaret, caret: VimCaret,
@@ -71,6 +95,16 @@ sealed class VimActionHandler(myRunForEachCaret: Boolean) : EditorActionHandlerB
return when (this) { return when (this) {
is ForEachCaret -> execute(editor, caret, context, cmd, operatorArguments) is ForEachCaret -> execute(editor, caret, context, cmd, operatorArguments)
is SingleExecution -> execute(editor, context, cmd, operatorArguments) is SingleExecution -> execute(editor, context, cmd, operatorArguments)
is ConditionalMulticaret -> {
val runAsMulticaret = runAsMulticaret(editor, context, cmd, operatorArguments)
return if (runAsMulticaret) {
var res = true
editor.forEachCaret { res = execute(editor, it, context, cmd, operatorArguments) && res }
res
} else {
execute(editor, context, cmd, operatorArguments)
}
}
} }
} }
} }

View File

@@ -132,7 +132,7 @@ sealed class VisualOperatorActionHandler : EditorActionHandlerBase(false) {
logger.debug { "Count of selection segments: ${selections.size}" } logger.debug { "Count of selection segments: ${selections.size}" }
logger.debug { selections.values.joinToString("\n") { vimSelection -> "Caret: $vimSelection" } } logger.debug { selections.values.joinToString("\n") { vimSelection -> "Caret: $vimSelection" } }
val commandWrapper = VisualStartFinishWrapper(editor, cmd) val commandWrapper = VisualStartFinishWrapper(editor, cmd, this)
commandWrapper.start() commandWrapper.start()
val res = arrayOf(true) val res = arrayOf(true)
@@ -230,7 +230,11 @@ sealed class VisualOperatorActionHandler : EditorActionHandlerBase(false) {
} }
} }
private class VisualStartFinishWrapper(private val editor: VimEditor, private val cmd: Command) { private class VisualStartFinishWrapper(
private val editor: VimEditor,
private val cmd: Command,
private val visualOperatorActionHandler: VisualOperatorActionHandler
) {
private val visualChanges = mutableMapOf<VimCaret, VisualChange?>() private val visualChanges = mutableMapOf<VimCaret, VisualChange?>()
fun start() { fun start() {
@@ -256,9 +260,13 @@ sealed class VisualOperatorActionHandler : EditorActionHandlerBase(false) {
fun finish(res: Boolean) { fun finish(res: Boolean) {
logger.debug("Finish visual command. Result: $res") logger.debug("Finish visual command. Result: $res")
if (CommandFlags.FLAG_MULTIKEY_UNDO !in cmd.flags && CommandFlags.FLAG_EXPECT_MORE !in cmd.flags) { if (visualOperatorActionHandler.id != "VimVisualOperatorAction" ||
logger.debug("Not multikey undo - exit visual") injector.keyGroup.operatorFunction?.postProcessSelection() != false
editor.exitVisualModeNative() ) {
if (CommandFlags.FLAG_MULTIKEY_UNDO !in cmd.flags && CommandFlags.FLAG_EXPECT_MORE !in cmd.flags) {
logger.debug("Not multikey undo - exit visual")
editor.exitVisualModeNative()
}
} }
if (res) { if (res) {

View File

@@ -15,22 +15,23 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.maddyhome.idea.vim.key
package com.maddyhome.idea.vim.key; import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.intellij.openapi.actionSystem.DataContext; import com.maddyhome.idea.vim.command.SelectionType
import com.intellij.openapi.editor.Editor;
import com.maddyhome.idea.vim.command.SelectionType;
import org.jetbrains.annotations.NotNull;
/** /**
* @author vlan * @author vlan
*/ */
public interface OperatorFunction { interface OperatorFunction {
/** /**
* The value of 'operatorfunc' to be used as the operator function in 'g@'. * The value of 'operatorfunc' to be used as the operator function in 'g@'.
* <p> *
*
* Make sure to synchronize your function properly using read/write actions. * Make sure to synchronize your function properly using read/write actions.
*/ */
boolean apply(@NotNull Editor editor, @NotNull DataContext context, @NotNull SelectionType selectionType); fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean
fun postProcessSelection(): Boolean = true
} }

View File

@@ -21,8 +21,8 @@ interface VimPut {
editor: VimEditor, editor: VimEditor,
context: ExecutionContext, context: ExecutionContext,
data: PutData, data: PutData,
updateVisualMarks: Boolean = false,
operatorArguments: OperatorArguments, operatorArguments: OperatorArguments,
updateVisualMarks: Boolean = false,
): Boolean ): Boolean
fun putTextForCaret(editor: VimEditor, caret: VimCaret, context: ExecutionContext, data: PutData, updateVisualMarks: Boolean = false): Boolean fun putTextForCaret(editor: VimEditor, caret: VimCaret, context: ExecutionContext, data: PutData, updateVisualMarks: Boolean = false): Boolean

View File

@@ -25,8 +25,8 @@ abstract class VimPutBase : VimPut {
editor: VimEditor, editor: VimEditor,
context: ExecutionContext, context: ExecutionContext,
data: PutData, data: PutData,
updateVisualMarks: Boolean,
operatorArguments: OperatorArguments, operatorArguments: OperatorArguments,
updateVisualMarks: Boolean,
): Boolean { ): Boolean {
val additionalData = collectPreModificationData(editor, data) val additionalData = collectPreModificationData(editor, data)
deleteSelectedText(editor, data, operatorArguments) deleteSelectedText(editor, data, operatorArguments)
@@ -67,15 +67,18 @@ abstract class VimPutBase : VimPut {
val caretsAndSelections = data.visualSelection?.caretsAndSelections ?: return val caretsAndSelections = data.visualSelection?.caretsAndSelections ?: return
val selection = caretsAndSelections[currentCaret] ?: caretsAndSelections.firstOrNull()?.value ?: return val selection = caretsAndSelections[currentCaret] ?: caretsAndSelections.firstOrNull()?.value ?: return
var fistIndex = selection.vimStart val leftIndex = min(selection.vimStart, selection.vimEnd)
val lastIndex = fistIndex + textLength - 1 val rightIndex = leftIndex + textLength - 1
if (wasTextInsertedLineWise(text)) { val rangeForMarks = if (wasTextInsertedLineWise(text)) {
// here we skip the \n char before the inserted text // here we skip the \n char after the inserted text
fistIndex += 1 TextRange(leftIndex, rightIndex - 1)
} else {
TextRange(leftIndex, rightIndex)
} }
injector.markGroup.setVisualSelectionMarks(editor, TextRange(fistIndex, lastIndex)) editor.vimLastSelectionType = SelectionType.CHARACTER_WISE
injector.markGroup.setVisualSelectionMarks(editor, rangeForMarks)
} }
protected fun deleteSelectedText(editor: VimEditor, data: PutData, operatorArguments: OperatorArguments) { protected fun deleteSelectedText(editor: VimEditor, data: PutData, operatorArguments: OperatorArguments) {