mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2024-11-25 16:42:55 +01:00
Compare commits
No commits in common. "da3d83ecc62e01b1ff14e25ad1770668a05379b4" and "85a1fbe89e358e5abbd140a7c38f8c9eb6da8de9" have entirely different histories.
da3d83ecc6
...
85a1fbe89e
@ -31,8 +31,6 @@ 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
|
||||
|
||||
|
@ -11,7 +11,7 @@ IdeaVim
|
||||
[![Gitter][gitter-svg]][gitter]
|
||||
[![Twitter][twitter-svg]][twitter]
|
||||
|
||||
IdeaVim is a Vim engine for JetBrains IDEs.
|
||||
IdeaVim is a Vim emulation plugin for IntelliJ Platform-based 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` in the menu to enable or disable vim.
|
||||
- Use `Tools | Vim Emulator` in the menu to enable or disable emulation.
|
||||
|
||||
- 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
|
||||
|
||||
[IdeaVim plugins](https://github.com/JetBrains/ideavim/wiki/Emulated-plugins):
|
||||
[Emulated Vim 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.
|
||||
|
||||
|
||||
IdeaVim Plugins
|
||||
Emulated Vim Plugins
|
||||
--------------------
|
||||
|
||||
See [doc/emulated-plugins.md](https://github.com/JetBrains/ideavim/wiki/Emulated-plugins)
|
||||
|
@ -26,7 +26,7 @@ plugins {
|
||||
java
|
||||
kotlin("jvm") version "1.6.21"
|
||||
|
||||
id("org.jetbrains.intellij") version "1.10.0-SNAPSHOT"
|
||||
id("org.jetbrains.intellij") version "1.9.0"
|
||||
id("org.jetbrains.changelog") version "1.3.1"
|
||||
|
||||
// ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle
|
||||
@ -166,18 +166,6 @@ 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")
|
||||
|
@ -1,10 +1,10 @@
|
||||
# suppress inspection "UnusedProperty" for whole file
|
||||
|
||||
ideaVersion=LATEST-EAP-SNAPSHOT
|
||||
ideaVersion=2022.2.2
|
||||
downloadIdeaSources=true
|
||||
instrumentPluginCode=true
|
||||
version=SNAPSHOT
|
||||
javaVersion=17
|
||||
javaVersion=11
|
||||
remoteRobotVersion=0.11.15
|
||||
antlrVersion=4.10.1
|
||||
|
||||
|
@ -27,7 +27,6 @@ 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;
|
||||
@ -102,41 +101,32 @@ public class EventFacade {
|
||||
EditorFactory.getInstance().addEditorFactoryListener(listener, parentDisposable);
|
||||
}
|
||||
|
||||
public void addEditorMouseListener(@NotNull Editor editor,
|
||||
@NotNull EditorMouseListener listener,
|
||||
@NotNull Disposable disposable) {
|
||||
editor.addEditorMouseListener(listener, disposable);
|
||||
public void addEditorMouseListener(@NotNull Editor editor, @NotNull EditorMouseListener listener) {
|
||||
editor.addEditorMouseListener(listener);
|
||||
}
|
||||
|
||||
public void removeEditorMouseListener(@NotNull Editor editor, @NotNull EditorMouseListener listener) {
|
||||
editor.removeEditorMouseListener(listener);
|
||||
}
|
||||
|
||||
public void addComponentMouseListener(@NotNull Component component,
|
||||
@NotNull MouseListener mouseListener,
|
||||
@NotNull Disposable disposable) {
|
||||
public void addComponentMouseListener(@NotNull Component component, @NotNull MouseListener mouseListener) {
|
||||
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,
|
||||
@NotNull Disposable disposable) {
|
||||
editor.addEditorMouseMotionListener(listener, disposable);
|
||||
public void addEditorMouseMotionListener(@NotNull Editor editor, @NotNull EditorMouseMotionListener listener) {
|
||||
editor.addEditorMouseMotionListener(listener);
|
||||
}
|
||||
|
||||
public void removeEditorMouseMotionListener(@NotNull Editor editor, @NotNull EditorMouseMotionListener listener) {
|
||||
editor.removeEditorMouseMotionListener(listener);
|
||||
}
|
||||
|
||||
public void addEditorSelectionListener(@NotNull Editor editor,
|
||||
@NotNull SelectionListener listener,
|
||||
@NotNull Disposable disposable) {
|
||||
editor.getSelectionModel().addSelectionListener(listener, disposable);
|
||||
public void addEditorSelectionListener(@NotNull Editor editor, @NotNull SelectionListener listener) {
|
||||
editor.getSelectionModel().addSelectionListener(listener);
|
||||
}
|
||||
|
||||
public void removeEditorSelectionListener(@NotNull Editor editor, @NotNull SelectionListener listener) {
|
||||
|
@ -114,7 +114,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
@Override
|
||||
public void dispose() {
|
||||
LOG.debug("disposeComponent");
|
||||
turnOffPlugin(false);
|
||||
turnOffPlugin();
|
||||
LOG.debug("done");
|
||||
}
|
||||
|
||||
@ -272,7 +272,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
if (isEnabled() == enabled) return;
|
||||
|
||||
if (!enabled) {
|
||||
getInstance().turnOffPlugin(true);
|
||||
getInstance().turnOffPlugin();
|
||||
}
|
||||
|
||||
getInstance().enabled = enabled;
|
||||
@ -365,14 +365,12 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
VimListenerManager.INSTANCE.turnOn();
|
||||
}
|
||||
|
||||
private void turnOffPlugin(boolean unsubscribe) {
|
||||
private void turnOffPlugin() {
|
||||
SearchGroup searchGroup = getSearchIfCreated();
|
||||
if (searchGroup != null) {
|
||||
searchGroup.turnOff();
|
||||
}
|
||||
if (unsubscribe) {
|
||||
VimListenerManager.INSTANCE.turnOff();
|
||||
}
|
||||
ExEntryPanel.fullReset();
|
||||
|
||||
// Unregister vim actions in command mode
|
||||
|
@ -243,7 +243,7 @@ class NerdTree : VimExtension {
|
||||
e.presentation.isEnabled = !speedSearchIsHere(project)
|
||||
}
|
||||
|
||||
override fun getActionUpdateThread() = ActionUpdateThread.EDT
|
||||
override fun getActionUpdateThread() = ActionUpdateThread.BGT
|
||||
|
||||
private fun speedSearchIsHere(project: Project): Boolean {
|
||||
val component = ProjectView.getInstance(project).currentProjectViewPane.tree ?: return false
|
||||
|
@ -21,7 +21,6 @@ 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;
|
||||
@ -36,13 +35,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;
|
||||
@ -156,12 +155,14 @@ public class ChangeGroup extends VimChangeGroupBase {
|
||||
}
|
||||
};
|
||||
|
||||
public void editorCreated(Editor editor, @NotNull Disposable disposable) {
|
||||
EventFacade.getInstance().addEditorMouseListener(editor, listener, disposable);
|
||||
@Override
|
||||
public void editorCreated(VimEditor editor) {
|
||||
EventFacade.getInstance().addEditorMouseListener(((IjVimEditor) editor).getEditor(), listener);
|
||||
}
|
||||
|
||||
public void editorReleased(Editor editor) {
|
||||
EventFacade.getInstance().removeEditorMouseListener(editor, listener);
|
||||
@Override
|
||||
public void editorReleased(VimEditor editor) {
|
||||
EventFacade.getInstance().removeEditorMouseListener(((IjVimEditor) editor).getEditor(), listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -807,9 +808,8 @@ public class ChangeGroup extends VimChangeGroupBase {
|
||||
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();
|
||||
.createHtmlTextBalloonBuilder("Wow, nice vim skills!", VimIcons.IDEAVIM, JBColor.background(), null)
|
||||
.createBalloon();
|
||||
balloon.show(JBPopupFactory.getInstance().guessBestPopupLocation(((IjVimEditor)editor).getEditor()),
|
||||
Balloon.Position.below);
|
||||
});
|
||||
|
@ -36,14 +36,9 @@ 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
|
||||
@ -78,6 +73,8 @@ 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
|
||||
@ -156,26 +153,17 @@ 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, disposable)
|
||||
eventFacade.addEditorMouseMotionListener(editor, EditorMouseHandler, disposable)
|
||||
eventFacade.addEditorSelectionListener(editor, EditorSelectionHandler, disposable)
|
||||
eventFacade.addComponentMouseListener(editor.contentComponent, ComponentMouseListener, disposable)
|
||||
eventFacade.addEditorMouseListener(editor, EditorMouseHandler)
|
||||
eventFacade.addEditorMouseMotionListener(editor, EditorMouseHandler)
|
||||
eventFacade.addEditorSelectionListener(editor, EditorSelectionHandler)
|
||||
eventFacade.addComponentMouseListener(editor.contentComponent, ComponentMouseListener)
|
||||
|
||||
VimPlugin.getEditor().editorCreated(editor)
|
||||
|
||||
VimPlugin.getChange().editorCreated(editor, disposable)
|
||||
|
||||
Disposer.register(disposable) {
|
||||
VimPlugin.getEditorIfCreated()?.editorDeinit(editor, true)
|
||||
}
|
||||
VimPlugin.getChange().editorCreated(IjVimEditor(editor))
|
||||
}
|
||||
|
||||
fun remove(editor: Editor, isReleased: Boolean) {
|
||||
@ -189,7 +177,7 @@ object VimListenerManager {
|
||||
|
||||
VimPlugin.getEditorIfCreated()?.editorDeinit(editor, isReleased)
|
||||
|
||||
VimPlugin.getChange().editorReleased(editor)
|
||||
VimPlugin.getChange().editorReleased(IjVimEditor(editor))
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,6 +209,7 @@ object VimListenerManager {
|
||||
}
|
||||
|
||||
override fun editorReleased(event: EditorFactoryEvent) {
|
||||
remove(event.editor, true)
|
||||
VimPlugin.getMark().editorReleased(event)
|
||||
}
|
||||
}
|
||||
|
@ -21,14 +21,15 @@ 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() {
|
||||
@ -38,15 +39,17 @@ internal class ShortcutConflictState : ApplicationUsagesCollector() {
|
||||
override fun getMetrics(): Set<MetricEvent> {
|
||||
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)
|
||||
getHandlersForShortcut(keystroke).forEach { mode ->
|
||||
metrics += HANDLER.metric(injector.parser.toKeyNotation(keystroke), 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)
|
||||
|
||||
@ -165,23 +168,12 @@ 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 { it.toReadableString() })
|
||||
private val KEY_STROKE = EventFields.String("key_stroke", keyStrokes.map { injector.parser.toKeyNotation(it) })
|
||||
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,
|
||||
|
@ -109,7 +109,7 @@
|
||||
]]>
|
||||
</change-notes>
|
||||
<description><![CDATA[
|
||||
<p>Vim engine for JetBrains IDEs</p>
|
||||
<p>Vim emulation plugin for IntelliJ Platform-based 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>
|
||||
|
@ -63,7 +63,7 @@ E548=E548: Digit expected: {0}
|
||||
E549=E549: Illegal percentage: {0}
|
||||
E774=E774: 'operatorfunc' is empty
|
||||
|
||||
action.VimPluginToggle.text=Vim
|
||||
action.VimPluginToggle.text=Vim Emulator
|
||||
action.VimPluginToggle.description=Toggle the vim plugin On/Off
|
||||
action.VimPluginToggle.enabled=Enabled
|
||||
action.VimPluginToggle.enable=Enable
|
||||
|
@ -19,7 +19,6 @@ 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
|
||||
@ -28,40 +27,25 @@ import com.maddyhome.idea.vim.command.VimStateMachine
|
||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||
import com.maddyhome.idea.vim.helper.mode
|
||||
|
||||
class ResetModeAction : VimActionHandler.ConditionalMulticaret() {
|
||||
private lateinit var modeBeforeReset: VimStateMachine.Mode
|
||||
class ResetModeAction : VimActionHandler.SingleExecution() {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
override fun execute(
|
||||
editor: VimEditor,
|
||||
context: ExecutionContext,
|
||||
cmd: Command,
|
||||
operatorArguments: OperatorArguments,
|
||||
): Boolean {
|
||||
error("This method should not be used")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@
|
||||
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
|
||||
@ -36,7 +35,8 @@ class MotionShiftEndAction : ShiftedSpecialKeyHandler() {
|
||||
|
||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||
|
||||
override fun motion(editor: VimEditor, context: ExecutionContext, cmd: Command, caret: VimCaret) {
|
||||
override fun motion(editor: VimEditor, context: ExecutionContext, cmd: Command) {
|
||||
editor.forEachCaret { caret ->
|
||||
var allow = false
|
||||
if (editor.inInsertMode) {
|
||||
allow = true
|
||||
@ -57,4 +57,5 @@ class MotionShiftEndAction : ShiftedSpecialKeyHandler() {
|
||||
caret.moveToOffset(newOffset)
|
||||
caret.vimLastColumn = VimMotionGroupBase.LAST_COLUMN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@
|
||||
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
|
||||
@ -32,8 +31,10 @@ class MotionShiftHomeAction : ShiftedSpecialKeyHandler() {
|
||||
|
||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||
|
||||
override fun motion(editor: VimEditor, context: ExecutionContext, cmd: Command, caret: VimCaret) {
|
||||
override fun motion(editor: VimEditor, context: ExecutionContext, cmd: Command) {
|
||||
editor.forEachCaret { caret ->
|
||||
val newOffset = injector.motion.moveCaretToLineStart(editor, caret)
|
||||
caret.moveToOffset(newOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@
|
||||
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
|
||||
@ -30,20 +29,23 @@ import com.maddyhome.idea.vim.handler.ShiftedArrowKeyHandler
|
||||
* @author Alex Plate
|
||||
*/
|
||||
|
||||
class MotionShiftLeftAction : ShiftedArrowKeyHandler(true) {
|
||||
class MotionShiftLeftAction : ShiftedArrowKeyHandler() {
|
||||
|
||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||
|
||||
override fun motionWithKeyModel(editor: VimEditor, caret: VimCaret, context: ExecutionContext, cmd: Command) {
|
||||
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 motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) {
|
||||
val caret = editor.currentCaret()
|
||||
editor.forEachCaret { caret ->
|
||||
val newOffset = injector.motion.findOffsetOfNextWord(editor, caret.offset.point, -cmd.count, false)
|
||||
if (newOffset is Motion.AbsoluteOffset) {
|
||||
caret.moveToOffset(newOffset.offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@
|
||||
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
|
||||
@ -30,20 +29,23 @@ import com.maddyhome.idea.vim.handler.ShiftedArrowKeyHandler
|
||||
* @author Alex Plate
|
||||
*/
|
||||
|
||||
class MotionShiftRightAction : ShiftedArrowKeyHandler(true) {
|
||||
class MotionShiftRightAction : ShiftedArrowKeyHandler() {
|
||||
|
||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||
|
||||
override fun motionWithKeyModel(editor: VimEditor, caret: VimCaret, context: ExecutionContext, cmd: Command) {
|
||||
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 motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) {
|
||||
val caret = editor.currentCaret()
|
||||
editor.forEachCaret { caret ->
|
||||
val newOffset = injector.motion.findOffsetOfNextWord(editor, caret.offset.point, cmd.count, false)
|
||||
if (newOffset is Motion.AbsoluteOffset) {
|
||||
caret.moveToOffset(newOffset.offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@
|
||||
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
|
||||
@ -29,17 +28,19 @@ import com.maddyhome.idea.vim.handler.ShiftedArrowKeyHandler
|
||||
* @author Alex Plate
|
||||
*/
|
||||
|
||||
class MotionShiftDownAction : ShiftedArrowKeyHandler(false) {
|
||||
class MotionShiftDownAction : ShiftedArrowKeyHandler() {
|
||||
|
||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||
|
||||
override fun motionWithKeyModel(editor: VimEditor, caret: VimCaret, context: ExecutionContext, cmd: Command) {
|
||||
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)
|
||||
|
||||
injector.engineEditorHelper.updateLastColumn(caret, col)
|
||||
}
|
||||
}
|
||||
|
||||
override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) {
|
||||
injector.motion.scrollFullPage(editor, editor.primaryCaret(), cmd.count)
|
||||
|
@ -19,7 +19,6 @@
|
||||
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
|
||||
@ -29,17 +28,19 @@ import com.maddyhome.idea.vim.handler.ShiftedArrowKeyHandler
|
||||
* @author Alex Plate
|
||||
*/
|
||||
|
||||
class MotionShiftUpAction : ShiftedArrowKeyHandler(false) {
|
||||
class MotionShiftUpAction : ShiftedArrowKeyHandler() {
|
||||
|
||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||
|
||||
override fun motionWithKeyModel(editor: VimEditor, caret: VimCaret, context: ExecutionContext, cmd: Command) {
|
||||
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)
|
||||
|
||||
injector.engineEditorHelper.updateLastColumn(caret, col)
|
||||
}
|
||||
}
|
||||
|
||||
override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) {
|
||||
injector.motion.scrollFullPage(editor, editor.primaryCaret(), -cmd.count)
|
||||
|
@ -30,17 +30,20 @@ import com.maddyhome.idea.vim.helper.inBlockSubMode
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
class VisualSwapEndsAction : VimActionHandler.ForEachCaret() {
|
||||
class VisualSwapEndsAction : VimActionHandler.SingleExecution() {
|
||||
|
||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||
|
||||
override fun execute(
|
||||
editor: VimEditor,
|
||||
caret: VimCaret,
|
||||
context: ExecutionContext,
|
||||
cmd: Command,
|
||||
operatorArguments: OperatorArguments
|
||||
): Boolean = swapVisualEnds(caret)
|
||||
operatorArguments: OperatorArguments,
|
||||
): Boolean {
|
||||
var ret = true
|
||||
editor.forEachCaret { ret = ret and swapVisualEnds(it) }
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,7 +19,6 @@
|
||||
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
|
||||
@ -30,10 +29,11 @@ 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.ConditionalMulticaret() {
|
||||
class VisualToggleLineModeAction : VimActionHandler.SingleExecution() {
|
||||
|
||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||
override fun runAsMulticaret(
|
||||
|
||||
override fun execute(
|
||||
editor: VimEditor,
|
||||
context: ExecutionContext,
|
||||
cmd: Command,
|
||||
@ -46,29 +46,10 @@ class VisualToggleLineModeAction : VimActionHandler.ConditionalMulticaret() {
|
||||
) as VimString
|
||||
).value
|
||||
return if ("cmd" in listOption) {
|
||||
injector.visualMotionGroup.enterSelectMode(editor, VimStateMachine.SubMode.VISUAL_LINE)
|
||||
true
|
||||
} else false
|
||||
injector.visualMotionGroup.enterSelectMode(editor, VimStateMachine.SubMode.VISUAL_LINE).also {
|
||||
editor.forEachCaret { it.vimSetSelection(it.offset.point) }
|
||||
}
|
||||
|
||||
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
|
||||
} else injector.visualMotionGroup
|
||||
.toggleVisual(editor, cmd.count, cmd.rawCount, VimStateMachine.SubMode.VISUAL_LINE)
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,10 @@ 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)
|
||||
|
@ -42,28 +42,8 @@ import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
||||
*
|
||||
* Handler is called once for all carets
|
||||
*/
|
||||
abstract class ShiftedSpecialKeyHandler : VimActionHandler.ConditionalMulticaret() {
|
||||
abstract class ShiftedSpecialKeyHandler : VimActionHandler.SingleExecution() {
|
||||
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) {
|
||||
@ -73,6 +53,7 @@ abstract class ShiftedSpecialKeyHandler : VimActionHandler.ConditionalMulticaret
|
||||
.toggleVisual(editor, 1, 0, VimStateMachine.SubMode.VISUAL_CHARACTER)
|
||||
}
|
||||
}
|
||||
motion(editor, context, cmd)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -80,7 +61,7 @@ abstract class ShiftedSpecialKeyHandler : VimActionHandler.ConditionalMulticaret
|
||||
* 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, caret: VimCaret)
|
||||
abstract fun motion(editor: VimEditor, context: ExecutionContext, cmd: Command)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,16 +71,16 @@ abstract class ShiftedSpecialKeyHandler : VimActionHandler.ConditionalMulticaret
|
||||
*
|
||||
* Handler is called once for all carets
|
||||
*/
|
||||
abstract class ShiftedArrowKeyHandler(private val runBothCommandsAsMulticaret: Boolean) : VimActionHandler.ConditionalMulticaret() {
|
||||
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
|
||||
|
||||
override fun runAsMulticaret(
|
||||
editor: VimEditor,
|
||||
context: ExecutionContext,
|
||||
cmd: Command,
|
||||
operatorArguments: OperatorArguments,
|
||||
): Boolean {
|
||||
val (inVisualMode, inSelectMode, withKey) = withKeyOrNot(editor)
|
||||
if (withKey) {
|
||||
val continueSelectSelection = OptionConstants.keymodel_continueselect in keymodelOption && inSelectMode
|
||||
val continueVisualSelection = OptionConstants.keymodel_continuevisual in keymodelOption && inVisualMode
|
||||
if (startSel || continueSelectSelection || continueVisualSelection) {
|
||||
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)
|
||||
@ -108,54 +89,17 @@ abstract class ShiftedArrowKeyHandler(private val runBothCommandsAsMulticaret: B
|
||||
.toggleVisual(editor, 1, 0, VimStateMachine.SubMode.VISUAL_CHARACTER)
|
||||
}
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
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)
|
||||
motionWithKeyModel(editor, 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, caret: VimCaret, context: ExecutionContext, cmd: Command)
|
||||
abstract fun motionWithKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command)
|
||||
|
||||
/**
|
||||
* This method is called when `keymodel` doesn't contain `startsel`,
|
||||
|
@ -61,30 +61,6 @@ 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,
|
||||
@ -95,16 +71,6 @@ 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user