1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2024-11-24 22:42:53 +01:00

Compare commits

..

19 Commits

Author SHA1 Message Date
c79286b9b0
Set plugin version to chylex-36 2024-07-08 06:02:46 +02:00
5f59b47b19
Revert per-caret registers 2024-07-08 06:02:46 +02:00
8d51537f79
Revert "Factor disposable objects on editor opening"
This reverts commit 1fa78935
2024-07-08 06:02:46 +02:00
052de10e3a
Fix(VIM-3364): Exception with mapped Generate action 2024-07-08 06:02:45 +02:00
9ece9a7a04
Apply scrolloff after executing native IDEA actions 2024-07-08 06:02:45 +02:00
84c868afc3
Stay on same line after reindenting 2024-07-08 06:02:45 +02:00
f29ebab390
Update search register when using f/t 2024-07-08 06:02:45 +02:00
0cb8bba3fd
Automatically add unambiguous imports after running a macro 2024-07-08 06:02:45 +02:00
c0ff2b5cd0
Fix(VIM-3179): Respect virtual space below editor (imperfectly) 2024-07-08 06:02:45 +02:00
460234553d
Fix(VIM-3178): Workaround to support "Jump to Source" action mapping 2024-07-08 06:02:45 +02:00
cdd5b2abaf
Add support for count for visual and line motion surround 2024-07-08 06:02:45 +02:00
9db1732eb3
Fix vim-surround not working with multiple cursors
Fixes multiple cursors with vim-surround commands `cs, ds, S` (but not `ys`).
2024-07-08 06:02:45 +02:00
63e292b21f
Fix(VIM-696) Restore visual mode after undo/redo, and disable incompatible actions 2024-07-08 06:02:44 +02:00
362175431d
Respect count with <Action> mappings 2024-07-08 06:02:44 +02:00
5e2cab4eda
Change matchit plugin to use HTML patterns in unrecognized files 2024-07-08 06:02:44 +02:00
b63792c8f8
Reset insert mode when switching active editor 2024-07-08 06:02:44 +02:00
f543b6a1d1
Remove update checker 2024-07-08 06:02:44 +02:00
d367b3bc72
Set custom plugin version 2024-07-08 06:02:44 +02:00
da2d8d707f
Revert "Migrate to IntelliJ Platform Gradle Plugin 2.0"
This reverts commit 4913b13a
2024-07-07 04:28:32 +02:00
77 changed files with 379 additions and 486 deletions

View File

@ -21,7 +21,7 @@ ideaVersion=2024.1.1
ideaType=IC
downloadIdeaSources=true
instrumentPluginCode=true
version=chylex-37
version=chylex-36
javaVersion=17
remoteRobotVersion=0.11.22
antlrVersion=4.10.1

View File

