*usr_52.txt*	For Vim バージョン 9.1.  Last change: 2025 Nov 09


		     VIM ユーザーマニュアル	by Bram Moolenaar


			大きなプラグインを作る


プラグインが単純なこと以上のことを行うと、大きくなる傾向があります。このファイ
ルは、それらが高速に読み込まれるようにする方法と、それらを小さなパーツに分割す
る方法を説明しています。

|52.1|	エクスポートとインポート
|52.2|	オートロード
|52.3|	インポート/エクスポートなしのオートロード
|52.4|	他に使えるメカニズム
|52.5|	旧来のスクリプトから Vim9 script を使う
|52.6|	Vim9 サンプル: comment および highlight-yank プラグイン

次章: |usr_90.txt|  Vim のインストール
前章: |usr_51.txt|  プラグインを作る
目次: |usr_toc.txt|

==============================================================================
*52.1*	エクスポートとインポート

Vim9 script は大きな Vim script を容易に書けるようにデザインされています。他の
スクリプト言語、特に TypeScript のようになっています。また、処理内の関数はコン
パイルされ素早く実行できます。これにより Vim9 script はとても速く、最大100倍に
なります。

基本的な考え方は、スクリプトファイルには、スクリプトファイル内でのみ使用される
プライベートな項目と、それらをインポートするスクリプトで使用できるエクスポート
される項目があるということです。これにより、何がどこで定義されているかが非常に
明確になります。

1つの関数をエクスポートし、1つのプライベート関数を持つスクリプトの例から始めま
しょう: >

	vim9script

	export def GetMessage(count: string): string
	   var nr = str2nr(count)
	   var result = $'To {nr} we say '
	   result ..= GetReply(nr)
	   return result
	enddef

	def GetReply(nr: number): string
	  if nr == 42
	     return 'yes'
	  elseif nr == 22
	     return 'maybe'
	  else
	     return 'no'
	  endif
	enddef

`export` は |Vim9| script でのみ動くので `vim9script` コマンドが必要です。

`export def GetMessage(...` の行は `export` から始まっていて、この関数が他のス
クリプトから呼び出すことができることを意味します。`def GetReply(...` の行は
`export` で始まっておらず、これはスクリプトローカル関数であり、このスクリプト
ファイル内でのみ使用できます。

次にインポートされるスクリプトについて説明します。この例の中ではこの配置を使用
しており、これは "pack" ディレクトリ下のプラグインに適しています:
	.../plugin/theplugin.vim
	.../lib/getmessage.vim

"..." ディレクトリが 'runtimepath' に追加されていると仮定すると、Vim は
"plugin" ディレクトリからプラグインを探し "theplugin.vim" を読み込みます。Vim
は "lib" ディレクトリを認識しません、そこに任意のスクリプトを置くことができま
す。

上の GetMessage() をエクスポートしているスクリプトは lib/getmessage.vim 内に
あります。GetMessage() 関数は plugin/theplugin.vim 内にて使用されます: >

	vim9script

	import "../lib/getmessage.vim"
	command -nargs=1 ShowMessage echomsg getmessage.GetMessage(<f-args>)

`import` コマンドは "../" で始まる相対パスを使用しています。これは、1つ上のディ
レクトリに移動することを意味します。他の種類のパスについては、`:import` コマン
ドを参照してください。

プラグインが提供するコマンドを試す方法: >
	ShowMessage 1
<	To 1 we say no ~
>
	ShowMessage 22
<	To 22 we say maybe ~

関数 GetMessage() には、インポートしたスクリプト名 "getmessage" がプリフィック
スとして付けられていることに注意してください。こうすることで、使用されるすべて
のインポートされた関数について、それがどのスクリプトからインポートされたかが分
かります。複数のスクリプトをインポートする場合、それぞれで GetMessage() 関数を
定義できます: >

	vim9script

	import "../lib/getmessage.vim"
	import "../lib/getother.vim"
	command -nargs=1 ShowMessage echomsg getmessage.GetMessage(<f-args>)
	command -nargs=1 ShowOther echomsg getother.GetMessage(<f-args>)

インポートしたスクリプトの名前が長いあるいは色々な箇所で使う場合、引数に "as"
を追加することで短くできます: >
	import "../lib/getmessage.vim" as msg
	command -nargs=1 ShowMessage echomsg msg.GetMessage(<f-args>)


