Vim по полной: Менеджер плагинов без фатальных недостатков

    Оглавление


    1. Введение (vim_lib)
    2. Менеджер плагинов без фатальных недостатков (vim_lib, vim_plugmanager)
    3. Уровень проекта и файловая система (vim_prj, nerdtree)
    4. Snippets и шаблоны файлов (UltiSnips, vim_template)
    5. Компиляция и выполнение чего угодно (vim-quickrun)
    6. Работа с Git (vim_git)
    7. Деплой (vim_deploy)
    8. Тестирование с помощью xUnit (vim_unittest)
    9. Библиотека, на которой все держится (vim_lib)
    10. Другие полезные плагины

    Я пользовался, наверно, всеми популярными менеджерами плагинов для Vim и у меня не было ни малейшего желания писать свой собственный, так как эти меня вполне устраивали, но было небольшое но, о котором я расскажу в этой статье.


    Плохие Другие менеджеры


    Как работает менеджер плагинов для Vim? Все тривиально. Во время старта редактора считывается .vimrc в котором объявляются все используемые в данный момент плагины. Эти имена конкатенируются с адресом каталога, который должен хранить плагины и полученные строки добавляются в runtimepath — переменная Vim, которая определяет, откуда редактору брать стартовые скрипты, плагины и все остальное.

    Основной проблемой большинства популярных менеджеров плагинов для Vim является то, что они заполняют переменную runtimepath как попало. К чему это ведет? Плагины загружаются беспорядочно, нет возможности переопределить настройки плагина для конкретного проекта, плагины переопределяют ваши собственные настройки Vim и т.д. Все еще больше усугубляется невозможностью контролировать порядок загрузки плагинов, когда нужно сначала загрузить плагин A, а только затем зависимый от него благин B. Одним словом — боль.

    Что нам нужно в идеале


    Я уже говорил раньше и подробнее остановлюсь на этом вопросе в следующей статье, а сейчас только немного коснусь его. Предлагаемая мной структура добавляет еще один уровень инициализации редактора Vim. Это означает, что ваши Vim-конфигурации и плагины будут существовать на трех уровнях:
    1. Системном ($VIMRUNTIME/) — конфигурации и плагины, которые распространяются на всех пользователей системы
    2. Пользовательские (~/.vim/) — конфигурации и плагины конкретного пользователя
    3. Проектные (./.vim/) — конфигурации и плагины, доступные только в данном проекте. Именно этого уровня нет в Vim, точнее он есть, но реализован довольно слабо

    Это позволит, например, настроить шаблоны для Java классов, а затем переопределить их для конкретного проекта. Можно так же установить некоторый плагин только для определенного проекта, и этот плагин не будет подгружаться Vim в других проектах, что сэкономит память и увеличит производительность редактора (например вы решили начать работать с Lua и не хотите ставить плагины в ~/.vim/). Только представьте, один редактор на все случаи жизни и никаких лагов.

    Стандарты плагинов


    Библиотека vim_lib определяет как структуру плагинов Vim (но не ограничивает вас только этой структурой, может использоваться любой плагин), так и порядок их подключения и загрузки, для этого реализованы классы sys/Plugin и sys/Autoload соответственно.

    Если реализовать плагин в строгом соответствии с требованиями класса sys/Plugin, то мы получим следующую структуру:
    myPlugin/
        plugin/
            myPlugin.vim
        autoload/
            ...
            myPlugin.vim
        doc/
            myPlugin.rux
    

    Файл plugin/myPlugin.vim содержит логику инициализации и подключения плагина.
    Пример
    " Date Create: 2015-02-23 22:45:56
    " Last Change: 2015-06-07 18:21:15
    " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru)
    " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html)
    
    " Аналог use в VimLanguage
    let s:Plugin = vim_lib#sys#Plugin#
    let s:System = vim_lib#sys#System#.new()
    
    let s:p = s:Plugin.new('vim_write', '1') " Создание объекта плагина с указанием его имени и версии
    
    " Опции плагина
    "" {{{
    " @var bool Следует ли выполнять замену ключевых значений в файле перед сохранением.
    "" }}}
    let s:p.preplace = 1
    "" {{{
    " @var bool Включен ли механизм автосохранения изменений.
    "" }}}
    let s:p.aw = 0
    "" {{{
    " @var string|array Массив, содержащий имена типов файлов, для которых задействован механизм автосохранения изменений. Если опция установлена в значение 'all', механизм задействован для всех типов файлов.
    "" }}}
    let s:p.awTypes = 'all'
    if !exists('g:vim_write#replacement')
      "" {{{
      " @var hash Словарь замен, используемый перед записью буфера для замены ключевых слов. Словарь имеет следующую структуру: {типФайла: [regexp, ...]}. Тип 'all' используется для всех файлов.
      "" }}}
      let g:vim_write#replacement = {}
    endif
    
    " Функция инициализации плагина, в ней можно писать любую логику
    function! s:p.run() " {{{
      call s:System.au('BufWritePre,FileWritePre', function('vim_write#_writePre'))
      call s:System.au('CursorHold', function('vim_write#_autowrite'))
    endfunction " }}}
    
    " Объявление пунктов меню плагина. Они попадут в Plugins.имяПлагина.пунктМеню
    call s:p.menu('Aw_start', 'awstart', '1')
    call s:p.menu('Aw_stop', 'awstop', '2')
    
    " Объявление команд плагина
    call s:p.comm('VimWriteAwStart', 'awstart()')
    call s:p.comm('VimWriteAwStop', 'awstop()')
    
    call s:p.reg() " Регистрация плагина, которая делает его доступным в редакторе
    


    Файл autoload/myPlugin.vim содержит методы плагина. Опытные пользователи Vim сразу заметят, что основной код плагина будет подгружаться при первом его использовании, а не во время запуска редактора. Делается это через глобальный вызов методов плагина. На пример, при выполнении команды VimWriteAwStart из предыдущего примера, будет вызван метод имяПлагина#awstart().
    Пример
    " Date Create: 2015-02-23 22:48:37
    " Last Change: 2015-06-07 18:21:15
    " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru)
    " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html)
    
    " Аналог use в VimLanguage
    let s:Publisher = vim_lib#sys#Publisher#.new()
    let s:Content = vim_lib#sys#Content#.new()
    
    "" {{{                                                                                                                                                 
    " Метод активирует механизм автосохранение.
    "" }}}
    function! vim_write#awstart() " {{{
      let g:vim_write#.autowrite = 1
    endfunction " }}}
    
    ...
    


    Файл doc/myPlugin.rux содержит документацию к плагину.

    Зачем все это нужно? Во-первых стандартизация. Писать, изменять и разбираться в плагинах становится легче. Во-вторых их проще отключить.

    Подробнее можно почитать здесь.

    Автозагрузка


    Класс sys/Authoload библиотеки определяет порядок инициализации как самого редактора Vim, так и его плагинов. Инициализация выполняется в несколько этапов:
    1. Подключение основных файлов конфигурации редактора ($VIMRUNTIME)
    2. Подключение общесистемных плагинов ($VIMRUNTIME/plugin/)
    3. Подключение файло-зависимых конфигураций ($VIMRUNTIME/ftplugin/)
    4. Подключение основных файлов конфигурации пользователя (~/.vimrc)
    5. Подключение пользовательских плагинов (~/.vim/plugin/)
    6. Подключение файло-зависимых плагинов пользователя (~/.vim/ftplugin/)
    7. Подключение основных файлов конфигурации проекта (./.vimrc)
    8. Подключение проектных плагинов (./.vim/plugin/)
    9. Подключение файло-зависимых плагинов проекта (./.vim/ftplugin)

    Выполняется это с помощью уже знакомой нам переменной runtimepath, но учитывается порядок загрузки плагинов, их локализация (системные, пользовательские или проектные), а так же поддерживается механизм переопределения конфигураций как редактора, так и любых плагинов на каждом из уровней (проектный уровень самый приоритетный).

    Вообще класс sys/Authoload позволяет реализовать любую иерархию вложенности и приоритет конфигураций, а не только трехступенчатую, но я не увидел необходимости добавлять новые уровни.

    Для использования автозагрузки, предлагаемой классом sys/Authoload, достаточно добавить в ваш файл .vimrc следующие строки:
    filetype off 
    set rtp=~/.vim/bundle/vim_lib
    call vim_lib#sys#Autoload#init('~/.vim', 'bundle') " Адрес до вашего ~/.vim/bundle
    
    Plugin 'vim_lib'
    " Другие плагины
    
    filetype indent plugin on
    

    Сделать это можно не только на пользовательском уровне (~/.vimrc), но и на системном ($VIMRUNTIME/.vimrc) или проектном (./.vimrc), при этом логика автозагрузки будет распространятся только от данного уровня и «ниже». На всех низлежайших уровнях достаточно просто подключать новые плагины, доступные только этому уровню:
    filetype off 
    call vim_lib#sys#Autoload#init('.vim', 'bundle') " Подключение плагина на проектном уровне
    Plugin 'myPlugin'
    filetype indent plugin on
    

    Для отключения плагина, достаточно просто закоментировать строку Plugin 'имя'.

    Сконфигурировать плагин можно прямо во время его подключения:
    Plugin 'vim_deploy', {
          \  'options': {'adapter': 'shipit'}, " Переопределение опций плагина
          \  'map': {'deploy': '<Leader>d'}, " Переопределение горячих клавиш
          \}
    

    На более низких уровнях можно переопределить эти конфигурации:
    let g:vim_deploy#options = {'adapter': 'gradle'}
    

    Все очень гибко и удобно.

    Менеджер плагинов


    Казалось бы, что мешает заставить другие менеджеры плагинов устанавливать плагины в нужные нам каталоги (системный, пользовательский или проектный)? Проблема здесь в том, что другие менеджеры не просто устанавливают сторонние плагины, но и определяют порядок из инициализации, а нам это не нужно (уже реализовано классом sys/Authoload библиотеки). Та самая проблема несовместимости Vim плагинов, о которой я говорил в предыдущей статье. Пришлось писать свое решение.

    vim_plugmanager имеет довольно простой интерфейс и позволяет устанавливать плагины из GitHub в системный каталог, каталог пользователя или проектный каталог, в зависимости от того, где вы сейчас находитесь.
    Окно со списком плагинов


    Как видно на рисунке, vim_plugmanager реализован в виде окна, в котором он отображает список установленных на данный момент плагинов (именно установленных, а не подключенных) с группировкой по уровням. Для добавления нового плагина достаточно нажать клавишу a, а для удаления навести курсор на плагин и нажать dd (очень привычно, не так ли?).

    После установки нового плагина его необходимо подключить. Для этого, как уже было сказано выше, достаточно добавить в текущий .vimrc (текущего уровня) запись Plugin 'имяПлагина'. Нужно это потому, что установка плагина представляет собой копирование его в каталог с плагинами, а подключение добавляет его в vimruntime, после чего он может быть загружен редактором.

    Подробнее можно почитать здесь.

    Пока все


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

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

    Only registered users can participate in poll. Log in, please.

    Нужна ли менеджеру плагинов Vim функция автоматического разрешения зависимостей (реализовано)

    • 93.6%Да29
    • 3.2%Нет1
    • 3.2%Другое (отвечу в комментариях)1
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 31

      0
      Уважаю вас за то, что не бросили попытки подружиться с vim'ом, а подружились и даже стали его менять под себя. У меня есть предложение для вашей экосистемы — сделайте/адаптируйте существующий пакетный менеджер, так чтобы любой плагин по зависмостям вытягивал все необходимые для его работы плагины определенных версии. В программировании такие уже используются (composer, npm, bower и т.д.). Потом на основании зависимостей строилась последовательность загрузки плагинов.
        0
        Реализовать это не сложно, если только плагины будут включать некоторый стандартизированный файл зависимостей (как composer.json, package.json и т.д.). Сейчас все глагины, которые реализованы с использованием sys/Plugin, при их объявлении включают имя, зависимости и версии, сделано это как раз для разрешения зависимостей в будущем.
        Пример
        let s:p = s:Plugin.new('vim_unittest_phpunit', '1', {'plugins': ['vim_unittest']}) " Имя, версия, список зависимостей
        


        Думаю, когда количество поддерживаемых плагинов увеличится, я реализую этот механизм.
          0
          Да, без стандартизированного файла зависимостей ничего не выйдет, но я думаю если взять все, что подходит для сферы плагинов vim'а из composer.json, package.json и т.д., то можно с минимумом усилий по проектированию получить работающий менеджер пакетов.

          Думаю, когда количество поддерживаемых плагинов увеличится, я реализую этот механизм.

          Я считаю это замкнутый круг, если хотите чтобы вокруг вашей экосистемы зародилось сообщество, которое стало бы создавать/переделывать плагины под ваш менеджер пакетов, нужно начать именно с создания менеджера пакетов. А сообщество единомышленников придаст вам уверенности и скорости в развитии вашего начинания.
            0
            Я не уверен, что пользователям Vim сейчас больше всего не хватает функции разрешения зависимостей. Добавлю опрос, поглядим на результат.
              0
              У меня просто душа радуется, когда я могу зайти в проект и набрать:
              composer install
              bower install
              npm install
              


              А не делать это все в ручную
                0
                Сказано — сделано. Реализовал простенький механизм автоматического разрешения зависимостей. Теперь, если в плагине будет присутствовать файл plugman.vim с перечислением зависимостей:
                Пример
                {
                  'name': 'vim_unittest_phpunit',
                  'requires': {
                    'Bashka/vim_unittest': '1'
                  }
                }
                


                то при установке такого плагина менеджер будет пытаться установить и все его зависимости с учетом порядка загрузки. Пока все очень сыро, быду дорабатывать.
                  0
                  А моцжет всё‐таки использовать уже существующий addon-info.json (https://bitbucket.org/vimcommunity/vim-pi-documentation)? Вообще я был бы рад, если бы vim-pi у меня кто‐то забрал, всё равно проект довольно заброшен и я не знаю, когда к нему вернусь.
                    0
                    А почему забросили, если не секрет?
                      0
                      Особо отдачи нет. Основная проблемы:

                      1. Не умею и не хочу заниматься рекламой. Так что кроме NeoBundle и VAM vim-pi никто не использует.
                      2. VAM имеет мало пользователей не в последную очередь из‐за документации. Я умею и люблю писать подробную и достаточно структурированую документацию… для разработчиков. MarcWeber любит писать различные tips-and-tricks и превращать документацию в помойку: tips-and-tricks полезные, но структурированность хромает. Документацию для новичков не умеет писать в проекте никто. Ещё у меня есть подозрение, что та же беда с UI. Т.е. закончить, наконец, новый вариант vim-pi я могу: там не так уж много вроде осталось. А вот изменять VAM под это дело как‐то не хочется, т.к. всё равно им мало кто пользуется.
                      3. Есть другие проекты: viml-to-lua translator, powerline, мои собственные дополнения.
                      4. Учёба на вечернем факультете, совмещённая с работой.
                        0
                        И, ещё одно: методология разработки, используемая MarcWeber (вроде называется «экстремальная разработка» или как‐то так). Я не могу относится серьёзно к дополнению, у которого нет релизов, даже если я его соавтор.
                          0
                          А почему вы считаете, что я смогу решить эти проблемы? )
                            0
                            1. Зачем‐то же вы написали здесь статью
                            2. У вас свой менеджер дополнений.


                            Учитывая, что скоро сессия закончится, я могу и доделать, наконец, новый репозиторий vim-pi. Если он кому‐то нужен.

                            Но формат addon-info.json слишком радикально меняться не собирается. Так что если вы хотите иметь зависимости, то логично использовать его, а не изобретать ещё‐один‐стандарт. Сам plugin index уже содержит информацию о зависимостях помимо распространяемой с некоторыми дополнениями, так что начинать будете не с нуля, в новый plugin index она точно перекочует.
                              0
                              Я готов использовать addon-info.json, но так как еще очень плохо знаком с ним, то полноценно перенять ваш труд и отвечать за дальнейшее развитие стандарта я пока не смогу.
              0
              Уже это реализовано. Взглляните сюда github.com/MarcWeber/vim-addon-manager, в частностии на пункт github.com/MarcWeber/vim-addon-manager#how-does-vam-know-about-dependencies.

              К тому же, VAM умеет очень многое. Самое полезное — это вызов плагинов при определенном расширении файла.
                0
                Ну увидел там описания механизма автоматического разрешения зависимостей.

                А VAM умеет устанавливать и считывать плагины, установленные в проектный каталог (на пример ./.vim/bundle, а не ~/.vim/bundle) и учитывать порядок загрузки плагинов (саначала корневые, потом зависимые)?

                Самое полезное — это вызов плагинов при определенном расширении файла

                Вы про ftplugin?
                  0
                  Ну = Не*
                    0
                    Во‐первых, не умеет в проектный каталог. Во‐вторых, можно и ftplugin, и вообще любое событие (autocommand). Просто для каких‐то событий уже есть код, который вызывает vam#ActivateAddons когда нужно.

                    Разрешение зависимостей есть: определяется порядок активации (либо добавления в &rtp, либо добавления и «ручного» запуска скриптов и событий), при установке подтягиваются зависимости, если о них известно. Возможности указать требуемую версию нет, должна быть в декларативном виде в vim-pi, если я, наконец, до неё доберусь (сейчас там в документации почему‐то даже нет описания ключа «dependencies», а VAM его знает и любит).
              0
              К чему это ведет? Плагины загружаются беспорядочно, нет возможности переопределить настройки плагина для конкретного проекта, плагины переопределяют ваши собственные настройки Vim и т.д.Все еще больше усугубляется невозможностью контролировать порядок загрузки плагинов, когда нужно сначала загрузить плагин A, а только затем зависимый от него благин B. Одним словом — боль.

              А можешь привести пару примеров(реальных из практики) в которых вот прям было больно из за менеджера плагинов?
                0
                Да, конечно. Представьте ситуацию, когда у вас есть некий плагин, который установлен в вашем ~/.vim/bundle и настроен в ~/.vimrc, но вы хотите переопределить его настройки для конкретного проекта в myProject/.vimrc. Проблема в том, что загрузка этого плагина выполняется после загрузки myProject/.vimrc, что делает невозможным переопределение. Другими словами, все известные менеджеры, которые я встречал, нарушали принятый в Vim порядок загрузки плагинов — сначала rc, затем plugin, затем ftplugin, затем after и все согласно уровням иерархии. Обычно плагины просто добавляются в конец runtimepath.

                Другой пример я описал в статье. Что делать, если мне нужно установить плагин именно в конкретный проект, а не в ~/.vim/bundle?
                  +2
                  Да, конечно. Представьте ситуацию, когда у вас есть некий плагин, который установлен в вашем ~/.vim/bundle и настроен в ~/.vimrc, но вы хотите переопределить его настройки для конкретного проекта в myProject/.vimrc. Проблема в том, что загрузка этого плагина выполняется после загрузки myProject/.vimrc, что делает невозможным переопределение. Другими словами, все известные менеджеры, которые я встречал, нарушали принятый в Vim порядок загрузки плагинов — сначала rc, затем plugin, затем ftplugin, затем after и все согласно уровням иерархии. Обычно плагины просто добавляются в конец runtimepath.


                  Я потому и спросил из практики случай, с которым было больно, с названиями и всем таким :) Описанную проблему я понял, но на практике не сталкивался с болью в этом месте, хотя в виме работаю очень много и активно:)

                  Другой пример я описал в статье. Что делать, если мне нужно установить плагин именно в конкретный проект, а не в ~/.vim/bundle?

                  не могу представить ситуацию когда это оправдано. (no offence, просто пытаюсь понять есть ли в этом месте боль)
                    0
                    Я потому и спросил из практики случай, с которым было больно, с названиями и всем таким :) Описанную проблему я понял, но на практике не сталкивался с болью в этом месте, хотя в виме работаю очень много и активно:)

                    Ну вы же спросили о моем практическом опыте, а не о своем. Возможно вы с таким не сталкивались, а вот я уже не раз.

                    не могу представить ситуацию когда это оправдано

                    Хочу использовать во всех моих проектах на Java для сборки gradle, а один вынужден собирать на каком нибудь Ant. Зачем мне ставить адаптер под Ant всем проектам, когда могу поставить его только одному.
                    Хочу в одном из проектов использовать ctags, а в других нет. Если поставлю плагин в ~/.vim/bundle, все крупные проекты начнут подвисать.
                      0
                      Ну вы же спросили о моем практическом опыте, а не о своем. Возможно вы с таким не сталкивались, а вот я уже не раз.

                      Так и не ответили, с какими плагинами и в какой ситуации в этом месте были проблемы :)

                      Хочу использовать во всех моих проектах на Java для сборки gradle

                      На мой взгляд странный флоу, но в приницпе кейс понятен :)
                        0
                        Так и не ответили, с какими плагинами и в какой ситуации в этом месте были проблемы :)

                        UltiSnips, vim_template и любой плагин, элементы которого может потребоваться переопределить в зависимости от проекта. Вы хотите чтобы я пример описал?

                        На мой взгляд странный флоу, но в приницпе кейс понятен

                        Ну взгляды, конечно, могут быть разными, но хороший менеджер плагинов должен учитывать и странные взгляды )
                          0
                          Во, теперь понятно :) спасибо.
                            0
                            Я для этого использую localrc. Большинство дополнений нормально переносят настройку после запуска plugin/**/*.vim.
                              0
                              Ну вы же все плагины и конфиги не складываете в ваш .vimrc, так зачем же делать это в localrc?

                              На пример мне нужен шаблон для тестов, который будет использоваться именно в этом проекте и больше нигде. Как еще реализовать это, если только не использовать дополнительный уровень загрузки?
                      0
                      VAM учитывает after при добавлении в &runtimepath. Загрузка rc не в его ответственности, plugin/ftplugin/after загружаются Vim и только если активация происходит после VimEnter VAM’у приходится брать на себя ответственность и загружать {,after/}{plugin,ftplugin,syntax}.
                        0
                        Но обычно менеджеры плагинов просто добавляют все имеющиеся в .vim/bundle/ каталоги в runtimepath, в результате загрузка выполняется в следующем порядке:
                        Базовые скрипты Vim
                        ~/.vimrc
                        ~/.vim/plugin/
                        ./.vimrc
                        ./.vim/plugin/
                        ~/.vim/bundle/
                        что не есть правильно.
                          0
                          Где вы такие менеджеры дополнений видели? У большинства вроде требуется написать список дополнений в vimrc. Кстати, в случае с VAM вполне можно написать список дополнений в vimrc и ещё один в localrc. Здесь абсолютно не нужно пихать дополнение в каталог проекта, оно всё равно не загрузится, пока не появится в аргументе vam#ActivateAddons().
                            0
                            Но, конечно, не все, как VAM, заботятся о правильном порядке дополнений в runtimepath.
                              0
                              Написать то список требуется, но этот список никак не определяет, в какую точку runtimepath будут добавляться эти дополнения, а добавляются они обычно с помощью runtimepath += плагин.

                    Only users with full accounts can post comments. Log in, please.