Привет, друзья. Как я уже рассказывал, в Юникоде есть символы-модификаторы, которые сами по себе не изображаются, но меняют вид предыдущего символа. Это различная диакритика, включая ударение, кратку над й и точки над ё, хотя эти символы есть и "в сборе" (сравните Ё и Ё: первый символ составной, а второй цельный), а также огласовки, подчерки, вычёркивание и всякое прочее в таком роде. Мы обсуждали средство вставлять в текст неудаляемое выделение вроде в̲о̲т̲ ̲т̲а̲к̲о̲г̲о̲.
Но как его убрать?
Прежде всего, давайте вспомним, как посмотреть на состав сложного символа. Для этого есть команды ga (и g8). Первая покажет символ и все модификаторы. Например, если вы нажмете A<C-V>U0332 и пробел, то увидите A̲. Поместив курсор на этот составной символ, вы получите такую информацию:
<A> 65, Hex 41, Octal 101 < ̲> 818, Hex 0332, Octal 1462
Теперь видно, что сложный символ состоит из A с кодом 65 и модификатора с кодом 818. Команда g8 выдаст байты, из которых состоит символ: 41 + cc b2
Далее, выставляем флаг delcombine (set delcombine) и теперь команды x, <BS>, <Del> будут удалять не весь составной символ, а модификаторы, по одному, и только под конец сам символ. Только помните, что xx на первом символе в тексте A̲A̲ удалит этот первый символ: сначала модификатор, потом А; а вот 2x удалит оба составных символа.
Это уже вариант удалить модификаторы. Но только он неудобный, если текст большой, а модификаторов много. Ну и помним, что есть символы в сборе, как Й, никак из него И не сделать.
Помним, что по умолчанию модификаторов не более двух, но это можно поменять, задав опцию maxcombine (но больше шести всё равно нельзя). Без этого вы не сможете монстрячить в таком духе: Я̶̲̅. В иврите и арабском четыре модификатора - это нормально.
Я заметил, что редактор Дзен работает в режиме delcombine, удаляя модификаторы. Это прикольно.
Вот здесь описана функция, которая либо очищает текст от заданного модификатора, либо добавляет его после каждого символа. Вот она:
function! ToggleCombining( character, text )
let l:cleanedText = ''
let l:idx = 0
while l:idx < len(a:text)
let l:codepoint = nr2char(char2nr(a:text[l:idx :]))
if l:codepoint !=# a:character
let l:cleanedText .= l:codepoint
endif
let l:idx += len(l:codepoint)
endwhile
if l:cleanedText !=# a:text
return l:cleanedText
else
return substitute(a:text, '[^[:cntrl:]]', '&' . a:character, 'g')
endif
endfunction
Давайте разберем. Определяется функция, восклицательный знак предписывает уже существующую функцию переопределять. У функции два аргумента: символ-модификатор и текст.
Создаются две переменные: в одной будет очищенный текст, другая просто номер символа в строке.
Запускается цикл по тексту: указатель идет от начала до конца текста.
Выражение a:text[l:idx :] берет текст от указателя до конца. Помним, что префикс a: - для аргументов функции. Функция char2nr берет первый символ текста и возвращает его код, а парная nr2char превращает код опять в символ: так мы изящно отцепляем символы по одному.
Условие if l:codepoint !=# a:character сравнивает отцепленный символ с указанным модификатором, а символ решетки означает игнорирование регистра. То есть условие выполнено, если символ - не данный модификатор.
Если так, то добавляем символ к очищенному тексту оператором "точка", совмещенным с присваиванием.
Сдвигаем указатель на столько байт, сколько было в отцепленном символе.
Теперь смотрим, совпадает ли очищенный текст с исходным. Если нет, то надо возвратить очищенный текст. Если же да, то надо добавить модификатор к каждому символу в тексте. Это делает функция замены, которая всё, что не управляющий символ, заменяет на него же плюс модификатор.
Пользоваться ею можно так:
echo ToggleCombining( "\u0332", "sample text" )
или более изощренно, например, через привязку:
vmap UU d:let @q=ToggleCombining( "\u0332", @")<CR> \| "qp
Теперь можно выделить текст и, нажав UU, подчеркнуть все символы в нем. А потом так же точно убрать.
Не забудьте про регистр-выражение, он тоже бывает удобен в этом контексте.
Удачи!