1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2024-11-25 16:42:55 +01:00

Compare commits

...

10 Commits

Author SHA1 Message Date
edb9b194bb
Set plugin version to chylex-12 2022-07-06 01:15:57 +02:00
Alex Plate
eae7ed95e2
Revert "Fix(VIM-308) Undo requires one more step if the cursor is not at the position where it was after undo"
This reverts commit 9dbe3c33
2022-07-06 01:15:56 +02:00
a1e2ae0eb9
Change matchit plugin to use HTML patterns in unrecognized files 2022-07-06 01:15:56 +02:00
eae2e3b6b8
Fix put command not working with multiple cursors 2022-07-06 01:15:56 +02:00
c2d997a520
Fix vim-surround not working with multiple cursors
Fixes multiple cursors with vim-surround commands `cs, ds, S` (but not `ys`).
2022-07-06 01:15:56 +02:00
e2a8a3c21a
Add VimScript 'renaming()' function 2022-07-06 01:08:14 +02:00
9b7fee6163
Add support for repeatable actions with ':raction' 2022-07-06 01:08:14 +02:00
d0f9d3dc70
Implement partial code completion support in macros
Works ok with insertions (Enter, Ctrl+Enter) but not with replacements (Tab)
2022-07-06 01:08:14 +02:00
8d3a69b338
Disable taking over arrow keys and Home/End 2022-07-06 01:08:14 +02:00
3c530474a1
Set custom plugin version 2022-07-06 01:08:14 +02:00
23 changed files with 393 additions and 96 deletions

View File

@ -1,9 +1,9 @@
# suppress inspection "UnusedProperty" for whole file # suppress inspection "UnusedProperty" for whole file
ideaVersion=LATEST-EAP-SNAPSHOT ideaVersion=2022.1.2
downloadIdeaSources=true downloadIdeaSources=true
instrumentPluginCode=true instrumentPluginCode=true
version=SNAPSHOT version=chylex-12
javaVersion=11 javaVersion=11
remoteRobotVersion=0.11.10 remoteRobotVersion=0.11.10
antlrVersion=4.10.1 antlrVersion=4.10.1

View File

