VimでNeoSnippetを活用してRailsでスニペットファイルを細かく切り替える


[neosinippet.vim](https://github.com/Shougo/neosnippet.vim)いつもお世話になっておりますm(__)m

# 背景

## なんでこんなことやるの？

スニペット便利だなーって思ってよく活用してます。
ただいろいろ定義してると候補が増えてくるので
もうちょっと賢くフィルタリングしてもらいたいなーって思ってました。

例えばRailsアプリ作ってる時に、modelファイルをいじってるのに
controller用のスニペットが候補に上がってくることがあります。

うまく絞り込んでくれれば候補選択する手間が省けるので
なんとかできないかなーって思ってました。

例えばこんな感じ。

- modelのファイル      → model用のスニペット
- controllerのファイル → controller用のスニペット
- viewのファイル       → view用のスニペット

・・・以下略


RailsじゃなくてもSymfonyなど他のフレームワーク使っていても
ファイルが役割分担されているのでその役割毎にうまくスニペットを
切り替えたいという欲求が有るかと思います。




## 課題

普通にファイルタイプ毎にスニペットを変えることはできそうです。

[ここ](http://blog.imagedrive.jp/diary/2013-08-15/snippet-file-for-epub)や[ここ](http://kuchitama.hateblo.jp/entry/20121204/p1)とかが参考になります。

でもこれだと拡張子の区別までしかできません。

Railsでcontrollerにしろmodelにしろviewにしろ全部.rbです。
百歩譲ってxxx_spec.rbのようなファイルならいいのですが、
ファイル名だけでは判断できない場合にうまいこと行きません。


そんな細かいこときにせず全候補出してそこから選べよという感じ
かもしれませんが気になって夜も眠れませんでした。


そして私のグーグル力では打開策がちょっとわかりませんでした。


## 解決策

大した策ではありませんが、方針は以下です。

- ファイル名だけでなくディレクトリパスまでフィルタリング条件に入れる！！！

Railsだと、以下の様なディレクトリ階層になっています。

```
- app/
    - assets/
    - controllers/
    - models/
    - views/
    - helpers/
    - mailers/
・・・・以下略
```

上記のようにディレクトリごとにスニペットを切り替えられたら
幸せだろうなーと思いました。

# vimで設定してみちゃう☆☆♪

## 事前準備

neosnippet.vimを導入しておきます。
必要に応じてneocomplete.vimなどもご一緒に。

NeoBundleを使うとプラグイン管理が楽ちんです。

## vimrcで設定記述


```.vimrc
" .vimrc

" スニペットファイルの場所指定
let g:neosnippet#snippets_directory='~/.vim/snippets/'


" ファイル名で区別出来る場合は直接呼び出し
" ファイル名で区別できない場合は一旦関数に投げる
augroup filetypedetect
  autocmd!  BufEnter *_spec.rb NeoSnippetSource ~/.vim/snippets/rspec.snip
  autocmd!  BufEnter *rb call s:LoadRailsSnippet()
augroup END

" rails用スニペット呼び出し関数
function! s:LoadRailsSnippet()

  " カレントディレクトリのディレクトリパス（絶対パス）取得
  let s:current_file_path = expand("%:p:h")

  " appフォルダ内でなければ無視
  if ( s:current_file_path !~ "app/" )
    return

  " app/modelsフォルダ内ならば
  elseif ( s:current_file_path =~ "app/models" )
    NeoSnippetSource ~/.vim/snippets/model.rails.snip

  " app/controllersフォルダ内ならば
  elseif ( s:current_file_path =~ "app/controllers" )
   NeoSnippetSource ~/.vim/snippets/controller.rails.snip

  " app/viewsフォルダ内ならば
  elseif ( s:current_file_path =~ "app/views" )
    NeoSnippetSource ~/.vim/snippets/view.rails.snip

  " app/helpersフォルダ内ならば
  elseif ( s:current_file_path =~ "app/helpers" )
    NeoSnippetSource ~/.vim/snippets/helper.rails.snip

  " app/assetsフォルダ内ならば
  elseif ( s:current_file_path =~ "app/assets" )
    NeoSnippetSource ~/.vim/snippets/asset.rails.snip
  endif
endfunction
```

ちなみにautocmdの部分で*.rbではなく*rbとしているのは.erbも意識しているためです

本当は変数とか使ってやりたかったのですがNeoSnippetSourceが何故か文字列を受け取らなかったのでsnipファイルパスをベタ打ちしています。

誰かもっときれいなのおしえてください＞＜



# neosnippetのちょっとしたTips

## スニペットを自分で用意するのが面倒（誰か作って！

- [neosnippet-snippets](https://github.com/Shougo/neosnippet-snippets)をインストール
- [ここ](http://qiita.com/joker1007/items/71d333341dca457b9b2e)を参考にする
- [ここ](https://github.com/honza/vim-snippets/tree/master/snippets)を参考にする


## 行中でも候補選択できるようにしたい

行頭ならたいてい反応しますが、行中だと反応しません。

例えば&lt;p&gt;|&lt;/p&gt;の「|」の部分にカーソルがあってここで呼び出そうとして反応しません。
特にrailsでviewのerbファイル


その場合にはスニペットファイルでオプションを設定しましょう。


```
snippet vif
abbr    <%= if ... %> ... <% end %>
options word
	<% if ${1:condition} %>
		${2}
	<% end %>
```


上記のようにoptions wordを指定すると行中でも反応するようになります。
デフォルトは行頭だけなので必ず指定しましょう。


## 候補選択の時、TABキーでぐるぐる候補を切り替えたい

neosnippetとneocomplete入れて公式記載の設定をするとTABキーバインドが
かぶっています。

するとどうなるかというと、候補一覧が出ている状況でTABを押したら
１つ目の候補が選択されてそのまま入力完して候補一覧が閉じられてしまいます。

C-nで候補選択しろよって話ですが、TABだけでぐるぐる回して候補切り替えていきたい
のでTABでは候補を展開しないようにしました。

公式オススメの方法ではありませんがこんなやり方もありますよというご参考で。

```
" neocomplete.vim公式どおり
inoremap <expr><TAB>  pumvisible() ? "\<C-n>" : "\<TAB>"


" neosnippet.vim公式指定をちょっといじる
imap <expr><CR> neosnippet#expandable() <bar><bar> neosnippet#jumpable() ? "\<Plug>(neosnippet_expand_or_jump)" : "\<CR>"
imap <expr><TAB> neosnippet#jumpable() ?
\ "\<Plug>(neosnippet_expand_or_jump)"
\: pumvisible() ? "\<C-n>" : "\<TAB>"
smap <expr><TAB> neosnippet#jumpable() ?
\ "\<Plug>(neosnippet_expand_or_jump)"
\: "\<TAB>"
```

要はTABをjumpable()の時だけ反応して、expandableの時に反応しないようにして
代わりにexpandable()の時はEnterキーを反応させるようにしました。

jumpable()はスニペット展開後にカーソル飛び先があるかどうかを判断するもので
タブでカーソル移動は便利なので残しています。


・・・がちゃんと読んでないので違うかもしれませんーー；





# 参考

より詳しく知りたい場合は以下を参考にしましょう。

- ヘルプ (:help neosnippet-snippet-syntax)