背景
VimでのPython開発環境構築
これまでPythonのプログラム開発は PyCharm で行ってきましたが,Vimに乗り換えようと試行錯誤しているところです.開発環境を整備していくなかで,PEP8を遵守するようにコードを自動で修正するプラグイン hachibeeDI/vim-operator-autopep8 がPython3でうまく動作しないことが分かりました.初心者なもので,自分の設定が間違っている可能性もあります.ただ,せっかくなので,GitHubの練習も兼ねて,はじめてのVimプラグインを作成してみることにしました.
[参考URL]
Python 3で動かないわけ
hachibeeDI/vim-operator-autopep8 は内部で,andviro/flake8-vim の flake8#auto
関数を呼び出してコードの修正を行わせています.その部分は,
function! flake8#auto(l1, l2) "{{{
cclose
while s:last_sign >= g:PyFlakeSignStart
execute "sign unplace" s:last_sign
let s:last_sign -= 1
endwhile
let s:matchDict = {}
call setqflist([])
exec s:pycmd . ' << EOF'
start, end = int(vim.eval('a:l1'))-1, int(vim.eval('a:l2'))
enc = vim.eval('&enc')
lines = fix_lines(list(unicode(x, enc, 'replace') for x in vim.current.buffer[start:end])).splitlines()
res = [ln.encode(enc, 'replace') for ln in lines]
vim.current.buffer[start:end] = res
EOF
endfunction "}}}
となっています.私のPCには,Python 2系は入っていないので,(このファイルの冒頭で定義される変数の)s:pycmd
はpy3
となっているはずです.それなのに,下から5行目でunicode
関数が使われているのでエラーになるものと思われます.ここをPython 3系で動くように変更したいと思います.(私はPython 2系は全く使わないので,下記ではPython 3系のみの対応になります.)
方針
andviro/flake8-vimはコードの修正よりも,PEP8・pyflakeによるコードのスタイル・シンタックスチェックが主な仕事だと思います.これらの機能は,
を参考にしてより快適な動作を実現できましたので,PEP8・pyflakeの機能はもはや必要ありません.よって,今回のプラグインはautopep8の機能に限定します.上記のflake8#auto
関数を修正しながら vim-operator-autopep8 に取り入れてみます.
また,andviro/flake8-vimでは,flake8やautopep8などのPythonパッケージを自前で持っているようでした.私は,できる限りPythonパッケージはPythonのパッケージマネージャ(conda,pip)に一元管理させたいと思いますので,今回のプラグインはautopep8パッケージを持ちません.その代わりに,別途,
$ conda install pep8
$ pip install autopep8
のようにして,pep8,autopep8の2パッケージをPython環境にインストールする必要があります.2つのパッケージを別々にインストールしている理由については,文末に記載しています.
GitHubデビュー
フォークしてみる
Vimプラグインを作成するためにGitHubの アカウント をはじめて作りました.hachibeeDI/vim-operator-autopep8 のページにとんで,フォークボタンをクリックします.無事,自分のレポジトリにvim-operator-autopep8が追加されました.
クローンしてローカルで修正
フォークしたレポジトリをローカルに落とします.Vimのプラグイン作成は,~\Documents\Vim\plugin
下で行うこととします.コマンドプロンプトを開き,
$ cd Documents\Vim\plugin
$ git clone https://github.com/daizutabi/vim-operator-autopep8.git
でローカルにクローンが作られました.
次に,andviro/flake8-vimのflake8#auto
関数を参考にして,autoload/operator/autopep8.vim内に,s:autopep8
関数を新規に作成します.この関数内で,autopep8.fix_code
関数を使ってコード修正を行います.まだ慣れないVimスクリプトではありますが,実際に修正している部分はPythonの埋め込みコード(?)なので,難しいことはありません.また,vim-operator-autopep8は,オペレータとして実装されています.そのため,例えば,関数内でautopep8を実行すると,コードの修正後にカーソルは関数の先頭に移動します.この挙動は時として望ましくありません.よく使うと想定される現在行の修正とバッファ全体の修正では,カーソルがなるべく動かないのが理想です.そこで,これらの機能を持った関数を追加しました(もはやオペレータにはなっていません).これらの新関数を使うために,plugin/operator/autopep8.vimでは,
nnoremap <silent> <Plug>Autopep8Line :<C-U>call operator#autopep8#do_line()<CR>
nnoremap <silent> <Plug>Autopep8Entire :<C-U>call operator#autopep8#do_entire()<CR>
とマッピングしています.
[2016/02/29追記] なお,開発中は_vimrc
に下記のように記述し,vim-operator-autopep8をロードしました.
NeoBundleLazy 'vim-operator-autopep8', {
\ 'base': '~/Documents/Vim/plugin',
\ 'type': 'nosync',
\ 'autoload': {
\ 'mappings' : ['<Plug>(operator-autopep8)',
\ '<Plug>Autopep8Line',
\ '<Plug>Autopep8Entire'],
\ }
\}
動作確認
キーマッピングを次のようにしたときのスクリーンショットを以下に示します.bps/vim-textobj-pythonを使って,Python関数単位のモーションをaf
で指定しています.また,\w
でflake8によるスタイルチェック・シンタックスチェックを行っています.
map ,p <Plug>(operator-autopep8)
map ,pp <Plug>Autopep8Line
map <F8> <Plug>Autopep8Entire
注意事項
pandasパッケージを使っている場合には注意が必要です.インデックスに名前を持つDataFrameの標準出力では,インデックスの名前の後から行末までをスペースで埋めています.DoctestでDataFrameの出力をテストするときには,この形式どおりに忠実に書く必要があります.しかし,autopep8を実行してしまうと,この行末までのスペースが削除されてしまい,Doctestにパスしなくなります.具体的な例を以下に示しておきます.ここでは,\td
でnose経由でDoctestを実行しています.autopep8実行後(<F8>)に,行末のスペースが削除されたことが原因でテストに失敗していることが分かります.
いよいよプッシュ
ローカルのレポジトリでコミットした後,GitHubにプッシュします.masterブランチのまま行いましたが,これでよいのかまだ分かっていません.こういう時の作法については今後勉強したいと思います.
[2016/02/29追記] GitHub上のレポジトリが更新されましたので,_vimrc
を以下のように変更します.
NeoBundleLazy 'daizutabi/vim-operator-autopep8', {
\ 'autoload': {
\ 'mappings' : ['<Plug>(operator-autopep8)',
\ '<Plug>Autopep8Line',
\ '<Plug>Autopep8Entire'],
\ }
\}
補足:Pythonパッケージのインストールについて
本文中,pep8とautopep8の両パッケージを
$ conda install pep8
$ pip install autopep8
のようにしてPython環境にインストールしました.2行に分けているのは,condaがautopep8を管理対象にしていないためです.pip install autopep8
単独では,自動でpipがpep8をインストールしてしまいます.私はなるべくcondaでパッケージ管理をしたいと思っているので,condaが扱えるpep8はcondaを使ってインストールしました.condaを使う理由は,Pythonのバージョンにマッチしたもののみをインストールしてくれるからです.(Python2でしか動かないパッケージがインストールされることがありません.)新規にパッケージをインストールする際には,
$ conda search <package-name>
として,condaを使ってインストールできないかチェックすることにしています.