deniteのgrepにVisualModeの力を授ける


denite.nvimとは

fzfとかのファインダーみたいなやつです。
様々な操作のインターフェースを提供、拡張ができるようになっており、
なんでも置き換えることができる黒魔術に似たなにかだと思っています。

grepをより便利に

gif内でgrepのデモしていますが、
今回は、Visualモードで選択した文字列をdenite使ってgrepできるようにします。

選択文字列を取得し、deniteへ

グーグル先生に聞いてみると
https://stackoverflow.com/questions/1533565/how-to-get-visually-selected-text-in-vimscript
が見つかりました。

getline("'<")[getpos("'<")[2] - 1:getpos("'>")[2] - 1]

とりあえず、関数化します。

function! g:GetVisualWord() abort
  let word = getline("'<")[getpos("'<")[2] - 1:getpos("'>")[2] - 1]
  return word
endfunction

Visualモードの時のみ、実行したいのでマッピング

xnoremap <silent> <SPACE>fg :Denite grep:::`GetVisualWord()`<CR>
" Space file grepって感じのマッピング

ただ、メタ文字とか入ると、正しいgrepされません。(grep個人差によります)
必要そうなそこらへんの文字をエスケープする関数を新たに作成。

function! g:GetVisualWordEscape() abort
  let word = substitute(GetVisualWord(), '\\', '\\\\', 'g')
  let word = substitute(word, '[.?*+^$|()[\]]', '\\\0', 'g')
  return word
endfunction
xnoremap <silent> <SPACE>fg :Denite grep:::`GetVisualWordEscape()`<CR>
" エスケープバージョンに変更

これで、こうなります。
Dente fileのgrepと、get(gのgrepができていますね!

コピーしてペして検索っていう手間がなくなって楽になりました!

大雑把に処理を確認

例えば、aiueo文字列があり、iueを選択した状態と仮定します。

01234 " 添字
12345 " 列番号
aiueo " 文字
 < >  " 選択文字: iue

一つずつ処理結果を見ていきます。


" 1.選択している行の文字列を取得
getline("'<")
" Output: aiueo

" 2.選択文字列の初めの文字位置を取得
getpos("'<")[2]
" Output: 2

" 3.選択文字列の最後の文字位置を取得
getpos("'>")[2]
" Output: 4

" 4.開始位置と終了位置の添字を取得
getpos("'<")[2] - 1
" Output: 1
getpos("'>")[2] - 1
" Output: 3

" 5.上記を合わせて、選択文字を取得
" getline("'<")[1:3]
getline("'<")[getpos("'<")[2] - 1:getpos("'>")[2] - 1]
" Output: iue

ポイント部分の確認

'<'>

'<は、最後に選択された、初めの行や文字を意味します。
逆に、'>は、最後に選択された、最後の行や文字を意味しています。
試しに、どこか選択したあと、ノーマルモードで、'<実行したら、選択した初めの行に飛びます。

getpos

つまり、getline("'<")は、初めの選択行の文字列を出力しています。

getposは、[bufnum, lnum, col, off]のリストを返します。
開始文字の位置を知りたいのでcolの2番目が必要ですので[2]です。

Denite grep

Denite grep:0:1:2は、位置によってそれぞれに意味があります。(:h denite-source-grepで詳しく)

  • 0は、指定のパスでgrep
  • 1は、指定のgrepオプションで実行
  • 2は、指定の文字列でgrep

という役割があるので、Denite grep:::grep-wordという感じで使います。

さいごに

選択文字列をgrepという直感的な操作になりました。
この選択文字取得方法だと、マルチバイト対応してないので、今後改良してきたいですね(すでにあるかな?知ってたら教えてください!!)

denite.nvimは非常に便利です!
コア部分は、Pythonで書かれているので、拡張もPythonでできます!(デジャブ)

筆者は、Vimアドベントカレンダー 12日にも参加しており(ただの宣伝)、Gitの差分関係(ブランチ間のコミットログ一覧表示、ブランチ間ファイル一覧表示、差分確認)を操作できるdenite拡張プラグインを紹介する予定ですので、興味持った方は、ぜひぜひチェックお願いします。