@ -149,6 +149,10 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
if ((keyCode == KeyEvent.VK_TAB || keyCode == KeyEvent.VK_ENTER) && editor.appCodeTemplateCaptured()) return false if ((keyCode == KeyEvent.VK_TAB || keyCode == KeyEvent.VK_ENTER) && editor.appCodeTemplateCaptured()) return false
if (keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_RIGHT) return false
if (keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN) return false
if (keyCode == KeyEvent.VK_HOME || keyCode == KeyEvent.VK_END) return false
if (editor.inInsertMode) { if (editor.inInsertMode) {
if (keyCode == KeyEvent.VK_TAB) { if (keyCode == KeyEvent.VK_TAB) {
// TODO: This stops VimEditorTab seeing <Tab> in insert mode and correctly scrolling the view // TODO: This stops VimEditorTab seeing <Tab> in insert mode and correctly scrolling the view

View File

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

View File

@ -22,12 +22,14 @@ import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimChangeGroup
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.VimStateMachine
import com.maddyhome.idea.vim.command.SelectionType
import com.maddyhome.idea.vim.command.MappingMode import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.command.SelectionType
import com.maddyhome.idea.vim.command.VimStateMachine
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.extension.ExtensionHandler
import com.maddyhome.idea.vim.extension.VimExtension import com.maddyhome.idea.vim.extension.VimExtension
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister
@ -37,9 +39,9 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMa
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister
import com.maddyhome.idea.vim.extension.ExtensionHandler
import com.maddyhome.idea.vim.helper.EditorHelper import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.editorMode import com.maddyhome.idea.vim.helper.editorMode
import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore
import com.maddyhome.idea.vim.key.OperatorFunction import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.newapi.IjVimCaret import com.maddyhome.idea.vim.newapi.IjVimCaret
import com.maddyhome.idea.vim.newapi.IjVimEditor import com.maddyhome.idea.vim.newapi.IjVimEditor
@ -84,22 +86,20 @@ class VimSurroundExtension : VimExtension {
override val isRepeatable = true override val isRepeatable = true
override fun execute(editor: VimEditor, context: ExecutionContext) { override fun execute(editor: VimEditor, context: ExecutionContext) {
setOperatorFunction(Operator()) setOperatorFunction(Operator(supportsMultipleCursors = false)) // TODO
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij) executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
} }
} }
private class VSurroundHandler : ExtensionHandler { private class VSurroundHandler : ExtensionHandler {
override fun execute(editor: VimEditor, context: ExecutionContext) { override fun execute(editor: VimEditor, context: ExecutionContext) {
val selectionStart = editor.ij.caretModel.primaryCaret.selectionStart
// NB: Operator ignores SelectionType anyway // NB: Operator ignores SelectionType anyway
if (!Operator().apply(editor.ij, context.ij, SelectionType.CHARACTER_WISE)) { if (!Operator(supportsMultipleCursors = true).apply(editor.ij, context.ij, SelectionType.CHARACTER_WISE)) {
return return
} }
runWriteAction { runWriteAction {
// Leave visual mode // Leave visual mode
executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij) executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
editor.ij.caretModel.moveToOffset(selectionStart)
} }
} }
} }
@ -120,6 +120,10 @@ class VimSurroundExtension : VimExtension {
companion object { companion object {
fun change(editor: Editor, charFrom: Char, newSurround: Pair<String, String>?) { fun change(editor: Editor, charFrom: Char, newSurround: Pair<String, String>?) {
editor.runWithEveryCaretAndRestore { changeAtCaret(editor, charFrom, newSurround) }
}
fun changeAtCaret(editor: Editor, charFrom: Char, newSurround: Pair<String, String>?) {
// We take over the " register, so preserve it // We take over the " register, so preserve it
val oldValue: List<KeyStroke>? = getRegister(REGISTER) val oldValue: List<KeyStroke>? = getRegister(REGISTER)
// Empty the " register // Empty the " register
@ -184,26 +188,44 @@ class VimSurroundExtension : VimExtension {
} }
} }
private class Operator : OperatorFunction { private class Operator(private val supportsMultipleCursors: Boolean) : OperatorFunction {
override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean { override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean {
val c = getChar(editor) val c = getChar(editor)
if (c.code == 0) return true if (c.code == 0) return true
val pair = getOrInputPair(c, editor) ?: return false val pair = getOrInputPair(c, editor) ?: return false
// XXX: Will it work with line-wise or block-wise selections?
val range = getSurroundRange(editor) ?: return false
runWriteAction { runWriteAction {
val change = VimPlugin.getChange() val change = VimPlugin.getChange()
val leftSurround = pair.first if (supportsMultipleCursors) {
val primaryCaret = editor.caretModel.primaryCaret editor.runWithEveryCaretAndRestore {
change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, leftSurround) applyOnce(editor, change, pair)
change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.endOffset + leftSurround.length, pair.second) }
}
else {
applyOnce(editor, change, pair)
// Jump back to start // Jump back to start
executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor) executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor)
} }
}
return true return true
} }
private fun applyOnce(editor: Editor, change: VimChangeGroup, pair: Pair<String, String>) {
// XXX: Will it work with line-wise or block-wise selections?
val range = getSurroundRange(editor)
if (range != null) {
val primaryCaret = editor.caretModel.primaryCaret
change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, pair.first)
change.insertText(
IjVimEditor(editor),
IjVimCaret(primaryCaret),
range.endOffset + pair.first.length,
pair.second
)
}
}
private fun getSurroundRange(editor: Editor): TextRange? = when (editor.editorMode) { private fun getSurroundRange(editor: Editor): TextRange? = when (editor.editorMode) {
VimStateMachine.Mode.COMMAND -> VimPlugin.getMark().getChangeMarks(editor.vim) VimStateMachine.Mode.COMMAND -> VimPlugin.getMark().getChangeMarks(editor.vim)
VimStateMachine.Mode.VISUAL -> editor.caretModel.primaryCaret.run { TextRange(selectionStart, selectionEnd) } VimStateMachine.Mode.VISUAL -> editor.caretModel.primaryCaret.run { TextRange(selectionStart, selectionEnd) }

View File

@ -50,7 +50,10 @@ import com.maddyhome.idea.vim.group.visual.VisualModeHelperKt;
import com.maddyhome.idea.vim.helper.*; import com.maddyhome.idea.vim.helper.*;
import com.maddyhome.idea.vim.key.KeyHandlerKeeper; import com.maddyhome.idea.vim.key.KeyHandlerKeeper;
import com.maddyhome.idea.vim.listener.VimInsertListener; import com.maddyhome.idea.vim.listener.VimInsertListener;
import com.maddyhome.idea.vim.newapi.*; import com.maddyhome.idea.vim.newapi.IjExecutionContext;
import com.maddyhome.idea.vim.newapi.IjExecutionContextKt;
import com.maddyhome.idea.vim.newapi.IjVimCaret;
import com.maddyhome.idea.vim.newapi.IjVimEditor;
import com.maddyhome.idea.vim.options.OptionConstants; import com.maddyhome.idea.vim.options.OptionConstants;
import com.maddyhome.idea.vim.options.OptionScope; import com.maddyhome.idea.vim.options.OptionScope;
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString; import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString;
@ -200,6 +203,61 @@ public class ChangeGroup extends VimChangeGroupBase {
return new Pair<>(range, type); return new Pair<>(range, type);
} }
/**
* Delete the range of text.
*
* @param editor The editor to delete the text from
* @param caret The caret to be moved after deletion
* @param range The range to delete
* @param type The type of deletion
* @param isChange Is from a change action
* @return true if able to delete the text, false if not
*/
@Override
public boolean deleteRange(@NotNull VimEditor editor,
@NotNull VimCaret caret,
@NotNull TextRange range,
@Nullable SelectionType type,
boolean isChange,
boolean noYank) {
// Update the last column before we delete, or we might be retrieving the data for a line that no longer exists
UserDataManager.setVimLastColumn(((IjVimCaret) caret).getCaret(), InlayHelperKt.getInlayAwareVisualColumn(((IjVimCaret) caret).getCaret()));
boolean removeLastNewLine = removeLastNewLine(editor, range, type);
final boolean res = deleteText(editor, range, type, noYank);
if (removeLastNewLine) {
int textLength = ((IjVimEditor) editor).getEditor().getDocument().getTextLength();
((IjVimEditor) editor).getEditor().getDocument().deleteString(textLength - 1, textLength);
}
if (res) {
int pos = EditorHelper.normalizeOffset(((IjVimEditor) editor).getEditor(), range.getStartOffset(), isChange);
if (type == SelectionType.LINE_WISE) {
pos = VimPlugin.getMotion()
.moveCaretToLineWithStartOfLineOption(editor, editor.offsetToLogicalPosition(pos).getLine(),
caret);
}
injector.getMotion().moveCaret(editor, caret, pos);
}
return res;
}
private boolean removeLastNewLine(@NotNull VimEditor editor, @NotNull TextRange range, @Nullable SelectionType type) {
int endOffset = range.getEndOffset();
int fileSize = EditorHelperRt.getFileSize(((IjVimEditor) editor).getEditor());
if (endOffset > fileSize) {
if (injector.getOptionService().isSet(OptionScope.GLOBAL.INSTANCE, OptionConstants.ideastrictmodeName, OptionConstants.ideastrictmodeName)) {
throw new IllegalStateException("Incorrect offset. File size: " + fileSize + ", offset: " + endOffset);
}
endOffset = fileSize;
}
return type == SelectionType.LINE_WISE &&
range.getStartOffset() != 0 &&
((IjVimEditor) editor).getEditor().getDocument().getCharsSequence().charAt(endOffset - 1) != '\n' &&
endOffset == fileSize;
}
@Override @Override
public void insertLineAround(@NotNull VimEditor editor, @NotNull ExecutionContext context, int shift) { public void insertLineAround(@NotNull VimEditor editor, @NotNull ExecutionContext context, int shift) {
com.maddyhome.idea.vim.newapi.ChangeGroupKt.insertLineAround(editor, context, shift); com.maddyhome.idea.vim.newapi.ChangeGroupKt.insertLineAround(editor, context, shift);
@ -228,7 +286,8 @@ public class ChangeGroup extends VimChangeGroupBase {
@NotNull VimCaret caret, @NotNull VimCaret caret,
@NotNull ExecutionContext context, @NotNull ExecutionContext context,
@NotNull Argument argument, @NotNull Argument argument,
@NotNull OperatorArguments operatorArguments) { @NotNull OperatorArguments operatorArguments,
boolean noYank) {
int count0 = operatorArguments.getCount0(); int count0 = operatorArguments.getCount0();
// Vim treats cw as ce and cW as cE if cursor is on a non-blank character // Vim treats cw as ce and cW as cE if cursor is on a non-blank character
final Command motion = argument.getMotion(); final Command motion = argument.getMotion();
@ -306,7 +365,7 @@ public class ChangeGroup extends VimChangeGroupBase {
Pair<TextRange, SelectionType> deleteRangeAndType = Pair<TextRange, SelectionType> deleteRangeAndType =
getDeleteRangeAndType(editor, caret, context, argument, true, operatorArguments.withCount0(count0)); getDeleteRangeAndType(editor, caret, context, argument, true, operatorArguments.withCount0(count0));
if (deleteRangeAndType == null) return false; if (deleteRangeAndType == null) return false;
return changeRange(editor, caret, deleteRangeAndType.getFirst(), deleteRangeAndType.getSecond(), context); return changeRange(editor, caret, deleteRangeAndType.getFirst(), deleteRangeAndType.getSecond(), context, noYank);
} }
} }
@ -436,7 +495,8 @@ public class ChangeGroup extends VimChangeGroupBase {
@NotNull VimCaret caret, @NotNull VimCaret caret,
@NotNull TextRange range, @NotNull TextRange range,
@NotNull SelectionType type, @NotNull SelectionType type,
ExecutionContext context) { @Nullable ExecutionContext context,
boolean noYank) {
int col = 0; int col = 0;
int lines = 0; int lines = 0;
if (type == SelectionType.BLOCK_WISE) { if (type == SelectionType.BLOCK_WISE) {
@ -450,7 +510,7 @@ public class ChangeGroup extends VimChangeGroupBase {
final VimLogicalPosition lp = editor.offsetToLogicalPosition(injector.getMotion().moveCaretToLineStartSkipLeading(editor, caret)); final VimLogicalPosition lp = editor.offsetToLogicalPosition(injector.getMotion().moveCaretToLineStartSkipLeading(editor, caret));
boolean res = deleteRange(editor, caret, range, type, true); boolean res = deleteRange(editor, caret, range, type, true, noYank);
if (res) { if (res) {
if (type == SelectionType.LINE_WISE) { if (type == SelectionType.LINE_WISE) {
// Please don't use `getDocument().getText().isEmpty()` because it converts CharSequence into String // Please don't use `getDocument().getText().isEmpty()` because it converts CharSequence into String
@ -675,7 +735,7 @@ public class ChangeGroup extends VimChangeGroupBase {
} }
} }
if (pos > wsoff) { if (pos > wsoff) {
deleteText(editor, new TextRange(wsoff, pos), null); deleteText(editor, new TextRange(wsoff, pos), null, false);
} }
} }
} }

View File

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

View File

@ -97,8 +97,23 @@ class PutGroup : VimPutBase() {
EditorHelper.getOrderedCaretsList(editor.ij).map { IjVimCaret(it) } EditorHelper.getOrderedCaretsList(editor.ij).map { IjVimCaret(it) }
} }
injector.application.runWriteAction { injector.application.runWriteAction {
val singleCaret = myCarets.singleOrNull()
if (singleCaret != null) {
putForCaret(editor, singleCaret, data, additionalData, context, text)
}
else {
val lines = text.text.split('\n')
if (lines.size != myCarets.size) {
myCarets.forEach { caret -> putForCaret(editor, caret, data, additionalData, context, text) } myCarets.forEach { caret -> putForCaret(editor, caret, data, additionalData, context, text) }
} }
else {
myCarets.asReversed().forEachIndexed { index, caret ->
val line = ProcessedTextData(lines[index], text.typeInRegister, text.transferableData)
putForCaret(editor, caret, data, additionalData, context, line)
}
}
}
}
} }
private fun putForCaret( private fun putForCaret(

View File

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

View File

@ -25,9 +25,7 @@ import com.intellij.openapi.command.undo.UndoManager
import com.intellij.openapi.components.Service import com.intellij.openapi.components.Service
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.ChangesListener
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.options.OptionScope import com.maddyhome.idea.vim.options.OptionScope
@ -49,7 +47,7 @@ class UndoRedoHelper : UndoRedoBase() {
SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) } SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) }
} else { } else {
val editor = CommonDataKeys.EDITOR.getData(context.ij)?.vim val editor = CommonDataKeys.EDITOR.getData(context.ij)?.vim
performUntilFileChanges(editor, { undoManager.isUndoAvailable(fileEditor) }, { undoManager.undo(fileEditor) }) undoManager.undo(fileEditor)
editor?.carets()?.forEach { editor?.carets()?.forEach {
val ijCaret = it.ij val ijCaret = it.ij
val hasSelection = ijCaret.hasSelection() val hasSelection = ijCaret.hasSelection()
@ -75,30 +73,14 @@ class UndoRedoHelper : UndoRedoBase() {
SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) } SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) }
} else { } else {
val editor = CommonDataKeys.EDITOR.getData(context.ij)?.vim val editor = CommonDataKeys.EDITOR.getData(context.ij)?.vim
performUntilFileChanges(editor, { undoManager.isRedoAvailable(fileEditor) }, { undoManager.redo(fileEditor) }) undoManager.redo(fileEditor)
if (editor?.primaryCaret()?.ij?.hasSelection() == true) {
undoManager.redo(fileEditor)
}
editor?.carets()?.forEach { it.ij.removeSelection() } editor?.carets()?.forEach { it.ij.removeSelection() }
} }
return true return true
} }
return false return false
} }
private fun performUntilFileChanges(editor: IjVimEditor?, check: () -> Boolean, action: Runnable) {
if (editor == null) return
val vimDocument = editor.document
val changeListener = object : ChangesListener {
var hasChanged = false
override fun documentChanged(change: ChangesListener.Change) {
hasChanged = true
}
}
vimDocument.addChangeListener(changeListener)
while (check() && !changeListener.hasChanged) {
action.run()
}
vimDocument.removeChangeListener(changeListener)
}
} }

