Привет, друзья. Про текстовые объекты материал уже был. Это команды, применяемые после оператора или при выделении и указывающие текстовые объект: слово, содержимое скобок и т.п. Отличий команд текстовых объектов от команд движения два: первые не применяются сами по себе, для смещения курсора, и они могут захватывать текст по обе стороны от курсора (движение обычно идет либо вперед по тексту, либо назад).
Сегодня мы рассмотрим несколько примеров создания своих текстовых объектов, опираясь на материал уважаемых коллег.
Если вам часто нужно сложное движение (какое-нибудь f.2bl) ИЛИ если вам часто нужно сначала шагнуть влево, а потом вправо (например, F[%), то есть смысл сделать свой текстовый объект.
Обычно текстовые объекты начинаются с i или a, причём первые не захватывают границу, а вторые - захватывают. Мнемоника может быть такая: iw="in word", aw="a word". Коллеги предлагают другую: iw='inner word', aw='around word'. Или вот i( или a( - первая забирает содержимое скобок, не захватывая скобки, а вторая - содержимое скобок вместе с ними. Поэтому di( очистит скобки, а da( уберет их вовсе. Курсору достаточно быть где-то внутри скобок. При этом вложенные скобки обрабатываются корректно.
В целом, текстовые объекты достаточно интуитивны: скобки четырех видов ([, {, (, <, причем можно использовать хоть открывающую, хоть закрывающую), кавычки трех видов (`,',"), слово w и W, предложение s, абзац p.
Давайте создадим свои объекты. Для этого надо создать привязки для двух режимов: визуального и специального режима "ожидания оператора", который как раз для этого и нужен.
Объект "строка", il и al
Конечно, строку мы можем легко выделить командой V, скопировать yy и так далее, но, во-первых, вместе с символом конца строки, что не всегда желательно, а во-вторых, вместе с пробелами в начале и в конце строки, а зачем они. Создадим две привязки:
xnoremap <silent> il :<c-u>normal! g_v^<cr>
onoremap <silent> il :<c-u>normal! g_v^<cr>
Что тут происходит? Ключ <silent> не показывает выполняемые команды.
Двоеточие включает режим командной строки. Сочетание <C-U> очищает ее, убирая подставленный туда диапазон '<,'> - это метки начала и конца выделения, которые нам как раз не нужны. Команда :normal! выполняет команду нормального режима, а восклицательный знак отключает привязки (которые могли бы повредить). Выполняемая команда g_v^ состоит из трех: g_ прыгает на последний непробельный символ строки, v включает режим выделения, ^ прыгает назад на первый непробельный символ.
Всё. Теперь vil, dil, yil и cil, не говоря уж о gUil и прочем - к вашим услугам.
Для строки с пробелами в начале и в конце, но без символа конца строки, логично создать объект al. Например, так:
xnoremap <silent> al :<c-u>normal! $v0<cr>
onoremap <silent> al :<c-u>normal! $v0<cr>
Отличие только в командах перемещения по строке: $ вместо g_и 0 вместо ^.
Объект "документ", id и ad
Иногда надо выделить весь документ: ggvG. Несложно, но почему бы не создать специальный объект, если вам надо делать это часто. Это тоже легко:
xnoremap <silent> id :<c-u>normal! G$Vgg0<cr>
onoremap <silent> id :<c-u>normal! GVgg<cr>
Команды G и $ перемещают курсор на последнюю строку и ее конец, а gg и 0 - на первую строку и ее начало, а V включает построчное выделение. В общем-то, в 0 и $ нужды нет.
Это предлагают коллеги в материале, который я использую. Я бы называл эти команды ad, так как это весь документ: логично использовать id для всего документа за исключением пустых строк в начале и в конце. Это чуть сложнее, но делается:
xnoremap id <esc>gg/\S<cr>V/\_s*\%$<cr>
onoremap id gg/\S<cr>V/\_s*\%$<cr>
Мы задействуем поиск, чтоб найти первый непробельный символ (\S) и потом серию пробелов (включая табы и концы строк, \_s) до конца файла \%$.
Текстовые объекты могут быть сколь угодно сложны. В правую часть привязки можно поместить функцию и делать в ней всё, что считаете нужным. Вот пример:
Объект "число": in и an
Мы хотим, чтобы по команде выделялось целое число: двоичное, шестнадцатеричное или десятичное. Поскольку процесс довольно сложный, упакуем его в функцию. Даже в две: одна будет искать число, а вторая - число вместе с пробелами вокруг него.
" регулярное выражение, совпадающее с числом, точнее, заготовка.
" Порядок важен, \d должно идти последним.
let s:regNums = [ '0b[01]', '0x\x', '\d' ]
function! s:inNumber()
let l:magic = &magic " включаем магию
set magic
let l:lineNr = line('.') "берем номер строки под курсором
let l:pat = join(s:regNums, '\+\|') . '\+' "создаем регулярку
if (!search(l:pat, 'ce', l:lineNr))
return "если не нашли, то числа нет, уходим
endif
normal! v "начинаем выделение от конца числа
call search(l:pat, 'cb', l:lineNr) "перемещаемся на начало числа
let &magic = l:magic "возвращаем настройки магии
endfunction
xnoremap <silent> in :<c-u>call <sid>inNumber()<cr>
onoremap <silent> in :<c-u>call <sid>inNumber()<cr>
Во второй функции всё похоже, только прибираем ещё и пробелы до и после числа:
function! s:aroundNumber()
let l:magic = &magic
set magic
let l:lineNr = line('.')
let l:pat = join(s:regNums, '\+\|') . '\+'
if (!search(l:pat, 'ce', l:lineNr))
return
endif
call search('\%'.(virtcol('.')+1).'v\s*', 'ce', l:lineNr) "забираем все пробелы после числа
normal! v
call search(l:pat, 'cb', l:lineNr)
call search('\s*\%'.virtcol('.').'v', 'b', l:lineNr) "пробелы до.
let &magic = l:magic
endfunction
xnoremap <silent> an :<c-u>call <sid>aroundNumber()<cr>
onoremap <silent> an :<c-u>call <sid>aroundNumber()<cr>
Объект "текст с отступом" (ii и ai)
Эти объекты выделяют текст с одинаковым отступом: это полезно для кода на Питоне или если вы придерживаетесь хорошего стиля программирования на других языках. Разница между i и a та же, что и везде: i забирает только текст без пустых строк до и после, а a прихватывает и пустые строки.
Авторы оригинального текста предупреждают, что питонисты подсаживаются на эту разработку!
Я не буду копировать текст функций, отсылая читателя к оригиналу. Код хорошо откомментирован, если захочется разобраться, но можно просто скопировать в .vimrc и пользоваться. Автор заверяет, что работает очень быстро.
Удачи, коллеги!