Vimscript: Bantuan untuk Autoloading, Cakupan & <SID>

9

Saya telah bekerja pada modularizing & mengkonversi kode di saya vimrcmenjadi beberapa bundel / plugin plugins mandiri dan dapat digunakan kembali. Saya mengalami masalah dengan autoloading & lingkup yang sulit saya pahami. Saya telah membaca :h autoload, :h <sid>, :h script-local, tapi aku masih belum cukup jelas tentang cara kerja ini.

Saya telah melihat beberapa plugin yang dikembangkan dengan baik untuk mengetahui beberapa pola yang umum digunakan, dan telah menyusun plugin saya sebagai berikut:

" ~/.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

Di awal vimrc saya, saya menjalankan plugin dengan:

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

Ini semua tampaknya berfungsi dengan benar dan seperti yang diharapkan - tetapi setiap kali sebuah fungsi dipanggil, s:set_default(...)fungsi tersebut dipanggil untuk setiap flag, yang tidak efisien - jadi saya mencoba untuk memindahkan mereka dari fungsinya:

" ~/.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() " {{{
" ...

Tetapi ini menyebabkan kesalahan yang saya tidak yakin bagaimana saya harus menyelesaikannya:

Error detected while processing /Users/nfarrar/.vim/myplugin.vim
line   40:
E117: Unknown function: <SNR>3_set_default

Saya masih belum benar-benar memahami pelingkupan vim, tetapi dari apa yang saya baca - tampaknya vim mengimplementasikan suatu bentuk pengubahan nama dengan skrip untuk memberikan 'cakupan'. Ini memberikan (tidak yakin bagaimana tepatnya proses ini bekerja) SID unik untuk setiap file yang dimuat saat runtime - dan ketika Anda memanggil fungsi yang diawali dengan pengidentifikasi skrip-lingkup ( s:), ia secara transparan menggantikan pengidentifikasi itu dengan SID yang dipetakan. .

Dalam beberapa kasus, saya telah melihat skrip yang memanggil fungsi seperti ini (tetapi tidak berfungsi dalam kasus saya, saya tidak mengerti mengapa, dan berharap seseorang dapat menjelaskan ini):

call <SID>set_default('g:myplugin_thing_one_flag', 1)
call <SNR>set_default('g:myplugin_thing_one_flag', 1)

Yang berikut ini berfungsi, tetapi saya tidak yakin apakah ini pola yang baik:

" ~/.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 " }}}

Dalam skrip lokal, menyatakan:

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

Sepertinya ini mungkin pendekatan yang perlu saya ambil, tapi saya tidak sepenuhnya yakin mengapa, atau bagaimana tepatnya menggunakannya. Adakah yang bisa memberikan wawasan?

nfarrar
sumber

Jawaban:

4

Dalam kasus pertama, kesalahan yang Anda dapatkan adalah bahwa Anda mencoba memanggil suatu fungsi sebelum keberadaannya. Artinya, Vim sedang mengalami kemajuan melalui skrip Anda. Saat melihat callsaluran, itu belum memproses functionsaluran yang membuat apa yang ingin Anda panggil, menghasilkan kesalahan unknown function.

