5

I want to edit words that need accents (á) were they are missing. It's no problem to write down accents in insert mode: it is just "' + letter", so I can easily write á, é, í, ó, ú.

How do get accent on a letter in normal mode (on top of a given letter)?

How do I transform "o" into "ó" in normal mode (with least keystrokes)?

  • spell check is one option, but it seems important to be able to do the check just by reading and modifying, without spell check.

  • with "r'o" this could of course be achieved, but I'm looking for less strokes.

  • Single stroke would be great. Like "~", which changes a minor letter to an upper case with just, from "a" to "A", and back. That is the intended behavior I'm seeking.

Best result would be if every accent could be done with one stroke on each word. This way one could use the repeat command: "." (which in keyboard implies pressing only one key (.), which is less than "~", that goes with function key or shift.

tl,dr: a shortcut for vowels, to edit faster a full text lacking all needed accents

1
  • I think you'll need to define some function that accepts your accented character as an argument in vimscript, and then bind that function to some key-combination. Unfortunately I can't help you with the vimscript.
    – Waldheri
    Jul 22, 2017 at 11:25

2 Answers 2

7

If you set 'digraph', (IOW makes sure :set digraph is in your .vimrc) it could become as simple as:

nnoremap <silent> µ s<c-r>"<bs>'<esc>

The key points are:

  • s<c-r>" that replaces the current character under the cursor with itself and stays in insert mode -- this is important for the next step.
  • e <BS> ' that becomes é...

Note we could also use tr(), but in that case we'll need to specify all possible replacements. We could even imagine cycles between e é è ê ë.

nnoremap <silent> µ s<c-r>=tr(@", 'aeioucnAEIOUCNáéíóúçñÁÉÍÓÚÇÑ', 'áéíóúçñÁÉÍÓUÇÑaeioucnAEIOUCN')<cr><esc>

Note that I use µ, the Greek mu character, to trigger the mapping as it's available on my French keyboard and easy to recognize when we're not used to mappings. If you prefer - use it. This is not important. It's up to you to chose a key that you don't, and will never, use for anything else.

Bonus: here is a mapping that'll work in visual mode in case you'll want to replace several characters at once. Remark: unlisted characters won't be affected.

xnoremap <silent> µ s<c-r>=tr(@",'aeioucnAEIOUCNáéíóúçñÁÉÍÓÚÇÑ', 'áéíóúçñÁÉÍÓUÇÑaeioucnAEIOUCN')<cr><esc>
0
3

You could put one of the following codes at the end of your vimrc file to make the - key toggle the vowel under the cursor between the accented and unaccented form:

The intuitive solution

function! ToggleAccent()
   " Vowels
   let withAccent   = [ "á", "é", "í", "ó", "ú", "Á", "É", "Í", "Ó", "Ú" ]
   let withNoAccent = [ "a", "e", "i", "o", "u", "A", "E", "I", "O", "U" ]

   " A better way of getting the character under the cursor
   " From: https://stackoverflow.com/a/23323958/1121933
   let character = matchstr( getline('.'), '\%' . col('.') . 'c.' )

   " If it's a vowel without an acute accent over it, 'position' will contain
   " the index of the matching element in the 'withNoAccent' list or -1 otherwise.
   let position = match( withNoAccent , character )
   if position != -1
      " Replace it with an accented vowel
      execute ":normal! r" . withAccent[position]
   else
      " Check if it's a vowel with an acute accent over it
      let position = match( withAccent , character )
      if position != -1
         " Replace it with a vowel with no accent
         execute ":normal! r" . withNoAccent[position]
     endif
   endif

   " Do nothing if it isn't a vowel
endfunction

" Map the '-' key
nnoremap <silent> - :call ToggleAccent()<CR>

The clever solution

See Luc Hermitte's answer, but here's what's going on:

  • s Delete the character under the cursor, save it into register " and start insert mode. See :help s
  • <c-r> Insert the content of the register that follows (= in that case). See :help i_CTRL-R
  • = This is the expression register, i.e., you're prompted to enter a expression. Once the expression is evaluated (after you hit Enter, represented by <cr>), the result (the string returned by tr()) is saved in that register. See :help i_CTRL-R_=
  • tr({src},{fromstr},{tostr}) Return a copy of the {src} string with all characters which appear in {fromstr} replaced by the character in that position in the {tostr} string (see :help tr().) That's what ToggleAccent() above tries to do. In that case {src} is @", that's the way Vim gets the content of register ". See Registers as Variables at Learn Vimscript the Hard Way
  • <cr> Hit Enter and evaluate the expression
  • <esc> Go back to normal mode

Unfortunately, the . command doesn't repeat custom mappings, but you can read Ingo Karkat's answer to get around it.

6
  • Works like a charm! I added "ñ" and "ç". Since the resulting command you provide is a single stroke: then "." is not needed. Hope this is useful to others.
    – nilon
    Jul 22, 2017 at 14:02
  • I chose other answer since code was simpler. Very handy anyway
    – nilon
    Jul 23, 2017 at 21:45
  • how did edit change content?
    – nilon
    Jul 24, 2017 at 1:57
  • I didn't. I just added reference information about Luc Hermitte's answer. Jul 24, 2017 at 2:01
  • The more I know Vim, the more I find difficult to define what "intuitive" means. It really depends on what we're used to. For many people Vim functions are completely un-intuitive, while macros (that I never use) are. IMO, correctly obtaining the exact character under the cursor is something definitively not intuitive for instance. Any way, before I remember of tr() I though first of adding <C-K>' to the current character, then your answer have reminded me of dictionaries, and eventually the though "hey, I'm implementing tr" came. Jul 24, 2017 at 8:58

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.