View File

@ -19,8 +19,10 @@
package com.maddyhome.idea.vim.listener package com.maddyhome.idea.vim.listener
import com.intellij.codeInsight.lookup.Lookup import com.intellij.codeInsight.lookup.Lookup
import com.intellij.codeInsight.lookup.LookupManager
import com.intellij.codeInsight.lookup.LookupManagerListener import com.intellij.codeInsight.lookup.LookupManagerListener
import com.intellij.codeInsight.lookup.impl.LookupImpl import com.intellij.codeInsight.lookup.impl.LookupImpl
import com.intellij.codeInsight.lookup.impl.actions.ChooseItemAction
import com.intellij.codeInsight.template.Template import com.intellij.codeInsight.template.Template
import com.intellij.codeInsight.template.TemplateEditingAdapter import com.intellij.codeInsight.template.TemplateEditingAdapter
import com.intellij.codeInsight.template.TemplateManagerListener import com.intellij.codeInsight.template.TemplateManagerListener
@ -35,10 +37,10 @@ import com.intellij.openapi.actionSystem.ex.AnActionListener
import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.DumbAwareToggleAction import com.intellij.openapi.project.DumbAwareToggleAction
import com.intellij.openapi.util.TextRange
import com.maddyhome.idea.vim.KeyHandler import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.VimStateMachine import com.maddyhome.idea.vim.command.VimStateMachine
import com.maddyhome.idea.vim.group.NotificationService
import com.maddyhome.idea.vim.helper.EditorDataContext import com.maddyhome.idea.vim.helper.EditorDataContext
import com.maddyhome.idea.vim.helper.vimStateMachine import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.helper.inNormalMode import com.maddyhome.idea.vim.helper.inNormalMode
@ -49,6 +51,8 @@ import com.maddyhome.idea.vim.options.OptionScope
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimInt import com.maddyhome.idea.vim.vimscript.model.datatypes.VimInt
import com.maddyhome.idea.vim.vimscript.model.options.helpers.IdeaRefactorModeHelper import com.maddyhome.idea.vim.vimscript.model.options.helpers.IdeaRefactorModeHelper
import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.NonNls
import java.awt.event.KeyEvent
import javax.swing.KeyStroke
/** /**
* @author Alex Plate * @author Alex Plate
@ -60,6 +64,8 @@ object IdeaSpecifics {
private val surrounderAction = private val surrounderAction =
"com.intellij.codeInsight.generation.surroundWith.SurroundWithHandler\$InvokeSurrounderAction" "com.intellij.codeInsight.generation.surroundWith.SurroundWithHandler\$InvokeSurrounderAction"
private var editor: Editor? = null private var editor: Editor? = null
private var completionPrevDocumentLength: Int? = null
private var completionPrevDocumentOffset: Int? = null
override fun beforeActionPerformed(action: AnAction, dataContext: DataContext, event: AnActionEvent) { override fun beforeActionPerformed(action: AnAction, dataContext: DataContext, event: AnActionEvent) {
if (!VimPlugin.isEnabled()) return if (!VimPlugin.isEnabled()) return
@ -68,19 +74,52 @@ object IdeaSpecifics {
editor = hostEditor editor = hostEditor
} }
//region Track action id
if (VimPlugin.getOptionService().isSet(OptionScope.GLOBAL, OptionConstants.trackactionidsName)) { if (VimPlugin.getOptionService().isSet(OptionScope.GLOBAL, OptionConstants.trackactionidsName)) {
if (action !is NotificationService.ActionIdNotifier.CopyActionId && action !is NotificationService.ActionIdNotifier.StopTracking) {
val id: String? = ActionManager.getInstance().getId(action) ?: (action.shortcutSet as? ProxyShortcutSet)?.actionId val id: String? = ActionManager.getInstance().getId(action) ?: (action.shortcutSet as? ProxyShortcutSet)?.actionId
VimPlugin.getNotifications(dataContext.getData(CommonDataKeys.PROJECT)).notifyActionId(id) VimPlugin.getNotifications(dataContext.getData(CommonDataKeys.PROJECT)).notifyActionId(id)
} }
if (hostEditor != null && action is ChooseItemAction && hostEditor.vimStateMachine?.isRecording == true) {
val lookup = LookupManager.getActiveLookup(hostEditor)
if (lookup != null) {
val charsToRemove = hostEditor.caretModel.primaryCaret.offset - lookup.lookupStart
val register = VimPlugin.getRegister()
val backSpace = KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0)
repeat(charsToRemove) {
register.recordKeyStroke(backSpace)
}
completionPrevDocumentLength = hostEditor.document.textLength - charsToRemove
completionPrevDocumentOffset = lookup.lookupStart
}
} }
//endregion
} }
override fun afterActionPerformed(action: AnAction, dataContext: DataContext, event: AnActionEvent) { override fun afterActionPerformed(action: AnAction, dataContext: DataContext, event: AnActionEvent) {
if (!VimPlugin.isEnabled()) return if (!VimPlugin.isEnabled()) return
val editor = editor
if (editor != null && action is ChooseItemAction && editor.vimStateMachine?.isRecording == true) {
val prevDocumentLength = completionPrevDocumentLength
val prevDocumentOffset = completionPrevDocumentOffset
if (prevDocumentLength != null && prevDocumentOffset != null) {
val register = VimPlugin.getRegister()
val addedTextLength = editor.document.textLength - prevDocumentLength
val caretShift = addedTextLength - (editor.caretModel.primaryCaret.offset - prevDocumentOffset)
val leftArrow = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0)
register.recordText(editor.document.getText(TextRange(prevDocumentOffset, prevDocumentOffset + addedTextLength)))
repeat(caretShift.coerceAtLeast(0)) {
register.recordKeyStroke(leftArrow)
}
}
this.completionPrevDocumentLength = null
this.completionPrevDocumentOffset = null
}
//region Enter insert mode after surround with if //region Enter insert mode after surround with if
if (surrounderAction == action.javaClass.name && surrounderItems.any { if (surrounderAction == action.javaClass.name && surrounderItems.any {
action.templatePresentation.text.endsWith( action.templatePresentation.text.endsWith(
@ -99,7 +138,7 @@ object IdeaSpecifics {
} }
//endregion //endregion
editor = null this.editor = null
} }
} }

View File

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

View File

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

View File

@ -1,4 +1,4 @@
<idea-plugin url="https://plugins.jetbrains.com/plugin/164" xmlns:xi="http://www.w3.org/2001/XInclude"> <idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
<name>IdeaVim</name> <name>IdeaVim</name>
<id>IdeaVIM</id> <id>IdeaVIM</id>
<change-notes><![CDATA[ <change-notes><![CDATA[
@ -65,7 +65,7 @@
<li><a href="https://youtrack.jetbrains.com/issues/VIM">Issue tracker</a>: feature requests and bug reports</li> <li><a href="https://youtrack.jetbrains.com/issues/VIM">Issue tracker</a>: feature requests and bug reports</li>
</ul> </ul>
]]></description> ]]></description>
<version>SNAPSHOT</version> <version>chylex</version>
<vendor>JetBrains</vendor> <vendor>JetBrains</vendor>
<!-- Please search for "[VERSION UPDATE]" in project in case you update the since-build version --> <!-- Please search for "[VERSION UPDATE]" in project in case you update the since-build version -->

View File

@ -75,28 +75,6 @@ class UndoActionTest : VimTestCase() {
assertFalse(hasSelection()) assertFalse(hasSelection())
} }
fun `test cursor movements do not require additional undo`() {
val keys = listOf("a1<Esc>ea2<Esc>ea3<Esc>", "uu")
val before = """
A Discovery
${c}I found it in a legendary land
all rocks and lavender and tufted grass,
where it was settled on some sodden sand
hard by the torrent of a mountain pass.
""".trimIndent()
val after = """
A Discovery
I1 found$c it in a legendary land
all rocks and lavender and tufted grass,
where it was settled on some sodden sand
hard by the torrent of a mountain pass.
""".trimIndent()
doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
assertFalse(hasSelection())
}
private fun hasSelection(): Boolean { private fun hasSelection(): Boolean {
val editor = myFixture.editor val editor = myFixture.editor
return editor.caretModel.primaryCaret.hasSelection() return editor.caretModel.primaryCaret.hasSelection()

View File

@ -17,6 +17,7 @@
*/ */
package com.maddyhome.idea.vim.action.change.change package com.maddyhome.idea.vim.action.change.change
import com.maddyhome.idea.vim.action.copy.YankMotionAction
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
@ -34,6 +35,19 @@ class ChangeMotionAction : ChangeEditorActionHandler.ForEachCaret(), DuplicableO
override val duplicateWith: Char = 'c' override val duplicateWith: Char = 'c'
private var isMultiCaret = false
override fun baseExecute(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments
): Boolean {
isMultiCaret = YankMotionAction().yankIfMultiCaret(cmd, editor, context, operatorArguments)
return super.baseExecute(editor, caret, context, cmd, operatorArguments)
}
override fun execute( override fun execute(
editor: VimEditor, editor: VimEditor,
caret: VimCaret, caret: VimCaret,
@ -46,7 +60,8 @@ class ChangeMotionAction : ChangeEditorActionHandler.ForEachCaret(), DuplicableO
caret, caret,
context, context,
argument, argument,
operatorArguments operatorArguments,
noYank = isMultiCaret
) )
} }
} }