@ -28,7 +28,7 @@ import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.helper.inRepeatMode
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
@ -102,7 +102,7 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
val argument = cmd.argument ?: return false
if (!editor.inRepeatMode) {
if (!editor.vimStateMachine.isDotRepeatInProgress) {
argumentCaptured = argument
}
val range = getMotionRange(editor, context, argument, operatorArguments)

View File

@ -17,6 +17,7 @@ import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.newapi.ij
@CommandOrMotion(keys = ["."], modes = [Mode.NORMAL])
@ -24,7 +25,7 @@ internal class RepeatChangeAction : VimActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.OTHER_WRITABLE
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
val state = injector.vimState
val state = editor.vimStateMachine
val lastCommand = VimRepeater.lastChangeCommand
if (lastCommand == null && Extension.lastExtensionHandler == null) return false

View File

@ -9,75 +9,51 @@ package com.maddyhome.idea.vim.ex
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.api.VimOutputPanel
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.VimExOutputPanel
import com.maddyhome.idea.vim.helper.vimExOutput
import com.maddyhome.idea.vim.ui.ExOutputPanel
import java.lang.ref.WeakReference
// TODO: We need a nicer way to handle output, especially wrt testing, appending + clearing
class ExOutputModel(private val myEditor: WeakReference<Editor>) : VimOutputPanel {
class ExOutputModel private constructor(private val myEditor: Editor) : VimExOutputPanel {
private var isActiveInTestMode = false
val editor get() = myEditor.get()
val isActive: Boolean
override val isActive: Boolean
get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
editor?.let { ExOutputPanel.getNullablePanel(it) }?.myActive ?: false
ExOutputPanel.isPanelActive(myEditor)
} else {
isActiveInTestMode
}
override fun addText(text: String, isNewLine: Boolean) {
if (this.text.isNotEmpty() && isNewLine) this.text += "\n$text" else this.text += text
}
override fun show() {
if (editor == null) return
val currentPanel = injector.outputPanel.getCurrentOutputPanel()
if (currentPanel != null && currentPanel != this) currentPanel.close()
editor!!.vimExOutput = this
val exOutputPanel = ExOutputPanel.getInstance(editor!!)
if (!exOutputPanel.myActive) {
if (ApplicationManager.getApplication().isUnitTestMode) {
isActiveInTestMode = true
} else {
exOutputPanel.activate()
}
}
}
override var text: String = ""
override var text: String? = null
get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
editor?.let { ExOutputPanel.getInstance(it).text } ?: ""
ExOutputPanel.getInstance(myEditor).text
} else {
// ExOutputPanel always returns a non-null string
field
field ?: ""
}
set(value) {
// ExOutputPanel will strip a trailing newline. We'll do it now so that tests have the same behaviour. We also
// never pass null to ExOutputPanel, but we do store it for tests, so we know if we're active or not
val newValue = value.removeSuffix("\n")
val newValue = value?.removeSuffix("\n")
if (!ApplicationManager.getApplication().isUnitTestMode) {
editor?.let { ExOutputPanel.getInstance(it).setText(newValue) }
ExOutputPanel.getInstance(myEditor).setText(newValue ?: "")
} else {
field = newValue
isActiveInTestMode = newValue.isNotEmpty()
isActiveInTestMode = !newValue.isNullOrEmpty()
}
}
fun output(text: String) {
override fun output(text: String) {
this.text = text
}
fun clear() {
text = ""
override fun clear() {
text = null
}
override fun close() {
if (!ApplicationManager.getApplication().isUnitTestMode) {
editor?.let { ExOutputPanel.getInstance(it).close() }
ExOutputPanel.getInstance(myEditor).close()
}
else {
isActiveInTestMode = false
@ -89,7 +65,7 @@ class ExOutputModel(private val myEditor: WeakReference<Editor>) : VimOutputPane
fun getInstance(editor: Editor): ExOutputModel {
var model = editor.vimExOutput
if (model == null) {
model = ExOutputModel(WeakReference(editor))
model = ExOutputModel(editor)
editor.vimExOutput = model
}
return model

View File

@ -24,8 +24,8 @@ import com.maddyhome.idea.vim.common.CommandAlias
import com.maddyhome.idea.vim.common.CommandAliasHandler
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.helper.TestInputModel
import com.maddyhome.idea.vim.helper.inRepeatMode
import com.maddyhome.idea.vim.helper.noneOfEnum
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.key.MappingOwner
import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.newapi.vim
@ -151,7 +151,7 @@ object VimExtensionFacade {
/** Returns a single key stroke from the user input similar to 'getchar()'. */
@JvmStatic
fun inputKeyStroke(editor: Editor): KeyStroke {
if (editor.vim.inRepeatMode) {
if (editor.vim.vimStateMachine.isDotRepeatInProgress) {
val input = Extension.consumeKeystroke()
LOG.trace("inputKeyStroke: dot repeat in progress. Input: $input")
return input ?: error("Not enough keystrokes saved: ${Extension.lastExtensionHandler}")

View File

@ -46,6 +46,7 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
import com.maddyhome.idea.vim.extension.exportOperatorFunction
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
import com.maddyhome.idea.vim.helper.PsiHelper
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
@ -64,7 +65,7 @@ internal class CommentaryExtension : VimExtension {
selectionType: SelectionType,
resetCaret: Boolean = true,
): Boolean {
val mode = editor.mode
val mode = editor.vimStateMachine.mode
if (mode !is Mode.VISUAL) {
editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset)
}

View File

@ -218,6 +218,45 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
editorEx.addPropertyChangeListener(FontSizeChangeListener.INSTANCE);
}
// We add Vim bindings to all opened editors, including editors used as UI controls rather than just project file
// editors. This includes editors used as part of the UI, such as the VCS commit message, or used as read-only
// viewers for text output, such as log files in run configurations or the Git Console tab. And editors are used for
// interactive stdin/stdout for console-based run configurations.
// We want to provide an intuitive experience for working with these additional editors, so we automatically switch
// to INSERT mode if they are interactive editors. Recognising these can be a bit tricky.
// These additional interactive editors are not file-based, but must have a writable document. However, log output
// documents are also writable (the IDE is writing new content as it becomes available) just not user-editable. So
// we must also check that the editor is not in read-only "viewer" mode (this includes "rendered" mode, which is
// read-only and also hides the caret).
// Furthermore, interactive stdin/stdout console output in run configurations is hosted in a read-only editor, but
// it can still be edited. The `ConsoleViewImpl` class installs a typing handler that ignores the editor's
// `isViewer` property and allows typing if the associated process (if any) is still running. We can get the
// editor's console view and check this ourselves, but we have to wait until the editor has finished initialising
// before it's available in user data.
// Finally, we have a special check for diff windows. If we compare against clipboard, we get a diff editor that is
// not file based, is writable, and not a viewer, but we don't want to treat this as an interactive editor.
// Note that we need a similar check in `VimEditor.isWritable` to allow Escape to work to exit insert mode. We need
// to know that a read-only editor that is hosting a console view with a running process can be treated as writable.
Runnable switchToInsertMode = () -> {
ExecutionContext context = injector.getExecutionContextManager().getEditorExecutionContext(new IjVimEditor(editor));
VimPlugin.getChange().insertBeforeCursor(new IjVimEditor(editor), context);
KeyHandler.getInstance().reset(new IjVimEditor(editor));
};
if (!editor.isViewer() &&
!EditorHelper.isFileEditor(editor) &&
editor.getDocument().isWritable() &&
!CommandStateHelper.inInsertMode(editor) &&
editor.getEditorKind() != EditorKind.DIFF) {
switchToInsertMode.run();
}
ApplicationManager.getApplication().invokeLater(
() -> {
if (editor.isDisposed()) return;
ConsoleViewImpl consoleView = editor.getUserData(ConsoleViewImpl.CONSOLE_VIEW_IN_EDITOR_VIEW);
if (consoleView != null && consoleView.isRunning() && !CommandStateHelper.inInsertMode(editor)) {
switchToInsertMode.run();
}
});
updateCaretsVisualAttributes(new IjVimEditor(editor));
}
@ -377,7 +416,7 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
// Note that IDE scale is handled by LafManager.lookAndFeelChanged
VimCommandLine activeCommandLine = injector.getCommandLine().getActiveCommandLine();
if (activeCommandLine != null) {
injector.getProcessGroup().cancelExEntry(new IjVimEditor(editor), true, false);
injector.getProcessGroup().cancelExEntry(new IjVimEditor(editor), false);
}
ExOutputModel exOutputModel = ExOutputModel.tryGetInstance(editor);
if (exOutputModel != null) {

View File

@ -271,7 +271,7 @@ class FileGroup : VimFileBase() {
val msg = StringBuilder()
val doc = editor.document
if (injector.vimState.mode !is VISUAL) {
if (getInstance(IjVimEditor(editor)).mode !is VISUAL) {
val lp = editor.caretModel.logicalPosition
val col = editor.caretModel.offset - doc.getLineStartOffset(lp.line)
var endoff = doc.getLineEndOffset(lp.line)

View File

@ -47,6 +47,7 @@ import com.maddyhome.idea.vim.helper.getNormalizedScrollOffset
import com.maddyhome.idea.vim.helper.getNormalizedSideScrollOffset
import com.maddyhome.idea.vim.helper.isEndAllowed
import com.maddyhome.idea.vim.helper.vimLastColumn
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.listener.AppCodeTemplates
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
import com.maddyhome.idea.vim.newapi.ij
@ -306,13 +307,13 @@ internal class MotionGroup : VimMotionGroupBase() {
val editor = fileEditor.editor
if (!editor.isDisposed) {
editor.vim.let { vimEditor ->
when (vimEditor.mode) {
when (vimEditor.vimStateMachine.mode) {
is Mode.VISUAL -> {
vimEditor.exitVisualMode()
KeyHandler.getInstance().reset(vimEditor)
}
is Mode.CMD_LINE -> {
injector.processGroup.cancelExEntry(vimEditor, refocusOwningEditor = false, resetCaret = false)
injector.processGroup.cancelExEntry(vimEditor, false)
ExOutputModel.tryGetInstance(editor)?.close()
}
else -> {}

View File

@ -73,7 +73,7 @@ internal object GuicursorChangeListener : EffectiveOptionValueChangeListener {
}
private fun Editor.guicursorMode(): GuiCursorMode {
return GuiCursorMode.fromMode(vim.mode, injector.vimState.isReplaceCharacter)
return GuiCursorMode.fromMode(vim.mode, vim.vimStateMachine.isReplaceCharacter)
}
/**
@ -146,15 +146,21 @@ internal fun getGuiCursorMode(editor: Editor) = editor.guicursorMode()
class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeListener {
override fun isReplaceCharChanged(editor: VimEditor) {
updateCaretsVisual()
updateCaretsVisual(editor)
}
override fun modeChanged(editor: VimEditor, oldMode: Mode) {
updateCaretsVisual()
updateCaretsVisual(editor)
}
private fun updateCaretsVisual() {
private fun updateCaretsVisual(editor: VimEditor) {
if (injector.globalOptions().ideaglobalmode) {
updateAllEditorsCaretsVisual()
} else {
val ijEditor = (editor as IjVimEditor).editor
ijEditor.updateCaretsVisualAttributes()
ijEditor.updateCaretsVisualPosition()
}
}
fun updateAllEditorsCaretsVisual() {

View File

@ -34,4 +34,4 @@ val Editor.inVisualMode: Boolean
@get:JvmName("inExMode")
internal val Editor.inExMode
get() = this.vim.mode is Mode.CMD_LINE
get() = this.vim.vimStateMachine.mode is Mode.CMD_LINE

View File

@ -31,7 +31,7 @@ import com.maddyhome.idea.vim.state.mode.returnTo
internal fun Editor.exitSelectMode(adjustCaretPosition: Boolean) {
if (!this.vim.inSelectMode) return
val returnTo = this.vim.mode.returnTo
val returnTo = this.vim.vimStateMachine.mode.returnTo
when (returnTo) {
ReturnTo.INSERT -> {
this.vim.mode = Mode.INSERT
@ -64,7 +64,7 @@ internal fun Editor.exitSelectMode(adjustCaretPosition: Boolean) {
internal fun VimEditor.exitSelectMode(adjustCaretPosition: Boolean) {
if (!this.inSelectMode) return
val returnTo = this.mode.returnTo
val returnTo = this.vimStateMachine.mode.returnTo
when (returnTo) {
ReturnTo.INSERT -> {
this.mode = Mode.INSERT

View File

@ -180,7 +180,7 @@ internal object ScrollViewHelper {
}
private fun getScrollJump(editor: VimEditor, height: Int): Int {
val flags = injector.vimState.executingCommandFlags
val flags = VimStateMachine.getInstance(editor).executingCommandFlags
val scrollJump = !flags.contains(CommandFlags.FLAG_IGNORE_SCROLL_JUMP)
// Default value is 1. Zero is a valid value, but we normalise to 1 - we always want to scroll at least one line
@ -203,7 +203,7 @@ internal object ScrollViewHelper {
val caretColumn = position.column
val halfWidth = getApproximateScreenWidth(editor) / 2
val scrollOffset = getNormalizedSideScrollOffset(editor)
val flags = injector.vimState.executingCommandFlags
val flags = VimStateMachine.getInstance(vimEditor).executingCommandFlags
val allowSidescroll = !flags.contains(CommandFlags.FLAG_IGNORE_SIDE_SCROLL_JUMP)
val sidescroll = injector.options(vimEditor).sidescroll
val offsetLeft = caretColumn - (currentVisualLeftColumn + scrollOffset)

View File

@ -103,6 +103,7 @@ internal var Editor.vimInitialised: Boolean by userDataOr { false }
// ------------------ Editor
internal fun unInitializeEditor(editor: Editor) {
editor.vimLastSelectionType = null
editor.vimStateMachine = null
editor.vimMorePanel = null
editor.vimExOutput = null
editor.vimLastHighlighters = null
@ -117,6 +118,7 @@ internal var Editor.vimIncsearchCurrentMatchOffset: Int? by userData()
* @see :help visualmode()
*/
internal var Editor.vimLastSelectionType: SelectionType? by userData()
internal var Editor.vimStateMachine: VimStateMachine? by userData()
internal var Editor.vimEditorGroup: Boolean by userDataOr { false }
internal var Editor.vimHasRelativeLineNumbersInstalled: Boolean by userDataOr { false }
internal var Editor.vimMorePanel: ExOutputPanel? by userData()

View File

@ -1,72 +0,0 @@
/*
* Copyright 2003-2024 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.listener
import com.intellij.execution.impl.ConsoleViewImpl
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.EditorKind
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.EditorListener
import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.inInsertMode
import com.maddyhome.idea.vim.newapi.ij
/**
* This listener is similar to the one we introduce in vim-engine to handle focus change,
* However, in IJ we would like to start editing in some editors in INSERT mode (e.g., consoles)
* It is different to we had previously. Now we go to INSERT mode not only when we focus on the console the first time, but every time.
* Going to INSERT on every focus is easier to implement and more consistent (behavior is always the same, you don't have to remember if you are focusing a console the first time or not)
*/
class IJEditorFocusListener : EditorListener {
override fun focusGained(editor: VimEditor) {
// We add Vim bindings to all opened editors, including editors used as UI controls rather than just project file
// editors. This includes editors used as part of the UI, such as the VCS commit message, or used as read-only
// viewers for text output, such as log files in run configurations or the Git Console tab. And editors are used for
// interactive stdin/stdout for console-based run configurations.
// We want to provide an intuitive experience for working with these additional editors, so we automatically switch
// to INSERT mode if they are interactive editors. Recognising these can be a bit tricky.
// These additional interactive editors are not file-based, but must have a writable document. However, log output
// documents are also writable (the IDE is writing new content as it becomes available) just not user-editable. So
// we must also check that the editor is not in read-only "viewer" mode (this includes "rendered" mode, which is
// read-only and also hides the caret).
// Furthermore, interactive stdin/stdout console output in run configurations is hosted in a read-only editor, but
// it can still be edited. The `ConsoleViewImpl` class installs a typing handler that ignores the editor's
// `isViewer` property and allows typing if the associated process (if any) is still running. We can get the
// editor's console view and check this ourselves, but we have to wait until the editor has finished initialising
// before it's available in user data.
// Finally, we have a special check for diff windows. If we compare against clipboard, we get a diff editor that is
// not file based, is writable, and not a viewer, but we don't want to treat this as an interactive editor.
// Note that we need a similar check in `VimEditor.isWritable` to allow Escape to work to exit insert mode. We need
// to know that a read-only editor that is hosting a console view with a running process can be treated as writable.
val switchToInsertMode = Runnable {
val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor)
VimPlugin.getChange().insertBeforeCursor(editor, context)
}
val ijEditor = editor.ij
if (!ijEditor.isViewer &&
!EditorHelper.isFileEditor(ijEditor) &&
ijEditor.document.isWritable &&
!ijEditor.inInsertMode && ijEditor.editorKind != EditorKind.DIFF
) {
switchToInsertMode.run()
}
ApplicationManager.getApplication().invokeLater {
if (ijEditor.isDisposed) return@invokeLater
val consoleView: ConsoleViewImpl? = ijEditor.getUserData(ConsoleViewImpl.CONSOLE_VIEW_IN_EDITOR_VIEW)
if (consoleView != null && consoleView.isRunning && !ijEditor.inInsertMode) {
switchToInsertMode.run()
}
}
KeyHandler.getInstance().reset(editor)
}
}

View File

@ -37,6 +37,7 @@ import com.maddyhome.idea.vim.action.VimShortcutKeyAction
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.group.NotificationService
import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.Mode
@ -127,6 +128,7 @@ internal object IdeaSpecifics {
}
) {
editor?.let {
val commandState = it.vim.vimStateMachine
it.vim.mode = Mode.NORMAL()
VimPlugin.getChange().insertBeforeCursor(it.vim, event.dataContext.vim)
KeyHandler.getInstance().reset(it.vim)

View File

@ -56,6 +56,7 @@ import com.intellij.util.ExceptionUtil
import com.jetbrains.rd.util.lifetime.Lifetime
import com.maddyhome.idea.vim.EventFacade
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.KeyHandlerStateResetter
import com.maddyhome.idea.vim.VimKeyListener
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.VimTypedActionHandler
@ -177,7 +178,7 @@ internal object VimListenerManager {
injector.listenersNotifier.macroRecordingListeners.add(macroWidgetListener)
injector.listenersNotifier.vimPluginListeners.add(macroWidgetListener)
injector.listenersNotifier.myEditorListeners.add(IJEditorFocusListener())
injector.listenersNotifier.myEditorListeners.add(KeyHandlerStateResetter())
injector.listenersNotifier.myEditorListeners.add(ShowCmdWidgetUpdater())
}
@ -745,7 +746,7 @@ internal object VimListenerManager {
val editor = event.editor
val commandLine = injector.commandLine.getActiveCommandLine()
if (commandLine != null) {
injector.processGroup.cancelExEntry(editor.vim, refocusOwningEditor = true, resetCaret = false)
injector.processGroup.cancelExEntry(editor.vim, false)
}
ExOutputModel.tryGetInstance(editor)?.close()
@ -776,7 +777,7 @@ internal object VimListenerManager {
) {
val commandLine = injector.commandLine.getActiveCommandLine()
if (commandLine != null) {
injector.processGroup.cancelExEntry(event.editor.vim, refocusOwningEditor = true, resetCaret = false)
injector.processGroup.cancelExEntry(event.editor.vim, false)
}
ExOutputModel.getInstance(event.editor).close()

View File

@ -151,7 +151,7 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
return editor.caretModel.allCarets.map { IjVimCaret(it) }
}
override var isFirstCaret = true
override var isFirstCaret = false
override var isReversingCarets = false
@Suppress("ideavimRunForEachCaret")
@ -159,6 +159,7 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
if (editor.vim.inBlockSelection) {
action(IjVimCaret(editor.caretModel.primaryCaret))
} else {
isFirstCaret = true
try {
editor.caretModel.runForEachCaret({
if (it.isValid) {
@ -167,12 +168,13 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
}
}, false)
} finally {
isFirstCaret = true
isFirstCaret = false
}
}
}
override fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean) {
isFirstCaret = true
isReversingCarets = reverse
try {
editor.caretModel.runForEachCaret({
@ -180,7 +182,7 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
isFirstCaret = false
}, reverse)
} finally {
isFirstCaret = true
isFirstCaret = false
isReversingCarets = false
}
}

View File

@ -11,6 +11,7 @@ package com.maddyhome.idea.vim.newapi
import com.intellij.openapi.components.service
import com.intellij.openapi.components.serviceIfCreated
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.textarea.TextComponentEditorImpl
import com.maddyhome.idea.vim.api.EngineEditorHelper
import com.maddyhome.idea.vim.api.ExecutionContextManager
@ -27,6 +28,8 @@ import com.maddyhome.idea.vim.api.VimDigraphGroup
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.VimEditorGroup
import com.maddyhome.idea.vim.api.VimEnabler
import com.maddyhome.idea.vim.api.VimExOutputPanel
import com.maddyhome.idea.vim.api.VimExOutputPanelService
import com.maddyhome.idea.vim.api.VimExtensionRegistrator
import com.maddyhome.idea.vim.api.VimFile
import com.maddyhome.idea.vim.api.VimInjector
@ -38,7 +41,6 @@ import com.maddyhome.idea.vim.api.VimMarkService
import com.maddyhome.idea.vim.api.VimMessages
import com.maddyhome.idea.vim.api.VimMotionGroup
import com.maddyhome.idea.vim.api.VimOptionGroup
import com.maddyhome.idea.vim.api.VimOutputPanelService
import com.maddyhome.idea.vim.api.VimProcessGroup
import com.maddyhome.idea.vim.api.VimPsiService
import com.maddyhome.idea.vim.api.VimRedrawService
@ -77,7 +79,9 @@ import com.maddyhome.idea.vim.helper.IjActionExecutor
import com.maddyhome.idea.vim.helper.IjEditorHelper
import com.maddyhome.idea.vim.helper.IjVimStringParser
import com.maddyhome.idea.vim.helper.UndoRedoHelper
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.history.VimHistory
import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
import com.maddyhome.idea.vim.macro.VimMacro
import com.maddyhome.idea.vim.put.VimPut
import com.maddyhome.idea.vim.register.VimRegisterGroup
@ -101,6 +105,12 @@ internal class IjVimInjector : VimInjectorBase() {
override val actionExecutor: VimActionExecutor
get() = service<IjActionExecutor>()
override val exOutputPanel: VimExOutputPanelService
get() = object : VimExOutputPanelService {
override fun getPanel(editor: VimEditor): VimExOutputPanel {
return ExOutputModel.getInstance(editor.ij)
}
}
override val historyGroup: VimHistory
get() = service<HistoryGroup>()
override val extensionRegistrator: VimExtensionRegistrator
@ -183,8 +193,6 @@ internal class IjVimInjector : VimInjectorBase() {
get() = com.maddyhome.idea.vim.vimscript.parser.VimscriptParser
override val commandLine: VimCommandLineService
get() = service()
override val outputPanel: VimOutputPanelService
get() = service()
override val optionGroup: VimOptionGroup
get() = service()
@ -198,14 +206,21 @@ internal class IjVimInjector : VimInjectorBase() {
override val redrawService: VimRedrawService
get() = service()
@Deprecated("Please use VimInjector.vimState", replaceWith = ReplaceWith("vimState"))
override fun commandStateFor(editor: VimEditor): VimStateMachine {
return vimState
var res = editor.ij.vimStateMachine
if (res == null) {
res = VimStateMachineImpl()
editor.ij.vimStateMachine = res
}
return res
}
@Deprecated("Please use VimInjector.vimState", replaceWith = ReplaceWith("vimState"))
override fun commandStateFor(editor: Any): VimStateMachine {
return vimState
return when (editor) {
is VimEditor -> this.commandStateFor(editor)
is Editor -> this.commandStateFor(IjVimEditor(editor))
else -> error("Unexpected type: $editor")
}
}
override val engineEditorHelper: EngineEditorHelper

View File

@ -58,7 +58,7 @@ public class ExOutputPanel extends JPanel {
private @Nullable LayoutManager myOldLayout = null;
private boolean myWasOpaque = false;
public boolean myActive = false;
private boolean myActive = false;
private static final VimLogger LOG = injector.getLogger(ExOutputPanel.class);
@ -90,16 +90,12 @@ public class ExOutputPanel extends JPanel {
updateUI();
}
public static @Nullable ExOutputPanel getNullablePanel(@NotNull Editor editor) {
return UserDataManager.getVimMorePanel(editor);
}
public static boolean isPanelActive(@NotNull Editor editor) {
return getNullablePanel(editor) != null;
return UserDataManager.getVimMorePanel(editor) != null;
}
public static @NotNull ExOutputPanel getInstance(@NotNull Editor editor) {
ExOutputPanel panel = getNullablePanel(editor);
ExOutputPanel panel = UserDataManager.getVimMorePanel(editor);
if (panel == null) {
panel = new ExOutputPanel(editor);
UserDataManager.setVimMorePanel(editor, panel);
@ -196,7 +192,7 @@ public class ExOutputPanel extends JPanel {
/**
* Turns on the more window for the given editor
*/
public void activate() {
private void activate() {
JRootPane root = SwingUtilities.getRootPane(myEditor.getContentComponent());
myOldGlass = (JComponent)root.getGlassPane();
if (myOldGlass != null) {

View File

@ -15,8 +15,8 @@ import com.maddyhome.idea.vim.api.VimCommandLineService
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.helper.TestInputModel
import com.maddyhome.idea.vim.helper.inRepeatMode
import com.maddyhome.idea.vim.helper.isCloseKeyStroke
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.ui.ModalEntry
@ -31,7 +31,7 @@ class ExEntryPanelService : VimCommandLineService {
override fun inputString(vimEditor: VimEditor, context: ExecutionContext, prompt: String, finishOn: Char?): String? {
val editor = vimEditor.ij
if (vimEditor.inRepeatMode) {
if (vimEditor.vimStateMachine.isDotRepeatInProgress) {
val input = Extension.consumeString()
return input ?: error("Not enough strings saved: ${Extension.lastExtensionHandler}")
}

View File

@ -293,7 +293,7 @@ public class ExTextField extends JTextField {
clearCurrentAction();
Editor editor = ExEntryPanel.instance.getEditor();
if (editor != null) {
VimPlugin.getProcess().cancelExEntry(new IjVimEditor(editor), true, true);
VimPlugin.getProcess().cancelExEntry(new IjVimEditor(editor), true);
}
}

View File

@ -1,34 +0,0 @@
/*
* Copyright 2003-2024 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.ui.ex
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.VimOutputPanel
import com.maddyhome.idea.vim.api.VimOutputPanelServiceBase
import com.maddyhome.idea.vim.ex.ExOutputModel
import com.maddyhome.idea.vim.newapi.ij
import java.lang.ref.WeakReference
class IjOutputPanelService : VimOutputPanelServiceBase() {
private var activeOutputPanel: VimOutputPanel? = null
override fun getCurrentOutputPanel(): VimOutputPanel? {
return activeOutputPanel?.takeIf {
(it as ExOutputModel)
it.isActive && it.editor != null
}
}
override fun create(editor: VimEditor, context: ExecutionContext): VimOutputPanel {
val panel = ExOutputModel(WeakReference(editor.ij))
activeOutputPanel = panel
return panel
}
}

View File

@ -24,8 +24,6 @@
serviceInterface="com.maddyhome.idea.vim.api.VimProcessGroup"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.ui.ex.ExEntryPanelService"
serviceInterface="com.maddyhome.idea.vim.api.VimCommandLineService"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.ui.ex.IjOutputPanelService"
serviceInterface="com.maddyhome.idea.vim.api.VimOutputPanelService"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.group.DigraphGroup"
serviceInterface="com.maddyhome.idea.vim.api.VimDigraphGroup"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.group.HistoryGroup"/>

View File

@ -10,6 +10,7 @@ package org.jetbrains.plugins.ideavim.action
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.SelectionType
import org.jetbrains.plugins.ideavim.SkipNeovimReason
@ -72,6 +73,8 @@ class MotionActionTest : VimTestCase() {
""".trimIndent()
doTest(listOf("12", "<Esc>"), content, content, Mode.NORMAL())
assertPluginError(false)
val vimCommandState = fixture.editor.vimStateMachine
kotlin.test.assertNotNull(vimCommandState)
assertEmpty(KeyHandler.getInstance().keyHandlerState.commandBuilder.keys.toList())
}

View File

@ -154,8 +154,6 @@ class SetCommandTest : VimTestCase() {
// 'fileencoding' defaults to "", but is automatically detected as UTF-8
enterCommand("set number relativenumber scrolloff nrformats")
assertExOutput(" nrformats=hex scrolloff=0")
injector.outputPanel.getCurrentOutputPanel()?.close()
assertCommandOutput("set",
"""
|--- Options ---
@ -171,20 +169,21 @@ class SetCommandTest : VimTestCase() {
assertCommandOutput("set all",
"""
|--- Options ---
|noargtextobj ideamarks scroll=0 nosurround
|nobomb ideawrite=all scrolljump=1 notextobj-entire
|nobreakindent noignorecase scrolloff=0 notextobj-indent
| colorcolumn= noincsearch selectmode= textwidth=0
|nocommentary nolist shellcmdflag=-x timeout
|nocursorline nomatchit shellxescape=@ timeoutlen=1000
|nodigraph maxmapdepth=20 shellxquote={ notrackactionids
|noexchange more showcmd undolevels=1000
| fileformat=unix nomultiple-cursors showmode virtualedit=
|nogdefault noNERDTree sidescroll=0 novisualbell
|nohighlightedyank nrformats=hex sidescrolloff=0 visualdelay=100
| history=50 nonumber nosmartcase whichwrap=b,s
|nohlsearch operatorfunc= nosneak wrap
|noideajoin norelativenumber startofline wrapscan
|noargtextobj ideamarks scrolljump=1 notextobj-indent
|nobomb ideawrite=all scrolloff=0 textwidth=0
|nobreakindent noignorecase selectmode= timeout
| colorcolumn= noincsearch shellcmdflag=-x timeoutlen=1000
|nocommentary nolist shellxescape=@ notrackactionids
|nocursorline nomatchit shellxquote={ undolevels=1000
|nodigraph maxmapdepth=20 showcmd virtualedit=
|noexchange more showmode novisualbell
| fileformat=unix nomultiple-cursors sidescroll=0 visualdelay=100
|nogdefault noNERDTree sidescrolloff=0 whichwrap=b,s
|nohighlightedyank nrformats=hex nosmartcase wrap
| history=50 nonumber nosneak wrapscan
|nohlsearch operatorfunc= startofline
|noideaglobalmode norelativenumber nosurround
|noideajoin scroll=0 notextobj-entire
| clipboard=ideaput,autoselect,exclude:cons\|linux
| fileencoding=utf-8
| guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175
@ -221,8 +220,6 @@ class SetCommandTest : VimTestCase() {
// 'fileencoding' defaults to "", but is automatically detected as UTF-8
enterCommand("set number relativenumber scrolloff nrformats")
assertExOutput(" nrformats=hex scrolloff=0")
injector.outputPanel.getCurrentOutputPanel()?.close()
assertCommandOutput("set!",
"""
|--- Options ---
@ -257,6 +254,7 @@ class SetCommandTest : VimTestCase() {
|nohlsearch
| ide=IntelliJ IDEA Community Edition
|noideacopypreprocess
|noideaglobalmode
|noideajoin
| ideamarks
| idearefactormode=select

View File

@ -435,21 +435,21 @@ class SetglobalCommandTest : VimTestCase() {
setOsSpecificOptionsToSafeValues()
assertCommandOutput("setglobal all", """
|--- Global option values ---
|noargtextobj ideamarks scrolljump=1 notextobj-indent
|nobomb ideawrite=all scrolloff=0 textwidth=0
|nobreakindent noignorecase selectmode= timeout
| colorcolumn= noincsearch shellcmdflag=-x timeoutlen=1000
|nocommentary nolist shellxescape=@ notrackactionids
|nocursorline nomatchit shellxquote={ undolevels=1000
|nodigraph maxmapdepth=20 showcmd virtualedit=
|noexchange more showmode novisualbell
| fileencoding= nomultiple-cursors sidescroll=0 visualdelay=100
| fileformat=unix noNERDTree sidescrolloff=0 whichwrap=b,s
|nogdefault nrformats=hex nosmartcase wrap
|nohighlightedyank nonumber nosneak wrapscan
| history=50 operatorfunc= startofline
|nohlsearch norelativenumber nosurround
|noideajoin scroll=0 notextobj-entire
|noargtextobj noideajoin scroll=0 notextobj-entire
|nobomb ideamarks scrolljump=1 notextobj-indent
|nobreakindent ideawrite=all scrolloff=0 textwidth=0
| colorcolumn= noignorecase selectmode= timeout
|nocommentary noincsearch shellcmdflag=-x timeoutlen=1000
|nocursorline nolist shellxescape=@ notrackactionids
|nodigraph nomatchit shellxquote={ undolevels=1000
|noexchange maxmapdepth=20 showcmd virtualedit=
| fileencoding= more showmode novisualbell
| fileformat=unix nomultiple-cursors sidescroll=0 visualdelay=100
|nogdefault noNERDTree sidescrolloff=0 whichwrap=b,s
|nohighlightedyank nrformats=hex nosmartcase wrap
| history=50 nonumber nosneak wrapscan
|nohlsearch operatorfunc= startofline
|noideaglobalmode norelativenumber nosurround
| clipboard=ideaput,autoselect,exclude:cons\|linux
| guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175
| ide=IntelliJ IDEA Community Edition
@ -523,6 +523,7 @@ class SetglobalCommandTest : VimTestCase() {
|nohlsearch
| ide=IntelliJ IDEA Community Edition
|noideacopypreprocess
|noideaglobalmode
|noideajoin
| ideamarks
| idearefactormode=select

View File

@ -489,20 +489,21 @@ class SetlocalCommandTest : VimTestCase() {
setOsSpecificOptionsToSafeValues()
assertCommandOutput("setlocal all", """
|--- Local option values ---
|noargtextobj ideamarks norelativenumber startofline
|nobomb idearefactormode= scroll=0 nosurround
|nobreakindent ideawrite=all scrolljump=1 notextobj-entire
| colorcolumn= noignorecase scrolloff=-1 notextobj-indent
|nocommentary noincsearch selectmode= textwidth=0
|nocursorline nolist shellcmdflag=-x timeout
|nodigraph nomatchit shellxescape=@ timeoutlen=1000
|noexchange maxmapdepth=20 shellxquote={ notrackactionids
| fileformat=unix more showcmd virtualedit=
|nogdefault nomultiple-cursors showmode novisualbell
|nohighlightedyank noNERDTree sidescroll=0 visualdelay=100
| history=50 nrformats=hex sidescrolloff=-1 whichwrap=b,s
|nohlsearch nonumber nosmartcase wrap
|--ideajoin operatorfunc= nosneak wrapscan
|noargtextobj ideamarks scroll=0 notextobj-entire
|nobomb idearefactormode= scrolljump=1 notextobj-indent
|nobreakindent ideawrite=all scrolloff=-1 textwidth=0
| colorcolumn= noignorecase selectmode= timeout
|nocommentary noincsearch shellcmdflag=-x timeoutlen=1000
|nocursorline nolist shellxescape=@ notrackactionids
|nodigraph nomatchit shellxquote={ virtualedit=
|noexchange maxmapdepth=20 showcmd novisualbell
| fileformat=unix more showmode visualdelay=100
|nogdefault nomultiple-cursors sidescroll=0 whichwrap=b,s
|nohighlightedyank noNERDTree sidescrolloff=-1 wrap
| history=50 nrformats=hex nosmartcase wrapscan
|nohlsearch nonumber nosneak
|noideaglobalmode operatorfunc= startofline
|--ideajoin norelativenumber nosurround
| clipboard=ideaput,autoselect,exclude:cons\|linux
| fileencoding=utf-8
| guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175
@ -576,6 +577,7 @@ class SetlocalCommandTest : VimTestCase() {
|nohlsearch
| ide=IntelliJ IDEA Community Edition
|--ideacopypreprocess
|noideaglobalmode
|--ideajoin
| ideamarks
| idearefactormode=

View File

@ -8,7 +8,6 @@
package org.jetbrains.plugins.ideavim.ex.implementation.statements
import com.maddyhome.idea.vim.api.injector
import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
import org.jetbrains.plugins.ideavim.VimTestCase
@ -56,7 +55,6 @@ class FunctionDeclarationTest : VimTestCase() {
)
typeText(commandToKeys("echo F1()"))
assertExOutput("5550")
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo F2()"))
assertExOutput("555")
@ -205,13 +203,10 @@ class FunctionDeclarationTest : VimTestCase() {
),
)
typeText(commandToKeys("echo F1()"))
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo F2()"))
assertExOutput("1")
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo F2()"))
assertExOutput("2")
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo F2()"))
assertExOutput("3")
@ -235,11 +230,9 @@ class FunctionDeclarationTest : VimTestCase() {
),
)
typeText(commandToKeys("echo F1()"))
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo F2()"))
assertExOutput("1")
typeText(commandToKeys("delf! F1"))
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo F2()"))
assertExOutput("2")
@ -267,12 +260,10 @@ class FunctionDeclarationTest : VimTestCase() {
assertPluginError(true)
assertPluginErrorMessageContains("E121: Undefined variable: x")
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo F2()"))
assertExOutput("10")
assertPluginError(false)
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo F1()"))
assertPluginError(true)
assertPluginErrorMessageContains("E121: Undefined variable: x")
@ -298,7 +289,6 @@ class FunctionDeclarationTest : VimTestCase() {
assertPluginError(true)
assertPluginErrorMessageContains("E121: Undefined variable: unknownVar")
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo x"))
assertExOutput("10")
assertPluginError(false)
@ -484,12 +474,7 @@ class FunctionDeclarationTest : VimTestCase() {
)
typeText(commandToKeys("1,4call F1()"))
assertPluginError(false)
assertExOutput("""
1:4
1:4
1:4
1:4
""".trimIndent())
assertExOutput("1:4")
assertState(
"""
-----

View File

@ -16,6 +16,7 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.LogicalPosition
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.common.CharacterPosition
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.register.RegisterConstants.ALTERNATE_BUFFER_REGISTER
import com.maddyhome.idea.vim.register.RegisterConstants.BLACK_HOLE_REGISTER
@ -167,7 +168,7 @@ object NeovimTesting {
fun vimMode() = neovimApi.mode.get().mode
private fun assertMode(editor: Editor) {
val ideavimState = editor.vim.mode.toVimNotation()
val ideavimState = editor.vim.vimStateMachine.mode.toVimNotation()
val neovimState = vimMode()
assertEquals(neovimState, ideavimState)
}

View File

@ -33,6 +33,7 @@ class TestOptionConstants {
const val whichwrap = "whichwrap"
// IdeaVim specific
const val ideaglobalmode = "ideaglobalmode"
const val ideatracetime = "ideatracetime"
}
}

View File

@ -128,7 +128,6 @@ abstract class VimTestCase {
KeyHandler.getInstance().fullReset(editor.vim)
}
KeyHandler.getInstance().keyHandlerState.reset(Mode.NORMAL())
injector.vimState.reset()
resetAllOptions()
VimPlugin.getKey().resetKeyMappings()
VimPlugin.getSearch().resetState()
@ -222,7 +221,6 @@ abstract class VimTestCase {
VimPlugin.getChange().resetRepeat()
VimPlugin.getKey().savedShortcutConflicts.clear()
assertTrue(KeyHandler.getInstance().keyStack.isEmpty())
injector.outputPanel.getCurrentOutputPanel()?.close()
// Tear down neovim
NeovimTesting.tearDown(testInfo)

View File

@ -156,6 +156,7 @@ private class OptionsVerificator : BeforeTestExecutionCallback, AfterTestExecuti
val LOG by lazy { vimLogger<OptionsVerificator>() }
private val ignored = setOf(
TestOptionConstants.guicursor,
TestOptionConstants.ideaglobalmode,
TestOptionConstants.ideatracetime,
TestIjOptionConstants.ideavimsupport,
TestOptionConstants.maxmapdepth,

View File

@ -27,9 +27,6 @@ class TestInjector(val injector: VimInjector) : VimInjector by injector {
tracers[key] = collector
}
override val vimState
get() = injector.vimState
override val optionGroup: VimOptionGroup
get() {
val tracer = tracers[OptionsTracer] as? OptionsTraceCollector

View File

@ -18,9 +18,11 @@ import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.command.MappingProcessor
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.CurrentCommandState
import com.maddyhome.idea.vim.common.EditorListener
import com.maddyhome.idea.vim.diagnostic.VimLogger
import com.maddyhome.idea.vim.diagnostic.trace
import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.impl.state.toMappingMode
import com.maddyhome.idea.vim.key.CommandPartNode
import com.maddyhome.idea.vim.key.KeyConsumer
@ -165,7 +167,7 @@ class KeyHandler {
if (commandBuilder.isReady) {
logger.trace("Ready command builder. Execute command.")
executeCommand(editor, context, injector.vimState, keyState)
executeCommand(editor, context, editor.vimStateMachine, keyState)
}
// Don't record the keystroke that stops the recording (unmapped this is `q`)
@ -184,7 +186,7 @@ class KeyHandler {
logger.trace("Command builder is set to BAD")
keyState.commandBuilder.commandState = CurrentCommandState.BAD_COMMAND
editor.resetOpPending()
injector.vimState.resetRegisterPending()
editor.vimStateMachine.resetRegisterPending()
editor.isReplaceCharacter = false
reset(keyState, editor.mode)
}
@ -265,7 +267,6 @@ class KeyHandler {
*/
fun reset(editor: VimEditor) {
logger.trace { "Reset is executed" }
editor.resetOpPending()
keyHandlerState.partialReset(editor.mode)
keyHandlerState.commandBuilder.resetAll(getKeyRoot(editor.mode.toMappingMode()))
}
@ -297,7 +298,7 @@ class KeyHandler {
injector.messages.clearError()
editor.mode = Mode.NORMAL()
injector.vimState.executingCommand = null
editor.vimStateMachine.executingCommand = null
keyHandlerState.digraphSequence.reset()
reset(keyHandlerState, editor.mode)
@ -321,7 +322,7 @@ class KeyHandler {
val operatorArguments: OperatorArguments,
) : Runnable {
override fun run() {
val editorState = injector.vimState
val editorState = VimStateMachine.getInstance(editor)
keyState.commandBuilder.commandState = CurrentCommandState.NEW_COMMAND
val register = cmd.register
if (register != null) {
@ -491,4 +492,15 @@ sealed interface KeyProcessResult {
}
}
class KeyHandlerStateResetter : EditorListener {
override fun focusGained(editor: VimEditor) {
KeyHandler.getInstance().reset(editor)
}
override fun focusLost(editor: VimEditor) {
// We do not reset the KeyHandler state here, because a command or a search prompt is not considered to be part of the
// editor, and resetting state would break the search functionality.
}
}
typealias KeyProcessing = (KeyHandlerState, VimEditor, ExecutionContext) -> Unit

View File

@ -16,6 +16,7 @@ import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.enumSetOf
import com.maddyhome.idea.vim.helper.vimStateMachine
import java.util.*
@CommandOrMotion(keys = ["<Insert>"], modes = [Mode.INSERT])
@ -37,5 +38,5 @@ class InsertInsertAction : VimActionHandler.SingleExecution() {
private fun processInsert(editor: VimEditor) {
editor.insertMode = !editor.insertMode
editor.toggleInsertOverwrite()
editor.vimStateMachine.toggleInsertOverwrite()
}

View File

@ -18,6 +18,7 @@ import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.pushVisualMode
import com.maddyhome.idea.vim.helper.setSelectMode
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.state.mode.SelectionType
/**
@ -41,7 +42,8 @@ class SelectToggleVisualMode : VimActionHandler.SingleExecution() {
companion object {
fun toggleMode(editor: VimEditor) {
val myMode = editor.mode
val commandState = editor.vimStateMachine
val myMode = commandState.mode
if (myMode is com.maddyhome.idea.vim.state.mode.Mode.VISUAL) {
editor.setSelectMode(myMode.selectionType)
if (myMode.selectionType != SelectionType.LINE_WISE) {

View File

@ -51,6 +51,10 @@ open class GlobalOptions(scope: OptionAccessScope): OptionsPropertiesBase(scope)
// IdeaVim specific options. Put any editor or IDE specific options in IjOptionProperties
// This is an experimental option that enables global mode for the editor. However,
// for the moment it has issues and there is no quality garantee if this option is enabled
var ideaglobalmode: Boolean by optionProperty(Options.ideaglobalmode)
// Temporary flags for work-in-progress behaviour. Hidden from the output of `:set all`
var ideastrictmode: Boolean by optionProperty(Options.ideastrictmode)
var ideatracetime: Boolean by optionProperty(Options.ideatracetime)

View File

@ -320,6 +320,7 @@ object Options {
)
// IdeaVim specific options. Put any editor or IDE specific options in IjOptionProperties
val ideaglobalmode: ToggleOption = addOption(ToggleOption("ideaglobalmode", GLOBAL, "ideaglobalmode", false))
// Temporary feature flags for work-in-progress behaviour, diagnostic switches, etc. Hidden from the output of `:set all`
val ideastrictmode: ToggleOption = addOption(ToggleOption("ideastrictmode", GLOBAL, "ideastrictmode", false, isHidden = true))

View File

@ -26,11 +26,13 @@ import com.maddyhome.idea.vim.helper.CharacterHelper
import com.maddyhome.idea.vim.helper.CharacterHelper.charType
import com.maddyhome.idea.vim.helper.StrictMode
import com.maddyhome.idea.vim.helper.usesVirtualSpace
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_END
import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_POS
import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_START
import com.maddyhome.idea.vim.register.RegisterConstants.LAST_INSERTED_TEXT_REGISTER
import com.maddyhome.idea.vim.state.VimStateMachine.Companion.getInstance
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.state.mode.toReturnTo
@ -428,7 +430,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
* @param mode The mode - indicate insert or replace
*/
override fun initInsert(editor: VimEditor, context: ExecutionContext, mode: Mode) {
val state = injector.vimState
val state = getInstance(editor)
for (caret in editor.nativeCarets()) {
caret.vimInsertStart = editor.createLiveMarker(caret.offset, caret.offset)
injector.markService.setMark(caret, MARK_CHANGE_START, caret.offset)
@ -440,7 +442,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
editor.insertMode = false
}
if (cmd.flags.contains(CommandFlags.FLAG_NO_REPEAT_INSERT)) {
val commandState = injector.vimState
val commandState = getInstance(editor)
repeatInsert(
editor,
context,
@ -449,7 +451,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
OperatorArguments(false, 1, commandState.mode),
)
} else {
val commandState = injector.vimState
val commandState = getInstance(editor)
repeatInsert(
editor,
context,
@ -482,7 +484,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
}
override fun runEnterAction(editor: VimEditor, context: ExecutionContext) {
val state = injector.vimState
val state = getInstance(editor)
if (!state.isDotRepeatInProgress) {
// While repeating the enter action has been already executed because `initInsert` repeats the input
val action = injector.nativeActionManager.enterAction
@ -500,7 +502,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
}
override fun runEnterAboveAction(editor: VimEditor, context: ExecutionContext) {
val state = injector.vimState
val state = getInstance(editor)
if (!state.isDotRepeatInProgress) {
// While repeating the enter action has been already executed because `initInsert` repeats the input
val action = injector.nativeActionManager.createLineAboveCaret
@ -541,7 +543,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
val markGroup = injector.markService
markGroup.setMark(editor, VimMarkService.INSERT_EXIT_MARK)
markGroup.setMark(editor, MARK_CHANGE_END)
if (editor.mode is Mode.REPLACE) {
if (getInstance(editor).mode is Mode.REPLACE) {
editor.insertMode = true
}
var cnt = if (lastInsert != null) lastInsert!!.count else 0
@ -556,7 +558,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
if (context != null) {
injector.changeGroup.repeatInsert(editor, context, if (cnt == 0) 0 else cnt - 1, true, operatorArguments)
}
if (editor.mode is Mode.INSERT) {
if (getInstance(editor).mode is Mode.INSERT) {
updateLastInsertedTextRegister()
}
@ -587,7 +589,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
* @param context The data context
*/
override fun processEnter(editor: VimEditor, context: ExecutionContext) {
if (editor.mode is Mode.REPLACE) {
if (editor.vimStateMachine.mode is Mode.REPLACE) {
editor.insertMode = true
}
val enterKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)
@ -597,7 +599,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
break
}
}
if (editor.mode is Mode.REPLACE) {
if (editor.vimStateMachine.mode is Mode.REPLACE) {
editor.insertMode = false
}
}

View File

@ -11,6 +11,7 @@ package com.maddyhome.idea.vim.api
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.LiveRange
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.ReturnTo
@ -125,22 +126,20 @@ import com.maddyhome.idea.vim.state.mode.returnTo
*/
interface VimEditor {
var mode: Mode
get() = injector.vimState.mode
get() = vimStateMachine.mode
set(value) {
val vimState = injector.vimState
if (vimState.mode == value) return
if (vimStateMachine.mode == value) return
val oldValue = vimState.mode
(vimState as VimStateMachineImpl).mode = value
val oldValue = vimStateMachine.mode
(vimStateMachine as VimStateMachineImpl).mode = value
injector.listenersNotifier.notifyModeChanged(this, oldValue)
}
var isReplaceCharacter: Boolean
get() = injector.vimState.isReplaceCharacter
get() = vimStateMachine.isReplaceCharacter
set(value) {
val vimState = injector.vimState
if (value != vimState.isReplaceCharacter) {
(vimState as VimStateMachineImpl).isReplaceCharacter = value
if (value != vimStateMachine.isReplaceCharacter) {
(vimStateMachine as VimStateMachineImpl).isReplaceCharacter = value
injector.listenersNotifier.notifyIsReplaceCharChanged(this)
}
}
@ -163,7 +162,7 @@ interface VimEditor {
* which indicated that the buffer is empty. However, the line count is still 1.
*
* The variable for line count is named `ml_line_count` in `memline` structure. There is a single spot where
* `0` is assigned to this variable (at the end of `buf_freeall` function), however I'm not sure that this affects
* `0` is assigned to this variable (at the end of `buf_freeall` public function), however I'm not sure that this affects
* the opened buffer.
* Another thing that I don't understand is that I don't see where this variable is updated. There is a small chance
* that this variable doesn't present the line count, so I may be wrong and line count can return zero.
@ -209,7 +208,7 @@ interface VimEditor {
fun isOneLineMode(): Boolean
/**
* function for refactoring, get rid of it
* public function for refactoring, get rid of it
*/
fun search(
pair: Pair<Int, Int>,
@ -305,23 +304,6 @@ interface VimEditor {
}
}
}
/**
* Toggles the insert/overwrite state. If currently insert, goto replace mode. If currently replace, goto insert
* mode.
*/
fun toggleInsertOverwrite() {
val oldMode = this.mode
var newMode = oldMode
if (oldMode == Mode.INSERT) {
newMode = Mode.REPLACE
} else if (oldMode == Mode.REPLACE) {
newMode = Mode.INSERT
}
if (oldMode != newMode) {
mode = newMode
}
}
}
interface MutableVimEditor : VimEditor {

View File

@ -0,0 +1,23 @@
/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.api
interface VimExOutputPanelService {
fun getPanel(editor: VimEditor): VimExOutputPanel
}
interface VimExOutputPanel {
val isActive: Boolean
val text: String?
fun output(text: String)
fun clear()
fun close()
}

View File

@ -22,8 +22,6 @@ import com.maddyhome.idea.vim.vimscript.services.VariableService
import com.maddyhome.idea.vim.yank.VimYankGroup
interface VimInjector {
val vimState: VimStateMachine
/**
* The window used when we need a window but there are no editor windows available.
*
@ -87,14 +85,12 @@ interface VimInjector {
val visualMotionGroup: VimVisualMotionGroup
// [FINISHED] Class moved to vim-engine, but it's attached to Editor using IJ things
@Deprecated("Please use VimInjector.vimState", replaceWith = ReplaceWith("vimState"))
fun commandStateFor(editor: VimEditor): VimStateMachine
// [FINISHED] Class moved to vim-engine, but it's attached to Editor using IJ things
/**
* COMPATIBILITY-LAYER: Added new method with Any
* Please see: https://jb.gg/zo8n0r
*/
@Deprecated("Please use VimInjector.vimState", replaceWith = ReplaceWith("vimState"))
fun commandStateFor(editor: Any): VimStateMachine
// !! in progress
@ -112,6 +108,9 @@ interface VimInjector {
// Can't be fully moved to vim-engine.
val actionExecutor: VimActionExecutor
// Can't be fully moved to vim-engine.
val exOutputPanel: VimExOutputPanelService
// Can't be fully moved to vim-engine.
val clipboardManager: VimClipboardManager
@ -176,7 +175,6 @@ interface VimInjector {
val variableService: VariableService
val commandLine: VimCommandLineService
val outputPanel: VimOutputPanelService
// !! in progress
val functionService: VimscriptFunctionService

View File

@ -16,10 +16,8 @@ import com.maddyhome.idea.vim.api.stubs.VimProcessGroupStub
import com.maddyhome.idea.vim.common.VimListenersNotifier
import com.maddyhome.idea.vim.diagnostic.VimLogger
import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
import com.maddyhome.idea.vim.register.VimRegisterGroup
import com.maddyhome.idea.vim.register.VimRegisterGroupBase
import com.maddyhome.idea.vim.state.VimStateMachine
import com.maddyhome.idea.vim.vimscript.services.VariableService
import com.maddyhome.idea.vim.vimscript.services.VimVariableServiceBase
import com.maddyhome.idea.vim.yank.VimYankGroup
@ -31,8 +29,6 @@ abstract class VimInjectorBase : VimInjector {
val registerGroupStub: VimRegisterGroupBase by lazy { object : VimRegisterGroupBase() {} }
}
override val vimState: VimStateMachine = VimStateMachineImpl()
override val parser: VimStringParser = object : VimStringParserBase() {}
override val optionGroup: VimOptionGroup by lazy { object : VimOptionGroupBase() {} }

View File

@ -27,6 +27,7 @@ import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.diagnostic.debug
import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.mark.Jump
import com.maddyhome.idea.vim.mark.Mark
import com.maddyhome.idea.vim.mark.VimMark
@ -367,7 +368,7 @@ abstract class VimMarkServiceBase : VimMarkService {
val markLineStartOffset = editor.getLineStartOffset(mark.line)
val markLineEndOffset = editor.getLineEndOffset(mark.line, true)
val command = injector.vimState.executingCommand
val command = editor.vimStateMachine.executingCommand
// If text is being changed from the start of the mark line (a special case for mark deletion)
val changeFromMarkLineStart =
(command != null && command.type === Command.Type.CHANGE && delStartOffset == markLineStartOffset)

View File

@ -1,39 +0,0 @@
/*
* Copyright 2003-2024 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.api
interface VimOutputPanel {
/**
* The current text displayed in the output panel.
* The actual text may be different (if we called the [addText] method and did not call [show] afterward)
*/
val text: String
/**
* Appends the specified text to the existing content of the output panel.
* If 'isNewLine' is true, the text will begin on a new line.
*
* Note: The full text content is not updated in the display until [show] is invoked.
*
* @param text The text to append.
* @param isNewLine Whether to start the appended text on a new line. Defaults to true.
*/
fun addText(text: String, isNewLine: Boolean = true)
/**
* This method shows the text output or updates the output text if the panel was already shown
*/
fun show()
/**
* Disposes or hides the output panel, depending on its implementation.
* This may free any associated resources.
*/
fun close()
}

View File

@ -1,33 +0,0 @@
/*
* Copyright 2003-2024 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.api
interface VimOutputPanelService {
/**
* Creates a new VimOutputPanel instance for building output without affecting the current panel until displayed.
*/
// TODO make it possible to pass null instead of editor
fun create(editor: VimEditor, context: ExecutionContext): VimOutputPanel
/**
* Retrieves the current VimOutputPanel or creates a new one if none exists.
*/
fun getOrCreate(editor: VimEditor, context: ExecutionContext): VimOutputPanel
/**
* Returns the currently active VimOutputPanel, if available.
*/
fun getCurrentOutputPanel(): VimOutputPanel?
/**
* Appends text to the existing output panel or creates a new one with the given text.
* Basic method that should be sufficient in most cases.
*/
fun output(editor: VimEditor, context: ExecutionContext, text: String)
}

View File

@ -1,21 +0,0 @@
/*
* Copyright 2003-2024 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.api
abstract class VimOutputPanelServiceBase : VimOutputPanelService {
override fun getOrCreate(editor: VimEditor, context: ExecutionContext): VimOutputPanel {
return getCurrentOutputPanel() ?: create(editor, context)
}
override fun output(editor: VimEditor, context: ExecutionContext, text: String) {
val panel = getOrCreate(editor, context)
panel.addText(text)
panel.show()
}
}

View File

@ -21,7 +21,7 @@ interface VimProcessGroup {
// TODO remove me
// TODO: Why ^^ ? Should that also include startExEntry?
fun processExKey(editor: VimEditor, stroke: KeyStroke, processResultBuilder: KeyProcessResult.KeyProcessResultBuilder): Boolean
fun cancelExEntry(editor: VimEditor, refocusOwningEditor: Boolean, resetCaret: Boolean)
fun cancelExEntry(editor: VimEditor, resetCaret: Boolean)
@kotlin.jvm.Throws(java.lang.Exception::class)
fun executeCommand(editor: VimEditor, command: String, input: CharSequence?, currentDirectoryPath: String?): String?

View File

@ -11,6 +11,7 @@ package com.maddyhome.idea.vim.api
import com.maddyhome.idea.vim.KeyHandler.Companion.getInstance
import com.maddyhome.idea.vim.KeyProcessResult
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.ReturnableFromCmd
import com.maddyhome.idea.vim.state.mode.inVisualMode
@ -26,7 +27,7 @@ abstract class VimProcessGroupBase : VimProcessGroup {
// Don't allow ex commands in one line editors
if (editor.isOneLineMode()) return
val currentMode = editor.mode
val currentMode = editor.vimStateMachine.mode
check(currentMode is ReturnableFromCmd) {
"Cannot enable cmd mode from current mode $currentMode"
}
@ -75,12 +76,12 @@ abstract class VimProcessGroupBase : VimProcessGroup {
}
}
override fun cancelExEntry(editor: VimEditor, refocusOwningEditor: Boolean, resetCaret: Boolean) {
override fun cancelExEntry(editor: VimEditor, resetCaret: Boolean) {
// If 'cpoptions' contains 'x', then Escape should execute the command line. This is the default for Vi but not Vim.
// IdeaVim does not (currently?) support 'cpoptions', so sticks with Vim's default behaviour. Escape cancels.
editor.mode = editor.mode.returnTo()
getInstance().reset(editor)
injector.commandLine.getActiveCommandLine()?.deactivate(refocusOwningEditor, resetCaret)
injector.commandLine.getActiveCommandLine()?.deactivate(refocusOwningEditor = true, resetCaret)
}
private fun getRange(editor: VimEditor, cmd: Command) = when {

View File

@ -20,6 +20,7 @@ import com.maddyhome.idea.vim.regexp.VimRegex
import com.maddyhome.idea.vim.regexp.VimRegexException
import com.maddyhome.idea.vim.regexp.VimRegexOptions
import com.maddyhome.idea.vim.regexp.match.VimMatchResult
import com.maddyhome.idea.vim.state.VimStateMachine
import com.maddyhome.idea.vim.state.mode.Mode
import org.jetbrains.annotations.Contract
import org.jetbrains.annotations.Range
@ -1453,7 +1454,7 @@ abstract class VimSearchHelperBase : VimSearchHelper {
while (selectionEndWithoutNewline < sequence.length && sequence[selectionEndWithoutNewline] == '\n') {
selectionEndWithoutNewline++
}
val mode = editor.mode
val mode = VimStateMachine.getInstance(editor).mode
if (mode is Mode.VISUAL) {
if (closingTagTextRange.startOffset == selectionEndWithoutNewline &&
openingTag.endOffset == selectionStart

View File

@ -16,6 +16,7 @@ import com.maddyhome.idea.vim.group.visual.vimUpdateEditorSelection
import com.maddyhome.idea.vim.helper.exitVisualMode
import com.maddyhome.idea.vim.helper.pushVisualMode
import com.maddyhome.idea.vim.helper.setSelectMode
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.ReturnTo
import com.maddyhome.idea.vim.state.mode.SelectionType
@ -71,7 +72,7 @@ abstract class VimVisualMotionGroupBase : VimVisualMotionGroup {
} else {
editor.mode = Mode.VISUAL(
selectionType,
returnTo ?: editor.mode.returnTo
returnTo ?: editor.vimStateMachine.mode.returnTo
)
editor.forEachCaret { it.vimSetSelection(it.offset) }
}

View File

@ -40,7 +40,7 @@ class VimProcessGroupStub : VimProcessGroupBase() {
TODO("Not yet implemented")
}
override fun cancelExEntry(editor: VimEditor, refocusOwningEditor: Boolean, resetCaret: Boolean) {
override fun cancelExEntry(editor: VimEditor, resetCaret: Boolean) {
TODO("Not yet implemented")
}

View File

@ -17,6 +17,7 @@ import com.maddyhome.idea.vim.api.options
import com.maddyhome.idea.vim.diagnostic.debug
import com.maddyhome.idea.vim.diagnostic.trace
import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.impl.state.toMappingMode
import com.maddyhome.idea.vim.key.KeyConsumer
import com.maddyhome.idea.vim.key.KeyMappingLayer
@ -41,12 +42,13 @@ object MappingProcessor: KeyConsumer {
log.debug("Start processing key mappings.")
val keyState = keyProcessResultBuilder.state
val commandState = editor.vimStateMachine
val mappingState = keyState.mappingState
val commandBuilder = keyState.commandBuilder
if (commandBuilder.isAwaitingCharOrDigraphArgument() ||
commandBuilder.isBuildingMultiKeyCommand() ||
isMappingDisabledForKey(key, keyState) ||
injector.vimState.isRegisterPending
commandState.isRegisterPending
) {
log.debug("Finish key processing, returning false")
return false

View File

@ -9,7 +9,6 @@
package com.maddyhome.idea.vim.common
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.state.mode.Mode
import org.jetbrains.annotations.ApiStatus.Internal
import java.util.concurrent.ConcurrentLinkedDeque
@ -23,37 +22,30 @@ class VimListenersNotifier {
val isReplaceCharListeners: MutableCollection<IsReplaceCharListener> = ConcurrentLinkedDeque()
fun notifyModeChanged(editor: VimEditor, oldMode: Mode) {
if (!injector.enabler.isEnabled()) return // we remove all the listeners when turning the plugin off, but let's do it just in case
modeChangeListeners.forEach { it.modeChanged(editor, oldMode) }
}
fun notifyEditorCreated(editor: VimEditor) {
if (!injector.enabler.isEnabled()) return // we remove all the listeners when turning the plugin off, but let's do it just in case
myEditorListeners.forEach { it.created(editor) }
}
fun notifyEditorReleased(editor: VimEditor) {
if (!injector.enabler.isEnabled()) return // we remove all the listeners when turning the plugin off, but let's do it just in case
myEditorListeners.forEach { it.released(editor) }
}
fun notifyEditorFocusGained(editor: VimEditor) {
if (!injector.enabler.isEnabled()) return // we remove all the listeners when turning the plugin off, but let's do it just in case
myEditorListeners.forEach { it.focusGained(editor) }
}
fun notifyEditorFocusLost(editor: VimEditor) {
if (!injector.enabler.isEnabled()) return // we remove all the listeners when turning the plugin off, but let's do it just in case
myEditorListeners.forEach { it.focusLost(editor) }
}
fun notifyMacroRecordingStarted() {
if (!injector.enabler.isEnabled()) return // we remove all the listeners when turning the plugin off, but let's do it just in case
macroRecordingListeners.forEach { it.recordingStarted() }
}
fun notifyMacroRecordingFinished() {
if (!injector.enabler.isEnabled()) return // we remove all the listeners when turning the plugin off, but let's do it just in case
macroRecordingListeners.forEach { it.recordingFinished() }
}
@ -66,7 +58,6 @@ class VimListenersNotifier {
}
fun notifyIsReplaceCharChanged(editor: VimEditor) {
if (!injector.enabler.isEnabled()) return // we remove all the listeners when turning the plugin off, but let's do it just in case
isReplaceCharListeners.forEach { it.isReplaceCharChanged(editor) }
}

View File

@ -18,6 +18,7 @@ import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.helper.noneOfEnum
import com.maddyhome.idea.vim.state.VimStateMachine
import org.jetbrains.annotations.NonNls
import java.util.*
import javax.swing.KeyStroke
@ -113,7 +114,7 @@ abstract class EditorActionHandlerBase(private val myRunForEachCaret: Boolean) {
}
if (currentCaret == primaryCaret) {
val cmd = injector.vimState.executingCommand ?: run {
val cmd = VimStateMachine.getInstance(editor).executingCommand ?: run {
injector.messages.indicateError()
return
}
@ -126,7 +127,7 @@ abstract class EditorActionHandlerBase(private val myRunForEachCaret: Boolean) {
logger.debug("Execute command with handler: " + this.javaClass.name)
val cmd = injector.vimState.executingCommand ?: run {
val cmd = VimStateMachine.getInstance(editor).executingCommand ?: run {
injector.messages.indicateError()
return
}

View File

@ -24,6 +24,8 @@ import com.maddyhome.idea.vim.group.visual.VimSimpleSelection
import com.maddyhome.idea.vim.group.visual.VisualChange
import com.maddyhome.idea.vim.group.visual.VisualOperation
import com.maddyhome.idea.vim.helper.exitVisualMode
import com.maddyhome.idea.vim.helper.inRepeatMode
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
import com.maddyhome.idea.vim.state.mode.inBlockSelection
@ -174,7 +176,7 @@ sealed class VisualOperatorActionHandler : EditorActionHandlerBase(false) {
private fun VimEditor.collectSelections(): Map<VimCaret, VimSelection>? {
return when {
!this.inVisualMode && injector.vimState.isDotRepeatInProgress -> {
!this.inVisualMode && this.inRepeatMode -> {
if (this.vimLastSelectionType == SelectionType.BLOCK_WISE) {
val primaryCaret = primaryCaret()
val range = primaryCaret.vimLastVisualOperatorRange ?: return null
@ -209,7 +211,7 @@ sealed class VisualOperatorActionHandler : EditorActionHandlerBase(false) {
)
}
else -> this.nativeCarets().associateWith { caret ->
val mode = this.mode
val mode = this.vimStateMachine.mode
VimSimpleSelection.createWithNative(
caret.vimSelectionStart,
caret.offset,
@ -233,7 +235,7 @@ sealed class VisualOperatorActionHandler : EditorActionHandlerBase(false) {
editor.forEachCaret {
val change =
if (editor.inVisualMode && !injector.vimState.isDotRepeatInProgress) {
if (editor.inVisualMode && !editor.inRepeatMode) {
VisualOperation.getRange(editor, it, cmd.flags)
} else {
null

View File

@ -26,7 +26,10 @@ val TextRange.endOffsetInclusive: Int
get() = if (this.endOffset > 0 && this.endOffset > this.startOffset) this.endOffset - 1 else this.endOffset
val VimEditor.inRepeatMode: Boolean
get() = injector.vimState.isDotRepeatInProgress
get() = this.vimStateMachine.isDotRepeatInProgress
val VimEditor.vimStateMachine: VimStateMachine
get() = VimStateMachine.getInstance(this)
val VimEditor.usesVirtualSpace: Boolean
get() = injector.options(this).virtualedit.contains(OptionConstants.virtualedit_onemore)

View File

@ -33,7 +33,7 @@ fun VimEditor.exitVisualMode() {
injector.markService.setVisualSelectionMarks(this)
this.nativeCarets().forEach { it.vimSelectionStartClear() }
val returnTo = this.mode.returnTo
val returnTo = this.vimStateMachine.mode.returnTo
when (returnTo) {
ReturnTo.INSERT -> {
this.mode = Mode.INSERT

View File

@ -47,12 +47,21 @@ class VimStateMachineImpl : VimStateMachine {
}
}
override fun reset() {
mode = Mode.NORMAL()
isDotRepeatInProgress = false
isRegisterPending = false
isReplaceCharacter = false
executingCommand = null
/**
* Toggles the insert/overwrite state. If currently insert, goto replace mode. If currently replace, goto insert
* mode.
*/
override fun toggleInsertOverwrite() {
val oldMode = this.mode
var newMode = oldMode
if (oldMode == Mode.INSERT) {
newMode = Mode.REPLACE
} else if (oldMode == Mode.REPLACE) {
newMode = Mode.INSERT
}
if (oldMode != newMode) {
mode = newMode
}
}
companion object {

View File

@ -22,6 +22,7 @@ import com.maddyhome.idea.vim.common.argumentCaptured
import com.maddyhome.idea.vim.diagnostic.trace
import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.key.CommandNode
import com.maddyhome.idea.vim.key.CommandPartNode
import com.maddyhome.idea.vim.key.KeyConsumer
@ -125,8 +126,9 @@ class CommandConsumer : KeyConsumer {
processBuilder.addExecutionStep { lambdaKeyState, lambdaEditor, lambdaContext ->
logger.trace("Set waiting for the argument")
val argumentType = action.argumentType
startWaitingForArgument(lambdaEditor, lambdaContext, action, argumentType!!, lambdaKeyState, injector.vimState)
lambdaKeyState.partialReset(lambdaEditor.mode)
val editorState = lambdaEditor.vimStateMachine
startWaitingForArgument(lambdaEditor, lambdaContext, action, argumentType!!, lambdaKeyState, editorState)
lambdaKeyState.partialReset(editorState.mode)
}
}

View File

@ -11,9 +11,9 @@ package com.maddyhome.idea.vim.key.consumers
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.KeyProcessResult
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.diagnostic.trace
import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.key.KeyConsumer
import com.maddyhome.idea.vim.state.KeyHandlerState
import com.maddyhome.idea.vim.state.mode.Mode
@ -43,7 +43,7 @@ class CommandCountConsumer : KeyConsumer {
private fun isCommandCountKey(chKey: Char, keyState: KeyHandlerState, editor: VimEditor): Boolean {
// Make sure to avoid handling '0' as the start of a count.
val editorState = injector.vimState
val editorState = editor.vimStateMachine
val commandBuilder = keyState.commandBuilder
val notRegisterPendingCommand = editorState.mode is Mode.NORMAL && !editorState.isRegisterPending
val visualMode = editorState.mode is Mode.VISUAL && !editorState.isRegisterPending

View File

@ -14,6 +14,7 @@ import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.diagnostic.trace
import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.key.KeyConsumer
import java.awt.event.KeyEvent
import javax.swing.KeyStroke
@ -32,7 +33,7 @@ class RegisterConsumer : KeyConsumer {
shouldRecord: KeyHandler.MutableBoolean,
): Boolean {
logger.trace { "Entered RegisterConsumer" }
if (!injector.vimState.isRegisterPending) return false
if (!editor.vimStateMachine.isRegisterPending) return false
logger.trace("Pending mode.")
keyProcessResultBuilder.state.commandBuilder.addKey(key)
@ -44,7 +45,7 @@ class RegisterConsumer : KeyConsumer {
private fun handleSelectRegister(editor: VimEditor, chKey: Char, processBuilder: KeyProcessResult.KeyProcessResultBuilder) {
logger.trace("Handle select register")
injector.vimState.resetRegisterPending()
editor.vimStateMachine.resetRegisterPending()
if (injector.registerGroup.isValid(chKey)) {
logger.trace("Valid register")
processBuilder.state.commandBuilder.pushCommandPart(chKey)

View File

@ -11,9 +11,9 @@ package com.maddyhome.idea.vim.key.consumers
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.KeyProcessResult
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.diagnostic.trace
import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.key.KeyConsumer
import com.maddyhome.idea.vim.state.KeyHandlerState
import com.maddyhome.idea.vim.state.VimStateMachine
@ -35,25 +35,24 @@ class SelectRegisterConsumer : KeyConsumer {
): Boolean {
logger.trace { "Entered SelectRegisterConsumer" }
val state = keyProcessResultBuilder.state
if (!isSelectRegister(key, state)) return false
if (!isSelectRegister(key, state, editor.vimStateMachine)) return false
logger.trace("Select register")
state.commandBuilder.addKey(key)
keyProcessResultBuilder.addExecutionStep { _, lambdaEditor, _ ->
injector.vimState.isRegisterPending = true
lambdaEditor.vimStateMachine.isRegisterPending = true
}
return true
}
private fun isSelectRegister(key: KeyStroke, keyState: KeyHandlerState): Boolean {
val vimState = injector.vimState
if (vimState.mode !is Mode.NORMAL && vimState.mode !is Mode.VISUAL) {
private fun isSelectRegister(key: KeyStroke, keyState: KeyHandlerState, editorState: VimStateMachine): Boolean {
if (editorState.mode !is Mode.NORMAL && editorState.mode !is Mode.VISUAL) {
return false
}
return if (vimState.isRegisterPending) {
return if (editorState.isRegisterPending) {
true
} else {
key.keyChar == '"' && !KeyHandler.getInstance().isOperatorPending(vimState.mode, keyState) && keyState.commandBuilder.expectedArgumentType == null
key.keyChar == '"' && !KeyHandler.getInstance().isOperatorPending(editorState.mode, keyState) && keyState.commandBuilder.expectedArgumentType == null
}
}
}

View File

@ -146,8 +146,8 @@ abstract class VimRegisterGroupBase : VimRegisterGroup {
myRegisters.clear()
}
private fun isSmallDeletionSpecialCase(): Boolean {
val currentCommand = injector.vimState.executingCommand
private fun isSmallDeletionSpecialCase(editor: VimEditor): Boolean {
val currentCommand = VimStateMachine.getInstance(editor).executingCommand
if (currentCommand != null) {
val argument = currentCommand.argument
if (argument != null) {
@ -264,7 +264,7 @@ abstract class VimRegisterGroupBase : VimRegisterGroup {
)
// Deletes go into numbered registers only if text is smaller than a line, register is used or it's a special case
if (!smallInlineDeletion && register == defaultRegister || isSmallDeletionSpecialCase()) {
if (!smallInlineDeletion && register == defaultRegister || isSmallDeletionSpecialCase(editor)) {
// Old 1 goes to 2, etc. Old 8 to 9, old 9 is lost
var d = '8'
while (d >= '1') {

View File

@ -8,9 +8,11 @@
package com.maddyhome.idea.vim.state
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
import com.maddyhome.idea.vim.state.mode.Mode
import java.util.*
@ -38,12 +40,22 @@ interface VimStateMachine {
fun resetRegisterPending()
fun reset()
/**
* Toggles the insert/overwrite state. If currently insert, goto replace mode. If currently replace, goto insert
* mode.
*/
fun toggleInsertOverwrite()
companion object {
@Deprecated("Please use VimInjector.vimState", replaceWith = ReplaceWith("injector.vimState", imports = ["com.maddyhome.idea.vim.api.injector"]))
private val globalState = VimStateMachineImpl()
// TODO do we really need this method? Can't we use editor.vimStateMachine?
fun getInstance(editor: Any?): VimStateMachine {
return injector.vimState
return if (editor == null || injector.globalOptions().ideaglobalmode) {
globalState
} else {
injector.commandStateFor(editor)
}
}
}
}

View File

@ -9,10 +9,13 @@
package com.maddyhome.idea.vim.state.mode
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.helper.vimStateMachine
val VimEditor.mode: Mode
get() = this.vimStateMachine.mode
val VimEditor.inVisualMode: Boolean
get() = injector.vimState.mode is Mode.VISUAL
get() = this.vimStateMachine.mode is Mode.VISUAL
val VimEditor.inBlockSelection: Boolean
get() = this.mode.selectionType == SelectionType.BLOCK_WISE

View File

@ -52,14 +52,14 @@ data class CmdCommand(val range: Range, val argument: String) : Command.SingleEx
}
override fun processCommand(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments): ExecutionResult {
val result: Boolean = if (argument.trim().isEmpty()) {
this.listAlias(editor, context, "")
this.listAlias(editor, "")
} else {
this.addAlias(editor, context)
this.addAlias(editor)
}
return if (result) ExecutionResult.Success else ExecutionResult.Error
}
private fun listAlias(editor: VimEditor, context: ExecutionContext, filter: String): Boolean {
private fun listAlias(editor: VimEditor, filter: String): Boolean {
val lineSeparator = "\n"
val allAliases = injector.commandGroup.listAliases()
val aliases = allAliases.filter {
@ -67,11 +67,11 @@ data class CmdCommand(val range: Range, val argument: String) : Command.SingleEx
}.map {
"${it.key.padEnd(12)}${it.value.numberOfArguments.padEnd(11)}${it.value.printValue()}"
}.sortedWith(String.CASE_INSENSITIVE_ORDER).joinToString(lineSeparator)
injector.outputPanel.output(editor, context, "Name Args Definition$lineSeparator$aliases")
injector.exOutputPanel.getPanel(editor).output("Name Args Definition$lineSeparator$aliases")
return true
}
private fun addAlias(editor: VimEditor, context: ExecutionContext): Boolean {
private fun addAlias(editor: VimEditor): Boolean {
var argument = argument.trim()
// Handle overwriting of aliases
@ -165,7 +165,7 @@ data class CmdCommand(val range: Range, val argument: String) : Command.SingleEx
// No message should be shown either, since there is no editor.
return false
}
return this.listAlias(editor, context, alias)
return this.listAlias(editor, alias)
}
// If we are not over-writing existing aliases, and an alias with the same command

View File

@ -29,7 +29,7 @@ data class EchoCommand(val range: Range, val args: List<Expression>) : Command.S
val text = args.joinToString(separator = " ", postfix = "\n") {
it.evaluate(editor, context, this).toString()
}
injector.outputPanel.output(editor, context, text)
injector.exOutputPanel.getPanel(editor).output(text)
return ExecutionResult.Success
}
}

View File

@ -131,7 +131,9 @@ data class GlobalCommand(val range: Range, val argument: String, val invert: Boo
globalBusy = true
try {
if (cmd.isEmpty() || (cmd.length == 1 && cmd[0] == '\n')) {
injector.outputPanel.output(editor, context, originalCommandString + '\n' + PrintCommand.getText(editor, lines))
val exOutputModel = injector.exOutputPanel.getPanel(editor)
exOutputModel.clear()
exOutputModel.output(originalCommandString + '\n' + PrintCommand.getText(editor, lines))
} else {
for (mark in marks) {
if (gotInt) break

View File

@ -105,7 +105,8 @@ data class HistoryCommand(val range: Range, val argument: String) : Command.Sing
else -> ""
}
injector.outputPanel.output(editor, context, res)
injector.exOutputPanel.getPanel(editor).output(res)
return ExecutionResult.Success
}

View File

@ -58,7 +58,8 @@ data class JumpsCommand(val range: Range, val argument: String) : Command.Single
text.append(">\n")
}
injector.outputPanel.output(editor, context, text.toString())
injector.exOutputPanel.getPanel(editor).output(text.toString())
return ExecutionResult.Success
}
}

View File

@ -49,7 +49,7 @@ data class MarksCommand(val range: Range, val argument: String) : Command.Single
" ${mark.key} $line $column $text"
}
injector.outputPanel.output(editor, context, res)
injector.exOutputPanel.getPanel(editor).output(res)
return ExecutionResult.Success
}

View File

@ -19,6 +19,7 @@ import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.ex.ranges.Range
import com.maddyhome.idea.vim.helper.exitVisualMode
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
// todo make it for each caret
@ -39,6 +40,7 @@ data class NormalCommand(val range: Range, val argument: String) : Command.Singl
argument = argument.substring(1)
}
val commandState = editor.vimStateMachine
val rangeUsed = range.size() != 0
when (editor.mode) {
is Mode.VISUAL -> {
@ -48,8 +50,8 @@ data class NormalCommand(val range: Range, val argument: String) : Command.Singl
editor.currentCaret().moveToBufferPosition(BufferPosition(selectionStart.line, selectionStart.col))
}
}
is Mode.CMD_LINE -> injector.processGroup.cancelExEntry(editor, refocusOwningEditor = true, resetCaret = false)
Mode.INSERT, Mode.REPLACE -> editor.exitInsertMode(context, OperatorArguments(false, 1, editor.mode))
is Mode.CMD_LINE -> injector.processGroup.cancelExEntry(editor, false)
Mode.INSERT, Mode.REPLACE -> editor.exitInsertMode(context, OperatorArguments(false, 1, commandState.mode))
is Mode.SELECT -> editor.exitSelectModeNative(false)
is Mode.OP_PENDING, is Mode.NORMAL -> Unit
}
@ -74,12 +76,12 @@ data class NormalCommand(val range: Range, val argument: String) : Command.Singl
}
// Exit if state leaves as insert or cmd_line
val mode = editor.mode
val mode = commandState.mode
if (mode is Mode.CMD_LINE) {
injector.processGroup.cancelExEntry(editor, refocusOwningEditor = true, resetCaret = false)
injector.processGroup.cancelExEntry(editor, false)
}
if (mode is Mode.INSERT || mode is Mode.REPLACE) {
editor.exitInsertMode(context, OperatorArguments(false, 1, mode))
editor.exitInsertMode(context, OperatorArguments(false, 1, commandState.mode))
}
}

View File

@ -29,12 +29,22 @@ data class PrintCommand(val range: Range, val argument: String) : Command.Single
editor.removeSecondaryCarets()
val caret = editor.currentCaret()
val lineRange = getLineRangeWithCount(editor, caret)
val text = getText(editor, (lineRange.startLine .. lineRange.endLine).toList())
// Move the caret to the start of the last line of the range
val offset = injector.motion.moveCaretToLineStartSkipLeading(editor, lineRange.endLine)
caret.moveToOffset(offset)
injector.outputPanel.output(editor, context, getText(editor, (lineRange.startLine .. lineRange.endLine).toList()))
// Note that we append to the existing text because we can be called multiple times by the :global command
// TODO: We need a better way to handle output. This is not very efficient, especially if we have a lot of output
val exOutputModel = injector.exOutputPanel.getPanel(editor)
if (!exOutputModel.isActive) {
// When we add text, we make the panel active. So if we're appending, it should already be active. If it's not,
// make sure it's clear
exOutputModel.clear()
}
val existingText = exOutputModel.text?.let { if (it.isNotEmpty()) it.plus("\n") else it } ?: ""
exOutputModel.output(existingText + text)
return ExecutionResult.Success
}

View File

@ -38,7 +38,7 @@ data class RegistersCommand(val range: Range, val argument: String) : Command.Si
" $type \"${reg.name} ${EngineStringHelper.toPrintableCharacters(text).take(200)}"
}
injector.outputPanel.output(editor, context, regs)
injector.exOutputPanel.getPanel(editor).output(regs)
return ExecutionResult.Success
}

View File

@ -61,7 +61,7 @@ abstract class SetCommandBase(range: Range, argument: String) : Command.SingleEx
context: ExecutionContext,
operatorArguments: OperatorArguments
): ExecutionResult {
parseOptionLine(editor, context, commandArgument, getScope(editor))
parseOptionLine(editor, commandArgument, getScope(editor))
return ExecutionResult.Success
}
@ -93,7 +93,7 @@ abstract class SetCommandBase(range: Range, argument: String) : Command.SingleEx
* @param args The raw text passed to the `:set` command
* @throws ExException Thrown if any option names or operations are incorrect
*/
fun parseOptionLine(editor: VimEditor, context: ExecutionContext, args: String, scope: OptionAccessScope) {
fun parseOptionLine(editor: VimEditor, args: String, scope: OptionAccessScope) {
val optionGroup = injector.optionGroup
val columnFormat = args.startsWith("!")
@ -105,14 +105,14 @@ fun parseOptionLine(editor: VimEditor, context: ExecutionContext, args: String,
val changedOptions = optionGroup.getAllOptions()
.filter { !optionGroup.isDefaultValue(it, scope) && (!it.isHidden || (injector.application.isInternal() && !injector.application.isUnitTest())) }
.map { Pair(it.name, it.name) }
showOptions(editor, context, changedOptions, scope, true, columnFormat)
showOptions(editor, changedOptions, scope, true, columnFormat)
return
}
argument == "all" -> {
val options = optionGroup.getAllOptions()
.filter { !it.isHidden || (injector.application.isInternal() && !injector.application.isUnitTest()) }
.map { Pair(it.name, it.name) }
showOptions(editor, context, options, scope, true, columnFormat)
showOptions(editor, options, scope, true, columnFormat)
return
}
argument == "all&" -> {
@ -196,7 +196,7 @@ fun parseOptionLine(editor: VimEditor, context: ExecutionContext, args: String,
// Now show all options that were individually requested
if (toShow.size > 0) {
showOptions(editor, context, toShow, scope, false, columnFormat)
showOptions(editor, toShow, scope, false, columnFormat)
}
if (error != null) {
@ -212,7 +212,6 @@ private fun getValidToggleOption(optionName: String, token: String) =
private fun showOptions(
editor: VimEditor,
context: ExecutionContext,
nameAndToken: Collection<Pair<String, String>>,
scope: OptionAccessScope,
showIntro: Boolean,
@ -276,7 +275,7 @@ private fun showOptions(
appendLine(option)
}
}
injector.outputPanel.output(editor, context, output)
injector.exOutputPanel.getPanel(editor).output(output)
if (unknownOption != null) {
throw exExceptionMessage("E518", unknownOption.second)