diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/highlight_group.c | 513 | ||||
-rw-r--r-- | src/nvim/screen.c | 506 |
2 files changed, 528 insertions, 491 deletions
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 72416923f1..ee78a79a97 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -4,6 +4,7 @@ // highlight_group.c: code for managing highlight groups // Includes highlighting matches +#include <stdbool.h> #include "nvim/autocmd.h" #include "nvim/api/private/helpers.h" #include "nvim/charset.h" @@ -11,11 +12,13 @@ #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/ex_docmd.h" +#include "nvim/fold.h" #include "nvim/garray.h" #include "nvim/highlight.h" #include "nvim/highlight_group.h" #include "nvim/lua/executor.h" #include "nvim/map.h" +#include "nvim/memline.h" #include "nvim/option.h" #include "nvim/regexp.h" #include "nvim/runtime.h" @@ -29,6 +32,7 @@ /// @} #define MAX_SYN_NAME 200 +#define SEARCH_HL_PRIORITY 0 static char *e_invalwindow = N_("E957: Invalid window number"); @@ -2813,8 +2817,8 @@ int name_to_ctermcolor(const char *name) /// particular ID is desired /// @param[in] conceal_char pointer to conceal replacement char /// @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, - list_T *pos_list, const char *const conceal_char) +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) FUNC_ATTR_NONNULL_ARG(1, 2) { matchitem_T *cur; @@ -2998,7 +3002,7 @@ fail: /// Delete match with ID 'id' in the match list of window 'wp'. /// /// @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 *prev = cur; @@ -3077,7 +3081,508 @@ matchitem_T *get_match(win_T *wp, int id) 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; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 0e1f469a4b..83d555e584 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -164,7 +164,6 @@ static bool resizing = false; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "screen.c.generated.h" #endif -#define SEARCH_HL_PRIORITY 0 static char *provider_err = NULL; @@ -768,7 +767,7 @@ static void win_update(win_T *wp, DecorProviders *providers) redraw_win_signcol(wp); - init_search_hl(wp); + init_search_hl(wp, &search_hl); /* Force redraw when width of 'number' or 'relativenumber' column * changes. */ @@ -1533,7 +1532,7 @@ static void win_update(win_T *wp, DecorProviders *providers) // will draw "@ " lines below. row = wp->w_grid.Rows + 1; } else { - prepare_search_hl(wp, lnum); + prepare_search_hl(wp, &search_hl, lnum); // Let the syntax stuff know we skipped a few lines. if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum && 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_lowprio = 0; // low-priority attribute for the line 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_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 } } - /* - * Handle highlighting the last used search pattern and matches. - * Do this for both search_hl and the match list. - */ - cur = wp->w_match_head; - shl_flag = false; - 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; - } + if (!number_only && !has_fold && !end_fill) { + v = ptr - line; + area_highlighting |= prepare_search_hl_line(wp, lnum, (colnr_T)v, + &line, &search_hl, &search_attr, + &search_attr_from_match); + ptr = line + v; // "line" may have been updated } 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) { - /* - * Check for start/end of search pattern match. - * 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). - */ + // 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. v = (ptr - line); - 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 (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; - } + search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &search_hl, &has_match_conc, + &match_conc, lcs_eol_one, &search_attr_from_match); + ptr = line + v; // "line" may have been changed // Do not allow a conceal over EOL otherwise EOL will be missed // 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. if (c == NUL && eol_hl_off == 0) { - long prevcol = (ptr - line) - 1; - - // 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++; - } + // flag to indicate whether prevcol equals startcol of search_hl or + // one of the matches + bool prevcol_hl_flag = get_prevcol_hl_flag(wp, &search_hl, + (long)(ptr - line) - 1); // 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'. - 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 && ((area_attr != 0 && vcol == fromcol && (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) { // Use attributes from match with highest priority among // 'search_hl' and the match list. - char_attr = search_hl.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; - } - } + get_search_match_hl(wp, &search_hl, (long)(ptr - line), &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' /// with character 'c1' in first column followed by 'c2' in the other columns. /// Use attributes 'attr'. |