To: vim_dev@googlegroups.com Subject: Patch 8.1.1743 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.1743 Problem: 'hlsearch' and match highlighting in the wrong place. Solution: Move highlighting from inside screen functions to highlight.c. Files: src/screen.c, src/highlight.c, src/proto/highlight.pro *** ../vim-8.1.1742/src/screen.c 2019-07-21 14:50:15.379092378 +0200 --- src/screen.c 2019-07-24 18:05:55.565571733 +0200 *************** *** 104,110 **** static int screen_cur_row, screen_cur_col; /* last known cursor position */ #ifdef FEAT_SEARCH_EXTRA ! static match_T search_hl; /* used for 'hlsearch' highlight matching */ #endif #ifdef FEAT_FOLDING --- 104,110 ---- static int screen_cur_row, screen_cur_col; /* last known cursor position */ #ifdef FEAT_SEARCH_EXTRA ! static match_T search_hl; // used for 'hlsearch' highlight matching #endif #ifdef FEAT_FOLDING *************** *** 135,147 **** static void redraw_custom_statusline(win_T *wp); #endif #ifdef FEAT_SEARCH_EXTRA - # define SEARCH_HL_PRIORITY 0 static void start_search_hl(void); static void end_search_hl(void); - static void init_search_hl(win_T *wp); - static void prepare_search_hl(win_T *wp, linenr_T lnum); - static void next_search_hl(win_T *win, match_T *shl, linenr_T lnum, colnr_T mincol, matchitem_T *cur); - static int next_search_hl_pos(match_T *shl, linenr_T lnum, posmatch_T *pos, colnr_T mincol); #endif static void screen_char(unsigned off, int row, int col); static void screen_char_2(unsigned off, int row, int col); --- 135,142 ---- *************** *** 1172,1178 **** #endif #ifdef FEAT_SEARCH_EXTRA ! init_search_hl(wp); #endif #ifdef FEAT_LINEBREAK --- 1167,1173 ---- #endif #ifdef FEAT_SEARCH_EXTRA ! init_search_hl(wp, &search_hl); #endif #ifdef FEAT_LINEBREAK *************** *** 2090,2096 **** else { #ifdef FEAT_SEARCH_EXTRA ! prepare_search_hl(wp, lnum); #endif #ifdef FEAT_SYN_HL /* Let the syntax stuff know we skipped a few lines. */ --- 2085,2091 ---- else { #ifdef FEAT_SEARCH_EXTRA ! prepare_search_hl(wp, &search_hl, lnum); #endif #ifdef FEAT_SYN_HL /* Let the syntax stuff know we skipped a few lines. */ *************** *** 3273,3289 **** int sign_present = FALSE; sign_attrs_T sattr; #endif - #ifdef FEAT_SEARCH_EXTRA - matchitem_T *cur; /* points to the match list */ - match_T *shl; /* points to search_hl or a match */ - int shl_flag; /* flag to indicate whether search_hl - has been processed or not */ - int pos_inprogress; /* marks that position match search is - in progress */ - int prevcol_hl_flag; /* flag to indicate whether prevcol - equals startcol of search_hl or one - of the matches */ - #endif #ifdef FEAT_ARABIC int prev_c = 0; /* previous Arabic character */ int prev_c1 = 0; /* first composing char for prev_c */ --- 3268,3273 ---- *************** *** 3808,3872 **** } #ifdef FEAT_SEARCH_EXTRA ! /* ! * Handle highlighting the last used search pattern and matches. ! * Do this for both search_hl and the match list. ! * Do not use search_hl in a popup window. ! */ ! cur = wp->w_match_head; ! shl_flag = (screen_line_flags & SLF_POPUP); ! while ((cur != NULL || shl_flag == FALSE) && !number_only) { - if (shl_flag == FALSE) - { - shl = &search_hl; - shl_flag = TRUE; - } - else - shl = &cur->hl; - shl->startcol = MAXCOL; - shl->endcol = MAXCOL; - shl->attr_cur = 0; - shl->is_addpos = FALSE; v = (long)(ptr - line); ! if (cur != NULL) ! cur->pos.cur = 0; ! next_search_hl(wp, shl, lnum, (colnr_T)v, ! shl == &search_hl ? NULL : cur); ! ! /* Need to get the line again, a multi-line regexp may have made it ! * invalid. */ ! line = ml_get_buf(wp->w_buffer, lnum, FALSE); ! ptr = line + v; ! ! if (shl->lnum != 0 && shl->lnum <= lnum) ! { ! if (shl->lnum == lnum) ! shl->startcol = shl->rm.startpos[0].col; ! else ! shl->startcol = 0; ! if (lnum == shl->lnum + shl->rm.endpos[0].lnum ! - shl->rm.startpos[0].lnum) ! shl->endcol = shl->rm.endpos[0].col; ! else ! shl->endcol = MAXCOL; ! /* Highlight one character for an empty match. */ ! if (shl->startcol == shl->endcol) ! { ! if (has_mbyte && line[shl->endcol] != NUL) ! shl->endcol += (*mb_ptr2len)(line + shl->endcol); ! else ! ++shl->endcol; ! } ! if ((long)shl->startcol < v) /* match at leftcol */ ! { ! shl->attr_cur = shl->attr; ! search_attr = shl->attr; ! } ! area_highlighting = TRUE; ! } ! if (shl != &search_hl && cur != NULL) ! cur = cur->next; } #endif --- 3792,3803 ---- } #ifdef FEAT_SEARCH_EXTRA ! if (!number_only) { v = (long)(ptr - line); ! area_highlighting |= prepare_search_hl_line(wp, lnum, (colnr_T)v, ! &line, &search_hl, &search_attr); ! ptr = line + v; // "line" may have been updated } #endif *************** *** 4247,4378 **** 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). */ v = (long)(ptr - line); ! cur = wp->w_match_head; ! shl_flag = FALSE; ! while (cur != NULL || shl_flag == FALSE) ! { ! if (shl_flag == FALSE ! && ((cur != NULL ! && cur->priority > SEARCH_HL_PRIORITY) ! || cur == NULL)) ! { ! shl = &search_hl; ! shl_flag = TRUE; ! if (screen_line_flags & SLF_POPUP) ! continue; // do not use search_hl ! } ! else ! shl = &cur->hl; ! if (cur != NULL) ! cur->pos.cur = 0; ! pos_inprogress = TRUE; ! while (shl->rm.regprog != NULL ! || (cur != NULL && pos_inprogress)) ! { ! if (shl->startcol != MAXCOL ! && v >= (long)shl->startcol ! && v < (long)shl->endcol) ! { ! int tmp_col = v + MB_PTR2LEN(ptr); ! ! if (shl->endcol < tmp_col) ! shl->endcol = tmp_col; ! shl->attr_cur = shl->attr; ! #ifdef FEAT_CONCEAL ! // Match with the "Conceal" group results in hiding ! // the match. ! if (cur != NULL ! && shl != &search_hl ! && syn_name2id((char_u *)"Conceal") ! == cur->hlg_id) ! { ! has_match_conc = ! v == (long)shl->startcol ? 2 : 1; ! match_conc = cur->conceal_char; ! } ! else ! has_match_conc = match_conc = 0; ! #endif ! } ! else if (v == (long)shl->endcol) ! { ! shl->attr_cur = 0; ! next_search_hl(wp, shl, lnum, (colnr_T)v, ! shl == &search_hl ? NULL : cur); ! pos_inprogress = cur == NULL || cur->pos.cur == 0 ! ? FALSE : TRUE; ! ! /* Need to get the line again, a multi-line regexp ! * may have made it invalid. */ ! line = ml_get_buf(wp->w_buffer, lnum, FALSE); ! ptr = line + v; ! ! if (shl->lnum == lnum) ! { ! shl->startcol = shl->rm.startpos[0].col; ! if (shl->rm.endpos[0].lnum == 0) ! shl->endcol = shl->rm.endpos[0].col; ! else ! shl->endcol = MAXCOL; ! ! if (shl->startcol == shl->endcol) ! { ! /* highlight empty match, try again after ! * it */ ! if (has_mbyte) ! shl->endcol += (*mb_ptr2len)(line ! + shl->endcol); ! else ! ++shl->endcol; ! } ! ! /* Loop to check if the match starts at the ! * current position */ ! continue; ! } ! } ! break; ! } ! if (shl != &search_hl && cur != NULL) ! cur = cur->next; ! } ! ! /* Use attributes from match with highest priority among ! * 'search_hl' and the match list. */ ! cur = wp->w_match_head; ! shl_flag = FALSE; ! search_attr = 0; ! while (cur != NULL || shl_flag == FALSE) ! { ! if (shl_flag == FALSE ! && ((cur != NULL ! && cur->priority > SEARCH_HL_PRIORITY) ! || cur == NULL)) ! { ! shl = &search_hl; ! shl_flag = TRUE; ! if (screen_line_flags & SLF_POPUP) ! continue; // do not use search_hl ! } ! else ! shl = &cur->hl; ! if (shl->attr_cur != 0) ! search_attr = shl->attr_cur; ! if (shl != &search_hl && cur != NULL) ! cur = cur->next; ! } ! /* Only highlight one character after the last column. */ ! if (*ptr == NUL && (did_line_attr >= 1 ! || (wp->w_p_list && lcs_eol_one == -1))) ! search_attr = 0; } #endif --- 4178,4192 ---- if (!n_extra) { /* ! * 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 = (long)(ptr - line); ! search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, ! &search_hl, &has_match_conc, &match_conc, ! did_line_attr, lcs_eol_one); ! ptr = line + v; // "line" may have been changed } #endif *************** *** 5561,5595 **** ) && eol_hl_off == 0) { #ifdef FEAT_SEARCH_EXTRA ! long prevcol = (long)(ptr - line) - (c == NUL); ! ! /* we're not really at that column when skipping some text */ ! if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol) ! ++prevcol; ! #endif ! ! /* Invert at least one char, used for Visual and empty line or ! * highlight match at end of line. If it's beyond the last ! * char on the screen, just overwrite that one (tricky!) Not ! * needed when a '$' was displayed for 'list'. */ ! #ifdef FEAT_SEARCH_EXTRA ! prevcol_hl_flag = FALSE; ! if (!search_hl.is_addpos && prevcol == (long)search_hl.startcol) ! prevcol_hl_flag = TRUE; ! else ! { ! cur = wp->w_match_head; ! while (cur != NULL) ! { ! if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol) ! { ! prevcol_hl_flag = TRUE; ! break; ! } ! cur = cur->next; ! } ! } ! #endif if (lcs_eol == lcs_eol_one && ((area_attr != 0 && vcol == fromcol && (VIsual_mode != Ctrl_V --- 5375,5389 ---- ) && eol_hl_off == 0) { #ifdef FEAT_SEARCH_EXTRA ! // flag to indicate whether prevcol equals startcol of search_hl or ! // one of the matches ! int prevcol_hl_flag = get_prevcol_hl_flag(wp, &search_hl, ! (long)(ptr - line) - (c == NUL)); ! #endif ! // Invert at least one char, used for Visual and empty line or ! // highlight match at end of line. If it's beyond the last ! // char on the screen, just overwrite that one (tricky!) Not ! // needed when a '$' was displayed for 'list'. if (lcs_eol == lcs_eol_one && ((area_attr != 0 && vcol == fromcol && (VIsual_mode != Ctrl_V *************** *** 5597,5604 **** || lnum == curwin->w_cursor.lnum) && c == NUL) #ifdef FEAT_SEARCH_EXTRA ! /* highlight 'hlsearch' match at end of line */ ! || (prevcol_hl_flag == TRUE # ifdef FEAT_SYN_HL && !(wp->w_p_cul && lnum == wp->w_cursor.lnum && !(wp == curwin && VIsual_active)) --- 5391,5398 ---- || lnum == curwin->w_cursor.lnum) && c == NUL) #ifdef FEAT_SEARCH_EXTRA ! // highlight 'hlsearch' match at end of line ! || (prevcol_hl_flag # ifdef FEAT_SYN_HL && !(wp->w_p_cul && lnum == wp->w_cursor.lnum && !(wp == curwin && VIsual_active)) *************** *** 5644,5673 **** #ifdef FEAT_SEARCH_EXTRA if (area_attr == 0) { ! /* Use attributes from match with highest priority among ! * 'search_hl' and the match list. */ ! cur = wp->w_match_head; ! shl_flag = FALSE; ! while (cur != NULL || shl_flag == FALSE) ! { ! if (shl_flag == FALSE ! && ((cur != NULL ! && cur->priority > SEARCH_HL_PRIORITY) ! || cur == NULL)) ! { ! shl = &search_hl; ! shl_flag = TRUE; ! if (screen_line_flags & SLF_POPUP) ! continue; // do not use search_hl ! } ! else ! shl = &cur->hl; ! if ((ptr - line) - 1 == (long)shl->startcol ! && (shl == &search_hl || !shl->is_addpos)) ! char_attr = shl->attr; ! if (shl != &search_hl && cur != NULL) ! cur = cur->next; ! } } #endif ScreenAttrs[off] = char_attr; --- 5438,5447 ---- #ifdef FEAT_SEARCH_EXTRA if (area_attr == 0) { ! // Use attributes from match with highest priority among ! // 'search_hl' and the match list. ! get_search_match_hl(wp, &search_hl, ! (long)(ptr - line), &char_attr); } #endif ScreenAttrs[off] = char_attr; *************** *** 7892,8214 **** search_hl.rm.regprog = NULL; } } - - /* - * Init for calling prepare_search_hl(). - */ - static void - init_search_hl(win_T *wp) - { - matchitem_T *cur; - - /* Setup for match and 'hlsearch' highlighting. Disable any previous - * match */ - cur = wp->w_match_head; - while (cur != NULL) - { - cur->hl.rm = cur->match; - if (cur->hlg_id == 0) - cur->hl.attr = 0; - else - cur->hl.attr = syn_id2attr(cur->hlg_id); - cur->hl.buf = wp->w_buffer; - cur->hl.lnum = 0; - cur->hl.first_lnum = 0; - # ifdef FEAT_RELTIME - /* Set the time limit to 'redrawtime'. */ - profile_setlimit(p_rdt, &(cur->hl.tm)); - # endif - cur = cur->next; - } - search_hl.buf = wp->w_buffer; - search_hl.lnum = 0; - search_hl.first_lnum = 0; - /* time limit is set at the toplevel, for all windows */ - } - - /* - * Advance to the match in window "wp" line "lnum" or past it. - */ - static void - prepare_search_hl(win_T *wp, linenr_T lnum) - { - matchitem_T *cur; /* points to the match list */ - match_T *shl; /* points to search_hl or a match */ - int shl_flag; /* flag to indicate whether search_hl - has been processed or not */ - int pos_inprogress; /* marks that position match search is - in progress */ - int n; - - /* - * When using a multi-line pattern, start searching at the top - * of the window or just after a closed fold. - * Do this both for search_hl and the match list. - */ - cur = wp->w_match_head; - shl_flag = FALSE; - while (cur != NULL || shl_flag == FALSE) - { - if (shl_flag == FALSE) - { - shl = &search_hl; - shl_flag = TRUE; - } - else - shl = &cur->hl; - if (shl->rm.regprog != NULL - && shl->lnum == 0 - && re_multiline(shl->rm.regprog)) - { - if (shl->first_lnum == 0) - { - # ifdef FEAT_FOLDING - for (shl->first_lnum = lnum; - shl->first_lnum > wp->w_topline; --shl->first_lnum) - if (hasFoldingWin(wp, shl->first_lnum - 1, - NULL, NULL, TRUE, NULL)) - break; - # else - shl->first_lnum = wp->w_topline; - # endif - } - if (cur != NULL) - cur->pos.cur = 0; - pos_inprogress = TRUE; - n = 0; - while (shl->first_lnum < lnum && (shl->rm.regprog != NULL - || (cur != NULL && pos_inprogress))) - { - next_search_hl(wp, shl, shl->first_lnum, (colnr_T)n, - shl == &search_hl ? NULL : cur); - pos_inprogress = cur == NULL || cur->pos.cur == 0 - ? FALSE : TRUE; - if (shl->lnum != 0) - { - shl->first_lnum = shl->lnum - + shl->rm.endpos[0].lnum - - shl->rm.startpos[0].lnum; - n = shl->rm.endpos[0].col; - } - else - { - ++shl->first_lnum; - n = 0; - } - } - } - if (shl != &search_hl && cur != NULL) - cur = cur->next; - } - } - - /* - * Search for a next 'hlsearch' or match. - * Uses shl->buf. - * Sets shl->lnum and shl->rm contents. - * Note: Assumes a previous match is always before "lnum", unless - * shl->lnum is zero. - * Careful: Any pointers for buffer lines will become invalid. - */ - static void - next_search_hl( - win_T *win, - match_T *shl, /* points to search_hl or a match */ - linenr_T lnum, - colnr_T mincol, /* minimal column for a match */ - matchitem_T *cur) /* to retrieve match positions if any */ - { - linenr_T l; - colnr_T matchcol; - long nmatched; - int save_called_emsg = called_emsg; - - // for :{range}s/pat only highlight inside the range - if (lnum < search_first_line || lnum > search_last_line) - { - shl->lnum = 0; - return; - } - - if (shl->lnum != 0) - { - /* Check for three situations: - * 1. If the "lnum" is below a previous match, start a new search. - * 2. If the previous match includes "mincol", use it. - * 3. Continue after the previous match. - */ - l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum; - if (lnum > l) - shl->lnum = 0; - else if (lnum < l || shl->rm.endpos[0].col > mincol) - return; - } - - /* - * Repeat searching for a match until one is found that includes "mincol" - * or none is found in this line. - */ - called_emsg = FALSE; - for (;;) - { - #ifdef FEAT_RELTIME - /* Stop searching after passing the time limit. */ - if (profile_passed_limit(&(shl->tm))) - { - shl->lnum = 0; /* no match found in time */ - break; - } - #endif - /* Three situations: - * 1. No useful previous match: search from start of line. - * 2. Not Vi compatible or empty match: continue at next character. - * Break the loop if this is beyond the end of the line. - * 3. Vi compatible searching: continue at end of previous match. - */ - if (shl->lnum == 0) - matchcol = 0; - else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL - || (shl->rm.endpos[0].lnum == 0 - && shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) - { - char_u *ml; - - matchcol = shl->rm.startpos[0].col; - ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol; - if (*ml == NUL) - { - ++matchcol; - shl->lnum = 0; - break; - } - if (has_mbyte) - matchcol += mb_ptr2len(ml); - else - ++matchcol; - } - else - matchcol = shl->rm.endpos[0].col; - - shl->lnum = lnum; - if (shl->rm.regprog != NULL) - { - /* Remember whether shl->rm is using a copy of the regprog in - * cur->match. */ - int regprog_is_copy = (shl != &search_hl && cur != NULL - && shl == &cur->hl - && cur->match.regprog == cur->hl.rm.regprog); - int timed_out = FALSE; - - nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, - matchcol, - #ifdef FEAT_RELTIME - &(shl->tm), &timed_out - #else - NULL, NULL - #endif - ); - /* Copy the regprog, in case it got freed and recompiled. */ - if (regprog_is_copy) - cur->match.regprog = cur->hl.rm.regprog; - - if (called_emsg || got_int || timed_out) - { - /* Error while handling regexp: stop using this regexp. */ - if (shl == &search_hl) - { - /* don't free regprog in the match list, it's a copy */ - vim_regfree(shl->rm.regprog); - set_no_hlsearch(TRUE); - } - shl->rm.regprog = NULL; - shl->lnum = 0; - got_int = FALSE; /* avoid the "Type :quit to exit Vim" - message */ - break; - } - } - else if (cur != NULL) - nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol); - else - nmatched = 0; - if (nmatched == 0) - { - shl->lnum = 0; /* no match found */ - break; - } - if (shl->rm.startpos[0].lnum > 0 - || shl->rm.startpos[0].col >= mincol - || nmatched > 1 - || shl->rm.endpos[0].col > mincol) - { - shl->lnum += shl->rm.startpos[0].lnum; - break; /* useful match found */ - } - } - - // Restore called_emsg for assert_fails(). - called_emsg = save_called_emsg; - } - - /* - * If there is a match fill "shl" and return one. - * Return zero otherwise. - */ - static int - next_search_hl_pos( - match_T *shl, /* points to a match */ - linenr_T lnum, - posmatch_T *posmatch, /* match positions */ - colnr_T mincol) /* minimal column for a match */ - { - int i; - int found = -1; - - for (i = posmatch->cur; i < MAXPOSMATCH; i++) - { - llpos_T *pos = &posmatch->pos[i]; - - if (pos->lnum == 0) - break; - if (pos->len == 0 && pos->col < mincol) - continue; - if (pos->lnum == lnum) - { - if (found >= 0) - { - /* if this match comes before the one at "found" then swap - * them */ - if (pos->col < posmatch->pos[found].col) - { - llpos_T tmp = *pos; - - *pos = posmatch->pos[found]; - posmatch->pos[found] = tmp; - } - } - else - found = i; - } - } - posmatch->cur = 0; - if (found >= 0) - { - colnr_T start = posmatch->pos[found].col == 0 - ? 0 : posmatch->pos[found].col - 1; - colnr_T end = posmatch->pos[found].col == 0 - ? MAXCOL : start + posmatch->pos[found].len; - - shl->lnum = lnum; - shl->rm.startpos[0].lnum = 0; - shl->rm.startpos[0].col = start; - shl->rm.endpos[0].lnum = 0; - shl->rm.endpos[0].col = end; - shl->is_addpos = TRUE; - posmatch->cur = found + 1; - return 1; - } - return 0; - } #endif static void --- 7666,7671 ---- *** ../vim-8.1.1742/src/highlight.c 2019-07-24 16:00:34.922180377 +0200 --- src/highlight.c 2019-07-24 18:05:21.093718407 +0200 *************** *** 3657,3662 **** --- 3657,3665 ---- #if defined(FEAT_SEARCH_EXTRA) || defined(PROTO) + + # define SEARCH_HL_PRIORITY 0 + /* * Add match to the match list of window 'wp'. The pattern 'pat' will be * highlighted with the group 'grp' with priority 'prio'. *************** *** 3948,3954 **** --- 3951,4560 ---- cur = cur->next; return cur; } + + /* + * Init for calling prepare_search_hl(). + */ + void + init_search_hl(win_T *wp, match_T *search_hl) + { + matchitem_T *cur; + + /* Setup for match and 'hlsearch' highlighting. Disable any previous + * match */ + cur = wp->w_match_head; + while (cur != NULL) + { + cur->hl.rm = cur->match; + if (cur->hlg_id == 0) + cur->hl.attr = 0; + else + cur->hl.attr = syn_id2attr(cur->hlg_id); + cur->hl.buf = wp->w_buffer; + cur->hl.lnum = 0; + cur->hl.first_lnum = 0; + # ifdef FEAT_RELTIME + /* Set the time limit to 'redrawtime'. */ + profile_setlimit(p_rdt, &(cur->hl.tm)); + # endif + cur = cur->next; + } + search_hl->buf = wp->w_buffer; + search_hl->lnum = 0; + search_hl->first_lnum = 0; + /* time limit is set at the toplevel, for all windows */ + } + + /* + * If there is a match fill "shl" and return one. + * Return zero otherwise. + */ + static int + next_search_hl_pos( + match_T *shl, /* points to a match */ + linenr_T lnum, + posmatch_T *posmatch, /* match positions */ + colnr_T mincol) /* minimal column for a match */ + { + int i; + int found = -1; + + for (i = posmatch->cur; i < MAXPOSMATCH; i++) + { + llpos_T *pos = &posmatch->pos[i]; + + if (pos->lnum == 0) + break; + if (pos->len == 0 && pos->col < mincol) + continue; + if (pos->lnum == lnum) + { + if (found >= 0) + { + /* if this match comes before the one at "found" then swap + * them */ + if (pos->col < posmatch->pos[found].col) + { + llpos_T tmp = *pos; + + *pos = posmatch->pos[found]; + posmatch->pos[found] = tmp; + } + } + else + found = i; + } + } + posmatch->cur = 0; + if (found >= 0) + { + colnr_T start = posmatch->pos[found].col == 0 + ? 0 : posmatch->pos[found].col - 1; + colnr_T end = posmatch->pos[found].col == 0 + ? MAXCOL : start + posmatch->pos[found].len; + + shl->lnum = lnum; + shl->rm.startpos[0].lnum = 0; + shl->rm.startpos[0].col = start; + shl->rm.endpos[0].lnum = 0; + shl->rm.endpos[0].col = end; + shl->is_addpos = TRUE; + posmatch->cur = found + 1; + return 1; + } + return 0; + } + + /* + * Search for a next 'hlsearch' or match. + * Uses shl->buf. + * Sets shl->lnum and shl->rm contents. + * Note: Assumes a previous match is always before "lnum", unless + * shl->lnum is zero. + * Careful: Any pointers for buffer lines will become invalid. + */ + static void + next_search_hl( + win_T *win, + match_T *search_hl, + match_T *shl, // points to search_hl or a match + linenr_T lnum, + colnr_T mincol, // minimal column for a match + matchitem_T *cur) // to retrieve match positions if any + { + linenr_T l; + colnr_T matchcol; + long nmatched; + int save_called_emsg = called_emsg; + + // for :{range}s/pat only highlight inside the range + if (lnum < search_first_line || lnum > search_last_line) + { + shl->lnum = 0; + return; + } + + if (shl->lnum != 0) + { + /* Check for three situations: + * 1. If the "lnum" is below a previous match, start a new search. + * 2. If the previous match includes "mincol", use it. + * 3. Continue after the previous match. + */ + l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum; + if (lnum > l) + shl->lnum = 0; + else if (lnum < l || shl->rm.endpos[0].col > mincol) + return; + } + + /* + * Repeat searching for a match until one is found that includes "mincol" + * or none is found in this line. + */ + called_emsg = FALSE; + for (;;) + { + # ifdef FEAT_RELTIME + /* Stop searching after passing the time limit. */ + if (profile_passed_limit(&(shl->tm))) + { + shl->lnum = 0; /* no match found in time */ + break; + } + # endif + /* Three situations: + * 1. No useful previous match: search from start of line. + * 2. Not Vi compatible or empty match: continue at next character. + * Break the loop if this is beyond the end of the line. + * 3. Vi compatible searching: continue at end of previous match. + */ + if (shl->lnum == 0) + matchcol = 0; + else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL + || (shl->rm.endpos[0].lnum == 0 + && shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) + { + char_u *ml; + + matchcol = shl->rm.startpos[0].col; + ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol; + if (*ml == NUL) + { + ++matchcol; + shl->lnum = 0; + break; + } + if (has_mbyte) + matchcol += mb_ptr2len(ml); + else + ++matchcol; + } + else + matchcol = shl->rm.endpos[0].col; + + shl->lnum = lnum; + if (shl->rm.regprog != NULL) + { + /* Remember whether shl->rm is using a copy of the regprog in + * cur->match. */ + int regprog_is_copy = (shl != search_hl && cur != NULL + && shl == &cur->hl + && cur->match.regprog == cur->hl.rm.regprog); + int timed_out = FALSE; + + nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, + matchcol, + #ifdef FEAT_RELTIME + &(shl->tm), &timed_out + #else + NULL, NULL #endif + ); + /* Copy the regprog, in case it got freed and recompiled. */ + if (regprog_is_copy) + cur->match.regprog = cur->hl.rm.regprog; + + if (called_emsg || got_int || timed_out) + { + /* Error while handling regexp: stop using this regexp. */ + if (shl == search_hl) + { + /* don't free regprog in the match list, it's a copy */ + vim_regfree(shl->rm.regprog); + set_no_hlsearch(TRUE); + } + shl->rm.regprog = NULL; + shl->lnum = 0; + got_int = FALSE; /* avoid the "Type :quit to exit Vim" + message */ + break; + } + } + else if (cur != NULL) + nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol); + else + nmatched = 0; + if (nmatched == 0) + { + shl->lnum = 0; /* no match found */ + break; + } + if (shl->rm.startpos[0].lnum > 0 + || shl->rm.startpos[0].col >= mincol + || nmatched > 1 + || shl->rm.endpos[0].col > mincol) + { + shl->lnum += shl->rm.startpos[0].lnum; + break; /* useful match found */ + } + } + + // Restore called_emsg for assert_fails(). + called_emsg = save_called_emsg; + } + + /* + * 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) + { + matchitem_T *cur; /* points to the match list */ + match_T *shl; /* points to search_hl or a match */ + int shl_flag; /* flag to indicate whether search_hl + has been processed or not */ + int pos_inprogress; /* marks that position match search is + in progress */ + int n; + + /* + * When using a multi-line pattern, start searching at the top + * of the window or just after a closed fold. + * Do this both for search_hl and the match list. + */ + cur = wp->w_match_head; + shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window + while (cur != NULL || shl_flag == FALSE) + { + if (shl_flag == FALSE) + { + shl = search_hl; + shl_flag = TRUE; + } + else + shl = &cur->hl; + if (shl->rm.regprog != NULL + && shl->lnum == 0 + && re_multiline(shl->rm.regprog)) + { + if (shl->first_lnum == 0) + { + # ifdef FEAT_FOLDING + for (shl->first_lnum = lnum; + shl->first_lnum > wp->w_topline; --shl->first_lnum) + if (hasFoldingWin(wp, shl->first_lnum - 1, + NULL, NULL, TRUE, NULL)) + break; + # else + shl->first_lnum = wp->w_topline; + # endif + } + if (cur != NULL) + cur->pos.cur = 0; + pos_inprogress = TRUE; + n = 0; + while (shl->first_lnum < lnum && (shl->rm.regprog != NULL + || (cur != NULL && pos_inprogress))) + { + next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n, + shl == search_hl ? NULL : cur); + pos_inprogress = cur == NULL || cur->pos.cur == 0 + ? FALSE : TRUE; + if (shl->lnum != 0) + { + shl->first_lnum = shl->lnum + + shl->rm.endpos[0].lnum + - shl->rm.startpos[0].lnum; + n = shl->rm.endpos[0].col; + } + else + { + ++shl->first_lnum; + n = 0; + } + } + } + if (shl != search_hl && cur != NULL) + cur = cur->next; + } + } + + /* + * 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. + */ + int + prepare_search_hl_line( + win_T *wp, + linenr_T lnum, + colnr_T mincol, + char_u **line, + match_T *search_hl, + int *search_attr) + { + matchitem_T *cur; // points to the match list + match_T *shl; // points to search_hl or a match + int shl_flag; // flag to indicate whether search_hl + // has been processed or not + int area_highlighting = FALSE; + + /* + * Handle highlighting the last used search pattern and matches. + * Do this for both search_hl and the match list. + * Do not use search_hl in a popup window. + */ + cur = wp->w_match_head; + shl_flag = WIN_IS_POPUP(wp); + while (cur != NULL || shl_flag == FALSE) + { + if (shl_flag == FALSE) + { + shl = search_hl; + shl_flag = TRUE; + } + else + shl = &cur->hl; + shl->startcol = MAXCOL; + shl->endcol = MAXCOL; + shl->attr_cur = 0; + shl->is_addpos = FALSE; + 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 (has_mbyte && (*line)[shl->endcol] != NUL) + shl->endcol += (*mb_ptr2len)((*line) + shl->endcol); + else + ++shl->endcol; + } + if ((long)shl->startcol < mincol) /* match at leftcol */ + { + shl->attr_cur = shl->attr; + *search_attr = shl->attr; + } + 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 udpated 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 did_line_attr, + int lcs_eol_one) + { + matchitem_T *cur; // points to the match list + match_T *shl; // points to search_hl or a match + int shl_flag; // flag to indicate whether search_hl + // has been processed or not + int pos_inprogress; // marks that position match search is in + // progress + int search_attr = 0; + + + // Do this for 'search_hl' and the match list (ordered by priority). + cur = wp->w_match_head; + shl_flag = WIN_IS_POPUP(wp); + while (cur != NULL || shl_flag == FALSE) + { + if (shl_flag == FALSE + && ((cur != NULL + && cur->priority > SEARCH_HL_PRIORITY) + || cur == NULL)) + { + shl = search_hl; + shl_flag = TRUE; + } + else + shl = &cur->hl; + if (cur != NULL) + cur->pos.cur = 0; + pos_inprogress = TRUE; + while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress)) + { + if (shl->startcol != MAXCOL + && col >= shl->startcol + && col < shl->endcol) + { + int next_col = col + MB_PTR2LEN(*line + col); + + if (shl->endcol < next_col) + shl->endcol = next_col; + shl->attr_cur = shl->attr; + # ifdef FEAT_CONCEAL + // Match with the "Conceal" group results in hiding + // the match. + if (cur != NULL + && shl != search_hl + && syn_name2id((char_u *)"Conceal") == cur->hlg_id) + { + *has_match_conc = col == shl->startcol ? 2 : 1; + *match_conc = cur->conceal_char; + } + else + *has_match_conc = *match_conc = 0; + # endif + } + 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 */ + if (has_mbyte) + shl->endcol += (*mb_ptr2len)(*line + shl->endcol); + else + ++shl->endcol; + } + + /* Loop to check if the match starts at the + * current position */ + continue; + } + } + break; + } + if (shl != search_hl && cur != NULL) + cur = cur->next; + } + + // Use attributes from match with highest priority among 'search_hl' and + // the match list. + cur = wp->w_match_head; + shl_flag = WIN_IS_POPUP(wp); + while (cur != NULL || shl_flag == FALSE) + { + if (shl_flag == FALSE + && ((cur != NULL + && cur->priority > SEARCH_HL_PRIORITY) + || cur == NULL)) + { + shl = search_hl; + shl_flag = TRUE; + } + else + shl = &cur->hl; + if (shl->attr_cur != 0) + search_attr = shl->attr_cur; + if (shl != search_hl && cur != NULL) + cur = cur->next; + } + /* Only highlight one character after the last column. */ + if (*(*line + col) == NUL && (did_line_attr >= 1 + || (wp->w_p_list && lcs_eol_one == -1))) + search_attr = 0; + return search_attr; + } + + int + get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol) + { + long prevcol = curcol; + int prevcol_hl_flag = FALSE; + 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 == (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; + } + } + return prevcol_hl_flag; + } + + /* + * 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; // points to the match list + match_T *shl; // points to search_hl or a match + int shl_flag; // flag to indicate whether search_hl + // has been processed or not + + cur = wp->w_match_head; + shl_flag = WIN_IS_POPUP(wp); + while (cur != NULL || shl_flag == FALSE) + { + if (shl_flag == FALSE + && ((cur != NULL + && cur->priority > SEARCH_HL_PRIORITY) + || cur == NULL)) + { + shl = search_hl; + shl_flag = TRUE; + } + 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; + } + } + + #endif // FEAT_SEARCH_EXTRA #if defined(FEAT_EVAL) || defined(PROTO) # ifdef FEAT_SEARCH_EXTRA *** ../vim-8.1.1742/src/proto/highlight.pro 2019-07-24 16:00:34.922180377 +0200 --- src/proto/highlight.pro 2019-07-24 17:56:47.031836041 +0200 *************** *** 44,49 **** --- 44,55 ---- char_u *get_highlight_name_ext(expand_T *xp, int idx, int skip_cleared); void free_highlight_fonts(void); void clear_matches(win_T *wp); + void init_search_hl(win_T *wp, match_T *search_hl); + void prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum); + int prepare_search_hl_line(win_T *wp, linenr_T lnum, colnr_T mincol, char_u **line, match_T *search_hl, int *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 did_line_attr, int lcs_eol_one); + int get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol); + void get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr); void f_clearmatches(typval_T *argvars, typval_T *rettv); void f_getmatches(typval_T *argvars, typval_T *rettv); void f_setmatches(typval_T *argvars, typval_T *rettv); *** ../vim-8.1.1742/src/version.c 2019-07-24 16:00:34.926180368 +0200 --- src/version.c 2019-07-24 17:47:04.217884908 +0200 *************** *** 779,780 **** --- 779,782 ---- { /* Add new patch number below this line */ + /**/ + 1743, /**/ -- BODY: I'm not dead! CART DRIVER: 'Ere. He says he's not dead. LARGE MAN: Yes he is. BODY: I'm not! "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///