今日の関数 searchit その2 (https://github.com/vim/vim)
nmatched = vim_regexec_multi(®match, win, buf, lnum, col, #ifdef FEAT_RELTIME tm #else NULL #endif ); /* Abort searching on an error (e.g., out of stack). */ if (called_emsg) break; if (nmatched > 0) { /* match may actually be in another line when using \zs */ matchpos = regmatch.startpos[0]; endpos = regmatch.endpos[0]; #ifdef FEAT_EVAL submatch = first_submatch(®match); #endif /* "lnum" may be past end of buffer for "\n\zs". */ if (lnum + matchpos.lnum > buf->b_ml.ml_line_count) ptr = (char_u *)""; else ptr = ml_get_buf(buf, lnum + matchpos.lnum, FALSE); /* * Forward search in the first line: match should be after * the start position. If not, continue at the end of the * match (this is vi compatible) or on the next char. */ if (dir == FORWARD && at_first_line) { match_ok = TRUE; /* * When the match starts in a next line it's certainly * past the start position. * When match lands on a NUL the cursor will be put * one back afterwards, compare with that position, * otherwise "/$" will get stuck on end of line. */ while (matchpos.lnum == 0 && ((options & SEARCH_END) && first_match ? (nmatched == 1 && (int)endpos.col - 1 < (int)start_pos.col + extra_col) : ((int)matchpos.col - (ptr[matchpos.col] == NUL) < (int)start_pos.col + extra_col))) { ... }
colに検索を開始する位置を入れた後の部分を読んでいきましょう。
まずは正規表現で検索します。
nmatched = vim_regexec_multi(®match, win, buf, lnum, col, #ifdef FEAT_RELTIME tm #else NULL #endif );
vim_regexec_multiはlnumの行のcol番目の文字から検索してマッチした行数を返します。
if (nmatched > 0) { /* match may actually be in another line when using \zs */ matchpos = regmatch.startpos[0]; endpos = regmatch.endpos[0]; #ifdef FEAT_EVAL submatch = first_submatch(®match); #endif /* "lnum" may be past end of buffer for "\n\zs". */ if (lnum + matchpos.lnum > buf->b_ml.ml_line_count) ptr = (char_u *)""; else ptr = ml_get_buf(buf, lnum + matchpos.lnum, FALSE);
nmatchedが0以上なのでここは検索がヒットしたということです。
matchposにマッチした最初の位置、endposにマッチした最後の位置を入れます。
ここでマッチした行のテキストをptrに格納するのですがその時そのmatchの位置が バッファから飛び出していないかをチェックします。
例えば最後の行にabcというテキストがあったとします。
そこに/c\n\zsという( \zは前の検索がマッチした次にマッチさせるオプション )検索をすると
matchはcの後の改行の後になるので最後の行の次の行を指してしまうことになります。
その時は上のコードのように空文字をptrにセットします。
普通にマッチしているならそのマッチした行をptrに入れます。
if (dir == FORWARD && at_first_line) { match_ok = TRUE; /*
次に前方検索で一行目の時の条件がきます。
while (matchpos.lnum == 0 && ((options & SEARCH_END) && first_match ? (nmatched == 1 && (int)endpos.col - 1 < (int)start_pos.col + extra_col) : ((int)matchpos.col - (ptr[matchpos.col] == NUL) < (int)start_pos.col + extra_col))) {
このwhileがとても読みづらいのですがちょっと整理してみましょう。
while ( ① matchpos.lnum == 0 && ((options & SEARCH_END) && first_match ? ② (nmatched == 1 && (int)endpos.col - 1 < (int)start_pos.col + extra_col) : ③ ((int)matchpos.col - (ptr[matchpos.col] == NUL) < (int)start_pos.col + extra_col))) {
① マッチがカーソルのある行にあり
SEARCH_ENDのオプションがあり
一番最初のマッチである
② マッチしている行は一行だけであり
マッチしている最後の部分がカーソルの位置より左にある。
③ マッチしている最初の部分(もしその位置がNULならその位置から-1したところ)がカーソルの位置より左にある。
このように整理してみるとこれはSEARCH_ENDのあるなしの場合わけだとわかります。
前方検索なので、もしマッチした文字がカーソル位置の左にあってはならないのでその調節を行います。
SEARCH_ENDがついているならそのマッチの最後の位置、ついていないならマッチの最初の位置が カーソルの左にある間はwhileが回ることになります。