Eu tenho trabalhado na modularização e conversão de um código no meu vimrc
em alguns plugins / plugins de pacotes / plugins independentes e reutilizáveis. Ocorreu um problema com o carregamento automático e o escopo que estou tendo dificuldade para entender. Eu li através de :h autoload
, :h <sid>
, :h script-local
, mas eu ainda não sou muito claro sobre como isso funciona.
Eu estive analisando alguns plugins bem desenvolvidos para descobrir alguns padrões usados com frequência e estruturei meus plugins da seguinte maneira:
" ~/.vim/autoload/myplugin.vim
if exists('g:loaded_myplugin')
finish
endif
let g:loaded_myplugin = 1
let g:myplugin_version = 0.0.1
" Save cpoptions.
let s:cpo_save = &cpo
set cpo&vim
function! myplugin#init() " {{{
" Default 'init' function. This will run the others with default values,
" but the intent is that they can be called individually if not all are
" desired.
call myplugin#init_thing_one()
call myplugin#init_thing_two()
endfunction" }}}
function! myplugin#init_thing_one() " {{{
" init thing one
call s:set_default('g:myplugin_thing_one_flag', 1)
" do some things ...
endfunction " }}}
function! myplugin#init_thing_two() " {{{
" init thing two
call s:set_default('g:myplugin_thing_two_flag', 1)
" do some things ...
endfunction " }}}
function! s:set_default(name, default) " {{{
" Helper function for setting default values.
if !exists(a:name)
let {a:name} = a:default
endif
endfunction " }}}
" Restore cpotions.
let &cpo = s:cpo_save
unlet s:cpo_save
No início do meu vimrc, eu executo o plugin com:
if has('vim_starting')
if &compatible | set nocompatible | endif
let g:myplugin_thing_one_flag = 0
let g:myplugin_thing_two_flag = 2
call myplugin#init()
endif
Tudo isso parece funcionar corretamente e conforme o esperado - mas cada vez que uma função é chamada, a s:set_default(...)
função é chamada para cada sinalizador, o que é ineficaz - então tentei movê-los para fora das funções:
" ~/.vim/autoload/myplugin.vim
" ...
set cpo&vim
" Set all defaults once, the first time this plugin is referenced:
call s:set_default('g:myplugin_thing_one_flag', 1)
call s:set_default('g:myplugin_thing_two_flag', 1)
function! myplugin#init() " {{{
" ...
Mas isso causa erros que não tenho certeza de como resolver:
Error detected while processing /Users/nfarrar/.vim/myplugin.vim
line 40:
E117: Unknown function: <SNR>3_set_default
Ainda não entendo muito bem o escopo do vim, mas pelo que li - parece que o vim implementa uma forma de manipulação de nomes com scripts para fornecer 'escopo'. Ele atribui (não sabe exatamente como esse processo funciona) um SID exclusivo para cada arquivo carregado em tempo de execução - e quando você chama uma função prefixada com um identificador de escopo de script ( s:
), substitui esse identificador de forma transparente por um SID mapeado .
Em alguns casos, vi scripts que chamam funções como essa (mas não funciona no meu caso, não entendo o porquê e espero que alguém possa explicar isso):
call <SID>set_default('g:myplugin_thing_one_flag', 1)
call <SNR>set_default('g:myplugin_thing_one_flag', 1)
O seguinte funciona, mas não tenho certeza se é um bom padrão:
" ~/.vim/autoload/myplugin.vim
" ...
set cpo&vim
" Set all defaults once, the first time this plugin is referenced:
call myplugin#set_default('g:myplugin_thing_one_flag', 1)
call myplugin#set_default('g:myplugin_thing_two_flag', 1)
function! myplugin#init() " {{{
" ...
function! myplugin#set_default(name, default) " {{{
" ...
endfunction " }}}
No script local, ele afirma:
When executing an autocommand or a user command, it will run in the context of
the script it was defined in. This makes it possible that the command calls a
local function or uses a local mapping.
Otherwise, using "<SID>" outside of a script context is an error.
If you need to get the script number to use in a complicated script, you can
use this function:
function s:SID()
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$')
endfun
Parece que essa pode ser a abordagem que preciso adotar, mas não sei ao certo por que ou exatamente como usá-lo. Alguém pode fornecer algumas dicas?
echom 'this is the function call'
na função que está sendo chamada de vimrc e outra emechom 'file was sourced'
qualquer outro lugar do arquivo (não em uma função), vejo a última primeiro, depois a primeira.Eu recomendo esta estrutura:
Isso é compatível com todos os gerenciadores de plugins modernos e mantém as coisas limpas. Destes:
myplugin/doc/myplugin.txt
deve ser o arquivo de ajudamyplugin/plugin/myplugin.vim
deve conter:myplugin/autoload/myplugin.vim
deve conter o código principal do seu plugin.Os escopos são realmente muito simples:
s:
podem aparecer em qualquer lugar, mas são locais no arquivo em que estão definidas; você pode chamá-los apenas do arquivo em que estão definidos;myplugin/autoload/myplugin.vim
devem ter nomesmyplugin#function()
e são globais; você pode chamá-los de qualquer lugar (mas lembre-se de que chamá-los faz com que o arquivomyplugin/autoload/myplugin.vim
seja carregado);Function()
, e também são globais; você pode chamá-los de qualquer lugar.<SID>
e<Plug>
são usados para mapeamentos, e são um tópico que você provavelmente deve evitar até ter um entendimento completo de como eles funcionam e que problema eles devem resolver.<SNR>
é algo que você nunca deve usar diretamente.fonte
autoload/
eplugin/
? Eu sempre coloquei tudoplugin/
e isso parece funcionar bem?plugin/
exceto que é carregado apenas uma vez necessário, em vez de carregado na inicialização.autoload
, se você não pode testar a existência de uma função se o arquivo em que ele vive não tiver sido carregado.