はじめてのVimプラグイン作成（autopep8）～ GitHubの練習を兼ねて，既存レポジトリのフォーク／クローン／更新／プッシュを行ってみた


# 背景

## VimでのPython開発環境構築

これまでPythonのプログラム開発は [PyCharm](https://www.jetbrains.com/pycharm/) で行ってきましたが，Vimに乗り換えようと試行錯誤しているところです．開発環境を整備していくなかで，PEP8を遵守するようにコードを自動で修正するプラグイン [hachibeeDI/vim-operator-autopep8](https://github.com/hachibeeDI/vim-operator-autopep8) がPython3でうまく動作しないことが分かりました．初心者なもので，自分の設定が間違っている可能性もあります．ただ，せっかくなので，GitHubの練習も兼ねて，はじめてのVimプラグインを作成してみることにしました．

[参考URL]
* [GitHub のフォーク （fork） とプルリクエスト （pull request） の使い方](http://cuaoar.jp/2013/03/github-fork-pull-request.html)
* [Windows 10にGVimベースのPython開発環境を構築する（１）インストール偏](http://qiita.com/daizutabi/items/72fa1c2f05d721e5cd48) 

## Python 3で動かないわけ

[hachibeeDI/vim-operator-autopep8](https://github.com/hachibeeDI/vim-operator-autopep8) は内部で，[andviro/flake8-vim](https://github.com/andviro/flake8-vim) の `flake8#auto` 関数を呼び出してコードの修正を行わせています．その部分は，

```vim:ftplugin/python/flake8.vim(L120-136)
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によるコードのスタイル・シンタックスチェックが主な仕事だと思います．これらの機能は，

* [vim-watchdogsで快適なシンタックスチェック](http://h-miyako.hatenablog.com/entry/2014/10/18/031830)
* [[vim]watchdogs.vim つくりました](http://d.hatena.ne.jp/osyo-manga/20120924/1348473304) 

を参考にしてより快適な動作を実現できましたので，PEP8・pyflakeの機能はもはや必要ありません．よって，今回のプラグインはautopep8の機能に限定します．上記の`flake8#auto`関数を修正しながら vim-operator-autopep8 に取り入れてみます．
また，andviro/flake8-vimでは，flake8やautopep8などのPythonパッケージを自前で持っているようでした．私は，できる限りPythonパッケージはPythonのパッケージマネージャ(conda，pip)に一元管理させたいと思いますので，今回のプラグインはautopep8パッケージを持ちません．その代わりに，別途，

```bat
$ conda install pep8
$ pip install autopep8
```

のようにして，pep8，autopep8の2パッケージをPython環境にインストールする必要があります．2つのパッケージを別々にインストールしている理由については，文末に記載しています．


# GitHubデビュー

## フォークしてみる

Vimプラグインを作成するためにGitHubの [アカウント](https://github.com/daizutabi/) をはじめて作りました．[hachibeeDI/vim-operator-autopep8](https://github.com/hachibeeDI/vim-operator-autopep8) のページにとんで，フォークボタンをクリックします．無事，自分のレポジトリにvim-operator-autopep8が追加されました．

## クローンしてローカルで修正

フォークしたレポジトリをローカルに落とします．Vimのプラグイン作成は，`~\Documents\Vim\plugin`下で行うこととします．コマンドプロンプトを開き，

```bat
$ cd Documents\Vim\plugin
$ git clone https://github.com/daizutabi/vim-operator-autopep8.git
```

でローカルにクローンが作られました．

次に，andviro/flake8-vimの`flake8#auto`関数を参考にして，[autoload/operator/autopep8.vim](https://github.com/daizutabi/vim-operator-autopep8/blob/master/autoload/operator/autopep8.vim)内に，`s:autopep8`関数を新規に作成します．この関数内で，`autopep8.fix_code`関数を使ってコード修正を行います．まだ慣れないVimスクリプトではありますが，実際に修正している部分はPythonの埋め込みコード（？）なので，難しいことはありません．また，vim-operator-autopep8は，オペレータとして実装されています．そのため，例えば，関数内でautopep8を実行すると，コードの修正後にカーソルは関数の先頭に移動します．この挙動は時として望ましくありません．よく使うと想定される現在行の修正とバッファ全体の修正では，カーソルがなるべく動かないのが理想です．そこで，これらの機能を持った関数を追加しました（もはやオペレータにはなっていません）．これらの新関数を使うために，[plugin/operator/autopep8.vim](https://github.com/daizutabi/vim-operator-autopep8/blob/master/plugin/operator/autopep8.vim)では，

```vim: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をロードしました．

```vim:_vimrc
NeoBundleLazy 'vim-operator-autopep8', {
\   'base': '~/Documents/Vim/plugin',
\   'type': 'nosync',
\   'autoload': {
\       'mappings' : ['<Plug>(operator-autopep8)',
\                     '<Plug>Autopep8Line',
\                     '<Plug>Autopep8Entire'],
\   }
\}
```


## 動作確認

キーマッピングを次のようにしたときのスクリーンショットを以下に示します．[bps/vim-textobj-python](https://github.com/bps/vim-textobj-python)を使って，Python関数単位のモーションを`af`で指定しています．また，`\w`でflake8によるスタイルチェック・シンタックスチェックを行っています．

```vim:after/ftplugin/pytyon.vim
map ,p <Plug>(operator-autopep8)
map ,pp <Plug>Autopep8Line
map <F8> <Plug>Autopep8Entire
```

![autopep8.gif](https://qiita-image-store.s3.amazonaws.com/0/115267/7643b49d-9f14-7b48-add0-f3f6b13fa506.gif)

### 注意事項

[pandas](http://pandas.pydata.org/)パッケージを使っている場合には注意が必要です．インデックスに名前を持つ[DataFrame](http://pandas.pydata.org/pandas-docs/stable/dsintro.html#dataframe)の標準出力では，インデックスの名前の後から行末までをスペースで埋めています．[Doctest](http://docs.python.jp/3.5/library/doctest.html)でDataFrameの出力をテストするときには，この形式どおりに忠実に書く必要があります．しかし，autopep8を実行してしまうと，この行末までのスペースが削除されてしまい，Doctestにパスしなくなります．具体的な例を以下に示しておきます．ここでは，`\td`で[nose](https://nose.readthedocs.org/en/latest/)経由でDoctestを実行しています．autopep8実行後（&lt;F8&gt;）に，行末のスペースが削除されたことが原因でテストに失敗していることが分かります．

![autopep8_pandas.gif](https://qiita-image-store.s3.amazonaws.com/0/115267/172e50a3-6c52-5138-9cf0-ea8bce06dbb2.gif)

## いよいよプッシュ

ローカルのレポジトリでコミットした後，GitHubにプッシュします．masterブランチのまま行いましたが，これでよいのかまだ分かっていません．こういう時の作法については今後勉強したいと思います．

[2016/02/29追記] GitHub上のレポジトリが更新されましたので，`_vimrc`を以下のように変更します．

```vim:_vimrc
NeoBundleLazy 'daizutabi/vim-operator-autopep8', {
\   'autoload': {
\       'mappings' : ['<Plug>(operator-autopep8)',
\                     '<Plug>Autopep8Line',
\                     '<Plug>Autopep8Entire'],
\   }
\}
```

# 補足：Pythonパッケージのインストールについて
本文中，pep8とautopep8の両パッケージを

```bat
$ conda install pep8
$ pip install autopep8
```

のようにしてPython環境にインストールしました．2行に分けているのは，condaがautopep8を管理対象にしていないためです．`pip install autopep8`単独では，自動でpipがpep8をインストールしてしまいます．私はなるべくcondaでパッケージ管理をしたいと思っているので，condaが扱えるpep8はcondaを使ってインストールしました．condaを使う理由は，Pythonのバージョンにマッチしたもののみをインストールしてくれるからです．（Python2でしか動かないパッケージがインストールされることがありません．）新規にパッケージをインストールする際には，

```bat
$ conda search <package-name>
```

として，condaを使ってインストールできないかチェックすることにしています．