Найти тему
Блокнот математика

Рубрика "Секреты Вим". Создание операторов

Привет, коллеги. Мы уже обсуждали создание собственных команд, которые можно "повесить" на клавиши или последовательности командами семейства map. Можно отдельно создавать команды для разных режимов. Что важно: можно даже в режиме вставки использовать последовательности вроде ;; (редко встречается) или даже qp (может и встретиться, но ввести можно, если сделать паузу между буквами). Можно создавать составные команды, в которых несколько команд выполняются одна за другой: между ними стоит символ |.

Однако такие команды автономны: делают то, что в них прописано. У нас нет возможности указать им, с каким текстом работать. Часто этого довольно: например, команда работает на данной строке, или сама "забирает" текст каким-то движением, или вообще работают со всем текстом.

Можно, правда, выставить метки, и сделать команду, которая будет работать по этим меткам. Но это сложно.

Можно сделать команду командной строки и давать ей диапазон. Но как сделать команду нормального режима, работающую с движением?

Оператором называется команда, которая применяется к движению и работает с указанным текстом. Операторами являются команды y, d, c. А как создавать свои операторы?

Только у нас надо указать, куда именно пойти.
Только у нас надо указать, куда именно пойти.

Для этого есть простой способ. Есть универсальный оператор g@, который ожидает движение и вызывает некоторую функцию. Она-то и делает то, что вам будет угодно. В том числе и с указанным текстом, который движение определило.

Текст выделяется спецметками [ и ], и потом по ним можно до него добраться. Функция может иметь какое угодно имя (по правилам Вим, имя должно начинаться с заглавной буквы или с префикса) и принимать один параметр (названный тоже как вам будет угодно). Имя этой функции надо поместить в опцию opfunc. Можно это сделать один раз в .vimrc, а можно при привязке, чтобы каждая привязка включала свою функцию.

О функциях будет отдельный материал.

Параметр функции строковой, его значения указывают на тип движения: line для построчного, char для посимвольного, block для визуальных блоков (хотя оператор g@ в визуальном режиме не действует). Это позволяет по-разному обрабатывать текст в разных случаях, хотя это тонко.

Еще раз: текст, выбранный движением, отмечен метками [ и ], и до него можно добраться через них. Вот здесь и можно различать типы движения: если мы прыгнули от середины строки 1 о середины строки 3, то посимвольное и построчное движения суть разные вещи.

Вот пример, совсем несложный, но полезный. Это всё в .vimrc, ввести вручную с командной строки или в файл и выполнить его через :source:

function! RMspaces(type)
:'[,']:s/\s\{2,}/ /g
endfunction
:map ;; :set opfunc=RMspaces<CR>g@

Здесь мы игнорируем тип движения и все движения полагаем построчными. Функция вызывает команду замены двух и более пробелов на один. Но только на строках между метками, а метки для нас любезно ставит универсальный оператор.

Теперь мы можем легко убрать лишние пробелы на:

  • текущей строке: ;;$
  • до конца документа: ;;G
  • на пяти строках ниже: ;;4j
  • по поиску: ;;/section

Но вот выделить и применить нельзя. Случай выделения нужно отработать отдельно, создав привязку для этого режима через vmap и предусмотрев для функции вариант работы без параметров. В Справке есть пример. Или просто:

:vmap ;; :s/\s\{2,}/ /g<CR>

Удачи, коллеги!

Научно-популярные каналы на Дзене: путеводитель
Новости популярной науки12 марта 2022