はじめに
「はじめてのVimプラグイン作成(autopep8)~ GitHubの練習を兼ねて,既存レポジトリのフォーク/クローン/更新/プッシュを行ってみた」では.既存のVimプラグインをカスタマイズして使用する方法について説明しました.今回は,スクラッチからプラグインを作成することにチャレンジします.Vim初心者が書いた自分のための備忘録ですので,かなりくどい記述になっています.これからVimプラグインを作成しようと思っている方に役立つことがあれば幸いです.
題材に選んだのはPython Doctestです.私は,PythonのNumPy,SciPy,pandasなどのライブラリを使うことが多いです.これらのライブラリを使った科学技術計算では,コーディングが間違っていた場合,エラーが出るわけではなく,不正解の答えを黙って返してきます.正しい結果かそうでないかを判断するのは,作った当人以外にいません.自作の関数が常に正しい結果を返すことを保証することが,非常に重要になります.ひとつの間違いが,全体に波及して,完全に間違った結論に至る,なんてことは起こしてはいけません.Doctestは,関数の定義のすぐそばで,計算結果のチェックができるので,日ごろから積極的に使っています.もっとDoctestを使いやすくするために,自作プラグインを作成することにしました.
以下の記事では,
でVim環境を構築したことを前提としています.参考にされる方は適宜ご自身の環境に置き換えてください.
レポジトリの作成とVim設定ファイルの更新
GitHubで新規にレポジトリを作成します.名前はvim-greater3
としました.C:\Users\daizu\Documents\Vim\plugin
下にクローンします.そして,新規に作成されたvim-greater3
ディレクトリの下に,plugin
とautoload
の2つのディレクトリを作成します.さらに,これら2つのディレクトリ各々の下にgreater3.vim
という名前で空のファイルを一つずつ作成します.
C:\Users\daizu\vimfiles\dotfiles\_vimrc
内で,このプラグインを登録します.
NeoBundleLazy 'vim-greater3', {
\ 'base': '~/Documents/Vim/plugin',
\ 'type': 'nosync',
\ 'autoload': {
\ 'mappings' : ['<Plug>(greater3)'],
\ }
\}
次に,C:\Users\daizu\vimfiles\after\ftplugin\python.vim
に,キーマッピングを登録します.
nmap <buffer><F10><Plug>(greater3)
Vimを立ち上げて,:nmap
としてみます.F10キーのマッピングは見当たりません.次に適当なPythonファイルを開いて:nmap
とすると,
n<F10> @<Plug>(greater3)
という行が見つかりました.ここまでは順調のようです.ここで,F10キーを押してみます.何も起こりませんでした.何かしらのエラーメッセージがでるものと予想していましたが,何も出ないのですね.
プラグインを書いていきましょう.C:\Users\daizu\Documents\Vim\plugin\vim-greater3\plugin\greater3.vim
に以下の内容を書いて保存します.
if exists('g:loaded_greater3')finishendifnnoremap<silent><Plug>(greater3) :<C-U>echo 'greater3'<CR>letg:loaded_greater3 =1
今行った変更をすぐに反映させるために,plugin\greater3.vim
を編集しているVimとは別にもうひとつVimを立ち上げます(以下の例でも同様です).Pythonファイルを開いてF10キーを押すと,コマンドラインにgreater3と表示されるようになりました.PythonファイルをQuickRunで実行するのも簡単です:
print('greater3!!!')
nnoremap<silent><Plug>(greater3) :<C-U>QuickRun<CR>
いよいよVimScriptを書いていきます.
nnoremap<silent><Plug>(greater3) :<C-U>call greater3#run()<CR>
PythonファイルでF10キーを押すと,コマンドラインに,
E117: Unknown function: greater3#run
と表示されます.予想通りです.プラグインの実体を作成していきます.Vimでは,定義されていない関数を呼び出そうとすると,autoload
ディレクトリ下を探索するそうです.C:\Users\daizu\Documents\Vim\plugin\vim-greater3\autoload\greater3.vim
に以下の内容を書いて保存します.
function! greater3#run()letl= line('.')
echo lendfunction
F10キーでカーソルの行番号が表示されるようになりました.
VimScriptとPythonを連携させていきましょう.「はじめてのVimプラグイン作成(autopep8)~ GitHubの練習を兼ねて,既存レポジトリのフォーク/クローン/更新/プッシュを行ってみた」で学んだことの実践です.VimScriptの中でPythonが使えるように,autoload\greater3.vim
を以下の内容に書き換えます.
function! greater3#run()letl= line('.')py3<< EOF
l= int(vim.eval('l'))vim.command('echo {}'.format(l))
EOF
endfunctionfunction!s:init_py_modules()py3<< EOF
import vimvim.command('echo "init_py_modules"')
EOF
endfunctioncalls:init_py_modules()
Pythonファイルを開いてF10キーを押すと,
init_py_modules
1
と表示されます.1は現在のカーソルがバッファの先頭にあるためですね.Pythonのvim
モジュールのvim.eval
関数,vim.command
関数でPython内からVimにアクセスできることが分かります.この機能を使えばいろいろなことができそうです.ここで,もう一度F10キーを押してみます.すると,
1
となりました.s:init_py_modules
関数は,autoload\greater3.vim
が読み込まれたときのみ実行されるので,2回目のF10キーでinit_py_modules
は表示されません.これは予想通りです.驚いたのが,greater3#run
関数内ではvim
モジュールをインポートしていないにも関わらず,2回目の実行でも問題なくvim
モジュールにアクセスできている点です.つまり,1回目のF10キーによるvim
モジュールのインポートは有効のまま,言い換えれば,Pythonセッションが継続している,ということでしょうか.これはDoctestのプラグインを作るうえで希望が見えてきました.
なぜこのようなこと言うかというと,次のファイルをQuickRunで実行するとしましょう.科学技術計算をするPythonプログラムでの典型的な書き出しです.
importnumpyasnpimportpandasaspdimportmatplotlib.pyplotasplt
:QuickRun
としてから実行が終わるまでちょっと時間がかかりますよね.2回目以降も同じくらい時間がかかっています.これは実行のたびに,ライブラリをインポートするためです.では,プラグインのほうで試してみます.autoload\greater3.vim
を以下の内容に書き換えます.
function! greater3#run()py3<< EOF
vim.command('echo {:.6f}'.format(np.pi))
EOF
endfunctionfunction!s:init_py_modules()py3<< EOF
import vim
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
vim.command('echo "init_py_modules"')
EOF
endfunctioncalls:init_py_modules()
1回目の実行ではQuickRunと同じくらいの時間がかかりますが,2回目以降では瞬時に結果が返ってきます.すばらしい.Doctestをするたびにライブラリのインポートが終わるのを待つのは嫌ですから.
まとめ
VimでPython Doctestを快適に行うことを目的としてVimプラグイン作成にチャレンジしています.本題に入る前ですが,長くなってきたので,一旦ここで終了します.今回の記事をまとめます.
- GitHubでVimプラグイン用レポジトリを作成し,ローカルにクローンしました.
- プラグインのひな形を作成して,F10キーで実行できるようにしました.
- プラグインの中でPythonを使えること,Vim環境と連携できること,を確認しました.
- プラグインの中でインポートしたPythonモジュールが,後から再度のインポートなしに利用可能であることが分かりました.その都度Python実行プロセスを立ち上げるQuickRunに対して実行速度の点で有利となることが期待されます.
次回は,Doctestを行うPythonモジュールを作成していきます.