今日の関数 searchit その3 (https://github.com/vim/vim)

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

前回どのような時にこのwhileに入るかを見ました。ここでは前方検索の時
matchした文字がカーソルよりも前に来るのを避けるための調節をします。

if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
{
 if (nmatched > 1)
 {
  /* end is in next line, thus no match in
   * this line */
  match_ok = FALSE;
  break;
 }
 matchcol = endpos.col;
 /* for empty match: advance one char */
 if (matchcol == matchpos.col
   && ptr[matchcol] != NUL)
 {
#ifdef FEAT_MBYTE
  if (has_mbyte)
   matchcol +=
    (*mb_ptr2len)(ptr + matchcol);
  else
#endif
   ++matchcol;
 }
}
else
{
 matchcol = matchpos.col;
 if (ptr[matchcol] != NUL)
 {
#ifdef FEAT_MBYTE
  if (has_mbyte)
   matchcol += (*mb_ptr2len)(ptr
     + matchcol);
  else
#endif
   ++matchcol;
 }
}

search.c

ここではまずCPOオプションの'c'フラグが立っているかをみています。
cフラグがあると検索が終わった時にそのマッチした文字の最後から次に検索するようになります。
"abababababab" で "/abab" と検索した場合フラグがある場合3つ,ない場合5つマッチします。

if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
{
 if (nmatched > 1)
 {
  /* end is in next line, thus no match in
   * this line */
  match_ok = FALSE;
  break;
 }
 matchcol = endpos.col;
 /* for empty match: advance one char */
 if (matchcol == matchpos.col
   && ptr[matchcol] != NUL)
 {
#ifdef FEAT_MBYTE
  if (has_mbyte)
   matchcol +=
    (*mb_ptr2len)(ptr + matchcol);
  else
#endif
   ++matchcol;
 }
}

search.c

ではまずフラグありの時です。
nmatchedが1以上の場合はmatchの最後が次の行なのでwhileループから抜け次の行のループに入ります。
nmatchedが1ならmatchの場所をそのマッチの最後の位置にします。
その際matchcolとmatchpos.colが同じ場合(empty match)の場合matchcolを一つ進めています。
これは"/^"のような検索の時がこのケースになります。
matchcolはもう一回検索する際のスタート位置としてこの後使われます。

...
}
else
{
 matchcol = matchpos.col;
 if (ptr[matchcol] != NUL)
 {
#ifdef FEAT_MBYTE
  if (has_mbyte)
   matchcol += (*mb_ptr2len)(ptr
     + matchcol);
  else
#endif
   ++matchcol;
 }
}

search.c

cオプション無しの場合です。ここは単にmatchcolにマッチした場所を入れるだけです。
ここでも前の場合と同じようにmatchcolを一つ進めて次の検索に備えます。

if (matchcol == 0 && (options & SEARCH_START))
    break;

search.c

matchcolが0でここに来るのは空の行で"/^"の検索した時で
ptr[matchcol]がNULになります。
この時SEARCH_STARTのフラグが立っていればここに入ります。
これはカーソルの位置を検索に入れることになり。今カーソルがいる行の先頭にmatchがセットされます。
もしこのSEARCH_STARTのフラグがなければもう一回ループして次の行の先頭にカーソルが動きます。

if (ptr[matchcol] == NUL
  || (nmatched = vim_regexec_multi(&regmatch,
    win, buf, lnum + matchpos.lnum,
    matchcol,
#ifdef FEAT_RELTIME
    tm
#else
    NULL
#endif
    )) == 0)
{
 match_ok = FALSE;
 break;
}

search.c

matchcolのところの文字がNULかもう一回regex検索をmatchcolの位置からして
見つからなかったなら次の行にループを進めます。

matchpos = regmatch.startpos[0];
endpos = regmatch.endpos[0];
# ifdef FEAT_EVAL
submatch = first_submatch(&regmatch);
# endif

/* Need to get the line pointer again, a
 * multi-line search may have made it invalid. */
ptr = ml_get_buf(buf, lnum + matchpos.lnum, FALSE);
}

search.c

regex検索がマッチしたのでその情報をmatchpos, endposにセットしptrにそのマッチした
行を入れてループから出ます。

if (nmatched > 0)
{
 matchpos = regmatch.startpos[0];
 endpos = regmatch.endpos[0];
#ifdef FEAT_EVAL
 submatch = first_submatch(&regmatch);
#endif
 if (lnum + matchpos.lnum > buf->b_ml.ml_line_count)
  ptr = (char_u *)"";
 else
  ptr = ml_get_buf(buf, lnum + matchpos.lnum, FALSE);

 if (dir == FORWARD && at_first_line)
 {
  match_ok = TRUE;

...


if (!match_ok)
 continue;
}

search.c

このmatch_okがループの最初にTRUEが入ってループを出る場合はここに
FALSEが入ります。