0
0
mirror of https://github.com/vim/vim.git synced 2025-09-29 04:34:16 -04:00

patch 8.1.1743: 'hlsearch' and match highlighting in the wrong place

Problem:    'hlsearch' and match highlighting in the wrong place.
Solution:   Move highlighting from inside screen functions to highlight.c.
This commit is contained in:
Bram Moolenaar
2019-07-24 18:13:16 +02:00
parent 7dfb016d25
commit bbca7732e8
4 changed files with 640 additions and 569 deletions

View File

@@ -104,7 +104,7 @@ static int screen_attr = 0;
static int screen_cur_row, screen_cur_col; /* last known cursor position */
#ifdef FEAT_SEARCH_EXTRA
static match_T search_hl; /* used for 'hlsearch' highlight matching */
static match_T search_hl; // used for 'hlsearch' highlight matching
#endif
#ifdef FEAT_FOLDING
@@ -135,13 +135,8 @@ static void draw_vsep_win(win_T *wp, int row);
static void redraw_custom_statusline(win_T *wp);
#endif
#ifdef FEAT_SEARCH_EXTRA
# define SEARCH_HL_PRIORITY 0
static void start_search_hl(void);
static void end_search_hl(void);
static void init_search_hl(win_T *wp);
static void prepare_search_hl(win_T *wp, linenr_T lnum);
static void next_search_hl(win_T *win, match_T *shl, linenr_T lnum, colnr_T mincol, matchitem_T *cur);
static int next_search_hl_pos(match_T *shl, linenr_T lnum, posmatch_T *pos, colnr_T mincol);
#endif
static void screen_char(unsigned off, int row, int col);
static void screen_char_2(unsigned off, int row, int col);
@@ -1172,7 +1167,7 @@ win_update(win_T *wp)
#endif
#ifdef FEAT_SEARCH_EXTRA
init_search_hl(wp);
init_search_hl(wp, &search_hl);
#endif
#ifdef FEAT_LINEBREAK
@@ -2090,7 +2085,7 @@ win_update(win_T *wp)
else
{
#ifdef FEAT_SEARCH_EXTRA
prepare_search_hl(wp, lnum);
prepare_search_hl(wp, &search_hl, lnum);
#endif
#ifdef FEAT_SYN_HL
/* Let the syntax stuff know we skipped a few lines. */
@@ -3273,17 +3268,6 @@ win_line(
int sign_present = FALSE;
sign_attrs_T sattr;
#endif
#ifdef FEAT_SEARCH_EXTRA
matchitem_T *cur; /* points to the match list */
match_T *shl; /* points to search_hl or a match */
int shl_flag; /* flag to indicate whether search_hl
has been processed or not */
int pos_inprogress; /* marks that position match search is
in progress */
int prevcol_hl_flag; /* flag to indicate whether prevcol
equals startcol of search_hl or one
of the matches */
#endif
#ifdef FEAT_ARABIC
int prev_c = 0; /* previous Arabic character */
int prev_c1 = 0; /* first composing char for prev_c */
@@ -3808,65 +3792,12 @@ win_line(
}
#ifdef FEAT_SEARCH_EXTRA
/*
* Handle highlighting the last used search pattern and matches.
* Do this for both search_hl and the match list.
* Do not use search_hl in a popup window.
*/
cur = wp->w_match_head;
shl_flag = (screen_line_flags & SLF_POPUP);
while ((cur != NULL || shl_flag == FALSE) && !number_only)
if (!number_only)
{
if (shl_flag == FALSE)
{
shl = &search_hl;
shl_flag = TRUE;
}
else
shl = &cur->hl;
shl->startcol = MAXCOL;
shl->endcol = MAXCOL;
shl->attr_cur = 0;
shl->is_addpos = FALSE;
v = (long)(ptr - line);
if (cur != NULL)
cur->pos.cur = 0;
next_search_hl(wp, shl, lnum, (colnr_T)v,
shl == &search_hl ? NULL : cur);
/* Need to get the line again, a multi-line regexp may have made it
* invalid. */
line = ml_get_buf(wp->w_buffer, lnum, FALSE);
ptr = line + v;
if (shl->lnum != 0 && shl->lnum <= lnum)
{
if (shl->lnum == lnum)
shl->startcol = shl->rm.startpos[0].col;
else
shl->startcol = 0;
if (lnum == shl->lnum + shl->rm.endpos[0].lnum
- shl->rm.startpos[0].lnum)
shl->endcol = shl->rm.endpos[0].col;
else
shl->endcol = MAXCOL;
/* Highlight one character for an empty match. */
if (shl->startcol == shl->endcol)
{
if (has_mbyte && line[shl->endcol] != NUL)
shl->endcol += (*mb_ptr2len)(line + shl->endcol);
else
++shl->endcol;
}
if ((long)shl->startcol < v) /* match at leftcol */
{
shl->attr_cur = shl->attr;
search_attr = shl->attr;
}
area_highlighting = TRUE;
}
if (shl != &search_hl && cur != NULL)
cur = cur->next;
area_highlighting |= prepare_search_hl_line(wp, lnum, (colnr_T)v,
&line, &search_hl, &search_attr);
ptr = line + v; // "line" may have been updated
}
#endif
@@ -4247,132 +4178,15 @@ win_line(
if (!n_extra)
{
/*
* Check for start/end of search pattern match.
* Check for start/end of 'hlsearch' and other matches.
* After end, check for start/end of next match.
* When another match, have to check for start again.
* Watch out for matching an empty string!
* Do this for 'search_hl' and the match list (ordered by
* priority).
*/
v = (long)(ptr - line);
cur = wp->w_match_head;
shl_flag = FALSE;
while (cur != NULL || shl_flag == FALSE)
{
if (shl_flag == FALSE
&& ((cur != NULL
&& cur->priority > SEARCH_HL_PRIORITY)
|| cur == NULL))
{
shl = &search_hl;
shl_flag = TRUE;
if (screen_line_flags & SLF_POPUP)
continue; // do not use search_hl
}
else
shl = &cur->hl;
if (cur != NULL)
cur->pos.cur = 0;
pos_inprogress = TRUE;
while (shl->rm.regprog != NULL
|| (cur != NULL && pos_inprogress))
{
if (shl->startcol != MAXCOL
&& v >= (long)shl->startcol
&& v < (long)shl->endcol)
{
int tmp_col = v + MB_PTR2LEN(ptr);
if (shl->endcol < tmp_col)
shl->endcol = tmp_col;
shl->attr_cur = shl->attr;
#ifdef FEAT_CONCEAL
// Match with the "Conceal" group results in hiding
// the match.
if (cur != NULL
&& shl != &search_hl
&& syn_name2id((char_u *)"Conceal")
== cur->hlg_id)
{
has_match_conc =
v == (long)shl->startcol ? 2 : 1;
match_conc = cur->conceal_char;
}
else
has_match_conc = match_conc = 0;
#endif
}
else if (v == (long)shl->endcol)
{
shl->attr_cur = 0;
next_search_hl(wp, shl, lnum, (colnr_T)v,
shl == &search_hl ? NULL : cur);
pos_inprogress = cur == NULL || cur->pos.cur == 0
? FALSE : TRUE;
/* Need to get the line again, a multi-line regexp
* may have made it invalid. */
line = ml_get_buf(wp->w_buffer, lnum, FALSE);
ptr = line + v;
if (shl->lnum == lnum)
{
shl->startcol = shl->rm.startpos[0].col;
if (shl->rm.endpos[0].lnum == 0)
shl->endcol = shl->rm.endpos[0].col;
else
shl->endcol = MAXCOL;
if (shl->startcol == shl->endcol)
{
/* highlight empty match, try again after
* it */
if (has_mbyte)
shl->endcol += (*mb_ptr2len)(line
+ shl->endcol);
else
++shl->endcol;
}
/* Loop to check if the match starts at the
* current position */
continue;
}
}
break;
}
if (shl != &search_hl && cur != NULL)
cur = cur->next;
}
/* Use attributes from match with highest priority among
* 'search_hl' and the match list. */
cur = wp->w_match_head;
shl_flag = FALSE;
search_attr = 0;
while (cur != NULL || shl_flag == FALSE)
{
if (shl_flag == FALSE
&& ((cur != NULL
&& cur->priority > SEARCH_HL_PRIORITY)
|| cur == NULL))
{
shl = &search_hl;
shl_flag = TRUE;
if (screen_line_flags & SLF_POPUP)
continue; // do not use search_hl
}
else
shl = &cur->hl;
if (shl->attr_cur != 0)
search_attr = shl->attr_cur;
if (shl != &search_hl && cur != NULL)
cur = cur->next;
}
/* Only highlight one character after the last column. */
if (*ptr == NUL && (did_line_attr >= 1
|| (wp->w_p_list && lcs_eol_one == -1)))
search_attr = 0;
search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line,
&search_hl, &has_match_conc, &match_conc,
did_line_attr, lcs_eol_one);
ptr = line + v; // "line" may have been changed
}
#endif
@@ -5561,35 +5375,15 @@ win_line(
) && eol_hl_off == 0)
{
#ifdef FEAT_SEARCH_EXTRA
long prevcol = (long)(ptr - line) - (c == NUL);
/* we're not really at that column when skipping some text */
if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
++prevcol;
#endif
/* Invert at least one char, used for Visual and empty line or
* highlight match at end of line. If it's beyond the last
* char on the screen, just overwrite that one (tricky!) Not
* needed when a '$' was displayed for 'list'. */
#ifdef FEAT_SEARCH_EXTRA
prevcol_hl_flag = FALSE;
if (!search_hl.is_addpos && prevcol == (long)search_hl.startcol)
prevcol_hl_flag = TRUE;
else
{
cur = wp->w_match_head;
while (cur != NULL)
{
if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol)
{
prevcol_hl_flag = TRUE;
break;
}
cur = cur->next;
}
}
// flag to indicate whether prevcol equals startcol of search_hl or
// one of the matches
int prevcol_hl_flag = get_prevcol_hl_flag(wp, &search_hl,
(long)(ptr - line) - (c == NUL));
#endif
// Invert at least one char, used for Visual and empty line or
// highlight match at end of line. If it's beyond the last
// char on the screen, just overwrite that one (tricky!) Not
// needed when a '$' was displayed for 'list'.
if (lcs_eol == lcs_eol_one
&& ((area_attr != 0 && vcol == fromcol
&& (VIsual_mode != Ctrl_V
@@ -5597,8 +5391,8 @@ win_line(
|| lnum == curwin->w_cursor.lnum)
&& c == NUL)
#ifdef FEAT_SEARCH_EXTRA
/* highlight 'hlsearch' match at end of line */
|| (prevcol_hl_flag == TRUE
// highlight 'hlsearch' match at end of line
|| (prevcol_hl_flag
# ifdef FEAT_SYN_HL
&& !(wp->w_p_cul && lnum == wp->w_cursor.lnum
&& !(wp == curwin && VIsual_active))
@@ -5644,30 +5438,10 @@ win_line(
#ifdef FEAT_SEARCH_EXTRA
if (area_attr == 0)
{
/* Use attributes from match with highest priority among
* 'search_hl' and the match list. */
cur = wp->w_match_head;
shl_flag = FALSE;
while (cur != NULL || shl_flag == FALSE)
{
if (shl_flag == FALSE
&& ((cur != NULL
&& cur->priority > SEARCH_HL_PRIORITY)
|| cur == NULL))
{
shl = &search_hl;
shl_flag = TRUE;
if (screen_line_flags & SLF_POPUP)
continue; // do not use search_hl
}
else
shl = &cur->hl;
if ((ptr - line) - 1 == (long)shl->startcol
&& (shl == &search_hl || !shl->is_addpos))
char_attr = shl->attr;
if (shl != &search_hl && cur != NULL)
cur = cur->next;
}
// Use attributes from match with highest priority among
// 'search_hl' and the match list.
get_search_match_hl(wp, &search_hl,
(long)(ptr - line), &char_attr);
}
#endif
ScreenAttrs[off] = char_attr;
@@ -7892,323 +7666,6 @@ end_search_hl(void)
search_hl.rm.regprog = NULL;
}
}
/*
* Init for calling prepare_search_hl().
*/
static void
init_search_hl(win_T *wp)
{
matchitem_T *cur;
/* Setup for match and 'hlsearch' highlighting. Disable any previous
* match */
cur = wp->w_match_head;
while (cur != NULL)
{
cur->hl.rm = cur->match;
if (cur->hlg_id == 0)
cur->hl.attr = 0;
else
cur->hl.attr = syn_id2attr(cur->hlg_id);
cur->hl.buf = wp->w_buffer;
cur->hl.lnum = 0;
cur->hl.first_lnum = 0;
# ifdef FEAT_RELTIME
/* Set the time limit to 'redrawtime'. */
profile_setlimit(p_rdt, &(cur->hl.tm));
# endif
cur = cur->next;
}
search_hl.buf = wp->w_buffer;
search_hl.lnum = 0;
search_hl.first_lnum = 0;
/* time limit is set at the toplevel, for all windows */
}
/*
* Advance to the match in window "wp" line "lnum" or past it.
*/
static void
prepare_search_hl(win_T *wp, linenr_T lnum)
{
matchitem_T *cur; /* points to the match list */
match_T *shl; /* points to search_hl or a match */
int shl_flag; /* flag to indicate whether search_hl
has been processed or not */
int pos_inprogress; /* marks that position match search is
in progress */
int n;
/*
* When using a multi-line pattern, start searching at the top
* of the window or just after a closed fold.
* Do this both for search_hl and the match list.
*/
cur = wp->w_match_head;
shl_flag = FALSE;
while (cur != NULL || shl_flag == FALSE)
{
if (shl_flag == FALSE)
{
shl = &search_hl;
shl_flag = TRUE;
}
else
shl = &cur->hl;
if (shl->rm.regprog != NULL
&& shl->lnum == 0
&& re_multiline(shl->rm.regprog))
{
if (shl->first_lnum == 0)
{
# ifdef FEAT_FOLDING
for (shl->first_lnum = lnum;
shl->first_lnum > wp->w_topline; --shl->first_lnum)
if (hasFoldingWin(wp, shl->first_lnum - 1,
NULL, NULL, TRUE, NULL))
break;
# else
shl->first_lnum = wp->w_topline;
# endif
}
if (cur != NULL)
cur->pos.cur = 0;
pos_inprogress = TRUE;
n = 0;
while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
|| (cur != NULL && pos_inprogress)))
{
next_search_hl(wp, shl, shl->first_lnum, (colnr_T)n,
shl == &search_hl ? NULL : cur);
pos_inprogress = cur == NULL || cur->pos.cur == 0
? FALSE : TRUE;
if (shl->lnum != 0)
{
shl->first_lnum = shl->lnum
+ shl->rm.endpos[0].lnum
- shl->rm.startpos[0].lnum;
n = shl->rm.endpos[0].col;
}
else
{
++shl->first_lnum;
n = 0;
}
}
}
if (shl != &search_hl && cur != NULL)
cur = cur->next;
}
}
/*
* Search for a next 'hlsearch' or match.
* Uses shl->buf.
* Sets shl->lnum and shl->rm contents.
* Note: Assumes a previous match is always before "lnum", unless
* shl->lnum is zero.
* Careful: Any pointers for buffer lines will become invalid.
*/
static void
next_search_hl(
win_T *win,
match_T *shl, /* points to search_hl or a match */
linenr_T lnum,
colnr_T mincol, /* minimal column for a match */
matchitem_T *cur) /* to retrieve match positions if any */
{
linenr_T l;
colnr_T matchcol;
long nmatched;
int save_called_emsg = called_emsg;
// for :{range}s/pat only highlight inside the range
if (lnum < search_first_line || lnum > search_last_line)
{
shl->lnum = 0;
return;
}
if (shl->lnum != 0)
{
/* Check for three situations:
* 1. If the "lnum" is below a previous match, start a new search.
* 2. If the previous match includes "mincol", use it.
* 3. Continue after the previous match.
*/
l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
if (lnum > l)
shl->lnum = 0;
else if (lnum < l || shl->rm.endpos[0].col > mincol)
return;
}
/*
* Repeat searching for a match until one is found that includes "mincol"
* or none is found in this line.
*/
called_emsg = FALSE;
for (;;)
{
#ifdef FEAT_RELTIME
/* Stop searching after passing the time limit. */
if (profile_passed_limit(&(shl->tm)))
{
shl->lnum = 0; /* no match found in time */
break;
}
#endif
/* Three situations:
* 1. No useful previous match: search from start of line.
* 2. Not Vi compatible or empty match: continue at next character.
* Break the loop if this is beyond the end of the line.
* 3. Vi compatible searching: continue at end of previous match.
*/
if (shl->lnum == 0)
matchcol = 0;
else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
|| (shl->rm.endpos[0].lnum == 0
&& shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
{
char_u *ml;
matchcol = shl->rm.startpos[0].col;
ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
if (*ml == NUL)
{
++matchcol;
shl->lnum = 0;
break;
}
if (has_mbyte)
matchcol += mb_ptr2len(ml);
else
++matchcol;
}
else
matchcol = shl->rm.endpos[0].col;
shl->lnum = lnum;
if (shl->rm.regprog != NULL)
{
/* Remember whether shl->rm is using a copy of the regprog in
* cur->match. */
int regprog_is_copy = (shl != &search_hl && cur != NULL
&& shl == &cur->hl
&& cur->match.regprog == cur->hl.rm.regprog);
int timed_out = FALSE;
nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
matchcol,
#ifdef FEAT_RELTIME
&(shl->tm), &timed_out
#else
NULL, NULL
#endif
);
/* Copy the regprog, in case it got freed and recompiled. */
if (regprog_is_copy)
cur->match.regprog = cur->hl.rm.regprog;
if (called_emsg || got_int || timed_out)
{
/* Error while handling regexp: stop using this regexp. */
if (shl == &search_hl)
{
/* don't free regprog in the match list, it's a copy */
vim_regfree(shl->rm.regprog);
set_no_hlsearch(TRUE);
}
shl->rm.regprog = NULL;
shl->lnum = 0;
got_int = FALSE; /* avoid the "Type :quit to exit Vim"
message */
break;
}
}
else if (cur != NULL)
nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
else
nmatched = 0;
if (nmatched == 0)
{
shl->lnum = 0; /* no match found */
break;
}
if (shl->rm.startpos[0].lnum > 0
|| shl->rm.startpos[0].col >= mincol
|| nmatched > 1
|| shl->rm.endpos[0].col > mincol)
{
shl->lnum += shl->rm.startpos[0].lnum;
break; /* useful match found */
}
}
// Restore called_emsg for assert_fails().
called_emsg = save_called_emsg;
}
/*
* If there is a match fill "shl" and return one.
* Return zero otherwise.
*/
static int
next_search_hl_pos(
match_T *shl, /* points to a match */
linenr_T lnum,
posmatch_T *posmatch, /* match positions */
colnr_T mincol) /* minimal column for a match */
{
int i;
int found = -1;
for (i = posmatch->cur; i < MAXPOSMATCH; i++)
{
llpos_T *pos = &posmatch->pos[i];
if (pos->lnum == 0)
break;
if (pos->len == 0 && pos->col < mincol)
continue;
if (pos->lnum == lnum)
{
if (found >= 0)
{
/* if this match comes before the one at "found" then swap
* them */
if (pos->col < posmatch->pos[found].col)
{
llpos_T tmp = *pos;
*pos = posmatch->pos[found];
posmatch->pos[found] = tmp;
}
}
else
found = i;
}
}
posmatch->cur = 0;
if (found >= 0)
{
colnr_T start = posmatch->pos[found].col == 0
? 0 : posmatch->pos[found].col - 1;
colnr_T end = posmatch->pos[found].col == 0
? MAXCOL : start + posmatch->pos[found].len;
shl->lnum = lnum;
shl->rm.startpos[0].lnum = 0;
shl->rm.startpos[0].col = start;
shl->rm.endpos[0].lnum = 0;
shl->rm.endpos[0].col = end;
shl->is_addpos = TRUE;
posmatch->cur = found + 1;
return 1;
}
return 0;
}
#endif
static void