Vim Tips Wiki
Register
Advertisement
Tip 1660 Printable Monobook Previous Next

created July 20, 2010 · complexity basic · author Bmdavll · version 7.0


This tip provides an easy method to enter Unicode combining characters into text. Unicode contains "combining" code points that modify the appearance of the previous character. Several of these can be used to create text with lines running through them, including U+0305 (COMBINING OVERLINE), U+0332 (COMBINING LOW LINE), U+0333 (COMBINING DOUBLE LOW LINE), and U+0336 (COMBINING LONG STROKE OVERLAY).

This is done by putting the appropriate combining character after each printable character in the text. To create these effects easily, you can add the following commands to your vimrc.

`Set up Unicode`[]

Unicode needs to be enabled so Vim will render combining characters properly. See Working with Unicode. Specifically, the 'fileencoding' and 'encoding' options should be set to a Unicode encoding such as utf-8.

Define commands[]

" modify selected text using combining diacritics
command! -range -nargs=0 Overline        call s:CombineSelection(<line1>, <line2>, '0305')
command! -range -nargs=0 Underline       call s:CombineSelection(<line1>, <line2>, '0332')
command! -range -nargs=0 DoubleUnderline call s:CombineSelection(<line1>, <line2>, '0333')
command! -range -nargs=0 Strikethrough   call s:CombineSelection(<line1>, <line2>, '0336')

function! s:CombineSelection(line1, line2, cp)
  execute 'let char = "\u'.a:cp.'"'
  execute a:line1.','.a:line2.'s/\%V[^[:cntrl:]]/&'.char.'/ge'
endfunction

Each command (:Overline, :Underline, :DoubleUnderline, and :Strikethrough) allows you to add a particular effect to the text. To use them, visually select the portion of the text you want to change. You can use regular visual, line-wise, or block-wise visual modes.

With the text still selected, enter the command name (for example, enter :Underline). You will see that the previously selected text has been underlined.

If you use a command frequently, you may want to define a mapping to make it easier. For example, the following will overline selected text when OO is typed:

vnoremap OO :Overline<CR>


Removing combining characters is not easy though. The delcombine option only works in Normal mode, and removing combining chars from a whole line char by char using 'x' is pretty tedious. Combining this script with the one from https://stackoverflow.com/questions/46141062/removing-unicode-combine-character-vim, one can build a toggle script like the following:

command! -range -nargs=0 Overline        call s:ToggleCombining(<line1>, <line2>, "\u0305")
command! -range -nargs=0 Underline       call s:ToggleCombining(<line1>, <line2>, "\u0332")
command! -range -nargs=0 DoubleUnderline call s:ToggleCombining(<line1>, <line2>, "\u0333")
command! -range -nargs=0 Strikethrough   call s:ToggleCombining(<line1>, <line2>, "\u0336")


function! s:ToggleCombining( line1, line2, character )

    let l:lines = getline(a:line1, a:line2)
    if len(l:lines) == 0
        return ''
    endif
    let l:text = join(l:lines, "\n")

    let l:cleanedText = ''
    let l:idx = 0
    while l:idx < len(l:text)
        let l:codepoint = nr2char(char2nr(l:text[l:idx :]))
        if l:codepoint !=# a:character
            let l:cleanedText .= l:codepoint
        endif

        let l:idx += len(l:codepoint)
    endwhile

    let l:finalText = ""
    if l:cleanedText !=# l:text
        let l:finalText = l:cleanedText
    else
        let l:finalText = substitute(l:text, '[^[:cntrl:]]', '&' . a:character, 'g')
    endif

    execute a:line1.','.a:line2.'s/\%V'.l:text.'/'.l:finalText.'/'
endfunction


Explanation[]

  • The commands add a combining character after each non-control character within the selected area in the given range.
  • If you invoke the commands while the selection is still active, the range will appear on the command line. :help :command-range
  • The combining characters are encoded in strings using Unicode escapes (for example, "\u0305"). :help expr-string
  • The characters are inserted only after non-control (printable) characters. If they come after control characters, the text is not well-formed and will not render correctly. :help [:cntrl:]
  • The pattern searches for a character prefixed by \%V: that means the character is only matched if it is within the previous selection. :help /\%V

To delete a combining character, first use :set delcombine. *:help 'delcombine'

To reverse adding a combining character see https://stackoverflow.com/questions/46141062/removing-unicode-combine-character-vim

Comments[]

The original script did not operate on the last selected character on my system. For example, selecting "xyz" and entering :Overline would overline xy only. I have patched the script with the following change:

  " Changed original line:
  execute a:line1.','.a:line2.'s/\%V[^[:cntrl:]]\%V/&'.char.'/ge'
  " to what is now in script:
  execute a:line1.','.a:line2.'s/\%V[^[:cntrl:]]/&'.char.'/ge'

I have mentioned this change in case it breaks anything. JohnBeckett 08:12, June 13, 2011 (UTC)

The reason is, \%V is a little bit buggy and not zero-width. This is mentioned in todo.txt. Chrisbra 11:27, June 14, 2011 (UTC)

Thrilling; now what if I strikethrough a whole block of text, like several paragraphs, but then want to unstrikethrough only a portion thereof, like two sentence? Am I stuck with using 'x' on each and every character?

Advertisement