aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/highlight_group.c513
-rw-r--r--src/nvim/screen.c506
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'.