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

bbca7732e8
This commit is contained in:
Lewis Russell 2022-03-18 14:36:59 +00:00
parent 3c62a3f9dd
commit 087a9603d0
2 changed files with 528 additions and 491 deletions

View File

@ -4,6 +4,7 @@
// highlight_group.c: code for managing highlight groups // highlight_group.c: code for managing highlight groups
// Includes highlighting matches // Includes highlighting matches
#include <stdbool.h>
#include "nvim/autocmd.h" #include "nvim/autocmd.h"
#include "nvim/api/private/helpers.h" #include "nvim/api/private/helpers.h"
#include "nvim/charset.h" #include "nvim/charset.h"
@ -11,11 +12,13 @@
#include "nvim/eval/funcs.h" #include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h" #include "nvim/eval/typval.h"
#include "nvim/ex_docmd.h" #include "nvim/ex_docmd.h"
#include "nvim/fold.h"
#include "nvim/garray.h" #include "nvim/garray.h"
#include "nvim/highlight.h" #include "nvim/highlight.h"
#include "nvim/highlight_group.h" #include "nvim/highlight_group.h"
#include "nvim/lua/executor.h" #include "nvim/lua/executor.h"
#include "nvim/map.h" #include "nvim/map.h"
#include "nvim/memline.h"
#include "nvim/option.h" #include "nvim/option.h"
#include "nvim/regexp.h" #include "nvim/regexp.h"
#include "nvim/runtime.h" #include "nvim/runtime.h"
@ -29,6 +32,7 @@
/// @} /// @}
#define MAX_SYN_NAME 200 #define MAX_SYN_NAME 200
#define SEARCH_HL_PRIORITY 0
static char *e_invalwindow = N_("E957: Invalid window number"); static char *e_invalwindow = N_("E957: Invalid window number");
@ -2813,8 +2817,8 @@ int name_to_ctermcolor(const char *name)
/// particular ID is desired /// particular ID is desired
/// @param[in] conceal_char pointer to conceal replacement char /// @param[in] conceal_char pointer to conceal replacement char
/// @return ID of added match, -1 on failure. /// @return ID of added match, -1 on failure.
int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id, static int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id,
list_T *pos_list, const char *const conceal_char) list_T *pos_list, const char *const conceal_char)
FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_NONNULL_ARG(1, 2)
{ {
matchitem_T *cur; matchitem_T *cur;
@ -2998,7 +3002,7 @@ fail:
/// Delete match with ID 'id' in the match list of window 'wp'. /// Delete match with ID 'id' in the match list of window 'wp'.
/// ///
/// @param perr print error messages if true. /// @param perr print error messages if true.
int match_delete(win_T *wp, int id, bool perr) static int match_delete(win_T *wp, int id, bool perr)
{ {
matchitem_T *cur = wp->w_match_head; matchitem_T *cur = wp->w_match_head;
matchitem_T *prev = cur; matchitem_T *prev = cur;
@ -3077,7 +3081,508 @@ matchitem_T *get_match(win_T *wp, int id)
return cur; return cur;
} }
int matchadd_dict_arg(typval_T *tv, const char **conceal_char, win_T **win) /// Init for calling prepare_search_hl().
void init_search_hl(win_T *wp, match_T *search_hl)
FUNC_ATTR_NONNULL_ALL
{
// Setup for match and 'hlsearch' highlighting. Disable any previous
// match
matchitem_T *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;
// Set the time limit to 'redrawtime'.
cur->hl.tm = profile_setlimit(p_rdt);
cur = cur->next;
}
search_hl->buf = wp->w_buffer;
search_hl->lnum = 0;
search_hl->first_lnum = 0;
search_hl->attr = win_hl_attr(wp, HLF_L);
// time limit is set at the toplevel, for all windows
}
/// @param shl points to a match. Fill on match.
/// @param posmatch match positions
/// @param mincol minimal column for a match
///
/// @return one on match, otherwise return zero.
static int next_search_hl_pos(match_T *shl, linenr_T lnum, posmatch_T *posmatch, colnr_T mincol)
FUNC_ATTR_NONNULL_ALL
{
int i;
int found = -1;
shl->lnum = 0;
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;
}
/// 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.
///
/// @param shl points to search_hl or a match
/// @param mincol minimal column for a match
/// @param cur to retrieve match positions if any
static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_T lnum,
colnr_T mincol, matchitem_T *cur)
FUNC_ATTR_NONNULL_ARG(2)
{
linenr_T l;
colnr_T matchcol;
long nmatched = 0;
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 (;;) {
// Stop searching after passing the time limit.
if (profile_passed_limit(shl->tm)) {
shl->lnum = 0; // no match found in time
break;
}
// 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;
}
matchcol += utfc_ptr2len(ml);
} 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.
bool 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,
&(shl->tm), &timed_out);
// 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);
}
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;
}
}
/// Advance to the match in window "wp" line "lnum" or past it.
void prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
FUNC_ATTR_NONNULL_ALL
{
matchitem_T *cur; // points to the match list
match_T *shl; // points to search_hl or a match
bool shl_flag; // flag to indicate whether search_hl
// has been processed or not
// 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; // -V595
}
if (shl->rm.regprog != NULL
&& shl->lnum == 0
&& re_multiline(shl->rm.regprog)) {
if (shl->first_lnum == 0) {
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;
}
}
}
if (cur != NULL) {
cur->pos.cur = 0;
}
bool pos_inprogress = true; // mark that a position match search is
// in progress
int n = 0;
while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
|| (cur != NULL && pos_inprogress))) {
next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
shl == search_hl ? NULL : cur);
pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
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;
}
}
}
/// Prepare for 'hlsearch' and match highlighting in one window line.
/// Return true if there is such highlighting and set "search_attr" to the
/// current highlight attribute.
bool prepare_search_hl_line(win_T *wp, linenr_T lnum, colnr_T mincol, char_u **line,
match_T *search_hl, int *search_attr, bool *search_attr_from_match)
{
matchitem_T *cur = wp->w_match_head; // points to the match list
match_T *shl; // points to search_hl or a match
bool shl_flag = false; // flag to indicate whether search_hl
// has been processed or not
bool area_highlighting = false;
// Handle highlighting the last used search pattern and matches.
// Do this for both search_hl and the match list.
while (cur != NULL || !shl_flag) {
if (!shl_flag) {
shl = search_hl;
shl_flag = true;
} else {
shl = &cur->hl; // -V595
}
shl->startcol = MAXCOL;
shl->endcol = MAXCOL;
shl->attr_cur = 0;
shl->is_addpos = false;
if (cur != NULL) {
cur->pos.cur = 0;
}
next_search_hl(wp, search_hl, shl, lnum, mincol,
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);
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 ((*line)[shl->endcol] != NUL) {
shl->endcol += utfc_ptr2len(*line + shl->endcol);
} else {
shl->endcol++;
}
}
if ((long)shl->startcol < mincol) { // match at leftcol
shl->attr_cur = shl->attr;
*search_attr = shl->attr;
*search_attr_from_match = shl != search_hl;
}
area_highlighting = true;
}
if (shl != search_hl && cur != NULL) {
cur = cur->next;
}
}
return area_highlighting;
}
/// For a position in a line: 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!
/// Return the updated search_attr.
int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char_u **line, match_T *search_hl,
int *has_match_conc, int *match_conc, int lcs_eol_one,
bool *search_attr_from_match)
{
matchitem_T *cur = wp->w_match_head; // points to the match list
match_T *shl; // points to search_hl or a match
bool shl_flag = false; // flag to indicate whether search_hl
// has been processed or not
int search_attr = 0;
// Do this for 'search_hl' and the match list (ordered by priority).
while (cur != NULL || !shl_flag) {
if (!shl_flag
&& (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) {
shl = search_hl;
shl_flag = true;
} else {
shl = &cur->hl;
}
if (cur != NULL) {
cur->pos.cur = 0;
}
bool pos_inprogress = true; // mark that a position match search is
// in progress
while (shl->rm.regprog != NULL
|| (cur != NULL && pos_inprogress)) {
if (shl->startcol != MAXCOL
&& col >= shl->startcol
&& col < shl->endcol) {
int next_col = col + utfc_ptr2len(*line + col);
if (shl->endcol < next_col) {
shl->endcol = next_col;
}
shl->attr_cur = shl->attr;
// Match with the "Conceal" group results in hiding
// the match.
if (cur != NULL
&& shl != search_hl
&& syn_name2id("Conceal") == cur->hlg_id) {
*has_match_conc = col == shl->startcol ? 2 : 1;
*match_conc = cur->conceal_char;
} else {
*has_match_conc = 0;
}
} else if (col == shl->endcol) {
shl->attr_cur = 0;
next_search_hl(wp, search_hl, shl, lnum, col,
shl == search_hl ? NULL : cur);
pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
// Need to get the line again, a multi-line regexp
// may have made it invalid.
*line = ml_get_buf(wp->w_buffer, lnum, false);
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
shl->endcol += utfc_ptr2len(*line + 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.
*search_attr_from_match = false;
search_attr = search_hl->attr_cur;
cur = wp->w_match_head;
shl_flag = false;
while (cur != NULL || !shl_flag) {
if (!shl_flag
&& (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) {
shl = search_hl;
shl_flag = true;
} else {
shl = &cur->hl;
}
if (shl->attr_cur != 0) {
search_attr = shl->attr_cur;
*search_attr_from_match = shl != search_hl;
}
if (shl != search_hl && cur != NULL) {
cur = cur->next;
}
}
// Only highlight one character after the last column.
if (*(*line + col) == NUL && (wp->w_p_list && lcs_eol_one == -1)) {
search_attr = 0;
}
return search_attr;
}
bool get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
{
long prevcol = curcol;
matchitem_T *cur; // points to the match list
// 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++;
}
if (!search_hl->is_addpos && prevcol == search_hl->startcol) {
return true;
} else {
cur = wp->w_match_head;
while (cur != NULL) {
if (!cur->hl.is_addpos && prevcol == cur->hl.startcol) {
return true;
}
cur = cur->next;
}
}
return false;
}
/// Get highlighting for the char after the text in "char_attr" from 'hlsearch'
/// or match highlighting.
void get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
{
matchitem_T *cur = wp->w_match_head; // points to the match list
match_T *shl; // points to search_hl or a match
bool shl_flag = false; // flag to indicate whether search_hl
// has been processed or not
*char_attr = search_hl->attr;
while (cur != NULL || !shl_flag) {
if (!shl_flag
&& (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) {
shl = search_hl;
shl_flag = true;
} else {
shl = &cur->hl;
}
if (col - 1 == (long)shl->startcol
&& (shl == search_hl || !shl->is_addpos)) {
*char_attr = shl->attr;
}
if (shl != search_hl && cur != NULL) {
cur = cur->next;
}
}
}
static int matchadd_dict_arg(typval_T *tv, const char **conceal_char, win_T **win)
{ {
dictitem_T *di; dictitem_T *di;

View File

@ -164,7 +164,6 @@ static bool resizing = false;
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "screen.c.generated.h" # include "screen.c.generated.h"
#endif #endif
#define SEARCH_HL_PRIORITY 0
static char *provider_err = NULL; static char *provider_err = NULL;
@ -768,7 +767,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
redraw_win_signcol(wp); redraw_win_signcol(wp);
init_search_hl(wp); init_search_hl(wp, &search_hl);
/* Force redraw when width of 'number' or 'relativenumber' column /* Force redraw when width of 'number' or 'relativenumber' column
* changes. */ * changes. */
@ -1533,7 +1532,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
// will draw "@ " lines below. // will draw "@ " lines below.
row = wp->w_grid.Rows + 1; row = wp->w_grid.Rows + 1;
} else { } else {
prepare_search_hl(wp, lnum); prepare_search_hl(wp, &search_hl, lnum);
// Let the syntax stuff know we skipped a few lines. // Let the syntax stuff know we skipped a few lines.
if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum
&& syntax_present(wp)) { && syntax_present(wp)) {
@ -2095,13 +2094,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
int line_attr_save; int line_attr_save;
int line_attr_lowprio = 0; // low-priority attribute for the line int line_attr_lowprio = 0; // low-priority attribute for the line
int line_attr_lowprio_save; int line_attr_lowprio_save;
matchitem_T *cur; // points to the match list
match_T *shl; // points to search_hl or a match
bool shl_flag; // flag to indicate whether search_hl
// has been processed or not
bool prevcol_hl_flag; // flag to indicate whether prevcol
// equals startcol of search_hl or one
// of the matches
int prev_c = 0; // previous Arabic character int prev_c = 0; // previous Arabic character
int prev_c1 = 0; // first composing char for prev_c int prev_c1 = 0; // first composing char for prev_c
@ -2599,66 +2591,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
} }
} }
/* if (!number_only && !has_fold && !end_fill) {
* Handle highlighting the last used search pattern and matches. v = ptr - line;
* Do this for both search_hl and the match list. area_highlighting |= prepare_search_hl_line(wp, lnum, (colnr_T)v,
*/ &line, &search_hl, &search_attr,
cur = wp->w_match_head; &search_attr_from_match);
shl_flag = false; ptr = line + v; // "line" may have been updated
while ((cur != NULL || !shl_flag) && !number_only
&& !has_fold && !end_fill) {
if (!shl_flag) {
shl = &search_hl;
shl_flag = true;
} else {
shl = &cur->hl; // -V595
}
shl->startcol = MAXCOL;
shl->endcol = MAXCOL;
shl->attr_cur = 0;
shl->is_addpos = false;
v = (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 (line[shl->endcol] != NUL) {
shl->endcol += utfc_ptr2len(line + shl->endcol);
} else {
++shl->endcol;
}
}
if ((long)shl->startcol < v) { // match at leftcol
shl->attr_cur = shl->attr;
search_attr = shl->attr;
search_attr_from_match = shl != &search_hl;
}
area_highlighting = true;
}
if (shl != &search_hl && cur != NULL) {
cur = cur->next;
}
} }
unsigned off = 0; // Offset relative start of line unsigned off = 0; // Offset relative start of line
@ -3013,115 +2951,13 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
} }
if (!n_extra) { if (!n_extra) {
/* // Check for start/end of 'hlsearch' and other matches.
* Check for start/end of search pattern match. // After end, check for start/end of next match.
* After end, check for start/end of next match. // When another match, have to check for start again.
* 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 = (ptr - line); v = (ptr - line);
cur = wp->w_match_head; search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &search_hl, &has_match_conc,
shl_flag = false; &match_conc, lcs_eol_one, &search_attr_from_match);
while (cur != NULL || !shl_flag) { ptr = line + v; // "line" may have been changed
if (!shl_flag
&& (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) {
shl = &search_hl;
shl_flag = true;
} else {
shl = &cur->hl;
}
if (cur != NULL) {
cur->pos.cur = 0;
}
bool pos_inprogress = true; // mark that a position match search is
// in progress
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 + utfc_ptr2len(ptr);
if (shl->endcol < tmp_col) {
shl->endcol = tmp_col;
}
shl->attr_cur = shl->attr;
// Match with the "Conceal" group results in hiding
// the match.
if (cur != NULL
&& shl != &search_hl
&& syn_name2id("Conceal") == cur->hlg_id) {
has_match_conc = v == (long)shl->startcol ? 2 : 1;
match_conc = cur->conceal_char;
} else {
has_match_conc = 0;
}
} 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);
// 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
shl->endcol += utfc_ptr2len(line + 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. */
search_attr_from_match = false;
search_attr = search_hl.attr_cur;
cur = wp->w_match_head;
shl_flag = false;
while (cur != NULL || !shl_flag) {
if (!shl_flag
&& (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) {
shl = &search_hl;
shl_flag = true;
} else {
shl = &cur->hl;
}
if (shl->attr_cur != 0) {
search_attr = shl->attr_cur;
search_attr_from_match = shl != &search_hl;
}
if (shl != &search_hl && cur != NULL) {
cur = cur->next;
}
}
// Only highlight one character after the last column.
if (*ptr == NUL
&& (wp->w_p_list && lcs_eol_one == -1)) {
search_attr = 0;
}
// Do not allow a conceal over EOL otherwise EOL will be missed // Do not allow a conceal over EOL otherwise EOL will be missed
// and bad things happen. // and bad things happen.
@ -3939,30 +3775,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
// At end of the text line or just after the last character. // At end of the text line or just after the last character.
if (c == NUL && eol_hl_off == 0) { if (c == NUL && eol_hl_off == 0) {
long prevcol = (ptr - line) - 1; // flag to indicate whether prevcol equals startcol of search_hl or
// one of the matches
// we're not really at that column when skipping some text bool prevcol_hl_flag = get_prevcol_hl_flag(wp, &search_hl,
if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol) { (long)(ptr - line) - 1);
prevcol++;
}
// Invert at least one char, used for Visual and empty line or // Invert at least one char, used for Visual and empty line or
// highlight match at end of line. If it's beyond the last // highlight match at end of line. If it's beyond the last
// char on the screen, just overwrite that one (tricky!) Not // char on the screen, just overwrite that one (tricky!) Not
// needed when a '$' was displayed for 'list'. // needed when a '$' was displayed for 'list'.
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;
}
}
if (wp->w_p_lcs_chars.eol == lcs_eol_one if (wp->w_p_lcs_chars.eol == lcs_eol_one
&& ((area_attr != 0 && vcol == fromcol && ((area_attr != 0 && vcol == fromcol
&& (VIsual_mode != Ctrl_V && (VIsual_mode != Ctrl_V
@ -3993,25 +3814,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
if (area_attr == 0 && !has_fold) { if (area_attr == 0 && !has_fold) {
// Use attributes from match with highest priority among // Use attributes from match with highest priority among
// 'search_hl' and the match list. // 'search_hl' and the match list.
char_attr = search_hl.attr; get_search_match_hl(wp, &search_hl, (long)(ptr - line), &char_attr);
cur = wp->w_match_head;
shl_flag = false;
while (cur != NULL || !shl_flag) {
if (!shl_flag
&& (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) {
shl = &search_hl;
shl_flag = true;
} 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;
}
}
} }
int eol_attr = char_attr; int eol_attr = char_attr;
@ -6192,277 +5995,6 @@ static void end_search_hl(void)
} }
/*
* Init for calling prepare_search_hl().
*/
static void init_search_hl(win_T *wp)
FUNC_ATTR_NONNULL_ALL
{
// Setup for match and 'hlsearch' highlighting. Disable any previous
// match
matchitem_T *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;
// Set the time limit to 'redrawtime'.
cur->hl.tm = profile_setlimit(p_rdt);
cur = cur->next;
}
search_hl.buf = wp->w_buffer;
search_hl.lnum = 0;
search_hl.first_lnum = 0;
search_hl.attr = win_hl_attr(wp, HLF_L);
// 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)
FUNC_ATTR_NONNULL_ALL
{
matchitem_T *cur; // points to the match list
match_T *shl; // points to search_hl or a match
bool shl_flag; // flag to indicate whether search_hl
// has been processed or not
// 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; // -V595
}
if (shl->rm.regprog != NULL
&& shl->lnum == 0
&& re_multiline(shl->rm.regprog)) {
if (shl->first_lnum == 0) {
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;
}
}
}
if (cur != NULL) {
cur->pos.cur = 0;
}
bool pos_inprogress = true; // mark that a position match search is
// in progress
int 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);
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.
///
/// @param shl points to search_hl or a match
/// @param mincol minimal column for a match
/// @param cur to retrieve match positions if any
static void next_search_hl(win_T *win, match_T *shl, linenr_T lnum, colnr_T mincol,
matchitem_T *cur)
FUNC_ATTR_NONNULL_ARG(2)
{
linenr_T l;
colnr_T matchcol;
long nmatched = 0;
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 (;;) {
// Stop searching after passing the time limit.
if (profile_passed_limit(shl->tm)) {
shl->lnum = 0; // no match found in time
break;
}
// 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;
}
matchcol += utfc_ptr2len(ml);
} 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.
bool 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,
&(shl->tm), &timed_out);
// 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);
}
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;
}
}
/// @param shl points to a match. Fill on match.
/// @param posmatch match positions
/// @param mincol minimal column for a match
///
/// @return one on match, otherwise return zero.
static int next_search_hl_pos(match_T *shl, linenr_T lnum, posmatch_T *posmatch, colnr_T mincol)
FUNC_ATTR_NONNULL_ALL
{
int i;
int found = -1;
shl->lnum = 0;
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;
}
/// Fill the grid from 'start_row' to 'end_row', from 'start_col' to 'end_col' /// Fill the grid from 'start_row' to 'end_row', from 'start_col' to 'end_col'
/// with character 'c1' in first column followed by 'c2' in the other columns. /// with character 'c1' in first column followed by 'c2' in the other columns.
/// Use attributes 'attr'. /// Use attributes 'attr'.