mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-08-13 06:16:58 +02:00
VIM-25 Smart put operations (via IJ)
This commit is contained in:
doc
src/com/maddyhome/idea/vim
action
copy
common
ex
extension
surround
group
option
ui
test/org/jetbrains/plugins/ideavim
@@ -3,16 +3,18 @@ List of Supported Set Commands
|
||||
|
||||
The following `:set` commands can appear in `~/.ideavimrc` or set manually in the command mode:
|
||||
|
||||
'clipboard' 'cb' clipboard options
|
||||
'digraph' 'dg' enable the entering of digraphs in Insert mode
|
||||
'gdefault' 'gd' the ":substitute" flag 'g' is default on
|
||||
'history' 'hi' number of command-lines that are remembered
|
||||
'hlsearch' 'hls' highlight matches with last search pattern
|
||||
'ignorecase' 'ic' ignore case in search patterns
|
||||
'iskeyword' 'isk' defines keywords for commands like 'w', '*', etc.
|
||||
'incsearch' 'is' show where search pattern typed so far matches
|
||||
'clipboard' 'cb' clipboard options
|
||||
'digraph' 'dg' enable the entering of digraphs in Insert mode
|
||||
'gdefault' 'gd' the ":substitute" flag 'g' is default on
|
||||
'history' 'hi' number of command-lines that are remembered
|
||||
'hlsearch' 'hls' highlight matches with last search pattern
|
||||
`ideaput` `ideaput` boolean (default on) - IdeaVim ONLY [To Be Released]
|
||||
enable native idea paste action for put operations
|
||||
'ignorecase' 'ic' ignore case in search patterns
|
||||
'iskeyword' 'isk' defines keywords for commands like 'w', '*', etc.
|
||||
'incsearch' 'is' show where search pattern typed so far matches
|
||||
|
||||
`keymodel` `km` String (default "continueselect,stopselect") [To Be Released]
|
||||
`keymodel` `km` String (default "continueselect,stopselect") [To Be Released]
|
||||
|
||||
List of comma separated words, which enable special things that keys
|
||||
can do. These values can be used:
|
||||
|
@@ -41,7 +41,7 @@ public class PutTextAfterCursorAction extends EditorAction {
|
||||
final Register lastRegister = VimPlugin.getRegister().getLastRegister();
|
||||
|
||||
final PutData.TextData textData =
|
||||
lastRegister != null ? new PutData.TextData(lastRegister.getText(), lastRegister.getType()) : null;
|
||||
lastRegister != null ? new PutData.TextData(lastRegister.getText(), lastRegister.getType(), lastRegister.getTransferableData()) : null;
|
||||
final PutData putData = new PutData(textData, null, count, false, true, false, -1);
|
||||
return VimPlugin.getPut().putText(editor, context, putData);
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ public class PutTextAfterCursorActionMoveCursor extends EditorAction {
|
||||
final Register lastRegister = VimPlugin.getRegister().getLastRegister();
|
||||
|
||||
final PutData.TextData textData =
|
||||
lastRegister != null ? new PutData.TextData(lastRegister.getText(), lastRegister.getType()) : null;
|
||||
lastRegister != null ? new PutData.TextData(lastRegister.getText(), lastRegister.getType(), lastRegister.getTransferableData()) : null;
|
||||
final PutData putData = new PutData(textData, null, count, false, true, true, -1);
|
||||
return VimPlugin.getPut().putText(editor, context, putData);
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ public class PutTextAfterCursorNoIndentAction extends EditorAction {
|
||||
final Register lastRegister = VimPlugin.getRegister().getLastRegister();
|
||||
|
||||
final PutData.TextData textData =
|
||||
lastRegister != null ? new PutData.TextData(lastRegister.getText(), lastRegister.getType()) : null;
|
||||
lastRegister != null ? new PutData.TextData(lastRegister.getText(), lastRegister.getType(), lastRegister.getTransferableData()) : null;
|
||||
final PutData putData = new PutData(textData, null, count, false, false, false, -1);
|
||||
return VimPlugin.getPut().putText(editor, context, putData);
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ public class PutTextBeforeCursorAction extends EditorAction {
|
||||
final Register lastRegister = VimPlugin.getRegister().getLastRegister();
|
||||
|
||||
final PutData.TextData textData =
|
||||
lastRegister != null ? new PutData.TextData(lastRegister.getText(), lastRegister.getType()) : null;
|
||||
lastRegister != null ? new PutData.TextData(lastRegister.getText(), lastRegister.getType(), lastRegister.getTransferableData()) : null;
|
||||
final PutData putData = new PutData(textData, null, count, true, true, false, -1);
|
||||
return VimPlugin.getPut().putText(editor, context, putData);
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ public class PutTextBeforeCursorActionMoveCursor extends EditorAction {
|
||||
final Register lastRegister = VimPlugin.getRegister().getLastRegister();
|
||||
|
||||
final PutData.TextData textData =
|
||||
lastRegister != null ? new PutData.TextData(lastRegister.getText(), lastRegister.getType()) : null;
|
||||
lastRegister != null ? new PutData.TextData(lastRegister.getText(), lastRegister.getType(), lastRegister.getTransferableData()) : null;
|
||||
final PutData putData = new PutData(textData, null, count, true, true, true, -1);
|
||||
return VimPlugin.getPut().putText(editor, context, putData);
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ public class PutTextBeforeCursorNoIndentAction extends EditorAction {
|
||||
final Register lastRegister = VimPlugin.getRegister().getLastRegister();
|
||||
|
||||
final PutData.TextData textData =
|
||||
lastRegister != null ? new PutData.TextData(lastRegister.getText(), lastRegister.getType()) : null;
|
||||
lastRegister != null ? new PutData.TextData(lastRegister.getText(), lastRegister.getType(), lastRegister.getTransferableData()) : null;
|
||||
final PutData putData = new PutData(textData, null, count, true, false, false, -1);
|
||||
return VimPlugin.getPut().putText(editor, context, putData);
|
||||
}
|
||||
|
@@ -39,7 +39,7 @@ private object PutVisualTextActionHandler : VisualOperatorActionHandler.SingleEx
|
||||
cmd: Command,
|
||||
caretsAndSelections: Map<Caret, VimSelection>): Boolean {
|
||||
if (caretsAndSelections.isEmpty()) return false
|
||||
val textData = VimPlugin.getRegister().lastRegister?.let { PutData.TextData(it.text, it.type) }
|
||||
val textData = VimPlugin.getRegister().lastRegister?.let { PutData.TextData(it.text, it.type, it.transferableData) }
|
||||
VimPlugin.getRegister().resetRegister()
|
||||
|
||||
val insertTextBeforeCaret = cmd.keys[0].keyChar == 'P'
|
||||
|
@@ -35,7 +35,7 @@ import javax.swing.KeyStroke
|
||||
private object PutVisualTextMoveCursorActionHandler : VisualOperatorActionHandler.SingleExecution() {
|
||||
override fun executeForAllCarets(editor: Editor, context: DataContext, cmd: Command, caretsAndSelections: Map<Caret, VimSelection>): Boolean {
|
||||
if (caretsAndSelections.isEmpty()) return false
|
||||
val textData = VimPlugin.getRegister().lastRegister?.let { PutData.TextData(it.text, it.type) }
|
||||
val textData = VimPlugin.getRegister().lastRegister?.let { PutData.TextData(it.text, it.type, it.transferableData) }
|
||||
VimPlugin.getRegister().resetRegister()
|
||||
|
||||
val insertTextBeforeCaret = cmd.keys[1].keyChar == 'P'
|
||||
|
@@ -35,7 +35,7 @@ import javax.swing.KeyStroke
|
||||
private object PutVisualTextNoIndentActionHandler : VisualOperatorActionHandler.SingleExecution() {
|
||||
override fun executeForAllCarets(editor: Editor, context: DataContext, cmd: Command, caretsAndSelections: Map<Caret, VimSelection>): Boolean {
|
||||
if (caretsAndSelections.isEmpty()) return false
|
||||
val textData = VimPlugin.getRegister().lastRegister?.let { PutData.TextData(it.text, it.type) }
|
||||
val textData = VimPlugin.getRegister().lastRegister?.let { PutData.TextData(it.text, it.type, it.transferableData) }
|
||||
VimPlugin.getRegister().resetRegister()
|
||||
|
||||
val insertBeforeCaret = cmd.keys[1].keyChar == 'P'
|
||||
|
@@ -18,6 +18,7 @@
|
||||
|
||||
package com.maddyhome.idea.vim.common;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferableData;
|
||||
import com.maddyhome.idea.vim.command.SelectionType;
|
||||
import com.maddyhome.idea.vim.helper.StringHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -25,6 +26,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
@@ -35,12 +37,7 @@ public class Register {
|
||||
private char name;
|
||||
@NotNull private final SelectionType type;
|
||||
@NotNull private final List<KeyStroke> keys;
|
||||
|
||||
public Register(char name, @NotNull SelectionType type, @NotNull String text) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.keys = StringHelper.stringToKeys(text);
|
||||
}
|
||||
@NotNull private List<? extends TextBlockTransferableData> transferableData = new ArrayList<>();
|
||||
|
||||
public Register(char name, @NotNull SelectionType type, @NotNull List<KeyStroke> keys) {
|
||||
this.name = name;
|
||||
@@ -48,6 +45,13 @@ public class Register {
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
public Register(char name, @NotNull SelectionType type, @NotNull String text, @NotNull List<? extends TextBlockTransferableData> transferableData) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.keys = StringHelper.stringToKeys(text);
|
||||
this.transferableData = transferableData;
|
||||
}
|
||||
|
||||
public void rename(char name) {
|
||||
this.name = name;
|
||||
}
|
||||
@@ -59,6 +63,11 @@ public class Register {
|
||||
return name;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<? extends TextBlockTransferableData> getTransferableData() {
|
||||
return transferableData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the register type.
|
||||
*/
|
||||
@@ -94,19 +103,18 @@ public class Register {
|
||||
/**
|
||||
* Append the supplied text to any existing text.
|
||||
*/
|
||||
public void addText(@NotNull String text) {
|
||||
public void addTextAndResetTransferableData(@NotNull String text) {
|
||||
addKeys(StringHelper.stringToKeys(text));
|
||||
transferableData.clear();
|
||||
}
|
||||
|
||||
public void addKeys(@NotNull List<KeyStroke> keys) {
|
||||
this.keys.addAll(keys);
|
||||
}
|
||||
|
||||
public static class KeySorter<V> implements Comparator<V> {
|
||||
public int compare(V o1, V o2) {
|
||||
Register a = (Register)o1;
|
||||
Register b = (Register)o2;
|
||||
return Character.compare(a.name, b.name);
|
||||
public static class KeySorter implements Comparator<Register> {
|
||||
public int compare(@NotNull Register o1, @NotNull Register o2) {
|
||||
return Character.compare(o1.name, o2.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -41,7 +41,8 @@ class CopyTextHandler : CommandHandler(
|
||||
val arg = CommandParser.getInstance().parse(cmd.argument)
|
||||
val line = arg.ranges.getFirstLine(editor, caret, context)
|
||||
|
||||
val textData = PutData.TextData(text, SelectionType.LINE_WISE)
|
||||
val transferableData = VimPlugin.getRegister().getTransferableData(editor, range, text)
|
||||
val textData = PutData.TextData(text, SelectionType.LINE_WISE, transferableData)
|
||||
val putData = PutData(textData, null, 1, insertTextBeforeCaret = false, _indent = true, caretAfterInsertedText = false, putToLine = line)
|
||||
VimPlugin.getPut().putTextForCaret(editor, caret, context, putData)
|
||||
}
|
||||
|
@@ -68,7 +68,7 @@ class MoveTextHandler : CommandHandler(
|
||||
val caret = carets[i]
|
||||
val text = texts[i]
|
||||
|
||||
val textData = PutData.TextData(text, SelectionType.LINE_WISE)
|
||||
val textData = PutData.TextData(text, SelectionType.LINE_WISE, emptyList())
|
||||
val putData = PutData(textData, null, 1, insertTextBeforeCaret = false, _indent = true, caretAfterInsertedText = false, putToLine = line)
|
||||
VimPlugin.getPut().putTextForCaret(editor, caret, context, putData)
|
||||
}
|
||||
|
@@ -46,7 +46,7 @@ class PutLinesHandler : CommandHandler(
|
||||
}
|
||||
|
||||
val line = if (cmd.ranges.size() == 0) -1 else cmd.getLine(editor, context)
|
||||
val textData = registerGroup.lastRegister?.let { PutData.TextData(it.text, SelectionType.LINE_WISE) }
|
||||
val textData = registerGroup.lastRegister?.let { PutData.TextData(it.text, SelectionType.LINE_WISE, it.transferableData) }
|
||||
val putData = PutData(textData, null, 1, false, false, false, line)
|
||||
return VimPlugin.getPut().putText(editor, context, putData)
|
||||
}
|
||||
|
@@ -35,6 +35,8 @@ import com.maddyhome.idea.vim.extension.VimNonDisposableExtension;
|
||||
import com.maddyhome.idea.vim.group.ChangeGroup;
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper;
|
||||
import com.maddyhome.idea.vim.key.OperatorFunction;
|
||||
import com.maddyhome.idea.vim.option.Options;
|
||||
import com.maddyhome.idea.vim.option.ToggleOption;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -46,14 +48,7 @@ import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormal;
|
||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister;
|
||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.inputKeyStroke;
|
||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.inputString;
|
||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
|
||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping;
|
||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction;
|
||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister;
|
||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.*;
|
||||
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
|
||||
|
||||
/**
|
||||
@@ -226,7 +221,11 @@ public class VimSurroundExtension extends VimNonDisposableExtension {
|
||||
}
|
||||
|
||||
private static void perform(@NotNull String sequence, @NotNull Editor editor) {
|
||||
final ToggleOption ideaput = (ToggleOption)Options.getInstance().getOption(Options.IDEAPUT);
|
||||
final boolean origValue = ideaput.getValue();
|
||||
ideaput.reset();
|
||||
executeNormal(parseKeys("\"" + REGISTER + sequence), editor);
|
||||
if (origValue) ideaput.set();
|
||||
}
|
||||
|
||||
private static void pasteSurround(@NotNull List<KeyStroke> innerValue, @NotNull Editor editor) {
|
||||
|
@@ -19,9 +19,19 @@
|
||||
package com.maddyhome.idea.vim.group;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.intellij.codeInsight.editorActions.CopyPastePostProcessor;
|
||||
import com.intellij.codeInsight.editorActions.CopyPastePreProcessor;
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferable;
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferableData;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.CaretStateTransferableData;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.IndexNotReadyException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.action.motion.mark.MotionGotoFileMarkAction;
|
||||
import com.maddyhome.idea.vim.action.motion.search.SearchAgainNextAction;
|
||||
@@ -44,6 +54,7 @@ import com.maddyhome.idea.vim.helper.StringHelper;
|
||||
import com.maddyhome.idea.vim.option.ListOption;
|
||||
import com.maddyhome.idea.vim.option.Options;
|
||||
import com.maddyhome.idea.vim.ui.ClipboardHandler;
|
||||
import kotlin.Pair;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -51,8 +62,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
import javax.swing.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -163,32 +174,35 @@ public class RegisterGroup {
|
||||
}
|
||||
|
||||
// If this is an uppercase register, we need to append the text to the corresponding lowercase register
|
||||
final List<TextBlockTransferableData> transferableData = start != -1 ? getTransferableData(editor, range, text) : new ArrayList<>();
|
||||
final String processedText = start != -1 ? preprocessText(editor, range, text, transferableData) : text;
|
||||
if (Character.isUpperCase(register)) {
|
||||
char lreg = Character.toLowerCase(register);
|
||||
Register r = registers.get(lreg);
|
||||
// Append the text if the lowercase register existed
|
||||
if (r != null) {
|
||||
r.addText(text);
|
||||
r.addTextAndResetTransferableData(processedText);
|
||||
}
|
||||
// Set the text if the lowercase register didn't exist yet
|
||||
else {
|
||||
registers.put(lreg, new Register(lreg, type, text));
|
||||
if (logger.isDebugEnabled()) logger.debug("register '" + register + "' contains: \"" + text + "\"");
|
||||
registers.put(lreg, new Register(lreg, type, processedText, new ArrayList<>(transferableData)));
|
||||
if (logger.isDebugEnabled()) logger.debug("register '" + register + "' contains: \"" + processedText + "\"");
|
||||
}
|
||||
}
|
||||
else if (CLIPBOARD_REGISTERS.contains(register)) {
|
||||
ClipboardHandler.setClipboardText(text);
|
||||
}
|
||||
// Put the text in the specified register
|
||||
else {
|
||||
registers.put(register, new Register(register, type, text));
|
||||
if (logger.isDebugEnabled()) logger.debug("register '" + register + "' contains: \"" + text + "\"");
|
||||
registers.put(register, new Register(register, type, processedText, new ArrayList<>(transferableData)));
|
||||
if (logger.isDebugEnabled()) logger.debug("register '" + register + "' contains: \"" + processedText + "\"");
|
||||
}
|
||||
|
||||
if (CLIPBOARD_REGISTERS.contains(register)) {
|
||||
ClipboardHandler.setClipboardText(processedText, new ArrayList<>(transferableData), text);
|
||||
}
|
||||
|
||||
// Also add it to the default register if the default wasn't specified
|
||||
if (register != defaultRegister && ".:/".indexOf(register) == -1) {
|
||||
registers.put(defaultRegister, new Register(defaultRegister, type, text));
|
||||
if (logger.isDebugEnabled()) logger.debug("register '" + register + "' contains: \"" + text + "\"");
|
||||
registers.put(defaultRegister, new Register(defaultRegister, type, processedText, new ArrayList<>(transferableData)));
|
||||
if (logger.isDebugEnabled()) logger.debug("register '" + register + "' contains: \"" + processedText + "\"");
|
||||
}
|
||||
|
||||
if (isDelete) {
|
||||
@@ -205,18 +219,18 @@ public class RegisterGroup {
|
||||
registers.put((char)(d + 1), t);
|
||||
}
|
||||
}
|
||||
registers.put('1', new Register('1', type, text));
|
||||
registers.put('1', new Register('1', type, processedText, new ArrayList<>(transferableData)));
|
||||
}
|
||||
|
||||
// Deletes smaller than one line and without specified register go the the "-" register
|
||||
if (smallInlineDeletion && register == defaultRegister) {
|
||||
registers.put('-', new Register('-', type, text));
|
||||
registers.put('-', new Register('-', type, processedText, new ArrayList<>(transferableData)));
|
||||
}
|
||||
}
|
||||
// Yanks also go to register 0 if the default register was used
|
||||
else if (register == defaultRegister) {
|
||||
registers.put('0', new Register('0', type, text));
|
||||
if (logger.isDebugEnabled()) logger.debug("register '" + '0' + "' contains: \"" + text + "\"");
|
||||
registers.put('0', new Register('0', type, processedText, new ArrayList<>(transferableData)));
|
||||
if (logger.isDebugEnabled()) logger.debug("register '" + '0' + "' contains: \"" + processedText + "\"");
|
||||
}
|
||||
|
||||
if (start != -1) {
|
||||
@@ -226,6 +240,45 @@ public class RegisterGroup {
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<TextBlockTransferableData> getTransferableData(@NotNull Editor editor,
|
||||
@NotNull TextRange textRange,
|
||||
String text) {
|
||||
final List<TextBlockTransferableData> transferableDatas = new ArrayList<>();
|
||||
final Project project = editor.getProject();
|
||||
if (project == null) return new ArrayList<>();
|
||||
|
||||
final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
|
||||
DumbService.getInstance(project).withAlternativeResolveEnabled(() -> {
|
||||
for (CopyPastePostProcessor<? extends TextBlockTransferableData> processor : CopyPastePostProcessor.EP_NAME
|
||||
.getExtensionList()) {
|
||||
try {
|
||||
transferableDatas.addAll(processor.collectTransferableData(file, editor, textRange.getStartOffsets(), textRange.getEndOffsets()));
|
||||
}
|
||||
catch (IndexNotReadyException ignore) {
|
||||
}
|
||||
}
|
||||
});
|
||||
transferableDatas.add(new CaretStateTransferableData(new int[]{0}, new int[]{text.length()}));
|
||||
return transferableDatas;
|
||||
}
|
||||
|
||||
private String preprocessText(@NotNull Editor editor, @NotNull TextRange textRange, String text, List<TextBlockTransferableData> transferableDatas) {
|
||||
final Project project = editor.getProject();
|
||||
if (project == null) return text;
|
||||
|
||||
final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
|
||||
String rawText = TextBlockTransferable.convertLineSeparators(text, "\n", transferableDatas);
|
||||
String escapedText = null;
|
||||
for (CopyPastePreProcessor processor : CopyPastePreProcessor.EP_NAME.getExtensionList()) {
|
||||
escapedText = processor.preprocessOnCopy(file, textRange.getStartOffsets(), textRange.getEndOffsets(), rawText);
|
||||
if (escapedText != null) {
|
||||
return escapedText;
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private boolean isSmallDeletionSpecialCase(Editor editor) {
|
||||
Command currentCommand = CommandState.getInstance(editor).getCommand();
|
||||
if (currentCommand != null) {
|
||||
@@ -300,7 +353,7 @@ public class RegisterGroup {
|
||||
res.add(register);
|
||||
}
|
||||
}
|
||||
res.sort(new Register.KeySorter<>());
|
||||
res.sort(new Register.KeySorter());
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -390,7 +443,6 @@ public class RegisterGroup {
|
||||
logger.debug("readData");
|
||||
final Element registersElement = element.getChild("registers");
|
||||
if (registersElement != null) {
|
||||
//noinspection unchecked
|
||||
final List<Element> registerElements = registersElement.getChildren("register");
|
||||
for (Element registerElement : registerElements) {
|
||||
final char key = registerElement.getAttributeValue("name").charAt(0);
|
||||
@@ -401,7 +453,7 @@ public class RegisterGroup {
|
||||
if (textElement != null) {
|
||||
final String text = StringHelper.getSafeXmlText(textElement);
|
||||
if (text != null) {
|
||||
register = new Register(key, type, text);
|
||||
register = new Register(key, type, text, Collections.emptyList());
|
||||
}
|
||||
else {
|
||||
register = null;
|
||||
@@ -409,7 +461,6 @@ public class RegisterGroup {
|
||||
}
|
||||
else {
|
||||
final Element keysElement = registerElement.getChild("keys");
|
||||
//noinspection unchecked
|
||||
final List<Element> keyElements = keysElement.getChildren("key");
|
||||
final List<KeyStroke> strokes = new ArrayList<>();
|
||||
for (Element keyElement : keyElements) {
|
||||
@@ -430,13 +481,15 @@ public class RegisterGroup {
|
||||
|
||||
@Nullable
|
||||
private Register refreshClipboardRegister(char r) {
|
||||
final String text = ClipboardHandler.getClipboardText();
|
||||
final Pair<String, List<TextBlockTransferableData>> clipboardData = ClipboardHandler.getClipboardTextAndTransferableData();
|
||||
final Register currentRegister = registers.get(r);
|
||||
final String text = clipboardData.getFirst();
|
||||
final List<TextBlockTransferableData> transferableData = clipboardData.getSecond();
|
||||
if (text != null) {
|
||||
if (currentRegister != null && text.equals(currentRegister.getText())) {
|
||||
return currentRegister;
|
||||
}
|
||||
return new Register(r, guessSelectionType(text), text);
|
||||
return new Register(r, guessSelectionType(text), text, transferableData);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@@ -18,10 +18,15 @@
|
||||
|
||||
package com.maddyhome.idea.vim.group.copy
|
||||
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferable
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferableData
|
||||
import com.intellij.ide.CopyPasteManagerEx
|
||||
import com.intellij.ide.PasteProvider
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.LogicalPosition
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.*
|
||||
import com.intellij.openapi.ide.CopyPasteManager
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.CommandState
|
||||
@@ -32,6 +37,7 @@ import com.maddyhome.idea.vim.group.MotionGroup
|
||||
import com.maddyhome.idea.vim.group.visual.VimSelection
|
||||
import com.maddyhome.idea.vim.handler.CaretOrder.DECREASING_OFFSET
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.option.Options
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.min
|
||||
@@ -58,24 +64,30 @@ data class PutData(
|
||||
|
||||
data class TextData(
|
||||
val rawText: String?,
|
||||
val typeInRegister: SelectionType
|
||||
val typeInRegister: SelectionType,
|
||||
val transferableData: List<TextBlockTransferableData>
|
||||
)
|
||||
}
|
||||
|
||||
private data class ProcessedTextData(
|
||||
val text: String,
|
||||
val typeInRegister: SelectionType,
|
||||
val transferableData: List<TextBlockTransferableData>
|
||||
)
|
||||
|
||||
class PutGroup {
|
||||
fun putText(editor: Editor, context: DataContext, data: PutData): Boolean {
|
||||
val additionalData = collectPreModificationData(editor, data)
|
||||
deleteSelectedText(editor, data)
|
||||
val (text, typeInRegister) = getText(editor, data) ?: return false
|
||||
putTextAndSetCaretPosition(editor, context, text, typeInRegister, data, additionalData)
|
||||
val processedText = processText(editor, data) ?: return false
|
||||
putTextAndSetCaretPosition(editor, context, processedText, data, additionalData)
|
||||
return true
|
||||
}
|
||||
|
||||
fun putTextForCaret(editor: Editor, caret: Caret, context: DataContext, data: PutData): Boolean {
|
||||
val additionalData = collectPreModificationData(editor, data)
|
||||
val (text, typeInRegister) = getText(editor, data) ?: return false
|
||||
putForCaret(editor, caret, typeInRegister, data, additionalData, context, text)
|
||||
val processedText = processText(editor, data) ?: return false
|
||||
putForCaret(editor, caret, data, additionalData, context, processedText)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -104,7 +116,7 @@ class PutGroup {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getText(editor: Editor, data: PutData): Pair<String, SelectionType>? {
|
||||
private fun processText(editor: Editor, data: PutData): ProcessedTextData? {
|
||||
var text = data.textData?.rawText ?: run {
|
||||
if (data.visualSelection != null) {
|
||||
val offset = editor.caretModel.primaryCaret.offset
|
||||
@@ -118,28 +130,39 @@ class PutGroup {
|
||||
|
||||
if (data.textData.typeInRegister == SelectionType.LINE_WISE && text.isNotEmpty() && text.last() != '\n') text += '\n'
|
||||
|
||||
return text to data.textData.typeInRegister
|
||||
return ProcessedTextData(text, data.textData.typeInRegister, data.textData.transferableData)
|
||||
}
|
||||
|
||||
private fun putTextAndSetCaretPosition(editor: Editor, context: DataContext, text: String, typeInRegister: SelectionType, data: PutData, additionalData: Map<String, Any>) {
|
||||
private fun putTextAndSetCaretPosition(editor: Editor, context: DataContext, text: ProcessedTextData, data: PutData, additionalData: Map<String, Any>) {
|
||||
val subMode = data.visualSelection?.typeInEditor?.toSubMode() ?: CommandState.SubMode.NONE
|
||||
if (Options.getInstance().isSet(Options.IDEAPUT)) {
|
||||
val idePasteProvider = getProviderForPasteViaIde(context, text.typeInRegister, data)
|
||||
if (idePasteProvider != null) {
|
||||
logger.debug("Perform put via idea paste")
|
||||
putTextViaIde(idePasteProvider, editor, context, text, subMode, data, additionalData)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Perform put via plugin")
|
||||
val myCarets = if (data.visualSelection != null) {
|
||||
data.visualSelection.caretsAndSelections.keys.sortedByDescending { it.logicalPosition }
|
||||
} else {
|
||||
EditorHelper.getOrderedCaretsList(editor, DECREASING_OFFSET)
|
||||
}
|
||||
myCarets.forEach { caret -> putForCaret(editor, caret, typeInRegister, data, additionalData, context, text) }
|
||||
myCarets.forEach { caret -> putForCaret(editor, caret, data, additionalData, context, text) }
|
||||
}
|
||||
|
||||
private fun putForCaret(editor: Editor, caret: Caret, typeInRegister: SelectionType, data: PutData, additionalData: Map<String, Any>, context: DataContext, text: String) {
|
||||
private fun putForCaret(editor: Editor, caret: Caret, data: PutData, additionalData: Map<String, Any>, context: DataContext, text: ProcessedTextData) {
|
||||
if (data.visualSelection?.typeInEditor == SelectionType.LINE_WISE && editor.isOneLineMode) return
|
||||
val startOffsets = prepareDocumentAndGetStartOffsets(editor, caret, typeInRegister, data, additionalData)
|
||||
val startOffsets = prepareDocumentAndGetStartOffsets(editor, caret, text.typeInRegister, data, additionalData)
|
||||
|
||||
startOffsets.forEach { startOffset ->
|
||||
val subMode = data.visualSelection?.typeInEditor?.toSubMode() ?: CommandState.SubMode.NONE
|
||||
val endOffset = putTextInternal(editor, caret, context, text, typeInRegister, subMode,
|
||||
val endOffset = putTextInternal(editor, caret, context, text.text, text.typeInRegister, subMode,
|
||||
startOffset, data.count, data.indent, data.caretAfterInsertedText)
|
||||
VimPlugin.getMark().setChangeMarks(editor, TextRange(startOffset, endOffset))
|
||||
moveCaretToEndPosition(editor, caret, startOffset, endOffset, typeInRegister, subMode, data.caretAfterInsertedText)
|
||||
moveCaretToEndPosition(editor, caret, startOffset, endOffset, text.typeInRegister, subMode, data.caretAfterInsertedText)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +203,7 @@ class PutGroup {
|
||||
}
|
||||
|
||||
var startOffset: Int
|
||||
val line = if (data.putToLine < 0) caret.visualPosition.line else data.putToLine
|
||||
val line = if (data.putToLine < 0) caret.logicalPosition.line else data.putToLine
|
||||
when (typeInRegister) {
|
||||
SelectionType.LINE_WISE -> {
|
||||
startOffset = min(editor.document.textLength, VimPlugin.getMotion().moveCaretToLineEnd(editor, line, true) + 1)
|
||||
@@ -202,6 +225,52 @@ class PutGroup {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getProviderForPasteViaIde(context: DataContext, typeInRegister: SelectionType, data: PutData): PasteProvider? {
|
||||
if (data.visualSelection != null && data.visualSelection.typeInEditor == SelectionType.BLOCK_WISE) return null
|
||||
if ((typeInRegister == SelectionType.LINE_WISE || typeInRegister == SelectionType.CHARACTER_WISE) && data.count == 1) {
|
||||
val provider = PlatformDataKeys.PASTE_PROVIDER.getData(context)
|
||||
if (provider != null && provider.isPasteEnabled(context)) return provider
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun putTextViaIde(pasteProvider: PasteProvider, editor: Editor, context: DataContext, text: ProcessedTextData, subMode: CommandState.SubMode, data: PutData, additionalData: Map<String, Any>) {
|
||||
val carets: MutableMap<Caret, RangeMarker> = mutableMapOf()
|
||||
EditorHelper.getOrderedCaretsList(editor, DECREASING_OFFSET).forEach { caret ->
|
||||
val startOffset = prepareDocumentAndGetStartOffsets(editor, caret, text.typeInRegister, data, additionalData).first()
|
||||
val pointMarker = editor.document.createRangeMarker(startOffset, startOffset)
|
||||
caret.moveToOffset(startOffset)
|
||||
carets[caret] = pointMarker
|
||||
}
|
||||
|
||||
val origContent: TextBlockTransferable = setClipboardText(text.text, text.transferableData)
|
||||
try {
|
||||
pasteProvider.performPaste(context)
|
||||
} finally {
|
||||
(CopyPasteManager.getInstance() as? CopyPasteManagerEx)?.run { removeContent(origContent) }
|
||||
}
|
||||
|
||||
carets.forEach { (caret, point) ->
|
||||
val startOffset = point.startOffset
|
||||
point.dispose()
|
||||
if (!caret.isValid) return@forEach
|
||||
val endOffset = if (data.indent) doIndent(editor, caret, context, startOffset, startOffset + text.text.length) else startOffset + text.text.length
|
||||
VimPlugin.getMark().setChangeMarks(editor, TextRange(startOffset, endOffset))
|
||||
VimPlugin.getMark().setMark(editor, MarkGroup.MARK_CHANGE_POS, startOffset)
|
||||
moveCaretToEndPosition(editor, caret, startOffset, endOffset, text.typeInRegister, subMode, data.caretAfterInsertedText)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setClipboardText(text: String, transferableData: List<TextBlockTransferableData>): TextBlockTransferable {
|
||||
val mutableTransferableData = transferableData.toMutableList()
|
||||
val s = TextBlockTransferable.convertLineSeparators(text, "\n", transferableData)
|
||||
if (mutableTransferableData.none { it is CaretStateTransferableData }) {
|
||||
mutableTransferableData += CaretStateTransferableData(intArrayOf(0), intArrayOf(s.length))
|
||||
}
|
||||
val content = TextBlockTransferable(s, mutableTransferableData, RawText(text))
|
||||
CopyPasteManager.getInstance().setContents(content)
|
||||
return content
|
||||
}
|
||||
|
||||
private fun putTextInternal(editor: Editor, caret: Caret, context: DataContext,
|
||||
text: String, type: SelectionType, mode: CommandState.SubMode,
|
||||
@@ -368,4 +437,8 @@ class PutGroup {
|
||||
}
|
||||
return maxLen
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = Logger.getInstance(PutGroup::class.java.name)
|
||||
}
|
||||
}
|
@@ -43,6 +43,7 @@ public class Options {
|
||||
public static final String VIMINFO = "viminfo";
|
||||
public static final String SELECTMODE = "selectmode";
|
||||
public static final String KEYMODEL = "keymodel";
|
||||
public static final String IDEAPUT = "ideaput";
|
||||
public static final String LOOKUPACTIONS = "lookupactions";
|
||||
|
||||
/**
|
||||
@@ -477,6 +478,7 @@ public class Options {
|
||||
addOption(new KeywordOption("iskeyword", "isk", new String[]{"@", "48-57", "_"}));
|
||||
addOption(new BoundListOption(SELECTMODE, "slm", new String[]{"template"}, new String[]{"mouse", "key", "cmd", "template", "refactoring"}));
|
||||
addOption(new BoundListOption(KEYMODEL, "km", new String[]{"continueselect", "stopselect"}, new String[]{"startsel", "stopsel", "stopselect", "stopvisual", "continueselect", "continuevisual"}));
|
||||
addOption(new ToggleOption(IDEAPUT, IDEAPUT, true));
|
||||
addOption(new ListOption(LOOKUPACTIONS, LOOKUPACTIONS, new String[]{"VimLookupUp", "VimLookupDown"}, null));
|
||||
|
||||
registerExtensionOptions();
|
||||
|
@@ -18,11 +18,21 @@
|
||||
|
||||
package com.maddyhome.idea.vim.ui;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import com.intellij.codeInsight.editorActions.CopyPastePostProcessor;
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferable;
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferableData;
|
||||
import com.intellij.openapi.editor.RawText;
|
||||
import kotlin.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.*;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.datatransfer.UnsupportedFlavorException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is a utility class for working with the system clipboard
|
||||
@@ -33,29 +43,33 @@ public class ClipboardHandler {
|
||||
*
|
||||
* @return The clipboard string or null if data isn't plain text
|
||||
*/
|
||||
@Nullable
|
||||
public static String getClipboardText() {
|
||||
@NotNull
|
||||
public static Pair<String, List<TextBlockTransferableData>> getClipboardTextAndTransferableData() {
|
||||
String res = null;
|
||||
List<TextBlockTransferableData> transferableData = new ArrayList<>();
|
||||
try {
|
||||
Clipboard board = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
Transferable trans = board.getContents(null);
|
||||
Object data = trans.getTransferData(DataFlavor.stringFlavor);
|
||||
|
||||
if (data != null) {
|
||||
res = data.toString();
|
||||
}
|
||||
res = data.toString();
|
||||
transferableData = collectTransferableData(trans);
|
||||
}
|
||||
catch (HeadlessException e) {
|
||||
// ignore
|
||||
}
|
||||
catch (UnsupportedFlavorException e) {
|
||||
// ignore
|
||||
}
|
||||
catch (IOException e) {
|
||||
// ignore
|
||||
catch (HeadlessException | UnsupportedFlavorException | IOException ignored) {
|
||||
}
|
||||
|
||||
return res;
|
||||
return new Pair<>(res, transferableData);
|
||||
}
|
||||
|
||||
private static List<TextBlockTransferableData> collectTransferableData(Transferable transferable) {
|
||||
List<TextBlockTransferableData> allValues = new ArrayList<>();
|
||||
for (CopyPastePostProcessor<? extends TextBlockTransferableData> processor : CopyPastePostProcessor.EP_NAME.getExtensionList()) {
|
||||
List<? extends TextBlockTransferableData> data = processor.extractTransferableData(transferable);
|
||||
if (!data.isEmpty()) {
|
||||
allValues.addAll(data);
|
||||
}
|
||||
}
|
||||
return allValues;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,14 +77,14 @@ public class ClipboardHandler {
|
||||
*
|
||||
* @param text The text to add to the clipboard
|
||||
*/
|
||||
public static void setClipboardText(String text) {
|
||||
public static void setClipboardText(String text, List<TextBlockTransferableData> transferableData, String rawText) {
|
||||
try {
|
||||
final String s = TextBlockTransferable.convertLineSeparators(text, "\n", transferableData);
|
||||
TextBlockTransferable content = new TextBlockTransferable(s, transferableData, new RawText(rawText));
|
||||
Clipboard board = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
StringSelection data = new StringSelection(text);
|
||||
board.setContents(data, null);
|
||||
board.setContents(content, null);
|
||||
}
|
||||
catch (HeadlessException e) {
|
||||
// ignore
|
||||
catch (HeadlessException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1652,26 +1652,28 @@ public class MultipleCaretsTest extends VimTestCase {
|
||||
}
|
||||
|
||||
public void testPutTextBeforeCursorLinewiseOverlapRange() {
|
||||
// Non-ide insert will produce double "<caret>zxcvbn\n"
|
||||
testPutOverlapLine("q<caret>we<caret>rty\n" + "asdfgh\n" + "<caret>zxcvbn\n",
|
||||
"<caret>zxcvbn\n" + "<caret>zxcvbn\n" + "qwerty\n" + "asdfgh\n" + "<caret>zxcvbn\n" + "zxcvbn\n",
|
||||
"<caret>zxcvbn\n" + "qwerty\n" + "asdfgh\n" + "<caret>zxcvbn\n" + "zxcvbn\n",
|
||||
true);
|
||||
testPutOverlapLine("qwerty\n" + "a<caret>sd<caret>fgh\n" + "<caret>zxcvbn\n",
|
||||
"qwerty\n" + "<caret>zxcvbn\n" + "<caret>zxcvbn\n" + "asdfgh\n" + "<caret>zxcvbn\n" + "zxcvbn\n",
|
||||
"qwerty\n" + "<caret>zxcvbn\n" + "asdfgh\n" + "<caret>zxcvbn\n" + "zxcvbn\n",
|
||||
true);
|
||||
testPutOverlapLine("qwerty\n" + "asd<caret>fgh\n" + "<caret>zxcvb<caret>n\n",
|
||||
"qwerty\n" + "<caret>zxcvbn\n" + "asdfgh\n" + "<caret>zxcvbn\n" + "<caret>zxcvbn\n" + "zxcvbn\n",
|
||||
"qwerty\n" + "<caret>zxcvbn\n" + "asdfgh\n" + "<caret>zxcvbn\n" + "zxcvbn\n",
|
||||
true);
|
||||
}
|
||||
|
||||
public void testPutTextAfterCursorLinewiseOverlapRange() {
|
||||
// Non-ide insert will produce double "<caret>zxcvbn\n"
|
||||
testPutOverlapLine("q<caret>wert<caret>y\n" + "asdfgh\n" + "<caret>zxcvbn\n",
|
||||
"qwerty\n" + "<caret>zxcvbn\n" + "<caret>zxcvbn\n" + "asdfgh\n" + "zxcvbn\n" + "<caret>zxcvbn\n",
|
||||
"qwerty\n" + "<caret>zxcvbn\n" + "asdfgh\n" + "zxcvbn\n" + "<caret>zxcvbn\n",
|
||||
false);
|
||||
testPutOverlapLine("qwerty\n" + "as<caret>dfg<caret>h\n" + "<caret>zxcvbn\n",
|
||||
"qwerty\n" + "asdfgh\n" + "<caret>zxcvbn\n" + "<caret>zxcvbn\n" + "zxcvbn\n" + "<caret>zxcvbn\n",
|
||||
"qwerty\n" + "asdfgh\n" + "<caret>zxcvbn\n" + "zxcvbn\n" + "<caret>zxcvbn\n",
|
||||
false);
|
||||
testPutOverlapLine("qwerty\n" + "asdfg<caret>h\n" + "<caret>zxcv<caret>bn\n",
|
||||
"qwerty\n" + "asdfgh\n" + "<caret>zxcvbn\n" + "zxcvbn\n" + "<caret>zxcvbn\n" + "<caret>zxcvbn\n",
|
||||
"qwerty\n" + "asdfgh\n" + "<caret>zxcvbn\n" + "zxcvbn\n" + "<caret>zxcvbn\n",
|
||||
false);
|
||||
}
|
||||
|
||||
|
@@ -23,6 +23,7 @@ import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.SelectionType
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||
import com.maddyhome.idea.vim.helper.VimBehaviourDiffers
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||
import org.junit.Ignore
|
||||
|
||||
@@ -86,6 +87,15 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() {
|
||||
myFixture.checkResult(newFile)
|
||||
}
|
||||
|
||||
@VimBehaviourDiffers(originalVimAfter = """
|
||||
A Discovery
|
||||
|
||||
ound it in a legendary land
|
||||
rocks and lavender and tufted grass,
|
||||
re it was settled on some sodden sand
|
||||
d by the torrent of a mountain pass.
|
||||
${c}A Discovery
|
||||
""")
|
||||
fun `test put line in block selection`() {
|
||||
val file = """
|
||||
${c}A Discovery
|
||||
|
@@ -84,20 +84,57 @@ class MultipleCaretsTest : VimTestCase() {
|
||||
// }
|
||||
|
||||
fun testPutText() {
|
||||
val before = "${c}qwe\n" + "rty\n" + "${c}as${c}d\n" + "fgh\n" + "zxc\n" + "vbn\n"
|
||||
// This test produces double ${c}zxc on 3rd line if non-idea paste is used
|
||||
val before = """
|
||||
${c}qwe
|
||||
rty
|
||||
${c}as${c}d
|
||||
fgh
|
||||
zxc
|
||||
vbn
|
||||
|
||||
""".trimIndent()
|
||||
val editor = configureByText(before)
|
||||
VimPlugin.getRegister().storeText(editor, TextRange(16, 19), SelectionType.CHARACTER_WISE, false)
|
||||
typeText(commandToKeys("pu"))
|
||||
val after = "qwe\n" + "${c}zxc\n" + "rty\n" + "asd\n" + "${c}zxc\n" + "${c}zxc\n" + "fgh\n" + "zxc\n" + "vbn\n"
|
||||
val after = """
|
||||
qwe
|
||||
${c}zxc
|
||||
rty
|
||||
asd
|
||||
${c}zxc
|
||||
fgh
|
||||
zxc
|
||||
vbn
|
||||
|
||||
""".trimIndent()
|
||||
myFixture.checkResult(after)
|
||||
}
|
||||
|
||||
fun testPutTextCertainLine() {
|
||||
val before = "${c}qwe\n" + "rty\n" + "${c}as${c}d\n" + "fgh\n" + "zxc\n" + "vbn\n"
|
||||
// This test produces triple ${c}zxc if non-idea paste is used
|
||||
val before = """
|
||||
${c}qwe
|
||||
rty
|
||||
${c}as${c}d
|
||||
fgh
|
||||
zxc
|
||||
vbn
|
||||
|
||||
""".trimIndent()
|
||||
val editor = configureByText(before)
|
||||
VimPlugin.getRegister().storeText(editor, TextRange(16, 19), SelectionType.CHARACTER_WISE, false)
|
||||
typeText(commandToKeys("4pu"))
|
||||
val after = "qwe\n" + "rty\n" + "asd\n" + "fgh\n" + "${c}zxc\n" + "${c}zxc\n" + "${c}zxc\n" + "zxc\n" + "vbn\n"
|
||||
val after = """
|
||||
qwe
|
||||
rty
|
||||
asd
|
||||
fgh
|
||||
${c}zxc
|
||||
zxc
|
||||
vbn
|
||||
|
||||
""".trimIndent()
|
||||
myFixture.checkResult(after)
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user