vimのtextobjで検索文字列を指定する

Vim Advent Calendar 2012の331日目の記事になります。
330日目は@LeafCageさんによる 俺が見たいのはメッセージの最後なんだ!:messagesを便利にするVimプラグイン でした。

はじめに

先日このようなvimの記事を見つけました。

feedkeys() を使用します。

function! HLsearch()
    call feedkeys(":set hlsearch\<CR>", "n")
endfunction
call HLsearch()

これでハイライトが行われます。

少々バッドノウハウっぽくはありますが、自分はこの関数内でのset hlsearchの方法を待ち望んでいました。
過去にvim-operator-userを使って検索オペレータを作ろうと思い立つも、関数内ハイライトの壁に阻まれて諦めていたのです。
でもこの方法さえ知ればもう怖いものはない、一度敗れた雪辱を果たしてやろうと思います。倍返しです。

ちなみに関数内でハイライトが効かないのは仕様だそうです(:h nohlsearch)。

実装

では早速実装してみましょう。
こんな感じ。

NeoBundle 'kana/vim-operator-user'

" 検索オペレータ
map <SPACE>/ <Plug>(operator-search)
call operator#user#define('search', 'OperatorSearch')
function! OperatorSearch(motion_wise)
    " v or V or <C-v>
    let v = operator#user#visual_command_from_wise_name(a:motion_wise)

    " textobjで選択した文字列を検索パターン用レジスタに格納
    let orig = @"
    execute 'normal! `[' . v . '`]y'
    let @/ = '\V' . substitute(escape(@", '\'), '\n', '\\n', 'g')
    let @" = orig

    " 検索履歴に追加
    call histadd('/', @/)

    " 強調表示
    if &hlsearch
        call feedkeys(":set hlsearch\<CR>", 'n')
    endif
endfunction

どやろか?

ただ上記は思いっきり自分仕様に作ってます。
自分は*の動作でも移動はせずハイライトのみするようにしているので、これも同じように作成しました。
その後n/Nで移動する感じです。

とりあえず動かしてみる。

f:id:saihoooooooo:20131026225917g:plain

うーん、いい感じっす。
あと捕捉ですがilの動作はvim-textobj-lineでやってます。

いやぁハイライトって本当にいいものですね。
私もう満足です。
一片の悔いなしっす。

が一応

さっきも言ったように上記は思いっきりオレオレ仕様であります。
普通は初回の動作でも移動+ハイライトになると思うので頑張って作ってみたいと思います。

map <SPACE>/ <Plug>(operator-search-forward)
map <SPACE>? <Plug>(operator-search-backward)

call operator#user#define('search-forward', 'OperatorSearch', 'call SetOpSearchCmdtype("/")')
call operator#user#define('search-backward', 'OperatorSearch', 'call SetOpSearchCmdtype("?")')

let s:op_search_cmdtype = ''
function! SetOpSearchCmdtype(cmdtype)
    let s:op_search_cmdtype = a:cmdtype
endfunction

function! OperatorSearch(motion_wise)
    let v = operator#user#visual_command_from_wise_name(a:motion_wise)
    let save = @"
    execute 'normal! `[' . v . '`]y'
    let @/ = '\V' . substitute(escape(@", '\'), '\n', '\\n', 'g')
    let @" = save
    call histadd('/', @/)
    if &hlsearch
        call feedkeys(":set hlsearch\<CR>", 'n')
    endif
    " キーマップから実行することで<SPACE>?でのn/Nの方向が?と同じになる
    call feedkeys(s:op_search_cmdtype . "\<CR>", 'n')
endfunction

うーん、検索周りは自分のつたないvimscript力ではこの辺が限界か。
一応動作はしますが、納得はいってません。
特に2回もfeedkeys()を使わされているのが悔しいです。
v:searchforwardもまた関数から出ると元に戻るようで。

プラグイン化も考えてたけどまた次回に・・・。
だれかキレイなものを作ってくれる方募集中です。

まとめ

@thincaさんのvim-visualstarはすっごいよく出来てる。