I am trying to brute-force the Caesar shift cipher in vim. I have recorded three macros f, a, and d where f copies the line and a applies a Caesar shift to all elements on the line by calling macro d which Caesar shifts a char. Both macros work fine individually, and when I spam @f@a on my keyboard, but when I create a new macro @f@a using any other register, the macro doesn't function properly, it doesn't create a new line and appears to only Caesar shift the current line.

This is my text file w/ macros#

# abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
# bcdefghijklmnopqrstuvwxyzaBCDEFGHIJKLMNOPQRSTUVWXYZA

"brute-forcing Caesar shift cipher w/ vim macros

"input is given at bottom of file

""duplicate bottom line

""Caesar shift line
!!wc -c^M0Yuo^[<80>ýapa@d^[<80>ýa0"rDk@r

""Caesar shift character

hello world

Edit: the new macro I was referring to was @f@a. The key sequence @f@a works fine but @x where @x=@f@a where x is any register does not behave the same. Can anyone reproduce this behavior?

Edit: I have included macro d. Note: macro r is recorded in macro a. I understand the vim macros are incomprehensible but how could, even ignoring the macros, a key-sequence like @f@a be different than @x which has @f@a stored inside register x?

  • 1
    Could you clean up macro a? Also you appear to have two other non-mentioned macros, there: @d and @r. And can we see the problematic macro?
    – romainl
    Oct 14, 2022 at 20:25
  • I will attempt to clear up the macro later but, when I write @f @a repeatedly both macros work perfectly fine. But do not work with @f@a recorded to any register. Even treating the macros as a black box, this should not be possible. Can any one reproduce this issue by copying the macros?
    – Progamer
    Oct 14, 2022 at 21:38
  • 1
    "Can any one reproduce this issue by copying the macros?" That's precisely why I asked you to clean up your macro. And again, your macro a appears to be using two other macros @d and @r that you didn't provide. How do you expect us to reproduce your issue with what you give?
    – romainl
    Oct 15, 2022 at 6:10
  • 1
    I have included macro d, macro r is recorded in macro a using "rD
    – Progamer
    Oct 15, 2022 at 11:17
  • Still no cleanup.
    – romainl
    Oct 16, 2022 at 7:13

1 Answer 1


This question is extremely my bag.

What's going wrong

The problem with your macro is your use of u. When you're recording the macro, it undoes the most recent change. But when you're playing back the macro, it undoes all the edits so far in the macro playback. (I presume this is a side-effect of the implementation of the essential behaviour that using it after playing back the macro undoes the entire invocation.)

So during the playback of macro "a, it just undoes the changes you made to the new line. But during the playback of your combined macro "x it undoes those changes and the duplication of the line made by macro "f.

How to fix it

There's many possible fixes for this, but the one I ended up implementing was to replace this:


0Y              # yank character count
  u             # undo (INTENDED TO revert to input text: broken)
   o            # start a new line below
    ^[<80>ýa    # exit insert mode (i.e. press <Esc>)
            p   # paste the character count on the new line
             a  # enter insert mode (at the end of the line)

With this:


         k      # move up, (leaving the character count in the buffer)
          @f    # make a new copy of the input text
            j   # move back down to the line containing the character count
             A  # enter insert mode at the end of the line

A complete solution

Here's a complete solution. I also converted the control characters into string special characters so it can be pasted into e.g. your vimrc, and replaced your use of wc in a !! filter with some Vimscript called from the expression register, so it runs entirely inside Vim and on computers that don't happen to have wc available:

" caesar shift line
" broken a
let @a = "0y$cc\<C-R>=len(@0)\<CR>\<Esc>0Yuo\<Esc>\x80\xfdapa@d\<Esc>\x80\xfda0\"rDk@r"
" fixed a
let @a = "0y$cc\<C-R>=len(@0)\<CR>\<Esc>k@fjA@d\<Esc>\x80\xfda0\"rDk@r"

" caesar shift character
let @d = "mfyl3Gpif\<Esc>\x80\xfdalajyl\<Esc>\x80\xfda0\"sD1G@s`fvpl"

" duplicate and caesar shift
let @x = "@f@a"

Extra credit

Your macros don't contain this issue, but there is another way that running two macros manually can behave differently to running them both from a third macro.

If the first macro aborts because of an error, then if you play them back manually the second one will carry on where the first left off. But when you run the third macro it will never run the second.

For a quick demonstration of this, try recording three macros by typing:


After recording the above, typing @a@b will move to the bottom of the buffer and delete the bottom line, whereas typing @c will move to the bottom of the buffer but won't delete anything, even though @c == "@a@b".

  • 1
    I guessed the problem was with the undo in the shower and was about to post my answer here but your answer is much better than I could write! I couldn't even comprehend my own macro because of the weird characters either before the solution. I really appreciated the indented explanation diagrams.
    – Progamer
    Oct 21, 2022 at 18:06
  • @Progamer Glad I could help! I had a lot of fun figuring out how your macros worked and writing up my answer. Check out filbranden's terrific answer here for an explanation of the weird characters in the macros in your question.
    – Rich
    Oct 21, 2022 at 18:47

Your Answer

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

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