Найти в Дзене
Блокнот математика

Рубрика "Секреты Вим". Скриптовый язык: встроенные функции

Привет, коллеги. Познакомимся с библиотекой функций Вим! Их можно использовать в скриптах, простых или сложных, или просто в одноразовых командах. Например, :echo pow(1.1, 10) или @a=printf('%f', tanh(0.42)) и потом можно вставить это в текст: "ap. Еще есть регистр выражений =, и можно вставлять через него в текст любые выражения, в том числе с любыми функциями. О нем в другой раз подробнее расскажу.

Создание своих функций и вызов мы уже обсуждали. Собственно, краткий список и полное описание библиотеки функций Вим вы найдете в Справке: help functions. Поэтому я не буду переписывать всё, а отмечу более важные и дам обзор.

Вот так я ее себе представляю. Хотя это больше Perl, в Вим все-таки функций радикально меньше. Он ценен именно доступом к тексту.
Вот так я ее себе представляю. Хотя это больше Perl, в Вим все-таки функций радикально меньше. Он ценен именно доступом к тексту.

Язык Вим имеет много функций, многие из которых стандартны и есть в любом языке программирования. Там есть элементарные математические функции, включая:

  • тригонометрию (sin, cos, tan, asin, acos, atan, atan2);
  • гиперболические (sinh, cosh, tanh);
  • степень pow и логарифмы (log, log10);
  • функции округления (ceil, floor, round, float2nr)
  • битовые функции (or, xor, and, invert)
  • функция isnan, проверяющая, не является ли аргумент значением Not-A-Number.
Для конвертации строк в вещественные числа есть функция str2float(), для обратной конвертации printf(), и float2nr() для отсечения дробной части. Есть также round, floor, ceil для округлений.
Есть функции string и eval: первая превращает объект в строку, вторая преобразует обратно.

Функции для списков и словарей мы рассмотрели, обсуждая списки и словари.

Для работы с текстовыми переменными набор богатый, но стандартный: смена регистра (tolower, toupper), выделение подстроки (index), сравнение с регулярным выражением (match и функции, которые начинаются с match), длина строки в символах и в байтах (strchars, len), перекодировка (iconv), замена символов одного множества на символы другого (tr) и всё в таком роде. Интересные функции strwidth и ей подобные, которые определяют ширину строки на экране.

Нас больше интересуют функции, имеющие доступ к Вим изнутри. Например, col и virtcol, определяющие номер столбца/символа под курсором или по метке. Обратите внимание, что русские буквы занимают два столбца (байта), так что если от начала строки написано слово из трех букв и курсор на последней букве, то echo col('.') выдаст 5. А virtcol выдаст 3.

Функция line определяет номер строки, на которой курсор или метка. Аргументом является строка, в которой может быть:

  • . — позиция курсора,
  • $ — последняя строка буфера
  • 'x — метка x (если метки нет, вернется нуль)
  • w0 или w$ — первая/последняя строка, видимая на экране.
  • v — В визуальном режиме соответствует началу выделения, в остальных случаях позиция курсора.

Функции winline и wincol определяют позицию курсора относительно окна. А cursor поместит курсор в заданную позицию (помните только, что русские буквы "широкие"). Функция screenchar снимет символ под курсором. А функция byte2line определит строку, на которой данный по счету байт (не символ!).

К самом тексту в буфере доступ тоже есть. Функция getline добудет строку или список строк, а setline заменит строку. Функция append вставит новую строку ниже указанной по номеру.

Можно сделать отступ (indent(), cindent(), lispindent()). Найти непустую строку (nextnonblank(), prevnonblank()). Найти текст по регулярному выражению (search() и другие функции), найти объявление (searchdecl()) или пару (searchpair).

Функции дают доступ ко всем объектам Вим: вкладкам, окнам, командной строке, привязкам, проверке орфографии, истории команд, фолдам, автодополнению, QuickFix-у, регистрам, тэгам. Есть интерактивные, взаимодействующие с пользователем.

Есть полный список файловых операций, включая поиск, переименование, запрос прав и размера, выполнение команд оболочки (system и systemlist), чтение и запись файлов, и т.п.

Есть функции для даты и времени, включая время последнего изменения файла. Правда, для работы с датой и временем функций явно недостаточно.

Функция mode() определяет, как сейчас режим. А wordcount() считает байты/слова/символы в буфере.