View File

@ -17,6 +17,7 @@
*/ */
package com.maddyhome.idea.vim.action.change.change package com.maddyhome.idea.vim.action.change.change
import com.maddyhome.idea.vim.action.copy.YankVisualAction
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
@ -37,6 +38,18 @@ class ChangeVisualAction : VisualOperatorActionHandler.ForEachCaret() {
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_MULTIKEY_UNDO, CommandFlags.FLAG_EXIT_VISUAL) override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_MULTIKEY_UNDO, CommandFlags.FLAG_EXIT_VISUAL)
private var isMultiCaret = false
override fun beforeExecution(
editor: VimEditor,
context: ExecutionContext,
cmd: Command,
caretsAndSelections: Map<VimCaret, VimSelection>
): Boolean {
isMultiCaret = YankVisualAction().yankIfMultiCaret(editor, caretsAndSelections)
return super.beforeExecution(editor, context, cmd, caretsAndSelections)
}
override fun executeAction( override fun executeAction(
editor: VimEditor, editor: VimEditor,
caret: VimCaret, caret: VimCaret,
@ -50,7 +63,8 @@ class ChangeVisualAction : VisualOperatorActionHandler.ForEachCaret() {
caret, caret,
range.toVimTextRange(false), range.toVimTextRange(false),
range.type, range.type,
context context,
noYank = isMultiCaret
) )
} }
} }

