Move anchor and cursor together

Is there a map I can do that will move the anchor with the cursor so the selection stays the same size?

The problem I’m trying to solve in case there are alternate solutions. I do a bit of diagramming and sometimes I need to move a bit of the diagram around to a new line and position. Example:

|      B.Offset
|      |      A.Length
|      |      |      B.Length
V      V      V      V
0      5      10     15     20     25     30     35     40     45

Now say I want to copy this diagram and update it so that B moves to the right 10 units. First I use C to select both B.Length and it’s V arrow and move them over 10 units by adding spaces.

|      B.Offset
|      |      A.Length
|      |      |      ..............B.Length
V      V      V      ..............V
0      5      10     15     20     25     30     35     40     45

Now I want to move B.Offset to the right of and below A.Length. What I’d like to do is select B.Offset and d yank and delete it, then move the whole selection (anchor and cursor) down two lines and to the right 14 columns so that it overlays the new spaces I just inserted. Then I can R replace the spaces with the yank.

|      |      A.Length
|      |      |      B.Offset......B.Length
V      V      V      ..............V
0      5      10     15     20     25     30     35     40     45

Not moving the anchor means that I have to mentally keep track of how big the selection was and manually select that many spaces for R replacement. Or p paste the yank and then d delete the spaces until B.Length is re-aligned with the V arrow again.

Worst kind of reply incoming, fair warning.

Maybe a different tool would serve you better, like for ascii diagrams, ASCIIFlow

or, my personal favorite tool for doing diagrams in text is:

… and now that I think about it, d2 have an ascii diagram output format would be cool.

… additially, sorry for no real help on your question :stuck_out_tongue:


It’s not the worst kind of reply, it’s actually a very valid point. I have used ASCIIFlow before, but I would like something that isn’t a webpage and also outputs ascii/unicode. I do most of my work in a terminal, so I’d like to stay there as much a possible. I’m actually writing a little terminal program to do art primitives, but it not ready for prime time yet.

D2 rocks and has a built in auto-formatter and is just done in text!

I am a bit of a D2 fanboy.

Hacked this together for sake of a challenge/practice:

define-command preview-replace %{
	remove-hooks window preview-replace

	hook -group preview-replace window RawKey <esc>|R %{
		remove-hooks window preview-replace
	hook -group preview-replace window ModeChange '.*' %{
		remove-hooks window preview-replace
	hook -group preview-replace window RawKey .* %{
		execute-keys ";%sh{echo ${#kak_reg_dquote}}<s-l><s-h><a-;>"

Note that the selection will wrap over to the next line if the yanked text is too long.


Cheers, I’ll give it a try.

I’ve solved this exact problem for ̶a̶u̶t̶o̶-pairs release :


and same with L. I’m not sure it works as well with j and k but I didn’t need it.

For your use case though, what I’d do is probably use a combination of

  • the & key which allows to align selections by adding spaces
  • C or <a-C> to Copy selection from one line to another, keeping alignement and size

Step by step :

To quickly go to the pipe you want to align to and mark the selection
To select the beginning of V and B.length
To append back the 25 pipe to the selection and align the selections, inserting spaces
Undo redo to select what’s just been added and replace it with points
To select B.Offset and yankdelete it
Or anything that gets you back to the beginning of …B.Length

Then you have a lot of options :

  • simply insert B.Offset, delete the dot and realign B.length with V using the & and uUr tricks. It is the most particular and hardest to turn into a mapping solution, but it may be fast enough that we don’t even need a mapping, especially if we think of aligning B.Length, V and 25 last rather than first.
  • Use an intermediate line to put work with what you copy, & and C
  • Shell out to get the length of the yank register (${#varl} or something, idk), and craft a selection accordingly, that you can then R replace.
  • plenty of other ways probably

Because I like solution 2 the most, here is my jab at it (remember, the cursor is at the beginning of what we want to replace) :
to insert a new line without hook, so as to not trigger automatic indentation
to save the selection, go to the empty line and paste what we want to insert, selecting it and append back our previous selection
To align. We have to ensure the cursor is at the beginning of each selection, as & aligns cursors.
To keep only what we just pasted as selection
to select the corresponding chunk on the line to make the insertion on (with undesired consequences if there is no room ! There is probably a more robust and much more lengthy solution than <a-C> using <a-z>u)
to exchange the selections.
To clear the temporary line.
to select what you just inserted.

I had a passing thought that it could be solved by mapping with the <a-;>, I just hadn’t taken the time to dig into it. The H<a-;>H<a-;> works really well. It seems the hard part is finding a good mapping for it. H K L all have an available <c-*> mapping available ( I don’t use the history navigation ). But <c-j> is eaten by legacy VT codes and <a-j> is too useful to remap.

When mappings come short, it may be time to make a user mode. You can use -lock option of the enter-user-mode command to stay in a mode.

The on-key command may or may not come in handy if you want to factorize all the possible key mappings from this mode.

Another way to do this is using selections_desc. It contains each cursor/anchor position in the format 1.2,3.4. So you can write a script that computes the length of each selection in a register, then add it to the current selections desc.

For example:

  1. Press Z to save selections_desc to register caret
  2. Go somewhere else
  3. Find the lengths of each selection (1.2,3.4 => |2-4| = 2) and add it to the current selections_desc with :select

Sample code:

import os
lengths = list(map(lambda x: abs(int(x.split(",")[0].split(".")[1]) - int(x.split(",")[1].split(".")[1])), os.getenv('reg_caret').split(" ")[1:]))

def new_selection(sel_desc, new_width):
    sel_desc = list(map(lambda x: list(map(int, x.split("."))), sel_desc.split(",")))
    new_cursor_column = min(sel_desc[0][1], sel_desc[1][1]) + new_width
    return f"{sel_desc[0][0]}.{sel_desc[0][1]},{sel_desc[1][0]}.{new_cursor_column}"

new_ones = list(map(lambda x: new_selection(*x), zip(os.getenv('selection_descs').split(' '), lengths)))
print(':select ' + ' '.join(new_ones))