Skip to content

Commit

Permalink
Implement visual put command which leaves unnamed register untouched (#…
Browse files Browse the repository at this point in the history
…876)

Starting from Vim 8.2 there's a different between p / P in visual mode.
The latter does not modify the unnamed register so that a P command can
be repeated several times without it being clobbered by the previously
selected text.

Closes #873.
  • Loading branch information
albertdev committed Apr 21, 2024
1 parent 631ee44 commit 39291a2
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public void execute(EditorAdaptor editorAdaptor, TextRange region, ContentType c
Position newPosition = region.getLeftBound().addModelOffset(-(position - previousNewlinePos));
region = new StartEndTextRange(newPosition, region.getRightBound());
}
yankAndUpdateLastDelete(editorAdaptor, region, contentType);
doIt(editorAdaptor, region, contentType);
} finally {
editorAdaptor.getHistory().endCompoundChange();
Expand All @@ -67,18 +68,24 @@ public void execute(EditorAdaptor editorAdaptor, TextRange region, ContentType c
public TextOperation repetition() {
return this;
}

public static void yankAndUpdateLastDelete(EditorAdaptor editorAdaptor, TextRange range, ContentType contentType) {
if(range == null) {
return;
}

public static void doIt(EditorAdaptor editorAdaptor, TextRange range, ContentType contentType) {
if(range == null) {
return;
}

YankOperation.doIt(editorAdaptor, range, contentType, false);
RegisterManager registerManager = editorAdaptor.getRegisterManager();
if(registerManager.isDefaultRegisterActive()) {
//get what YankOperation just set
RegisterContent register = registerManager.getActiveRegister().getContent();
registerManager.setLastDelete(register);
//get what YankOperation just set
RegisterContent register = registerManager.getActiveRegister().getContent();
registerManager.setLastDelete(register);
}
}

public static void doIt(EditorAdaptor editorAdaptor, TextRange range, ContentType contentType) {
if(range == null) {
return;
}

TextContent txtContent = editorAdaptor.getModelContent();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,23 @@
import net.sourceforge.vrapper.vim.EditorAdaptor;
import net.sourceforge.vrapper.vim.commands.motions.StickyColumnPolicy;
import net.sourceforge.vrapper.vim.register.RegisterContent;
import net.sourceforge.vrapper.vim.register.RegisterManager;

/**
* Replaces a range of text matched by a TextObject with the contents of a register.
*/
public class PasteOperation implements TextOperation {

public static final PasteOperation INSTANCE = new PasteOperation(true);
public static final PasteOperation INSTANCE_TEMPVISUAL = new PasteOperation(false);
public static final PasteOperation REPLACE_YANK = new PasteOperation(true, true);
public static final PasteOperation REPLACE = new PasteOperation(false, true);
public static final PasteOperation REPLACE_YANK_TEMPVISUAL = new PasteOperation(true, false);
public static final PasteOperation REPLACE_TEMPVISUAL = new PasteOperation(false, false);

private final boolean fixNormalModeCursor;
private final boolean yankToUnnamed;

private PasteOperation(boolean fixNormalCursor) {
private PasteOperation(boolean yankToUnnamed, boolean fixNormalCursor) {
this.yankToUnnamed = yankToUnnamed;
fixNormalModeCursor = fixNormalCursor;
}

Expand All @@ -44,7 +49,8 @@ public TextOperation repetition() {
protected void doIt(EditorAdaptor editorAdaptor, int count, TextRange range, ContentType contentType) {
if (count == Command.NO_COUNT_GIVEN)
count = 1;
RegisterContent registerContent = editorAdaptor.getRegisterManager().getActiveRegister().getContent();
RegisterManager registerManager = editorAdaptor.getRegisterManager();
RegisterContent registerContent = registerManager.getActiveRegister().getContent();
String text = StringUtils.multiply(registerContent.getText(), count);

final String newLine = editorAdaptor.getConfiguration().getNewLine();
Expand All @@ -56,10 +62,12 @@ protected void doIt(EditorAdaptor editorAdaptor, int count, TextRange range, Con
TextContent content = editorAdaptor.getModelContent();
int offset = range.getLeftBound().getModelOffset();

//use the default register for the DeleteOperation so the deleted text
//will be stored in the default register, rather than overwriting the contents
//of the current active register (which we're attempting to paste from)
editorAdaptor.getRegisterManager().activateDefaultRegister();
// if we're going to do a yank then activate a different register so the register we got text from
// is not overwritten by yanking. use the default register (and/or 'clipboard' defined register) instead
if (yankToUnnamed) {
registerManager.activateDefaultRegister();
DeleteOperation.yankAndUpdateLastDelete(editorAdaptor, range, contentType);
}

DeleteOperation.doIt(editorAdaptor, range, contentType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,8 @@ protected State<Command> buildInitialState() {
final Command leaveVisual = LeaveVisualModeCommand.INSTANCE;
final Command yank = new SelectionBasedTextOperationCommand(YankOperation.INSTANCE);
final Command delete = new SelectionBasedTextOperationCommand(DeleteOperation.INSTANCE);
final Command paste = new SelectionBasedTextOperationCommand(PasteOperation.INSTANCE);
final Command replaceYank = new SelectionBasedTextOperationCommand(PasteOperation.REPLACE_YANK);
final Command replace = new SelectionBasedTextOperationCommand(PasteOperation.REPLACE);
final Command change = new SelectionBasedTextOperationCommand.DontChangeMode(ChangeOperation.INSTANCE);
final Command format = new SelectionBasedTextOperationCommand(FormatOperation.INSTANCE);
final Command shiftLeft = new SelectionBasedTextOperationCommand(InsertShiftWidth.REMOVE_VISUAL);
Expand Down Expand Up @@ -224,8 +225,8 @@ protected State<Command> buildInitialState() {
leafBind('x', delete),
leafBind('X', delete),
leafBind(SpecialKey.DELETE, delete),
leafBind('p', paste),
leafBind('P', paste),
leafBind('p', replaceYank),
leafBind('P', replace),
leafBind('~', swapCase),
leafBind('J', joinLines),
leafBind(':', commandLineMode),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@ public String getDisplayName() {

@Override
protected State<Command> buildInitialState() {
Command pasteTempVisual = new SelectionBasedTextOperationCommand(
PasteOperation.INSTANCE_TEMPVISUAL);
Command replaceYankTempVisual = new SelectionBasedTextOperationCommand(
PasteOperation.REPLACE_YANK_TEMPVISUAL);
Command replaceTempVisual = new SelectionBasedTextOperationCommand(
PasteOperation.REPLACE_TEMPVISUAL);

return union(super.getPlatformSpecificState(VisualMode.NAME),
state(
leafBind('p', pasteTempVisual),
leafBind('P', pasteTempVisual)),
leafBind('p', replaceYankTempVisual),
leafBind('P', replaceTempVisual)),
super.buildInitialState());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,49 @@ public void testPastingInVisualMode() throws Exception {
assertYanked(ContentType.TEXT, "awesome");
}

@Test
public void testPastingWithoutYankInVisualMode() throws Exception {
var defaultRegisterContent = "a series of tubes";
defaultRegister.setContent(new StringRegisterContent(ContentType.TEXT, defaultRegisterContent));
checkLeavingCommand(forKeySeq("P"),
false, "The internet is ","awesome","!",
"The internet is a series of tube",'s',"!");
assertEquals(NormalMode.NAME, adaptor.getCurrentModeName());
assertYanked(ContentType.TEXT, defaultRegisterContent);

defaultRegisterContent = "\t\ta series of tubes\n";
defaultRegister.setContent(new StringRegisterContent(ContentType.LINES, defaultRegisterContent));
checkLeavingCommand(forKeySeq("P"),
true, "The internet is ","awesome","!",
"The internet is \n\t\t",'a'," series of tubes\n!");
assertEquals(NormalMode.NAME, adaptor.getCurrentModeName());
assertYanked(ContentType.LINES, defaultRegisterContent);

defaultRegisterContent = "a series of tubes\n";
defaultRegister.setContent(new StringRegisterContent(ContentType.LINES, defaultRegisterContent));
checkCommand(forKeySeq("P"),
false, "The internet is \n","awesome\n","!",
false, "The internet is \n","","a series of tubes\n!");
assertEquals(NormalMode.NAME, adaptor.getCurrentModeName());
assertYanked(ContentType.LINES, defaultRegisterContent);

defaultRegisterContent = "a series of tubes";
defaultRegister.setContent(new StringRegisterContent(ContentType.TEXT, defaultRegisterContent));
checkCommand(forKeySeq("P"),
false, "The internet is \n","awesome\n","!",
false, "The internet is \n","","a series of tubes\n!");
assertEquals(NormalMode.NAME, adaptor.getCurrentModeName());
assertYanked(ContentType.TEXT, defaultRegisterContent);

defaultRegisterContent = "a series of tubes";
defaultRegister.setContent(new StringRegisterContent(ContentType.TEXT, defaultRegisterContent));
checkCommand(forKeySeq("2P"),
false, "The internet is ","awesome","!",
false, "The internet is a series of tubesa series of tube","","s!");
assertEquals(NormalMode.NAME, adaptor.getCurrentModeName());
assertYanked(ContentType.TEXT, defaultRegisterContent);
}

@Test
public void visualModeShouldHaveAName() {
adaptor.changeModeSafely(VisualMode.NAME);
Expand Down

0 comments on commit 39291a2

Please sign in to comment.