View File

@ -17,6 +17,7 @@
*/ */
package com.maddyhome.idea.vim.action.change.delete package com.maddyhome.idea.vim.action.change.delete
import com.maddyhome.idea.vim.action.copy.YankMotionAction
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
@ -36,6 +37,19 @@ class DeleteMotionAction : ChangeEditorActionHandler.ForEachCaret(), DuplicableO
override val duplicateWith: Char = 'd' override val duplicateWith: Char = 'd'
private var isMultiCaret = false
override fun baseExecute(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments
): Boolean {
isMultiCaret = YankMotionAction().yankIfMultiCaret(cmd, editor, context, operatorArguments)
return super.baseExecute(editor, caret, context, cmd, operatorArguments)
}
override fun execute( override fun execute(
editor: VimEditor, editor: VimEditor,
caret: VimCaret, caret: VimCaret,
@ -53,7 +67,7 @@ class DeleteMotionAction : ChangeEditorActionHandler.ForEachCaret(), DuplicableO
val (first, second) = injector.changeGroup val (first, second) = injector.changeGroup
.getDeleteRangeAndType(editor, caret, context, argument, false, operatorArguments) .getDeleteRangeAndType(editor, caret, context, argument, false, operatorArguments)
?: return false ?: return false
return injector.changeGroup.deleteRange(editor, caret, first, second, false) return injector.changeGroup.deleteRange(editor, caret, first, second, false, noYank = isMultiCaret)
} }
} }
} }

