1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-09-15 17:32:14 +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.PropertyBased
import _Self.buildTypes.Qodana
import _Self.buildTypes.TestsForIntelliJ20222
import _Self.buildTypes.TestsForIntelliJEAP
import _Self.subprojects.GitHub
import _Self.subprojects.OldTests
@@ -39,6 +40,7 @@ object Project : Project({
// Builds
buildType(TestsForIntelliJEAP)
buildType(TestsForIntelliJ20222)
buildType(PropertyBased)
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 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)
 
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][github]](https://github.com/marquiswang)
 
Marquis Wang
* [![icon][mail]](mailto:madgnome@gmail.com)
[![icon][github-off]](#)
[![icon][github]](https://github.com/madgnome)
 
Julien Hoarau
* [![icon][mail]](mailto:masanobu.imai@gmail.com)
[![icon][github-off]](#)
[![icon][github]](https://github.com/masanobuimai)
 
Masanobu Imai
* [![icon][mail]](mailto:poxvuibr@gmail.com)
@@ -57,7 +89,7 @@ Contributors:
 
John Lindquist
* [![icon][mail]](mailto:iklotzko@ltech.com)
[![icon][github-off]](#)
[![icon][github]](https://github.com/iklotzko)
 
Ira Klotzko
* [![icon][mail]](mailto:alex@selesse.com)
@@ -65,7 +97,7 @@ Contributors:
 
Alex Selesse
* [![icon][mail]](mailto:dbennett@palantir.com)
[![icon][github-off]](#)
[![icon][github]](https://github.com/dathanb)
 
Dathan Bennett
* [![icon][mail]](mailto:kphayen@gmail.com)
@@ -77,11 +109,11 @@ Contributors:
 
Alexey Shmalko
* [![icon][mail]](mailto:a.m.brookins@gmail.com)
[![icon][github-off]](#)
[![icon][github]](https://github.com/abrookins)
 
Andrew Brookins
* [![icon][mail]](mailto:changwang83@gmail.com)
[![icon][github-off]](#)
[![icon][github]](https://github.com/changwang)
 
Chang Wang
* [![icon][mail]](mailto:josejaime.sanchez@gmail.com)
@@ -89,19 +121,19 @@ Contributors:
 
Jaime Sanchez
* [![icon][mail]](mailto:thomas@homburg.dk)
[![icon][github-off]](#)
[![icon][github]](https://github.com/homburg)
 
Thomas B Homburg
* [![icon][mail]](mailto:smartbomb@server.fake)
[![icon][github-off]](#)
[![icon][github]](https://github.com/smartbomb)
 
smartbomb
* [![icon][mail]](mailto:tuomas.tynkkynen@iki.fi)
[![icon][github-off]](#)
[![icon][github]](https://github.com/dezgeg)
 
Tuomas Tynkkynen
* [![icon][mail]](mailto:jackson@donorschoose.org)
[![icon][github-off]](#)
[![icon][github]](https://github.com/jdpopkin)
 
Jackson Popkin
* [![icon][mail]](mailto:yuyuyu1999@gmail.com)
@@ -109,7 +141,7 @@ Contributors:
 
Teruo Kunihiro
* [![icon][mail]](mailto:lubashka.994@mail.ru)
[![icon][github-off]](#)
[![icon][github]](https://github.com/lubba)
 
Liubov Paina
* [![icon][mail]](mailto:me@dhleong.net)
@@ -137,7 +169,7 @@ Contributors:
 
tieTYT
* [![icon][mail]](mailto:nickgieschen@gmail.com)
[![icon][github-off]](#)
[![icon][github]](https://github.com/nickgieschen)
 
Nick Gieschen
* [![icon][mail]](mailto:ikenox@gmail.com)
@@ -149,7 +181,7 @@ Contributors:
 
Maximilian Luz
* [![icon][mail]](mailto:vparfinenko@excelsior-usa.com)
[![icon][github-off]](#)
[![icon][github]](https://github.com/cypok)
 
Vladimir Parfinenko
* [![icon][mail]](mailto:hassmann@hwdev.de)
@@ -193,7 +225,7 @@ Contributors:
 
Marcel Hild
* [![icon][mail]](mailto:vedranb@gmail.com)
[![icon][github-off]](#)
[![icon][github]](https://github.com/vedran)
 
Vedran Budimcic
* [![icon][mail]](mailto:andreigasparovici1@gmail.com)
@@ -208,10 +240,13 @@ Contributors:
[![icon][github]](https://github.com/TonyArra)
 
Tony Arra
* [![icon][mail]](mailto:bradziolko@gmail.com)
[![icon][github]](https://github.com/bradziolko)
* [![icon][mail]](mailto:mj@ziolko.dev)
[![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][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
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
### Features:

View File

@@ -11,7 +11,7 @@ IdeaVim
[![Gitter][gitter-svg]][gitter]
[![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:
* [Bug tracker](https://youtrack.jetbrains.com/issues/VIM)
@@ -36,7 +36,7 @@ Setup
- IdeaVim can be installed via `Settings | Plugins`.
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.
@@ -88,7 +88,7 @@ Here are some examples of supported vim features and commands:
* Vim web help
* `~/.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
* 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.
Emulated Vim Plugins
IdeaVim Plugins
--------------------
See [doc/emulated-plugins.md](https://github.com/JetBrains/ideavim/wiki/Emulated-plugins)

View File

@@ -26,7 +26,7 @@ plugins {
java
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"
// 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 {
tasks.compileJava {
// 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") {
doLast {
val uncheckedEmails = setOf(

View File

@@ -1,14 +1,13 @@
# suppress inspection "UnusedProperty" for whole file
ideaVersion=LATEST-EAP-SNAPSHOT
ideaVersion=2022.2.2
downloadIdeaSources=true
instrumentPluginCode=true
version=SNAPSHOT
javaVersion=11
version=chylex-13
javaVersion=17
remoteRobotVersion=0.11.15
antlrVersion=4.10.1
# Please don't forget to update kotlin version in buildscript section
kotlinVersion=1.6.21
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.TypedActionHandler;
import com.intellij.openapi.editor.event.*;
import com.intellij.openapi.util.Disposer;
import com.maddyhome.idea.vim.helper.HandlerInjector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -101,32 +102,41 @@ public class EventFacade {
EditorFactory.getInstance().addEditorFactoryListener(listener, parentDisposable);
}
public void addEditorMouseListener(@NotNull Editor editor, @NotNull EditorMouseListener listener) {
editor.addEditorMouseListener(listener);
public void addEditorMouseListener(@NotNull Editor editor,
@NotNull EditorMouseListener listener,
@NotNull Disposable disposable) {
editor.addEditorMouseListener(listener, disposable);
}
public void removeEditorMouseListener(@NotNull Editor editor, @NotNull EditorMouseListener 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);
Disposer.register(disposable, () -> component.removeMouseListener(mouseListener));
}
public void removeComponentMouseListener(@NotNull Component component, @NotNull MouseListener mouseListener) {
component.removeMouseListener(mouseListener);
}
public void addEditorMouseMotionListener(@NotNull Editor editor, @NotNull EditorMouseMotionListener listener) {
editor.addEditorMouseMotionListener(listener);
public void addEditorMouseMotionListener(@NotNull Editor editor,
@NotNull EditorMouseMotionListener listener,
@NotNull Disposable disposable) {
editor.addEditorMouseMotionListener(listener, disposable);
}
public void removeEditorMouseMotionListener(@NotNull Editor editor, @NotNull EditorMouseMotionListener listener) {
editor.removeEditorMouseMotionListener(listener);
}
public void addEditorSelectionListener(@NotNull Editor editor, @NotNull SelectionListener listener) {
editor.getSelectionModel().addSelectionListener(listener);
public void addEditorSelectionListener(@NotNull Editor editor,
@NotNull SelectionListener listener,
@NotNull Disposable disposable) {
editor.getSelectionModel().addSelectionListener(listener, disposable);
}
public void removeEditorSelectionListener(@NotNull Editor editor, @NotNull SelectionListener listener) {

View File

@@ -114,7 +114,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
@Override
public void dispose() {
LOG.debug("disposeComponent");
turnOffPlugin();
turnOffPlugin(false);
LOG.debug("done");
}
@@ -272,7 +272,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
if (isEnabled() == enabled) return;
if (!enabled) {
getInstance().turnOffPlugin();
getInstance().turnOffPlugin(true);
}
getInstance().enabled = enabled;
@@ -365,12 +365,14 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
VimListenerManager.INSTANCE.turnOn();
}
private void turnOffPlugin() {
private void turnOffPlugin(boolean unsubscribe) {
SearchGroup searchGroup = getSearchIfCreated();
if (searchGroup != null) {
searchGroup.turnOff();
}
VimListenerManager.INSTANCE.turnOff();
if (unsubscribe) {
VimListenerManager.INSTANCE.turnOff();
}
ExEntryPanel.fullReset();
// 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 || 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 (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.VimCaret
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandFlags
@@ -40,7 +41,7 @@ import com.maddyhome.idea.vim.newapi.ij
import java.util.*
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) {
VimPlugin.showMessage(MessageHelper.message("E774"))
return false
@@ -49,7 +50,7 @@ private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textR
val saveRepeatHandler = VimRepeater.repeatHandler
VimPlugin.getMark().setChangeMarks(editor, textRange)
KeyHandler.getInstance().reset(editor)
val result = operatorFunction.apply(editor.ij, context.ij, selectionType)
val result = operatorFunction.apply(editor, context, selectionType)
VimRepeater.repeatHandler = saveRepeatHandler
return result
}

View File

@@ -127,7 +127,7 @@ object VimExtensionFacade {
/** Sets the value of 'operatorfunc' to be used as the operator function in 'g@'. */
@JvmStatic
fun setOperatorFunction(function: OperatorFunction) {
VimPlugin.getKey().setOperatorFunction(function)
VimPlugin.getKey().operatorFunction = function
}
/**
@@ -186,7 +186,7 @@ object VimExtensionFacade {
@JvmStatic
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
}
@@ -199,7 +199,7 @@ object VimExtensionFacade {
/** Set the current contents of the given register */
@JvmStatic
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 */

View File

@@ -17,10 +17,11 @@
*/
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.application.runWriteAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
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.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim
import java.util.*
class CommentaryExtension : VimExtension {
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
if (mode !== VimStateMachine.Mode.VISUAL) {
editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset)
}
return runWriteAction {
try {
// Treat block- and character-wise selections as block comments. Be ready to fall back to if the first action
// isn't available
val actions = if (selectionType === SelectionType.LINE_WISE) {
listOf(IdeActions.ACTION_COMMENT_LINE, IdeActions.ACTION_COMMENT_BLOCK)
} else {
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)
}
// Treat block- and character-wise selections as block comments. Be ready to fall back to if the first action
// isn't available
val actions = if (selectionType === SelectionType.LINE_WISE) {
listOf(IdeActions.ACTION_COMMENT_LINE, IdeActions.ACTION_COMMENT_BLOCK)
} else {
listOf(IdeActions.ACTION_COMMENT_BLOCK, IdeActions.ACTION_COMMENT_LINE)
}
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.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
// is still using the old mapping
@@ -132,14 +157,19 @@ class CommentaryExtension : VimExtension {
private class CommentaryOperatorHandler : OperatorFunction, ExtensionHandler {
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) {
setOperatorFunction(this)
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
}
override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean {
val range = VimPlugin.getMark().getChangeMarks(editor.vim) ?: return false
return doCommentary(editor.vim, context.vim, range, selectionType, true)
override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean {
val range = VimPlugin.getMark().getChangeMarks(editor) ?: return false
return doCommentary(editor, context, range, selectionType, true)
}
}
@@ -174,7 +204,7 @@ class CommentaryExtension : VimExtension {
context: ExecutionContext,
count: Int,
rawCount: Int,
argument: Argument?
argument: Argument?,
): TextRange? {
val nativeEditor = (editor as IjVimEditor).editor

View File

@@ -18,7 +18,6 @@
package com.maddyhome.idea.vim.extension.exchange
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.LogicalPosition
@@ -133,7 +132,7 @@ class VimExchangeExtension : VimExtension {
val subMode = editor.subMode
// Leave visual mode to create selection marks
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")
}
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 {
val attributes = editor.colorsScheme.getAttributes(EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES)
val hlArea = when (ex.type) {

View File

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

View File

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

View File

@@ -18,7 +18,6 @@
package com.maddyhome.idea.vim.extension.replacewithregister
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext
@@ -115,21 +114,22 @@ class ReplaceWithRegister : VimExtension {
}
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 visualSelection = PutData.VisualSelection(
mapOf(
editor.caretModel.primaryCaret.vim to VimSelection.create(
vimEditor.primaryCaret() to VimSelection.create(
range.startOffset,
range.endOffset - 1,
selectionType,
IjVimEditor(editor)
vimEditor
)
),
selectionType
)
// todo multicaret
doReplace(editor, editor.vim.primaryCaret(), visualSelection)
doReplace(editor, vimEditor.primaryCaret(), visualSelection)
return true
}
@@ -152,7 +152,7 @@ class ReplaceWithRegister : VimExtension {
private fun doReplace(editor: Editor, caret: VimCaret, visualSelection: PutData.VisualSelection) {
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 usedText = savedRegister.text
@@ -185,8 +185,8 @@ class ReplaceWithRegister : VimExtension {
)
}
caret.registerStorage.saveRegister(savedRegister.name, savedRegister)
caret.registerStorage.saveRegister(VimPlugin.getRegister().defaultRegister, savedRegister)
caret.registerStorage.saveRegister(caret, savedRegister.name, savedRegister)
caret.registerStorage.saveRegister(caret, VimPlugin.getRegister().defaultRegister, savedRegister)
}
}
}

View File

@@ -17,12 +17,12 @@
*/
package com.maddyhome.idea.vim.extension.surround
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext
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.injector
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.setRegisterForCaret
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.newapi.IjVimCaret
import com.maddyhome.idea.vim.newapi.IjVimEditor
@@ -85,22 +86,20 @@ class VimSurroundExtension : VimExtension {
override val isRepeatable = true
override fun execute(editor: VimEditor, context: ExecutionContext) {
setOperatorFunction(Operator())
setOperatorFunction(Operator(supportsMultipleCursors = false)) // TODO
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
}
}
private class VSurroundHandler : ExtensionHandler {
override fun execute(editor: VimEditor, context: ExecutionContext) {
val selectionStart = editor.ij.caretModel.primaryCaret.selectionStart
// 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
}
runWriteAction {
// Leave visual mode
executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
editor.ij.caretModel.moveToOffset(selectionStart)
}
}
}
@@ -121,6 +120,10 @@ class VimSurroundExtension : VimExtension {
companion object {
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
val surroundings = editor.sortedCarets()
.map {
@@ -219,25 +222,44 @@ class VimSurroundExtension : VimExtension {
}
}
private class Operator : OperatorFunction {
override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean {
private class Operator(private val supportsMultipleCursors: Boolean) : OperatorFunction {
override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean {
val editor = vimEditor.ij
val c = getChar(editor)
if (c.code == 0) return true
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 {
val change = VimPlugin.getChange()
val leftSurround = pair.first
val primaryCaret = editor.caretModel.primaryCaret
change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, leftSurround)
change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.endOffset + leftSurround.length, pair.second)
// Jump back to start
executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor)
if (supportsMultipleCursors) {
editor.runWithEveryCaretAndRestore {
applyOnce(editor, change, pair)
}
}
else {
applyOnce(editor, change, pair)
// Jump back to start
executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor)
}
}
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) {
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.collect.ImmutableSet;
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.IdeActions;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
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.impl.TextRangeInterval;
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.psi.PsiFile;
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.VisualModeHelperKt;
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.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.OptionScope;
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString;
import kotlin.Pair;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.text.StringsKt;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -85,6 +97,8 @@ public class ChangeGroup extends VimChangeGroupBase {
private final List<VimInsertListener> insertListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private long lastShownTime = 0L;
/**
* Inserts a new line above the caret position
*
@@ -142,14 +156,12 @@ public class ChangeGroup extends VimChangeGroupBase {
}
};
@Override
public void editorCreated(VimEditor editor) {
EventFacade.getInstance().addEditorMouseListener(((IjVimEditor) editor).getEditor(), listener);
public void editorCreated(Editor editor, @NotNull Disposable disposable) {
EventFacade.getInstance().addEditorMouseListener(editor, listener, disposable);
}
@Override
public void editorReleased(VimEditor editor) {
EventFacade.getInstance().removeEditorMouseListener(((IjVimEditor) editor).getEditor(), listener);
public void editorReleased(Editor editor) {
EventFacade.getInstance().removeEditorMouseListener(editor, listener);
}
@Override
@@ -571,17 +583,31 @@ public class ChangeGroup extends VimChangeGroupBase {
final int startOffset = injector.getEngineEditorHelper().getLineStartForOffset(editor, range.getStartOffset());
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();
if (joinLinesAction != null) {
VimInjectorKt.getInjector().getActionExecutor().executeAction(joinLinesAction, context);
Project project = ijEditor.getProject();
Function0<Unit> actionExecution = () -> {
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
@@ -773,6 +799,23 @@ public class ChangeGroup extends VimChangeGroupBase {
@NotNull TextRange selectedRange,
final int count,
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();
boolean alpha = nf.contains("alpha");
boolean hex = nf.contains("hex");

View File

@@ -232,7 +232,7 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
KeyHandler.getInstance().reset(new IjVimEditor(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) {

View File

@@ -73,8 +73,6 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
private static final Logger logger = Logger.getInstance(KeyGroup.class);
private @Nullable OperatorFunction operatorFunction = null;
public void registerRequiredShortcutKeys(@NotNull VimEditor editor) {
EventFacade.getInstance()
.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) {
final Element conflictsElement = new Element(SHORTCUT_CONFLICTS_ELEMENT);
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.FileEditorManagerEvent;
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.EditorWindow;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
@@ -1168,15 +1170,25 @@ public class MotionGroup extends VimMotionGroupBase {
@Override
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();
}
@Override
public int moveCaretGotoNextTab(@NotNull VimEditor editor, @NotNull ExecutionContext context, int rawCount) {
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();
}

View File

@@ -92,7 +92,7 @@ public class ProcessGroup extends VimProcessGroupBase {
String initText = getRange(((IjVimEditor) editor).getEditor(), cmd);
VimStateMachine.getInstance(editor).pushModes(VimStateMachine.Mode.CMD_LINE, VimStateMachine.SubMode.NONE);
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
@@ -123,7 +123,7 @@ public class ProcessGroup extends VimProcessGroupBase {
logger.debug("processing command");
final String text = panel.getText();
String text = panel.getText();
if (!panel.getLabel().equals(":")) {
// 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());
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) {
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)
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)) {
group.add(bookmark, type)
return bookmark

View File

@@ -190,7 +190,12 @@ class PutGroup : VimPutBase() {
}
}
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") }
listOf(caret.offset + 1)
} else listOf(caret.offset)

View File

@@ -211,7 +211,7 @@ class YankGroup : YankGroupBase() {
var result = true
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
}

View File

@@ -22,6 +22,7 @@ package com.maddyhome.idea.vim.helper
import com.intellij.codeWithMe.ClientId
import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.CaretState
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ex.util.EditorUtil
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
@@ -106,3 +107,41 @@ val Caret.vimLine: Int
*/
val Editor.vimLine: Int
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.util.PsiTreeUtil;
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.common.CharacterPosition;
import com.maddyhome.idea.vim.common.Direction;
@@ -1201,7 +1202,7 @@ public class SearchHelper {
int last = -1;
int res = start;
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) {
break;
}
@@ -1230,105 +1231,6 @@ public class SearchHelper {
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,
@NotNull TextRange textRange,
final boolean alpha,
@@ -1654,10 +1556,10 @@ public class SearchHelper {
if ((!onWordStart && !(startSpace && isOuter)) || hasSelection || (count > 1 && 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 {
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);
@@ -1805,7 +1707,7 @@ public class SearchHelper {
if (step > 0 && pos < size - 1) {
if (CharacterHelper.charType(chars.charAt(pos + 1), bigWord) == CharacterHelper.CharacterType.WHITESPACE &&
!spaceWords) {
pos = skipSpace(chars, pos + 1, step, size) - 1;
pos = (int)(VimSearchHelperBase.Companion.skipSpace(chars, pos + 1, step, size) - 1);
}
if (pos < size - 1 &&
CharacterHelper.charType(chars.charAt(pos), bigWord) !=
@@ -1830,7 +1732,7 @@ public class SearchHelper {
res = pos - 1;
}
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;
}
else {
@@ -1858,34 +1760,6 @@ public class SearchHelper {
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
*

View File

@@ -25,6 +25,7 @@ import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.command.impl.UndoManagerImpl
import com.intellij.openapi.command.undo.UndoManager
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.injector
import com.maddyhome.idea.vim.common.ChangesListener
@@ -48,16 +49,17 @@ class UndoRedoHelper : UndoRedoBase() {
override fun undo(context: ExecutionContext): Boolean {
val ijContext = context.context as DataContext
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)
if (fileEditor != null && undoManager.isUndoAvailable(fileEditor)) {
val editor = CommonDataKeys.EDITOR.getData(context.ij)?.vim
if (undoManager.isUndoAvailable(fileEditor)) {
if (injector.optionService.isSet(OptionScope.GLOBAL, IjVimOptionService.oldUndo)) {
SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) }
} 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 hasSelection = ijCaret.hasSelection()
if (hasSelection) {
@@ -78,16 +80,17 @@ class UndoRedoHelper : UndoRedoBase() {
override fun redo(context: ExecutionContext): Boolean {
val ijContext = context.context as DataContext
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 editor = CommonDataKeys.EDITOR.getData(context.ij)?.vim
if (fileEditor != null && undoManager.isRedoAvailable(fileEditor)) {
if (undoManager.isRedoAvailable(fileEditor)) {
if (injector.optionService.isSet(OptionScope.GLOBAL, IjVimOptionService.oldUndo)) {
SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) }
} else {
performUntilFileChanges(editor, { undoManager.isRedoAvailable(fileEditor) }, { undoManager.redo(fileEditor) })
performUntilFileChanges(vimEditor, { undoManager.isRedoAvailable(fileEditor) }, { undoManager.redo(fileEditor) })
CommandProcessor.getInstance().runUndoTransparentAction {
editor?.carets()?.forEach { it.ij.removeSelection() }
vimEditor.carets().forEach { it.ij.removeSelection() }
}
}
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.ex.DocumentEx
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.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.jetbrains.rd.util.lifetime.intersect
import com.maddyhome.idea.vim.EventFacade
import com.maddyhome.idea.vim.KeyHandler
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.skipNDragEvents
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.options.OptionConstants
import com.maddyhome.idea.vim.options.OptionScope
@@ -153,17 +156,26 @@ object VimListenerManager {
}
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)
Disposer.register(disposable) { editor.contentComponent.removeKeyListener(VimKeyListener) }
val eventFacade = EventFacade.getInstance()
eventFacade.addEditorMouseListener(editor, EditorMouseHandler)
eventFacade.addEditorMouseMotionListener(editor, EditorMouseHandler)
eventFacade.addEditorSelectionListener(editor, EditorSelectionHandler)
eventFacade.addComponentMouseListener(editor.contentComponent, ComponentMouseListener)
eventFacade.addEditorMouseListener(editor, EditorMouseHandler, disposable)
eventFacade.addEditorMouseMotionListener(editor, EditorMouseHandler, disposable)
eventFacade.addEditorSelectionListener(editor, EditorSelectionHandler, disposable)
eventFacade.addComponentMouseListener(editor.contentComponent, ComponentMouseListener, disposable)
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) {
@@ -177,7 +189,7 @@ object VimListenerManager {
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) {
remove(event.editor, true)
VimPlugin.getMark().editorReleased(event)
}
}

View File

@@ -53,7 +53,7 @@ class IjVimCaret(val caret: Caret) : VimCaretBase() {
get() {
var storage = this.caret.registerStorage
if (storage == null) {
storage = CaretRegisterStorageBase(editor.primaryCaret().ij == caret)
storage = CaretRegisterStorageBase()
this.caret.registerStorage = storage
}
return storage
@@ -87,6 +87,8 @@ class IjVimCaret(val caret: Caret) : VimCaretBase() {
}
override val vimLine: Int
get() = this.caret.vimLine
override val isPrimary: Boolean
get() = editor.primaryCaret().ij == this.caret
override fun moveToOffset(offset: Int) {
// 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.maddyhome.idea.vim.api.VimCaret
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.helper.SearchHelper
import com.maddyhome.idea.vim.helper.SearchOptions
import java.util.*
@Service
class IjVimSearchHelper : VimSearchHelper {
class IjVimSearchHelper : VimSearchHelperBase() {
override fun findNextParagraph(editor: VimEditor, caret: VimCaret, count: Int, allowBlanks: Boolean): Int {
return SearchHelper.findNextParagraph(
(editor as IjVimEditor).editor,
@@ -121,15 +121,6 @@ class IjVimSearchHelper : VimSearchHelper {
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(
editor: VimEditor,
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.eventLog.EventLogGroup
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.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.key.ShortcutOwner
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.SHIFT_DOWN_MASK
import java.awt.event.KeyEvent
import javax.swing.KeyStroke
internal class ShortcutConflictState : ApplicationUsagesCollector() {
@@ -37,93 +36,15 @@ internal class ShortcutConflictState : ApplicationUsagesCollector() {
override fun getGroup(): EventLogGroup = GROUP
override fun getMetrics(): Set<MetricEvent> {
return setOf(
HANDLERS.metric(
CTRL_1 withKeyStroke KeyStroke.getKeyStroke('1'.code, CTRL_DOWN_MASK),
CTRL_2 withKeyStroke KeyStroke.getKeyStroke('2'.code, CTRL_DOWN_MASK),
CTRL_3 withKeyStroke KeyStroke.getKeyStroke('3'.code, CTRL_DOWN_MASK),
CTRL_4 withKeyStroke KeyStroke.getKeyStroke('4'.code, CTRL_DOWN_MASK),
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),
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 })
val metrics = mutableSetOf<MetricEvent>()
keyStrokes.forEach { keystroke ->
getHandlersForShortcut(keystroke)
.filter { !setOf(HandledModes.INSERT_UNDEFINED, HandledModes.NORMAL_UNDEFINED, HandledModes.VISUAL_AND_SELECT_UNDEFINED).contains(it) }
.forEach { mode ->
metrics += HANDLER.metric(keystroke.toReadableString(), mode)
}
}
return metrics
}
private fun getHandlersForShortcut(shortcut: KeyStroke): List<HandledModes> {
@@ -162,173 +83,105 @@ internal class ShortcutConflictState : ApplicationUsagesCollector() {
companion object {
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 CTRL_2 = EventFields.StringList("Ctrl-2", values)
private val CTRL_3 = EventFields.StringList("Ctrl-3", values)
private val CTRL_4 = EventFields.StringList("Ctrl-4", values)
private val CTRL_5 = EventFields.StringList("Ctrl-5", values)
private val CTRL_6 = EventFields.StringList("Ctrl-6", values)
private val CTRL_7 = EventFields.StringList("Ctrl-7", values)
private val CTRL_8 = EventFields.StringList("Ctrl-8", values)
private val CTRL_9 = EventFields.StringList("Ctrl-9", values)
private val CTRL_0 = EventFields.StringList("Ctrl-0", values)
private val keyStrokes = listOf(
KeyStroke.getKeyStroke('1'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('2'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('3'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('4'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('5'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('6'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('7'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('8'.code, CTRL_DOWN_MASK),
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)
private val CTRL_SHIFT_2 = EventFields.StringList("Ctrl-Shift-2", values)
private val CTRL_SHIFT_3 = EventFields.StringList("Ctrl-Shift-3", values)
private val CTRL_SHIFT_4 = EventFields.StringList("Ctrl-Shift-4", values)
private val CTRL_SHIFT_5 = EventFields.StringList("Ctrl-Shift-5", values)
private val CTRL_SHIFT_6 = EventFields.StringList("Ctrl-Shift-6", values)
private val CTRL_SHIFT_7 = EventFields.StringList("Ctrl-Shift-7", values)
private val CTRL_SHIFT_8 = EventFields.StringList("Ctrl-Shift-8", values)
private val CTRL_SHIFT_9 = EventFields.StringList("Ctrl-Shift-9", values)
private val CTRL_SHIFT_0 = EventFields.StringList("Ctrl-Shift-0", values)
KeyStroke.getKeyStroke('1'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('2'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('3'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('4'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('5'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('6'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('7'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('8'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('9'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('0'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
private val CTRL_A = EventFields.StringList("Ctrl-A", values)
private val CTRL_B = EventFields.StringList("Ctrl-B", values)
private val CTRL_C = EventFields.StringList("Ctrl-C", values)
private val CTRL_D = EventFields.StringList("Ctrl-D", values)
private val CTRL_E = EventFields.StringList("Ctrl-E", values)
private val CTRL_F = EventFields.StringList("Ctrl-F", values)
private val CTRL_G = EventFields.StringList("Ctrl-G", values)
private val CTRL_H = EventFields.StringList("Ctrl-H", values)
private val CTRL_I = EventFields.StringList("Ctrl-I", values)
private val CTRL_J = EventFields.StringList("Ctrl-J", values)
private val CTRL_K = EventFields.StringList("Ctrl-K", values)
private val CTRL_L = EventFields.StringList("Ctrl-L", values)
private val CTRL_M = EventFields.StringList("Ctrl-M", values)
private val CTRL_N = EventFields.StringList("Ctrl-N", values)
private val CTRL_O = EventFields.StringList("Ctrl-O", values)
private val CTRL_P = EventFields.StringList("Ctrl-P", values)
private val CTRL_Q = EventFields.StringList("Ctrl-Q", values)
private val CTRL_R = EventFields.StringList("Ctrl-R", values)
private val CTRL_S = EventFields.StringList("Ctrl-S", values)
private val CTRL_T = EventFields.StringList("Ctrl-T", values)
private val CTRL_U = EventFields.StringList("Ctrl-U", values)
private val CTRL_V = EventFields.StringList("Ctrl-V", values)
private val CTRL_W = EventFields.StringList("Ctrl-W", values)
private val CTRL_X = EventFields.StringList("Ctrl-X", values)
private val CTRL_Y = EventFields.StringList("Ctrl-Y", values)
private val CTRL_Z = EventFields.StringList("Ctrl-Z", values)
private val CTRL_BR1 = EventFields.StringList("Ctrl-[", values)
private val CTRL_BR2 = EventFields.StringList("Ctrl-]", values)
KeyStroke.getKeyStroke('A'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('B'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('C'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('D'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('E'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('F'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('G'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('H'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('I'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('J'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('K'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('L'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('M'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('N'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('O'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('P'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('Q'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('R'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('S'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('T'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('U'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('V'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('W'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('X'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('Y'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('Z'.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke('['.code, CTRL_DOWN_MASK),
KeyStroke.getKeyStroke(']'.code, CTRL_DOWN_MASK),
private val CTRL_SHIFT_A = EventFields.StringList("Ctrl-Shift-A", values)
private val CTRL_SHIFT_B = EventFields.StringList("Ctrl-Shift-B", values)
private val CTRL_SHIFT_C = EventFields.StringList("Ctrl-Shift-C", values)
private val CTRL_SHIFT_D = EventFields.StringList("Ctrl-Shift-D", values)
private val CTRL_SHIFT_E = EventFields.StringList("Ctrl-Shift-E", values)
private val CTRL_SHIFT_F = EventFields.StringList("Ctrl-Shift-F", values)
private val CTRL_SHIFT_G = EventFields.StringList("Ctrl-Shift-G", values)
private val CTRL_SHIFT_H = EventFields.StringList("Ctrl-Shift-H", values)
private val CTRL_SHIFT_I = EventFields.StringList("Ctrl-Shift-I", values)
private val CTRL_SHIFT_J = EventFields.StringList("Ctrl-Shift-J", values)
private val CTRL_SHIFT_K = EventFields.StringList("Ctrl-Shift-K", values)
private val CTRL_SHIFT_L = EventFields.StringList("Ctrl-Shift-L", values)
private val CTRL_SHIFT_M = EventFields.StringList("Ctrl-Shift-M", values)
private val CTRL_SHIFT_N = EventFields.StringList("Ctrl-Shift-N", values)
private val CTRL_SHIFT_O = EventFields.StringList("Ctrl-Shift-O", values)
private val CTRL_SHIFT_P = EventFields.StringList("Ctrl-Shift-P", values)
private val CTRL_SHIFT_Q = EventFields.StringList("Ctrl-Shift-Q", values)
private val CTRL_SHIFT_R = EventFields.StringList("Ctrl-Shift-R", values)
private val CTRL_SHIFT_S = EventFields.StringList("Ctrl-Shift-S", values)
private val CTRL_SHIFT_T = EventFields.StringList("Ctrl-Shift-T", values)
private val CTRL_SHIFT_U = EventFields.StringList("Ctrl-Shift-U", values)
private val CTRL_SHIFT_V = EventFields.StringList("Ctrl-Shift-V", values)
private val CTRL_SHIFT_W = EventFields.StringList("Ctrl-Shift-W", values)
private val CTRL_SHIFT_X = EventFields.StringList("Ctrl-Shift-X", values)
private val CTRL_SHIFT_Y = EventFields.StringList("Ctrl-Shift-Y", values)
private val CTRL_SHIFT_Z = EventFields.StringList("Ctrl-Shift-Z", values)
private val CTRL_SHIFT_BR1 = EventFields.StringList("Ctrl-Shift-[", values)
private val CTRL_SHIFT_BR2 = EventFields.StringList("Ctrl-Shift-]", values)
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,
KeyStroke.getKeyStroke('A'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('B'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('C'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('D'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('E'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('F'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('G'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('H'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('I'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('J'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('K'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('L'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('M'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('N'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('O'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('P'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('Q'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('R'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('S'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('T'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('U'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('V'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('W'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('X'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('Y'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('Z'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke('['.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
KeyStroke.getKeyStroke(']'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
)
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 {
NORMAL_UNDEFINED,
NORMAL_IDE,

View File

@@ -59,8 +59,8 @@ internal class VimscriptState : ApplicationUsagesCollector() {
var isFunctionDeclarationUsed = false
var isFunctionCallUsed = false
private val SOURCED_FILES = EventFields.Int("number_of_sourced_files")
private val IDEAVIMRC_SIZE = EventFields.Int("ideavimrc_size")
private val SOURCED_FILES = EventFields.RoundedInt("number_of_sourced_files")
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_PLUG = EventFields.StringList("extensions_enabled_by_plug", PluginState.extensionNames)
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.DefaultActionGroup
import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.toolbar.floating.AbstractFloatingToolbarProvider
import com.intellij.openapi.editor.toolbar.floating.FloatingToolbarComponent
@@ -151,7 +150,7 @@ class ReloadVimRc : DumbAwareAction() {
val editor = e.getData(PlatformDataKeys.EDITOR) ?: return
FileDocumentManager.getInstance().saveDocumentAsIs(editor.document)
injector.keyGroup.removeKeyMapping(MappingOwner.IdeaVim.InitScript)
service<Troubleshooter>().removeByType("old-action-notation-in-mappings")
Troubleshooter.instance.removeByType("old-action-notation-in-mappings")
executeIdeaVimRc()
}
}

View File

@@ -21,7 +21,7 @@ package com.maddyhome.idea.vim.ui.ex
import com.intellij.openapi.diagnostic.logger
import com.maddyhome.idea.vim.KeyHandler
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 java.awt.event.ActionEvent
import java.awt.event.KeyEvent
@@ -238,14 +238,14 @@ class DeletePreviousWordAction : TextAction(DefaultEditorKit.deletePrevWordActio
target.saveLastEntry()
val doc = target.document
val caret = target.caret
val offset = SearchHelper.findNextWord(
target.actualText, caret.dot, target.actualText.length,
val offset = VimSearchHelperBase.Companion.findNextWord(
target.actualText, caret.dot.toLong(), target.actualText.length.toLong(),
-1, false, false
)
if (logger.isDebugEnabled) logger.debug("offset=$offset")
try {
val pos = caret.dot
doc.remove(offset, pos - offset)
doc.remove(offset.toInt(), (pos - offset).toInt())
} catch (ex: BadLocationException) {
// 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.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.RenamingFunctionHandler" name="renaming"/>
</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>
<id>IdeaVIM</id>
<change-notes><![CDATA[
@@ -109,7 +109,7 @@
]]>
</change-notes>
<description><![CDATA[
<p>Vim emulation plugin for IntelliJ Platform-based IDEs.</p>
<p>Vim engine for JetBrains IDEs</p>
<br/>
<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>
@@ -120,7 +120,7 @@
<li><a href="https://youtrack.jetbrains.com/issues/VIM">Issue tracker</a>: feature requests and bug reports</li>
</ul>
]]></description>
<version>SNAPSHOT</version>
<version>chylex</version>
<vendor>JetBrains</vendor>
<!-- 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}
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.enabled=Enabled
action.VimPluginToggle.enable=Enable

View File

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

View File

@@ -20,6 +20,7 @@ package org.jetbrains.plugins.ideavim.action
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.VimStateMachine
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
import com.maddyhome.idea.vim.helper.vimStateMachine
import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -1039,4 +1040,16 @@ two
)
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
//class InsertRegisterTest : VimTestCase() {
// todo test cursor position VIM-2732
// class InsertRegisterTest : VimTestCase() {
// todo test cursor position VIM-2732
// fun `test multiline insert from expression register`() {
// val keys = "VjyGo<C-r>=@\"<CR>"
// val before = """
@@ -43,4 +43,4 @@ package org.jetbrains.plugins.ideavim.action.change.change
// """.trimIndent()
// 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`() {
typeTextInFile(
injector.parser.parseKeys("dd"),

View File

@@ -18,6 +18,13 @@
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.api.injector
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.rangeOf
import org.junit.Test
import java.awt.datatransfer.Transferable
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`() {
setRegister('4', "XXX ")
doTest(
@@ -99,4 +130,24 @@ class PutTestAfterCursorActionTest : VimTestCase() {
""".trimIndent()
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"))
val editor = myFixture.editor.vim
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)
}
@@ -165,7 +165,7 @@ class YankVisualActionTest : VimTestCase() {
typeText(injector.parser.parseKeys("V" + "y"))
val editor = myFixture.editor.vim
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("all rocks and lavender and tufted grass,\n", "hard by the torrent of a mountain pass.\n"),
registers

View File

@@ -121,6 +121,22 @@ n ,f <Plug>Foo
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() {
configureByText("\n")
typeText(commandToKeys("inoremap a b"))

View File

@@ -38,8 +38,8 @@ class MarksCommandTest : VimTestCase() {
assertState(
"""I found it in a legendary land
|all rocks and lavender and tufted grass,
|${s}all rocks and lavender and tufted grass,
|${se}hard by the torrent of a mountain pass.
|${s}all rocks and lavender and tufted grass,$se
|hard by the torrent of a mountain pass.
""".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.ex.evaluate
class ExpressionTest: VimTestCase() {
class ExpressionTest : VimTestCase() {
fun `test multiline register content`() {
configureByText("${c}Oh\nHi\nMark\n")
typeText(parseKeys("VGy"))
assertEquals("Oh\nHi\nMark\n", Register('"').evaluate().toString())
}
}
}

View File

@@ -18,8 +18,8 @@
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.helper.SearchHelper;
import com.maddyhome.idea.vim.helper.SearchHelperKtKt;
import org.jetbrains.plugins.ideavim.SkipNeovimReason;
import org.jetbrains.plugins.ideavim.TestWithoutNeovim;
@@ -29,7 +29,7 @@ public class SearchHelperTest extends VimTestCase {
@TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
public void testFindNextWord() {
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"));
}
@@ -37,7 +37,7 @@ public class SearchHelperTest extends VimTestCase {
@TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
public void testFindSecondNextWord() {
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"));
}
@@ -45,7 +45,7 @@ public class SearchHelperTest extends VimTestCase {
@TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
public void testFindAfterLastWord() {
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());
}
@@ -53,7 +53,8 @@ public class SearchHelperTest extends VimTestCase {
@TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
public void testFindPreviousWord() {
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
assertEquals(previousWordPosition, text.indexOf("first"));
@@ -62,7 +63,8 @@ public class SearchHelperTest extends VimTestCase {
@TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
public void testFindSecondPreviousWord() {
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
assertEquals(previousWordPosition, text.indexOf("first"));
@@ -71,7 +73,8 @@ public class SearchHelperTest extends VimTestCase {
@TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
public void testFindBeforeFirstWord() {
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
assertEquals(previousWordPosition, text.indexOf("first"));
@@ -80,7 +83,8 @@ public class SearchHelperTest extends VimTestCase {
@TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
public void testFindPreviousWordWhenCursorOutOfBound() {
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"));
}

View File

@@ -19,6 +19,7 @@ package com.maddyhome.idea.vim.action
import com.maddyhome.idea.vim.KeyHandler
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.injector
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.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 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(
editor: VimEditor,
@@ -36,16 +62,6 @@ class ResetModeAction : VimActionHandler.SingleExecution() {
cmd: Command,
operatorArguments: OperatorArguments,
): Boolean {
val modeBeforeReset = editor.mode
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
error("This method should not be used")
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,6 +22,7 @@ interface VimCaret {
val vimLeadSelectionOffset: Int
var vimLastVisualOperatorRange: VisualChange?
val vimLine: Int
val isPrimary: Boolean
fun moveToOffset(offset: Int)
fun moveToOffsetNative(offset: Int)
fun moveToLogicalPosition(logicalPosition: VimLogicalPosition)
@@ -43,17 +44,18 @@ interface VimCaret {
}
interface CaretRegisterStorage {
// todo methods shouldn't have caret in signature
/**
* 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
* 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 saveRegister(r: Char, register: Register)
fun setKeys(caret: VimCaret, register: Char, keys: List<KeyStroke>)
fun saveRegister(caret: VimCaret, r: Char, register: Register)
}

View File

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

View File

@@ -47,10 +47,6 @@ interface VimChangeGroup {
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 processEnter(editor: VimEditor, context: ExecutionContext)

View File

@@ -158,7 +158,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
}
}
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
) {
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.MappingInfo
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.vimscript.model.expressions.Expression
import javax.swing.KeyStroke
@@ -73,4 +74,5 @@ interface VimKeyGroup {
val shortcutConflicts: 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.MappingInfo
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.RootNode
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 keyMappings: MutableMap<MappingMode, KeyMapping> = EnumMap(MappingMode::class.java)
override var operatorFunction: OperatorFunction? = null
override fun removeKeyMapping(modes: Set<MappingMode>, keys: List<KeyStroke>) {
modes.map { getKeyMapping(it) }.forEach { it.delete(keys) }
}

View File

@@ -83,7 +83,7 @@ interface VimSearchHelper {
spaceWords: Boolean,
): 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(
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 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)
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 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)

View File

@@ -42,8 +42,28 @@ import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
*
* 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 {
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
if (startSel && !editor.inVisualMode && !editor.inSelectMode) {
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)
}
}
motion(editor, context, cmd)
return true
}
@@ -61,7 +80,7 @@ abstract class ShiftedSpecialKeyHandler : VimActionHandler.SingleExecution() {
* This method is called when `keymodel` doesn't contain `startsel`,
* 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
*/
abstract class ShiftedArrowKeyHandler : VimActionHandler.SingleExecution() {
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
abstract class ShiftedArrowKeyHandler(private val runBothCommandsAsMulticaret: Boolean) : VimActionHandler.ConditionalMulticaret() {
val continueSelectSelection = OptionConstants.keymodel_continueselect in keymodelOption && inSelectMode
val continueVisualSelection = OptionConstants.keymodel_continuevisual in keymodelOption && inVisualMode
if (startSel || continueSelectSelection || continueVisualSelection) {
override fun runAsMulticaret(
editor: VimEditor,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments,
): Boolean {
val (inVisualMode, inSelectMode, withKey) = withKeyOrNot(editor)
if (withKey) {
if (!inVisualMode && !inSelectMode) {
if (OptionConstants.selectmode_key in (injector.optionService.getOptionValue(OptionScope.GLOBAL, OptionConstants.selectmodeName) as VimString).value) {
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)
}
}
motionWithKeyModel(editor, context, cmd)
return true
} 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
}
/**
* 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`,

View File

@@ -61,6 +61,30 @@ sealed class VimActionHandler(myRunForEachCaret: Boolean) : EditorActionHandlerB
): 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(
editor: VimEditor,
caret: VimCaret,
@@ -71,6 +95,16 @@ sealed class VimActionHandler(myRunForEachCaret: Boolean) : EditorActionHandlerB
return when (this) {
is ForEachCaret -> execute(editor, caret, 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 { selections.values.joinToString("\n") { vimSelection -> "Caret: $vimSelection" } }
val commandWrapper = VisualStartFinishWrapper(editor, cmd)
val commandWrapper = VisualStartFinishWrapper(editor, cmd, this)
commandWrapper.start()
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?>()
fun start() {
@@ -256,9 +260,13 @@ sealed class VisualOperatorActionHandler : EditorActionHandlerBase(false) {
fun finish(res: Boolean) {
logger.debug("Finish visual command. Result: $res")
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 (visualOperatorActionHandler.id != "VimVisualOperatorAction" ||
injector.keyGroup.operatorFunction?.postProcessSelection() != false
) {
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) {

View File

@@ -15,22 +15,23 @@
* 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.key
package com.maddyhome.idea.vim.key;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.maddyhome.idea.vim.command.SelectionType;
import org.jetbrains.annotations.NotNull;
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.command.SelectionType
/**
* @author vlan
*/
public interface OperatorFunction {
interface OperatorFunction {
/**
* 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.
*/
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,
context: ExecutionContext,
data: PutData,
updateVisualMarks: Boolean = false,
operatorArguments: OperatorArguments,
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,
context: ExecutionContext,
data: PutData,
updateVisualMarks: Boolean,
operatorArguments: OperatorArguments,
updateVisualMarks: Boolean,
): Boolean {
val additionalData = collectPreModificationData(editor, data)
deleteSelectedText(editor, data, operatorArguments)
@@ -67,15 +67,18 @@ abstract class VimPutBase : VimPut {
val caretsAndSelections = data.visualSelection?.caretsAndSelections ?: return
val selection = caretsAndSelections[currentCaret] ?: caretsAndSelections.firstOrNull()?.value ?: return
var fistIndex = selection.vimStart
val lastIndex = fistIndex + textLength - 1
val leftIndex = min(selection.vimStart, selection.vimEnd)
val rightIndex = leftIndex + textLength - 1
if (wasTextInsertedLineWise(text)) {
// here we skip the \n char before the inserted text
fistIndex += 1
val rangeForMarks = if (wasTextInsertedLineWise(text)) {
// here we skip the \n char after the inserted text
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) {