Jika Anda memindahkan panggilan Anda ke pengaturan default ke akhir skrip Anda (sebelum Anda mengembalikan cpotetapi setelah semua skrip Anda function, tidak akan ada kesalahan, karena Vim akan memproses skrip untuk membuat fungsi terlebih dahulu, jadi setelah itu dapatkan ke callgaris, fungsi ada, mis

"....

function! s:set_default(name, default) " {{{
  " Helper function for setting default values.
  if !exists(a:name)
    let {a:name} = a:default
  endif
endfunction " }}}

" 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)

" Restore cpotions.
let &cpo = s:cpo_save
unlet s:cpo_save

Saya tidak tahu mengapa sintaks alternatif memanggil Anda set_defaultsebagai fungsi autoload dari dalam skrip autoload berfungsi ketika fungsi tersebut belum didefinisikan. Dugaan saya adalah ini adalah efek samping dari implementasi (di mana skrip yang sudah dibaca tidak dibaca ulang, atau Anda akan memiliki rekursi tak terbatas). Saya tidak akan mengandalkan itu selalu bekerja seperti itu.

John O'M.
sumber
Jika saya mengerti dengan benar, Anda mengatakan seluruh file tidak bersumber & dieksekusi sebelum eksekusi pemanggilan fungsi didefinisikan dalam vimrc saya? Mungkin saya salah paham ... tetapi bagi saya seluruh skrip autoload bersumber & dieksekusi terlebih dahulu. Jika saya menambahkan pernyataan echom 'this is the function call'dalam fungsi yang dipanggil dari vimrc dan yang lain di echom 'file was sourced'mana saja dalam file (bukan dalam fungsi), saya melihat yang pertama terlebih dahulu, lalu yang pertama.
nfarrar
Maaf, saya baru menyadari apa yang Anda katakan - dan Anda benar. Karena pemanggilan fungsi terjadi dalam skrip saat bersumber, pemanggilan fungsi perlu terjadi setelah fungsi tersebut didefinisikan. Terima kasih!
nfarrar
13

Saya merekomendasikan struktur ini:

.
└── myplugin
    ├── LICENSE.txt
    ├── README.md
    ├── autoload
    │   └── myplugin.vim
    ├── doc
    │   └── myplugin.txt
    └── plugin
        └── myplugin.vim

Ini kompatibel untuk semua manajer plugin modern, dan menjaga semuanya tetap bersih. Ini:

  • myplugin/doc/myplugin.txt harus menjadi file bantuan
  • myplugin/plugin/myplugin.vim harus mengandung:
    • memeriksa prasyarat apa pun yang dibutuhkan plugin Anda, seperti versi Vim minimal dan fitur waktu kompilasi Vim
    • pemetaan kunci
    • inisialisasi satu kali, seperti pengaturan default
  • myplugin/autoload/myplugin.vim harus berisi kode utama plugin Anda.

Lingkup sebenarnya cukup sederhana:

  • fungsi dengan nama yang dimulai dengan s:dapat muncul di mana saja, tetapi bersifat lokal untuk file tempat mereka didefinisikan; Anda hanya dapat memanggil mereka dari file di mana mereka didefinisikan;
  • fungsi di myplugin/autoload/myplugin.vimharus memiliki nama myplugin#function()dan bersifat global; Anda dapat memanggil mereka dari mana saja (tetapi berhati-hatilah karena memanggil mereka menyebabkan file myplugin/autoload/myplugin.vimdimuat);
  • semua fungsi lain harus memiliki nama yang dimulai dengan huruf besar, seperti Function(), dan juga global; Anda dapat memanggil mereka dari mana saja.

<SID>dan <Plug>digunakan untuk pemetaan, dan itu adalah topik yang mungkin harus Anda hindari sampai Anda memiliki pemahaman penuh tentang cara kerjanya dan masalah apa yang harus mereka selesaikan.

<SNR> adalah sesuatu yang tidak boleh Anda gunakan secara langsung.

lcd047
sumber
Mengapa terpisah antara direktori autoload/dan plugin/? Saya selalu memasukkan semuanya ke dalam plugin/dan sepertinya itu berfungsi dengan baik?
Martin Tournoij
2
Direktori pengisian otomatis hanya memuat kontennya saat diperlukan. Ini dapat mempercepat waktu mulai vim. Dengan kata lain, ini berfungsi hampir sama plugin/kecuali kecuali hanya dimuat setelah dibutuhkan alih-alih dimuat pada startup.
EvergreenTree
2
@Carpetsmoker Cukup banyak seperti kata @EvergreenTree. Tentu saja, itu tidak terlalu penting untuk plugin "kecil", tetapi ini masih praktik yang baik. Dengan Vim, Anda hanya memiliki sedikit kontrol terhadap pemulung, dan memuat barang hanya ketika dan jika dibutuhkan dapat membuat perbedaan. Di sisi lain, ada kelemahan kecil untuk memindahkan semuanya autoload, karena Anda tidak dapat menguji keberadaan suatu fungsi jika file yang digunakan belum dimuat.
lcd047