View File

@ -17,6 +17,7 @@
*/ */
package com.maddyhome.idea.vim.action.change.delete package com.maddyhome.idea.vim.action.change.delete
import com.maddyhome.idea.vim.action.copy.YankVisualAction
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
@ -37,6 +38,18 @@ class DeleteVisualAction : VisualOperatorActionHandler.ForEachCaret() {
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXIT_VISUAL) override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXIT_VISUAL)
private var isMultiCaret = false
override fun beforeExecution(
editor: VimEditor,
context: ExecutionContext,
cmd: Command,
caretsAndSelections: Map<VimCaret, VimSelection>,
): Boolean {
isMultiCaret = YankVisualAction().yankIfMultiCaret(editor, caretsAndSelections)
return super.beforeExecution(editor, context, cmd, caretsAndSelections)
}
override fun executeAction( override fun executeAction(
editor: VimEditor, editor: VimEditor,
caret: VimCaret, caret: VimCaret,
@ -45,7 +58,13 @@ class DeleteVisualAction : VisualOperatorActionHandler.ForEachCaret() {
range: VimSelection, range: VimSelection,
operatorArguments: OperatorArguments, operatorArguments: OperatorArguments,
): Boolean { ): Boolean {
val selectionType = range.type return injector.changeGroup.deleteRange(
return injector.changeGroup.deleteRange(editor, caret, range.toVimTextRange(false), selectionType, false) editor,
caret,
range.toVimTextRange(false),
range.type,
false,
noYank = isMultiCaret
)
} }
} }

