Vimscript:自動読み込み、スコープ、<SID>のヘルプ


9

私は自分のコードをモジュール化してvimrc、いくつかの自己完結型で再利用可能なバンドル/プラグインプラグインに変換する作業を行っています。理解できないオートロードとスコープの問題に遭遇しました。私はを読みました:h autoload:h <sid>:h script-local、私はどのようにこの作品にはまだ非常に明確ではありませんよ。

私はいくつかのよく開発されたプラグインを調べて、一般的に使用されるパターンを理解し、プラグインを次のように構成しました。

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

vimrcの開始時に、次のコマンドでプラグインを実行します。

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

これはすべて正しく期待どおりに機能しているように見えますが、s:set_default(...)関数が呼び出されるたびに、関数は各フラグに対して呼び出されるため、非効率的です。そのため、関数からそれらを移動しようとしました。

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

しかし、これは私がどのように解決すべきかわからないエラーを引き起こします:

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

私はまだvimのスコープをしっかりと理解していませんが、私が読んだことから-vimは 'スコープ'を提供するためにスクリプトを使用して名前マングルの形式を実装しているようです。実行時に読み込まれる各ファイルに一意のSIDを割り当てます(このプロセスがどのように機能するかは不明ですs:)。スクリプトスコープの識別子()が前に付いている関数を呼び出すと、その識別子がマップされたSIDに透過的に置き換えられます。 。

場合によっては、次のような関数を呼び出すスクリプトを見たことがあります(ただし、私の場合は機能しません。理由がわかりません。誰かがこれを説明できるといいのですが)。

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

以下は機能しますが、それが良いパターンであるかどうかはわかりません:

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

ローカルスクリプトでは、次のように述べています。

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

これは私が取るべきアプローチかもしれないように見えますが、私はその理由、または正確にそれを使用する方法を完全に確信していません。誰かが洞察を提供できますか?

回答:


4

最初のケースでは、存在する前に関数を呼び出そうとしているというエラーが発生します。つまり、Vimはスクリプトを処理しています。それは見ているときにcallラインを、それがまだ処理されていないfunctionエラーが発生し、呼び出したいものを作成する行をunknown function

セットアップのデフォルトへの呼び出しをスクリプトの最後に移動した場合(復元する前cpo、ただしすべてのfunctionsの後で)、Vimが最初にスクリプトを処理して関数を作成するので、エラーは発生しません。callライン、機能が存在する。例えば

"....

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

set_default関数がまだ定義されていないときに、autoloadスクリプト内からautoload関数としてyourを呼び出す代替構文が機能する理由はわかりません。私の推測では、これは実装の副作用です(既に読み取られたスクリプトが再度読み取られないか、無限の再帰が発生します)。私はそれが常にそのように機能することを期待しません。


私が正しく理解している場合は、vimrcで定義されている関数呼び出しの実行前に、ファイル全体が取得および実行されていないということですか?多分私は誤解しているかもしれません...しかし、私にはオートロードスクリプト全体が最初にソースになって実行されるように思えます。echom 'this is the function call'vimrcから呼び出されている関数にステートメントを追加echom 'file was sourced'し、ファイル内の他の場所(関数ではない)にステートメントを追加すると、最初に後者が表示され、次に前者が表示されます。
nfarrar

すみません、私はあなたが言っていることを理解しました-そしてあなたは正しいです。関数呼び出しは、ソースとしてスクリプトで行われるため、関数が定義された後に行う必要があります。ありがとうございました!
nfarrar 2015年

12

私はこの構造をお勧めします:

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

これは、すべての最新のプラグインマネージャーと互換性があり、クリーンな状態を保ちます。これらの:

  • myplugin/doc/myplugin.txt ヘルプファイルである必要があります
  • myplugin/plugin/myplugin.vim 含む必要があります:
    • 最小のVimバージョンやVimのコンパイル時機能など、プラグインに必要な前提条件をチェックします
    • キーマッピング
    • デフォルトの設定など、1回の初期化
  • myplugin/autoload/myplugin.vim プラグインのメインコードを含める必要があります。

スコープは実際にはかなり単純です:

  • 名前がで始まる関数はs:どこにでも現れる可能性がありますが、それらが定義されているファイルに対してローカルです。それらが定義されているファイルからのみ呼び出すことができます。
  • の関数にmyplugin/autoload/myplugin.vimは名前が必要myplugin#function()で、グローバルです。どこからでも呼び出すことができます(ただし、呼び出すとファイルmyplugin/autoload/myplugin.vimがロードされることに注意してください)。
  • 他のすべての関数は、大文字などで始まる名前を持つ必要Function()があり、グローバルでもあります。どこからでも呼び出すことができます。

<SID><Plug>マッピングに使用されます。これらは、それらがどのように機能し、どのような問題を解決する必要があるかを完全に理解するまでは避けるべきトピックです。

<SNR> 直接使用してはいけないものです。


autoload/plugin/ディレクトリを分離するのはなぜですか?私はいつもすべてを入れてきました、plugin/そしてそれはうまくいくようです?
Martin Tournoij、2015年

1
autoloadディレクトリは、必要なときにのみコンテンツをロードします。これはvimの起動時間をいくらかスピードアップすることができます。つまり、plugin/起動時にロードされるのではなく、必要なときに一度だけロードされることを除いて、ほぼ同じように機能します。
EvergreenTree

2
@Carpetsmoker @EvergreenTreeが言ったように。もちろん、「小さな」プラグインの場合はそれほど重要ではありませんが、それでも良い方法です。Vimではガベージコレクターをほとんど制御できず、必要なときにのみロードすることで違いが生まれます。一方、すべてをに移動することには微妙な欠点がautoloadあります。関数が存在するファイルがロードされていない場合、関数の存在をテストすることはできません。
lcd047
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.