☆再読み込み

1つ覚えておくべきこと: インポートされた "lib/getmessage.vim" スクリプトは1度だ
け読み込まれます。インポート済みで2度目に読み込む時はスキップされ、項目は最初
に作成された以降そのままです。これはインポートコマンドが他のスクリプト内にあ
るか、再読み込みされた同じスクリプト内にあるかは関係ありません。

これはプラグインを使用する場合は効率的ですが、プラグインを開発中の場合は、イン
ポート後に "lib/getmessage.vim" を変更しても効果がありません。Vim を終了し、再
起動する必要があります。(理論的根拠: スクリプトで定義された項目は、コンパイル
された関数で使用できます。スクリプトを再度読み込むと、これらの関数が壊れる可能
性があります)。


☆グローバルの使用

時には、グローバル変数や関数を使用して、どこでも使えるようにしたい場合がありま
す。プラグインに設定を渡すグローバル変数がその良い例です。他のスクリプトが同じ
名前を使うのを避けるために、他の場所で使われる可能性が非常に低いプリフィックス
を使用します。例えば、"mytags" プラグインがある場合、次のように使用します: >

	g:mytags_location = '$HOME/project'
	g:mytags_style = 'fast'

==============================================================================
*52.2*	オートロード

大きなスクリプトを分割しても、スクリプトが使用された瞬間にすべての行がロードさ
れ、実行されます。`import` するたびに、インポートしたスクリプトをロードして、
そこで定義されている項目を探します。これはエラーを早期に発見するためには良いこ
とですが、同時に時間もかかります。そのため、その機能があまり使用されない場合
は、無駄な時間がかかってしまいます。

`import` でスクリプトをすぐにロードするのではなく、必要になるまで延期すること
ができます。上記の例を使用すると、plugin/theplugin.vim スクリプトで行う必要が
ある変更は1箇所だけです: >
	import autoload "../lib/getmessage.vim"

スクリプトの残りの部分は何も変更する必要はありません。ただし、型はチェックされ
ません。GetMessage() 関数が使用されるまで、その存在すらチェックされません。ス
クリプトにとってどちらがより重要かを決定する必要があります: 高速な起動か早期に
エラーが分かるか。すべての動作を確認した後で、"autoload" 引数を追加することも
できます。


☆オートロードディレクトリ

もう1つの形式は絶対パスでも相対パスでもないスクリプト名で autoload を使用する
ことです: >
	import autoload "monthlib.vim"

これはスクリプト "monthlib.vim" を 'runtimepath' のオートロードディレクトリの
中から検索します。Unix では、ディレクトリの1つが "~/.vim/autoload" であること
が多いです。これはまた 'packpath' の、その下の "start" からも検索します。

この方法の主要な利点として、このスクリプトを他のスクリプトと共有するのが容易で
あることです。Vim が 'runtimepath' 内の "autoload" ディレクトリから検索するた
め、スクリプト名は一意である必要があり、プラグインはいくつかのプラグインマネー
ジャーとともに利用される場合では、'runtimepath' にディレクトリを追加されること
があり、それぞれが "autoload" ディレクトリを持っているためです。

オートロードなしは: >
	import "monthlib.vim"

Vim は 'runtimepath' のインポートディレクトリ内からスクリプト "monthlib.vim"
を検索します。Note この場合、"autoload" を追加または削除すると、スクリプトが見
つかる場所が変わります。相対パスまたは絶対パスでは、場所は変わりません。

==============================================================================
*52.3*	インポート/エクスポートなしのオートロード

						*write-library-script*
インポート/エクスポート以前のメカニズムは依然として使用でき一部ユーザーは多少
単純であると感じるかもしれません。それは特別な名前によって関数を呼び出すという
アイデアです。その関数はオートロードスクリプト内にあります。そのスクリプトのこ
とをライブラリスクリプトと呼びます。

オートロードのメカニズムは、関数名が "#" 文字を含んでいることが前提となってい
ます: >

	mylib#myfunction(arg)

