Hatena::ブログ(Diary)

Heavens hell

2012-01-21

[] 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 を使えば同様にメソッドだけテストが出来るのでこれを使えばできそう。

またテストだけでなくて、たとえば SphinxreST なファイルをコンパイルする際に source と dest の位置を書き換えるとかも出来る。


こんな手間の掛かる事をしなくても QuickRun 本体で出来るのかもしれない。

まぁ自分専用なつもりで書いたので、いいかなーと。

例によって Vim Script はこれでええのかなーと思って書いた。

あとドキュメントは書いてないので ToDo としている。


追記(1/22)

との事なので、QuickRun 0.6 が正式にリリースされたら、このプラグインは用なしになります。

楽しみ!

*1:ただし "."は消すようにした

2012-01-18

[] 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 を初めて書いていて、こういう書き方でいいのかなと悩みながら書いたので正直全く自信がない。

書いている途中で分からない事を呟いてたら、@ さんや @ さんにアドバイス頂きました。

ありがとうございます!

2012-01-12

[] PyFlakes.vim で遭遇したエラー

2012/02/02 追記

Pull request したのがいつの間にか本体に取り込まれていた。

なので最新を git clone したら発生しないはず。


PyFlakes.vim で以下の条件の時にエラーが発生した。

  1. MacVim.app を起動する
  2. set filetype=pythonPython なファイルにする
  3. 適当にエラーが出るようなものを書く
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

[] Syntastic が使用するコマンド

id:Layzie これ入れてみたけど、色々な言語に対応していて、便利だった。けど、どんなコマンドが必要なのかがドキュメントに無いので、ちょっと不便。(調べれば良いんだけど

はてなブックマーク - Layzieのだだ漏れ見聞記 - 2012年1月9日

仰る通り!


というわけで、.vim/bundle/syntastic/syntax_checkers 以下に書かれているのを grep "makeprg" *.vim を中心に見てみた。

言語ファイルタイプコマンド
AppleScriptapplescriptosacompile
Ccgcc
CoffeeScriptcoffeecoffee
C++c++g++
CSScsscsslint
Cucumbercucumbercucumber
Cudacudanvcc*1
Docbookdocbkxmllint
Erlangerlangescript
eRubyerubysed, ruby
Fortranfortrangfortran
Gentoo metadatagentoo_metadataxmllint
go-langgo6g
Hamlhamlhaml
Haskellhaskellghc-mod
Htmlhtmltidy
JavaScriptjsgjslint, jslint, jsl, jshint*2
JSONjsonjsonlint, jsonval*3
Lesslesslessc
Lualualuac
MatLabmatlabmlint
Perlperlperl*4
PHPphpphp
Puppetpuppetpuppet
Pythonpythonflake8, pyflakes*5
reSTrstrst2pseudoxml.py*6
Rubyrubyruby
Sasssasssass
Scssscsssass
shshbash, zsh, sh*7
Tcltcltclsh
TeXtexlacheck
Valavalavalac
XHTMLxhtmltidy
XMLxmlxmllint
XSLTxsltxmllint
YAMLyamljs-yaml
ZPT(Zope Page Template)zptzptlint*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:シェバングから判定する

*8http://pypi.python.org/pypi/zptlint

2012-01-06

[] Syntastic というシンタックスチェックプラグインが凄そう

Syntastic という文法チェック用のツールが凄そうというお話。

scrooloose/syntastic - GitHub


自分の VimPHP の場合 errormarker.vimPHP シンタックスチェックをし、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 に表示される

f:id:heavenshell:20120107010252p:image

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 から 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

[] 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 ネタでした。