О Большом Текстовом Файле проговорился совершенно случайно, зато теперь ни разу не жалею. Очень благодарен человекам (сэру
ollycat, без вопросов которого я бы не взялся за эти заметки - в особенности), за их мнения и примечания. Почему-то всегда лучше работается, когда есть с кем обсудить эту работу. Вопрос с тэгами, поднятый
d7s, я решил вчера благодаря паре часов свободного времени и прочищенным свежим воздухом и бадминтоном мозгам :) Заодно, к своему удовольствию разобрался со встроенным в Vim скриптовым языком, до которого ни как не доходили руки.
Чего мне хотелось? Меню, которое бы выводило список тэгов и проставляло бы их в тексте автоматом. Тэг напомню выглядит как отмеченное крылышками =слово=, с завершающим тэгом ==. Некоторое время я обдумывал где хранить такое добро, перепробовал несколько разных вариантов. Потом понял, что в принципе хватит отдельного файлика tag.txt и некоторой "особой виммерской магии". Еще час вдумчивого перелистывания мануала и родился такой вот
скрипт.
" автодополнение по табу ---------------------------------------------------
function Snipping_close()
execute "q"
endfunction
function Snipping()
if bufname("%") == "tag.txt"
if strpart(getline('.'), 0, 1) != '='
normal yy
execute "q"
normal p
normal $
else
normal yy
execute "q"
normal p
normal o==
normal O
endif
else
execute "vsplit tag.txt"
map :call Snipping()
map :call Snipping_close()
endif
endfunction
map :call Snipping()
" автодополнение по табу ---------------------------------------------------
Который был добавлен в vimrc. В принципе еще еще хорошо немного допилить, но сейчас он вполне функционален.
* Как это работает с точки зрения пользователя?
Исходная позиция:
1. По Ctrl+Tab открывается боковое окно-меню в котором появляется список тэгов. Если пользователь передумал что-то вставлять - он нажимает Ctrl+Tab еще раз и окошко просто закрывается.
2. Пользователь становится курсором на нужный тэг и жмет Enter.
3. Боковое окно-меню закрывается и в текст добавляется нужный тэг.
4. Если тэг начинается крылышками - он добавляется в текст в виде:
=тэг=
==
5. Если тэг не начинается крылышками - он добавляется в текст "как есть". Предусмотрел это, потому, что оказалось что очень удобно так копировать в текст заготовки для заголовков и прочих спецзнаков.
* Как это работает с точки зрения алгоритма?
map ... завязывает соответствующий хоткей на функцию Snipping(). Дальше я все рассматриваю "задом наперед" - потому, что этого требовала логика программы. Сначала надо было объявить функцию, потом завязывать ее на хоткей.
При вызове функция Snipping() получает название буфера (функция bufname("%") - возвращает название текущего открытого файла - %) и исходя их него проверяет где был нажат хоткей - в основном окне или в окошке-меню (в этом случае название текущего файла будет "tag.txt").
// буде этот текст попадет в руки начинающему виммеру - напомню, что "буферами" по терминологии Vim называются открытые в данный момент внутри редактора файлы. Не путать с буфером для копипаста, который в Vim'е называется "регистр". Меня это некоторое время сбивало с толку, но в принципе своя логика в такой терминологии есть.
Если это не файл "tag.txt" - выполняется ветка else - execute "vsplit tag.txt" эквивалентно :vsplit tag.txt - то есть делается вертикальный сплит и в нем открывается файл с тэгами.
Дальше идет интересный нюанс. Если в синтаксисе map указан ключ - то привязка хоткеев к действию будет распространятся только на текущий буффер. То есть следующие две строки меняют правила игры для открытого tag.txt и только внутри него.
map :call Snipping() - переназначает Enter на вызов функции Snipping() (то есть теперь Enter будет работать не как "Ввод", а как вызов Snipping())
map :call Snipping_close() - переназначает сочетание Ctrl+Tab на вызов функции Snipping_close() Сама эта функция, раз уж я ее упомянул, просто закрывает tag.txt - execute "q" эквивалентно :q
Еще раз напомню - обе эти привязки действуют только внутри tag.txt и как только мы его покинем все вернется на круги своя - Enter снова станет "Вводом".
Если имя файла, в котором была вызвана функция Snipping() - "tag.txt" - переходим ко второй ветке алгоритма. Следует скопировать тэг в основной текст и закрыть окно.
Сначала проверяется, что это за тэг был выбран - начинается он на '=' или нет.
strpart({src}, {start}[, {len}]) возвращает кусочек строки src, начинающийся с символа (байта) номер start (первый символ строки имеет номер 0) и длиной в len.
getline('.') возвращает текущую ('.') строку.
Функция strpart(getline('.'), 0, 1) берет первый символ строки тэга, а if проверяет равен ли он "=" (в строке - крылатый тэг или что-то другое).
Если тэг не крылатый - он просто копируется (normal yy - это просто - yy основного режима - копирование строки), окно закрывается (execute "q" - :q), тэг вставляется в основной файл и курсор передвигается в конец строки тэга (чтобы сразу можно было начинать писать).
Если тэг крылатый - он копируется (normal yy - yy копирование строки), окно закрывается (execute "q" - :q), тэг вставляется в основной файл ("p"), затем вставляется новая строка (o), в нее вписывается завершающий тэг "==", создается новая строка ("O") между тэгом и его завершением и - можно писать.
Получается очень удобно. Так можно копировать не только тэги, но и примитивы для заголовков (тильды разного количества), выделений и так далее (на скриншоте хорошо заметны отдельные элементы в самом верху). Что удобно - tag.txt можно редактировать вручную "не отходя от кассы" или использовать предложенный сэром
ollycat метод автоматического сбора тэгов.
В принципе это дело можно доработать так, чтобы при выделенном тексте этот текст обрамлялся нужным тегом (дополнить скрипт условием проверки и в случае чего - копированием нужно фрагмента в регистр) можно изменить порядок вызова функций, чтобы он был более прозрачным и так далее.
Так же я задумался о том, как быть с добавлением тэга к дате заметки. Точнее так. Я вижу несколько путей работы - первый это глобальный скрипт на питоне, который каждый раз сканирует файл, находит между датами теги и сам их цепляет к датам. Второй - локальный скрипт, который будет работать примерно так - после вставки тэга стать курсором на == и запусть скрипт по хоткею. Скрипт поднимется выше, найдет название тега, скопирует его, поднимется еще выше, найдет первую строку, начинающуюся с "^д 200" и допишет тег к ней в хвост. Возможно, я реализую оба момента.
Но это еще "повод на подумать", поскольку я пока не решил для себя окончательно как именно должны выглядеть тэги в тексте (допустим =нот= ... == удобно обрабатывать, зато =нот= ... =нот= выглядит эстетичнее и легче читается). Еще - как быть с несколькими тегами? А еще мне очень удобно писать заголовки к тегам. Примерно так:
=нот= мысли о блоге
...
==
Думаю, все это устаканится со временем. Точнее так - практика расставит все на свои места.
P.S. Упреждая возможные вопросы. В Vim есть множество хороших плагинов для добавления сниппетов в текст. Но мне хотелось чего-нибудь своего и простого, чтобы не перегружать Vim. Кстати, из плагинов пользую - аутлайнер (
TVO) и
Project - аффигительно удобные штуки.