Vim は埋め込まれた "#" 文字によって関数名を認識し、まだ定義されていない場合は
"autoload/mylib.vim" というスクリプトを 'runtimepath' から探します。そのスクリ
プトは "mylib#myfunction()" 関数を定義している必要があります。言うまでもなく、
"mylib" という名前は "#" の前の部分であり、".vim" を追加してスクリプト名として
使用されます。

mylib.vim スクリプト内に他の多くの関数を置ことができ、ライブラリスクリプト内の
構成は自由です。ただし、'#' の前部分がスクリプト名に一致する関数名を使用する必
要があります。そうしないと、Vim はロードするスクリプトを認識できません。この点
がインポート/エクスポートのメカニズムとの違いになります。

本当に夢中になり多数のライブラリスクリプトを書くのであれば、サブディレクトリを
使用したいでしょう。例: >

	netlib#ftp#read('somefile')

ここでは、スクリプト名は、関数名から最後の "#" までが取得されます。途中の "#"
はスラッシュで置き換えられ、最後の "#" は ".vim" で置き換えられます。したがっ
て、"netlib/ftp.vim" となります。Unixの場合、これに使われるライブラリスクリプ
トは次のようになります:

	~/.vim/autoload/netlib/ftp.vim

ここで、関数はこのように定義されています: >

	def netlib#ftp#read(fname: string)
		#  Read the file fname through ftp
	enddef

関数が定義されている名前は、関数を呼び出すときに使われる名前と全く同じであるこ
とに注意してください。また、最後の '#' の前の部分は、サブディレクトリとスクリ
プト名に完全に一致しています。

変数にも同じメカニズムが使用できます: >

	var weekdays = dutch#weekdays

これによりスクリプト "autoload/dutch.vim" がロードされ、次のような内容が含ま
れる必要があります: >

	var dutch#weekdays = ['zondag', 'maandag', 'dinsdag', 'woensdag',
		\ 'donderdag', 'vrijdag', 'zaterdag']

参考文献: |autoload|。

==============================================================================
*52.4*	他に使えるメカニズム

複数のファイルを使用するのが面倒で、すべてを1つのスクリプトにまとめておくのを
好む人もいます。その結果として、起動が遅くなるのを回避するため、小さな部分のみ
を定義し、残りは実際に使用されるまで延期するメカニズムがあります。
						*write-plugin-quickload*

基本的な考え方は、プラグインは2回ロードされるということです。初回は、機能を提
供するためのユーザーコマンドとマッピングが定義されます。2回目は、その機能を実
装する関数が定義されます。

高速ロードがスクリプトを2回ロードすることを意味するのは意外に思われるかもしれ
ません。つまり、初回は素早く読み込み、2回目にスクリプトの大部分を読み込ませる
ということです。この機能は、実際に使用するときにのみ発生します。常に機能を使用
する場合、実際には遅くなります!

これは、FuncUndefined 自動コマンドを使用しています。上で説明した |autoload| 機
能とは異なる動作をします。

以下の例は、その方法を示しています: >

	" Vim global plugin for demonstrating quick loading
	" Last Change:	2005 Feb 25
	" Maintainer:	Bram Moolenaar <Bram@vim.org>
	" License:	This file is placed in the public domain.

	if !exists("s:did_load")
		command -nargs=* BNRead  call BufNetRead(<f-args>)
		map <F19> :call BufNetWrite('something')<CR>

		let s:did_load = 1
		exe 'au FuncUndefined BufNet* source ' .. expand('<sfile>')
		finish
	endif

	function BufNetRead(...)
		echo 'BufNetRead(' .. string(a:000) .. ')'
		" read functionality here
	endfunction

	function BufNetWrite(...)
		echo 'BufNetWrite(' .. string(a:000) .. ')'
		" write functionality here
	endfunction

スクリプトの初回ロード時は "s:did_load" は未設定です。"if" と "endif" の間のコ
マンドが実行されます。これは |:finish| コマンドで終わり、残りのスクリプトは実
行されません。

スクリプトの2回目ロード時は "s:did_load" は存在し、"endif" 以降のコマンドが実
行されます。これは(長い可能性のある) BufNetRead() と BufNetWrite() 関数を定義
します。

あなたのプラグインディレクト内にスクリプトを置くと Vim は初期化時に実行します。
このような流れでイベントが発生します:

