comparison src/highlight.c @ 21054:b1fac55cf8a3 v8.2.1078

patch 8.2.1078: highlight and match functionality together in one file Commit: https://github.com/vim/vim/commit/06cf97e714fd8bf9b35ff5f8a6f2302c79acdd03 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jun 28 13:17:26 2020 +0200 patch 8.2.1078: highlight and match functionality together in one file Problem: Highlight and match functionality together in one file. Solution: Move match functionality to a separate file. (Yegappan Lakshmanan, closes #6352)
author Bram Moolenaar <Bram@vim.org>
date Sun, 28 Jun 2020 13:30:04 +0200
parents ca2e18364888
children a9ff3e0d6d54
comparison
equal deleted inserted replaced
21053:c1f0a0815090 21054:b1fac55cf8a3
7 * See README.txt for an overview of the Vim source code. 7 * See README.txt for an overview of the Vim source code.
8 */ 8 */
9 9
10 /* 10 /*
11 * Highlighting stuff. 11 * Highlighting stuff.
12 * Includes highlighting matches.
13 */ 12 */
14 13
15 #include "vim.h" 14 #include "vim.h"
16 15
17 #define SG_TERM 1 // term has been set 16 #define SG_TERM 1 // term has been set
3692 gui_mch_free_font(gui.ital_font); 3691 gui_mch_free_font(gui.ital_font);
3693 gui_mch_free_font(gui.boldital_font); 3692 gui_mch_free_font(gui.boldital_font);
3694 # endif 3693 # endif
3695 } 3694 }
3696 #endif 3695 #endif
3697
3698
3699 #if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3700
3701 # define SEARCH_HL_PRIORITY 0
3702
3703 /*
3704 * Add match to the match list of window 'wp'. The pattern 'pat' will be
3705 * highlighted with the group 'grp' with priority 'prio'.
3706 * Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
3707 * If no particular ID is desired, -1 must be specified for 'id'.
3708 * Return ID of added match, -1 on failure.
3709 */
3710 static int
3711 match_add(
3712 win_T *wp,
3713 char_u *grp,
3714 char_u *pat,
3715 int prio,
3716 int id,
3717 list_T *pos_list,
3718 char_u *conceal_char UNUSED) // pointer to conceal replacement char
3719 {
3720 matchitem_T *cur;
3721 matchitem_T *prev;
3722 matchitem_T *m;
3723 int hlg_id;
3724 regprog_T *regprog = NULL;
3725 int rtype = SOME_VALID;
3726
3727 if (*grp == NUL || (pat != NULL && *pat == NUL))
3728 return -1;
3729 if (id < -1 || id == 0)
3730 {
3731 semsg(_("E799: Invalid ID: %d (must be greater than or equal to 1)"),
3732 id);
3733 return -1;
3734 }
3735 if (id != -1)
3736 {
3737 cur = wp->w_match_head;
3738 while (cur != NULL)
3739 {
3740 if (cur->id == id)
3741 {
3742 semsg(_("E801: ID already taken: %d"), id);
3743 return -1;
3744 }
3745 cur = cur->next;
3746 }
3747 }
3748 if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0)
3749 {
3750 semsg(_(e_nogroup), grp);
3751 return -1;
3752 }
3753 if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL)
3754 {
3755 semsg(_(e_invarg2), pat);
3756 return -1;
3757 }
3758
3759 // Find available match ID.
3760 while (id == -1)
3761 {
3762 cur = wp->w_match_head;
3763 while (cur != NULL && cur->id != wp->w_next_match_id)
3764 cur = cur->next;
3765 if (cur == NULL)
3766 id = wp->w_next_match_id;
3767 wp->w_next_match_id++;
3768 }
3769
3770 // Build new match.
3771 m = ALLOC_CLEAR_ONE(matchitem_T);
3772 m->id = id;
3773 m->priority = prio;
3774 m->pattern = pat == NULL ? NULL : vim_strsave(pat);
3775 m->hlg_id = hlg_id;
3776 m->match.regprog = regprog;
3777 m->match.rmm_ic = FALSE;
3778 m->match.rmm_maxcol = 0;
3779 # if defined(FEAT_CONCEAL)
3780 m->conceal_char = 0;
3781 if (conceal_char != NULL)
3782 m->conceal_char = (*mb_ptr2char)(conceal_char);
3783 # endif
3784
3785 // Set up position matches
3786 if (pos_list != NULL)
3787 {
3788 linenr_T toplnum = 0;
3789 linenr_T botlnum = 0;
3790 listitem_T *li;
3791 int i;
3792
3793 CHECK_LIST_MATERIALIZE(pos_list);
3794 for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH;
3795 i++, li = li->li_next)
3796 {
3797 linenr_T lnum = 0;
3798 colnr_T col = 0;
3799 int len = 1;
3800 list_T *subl;
3801 listitem_T *subli;
3802 int error = FALSE;
3803
3804 if (li->li_tv.v_type == VAR_LIST)
3805 {
3806 subl = li->li_tv.vval.v_list;
3807 if (subl == NULL)
3808 goto fail;
3809 subli = subl->lv_first;
3810 if (subli == NULL)
3811 goto fail;
3812 lnum = tv_get_number_chk(&subli->li_tv, &error);
3813 if (error == TRUE)
3814 goto fail;
3815 if (lnum == 0)
3816 {
3817 --i;
3818 continue;
3819 }
3820 m->pos.pos[i].lnum = lnum;
3821 subli = subli->li_next;
3822 if (subli != NULL)
3823 {
3824 col = tv_get_number_chk(&subli->li_tv, &error);
3825 if (error == TRUE)
3826 goto fail;
3827 subli = subli->li_next;
3828 if (subli != NULL)
3829 {
3830 len = tv_get_number_chk(&subli->li_tv, &error);
3831 if (error == TRUE)
3832 goto fail;
3833 }
3834 }
3835 m->pos.pos[i].col = col;
3836 m->pos.pos[i].len = len;
3837 }
3838 else if (li->li_tv.v_type == VAR_NUMBER)
3839 {
3840 if (li->li_tv.vval.v_number == 0)
3841 {
3842 --i;
3843 continue;
3844 }
3845 m->pos.pos[i].lnum = li->li_tv.vval.v_number;
3846 m->pos.pos[i].col = 0;
3847 m->pos.pos[i].len = 0;
3848 }
3849 else
3850 {
3851 emsg(_("E290: List or number required"));
3852 goto fail;
3853 }
3854 if (toplnum == 0 || lnum < toplnum)
3855 toplnum = lnum;
3856 if (botlnum == 0 || lnum >= botlnum)
3857 botlnum = lnum + 1;
3858 }
3859
3860 // Calculate top and bottom lines for redrawing area
3861 if (toplnum != 0)
3862 {
3863 if (wp->w_buffer->b_mod_set)
3864 {
3865 if (wp->w_buffer->b_mod_top > toplnum)
3866 wp->w_buffer->b_mod_top = toplnum;
3867 if (wp->w_buffer->b_mod_bot < botlnum)
3868 wp->w_buffer->b_mod_bot = botlnum;
3869 }
3870 else
3871 {
3872 wp->w_buffer->b_mod_set = TRUE;
3873 wp->w_buffer->b_mod_top = toplnum;
3874 wp->w_buffer->b_mod_bot = botlnum;
3875 wp->w_buffer->b_mod_xlines = 0;
3876 }
3877 m->pos.toplnum = toplnum;
3878 m->pos.botlnum = botlnum;
3879 rtype = VALID;
3880 }
3881 }
3882
3883 // Insert new match. The match list is in ascending order with regard to
3884 // the match priorities.
3885 cur = wp->w_match_head;
3886 prev = cur;
3887 while (cur != NULL && prio >= cur->priority)
3888 {
3889 prev = cur;
3890 cur = cur->next;
3891 }
3892 if (cur == prev)
3893 wp->w_match_head = m;
3894 else
3895 prev->next = m;
3896 m->next = cur;
3897
3898 redraw_win_later(wp, rtype);
3899 return id;
3900
3901 fail:
3902 vim_free(m);
3903 return -1;
3904 }
3905
3906 /*
3907 * Delete match with ID 'id' in the match list of window 'wp'.
3908 * Print error messages if 'perr' is TRUE.
3909 */
3910 static int
3911 match_delete(win_T *wp, int id, int perr)
3912 {
3913 matchitem_T *cur = wp->w_match_head;
3914 matchitem_T *prev = cur;
3915 int rtype = SOME_VALID;
3916
3917 if (id < 1)
3918 {
3919 if (perr == TRUE)
3920 semsg(_("E802: Invalid ID: %d (must be greater than or equal to 1)"),
3921 id);
3922 return -1;
3923 }
3924 while (cur != NULL && cur->id != id)
3925 {
3926 prev = cur;
3927 cur = cur->next;
3928 }
3929 if (cur == NULL)
3930 {
3931 if (perr == TRUE)
3932 semsg(_("E803: ID not found: %d"), id);
3933 return -1;
3934 }
3935 if (cur == prev)
3936 wp->w_match_head = cur->next;
3937 else
3938 prev->next = cur->next;
3939 vim_regfree(cur->match.regprog);
3940 vim_free(cur->pattern);
3941 if (cur->pos.toplnum != 0)
3942 {
3943 if (wp->w_buffer->b_mod_set)
3944 {
3945 if (wp->w_buffer->b_mod_top > cur->pos.toplnum)
3946 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3947 if (wp->w_buffer->b_mod_bot < cur->pos.botlnum)
3948 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3949 }
3950 else
3951 {
3952 wp->w_buffer->b_mod_set = TRUE;
3953 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3954 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3955 wp->w_buffer->b_mod_xlines = 0;
3956 }
3957 rtype = VALID;
3958 }
3959 vim_free(cur);
3960 redraw_win_later(wp, rtype);
3961 return 0;
3962 }
3963
3964 /*
3965 * Delete all matches in the match list of window 'wp'.
3966 */
3967 void
3968 clear_matches(win_T *wp)
3969 {
3970 matchitem_T *m;
3971
3972 while (wp->w_match_head != NULL)
3973 {
3974 m = wp->w_match_head->next;
3975 vim_regfree(wp->w_match_head->match.regprog);
3976 vim_free(wp->w_match_head->pattern);
3977 vim_free(wp->w_match_head);
3978 wp->w_match_head = m;
3979 }
3980 redraw_win_later(wp, SOME_VALID);
3981 }
3982
3983 /*
3984 * Get match from ID 'id' in window 'wp'.
3985 * Return NULL if match not found.
3986 */
3987 static matchitem_T *
3988 get_match(win_T *wp, int id)
3989 {
3990 matchitem_T *cur = wp->w_match_head;
3991
3992 while (cur != NULL && cur->id != id)
3993 cur = cur->next;
3994 return cur;
3995 }
3996
3997 /*
3998 * Init for calling prepare_search_hl().
3999 */
4000 void
4001 init_search_hl(win_T *wp, match_T *search_hl)
4002 {
4003 matchitem_T *cur;
4004
4005 // Setup for match and 'hlsearch' highlighting. Disable any previous
4006 // match
4007 cur = wp->w_match_head;
4008 while (cur != NULL)
4009 {
4010 cur->hl.rm = cur->match;
4011 if (cur->hlg_id == 0)
4012 cur->hl.attr = 0;
4013 else
4014 cur->hl.attr = syn_id2attr(cur->hlg_id);
4015 cur->hl.buf = wp->w_buffer;
4016 cur->hl.lnum = 0;
4017 cur->hl.first_lnum = 0;
4018 # ifdef FEAT_RELTIME
4019 // Set the time limit to 'redrawtime'.
4020 profile_setlimit(p_rdt, &(cur->hl.tm));
4021 # endif
4022 cur = cur->next;
4023 }
4024 search_hl->buf = wp->w_buffer;
4025 search_hl->lnum = 0;
4026 search_hl->first_lnum = 0;
4027 // time limit is set at the toplevel, for all windows
4028 }
4029
4030 /*
4031 * If there is a match fill "shl" and return one.
4032 * Return zero otherwise.
4033 */
4034 static int
4035 next_search_hl_pos(
4036 match_T *shl, // points to a match
4037 linenr_T lnum,
4038 posmatch_T *posmatch, // match positions
4039 colnr_T mincol) // minimal column for a match
4040 {
4041 int i;
4042 int found = -1;
4043
4044 for (i = posmatch->cur; i < MAXPOSMATCH; i++)
4045 {
4046 llpos_T *pos = &posmatch->pos[i];
4047
4048 if (pos->lnum == 0)
4049 break;
4050 if (pos->len == 0 && pos->col < mincol)
4051 continue;
4052 if (pos->lnum == lnum)
4053 {
4054 if (found >= 0)
4055 {
4056 // if this match comes before the one at "found" then swap
4057 // them
4058 if (pos->col < posmatch->pos[found].col)
4059 {
4060 llpos_T tmp = *pos;
4061
4062 *pos = posmatch->pos[found];
4063 posmatch->pos[found] = tmp;
4064 }
4065 }
4066 else
4067 found = i;
4068 }
4069 }
4070 posmatch->cur = 0;
4071 if (found >= 0)
4072 {
4073 colnr_T start = posmatch->pos[found].col == 0
4074 ? 0 : posmatch->pos[found].col - 1;
4075 colnr_T end = posmatch->pos[found].col == 0
4076 ? MAXCOL : start + posmatch->pos[found].len;
4077
4078 shl->lnum = lnum;
4079 shl->rm.startpos[0].lnum = 0;
4080 shl->rm.startpos[0].col = start;
4081 shl->rm.endpos[0].lnum = 0;
4082 shl->rm.endpos[0].col = end;
4083 shl->is_addpos = TRUE;
4084 posmatch->cur = found + 1;
4085 return 1;
4086 }
4087 return 0;
4088 }
4089
4090 /*
4091 * Search for a next 'hlsearch' or match.
4092 * Uses shl->buf.
4093 * Sets shl->lnum and shl->rm contents.
4094 * Note: Assumes a previous match is always before "lnum", unless
4095 * shl->lnum is zero.
4096 * Careful: Any pointers for buffer lines will become invalid.
4097 */
4098 static void
4099 next_search_hl(
4100 win_T *win,
4101 match_T *search_hl,
4102 match_T *shl, // points to search_hl or a match
4103 linenr_T lnum,
4104 colnr_T mincol, // minimal column for a match
4105 matchitem_T *cur) // to retrieve match positions if any
4106 {
4107 linenr_T l;
4108 colnr_T matchcol;
4109 long nmatched;
4110 int called_emsg_before = called_emsg;
4111
4112 // for :{range}s/pat only highlight inside the range
4113 if (lnum < search_first_line || lnum > search_last_line)
4114 {
4115 shl->lnum = 0;
4116 return;
4117 }
4118
4119 if (shl->lnum != 0)
4120 {
4121 // Check for three situations:
4122 // 1. If the "lnum" is below a previous match, start a new search.
4123 // 2. If the previous match includes "mincol", use it.
4124 // 3. Continue after the previous match.
4125 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
4126 if (lnum > l)
4127 shl->lnum = 0;
4128 else if (lnum < l || shl->rm.endpos[0].col > mincol)
4129 return;
4130 }
4131
4132 /*
4133 * Repeat searching for a match until one is found that includes "mincol"
4134 * or none is found in this line.
4135 */
4136 for (;;)
4137 {
4138 # ifdef FEAT_RELTIME
4139 // Stop searching after passing the time limit.
4140 if (profile_passed_limit(&(shl->tm)))
4141 {
4142 shl->lnum = 0; // no match found in time
4143 break;
4144 }
4145 # endif
4146 // Three situations:
4147 // 1. No useful previous match: search from start of line.
4148 // 2. Not Vi compatible or empty match: continue at next character.
4149 // Break the loop if this is beyond the end of the line.
4150 // 3. Vi compatible searching: continue at end of previous match.
4151 if (shl->lnum == 0)
4152 matchcol = 0;
4153 else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
4154 || (shl->rm.endpos[0].lnum == 0
4155 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
4156 {
4157 char_u *ml;
4158
4159 matchcol = shl->rm.startpos[0].col;
4160 ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
4161 if (*ml == NUL)
4162 {
4163 ++matchcol;
4164 shl->lnum = 0;
4165 break;
4166 }
4167 if (has_mbyte)
4168 matchcol += mb_ptr2len(ml);
4169 else
4170 ++matchcol;
4171 }
4172 else
4173 matchcol = shl->rm.endpos[0].col;
4174
4175 shl->lnum = lnum;
4176 if (shl->rm.regprog != NULL)
4177 {
4178 // Remember whether shl->rm is using a copy of the regprog in
4179 // cur->match.
4180 int regprog_is_copy = (shl != search_hl && cur != NULL
4181 && shl == &cur->hl
4182 && cur->match.regprog == cur->hl.rm.regprog);
4183 int timed_out = FALSE;
4184
4185 nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
4186 matchcol,
4187 #ifdef FEAT_RELTIME
4188 &(shl->tm), &timed_out
4189 #else
4190 NULL, NULL
4191 #endif
4192 );
4193 // Copy the regprog, in case it got freed and recompiled.
4194 if (regprog_is_copy)
4195 cur->match.regprog = cur->hl.rm.regprog;
4196
4197 if (called_emsg > called_emsg_before || got_int || timed_out)
4198 {
4199 // Error while handling regexp: stop using this regexp.
4200 if (shl == search_hl)
4201 {
4202 // don't free regprog in the match list, it's a copy
4203 vim_regfree(shl->rm.regprog);
4204 set_no_hlsearch(TRUE);
4205 }
4206 shl->rm.regprog = NULL;
4207 shl->lnum = 0;
4208 got_int = FALSE; // avoid the "Type :quit to exit Vim" message
4209 break;
4210 }
4211 }
4212 else if (cur != NULL)
4213 nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
4214 else
4215 nmatched = 0;
4216 if (nmatched == 0)
4217 {
4218 shl->lnum = 0; // no match found
4219 break;
4220 }
4221 if (shl->rm.startpos[0].lnum > 0
4222 || shl->rm.startpos[0].col >= mincol
4223 || nmatched > 1
4224 || shl->rm.endpos[0].col > mincol)
4225 {
4226 shl->lnum += shl->rm.startpos[0].lnum;
4227 break; // useful match found
4228 }
4229 }
4230 }
4231
4232 /*
4233 * Advance to the match in window "wp" line "lnum" or past it.
4234 */
4235 void
4236 prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
4237 {
4238 matchitem_T *cur; // points to the match list
4239 match_T *shl; // points to search_hl or a match
4240 int shl_flag; // flag to indicate whether search_hl
4241 // has been processed or not
4242 int pos_inprogress; // marks that position match search is
4243 // in progress
4244 int n;
4245
4246 // When using a multi-line pattern, start searching at the top
4247 // of the window or just after a closed fold.
4248 // Do this both for search_hl and the match list.
4249 cur = wp->w_match_head;
4250 shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window
4251 while (cur != NULL || shl_flag == FALSE)
4252 {
4253 if (shl_flag == FALSE)
4254 {
4255 shl = search_hl;
4256 shl_flag = TRUE;
4257 }
4258 else
4259 shl = &cur->hl;
4260 if (shl->rm.regprog != NULL
4261 && shl->lnum == 0
4262 && re_multiline(shl->rm.regprog))
4263 {
4264 if (shl->first_lnum == 0)
4265 {
4266 # ifdef FEAT_FOLDING
4267 for (shl->first_lnum = lnum;
4268 shl->first_lnum > wp->w_topline; --shl->first_lnum)
4269 if (hasFoldingWin(wp, shl->first_lnum - 1,
4270 NULL, NULL, TRUE, NULL))
4271 break;
4272 # else
4273 shl->first_lnum = wp->w_topline;
4274 # endif
4275 }
4276 if (cur != NULL)
4277 cur->pos.cur = 0;
4278 pos_inprogress = TRUE;
4279 n = 0;
4280 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
4281 || (cur != NULL && pos_inprogress)))
4282 {
4283 next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
4284 shl == search_hl ? NULL : cur);
4285 pos_inprogress = cur == NULL || cur->pos.cur == 0
4286 ? FALSE : TRUE;
4287 if (shl->lnum != 0)
4288 {
4289 shl->first_lnum = shl->lnum
4290 + shl->rm.endpos[0].lnum
4291 - shl->rm.startpos[0].lnum;
4292 n = shl->rm.endpos[0].col;
4293 }
4294 else
4295 {
4296 ++shl->first_lnum;
4297 n = 0;
4298 }
4299 }
4300 }
4301 if (shl != search_hl && cur != NULL)
4302 cur = cur->next;
4303 }
4304 }
4305
4306 /*
4307 * Prepare for 'hlsearch' and match highlighting in one window line.
4308 * Return TRUE if there is such highlighting and set "search_attr" to the
4309 * current highlight attribute.
4310 */
4311 int
4312 prepare_search_hl_line(
4313 win_T *wp,
4314 linenr_T lnum,
4315 colnr_T mincol,
4316 char_u **line,
4317 match_T *search_hl,
4318 int *search_attr)
4319 {
4320 matchitem_T *cur; // points to the match list
4321 match_T *shl; // points to search_hl or a match
4322 int shl_flag; // flag to indicate whether search_hl
4323 // has been processed or not
4324 int area_highlighting = FALSE;
4325
4326 /*
4327 * Handle highlighting the last used search pattern and matches.
4328 * Do this for both search_hl and the match list.
4329 * Do not use search_hl in a popup window.
4330 */
4331 cur = wp->w_match_head;
4332 shl_flag = WIN_IS_POPUP(wp);
4333 while (cur != NULL || shl_flag == FALSE)
4334 {
4335 if (shl_flag == FALSE)
4336 {
4337 shl = search_hl;
4338 shl_flag = TRUE;
4339 }
4340 else
4341 shl = &cur->hl;
4342 shl->startcol = MAXCOL;
4343 shl->endcol = MAXCOL;
4344 shl->attr_cur = 0;
4345 shl->is_addpos = FALSE;
4346 if (cur != NULL)
4347 cur->pos.cur = 0;
4348 next_search_hl(wp, search_hl, shl, lnum, mincol,
4349 shl == search_hl ? NULL : cur);
4350
4351 // Need to get the line again, a multi-line regexp may have made it
4352 // invalid.
4353 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4354
4355 if (shl->lnum != 0 && shl->lnum <= lnum)
4356 {
4357 if (shl->lnum == lnum)
4358 shl->startcol = shl->rm.startpos[0].col;
4359 else
4360 shl->startcol = 0;
4361 if (lnum == shl->lnum + shl->rm.endpos[0].lnum
4362 - shl->rm.startpos[0].lnum)
4363 shl->endcol = shl->rm.endpos[0].col;
4364 else
4365 shl->endcol = MAXCOL;
4366 // Highlight one character for an empty match.
4367 if (shl->startcol == shl->endcol)
4368 {
4369 if (has_mbyte && (*line)[shl->endcol] != NUL)
4370 shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
4371 else
4372 ++shl->endcol;
4373 }
4374 if ((long)shl->startcol < mincol) // match at leftcol
4375 {
4376 shl->attr_cur = shl->attr;
4377 *search_attr = shl->attr;
4378 }
4379 area_highlighting = TRUE;
4380 }
4381 if (shl != search_hl && cur != NULL)
4382 cur = cur->next;
4383 }
4384 return area_highlighting;
4385 }
4386
4387 /*
4388 * For a position in a line: Check for start/end of 'hlsearch' and other
4389 * matches.
4390 * After end, check for start/end of next match.
4391 * When another match, have to check for start again.
4392 * Watch out for matching an empty string!
4393 * Return the updated search_attr.
4394 */
4395 int
4396 update_search_hl(
4397 win_T *wp,
4398 linenr_T lnum,
4399 colnr_T col,
4400 char_u **line,
4401 match_T *search_hl,
4402 int *has_match_conc UNUSED,
4403 int *match_conc UNUSED,
4404 int did_line_attr,
4405 int lcs_eol_one)
4406 {
4407 matchitem_T *cur; // points to the match list
4408 match_T *shl; // points to search_hl or a match
4409 int shl_flag; // flag to indicate whether search_hl
4410 // has been processed or not
4411 int pos_inprogress; // marks that position match search is in
4412 // progress
4413 int search_attr = 0;
4414
4415
4416 // Do this for 'search_hl' and the match list (ordered by priority).
4417 cur = wp->w_match_head;
4418 shl_flag = WIN_IS_POPUP(wp);
4419 while (cur != NULL || shl_flag == FALSE)
4420 {
4421 if (shl_flag == FALSE
4422 && (cur == NULL
4423 || cur->priority > SEARCH_HL_PRIORITY))
4424 {
4425 shl = search_hl;
4426 shl_flag = TRUE;
4427 }
4428 else
4429 shl = &cur->hl;
4430 if (cur != NULL)
4431 cur->pos.cur = 0;
4432 pos_inprogress = TRUE;
4433 while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
4434 {
4435 if (shl->startcol != MAXCOL
4436 && col >= shl->startcol
4437 && col < shl->endcol)
4438 {
4439 int next_col = col + mb_ptr2len(*line + col);
4440
4441 if (shl->endcol < next_col)
4442 shl->endcol = next_col;
4443 shl->attr_cur = shl->attr;
4444 # ifdef FEAT_CONCEAL
4445 // Match with the "Conceal" group results in hiding
4446 // the match.
4447 if (cur != NULL
4448 && shl != search_hl
4449 && syn_name2id((char_u *)"Conceal") == cur->hlg_id)
4450 {
4451 *has_match_conc = col == shl->startcol ? 2 : 1;
4452 *match_conc = cur->conceal_char;
4453 }
4454 else
4455 *has_match_conc = 0;
4456 # endif
4457 }
4458 else if (col == shl->endcol)
4459 {
4460 shl->attr_cur = 0;
4461 next_search_hl(wp, search_hl, shl, lnum, col,
4462 shl == search_hl ? NULL : cur);
4463 pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
4464
4465 // Need to get the line again, a multi-line regexp may have
4466 // made it invalid.
4467 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4468
4469 if (shl->lnum == lnum)
4470 {
4471 shl->startcol = shl->rm.startpos[0].col;
4472 if (shl->rm.endpos[0].lnum == 0)
4473 shl->endcol = shl->rm.endpos[0].col;
4474 else
4475 shl->endcol = MAXCOL;
4476
4477 if (shl->startcol == shl->endcol)
4478 {
4479 // highlight empty match, try again after
4480 // it
4481 if (has_mbyte)
4482 shl->endcol += (*mb_ptr2len)(*line + shl->endcol);
4483 else
4484 ++shl->endcol;
4485 }
4486
4487 // Loop to check if the match starts at the
4488 // current position
4489 continue;
4490 }
4491 }
4492 break;
4493 }
4494 if (shl != search_hl && cur != NULL)
4495 cur = cur->next;
4496 }
4497
4498 // Use attributes from match with highest priority among 'search_hl' and
4499 // the match list.
4500 cur = wp->w_match_head;
4501 shl_flag = WIN_IS_POPUP(wp);
4502 while (cur != NULL || shl_flag == FALSE)
4503 {
4504 if (shl_flag == FALSE
4505 && (cur == NULL ||
4506 cur->priority > SEARCH_HL_PRIORITY))
4507 {
4508 shl = search_hl;
4509 shl_flag = TRUE;
4510 }
4511 else
4512 shl = &cur->hl;
4513 if (shl->attr_cur != 0)
4514 search_attr = shl->attr_cur;
4515 if (shl != search_hl && cur != NULL)
4516 cur = cur->next;
4517 }
4518 // Only highlight one character after the last column.
4519 if (*(*line + col) == NUL && (did_line_attr >= 1
4520 || (wp->w_p_list && lcs_eol_one == -1)))
4521 search_attr = 0;
4522 return search_attr;
4523 }
4524
4525 int
4526 get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
4527 {
4528 long prevcol = curcol;
4529 int prevcol_hl_flag = FALSE;
4530 matchitem_T *cur; // points to the match list
4531
4532 // we're not really at that column when skipping some text
4533 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
4534 ++prevcol;
4535
4536 if (!search_hl->is_addpos && prevcol == (long)search_hl->startcol)
4537 prevcol_hl_flag = TRUE;
4538 else
4539 {
4540 cur = wp->w_match_head;
4541 while (cur != NULL)
4542 {
4543 if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol)
4544 {
4545 prevcol_hl_flag = TRUE;
4546 break;
4547 }
4548 cur = cur->next;
4549 }
4550 }
4551 return prevcol_hl_flag;
4552 }
4553
4554 /*
4555 * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
4556 * or match highlighting.
4557 */
4558 void
4559 get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
4560 {
4561 matchitem_T *cur; // points to the match list
4562 match_T *shl; // points to search_hl or a match
4563 int shl_flag; // flag to indicate whether search_hl
4564 // has been processed or not
4565
4566 cur = wp->w_match_head;
4567 shl_flag = WIN_IS_POPUP(wp);
4568 while (cur != NULL || shl_flag == FALSE)
4569 {
4570 if (shl_flag == FALSE
4571 && ((cur != NULL
4572 && cur->priority > SEARCH_HL_PRIORITY)
4573 || cur == NULL))
4574 {
4575 shl = search_hl;
4576 shl_flag = TRUE;
4577 }
4578 else
4579 shl = &cur->hl;
4580 if (col - 1 == (long)shl->startcol
4581 && (shl == search_hl || !shl->is_addpos))
4582 *char_attr = shl->attr;
4583 if (shl != search_hl && cur != NULL)
4584 cur = cur->next;
4585 }
4586 }
4587
4588 #endif // FEAT_SEARCH_EXTRA
4589
4590 #if defined(FEAT_EVAL) || defined(PROTO)
4591 # ifdef FEAT_SEARCH_EXTRA
4592 static int
4593 matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
4594 {
4595 dictitem_T *di;
4596
4597 if (tv->v_type != VAR_DICT)
4598 {
4599 emsg(_(e_dictreq));
4600 return FAIL;
4601 }
4602
4603 if (dict_find(tv->vval.v_dict, (char_u *)"conceal", -1) != NULL)
4604 *conceal_char = dict_get_string(tv->vval.v_dict,
4605 (char_u *)"conceal", FALSE);
4606
4607 if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL)
4608 {
4609 *win = find_win_by_nr_or_id(&di->di_tv);
4610 if (*win == NULL)
4611 {
4612 emsg(_(e_invalwindow));
4613 return FAIL;
4614 }
4615 }
4616
4617 return OK;
4618 }
4619 #endif
4620
4621 /*
4622 * "clearmatches()" function
4623 */
4624 void
4625 f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4626 {
4627 #ifdef FEAT_SEARCH_EXTRA
4628 win_T *win = get_optional_window(argvars, 0);
4629
4630 if (win != NULL)
4631 clear_matches(win);
4632 #endif
4633 }
4634
4635 /*
4636 * "getmatches()" function
4637 */
4638 void
4639 f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4640 {
4641 # ifdef FEAT_SEARCH_EXTRA
4642 dict_T *dict;
4643 matchitem_T *cur;
4644 int i;
4645 win_T *win = get_optional_window(argvars, 0);
4646
4647 if (rettv_list_alloc(rettv) == FAIL || win == NULL)
4648 return;
4649
4650 cur = win->w_match_head;
4651 while (cur != NULL)
4652 {
4653 dict = dict_alloc();
4654 if (dict == NULL)
4655 return;
4656 if (cur->match.regprog == NULL)
4657 {
4658 // match added with matchaddpos()
4659 for (i = 0; i < MAXPOSMATCH; ++i)
4660 {
4661 llpos_T *llpos;
4662 char buf[30]; // use 30 to avoid compiler warning
4663 list_T *l;
4664
4665 llpos = &cur->pos.pos[i];
4666 if (llpos->lnum == 0)
4667 break;
4668 l = list_alloc();
4669 if (l == NULL)
4670 break;
4671 list_append_number(l, (varnumber_T)llpos->lnum);
4672 if (llpos->col > 0)
4673 {
4674 list_append_number(l, (varnumber_T)llpos->col);
4675 list_append_number(l, (varnumber_T)llpos->len);
4676 }
4677 sprintf(buf, "pos%d", i + 1);
4678 dict_add_list(dict, buf, l);
4679 }
4680 }
4681 else
4682 {
4683 dict_add_string(dict, "pattern", cur->pattern);
4684 }
4685 dict_add_string(dict, "group", syn_id2name(cur->hlg_id));
4686 dict_add_number(dict, "priority", (long)cur->priority);
4687 dict_add_number(dict, "id", (long)cur->id);
4688 # if defined(FEAT_CONCEAL)
4689 if (cur->conceal_char)
4690 {
4691 char_u buf[MB_MAXBYTES + 1];
4692
4693 buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL;
4694 dict_add_string(dict, "conceal", (char_u *)&buf);
4695 }
4696 # endif
4697 list_append_dict(rettv->vval.v_list, dict);
4698 cur = cur->next;
4699 }
4700 # endif
4701 }
4702
4703 /*
4704 * "setmatches()" function
4705 */
4706 void
4707 f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4708 {
4709 #ifdef FEAT_SEARCH_EXTRA
4710 list_T *l;
4711 listitem_T *li;
4712 dict_T *d;
4713 list_T *s = NULL;
4714 win_T *win = get_optional_window(argvars, 1);
4715
4716 rettv->vval.v_number = -1;
4717 if (argvars[0].v_type != VAR_LIST)
4718 {
4719 emsg(_(e_listreq));
4720 return;
4721 }
4722 if (win == NULL)
4723 return;
4724
4725 if ((l = argvars[0].vval.v_list) != NULL)
4726 {
4727 // To some extent make sure that we are dealing with a list from
4728 // "getmatches()".
4729 li = l->lv_first;
4730 while (li != NULL)
4731 {
4732 if (li->li_tv.v_type != VAR_DICT
4733 || (d = li->li_tv.vval.v_dict) == NULL)
4734 {
4735 emsg(_(e_invarg));
4736 return;
4737 }
4738 if (!(dict_find(d, (char_u *)"group", -1) != NULL
4739 && (dict_find(d, (char_u *)"pattern", -1) != NULL
4740 || dict_find(d, (char_u *)"pos1", -1) != NULL)
4741 && dict_find(d, (char_u *)"priority", -1) != NULL
4742 && dict_find(d, (char_u *)"id", -1) != NULL))
4743 {
4744 emsg(_(e_invarg));
4745 return;
4746 }
4747 li = li->li_next;
4748 }
4749
4750 clear_matches(win);
4751 li = l->lv_first;
4752 while (li != NULL)
4753 {
4754 int i = 0;
4755 char buf[30]; // use 30 to avoid compiler warning
4756 dictitem_T *di;
4757 char_u *group;
4758 int priority;
4759 int id;
4760 char_u *conceal;
4761
4762 d = li->li_tv.vval.v_dict;
4763 if (dict_find(d, (char_u *)"pattern", -1) == NULL)
4764 {
4765 if (s == NULL)
4766 {
4767 s = list_alloc();
4768 if (s == NULL)
4769 return;
4770 }
4771
4772 // match from matchaddpos()
4773 for (i = 1; i < 9; i++)
4774 {
4775 sprintf((char *)buf, (char *)"pos%d", i);
4776 if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
4777 {
4778 if (di->di_tv.v_type != VAR_LIST)
4779 return;
4780
4781 list_append_tv(s, &di->di_tv);
4782 s->lv_refcount++;
4783 }
4784 else
4785 break;
4786 }
4787 }
4788
4789 group = dict_get_string(d, (char_u *)"group", TRUE);
4790 priority = (int)dict_get_number(d, (char_u *)"priority");
4791 id = (int)dict_get_number(d, (char_u *)"id");
4792 conceal = dict_find(d, (char_u *)"conceal", -1) != NULL
4793 ? dict_get_string(d, (char_u *)"conceal", TRUE)
4794 : NULL;
4795 if (i == 0)
4796 {
4797 match_add(win, group,
4798 dict_get_string(d, (char_u *)"pattern", FALSE),
4799 priority, id, NULL, conceal);
4800 }
4801 else
4802 {
4803 match_add(win, group, NULL, priority, id, s, conceal);
4804 list_unref(s);
4805 s = NULL;
4806 }
4807 vim_free(group);
4808 vim_free(conceal);
4809
4810 li = li->li_next;
4811 }
4812 rettv->vval.v_number = 0;
4813 }
4814 #endif
4815 }
4816
4817 /*
4818 * "matchadd()" function
4819 */
4820 void
4821 f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4822 {
4823 # ifdef FEAT_SEARCH_EXTRA
4824 char_u buf[NUMBUFLEN];
4825 char_u *grp = tv_get_string_buf_chk(&argvars[0], buf); // group
4826 char_u *pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern
4827 int prio = 10; // default priority
4828 int id = -1;
4829 int error = FALSE;
4830 char_u *conceal_char = NULL;
4831 win_T *win = curwin;
4832
4833 rettv->vval.v_number = -1;
4834
4835 if (grp == NULL || pat == NULL)
4836 return;
4837 if (argvars[2].v_type != VAR_UNKNOWN)
4838 {
4839 prio = (int)tv_get_number_chk(&argvars[2], &error);
4840 if (argvars[3].v_type != VAR_UNKNOWN)
4841 {
4842 id = (int)tv_get_number_chk(&argvars[3], &error);
4843 if (argvars[4].v_type != VAR_UNKNOWN
4844 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4845 return;
4846 }
4847 }
4848 if (error == TRUE)
4849 return;
4850 if (id >= 1 && id <= 3)
4851 {
4852 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4853 return;
4854 }
4855
4856 rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
4857 conceal_char);
4858 # endif
4859 }
4860
4861 /*
4862 * "matchaddpos()" function
4863 */
4864 void
4865 f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4866 {
4867 # ifdef FEAT_SEARCH_EXTRA
4868 char_u buf[NUMBUFLEN];
4869 char_u *group;
4870 int prio = 10;
4871 int id = -1;
4872 int error = FALSE;
4873 list_T *l;
4874 char_u *conceal_char = NULL;
4875 win_T *win = curwin;
4876
4877 rettv->vval.v_number = -1;
4878
4879 group = tv_get_string_buf_chk(&argvars[0], buf);
4880 if (group == NULL)
4881 return;
4882
4883 if (argvars[1].v_type != VAR_LIST)
4884 {
4885 semsg(_(e_listarg), "matchaddpos()");
4886 return;
4887 }
4888 l = argvars[1].vval.v_list;
4889 if (l == NULL)
4890 return;
4891
4892 if (argvars[2].v_type != VAR_UNKNOWN)
4893 {
4894 prio = (int)tv_get_number_chk(&argvars[2], &error);
4895 if (argvars[3].v_type != VAR_UNKNOWN)
4896 {
4897 id = (int)tv_get_number_chk(&argvars[3], &error);
4898
4899 if (argvars[4].v_type != VAR_UNKNOWN
4900 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4901 return;
4902 }
4903 }
4904 if (error == TRUE)
4905 return;
4906
4907 // id == 3 is ok because matchaddpos() is supposed to substitute :3match
4908 if (id == 1 || id == 2)
4909 {
4910 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4911 return;
4912 }
4913
4914 rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
4915 conceal_char);
4916 # endif
4917 }
4918
4919 /*
4920 * "matcharg()" function
4921 */
4922 void
4923 f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
4924 {
4925 if (rettv_list_alloc(rettv) == OK)
4926 {
4927 # ifdef FEAT_SEARCH_EXTRA
4928 int id = (int)tv_get_number(&argvars[0]);
4929 matchitem_T *m;
4930
4931 if (id >= 1 && id <= 3)
4932 {
4933 if ((m = (matchitem_T *)get_match(curwin, id)) != NULL)
4934 {
4935 list_append_string(rettv->vval.v_list,
4936 syn_id2name(m->hlg_id), -1);
4937 list_append_string(rettv->vval.v_list, m->pattern, -1);
4938 }
4939 else
4940 {
4941 list_append_string(rettv->vval.v_list, NULL, -1);
4942 list_append_string(rettv->vval.v_list, NULL, -1);
4943 }
4944 }
4945 # endif
4946 }
4947 }
4948
4949 /*
4950 * "matchdelete()" function
4951 */
4952 void
4953 f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4954 {
4955 # ifdef FEAT_SEARCH_EXTRA
4956 win_T *win = get_optional_window(argvars, 1);
4957
4958 if (win == NULL)
4959 rettv->vval.v_number = -1;
4960 else
4961 rettv->vval.v_number = match_delete(win,
4962 (int)tv_get_number(&argvars[0]), TRUE);
4963 # endif
4964 }
4965 #endif
4966
4967 #if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
4968 /*
4969 * ":[N]match {group} {pattern}"
4970 * Sets nextcmd to the start of the next command, if any. Also called when
4971 * skipping commands to find the next command.
4972 */
4973 void
4974 ex_match(exarg_T *eap)
4975 {
4976 char_u *p;
4977 char_u *g = NULL;
4978 char_u *end;
4979 int c;
4980 int id;
4981
4982 if (eap->line2 <= 3)
4983 id = eap->line2;
4984 else
4985 {
4986 emsg(_(e_invcmd));
4987 return;
4988 }
4989
4990 // First clear any old pattern.
4991 if (!eap->skip)
4992 match_delete(curwin, id, FALSE);
4993
4994 if (ends_excmd2(eap->cmd, eap->arg))
4995 end = eap->arg;
4996 else if ((STRNICMP(eap->arg, "none", 4) == 0
4997 && (VIM_ISWHITE(eap->arg[4])
4998 || ends_excmd2(eap->arg, eap->arg + 4))))
4999 end = eap->arg + 4;
5000 else
5001 {
5002 p = skiptowhite(eap->arg);
5003 if (!eap->skip)
5004 g = vim_strnsave(eap->arg, p - eap->arg);
5005 p = skipwhite(p);
5006 if (*p == NUL)
5007 {
5008 // There must be two arguments.
5009 vim_free(g);
5010 semsg(_(e_invarg2), eap->arg);
5011 return;
5012 }
5013 end = skip_regexp(p + 1, *p, TRUE);
5014 if (!eap->skip)
5015 {
5016 if (*end != NUL && !ends_excmd2(end, skipwhite(end + 1)))
5017 {
5018 vim_free(g);
5019 eap->errmsg = e_trailing;
5020 return;
5021 }
5022 if (*end != *p)
5023 {
5024 vim_free(g);
5025 semsg(_(e_invarg2), p);
5026 return;
5027 }
5028
5029 c = *end;
5030 *end = NUL;
5031 match_add(curwin, g, p + 1, 10, id, NULL, NULL);
5032 vim_free(g);
5033 *end = c;
5034 }
5035 }
5036 eap->nextcmd = find_nextcmd(end);
5037 }
5038 #endif