So I have a case where I have some unicode letters say [B, Ḅ, Ḃ] and when I have my mouse selected over the B, (in normal mode) I want some way of cycling through the list. Like pressing some command should replace B with , pressing it again should replace with .

I looked at this https://github.com/zef/vim-cycle/blob/master/plugin/cycle.vim, but it was not quite what I was looking for as

  1. It works on words not letters
  2. I sometimes need multiple groups

Say this is group associated with hotkey A [$ Ɗ Ʋ] I also need a second group B, with a second hotkey which would cycle [$ ¥], [Ɗ ɗ] and [Ʋ ʋ].

Finally (and I might ask this in a follow up question), very rarely I would need to do a two group cycle. Something like [С, п, Ðḟ, ...] Which would always start with a Ð. Any ideas, or resources to look for?

I tagged this with vimscript Lua, simply because I am open to solving this issue with Lua if it makes it easier.

2 Answers 2


At least for single characters, here's what I would do. First, let's build a little function for an expression map. It takes a dictionary corresponding to the cycle, examines the character under the cursor, and returns the sequence of commands to do. This is either a r command or nothing.

function CyclePair(which_pairs) abort
  let char_under_cursor = strpart(getline('.'), col('.') - 1, 1, v:true)
  return has_key(a:which_pairs, char_under_cursor)
        \ ? 'r'.a:which_pairs[char_under_cursor]
        \ : ''

Then we can map it like

nnoremap <expr> <leader>r CyclePair({'a': 'b', 'b': 'c', 'c': 'a'})
" ... more mappings, different pairs, no big deal.

Typing the dictionary is a slight pain and easy to get wrong. It would be nicer to just use a list.

function MakePairs(xs) abort
  let len = len(a:xs)
  return range(len)->reduce({ acc, val -> extend(acc, {a:xs[val]: a:xs[(val+1)%len]}) }, {})

So now we can write

nnoremap <expr> <leader>r CyclePair(MakePairs(['a', 'b', 'c']))
  • With this approach would I need a different keybinding for each cycle pair? I think I have 26 or so in total (in group A), so I feel like it might be better if it were CyclePairs instead. Alas, it seems inefficient having to look through 26 groups to see if the char is in one of them. With that being said, this seems like a great start! Commented Jul 8, 2021 at 11:54
  • @N3buchadnezzar ah, i did forget about that. Part of the issue is there is overlap between cycles, so that may be non-trivial. But dictionary could have multiple unrelated cycles in it (a to b to c and d to e to f).
    – D. Ben Knoble
    Commented Jul 8, 2021 at 12:23
  • 1
    I might be on to something with tr. E.g tr(@", 'ABC', 'BCA') would perform a simple 3 cycle. I'll look into it today and might answer my own question if it works out =) Commented Jul 8, 2021 at 12:38
  • Ah, I forgot about tr. That might make some of the code more bearable.
    – D. Ben Knoble
    Commented Jul 8, 2021 at 17:50

So I found a solution thanks to How to add accent to letters in normal mode? using tr. As stated in the comments a simple 3 cycle would be done by

tr(@", 'ABC', 'BCA')

Which cycles A -> B -> C -> A. Wrapped in a command it looks like

nnoremap <silent> <localleader>ln s<c-r>=tr(@", 'ABC', 'BCA')

However, this gets a bit nasty when trying to add several cycles into the same command as in my real example beneath

nnoremap <silent> <localleader>ln s<c-r>=tr(@", '3456789³⁴⁵⁶⁷⁸⁹AẠȦÄBḄḂƁCÇĊƇDÐḌḊƊEẸĖ', '³⁴⁵⁶⁷⁸⁹3456789ẠȦÄAḄḂƁBÇĊƇCÐḌḊƊDẸĖE')<cr><esc>
" nnoremap <silent> <localleader>lp s<c-r>=tr(@", 'ABC', 'BCA')<cr><esc>

nnoremap <silent> <localleader>lq s<c-r>=tr(@", '©ß¢ÇçÑñ£Ŀŀ¦¡¿/ƒ\¤$ƊƲ¥ɗʋ#?Ƒ⁺@`"þ{}€ÞƝƤƙɼƭⱮƇƬ', 'ߢÇçÑñ£Ŀŀ¦¡¿/ƒ\¤$ƊƲ¥ɗʋ#?Ƒ⁺@`"þ{}€ÞƝƤƙɼƭⱮƇƬ©')<cr><esc>
" nnoremap <silent> <localleader>la s<c-r>=tr(@", 'ABC', 'BCA')<cr><esc>

Do note that everything works, but I will be asking a separate follow up question on how to make the formatting more bearable, because this will turn into a nightmare debugging, =)

and I have only added A-E still got D-Z...

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.