View File

@ -38,8 +38,27 @@ class YankMotionAction : VimActionHandler.SingleExecution(), DuplicableOperatorA
context: ExecutionContext, context: ExecutionContext,
cmd: Command, cmd: Command,
operatorArguments: OperatorArguments, operatorArguments: OperatorArguments,
): Boolean {
return executeYank(cmd, editor, context, operatorArguments)
}
private fun executeYank(
cmd: Command,
editor: VimEditor,
context: ExecutionContext,
operatorArguments: OperatorArguments,
): Boolean { ): Boolean {
val argument = cmd.argument ?: return false val argument = cmd.argument ?: return false
return injector.yank.yankMotion(editor, context, argument, operatorArguments) return injector.yank.yankMotion(editor, context, argument, operatorArguments)
} }
fun yankIfMultiCaret(cmd: Command, editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments): Boolean {
if (editor.nativeCarets().size > 1) {
executeYank(cmd, editor, context, operatorArguments)
return true
}
else {
return false
}
}
} }

View File

@ -44,6 +44,13 @@ class YankVisualAction : VisualOperatorActionHandler.SingleExecution() {
cmd: Command, cmd: Command,
caretsAndSelections: Map<VimCaret, VimSelection>, caretsAndSelections: Map<VimCaret, VimSelection>,
operatorArguments: OperatorArguments, operatorArguments: OperatorArguments,
): Boolean {
return executeYank(editor, caretsAndSelections)
}
private fun executeYank(
editor: VimEditor,
caretsAndSelections: Map<VimCaret, VimSelection>,
): Boolean { ): Boolean {
val selections = caretsAndSelections.values val selections = caretsAndSelections.values
val starts: MutableList<Int> = ArrayList() val starts: MutableList<Int> = ArrayList()
@ -58,4 +65,14 @@ class YankVisualAction : VisualOperatorActionHandler.SingleExecution() {
val endsArray = ends.toIntArray() val endsArray = ends.toIntArray()
return injector.yank.yankRange(editor, TextRange(startsArray, endsArray), vimSelection.type, true) return injector.yank.yankRange(editor, TextRange(startsArray, endsArray), vimSelection.type, true)
} }
fun yankIfMultiCaret(editor: VimEditor, caretsAndSelections: Map<VimCaret, VimSelection>): Boolean {
if (caretsAndSelections.size > 1) {
executeYank(editor, caretsAndSelections)
return true
}
else {
return false
}
}
} }

View File