1. 起動時にスクリプトが読み込まれた時、"BNRead" コマンドが定義され、<F19> キー
   がマッピングされる。|FuncUndefined| 自動コマンドが定義される。":finish" コ
   マンドは、スクリプトを早期に終了させる。

2. ユーザーが BNRead コマンドをタイプするか <F19> キーを押す。BufNetRead()
   か BufNetWrite() 関数が呼ばれる。

3. Vim は関数を見つけることができず、|FuncUndefined| 自動コマンドイベントが発
   生する。パターン "BufNet*" は呼び出された関数と一致するため、コマンド
   "source fname" が実行される。"fname" は、"<sfile>" (|expand()| を参照) を展
   開したものであるため、スクリプトがどこにあるかに関係なく、スクリプトの名前
   と等しくなる。

4. スクリプトは再度読み込まれ、"s:did_load" 変数は存在し、関数が定義される。

ロードされた後の関数が |FuncUndefined| 自動コマンドのパターンにマッチすること
に注意してください。他のプラグインで定義するパターンにマッチする関数がないよう
にしないといけません。

==============================================================================
*52.5*	旧来のスクリプトから Vim9 script を使う		*source-vim9-script*

いくつかの場合として持っている旧来の Vim script にて Vim9 script のアイテムを
使いたいことがあります。例えばあなたの .vimrc がプラグインの初期化をする場合な
どです。最良の方法は `:import` を使うことです。例: >

	import 'myNicePlugin.vim'
	call myNicePlugin.NiceInit('today')

これは、Vim9 script ファイル内でエクスポートされた関数 "NiceInit" を見つけ、ス
クリプトローカルな項目 "myNicePlugin.NiceInit" として使用できるようにします。
`:import` は、"s:" が指定されていない場合でも、常にスクリプトの名前空間を使用
します。"myNicePlugin.vim" がすでに読み込まれていた場合、再度読み込まれること
はありません。

その上アイテムがグローバルの名前空間に置かれるのを防ぎ (名前の衝突が不測の問題
になり得ます)、そしてたとえ何度もアイテムをインポートしても、スクリプトの読み
込みがただ一度であることを意味します。

いくつかの場合、例えばテストなどで、ただ Vim9 script を読み込みたいだけのこと
があります。大丈夫ですが、グローバルのアイテムだけが利用可能となります。Vim9
script ではこうしたグローバルのアイテムで確実にユニークな名前を使うようにしな
くてはなりません。 例: >
	source ~/.vim/extra/myNicePlugin.vim
	call g:NicePluginTest()

==============================================================================
*52.6*	Vim9 サンプル: comment および highlight-yank プラグイン

☆comment パッケージ

Vim には、Vim9 script で記述されたコメントプラグインが付属しています。
|comment-install|
$VIMRUNTIME/pack/dist/opt/comment/ にあるパッケージを確認してください。

☆highlight-yank プラグイン

Vim には、Vim9 script で記述された highlight-yank プラグインが付属しています。
|hlyank-install|。以下は簡略化された実装です: >vim9

	vim9script

	def HighlightedYank(hlgroup = 'IncSearch', duration = 300, in_visual = true)
	  if v:event.operator ==? 'y'
	    if !in_visual && visualmode() != null_string
	      visualmode(1)
	      return
	    endif
	    var [beg, end] = [getpos("'["), getpos("']")]
	    var type = v:event.regtype ?? 'v'
	    var pos = getregionpos(beg, end, {type: type, exclusive: false})
	    var m = matchaddpos(hlgroup, pos->mapnew((_, v) => {
	      var col_beg = v[0][2] + v[0][3]
	      var col_end = v[1][2] + v[1][3] + 1
	      return [v[0][1], col_beg, col_end - col_beg]
	    }))
	    var winid = win_getid()
	    timer_start(duration, (_) => m->matchdelete(winid))
	  endif
	enddef

	autocmd TextYankPost * HighlightedYank()
<
完全な例については、`$VIMRUNTIME/pack/dist/opt/hlyank/` にあるパッケージを参照
してください。

==============================================================================

次章: |usr_90.txt|  Vim のインストール


Copyright: see |manual-copyright|  vim:tw=78:ts=8:noet:ft=help:norl:
