2012-01-21
■[Vim] QuickRun.vim 実行前に定義した関数を実行するプラグインを作った
id:thinca さん作の QuickRun.vim は自分がコードを書く上で書かせないプラグインの一つでとても重宝している。
テストコードでもテストランナーを実行してくれるのが嬉しい。
ただ、毎回毎回開いているテストコードのテストを全て実行して欲しくない時もある。
たとえばガリガリ開発している時は毎回毎回全てのテストが実行される必要はない。
取りかかっているテストのメソッドのみを実行したい。
# テストを素早く実行できるのは大事なんじゃないかと思う。
Python の場合、nose を使用すれば特定のメソッドのコードを実行するというのが可能。
nosetests /path/to/test/file.py:test_function
Basic usage — nose 1.1.3 documentation
ということで QuickRun 実行時にこれを渡してやれば行けそう。
Vim でテストコードを開いていて、メソッド名が test_ で始まるものを探せば実行するメソッドは取れる。
クラス名は現在行から上に class Test で始まる行を取得すれば取れる。
で、g:quickrun_config のオプションに設定してやれば行けそう。
問題はどうやって QuickRun 実行時に g:quickrun_config にこれを設定してやるのかがわからなった。
QuickRun のマニュアルを読んで、実行を一番最初に受け取るメソッド以降を少しだけ読んでみたが、フックは出来なさそう。
折角 QuickRun という素晴らしい実行インタフェースがあるので、安易に system('nosetests') とかで実行するより、QuickRun の機能を使いたい。
実行前に g:quickrun_config を書き換えられれば良いので、自分で定義した関数で設定を動的に書き換えて、quickrun#command() を実行してやれば良いんじゃないかと思った。
で、どうせならと思い簡単なプラグインを作った。
https://github.com/heavenshell/vim-unitquickrun
自分の QuickRun 関連の設定は以下の通り
augroup QuickRunUnitTest autocmd! autocmd BufWinEnter,BufNewFile *test.php setlocal filetype=php.unit autocmd BufWinEnter,BufNewFile test_*.py setlocal filetype=python.unit autocmd BufWinEnter,BufNewFile *.t setlocal filetype=perl.unit augroup END let g:quickrun_config = {} let g:quickrun_config['php.unit'] = {'command': 'phpunitrunner'} let g:quickrun_config['python.unit'] = {'command': 'nosetests', 'cmdopt': '-s -vv'} let g:quickrun_config['perl.unit'] = {'command': 'prove'} let g:quickrun_config['rst'] = {'command': 'sphinx-build', 'cmdopt': '-b html -d ..build/doctrees ../source ../build'}
ここで、test_* で始まる Python コードは python.unit として定義しているので、ファイルタイプ*1ごとにautoload/unitquicrun/ にファイルを作り実行できるようにした。
呼び出しのオプションは基本的に QuickRun と同じ。
デフォルトでは <Leader>u に割り当てている。
nnoremap <silent> ,r :UnitQuickRun -mode n -runner vimproc:100<CR>
とかで変更できる。
作ったファイル内で g:quickrun_config を書き換えて、quickrun#command() を呼び出したら意図通りに動いた。
毎回動的に処理をするので速度は遅くなっているが、全てのテストを実行するよりは速い。
Python だけじゃなくて PHP でも Stagehand_TestRunner を使えば同様にメソッドだけテストが出来るのでこれを使えばできそう。
またテストだけでなくて、たとえば Sphinx で reST なファイルをコンパイルする際に source と dest の位置を書き換えるとかも出来る。
こんな手間の掛かる事をしなくても QuickRun 本体で出来るのかもしれない。
まぁ自分専用なつもりで書いたので、いいかなーと。
例によって Vim Script はこれでええのかなーと思って書いた。
あとドキュメントは書いてないので ToDo としている。
追記(1/22)
ぐぬぬ。早くhook入れんと。 / QuickRun.vim 実行前に定義した関数を実行するプラグインを作った - Heavens hell URL
@heavenshell 実は開発ブランチで試作版は試せます。まだfixしきれてないですが。
2012-01-21 23:59:13 via twicca to @heavenshell
との事なので、QuickRun 0.6 が正式にリリースされたら、このプラグインは用なしになります。
楽しみ!
*1:ただし "."は消すようにした
2012-01-18
■[Vim] docstring を挿入するプラグインを作った
Vim を使って PHP スクリプトを作成しているときに phpdoc.vim というプラグインがもの凄く重宝してた。
何をするものかというと、例えば以下のようなコードがあったとする。
<?php class Foo { pubilic function __construct($arg1, $arg2) { } }
class や function の行にカーソルがある場合に
:call PhpDocSingle()
と実行すると
<?php class ClassName { /** * __construct * * @param mixed $arg1 * @param mixed $arg2 * @access public * @return void */ public function __construct($arg1, $arg2) { } }
といった感じでヘッダーコメントが挿入される。
この機能が地味に便利で、Python なコードを書いている時も自動で docstring が挿入して欲しい。
少し探してみたが無かったので、自分で作ってみた(Vim Script を一から書いたのは初めて)。
https://github.com/heavenshell/vim-pydocstring
# -*- coding: utf-8 -*- def foo(arg): pass
これで :Pydocstring を呼ぶと
"""foo
:param arg:
"""
pass
というのが挿入される。
引数が無い場合は
def foo(): """foo""" pass
ワンラインの docstring となる。
\ によって改行が入ってる場合は
def foo(arg1, \ arg2): """foo :param arg1: :param arg2: """ pass
となる。
差し込む docstring はテンプレートファイルになっているので、デフォルトのが気に入らなければ書き換える事ができる。
またテンプレートファイルの場所は
let g:pydocstring_templates_dir = '/path/to/temlate/'
デフォルトでは ~/.vim/template/pydocstring/ としている。
# pathogen とかを使っていると、~/.vim/bundle/vim-pydocstring/template/pydocstring/
テンプレート内で使用できる変数は {{_header_}} と {{_args_}} の二つだけ。
{{_header_}} はクラス名|関数名|メソッド名で、{{_args_}} は引数が全て挿入される。
Python のメソッドやクラスメソッドは引数の一つめを self や cls といったキーワードを設定する。
そのため docstring には表示しないようにデフォルトではなっている。
let g:pydocstring_ignore_args_pattern = 'self\|cls'
cls は docstring として表示したい場合は self といったように Vim 正規表現を設定する。
キーマップはデフォルトで
nmap <silent> <C-l> <Plug>(pydocstring)
と設定している。
class や def の上で <C-l> を押すと docstring が自動で挿入される。
class や def が無い所で <C-l> を実行するとコメント行が挿入するようにしている。
let g:pydocstring_enable_comment = 0
とする事でコメントを挿入しないようになる。
Vim Script を初めて書いていて、こういう書き方でいいのかなと悩みながら書いたので正直全く自信がない。
書いている途中で分からない事を呟いてたら、@kaoriya さんや @mattn_jp さんにアドバイス頂きました。
ありがとうございます!
2012-01-12
■[Vim] PyFlakes.vim で遭遇したエラー
2012/02/02 追記
Pull request したのがいつの間にか本体に取り込まれていた。
なので最新を git clone したら発生しないはず。
PyFlakes.vim で以下の条件の時にエラーが発生した。
aaa = bbb
こんな感じでやると以下のようなエラーがでる
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
Traceback (most recent call last):
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
File "<string>", line 1, in <module>
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
File "<string>", line 82, in check
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
File "/Users/xxx/.vim/bundle/pyflakes-vim/ftplugin/python/pyflakes/pyflakes/checker.py", li
ne 195, in __init__
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
self.handleChildren(tree)
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
File "/Users/xxx/.vim/bundle/pyflakes-vim/ftplugin/python/pyflakes/pyflakes/checker.py", li
ne 287, in handleChildren
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
self.handleNode(node, tree)
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
File "/Users/xxx/.vim/bundle/pyflakes-vim/ftplugin/python/pyflakes/pyflakes/checker.py", li
ne 309, in handleNode
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
handler(node)
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
File "/Users/xxx/.vim/bundle/pyflakes-vim/ftplugin/python/pyflakes/pyflakes/checker.py", li
ne 589, in ASSIGN
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
self.handleNode(node.value, node)
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
File "/Users/xxx/.vim/bundle/pyflakes-vim/ftplugin/python/pyflakes/pyflakes/checker.py", li
ne 309, in handleNode
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
handler(node)
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
File "/Users/xxx/.vim/bundle/pyflakes-vim/ftplugin/python/pyflakes/pyflakes/checker.py", li
ne 461, in NAME
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
if (os.path.basename(self.filename) == '__init__.py' and
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/posixpath.py", line 1
11, in basename
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
i = p.rfind('/') + 1
function <SNR>146_RunPyflakes の処理中にエラーが検出されました:
行 45:
AttributeError: 'NoneType' object has no attribute 'rfind'
なんで起きたかというと、pyflakes.vim でチェック時にバッファ名を取得していて、それを PyFlakes のチェックメソッドに渡している。
def check(buffer): filename = buffer.name contents = buffer[:] # -- snip -- else: # pyflakes looks to _MAGIC_GLOBALS in checker.py to see which # UndefinedNames to ignore old_globals = getattr(checker,' _MAGIC_GLOBALS', []) checker._MAGIC_GLOBALS = set(old_globals) | builtins w = checker.Checker(tree, filename)
この filename が None の場合(バッファ名が設定してない)に上記のようなエラーが起きる。
checker.Checker のコンストラクタの filename はデフォルト引数として '(none)' を期待しているっぽい。
とりあえず、以下のようにすればエラーにならない。
filename = '(none)' if filename is None else filename w = checker.Checker(tree, filename)
多分あまり遭遇しないケースな気がする。
2012-01-09
■[Vim] Syntastic が使用するコマンド
id:Layzie これ入れてみたけど、色々な言語に対応していて、便利だった。けど、どんなコマンドが必要なのかがドキュメントに無いので、ちょっと不便。(調べれば良いんだけど
はてなブックマーク - Layzieのだだ漏れ見聞記 - 2012年1月9日
仰る通り!
というわけで、.vim/bundle/syntastic/syntax_checkers 以下に書かれているのを grep "makeprg" *.vim を中心に見てみた。
| 言語 | ファイルタイプ | コマンド |
|---|---|---|
| AppleScript | applescript | osacompile |
| C | c | gcc |
| CoffeeScript | coffee | coffee |
| C++ | c++ | g++ |
| CSS | css | csslint |
| Cucumber | cucumber | cucumber |
| Cuda | cuda | nvcc*1 |
| Docbook | docbk | xmllint |
| Erlang | erlang | escript |
| eRuby | eruby | sed, ruby |
| Fortran | fortran | gfortran |
| Gentoo metadata | gentoo_metadata | xmllint |
| go-lang | go | 6g |
| Haml | haml | haml |
| Haskell | haskell | ghc-mod |
| Html | html | tidy |
| JavaScript | js | gjslint, jslint, jsl, jshint*2 |
| JSON | json | jsonlint, jsonval*3 |
| Less | less | lessc |
| Lua | lua | luac |
| MatLab | matlab | mlint |
| Perl | perl | perl*4 |
| PHP | php | php |
| Puppet | puppet | puppet |
| Python | python | flake8, pyflakes*5 |
| reST | rst | rst2pseudoxml.py*6 |
| Ruby | ruby | ruby |
| Sass | sass | sass |
| Scss | scss | sass |
| sh | sh | bash, zsh, sh*7 |
| Tcl | tcl | tclsh |
| TeX | tex | lacheck |
| Vala | vala | valac |
| XHTML | xhtml | tidy |
| XML | xml | xmllint |
| XSLT | xslt | xmllint |
| YAML | yaml | js-yaml |
| ZPT(Zope Page Template) | zpt | zptlint*8 |
つまり syntastic/syntax_checkers にファイルタイプに対応した Vim Script を書く事でオリジナルのチェックツールも作れるっぽい。
*1:let g:syntastic_nvcc_binary で変更可
*2:let g:syntastic_javascript_checker でいづれかを指定する
*3:let g:syntastic_json_checker でいづれかを指定する
*4:$VIMRUNTIME/tools/efm_perl.pl が必要。MacVim だとデフォルトで入ってた
*5:let g:syntastic_python_checker で指定可
*6:Docutils に入ってるっぽい
*7:シェバングから判定する
2012-01-06
■[Vim] Syntastic というシンタックスチェックプラグインが凄そう
Syntastic という文法チェック用のツールが凄そうというお話。
自分の Vim は PHP の場合 errormarker.vim の PHP シンタックスチェックをし、Python の場合は PyFlakes*1 を使いチェックをしている。
Syntastic はそのようなチェックを統合的に扱えるプラグインのよう。
At the time of this writing, syntax checking plugins exist for applescript, c, coffee, cpp, css, cucumber, cuda, docbk, erlang, eruby, fortran, gentoo_metadata, go, haml, haskell, html, javascript, json, less, lua, matlab, perl, php, puppet, python, rst, ruby, sass/scss, sh, tcl, tex, vala, xhtml, xml, xslt, yaml, zpt
scrooloose/syntastic - GitHub
現時点ではこれだけのファイルタイプに対応しているっぽい。
Pathogen を使用しているので、bundle の下で git clone をして以下のようなコードを書いてみた。
#!/usr/bin/env perl use strict; use warnings; foo = bar
上記のようなコードを保存した瞬間にエラーが出ている所に >> と印がついた。
また :Errors というコマンドを実行すると、QuickFix に表示される
GVim のような GUI だとおそらくバルーン表示とかも出来るっぽい。
設定も色々細かくできるようで、たとえばファイルを開いた瞬間にチェックをするとか、チェックとともに QuickFix を表示するといった機能もある。
また全て自動でチェックするのではなく、特定の言語は手動でチェックしたいという場合もオプションで設定できる。
たとえば自分は Python の場合は PyFlakes でチェックして欲しくて、Syntastic ではチェックして欲しくない。
let g:syntastic_mode_map = { 'mode': 'active', \ 'active_filetypes': ['perl'], \ 'passive_filetypes': ['php'] }
こんな感じで、 active_filetypes に自動でチェックして欲しいファイルタイプを記述し、passive_filetypes には明示的に :SyntasticCheck を呼ぶとチェックするような形になる。
上記の場合、ファイルタイプが Perl の場合は保存でチェック、PHP の場合は保存しても自動でチェックしない。
自分で errormarker.vim 使ってをチェックツールをごりごり書いている場合はあまりメリットはないかもしれないけど、新しい言語を始めたり、滅多に使わない言語を Vim で書く場合は凄く重宝しそう。
2012-01-05
■[Vim] Vim から rst ファイルをビルドする
Sphinx を使ってドキュメントを書く時は Vim で rst を書いて、シェルに戻り make html をやっていた。
この Vim からシェルに戻るという行為が煩わしくなってきたので、Vim で完結できないかと試行錯誤してた。
ディレクトリ構成はこんな感じ。
index.rst を開いているという仮定で、前提として Vim で index.rst を開いたら自動で index.rst の階層に移動するという設定をしている。
$ tree -L 2
.
├── Makefile
├── make.bat
└── source
├── _static
├── _templates
├── _themes
├── conf.py
└── index.rst
まず :make を使えば make コマンドが実行できるのでこれで出来ないかとやってみた。
:make -f ../Makefile html
sphinx-build -b html -d build/doctrees source build/html Error: Cannot find source directory. make: *** [html] Error 1
source ディレクトリが見つからないというもの。
Makefile を中身を見たけど読み方が良くわからないので、QuickRun.vim を使った。
let g:quickrun_config['rst'] = {'command': 'sphinx-build', 'cmdopt': '-b html -d ../build/doctrees ../source ../build/html'}
これで QuickRun.vim を実行すると無事にビルドできた。
ただしこの設定は rst ファイルが sources の下に無いと動かないけど、自分はこの構成で作る事が多いので、特に問題なさそう。
# 良い方法ないかな…。
2012-01-03
■[Vim] gf で Python の import で宣言しているファイルを開く
元ネタはこれ。
たった一行で人生が変わった.vimrcでの設定とその意味 - Weblog - hail2u.net
さっそく上記の設定をして、HTML 内で gf をしたら JavaScript なファイルに飛んでおお!っとなった。
JavaScript が行けるのなら、Python の import で宣言しているのもいけるはず!と言う事でやってみた。
.vimrc
autocmd FileType python setlocal includeexpr=FormatPyImport(v:fname) | setlocal path+=;/ function! FormatPyImport(str) return substitute(substitute(substitute(a:str, '^from \|^import ', '', ''), 'import \a\+', '', ''), '\.', '\/', 'g') endfunction
foo.py
# -*- coding: utf-8 -*- class Foo(object): pass
bar.py
# -*- coding: utf-8 -*- def bar(): pass
sample.py
#!/usr/bin/env python # -*- coding: utf-8 -*- import bar from foo import Foo
sample.py 内で bar や foo の上で gf を実行すると、foo.py や bar.py に移動できた。
が、.vimrc に書いたやりかたが全く気にくわない。
substitute を 3 回も書いてる所がもやもやする。
もっとエレガントな方法があるはず…と思ったが、普通バッファセレクタ系のプラグイン使うよねーとか思ったのでこのまま。
という事で 2012 年一発目は Vim ネタでした。

