POWERMAN
"In each of us sleeps a genius...
and his sleep gets deeper everyday."

Особенности синтаксиса Vim script относительно обычных скриптовых языков вроде Perl, Python или Bash.

Запуск команд и вывод результатов

Note В следующих трёх примерах команды начинаются на «:» и заканчиваются «<Enter>» чтобы показать, что нужно нажимать. Следующие примеры будут записаны так, как пишется код в ~/.vimrc и *.vim файлах, без «:» и «<Enter>».

echo выводит свои параметры через пробел:

:let i = 5<Enter>
:echo 'a   b' i &textwidth getcwd()<Enter>

Группу команд можно записать в одну строку через "|" (но это работает не для всех команд, и для части тех где работает нельзя перед "|" ставить лишние пробелы, как сделано для наглядности в этом примере):

:let i=10 | echo 'i='.i<Enter>

Выполнить многострочный vim script скопированный в буфер (строчки не должны начинаться на ":", и иногда глючит если строки начинаются с пробелов - можно обойти если выделять/копировать вертикальный блок без начальных пробелов):

:@"<Enter>

Каждая строчка vim script должна начинаться на команду vim. Имена переменных и функций командами не являются!

" НЕ ПРАВИЛЬНО
i++
func(i)
" ПРАВИЛЬНО
let i += 1
call func(i)

Переменные

let выводит текущие определённые переменные.

let переменная = значение создаёт переменные и изменяет их значения.

unlet переменная удаляет переменные (unlet! переменная не ругается на несуществующие), можно указывать несколько переменных сразу.

Области видимости переменных определяются префиксом в имени:
var     глобальная, внутри функции локальная
g:var   глобальная (идентично var вне функций)
l:var   локально для текущей функции (идентично var внутри функции)
a:var   аргумент текущей функции
s:var   локально для текущего скрипта
b:var   локально для текущего буфера
w:var   локально для текущего окна
t:var   локально для текущего таба
v:var   переменные самого Vim
Другие группы переменных:
$VAR    переменная окружения
&opt    опция vim
@r      регистр vim
Типы переменных:
Number          10, 0.1
String          'str', "str"
List            [], [1,'str']
Dictionary      {}, {'key1': 1, "key2": 'str'}
Funcref         function('FuncName')
Проверка на существование переменной:
if exists('s:var')
    echo s:var
endif

Истина/ложь

Истина - всё кроме 0.

При логической проверке строки конвертируются в числа, так что строка 'true' вычисляется как 0, т.е. ложь.

Строки

В одинарных кавычках все символы означают сами себя, кроме '' - это одинарная кавычка. Это особенно удобно для записи регулярных выражений.

В двойных кавычках экранирование слешем, работают стандартные \", \\, \n, \x20, etc. плюс кнопки вроде \<Esc> или \<C-W>.

Строки конкатенируются оператором . (точка).

Строки со строками сравниваются по байтам.

При сравнении строки с числом она конвертируется в число.

Эта проверка вернёт истину:
if 0 == 'one'
    echo 'Сюрприз!'
endif

При сравнении строк и применении регулярного выражения будет ли учтён регистр определяется опцией &ignorecase. Для форсирования регистра можно к операции сравнения добавить # для проверки регистра или ? для игнорирования регистра.

echo str =~# 'regex'
echo str ==? 'string'

Списки, хеши и объекты

Списки объявляются как ["val1","val2"].

Работа с элементом: list[0].

Списки конкатенируются оператором + (плюс) или функцией extend().

let alist = ["one","two",3]
for i in alist
    echo i
endfor

Хеши объявляются как {"key1": "value1", "key2": "value2"}.

Работа с элементом: hash["key1"] или hash.key1.

let ahash = {"john":10,"alex":20}
for k in sort(keys(ahash))
    echo k
endfor

Значениями ключей хеша могут быть функции, внутри функции доступна переменная self:

function ahash.showalex() dict
    echo self.alex
endfunction

ООП - хеш "объект" это просто копия хеша "класса":

let class = {"property": default_value}
function class.method() dict
    echo self.property
