rohaniのブログ

ゆるっと自然言語処理奴。ときどき工作系バイト。

vimにPythonコーディング環境を整える

研究室で、新しいMacを譲り受けた。 色々とセットアップしたついでに、Vimもちょっと凝りたくなったので弄ってみたら、便利すぎて素のvimでコーディングできなくなった。。。

色々と四苦八苦して導入したので、綺麗にはまとめられないのだけれど、知見を残しておく。

環境

最終的な.vimrc

" ------------------------------------------------------------------------------
"dein settings
" ------------------------------------------------------------------------------

if !&compatible
  set nocompatible
endif

"Vim起動完了時にインストール
augroup PluginInstall
  autocmd!
  autocmd VimEnter * if dein#check_install() | call dein#install() | endif
augroup END

"各プラグインをインストールするディレクトリ
let s:plugin_dir = expand('~/.vim/bundle')

"dein.vimをインストールするディレクトリをランタイムパスへ追加
let s:dein_dir = s:plugin_dir . '/repos/github.com/Shougo/dein.vim'
execute 'set runtimepath+=' . s:dein_dir

"プラグイン読み込み&キャッシュ作成
if dein#load_state(s:plugin_dir)
  call dein#begin(s:plugin_dir)
  
  "インストールするプラグイン
  call dein#add(s:dein_dir)

  call dein#add('Shougo/deoplete.nvim', {'on_i': 1})
  let g:deoplete#enable_at_startup = 1
  if !has('nvim')
    call dein#add('roxma/nvim-yarp')
    call dein#add('roxma/vim-hug-neovim-rpc')
  endif
  call dein#add('Shougo/neosnippet.vim', {'on_i' : 1, 'on_ft' : 'snippet', 'depends' : 'neosnippet-snippets'})  "code snippet; 簡単に言うと定型構文をすばやく挿入できる機能
  call dein#add('Shougo/neosnippet-snippets')
  call dein#add('vim-syntastic/syntastic')
  call dein#add('Vimjas/vim-python-pep8-indent', {'on_ft' : 'python'})

  call dein#end()
  call dein#save_state()
endif

filetype plugin indent on
syntax enable 

"不足プラグインの自動インストール
if dein#check_install()
  call dein#install()
endif