@ -83,14 +83,14 @@ interface VimChangeGroup {
fun getDeleteRangeAndType2(editor: VimEditor, caret: VimCaret, context: ExecutionContext, argument: Argument, isChange: Boolean, operatorArguments: OperatorArguments): Pair<TextRange, SelectionType>? fun getDeleteRangeAndType2(editor: VimEditor, caret: VimCaret, context: ExecutionContext, argument: Argument, isChange: Boolean, operatorArguments: OperatorArguments): Pair<TextRange, SelectionType>?
fun deleteRange(editor: VimEditor, caret: VimCaret, range: TextRange, type: SelectionType?, isChange: Boolean): Boolean fun deleteRange(editor: VimEditor, caret: VimCaret, range: TextRange, type: SelectionType?, isChange: Boolean, noYank: Boolean = false): Boolean
fun deleteRange2(editor: VimEditor, caret: VimCaret, range: TextRange, type: SelectionType): Boolean fun deleteRange2(editor: VimEditor, caret: VimCaret, range: TextRange, type: SelectionType): Boolean
fun changeCharacters(editor: VimEditor, caret: VimCaret, count: Int): Boolean fun changeCharacters(editor: VimEditor, caret: VimCaret, count: Int): Boolean
fun changeEndOfLine(editor: VimEditor, caret: VimCaret, count: Int): Boolean fun changeEndOfLine(editor: VimEditor, caret: VimCaret, count: Int): Boolean
fun changeMotion(editor: VimEditor, caret: VimCaret, context: ExecutionContext, argument: Argument, operatorArguments: OperatorArguments): Boolean fun changeMotion(editor: VimEditor, caret: VimCaret, context: ExecutionContext, argument: Argument, operatorArguments: OperatorArguments, noYank: Boolean = false): Boolean
fun changeCaseToggleCharacter(editor: VimEditor, caret: VimCaret, count: Int): Boolean fun changeCaseToggleCharacter(editor: VimEditor, caret: VimCaret, count: Int): Boolean
@ -98,7 +98,7 @@ interface VimChangeGroup {
fun changeCaseRange(editor: VimEditor, caret: VimCaret, range: TextRange, type: Char): Boolean fun changeCaseRange(editor: VimEditor, caret: VimCaret, range: TextRange, type: Char): Boolean
fun changeRange(editor: VimEditor, caret: VimCaret, range: TextRange, type: SelectionType, context: ExecutionContext?): Boolean fun changeRange(editor: VimEditor, caret: VimCaret, range: TextRange, type: SelectionType, context: ExecutionContext?, noYank: Boolean = false): Boolean
fun changeCaseMotion(editor: VimEditor, caret: VimCaret, context: ExecutionContext?, type: Char, argument: Argument, operatorArguments: OperatorArguments): Boolean fun changeCaseMotion(editor: VimEditor, caret: VimCaret, context: ExecutionContext?, type: Char, argument: Argument, operatorArguments: OperatorArguments): Boolean

View File

@ -4,10 +4,10 @@ import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.command.Argument import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandFlags import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.VimStateMachine
import com.maddyhome.idea.vim.command.VimStateMachine.Companion.getInstance
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.command.SelectionType import com.maddyhome.idea.vim.command.SelectionType
import com.maddyhome.idea.vim.command.VimStateMachine
import com.maddyhome.idea.vim.command.VimStateMachine.Companion.getInstance
import com.maddyhome.idea.vim.common.ChangesListener import com.maddyhome.idea.vim.common.ChangesListener
import com.maddyhome.idea.vim.common.Offset import com.maddyhome.idea.vim.common.Offset
import com.maddyhome.idea.vim.common.OperatedRange import com.maddyhome.idea.vim.common.OperatedRange
@ -17,10 +17,10 @@ import com.maddyhome.idea.vim.diagnostic.debug
import com.maddyhome.idea.vim.diagnostic.vimLogger import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.group.visual.VimSelection import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.helper.inInsertMode import com.maddyhome.idea.vim.helper.inInsertMode
import com.maddyhome.idea.vim.helper.inSingleCommandMode import com.maddyhome.idea.vim.helper.inSingleCommandMode
import com.maddyhome.idea.vim.helper.usesVirtualSpace 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.listener.SelectionVimListenerSuppressor
import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_END 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_POS
@ -132,6 +132,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
editor: VimEditor, editor: VimEditor,
range: TextRange, range: TextRange,
type: SelectionType?, type: SelectionType?,
noYank: Boolean = false
): Boolean { ): Boolean {
var updatedRange = range var updatedRange = range
// Fix for https://youtrack.jetbrains.net/issue/VIM-35 // Fix for https://youtrack.jetbrains.net/issue/VIM-35
@ -145,6 +146,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
} }
} }
if (type == null || if (type == null ||
noYank ||
editor.inInsertMode || injector.registerGroup.storeText(editor, updatedRange, type, true) editor.inInsertMode || injector.registerGroup.storeText(editor, updatedRange, type, true)
) { ) {
val startOffsets = updatedRange.startOffsets val startOffsets = updatedRange.startOffsets
@ -851,6 +853,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
range: TextRange, range: TextRange,
type: SelectionType?, type: SelectionType?,
isChange: Boolean, isChange: Boolean,
noYank: Boolean
): Boolean { ): Boolean {
// Update the last column before we delete, or we might be retrieving the data for a line that no longer exists // Update the last column before we delete, or we might be retrieving the data for a line that no longer exists

View File

@ -66,7 +66,7 @@ sealed class ChangeEditorActionHandler : EditorActionHandlerBase(false) {
): Boolean ): Boolean
} }
final override fun baseExecute( override fun baseExecute(
editor: VimEditor, editor: VimEditor,
caret: VimCaret, caret: VimCaret,
context: ExecutionContext, context: ExecutionContext,