今日の関数 searchit その5 (https://github.com/vim/vim)
/* With the SEARCH_END option move to the last character * of the match. Don't do it for an empty match, end * should be same as start then. */ if ((options & SEARCH_END) && !(options & SEARCH_NOOF) && !(matchpos.lnum == endpos.lnum && matchpos.col == endpos.col)) { /* For a match in the first column, set the position * on the NUL in the previous line. */ pos->lnum = lnum + endpos.lnum; pos->col = endpos.col; if (endpos.col == 0) { if (pos->lnum > 1) /* just in case */ { --pos->lnum; pos->col = (colnr_T)STRLEN(ml_get_buf(buf, pos->lnum, FALSE)); } } else { --pos->col; #ifdef FEAT_MBYTE if (has_mbyte && pos->lnum <= buf->b_ml.ml_line_count) { ptr = ml_get_buf(buf, pos->lnum, FALSE); pos->col -= (*mb_head_off)(ptr, ptr + pos->col); } #endif } } else { pos->lnum = lnum + matchpos.lnum; pos->col = matchpos.col; }
matchした後でSEARCH_ENDの設定をしているところです。
if ((options & SEARCH_END) && !(options & SEARCH_NOOF) && !(matchpos.lnum == endpos.lnum && matchpos.col == endpos.col)) {
SEARCH_ENDのオプションがついていてmatchした文字が空でないかをチェックします。
pos->lnum = lnum + endpos.lnum; pos->col = endpos.col; if (endpos.col == 0) { if (pos->lnum > 1) /* just in case */ { --pos->lnum; pos->col = (colnr_T)STRLEN(ml_get_buf(buf, pos->lnum, FALSE)); } } else { --pos->col; #ifdef FEAT_MBYTE if (has_mbyte && pos->lnum <= buf->b_ml.ml_line_count) { ptr = ml_get_buf(buf, pos->lnum, FALSE); pos->col -= (*mb_head_off)(ptr, ptr + pos->col); } #endif }
SEARCH_ENDオプションがついているのでカーソルの位置をマッチした最後に持っていきます。
その時マッチの最後の部分が0ならば前の行の最後にカーソルを移動します。そうでなければ
マッチした最後の部分から一つ戻った位置にカーソルをおきます。
else { pos->lnum = lnum + matchpos.lnum; pos->col = matchpos.col; }
SEARCH_ENDオプションがない場合です。
カーソルの位置をマッチした位置に持っていきます。
#ifdef FEAT_VIRTUALEDIT pos->coladd = 0; #endif found = 1; first_match = FALSE; /* Set variables used for 'incsearch' highlighting. */ search_match_lines = endpos.lnum - matchpos.lnum; search_match_endcol = endpos.col; break; }
found, first_matchなどのフラグを調整して行ごとのループ(697行目のfor loop)から抜け出します。
line_breakcheck(); /* stop if ctrl-C typed */ if (got_int) break;
ここに来る場合はマッチしなかった場合です。
毎行ループするたびこの部分を通ります。
まずはctrl-cで中断されないかをチェックします。
#ifdef FEAT_SEARCH_EXTRA /* Cancel searching if a character was typed. Used for * 'incsearch'. Don't check too often, that would slowdown * searching too much. */ if ((options & SEARCH_PEEK) && ((lnum - pos->lnum) & 0x3f) == 0 && char_avail()) { break_loop = TRUE; break; } #endif
ここではサーチしている間にまた文字入力がされていないかをチェックします。
このifの二番目の条件は
あまりに頻繁にbreakしないように間隔を空けるための条件です。
lnumはループしている行数です。それからカーソルのある行数を引いた数が
0x3f(0011 1111,10進数では63)との論理的ANDが0の時breakします。
つまり64で割り切れる数の時この条件が満たされます(0x0100 0000(64) & 0011 1111(0x3f) == 0)。
for (loop = 0; loop <= 1; ++loop) { ... if (loop && lnum == start_pos.lnum) break; /* if second loop, stop where started */ } at_first_line = FALSE;
loopが0でないということは二回目のloopだということなので
その上で検索のスタート位置の行とlnumが一致しているということは
検索の結果が一周回って元に戻ってきたということです。
その時もbreakします。
/* * Stop the search if wrapscan isn't set, "stop_lnum" is * specified, after an interrupt, after a match and after looping * twice. */ if (!p_ws || stop_lnum != 0 || got_int || called_emsg #ifdef FEAT_SEARCH_EXTRA || break_loop #endif || found || loop) break;
ここに来るのは全部の行を周り終わった時です。
wrapscanでない場合、中断された時、ループを二回回った時にbreakします。
/* * If 'wrapscan' is set we continue at the other end of the file. * If 'shortmess' does not contain 's', we give a message. * This message is also remembered in keep_msg for when the screen * is redrawn. The keep_msg is cleared whenever another message is * written. */ if (dir == BACKWARD) /* start second loop at the other end */ lnum = buf->b_ml.ml_line_count; else lnum = 1; if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG)) give_warning((char_u *)_(dir == BACKWARD ? top_bot_msg : bot_top_msg), TRUE); }
行を全部ループし終わったのでwrapscanするために後方検索ならlnumにbufferの一番最後の行の数を入れて
前方検索ならlnumを1にして次のloopに入ります。
その際検索が最後または最初の行に戻ることをワーニングします。