endfunction

let obj = copy(class)
call obj.method()

let subclass = copy(class)
function! subclass.method() dict
    echo self.property + 1
endfunction

Операторы

if COND
elseif COND
else
endif

while COND
    continue
    break
endwhile

for VAR in LIST
endfor

try
catch
finally
endtry

try
catch /REGEX/
catch /REGEX/
finally
endtry

eval

execute 'echo ' . varname
echo {varname}

normal  1Gjj
execute 'normal ' . cmd_keys
execute 'normal i' . insert_keys . "\<Esc>"

let optval = eval('&' . optname)
execute 'let optval = &' . optname

Функции

function без параметров выводит все определённые функции.

function ИмяФункции выводит код этой функции.

function ИмяФункции(параметры) создаёт функцию (для переопределения функции её нужно объявлять через function!).

delfunction ИмяФункции удаляет функцию.

Имена должны начинаться с большой буквы (чтобы не путаться с встроенными функциями).

По умолчанию (без return) функция возвращают 0.

Примеры
function Func(param1, param2)
    return a:param1 + a:param2
endfunction

function HandleRange(param1) range
    " при вызове а-ля: 10,20call HandleRange("some")
    " так же получит a:firstline и a:lastfile
    " (обычная функция будет вызвана по разу на каждую строку)
endfunction

function VariableParams(param1, ...)
    " кол-во аргументов в a:0, сами аргументы начиная с a:1,...
endfunction

В выражениях вызываются обычно, а вне выражений - через call.

if func1()
    let var = func2()
    call func3()
endif

function("ИмяФункции") возвращает ссылку на функцию (которую нужно сохранять в переменную с именем начинающимся на большую букву).

let MyFunc = function("MyRealFunc")
echo MyFunc(param)
call MyFunc(param)
echo call(MyFunc, [param]);
call call(MyFunc, [param]);
echo call("MyRealFunc", [param]);
call call("MyRealFunc", [param]);

Скрипты/плагины

Области видимости функций определяются префиксом в имени:
Func    глобальная
s:Func  локально для текущего скрипта

Локальные функции нужно map-ить внутри скрипта для вызова снаружи используя <SID> (который фактически добавит к имени уникальный префикс):

" НЕ ПРАВИЛЬНО (при вызове снаружи скрипта s:Func() не известен)
nmap <unique>                   <Leader>a           :call s:Func()<CR>
" ПРАВИЛЬНО
nmap <unique>                   <Leader>a           :call <SID>Func()<CR>

Недостаток этого подхода в том, что снаружи скрипта <SID> не работает, так что пользователь не сможет переназначить map. Это обходится либо объявлением вызываемых снаружи функций как глобальных (но тогда желательно включать в имя функции префиксом имя плагина, для избежания конфликтов), либо созданием глобальной "виртуальной кнопки" с именем <Plug>ПлагинФункция - на эту кнопку юзер сможет назначать свои map снаружи скрипта:

""" у пользователя в ~/.vimrc:
nmap                            <Leader>z           <Plug>MypluginFunc
""" внутри плагина:
if !hasmapto('<Plug>MypluginFunc')
    nmap <unique>               <Leader>a           <Plug>MypluginFunc
endif
noremap <unique>                <Plug>MypluginFunc  :call <SID>Func()<CR>
" либо, если точек входа несколько, то удобнее ввести промежуточный map
noremap <unique> <script>       <Plug>MypluginFunc  <SID>Func
noremenu <script>               Myplugin.Func       <SID>Func
noremap                         <SID>Func           :call <SID>Func()<CR>

В ftplugin/ (FileType-плагинах) нужно не забывать использовать локальные версии команд:

let s:var = 'значение общее для всех буферов этого типа'
let b:var = 'значение локальное для этого буфера'
setlocal ...
map <buffer> ... <LocalLeader>...
command -buffer ...

Автозагрузка: если имя функции содержит #, например Myplugin#Func, то не найдя эту функцию Vim попытается подгрузить файл autoload/Myplugin.vim.