mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2024-11-25 16:42:55 +01:00
Compare commits
10 Commits
9f469d0eb2
...
edb9b194bb
Author | SHA1 | Date | |
---|---|---|---|
edb9b194bb | |||
|
eae7ed95e2 | ||
a1e2ae0eb9 | |||
eae2e3b6b8 | |||
c2d997a520 | |||
e2a8a3c21a | |||
9b7fee6163 | |||
d0f9d3dc70 | |||
8d3a69b338 | |||
3c530474a1 |
@ -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
|
||||||
|
@ -148,6 +148,10 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
|
|||||||
if (keyCode == KeyEvent.VK_TAB && editor.isTemplateActive()) return false
|
if (keyCode == KeyEvent.VK_TAB && editor.isTemplateActive()) return false
|
||||||
|
|
||||||
if ((keyCode == KeyEvent.VK_TAB || keyCode == KeyEvent.VK_ENTER) && editor.appCodeTemplateCaptured()) return false
|
if ((keyCode == KeyEvent.VK_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) {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,25 +188,43 @@ 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)
|
}
|
||||||
// Jump back to start
|
}
|
||||||
executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor)
|
else {
|
||||||
|
applyOnce(editor, change, pair)
|
||||||
|
// Jump back to start
|
||||||
|
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)
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,7 +134,15 @@ public class ProcessGroup extends VimProcessGroupBase {
|
|||||||
|
|
||||||
if (logger.isDebugEnabled()) logger.debug("swing=" + SwingUtilities.isEventDispatchThread());
|
if (logger.isDebugEnabled()) logger.debug("swing=" + SwingUtilities.isEventDispatchThread());
|
||||||
|
|
||||||
VimInjectorKt.getInjector().getVimscriptExecutor().execute(text, editor, context, skipHistory(editor), true, CommandLineVimLContext.INSTANCE);
|
int repeat = 1;
|
||||||
|
if (text.contains("raction ")) {
|
||||||
|
text = text.replace("raction ", "action ");
|
||||||
|
repeat = panel.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < repeat; i++) {
|
||||||
|
VimInjectorKt.getInjector().getVimscriptExecutor().execute(text, editor, context, skipHistory(editor), true, CommandLineVimLContext.INSTANCE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (ExException e) {
|
catch (ExException e) {
|
||||||
VimPlugin.showMessage(e.getMessage());
|
VimPlugin.showMessage(e.getMessage());
|
||||||
|
@ -97,7 +97,22 @@ class PutGroup : VimPutBase() {
|
|||||||
EditorHelper.getOrderedCaretsList(editor.ij).map { IjVimCaret(it) }
|
EditorHelper.getOrderedCaretsList(editor.ij).map { IjVimCaret(it) }
|
||||||
}
|
}
|
||||||
injector.application.runWriteAction {
|
injector.application.runWriteAction {
|
||||||
myCarets.forEach { caret -> putForCaret(editor, caret, data, additionalData, context, text) }
|
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) }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
myCarets.asReversed().forEachIndexed { index, caret ->
|
||||||
|
val line = ProcessedTextData(lines[index], text.typeInRegister, text.transferableData)
|
||||||
|
putForCaret(editor, caret, data, additionalData, context, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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>
|
||||||
|
@ -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 -->
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user