Немного примеров. Первый из книги Learing the vi and Vim Editors, 7ed, Robbins et al, 2008. Переключает цветовую схему в зависимости от времени суток. В книге алгоритм последовательно развивается, но мы возьмем последний вариант:

let g:Favcolorschemes = ["darkblue", "morning", "shine", "evening"]
let g:colors_name = "none"
function SetTimeOfDayColors()
let g:CurrentHour = (strftime("%H") + 0) / 6
if g:colors_name !~ g:Favcolorschemes[g:CurrentHour]
execute "colorscheme " . g:Favcolorschemes[g:CurrentHour]
echo "execute " "colorscheme " . g:Favcolorschemes[g:CurrentHour]
redraw
endif
endfunction
set statusline += \ %{SetTimeOfDayColors()}

Разберемся? Сначала определён список из четырех имен цветовых схем. И задано какое-то значние глобалной переменной. Затем определена функция без параметров. Последняя строчка модифицирует строку информации, добавляя поле. В этом поле ничего не будет, но функция вычислится. А строка эта обновляется часто, что позволяет нам не заботится о вызове функции как-то иначе.

Сама функция вычисляет глобальную переменную g:CurrentHour, которая равна текущему часу, деленному нацело на шесть. Получается четверть суток: нулевая, до шести утра; первая, до полудня; вторая, до шести вечера; и третья, до полуночи. Час возвращает встроенная функция strftime с параметром "%H". Можно много чего указать, получив время или дату в любом желаемом виде.

Далее мы сравниваем переменную g:colors_name с соответствующим элементом списка. Если совпадает, ничего менять не надо, а если нет, то переключаем цветовую схему и докладываем об этом. Ну и всё.

А вот мой пример:

function! Minus(d1,d2)
let s1=system('date -d ' . a:d1 . ' +%s')
let s2=system('date -d ' . a:d2 . ' +%s')
return (s2-s1)/86400
endfunction
function! Plus(d1,d2)
let s1=system('date -d ' . a:d1 . ' +%s')
return strftime('%d %b %Y', s1 + a:d2*86400)
endfunction

Это арифметика дат: первая функция вычитает из одной даты другую, возвращает число дней между. Например,

echo Minus('01dec2020', '31dec2021')

даст 395 дней.

UPD: функция Minus вычисляет разность дат в днях, то есть между 01dec2022 и 02dec2022 один день, а между датой и ею самой ноль дней. Это вычитание, обратная операция к сложению (вторая функция). Иногда бывает нужно знать число дней между двумя датами, которое на 1 больше разности. Так, с 01dec2022 по 02dec2022 - это два дня. Функцию Between сделать несложно.

Вторая позволяет прибавить к дате целое число дней:

echo Plus('19dec2021', 42)
echo Plus('31dec2021', 1)

дадут '30 янв 2022' и 01 янв 2022, соответственно.

Мы используем функцию system, системную утилиту date, а также функцию strftime (хотя могли бы обойтись той же системной утилитой).

Вот еще один пример, канонический. Он есть в Справке Вим, он есть и в упомянутой выше книге Роббинса и др:

autocmd BufWritePre,FileWritePre *.html mark s|call LastMod()|'s
function LastMod()
if line("$") > 20
let lastModifiedline = 20
else
let lastModifiedline = line("$")
endif
exe "1," . lastModifiedline . "g/Last modified: /s/Last modified:.*/Last modified: " .\ strftime("%Y %b %d")
endfunс

Здесь мы определили автокоманду для html-файлов, которая срабатывает при попытке записи файла, но до записи. Действия автокоманды состоят в том, чтобы пометить позицию командой mark (закладка будет под буквой s), вызвать функцию и вернуться к метке-закладке обычной командой `s.

Что же делает функция? Она сначала определяет число строк в файле, запросив номер последней строки (line('$')). Если строк больше двадцати, проверять будем первые двадцать только, а если нет, то весь файл.

Далее команда execute выполняет команду командной строки (уф). В начале 1,n, где n - значение переменной: то есть это диапазон, в котором будем действовать. Оператор "точка" клеит выражения одно к другому. Далее идет команда g, фильтр строк: он отсеивает все строки, кроме той, что содержит текст "Last modified:". На этой строке осуществляется замена этого текста с датой после (точнее, чем угодно после!) на ту же фразу плюс текущую дату (см. функцию strftime).

Таким образом, при каждой записи файла мы будем автоматически обновлять строку "Last modified" на актуальную дату! Строка должна быть где-то в начале файла.

Я думаю, хватит примеров; остальным посвятим другие заметки.

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

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