"disabledされてるpluginを、削除
":call dein#recache_runtimepath()
"すること。
call map(dein#check_clean(), "delete(v:val, 'rf')")
call dein#recache_runtimepath()

highlight Normal ctermbg=none  "vimの背景色をターミナルの背景色と揃える
set backspace=indent,eol,start  "バックスペースキーでいろいろ消せるようにする

" ------------------------------------------------------------------------------
"見た目系
" ------------------------------------------------------------------------------
set number  "行番号
set ruler  "ルーラを表示
set hlsearch  "検索時にハイライト
set title  "titleを表示
colorscheme iceberg  "colorschemeを設定
set list  "不可視文字の可視化
set listchars=tab:»-,trail:-,extends:»,precedes:«,nbsp:%,eol:set wrap
set textwidth=0  "折り返しなし
set colorcolumn=80  "80文字の場所にハイライト
set showmatch  "一致するカッコを一瞬ハイライト
set matchtime=2
set cursorline  "cursorの行列をハイライト
set cursorcolumn

" ------------------------------------------------------------------------------
"syntasticの設定
" ------------------------------------------------------------------------------
if dein#is_sourced('syntastic')
  set statusline+=%#warningmsg#
  set statusline+=%{SyntasticStatuslineFlag()}
  set statusline+=%*
  
  let g:syntastic_always_populate_loc_list = 1
  let g:syntastic_auto_loc_list = 1
  let g:syntastic_check_on_open = 1
  let g:syntastic_check_on_wq = 0
  
  let g:syntastic_mode_map = { 'mode': 'active', 
    \ 'active_filetypes': ['vim', 'python', 'html'],
    \ 'passive_filetypes': [] }

  let g:syntastic_python_checkers = ["flake8"]

  let g:syntastic_error_symbol='✗'
  let g:syntastic_style_error_symbol = '✗'
  let g:syntastic_warning_symbol = '⚠'
  let g:syntastic_style_warning_symbol = '⚠'
endif

" ------------------------------------------------------------------------------
"neosnipetの設定
" ------------------------------------------------------------------------------
" Plugin key-mappings.
" Note: It must be "imap" and "smap".  It uses <Plug> mappings.
imap <C-k>     <Plug>(neosnippet_expand_or_jump)
smap <C-k>     <Plug>(neosnippet_expand_or_jump)
xmap <C-k>     <Plug>(neosnippet_expand_target)

" SuperTab like snippets behavior.
" Note: It must be "imap" and "smap".  It uses <Plug> mappings.
imap <expr><TAB>
 \ pumvisible() ? "\<C-n>" :
 \ neosnippet#expandable_or_jumpable() ?
 \    "\<Plug>(neosnippet_expand_or_jump)" : "\<TAB>"  "TABで選択
imap <expr><CR> 
 \ neosnippet#expandable() ? 
 \    "\<Plug>(neosnippet_expand_or_jump)" : 
 \    pumvisible() ? "<C-y>" : "<CR>"  "ENTERで決定

" For conceal markers.
if has('conceal')
  set conceallevel=2 concealcursor=niv
endif

"set snippet file dir
let g:neosnippet#snippets_directory='~/.vim/bundle/neosnippet-snippets/snippets/,~/.vim/snippets'

手順(というか七転八倒記録)

プラグインマネージャdein.vimを使ってみようと思い立った

プラグインマネージャをわざわざ導入する必要はない」といった意見も散見するけれど、入れてみた感想としては「プラグインのインストール&アンインストールがめっちゃ楽になる」ので入れておいて損はないと思う。

プラグインマネージャには色々あるのだけれど、せっかくなら日本で最近シェアの広そうなdein.vimに挑戦してみることにした。

github.com

Vimを最新バージョンにしようとしたが、--with-luaできなかった。けどなんとかなった。

最新のdein.vimを使うには、Vim 8.0以上が必要になる。 公式にも、If you use Vim 7.4, please use dein.vim ver.1.5 instead. と書いてあるし、実際Vim7.4に導入しようとしたらエラーを吐かれた。 したがって、まずVim自体のバージョンを確認し、必要とあればアップデートを行う必要がある。

私の環境はmacなので、Homebrewでアップデートを行った。

luaPython3オプションが+になっている必要があるので--with-luaするべし、といった情報が上がっていて、素直に--with-lua --with-python3したのだが、エラーが発生した。

こちらなどを参考にbrew doctorしてみたりしたが、どうも解決しなかった。

結局、luaは諦めてバージョンだけアップしようと

brew uninstall vim
brew install vim

したら、オプション指定をしなかったのに、luapython3+付いている最新版のvimが入ってくれた。

解せぬ。

(install->uninstallじゃなくて、brew reinstallだったかもしれない...)

dein.vimを手動でインストールした

公式のReadmeを参考に、dein.vimをインストールした。

様々な参考サイトで、.vimrcに「dein.vimがまだ入っていなければ最初にgit cloneする」という処理まで書いていたが、それをするとインストールログが表示されなくてwarning等を見逃すからなのか、はたまた別の問題があったのか、うまくいかなかった。

そんなわけで、最初のインストールはReadmeに倣って下記の3コマンドを叩いた。

インストールディレクトリについては、~/.cache/deinにしたり~/.vim.bundlesにしたり、、といった様々な流派が存在するようだけれど、私は一番参考になったサイト(Vimプラグインの管理にdein.vimを使う - yuhei.kagaya)に則って~/.vim/bundleにした。

mkdir ~/.vim/bundle
curl https://raw.githubusercontent.com/Shougo/dein.vim/master/bin/installer.sh > installer.sh
sh ./installer.sh ~/.vim/bundle

dein.vimの設定を書く

.vimrcにdein.vimの設定を書く。必要最低限の設定は、インストール時に表示されるので、それをコピペすれば良い親切設計になっている。 私も基本的にはそれをそのまま使わせてもらった。違いは、プラグインの削除に関する最後の3行だけだと思う。

" ------------------------------------------------------------------------------
"dein settings
" ------------------------------------------------------------------------------

if !&compatible
  set nocompatible
endif

"Vim起動完了時にインストール
augroup PluginInstall
  autocmd!
  autocmd VimEnter * if dein#check_install() | call dein#install() | endif
augroup END

"各プラグインをインストールするディレクトリ
let s:plugin_dir = expand('~/.vim/bundle')

"dein.vimをインストールするディレクトリをランタイムパスへ追加
let s:dein_dir = s:plugin_dir . '/repos/github.com/Shougo/dein.vim'
execute 'set runtimepath+=' . s:dein_dir

"プラグイン読み込み&キャッシュ作成
if dein#load_state(s:plugin_dir)
  call dein#begin(s:plugin_dir)
  
  "インストールするプラグイン
  call dein#add(s:dein_dir)

  call dein#end()
  call dein#save_state()
endif

filetype plugin indent on
syntax enable 

"不足プラグインの自動インストール
if dein#check_install()
  call dein#install()
endif

"disabledされてるpluginを、削除
call map(dein#check_clean(), "delete(v:val, 'rf')")
call dein#recache_runtimepath()

Pythonソースコードの自動補完と静的解析ができるjedi-vimを入れたかった。動かせなかった。

こちら(jedi-vim を導入してVimでPythonコードの補完をさせる - A little bit of everything)の記事を参考に、jedi-vimを入れた。

:help jedi-vimjedi-vimが入っていることは確かめられたのだが、補完のための.を押すと-- オムニ補完 (^O^N^P) パターンは見つかりませんでしたと出てきて、正常に動作しなかった。 このような状況は幾件か報告されていて、例えばここなどを参考にvimpythonのバージョンとosのpythonのバージョンやパスを確かめてみたりしたのだけれど、結局原因は見つけられなかった。

そんなわけで、今回はjedi-vimを見送ることにして、自動補完と静的解析を別々に行う他のツールを試すことにした。

自動補完のためのdeoplete.nvimを導入

自動補完にも、neocompleteneocomplcacheなど、色々なものがあるらしい。 最初はよく名を見るneocompleteをインストールしようとしたのだけれど、どうもうまく動作しなかったので他の手段を模索した。 調べていくと、neocompletedeinを作ったShougoさんが手がけたツールらしいのだけれど、どうもメンテナンスは現在行われていないらしく、新たに作られたのがdeopleteという自動補完ツールらしいと分かった。 そんなわけで、今回はdeoplete.nvimを使ってみることにした。

github.com

導入方法は公式のReadmeを参考にした。deinでの導入方法と、併せて使いたいツールが載っている。

上記のインストールするプラグインのところに下記を記載して、vimを再度開くだけで導入完了。

  call dein#add('Shougo/deoplete.nvim', {'on_i': 1})
  let g:deoplete#enable_at_startup = 1
  if !has('nvim')
    call dein#add('roxma/nvim-yarp')
    call dein#add('roxma/vim-hug-neovim-rpc')
  endif

neosnippet.vimも入れたら、コード補完が更に簡単になった

スニペットとは繰り返し登場するコードの断片、またはそれをすぐに呼び出せるように管理するエディタの機能のことです。自分がよく使用する記述をスニペットにしておけばいちいち検索したり、昔書いたソースを探ってみたりすることなく、使いたい時に正確な記述をサッと呼び出して使うことができます。(一度書いたコードは二度と探さない!スニペットを究めて快適コーディング!【HTML, CSS, JavaScript】 | WebNAUT

折角deopleteを入れたので、同じShougoさん作のneosnippet.vimも入れてみた。

github.com

まず、プラグインインストールのdeoplete関連の下のところに下記を書いてインストールする。

call dein#add('Shougo/neosnippet.vim', {'on_i' : 1, 'on_ft' : 'snippet', 'depends' : 'neosnippet-snippets'})  "code snippet; 簡単に言うと定型構文をすばやく挿入できる機能
call dein#add('Shougo/neosnippet-snippets')

続いて、TABで補完対象を選択し、ENTERで決定できるように、dein関係の設定の下に下記を書く。

" ------------------------------------------------------------------------------
"neosnipetの設定
" ------------------------------------------------------------------------------
" Plugin key-mappings.
" Note: It must be "imap" and "smap".  It uses <Plug> mappings.
imap <C-k>     <Plug>(neosnippet_expand_or_jump)
smap <C-k>     <Plug>(neosnippet_expand_or_jump)
xmap <C-k>     <Plug>(neosnippet_expand_target)

" SuperTab like snippets behavior.
" Note: It must be "imap" and "smap".  It uses <Plug> mappings.
imap <expr><TAB>
 \ pumvisible() ? "\<C-n>" :
 \ neosnippet#expandable_or_jumpable() ?
 \    "\<Plug>(neosnippet_expand_or_jump)" : "\<TAB>"  "TABで選択
imap <expr><CR> 
 \ neosnippet#expandable() ? 
 \    "\<Plug>(neosnippet_expand_or_jump)" : 
 \    pumvisible() ? "<C-y>" : "<CR>"  "ENTERで決定

" For conceal markers.
if has('conceal')
  set conceallevel=2 concealcursor=niv
endif

"set snippet file dir
let g:neosnippet#snippets_directory='~/.vim/bundle/neosnippet-snippets/snippets/,~/.vim/snippets'

この設定をして初めて、私のトリアタマでもneosnippetの恩恵が受けられるようになった。笑

静的文法解析のためのSyntasticflake8

自分のコードがPEP8に準拠できてるのかをチェックするために、flake8Syntasticをインストールした。

flake8.pycqa.org

github.com

flake8pip installしておく。

pip3 install flake8

あとは、プラグインインストールのところに以下を書いて、vim再起動でインストール。

call dein#add('vim-syntastic/syntastic')

続いて、Syntasticの細かい設定をdein関係の設定の後に追記。 解析結果をどう表示するのか?関係が記述されているのかな。 基本的な設定は、SyntasticのReadmeに載っているのでそのまま書けばいい。Python構文解析のときだけflake8を使うように変更してある。

" ------------------------------------------------------------------------------
"syntasticの設定
" ------------------------------------------------------------------------------
if dein#is_sourced('syntastic')
  set statusline+=%#warningmsg#
  set statusline+=%{SyntasticStatuslineFlag()}
  set statusline+=%*
  
  let g:syntastic_always_populate_loc_list = 1
  let g:syntastic_auto_loc_list = 1
  let g:syntastic_check_on_open = 1
  let g:syntastic_check_on_wq = 0
  
  let g:syntastic_mode_map = { 'mode': 'active', 
    \ 'active_filetypes': ['vim', 'python', 'html'],
    \ 'passive_filetypes': [] }

  let g:syntastic_python_checkers = ["flake8"]

  let g:syntastic_error_symbol='✗'
  let g:syntastic_style_error_symbol = '✗'
  let g:syntastic_warning_symbol = '⚠'
  let g:syntastic_style_warning_symbol = '⚠'
endif

PEP8準拠のインデントを自動挿入してくれるvim-python-pep8-indentは文の途中で折り返すときに便利

入れてみて意外と便利だったのはvim-python-pep8-indent。PEP8的に、1行80文字でコーディングしようとすると、どうしても折り返しが必要になる。そんなとき、自分で考えなくても適切なインデントを追加してくれるこのプラグインにはとても助けられている。

github.com

プラグインインストールのところに以下を書くだけ。

call dein#add('Vimjas/vim-python-pep8-indent', {'on_ft' : 'python'})

インデントを可視化してくれるvim-indent-guidesを入れたかった。動かなかった。

可視化してくれるときもあるけど、概ね可視化してくれない、というよくわからない状況になった。

ちなみに、インストール部分には下記を書き、

call dein#add('nathanaelkane/vim-indent-guides', {'lazy' : 1})
let g:indent_guides_enable_on_vim_startup = 1

詳細設定には以下を設定した。

" ------------------------------------------------------------------------------
"vim-indent-guidesの設定
" ------------------------------------------------------------------------------
let g:indent_guides_auto_colors=0
autocmd VimEnter,Colorscheme * :hi IndentGuidesOdd   ctermbg=black
autocmd VimEnter,Colorscheme * :hi IndentGuidesEven ctermbg=darkgrey
let g:indent_guides_exclude_filetypes = ['help', 'nerdtree', 'tagbar']
let g:indent_guides_start_level = 2
let g:indent_guides_guide_size = 1

結局、動かない原因はわからなかった。

カーソル位置のハイライトがあるし、インデントがおかしいときはSyntasticが教えてくれるから、今回は諦めてしまった。

感想

こんな感じで、はじめてのvimプラグイン導入をしてみた。

使い始めてみると驚くほど便利で、すぐに素のvimが使えなくなってしまった。。。 自分にあまり権限が与えら得ていない環境とかだと、今回インストールしたような新しめのプラグインを導入することができなくてギャップに苦しむことになる(そのときの妥協案環境の話もまた今度メモしたい)のだけれど、それを鑑みてもやっぱり上記のプラグインから得られる恩恵は大きくて、vimへの愛着はとても増したと思う。

それから、今回七転八倒していく中で、沢山の人達の無償の貢献がvim界隈を支えているのだということに気づいた。自分もいつか何かのかたちで貢献できればなと思った。