2

Consider this mapping:

vnoremap <leader>U ygv"=toupper(@@)<cr>p

It will yank the current visual selection, select it back, and then put a modified version of it. In this case, uppercase letters.

This works great on character-wise and linewise Visual modes, where the final p command will replace the contents of the previous selection, leaving everything else in place.

However, in block-wise Visual mode, it doesn't work as expected. Even though the resulting register has the same "format" as the one before the transformation, the put operation ends up shifting things around, putting the multi-line content in the first line of the block and breaking some lines as it does that.

This same problem doesn't happen if I simply put the register, unmodified. Inspecting the register with :reg " doesn't show anything out of the ordinary.

In fact, applying the transformation manually with :let @@ = toupper(@@) and inspecting the register afterwards doesn't show anything that's been lost, but putting it into a Visual block selection still fails at that point.

So I ask:

  • Is there some magic in how contents coming from Visual block yanks/deletes are stored into registers?
  • If so, is there anyway to inspect that, see it?
  • Is there any way to reproduce that, so that after the toupper() conversion I could still be able to preserve that it's meant to be a block?
  • If that's not possible, are there any workarounds available for this situation?

I looked at help for Visual block mode and didn't find anything (in fact, didn't find specific help on v_b_p.)

Also, toupper() is just a simple example. Of course I could simply use U for that case. The use of toupper() is just as a proxy for a more general transformation. (This issue came up as I was writing an answer to this question.)

4
  • 1
    I have found something that may help on :h @=, 4th paragraph.
    – perelo
    Aug 22, 2019 at 13:35
  • @perelo Yes, that helps explain it, but :let @@ = tpupper(@@) also fails and doesn't use the expression register... I imagine it's for a similar reason, though, so you might be up to something there.
    – filbranden
    Aug 22, 2019 at 13:37
  • 2
    Does setreg("@", toupper(@@), "b") help? Aug 22, 2019 at 14:13
  • 1
    @JürgenKrämer Yes that works! There's also getregtype() which returns the type of the register and works perfectly as a third argument to setreg(). Would you care to write that as an answer? Thanks!
    – filbranden
    Aug 22, 2019 at 14:40

1 Answer 1

5

To modify the content of a register while keeping its type (characterwise, linewise or block) you need to use setreg(). Otherwise Vim is only able to choose the register type as characterwise or linewise. There is note at :help setreg() which hints at this:

Note: you may not reliably restore register value without using the third argument to getreg() as without it newlines are represented as newlines AND Nul bytes are represented as newlines as well, see NL-used-for-Nul.

In your example I would use the following command to uppercase the blockwise selection in the default register:

call setreg('"', toupper(@"), getregtype('"'))

(I chose to use the default register instead of @@, because in my tests getregtype('@@') returned an empty string.)

1
  • When reading the contents of the register, @" and @@ are the same. But when you need the name of the register (in a string), then it should be '"', since that's the actual name of the register. Thanks for the answer!
    – filbranden
    Aug 22, 2019 at 20:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.