Emacs style isearch ハック

Emacs のようにインクリメンタルサーチしている最中も全部のマッチ箇所をハイライトするハック。

--- ../src.orig/ex_getln.c	Mon May  1 00:32:01 2006
+++ ex_getln.c	Sat Dec 30 08:57:11 2006
@@ -1701,7 +1713,7 @@
 		out_flush();
 		++emsg_off;    /* So it doesn't beep if bad expr */
 		i = do_search(NULL, firstc, ccline.cmdbuff, count,
-			SEARCH_KEEP + SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK);
+			SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK);
 		--emsg_off;
 		/* if interrupted while searching, behave like it failed */
 		if (got_int)
--- ../src.orig/normal.c	Sat Apr 29 22:11:18 2006
+++ normal.c	Sat Dec 30 08:54:16 2006
@@ -6063,6 +6066,8 @@
 
     i = do_search(cap->oap, dir, pat, cap->count1,
 				 opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG);
+    if (p_is)
+	no_hlsearch = TRUE;
     if (i == 0)
 	clearop(cap->oap);
     else

インクリメンタルサーチの流れ
getcmdline() の中の

324        /*
325         * Collect the command string, handling editing keys.
326         */
327        for (;;)
328        {

から始まる長いループの中で safe_vgetc() を使って1文字ずつキー入力を取得する。
キーを取得すると↓に来る。

1689    cmdline_changed:
1690    #ifdef FEAT_SEARCH_EXTRA
1691    	/*
1692    	 * 'incsearch' highlighting.
1693    	 */
1694    	if (p_is && !cmd_silent && (firstc == '/' || firstc == '?'))

入力バッファにキーがたまっているときはちゃんと描画などをスキップするようになっているようだ:

1698    	    /* if there is a character waiting, search and redraw later */
1699    	    if (char_avail())
1700    	    {
1701    		incsearch_postponed = TRUE;
1702    		continue;
1703    	    }

↓は今回変更した箇所。

1715    		i = do_search(NULL, firstc, ccline.cmdbuff, count,
1716    			SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK);

get_cmdline の最初でカーソル位置を退避しておき、1文字入力ごとにカーソル移動。確定したらもとの位置に戻し、nv_search でもう一度検索しているようだ。Emacs のように、インクリメンタルサーチ中にもう一度 C-s で次を検索というのは難しいかも。


○ 描画の流れ
do_search() の後の update_screen から以下のようにもぐっていく。

update_screen	    // 即座に全画面更新
-> start_search_hl  // 'hlsearch' の準備
-> last_pat_prog    // コンパイルした検索情報(regmmatch_T)を返す
-> search_regcomp   // 正規表現をコンパイルし、フラグに応じて履歴に入れたり、spats[]に入れたりする。

spats はパターン、/e+1 などのオフセット、ignorecase などの検索オプションをまとめて一時的に保管しておくバッファのようだ。
今回は do_search のときにフラグ SEARCH_KEEP を渡さないようにしたため、1文字ごとに spats を更新し、検索が確定したときと同様に、マッチ全部をハイライトするようになる。


'hlsearch' をオンにしていると、検索時とハイライト時で、同じ正規表現を2回コンパイルすることになる。
しかもエンターでインクリメンタルサーチを終了させると、もう一度 nv_search で同じパターンを検索してるっぽい。
そのとき do_search の中で no_hlsearch = FALSE; している。Emacs のように検索終了したらハイライトをやめるようにするため、その直後で無理矢理 no_hlsearch = TRUE; する:

     i = do_search(cap->oap, dir, pat, cap->count1,
 				 opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG);
+    if (p_is)
+	no_hlsearch = TRUE;

変更箇所こそわずかだが、今回のハックはつらかった。
でも見た目が派手なため、知らない人が見るとけっこう驚くかも。
http://www.k3.dion.ne.jp/~jod/emacs-style-isearch.png