haya14busa

haya14busa’s memo

Vimの検索ハイライト,hlsearch,:nohlsearch,v:hlsearchがややこしい

この記事は Vim Advent Calendar 2013 73日目の記事になります。

普段何気なくvimrcに書いているset hlsearch:nohlsearch。実はこれ、結構ややこしいです。

まずはhelp!

そして他に注意すべきものとして

  • set nohlsearch <- set hlsearchの逆。:nohlsearchとは違う
  • &hlsearch <- オプションhlsearchの値の変数。set hlsearchなら1,set nohlsearchなら0

ややこしいですね。

v:hlsearchを解説しながら全体的に説明するよ

最近縁があったのでv:hlsearchの日本語訳を書かせて頂きましたが、ぶっちゃけ元の英語の説明もわかりづらいのでここで解説します。

                    v:hlsearch hlsearch-variable
v:hlsearch  検索による強調表示がオンになっているかどうかを決定する変数。
        |+extra_search| 機能が必要である ‘hlsearch’ が有効になっている
        時のみ意味をなす。この変数を0に設定することは、 |:nohlsearch|
        コマンドを実行することと同様に働き、1に設定することは以下と同様に働く >
            let &hlsearch = &hlsearch

まず仕様ですが、この変数は実際に現在検索によるハイライトが行われているかどうかを判定、決定できます。

判定

echo v:hlsearch

  • ハイライトが行われている -> 1
  • ハイライトが行われていない -> 0

決定(ハイライトをON or OFFにする)

この変数を0に設定することは、 |:nohlsearch| コマンドを実行することと同様に働き、 1に設定することは以下と同様に働く let &hlsearch = &hlsearch

わかりずらい!!!

let v:hlsearch = 0

これは、helpによると:nohlsearchコマンドを実行することと同様に働きます。

|:nohlsearch| コマンドを使うと、一時的に強調表示をやめさせることができます。

一時的に というのがミソです。この:nohlsearchコマンドはhlsearchオプションの変数である、&hlsearchを変更しないのです。つまりset hlsearchしている場合&hlsearch変数の値は1ですが、:nohlsearchを実行しても&hlsearchの値は1のままでハイライトを一時的にオフにします。

let v:hlsearch = 1

helpによると1に設定することは

let &hlsearch = &hlsearch

をすることと同様に働くようです。ここがかなりわかりずらい。

前提として、

  1. 基本的にv:hlsearchは実際にハイライトを行っているかどうかを決定するのですがset nohlsearchが設定されていた場合はset nohlsearchが優先されます。
  2. コマンド:nohlsearchによって一時的にハイライトをオフにした場合、再び検索するなどしたときにハイライトが有効になるのですが、set hlsearch,つまりはlet &hlsearch = 1を設定した場合も同様にトリガーとなってハイライトが有効になります(実装は見ていないので予測含む。動作確認はしています)

よって、もしset nohlsearchが設定されていれば&hlsearchの値は0なのでlet &hlsearch = 0と同様の動作となり何も起こりません。

反対に、set hlsearchが設定されていれば&hlsearchの値は1で、let &hlsearch = 1と同様になり、前提の2からこの動作がトリガーとなって再びハイライトがONになります。

はぁ、ややこしかった。わからなかった方は直接@haya14busaまで質問とかしてください。また間違ってたらご指摘よろしくおねがいします。

ここからv:hlsearchから得られたTipsを2つ紹介します

Reloadableなvimrcの設定

一般的には下記のような設定をしている方が多いと思います

set hlsearch
nnoremap <silent><Esc><Esc> :<C-u>nohlsearch<CR>

が、リローダブルなvimrcを目指している方はこの問題に少なからず気づいているかもしれません。

つまり、set hlsearchを記述しているとリロード時に問答無用でハイライトされてしまうのです。(ウザイ

これを改善しましょう

1. 簡単かつv:hlsearchがなくてもOK

“ hlsearchは使うがvimrcを読み込んだ時にハイライトしないようにする
set hlsearch | nohlsearch

上記のようにすると&hlsearchの値は1で検索のハイライトは有効になっていますが、:nohlsearchコマンドによりそれを一時的にOFFにします。

欠点: ハイライトしている状態でリロードするとハイライトが消えてしまう。

2. v:hlsearchを使って1の欠点をなくす

if exists(‘v:hlsearch’)
  let v_hl_save = v:hlsearch
endif
set hlsearch
execute exists(‘v:hlsearch’) ? ‘let v:hlsearch = v_hl_save’ : ‘nohlsearch’

欠点: 書き方が悪いということを除いてもわざわざそこまでしたくない…

スクリプトから扱う

ハイライトを確認する

v:hlsearchが登場する以前では&hlsearchの値を見ることしかできず、ハイライトの設定がされているかどうかは分かるけれども、実際にハイライトされているかどうかがわかりませんでした。(:nohlsearchで一時的にハイライトしていなくてもset hlsearchしていれば一貫して&hlsearchの値は1です)

v:hlsearchの登場によって改善されたのはほぼこの一点といってもいいでしょう。

実際に関数からハイライトさせる

上記の記事の問題はv:hlsearchでも同じなようで、関数内でlet v:hlsearch = 1にしても実行が終わると勝手に元の値に戻されてしまいます。これを回避するには記事にあるように:h feedkeys()を使うしかないようです。残念…

ちょっと改善

上記の記事の関数だとset nohlsearchのオプションを無視してしまうので下記のように:let &hlsearch=&hlsearch\<CR>を使うとよりお行儀よくハイライトさせることができます。

function! HLsearch()
    call feedkeys(“:let &hlsearch=&hlsearch&lt;CR>”, “n”)
    “ または
    ” call feedkeys(“:let v:hlsearch=1&lt;CR>”, “n”)
endfunction

最後に

最新への追従 #84: https://github.com/vim-jp/vimdoc-ja/issues/84

Vimの日本語訳がオリジナルのヘルプに追従しきれていないようです。実際v:hlsearchを訳して、レビューしてもらったりしましたが、結構新鮮で面白かったしこれからも暇があれば、僕も日本語訳に参加して貢献できればいいなと思います。人手は足りていないようなので、気になった方は簡単な/分かるところからでもいいから訳して、vim-jpに投げてみるといいのではないでしょうか。きっとみんな優しくしてくれると思います。

Comments