| ч38. словарь | Содержание | ч40 Функциональное программирование |
Ранее мы говорили о том, как задавать параметры в Vim. Для логических параметров мы можем использовать set someoption! для "переключения" состояния. Это особенно удобно, когда мы создаем сопоставление для этой команды.
Выполните следующую команду:
:nnoremap <leader>N :setlocal number!<cr>
Испытайте наше новое сочетание <leader>N в обычном режиме. Vim отключит и включит номера строк для текущего окна. Создание "переключающего" сопоставления, подобного этому, действительно удобно, потому что нам не нужно иметь две отдельные клавиши, чтобы что-то выключить или включить.
К сожалению, это работает только для логических параметров. Если мы захотим переключить не булевый параметр, нам нужно будет проделать немного больше работы.
Переключение параметров
Давайте создадим функцию, которая будет переключать параметр, и сопоставление которое его вызовет. Поместите следующий код в свой ~/.vimrc (или отдельный файл как вы это уже умеете делать, ~/.vim/plugin/)
nnoremap <leader>f :call FoldColumnToggle()<cr>
function! FoldColumnToggle()
echom &foldcolumn
endfunction
Сохраните и запустите исходный файл (:w | source %) , затем испытайте его, нажав [<leader>f ] Vim , отобразит текущее значение параметра foldcolumn . Если вы не знаете что этот параметр делает, добро пожаловать в :help foldcolumn.
Давайте добавим функциональность - переключатель. Отредактируйте код, чтобы он выглядел следующим образом:
nnoremap <leader>f :call FoldColumnToggle()<cr>
function! FoldColumnToggle()
if &foldcolumn
setlocal foldcolumn=0
else
setlocal foldcolumn=4
endif
endfunction
Сохраните и запустите наш код, попробуйте его. Каждый раз, когда вы нажимаете [<leader>f ], Vim будет либо показывать, либо скрывать столбец для складки.
Оператор [ if ] просто проверяет, [ &foldcolumn ] является ли он истинным или ложным. (помните, что Vim обрабатывает число 0 как ложное, а любое другое число - как истинное).
Если [ &foldcolumn ] не равен нулю, то он устанавливает его значение равным нулю (что скрывает его). В противном случае он устанавливает его равным четырем. Довольно просто.
Вы можете использовать подобную функцию, для переключения любого параметра, где 0 означает "выкл.", а любое другое число - "вкл."
Переключение других вещей
Параметры - это не единственное, что мы можем переключить. Еще одна особенно полезная вещь, для которой мы можем создать сопоставление, - это окно quickfix. Давайте начнем с того же скелета, что и раньше. Добавьте следующий код в свой файл:
nnoremap <leader>q :call QuickfixToggle()<cr>
function! QuickfixToggle()
return
endfunction
Это сопоставление пока ничего не делает. Давайте превратим его во что-то более полезное (но еще не полностью законченное). Измените код, чтобы он выглядел следующим образом:
nnoremap <leader>q :call QuickfixToggle()<cr>
function! QuickfixToggle()
copen
endfunction
Сохраните и запустите этот исходный файл. Если вы попробуете сопоставление сейчас, то вы увидите, что оно просто открывает окно quickfix.
Чтобы найти "переключающее" поведение, мы будем использовать быстрое и не удачное решение: глобальную переменную. Измените код, чтобы он выглядел следующим образом:
nnoremap <leader>q :call QuickfixToggle()<cr>
function! QuickfixToggle()
if g:quickfix_is_open
cclose
let g:quickfix_is_open = 0
else
copen
let g:quickfix_is_open = 1
endif
endfunction
То, что мы сделали, довольно просто - мы просто сохраняем глобальную переменную , описывающую открытое / закрытое состояние окна quickfix всякий раз, когда мы вызываем функцию.
Сохраните и запустите этот исходный файл и попробуйте наше сопоставление. Vim будет жаловаться, что переменная не определена! Давайте исправим это, инициализировав его один раз:
nnoremap <leader>q :call QuickfixToggle()<cr>
let g:quickfix_is_open = 0
function! QuickfixToggle()
if g:quickfix_is_open
cclose
let g:quickfix_is_open = 0
else
copen
let g:quickfix_is_open = 1
endif
endfunction
Сохраните и запустите этот исходный файл и попробуйте наше сопоставление. Это работает.
Улучшения
Наша функция переключения работает, но имеет несколько проблем.
Во-первых, если пользователь вручную открывает или закрывает окно с помощью [ :copen ] или [ :cclose ], наша глобальная переменная не обновляется. На практике это не является большой проблемой, потому что большую часть времени пользователь, вероятно, будет открывать окно с сопоставлением, а если нет, они всегда могут просто нажать это сочетание снова.
Это иллюстрирует важный момент в написании кода Vimscript: если вы попытаетесь обработать каждый отдельный случай, вы увязнете в них и никогда не сможете закончить работу.
Получить что-то, что работает большую часть времени (и не падает, когда оно не работает), как правило, лучше, чем тратить часы на то, чтобы оно было на 100% идеальным.
Исключение составляют случаи, когда вы пишете плагин, который, как вы ожидаете, будут использовать многие люди. В этом случае лучше потратить время и сделать его пуленепробиваемым, чтобы ваши пользователи были довольны и не спамили бы вас сообщениями об ошибках.
Восстановление буфера
Другая проблема с нашей функцией заключается в том, что если пользователь запускает сопоставление, когда он уже находятся в окне quickfix, при закрытии окна мы не возвращаемся туда откуда вызвали quickfix а переходим в то место которое мы смотрели. Это быстро начнет раздражать, особенно если вы просто хотели что то быстро посмотреть и вернуться назад к работе. Можно конечно использовать <ctrl - o> но таких шагов может быть очень много, лучше потом за один шаг вернуться назад, чем прыгать по десяткам документов. Давайте это исправим.
Чтобы решить эту проблему, мы введем идиому, которая очень пригодится при написании плагинов Vim. Отредактируйте свой код, чтобы он выглядел следующим образом:
nnoremap <leader>q :call QuickfixToggle()<cr>
let g:quickfix_is_open = 0
function! QuickfixToggle()
if g:quickfix_is_open
cclose
let g:quickfix_is_open = 0
execute "normal! :b " . g:quickfix_return_to_window . "\<cr>"
else
let g:quickfix_return_to_window = winbufnr(0)
copen
let g:quickfix_is_open = 1
endif
endfunction
Мы добавили в это сопоставление две новые строки. Одна из них устанавливает другую глобальную переменную (в else), которая перед запуском :copen сохраняет номер текущего буфера в текущем окне,
Вторая строка (в if ) указывает Vim в какой буфер нужно переключиться. В итоге эта строка развернется в [ :b 1 ] где 1 это номер буфера из которого мы открыли окно quickfix
Но сейчас наша функция QuickfixToggle() очень плохо работает с функцией GrepOperator () которую мы определили при работе с grep. Так как, она открывает окно quickfix не зная о том что мы сохраняем буфер из которого она была вызвана. Что бы это исправить замените строчку открытия окна copen на вызов нашей функции QuickfixToggle()
было:
silent execute "grep! -R " . shellescape(@@) . " ."
copen
let @@ = saved_unnamed_register
стало:
silent execute "grep! -R " . shellescape(@@) . " ."
call QuickfixToggle()
let @@ = saved_unnamed_register
Упражнения
Прочитать :help foldcolumn.
Прочитать :help winnr()
Прочитать :help copen
Прочитать :help winbufnr()
Прочитать :help ctrl-w_w.
Прочитать :help wincmd.
Используйте пространство имен для функций, добавляя s: и <SID>там, где это необходимо.
| ч38. словарь | Содержание | ч40 Функциональное программирование |