今日の関数 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;
}

search.c

matchした後でSEARCH_ENDの設定をしているところです。

if ((options & SEARCH_END) && !(options & SEARCH_NOOF)
  && !(matchpos.lnum == endpos.lnum
   && matchpos.col == endpos.col))
{

search.c

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.c

SEARCH_ENDオプションがついているのでカーソルの位置をマッチした最後に持っていきます。
その時マッチの最後の部分が0ならば前の行の最後にカーソルを移動します。そうでなければ
マッチした最後の部分から一つ戻った位置にカーソルをおきます。

else
{
	pos->lnum = lnum + matchpos.lnum;
	pos->col = matchpos.col;
}

search.c

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;
}

search.c

found, first_matchなどのフラグを調整して行ごとのループ(697行目のfor loop)から抜け出します。

line_breakcheck(); /* stop if ctrl-C typed */
if (got_int)
 break;

search.c

ここに来る場合はマッチしなかった場合です。
毎行ループするたびこの部分を通ります。
まずは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

search.c

ここではサーチしている間にまた文字入力がされていないかをチェックします。
この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;

search.c

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;

search.c

ここに来るのは全部の行を周り終わった時です。
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);
 }

search.c

行を全部ループし終わったのでwrapscanするために後方検索ならlnumにbufferの一番最後の行の数を入れて
前方検索ならlnumを1にして次のloopに入ります。
その際検索が最後または最初の行に戻ることをワーニングします。