blob: 7bda6f9834176fcc6c7261956806924744f5985c [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9/*
10 * search.c: code for normal mode searching commands
11 */
12
13#include "vim.h"
14
Bram Moolenaar071d4272004-06-13 20:20:40 +000015#ifdef FEAT_EVAL
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010016static void set_vv_searchforward(void);
17static int first_submatch(regmmatch_T *rp);
Bram Moolenaar071d4272004-06-13 20:20:40 +000018#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010019static int check_linecomment(char_u *line);
Bram Moolenaar071d4272004-06-13 20:20:40 +000020#ifdef FEAT_FIND_ID
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010021static void show_pat_in_path(char_u *, int,
22 int, int, FILE *, linenr_T *, long);
Bram Moolenaar071d4272004-06-13 20:20:40 +000023#endif
Bram Moolenaar8f46e4c2019-05-24 22:08:15 +020024static void search_stat(int dirc, pos_T *pos, int show_top_bot_msg, char_u *msgbuf, int recompute);
Bram Moolenaar071d4272004-06-13 20:20:40 +000025
Bram Moolenaar071d4272004-06-13 20:20:40 +000026/*
27 * This file contains various searching-related routines. These fall into
28 * three groups:
29 * 1. string searches (for /, ?, n, and N)
30 * 2. character searches within a single line (for f, F, t, T, etc)
31 * 3. "other" kinds of searches like the '%' command, and 'word' searches.
32 */
33
34/*
35 * String searches
36 *
37 * The string search functions are divided into two levels:
38 * lowest: searchit(); uses an pos_T for starting position and found match.
39 * Highest: do_search(); uses curwin->w_cursor; calls searchit().
40 *
41 * The last search pattern is remembered for repeating the same search.
42 * This pattern is shared between the :g, :s, ? and / commands.
43 * This is in search_regcomp().
44 *
45 * The actual string matching is done using a heavily modified version of
46 * Henry Spencer's regular expression library. See regexp.c.
47 */
48
Bram Moolenaar071d4272004-06-13 20:20:40 +000049/*
50 * Two search patterns are remembered: One for the :substitute command and
51 * one for other searches. last_idx points to the one that was used the last
52 * time.
53 */
Bram Moolenaarc3328162019-07-23 22:15:25 +020054static spat_T spats[2] =
Bram Moolenaar071d4272004-06-13 20:20:40 +000055{
Bram Moolenaar63d9e732019-12-05 21:10:38 +010056 {NULL, TRUE, FALSE, {'/', 0, 0, 0L}}, // last used search pat
57 {NULL, TRUE, FALSE, {'/', 0, 0, 0L}} // last used substitute pat
Bram Moolenaar071d4272004-06-13 20:20:40 +000058};
59
Bram Moolenaar63d9e732019-12-05 21:10:38 +010060static int last_idx = 0; // index in spats[] for RE_LAST
Bram Moolenaar071d4272004-06-13 20:20:40 +000061
Bram Moolenaar63d9e732019-12-05 21:10:38 +010062static char_u lastc[2] = {NUL, NUL}; // last character searched for
63static int lastcdir = FORWARD; // last direction of character search
64static int last_t_cmd = TRUE; // last search t_cmd
Bram Moolenaardbd24b52015-08-11 14:26:19 +020065static char_u lastc_bytes[MB_MAXBYTES + 1];
Bram Moolenaar63d9e732019-12-05 21:10:38 +010066static int lastc_bytelen = 1; // >1 for multi-byte char
Bram Moolenaardbd24b52015-08-11 14:26:19 +020067
Bram Moolenaar63d9e732019-12-05 21:10:38 +010068// copy of spats[], for keeping the search patterns while executing autocmds
Bram Moolenaarc3328162019-07-23 22:15:25 +020069static spat_T saved_spats[2];
Bram Moolenaar071d4272004-06-13 20:20:40 +000070# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaared8bc782018-12-01 21:08:21 +010071static int saved_spats_last_idx = 0;
72static int saved_spats_no_hlsearch = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000073# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000074
Bram Moolenaar63d9e732019-12-05 21:10:38 +010075static char_u *mr_pattern = NULL; // pattern used by search_regcomp()
Bram Moolenaar071d4272004-06-13 20:20:40 +000076#ifdef FEAT_RIGHTLEFT
Bram Moolenaar63d9e732019-12-05 21:10:38 +010077static int mr_pattern_alloced = FALSE; // mr_pattern was allocated
Bram Moolenaar071d4272004-06-13 20:20:40 +000078#endif
79
80#ifdef FEAT_FIND_ID
81/*
82 * Type used by find_pattern_in_path() to remember which included files have
83 * been searched already.
84 */
85typedef struct SearchedFile
86{
Bram Moolenaar63d9e732019-12-05 21:10:38 +010087 FILE *fp; // File pointer
88 char_u *name; // Full name of file
89 linenr_T lnum; // Line we were up to in file
90 int matched; // Found a match in this file
Bram Moolenaar071d4272004-06-13 20:20:40 +000091} SearchedFile;
92#endif
93
94/*
95 * translate search pattern for vim_regcomp()
96 *
97 * pat_save == RE_SEARCH: save pat in spats[RE_SEARCH].pat (normal search cmd)
98 * pat_save == RE_SUBST: save pat in spats[RE_SUBST].pat (:substitute command)
99 * pat_save == RE_BOTH: save pat in both patterns (:global command)
100 * pat_use == RE_SEARCH: use previous search pattern if "pat" is NULL
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000101 * pat_use == RE_SUBST: use previous substitute pattern if "pat" is NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +0000102 * pat_use == RE_LAST: use last used pattern if "pat" is NULL
103 * options & SEARCH_HIS: put search string in history
104 * options & SEARCH_KEEP: keep previous search pattern
105 *
106 * returns FAIL if failed, OK otherwise.
107 */
108 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100109search_regcomp(
110 char_u *pat,
111 int pat_save,
112 int pat_use,
113 int options,
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100114 regmmatch_T *regmatch) // return: pattern and ignore-case flag
Bram Moolenaar071d4272004-06-13 20:20:40 +0000115{
116 int magic;
117 int i;
118
119 rc_did_emsg = FALSE;
120 magic = p_magic;
121
122 /*
123 * If no pattern given, use a previously defined pattern.
124 */
125 if (pat == NULL || *pat == NUL)
126 {
127 if (pat_use == RE_LAST)
128 i = last_idx;
129 else
130 i = pat_use;
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100131 if (spats[i].pat == NULL) // pattern was never defined
Bram Moolenaar071d4272004-06-13 20:20:40 +0000132 {
133 if (pat_use == RE_SUBST)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100134 emsg(_(e_nopresub));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000135 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100136 emsg(_(e_noprevre));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000137 rc_did_emsg = TRUE;
138 return FAIL;
139 }
140 pat = spats[i].pat;
141 magic = spats[i].magic;
142 no_smartcase = spats[i].no_scs;
143 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100144 else if (options & SEARCH_HIS) // put new pattern in history
Bram Moolenaar071d4272004-06-13 20:20:40 +0000145 add_to_history(HIST_SEARCH, pat, TRUE, NUL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000146
147#ifdef FEAT_RIGHTLEFT
148 if (mr_pattern_alloced)
149 {
150 vim_free(mr_pattern);
151 mr_pattern_alloced = FALSE;
152 }
153
154 if (curwin->w_p_rl && *curwin->w_p_rlc == 's')
155 {
156 char_u *rev_pattern;
157
158 rev_pattern = reverse_text(pat);
159 if (rev_pattern == NULL)
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100160 mr_pattern = pat; // out of memory, keep normal pattern.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000161 else
162 {
163 mr_pattern = rev_pattern;
164 mr_pattern_alloced = TRUE;
165 }
166 }
167 else
168#endif
169 mr_pattern = pat;
170
171 /*
172 * Save the currently used pattern in the appropriate place,
173 * unless the pattern should not be remembered.
174 */
Bram Moolenaar14177b72014-01-14 15:53:51 +0100175 if (!(options & SEARCH_KEEP) && !cmdmod.keeppatterns)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000176 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100177 // search or global command
Bram Moolenaar071d4272004-06-13 20:20:40 +0000178 if (pat_save == RE_SEARCH || pat_save == RE_BOTH)
179 save_re_pat(RE_SEARCH, pat, magic);
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100180 // substitute or global command
Bram Moolenaar071d4272004-06-13 20:20:40 +0000181 if (pat_save == RE_SUBST || pat_save == RE_BOTH)
182 save_re_pat(RE_SUBST, pat, magic);
183 }
184
185 regmatch->rmm_ic = ignorecase(pat);
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000186 regmatch->rmm_maxcol = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000187 regmatch->regprog = vim_regcomp(pat, magic ? RE_MAGIC : 0);
188 if (regmatch->regprog == NULL)
189 return FAIL;
190 return OK;
191}
192
193/*
194 * Get search pattern used by search_regcomp().
195 */
196 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100197get_search_pat(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000198{
199 return mr_pattern;
200}
201
Bram Moolenaarabc97732007-08-08 20:49:37 +0000202#if defined(FEAT_RIGHTLEFT) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000203/*
204 * Reverse text into allocated memory.
205 * Returns the allocated string, NULL when out of memory.
206 */
Bram Moolenaarabc97732007-08-08 20:49:37 +0000207 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100208reverse_text(char_u *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000209{
210 unsigned len;
211 unsigned s_i, rev_i;
212 char_u *rev;
213
214 /*
215 * Reverse the pattern.
216 */
217 len = (unsigned)STRLEN(s);
218 rev = alloc(len + 1);
219 if (rev != NULL)
220 {
221 rev_i = len;
222 for (s_i = 0; s_i < len; ++s_i)
223 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000224 if (has_mbyte)
225 {
226 int mb_len;
227
Bram Moolenaar0fa313a2005-08-10 21:07:57 +0000228 mb_len = (*mb_ptr2len)(s + s_i);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000229 rev_i -= mb_len;
230 mch_memmove(rev + rev_i, s + s_i, mb_len);
231 s_i += mb_len - 1;
232 }
233 else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000234 rev[--rev_i] = s[s_i];
235
236 }
237 rev[len] = NUL;
238 }
239 return rev;
240}
241#endif
242
Bram Moolenaarcc2b9d52014-12-13 03:17:11 +0100243 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100244save_re_pat(int idx, char_u *pat, int magic)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000245{
246 if (spats[idx].pat != pat)
247 {
248 vim_free(spats[idx].pat);
249 spats[idx].pat = vim_strsave(pat);
250 spats[idx].magic = magic;
251 spats[idx].no_scs = no_smartcase;
252 last_idx = idx;
253#ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100254 // If 'hlsearch' set and search pat changed: need redraw.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000255 if (p_hls)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +0000256 redraw_all_later(SOME_VALID);
Bram Moolenaar451fc7b2018-04-27 22:53:07 +0200257 set_no_hlsearch(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000258#endif
259 }
260}
261
Bram Moolenaar071d4272004-06-13 20:20:40 +0000262/*
263 * Save the search patterns, so they can be restored later.
264 * Used before/after executing autocommands and user functions.
265 */
266static int save_level = 0;
267
268 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100269save_search_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000270{
271 if (save_level++ == 0)
272 {
273 saved_spats[0] = spats[0];
274 if (spats[0].pat != NULL)
275 saved_spats[0].pat = vim_strsave(spats[0].pat);
276 saved_spats[1] = spats[1];
277 if (spats[1].pat != NULL)
278 saved_spats[1].pat = vim_strsave(spats[1].pat);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +0100279#ifdef FEAT_SEARCH_EXTRA
Bram Moolenaared8bc782018-12-01 21:08:21 +0100280 saved_spats_last_idx = last_idx;
281 saved_spats_no_hlsearch = no_hlsearch;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +0100282#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000283 }
284}
285
286 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100287restore_search_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000288{
289 if (--save_level == 0)
290 {
291 vim_free(spats[0].pat);
292 spats[0] = saved_spats[0];
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +0100293#if defined(FEAT_EVAL)
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000294 set_vv_searchforward();
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +0100295#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000296 vim_free(spats[1].pat);
297 spats[1] = saved_spats[1];
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +0100298#ifdef FEAT_SEARCH_EXTRA
Bram Moolenaared8bc782018-12-01 21:08:21 +0100299 last_idx = saved_spats_last_idx;
300 set_no_hlsearch(saved_spats_no_hlsearch);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +0100301#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000302 }
303}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000304
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000305#if defined(EXITFREE) || defined(PROTO)
306 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100307free_search_patterns(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000308{
309 vim_free(spats[0].pat);
310 vim_free(spats[1].pat);
Bram Moolenaarf2427622009-04-22 16:45:21 +0000311
312# ifdef FEAT_RIGHTLEFT
313 if (mr_pattern_alloced)
314 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +0200315 vim_free(mr_pattern);
316 mr_pattern_alloced = FALSE;
317 mr_pattern = NULL;
Bram Moolenaarf2427622009-04-22 16:45:21 +0000318 }
319# endif
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000320}
321#endif
322
Bram Moolenaar2e51d9a2017-10-29 16:40:30 +0100323#ifdef FEAT_SEARCH_EXTRA
Bram Moolenaared8bc782018-12-01 21:08:21 +0100324// copy of spats[RE_SEARCH], for keeping the search patterns while incremental
325// searching
Bram Moolenaarc3328162019-07-23 22:15:25 +0200326static spat_T saved_last_search_spat;
Bram Moolenaared8bc782018-12-01 21:08:21 +0100327static int did_save_last_search_spat = 0;
328static int saved_last_idx = 0;
329static int saved_no_hlsearch = 0;
330
Bram Moolenaar2e51d9a2017-10-29 16:40:30 +0100331/*
332 * Save and restore the search pattern for incremental highlight search
333 * feature.
334 *
Bram Moolenaarc4568ab2018-11-16 16:21:05 +0100335 * It's similar to but different from save_search_patterns() and
Bram Moolenaar2e51d9a2017-10-29 16:40:30 +0100336 * restore_search_patterns(), because the search pattern must be restored when
Bram Moolenaarc4568ab2018-11-16 16:21:05 +0100337 * canceling incremental searching even if it's called inside user functions.
Bram Moolenaar2e51d9a2017-10-29 16:40:30 +0100338 */
339 void
340save_last_search_pattern(void)
341{
Bram Moolenaar01a060d2018-11-30 21:57:55 +0100342 if (did_save_last_search_spat != 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100343 iemsg("did_save_last_search_spat is not zero");
Bram Moolenaar01a060d2018-11-30 21:57:55 +0100344 else
345 ++did_save_last_search_spat;
346
Bram Moolenaar2e51d9a2017-10-29 16:40:30 +0100347 saved_last_search_spat = spats[RE_SEARCH];
348 if (spats[RE_SEARCH].pat != NULL)
349 saved_last_search_spat.pat = vim_strsave(spats[RE_SEARCH].pat);
350 saved_last_idx = last_idx;
351 saved_no_hlsearch = no_hlsearch;
352}
353
354 void
355restore_last_search_pattern(void)
356{
Bram Moolenaar01a060d2018-11-30 21:57:55 +0100357 if (did_save_last_search_spat != 1)
358 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100359 iemsg("did_save_last_search_spat is not one");
Bram Moolenaar01a060d2018-11-30 21:57:55 +0100360 return;
361 }
362 --did_save_last_search_spat;
363
Bram Moolenaar2e51d9a2017-10-29 16:40:30 +0100364 vim_free(spats[RE_SEARCH].pat);
365 spats[RE_SEARCH] = saved_last_search_spat;
Bram Moolenaar01a060d2018-11-30 21:57:55 +0100366 saved_last_search_spat.pat = NULL;
Bram Moolenaar2e51d9a2017-10-29 16:40:30 +0100367# if defined(FEAT_EVAL)
368 set_vv_searchforward();
369# endif
370 last_idx = saved_last_idx;
Bram Moolenaar451fc7b2018-04-27 22:53:07 +0200371 set_no_hlsearch(saved_no_hlsearch);
Bram Moolenaar2e51d9a2017-10-29 16:40:30 +0100372}
Bram Moolenaard0480092017-11-16 22:20:39 +0100373
374 char_u *
375last_search_pattern(void)
376{
377 return spats[RE_SEARCH].pat;
378}
Bram Moolenaar2e51d9a2017-10-29 16:40:30 +0100379#endif
380
Bram Moolenaar071d4272004-06-13 20:20:40 +0000381/*
382 * Return TRUE when case should be ignored for search pattern "pat".
383 * Uses the 'ignorecase' and 'smartcase' options.
384 */
385 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100386ignorecase(char_u *pat)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000387{
Bram Moolenaar66e29d72016-08-20 16:57:02 +0200388 return ignorecase_opt(pat, p_ic, p_scs);
389}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000390
Bram Moolenaar66e29d72016-08-20 16:57:02 +0200391/*
392 * As ignorecase() put pass the "ic" and "scs" flags.
393 */
394 int
395ignorecase_opt(char_u *pat, int ic_in, int scs)
396{
397 int ic = ic_in;
398
399 if (ic && !no_smartcase && scs
Bram Moolenaare2c453d2019-08-21 14:37:09 +0200400 && !(ctrl_x_mode_not_default() && curbuf->b_p_inf))
Bram Moolenaara9dc3752010-07-11 20:46:53 +0200401 ic = !pat_has_uppercase(pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000402 no_smartcase = FALSE;
403
404 return ic;
405}
406
Bram Moolenaara9dc3752010-07-11 20:46:53 +0200407/*
Bram Moolenaardbd24b52015-08-11 14:26:19 +0200408 * Return TRUE if pattern "pat" has an uppercase character.
Bram Moolenaara9dc3752010-07-11 20:46:53 +0200409 */
410 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100411pat_has_uppercase(char_u *pat)
Bram Moolenaara9dc3752010-07-11 20:46:53 +0200412{
413 char_u *p = pat;
414
415 while (*p != NUL)
416 {
Bram Moolenaara9dc3752010-07-11 20:46:53 +0200417 int l;
418
419 if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)
420 {
421 if (enc_utf8 && utf_isupper(utf_ptr2char(p)))
422 return TRUE;
423 p += l;
424 }
Bram Moolenaar264b74f2019-01-24 17:18:42 +0100425 else if (*p == '\\')
Bram Moolenaara9dc3752010-07-11 20:46:53 +0200426 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100427 if (p[1] == '_' && p[2] != NUL) // skip "\_X"
Bram Moolenaara9dc3752010-07-11 20:46:53 +0200428 p += 3;
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100429 else if (p[1] == '%' && p[2] != NUL) // skip "\%X"
Bram Moolenaara9dc3752010-07-11 20:46:53 +0200430 p += 3;
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100431 else if (p[1] != NUL) // skip "\X"
Bram Moolenaara9dc3752010-07-11 20:46:53 +0200432 p += 2;
433 else
434 p += 1;
435 }
436 else if (MB_ISUPPER(*p))
437 return TRUE;
438 else
439 ++p;
440 }
441 return FALSE;
442}
443
Bram Moolenaar113e1072019-01-20 15:30:40 +0100444#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000445 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100446last_csearch(void)
Bram Moolenaardbd24b52015-08-11 14:26:19 +0200447{
Bram Moolenaardbd24b52015-08-11 14:26:19 +0200448 return lastc_bytes;
Bram Moolenaardbd24b52015-08-11 14:26:19 +0200449}
450
451 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100452last_csearch_forward(void)
Bram Moolenaardbd24b52015-08-11 14:26:19 +0200453{
454 return lastcdir == FORWARD;
455}
456
457 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100458last_csearch_until(void)
Bram Moolenaardbd24b52015-08-11 14:26:19 +0200459{
460 return last_t_cmd == TRUE;
461}
462
463 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100464set_last_csearch(int c, char_u *s UNUSED, int len UNUSED)
Bram Moolenaardbd24b52015-08-11 14:26:19 +0200465{
466 *lastc = c;
Bram Moolenaardbd24b52015-08-11 14:26:19 +0200467 lastc_bytelen = len;
468 if (len)
469 memcpy(lastc_bytes, s, len);
470 else
Bram Moolenaara80faa82020-04-12 19:37:17 +0200471 CLEAR_FIELD(lastc_bytes);
Bram Moolenaardbd24b52015-08-11 14:26:19 +0200472}
Bram Moolenaar113e1072019-01-20 15:30:40 +0100473#endif
Bram Moolenaardbd24b52015-08-11 14:26:19 +0200474
475 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100476set_csearch_direction(int cdir)
Bram Moolenaardbd24b52015-08-11 14:26:19 +0200477{
478 lastcdir = cdir;
479}
480
481 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100482set_csearch_until(int t_cmd)
Bram Moolenaardbd24b52015-08-11 14:26:19 +0200483{
484 last_t_cmd = t_cmd;
485}
486
487 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100488last_search_pat(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000489{
490 return spats[last_idx].pat;
491}
492
493/*
494 * Reset search direction to forward. For "gd" and "gD" commands.
495 */
496 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100497reset_search_dir(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000498{
499 spats[0].off.dir = '/';
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000500#if defined(FEAT_EVAL)
501 set_vv_searchforward();
502#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000503}
504
505#if defined(FEAT_EVAL) || defined(FEAT_VIMINFO)
506/*
507 * Set the last search pattern. For ":let @/ =" and viminfo.
508 * Also set the saved search pattern, so that this works in an autocommand.
509 */
510 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100511set_last_search_pat(
512 char_u *s,
513 int idx,
514 int magic,
515 int setlast)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000516{
517 vim_free(spats[idx].pat);
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100518 // An empty string means that nothing should be matched.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000519 if (*s == NUL)
520 spats[idx].pat = NULL;
521 else
522 spats[idx].pat = vim_strsave(s);
523 spats[idx].magic = magic;
524 spats[idx].no_scs = FALSE;
525 spats[idx].off.dir = '/';
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000526#if defined(FEAT_EVAL)
527 set_vv_searchforward();
528#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000529 spats[idx].off.line = FALSE;
530 spats[idx].off.end = FALSE;
531 spats[idx].off.off = 0;
532 if (setlast)
533 last_idx = idx;
534 if (save_level)
535 {
536 vim_free(saved_spats[idx].pat);
537 saved_spats[idx] = spats[0];
538 if (spats[idx].pat == NULL)
539 saved_spats[idx].pat = NULL;
540 else
541 saved_spats[idx].pat = vim_strsave(spats[idx].pat);
Bram Moolenaar975880b2019-03-03 14:42:11 +0100542# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaared8bc782018-12-01 21:08:21 +0100543 saved_spats_last_idx = last_idx;
Bram Moolenaar975880b2019-03-03 14:42:11 +0100544# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000545 }
546# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100547 // If 'hlsearch' set and search pat changed: need redraw.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000548 if (p_hls && idx == last_idx && !no_hlsearch)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +0000549 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000550# endif
551}
552#endif
553
554#ifdef FEAT_SEARCH_EXTRA
555/*
556 * Get a regexp program for the last used search pattern.
557 * This is used for highlighting all matches in a window.
558 * Values returned in regmatch->regprog and regmatch->rmm_ic.
559 */
560 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100561last_pat_prog(regmmatch_T *regmatch)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000562{
563 if (spats[last_idx].pat == NULL)
564 {
565 regmatch->regprog = NULL;
566 return;
567 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100568 ++emsg_off; // So it doesn't beep if bad expr
Bram Moolenaar071d4272004-06-13 20:20:40 +0000569 (void)search_regcomp((char_u *)"", 0, last_idx, SEARCH_KEEP, regmatch);
570 --emsg_off;
571}
572#endif
573
574/*
Bram Moolenaarf7ff6e82014-03-23 15:13:05 +0100575 * Lowest level search function.
Bram Moolenaar5d24a222018-12-23 19:10:09 +0100576 * Search for 'count'th occurrence of pattern "pat" in direction "dir".
577 * Start at position "pos" and return the found position in "pos".
Bram Moolenaar071d4272004-06-13 20:20:40 +0000578 *
579 * if (options & SEARCH_MSG) == 0 don't give any messages
580 * if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages
581 * if (options & SEARCH_MSG) == SEARCH_MSG give all messages
582 * if (options & SEARCH_HIS) put search pattern in history
583 * if (options & SEARCH_END) return position at end of match
584 * if (options & SEARCH_START) accept match at pos itself
585 * if (options & SEARCH_KEEP) keep previous search pattern
586 * if (options & SEARCH_FOLD) match only once in a closed fold
587 * if (options & SEARCH_PEEK) check for typed char, cancel search
Bram Moolenaarad4d8a12015-12-28 19:20:36 +0100588 * if (options & SEARCH_COL) start at pos->col instead of zero
Bram Moolenaar071d4272004-06-13 20:20:40 +0000589 *
590 * Return FAIL (zero) for failure, non-zero for success.
591 * When FEAT_EVAL is defined, returns the index of the first matching
592 * subpattern plus one; one if there was none.
593 */
594 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100595searchit(
Bram Moolenaar92ea26b2019-10-18 20:53:34 +0200596 win_T *win, // window to search in; can be NULL for a
597 // buffer without a window!
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100598 buf_T *buf,
599 pos_T *pos,
Bram Moolenaar5d24a222018-12-23 19:10:09 +0100600 pos_T *end_pos, // set to end of the match, unless NULL
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100601 int dir,
602 char_u *pat,
603 long count,
604 int options,
Bram Moolenaar92ea26b2019-10-18 20:53:34 +0200605 int pat_use, // which pattern to use when "pat" is empty
606 searchit_arg_T *extra_arg) // optional extra arguments, can be NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +0000607{
608 int found;
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100609 linenr_T lnum; // no init to shut up Apollo cc
Bram Moolenaarad4d8a12015-12-28 19:20:36 +0100610 colnr_T col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000611 regmmatch_T regmatch;
612 char_u *ptr;
613 colnr_T matchcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000614 lpos_T endpos;
Bram Moolenaar677ee682005-01-27 14:41:15 +0000615 lpos_T matchpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000616 int loop;
617 pos_T start_pos;
618 int at_first_line;
619 int extra_col;
Bram Moolenaar5f1e68b2015-07-10 14:43:35 +0200620 int start_char_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000621 int match_ok;
622 long nmatched;
623 int submatch = 0;
Bram Moolenaara3dfccc2014-11-27 17:29:56 +0100624 int first_match = TRUE;
Bram Moolenaar53989552019-12-23 22:59:18 +0100625 int called_emsg_before = called_emsg;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000626#ifdef FEAT_SEARCH_EXTRA
627 int break_loop = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000628#endif
Bram Moolenaar92ea26b2019-10-18 20:53:34 +0200629 linenr_T stop_lnum = 0; // stop after this line number when != 0
630#ifdef FEAT_RELTIME
631 proftime_T *tm = NULL; // timeout limit or NULL
632 int *timed_out = NULL; // set when timed out or NULL
633#endif
634
635 if (extra_arg != NULL)
636 {
637 stop_lnum = extra_arg->sa_stop_lnum;
638#ifdef FEAT_RELTIME
639 tm = extra_arg->sa_tm;
640 timed_out = &extra_arg->sa_timed_out;
641#endif
642 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000643
644 if (search_regcomp(pat, RE_SEARCH, pat_use,
645 (options & (SEARCH_HIS + SEARCH_KEEP)), &regmatch) == FAIL)
646 {
647 if ((options & SEARCH_MSG) && !rc_did_emsg)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100648 semsg(_("E383: Invalid search string: %s"), mr_pattern);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000649 return FAIL;
650 }
651
Bram Moolenaar280f1262006-01-30 00:14:18 +0000652 /*
653 * find the string
654 */
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100655 do // loop for count
Bram Moolenaar071d4272004-06-13 20:20:40 +0000656 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100657 // When not accepting a match at the start position set "extra_col" to
658 // a non-zero value. Don't do that when starting at MAXCOL, since
659 // MAXCOL + 1 is zero.
Bram Moolenaar5f1e68b2015-07-10 14:43:35 +0200660 if (pos->col == MAXCOL)
661 start_char_len = 0;
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100662 // Watch out for the "col" being MAXCOL - 2, used in a closed fold.
Bram Moolenaar5f1e68b2015-07-10 14:43:35 +0200663 else if (has_mbyte
664 && pos->lnum >= 1 && pos->lnum <= buf->b_ml.ml_line_count
665 && pos->col < MAXCOL - 2)
Bram Moolenaara3dfccc2014-11-27 17:29:56 +0100666 {
Bram Moolenaar82846a02018-02-09 18:09:54 +0100667 ptr = ml_get_buf(buf, pos->lnum, FALSE);
Bram Moolenaar8846ac52018-02-09 19:24:01 +0100668 if ((int)STRLEN(ptr) <= pos->col)
Bram Moolenaar5f1e68b2015-07-10 14:43:35 +0200669 start_char_len = 1;
Bram Moolenaara3dfccc2014-11-27 17:29:56 +0100670 else
Bram Moolenaar82846a02018-02-09 18:09:54 +0100671 start_char_len = (*mb_ptr2len)(ptr + pos->col);
Bram Moolenaara3dfccc2014-11-27 17:29:56 +0100672 }
Bram Moolenaara3dfccc2014-11-27 17:29:56 +0100673 else
Bram Moolenaar5f1e68b2015-07-10 14:43:35 +0200674 start_char_len = 1;
675 if (dir == FORWARD)
676 {
677 if (options & SEARCH_START)
678 extra_col = 0;
679 else
680 extra_col = start_char_len;
681 }
682 else
683 {
684 if (options & SEARCH_START)
685 extra_col = start_char_len;
686 else
687 extra_col = 0;
688 }
Bram Moolenaara3dfccc2014-11-27 17:29:56 +0100689
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100690 start_pos = *pos; // remember start pos for detecting no match
691 found = 0; // default: not found
692 at_first_line = TRUE; // default: start in first line
693 if (pos->lnum == 0) // correct lnum for when starting in line 0
Bram Moolenaar071d4272004-06-13 20:20:40 +0000694 {
695 pos->lnum = 1;
696 pos->col = 0;
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100697 at_first_line = FALSE; // not in first line now
Bram Moolenaar071d4272004-06-13 20:20:40 +0000698 }
699
700 /*
701 * Start searching in current line, unless searching backwards and
702 * we're in column 0.
Bram Moolenaar7a42fa32007-07-10 11:28:55 +0000703 * If we are searching backwards, in column 0, and not including the
704 * current position, gain some efficiency by skipping back a line.
705 * Otherwise begin the search in the current line.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000706 */
Bram Moolenaar7a42fa32007-07-10 11:28:55 +0000707 if (dir == BACKWARD && start_pos.col == 0
708 && (options & SEARCH_START) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000709 {
710 lnum = pos->lnum - 1;
711 at_first_line = FALSE;
712 }
713 else
714 lnum = pos->lnum;
715
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100716 for (loop = 0; loop <= 1; ++loop) // loop twice if 'wrapscan' set
Bram Moolenaar071d4272004-06-13 20:20:40 +0000717 {
718 for ( ; lnum > 0 && lnum <= buf->b_ml.ml_line_count;
719 lnum += dir, at_first_line = FALSE)
720 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100721 // Stop after checking "stop_lnum", if it's set.
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000722 if (stop_lnum != 0 && (dir == FORWARD
723 ? lnum > stop_lnum : lnum < stop_lnum))
724 break;
Bram Moolenaar76929292008-01-06 19:07:36 +0000725#ifdef FEAT_RELTIME
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100726 // Stop after passing the "tm" time limit.
Bram Moolenaar76929292008-01-06 19:07:36 +0000727 if (tm != NULL && profile_passed_limit(tm))
728 break;
729#endif
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000730
Bram Moolenaar071d4272004-06-13 20:20:40 +0000731 /*
Bram Moolenaar677ee682005-01-27 14:41:15 +0000732 * Look for a match somewhere in line "lnum".
Bram Moolenaar071d4272004-06-13 20:20:40 +0000733 */
Bram Moolenaarad4d8a12015-12-28 19:20:36 +0100734 col = at_first_line && (options & SEARCH_COL) ? pos->col
735 : (colnr_T)0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000736 nmatched = vim_regexec_multi(&regmatch, win, buf,
Bram Moolenaarad4d8a12015-12-28 19:20:36 +0100737 lnum, col,
Bram Moolenaar91a4e822008-01-19 14:59:58 +0000738#ifdef FEAT_RELTIME
Bram Moolenaarfbd0b0a2017-06-17 18:44:21 +0200739 tm, timed_out
Bram Moolenaar91a4e822008-01-19 14:59:58 +0000740#else
Bram Moolenaarfbd0b0a2017-06-17 18:44:21 +0200741 NULL, NULL
Bram Moolenaar91a4e822008-01-19 14:59:58 +0000742#endif
743 );
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100744 // Abort searching on an error (e.g., out of stack).
Bram Moolenaar53989552019-12-23 22:59:18 +0100745 if (called_emsg > called_emsg_before
Bram Moolenaarfbd0b0a2017-06-17 18:44:21 +0200746#ifdef FEAT_RELTIME
747 || (timed_out != NULL && *timed_out)
748#endif
749 )
Bram Moolenaar071d4272004-06-13 20:20:40 +0000750 break;
751 if (nmatched > 0)
752 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100753 // match may actually be in another line when using \zs
Bram Moolenaar677ee682005-01-27 14:41:15 +0000754 matchpos = regmatch.startpos[0];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000755 endpos = regmatch.endpos[0];
Bram Moolenaar91a4e822008-01-19 14:59:58 +0000756#ifdef FEAT_EVAL
Bram Moolenaar071d4272004-06-13 20:20:40 +0000757 submatch = first_submatch(&regmatch);
Bram Moolenaar91a4e822008-01-19 14:59:58 +0000758#endif
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100759 // "lnum" may be past end of buffer for "\n\zs".
Bram Moolenaar32466aa2006-02-24 23:53:04 +0000760 if (lnum + matchpos.lnum > buf->b_ml.ml_line_count)
761 ptr = (char_u *)"";
762 else
763 ptr = ml_get_buf(buf, lnum + matchpos.lnum, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000764
765 /*
766 * Forward search in the first line: match should be after
767 * the start position. If not, continue at the end of the
768 * match (this is vi compatible) or on the next char.
769 */
770 if (dir == FORWARD && at_first_line)
771 {
772 match_ok = TRUE;
773 /*
Bram Moolenaar677ee682005-01-27 14:41:15 +0000774 * When the match starts in a next line it's certainly
775 * past the start position.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000776 * When match lands on a NUL the cursor will be put
777 * one back afterwards, compare with that position,
778 * otherwise "/$" will get stuck on end of line.
779 */
Bram Moolenaar677ee682005-01-27 14:41:15 +0000780 while (matchpos.lnum == 0
Bram Moolenaara3dfccc2014-11-27 17:29:56 +0100781 && ((options & SEARCH_END) && first_match
Bram Moolenaar677ee682005-01-27 14:41:15 +0000782 ? (nmatched == 1
783 && (int)endpos.col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +0000784 < (int)start_pos.col + extra_col)
Bram Moolenaar677ee682005-01-27 14:41:15 +0000785 : ((int)matchpos.col
786 - (ptr[matchpos.col] == NUL)
787 < (int)start_pos.col + extra_col)))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000788 {
789 /*
790 * If vi-compatible searching, continue at the end
791 * of the match, otherwise continue one position
792 * forward.
793 */
794 if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
795 {
796 if (nmatched > 1)
797 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100798 // end is in next line, thus no match in
799 // this line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000800 match_ok = FALSE;
801 break;
802 }
803 matchcol = endpos.col;
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100804 // for empty match: advance one char
Bram Moolenaar677ee682005-01-27 14:41:15 +0000805 if (matchcol == matchpos.col
Bram Moolenaar071d4272004-06-13 20:20:40 +0000806 && ptr[matchcol] != NUL)
807 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000808 if (has_mbyte)
809 matchcol +=
Bram Moolenaar0fa313a2005-08-10 21:07:57 +0000810 (*mb_ptr2len)(ptr + matchcol);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000811 else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000812 ++matchcol;
813 }
814 }
815 else
816 {
Bram Moolenaar677ee682005-01-27 14:41:15 +0000817 matchcol = matchpos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000818 if (ptr[matchcol] != NUL)
819 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000820 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +0000821 matchcol += (*mb_ptr2len)(ptr
Bram Moolenaar071d4272004-06-13 20:20:40 +0000822 + matchcol);
823 else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000824 ++matchcol;
825 }
826 }
Bram Moolenaar7bcb30e2013-04-03 21:14:29 +0200827 if (matchcol == 0 && (options & SEARCH_START))
Bram Moolenaardb333a52013-03-19 15:27:48 +0100828 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000829 if (ptr[matchcol] == NUL
830 || (nmatched = vim_regexec_multi(&regmatch,
Bram Moolenaar677ee682005-01-27 14:41:15 +0000831 win, buf, lnum + matchpos.lnum,
Bram Moolenaar91a4e822008-01-19 14:59:58 +0000832 matchcol,
833#ifdef FEAT_RELTIME
Bram Moolenaarfbd0b0a2017-06-17 18:44:21 +0200834 tm, timed_out
Bram Moolenaar91a4e822008-01-19 14:59:58 +0000835#else
Bram Moolenaarfbd0b0a2017-06-17 18:44:21 +0200836 NULL, NULL
Bram Moolenaar91a4e822008-01-19 14:59:58 +0000837#endif
838 )) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000839 {
840 match_ok = FALSE;
841 break;
842 }
Bram Moolenaar677ee682005-01-27 14:41:15 +0000843 matchpos = regmatch.startpos[0];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000844 endpos = regmatch.endpos[0];
845# ifdef FEAT_EVAL
846 submatch = first_submatch(&regmatch);
847# endif
848
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100849 // Need to get the line pointer again, a
850 // multi-line search may have made it invalid.
Bram Moolenaar677ee682005-01-27 14:41:15 +0000851 ptr = ml_get_buf(buf, lnum + matchpos.lnum, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000852 }
853 if (!match_ok)
854 continue;
855 }
856 if (dir == BACKWARD)
857 {
858 /*
859 * Now, if there are multiple matches on this line,
860 * we have to get the last one. Or the last one before
861 * the cursor, if we're on that line.
862 * When putting the new cursor at the end, compare
863 * relative to the end of the match.
864 */
865 match_ok = FALSE;
866 for (;;)
867 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100868 // Remember a position that is before the start
869 // position, we use it if it's the last match in
870 // the line. Always accept a position after
871 // wrapping around.
Bram Moolenaar677ee682005-01-27 14:41:15 +0000872 if (loop
873 || ((options & SEARCH_END)
874 ? (lnum + regmatch.endpos[0].lnum
875 < start_pos.lnum
876 || (lnum + regmatch.endpos[0].lnum
877 == start_pos.lnum
878 && (int)regmatch.endpos[0].col - 1
Bram Moolenaar5f1e68b2015-07-10 14:43:35 +0200879 < (int)start_pos.col
880 + extra_col))
Bram Moolenaar677ee682005-01-27 14:41:15 +0000881 : (lnum + regmatch.startpos[0].lnum
882 < start_pos.lnum
883 || (lnum + regmatch.startpos[0].lnum
884 == start_pos.lnum
885 && (int)regmatch.startpos[0].col
Bram Moolenaar5f1e68b2015-07-10 14:43:35 +0200886 < (int)start_pos.col
887 + extra_col))))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000888 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000889 match_ok = TRUE;
Bram Moolenaar677ee682005-01-27 14:41:15 +0000890 matchpos = regmatch.startpos[0];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000891 endpos = regmatch.endpos[0];
892# ifdef FEAT_EVAL
893 submatch = first_submatch(&regmatch);
894# endif
895 }
896 else
897 break;
898
899 /*
900 * We found a valid match, now check if there is
901 * another one after it.
902 * If vi-compatible searching, continue at the end
903 * of the match, otherwise continue one position
904 * forward.
905 */
906 if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
907 {
908 if (nmatched > 1)
909 break;
910 matchcol = endpos.col;
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100911 // for empty match: advance one char
Bram Moolenaar677ee682005-01-27 14:41:15 +0000912 if (matchcol == matchpos.col
Bram Moolenaar071d4272004-06-13 20:20:40 +0000913 && ptr[matchcol] != NUL)
914 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000915 if (has_mbyte)
916 matchcol +=
Bram Moolenaar0fa313a2005-08-10 21:07:57 +0000917 (*mb_ptr2len)(ptr + matchcol);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000918 else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000919 ++matchcol;
920 }
921 }
922 else
923 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100924 // Stop when the match is in a next line.
Bram Moolenaar677ee682005-01-27 14:41:15 +0000925 if (matchpos.lnum > 0)
926 break;
927 matchcol = matchpos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000928 if (ptr[matchcol] != NUL)
929 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000930 if (has_mbyte)
931 matchcol +=
Bram Moolenaar0fa313a2005-08-10 21:07:57 +0000932 (*mb_ptr2len)(ptr + matchcol);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000933 else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000934 ++matchcol;
935 }
936 }
937 if (ptr[matchcol] == NUL
938 || (nmatched = vim_regexec_multi(&regmatch,
Bram Moolenaar677ee682005-01-27 14:41:15 +0000939 win, buf, lnum + matchpos.lnum,
Bram Moolenaar91a4e822008-01-19 14:59:58 +0000940 matchcol,
941#ifdef FEAT_RELTIME
Bram Moolenaarfbd0b0a2017-06-17 18:44:21 +0200942 tm, timed_out
Bram Moolenaar91a4e822008-01-19 14:59:58 +0000943#else
Bram Moolenaarfbd0b0a2017-06-17 18:44:21 +0200944 NULL, NULL
Bram Moolenaar91a4e822008-01-19 14:59:58 +0000945#endif
946 )) == 0)
Bram Moolenaar9d322762018-02-09 16:04:25 +0100947 {
948#ifdef FEAT_RELTIME
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100949 // If the search timed out, we did find a match
950 // but it might be the wrong one, so that's not
951 // OK.
Bram Moolenaar9d322762018-02-09 16:04:25 +0100952 if (timed_out != NULL && *timed_out)
953 match_ok = FALSE;
954#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000955 break;
Bram Moolenaar9d322762018-02-09 16:04:25 +0100956 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000957
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100958 // Need to get the line pointer again, a
959 // multi-line search may have made it invalid.
Bram Moolenaar677ee682005-01-27 14:41:15 +0000960 ptr = ml_get_buf(buf, lnum + matchpos.lnum, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000961 }
962
963 /*
964 * If there is only a match after the cursor, skip
965 * this match.
966 */
967 if (!match_ok)
968 continue;
969 }
970
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100971 // With the SEARCH_END option move to the last character
972 // of the match. Don't do it for an empty match, end
973 // should be same as start then.
Bram Moolenaar7bcb30e2013-04-03 21:14:29 +0200974 if ((options & SEARCH_END) && !(options & SEARCH_NOOF)
Bram Moolenaar5bcbd532008-02-20 12:43:01 +0000975 && !(matchpos.lnum == endpos.lnum
976 && matchpos.col == endpos.col))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000977 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100978 // For a match in the first column, set the position
979 // on the NUL in the previous line.
Bram Moolenaar677ee682005-01-27 14:41:15 +0000980 pos->lnum = lnum + endpos.lnum;
Bram Moolenaar5bcbd532008-02-20 12:43:01 +0000981 pos->col = endpos.col;
982 if (endpos.col == 0)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000983 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100984 if (pos->lnum > 1) // just in case
Bram Moolenaar5bcbd532008-02-20 12:43:01 +0000985 {
986 --pos->lnum;
987 pos->col = (colnr_T)STRLEN(ml_get_buf(buf,
988 pos->lnum, FALSE));
989 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000990 }
Bram Moolenaar5bcbd532008-02-20 12:43:01 +0000991 else
992 {
993 --pos->col;
Bram Moolenaar5bcbd532008-02-20 12:43:01 +0000994 if (has_mbyte
995 && pos->lnum <= buf->b_ml.ml_line_count)
996 {
997 ptr = ml_get_buf(buf, pos->lnum, FALSE);
998 pos->col -= (*mb_head_off)(ptr, ptr + pos->col);
999 }
Bram Moolenaar5bcbd532008-02-20 12:43:01 +00001000 }
Bram Moolenaar5d24a222018-12-23 19:10:09 +01001001 if (end_pos != NULL)
1002 {
1003 end_pos->lnum = lnum + matchpos.lnum;
1004 end_pos->col = matchpos.col;
1005 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001006 }
1007 else
1008 {
Bram Moolenaar677ee682005-01-27 14:41:15 +00001009 pos->lnum = lnum + matchpos.lnum;
1010 pos->col = matchpos.col;
Bram Moolenaar5d24a222018-12-23 19:10:09 +01001011 if (end_pos != NULL)
1012 {
1013 end_pos->lnum = lnum + endpos.lnum;
1014 end_pos->col = endpos.col;
1015 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001016 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001017 pos->coladd = 0;
Bram Moolenaar5d24a222018-12-23 19:10:09 +01001018 if (end_pos != NULL)
1019 end_pos->coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001020 found = 1;
Bram Moolenaara3dfccc2014-11-27 17:29:56 +01001021 first_match = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001022
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001023 // Set variables used for 'incsearch' highlighting.
Bram Moolenaar677ee682005-01-27 14:41:15 +00001024 search_match_lines = endpos.lnum - matchpos.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001025 search_match_endcol = endpos.col;
1026 break;
1027 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001028 line_breakcheck(); // stop if ctrl-C typed
Bram Moolenaar071d4272004-06-13 20:20:40 +00001029 if (got_int)
1030 break;
1031
1032#ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001033 // Cancel searching if a character was typed. Used for
1034 // 'incsearch'. Don't check too often, that would slowdown
1035 // searching too much.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001036 if ((options & SEARCH_PEEK)
1037 && ((lnum - pos->lnum) & 0x3f) == 0
1038 && char_avail())
1039 {
1040 break_loop = TRUE;
1041 break;
1042 }
1043#endif
1044
1045 if (loop && lnum == start_pos.lnum)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001046 break; // if second loop, stop where started
Bram Moolenaar071d4272004-06-13 20:20:40 +00001047 }
1048 at_first_line = FALSE;
1049
1050 /*
Bram Moolenaara23ccb82006-02-27 00:08:02 +00001051 * Stop the search if wrapscan isn't set, "stop_lnum" is
1052 * specified, after an interrupt, after a match and after looping
1053 * twice.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001054 */
Bram Moolenaar53989552019-12-23 22:59:18 +01001055 if (!p_ws || stop_lnum != 0 || got_int
1056 || called_emsg > called_emsg_before
Bram Moolenaarfbd0b0a2017-06-17 18:44:21 +02001057#ifdef FEAT_RELTIME
1058 || (timed_out != NULL && *timed_out)
Bram Moolenaar78a15312009-05-15 19:33:18 +00001059#endif
Bram Moolenaarfbd0b0a2017-06-17 18:44:21 +02001060#ifdef FEAT_SEARCH_EXTRA
1061 || break_loop
1062#endif
1063 || found || loop)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001064 break;
1065
1066 /*
1067 * If 'wrapscan' is set we continue at the other end of the file.
1068 * If 'shortmess' does not contain 's', we give a message.
1069 * This message is also remembered in keep_msg for when the screen
1070 * is redrawn. The keep_msg is cleared whenever another message is
1071 * written.
1072 */
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001073 if (dir == BACKWARD) // start second loop at the other end
Bram Moolenaar071d4272004-06-13 20:20:40 +00001074 lnum = buf->b_ml.ml_line_count;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001075 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001076 lnum = 1;
Bram Moolenaar92d640f2005-09-05 22:11:52 +00001077 if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
1078 give_warning((char_u *)_(dir == BACKWARD
1079 ? top_bot_msg : bot_top_msg), TRUE);
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02001080 if (extra_arg != NULL)
1081 extra_arg->sa_wrapped = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001082 }
Bram Moolenaar53989552019-12-23 22:59:18 +01001083 if (got_int || called_emsg > called_emsg_before
Bram Moolenaarfbd0b0a2017-06-17 18:44:21 +02001084#ifdef FEAT_RELTIME
1085 || (timed_out != NULL && *timed_out)
1086#endif
Bram Moolenaar78a15312009-05-15 19:33:18 +00001087#ifdef FEAT_SEARCH_EXTRA
1088 || break_loop
1089#endif
1090 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001091 break;
1092 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001093 while (--count > 0 && found); // stop after count matches or no match
Bram Moolenaar071d4272004-06-13 20:20:40 +00001094
Bram Moolenaar473de612013-06-08 18:19:48 +02001095 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001096
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001097 if (!found) // did not find it
Bram Moolenaar071d4272004-06-13 20:20:40 +00001098 {
1099 if (got_int)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001100 emsg(_(e_interr));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001101 else if ((options & SEARCH_MSG) == SEARCH_MSG)
1102 {
1103 if (p_ws)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001104 semsg(_(e_patnotf2), mr_pattern);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001105 else if (lnum == 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001106 semsg(_("E384: search hit TOP without match for: %s"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001107 mr_pattern);
1108 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001109 semsg(_("E385: search hit BOTTOM without match for: %s"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001110 mr_pattern);
1111 }
1112 return FAIL;
1113 }
1114
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001115 // A pattern like "\n\zs" may go past the last line.
Bram Moolenaar32466aa2006-02-24 23:53:04 +00001116 if (pos->lnum > buf->b_ml.ml_line_count)
1117 {
1118 pos->lnum = buf->b_ml.ml_line_count;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00001119 pos->col = (int)STRLEN(ml_get_buf(buf, pos->lnum, FALSE));
Bram Moolenaar32466aa2006-02-24 23:53:04 +00001120 if (pos->col > 0)
1121 --pos->col;
1122 }
1123
Bram Moolenaar071d4272004-06-13 20:20:40 +00001124 return submatch + 1;
1125}
1126
1127#ifdef FEAT_EVAL
Bram Moolenaar8c8de832008-06-24 22:58:06 +00001128 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001129set_search_direction(int cdir)
Bram Moolenaar8c8de832008-06-24 22:58:06 +00001130{
1131 spats[0].off.dir = cdir;
1132}
1133
1134 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001135set_vv_searchforward(void)
Bram Moolenaar8c8de832008-06-24 22:58:06 +00001136{
1137 set_vim_var_nr(VV_SEARCHFORWARD, (long)(spats[0].off.dir == '/'));
1138}
1139
Bram Moolenaar071d4272004-06-13 20:20:40 +00001140/*
1141 * Return the number of the first subpat that matched.
Bram Moolenaarad4d8a12015-12-28 19:20:36 +01001142 * Return zero if none of them matched.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001143 */
1144 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001145first_submatch(regmmatch_T *rp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001146{
1147 int submatch;
1148
1149 for (submatch = 1; ; ++submatch)
1150 {
1151 if (rp->startpos[submatch].lnum >= 0)
1152 break;
1153 if (submatch == 9)
1154 {
1155 submatch = 0;
1156 break;
1157 }
1158 }
1159 return submatch;
1160}
1161#endif
1162
1163/*
1164 * Highest level string search function.
Bram Moolenaarb8017e72007-05-10 18:59:07 +00001165 * Search for the 'count'th occurrence of pattern 'pat' in direction 'dirc'
Bram Moolenaar071d4272004-06-13 20:20:40 +00001166 * If 'dirc' is 0: use previous dir.
1167 * If 'pat' is NULL or empty : use previous string.
1168 * If 'options & SEARCH_REV' : go in reverse of previous dir.
1169 * If 'options & SEARCH_ECHO': echo the search command and handle options
1170 * If 'options & SEARCH_MSG' : may give error message
1171 * If 'options & SEARCH_OPT' : interpret optional flags
1172 * If 'options & SEARCH_HIS' : put search pattern in history
1173 * If 'options & SEARCH_NOOF': don't add offset to position
1174 * If 'options & SEARCH_MARK': set previous context mark
1175 * If 'options & SEARCH_KEEP': keep previous search pattern
1176 * If 'options & SEARCH_START': accept match at curpos itself
1177 * If 'options & SEARCH_PEEK': check for typed char, cancel search
1178 *
1179 * Careful: If spats[0].off.line == TRUE and spats[0].off.off == 0 this
1180 * makes the movement linewise without moving the match position.
1181 *
Bram Moolenaarb6c27352015-03-05 19:57:49 +01001182 * Return 0 for failure, 1 for found, 2 for found and line offset added.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001183 */
1184 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001185do_search(
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001186 oparg_T *oap, // can be NULL
1187 int dirc, // '/' or '?'
Bram Moolenaarc036e872020-02-21 21:30:52 +01001188 int search_delim, // the delimiter for the search, e.g. '%' in s%regex%replacement%
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001189 char_u *pat,
1190 long count,
1191 int options,
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02001192 searchit_arg_T *sia) // optional arguments or NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001193{
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001194 pos_T pos; // position of the last match
Bram Moolenaar071d4272004-06-13 20:20:40 +00001195 char_u *searchstr;
Bram Moolenaarc3328162019-07-23 22:15:25 +02001196 soffset_T old_off;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001197 int retval; // Return value
Bram Moolenaar071d4272004-06-13 20:20:40 +00001198 char_u *p;
1199 long c;
1200 char_u *dircp;
1201 char_u *strcopy = NULL;
1202 char_u *ps;
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02001203 char_u *msgbuf = NULL;
1204 size_t len;
Bram Moolenaar8f46e4c2019-05-24 22:08:15 +02001205 int has_offset = FALSE;
Bram Moolenaarb6cb26f2019-05-07 21:34:37 +02001206#define SEARCH_STAT_BUF_LEN 12
Bram Moolenaar071d4272004-06-13 20:20:40 +00001207
1208 /*
1209 * A line offset is not remembered, this is vi compatible.
1210 */
1211 if (spats[0].off.line && vim_strchr(p_cpo, CPO_LINEOFF) != NULL)
1212 {
1213 spats[0].off.line = FALSE;
1214 spats[0].off.off = 0;
1215 }
1216
1217 /*
1218 * Save the values for when (options & SEARCH_KEEP) is used.
1219 * (there is no "if ()" around this because gcc wants them initialized)
1220 */
1221 old_off = spats[0].off;
1222
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001223 pos = curwin->w_cursor; // start searching at the cursor position
Bram Moolenaar071d4272004-06-13 20:20:40 +00001224
1225 /*
1226 * Find out the direction of the search.
1227 */
1228 if (dirc == 0)
1229 dirc = spats[0].off.dir;
1230 else
Bram Moolenaar8c8de832008-06-24 22:58:06 +00001231 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001232 spats[0].off.dir = dirc;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00001233#if defined(FEAT_EVAL)
1234 set_vv_searchforward();
1235#endif
1236 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001237 if (options & SEARCH_REV)
1238 {
Bram Moolenaar4f974752019-02-17 17:44:42 +01001239#ifdef MSWIN
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001240 // There is a bug in the Visual C++ 2.2 compiler which means that
1241 // dirc always ends up being '/'
Bram Moolenaar071d4272004-06-13 20:20:40 +00001242 dirc = (dirc == '/') ? '?' : '/';
1243#else
1244 if (dirc == '/')
1245 dirc = '?';
1246 else
1247 dirc = '/';
1248#endif
1249 }
1250
1251#ifdef FEAT_FOLDING
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001252 // If the cursor is in a closed fold, don't find another match in the same
1253 // fold.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254 if (dirc == '/')
1255 {
1256 if (hasFolding(pos.lnum, NULL, &pos.lnum))
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001257 pos.col = MAXCOL - 2; // avoid overflow when adding 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001258 }
1259 else
1260 {
1261 if (hasFolding(pos.lnum, &pos.lnum, NULL))
1262 pos.col = 0;
1263 }
1264#endif
1265
1266#ifdef FEAT_SEARCH_EXTRA
1267 /*
1268 * Turn 'hlsearch' highlighting back on.
1269 */
1270 if (no_hlsearch && !(options & SEARCH_KEEP))
1271 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00001272 redraw_all_later(SOME_VALID);
Bram Moolenaar451fc7b2018-04-27 22:53:07 +02001273 set_no_hlsearch(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001274 }
1275#endif
1276
1277 /*
1278 * Repeat the search when pattern followed by ';', e.g. "/foo/;?bar".
1279 */
1280 for (;;)
1281 {
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02001282 int show_top_bot_msg = FALSE;
Bram Moolenaarc7a10b32019-05-06 21:37:18 +02001283
Bram Moolenaar071d4272004-06-13 20:20:40 +00001284 searchstr = pat;
1285 dircp = NULL;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001286 // use previous pattern
Bram Moolenaarc036e872020-02-21 21:30:52 +01001287 if (pat == NULL || *pat == NUL || *pat == search_delim)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001288 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001289 if (spats[RE_SEARCH].pat == NULL) // no previous pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00001290 {
Bram Moolenaarea683da2016-09-09 21:41:34 +02001291 searchstr = spats[RE_SUBST].pat;
1292 if (searchstr == NULL)
Bram Moolenaarb4b0a082011-02-25 18:38:36 +01001293 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001294 emsg(_(e_noprevre));
Bram Moolenaarb4b0a082011-02-25 18:38:36 +01001295 retval = 0;
1296 goto end_do_search;
1297 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001298 }
Bram Moolenaarb4b0a082011-02-25 18:38:36 +01001299 else
1300 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001301 // make search_regcomp() use spats[RE_SEARCH].pat
Bram Moolenaarb4b0a082011-02-25 18:38:36 +01001302 searchstr = (char_u *)"";
1303 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001304 }
1305
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001306 if (pat != NULL && *pat != NUL) // look for (new) offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00001307 {
1308 /*
1309 * Find end of regular expression.
1310 * If there is a matching '/' or '?', toss it.
1311 */
1312 ps = strcopy;
Bram Moolenaare8c4abb2020-04-02 21:13:25 +02001313 p = skip_regexp_ex(pat, search_delim, (int)p_magic, &strcopy, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001314 if (strcopy != ps)
1315 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001316 // made a copy of "pat" to change "\?" to "?"
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00001317 searchcmdlen += (int)(STRLEN(pat) - STRLEN(strcopy));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001318 pat = strcopy;
1319 searchstr = strcopy;
1320 }
Bram Moolenaarc036e872020-02-21 21:30:52 +01001321 if (*p == search_delim)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001322 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001323 dircp = p; // remember where we put the NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324 *p++ = NUL;
1325 }
1326 spats[0].off.line = FALSE;
1327 spats[0].off.end = FALSE;
1328 spats[0].off.off = 0;
1329 /*
1330 * Check for a line offset or a character offset.
1331 * For get_address (echo off) we don't check for a character
1332 * offset, because it is meaningless and the 's' could be a
1333 * substitute command.
1334 */
1335 if (*p == '+' || *p == '-' || VIM_ISDIGIT(*p))
1336 spats[0].off.line = TRUE;
1337 else if ((options & SEARCH_OPT) &&
1338 (*p == 'e' || *p == 's' || *p == 'b'))
1339 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001340 if (*p == 'e') // end
Bram Moolenaar071d4272004-06-13 20:20:40 +00001341 spats[0].off.end = SEARCH_END;
1342 ++p;
1343 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001344 if (VIM_ISDIGIT(*p) || *p == '+' || *p == '-') // got an offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00001345 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001346 // 'nr' or '+nr' or '-nr'
Bram Moolenaar071d4272004-06-13 20:20:40 +00001347 if (VIM_ISDIGIT(*p) || VIM_ISDIGIT(*(p + 1)))
1348 spats[0].off.off = atol((char *)p);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001349 else if (*p == '-') // single '-'
Bram Moolenaar071d4272004-06-13 20:20:40 +00001350 spats[0].off.off = -1;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001351 else // single '+'
Bram Moolenaar071d4272004-06-13 20:20:40 +00001352 spats[0].off.off = 1;
1353 ++p;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001354 while (VIM_ISDIGIT(*p)) // skip number
Bram Moolenaar071d4272004-06-13 20:20:40 +00001355 ++p;
1356 }
1357
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001358 // compute length of search command for get_address()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001359 searchcmdlen += (int)(p - pat);
1360
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001361 pat = p; // put pat after search command
Bram Moolenaar071d4272004-06-13 20:20:40 +00001362 }
1363
Bram Moolenaar359ad1a2019-09-02 21:44:59 +02001364 if ((options & SEARCH_ECHO) && messaging() &&
1365 !msg_silent &&
1366 (!cmd_silent || !shortmess(SHM_SEARCHCOUNT)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001367 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001368 char_u *trunc;
Bram Moolenaar984f0312019-05-24 13:11:47 +02001369 char_u off_buf[40];
Bram Moolenaard33a7642019-05-24 17:56:14 +02001370 size_t off_len = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001371
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02001372 // Compute msg_row early.
1373 msg_start();
1374
Bram Moolenaar984f0312019-05-24 13:11:47 +02001375 // Get the offset, so we know how long it is.
Bram Moolenaar359ad1a2019-09-02 21:44:59 +02001376 if (!cmd_silent &&
1377 (spats[0].off.line || spats[0].off.end || spats[0].off.off))
Bram Moolenaar984f0312019-05-24 13:11:47 +02001378 {
1379 p = off_buf;
1380 *p++ = dirc;
1381 if (spats[0].off.end)
1382 *p++ = 'e';
1383 else if (!spats[0].off.line)
1384 *p++ = 's';
1385 if (spats[0].off.off > 0 || spats[0].off.line)
1386 *p++ = '+';
1387 *p = NUL;
1388 if (spats[0].off.off != 0 || spats[0].off.line)
1389 sprintf((char *)p, "%ld", spats[0].off.off);
1390 off_len = STRLEN(off_buf);
1391 }
1392
Bram Moolenaar071d4272004-06-13 20:20:40 +00001393 if (*searchstr == NUL)
Bram Moolenaar2fb8f682018-12-01 13:14:45 +01001394 p = spats[0].pat;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001395 else
1396 p = searchstr;
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02001397
Bram Moolenaar359ad1a2019-09-02 21:44:59 +02001398 if (!shortmess(SHM_SEARCHCOUNT) || cmd_silent)
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02001399 {
1400 // Reserve enough space for the search pattern + offset +
Bram Moolenaar984f0312019-05-24 13:11:47 +02001401 // search stat. Use all the space available, so that the
1402 // search state is right aligned. If there is not enough space
1403 // msg_strtrunc() will shorten in the middle.
Bram Moolenaar19e8ac72019-09-03 22:23:38 +02001404 if (msg_scrolled != 0 && !cmd_silent)
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02001405 // Use all the columns.
1406 len = (int)(Rows - msg_row) * Columns - 1;
1407 else
1408 // Use up to 'showcmd' column.
1409 len = (int)(Rows - msg_row - 1) * Columns + sc_col - 1;
Bram Moolenaar984f0312019-05-24 13:11:47 +02001410 if (len < STRLEN(p) + off_len + SEARCH_STAT_BUF_LEN + 3)
1411 len = STRLEN(p) + off_len + SEARCH_STAT_BUF_LEN + 3;
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02001412 }
1413 else
1414 // Reserve enough space for the search pattern + offset.
Bram Moolenaar984f0312019-05-24 13:11:47 +02001415 len = STRLEN(p) + off_len + 3;
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02001416
Bram Moolenaar880e4d92020-04-11 21:31:28 +02001417 vim_free(msgbuf);
Bram Moolenaar51e14382019-05-25 20:21:28 +02001418 msgbuf = alloc(len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001419 if (msgbuf != NULL)
1420 {
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02001421 vim_memset(msgbuf, ' ', len);
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02001422 msgbuf[len - 1] = NUL;
Bram Moolenaar359ad1a2019-09-02 21:44:59 +02001423 // do not fill the msgbuf buffer, if cmd_silent is set, leave it
1424 // empty for the search_stat feature.
1425 if (!cmd_silent)
Bram Moolenaarcafda4f2005-09-06 19:25:11 +00001426 {
Bram Moolenaar359ad1a2019-09-02 21:44:59 +02001427 msgbuf[0] = dirc;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001428
Bram Moolenaar359ad1a2019-09-02 21:44:59 +02001429 if (enc_utf8 && utf_iscomposing(utf_ptr2char(p)))
1430 {
1431 // Use a space to draw the composing char on.
1432 msgbuf[1] = ' ';
1433 mch_memmove(msgbuf + 2, p, STRLEN(p));
1434 }
1435 else
1436 mch_memmove(msgbuf + 1, p, STRLEN(p));
1437 if (off_len > 0)
1438 mch_memmove(msgbuf + STRLEN(p) + 1, off_buf, off_len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001439
Bram Moolenaar359ad1a2019-09-02 21:44:59 +02001440 trunc = msg_strtrunc(msgbuf, TRUE);
1441 if (trunc != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001442 {
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02001443 vim_free(msgbuf);
Bram Moolenaar359ad1a2019-09-02 21:44:59 +02001444 msgbuf = trunc;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001445 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001446
Bram Moolenaar359ad1a2019-09-02 21:44:59 +02001447 #ifdef FEAT_RIGHTLEFT
1448 // The search pattern could be shown on the right in rightleft
1449 // mode, but the 'ruler' and 'showcmd' area use it too, thus
1450 // it would be blanked out again very soon. Show it on the
1451 // left, but do reverse the text.
1452 if (curwin->w_p_rl && *curwin->w_p_rlc == 's')
1453 {
1454 char_u *r;
1455 size_t pat_len;
1456
1457 r = reverse_text(msgbuf);
1458 if (r != NULL)
1459 {
1460 vim_free(msgbuf);
1461 msgbuf = r;
1462 // move reversed text to beginning of buffer
1463 while (*r != NUL && *r == ' ')
1464 r++;
1465 pat_len = msgbuf + STRLEN(msgbuf) - r;
1466 mch_memmove(msgbuf, r, pat_len);
1467 // overwrite old text
1468 if ((size_t)(r - msgbuf) >= pat_len)
1469 vim_memset(r, ' ', pat_len);
1470 else
1471 vim_memset(msgbuf + pat_len, ' ', r - msgbuf);
1472 }
1473 }
1474 #endif
1475 msg_outtrans(msgbuf);
1476 msg_clr_eos();
1477 msg_check();
1478
1479 gotocmdline(FALSE);
1480 out_flush();
1481 msg_nowait = TRUE; // don't wait for this message
1482 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001483 }
1484 }
1485
1486 /*
1487 * If there is a character offset, subtract it from the current
1488 * position, so we don't get stuck at "?pat?e+2" or "/pat/s-2".
Bram Moolenaared203462004-06-16 11:19:22 +00001489 * Skip this if pos.col is near MAXCOL (closed fold).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001490 * This is not done for a line offset, because then we would not be vi
1491 * compatible.
1492 */
Bram Moolenaared203462004-06-16 11:19:22 +00001493 if (!spats[0].off.line && spats[0].off.off && pos.col < MAXCOL - 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001494 {
1495 if (spats[0].off.off > 0)
1496 {
1497 for (c = spats[0].off.off; c; --c)
1498 if (decl(&pos) == -1)
1499 break;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001500 if (c) // at start of buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00001501 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001502 pos.lnum = 0; // allow lnum == 0 here
Bram Moolenaar071d4272004-06-13 20:20:40 +00001503 pos.col = MAXCOL;
1504 }
1505 }
1506 else
1507 {
1508 for (c = spats[0].off.off; c; ++c)
1509 if (incl(&pos) == -1)
1510 break;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001511 if (c) // at end of buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00001512 {
1513 pos.lnum = curbuf->b_ml.ml_line_count + 1;
1514 pos.col = 0;
1515 }
1516 }
1517 }
1518
Bram Moolenaar14184a32019-02-16 15:10:30 +01001519 c = searchit(curwin, curbuf, &pos, NULL,
1520 dirc == '/' ? FORWARD : BACKWARD,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001521 searchstr, count, spats[0].off.end + (options &
1522 (SEARCH_KEEP + SEARCH_PEEK + SEARCH_HIS
1523 + SEARCH_MSG + SEARCH_START
1524 + ((pat != NULL && *pat == ';') ? 0 : SEARCH_NOOF))),
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02001525 RE_LAST, sia);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001526
1527 if (dircp != NULL)
Bram Moolenaarc036e872020-02-21 21:30:52 +01001528 *dircp = search_delim; // restore second '/' or '?' for normal_cmd()
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02001529
1530 if (!shortmess(SHM_SEARCH)
1531 && ((dirc == '/' && LT_POS(pos, curwin->w_cursor))
1532 || (dirc == '?' && LT_POS(curwin->w_cursor, pos))))
Bram Moolenaarc7a10b32019-05-06 21:37:18 +02001533 show_top_bot_msg = TRUE;
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02001534
Bram Moolenaar071d4272004-06-13 20:20:40 +00001535 if (c == FAIL)
1536 {
1537 retval = 0;
1538 goto end_do_search;
1539 }
1540 if (spats[0].off.end && oap != NULL)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001541 oap->inclusive = TRUE; // 'e' includes last character
Bram Moolenaar071d4272004-06-13 20:20:40 +00001542
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001543 retval = 1; // pattern found
Bram Moolenaar071d4272004-06-13 20:20:40 +00001544
1545 /*
1546 * Add character and/or line offset
1547 */
Bram Moolenaar9160f302006-08-29 15:58:12 +00001548 if (!(options & SEARCH_NOOF) || (pat != NULL && *pat == ';'))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001549 {
Bram Moolenaar8f46e4c2019-05-24 22:08:15 +02001550 pos_T org_pos = pos;
1551
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001552 if (spats[0].off.line) // Add the offset to the line number.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001553 {
1554 c = pos.lnum + spats[0].off.off;
1555 if (c < 1)
1556 pos.lnum = 1;
1557 else if (c > curbuf->b_ml.ml_line_count)
1558 pos.lnum = curbuf->b_ml.ml_line_count;
1559 else
1560 pos.lnum = c;
1561 pos.col = 0;
1562
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001563 retval = 2; // pattern found, line offset added
Bram Moolenaar071d4272004-06-13 20:20:40 +00001564 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001565 else if (pos.col < MAXCOL - 2) // just in case
Bram Moolenaar071d4272004-06-13 20:20:40 +00001566 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001567 // to the right, check for end of file
Bram Moolenaar8c8de832008-06-24 22:58:06 +00001568 c = spats[0].off.off;
1569 if (c > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001570 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00001571 while (c-- > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001572 if (incl(&pos) == -1)
1573 break;
1574 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001575 // to the left, check for start of file
Bram Moolenaar071d4272004-06-13 20:20:40 +00001576 else
1577 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00001578 while (c++ < 0)
1579 if (decl(&pos) == -1)
1580 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001581 }
1582 }
Bram Moolenaar8f46e4c2019-05-24 22:08:15 +02001583 if (!EQUAL_POS(pos, org_pos))
1584 has_offset = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001585 }
1586
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02001587 // Show [1/15] if 'S' is not in 'shortmess'.
1588 if ((options & SEARCH_ECHO)
1589 && messaging()
Bram Moolenaar359ad1a2019-09-02 21:44:59 +02001590 && !msg_silent
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02001591 && c != FAIL
1592 && !shortmess(SHM_SEARCHCOUNT)
1593 && msgbuf != NULL)
Bram Moolenaar8f46e4c2019-05-24 22:08:15 +02001594 search_stat(dirc, &pos, show_top_bot_msg, msgbuf,
Bram Moolenaar6cb07262020-05-29 22:49:43 +02001595 (count != 1 || has_offset
1596#ifdef FEAT_FOLDING
1597 || (!(fdo_flags & FDO_SEARCH) &&
1598 hasFolding(curwin->w_cursor.lnum, NULL, NULL))
1599#endif
1600 ));
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02001601
Bram Moolenaar071d4272004-06-13 20:20:40 +00001602 /*
1603 * The search command can be followed by a ';' to do another search.
1604 * For example: "/pat/;/foo/+3;?bar"
1605 * This is like doing another search command, except:
1606 * - The remembered direction '/' or '?' is from the first search.
1607 * - When an error happens the cursor isn't moved at all.
1608 * Don't do this when called by get_address() (it handles ';' itself).
1609 */
1610 if (!(options & SEARCH_OPT) || pat == NULL || *pat != ';')
1611 break;
1612
1613 dirc = *++pat;
Bram Moolenaarc036e872020-02-21 21:30:52 +01001614 search_delim = dirc;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001615 if (dirc != '?' && dirc != '/')
1616 {
1617 retval = 0;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001618 emsg(_("E386: Expected '?' or '/' after ';'"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001619 goto end_do_search;
1620 }
1621 ++pat;
1622 }
1623
1624 if (options & SEARCH_MARK)
1625 setpcmark();
1626 curwin->w_cursor = pos;
1627 curwin->w_set_curswant = TRUE;
1628
1629end_do_search:
Bram Moolenaarac8400d2014-01-14 21:31:34 +01001630 if ((options & SEARCH_KEEP) || cmdmod.keeppatterns)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001631 spats[0].off = old_off;
1632 vim_free(strcopy);
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02001633 vim_free(msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001634
1635 return retval;
1636}
1637
Bram Moolenaar071d4272004-06-13 20:20:40 +00001638/*
1639 * search_for_exact_line(buf, pos, dir, pat)
1640 *
1641 * Search for a line starting with the given pattern (ignoring leading
Bram Moolenaar8ad80de2017-06-05 16:01:59 +02001642 * white-space), starting from pos and going in direction "dir". "pos" will
Bram Moolenaar071d4272004-06-13 20:20:40 +00001643 * contain the position of the match found. Blank lines match only if
Bram Moolenaar8ad80de2017-06-05 16:01:59 +02001644 * ADDING is set. If p_ic is set then the pattern must be in lowercase.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001645 * Return OK for success, or FAIL if no line found.
1646 */
1647 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001648search_for_exact_line(
1649 buf_T *buf,
1650 pos_T *pos,
1651 int dir,
1652 char_u *pat)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001653{
1654 linenr_T start = 0;
1655 char_u *ptr;
1656 char_u *p;
1657
1658 if (buf->b_ml.ml_line_count == 0)
1659 return FAIL;
1660 for (;;)
1661 {
1662 pos->lnum += dir;
1663 if (pos->lnum < 1)
1664 {
1665 if (p_ws)
1666 {
1667 pos->lnum = buf->b_ml.ml_line_count;
1668 if (!shortmess(SHM_SEARCH))
1669 give_warning((char_u *)_(top_bot_msg), TRUE);
1670 }
1671 else
1672 {
1673 pos->lnum = 1;
1674 break;
1675 }
1676 }
1677 else if (pos->lnum > buf->b_ml.ml_line_count)
1678 {
1679 if (p_ws)
1680 {
1681 pos->lnum = 1;
1682 if (!shortmess(SHM_SEARCH))
1683 give_warning((char_u *)_(bot_top_msg), TRUE);
1684 }
1685 else
1686 {
1687 pos->lnum = 1;
1688 break;
1689 }
1690 }
1691 if (pos->lnum == start)
1692 break;
1693 if (start == 0)
1694 start = pos->lnum;
1695 ptr = ml_get_buf(buf, pos->lnum, FALSE);
1696 p = skipwhite(ptr);
1697 pos->col = (colnr_T) (p - ptr);
1698
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001699 // when adding lines the matching line may be empty but it is not
1700 // ignored because we are interested in the next line -- Acevedo
Bram Moolenaar4be06f92005-07-29 22:36:03 +00001701 if ((compl_cont_status & CONT_ADDING)
1702 && !(compl_cont_status & CONT_SOL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001703 {
1704 if ((p_ic ? MB_STRICMP(p, pat) : STRCMP(p, pat)) == 0)
1705 return OK;
1706 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001707 else if (*p != NUL) // ignore empty lines
1708 { // expanding lines or words
Bram Moolenaar4be06f92005-07-29 22:36:03 +00001709 if ((p_ic ? MB_STRNICMP(p, pat, compl_length)
1710 : STRNCMP(p, pat, compl_length)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001711 return OK;
1712 }
1713 }
1714 return FAIL;
1715}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001716
1717/*
1718 * Character Searches
1719 */
1720
1721/*
1722 * Search for a character in a line. If "t_cmd" is FALSE, move to the
1723 * position of the character, otherwise move to just before the char.
1724 * Do this "cap->count1" times.
1725 * Return FAIL or OK.
1726 */
1727 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001728searchc(cmdarg_T *cap, int t_cmd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001729{
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001730 int c = cap->nchar; // char to search for
1731 int dir = cap->arg; // TRUE for searching forward
1732 long count = cap->count1; // repeat count
Bram Moolenaar071d4272004-06-13 20:20:40 +00001733 int col;
1734 char_u *p;
1735 int len;
Bram Moolenaar8b3e0332011-06-26 05:36:34 +02001736 int stop = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001737
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001738 if (c != NUL) // normal search: remember args for repeat
Bram Moolenaar071d4272004-06-13 20:20:40 +00001739 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001740 if (!KeyStuffed) // don't remember when redoing
Bram Moolenaar071d4272004-06-13 20:20:40 +00001741 {
Bram Moolenaardbd24b52015-08-11 14:26:19 +02001742 *lastc = c;
1743 set_csearch_direction(dir);
1744 set_csearch_until(t_cmd);
Bram Moolenaardbd24b52015-08-11 14:26:19 +02001745 lastc_bytelen = (*mb_char2bytes)(c, lastc_bytes);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001746 if (cap->ncharC1 != 0)
1747 {
Bram Moolenaardbd24b52015-08-11 14:26:19 +02001748 lastc_bytelen += (*mb_char2bytes)(cap->ncharC1,
1749 lastc_bytes + lastc_bytelen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001750 if (cap->ncharC2 != 0)
Bram Moolenaardbd24b52015-08-11 14:26:19 +02001751 lastc_bytelen += (*mb_char2bytes)(cap->ncharC2,
1752 lastc_bytes + lastc_bytelen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001753 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001754 }
1755 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001756 else // repeat previous search
Bram Moolenaar071d4272004-06-13 20:20:40 +00001757 {
Bram Moolenaar264b74f2019-01-24 17:18:42 +01001758 if (*lastc == NUL && lastc_bytelen == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001759 return FAIL;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001760 if (dir) // repeat in opposite direction
Bram Moolenaar071d4272004-06-13 20:20:40 +00001761 dir = -lastcdir;
1762 else
1763 dir = lastcdir;
1764 t_cmd = last_t_cmd;
Bram Moolenaardbd24b52015-08-11 14:26:19 +02001765 c = *lastc;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001766 // For multi-byte re-use last lastc_bytes[] and lastc_bytelen.
Bram Moolenaar8b3e0332011-06-26 05:36:34 +02001767
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001768 // Force a move of at least one char, so ";" and "," will move the
1769 // cursor, even if the cursor is right in front of char we are looking
1770 // at.
Bram Moolenaar19fd09a2011-07-15 13:21:30 +02001771 if (vim_strchr(p_cpo, CPO_SCOLON) == NULL && count == 1 && t_cmd)
Bram Moolenaar8b3e0332011-06-26 05:36:34 +02001772 stop = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001773 }
1774
Bram Moolenaar60a795a2005-09-16 21:55:43 +00001775 if (dir == BACKWARD)
1776 cap->oap->inclusive = FALSE;
1777 else
1778 cap->oap->inclusive = TRUE;
1779
Bram Moolenaar071d4272004-06-13 20:20:40 +00001780 p = ml_get_curline();
1781 col = curwin->w_cursor.col;
1782 len = (int)STRLEN(p);
1783
1784 while (count--)
1785 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001786 if (has_mbyte)
1787 {
1788 for (;;)
1789 {
1790 if (dir > 0)
1791 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00001792 col += (*mb_ptr2len)(p + col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001793 if (col >= len)
1794 return FAIL;
1795 }
1796 else
1797 {
1798 if (col == 0)
1799 return FAIL;
1800 col -= (*mb_head_off)(p, p + col - 1) + 1;
1801 }
Bram Moolenaardbd24b52015-08-11 14:26:19 +02001802 if (lastc_bytelen == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001803 {
Bram Moolenaar8b3e0332011-06-26 05:36:34 +02001804 if (p[col] == c && stop)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001805 break;
1806 }
Bram Moolenaar66727e12017-03-01 22:17:05 +01001807 else if (STRNCMP(p + col, lastc_bytes, lastc_bytelen) == 0
Bram Moolenaarb129a442016-12-01 17:25:20 +01001808 && stop)
Bram Moolenaar66727e12017-03-01 22:17:05 +01001809 break;
Bram Moolenaar8b3e0332011-06-26 05:36:34 +02001810 stop = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811 }
1812 }
1813 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001814 {
1815 for (;;)
1816 {
1817 if ((col += dir) < 0 || col >= len)
1818 return FAIL;
Bram Moolenaar8b3e0332011-06-26 05:36:34 +02001819 if (p[col] == c && stop)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001820 break;
Bram Moolenaar8b3e0332011-06-26 05:36:34 +02001821 stop = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001822 }
1823 }
1824 }
1825
1826 if (t_cmd)
1827 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001828 // backup to before the character (possibly double-byte)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001829 col -= dir;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001830 if (has_mbyte)
1831 {
1832 if (dir < 0)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001833 // Landed on the search char which is lastc_bytelen long
Bram Moolenaardbd24b52015-08-11 14:26:19 +02001834 col += lastc_bytelen - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001835 else
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001836 // To previous char, which may be multi-byte.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001837 col -= (*mb_head_off)(p, p + col);
1838 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001839 }
1840 curwin->w_cursor.col = col;
1841
1842 return OK;
1843}
1844
1845/*
1846 * "Other" Searches
1847 */
1848
1849/*
1850 * findmatch - find the matching paren or brace
1851 *
1852 * Improvement over vi: Braces inside quotes are ignored.
1853 */
1854 pos_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001855findmatch(oparg_T *oap, int initc)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001856{
1857 return findmatchlimit(oap, initc, 0, 0);
1858}
1859
1860/*
1861 * Return TRUE if the character before "linep[col]" equals "ch".
1862 * Return FALSE if "col" is zero.
1863 * Update "*prevcol" to the column of the previous character, unless "prevcol"
1864 * is NULL.
1865 * Handles multibyte string correctly.
1866 */
1867 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001868check_prevcol(
1869 char_u *linep,
1870 int col,
1871 int ch,
1872 int *prevcol)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001873{
1874 --col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001875 if (col > 0 && has_mbyte)
1876 col -= (*mb_head_off)(linep, linep + col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001877 if (prevcol)
1878 *prevcol = col;
1879 return (col >= 0 && linep[col] == ch) ? TRUE : FALSE;
1880}
1881
Bram Moolenaarf7bb86d2015-07-28 21:17:36 +02001882/*
1883 * Raw string start is found at linep[startpos.col - 1].
1884 * Return TRUE if the matching end can be found between startpos and endpos.
1885 */
1886 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001887find_rawstring_end(char_u *linep, pos_T *startpos, pos_T *endpos)
Bram Moolenaarf7bb86d2015-07-28 21:17:36 +02001888{
1889 char_u *p;
1890 char_u *delim_copy;
1891 size_t delim_len;
1892 linenr_T lnum;
1893 int found = FALSE;
1894
1895 for (p = linep + startpos->col + 1; *p && *p != '('; ++p)
1896 ;
1897 delim_len = (p - linep) - startpos->col - 1;
Bram Moolenaar6ed535d2015-08-26 23:01:21 +02001898 delim_copy = vim_strnsave(linep + startpos->col + 1, (int)delim_len);
Bram Moolenaarf7bb86d2015-07-28 21:17:36 +02001899 if (delim_copy == NULL)
1900 return FALSE;
1901 for (lnum = startpos->lnum; lnum <= endpos->lnum; ++lnum)
1902 {
1903 char_u *line = ml_get(lnum);
1904
1905 for (p = line + (lnum == startpos->lnum
1906 ? startpos->col + 1 : 0); *p; ++p)
1907 {
1908 if (lnum == endpos->lnum && (colnr_T)(p - line) >= endpos->col)
1909 break;
1910 if (*p == ')' && p[delim_len + 1] == '"'
1911 && STRNCMP(delim_copy, p + 1, delim_len) == 0)
1912 {
1913 found = TRUE;
1914 break;
1915 }
1916 }
1917 if (found)
1918 break;
1919 }
1920 vim_free(delim_copy);
1921 return found;
1922}
1923
Bram Moolenaar071d4272004-06-13 20:20:40 +00001924/*
Bram Moolenaar556ae8e2019-11-21 22:27:22 +01001925 * Check matchpairs option for "*initc".
1926 * If there is a match set "*initc" to the matching character and "*findc" to
1927 * the opposite character. Set "*backwards" to the direction.
1928 * When "switchit" is TRUE swap the direction.
1929 */
1930 static void
1931find_mps_values(
1932 int *initc,
1933 int *findc,
1934 int *backwards,
1935 int switchit)
1936{
1937 char_u *ptr;
1938
1939 ptr = curbuf->b_p_mps;
1940 while (*ptr != NUL)
1941 {
1942 if (has_mbyte)
1943 {
1944 char_u *prev;
1945
1946 if (mb_ptr2char(ptr) == *initc)
1947 {
1948 if (switchit)
1949 {
1950 *findc = *initc;
1951 *initc = mb_ptr2char(ptr + mb_ptr2len(ptr) + 1);
1952 *backwards = TRUE;
1953 }
1954 else
1955 {
1956 *findc = mb_ptr2char(ptr + mb_ptr2len(ptr) + 1);
1957 *backwards = FALSE;
1958 }
1959 return;
1960 }
1961 prev = ptr;
1962 ptr += mb_ptr2len(ptr) + 1;
1963 if (mb_ptr2char(ptr) == *initc)
1964 {
1965 if (switchit)
1966 {
1967 *findc = *initc;
1968 *initc = mb_ptr2char(prev);
1969 *backwards = FALSE;
1970 }
1971 else
1972 {
1973 *findc = mb_ptr2char(prev);
1974 *backwards = TRUE;
1975 }
1976 return;
1977 }
1978 ptr += mb_ptr2len(ptr);
1979 }
1980 else
1981 {
1982 if (*ptr == *initc)
1983 {
1984 if (switchit)
1985 {
1986 *backwards = TRUE;
1987 *findc = *initc;
1988 *initc = ptr[2];
1989 }
1990 else
1991 {
1992 *backwards = FALSE;
1993 *findc = ptr[2];
1994 }
1995 return;
1996 }
1997 ptr += 2;
1998 if (*ptr == *initc)
1999 {
2000 if (switchit)
2001 {
2002 *backwards = FALSE;
2003 *findc = *initc;
2004 *initc = ptr[-2];
2005 }
2006 else
2007 {
2008 *backwards = TRUE;
2009 *findc = ptr[-2];
2010 }
2011 return;
2012 }
2013 ++ptr;
2014 }
2015 if (*ptr == ',')
2016 ++ptr;
2017 }
2018}
2019
2020/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002021 * findmatchlimit -- find the matching paren or brace, if it exists within
Bram Moolenaarf7bb86d2015-07-28 21:17:36 +02002022 * maxtravel lines of the cursor. A maxtravel of 0 means search until falling
2023 * off the edge of the file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002024 *
2025 * "initc" is the character to find a match for. NUL means to find the
Bram Moolenaarf7bb86d2015-07-28 21:17:36 +02002026 * character at or after the cursor. Special values:
2027 * '*' look for C-style comment / *
2028 * '/' look for C-style comment / *, ignoring comment-end
2029 * '#' look for preprocessor directives
2030 * 'R' look for raw string start: R"delim(text)delim" (only backwards)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002031 *
2032 * flags: FM_BACKWARD search backwards (when initc is '/', '*' or '#')
2033 * FM_FORWARD search forwards (when initc is '/', '*' or '#')
2034 * FM_BLOCKSTOP stop at start/end of block ({ or } in column 0)
2035 * FM_SKIPCOMM skip comments (not implemented yet!)
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002036 *
Bram Moolenaarf7bb86d2015-07-28 21:17:36 +02002037 * "oap" is only used to set oap->motion_type for a linewise motion, it can be
Bram Moolenaarf75a9632005-09-13 21:20:47 +00002038 * NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002039 */
2040
2041 pos_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002042findmatchlimit(
2043 oparg_T *oap,
2044 int initc,
2045 int flags,
2046 int maxtravel)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002047{
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002048 static pos_T pos; // current search position
2049 int findc = 0; // matching brace
Bram Moolenaar071d4272004-06-13 20:20:40 +00002050 int c;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002051 int count = 0; // cumulative number of braces
2052 int backwards = FALSE; // init for gcc
2053 int raw_string = FALSE; // search for raw string
2054 int inquote = FALSE; // TRUE when inside quotes
2055 char_u *linep; // pointer to current line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002056 char_u *ptr;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002057 int do_quotes; // check for quotes in current line
2058 int at_start; // do_quotes value at start position
2059 int hash_dir = 0; // Direction searched for # things
2060 int comment_dir = 0; // Direction searched for comments
2061 pos_T match_pos; // Where last slash-star was found
2062 int start_in_quotes; // start position is in quotes
2063 int traveled = 0; // how far we've searched so far
2064 int ignore_cend = FALSE; // ignore comment end
2065 int cpo_match; // vi compatible matching
2066 int cpo_bsl; // don't recognize backslashes
2067 int match_escaped = 0; // search for escaped match
2068 int dir; // Direction to search
2069 int comment_col = MAXCOL; // start of / / comment
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002070#ifdef FEAT_LISP
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002071 int lispcomm = FALSE; // inside of Lisp-style comment
2072 int lisp = curbuf->b_p_lisp; // engage Lisp-specific hacks ;)
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002073#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002074
2075 pos = curwin->w_cursor;
Bram Moolenaarc56c4592013-08-14 17:45:29 +02002076 pos.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002077 linep = ml_get(pos.lnum);
2078
2079 cpo_match = (vim_strchr(p_cpo, CPO_MATCH) != NULL);
2080 cpo_bsl = (vim_strchr(p_cpo, CPO_MATCHBSL) != NULL);
2081
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002082 // Direction to search when initc is '/', '*' or '#'
Bram Moolenaar071d4272004-06-13 20:20:40 +00002083 if (flags & FM_BACKWARD)
2084 dir = BACKWARD;
2085 else if (flags & FM_FORWARD)
2086 dir = FORWARD;
2087 else
2088 dir = 0;
2089
2090 /*
2091 * if initc given, look in the table for the matching character
2092 * '/' and '*' are special cases: look for start or end of comment.
2093 * When '/' is used, we ignore running backwards into an star-slash, for
2094 * "[*" command, we just want to find any comment.
2095 */
Bram Moolenaarf7bb86d2015-07-28 21:17:36 +02002096 if (initc == '/' || initc == '*' || initc == 'R')
Bram Moolenaar071d4272004-06-13 20:20:40 +00002097 {
2098 comment_dir = dir;
2099 if (initc == '/')
2100 ignore_cend = TRUE;
2101 backwards = (dir == FORWARD) ? FALSE : TRUE;
Bram Moolenaarf7bb86d2015-07-28 21:17:36 +02002102 raw_string = (initc == 'R');
Bram Moolenaar071d4272004-06-13 20:20:40 +00002103 initc = NUL;
2104 }
2105 else if (initc != '#' && initc != NUL)
2106 {
Bram Moolenaar8c7694a2013-01-17 17:02:05 +01002107 find_mps_values(&initc, &findc, &backwards, TRUE);
2108 if (findc == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002109 return NULL;
2110 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002111 else
2112 {
Bram Moolenaarf7bb86d2015-07-28 21:17:36 +02002113 /*
2114 * Either initc is '#', or no initc was given and we need to look
2115 * under the cursor.
2116 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002117 if (initc == '#')
2118 {
2119 hash_dir = dir;
2120 }
2121 else
2122 {
2123 /*
2124 * initc was not given, must look for something to match under
2125 * or near the cursor.
2126 * Only check for special things when 'cpo' doesn't have '%'.
2127 */
2128 if (!cpo_match)
2129 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002130 // Are we before or at #if, #else etc.?
Bram Moolenaar071d4272004-06-13 20:20:40 +00002131 ptr = skipwhite(linep);
2132 if (*ptr == '#' && pos.col <= (colnr_T)(ptr - linep))
2133 {
2134 ptr = skipwhite(ptr + 1);
2135 if ( STRNCMP(ptr, "if", 2) == 0
2136 || STRNCMP(ptr, "endif", 5) == 0
2137 || STRNCMP(ptr, "el", 2) == 0)
2138 hash_dir = 1;
2139 }
2140
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002141 // Are we on a comment?
Bram Moolenaar071d4272004-06-13 20:20:40 +00002142 else if (linep[pos.col] == '/')
2143 {
2144 if (linep[pos.col + 1] == '*')
2145 {
2146 comment_dir = FORWARD;
2147 backwards = FALSE;
2148 pos.col++;
2149 }
2150 else if (pos.col > 0 && linep[pos.col - 1] == '*')
2151 {
2152 comment_dir = BACKWARD;
2153 backwards = TRUE;
2154 pos.col--;
2155 }
2156 }
2157 else if (linep[pos.col] == '*')
2158 {
2159 if (linep[pos.col + 1] == '/')
2160 {
2161 comment_dir = BACKWARD;
2162 backwards = TRUE;
2163 }
2164 else if (pos.col > 0 && linep[pos.col - 1] == '/')
2165 {
2166 comment_dir = FORWARD;
2167 backwards = FALSE;
2168 }
2169 }
2170 }
2171
2172 /*
2173 * If we are not on a comment or the # at the start of a line, then
2174 * look for brace anywhere on this line after the cursor.
2175 */
2176 if (!hash_dir && !comment_dir)
2177 {
2178 /*
2179 * Find the brace under or after the cursor.
2180 * If beyond the end of the line, use the last character in
2181 * the line.
2182 */
2183 if (linep[pos.col] == NUL && pos.col)
2184 --pos.col;
2185 for (;;)
2186 {
Bram Moolenaar8c7694a2013-01-17 17:02:05 +01002187 initc = PTR2CHAR(linep + pos.col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002188 if (initc == NUL)
2189 break;
2190
Bram Moolenaar8c7694a2013-01-17 17:02:05 +01002191 find_mps_values(&initc, &findc, &backwards, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002192 if (findc)
2193 break;
Bram Moolenaar1614a142019-10-06 22:00:13 +02002194 pos.col += mb_ptr2len(linep + pos.col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002195 }
2196 if (!findc)
2197 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002198 // no brace in the line, maybe use " #if" then
Bram Moolenaar071d4272004-06-13 20:20:40 +00002199 if (!cpo_match && *skipwhite(linep) == '#')
2200 hash_dir = 1;
2201 else
2202 return NULL;
2203 }
2204 else if (!cpo_bsl)
2205 {
2206 int col, bslcnt = 0;
2207
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002208 // Set "match_escaped" if there are an odd number of
2209 // backslashes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002210 for (col = pos.col; check_prevcol(linep, col, '\\', &col);)
2211 bslcnt++;
2212 match_escaped = (bslcnt & 1);
2213 }
2214 }
2215 }
2216 if (hash_dir)
2217 {
2218 /*
2219 * Look for matching #if, #else, #elif, or #endif
2220 */
2221 if (oap != NULL)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002222 oap->motion_type = MLINE; // Linewise for this case only
Bram Moolenaar071d4272004-06-13 20:20:40 +00002223 if (initc != '#')
2224 {
2225 ptr = skipwhite(skipwhite(linep) + 1);
2226 if (STRNCMP(ptr, "if", 2) == 0 || STRNCMP(ptr, "el", 2) == 0)
2227 hash_dir = 1;
2228 else if (STRNCMP(ptr, "endif", 5) == 0)
2229 hash_dir = -1;
2230 else
2231 return NULL;
2232 }
2233 pos.col = 0;
2234 while (!got_int)
2235 {
2236 if (hash_dir > 0)
2237 {
2238 if (pos.lnum == curbuf->b_ml.ml_line_count)
2239 break;
2240 }
2241 else if (pos.lnum == 1)
2242 break;
2243 pos.lnum += hash_dir;
2244 linep = ml_get(pos.lnum);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002245 line_breakcheck(); // check for CTRL-C typed
Bram Moolenaar071d4272004-06-13 20:20:40 +00002246 ptr = skipwhite(linep);
2247 if (*ptr != '#')
2248 continue;
2249 pos.col = (colnr_T) (ptr - linep);
2250 ptr = skipwhite(ptr + 1);
2251 if (hash_dir > 0)
2252 {
2253 if (STRNCMP(ptr, "if", 2) == 0)
2254 count++;
2255 else if (STRNCMP(ptr, "el", 2) == 0)
2256 {
2257 if (count == 0)
2258 return &pos;
2259 }
2260 else if (STRNCMP(ptr, "endif", 5) == 0)
2261 {
2262 if (count == 0)
2263 return &pos;
2264 count--;
2265 }
2266 }
2267 else
2268 {
2269 if (STRNCMP(ptr, "if", 2) == 0)
2270 {
2271 if (count == 0)
2272 return &pos;
2273 count--;
2274 }
2275 else if (initc == '#' && STRNCMP(ptr, "el", 2) == 0)
2276 {
2277 if (count == 0)
2278 return &pos;
2279 }
2280 else if (STRNCMP(ptr, "endif", 5) == 0)
2281 count++;
2282 }
2283 }
2284 return NULL;
2285 }
2286 }
2287
2288#ifdef FEAT_RIGHTLEFT
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002289 // This is just guessing: when 'rightleft' is set, search for a matching
2290 // paren/brace in the other direction.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002291 if (curwin->w_p_rl && vim_strchr((char_u *)"()[]{}<>", initc) != NULL)
2292 backwards = !backwards;
2293#endif
2294
2295 do_quotes = -1;
2296 start_in_quotes = MAYBE;
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01002297 CLEAR_POS(&match_pos);
Bram Moolenaarfd2ac762006-03-01 22:09:21 +00002298
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002299 // backward search: Check if this line contains a single-line comment
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002300 if ((backwards && comment_dir)
2301#ifdef FEAT_LISP
2302 || lisp
2303#endif
2304 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002305 comment_col = check_linecomment(linep);
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002306#ifdef FEAT_LISP
2307 if (lisp && comment_col != MAXCOL && pos.col > (colnr_T)comment_col)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002308 lispcomm = TRUE; // find match inside this comment
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002309#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002310 while (!got_int)
2311 {
2312 /*
2313 * Go to the next position, forward or backward. We could use
2314 * inc() and dec() here, but that is much slower
2315 */
2316 if (backwards)
2317 {
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002318#ifdef FEAT_LISP
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002319 // char to match is inside of comment, don't search outside
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002320 if (lispcomm && pos.col < (colnr_T)comment_col)
2321 break;
2322#endif
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002323 if (pos.col == 0) // at start of line, go to prev. one
Bram Moolenaar071d4272004-06-13 20:20:40 +00002324 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002325 if (pos.lnum == 1) // start of file
Bram Moolenaar071d4272004-06-13 20:20:40 +00002326 break;
2327 --pos.lnum;
2328
Bram Moolenaar9e54a0e2006-04-14 20:42:25 +00002329 if (maxtravel > 0 && ++traveled > maxtravel)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002330 break;
2331
2332 linep = ml_get(pos.lnum);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002333 pos.col = (colnr_T)STRLEN(linep); // pos.col on trailing NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002334 do_quotes = -1;
2335 line_breakcheck();
2336
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002337 // Check if this line contains a single-line comment
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002338 if (comment_dir
2339#ifdef FEAT_LISP
2340 || lisp
2341#endif
2342 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002343 comment_col = check_linecomment(linep);
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002344#ifdef FEAT_LISP
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002345 // skip comment
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002346 if (lisp && comment_col != MAXCOL)
2347 pos.col = comment_col;
2348#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002349 }
2350 else
2351 {
2352 --pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002353 if (has_mbyte)
2354 pos.col -= (*mb_head_off)(linep, linep + pos.col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002355 }
2356 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002357 else // forward search
Bram Moolenaar071d4272004-06-13 20:20:40 +00002358 {
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002359 if (linep[pos.col] == NUL
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002360 // at end of line, go to next one
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002361#ifdef FEAT_LISP
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002362 // don't search for match in comment
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002363 || (lisp && comment_col != MAXCOL
2364 && pos.col == (colnr_T)comment_col)
2365#endif
2366 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002367 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002368 if (pos.lnum == curbuf->b_ml.ml_line_count // end of file
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002369#ifdef FEAT_LISP
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002370 // line is exhausted and comment with it,
2371 // don't search for match in code
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002372 || lispcomm
2373#endif
2374 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002375 break;
2376 ++pos.lnum;
2377
2378 if (maxtravel && traveled++ > maxtravel)
2379 break;
2380
2381 linep = ml_get(pos.lnum);
2382 pos.col = 0;
2383 do_quotes = -1;
2384 line_breakcheck();
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002385#ifdef FEAT_LISP
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002386 if (lisp) // find comment pos in new line
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002387 comment_col = check_linecomment(linep);
2388#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002389 }
2390 else
2391 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002392 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00002393 pos.col += (*mb_ptr2len)(linep + pos.col);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002394 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002395 ++pos.col;
2396 }
2397 }
2398
2399 /*
2400 * If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0.
2401 */
2402 if (pos.col == 0 && (flags & FM_BLOCKSTOP) &&
2403 (linep[0] == '{' || linep[0] == '}'))
2404 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002405 if (linep[0] == findc && count == 0) // match!
Bram Moolenaar071d4272004-06-13 20:20:40 +00002406 return &pos;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002407 break; // out of scope
Bram Moolenaar071d4272004-06-13 20:20:40 +00002408 }
2409
2410 if (comment_dir)
2411 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002412 // Note: comments do not nest, and we ignore quotes in them
2413 // TODO: ignore comment brackets inside strings
Bram Moolenaar071d4272004-06-13 20:20:40 +00002414 if (comment_dir == FORWARD)
2415 {
2416 if (linep[pos.col] == '*' && linep[pos.col + 1] == '/')
2417 {
2418 pos.col++;
2419 return &pos;
2420 }
2421 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002422 else // Searching backwards
Bram Moolenaar071d4272004-06-13 20:20:40 +00002423 {
2424 /*
2425 * A comment may contain / * or / /, it may also start or end
Bram Moolenaarf8c53d32017-11-12 15:36:38 +01002426 * with / * /. Ignore a / * after / / and after *.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002427 */
2428 if (pos.col == 0)
2429 continue;
Bram Moolenaarf7bb86d2015-07-28 21:17:36 +02002430 else if (raw_string)
2431 {
2432 if (linep[pos.col - 1] == 'R'
2433 && linep[pos.col] == '"'
2434 && vim_strchr(linep + pos.col + 1, '(') != NULL)
2435 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002436 // Possible start of raw string. Now that we have the
2437 // delimiter we can check if it ends before where we
2438 // started searching, or before the previously found
2439 // raw string start.
Bram Moolenaarf7bb86d2015-07-28 21:17:36 +02002440 if (!find_rawstring_end(linep, &pos,
2441 count > 0 ? &match_pos : &curwin->w_cursor))
2442 {
2443 count++;
2444 match_pos = pos;
2445 match_pos.col--;
2446 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002447 linep = ml_get(pos.lnum); // may have been released
Bram Moolenaarf7bb86d2015-07-28 21:17:36 +02002448 }
2449 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002450 else if ( linep[pos.col - 1] == '/'
2451 && linep[pos.col] == '*'
Bram Moolenaarf8c53d32017-11-12 15:36:38 +01002452 && (pos.col == 1 || linep[pos.col - 2] != '*')
Bram Moolenaar071d4272004-06-13 20:20:40 +00002453 && (int)pos.col < comment_col)
2454 {
2455 count++;
2456 match_pos = pos;
2457 match_pos.col--;
2458 }
2459 else if (linep[pos.col - 1] == '*' && linep[pos.col] == '/')
2460 {
2461 if (count > 0)
2462 pos = match_pos;
2463 else if (pos.col > 1 && linep[pos.col - 2] == '/'
2464 && (int)pos.col <= comment_col)
2465 pos.col -= 2;
2466 else if (ignore_cend)
2467 continue;
2468 else
2469 return NULL;
2470 return &pos;
2471 }
2472 }
2473 continue;
2474 }
2475
2476 /*
2477 * If smart matching ('cpoptions' does not contain '%'), braces inside
2478 * of quotes are ignored, but only if there is an even number of
2479 * quotes in the line.
2480 */
2481 if (cpo_match)
2482 do_quotes = 0;
2483 else if (do_quotes == -1)
2484 {
2485 /*
2486 * Count the number of quotes in the line, skipping \" and '"'.
2487 * Watch out for "\\".
2488 */
2489 at_start = do_quotes;
2490 for (ptr = linep; *ptr; ++ptr)
2491 {
2492 if (ptr == linep + pos.col + backwards)
2493 at_start = (do_quotes & 1);
2494 if (*ptr == '"'
2495 && (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\''))
2496 ++do_quotes;
2497 if (*ptr == '\\' && ptr[1] != NUL)
2498 ++ptr;
2499 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002500 do_quotes &= 1; // result is 1 with even number of quotes
Bram Moolenaar071d4272004-06-13 20:20:40 +00002501
2502 /*
2503 * If we find an uneven count, check current line and previous
2504 * one for a '\' at the end.
2505 */
2506 if (!do_quotes)
2507 {
2508 inquote = FALSE;
2509 if (ptr[-1] == '\\')
2510 {
2511 do_quotes = 1;
2512 if (start_in_quotes == MAYBE)
2513 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002514 // Do we need to use at_start here?
Bram Moolenaar071d4272004-06-13 20:20:40 +00002515 inquote = TRUE;
2516 start_in_quotes = TRUE;
2517 }
2518 else if (backwards)
2519 inquote = TRUE;
2520 }
2521 if (pos.lnum > 1)
2522 {
2523 ptr = ml_get(pos.lnum - 1);
2524 if (*ptr && *(ptr + STRLEN(ptr) - 1) == '\\')
2525 {
2526 do_quotes = 1;
2527 if (start_in_quotes == MAYBE)
2528 {
2529 inquote = at_start;
2530 if (inquote)
2531 start_in_quotes = TRUE;
2532 }
2533 else if (!backwards)
2534 inquote = TRUE;
2535 }
Bram Moolenaaraec11792007-07-10 11:09:36 +00002536
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002537 // ml_get() only keeps one line, need to get linep again
Bram Moolenaaraec11792007-07-10 11:09:36 +00002538 linep = ml_get(pos.lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002539 }
2540 }
2541 }
2542 if (start_in_quotes == MAYBE)
2543 start_in_quotes = FALSE;
2544
2545 /*
2546 * If 'smartmatch' is set:
2547 * Things inside quotes are ignored by setting 'inquote'. If we
2548 * find a quote without a preceding '\' invert 'inquote'. At the
2549 * end of a line not ending in '\' we reset 'inquote'.
2550 *
2551 * In lines with an uneven number of quotes (without preceding '\')
2552 * we do not know which part to ignore. Therefore we only set
2553 * inquote if the number of quotes in a line is even, unless this
2554 * line or the previous one ends in a '\'. Complicated, isn't it?
2555 */
Bram Moolenaar8c7694a2013-01-17 17:02:05 +01002556 c = PTR2CHAR(linep + pos.col);
2557 switch (c)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002558 {
2559 case NUL:
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002560 // at end of line without trailing backslash, reset inquote
Bram Moolenaar071d4272004-06-13 20:20:40 +00002561 if (pos.col == 0 || linep[pos.col - 1] != '\\')
2562 {
2563 inquote = FALSE;
2564 start_in_quotes = FALSE;
2565 }
2566 break;
2567
2568 case '"':
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002569 // a quote that is preceded with an odd number of backslashes is
2570 // ignored
Bram Moolenaar071d4272004-06-13 20:20:40 +00002571 if (do_quotes)
2572 {
2573 int col;
2574
2575 for (col = pos.col - 1; col >= 0; --col)
2576 if (linep[col] != '\\')
2577 break;
2578 if ((((int)pos.col - 1 - col) & 1) == 0)
2579 {
2580 inquote = !inquote;
2581 start_in_quotes = FALSE;
2582 }
2583 }
2584 break;
2585
2586 /*
2587 * If smart matching ('cpoptions' does not contain '%'):
2588 * Skip things in single quotes: 'x' or '\x'. Be careful for single
2589 * single quotes, eg jon's. Things like '\233' or '\x3f' are not
2590 * skipped, there is never a brace in them.
2591 * Ignore this when finding matches for `'.
2592 */
2593 case '\'':
2594 if (!cpo_match && initc != '\'' && findc != '\'')
2595 {
2596 if (backwards)
2597 {
2598 if (pos.col > 1)
2599 {
2600 if (linep[pos.col - 2] == '\'')
2601 {
2602 pos.col -= 2;
2603 break;
2604 }
2605 else if (linep[pos.col - 2] == '\\' &&
2606 pos.col > 2 && linep[pos.col - 3] == '\'')
2607 {
2608 pos.col -= 3;
2609 break;
2610 }
2611 }
2612 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002613 else if (linep[pos.col + 1]) // forward search
Bram Moolenaar071d4272004-06-13 20:20:40 +00002614 {
2615 if (linep[pos.col + 1] == '\\' &&
2616 linep[pos.col + 2] && linep[pos.col + 3] == '\'')
2617 {
2618 pos.col += 3;
2619 break;
2620 }
2621 else if (linep[pos.col + 2] == '\'')
2622 {
2623 pos.col += 2;
2624 break;
2625 }
2626 }
2627 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002628 // FALLTHROUGH
Bram Moolenaar071d4272004-06-13 20:20:40 +00002629
2630 default:
2631#ifdef FEAT_LISP
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002632 /*
2633 * For Lisp skip over backslashed (), {} and [].
2634 * (actually, we skip #\( et al)
2635 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002636 if (curbuf->b_p_lisp
2637 && vim_strchr((char_u *)"(){}[]", c) != NULL
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002638 && pos.col > 1
2639 && check_prevcol(linep, pos.col, '\\', NULL)
2640 && check_prevcol(linep, pos.col - 1, '#', NULL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002641 break;
2642#endif
2643
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002644 // Check for match outside of quotes, and inside of
2645 // quotes when the start is also inside of quotes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002646 if ((!inquote || start_in_quotes == TRUE)
2647 && (c == initc || c == findc))
2648 {
2649 int col, bslcnt = 0;
2650
2651 if (!cpo_bsl)
2652 {
2653 for (col = pos.col; check_prevcol(linep, col, '\\', &col);)
2654 bslcnt++;
2655 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002656 // Only accept a match when 'M' is in 'cpo' or when escaping
2657 // is what we expect.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002658 if (cpo_bsl || (bslcnt & 1) == match_escaped)
2659 {
2660 if (c == initc)
2661 count++;
2662 else
2663 {
2664 if (count == 0)
2665 return &pos;
2666 count--;
2667 }
2668 }
2669 }
2670 }
2671 }
2672
2673 if (comment_dir == BACKWARD && count > 0)
2674 {
2675 pos = match_pos;
2676 return &pos;
2677 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002678 return (pos_T *)NULL; // never found it
Bram Moolenaar071d4272004-06-13 20:20:40 +00002679}
2680
2681/*
2682 * Check if line[] contains a / / comment.
2683 * Return MAXCOL if not, otherwise return the column.
2684 * TODO: skip strings.
2685 */
2686 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002687check_linecomment(char_u *line)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002688{
2689 char_u *p;
2690
2691 p = line;
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002692#ifdef FEAT_LISP
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002693 // skip Lispish one-line comments
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002694 if (curbuf->b_p_lisp)
2695 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002696 if (vim_strchr(p, ';') != NULL) // there may be comments
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002697 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002698 int in_str = FALSE; // inside of string
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002699
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002700 p = line; // scan from start
Bram Moolenaar520470a2005-06-16 21:59:56 +00002701 while ((p = vim_strpbrk(p, (char_u *)"\";")) != NULL)
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002702 {
2703 if (*p == '"')
2704 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01002705 if (in_str)
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002706 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002707 if (*(p - 1) != '\\') // skip escaped quote
Bram Moolenaar70b2a562012-01-10 22:26:17 +01002708 in_str = FALSE;
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002709 }
2710 else if (p == line || ((p - line) >= 2
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002711 // skip #\" form
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002712 && *(p - 1) != '\\' && *(p - 2) != '#'))
Bram Moolenaar70b2a562012-01-10 22:26:17 +01002713 in_str = TRUE;
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002714 }
Bram Moolenaar70b2a562012-01-10 22:26:17 +01002715 else if (!in_str && ((p - line) < 2
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002716 || (*(p - 1) != '\\' && *(p - 2) != '#')))
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002717 break; // found!
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002718 ++p;
2719 }
2720 }
2721 else
2722 p = NULL;
2723 }
2724 else
2725#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002726 while ((p = vim_strchr(p, '/')) != NULL)
2727 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002728 // accept a double /, unless it's preceded with * and followed by *,
2729 // because * / / * is an end and start of a C comment
Bram Moolenaar78d4aba2008-01-01 14:43:35 +00002730 if (p[1] == '/' && (p == line || p[-1] != '*' || p[2] != '*'))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002731 break;
2732 ++p;
2733 }
2734
2735 if (p == NULL)
2736 return MAXCOL;
2737 return (int)(p - line);
2738}
2739
2740/*
2741 * Move cursor briefly to character matching the one under the cursor.
2742 * Used for Insert mode and "r" command.
2743 * Show the match only if it is visible on the screen.
2744 * If there isn't a match, then beep.
2745 */
2746 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002747showmatch(
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002748 int c) // char to show match for
Bram Moolenaar071d4272004-06-13 20:20:40 +00002749{
2750 pos_T *lpos, save_cursor;
2751 pos_T mpos;
2752 colnr_T vcol;
2753 long save_so;
2754 long save_siso;
2755#ifdef CURSOR_SHAPE
2756 int save_state;
2757#endif
2758 colnr_T save_dollar_vcol;
2759 char_u *p;
Bram Moolenaar375e3392019-01-31 18:26:10 +01002760 long *so = curwin->w_p_so >= 0 ? &curwin->w_p_so : &p_so;
2761 long *siso = curwin->w_p_siso >= 0 ? &curwin->w_p_siso : &p_siso;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002762
2763 /*
2764 * Only show match for chars in the 'matchpairs' option.
2765 */
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002766 // 'matchpairs' is "x:y,x:y"
Bram Moolenaar8c7694a2013-01-17 17:02:05 +01002767 for (p = curbuf->b_p_mps; *p != NUL; ++p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002768 {
2769#ifdef FEAT_RIGHTLEFT
Bram Moolenaar187d3ac2013-02-20 18:39:13 +01002770 if (PTR2CHAR(p) == c && (curwin->w_p_rl ^ p_ri))
Bram Moolenaar8c7694a2013-01-17 17:02:05 +01002771 break;
Bram Moolenaar187d3ac2013-02-20 18:39:13 +01002772#endif
Bram Moolenaar1614a142019-10-06 22:00:13 +02002773 p += mb_ptr2len(p) + 1;
Bram Moolenaar8c7694a2013-01-17 17:02:05 +01002774 if (PTR2CHAR(p) == c
Bram Moolenaar071d4272004-06-13 20:20:40 +00002775#ifdef FEAT_RIGHTLEFT
2776 && !(curwin->w_p_rl ^ p_ri)
2777#endif
2778 )
2779 break;
Bram Moolenaar1614a142019-10-06 22:00:13 +02002780 p += mb_ptr2len(p);
Bram Moolenaar8c7694a2013-01-17 17:02:05 +01002781 if (*p == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002782 return;
2783 }
2784
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002785 if ((lpos = findmatch(NULL, NUL)) == NULL) // no match, so beep
Bram Moolenaar165bc692015-07-21 17:53:25 +02002786 vim_beep(BO_MATCH);
Bram Moolenaar187d3ac2013-02-20 18:39:13 +01002787 else if (lpos->lnum >= curwin->w_topline && lpos->lnum < curwin->w_botline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002788 {
2789 if (!curwin->w_p_wrap)
2790 getvcol(curwin, lpos, NULL, &vcol, NULL);
2791 if (curwin->w_p_wrap || (vcol >= curwin->w_leftcol
Bram Moolenaar02631462017-09-22 15:20:32 +02002792 && vcol < curwin->w_leftcol + curwin->w_width))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002793 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002794 mpos = *lpos; // save the pos, update_screen() may change it
Bram Moolenaar071d4272004-06-13 20:20:40 +00002795 save_cursor = curwin->w_cursor;
Bram Moolenaar375e3392019-01-31 18:26:10 +01002796 save_so = *so;
2797 save_siso = *siso;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002798 // Handle "$" in 'cpo': If the ')' is typed on top of the "$",
2799 // stop displaying the "$".
Bram Moolenaar76b9b362012-02-04 23:35:00 +01002800 if (dollar_vcol >= 0 && dollar_vcol == curwin->w_virtcol)
2801 dollar_vcol = -1;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002802 ++curwin->w_virtcol; // do display ')' just before "$"
2803 update_screen(VALID); // show the new char first
Bram Moolenaar071d4272004-06-13 20:20:40 +00002804
2805 save_dollar_vcol = dollar_vcol;
2806#ifdef CURSOR_SHAPE
2807 save_state = State;
2808 State = SHOWMATCH;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002809 ui_cursor_shape(); // may show different cursor shape
Bram Moolenaar071d4272004-06-13 20:20:40 +00002810#endif
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002811 curwin->w_cursor = mpos; // move to matching char
2812 *so = 0; // don't use 'scrolloff' here
2813 *siso = 0; // don't use 'sidescrolloff' here
Bram Moolenaar071d4272004-06-13 20:20:40 +00002814 showruler(FALSE);
2815 setcursor();
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002816 cursor_on(); // make sure that the cursor is shown
Bram Moolenaara338adc2018-01-31 20:51:47 +01002817 out_flush_cursor(TRUE, FALSE);
2818
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002819 // Restore dollar_vcol(), because setcursor() may call curs_rows()
2820 // which resets it if the matching position is in a previous line
2821 // and has a higher column number.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002822 dollar_vcol = save_dollar_vcol;
2823
2824 /*
2825 * brief pause, unless 'm' is present in 'cpo' and a character is
2826 * available.
2827 */
2828 if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL)
Bram Moolenaareda1da02019-11-17 17:06:33 +01002829 ui_delay(p_mat * 100L + 8, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002830 else if (!char_avail())
Bram Moolenaareda1da02019-11-17 17:06:33 +01002831 ui_delay(p_mat * 100L + 9, FALSE);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002832 curwin->w_cursor = save_cursor; // restore cursor position
Bram Moolenaar375e3392019-01-31 18:26:10 +01002833 *so = save_so;
2834 *siso = save_siso;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002835#ifdef CURSOR_SHAPE
2836 State = save_state;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002837 ui_cursor_shape(); // may show different cursor shape
Bram Moolenaar071d4272004-06-13 20:20:40 +00002838#endif
2839 }
2840 }
2841}
2842
2843/*
Bram Moolenaar453c1922019-10-26 14:42:09 +02002844 * Check if the pattern is zero-width.
Bram Moolenaaredaad6e2019-10-24 15:23:37 +02002845 * If move is TRUE, check from the beginning of the buffer, else from position
2846 * "cur".
2847 * "direction" is FORWARD or BACKWARD.
2848 * Returns TRUE, FALSE or -1 for failure.
2849 */
2850 static int
2851is_zero_width(char_u *pattern, int move, pos_T *cur, int direction)
2852{
2853 regmmatch_T regmatch;
2854 int nmatched = 0;
2855 int result = -1;
2856 pos_T pos;
Bram Moolenaar53989552019-12-23 22:59:18 +01002857 int called_emsg_before = called_emsg;
Bram Moolenaaredaad6e2019-10-24 15:23:37 +02002858 int flag = 0;
2859
2860 if (pattern == NULL)
2861 pattern = spats[last_idx].pat;
2862
2863 if (search_regcomp(pattern, RE_SEARCH, RE_SEARCH,
2864 SEARCH_KEEP, &regmatch) == FAIL)
2865 return -1;
2866
2867 // init startcol correctly
2868 regmatch.startpos[0].col = -1;
2869 // move to match
2870 if (move)
2871 {
2872 CLEAR_POS(&pos);
2873 }
2874 else
2875 {
2876 pos = *cur;
2877 // accept a match at the cursor position
2878 flag = SEARCH_START;
2879 }
2880
2881 if (searchit(curwin, curbuf, &pos, NULL, direction, pattern, 1,
2882 SEARCH_KEEP + flag, RE_SEARCH, NULL) != FAIL)
2883 {
2884 // Zero-width pattern should match somewhere, then we can check if
2885 // start and end are in the same position.
Bram Moolenaaredaad6e2019-10-24 15:23:37 +02002886 do
2887 {
2888 regmatch.startpos[0].col++;
2889 nmatched = vim_regexec_multi(&regmatch, curwin, curbuf,
2890 pos.lnum, regmatch.startpos[0].col, NULL, NULL);
2891 if (nmatched != 0)
2892 break;
2893 } while (direction == FORWARD ? regmatch.startpos[0].col < pos.col
2894 : regmatch.startpos[0].col > pos.col);
2895
Bram Moolenaar53989552019-12-23 22:59:18 +01002896 if (called_emsg == called_emsg_before)
Bram Moolenaaredaad6e2019-10-24 15:23:37 +02002897 {
2898 result = (nmatched != 0
2899 && regmatch.startpos[0].lnum == regmatch.endpos[0].lnum
2900 && regmatch.startpos[0].col == regmatch.endpos[0].col);
2901 }
2902 }
2903
Bram Moolenaaredaad6e2019-10-24 15:23:37 +02002904 vim_regfree(regmatch.regprog);
2905 return result;
2906}
2907
Bram Moolenaardde0efe2012-08-23 15:53:05 +02002908
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002909/*
2910 * Find next search match under cursor, cursor at end.
2911 * Used while an operator is pending, and in Visual mode.
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002912 */
2913 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002914current_search(
2915 long count,
Bram Moolenaar5d24a222018-12-23 19:10:09 +01002916 int forward) // TRUE for forward, FALSE for backward
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002917{
Bram Moolenaar5d24a222018-12-23 19:10:09 +01002918 pos_T start_pos; // start position of the pattern match
2919 pos_T end_pos; // end position of the pattern match
2920 pos_T orig_pos; // position of the cursor at beginning
2921 pos_T pos; // position after the pattern
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002922 int i;
2923 int dir;
Bram Moolenaar5d24a222018-12-23 19:10:09 +01002924 int result; // result of various function calls
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002925 char_u old_p_ws = p_ws;
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002926 int flags = 0;
Bram Moolenaarde9149e2013-07-17 19:22:13 +02002927 pos_T save_VIsual = VIsual;
Bram Moolenaaredaad6e2019-10-24 15:23:37 +02002928 int zero_width;
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002929
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002930 // Correct cursor when 'selection' is exclusive
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01002931 if (VIsual_active && *p_sel == 'e' && LT_POS(VIsual, curwin->w_cursor))
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002932 dec_cursor();
2933
Bram Moolenaaredaad6e2019-10-24 15:23:37 +02002934 orig_pos = pos = curwin->w_cursor;
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002935 if (VIsual_active)
2936 {
Bram Moolenaaredaad6e2019-10-24 15:23:37 +02002937 if (forward)
2938 incl(&pos);
2939 else
2940 decl(&pos);
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002941 }
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002942
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002943 // Is the pattern is zero-width?, this time, don't care about the direction
Bram Moolenaaredaad6e2019-10-24 15:23:37 +02002944 zero_width = is_zero_width(spats[last_idx].pat, TRUE, &curwin->w_cursor,
Bram Moolenaar22ab5472017-09-26 12:28:45 +02002945 FORWARD);
Bram Moolenaaredaad6e2019-10-24 15:23:37 +02002946 if (zero_width == -1)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002947 return FAIL; // pattern not found
Bram Moolenaarba6ba362012-08-08 15:27:57 +02002948
Bram Moolenaarba6ba362012-08-08 15:27:57 +02002949 /*
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002950 * The trick is to first search backwards and then search forward again,
2951 * so that a match at the current cursor position will be correctly
2952 * captured.
2953 */
2954 for (i = 0; i < 2; i++)
2955 {
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002956 if (forward)
2957 dir = i;
2958 else
2959 dir = !i;
Bram Moolenaarba6ba362012-08-08 15:27:57 +02002960
2961 flags = 0;
Bram Moolenaaredaad6e2019-10-24 15:23:37 +02002962 if (!dir && !zero_width)
Bram Moolenaarba6ba362012-08-08 15:27:57 +02002963 flags = SEARCH_END;
Bram Moolenaar5d24a222018-12-23 19:10:09 +01002964 end_pos = pos;
Bram Moolenaarba6ba362012-08-08 15:27:57 +02002965
Bram Moolenaar82cf7f62019-11-02 23:22:47 +01002966 // wrapping should not occur in the first round
2967 if (i == 0)
2968 p_ws = FALSE;
2969
Bram Moolenaar5d24a222018-12-23 19:10:09 +01002970 result = searchit(curwin, curbuf, &pos, &end_pos,
2971 (dir ? FORWARD : BACKWARD),
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002972 spats[last_idx].pat, (long) (i ? count : 1),
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02002973 SEARCH_KEEP | flags, RE_SEARCH, NULL);
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002974
Bram Moolenaar82cf7f62019-11-02 23:22:47 +01002975 p_ws = old_p_ws;
2976
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002977 // First search may fail, but then start searching from the
2978 // beginning of the file (cursor might be on the search match)
2979 // except when Visual mode is active, so that extending the visual
2980 // selection works.
2981 if (i == 1 && !result) // not found, abort
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002982 {
2983 curwin->w_cursor = orig_pos;
2984 if (VIsual_active)
2985 VIsual = save_VIsual;
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002986 return FAIL;
2987 }
Bram Moolenaar5d24a222018-12-23 19:10:09 +01002988 else if (i == 0 && !result)
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002989 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01002990 if (forward)
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002991 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002992 // try again from start of buffer
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01002993 CLEAR_POS(&pos);
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002994 }
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01002995 else
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002996 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002997 // try again from end of buffer
2998 // searching backwards, so set pos to last line and col
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02002999 pos.lnum = curwin->w_buffer->b_ml.ml_line_count;
Bram Moolenaar09168a72012-08-02 21:24:42 +02003000 pos.col = (colnr_T)STRLEN(
3001 ml_get(curwin->w_buffer->b_ml.ml_line_count));
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02003002 }
3003 }
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02003004 }
3005
3006 start_pos = pos;
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02003007
3008 if (!VIsual_active)
3009 VIsual = start_pos;
3010
Bram Moolenaar5d24a222018-12-23 19:10:09 +01003011 // put cursor on last character of match
3012 curwin->w_cursor = end_pos;
Bram Moolenaar453c1922019-10-26 14:42:09 +02003013 if (LT_POS(VIsual, end_pos) && forward)
Bram Moolenaar5d24a222018-12-23 19:10:09 +01003014 dec_cursor();
Bram Moolenaaredaad6e2019-10-24 15:23:37 +02003015 else if (VIsual_active && LT_POS(curwin->w_cursor, VIsual))
3016 curwin->w_cursor = pos; // put the cursor on the start of the match
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02003017 VIsual_active = TRUE;
3018 VIsual_mode = 'v';
3019
Bram Moolenaarb7633612019-02-10 21:48:25 +01003020 if (*p_sel == 'e')
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02003021 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003022 // Correction for exclusive selection depends on the direction.
Bram Moolenaarb7633612019-02-10 21:48:25 +01003023 if (forward && LTOREQ_POS(VIsual, curwin->w_cursor))
3024 inc_cursor();
3025 else if (!forward && LTOREQ_POS(curwin->w_cursor, VIsual))
3026 inc(&VIsual);
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02003027 }
3028
3029#ifdef FEAT_FOLDING
3030 if (fdo_flags & FDO_SEARCH && KeyTyped)
3031 foldOpenCursor();
3032#endif
3033
3034 may_start_select('c');
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02003035 setmouse();
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02003036#ifdef FEAT_CLIPBOARD
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003037 // Make sure the clipboard gets updated. Needed because start and
3038 // end are still the same, and the selection needs to be owned
Bram Moolenaar8a0f3c72012-07-29 12:55:32 +02003039 clip_star.vmode = NUL;
3040#endif
3041 redraw_curbuf_later(INVERTED);
3042 showmode();
3043
3044 return OK;
3045}
Bram Moolenaardde0efe2012-08-23 15:53:05 +02003046
Bram Moolenaar071d4272004-06-13 20:20:40 +00003047#if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(FEAT_TEXTOBJ) \
3048 || defined(PROTO)
3049/*
3050 * return TRUE if line 'lnum' is empty or has white chars only.
3051 */
3052 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003053linewhite(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003054{
3055 char_u *p;
3056
3057 p = skipwhite(ml_get(lnum));
3058 return (*p == NUL);
3059}
3060#endif
3061
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02003062/*
3063 * Add the search count "[3/19]" to "msgbuf".
Bram Moolenaar8f46e4c2019-05-24 22:08:15 +02003064 * When "recompute" is TRUE always recompute the numbers.
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02003065 */
3066 static void
3067search_stat(
3068 int dirc,
3069 pos_T *pos,
Bram Moolenaarc7a10b32019-05-06 21:37:18 +02003070 int show_top_bot_msg,
Bram Moolenaar8f46e4c2019-05-24 22:08:15 +02003071 char_u *msgbuf,
3072 int recompute)
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02003073{
3074 int save_ws = p_ws;
3075 int wraparound = FALSE;
3076 pos_T p = (*pos);
3077 static pos_T lastpos = {0, 0, 0};
3078 static int cur = 0;
3079 static int cnt = 0;
3080 static int chgtick = 0;
3081 static char_u *lastpat = NULL;
3082 static buf_T *lbuf = NULL;
3083#ifdef FEAT_RELTIME
3084 proftime_T start;
3085#endif
3086#define OUT_OF_TIME 999
3087
3088 wraparound = ((dirc == '?' && LT_POS(lastpos, p))
3089 || (dirc == '/' && LT_POS(p, lastpos)));
3090
3091 // If anything relevant changed the count has to be recomputed.
3092 // MB_STRNICMP ignores case, but we should not ignore case.
3093 // Unfortunately, there is no MB_STRNICMP function.
3094 if (!(chgtick == CHANGEDTICK(curbuf)
3095 && MB_STRNICMP(lastpat, spats[last_idx].pat, STRLEN(lastpat)) == 0
3096 && STRLEN(lastpat) == STRLEN(spats[last_idx].pat)
3097 && EQUAL_POS(lastpos, curwin->w_cursor)
Bram Moolenaar8f46e4c2019-05-24 22:08:15 +02003098 && lbuf == curbuf) || wraparound || cur < 0 || cur > 99 || recompute)
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02003099 {
3100 cur = 0;
3101 cnt = 0;
3102 CLEAR_POS(&lastpos);
3103 lbuf = curbuf;
3104 }
3105
3106 if (EQUAL_POS(lastpos, curwin->w_cursor) && !wraparound
3107 && (dirc == '/' ? cur < cnt : cur > 0))
3108 cur += dirc == '/' ? 1 : -1;
3109 else
3110 {
3111 p_ws = FALSE;
3112#ifdef FEAT_RELTIME
3113 profile_setlimit(20L, &start);
3114#endif
3115 while (!got_int && searchit(curwin, curbuf, &lastpos, NULL,
Bram Moolenaar92ea26b2019-10-18 20:53:34 +02003116 FORWARD, NULL, 1, SEARCH_KEEP, RE_LAST, NULL) != FAIL)
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02003117 {
3118#ifdef FEAT_RELTIME
3119 // Stop after passing the time limit.
3120 if (profile_passed_limit(&start))
3121 {
3122 cnt = OUT_OF_TIME;
3123 cur = OUT_OF_TIME;
3124 break;
3125 }
3126#endif
3127 cnt++;
3128 if (LTOREQ_POS(lastpos, p))
3129 cur++;
3130 fast_breakcheck();
3131 if (cnt > 99)
3132 break;
3133 }
3134 if (got_int)
3135 cur = -1; // abort
3136 }
3137 if (cur > 0)
3138 {
Bram Moolenaarb6cb26f2019-05-07 21:34:37 +02003139 char t[SEARCH_STAT_BUF_LEN] = "";
Bram Moolenaare2ad8262019-05-24 13:22:22 +02003140 size_t len;
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02003141
3142#ifdef FEAT_RIGHTLEFT
3143 if (curwin->w_p_rl && *curwin->w_p_rlc == 's')
3144 {
3145 if (cur == OUT_OF_TIME)
Bram Moolenaarb6cb26f2019-05-07 21:34:37 +02003146 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]");
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02003147 else if (cnt > 99 && cur > 99)
Bram Moolenaarb6cb26f2019-05-07 21:34:37 +02003148 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>99/>99]");
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02003149 else if (cnt > 99)
Bram Moolenaarb6cb26f2019-05-07 21:34:37 +02003150 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>99/%d]", cur);
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02003151 else
Bram Moolenaarb6cb26f2019-05-07 21:34:37 +02003152 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]", cnt, cur);
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02003153 }
3154 else
3155#endif
3156 {
3157 if (cur == OUT_OF_TIME)
Bram Moolenaarb6cb26f2019-05-07 21:34:37 +02003158 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]");
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02003159 else if (cnt > 99 && cur > 99)
Bram Moolenaarb6cb26f2019-05-07 21:34:37 +02003160 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>99/>99]");
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02003161 else if (cnt > 99)
Bram Moolenaarb6cb26f2019-05-07 21:34:37 +02003162 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/>99]", cur);
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02003163 else
Bram Moolenaarb6cb26f2019-05-07 21:34:37 +02003164 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]", cur, cnt);
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02003165 }
Bram Moolenaarc7a10b32019-05-06 21:37:18 +02003166
3167 len = STRLEN(t);
Bram Moolenaardc6855a2019-05-18 19:26:29 +02003168 if (show_top_bot_msg && len + 2 < SEARCH_STAT_BUF_LEN)
Bram Moolenaarc7a10b32019-05-06 21:37:18 +02003169 {
Bram Moolenaar16b58ae2019-09-06 20:40:21 +02003170 mch_memmove(t + 2, t, len);
3171 t[0] = 'W';
3172 t[1] = ' ';
Bram Moolenaarc7a10b32019-05-06 21:37:18 +02003173 len += 2;
3174 }
3175
3176 mch_memmove(msgbuf + STRLEN(msgbuf) - len, t, len);
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02003177 if (dirc == '?' && cur == 100)
3178 cur = -1;
3179
3180 vim_free(lastpat);
3181 lastpat = vim_strsave(spats[last_idx].pat);
3182 chgtick = CHANGEDTICK(curbuf);
3183 lbuf = curbuf;
3184 lastpos = p;
3185
Bram Moolenaar984f0312019-05-24 13:11:47 +02003186 // keep the message even after redraw, but don't put in history
3187 msg_hist_off = TRUE;
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02003188 give_warning(msgbuf, FALSE);
Bram Moolenaar984f0312019-05-24 13:11:47 +02003189 msg_hist_off = FALSE;
Bram Moolenaar9dfa3132019-05-04 21:08:40 +02003190 }
3191 p_ws = save_ws;
3192}
3193
Bram Moolenaar071d4272004-06-13 20:20:40 +00003194#if defined(FEAT_FIND_ID) || defined(PROTO)
3195/*
3196 * Find identifiers or defines in included files.
Bram Moolenaarb4b0a082011-02-25 18:38:36 +01003197 * If p_ic && (compl_cont_status & CONT_SOL) then ptr must be in lowercase.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003198 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003199 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003200find_pattern_in_path(
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003201 char_u *ptr, // pointer to search pattern
3202 int dir UNUSED, // direction of expansion
3203 int len, // length of search pattern
3204 int whole, // match whole words only
3205 int skip_comments, // don't match inside comments
3206 int type, // Type of search; are we looking for a type?
3207 // a macro?
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003208 long count,
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003209 int action, // What to do when we find it
3210 linenr_T start_lnum, // first line to start searching
3211 linenr_T end_lnum) // last line for searching
Bram Moolenaar071d4272004-06-13 20:20:40 +00003212{
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003213 SearchedFile *files; // Stack of included files
3214 SearchedFile *bigger; // When we need more space
Bram Moolenaar071d4272004-06-13 20:20:40 +00003215 int max_path_depth = 50;
3216 long match_count = 1;
3217
3218 char_u *pat;
3219 char_u *new_fname;
3220 char_u *curr_fname = curbuf->b_fname;
3221 char_u *prev_fname = NULL;
3222 linenr_T lnum;
3223 int depth;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003224 int depth_displayed; // For type==CHECK_PATH
Bram Moolenaar071d4272004-06-13 20:20:40 +00003225 int old_files;
3226 int already_searched;
3227 char_u *file_line;
3228 char_u *line;
3229 char_u *p;
3230 char_u save_char;
3231 int define_matched;
3232 regmatch_T regmatch;
3233 regmatch_T incl_regmatch;
3234 regmatch_T def_regmatch;
3235 int matched = FALSE;
3236 int did_show = FALSE;
3237 int found = FALSE;
3238 int i;
3239 char_u *already = NULL;
3240 char_u *startp = NULL;
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00003241 char_u *inc_opt = NULL;
Bram Moolenaar4033c552017-09-16 20:54:51 +02003242#if defined(FEAT_QUICKFIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003243 win_T *curwin_save = NULL;
3244#endif
3245
3246 regmatch.regprog = NULL;
3247 incl_regmatch.regprog = NULL;
3248 def_regmatch.regprog = NULL;
3249
3250 file_line = alloc(LSIZE);
3251 if (file_line == NULL)
3252 return;
3253
Bram Moolenaar071d4272004-06-13 20:20:40 +00003254 if (type != CHECK_PATH && type != FIND_DEFINE
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003255 // when CONT_SOL is set compare "ptr" with the beginning of the line
3256 // is faster than quote_meta/regcomp/regexec "ptr" -- Acevedo
Bram Moolenaare2c453d2019-08-21 14:37:09 +02003257 && !(compl_cont_status & CONT_SOL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003258 {
3259 pat = alloc(len + 5);
3260 if (pat == NULL)
3261 goto fpip_end;
3262 sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", len, ptr);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003263 // ignore case according to p_ic, p_scs and pat
Bram Moolenaar071d4272004-06-13 20:20:40 +00003264 regmatch.rm_ic = ignorecase(pat);
3265 regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0);
3266 vim_free(pat);
3267 if (regmatch.regprog == NULL)
3268 goto fpip_end;
3269 }
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00003270 inc_opt = (*curbuf->b_p_inc == NUL) ? p_inc : curbuf->b_p_inc;
3271 if (*inc_opt != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003272 {
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00003273 incl_regmatch.regprog = vim_regcomp(inc_opt, p_magic ? RE_MAGIC : 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003274 if (incl_regmatch.regprog == NULL)
3275 goto fpip_end;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003276 incl_regmatch.rm_ic = FALSE; // don't ignore case in incl. pat.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003277 }
3278 if (type == FIND_DEFINE && (*curbuf->b_p_def != NUL || *p_def != NUL))
3279 {
3280 def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL
3281 ? p_def : curbuf->b_p_def, p_magic ? RE_MAGIC : 0);
3282 if (def_regmatch.regprog == NULL)
3283 goto fpip_end;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003284 def_regmatch.rm_ic = FALSE; // don't ignore case in define pat.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003285 }
Bram Moolenaarc799fe22019-05-28 23:08:19 +02003286 files = lalloc_clear(max_path_depth * sizeof(SearchedFile), TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003287 if (files == NULL)
3288 goto fpip_end;
3289 old_files = max_path_depth;
3290 depth = depth_displayed = -1;
3291
3292 lnum = start_lnum;
3293 if (end_lnum > curbuf->b_ml.ml_line_count)
3294 end_lnum = curbuf->b_ml.ml_line_count;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003295 if (lnum > end_lnum) // do at least one line
Bram Moolenaar071d4272004-06-13 20:20:40 +00003296 lnum = end_lnum;
3297 line = ml_get(lnum);
3298
3299 for (;;)
3300 {
3301 if (incl_regmatch.regprog != NULL
3302 && vim_regexec(&incl_regmatch, line, (colnr_T)0))
3303 {
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00003304 char_u *p_fname = (curr_fname == curbuf->b_fname)
3305 ? curbuf->b_ffname : curr_fname;
3306
3307 if (inc_opt != NULL && strstr((char *)inc_opt, "\\zs") != NULL)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003308 // Use text from '\zs' to '\ze' (or end) of 'include'.
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00003309 new_fname = find_file_name_in_path(incl_regmatch.startp[0],
Bram Moolenaarc84e3c12013-07-03 22:28:36 +02003310 (int)(incl_regmatch.endp[0] - incl_regmatch.startp[0]),
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00003311 FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, p_fname);
3312 else
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003313 // Use text after match with 'include'.
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00003314 new_fname = file_name_in_line(incl_regmatch.endp[0], 0,
Bram Moolenaard1f56e62006-02-22 21:25:37 +00003315 FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, p_fname, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003316 already_searched = FALSE;
3317 if (new_fname != NULL)
3318 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003319 // Check whether we have already searched in this file
Bram Moolenaar071d4272004-06-13 20:20:40 +00003320 for (i = 0;; i++)
3321 {
3322 if (i == depth + 1)
3323 i = old_files;
3324 if (i == max_path_depth)
3325 break;
Bram Moolenaar99499b12019-05-23 21:35:48 +02003326 if (fullpathcmp(new_fname, files[i].name, TRUE, TRUE)
3327 & FPC_SAME)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003328 {
3329 if (type != CHECK_PATH &&
3330 action == ACTION_SHOW_ALL && files[i].matched)
3331 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003332 msg_putchar('\n'); // cursor below last one
3333 if (!got_int) // don't display if 'q'
3334 // typed at "--more--"
3335 // message
Bram Moolenaar071d4272004-06-13 20:20:40 +00003336 {
3337 msg_home_replace_hl(new_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01003338 msg_puts(_(" (includes previously listed match)"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003339 prev_fname = NULL;
3340 }
3341 }
Bram Moolenaard23a8232018-02-10 18:45:26 +01003342 VIM_CLEAR(new_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003343 already_searched = TRUE;
3344 break;
3345 }
3346 }
3347 }
3348
3349 if (type == CHECK_PATH && (action == ACTION_SHOW_ALL
3350 || (new_fname == NULL && !already_searched)))
3351 {
3352 if (did_show)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003353 msg_putchar('\n'); // cursor below last one
Bram Moolenaar071d4272004-06-13 20:20:40 +00003354 else
3355 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003356 gotocmdline(TRUE); // cursor at status line
Bram Moolenaar32526b32019-01-19 17:43:09 +01003357 msg_puts_title(_("--- Included files "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003358 if (action != ACTION_SHOW_ALL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003359 msg_puts_title(_("not found "));
3360 msg_puts_title(_("in path ---\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003361 }
3362 did_show = TRUE;
3363 while (depth_displayed < depth && !got_int)
3364 {
3365 ++depth_displayed;
3366 for (i = 0; i < depth_displayed; i++)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003367 msg_puts(" ");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003368 msg_home_replace(files[depth_displayed].name);
Bram Moolenaar32526b32019-01-19 17:43:09 +01003369 msg_puts(" -->\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003370 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003371 if (!got_int) // don't display if 'q' typed
3372 // for "--more--" message
Bram Moolenaar071d4272004-06-13 20:20:40 +00003373 {
3374 for (i = 0; i <= depth_displayed; i++)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003375 msg_puts(" ");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003376 if (new_fname != NULL)
3377 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003378 // using "new_fname" is more reliable, e.g., when
3379 // 'includeexpr' is set.
Bram Moolenaar8820b482017-03-16 17:23:31 +01003380 msg_outtrans_attr(new_fname, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003381 }
3382 else
3383 {
3384 /*
3385 * Isolate the file name.
3386 * Include the surrounding "" or <> if present.
3387 */
Bram Moolenaar058bdcf2012-07-25 13:46:30 +02003388 if (inc_opt != NULL
3389 && strstr((char *)inc_opt, "\\zs") != NULL)
3390 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003391 // pattern contains \zs, use the match
Bram Moolenaar058bdcf2012-07-25 13:46:30 +02003392 p = incl_regmatch.startp[0];
3393 i = (int)(incl_regmatch.endp[0]
3394 - incl_regmatch.startp[0]);
3395 }
3396 else
3397 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003398 // find the file name after the end of the match
Bram Moolenaar058bdcf2012-07-25 13:46:30 +02003399 for (p = incl_regmatch.endp[0];
3400 *p && !vim_isfilec(*p); p++)
3401 ;
3402 for (i = 0; vim_isfilec(p[i]); i++)
3403 ;
3404 }
3405
Bram Moolenaar071d4272004-06-13 20:20:40 +00003406 if (i == 0)
3407 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003408 // Nothing found, use the rest of the line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003409 p = incl_regmatch.endp[0];
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003410 i = (int)STRLEN(p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003411 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003412 // Avoid checking before the start of the line, can
3413 // happen if \zs appears in the regexp.
Bram Moolenaar058bdcf2012-07-25 13:46:30 +02003414 else if (p > line)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003415 {
3416 if (p[-1] == '"' || p[-1] == '<')
3417 {
3418 --p;
3419 ++i;
3420 }
3421 if (p[i] == '"' || p[i] == '>')
3422 ++i;
3423 }
3424 save_char = p[i];
3425 p[i] = NUL;
Bram Moolenaar8820b482017-03-16 17:23:31 +01003426 msg_outtrans_attr(p, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003427 p[i] = save_char;
3428 }
3429
3430 if (new_fname == NULL && action == ACTION_SHOW_ALL)
3431 {
3432 if (already_searched)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003433 msg_puts(_(" (Already listed)"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003434 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01003435 msg_puts(_(" NOT FOUND"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003436 }
3437 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003438 out_flush(); // output each line directly
Bram Moolenaar071d4272004-06-13 20:20:40 +00003439 }
3440
3441 if (new_fname != NULL)
3442 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003443 // Push the new file onto the file stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003444 if (depth + 1 == old_files)
3445 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02003446 bigger = ALLOC_MULT(SearchedFile, max_path_depth * 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003447 if (bigger != NULL)
3448 {
3449 for (i = 0; i <= depth; i++)
3450 bigger[i] = files[i];
3451 for (i = depth + 1; i < old_files + max_path_depth; i++)
3452 {
3453 bigger[i].fp = NULL;
3454 bigger[i].name = NULL;
3455 bigger[i].lnum = 0;
3456 bigger[i].matched = FALSE;
3457 }
3458 for (i = old_files; i < max_path_depth; i++)
3459 bigger[i + max_path_depth] = files[i];
3460 old_files += max_path_depth;
3461 max_path_depth *= 2;
3462 vim_free(files);
3463 files = bigger;
3464 }
3465 }
3466 if ((files[depth + 1].fp = mch_fopen((char *)new_fname, "r"))
3467 == NULL)
3468 vim_free(new_fname);
3469 else
3470 {
3471 if (++depth == old_files)
3472 {
3473 /*
3474 * lalloc() for 'bigger' must have failed above. We
3475 * will forget one of our already visited files now.
3476 */
3477 vim_free(files[old_files].name);
3478 ++old_files;
3479 }
3480 files[depth].name = curr_fname = new_fname;
3481 files[depth].lnum = 0;
3482 files[depth].matched = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003483 if (action == ACTION_EXPAND)
3484 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003485 msg_hist_off = TRUE; // reset in msg_trunc_attr()
Bram Moolenaar555b2802005-05-19 21:08:39 +00003486 vim_snprintf((char*)IObuff, IOSIZE,
3487 _("Scanning included file: %s"),
3488 (char *)new_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01003489 msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003490 }
Bram Moolenaare2c453d2019-08-21 14:37:09 +02003491 else if (p_verbose >= 5)
Bram Moolenaar87b5ca52006-03-04 21:55:31 +00003492 {
3493 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003494 smsg(_("Searching included file %s"),
Bram Moolenaar87b5ca52006-03-04 21:55:31 +00003495 (char *)new_fname);
3496 verbose_leave();
3497 }
3498
Bram Moolenaar071d4272004-06-13 20:20:40 +00003499 }
3500 }
3501 }
3502 else
3503 {
3504 /*
3505 * Check if the line is a define (type == FIND_DEFINE)
3506 */
3507 p = line;
3508search_line:
3509 define_matched = FALSE;
3510 if (def_regmatch.regprog != NULL
3511 && vim_regexec(&def_regmatch, line, (colnr_T)0))
3512 {
3513 /*
3514 * Pattern must be first identifier after 'define', so skip
3515 * to that position before checking for match of pattern. Also
3516 * don't let it match beyond the end of this identifier.
3517 */
3518 p = def_regmatch.endp[0];
3519 while (*p && !vim_iswordc(*p))
3520 p++;
3521 define_matched = TRUE;
3522 }
3523
3524 /*
3525 * Look for a match. Don't do this if we are looking for a
3526 * define and this line didn't match define_prog above.
3527 */
3528 if (def_regmatch.regprog == NULL || define_matched)
3529 {
Bram Moolenaare2c453d2019-08-21 14:37:09 +02003530 if (define_matched || (compl_cont_status & CONT_SOL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003531 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003532 // compare the first "len" chars from "ptr"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003533 startp = skipwhite(p);
3534 if (p_ic)
3535 matched = !MB_STRNICMP(startp, ptr, len);
3536 else
3537 matched = !STRNCMP(startp, ptr, len);
3538 if (matched && define_matched && whole
3539 && vim_iswordc(startp[len]))
3540 matched = FALSE;
3541 }
3542 else if (regmatch.regprog != NULL
3543 && vim_regexec(&regmatch, line, (colnr_T)(p - line)))
3544 {
3545 matched = TRUE;
3546 startp = regmatch.startp[0];
3547 /*
3548 * Check if the line is not a comment line (unless we are
3549 * looking for a define). A line starting with "# define"
3550 * is not considered to be a comment line.
3551 */
3552 if (!define_matched && skip_comments)
3553 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003554 if ((*line != '#' ||
3555 STRNCMP(skipwhite(line + 1), "define", 6) != 0)
Bram Moolenaar81340392012-06-06 16:12:59 +02003556 && get_leader_len(line, NULL, FALSE, TRUE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003557 matched = FALSE;
3558
3559 /*
3560 * Also check for a "/ *" or "/ /" before the match.
3561 * Skips lines like "int backwards; / * normal index
3562 * * /" when looking for "normal".
3563 * Note: Doesn't skip "/ *" in comments.
3564 */
3565 p = skipwhite(line);
3566 if (matched
3567 || (p[0] == '/' && p[1] == '*') || p[0] == '*')
Bram Moolenaar071d4272004-06-13 20:20:40 +00003568 for (p = line; *p && p < startp; ++p)
3569 {
3570 if (matched
3571 && p[0] == '/'
3572 && (p[1] == '*' || p[1] == '/'))
3573 {
3574 matched = FALSE;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003575 // After "//" all text is comment
Bram Moolenaar071d4272004-06-13 20:20:40 +00003576 if (p[1] == '/')
3577 break;
3578 ++p;
3579 }
3580 else if (!matched && p[0] == '*' && p[1] == '/')
3581 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003582 // Can find match after "* /".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003583 matched = TRUE;
3584 ++p;
3585 }
3586 }
3587 }
3588 }
3589 }
3590 }
3591 if (matched)
3592 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003593 if (action == ACTION_EXPAND)
3594 {
Bram Moolenaard9eefe32019-04-06 14:22:21 +02003595 int cont_s_ipos = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003596 int add_r;
3597 char_u *aux;
3598
3599 if (depth == -1 && lnum == curwin->w_cursor.lnum)
3600 break;
3601 found = TRUE;
3602 aux = p = startp;
Bram Moolenaar4be06f92005-07-29 22:36:03 +00003603 if (compl_cont_status & CONT_ADDING)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003604 {
Bram Moolenaar4be06f92005-07-29 22:36:03 +00003605 p += compl_length;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003606 if (vim_iswordp(p))
3607 goto exit_matched;
3608 p = find_word_start(p);
3609 }
3610 p = find_word_end(p);
3611 i = (int)(p - aux);
3612
Bram Moolenaar4be06f92005-07-29 22:36:03 +00003613 if ((compl_cont_status & CONT_ADDING) && i == compl_length)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003614 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003615 // IOSIZE > compl_length, so the STRNCPY works
Bram Moolenaar071d4272004-06-13 20:20:40 +00003616 STRNCPY(IObuff, aux, i);
Bram Moolenaar89d40322006-08-29 15:30:07 +00003617
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003618 // Get the next line: when "depth" < 0 from the current
3619 // buffer, otherwise from the included file. Jump to
3620 // exit_matched when past the last line.
Bram Moolenaar89d40322006-08-29 15:30:07 +00003621 if (depth < 0)
3622 {
3623 if (lnum >= end_lnum)
3624 goto exit_matched;
3625 line = ml_get(++lnum);
3626 }
3627 else if (vim_fgets(line = file_line,
3628 LSIZE, files[depth].fp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003629 goto exit_matched;
3630
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003631 // we read a line, set "already" to check this "line" later
3632 // if depth >= 0 we'll increase files[depth].lnum far
3633 // bellow -- Acevedo
Bram Moolenaar071d4272004-06-13 20:20:40 +00003634 already = aux = p = skipwhite(line);
3635 p = find_word_start(p);
3636 p = find_word_end(p);
3637 if (p > aux)
3638 {
3639 if (*aux != ')' && IObuff[i-1] != TAB)
3640 {
3641 if (IObuff[i-1] != ' ')
3642 IObuff[i++] = ' ';
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003643 // IObuf =~ "\(\k\|\i\).* ", thus i >= 2
Bram Moolenaar071d4272004-06-13 20:20:40 +00003644 if (p_js
3645 && (IObuff[i-2] == '.'
3646 || (vim_strchr(p_cpo, CPO_JOINSP) == NULL
3647 && (IObuff[i-2] == '?'
3648 || IObuff[i-2] == '!'))))
3649 IObuff[i++] = ' ';
3650 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003651 // copy as much as possible of the new word
Bram Moolenaar071d4272004-06-13 20:20:40 +00003652 if (p - aux >= IOSIZE - i)
3653 p = aux + IOSIZE - i - 1;
3654 STRNCPY(IObuff + i, aux, p - aux);
3655 i += (int)(p - aux);
Bram Moolenaard9eefe32019-04-06 14:22:21 +02003656 cont_s_ipos = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003657 }
3658 IObuff[i] = NUL;
3659 aux = IObuff;
3660
Bram Moolenaar4be06f92005-07-29 22:36:03 +00003661 if (i == compl_length)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003662 goto exit_matched;
3663 }
3664
Bram Moolenaare8c3a142006-08-29 14:30:35 +00003665 add_r = ins_compl_add_infercase(aux, i, p_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003666 curr_fname == curbuf->b_fname ? NULL : curr_fname,
Bram Moolenaard9eefe32019-04-06 14:22:21 +02003667 dir, cont_s_ipos);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003668 if (add_r == OK)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003669 // if dir was BACKWARD then honor it just once
Bram Moolenaar071d4272004-06-13 20:20:40 +00003670 dir = FORWARD;
Bram Moolenaar572cb562005-08-05 21:35:02 +00003671 else if (add_r == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003672 break;
3673 }
Bram Moolenaare2c453d2019-08-21 14:37:09 +02003674 else if (action == ACTION_SHOW_ALL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003675 {
3676 found = TRUE;
3677 if (!did_show)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003678 gotocmdline(TRUE); // cursor at status line
Bram Moolenaar071d4272004-06-13 20:20:40 +00003679 if (curr_fname != prev_fname)
3680 {
3681 if (did_show)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003682 msg_putchar('\n'); // cursor below last one
3683 if (!got_int) // don't display if 'q' typed
3684 // at "--more--" message
Bram Moolenaar071d4272004-06-13 20:20:40 +00003685 msg_home_replace_hl(curr_fname);
3686 prev_fname = curr_fname;
3687 }
3688 did_show = TRUE;
3689 if (!got_int)
3690 show_pat_in_path(line, type, TRUE, action,
3691 (depth == -1) ? NULL : files[depth].fp,
3692 (depth == -1) ? &lnum : &files[depth].lnum,
3693 match_count++);
3694
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003695 // Set matched flag for this file and all the ones that
3696 // include it
Bram Moolenaar071d4272004-06-13 20:20:40 +00003697 for (i = 0; i <= depth; ++i)
3698 files[i].matched = TRUE;
3699 }
3700 else if (--count <= 0)
3701 {
3702 found = TRUE;
3703 if (depth == -1 && lnum == curwin->w_cursor.lnum
Bram Moolenaar4033c552017-09-16 20:54:51 +02003704#if defined(FEAT_QUICKFIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003705 && g_do_tagpreview == 0
3706#endif
3707 )
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003708 emsg(_("E387: Match is on current line"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003709 else if (action == ACTION_SHOW)
3710 {
3711 show_pat_in_path(line, type, did_show, action,
3712 (depth == -1) ? NULL : files[depth].fp,
3713 (depth == -1) ? &lnum : &files[depth].lnum, 1L);
3714 did_show = TRUE;
3715 }
3716 else
3717 {
3718#ifdef FEAT_GUI
3719 need_mouse_correct = TRUE;
3720#endif
Bram Moolenaar4033c552017-09-16 20:54:51 +02003721#if defined(FEAT_QUICKFIX)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003722 // ":psearch" uses the preview window
Bram Moolenaar071d4272004-06-13 20:20:40 +00003723 if (g_do_tagpreview != 0)
3724 {
3725 curwin_save = curwin;
Bram Moolenaar576a4a62019-08-18 15:25:17 +02003726 prepare_tagpreview(TRUE, TRUE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003727 }
3728#endif
3729 if (action == ACTION_SPLIT)
3730 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003731 if (win_split(0, 0) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003732 break;
Bram Moolenaar3368ea22010-09-21 16:56:35 +02003733 RESET_BINDING(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003734 }
3735 if (depth == -1)
3736 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003737 // match in current file
Bram Moolenaar4033c552017-09-16 20:54:51 +02003738#if defined(FEAT_QUICKFIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003739 if (g_do_tagpreview != 0)
3740 {
Bram Moolenaar8ad80de2017-06-05 16:01:59 +02003741 if (!GETFILE_SUCCESS(getfile(
Bram Moolenaarc31f9ae2017-07-23 22:02:02 +02003742 curwin_save->w_buffer->b_fnum, NULL,
Bram Moolenaar8ad80de2017-06-05 16:01:59 +02003743 NULL, TRUE, lnum, FALSE)))
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003744 break; // failed to jump to file
Bram Moolenaar071d4272004-06-13 20:20:40 +00003745 }
3746 else
3747#endif
3748 setpcmark();
3749 curwin->w_cursor.lnum = lnum;
Bram Moolenaarc31f9ae2017-07-23 22:02:02 +02003750 check_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003751 }
3752 else
3753 {
Bram Moolenaar8ad80de2017-06-05 16:01:59 +02003754 if (!GETFILE_SUCCESS(getfile(
3755 0, files[depth].name, NULL, TRUE,
3756 files[depth].lnum, FALSE)))
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003757 break; // failed to jump to file
3758 // autocommands may have changed the lnum, we don't
3759 // want that here
Bram Moolenaar071d4272004-06-13 20:20:40 +00003760 curwin->w_cursor.lnum = files[depth].lnum;
3761 }
3762 }
3763 if (action != ACTION_SHOW)
3764 {
Bram Moolenaarfe81d452009-04-22 14:44:41 +00003765 curwin->w_cursor.col = (colnr_T)(startp - line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003766 curwin->w_set_curswant = TRUE;
3767 }
3768
Bram Moolenaar4033c552017-09-16 20:54:51 +02003769#if defined(FEAT_QUICKFIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003770 if (g_do_tagpreview != 0
Bram Moolenaar997fb4b2006-02-17 21:53:23 +00003771 && curwin != curwin_save && win_valid(curwin_save))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003772 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003773 // Return cursor to where we were
Bram Moolenaar071d4272004-06-13 20:20:40 +00003774 validate_cursor();
3775 redraw_later(VALID);
3776 win_enter(curwin_save, TRUE);
3777 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003778# ifdef FEAT_PROP_POPUP
Bram Moolenaar1b6d9c42019-08-05 21:52:04 +02003779 else if (WIN_IS_POPUP(curwin))
3780 // can't keep focus in popup window
3781 win_enter(firstwin, TRUE);
3782# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003783#endif
3784 break;
3785 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003786exit_matched:
Bram Moolenaar071d4272004-06-13 20:20:40 +00003787 matched = FALSE;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003788 // look for other matches in the rest of the line if we
3789 // are not at the end of it already
Bram Moolenaar071d4272004-06-13 20:20:40 +00003790 if (def_regmatch.regprog == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00003791 && action == ACTION_EXPAND
Bram Moolenaar4be06f92005-07-29 22:36:03 +00003792 && !(compl_cont_status & CONT_SOL)
Bram Moolenaarfe81d452009-04-22 14:44:41 +00003793 && *startp != NUL
Bram Moolenaar1614a142019-10-06 22:00:13 +02003794 && *(p = startp + mb_ptr2len(startp)) != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003795 goto search_line;
3796 }
3797 line_breakcheck();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003798 if (action == ACTION_EXPAND)
Bram Moolenaar472e8592016-10-15 17:06:47 +02003799 ins_compl_check_keys(30, FALSE);
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003800 if (got_int || ins_compl_interrupted())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003801 break;
3802
3803 /*
3804 * Read the next line. When reading an included file and encountering
3805 * end-of-file, close the file and continue in the file that included
3806 * it.
3807 */
3808 while (depth >= 0 && !already
3809 && vim_fgets(line = file_line, LSIZE, files[depth].fp))
3810 {
3811 fclose(files[depth].fp);
3812 --old_files;
3813 files[old_files].name = files[depth].name;
3814 files[old_files].matched = files[depth].matched;
3815 --depth;
3816 curr_fname = (depth == -1) ? curbuf->b_fname
3817 : files[depth].name;
3818 if (depth < depth_displayed)
3819 depth_displayed = depth;
3820 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003821 if (depth >= 0) // we could read the line
Bram Moolenaarc84e3c12013-07-03 22:28:36 +02003822 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003823 files[depth].lnum++;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003824 // Remove any CR and LF from the line.
Bram Moolenaarc84e3c12013-07-03 22:28:36 +02003825 i = (int)STRLEN(line);
3826 if (i > 0 && line[i - 1] == '\n')
3827 line[--i] = NUL;
3828 if (i > 0 && line[i - 1] == '\r')
3829 line[--i] = NUL;
3830 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003831 else if (!already)
3832 {
3833 if (++lnum > end_lnum)
3834 break;
3835 line = ml_get(lnum);
3836 }
3837 already = NULL;
3838 }
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003839 // End of big for (;;) loop.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003840
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003841 // Close any files that are still open.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003842 for (i = 0; i <= depth; i++)
3843 {
3844 fclose(files[i].fp);
3845 vim_free(files[i].name);
3846 }
3847 for (i = old_files; i < max_path_depth; i++)
3848 vim_free(files[i].name);
3849 vim_free(files);
3850
3851 if (type == CHECK_PATH)
3852 {
3853 if (!did_show)
3854 {
3855 if (action != ACTION_SHOW_ALL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003856 msg(_("All included files were found"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003857 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01003858 msg(_("No included files"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003859 }
3860 }
Bram Moolenaare2c453d2019-08-21 14:37:09 +02003861 else if (!found && action != ACTION_EXPAND)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003862 {
Bram Moolenaar7591bb32019-03-30 13:53:47 +01003863 if (got_int || ins_compl_interrupted())
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003864 emsg(_(e_interr));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003865 else if (type == FIND_DEFINE)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003866 emsg(_("E388: Couldn't find definition"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003867 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003868 emsg(_("E389: Couldn't find pattern"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003869 }
3870 if (action == ACTION_SHOW || action == ACTION_SHOW_ALL)
3871 msg_end();
3872
3873fpip_end:
3874 vim_free(file_line);
Bram Moolenaar473de612013-06-08 18:19:48 +02003875 vim_regfree(regmatch.regprog);
3876 vim_regfree(incl_regmatch.regprog);
3877 vim_regfree(def_regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003878}
3879
3880 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003881show_pat_in_path(
3882 char_u *line,
3883 int type,
3884 int did_show,
3885 int action,
3886 FILE *fp,
3887 linenr_T *lnum,
3888 long count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003889{
3890 char_u *p;
3891
3892 if (did_show)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003893 msg_putchar('\n'); // cursor below last one
Bram Moolenaar91170f82006-05-05 21:15:17 +00003894 else if (!msg_silent)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003895 gotocmdline(TRUE); // cursor at status line
3896 if (got_int) // 'q' typed at "--more--" message
Bram Moolenaar071d4272004-06-13 20:20:40 +00003897 return;
3898 for (;;)
3899 {
3900 p = line + STRLEN(line) - 1;
3901 if (fp != NULL)
3902 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003903 // We used fgets(), so get rid of newline at end
Bram Moolenaar071d4272004-06-13 20:20:40 +00003904 if (p >= line && *p == '\n')
3905 --p;
3906 if (p >= line && *p == '\r')
3907 --p;
3908 *(p + 1) = NUL;
3909 }
3910 if (action == ACTION_SHOW_ALL)
3911 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003912 sprintf((char *)IObuff, "%3ld: ", count); // show match nr
Bram Moolenaar32526b32019-01-19 17:43:09 +01003913 msg_puts((char *)IObuff);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003914 sprintf((char *)IObuff, "%4ld", *lnum); // show line nr
3915 // Highlight line numbers
Bram Moolenaar32526b32019-01-19 17:43:09 +01003916 msg_puts_attr((char *)IObuff, HL_ATTR(HLF_N));
3917 msg_puts(" ");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003918 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003919 msg_prt_line(line, FALSE);
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003920 out_flush(); // show one line at a time
Bram Moolenaar071d4272004-06-13 20:20:40 +00003921
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003922 // Definition continues until line that doesn't end with '\'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003923 if (got_int || type != FIND_DEFINE || p < line || *p != '\\')
3924 break;
3925
3926 if (fp != NULL)
3927 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01003928 if (vim_fgets(line, LSIZE, fp)) // end of file
Bram Moolenaar071d4272004-06-13 20:20:40 +00003929 break;
3930 ++*lnum;
3931 }
3932 else
3933 {
3934 if (++*lnum > curbuf->b_ml.ml_line_count)
3935 break;
3936 line = ml_get(*lnum);
3937 }
3938 msg_putchar('\n');
3939 }
3940}
3941#endif
3942
3943#ifdef FEAT_VIMINFO
Bram Moolenaar6bd1d772019-10-09 22:01:25 +02003944/*
3945 * Return the last used search pattern at "idx".
3946 */
Bram Moolenaarc3328162019-07-23 22:15:25 +02003947 spat_T *
3948get_spat(int idx)
3949{
3950 return &spats[idx];
3951}
3952
Bram Moolenaar6bd1d772019-10-09 22:01:25 +02003953/*
3954 * Return the last used search pattern index.
3955 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003956 int
Bram Moolenaarc3328162019-07-23 22:15:25 +02003957get_spat_last_idx(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003958{
Bram Moolenaarc3328162019-07-23 22:15:25 +02003959 return last_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003960}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003961#endif