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

Compare commits

...

23 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
32 changed files with 455 additions and 179 deletions

View File

@@ -31,6 +31,8 @@ usual beta standards.
* [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

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.9.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")

View File

@@ -3,12 +3,11 @@
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

@@ -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

View File

@@ -22,6 +22,7 @@ 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
@@ -39,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
@@ -84,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, context, 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)
}
}
}
@@ -120,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 {
@@ -218,26 +222,44 @@ class VimSurroundExtension : VimExtension {
}
}
private class Operator : OperatorFunction {
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

@@ -21,6 +21,7 @@ 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;
@@ -35,13 +36,13 @@ 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;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.ui.JBColor;
import com.intellij.util.containers.ContainerUtil;
import com.maddyhome.idea.vim.EventFacade;
import com.maddyhome.idea.vim.VimPlugin;
@@ -155,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
@@ -808,8 +807,9 @@ public class ChangeGroup extends VimChangeGroupBase {
lastShownTime = currentTime;
ApplicationManager.getApplication().invokeLater(() -> {
final Balloon balloon = JBPopupFactory.getInstance()
.createHtmlTextBalloonBuilder("Wow, nice vim skills!", VimIcons.IDEAVIM, JBColor.background(), null)
.createBalloon();
.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);
});

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

@@ -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

@@ -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

@@ -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

@@ -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.service.fus.collectors.ApplicationUsagesCollector
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector
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() {
@@ -39,17 +38,15 @@ internal class ShortcutConflictState : ApplicationUsagesCollector() {
override fun getMetrics(): Set<MetricEvent> {
val metrics = mutableSetOf<MetricEvent>()
keyStrokes.forEach { keystroke ->
getHandlersForShortcut(keystroke).forEach { mode ->
metrics += HANDLER.metric(injector.parser.toKeyNotation(keystroke), mode)
}
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
}
fun StringListEventField.withKeyStroke(ks: KeyStroke): EventPair<List<String>> {
return this.with(getHandlersForShortcut(ks).map { it.name })
}
private fun getHandlersForShortcut(shortcut: KeyStroke): List<HandledModes> {
val modes = VimPlugin.getKey().shortcutConflicts[shortcut] ?: return listOf(HandledModes.NORMAL_UNDEFINED, HandledModes.INSERT_UNDEFINED, HandledModes.VISUAL_AND_SELECT_UNDEFINED)
@@ -168,12 +165,23 @@ internal class ShortcutConflictState : ApplicationUsagesCollector() {
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 { injector.parser.toKeyNotation(it) })
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

@@ -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

@@ -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

@@ -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

@@ -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

@@ -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)
}
}
}
}
}