1 | // This is an open source non-commercial project. Dear PVS-Studio, please check |
2 | // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com |
3 | |
4 | /* |
5 | * quickfix.c: functions for quickfix mode, using a file with error messages |
6 | */ |
7 | |
8 | #include <assert.h> |
9 | #include <inttypes.h> |
10 | #include <stdbool.h> |
11 | #include <string.h> |
12 | |
13 | #include "nvim/vim.h" |
14 | #include "nvim/ascii.h" |
15 | #include "nvim/quickfix.h" |
16 | #include "nvim/buffer.h" |
17 | #include "nvim/charset.h" |
18 | #include "nvim/cursor.h" |
19 | #include "nvim/edit.h" |
20 | #include "nvim/eval.h" |
21 | #include "nvim/ex_cmds.h" |
22 | #include "nvim/ex_cmds2.h" |
23 | #include "nvim/ex_docmd.h" |
24 | #include "nvim/ex_eval.h" |
25 | #include "nvim/ex_getln.h" |
26 | #include "nvim/fileio.h" |
27 | #include "nvim/fold.h" |
28 | #include "nvim/mark.h" |
29 | #include "nvim/mbyte.h" |
30 | #include "nvim/memline.h" |
31 | #include "nvim/message.h" |
32 | #include "nvim/misc1.h" |
33 | #include "nvim/memory.h" |
34 | #include "nvim/move.h" |
35 | #include "nvim/normal.h" |
36 | #include "nvim/option.h" |
37 | #include "nvim/os_unix.h" |
38 | #include "nvim/path.h" |
39 | #include "nvim/regexp.h" |
40 | #include "nvim/screen.h" |
41 | #include "nvim/search.h" |
42 | #include "nvim/strings.h" |
43 | #include "nvim/syntax.h" |
44 | #include "nvim/ui.h" |
45 | #include "nvim/window.h" |
46 | #include "nvim/os/os.h" |
47 | #include "nvim/os/input.h" |
48 | #include "nvim/api/private/helpers.h" |
49 | |
50 | |
51 | struct dir_stack_T { |
52 | struct dir_stack_T *next; |
53 | char_u *dirname; |
54 | }; |
55 | |
56 | /* |
57 | * For each error the next struct is allocated and linked in a list. |
58 | */ |
59 | typedef struct qfline_S qfline_T; |
60 | struct qfline_S { |
61 | qfline_T *qf_next; ///< pointer to next error in the list |
62 | qfline_T *qf_prev; ///< pointer to previous error in the list |
63 | linenr_T qf_lnum; ///< line number where the error occurred |
64 | int qf_fnum; ///< file number for the line |
65 | int qf_col; ///< column where the error occurred |
66 | int qf_nr; ///< error number |
67 | char_u *qf_module; ///< module name for this error |
68 | char_u *qf_pattern; ///< search pattern for the error |
69 | char_u *qf_text; ///< description of the error |
70 | char_u qf_viscol; ///< set to TRUE if qf_col is screen column |
71 | char_u qf_cleared; ///< set to TRUE if line has been deleted |
72 | char_u qf_type; ///< type of the error (mostly 'E'); 1 for |
73 | // :helpgrep |
74 | char_u qf_valid; ///< valid error message detected |
75 | }; |
76 | |
77 | /* |
78 | * There is a stack of error lists. |
79 | */ |
80 | #define LISTCOUNT 10 |
81 | #define INVALID_QFIDX (-1) |
82 | |
83 | /// Quickfix/Location list definition |
84 | /// |
85 | /// Usually the list contains one or more entries. But an empty list can be |
86 | /// created using setqflist()/setloclist() with a title and/or user context |
87 | /// information and entries can be added later using setqflist()/setloclist(). |
88 | typedef struct qf_list_S { |
89 | unsigned qf_id; ///< Unique identifier for this list |
90 | qfline_T *qf_start; ///< pointer to the first error |
91 | qfline_T *qf_last; ///< pointer to the last error |
92 | qfline_T *qf_ptr; ///< pointer to the current error |
93 | int qf_count; ///< number of errors (0 means empty list) |
94 | int qf_index; ///< current index in the error list |
95 | int qf_nonevalid; ///< TRUE if not a single valid entry found |
96 | char_u *qf_title; ///< title derived from the command that created |
97 | ///< the error list or set by setqflist |
98 | typval_T *qf_ctx; ///< context set by setqflist/setloclist |
99 | |
100 | struct dir_stack_T *qf_dir_stack; |
101 | char_u *qf_directory; |
102 | struct dir_stack_T *qf_file_stack; |
103 | char_u *qf_currfile; |
104 | bool qf_multiline; |
105 | bool qf_multiignore; |
106 | bool qf_multiscan; |
107 | long qf_changedtick; |
108 | } qf_list_T; |
109 | |
110 | /// Quickfix/Location list stack definition |
111 | /// Contains a list of quickfix/location lists (qf_list_T) |
112 | struct qf_info_S { |
113 | /* |
114 | * Count of references to this list. Used only for location lists. |
115 | * When a location list window reference this list, qf_refcount |
116 | * will be 2. Otherwise, qf_refcount will be 1. When qf_refcount |
117 | * reaches 0, the list is freed. |
118 | */ |
119 | int qf_refcount; |
120 | int qf_listcount; /* current number of lists */ |
121 | int qf_curlist; /* current error list */ |
122 | qf_list_T qf_lists[LISTCOUNT]; |
123 | }; |
124 | |
125 | static qf_info_T ql_info; // global quickfix list |
126 | static unsigned last_qf_id = 0; // Last Used quickfix list id |
127 | |
128 | #define FMT_PATTERNS 11 // maximum number of % recognized |
129 | |
130 | /* |
131 | * Structure used to hold the info of one part of 'errorformat' |
132 | */ |
133 | typedef struct efm_S efm_T; |
134 | struct efm_S { |
135 | regprog_T *prog; /* pre-formatted part of 'errorformat' */ |
136 | efm_T *next; /* pointer to next (NULL if last) */ |
137 | char_u addr[FMT_PATTERNS]; /* indices of used % patterns */ |
138 | char_u prefix; /* prefix of this format line: */ |
139 | /* 'D' enter directory */ |
140 | /* 'X' leave directory */ |
141 | /* 'A' start of multi-line message */ |
142 | /* 'E' error message */ |
143 | /* 'W' warning message */ |
144 | /* 'I' informational message */ |
145 | /* 'C' continuation line */ |
146 | /* 'Z' end of multi-line message */ |
147 | /* 'G' general, unspecific message */ |
148 | /* 'P' push file (partial) message */ |
149 | /* 'Q' pop/quit file (partial) message */ |
150 | /* 'O' overread (partial) message */ |
151 | char_u flags; /* additional flags given in prefix */ |
152 | /* '-' do not include this line */ |
153 | /* '+' include whole line in message */ |
154 | int conthere; /* %> used */ |
155 | }; |
156 | |
157 | enum { |
158 | QF_FAIL = 0, |
159 | QF_OK = 1, |
160 | QF_END_OF_INPUT = 2, |
161 | QF_NOMEM = 3, |
162 | QF_IGNORE_LINE = 4, |
163 | QF_MULTISCAN = 5, |
164 | }; |
165 | |
166 | typedef struct { |
167 | char_u *linebuf; |
168 | size_t linelen; |
169 | char_u *growbuf; |
170 | size_t growbufsiz; |
171 | FILE *fd; |
172 | typval_T *tv; |
173 | char_u *p_str; |
174 | list_T *p_list; |
175 | listitem_T *p_li; |
176 | buf_T *buf; |
177 | linenr_T buflnum; |
178 | linenr_T lnumlast; |
179 | vimconv_T vc; |
180 | } qfstate_T; |
181 | |
182 | typedef struct { |
183 | char_u *namebuf; |
184 | char_u *module; |
185 | char_u *errmsg; |
186 | size_t errmsglen; |
187 | long lnum; |
188 | int col; |
189 | bool use_viscol; |
190 | char_u *pattern; |
191 | int enr; |
192 | char_u type; |
193 | bool valid; |
194 | } qffields_T; |
195 | |
196 | #ifdef INCLUDE_GENERATED_DECLARATIONS |
197 | # include "quickfix.c.generated.h" |
198 | #endif |
199 | /* Quickfix window check helper macro */ |
200 | #define IS_QF_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref == NULL) |
201 | /* Location list window check helper macro */ |
202 | #define IS_LL_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL) |
203 | |
204 | // Quickfix and location list stack check helper macros |
205 | #define IS_QF_STACK(qi) (qi == &ql_info) |
206 | #define IS_LL_STACK(qi) (qi != &ql_info) |
207 | |
208 | // |
209 | // Return location list for window 'wp' |
210 | // For location list window, return the referenced location list |
211 | // |
212 | #define GET_LOC_LIST(wp) (IS_LL_WINDOW(wp) ? wp->w_llist_ref : wp->w_llist) |
213 | |
214 | // Looking up a buffer can be slow if there are many. Remember the last one |
215 | // to make this a lot faster if there are multiple matches in the same file. |
216 | static char_u *qf_last_bufname = NULL; |
217 | static bufref_T qf_last_bufref = { NULL, 0, 0 }; |
218 | |
219 | static char *e_loc_list_changed = N_("E926: Current location list was changed" ); |
220 | |
221 | /// Read the errorfile "efile" into memory, line by line, building the error |
222 | /// list. Set the error list's title to qf_title. |
223 | /// |
224 | /// @params wp If non-NULL, make a location list |
225 | /// @params efile If non-NULL, errorfile to parse |
226 | /// @params errorformat 'errorformat' string used to parse the error lines |
227 | /// @params newlist If true, create a new error list |
228 | /// @params qf_title If non-NULL, title of the error list |
229 | /// @params enc If non-NULL, encoding used to parse errors |
230 | /// |
231 | /// @returns -1 for error, number of errors for success. |
232 | int qf_init(win_T *wp, char_u *efile, char_u *errorformat, int newlist, |
233 | char_u *qf_title, char_u *enc) |
234 | { |
235 | qf_info_T *qi = &ql_info; |
236 | |
237 | if (wp != NULL) { |
238 | qi = ll_get_or_alloc_list(wp); |
239 | } |
240 | |
241 | return qf_init_ext(qi, qi->qf_curlist, efile, curbuf, NULL, errorformat, |
242 | newlist, (linenr_T)0, (linenr_T)0, qf_title, enc); |
243 | } |
244 | |
245 | // Maximum number of bytes allowed per line while reading an errorfile. |
246 | static const size_t LINE_MAXLEN = 4096; |
247 | |
248 | static struct fmtpattern |
249 | { |
250 | char_u convchar; |
251 | char *pattern; |
252 | } fmt_pat[FMT_PATTERNS] = |
253 | { |
254 | { 'f', ".\\+" }, // only used when at end |
255 | { 'n', "\\d\\+" }, |
256 | { 'l', "\\d\\+" }, |
257 | { 'c', "\\d\\+" }, |
258 | { 't', "." }, |
259 | { 'm', ".\\+" }, |
260 | { 'r', ".*" }, |
261 | { 'p', "[- .]*" }, // NOLINT(whitespace/tab) |
262 | { 'v', "\\d\\+" }, |
263 | { 's', ".\\+" }, |
264 | { 'o', ".\\+" } |
265 | }; |
266 | |
267 | // Converts a 'errorformat' string to regular expression pattern |
268 | static int efm_to_regpat(char_u *efm, int len, efm_T *fmt_ptr, |
269 | char_u *regpat, char_u *errmsg) |
270 | { |
271 | // Build regexp pattern from current 'errorformat' option |
272 | char_u *ptr = regpat; |
273 | *ptr++ = '^'; |
274 | int round = 0; |
275 | for (char_u *efmp = efm; efmp < efm + len; efmp++) { |
276 | if (*efmp == '%') { |
277 | efmp++; |
278 | int idx; |
279 | for (idx = 0; idx < FMT_PATTERNS; idx++) { |
280 | if (fmt_pat[idx].convchar == *efmp) { |
281 | break; |
282 | } |
283 | } |
284 | if (idx < FMT_PATTERNS) { |
285 | if (fmt_ptr->addr[idx]) { |
286 | snprintf((char *)errmsg, CMDBUFFSIZE + 1, |
287 | _("E372: Too many %%%c in format string" ), *efmp); |
288 | EMSG(errmsg); |
289 | return -1; |
290 | } |
291 | if ((idx |
292 | && idx < 6 |
293 | && vim_strchr((char_u *)"DXOPQ" , fmt_ptr->prefix) != NULL) |
294 | || (idx == 6 |
295 | && vim_strchr((char_u *)"OPQ" , fmt_ptr->prefix) == NULL)) { |
296 | snprintf((char *)errmsg, CMDBUFFSIZE + 1, |
297 | _("E373: Unexpected %%%c in format string" ), *efmp); |
298 | EMSG(errmsg); |
299 | return -1; |
300 | } |
301 | round++; |
302 | fmt_ptr->addr[idx] = (char_u)round; |
303 | *ptr++ = '\\'; |
304 | *ptr++ = '('; |
305 | #ifdef BACKSLASH_IN_FILENAME |
306 | if (*efmp == 'f') { |
307 | // Also match "c:" in the file name, even when |
308 | // checking for a colon next: "%f:". |
309 | // "\%(\a:\)\=" |
310 | STRCPY(ptr, "\\%(\\a:\\)\\=" ); |
311 | ptr += 10; |
312 | } |
313 | #endif |
314 | if (*efmp == 'f' && efmp[1] != NUL) { |
315 | if (efmp[1] != '\\' && efmp[1] != '%') { |
316 | // A file name may contain spaces, but this isn't |
317 | // in "\f". For "%f:%l:%m" there may be a ":" in |
318 | // the file name. Use ".\{-1,}x" instead (x is |
319 | // the next character), the requirement that :999: |
320 | // follows should work. |
321 | STRCPY(ptr, ".\\{-1,}" ); |
322 | ptr += 7; |
323 | } else { |
324 | // File name followed by '\\' or '%': include as |
325 | // many file name chars as possible. |
326 | STRCPY(ptr, "\\f\\+" ); |
327 | ptr += 4; |
328 | } |
329 | } else { |
330 | char_u *srcptr = (char_u *)fmt_pat[idx].pattern; |
331 | while ((*ptr = *srcptr++) != NUL) { |
332 | ptr++; |
333 | } |
334 | } |
335 | *ptr++ = '\\'; |
336 | *ptr++ = ')'; |
337 | } else if (*efmp == '*') { |
338 | if (*++efmp == '[' || *efmp == '\\') { |
339 | if ((*ptr++ = *efmp) == '[') { // %*[^a-z0-9] etc. |
340 | if (efmp[1] == '^') { |
341 | *ptr++ = *++efmp; |
342 | } |
343 | if (efmp < efm + len) { |
344 | efmp++; |
345 | *ptr++ = *efmp; // could be ']' |
346 | while (efmp < efm + len) { |
347 | efmp++; |
348 | if ((*ptr++ = *efmp) == ']') { |
349 | break; |
350 | } |
351 | } |
352 | if (efmp == efm + len) { |
353 | EMSG(_("E374: Missing ] in format string" )); |
354 | return -1; |
355 | } |
356 | } |
357 | } else if (efmp < efm + len) { // %*\D, %*\s etc. |
358 | efmp++; |
359 | *ptr++ = *efmp; |
360 | } |
361 | *ptr++ = '\\'; |
362 | *ptr++ = '+'; |
363 | } else { |
364 | // TODO(vim): scanf()-like: %*ud, %*3c, %*f, ... ? |
365 | snprintf((char *)errmsg, CMDBUFFSIZE + 1, |
366 | _("E375: Unsupported %%%c in format string" ), *efmp); |
367 | EMSG(errmsg); |
368 | return -1; |
369 | } |
370 | } else if (vim_strchr((char_u *)"%\\.^$~[" , *efmp) != NULL) { |
371 | *ptr++ = *efmp; // regexp magic characters |
372 | } else if (*efmp == '#') { |
373 | *ptr++ = '*'; |
374 | } else if (*efmp == '>') { |
375 | fmt_ptr->conthere = true; |
376 | } else if (efmp == efm + 1) { // analyse prefix |
377 | if (vim_strchr((char_u *)"+-" , *efmp) != NULL) { |
378 | fmt_ptr->flags = *efmp++; |
379 | } |
380 | if (vim_strchr((char_u *)"DXAEWICZGOPQ" , *efmp) != NULL) { |
381 | fmt_ptr->prefix = *efmp; |
382 | } else { |
383 | snprintf((char *)errmsg, CMDBUFFSIZE + 1, |
384 | _("E376: Invalid %%%c in format string prefix" ), *efmp); |
385 | EMSG(errmsg); |
386 | return -1; |
387 | } |
388 | } else { |
389 | snprintf((char *)errmsg, CMDBUFFSIZE + 1, |
390 | _("E377: Invalid %%%c in format string" ), *efmp); |
391 | EMSG(errmsg); |
392 | return -1; |
393 | } |
394 | } else { // copy normal character |
395 | if (*efmp == '\\' && efmp + 1 < efm + len) { |
396 | efmp++; |
397 | } else if (vim_strchr((char_u *)".*^$~[" , *efmp) != NULL) { |
398 | *ptr++ = '\\'; // escape regexp atoms |
399 | } |
400 | if (*efmp) { |
401 | *ptr++ = *efmp; |
402 | } |
403 | } |
404 | } |
405 | *ptr++ = '$'; |
406 | *ptr = NUL; |
407 | |
408 | return 0; |
409 | } |
410 | |
411 | static efm_T *fmt_start = NULL; // cached across qf_parse_line() calls |
412 | |
413 | static void free_efm_list(efm_T **efm_first) |
414 | { |
415 | for (efm_T *efm_ptr = *efm_first; efm_ptr != NULL; efm_ptr = *efm_first) { |
416 | *efm_first = efm_ptr->next; |
417 | vim_regfree(efm_ptr->prog); |
418 | xfree(efm_ptr); |
419 | } |
420 | |
421 | fmt_start = NULL; |
422 | } |
423 | |
424 | // Parse 'errorformat' option |
425 | static efm_T * parse_efm_option(char_u *efm) |
426 | { |
427 | efm_T *fmt_ptr = NULL; |
428 | efm_T *fmt_first = NULL; |
429 | efm_T *fmt_last = NULL; |
430 | int len; |
431 | |
432 | size_t errmsglen = CMDBUFFSIZE + 1; |
433 | char_u *errmsg = xmalloc(errmsglen); |
434 | |
435 | // Get some space to modify the format string into. |
436 | size_t i = (FMT_PATTERNS * 3) + (STRLEN(efm) << 2); |
437 | for (int round = FMT_PATTERNS - 1; round >= 0; ) { |
438 | i += STRLEN(fmt_pat[round--].pattern); |
439 | } |
440 | #ifdef BACKSLASH_IN_FILENAME |
441 | i += 12; // "%f" can become twelve chars longer (see efm_to_regpat) |
442 | #else |
443 | i += 2; // "%f" can become two chars longer |
444 | #endif |
445 | char_u *fmtstr = xmalloc(i); |
446 | |
447 | while (efm[0] != NUL) { |
448 | // Allocate a new eformat structure and put it at the end of the list |
449 | fmt_ptr = (efm_T *)xcalloc(1, sizeof(efm_T)); |
450 | if (fmt_first == NULL) { // first one |
451 | fmt_first = fmt_ptr; |
452 | } else { |
453 | fmt_last->next = fmt_ptr; |
454 | } |
455 | fmt_last = fmt_ptr; |
456 | |
457 | // Isolate one part in the 'errorformat' option |
458 | for (len = 0; efm[len] != NUL && efm[len] != ','; len++) { |
459 | if (efm[len] == '\\' && efm[len + 1] != NUL) { |
460 | len++; |
461 | } |
462 | } |
463 | |
464 | if (efm_to_regpat(efm, len, fmt_ptr, fmtstr, errmsg) == -1) { |
465 | goto parse_efm_error; |
466 | } |
467 | if ((fmt_ptr->prog = vim_regcomp(fmtstr, RE_MAGIC + RE_STRING)) == NULL) { |
468 | goto parse_efm_error; |
469 | } |
470 | // Advance to next part |
471 | efm = skip_to_option_part(efm + len); // skip comma and spaces |
472 | } |
473 | |
474 | if (fmt_first == NULL) { // nothing found |
475 | EMSG(_("E378: 'errorformat' contains no pattern" )); |
476 | } |
477 | |
478 | goto parse_efm_end; |
479 | |
480 | parse_efm_error: |
481 | free_efm_list(&fmt_first); |
482 | |
483 | parse_efm_end: |
484 | xfree(fmtstr); |
485 | xfree(errmsg); |
486 | |
487 | return fmt_first; |
488 | } |
489 | |
490 | static char_u *qf_grow_linebuf(qfstate_T *state, size_t newsz) |
491 | { |
492 | // If the line exceeds LINE_MAXLEN exclude the last |
493 | // byte since it's not a NL character. |
494 | state->linelen = newsz > LINE_MAXLEN ? LINE_MAXLEN - 1 : newsz; |
495 | if (state->growbuf == NULL) { |
496 | state->growbuf = xmalloc(state->linelen + 1); |
497 | state->growbufsiz = state->linelen; |
498 | } else if (state->linelen > state->growbufsiz) { |
499 | state->growbuf = xrealloc(state->growbuf, state->linelen + 1); |
500 | state->growbufsiz = state->linelen; |
501 | } |
502 | return state->growbuf; |
503 | } |
504 | |
505 | /// Get the next string (separated by newline) from state->p_str. |
506 | static int qf_get_next_str_line(qfstate_T *state) |
507 | { |
508 | // Get the next line from the supplied string |
509 | char_u *p_str = state->p_str; |
510 | char_u *p; |
511 | size_t len; |
512 | |
513 | if (*p_str == NUL) { // Reached the end of the string |
514 | return QF_END_OF_INPUT; |
515 | } |
516 | |
517 | p = vim_strchr(p_str, '\n'); |
518 | if (p != NULL) { |
519 | len = (size_t)(p - p_str) + 1; |
520 | } else { |
521 | len = STRLEN(p_str); |
522 | } |
523 | |
524 | if (len > IOSIZE - 2) { |
525 | state->linebuf = qf_grow_linebuf(state, len); |
526 | } else { |
527 | state->linebuf = IObuff; |
528 | state->linelen = len; |
529 | } |
530 | STRLCPY(state->linebuf, p_str, state->linelen + 1); |
531 | |
532 | // Increment using len in order to discard the rest of the line if it |
533 | // exceeds LINE_MAXLEN. |
534 | p_str += len; |
535 | state->p_str = p_str; |
536 | |
537 | return QF_OK; |
538 | } |
539 | |
540 | /// Get the next string from state->p_Li. |
541 | static int qf_get_next_list_line(qfstate_T *state) |
542 | { |
543 | listitem_T *p_li = state->p_li; |
544 | size_t len; |
545 | |
546 | // Get the next line from the supplied list |
547 | while (p_li != NULL |
548 | && (TV_LIST_ITEM_TV(p_li)->v_type != VAR_STRING |
549 | || TV_LIST_ITEM_TV(p_li)->vval.v_string == NULL)) { |
550 | p_li = TV_LIST_ITEM_NEXT(state->p_list, p_li); // Skip non-string items. |
551 | } |
552 | |
553 | if (p_li == NULL) { // End of the list. |
554 | state->p_li = NULL; |
555 | return QF_END_OF_INPUT; |
556 | } |
557 | |
558 | len = STRLEN(TV_LIST_ITEM_TV(p_li)->vval.v_string); |
559 | if (len > IOSIZE - 2) { |
560 | state->linebuf = qf_grow_linebuf(state, len); |
561 | } else { |
562 | state->linebuf = IObuff; |
563 | state->linelen = len; |
564 | } |
565 | |
566 | STRLCPY(state->linebuf, TV_LIST_ITEM_TV(p_li)->vval.v_string, |
567 | state->linelen + 1); |
568 | |
569 | state->p_li = TV_LIST_ITEM_NEXT(state->p_list, p_li); |
570 | return QF_OK; |
571 | } |
572 | |
573 | /// Get the next string from state->buf. |
574 | static int qf_get_next_buf_line(qfstate_T *state) |
575 | { |
576 | char_u *p_buf = NULL; |
577 | size_t len; |
578 | |
579 | // Get the next line from the supplied buffer |
580 | if (state->buflnum > state->lnumlast) { |
581 | return QF_END_OF_INPUT; |
582 | } |
583 | p_buf = ml_get_buf(state->buf, state->buflnum, false); |
584 | state->buflnum += 1; |
585 | |
586 | len = STRLEN(p_buf); |
587 | if (len > IOSIZE - 2) { |
588 | state->linebuf = qf_grow_linebuf(state, len); |
589 | } else { |
590 | state->linebuf = IObuff; |
591 | state->linelen = len; |
592 | } |
593 | STRLCPY(state->linebuf, p_buf, state->linelen + 1); |
594 | |
595 | return QF_OK; |
596 | } |
597 | |
598 | /// Get the next string from file state->fd. |
599 | static int qf_get_next_file_line(qfstate_T *state) |
600 | { |
601 | size_t growbuflen; |
602 | |
603 | retry: |
604 | errno = 0; |
605 | if (fgets((char *)IObuff, IOSIZE, state->fd) == NULL) { |
606 | if (errno == EINTR) { |
607 | goto retry; |
608 | } |
609 | return QF_END_OF_INPUT; |
610 | } |
611 | |
612 | bool discard = false; |
613 | state->linelen = STRLEN(IObuff); |
614 | if (state->linelen == IOSIZE - 1 |
615 | && !(IObuff[state->linelen - 1] == '\n')) { |
616 | // The current line exceeds IObuff, continue reading using growbuf |
617 | // until EOL or LINE_MAXLEN bytes is read. |
618 | if (state->growbuf == NULL) { |
619 | state->growbufsiz = 2 * (IOSIZE - 1); |
620 | state->growbuf = xmalloc(state->growbufsiz); |
621 | } |
622 | |
623 | // Copy the read part of the line, excluding null-terminator |
624 | memcpy(state->growbuf, IObuff, IOSIZE - 1); |
625 | growbuflen = state->linelen; |
626 | |
627 | for (;;) { |
628 | errno = 0; |
629 | if (fgets((char *)state->growbuf + growbuflen, |
630 | (int)(state->growbufsiz - growbuflen), state->fd) == NULL) { |
631 | if (errno == EINTR) { |
632 | continue; |
633 | } |
634 | break; |
635 | } |
636 | state->linelen = STRLEN(state->growbuf + growbuflen); |
637 | growbuflen += state->linelen; |
638 | if (state->growbuf[growbuflen - 1] == '\n') { |
639 | break; |
640 | } |
641 | if (state->growbufsiz == LINE_MAXLEN) { |
642 | discard = true; |
643 | break; |
644 | } |
645 | |
646 | state->growbufsiz = (2 * state->growbufsiz < LINE_MAXLEN) |
647 | ? 2 * state->growbufsiz : LINE_MAXLEN; |
648 | state->growbuf = xrealloc(state->growbuf, state->growbufsiz); |
649 | } |
650 | |
651 | while (discard) { |
652 | // The current line is longer than LINE_MAXLEN, continue reading but |
653 | // discard everything until EOL or EOF is reached. |
654 | errno = 0; |
655 | if (fgets((char *)IObuff, IOSIZE, state->fd) == NULL) { |
656 | if (errno == EINTR) { |
657 | continue; |
658 | } |
659 | break; |
660 | } |
661 | if (STRLEN(IObuff) < IOSIZE - 1 || IObuff[IOSIZE - 1] == '\n') { |
662 | break; |
663 | } |
664 | } |
665 | |
666 | state->linebuf = state->growbuf; |
667 | state->linelen = growbuflen; |
668 | } else { |
669 | state->linebuf = IObuff; |
670 | } |
671 | |
672 | // Convert a line if it contains a non-ASCII character |
673 | if (state->vc.vc_type != CONV_NONE && has_non_ascii(state->linebuf)) { |
674 | char_u *line = string_convert(&state->vc, state->linebuf, &state->linelen); |
675 | if (line != NULL) { |
676 | if (state->linelen < IOSIZE) { |
677 | STRLCPY(state->linebuf, line, state->linelen + 1); |
678 | xfree(line); |
679 | } else { |
680 | xfree(state->growbuf); |
681 | state->linebuf = state->growbuf = line; |
682 | state->growbufsiz = state->linelen < LINE_MAXLEN |
683 | ? state->linelen : LINE_MAXLEN; |
684 | } |
685 | } |
686 | } |
687 | return QF_OK; |
688 | } |
689 | |
690 | /// Get the next string from a file/buffer/list/string. |
691 | static int qf_get_nextline(qfstate_T *state) |
692 | { |
693 | int status = QF_FAIL; |
694 | |
695 | if (state->fd == NULL) { |
696 | if (state->tv != NULL) { |
697 | if (state->tv->v_type == VAR_STRING) { |
698 | // Get the next line from the supplied string |
699 | status = qf_get_next_str_line(state); |
700 | } else if (state->tv->v_type == VAR_LIST) { |
701 | // Get the next line from the supplied list |
702 | status = qf_get_next_list_line(state); |
703 | } |
704 | } else { |
705 | // Get the next line from the supplied buffer |
706 | status = qf_get_next_buf_line(state); |
707 | } |
708 | } else { |
709 | // Get the next line from the supplied file |
710 | status = qf_get_next_file_line(state); |
711 | } |
712 | |
713 | if (status != QF_OK) { |
714 | return status; |
715 | } |
716 | |
717 | if (state->linelen > 0 && state->linebuf[state->linelen - 1] == '\n') { |
718 | state->linebuf[state->linelen - 1] = NUL; |
719 | #ifdef USE_CRNL |
720 | if (state->linelen > 1 && state->linebuf[state->linelen - 2] == '\r') { |
721 | state->linebuf[state->linelen - 2] = NUL; |
722 | } |
723 | #endif |
724 | } |
725 | |
726 | remove_bom(state->linebuf); |
727 | |
728 | return QF_OK; |
729 | } |
730 | |
731 | // Returns true if the specified quickfix/location list is empty. |
732 | static bool qf_list_empty(const qf_info_T *qi, int qf_idx) |
733 | FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT |
734 | { |
735 | if (qi == NULL || qf_idx < 0 || qf_idx >= LISTCOUNT) { |
736 | return true; |
737 | } |
738 | return qi->qf_lists[qf_idx].qf_count <= 0; |
739 | } |
740 | |
741 | /// Parse a line and get the quickfix fields. |
742 | /// Return the QF_ status. |
743 | static int qf_parse_line(qf_info_T *qi, int qf_idx, char_u *linebuf, |
744 | size_t linelen, efm_T *fmt_first, qffields_T *fields) |
745 | { |
746 | efm_T *fmt_ptr; |
747 | int idx = 0; |
748 | char_u *tail = NULL; |
749 | qf_list_T *qfl = &qi->qf_lists[qf_idx]; |
750 | int status; |
751 | |
752 | restofline: |
753 | // If there was no %> item start at the first pattern |
754 | if (fmt_start == NULL) { |
755 | fmt_ptr = fmt_first; |
756 | } else { |
757 | // Otherwise start from the last used pattern. |
758 | fmt_ptr = fmt_start; |
759 | fmt_start = NULL; |
760 | } |
761 | |
762 | // Try to match each part of 'errorformat' until we find a complete |
763 | // match or no match. |
764 | fields->valid = true; |
765 | for (; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next) { |
766 | idx = fmt_ptr->prefix; |
767 | status = qf_parse_get_fields(linebuf, linelen, fmt_ptr, fields, |
768 | qfl->qf_multiline, qfl->qf_multiscan, |
769 | &tail); |
770 | if (status == QF_NOMEM) { |
771 | return status; |
772 | } |
773 | if (status == QF_OK) { |
774 | break; |
775 | } |
776 | } |
777 | qfl->qf_multiscan = false; |
778 | |
779 | if (fmt_ptr == NULL || idx == 'D' || idx == 'X') { |
780 | if (fmt_ptr != NULL) { |
781 | // 'D' and 'X' directory specifiers. |
782 | status = qf_parse_dir_pfx(idx, fields, qfl); |
783 | if (status != QF_OK) { |
784 | return status; |
785 | } |
786 | } |
787 | status = qf_parse_line_nomatch(linebuf, linelen, fields); |
788 | if (status != QF_OK) { |
789 | return status; |
790 | } |
791 | if (fmt_ptr == NULL) { |
792 | qfl->qf_multiline = qfl->qf_multiignore = false; |
793 | } |
794 | } else { |
795 | // honor %> item |
796 | if (fmt_ptr->conthere) { |
797 | fmt_start = fmt_ptr; |
798 | } |
799 | |
800 | if (vim_strchr((char_u *)"AEWI" , idx) != NULL) { |
801 | qfl->qf_multiline = true; // start of a multi-line message |
802 | qfl->qf_multiignore = false; // reset continuation |
803 | } else if (vim_strchr((char_u *)"CZ" , idx) != NULL) { |
804 | // continuation of multi-line msg |
805 | status = qf_parse_multiline_pfx(qi, qf_idx, idx, qfl, fields); |
806 | if (status != QF_OK) { |
807 | return status; |
808 | } |
809 | } else if (vim_strchr((char_u *)"OPQ" , idx) != NULL) { |
810 | // global file names |
811 | status = qf_parse_file_pfx(idx, fields, qfl, tail); |
812 | if (status == QF_MULTISCAN) { |
813 | goto restofline; |
814 | } |
815 | } |
816 | if (fmt_ptr->flags == '-') { // generally exclude this line |
817 | if (qfl->qf_multiline) { |
818 | // also exclude continuation lines |
819 | qfl->qf_multiignore = true; |
820 | } |
821 | return QF_IGNORE_LINE; |
822 | } |
823 | } |
824 | |
825 | return QF_OK; |
826 | } |
827 | |
828 | // Read the errorfile "efile" into memory, line by line, building the error |
829 | // list. |
830 | // Alternative: when "efile" is NULL read errors from buffer "buf". |
831 | // Alternative: when "tv" is not NULL get errors from the string or list. |
832 | // Always use 'errorformat' from "buf" if there is a local value. |
833 | // Then "lnumfirst" and "lnumlast" specify the range of lines to use. |
834 | // Set the title of the list to "qf_title". |
835 | // Return -1 for error, number of errors for success. |
836 | static int |
837 | qf_init_ext( |
838 | qf_info_T *qi, |
839 | int qf_idx, |
840 | char_u *efile, |
841 | buf_T *buf, |
842 | typval_T *tv, |
843 | char_u *errorformat, |
844 | int newlist, // TRUE: start a new error list |
845 | linenr_T lnumfirst, // first line number to use |
846 | linenr_T lnumlast, // last line number to use |
847 | char_u *qf_title, |
848 | char_u *enc |
849 | ) |
850 | { |
851 | qfstate_T state; |
852 | qffields_T fields; |
853 | qfline_T *old_last = NULL; |
854 | bool adding = false; |
855 | static efm_T *fmt_first = NULL; |
856 | char_u *efm; |
857 | static char_u *last_efm = NULL; |
858 | int retval = -1; // default: return error flag |
859 | int status; |
860 | |
861 | // Do not used the cached buffer, it may have been wiped out. |
862 | XFREE_CLEAR(qf_last_bufname); |
863 | |
864 | memset(&state, 0, sizeof(state)); |
865 | memset(&fields, 0, sizeof(fields)); |
866 | state.vc.vc_type = CONV_NONE; |
867 | if (enc != NULL && *enc != NUL) { |
868 | convert_setup(&state.vc, enc, p_enc); |
869 | } |
870 | |
871 | fields.namebuf = xmalloc(CMDBUFFSIZE + 1); |
872 | fields.module = xmalloc(CMDBUFFSIZE + 1); |
873 | fields.errmsglen = CMDBUFFSIZE + 1; |
874 | fields.errmsg = xmalloc(fields.errmsglen); |
875 | fields.pattern = xmalloc(CMDBUFFSIZE + 1); |
876 | |
877 | if (efile != NULL && (state.fd = os_fopen((char *)efile, "r" )) == NULL) { |
878 | EMSG2(_(e_openerrf), efile); |
879 | goto qf_init_end; |
880 | } |
881 | |
882 | if (newlist || qf_idx == qi->qf_listcount) { |
883 | // make place for a new list |
884 | qf_new_list(qi, qf_title); |
885 | qf_idx = qi->qf_curlist; |
886 | } else { |
887 | // Adding to existing list, use last entry. |
888 | adding = true; |
889 | if (qi->qf_lists[qf_idx].qf_count > 0) { |
890 | old_last = qi->qf_lists[qf_idx].qf_last; |
891 | } |
892 | } |
893 | |
894 | qf_list_T *qfl = &qi->qf_lists[qf_idx]; |
895 | |
896 | // Use the local value of 'errorformat' if it's set. |
897 | if (errorformat == p_efm && tv == NULL && buf && *buf->b_p_efm != NUL) { |
898 | efm = buf->b_p_efm; |
899 | } else { |
900 | efm = errorformat; |
901 | } |
902 | |
903 | // If the errorformat didn't change between calls, then reuse the previously |
904 | // parsed values. |
905 | if (last_efm == NULL || (STRCMP(last_efm, efm) != 0)) { |
906 | // free the previously parsed data |
907 | XFREE_CLEAR(last_efm); |
908 | free_efm_list(&fmt_first); |
909 | |
910 | // parse the current 'efm' |
911 | fmt_first = parse_efm_option(efm); |
912 | if (fmt_first != NULL) { |
913 | last_efm = vim_strsave(efm); |
914 | } |
915 | } |
916 | |
917 | if (fmt_first == NULL) { // nothing found |
918 | goto error2; |
919 | } |
920 | |
921 | /* |
922 | * got_int is reset here, because it was probably set when killing the |
923 | * ":make" command, but we still want to read the errorfile then. |
924 | */ |
925 | got_int = FALSE; |
926 | |
927 | if (tv != NULL) { |
928 | if (tv->v_type == VAR_STRING) { |
929 | state.p_str = tv->vval.v_string; |
930 | } else if (tv->v_type == VAR_LIST) { |
931 | state.p_list = tv->vval.v_list; |
932 | state.p_li = tv_list_first(tv->vval.v_list); |
933 | } |
934 | state.tv = tv; |
935 | } |
936 | state.buf = buf; |
937 | state.buflnum = lnumfirst; |
938 | state.lnumlast = lnumlast; |
939 | |
940 | /* |
941 | * Read the lines in the error file one by one. |
942 | * Try to recognize one of the error formats in each line. |
943 | */ |
944 | while (!got_int) { |
945 | // Get the next line from a file/buffer/list/string |
946 | status = qf_get_nextline(&state); |
947 | if (status == QF_END_OF_INPUT) { // end of input |
948 | break; |
949 | } |
950 | |
951 | status = qf_parse_line(qi, qf_idx, state.linebuf, state.linelen, |
952 | fmt_first, &fields); |
953 | if (status == QF_FAIL) { |
954 | goto error2; |
955 | } |
956 | if (status == QF_IGNORE_LINE) { |
957 | continue; |
958 | } |
959 | |
960 | if (qf_add_entry(qi, |
961 | qf_idx, |
962 | qfl->qf_directory, |
963 | (*fields.namebuf || qfl->qf_directory) |
964 | ? fields.namebuf : ((qfl->qf_currfile && fields.valid) |
965 | ? qfl->qf_currfile : (char_u *)NULL), |
966 | fields.module, |
967 | 0, |
968 | fields.errmsg, |
969 | fields.lnum, |
970 | fields.col, |
971 | fields.use_viscol, |
972 | fields.pattern, |
973 | fields.enr, |
974 | fields.type, |
975 | fields.valid) == FAIL) { |
976 | goto error2; |
977 | } |
978 | line_breakcheck(); |
979 | } |
980 | if (state.fd == NULL || !ferror(state.fd)) { |
981 | if (qfl->qf_index == 0) { |
982 | // no valid entry found |
983 | qfl->qf_ptr = qfl->qf_start; |
984 | qfl->qf_index = 1; |
985 | qfl->qf_nonevalid = true; |
986 | } else { |
987 | qfl->qf_nonevalid = false; |
988 | if (qfl->qf_ptr == NULL) { |
989 | qfl->qf_ptr = qfl->qf_start; |
990 | } |
991 | } |
992 | // return number of matches |
993 | retval = qfl->qf_count; |
994 | goto qf_init_end; |
995 | } |
996 | EMSG(_(e_readerrf)); |
997 | error2: |
998 | if (!adding) { |
999 | // Error when creating a new list. Free the new list |
1000 | qf_free(qi, qi->qf_curlist); |
1001 | qi->qf_listcount--; |
1002 | if (qi->qf_curlist > 0) { |
1003 | qi->qf_curlist--; |
1004 | } |
1005 | } |
1006 | qf_init_end: |
1007 | if (state.fd != NULL) { |
1008 | fclose(state.fd); |
1009 | } |
1010 | xfree(fields.namebuf); |
1011 | xfree(fields.module); |
1012 | xfree(fields.errmsg); |
1013 | xfree(fields.pattern); |
1014 | xfree(state.growbuf); |
1015 | |
1016 | if (qf_idx == qi->qf_curlist) { |
1017 | qf_update_buffer(qi, old_last); |
1018 | } |
1019 | |
1020 | if (state.vc.vc_type != CONV_NONE) { |
1021 | convert_setup(&state.vc, NULL, NULL); |
1022 | } |
1023 | |
1024 | return retval; |
1025 | } |
1026 | |
1027 | /// Set the title of the specified quickfix list. Frees the previous title. |
1028 | /// Prepends ':' to the title. |
1029 | static void qf_store_title(qf_info_T *qi, int qf_idx, const char_u *title) |
1030 | FUNC_ATTR_NONNULL_ARG(1) |
1031 | { |
1032 | XFREE_CLEAR(qi->qf_lists[qf_idx].qf_title); |
1033 | |
1034 | if (title != NULL) { |
1035 | size_t len = STRLEN(title) + 1; |
1036 | char_u *p = xmallocz(len); |
1037 | |
1038 | qi->qf_lists[qf_idx].qf_title = p; |
1039 | xstrlcpy((char *)p, (const char *)title, len + 1); |
1040 | } |
1041 | } |
1042 | |
1043 | /// The title of a quickfix/location list is set, by default, to the command |
1044 | /// that created the quickfix list with the ":" prefix. |
1045 | /// Create a quickfix list title string by prepending ":" to a user command. |
1046 | /// Returns a pointer to a static buffer with the title. |
1047 | static char_u * qf_cmdtitle(char_u *cmd) |
1048 | { |
1049 | static char_u qftitle_str[IOSIZE]; |
1050 | |
1051 | snprintf((char *)qftitle_str, IOSIZE, ":%s" , (char *)cmd); |
1052 | |
1053 | return qftitle_str; |
1054 | } |
1055 | |
1056 | // Prepare for adding a new quickfix list. If the current list is in the |
1057 | // middle of the stack, then all the following lists are freed and then |
1058 | // the new list is added. |
1059 | static void qf_new_list(qf_info_T *qi, char_u *qf_title) |
1060 | { |
1061 | int i; |
1062 | |
1063 | // If the current entry is not the last entry, delete entries beyond |
1064 | // the current entry. This makes it possible to browse in a tree-like |
1065 | // way with ":grep'. |
1066 | while (qi->qf_listcount > qi->qf_curlist + 1) |
1067 | qf_free(qi, --qi->qf_listcount); |
1068 | |
1069 | /* |
1070 | * When the stack is full, remove to oldest entry |
1071 | * Otherwise, add a new entry. |
1072 | */ |
1073 | if (qi->qf_listcount == LISTCOUNT) { |
1074 | qf_free(qi, 0); |
1075 | for (i = 1; i < LISTCOUNT; ++i) |
1076 | qi->qf_lists[i - 1] = qi->qf_lists[i]; |
1077 | qi->qf_curlist = LISTCOUNT - 1; |
1078 | } else |
1079 | qi->qf_curlist = qi->qf_listcount++; |
1080 | memset(&qi->qf_lists[qi->qf_curlist], 0, (size_t)(sizeof(qf_list_T))); |
1081 | qf_store_title(qi, qi->qf_curlist, qf_title); |
1082 | qi->qf_lists[qi->qf_curlist].qf_id = ++last_qf_id; |
1083 | } |
1084 | |
1085 | /// Parse the error format matches in 'regmatch' and set the values in 'fields'. |
1086 | /// fmt_ptr contains the 'efm' format specifiers/prefixes that have a match. |
1087 | /// Returns QF_OK if all the matches are successfully parsed. On failure, |
1088 | /// returns QF_FAIL or QF_NOMEM. |
1089 | static int qf_parse_match(char_u *linebuf, size_t linelen, efm_T *fmt_ptr, |
1090 | regmatch_T *regmatch, qffields_T *fields, |
1091 | int qf_multiline, int qf_multiscan, char_u **tail) |
1092 | { |
1093 | char_u idx = fmt_ptr->prefix; |
1094 | int i; |
1095 | size_t len; |
1096 | |
1097 | if ((idx == 'C' || idx == 'Z') && !qf_multiline) { |
1098 | return QF_FAIL; |
1099 | } |
1100 | if (vim_strchr((char_u *)"EWI" , idx) != NULL) { |
1101 | fields->type = idx; |
1102 | } else { |
1103 | fields->type = 0; |
1104 | } |
1105 | |
1106 | // Extract error message data from matched line. |
1107 | // We check for an actual submatch, because "\[" and "\]" in |
1108 | // the 'errorformat' may cause the wrong submatch to be used. |
1109 | if ((i = (int)fmt_ptr->addr[0]) > 0) { // %f |
1110 | if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) { |
1111 | return QF_FAIL; |
1112 | } |
1113 | |
1114 | // Expand ~/file and $HOME/file to full path. |
1115 | char_u c = *regmatch->endp[i]; |
1116 | *regmatch->endp[i] = NUL; |
1117 | expand_env(regmatch->startp[i], fields->namebuf, CMDBUFFSIZE); |
1118 | *regmatch->endp[i] = c; |
1119 | |
1120 | if (vim_strchr((char_u *)"OPQ" , idx) != NULL |
1121 | && !os_path_exists(fields->namebuf)) { |
1122 | return QF_FAIL; |
1123 | } |
1124 | } |
1125 | if ((i = (int)fmt_ptr->addr[1]) > 0) { // %n |
1126 | if (regmatch->startp[i] == NULL) { |
1127 | return QF_FAIL; |
1128 | } |
1129 | fields->enr = (int)atol((char *)regmatch->startp[i]); |
1130 | } |
1131 | if ((i = (int)fmt_ptr->addr[2]) > 0) { // %l |
1132 | if (regmatch->startp[i] == NULL) { |
1133 | return QF_FAIL; |
1134 | } |
1135 | fields->lnum = atol((char *)regmatch->startp[i]); |
1136 | } |
1137 | if ((i = (int)fmt_ptr->addr[3]) > 0) { // %c |
1138 | if (regmatch->startp[i] == NULL) { |
1139 | return QF_FAIL; |
1140 | } |
1141 | fields->col = (int)atol((char *)regmatch->startp[i]); |
1142 | } |
1143 | if ((i = (int)fmt_ptr->addr[4]) > 0) { // %t |
1144 | if (regmatch->startp[i] == NULL) { |
1145 | return QF_FAIL; |
1146 | } |
1147 | fields->type = *regmatch->startp[i]; |
1148 | } |
1149 | if (fmt_ptr->flags == '+' && !qf_multiscan) { // %+ |
1150 | if (linelen >= fields->errmsglen) { |
1151 | // linelen + null terminator |
1152 | fields->errmsg = xrealloc(fields->errmsg, linelen + 1); |
1153 | fields->errmsglen = linelen + 1; |
1154 | } |
1155 | STRLCPY(fields->errmsg, linebuf, linelen + 1); |
1156 | } else if ((i = (int)fmt_ptr->addr[5]) > 0) { // %m |
1157 | if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) { |
1158 | return QF_FAIL; |
1159 | } |
1160 | len = (size_t)(regmatch->endp[i] - regmatch->startp[i]); |
1161 | if (len >= fields->errmsglen) { |
1162 | // len + null terminator |
1163 | fields->errmsg = xrealloc(fields->errmsg, len + 1); |
1164 | fields->errmsglen = len + 1; |
1165 | } |
1166 | STRLCPY(fields->errmsg, regmatch->startp[i], len + 1); |
1167 | } |
1168 | if ((i = (int)fmt_ptr->addr[6]) > 0) { // %r |
1169 | if (regmatch->startp[i] == NULL) { |
1170 | return QF_FAIL; |
1171 | } |
1172 | *tail = regmatch->startp[i]; |
1173 | } |
1174 | if ((i = (int)fmt_ptr->addr[7]) > 0) { // %p |
1175 | if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) { |
1176 | return QF_FAIL; |
1177 | } |
1178 | fields->col = 0; |
1179 | char_u *match_ptr; |
1180 | for (match_ptr = regmatch->startp[i]; match_ptr != regmatch->endp[i]; |
1181 | match_ptr++) { |
1182 | fields->col++; |
1183 | if (*match_ptr == TAB) { |
1184 | fields->col += 7; |
1185 | fields->col -= fields->col % 8; |
1186 | } |
1187 | } |
1188 | fields->col++; |
1189 | fields->use_viscol = true; |
1190 | } |
1191 | if ((i = (int)fmt_ptr->addr[8]) > 0) { // %v |
1192 | if (regmatch->startp[i] == NULL) { |
1193 | return QF_FAIL; |
1194 | } |
1195 | fields->col = (int)atol((char *)regmatch->startp[i]); |
1196 | fields->use_viscol = true; |
1197 | } |
1198 | if ((i = (int)fmt_ptr->addr[9]) > 0) { // %s |
1199 | if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) { |
1200 | return QF_FAIL; |
1201 | } |
1202 | len = (size_t)(regmatch->endp[i] - regmatch->startp[i]); |
1203 | if (len > CMDBUFFSIZE - 5) { |
1204 | len = CMDBUFFSIZE - 5; |
1205 | } |
1206 | STRCPY(fields->pattern, "^\\V" ); |
1207 | STRNCAT(fields->pattern, regmatch->startp[i], len); |
1208 | fields->pattern[len + 3] = '\\'; |
1209 | fields->pattern[len + 4] = '$'; |
1210 | fields->pattern[len + 5] = NUL; |
1211 | } |
1212 | if ((i = (int)fmt_ptr->addr[10]) > 0) { // %o |
1213 | if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) { |
1214 | return QF_FAIL; |
1215 | } |
1216 | len = (size_t)(regmatch->endp[i] - regmatch->startp[i]); |
1217 | if (len > CMDBUFFSIZE) { |
1218 | len = CMDBUFFSIZE; |
1219 | } |
1220 | STRNCAT(fields->module, regmatch->startp[i], len); |
1221 | } |
1222 | |
1223 | return QF_OK; |
1224 | } |
1225 | |
1226 | /// Parse an error line in 'linebuf' using a single error format string in |
1227 | /// 'fmt_ptr->prog' and return the matching values in 'fields'. |
1228 | /// Returns QF_OK if the efm format matches completely and the fields are |
1229 | /// successfully copied. Otherwise returns QF_FAIL or QF_NOMEM. |
1230 | static int qf_parse_get_fields(char_u *linebuf, size_t linelen, efm_T *fmt_ptr, |
1231 | qffields_T *fields, int qf_multiline, |
1232 | int qf_multiscan, char_u **tail) |
1233 | { |
1234 | regmatch_T regmatch; |
1235 | int status = QF_FAIL; |
1236 | int r; |
1237 | |
1238 | if (qf_multiscan && vim_strchr((char_u *)"OPQ" , fmt_ptr->prefix) == NULL) { |
1239 | return QF_FAIL; |
1240 | } |
1241 | |
1242 | fields->namebuf[0] = NUL; |
1243 | fields->module[0] = NUL; |
1244 | fields->pattern[0] = NUL; |
1245 | if (!qf_multiscan) { |
1246 | fields->errmsg[0] = NUL; |
1247 | } |
1248 | fields->lnum = 0; |
1249 | fields->col = 0; |
1250 | fields->use_viscol = false; |
1251 | fields->enr = -1; |
1252 | fields->type = 0; |
1253 | *tail = NULL; |
1254 | |
1255 | // Always ignore case when looking for a matching error. |
1256 | regmatch.rm_ic = true; |
1257 | regmatch.regprog = fmt_ptr->prog; |
1258 | r = vim_regexec(®match, linebuf, (colnr_T)0); |
1259 | fmt_ptr->prog = regmatch.regprog; |
1260 | if (r) { |
1261 | status = qf_parse_match(linebuf, linelen, fmt_ptr, ®match, fields, |
1262 | qf_multiline, qf_multiscan, tail); |
1263 | } |
1264 | |
1265 | return status; |
1266 | } |
1267 | |
1268 | /// Parse directory error format prefixes (%D and %X). |
1269 | /// Push and pop directories from the directory stack when scanning directory |
1270 | /// names. |
1271 | static int qf_parse_dir_pfx(int idx, qffields_T *fields, qf_list_T *qfl) |
1272 | { |
1273 | if (idx == 'D') { // enter directory |
1274 | if (*fields->namebuf == NUL) { |
1275 | EMSG(_("E379: Missing or empty directory name" )); |
1276 | return QF_FAIL; |
1277 | } |
1278 | qfl->qf_directory = qf_push_dir(fields->namebuf, &qfl->qf_dir_stack, false); |
1279 | if (qfl->qf_directory == NULL) { |
1280 | return QF_FAIL; |
1281 | } |
1282 | } else if (idx == 'X') { // leave directory |
1283 | qfl->qf_directory = qf_pop_dir(&qfl->qf_dir_stack); |
1284 | } |
1285 | |
1286 | return QF_OK; |
1287 | } |
1288 | |
1289 | /// Parse global file name error format prefixes (%O, %P and %Q). |
1290 | static int qf_parse_file_pfx(int idx, qffields_T *fields, qf_list_T *qfl, |
1291 | char_u *tail) |
1292 | { |
1293 | fields->valid = false; |
1294 | if (*fields->namebuf == NUL || os_path_exists(fields->namebuf)) { |
1295 | if (*fields->namebuf && idx == 'P') { |
1296 | qfl->qf_currfile = qf_push_dir(fields->namebuf, &qfl->qf_file_stack, |
1297 | true); |
1298 | } else if (idx == 'Q') { |
1299 | qfl->qf_currfile = qf_pop_dir(&qfl->qf_file_stack); |
1300 | } |
1301 | *fields->namebuf = NUL; |
1302 | if (tail && *tail) { |
1303 | STRMOVE(IObuff, skipwhite(tail)); |
1304 | qfl->qf_multiscan = true; |
1305 | return QF_MULTISCAN; |
1306 | } |
1307 | } |
1308 | |
1309 | return QF_OK; |
1310 | } |
1311 | |
1312 | /// Parse a non-error line (a line which doesn't match any of the error |
1313 | /// format in 'efm'). |
1314 | static int qf_parse_line_nomatch(char_u *linebuf, size_t linelen, |
1315 | qffields_T *fields) |
1316 | { |
1317 | fields->namebuf[0] = NUL; // no match found, remove file name |
1318 | fields->lnum = 0; // don't jump to this line |
1319 | fields->valid = false; |
1320 | if (linelen >= fields->errmsglen) { |
1321 | // linelen + null terminator |
1322 | fields->errmsg = xrealloc(fields->errmsg, linelen + 1); |
1323 | fields->errmsglen = linelen + 1; |
1324 | } |
1325 | // copy whole line to error message |
1326 | STRLCPY(fields->errmsg, linebuf, linelen + 1); |
1327 | |
1328 | return QF_OK; |
1329 | } |
1330 | |
1331 | /// Parse multi-line error format prefixes (%C and %Z) |
1332 | static int qf_parse_multiline_pfx(qf_info_T *qi, int qf_idx, int idx, |
1333 | qf_list_T *qfl, qffields_T *fields) |
1334 | { |
1335 | if (!qfl->qf_multiignore) { |
1336 | qfline_T *qfprev = qfl->qf_last; |
1337 | |
1338 | if (qfprev == NULL) { |
1339 | return QF_FAIL; |
1340 | } |
1341 | if (*fields->errmsg) { |
1342 | size_t textlen = strlen((char *)qfprev->qf_text); |
1343 | size_t errlen = strlen((char *)fields->errmsg); |
1344 | qfprev->qf_text = xrealloc(qfprev->qf_text, textlen + errlen + 2); |
1345 | qfprev->qf_text[textlen] = '\n'; |
1346 | STRCPY(qfprev->qf_text + textlen + 1, fields->errmsg); |
1347 | } |
1348 | if (qfprev->qf_nr == -1) { |
1349 | qfprev->qf_nr = fields->enr; |
1350 | } |
1351 | if (vim_isprintc(fields->type) && !qfprev->qf_type) { |
1352 | // only printable chars allowed |
1353 | qfprev->qf_type = fields->type; |
1354 | } |
1355 | |
1356 | if (!qfprev->qf_lnum) { |
1357 | qfprev->qf_lnum = fields->lnum; |
1358 | } |
1359 | if (!qfprev->qf_col) { |
1360 | qfprev->qf_col = fields->col; |
1361 | } |
1362 | qfprev->qf_viscol = fields->use_viscol; |
1363 | if (!qfprev->qf_fnum) { |
1364 | qfprev->qf_fnum = qf_get_fnum(qi, qf_idx, qfl->qf_directory, |
1365 | *fields->namebuf || qfl->qf_directory |
1366 | ? fields->namebuf |
1367 | : qfl->qf_currfile && fields->valid |
1368 | ? qfl->qf_currfile : 0); |
1369 | } |
1370 | } |
1371 | if (idx == 'Z') { |
1372 | qfl->qf_multiline = qfl->qf_multiignore = false; |
1373 | } |
1374 | line_breakcheck(); |
1375 | |
1376 | return QF_IGNORE_LINE; |
1377 | } |
1378 | |
1379 | /// Free a location list. |
1380 | static void ll_free_all(qf_info_T **pqi) |
1381 | { |
1382 | int i; |
1383 | qf_info_T *qi; |
1384 | |
1385 | qi = *pqi; |
1386 | if (qi == NULL) |
1387 | return; |
1388 | *pqi = NULL; /* Remove reference to this list */ |
1389 | |
1390 | qi->qf_refcount--; |
1391 | if (qi->qf_refcount < 1) { |
1392 | /* No references to this location list */ |
1393 | for (i = 0; i < qi->qf_listcount; ++i) |
1394 | qf_free(qi, i); |
1395 | xfree(qi); |
1396 | } |
1397 | } |
1398 | |
1399 | /// Free all the quickfix/location lists in the stack. |
1400 | void qf_free_all(win_T *wp) |
1401 | { |
1402 | int i; |
1403 | qf_info_T *qi = &ql_info; |
1404 | |
1405 | if (wp != NULL) { |
1406 | /* location list */ |
1407 | ll_free_all(&wp->w_llist); |
1408 | ll_free_all(&wp->w_llist_ref); |
1409 | } else |
1410 | /* quickfix list */ |
1411 | for (i = 0; i < qi->qf_listcount; ++i) |
1412 | qf_free(qi, i); |
1413 | } |
1414 | |
1415 | /// Add an entry to the end of the list of errors. |
1416 | /// |
1417 | /// @param qi quickfix list |
1418 | /// @param qf_idx list index |
1419 | /// @param dir optional directory name |
1420 | /// @param fname file name or NULL |
1421 | /// @param module module name or NULL |
1422 | /// @param bufnum buffer number or zero |
1423 | /// @param mesg message |
1424 | /// @param lnum line number |
1425 | /// @param col column |
1426 | /// @param vis_col using visual column |
1427 | /// @param pattern search pattern |
1428 | /// @param nr error number |
1429 | /// @param type type character |
1430 | /// @param valid valid entry |
1431 | /// |
1432 | /// @returns OK or FAIL. |
1433 | static int qf_add_entry(qf_info_T *qi, int qf_idx, char_u *dir, char_u *fname, |
1434 | char_u *module, int bufnum, char_u *mesg, long lnum, |
1435 | int col, char_u vis_col, char_u *pattern, int nr, |
1436 | char_u type, char_u valid) |
1437 | { |
1438 | qfline_T *qfp = xmalloc(sizeof(qfline_T)); |
1439 | qfline_T **lastp; // pointer to qf_last or NULL |
1440 | |
1441 | if (bufnum != 0) { |
1442 | buf_T *buf = buflist_findnr(bufnum); |
1443 | |
1444 | qfp->qf_fnum = bufnum; |
1445 | if (buf != NULL) { |
1446 | buf->b_has_qf_entry |= |
1447 | IS_QF_STACK(qi) ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY; |
1448 | } |
1449 | } else { |
1450 | qfp->qf_fnum = qf_get_fnum(qi, qf_idx, dir, fname); |
1451 | } |
1452 | qfp->qf_text = vim_strsave(mesg); |
1453 | qfp->qf_lnum = lnum; |
1454 | qfp->qf_col = col; |
1455 | qfp->qf_viscol = vis_col; |
1456 | if (pattern == NULL || *pattern == NUL) { |
1457 | qfp->qf_pattern = NULL; |
1458 | } else { |
1459 | qfp->qf_pattern = vim_strsave(pattern); |
1460 | } |
1461 | if (module == NULL || *module == NUL) { |
1462 | qfp->qf_module = NULL; |
1463 | } else { |
1464 | qfp->qf_module = vim_strsave(module); |
1465 | } |
1466 | qfp->qf_nr = nr; |
1467 | if (type != 1 && !vim_isprintc(type)) { // only printable chars allowed |
1468 | type = 0; |
1469 | } |
1470 | qfp->qf_type = (char_u)type; |
1471 | qfp->qf_valid = valid; |
1472 | |
1473 | lastp = &qi->qf_lists[qf_idx].qf_last; |
1474 | if (qi->qf_lists[qf_idx].qf_count == 0) { |
1475 | // first element in the list |
1476 | qi->qf_lists[qf_idx].qf_start = qfp; |
1477 | qi->qf_lists[qf_idx].qf_ptr = qfp; |
1478 | qi->qf_lists[qf_idx].qf_index = 0; |
1479 | qfp->qf_prev = NULL; |
1480 | } else { |
1481 | assert(*lastp); |
1482 | qfp->qf_prev = *lastp; |
1483 | (*lastp)->qf_next = qfp; |
1484 | } |
1485 | qfp->qf_next = NULL; |
1486 | qfp->qf_cleared = false; |
1487 | *lastp = qfp; |
1488 | qi->qf_lists[qf_idx].qf_count++; |
1489 | if (qi->qf_lists[qf_idx].qf_index == 0 && qfp->qf_valid) { |
1490 | // first valid entry |
1491 | qi->qf_lists[qf_idx].qf_index = qi->qf_lists[qf_idx].qf_count; |
1492 | qi->qf_lists[qf_idx].qf_ptr = qfp; |
1493 | } |
1494 | |
1495 | return OK; |
1496 | } |
1497 | |
1498 | /* |
1499 | * Allocate a new location list |
1500 | */ |
1501 | static qf_info_T *ll_new_list(void) |
1502 | { |
1503 | qf_info_T *qi = xcalloc(1, sizeof(qf_info_T)); |
1504 | qi->qf_refcount++; |
1505 | |
1506 | return qi; |
1507 | } |
1508 | |
1509 | /* |
1510 | * Return the location list for window 'wp'. |
1511 | * If not present, allocate a location list |
1512 | */ |
1513 | static qf_info_T *ll_get_or_alloc_list(win_T *wp) |
1514 | { |
1515 | if (IS_LL_WINDOW(wp)) |
1516 | /* For a location list window, use the referenced location list */ |
1517 | return wp->w_llist_ref; |
1518 | |
1519 | /* |
1520 | * For a non-location list window, w_llist_ref should not point to a |
1521 | * location list. |
1522 | */ |
1523 | ll_free_all(&wp->w_llist_ref); |
1524 | |
1525 | if (wp->w_llist == NULL) |
1526 | wp->w_llist = ll_new_list(); /* new location list */ |
1527 | return wp->w_llist; |
1528 | } |
1529 | |
1530 | /* |
1531 | * Copy the location list from window "from" to window "to". |
1532 | */ |
1533 | void copy_loclist(win_T *from, win_T *to) |
1534 | { |
1535 | qf_info_T *qi; |
1536 | int idx; |
1537 | int i; |
1538 | |
1539 | /* |
1540 | * When copying from a location list window, copy the referenced |
1541 | * location list. For other windows, copy the location list for |
1542 | * that window. |
1543 | */ |
1544 | if (IS_LL_WINDOW(from)) |
1545 | qi = from->w_llist_ref; |
1546 | else |
1547 | qi = from->w_llist; |
1548 | |
1549 | if (qi == NULL) /* no location list to copy */ |
1550 | return; |
1551 | |
1552 | /* allocate a new location list */ |
1553 | to->w_llist = ll_new_list(); |
1554 | |
1555 | to->w_llist->qf_listcount = qi->qf_listcount; |
1556 | |
1557 | /* Copy the location lists one at a time */ |
1558 | for (idx = 0; idx < qi->qf_listcount; idx++) { |
1559 | qf_list_T *from_qfl; |
1560 | qf_list_T *to_qfl; |
1561 | |
1562 | to->w_llist->qf_curlist = idx; |
1563 | |
1564 | from_qfl = &qi->qf_lists[idx]; |
1565 | to_qfl = &to->w_llist->qf_lists[idx]; |
1566 | |
1567 | /* Some of the fields are populated by qf_add_entry() */ |
1568 | to_qfl->qf_nonevalid = from_qfl->qf_nonevalid; |
1569 | to_qfl->qf_count = 0; |
1570 | to_qfl->qf_index = 0; |
1571 | to_qfl->qf_start = NULL; |
1572 | to_qfl->qf_last = NULL; |
1573 | to_qfl->qf_ptr = NULL; |
1574 | if (from_qfl->qf_title != NULL) |
1575 | to_qfl->qf_title = vim_strsave(from_qfl->qf_title); |
1576 | else |
1577 | to_qfl->qf_title = NULL; |
1578 | |
1579 | if (from_qfl->qf_ctx != NULL) { |
1580 | to_qfl->qf_ctx = xcalloc(1, sizeof(typval_T)); |
1581 | tv_copy(from_qfl->qf_ctx, to_qfl->qf_ctx); |
1582 | } else { |
1583 | to_qfl->qf_ctx = NULL; |
1584 | } |
1585 | |
1586 | if (from_qfl->qf_count) { |
1587 | qfline_T *from_qfp; |
1588 | qfline_T *prevp; |
1589 | |
1590 | // copy all the location entries in this list |
1591 | for (i = 0, from_qfp = from_qfl->qf_start; |
1592 | i < from_qfl->qf_count && from_qfp != NULL; |
1593 | i++, from_qfp = from_qfp->qf_next) { |
1594 | if (qf_add_entry(to->w_llist, |
1595 | to->w_llist->qf_curlist, |
1596 | NULL, |
1597 | NULL, |
1598 | from_qfp->qf_module, |
1599 | 0, |
1600 | from_qfp->qf_text, |
1601 | from_qfp->qf_lnum, |
1602 | from_qfp->qf_col, |
1603 | from_qfp->qf_viscol, |
1604 | from_qfp->qf_pattern, |
1605 | from_qfp->qf_nr, |
1606 | 0, |
1607 | from_qfp->qf_valid) == FAIL) { |
1608 | qf_free_all(to); |
1609 | return; |
1610 | } |
1611 | /* |
1612 | * qf_add_entry() will not set the qf_num field, as the |
1613 | * directory and file names are not supplied. So the qf_fnum |
1614 | * field is copied here. |
1615 | */ |
1616 | prevp = to->w_llist->qf_lists[to->w_llist->qf_curlist].qf_last; |
1617 | prevp->qf_fnum = from_qfp->qf_fnum; // file number |
1618 | prevp->qf_type = from_qfp->qf_type; // error type |
1619 | if (from_qfl->qf_ptr == from_qfp) { |
1620 | to_qfl->qf_ptr = prevp; // current location |
1621 | } |
1622 | } |
1623 | } |
1624 | |
1625 | to_qfl->qf_index = from_qfl->qf_index; /* current index in the list */ |
1626 | |
1627 | // Assign a new ID for the location list |
1628 | to_qfl->qf_id = ++last_qf_id; |
1629 | to_qfl->qf_changedtick = 0L; |
1630 | |
1631 | /* When no valid entries are present in the list, qf_ptr points to |
1632 | * the first item in the list */ |
1633 | if (to_qfl->qf_nonevalid) { |
1634 | to_qfl->qf_ptr = to_qfl->qf_start; |
1635 | to_qfl->qf_index = 1; |
1636 | } |
1637 | } |
1638 | |
1639 | to->w_llist->qf_curlist = qi->qf_curlist; /* current list */ |
1640 | } |
1641 | |
1642 | // Get buffer number for file "directory/fname". |
1643 | // Also sets the b_has_qf_entry flag. |
1644 | static int qf_get_fnum(qf_info_T *qi, int qf_idx, char_u *directory, |
1645 | char_u *fname) |
1646 | { |
1647 | char_u *ptr = NULL; |
1648 | char_u *bufname; |
1649 | buf_T *buf; |
1650 | if (fname == NULL || *fname == NUL) { // no file name |
1651 | return 0; |
1652 | } |
1653 | |
1654 | #ifdef BACKSLASH_IN_FILENAME |
1655 | if (directory != NULL) { |
1656 | slash_adjust(directory); |
1657 | } |
1658 | slash_adjust(fname); |
1659 | #endif |
1660 | if (directory != NULL && !vim_isAbsName(fname)) { |
1661 | ptr = (char_u *)concat_fnames((char *)directory, (char *)fname, true); |
1662 | // Here we check if the file really exists. |
1663 | // This should normally be true, but if make works without |
1664 | // "leaving directory"-messages we might have missed a |
1665 | // directory change. |
1666 | if (!os_path_exists(ptr)) { |
1667 | xfree(ptr); |
1668 | directory = qf_guess_filepath(qi, qf_idx, fname); |
1669 | if (directory) { |
1670 | ptr = (char_u *)concat_fnames((char *)directory, (char *)fname, true); |
1671 | } else { |
1672 | ptr = vim_strsave(fname); |
1673 | } |
1674 | } |
1675 | // Use concatenated directory name and file name. |
1676 | bufname = ptr; |
1677 | } else { |
1678 | bufname = fname; |
1679 | } |
1680 | |
1681 | if (qf_last_bufname != NULL |
1682 | && STRCMP(bufname, qf_last_bufname) == 0 |
1683 | && bufref_valid(&qf_last_bufref)) { |
1684 | buf = qf_last_bufref.br_buf; |
1685 | xfree(ptr); |
1686 | } else { |
1687 | xfree(qf_last_bufname); |
1688 | buf = buflist_new(bufname, NULL, (linenr_T)0, BLN_NOOPT); |
1689 | qf_last_bufname = (bufname == ptr) ? bufname : vim_strsave(bufname); |
1690 | set_bufref(&qf_last_bufref, buf); |
1691 | } |
1692 | if (buf == NULL) { |
1693 | return 0; |
1694 | } |
1695 | buf->b_has_qf_entry = |
1696 | IS_QF_STACK(qi) ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY; |
1697 | return buf->b_fnum; |
1698 | } |
1699 | |
1700 | // Push dirbuf onto the directory stack and return pointer to actual dir or |
1701 | // NULL on error. |
1702 | static char_u *qf_push_dir(char_u *dirbuf, struct dir_stack_T **stackptr, |
1703 | bool is_file_stack) |
1704 | { |
1705 | struct dir_stack_T *ds_ptr; |
1706 | |
1707 | /* allocate new stack element and hook it in */ |
1708 | struct dir_stack_T *ds_new = xmalloc(sizeof(struct dir_stack_T)); |
1709 | |
1710 | ds_new->next = *stackptr; |
1711 | *stackptr = ds_new; |
1712 | |
1713 | /* store directory on the stack */ |
1714 | if (vim_isAbsName(dirbuf) |
1715 | || (*stackptr)->next == NULL |
1716 | || (*stackptr && is_file_stack)) |
1717 | (*stackptr)->dirname = vim_strsave(dirbuf); |
1718 | else { |
1719 | /* Okay we don't have an absolute path. |
1720 | * dirbuf must be a subdir of one of the directories on the stack. |
1721 | * Let's search... |
1722 | */ |
1723 | ds_new = (*stackptr)->next; |
1724 | (*stackptr)->dirname = NULL; |
1725 | while (ds_new) { |
1726 | xfree((*stackptr)->dirname); |
1727 | (*stackptr)->dirname = (char_u *)concat_fnames((char *)ds_new->dirname, |
1728 | (char *)dirbuf, TRUE); |
1729 | if (os_isdir((*stackptr)->dirname)) |
1730 | break; |
1731 | |
1732 | ds_new = ds_new->next; |
1733 | } |
1734 | |
1735 | /* clean up all dirs we already left */ |
1736 | while ((*stackptr)->next != ds_new) { |
1737 | ds_ptr = (*stackptr)->next; |
1738 | (*stackptr)->next = (*stackptr)->next->next; |
1739 | xfree(ds_ptr->dirname); |
1740 | xfree(ds_ptr); |
1741 | } |
1742 | |
1743 | /* Nothing found -> it must be on top level */ |
1744 | if (ds_new == NULL) { |
1745 | xfree((*stackptr)->dirname); |
1746 | (*stackptr)->dirname = vim_strsave(dirbuf); |
1747 | } |
1748 | } |
1749 | |
1750 | if ((*stackptr)->dirname != NULL) |
1751 | return (*stackptr)->dirname; |
1752 | else { |
1753 | ds_ptr = *stackptr; |
1754 | *stackptr = (*stackptr)->next; |
1755 | xfree(ds_ptr); |
1756 | return NULL; |
1757 | } |
1758 | } |
1759 | |
1760 | |
1761 | /* |
1762 | * pop dirbuf from the directory stack and return previous directory or NULL if |
1763 | * stack is empty |
1764 | */ |
1765 | static char_u *qf_pop_dir(struct dir_stack_T **stackptr) |
1766 | { |
1767 | struct dir_stack_T *ds_ptr; |
1768 | |
1769 | /* TODO: Should we check if dirbuf is the directory on top of the stack? |
1770 | * What to do if it isn't? */ |
1771 | |
1772 | /* pop top element and free it */ |
1773 | if (*stackptr != NULL) { |
1774 | ds_ptr = *stackptr; |
1775 | *stackptr = (*stackptr)->next; |
1776 | xfree(ds_ptr->dirname); |
1777 | xfree(ds_ptr); |
1778 | } |
1779 | |
1780 | /* return NEW top element as current dir or NULL if stack is empty*/ |
1781 | return *stackptr ? (*stackptr)->dirname : NULL; |
1782 | } |
1783 | |
1784 | /* |
1785 | * clean up directory stack |
1786 | */ |
1787 | static void qf_clean_dir_stack(struct dir_stack_T **stackptr) |
1788 | { |
1789 | struct dir_stack_T *ds_ptr; |
1790 | |
1791 | while ((ds_ptr = *stackptr) != NULL) { |
1792 | *stackptr = (*stackptr)->next; |
1793 | xfree(ds_ptr->dirname); |
1794 | xfree(ds_ptr); |
1795 | } |
1796 | } |
1797 | |
1798 | /* |
1799 | * Check in which directory of the directory stack the given file can be |
1800 | * found. |
1801 | * Returns a pointer to the directory name or NULL if not found. |
1802 | * Cleans up intermediate directory entries. |
1803 | * |
1804 | * TODO: How to solve the following problem? |
1805 | * If we have the this directory tree: |
1806 | * ./ |
1807 | * ./aa |
1808 | * ./aa/bb |
1809 | * ./bb |
1810 | * ./bb/x.c |
1811 | * and make says: |
1812 | * making all in aa |
1813 | * making all in bb |
1814 | * x.c:9: Error |
1815 | * Then qf_push_dir thinks we are in ./aa/bb, but we are in ./bb. |
1816 | * qf_guess_filepath will return NULL. |
1817 | */ |
1818 | static char_u *qf_guess_filepath(qf_info_T *qi, int qf_idx, char_u *filename) |
1819 | { |
1820 | struct dir_stack_T *ds_ptr; |
1821 | struct dir_stack_T *ds_tmp; |
1822 | char_u *fullname; |
1823 | qf_list_T *qfl = &qi->qf_lists[qf_idx]; |
1824 | |
1825 | // no dirs on the stack - there's nothing we can do |
1826 | if (qfl->qf_dir_stack == NULL) { |
1827 | return NULL; |
1828 | } |
1829 | |
1830 | ds_ptr = qfl->qf_dir_stack->next; |
1831 | fullname = NULL; |
1832 | while (ds_ptr) { |
1833 | xfree(fullname); |
1834 | fullname = (char_u *)concat_fnames((char *)ds_ptr->dirname, (char *)filename, TRUE); |
1835 | |
1836 | if (os_path_exists(fullname)) { |
1837 | break; |
1838 | } |
1839 | |
1840 | ds_ptr = ds_ptr->next; |
1841 | } |
1842 | |
1843 | xfree(fullname); |
1844 | |
1845 | // clean up all dirs we already left |
1846 | while (qfl->qf_dir_stack->next != ds_ptr) { |
1847 | ds_tmp = qfl->qf_dir_stack->next; |
1848 | qfl->qf_dir_stack->next = qfl->qf_dir_stack->next->next; |
1849 | xfree(ds_tmp->dirname); |
1850 | xfree(ds_tmp); |
1851 | } |
1852 | |
1853 | return ds_ptr == NULL ? NULL : ds_ptr->dirname; |
1854 | } |
1855 | |
1856 | /// Returns true, if a quickfix/location list with the given identifier exists. |
1857 | static bool qflist_valid(win_T *wp, unsigned int qf_id) |
1858 | { |
1859 | qf_info_T *qi = &ql_info; |
1860 | |
1861 | if (wp) { |
1862 | qi = GET_LOC_LIST(wp); |
1863 | if (!qi) { |
1864 | return false; |
1865 | } |
1866 | } |
1867 | |
1868 | for (int i = 0; i < qi->qf_listcount; i++) { |
1869 | if (qi->qf_lists[i].qf_id == qf_id) { |
1870 | return true; |
1871 | } |
1872 | } |
1873 | |
1874 | return false; |
1875 | } |
1876 | |
1877 | /// When loading a file from the quickfix, the autocommands may modify it. |
1878 | /// This may invalidate the current quickfix entry. This function checks |
1879 | /// whether a entry is still present in the quickfix. |
1880 | /// Similar to location list. |
1881 | static bool is_qf_entry_present(qf_info_T *qi, qfline_T *qf_ptr) |
1882 | { |
1883 | qf_list_T *qfl; |
1884 | qfline_T *qfp; |
1885 | int i; |
1886 | |
1887 | qfl = &qi->qf_lists[qi->qf_curlist]; |
1888 | |
1889 | // Search for the entry in the current list |
1890 | for (i = 0, qfp = qfl->qf_start; i < qfl->qf_count; i++, qfp = qfp->qf_next) { |
1891 | if (qfp == NULL || qfp == qf_ptr) { |
1892 | break; |
1893 | } |
1894 | } |
1895 | |
1896 | if (i == qfl->qf_count) { // Entry is not found |
1897 | return false; |
1898 | } |
1899 | |
1900 | return true; |
1901 | } |
1902 | |
1903 | /// Get the next valid entry in the current quickfix/location list. The search |
1904 | /// starts from the current entry. Returns NULL on failure. |
1905 | static qfline_T *get_next_valid_entry(qf_info_T *qi, qfline_T *qf_ptr, |
1906 | int *qf_index, int dir) |
1907 | { |
1908 | int idx = *qf_index; |
1909 | int old_qf_fnum = qf_ptr->qf_fnum; |
1910 | |
1911 | do { |
1912 | if (idx == qi->qf_lists[qi->qf_curlist].qf_count |
1913 | || qf_ptr->qf_next == NULL) { |
1914 | return NULL; |
1915 | } |
1916 | idx++; |
1917 | qf_ptr = qf_ptr->qf_next; |
1918 | } while ((!qi->qf_lists[qi->qf_curlist].qf_nonevalid && !qf_ptr->qf_valid) |
1919 | || (dir == FORWARD_FILE && qf_ptr->qf_fnum == old_qf_fnum)); |
1920 | |
1921 | *qf_index = idx; |
1922 | return qf_ptr; |
1923 | } |
1924 | |
1925 | /// Get the previous valid entry in the current quickfix/location list. The |
1926 | /// search starts from the current entry. Returns NULL on failure. |
1927 | static qfline_T *get_prev_valid_entry(qf_info_T *qi, qfline_T *qf_ptr, |
1928 | int *qf_index, int dir) |
1929 | { |
1930 | int idx = *qf_index; |
1931 | int old_qf_fnum = qf_ptr->qf_fnum; |
1932 | |
1933 | do { |
1934 | if (idx == 1 || qf_ptr->qf_prev == NULL) { |
1935 | return NULL; |
1936 | } |
1937 | idx--; |
1938 | qf_ptr = qf_ptr->qf_prev; |
1939 | } while ((!qi->qf_lists[qi->qf_curlist].qf_nonevalid && !qf_ptr->qf_valid) |
1940 | || (dir == BACKWARD_FILE && qf_ptr->qf_fnum == old_qf_fnum)); |
1941 | |
1942 | *qf_index = idx; |
1943 | return qf_ptr; |
1944 | } |
1945 | |
1946 | /// Get the n'th (errornr) previous/next valid entry from the current entry in |
1947 | /// the quickfix list. |
1948 | /// dir == FORWARD or FORWARD_FILE: next valid entry |
1949 | /// dir == BACKWARD or BACKWARD_FILE: previous valid entry |
1950 | static qfline_T *get_nth_valid_entry(qf_info_T *qi, int errornr, |
1951 | qfline_T *qf_ptr, int *qf_index, int dir) |
1952 | { |
1953 | qfline_T *prev_qf_ptr; |
1954 | int prev_index; |
1955 | static char_u *e_no_more_items = (char_u *)N_("E553: No more items" ); |
1956 | char_u *err = e_no_more_items; |
1957 | |
1958 | while (errornr--) { |
1959 | prev_qf_ptr = qf_ptr; |
1960 | prev_index = *qf_index; |
1961 | |
1962 | if (dir == FORWARD || dir == FORWARD_FILE) { |
1963 | qf_ptr = get_next_valid_entry(qi, qf_ptr, qf_index, dir); |
1964 | } else { |
1965 | qf_ptr = get_prev_valid_entry(qi, qf_ptr, qf_index, dir); |
1966 | } |
1967 | |
1968 | if (qf_ptr == NULL) { |
1969 | qf_ptr = prev_qf_ptr; |
1970 | *qf_index = prev_index; |
1971 | if (err != NULL) { |
1972 | EMSG(_(err)); |
1973 | return NULL; |
1974 | } |
1975 | break; |
1976 | } |
1977 | |
1978 | err = NULL; |
1979 | } |
1980 | |
1981 | return qf_ptr; |
1982 | } |
1983 | |
1984 | /// Get n'th (errornr) quickfix entry |
1985 | static qfline_T *get_nth_entry(qf_info_T *qi, int errornr, qfline_T *qf_ptr, |
1986 | int *cur_qfidx) |
1987 | { |
1988 | int qf_idx = *cur_qfidx; |
1989 | |
1990 | // New error number is less than the current error number |
1991 | while (errornr < qf_idx && qf_idx > 1 && qf_ptr->qf_prev != NULL) { |
1992 | qf_idx--; |
1993 | qf_ptr = qf_ptr->qf_prev; |
1994 | } |
1995 | |
1996 | // New error number is greater than the current error number |
1997 | while (errornr > qf_idx |
1998 | && qf_idx < qi->qf_lists[qi->qf_curlist].qf_count |
1999 | && qf_ptr->qf_next != NULL) { |
2000 | qf_idx++; |
2001 | qf_ptr = qf_ptr->qf_next; |
2002 | } |
2003 | |
2004 | *cur_qfidx = qf_idx; |
2005 | return qf_ptr; |
2006 | } |
2007 | |
2008 | /// Find a help window or open one. |
2009 | static int jump_to_help_window(qf_info_T *qi, int *opened_window) |
2010 | { |
2011 | win_T *wp = NULL; |
2012 | |
2013 | if (cmdmod.tab != 0) { |
2014 | wp = NULL; |
2015 | } else { |
2016 | FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) { |
2017 | if (bt_help(wp2->w_buffer)) { |
2018 | wp = wp2; |
2019 | break; |
2020 | } |
2021 | } |
2022 | } |
2023 | |
2024 | if (wp != NULL && wp->w_buffer->b_nwindows > 0) { |
2025 | win_enter(wp, true); |
2026 | } else { |
2027 | // Split off help window; put it at far top if no position |
2028 | // specified, the current window is vertically split and narrow. |
2029 | int flags = WSP_HELP; |
2030 | if (cmdmod.split == 0 |
2031 | && curwin->w_width != Columns |
2032 | && curwin->w_width < 80) { |
2033 | flags |= WSP_TOP; |
2034 | } |
2035 | |
2036 | if (IS_LL_STACK(qi)) { |
2037 | flags |= WSP_NEWLOC; // don't copy the location list |
2038 | } |
2039 | |
2040 | if (win_split(0, flags) == FAIL) { |
2041 | return FAIL; |
2042 | } |
2043 | |
2044 | *opened_window = true; |
2045 | |
2046 | if (curwin->w_height < p_hh) { |
2047 | win_setheight((int)p_hh); |
2048 | } |
2049 | |
2050 | if (IS_LL_STACK(qi)) { // not a quickfix list |
2051 | // The new window should use the supplied location list |
2052 | curwin->w_llist = qi; |
2053 | qi->qf_refcount++; |
2054 | } |
2055 | } |
2056 | |
2057 | if (!p_im) { |
2058 | restart_edit = 0; // don't want insert mode in help file |
2059 | } |
2060 | |
2061 | return OK; |
2062 | } |
2063 | |
2064 | /// Find a suitable window for opening a file (qf_fnum) and jump to it. |
2065 | /// If the file is already opened in a window, jump to it. |
2066 | static int qf_jump_to_usable_window(int qf_fnum, int *opened_window) |
2067 | { |
2068 | win_T *usable_win_ptr = NULL; |
2069 | int usable_win; |
2070 | int flags; |
2071 | win_T *win; |
2072 | win_T *altwin; |
2073 | |
2074 | usable_win = 0; |
2075 | |
2076 | qf_info_T *ll_ref = curwin->w_llist_ref; |
2077 | if (ll_ref != NULL) { |
2078 | // Find a window using the same location list that is not a |
2079 | // quickfix window. |
2080 | FOR_ALL_WINDOWS_IN_TAB(usable_win_ptr2, curtab) { |
2081 | if (usable_win_ptr2->w_llist == ll_ref |
2082 | && !bt_quickfix(usable_win_ptr2->w_buffer)) { |
2083 | usable_win_ptr = usable_win_ptr2; |
2084 | usable_win = 1; |
2085 | break; |
2086 | } |
2087 | } |
2088 | } |
2089 | |
2090 | if (!usable_win) { |
2091 | // Locate a window showing a normal buffer |
2092 | FOR_ALL_WINDOWS_IN_TAB(win2, curtab) { |
2093 | if (win2->w_buffer->b_p_bt[0] == NUL) { |
2094 | usable_win = 1; |
2095 | break; |
2096 | } |
2097 | } |
2098 | } |
2099 | |
2100 | // If no usable window is found and 'switchbuf' contains "usetab" |
2101 | // then search in other tabs. |
2102 | if (!usable_win && (swb_flags & SWB_USETAB)) { |
2103 | FOR_ALL_TAB_WINDOWS(tp, wp) { |
2104 | if (wp->w_buffer->b_fnum == qf_fnum) { |
2105 | goto_tabpage_win(tp, wp); |
2106 | usable_win = 1; |
2107 | goto win_found; |
2108 | } |
2109 | } |
2110 | } |
2111 | win_found: |
2112 | |
2113 | // If there is only one window and it is the quickfix window, create a |
2114 | // new one above the quickfix window. |
2115 | if ((ONE_WINDOW && bt_quickfix(curbuf)) || !usable_win) { |
2116 | flags = WSP_ABOVE; |
2117 | if (ll_ref != NULL) { |
2118 | flags |= WSP_NEWLOC; |
2119 | } |
2120 | if (win_split(0, flags) == FAIL) { |
2121 | return FAIL; // not enough room for window |
2122 | } |
2123 | *opened_window = true; // close it when fail |
2124 | p_swb = empty_option; // don't split again |
2125 | swb_flags = 0; |
2126 | RESET_BINDING(curwin); |
2127 | if (ll_ref != NULL) { |
2128 | // The new window should use the location list from the |
2129 | // location list window |
2130 | curwin->w_llist = ll_ref; |
2131 | ll_ref->qf_refcount++; |
2132 | } |
2133 | } else { |
2134 | if (curwin->w_llist_ref != NULL) { |
2135 | // In a location window |
2136 | win = usable_win_ptr; |
2137 | if (win == NULL) { |
2138 | // Find the window showing the selected file |
2139 | FOR_ALL_WINDOWS_IN_TAB(win2, curtab) { |
2140 | if (win2->w_buffer->b_fnum == qf_fnum) { |
2141 | win = win2; |
2142 | break; |
2143 | } |
2144 | } |
2145 | if (win == NULL) { |
2146 | // Find a previous usable window |
2147 | win = curwin; |
2148 | do { |
2149 | if (win->w_buffer->b_p_bt[0] == NUL) { |
2150 | break; |
2151 | } |
2152 | if (win->w_prev == NULL) { |
2153 | win = lastwin; // wrap around the top |
2154 | } else { |
2155 | win = win->w_prev; // go to previous window |
2156 | } |
2157 | } while (win != curwin); |
2158 | } |
2159 | } |
2160 | win_goto(win); |
2161 | |
2162 | // If the location list for the window is not set, then set it |
2163 | // to the location list from the location window |
2164 | if (win->w_llist == NULL) { |
2165 | win->w_llist = ll_ref; |
2166 | if (ll_ref != NULL) { |
2167 | ll_ref->qf_refcount++; |
2168 | } |
2169 | } |
2170 | } else { |
2171 | // Try to find a window that shows the right buffer. |
2172 | // Default to the window just above the quickfix buffer. |
2173 | win = curwin; |
2174 | altwin = NULL; |
2175 | for (;;) { |
2176 | if (win->w_buffer->b_fnum == qf_fnum) { |
2177 | break; |
2178 | } |
2179 | if (win->w_prev == NULL) { |
2180 | win = lastwin; // wrap around the top |
2181 | } else { |
2182 | win = win->w_prev; // go to previous window |
2183 | } |
2184 | if (IS_QF_WINDOW(win)) { |
2185 | // Didn't find it, go to the window before the quickfix window. |
2186 | if (altwin != NULL) { |
2187 | win = altwin; |
2188 | } else if (curwin->w_prev != NULL) { |
2189 | win = curwin->w_prev; |
2190 | } else { |
2191 | win = curwin->w_next; |
2192 | } |
2193 | break; |
2194 | } |
2195 | |
2196 | // Remember a usable window. |
2197 | if (altwin == NULL && !win->w_p_pvw |
2198 | && win->w_buffer->b_p_bt[0] == NUL) { |
2199 | altwin = win; |
2200 | } |
2201 | } |
2202 | |
2203 | win_goto(win); |
2204 | } |
2205 | } |
2206 | |
2207 | return OK; |
2208 | } |
2209 | |
2210 | /// Edit the selected file or help file. |
2211 | static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, |
2212 | win_T *oldwin, int *opened_window, int *abort) |
2213 | { |
2214 | int retval = OK; |
2215 | |
2216 | if (qf_ptr->qf_type == 1) { |
2217 | // Open help file (do_ecmd() will set b_help flag, readfile() will |
2218 | // set b_p_ro flag). |
2219 | if (!can_abandon(curbuf, forceit)) { |
2220 | no_write_message(); |
2221 | retval = FAIL; |
2222 | } else { |
2223 | retval = do_ecmd(qf_ptr->qf_fnum, NULL, NULL, NULL, (linenr_T)1, |
2224 | ECMD_HIDE + ECMD_SET_HELP, |
2225 | oldwin == curwin ? curwin : NULL); |
2226 | } |
2227 | } else { |
2228 | unsigned save_qfid = qi->qf_lists[qi->qf_curlist].qf_id; |
2229 | |
2230 | retval = buflist_getfile(qf_ptr->qf_fnum, (linenr_T)1, |
2231 | GETF_SETMARK | GETF_SWITCH, forceit); |
2232 | |
2233 | if (IS_LL_STACK(qi)) { |
2234 | // Location list. Check whether the associated window is still |
2235 | // present and the list is still valid. |
2236 | if (!win_valid_any_tab(oldwin)) { |
2237 | EMSG(_("E924: Current window was closed" )); |
2238 | *abort = true; |
2239 | *opened_window = false; |
2240 | } else if (!qflist_valid(oldwin, save_qfid)) { |
2241 | EMSG(_(e_loc_list_changed)); |
2242 | *abort = true; |
2243 | } |
2244 | } else if (!is_qf_entry_present(qi, qf_ptr)) { |
2245 | if (IS_QF_STACK(qi)) { |
2246 | EMSG(_("E925: Current quickfix was changed" )); |
2247 | } else { |
2248 | EMSG(_(e_loc_list_changed)); |
2249 | } |
2250 | *abort = true; |
2251 | } |
2252 | |
2253 | if (*abort) { |
2254 | retval = FAIL; |
2255 | } |
2256 | } |
2257 | |
2258 | return retval; |
2259 | } |
2260 | |
2261 | /// Goto the error line in the current file using either line/column number or a |
2262 | /// search pattern. |
2263 | static void qf_jump_goto_line(linenr_T qf_lnum, int qf_col, char_u qf_viscol, |
2264 | char_u *qf_pattern) |
2265 | { |
2266 | linenr_T i; |
2267 | char_u *line; |
2268 | colnr_T screen_col; |
2269 | colnr_T char_col; |
2270 | |
2271 | if (qf_pattern == NULL) { |
2272 | // Go to line with error, unless qf_lnum is 0. |
2273 | i = qf_lnum; |
2274 | if (i > 0) { |
2275 | if (i > curbuf->b_ml.ml_line_count) { |
2276 | i = curbuf->b_ml.ml_line_count; |
2277 | } |
2278 | curwin->w_cursor.lnum = i; |
2279 | } |
2280 | if (qf_col > 0) { |
2281 | curwin->w_cursor.col = qf_col - 1; |
2282 | curwin->w_cursor.coladd = 0; |
2283 | if (qf_viscol == true) { |
2284 | // Check each character from the beginning of the error |
2285 | // line up to the error column. For each tab character |
2286 | // found, reduce the error column value by the length of |
2287 | // a tab character. |
2288 | line = get_cursor_line_ptr(); |
2289 | screen_col = 0; |
2290 | for (char_col = 0; char_col < curwin->w_cursor.col; char_col++) { |
2291 | if (*line == NUL) { |
2292 | break; |
2293 | } |
2294 | if (*line++ == '\t') { |
2295 | curwin->w_cursor.col -= 7 - (screen_col % 8); |
2296 | screen_col += 8 - (screen_col % 8); |
2297 | } else { |
2298 | screen_col++; |
2299 | } |
2300 | } |
2301 | } |
2302 | check_cursor(); |
2303 | } else { |
2304 | beginline(BL_WHITE | BL_FIX); |
2305 | } |
2306 | } else { |
2307 | // Move the cursor to the first line in the buffer |
2308 | pos_T save_cursor = curwin->w_cursor; |
2309 | curwin->w_cursor.lnum = 0; |
2310 | if (!do_search(NULL, '/', qf_pattern, (long)1, SEARCH_KEEP, NULL, NULL)) { |
2311 | curwin->w_cursor = save_cursor; |
2312 | } |
2313 | } |
2314 | } |
2315 | |
2316 | /// Display quickfix list index and size message |
2317 | static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, |
2318 | buf_T *old_curbuf, linenr_T old_lnum) |
2319 | { |
2320 | // Update the screen before showing the message, unless the screen |
2321 | // scrolled up. |
2322 | if (!msg_scrolled) { |
2323 | update_topline_redraw(); |
2324 | } |
2325 | snprintf((char *)IObuff, IOSIZE, _("(%d of %d)%s%s: " ), qf_index, |
2326 | qi->qf_lists[qi->qf_curlist].qf_count, |
2327 | qf_ptr->qf_cleared ? _(" (line deleted)" ) : "" , |
2328 | (char *)qf_types(qf_ptr->qf_type, qf_ptr->qf_nr)); |
2329 | // Add the message, skipping leading whitespace and newlines. |
2330 | int len = (int)STRLEN(IObuff); |
2331 | qf_fmt_text(skipwhite(qf_ptr->qf_text), IObuff + len, IOSIZE - len); |
2332 | |
2333 | // Output the message. Overwrite to avoid scrolling when the 'O' |
2334 | // flag is present in 'shortmess'; But when not jumping, print the |
2335 | // whole message. |
2336 | linenr_T i = msg_scroll; |
2337 | if (curbuf == old_curbuf && curwin->w_cursor.lnum == old_lnum) { |
2338 | msg_scroll = true; |
2339 | } else if (!msg_scrolled && shortmess(SHM_OVERALL)) { |
2340 | msg_scroll = false; |
2341 | } |
2342 | msg_ext_set_kind("quickfix" ); |
2343 | msg_attr_keep(IObuff, 0, true, false); |
2344 | msg_scroll = (int)i; |
2345 | } |
2346 | |
2347 | /// jump to a quickfix line |
2348 | /// if dir == FORWARD go "errornr" valid entries forward |
2349 | /// if dir == BACKWARD go "errornr" valid entries backward |
2350 | /// if dir == FORWARD_FILE go "errornr" valid entries files backward |
2351 | /// if dir == BACKWARD_FILE go "errornr" valid entries files backward |
2352 | /// else if "errornr" is zero, redisplay the same line |
2353 | /// else go to entry "errornr" |
2354 | void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit) |
2355 | { |
2356 | qfline_T *qf_ptr; |
2357 | qfline_T *old_qf_ptr; |
2358 | int qf_index; |
2359 | int old_qf_index; |
2360 | buf_T *old_curbuf; |
2361 | linenr_T old_lnum; |
2362 | char_u *old_swb = p_swb; |
2363 | unsigned old_swb_flags = swb_flags; |
2364 | int opened_window = false; |
2365 | win_T *oldwin = curwin; |
2366 | int print_message = true; |
2367 | const bool old_KeyTyped = KeyTyped; // getting file may reset it |
2368 | int retval = OK; |
2369 | |
2370 | if (qi == NULL) |
2371 | qi = &ql_info; |
2372 | |
2373 | if (qi->qf_curlist >= qi->qf_listcount |
2374 | || qi->qf_lists[qi->qf_curlist].qf_count == 0) { |
2375 | EMSG(_(e_quickfix)); |
2376 | return; |
2377 | } |
2378 | |
2379 | qf_ptr = qi->qf_lists[qi->qf_curlist].qf_ptr; |
2380 | old_qf_ptr = qf_ptr; |
2381 | qf_index = qi->qf_lists[qi->qf_curlist].qf_index; |
2382 | old_qf_index = qf_index; |
2383 | if (dir != 0) { // next/prev valid entry |
2384 | qf_ptr = get_nth_valid_entry(qi, errornr, qf_ptr, &qf_index, dir); |
2385 | if (qf_ptr == NULL) { |
2386 | qf_ptr = old_qf_ptr; |
2387 | qf_index = old_qf_index; |
2388 | goto theend; // The horror... the horror... |
2389 | } |
2390 | } else if (errornr != 0) { // go to specified number |
2391 | qf_ptr = get_nth_entry(qi, errornr, qf_ptr, &qf_index); |
2392 | } |
2393 | |
2394 | qi->qf_lists[qi->qf_curlist].qf_index = qf_index; |
2395 | if (qf_win_pos_update(qi, old_qf_index)) |
2396 | /* No need to print the error message if it's visible in the error |
2397 | * window */ |
2398 | print_message = FALSE; |
2399 | |
2400 | // For ":helpgrep" find a help window or open one. |
2401 | if (qf_ptr->qf_type == 1 && (!bt_help(curwin->w_buffer) || cmdmod.tab != 0)) { |
2402 | if (jump_to_help_window(qi, &opened_window) == FAIL) { |
2403 | goto theend; |
2404 | } |
2405 | } |
2406 | |
2407 | // If currently in the quickfix window, find another window to show the |
2408 | // file in. |
2409 | if (bt_quickfix(curbuf) && !opened_window) { |
2410 | // If there is no file specified, we don't know where to go. |
2411 | // But do advance, otherwise ":cn" gets stuck. |
2412 | if (qf_ptr->qf_fnum == 0) { |
2413 | goto theend; |
2414 | } |
2415 | if (qf_jump_to_usable_window(qf_ptr->qf_fnum, &opened_window) == FAIL) { |
2416 | goto failed; |
2417 | } |
2418 | } |
2419 | |
2420 | /* |
2421 | * If there is a file name, |
2422 | * read the wanted file if needed, and check autowrite etc. |
2423 | */ |
2424 | old_curbuf = curbuf; |
2425 | old_lnum = curwin->w_cursor.lnum; |
2426 | |
2427 | if (qf_ptr->qf_fnum != 0) { |
2428 | int abort = false; |
2429 | retval = qf_jump_edit_buffer(qi, qf_ptr, forceit, oldwin, &opened_window, |
2430 | &abort); |
2431 | if (abort) { |
2432 | qi = NULL; |
2433 | qf_ptr = NULL; |
2434 | } |
2435 | } |
2436 | |
2437 | if (retval == OK) { |
2438 | // When not switched to another buffer, still need to set pc mark |
2439 | if (curbuf == old_curbuf) { |
2440 | setpcmark(); |
2441 | } |
2442 | |
2443 | if (qf_ptr != NULL) { |
2444 | qf_jump_goto_line(qf_ptr->qf_lnum, qf_ptr->qf_col, qf_ptr->qf_viscol, |
2445 | qf_ptr->qf_pattern); |
2446 | } |
2447 | |
2448 | if ((fdo_flags & FDO_QUICKFIX) && old_KeyTyped) |
2449 | foldOpenCursor(); |
2450 | if (print_message) { |
2451 | qf_jump_print_msg(qi, qf_index, qf_ptr, old_curbuf, old_lnum); |
2452 | } |
2453 | } else { |
2454 | if (opened_window) { |
2455 | win_close(curwin, true); // Close opened window |
2456 | } |
2457 | if (qf_ptr != NULL && qf_ptr->qf_fnum != 0) { |
2458 | // Couldn't open file, so put index back where it was. This could |
2459 | // happen if the file was readonly and we changed something. |
2460 | failed: |
2461 | qf_ptr = old_qf_ptr; |
2462 | qf_index = old_qf_index; |
2463 | } |
2464 | } |
2465 | theend: |
2466 | if (qi != NULL) { |
2467 | qi->qf_lists[qi->qf_curlist].qf_ptr = qf_ptr; |
2468 | qi->qf_lists[qi->qf_curlist].qf_index = qf_index; |
2469 | } |
2470 | if (p_swb != old_swb && opened_window) { |
2471 | /* Restore old 'switchbuf' value, but not when an autocommand or |
2472 | * modeline has changed the value. */ |
2473 | if (p_swb == empty_option) { |
2474 | p_swb = old_swb; |
2475 | swb_flags = old_swb_flags; |
2476 | } else |
2477 | free_string_option(old_swb); |
2478 | } |
2479 | } |
2480 | |
2481 | /* |
2482 | * ":clist": list all errors |
2483 | * ":llist": list all locations |
2484 | */ |
2485 | void qf_list(exarg_T *eap) |
2486 | { |
2487 | buf_T *buf; |
2488 | char_u *fname; |
2489 | qfline_T *qfp; |
2490 | int i; |
2491 | int idx1 = 1; |
2492 | int idx2 = -1; |
2493 | char_u *arg = eap->arg; |
2494 | int qfFileAttr; |
2495 | int qfSepAttr; |
2496 | int qfLineAttr; |
2497 | int all = eap->forceit; // if not :cl!, only show |
2498 | // recognised errors |
2499 | qf_info_T *qi = &ql_info; |
2500 | |
2501 | if (eap->cmdidx == CMD_llist) { |
2502 | qi = GET_LOC_LIST(curwin); |
2503 | if (qi == NULL) { |
2504 | EMSG(_(e_loclist)); |
2505 | return; |
2506 | } |
2507 | } |
2508 | |
2509 | if (qi->qf_curlist >= qi->qf_listcount |
2510 | || qi->qf_lists[qi->qf_curlist].qf_count == 0) { |
2511 | EMSG(_(e_quickfix)); |
2512 | return; |
2513 | } |
2514 | |
2515 | bool plus = false; |
2516 | if (*arg == '+') { |
2517 | arg++; |
2518 | plus = true; |
2519 | } |
2520 | if (!get_list_range(&arg, &idx1, &idx2) || *arg != NUL) { |
2521 | EMSG(_(e_trailing)); |
2522 | return; |
2523 | } |
2524 | if (plus) { |
2525 | i = qi->qf_lists[qi->qf_curlist].qf_index; |
2526 | idx2 = i + idx1; |
2527 | idx1 = i; |
2528 | } else { |
2529 | i = qi->qf_lists[qi->qf_curlist].qf_count; |
2530 | if (idx1 < 0) { |
2531 | idx1 = (-idx1 > i) ? 0 : idx1 + i + 1; |
2532 | } |
2533 | if (idx2 < 0) { |
2534 | idx2 = (-idx2 > i) ? 0 : idx2 + i + 1; |
2535 | } |
2536 | } |
2537 | |
2538 | // Shorten all the file names, so that it is easy to read. |
2539 | shorten_fnames(false); |
2540 | |
2541 | // Get the attributes for the different quickfix highlight items. Note |
2542 | // that this depends on syntax items defined in the qf.vim syntax file |
2543 | qfFileAttr = syn_name2attr((char_u *)"qfFileName" ); |
2544 | if (qfFileAttr == 0) { |
2545 | qfFileAttr = HL_ATTR(HLF_D); |
2546 | } |
2547 | qfSepAttr = syn_name2attr((char_u *)"qfSeparator" ); |
2548 | if (qfSepAttr == 0) { |
2549 | qfSepAttr = HL_ATTR(HLF_D); |
2550 | } |
2551 | qfLineAttr = syn_name2attr((char_u *)"qfLineNr" ); |
2552 | if (qfLineAttr == 0) { |
2553 | qfLineAttr = HL_ATTR(HLF_N); |
2554 | } |
2555 | |
2556 | if (qi->qf_lists[qi->qf_curlist].qf_nonevalid) { |
2557 | all = true; |
2558 | } |
2559 | qfp = qi->qf_lists[qi->qf_curlist].qf_start; |
2560 | for (i = 1; !got_int && i <= qi->qf_lists[qi->qf_curlist].qf_count; ) { |
2561 | if ((qfp->qf_valid || all) && idx1 <= i && i <= idx2) { |
2562 | if (got_int) { |
2563 | break; |
2564 | } |
2565 | |
2566 | fname = NULL; |
2567 | if (qfp->qf_module != NULL && *qfp->qf_module != NUL) { |
2568 | vim_snprintf((char *)IObuff, IOSIZE, "%2d %s" , i, |
2569 | (char *)qfp->qf_module); |
2570 | } else { |
2571 | if (qfp->qf_fnum != 0 && (buf = buflist_findnr(qfp->qf_fnum)) != NULL) { |
2572 | fname = buf->b_fname; |
2573 | if (qfp->qf_type == 1) { // :helpgrep |
2574 | fname = path_tail(fname); |
2575 | } |
2576 | } |
2577 | if (fname == NULL) { |
2578 | snprintf((char *)IObuff, IOSIZE, "%2d" , i); |
2579 | } else { |
2580 | vim_snprintf((char *)IObuff, IOSIZE, "%2d %s" , i, (char *)fname); |
2581 | } |
2582 | } |
2583 | |
2584 | // Support for filtering entries using :filter /pat/ clist |
2585 | // Match against the module name, file name, search pattern and |
2586 | // text of the entry. |
2587 | bool filter_entry = true; |
2588 | if (qfp->qf_module != NULL && *qfp->qf_module != NUL) { |
2589 | filter_entry &= message_filtered(qfp->qf_module); |
2590 | } |
2591 | if (filter_entry && fname != NULL) { |
2592 | filter_entry &= message_filtered(fname); |
2593 | } |
2594 | if (filter_entry && qfp->qf_pattern != NULL) { |
2595 | filter_entry &= message_filtered(qfp->qf_pattern); |
2596 | } |
2597 | if (filter_entry) { |
2598 | filter_entry &= message_filtered(qfp->qf_text); |
2599 | } |
2600 | if (filter_entry) { |
2601 | goto next_entry; |
2602 | } |
2603 | msg_putchar('\n'); |
2604 | msg_outtrans_attr(IObuff, i == qi->qf_lists[qi->qf_curlist].qf_index |
2605 | ? HL_ATTR(HLF_QFL) : qfFileAttr); |
2606 | |
2607 | if (qfp->qf_lnum != 0) { |
2608 | msg_puts_attr(":" , qfSepAttr); |
2609 | } |
2610 | if (qfp->qf_lnum == 0) { |
2611 | IObuff[0] = NUL; |
2612 | } else if (qfp->qf_col == 0) { |
2613 | vim_snprintf((char *)IObuff, IOSIZE, "%" PRIdLINENR, qfp->qf_lnum); |
2614 | } else { |
2615 | vim_snprintf((char *)IObuff, IOSIZE, "%" PRIdLINENR " col %d" , |
2616 | qfp->qf_lnum, qfp->qf_col); |
2617 | } |
2618 | vim_snprintf((char *)IObuff + STRLEN(IObuff), IOSIZE, "%s" , |
2619 | (char *)qf_types(qfp->qf_type, qfp->qf_nr)); |
2620 | msg_puts_attr((const char *)IObuff, qfLineAttr); |
2621 | msg_puts_attr(":" , qfSepAttr); |
2622 | if (qfp->qf_pattern != NULL) { |
2623 | qf_fmt_text(qfp->qf_pattern, IObuff, IOSIZE); |
2624 | msg_puts((const char *)IObuff); |
2625 | msg_puts_attr(":" , qfSepAttr); |
2626 | } |
2627 | msg_puts(" " ); |
2628 | |
2629 | /* Remove newlines and leading whitespace from the text. For an |
2630 | * unrecognized line keep the indent, the compiler may mark a word |
2631 | * with ^^^^. */ |
2632 | qf_fmt_text((fname != NULL || qfp->qf_lnum != 0) |
2633 | ? skipwhite(qfp->qf_text) : qfp->qf_text, |
2634 | IObuff, IOSIZE); |
2635 | msg_prt_line(IObuff, FALSE); |
2636 | ui_flush(); /* show one line at a time */ |
2637 | } |
2638 | |
2639 | next_entry: |
2640 | qfp = qfp->qf_next; |
2641 | if (qfp == NULL) { |
2642 | break; |
2643 | } |
2644 | i++; |
2645 | os_breakcheck(); |
2646 | } |
2647 | } |
2648 | |
2649 | /* |
2650 | * Remove newlines and leading whitespace from an error message. |
2651 | * Put the result in "buf[bufsize]". |
2652 | */ |
2653 | static void qf_fmt_text(char_u *text, char_u *buf, int bufsize) |
2654 | { |
2655 | int i; |
2656 | char_u *p = text; |
2657 | |
2658 | for (i = 0; *p != NUL && i < bufsize - 1; ++i) { |
2659 | if (*p == '\n') { |
2660 | buf[i] = ' '; |
2661 | while (*++p != NUL) |
2662 | if (!ascii_iswhite(*p) && *p != '\n') |
2663 | break; |
2664 | } else |
2665 | buf[i] = *p++; |
2666 | } |
2667 | buf[i] = NUL; |
2668 | } |
2669 | |
2670 | /// Display information (list number, list size and the title) about a |
2671 | /// quickfix/location list. |
2672 | static void qf_msg(qf_info_T *qi, int which, char *lead) |
2673 | { |
2674 | char *title = (char *)qi->qf_lists[which].qf_title; |
2675 | int count = qi->qf_lists[which].qf_count; |
2676 | char_u buf[IOSIZE]; |
2677 | |
2678 | vim_snprintf((char *)buf, IOSIZE, _("%serror list %d of %d; %d errors " ), |
2679 | lead, |
2680 | which + 1, |
2681 | qi->qf_listcount, |
2682 | count); |
2683 | |
2684 | if (title != NULL) { |
2685 | size_t len = STRLEN(buf); |
2686 | |
2687 | if (len < 34) { |
2688 | memset(buf + len, ' ', 34 - len); |
2689 | buf[34] = NUL; |
2690 | } |
2691 | xstrlcat((char *)buf, title, IOSIZE); |
2692 | } |
2693 | trunc_string(buf, buf, Columns - 1, IOSIZE); |
2694 | msg(buf); |
2695 | } |
2696 | |
2697 | /* |
2698 | * ":colder [count]": Up in the quickfix stack. |
2699 | * ":cnewer [count]": Down in the quickfix stack. |
2700 | * ":lolder [count]": Up in the location list stack. |
2701 | * ":lnewer [count]": Down in the location list stack. |
2702 | */ |
2703 | void qf_age(exarg_T *eap) |
2704 | { |
2705 | qf_info_T *qi = &ql_info; |
2706 | int count; |
2707 | |
2708 | if (eap->cmdidx == CMD_lolder || eap->cmdidx == CMD_lnewer) { |
2709 | qi = GET_LOC_LIST(curwin); |
2710 | if (qi == NULL) { |
2711 | EMSG(_(e_loclist)); |
2712 | return; |
2713 | } |
2714 | } |
2715 | |
2716 | if (eap->addr_count != 0) { |
2717 | assert(eap->line2 <= INT_MAX); |
2718 | count = (int)eap->line2; |
2719 | } else { |
2720 | count = 1; |
2721 | } |
2722 | while (count--) { |
2723 | if (eap->cmdidx == CMD_colder || eap->cmdidx == CMD_lolder) { |
2724 | if (qi->qf_curlist == 0) { |
2725 | EMSG(_("E380: At bottom of quickfix stack" )); |
2726 | break; |
2727 | } |
2728 | --qi->qf_curlist; |
2729 | } else { |
2730 | if (qi->qf_curlist >= qi->qf_listcount - 1) { |
2731 | EMSG(_("E381: At top of quickfix stack" )); |
2732 | break; |
2733 | } |
2734 | ++qi->qf_curlist; |
2735 | } |
2736 | } |
2737 | qf_msg(qi, qi->qf_curlist, "" ); |
2738 | qf_update_buffer(qi, NULL); |
2739 | } |
2740 | |
2741 | /// Display the information about all the quickfix/location lists in the stack. |
2742 | void qf_history(exarg_T *eap) |
2743 | { |
2744 | qf_info_T *qi = &ql_info; |
2745 | int i; |
2746 | |
2747 | if (eap->cmdidx == CMD_lhistory) { |
2748 | qi = GET_LOC_LIST(curwin); |
2749 | } |
2750 | if (qi == NULL || (qi->qf_listcount == 0 |
2751 | && qi->qf_lists[qi->qf_curlist].qf_count == 0)) { |
2752 | MSG(_("No entries" )); |
2753 | } else { |
2754 | for (i = 0; i < qi->qf_listcount; i++) { |
2755 | qf_msg(qi, i, i == qi->qf_curlist ? "> " : " " ); |
2756 | } |
2757 | } |
2758 | } |
2759 | |
2760 | /// Free all the entries in the error list "idx". Note that other information |
2761 | /// associated with the list like context and title are not freed. |
2762 | static void qf_free_items(qf_info_T *qi, int idx) |
2763 | { |
2764 | qfline_T *qfp; |
2765 | qfline_T *qfpnext; |
2766 | bool stop = false; |
2767 | qf_list_T *qfl = &qi->qf_lists[idx]; |
2768 | |
2769 | while (qfl->qf_count && qfl->qf_start != NULL) { |
2770 | qfp = qfl->qf_start; |
2771 | qfpnext = qfp->qf_next; |
2772 | if (!stop) { |
2773 | xfree(qfp->qf_module); |
2774 | xfree(qfp->qf_text); |
2775 | xfree(qfp->qf_pattern); |
2776 | stop = (qfp == qfpnext); |
2777 | xfree(qfp); |
2778 | if (stop) { |
2779 | // Somehow qf_count may have an incorrect value, set it to 1 |
2780 | // to avoid crashing when it's wrong. |
2781 | // TODO(vim): Avoid qf_count being incorrect. |
2782 | qfl->qf_count = 1; |
2783 | } |
2784 | } |
2785 | qfl->qf_start = qfpnext; |
2786 | qfl->qf_count--; |
2787 | } |
2788 | |
2789 | qfl->qf_start = NULL; |
2790 | qfl->qf_ptr = NULL; |
2791 | qfl->qf_index = 0; |
2792 | qfl->qf_start = NULL; |
2793 | qfl->qf_last = NULL; |
2794 | qfl->qf_ptr = NULL; |
2795 | qfl->qf_nonevalid = true; |
2796 | |
2797 | qf_clean_dir_stack(&qfl->qf_dir_stack); |
2798 | qfl->qf_directory = NULL; |
2799 | qf_clean_dir_stack(&qfl->qf_file_stack); |
2800 | qfl->qf_currfile = NULL; |
2801 | qfl->qf_multiline = false; |
2802 | qfl->qf_multiignore = false; |
2803 | qfl->qf_multiscan = false; |
2804 | } |
2805 | |
2806 | /// Free error list "idx". Frees all the entries in the quickfix list, |
2807 | /// associated context information and the title. |
2808 | static void qf_free(qf_info_T *qi, int idx) |
2809 | { |
2810 | qf_list_T *qfl = &qi->qf_lists[idx]; |
2811 | qf_free_items(qi, idx); |
2812 | |
2813 | XFREE_CLEAR(qfl->qf_title); |
2814 | tv_free(qfl->qf_ctx); |
2815 | qfl->qf_ctx = NULL; |
2816 | qfl->qf_id = 0; |
2817 | qfl->qf_changedtick = 0L; |
2818 | } |
2819 | |
2820 | /* |
2821 | * qf_mark_adjust: adjust marks |
2822 | */ |
2823 | bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, |
2824 | long amount_after) |
2825 | { |
2826 | int i; |
2827 | qfline_T *qfp; |
2828 | int idx; |
2829 | qf_info_T *qi = &ql_info; |
2830 | bool found_one = false; |
2831 | int buf_has_flag = wp == NULL ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY; |
2832 | |
2833 | if (!(curbuf->b_has_qf_entry & buf_has_flag)) { |
2834 | return false; |
2835 | } |
2836 | if (wp != NULL) { |
2837 | if (wp->w_llist == NULL) { |
2838 | return false; |
2839 | } |
2840 | qi = wp->w_llist; |
2841 | } |
2842 | |
2843 | for (idx = 0; idx < qi->qf_listcount; ++idx) |
2844 | if (qi->qf_lists[idx].qf_count) |
2845 | for (i = 0, qfp = qi->qf_lists[idx].qf_start; |
2846 | i < qi->qf_lists[idx].qf_count && qfp != NULL; |
2847 | i++, qfp = qfp->qf_next) { |
2848 | if (qfp->qf_fnum == curbuf->b_fnum) { |
2849 | found_one = true; |
2850 | if (qfp->qf_lnum >= line1 && qfp->qf_lnum <= line2) { |
2851 | if (amount == MAXLNUM) |
2852 | qfp->qf_cleared = TRUE; |
2853 | else |
2854 | qfp->qf_lnum += amount; |
2855 | } else if (amount_after && qfp->qf_lnum > line2) |
2856 | qfp->qf_lnum += amount_after; |
2857 | } |
2858 | } |
2859 | |
2860 | return found_one; |
2861 | } |
2862 | |
2863 | /* |
2864 | * Make a nice message out of the error character and the error number: |
2865 | * char number message |
2866 | * e or E 0 " error" |
2867 | * w or W 0 " warning" |
2868 | * i or I 0 " info" |
2869 | * 0 0 "" |
2870 | * other 0 " c" |
2871 | * e or E n " error n" |
2872 | * w or W n " warning n" |
2873 | * i or I n " info n" |
2874 | * 0 n " error n" |
2875 | * other n " c n" |
2876 | * 1 x "" :helpgrep |
2877 | */ |
2878 | static char_u *qf_types(int c, int nr) |
2879 | { |
2880 | static char_u buf[20]; |
2881 | static char_u cc[3]; |
2882 | char_u *p; |
2883 | |
2884 | if (c == 'W' || c == 'w') |
2885 | p = (char_u *)" warning" ; |
2886 | else if (c == 'I' || c == 'i') |
2887 | p = (char_u *)" info" ; |
2888 | else if (c == 'E' || c == 'e' || (c == 0 && nr > 0)) |
2889 | p = (char_u *)" error" ; |
2890 | else if (c == 0 || c == 1) |
2891 | p = (char_u *)"" ; |
2892 | else { |
2893 | cc[0] = ' '; |
2894 | cc[1] = (char_u)c; |
2895 | cc[2] = NUL; |
2896 | p = cc; |
2897 | } |
2898 | |
2899 | if (nr <= 0) |
2900 | return p; |
2901 | |
2902 | sprintf((char *)buf, "%s %3d" , (char *)p, nr); |
2903 | return buf; |
2904 | } |
2905 | |
2906 | // When "split" is false: Open the entry/result under the cursor. |
2907 | // When "split" is true: Open the entry/result under the cursor in a new window. |
2908 | void qf_view_result(bool split) |
2909 | { |
2910 | qf_info_T *qi = &ql_info; |
2911 | |
2912 | if (!bt_quickfix(curbuf)) { |
2913 | return; |
2914 | } |
2915 | if (IS_LL_WINDOW(curwin)) { |
2916 | qi = GET_LOC_LIST(curwin); |
2917 | } |
2918 | if (qf_list_empty(qi, qi->qf_curlist)) { |
2919 | EMSG(_(e_quickfix)); |
2920 | return; |
2921 | } |
2922 | |
2923 | if (split) { |
2924 | char cmd[32]; |
2925 | |
2926 | snprintf(cmd, sizeof(cmd), "split +%" PRId64 "%s" , |
2927 | (int64_t)curwin->w_cursor.lnum, |
2928 | IS_LL_WINDOW(curwin) ? "ll" : "cc" ); |
2929 | if (do_cmdline_cmd(cmd) == OK) { |
2930 | do_cmdline_cmd("clearjumps" ); |
2931 | } |
2932 | return; |
2933 | } |
2934 | |
2935 | do_cmdline_cmd((IS_LL_WINDOW(curwin) ? ".ll" : ".cc" )); |
2936 | } |
2937 | |
2938 | /* |
2939 | * ":cwindow": open the quickfix window if we have errors to display, |
2940 | * close it if not. |
2941 | * ":lwindow": open the location list window if we have locations to display, |
2942 | * close it if not. |
2943 | */ |
2944 | void ex_cwindow(exarg_T *eap) |
2945 | { |
2946 | qf_info_T *qi = &ql_info; |
2947 | win_T *win; |
2948 | |
2949 | if (eap->cmdidx == CMD_lwindow) { |
2950 | qi = GET_LOC_LIST(curwin); |
2951 | if (qi == NULL) |
2952 | return; |
2953 | } |
2954 | |
2955 | /* Look for an existing quickfix window. */ |
2956 | win = qf_find_win(qi); |
2957 | |
2958 | /* |
2959 | * If a quickfix window is open but we have no errors to display, |
2960 | * close the window. If a quickfix window is not open, then open |
2961 | * it if we have errors; otherwise, leave it closed. |
2962 | */ |
2963 | if (qi->qf_lists[qi->qf_curlist].qf_nonevalid |
2964 | || qi->qf_lists[qi->qf_curlist].qf_count == 0 |
2965 | || qi->qf_curlist >= qi->qf_listcount) { |
2966 | if (win != NULL) |
2967 | ex_cclose(eap); |
2968 | } else if (win == NULL) |
2969 | ex_copen(eap); |
2970 | } |
2971 | |
2972 | /* |
2973 | * ":cclose": close the window showing the list of errors. |
2974 | * ":lclose": close the window showing the location list |
2975 | */ |
2976 | void ex_cclose(exarg_T *eap) |
2977 | { |
2978 | win_T *win = NULL; |
2979 | qf_info_T *qi = &ql_info; |
2980 | |
2981 | if (eap->cmdidx == CMD_lclose || eap->cmdidx == CMD_lwindow) { |
2982 | qi = GET_LOC_LIST(curwin); |
2983 | if (qi == NULL) |
2984 | return; |
2985 | } |
2986 | |
2987 | /* Find existing quickfix window and close it. */ |
2988 | win = qf_find_win(qi); |
2989 | if (win != NULL) { |
2990 | win_close(win, false); |
2991 | } |
2992 | } |
2993 | |
2994 | /* |
2995 | * ":copen": open a window that shows the list of errors. |
2996 | * ":lopen": open a window that shows the location list. |
2997 | */ |
2998 | void ex_copen(exarg_T *eap) |
2999 | { |
3000 | qf_info_T *qi = &ql_info; |
3001 | int height; |
3002 | win_T *win; |
3003 | tabpage_T *prevtab = curtab; |
3004 | buf_T *qf_buf; |
3005 | win_T *oldwin = curwin; |
3006 | |
3007 | if (eap->cmdidx == CMD_lopen || eap->cmdidx == CMD_lwindow) { |
3008 | qi = GET_LOC_LIST(curwin); |
3009 | if (qi == NULL) { |
3010 | EMSG(_(e_loclist)); |
3011 | return; |
3012 | } |
3013 | } |
3014 | |
3015 | if (eap->addr_count != 0) { |
3016 | assert(eap->line2 <= INT_MAX); |
3017 | height = (int)eap->line2; |
3018 | } else { |
3019 | height = QF_WINHEIGHT; |
3020 | } |
3021 | reset_VIsual_and_resel(); // stop Visual mode |
3022 | |
3023 | /* |
3024 | * Find existing quickfix window, or open a new one. |
3025 | */ |
3026 | win = qf_find_win(qi); |
3027 | |
3028 | if (win != NULL && cmdmod.tab == 0) { |
3029 | win_goto(win); |
3030 | if (eap->addr_count != 0) { |
3031 | if (cmdmod.split & WSP_VERT) { |
3032 | if (height != win->w_width) { |
3033 | win_setwidth(height); |
3034 | } |
3035 | } else { |
3036 | if (height != win->w_height) { |
3037 | win_setheight(height); |
3038 | } |
3039 | } |
3040 | } |
3041 | } else { |
3042 | int flags = 0; |
3043 | |
3044 | qf_buf = qf_find_buf(qi); |
3045 | |
3046 | /* The current window becomes the previous window afterwards. */ |
3047 | win = curwin; |
3048 | |
3049 | if ((eap->cmdidx == CMD_copen || eap->cmdidx == CMD_cwindow) |
3050 | && cmdmod.split == 0) |
3051 | // Create the new quickfix window at the very bottom, except when |
3052 | // :belowright or :aboveleft is used. |
3053 | win_goto(lastwin); |
3054 | // Default is to open the window below the current window |
3055 | if (cmdmod.split == 0) { |
3056 | flags = WSP_BELOW; |
3057 | } |
3058 | flags |= WSP_NEWLOC; |
3059 | if (win_split(height, flags) == FAIL) { |
3060 | return; // not enough room for window |
3061 | } |
3062 | RESET_BINDING(curwin); |
3063 | |
3064 | if (eap->cmdidx == CMD_lopen || eap->cmdidx == CMD_lwindow) { |
3065 | /* |
3066 | * For the location list window, create a reference to the |
3067 | * location list from the window 'win'. |
3068 | */ |
3069 | curwin->w_llist_ref = win->w_llist; |
3070 | win->w_llist->qf_refcount++; |
3071 | } |
3072 | |
3073 | if (oldwin != curwin) |
3074 | oldwin = NULL; /* don't store info when in another window */ |
3075 | if (qf_buf != NULL) |
3076 | /* Use the existing quickfix buffer */ |
3077 | (void)do_ecmd(qf_buf->b_fnum, NULL, NULL, NULL, ECMD_ONE, |
3078 | ECMD_HIDE + ECMD_OLDBUF, oldwin); |
3079 | else { |
3080 | /* Create a new quickfix buffer */ |
3081 | (void)do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, oldwin); |
3082 | // Switch off 'swapfile'. |
3083 | set_option_value("swf" , 0L, NULL, OPT_LOCAL); |
3084 | set_option_value("bt" , 0L, "quickfix" , OPT_LOCAL); |
3085 | set_option_value("bh" , 0L, "wipe" , OPT_LOCAL); |
3086 | RESET_BINDING(curwin); |
3087 | curwin->w_p_diff = false; |
3088 | set_option_value("fdm" , 0L, "manual" , OPT_LOCAL); |
3089 | } |
3090 | |
3091 | /* Only set the height when still in the same tab page and there is no |
3092 | * window to the side. */ |
3093 | if (curtab == prevtab |
3094 | && curwin->w_width == Columns |
3095 | ) |
3096 | win_setheight(height); |
3097 | curwin->w_p_wfh = TRUE; /* set 'winfixheight' */ |
3098 | if (win_valid(win)) |
3099 | prevwin = win; |
3100 | } |
3101 | |
3102 | qf_set_title_var(qi); |
3103 | |
3104 | // Fill the buffer with the quickfix list. |
3105 | qf_fill_buffer(qi, curbuf, NULL); |
3106 | |
3107 | curwin->w_cursor.lnum = qi->qf_lists[qi->qf_curlist].qf_index; |
3108 | curwin->w_cursor.col = 0; |
3109 | check_cursor(); |
3110 | update_topline(); /* scroll to show the line */ |
3111 | } |
3112 | |
3113 | // Move the cursor in the quickfix window to "lnum". |
3114 | static void qf_win_goto(win_T *win, linenr_T lnum) |
3115 | { |
3116 | win_T *old_curwin = curwin; |
3117 | |
3118 | curwin = win; |
3119 | curbuf = win->w_buffer; |
3120 | curwin->w_cursor.lnum = lnum; |
3121 | curwin->w_cursor.col = 0; |
3122 | curwin->w_cursor.coladd = 0; |
3123 | curwin->w_curswant = 0; |
3124 | update_topline(); // scroll to show the line |
3125 | redraw_later(VALID); |
3126 | curwin->w_redr_status = true; // update ruler |
3127 | curwin = old_curwin; |
3128 | curbuf = curwin->w_buffer; |
3129 | } |
3130 | |
3131 | // :cbottom/:lbottom command. |
3132 | void ex_cbottom(exarg_T *eap) |
3133 | { |
3134 | qf_info_T *qi = &ql_info; |
3135 | |
3136 | if (eap->cmdidx == CMD_lbottom) { |
3137 | qi = GET_LOC_LIST(curwin); |
3138 | if (qi == NULL) { |
3139 | EMSG(_(e_loclist)); |
3140 | return; |
3141 | } |
3142 | } |
3143 | |
3144 | win_T *win = qf_find_win(qi); |
3145 | |
3146 | if (win != NULL && win->w_cursor.lnum != win->w_buffer->b_ml.ml_line_count) { |
3147 | qf_win_goto(win, win->w_buffer->b_ml.ml_line_count); |
3148 | } |
3149 | } |
3150 | |
3151 | /* |
3152 | * Return the number of the current entry (line number in the quickfix |
3153 | * window). |
3154 | */ |
3155 | linenr_T qf_current_entry(win_T *wp) |
3156 | { |
3157 | qf_info_T *qi = &ql_info; |
3158 | |
3159 | if (IS_LL_WINDOW(wp)) |
3160 | /* In the location list window, use the referenced location list */ |
3161 | qi = wp->w_llist_ref; |
3162 | |
3163 | return qi->qf_lists[qi->qf_curlist].qf_index; |
3164 | } |
3165 | |
3166 | /* |
3167 | * Update the cursor position in the quickfix window to the current error. |
3168 | * Return TRUE if there is a quickfix window. |
3169 | */ |
3170 | static int |
3171 | qf_win_pos_update ( |
3172 | qf_info_T *qi, |
3173 | int old_qf_index /* previous qf_index or zero */ |
3174 | ) |
3175 | { |
3176 | win_T *win; |
3177 | int qf_index = qi->qf_lists[qi->qf_curlist].qf_index; |
3178 | |
3179 | /* |
3180 | * Put the cursor on the current error in the quickfix window, so that |
3181 | * it's viewable. |
3182 | */ |
3183 | win = qf_find_win(qi); |
3184 | if (win != NULL |
3185 | && qf_index <= win->w_buffer->b_ml.ml_line_count |
3186 | && old_qf_index != qf_index) { |
3187 | if (qf_index > old_qf_index) { |
3188 | win->w_redraw_top = old_qf_index; |
3189 | win->w_redraw_bot = qf_index; |
3190 | } else { |
3191 | win->w_redraw_top = qf_index; |
3192 | win->w_redraw_bot = old_qf_index; |
3193 | } |
3194 | qf_win_goto(win, qf_index); |
3195 | } |
3196 | return win != NULL; |
3197 | } |
3198 | |
3199 | /// Checks whether the given window is displaying the specified |
3200 | /// quickfix/location list buffer. |
3201 | static int is_qf_win(win_T *win, qf_info_T *qi) |
3202 | { |
3203 | // |
3204 | // A window displaying the quickfix buffer will have the w_llist_ref field |
3205 | // set to NULL. |
3206 | // A window displaying a location list buffer will have the w_llist_ref |
3207 | // pointing to the location list. |
3208 | // |
3209 | if (bt_quickfix(win->w_buffer)) { |
3210 | if ((IS_QF_STACK(qi) && win->w_llist_ref == NULL) |
3211 | || (IS_LL_STACK(qi) && win->w_llist_ref == qi)) { |
3212 | return true; |
3213 | } |
3214 | } |
3215 | |
3216 | return false; |
3217 | } |
3218 | |
3219 | /// Find a window displaying the quickfix/location list 'qi' |
3220 | /// Only searches in the current tabpage. |
3221 | static win_T *qf_find_win(qf_info_T *qi) |
3222 | { |
3223 | FOR_ALL_WINDOWS_IN_TAB(win, curtab) { |
3224 | if (is_qf_win(win, qi)) { |
3225 | return win; |
3226 | } |
3227 | } |
3228 | |
3229 | return NULL; |
3230 | } |
3231 | |
3232 | /* |
3233 | * Find a quickfix buffer. |
3234 | * Searches in windows opened in all the tabs. |
3235 | */ |
3236 | static buf_T *qf_find_buf(qf_info_T *qi) |
3237 | { |
3238 | FOR_ALL_TAB_WINDOWS(tp, win) { |
3239 | if (is_qf_win(win, qi)) { |
3240 | return win->w_buffer; |
3241 | } |
3242 | } |
3243 | |
3244 | return NULL; |
3245 | } |
3246 | |
3247 | /// Update the w:quickfix_title variable in the quickfix/location list window |
3248 | static void qf_update_win_titlevar(qf_info_T *qi) |
3249 | { |
3250 | win_T *win; |
3251 | |
3252 | if ((win = qf_find_win(qi)) != NULL) { |
3253 | win_T *curwin_save = curwin; |
3254 | curwin = win; |
3255 | qf_set_title_var(qi); |
3256 | curwin = curwin_save; |
3257 | } |
3258 | } |
3259 | |
3260 | /* |
3261 | * Find the quickfix buffer. If it exists, update the contents. |
3262 | */ |
3263 | static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last) |
3264 | { |
3265 | buf_T *buf; |
3266 | win_T *win; |
3267 | aco_save_T aco; |
3268 | |
3269 | /* Check if a buffer for the quickfix list exists. Update it. */ |
3270 | buf = qf_find_buf(qi); |
3271 | if (buf != NULL) { |
3272 | linenr_T old_line_count = buf->b_ml.ml_line_count; |
3273 | |
3274 | if (old_last == NULL) { |
3275 | // set curwin/curbuf to buf and save a few things |
3276 | aucmd_prepbuf(&aco, buf); |
3277 | } |
3278 | |
3279 | qf_update_win_titlevar(qi); |
3280 | |
3281 | qf_fill_buffer(qi, buf, old_last); |
3282 | buf_inc_changedtick(buf); |
3283 | |
3284 | if (old_last == NULL) { |
3285 | (void)qf_win_pos_update(qi, 0); |
3286 | |
3287 | // restore curwin/curbuf and a few other things |
3288 | aucmd_restbuf(&aco); |
3289 | } |
3290 | |
3291 | // Only redraw when added lines are visible. This avoids flickering when |
3292 | // the added lines are not visible. |
3293 | if ((win = qf_find_win(qi)) != NULL && old_line_count < win->w_botline) { |
3294 | redraw_buf_later(buf, NOT_VALID); |
3295 | } |
3296 | } |
3297 | } |
3298 | |
3299 | // Set "w:quickfix_title" if "qi" has a title. |
3300 | static void qf_set_title_var(qf_info_T *qi) |
3301 | { |
3302 | if (qi->qf_lists[qi->qf_curlist].qf_title != NULL) { |
3303 | set_internal_string_var((char_u *)"w:quickfix_title" , |
3304 | qi->qf_lists[qi->qf_curlist].qf_title); |
3305 | } |
3306 | } |
3307 | |
3308 | // Fill current buffer with quickfix errors, replacing any previous contents. |
3309 | // curbuf must be the quickfix buffer! |
3310 | // If "old_last" is not NULL append the items after this one. |
3311 | // When "old_last" is NULL then "buf" must equal "curbuf"! Because ml_delete() |
3312 | // is used and autocommands will be triggered. |
3313 | static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last) |
3314 | { |
3315 | linenr_T lnum; |
3316 | qfline_T *qfp; |
3317 | buf_T *errbuf; |
3318 | int len; |
3319 | const bool old_KeyTyped = KeyTyped; |
3320 | |
3321 | if (old_last == NULL) { |
3322 | if (buf != curbuf) { |
3323 | internal_error("qf_fill_buffer()" ); |
3324 | return; |
3325 | } |
3326 | |
3327 | // delete all existing lines |
3328 | while ((curbuf->b_ml.ml_flags & ML_EMPTY) == 0) { |
3329 | (void)ml_delete((linenr_T)1, false); |
3330 | } |
3331 | } |
3332 | |
3333 | /* Check if there is anything to display */ |
3334 | if (qi->qf_curlist < qi->qf_listcount) { |
3335 | char_u dirname[MAXPATHL]; |
3336 | |
3337 | *dirname = NUL; |
3338 | |
3339 | // Add one line for each error |
3340 | if (old_last == NULL) { |
3341 | qfp = qi->qf_lists[qi->qf_curlist].qf_start; |
3342 | lnum = 0; |
3343 | } else { |
3344 | qfp = old_last->qf_next; |
3345 | lnum = buf->b_ml.ml_line_count; |
3346 | } |
3347 | while (lnum < qi->qf_lists[qi->qf_curlist].qf_count) { |
3348 | if (qfp->qf_module != NULL) { |
3349 | STRCPY(IObuff, qfp->qf_module); |
3350 | len = (int)STRLEN(IObuff); |
3351 | } else if (qfp->qf_fnum != 0 |
3352 | && (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL |
3353 | && errbuf->b_fname != NULL) { |
3354 | if (qfp->qf_type == 1) { // :helpgrep |
3355 | STRLCPY(IObuff, path_tail(errbuf->b_fname), sizeof(IObuff)); |
3356 | } else { |
3357 | // shorten the file name if not done already |
3358 | if (errbuf->b_sfname == NULL |
3359 | || path_is_absolute(errbuf->b_sfname)) { |
3360 | if (*dirname == NUL) { |
3361 | os_dirname(dirname, MAXPATHL); |
3362 | } |
3363 | shorten_buf_fname(errbuf, dirname, false); |
3364 | } |
3365 | STRLCPY(IObuff, errbuf->b_fname, sizeof(IObuff)); |
3366 | } |
3367 | len = (int)STRLEN(IObuff); |
3368 | } else { |
3369 | len = 0; |
3370 | } |
3371 | IObuff[len++] = '|'; |
3372 | |
3373 | if (qfp->qf_lnum > 0) { |
3374 | sprintf((char *)IObuff + len, "%" PRId64, (int64_t)qfp->qf_lnum); |
3375 | len += (int)STRLEN(IObuff + len); |
3376 | |
3377 | if (qfp->qf_col > 0) { |
3378 | sprintf((char *)IObuff + len, " col %d" , qfp->qf_col); |
3379 | len += (int)STRLEN(IObuff + len); |
3380 | } |
3381 | |
3382 | sprintf((char *)IObuff + len, "%s" , |
3383 | (char *)qf_types(qfp->qf_type, qfp->qf_nr)); |
3384 | len += (int)STRLEN(IObuff + len); |
3385 | } else if (qfp->qf_pattern != NULL) { |
3386 | qf_fmt_text(qfp->qf_pattern, IObuff + len, IOSIZE - len); |
3387 | len += (int)STRLEN(IObuff + len); |
3388 | } |
3389 | IObuff[len++] = '|'; |
3390 | IObuff[len++] = ' '; |
3391 | |
3392 | /* Remove newlines and leading whitespace from the text. |
3393 | * For an unrecognized line keep the indent, the compiler may |
3394 | * mark a word with ^^^^. */ |
3395 | qf_fmt_text(len > 3 ? skipwhite(qfp->qf_text) : qfp->qf_text, |
3396 | IObuff + len, IOSIZE - len); |
3397 | |
3398 | if (ml_append_buf(buf, lnum, IObuff, (colnr_T)STRLEN(IObuff) + 1, false) |
3399 | == FAIL) { |
3400 | break; |
3401 | } |
3402 | lnum++; |
3403 | qfp = qfp->qf_next; |
3404 | if (qfp == NULL) { |
3405 | break; |
3406 | } |
3407 | } |
3408 | if (old_last == NULL) { |
3409 | // Delete the empty line which is now at the end |
3410 | (void)ml_delete(lnum + 1, false); |
3411 | } |
3412 | } |
3413 | |
3414 | // Correct cursor position. |
3415 | check_lnums(true); |
3416 | |
3417 | if (old_last == NULL) { |
3418 | // Set the 'filetype' to "qf" each time after filling the buffer. This |
3419 | // resembles reading a file into a buffer, it's more logical when using |
3420 | // autocommands. |
3421 | curbuf_lock++; |
3422 | set_option_value("ft" , 0L, "qf" , OPT_LOCAL); |
3423 | curbuf->b_p_ma = false; |
3424 | |
3425 | keep_filetype = true; // don't detect 'filetype' |
3426 | apply_autocmds(EVENT_BUFREADPOST, (char_u *)"quickfix" , NULL, |
3427 | false, curbuf); |
3428 | apply_autocmds(EVENT_BUFWINENTER, (char_u *)"quickfix" , NULL, |
3429 | false, curbuf); |
3430 | keep_filetype = false; |
3431 | curbuf_lock--; |
3432 | |
3433 | // make sure it will be redrawn |
3434 | redraw_curbuf_later(NOT_VALID); |
3435 | } |
3436 | |
3437 | /* Restore KeyTyped, setting 'filetype' may reset it. */ |
3438 | KeyTyped = old_KeyTyped; |
3439 | } |
3440 | |
3441 | static void qf_list_changed(qf_info_T *qi, int qf_idx) |
3442 | { |
3443 | qi->qf_lists[qf_idx].qf_changedtick++; |
3444 | } |
3445 | |
3446 | /// Return the quickfix/location list number with the given identifier. |
3447 | /// |
3448 | /// @returns -1 if list is not found. |
3449 | static int qf_id2nr(const qf_info_T *const qi, const unsigned qfid) |
3450 | { |
3451 | for (int qf_idx = 0; qf_idx < qi->qf_listcount; qf_idx++) { |
3452 | if (qi->qf_lists[qf_idx].qf_id == qfid) { |
3453 | return qf_idx; |
3454 | } |
3455 | } |
3456 | return INVALID_QFIDX; |
3457 | } |
3458 | |
3459 | // If the current list is not "save_qfid" and we can find the list with that ID |
3460 | // then make it the current list. |
3461 | // This is used when autocommands may have changed the current list. |
3462 | // Returns OK if successfully restored the list. Returns FAIL if the list with |
3463 | // the specified identifier (save_qfid) is not found in the stack. |
3464 | static int qf_restore_list(qf_info_T *qi, unsigned save_qfid) |
3465 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT |
3466 | { |
3467 | if (qi->qf_lists[qi->qf_curlist].qf_id != save_qfid) { |
3468 | const int curlist = qf_id2nr(qi, save_qfid); |
3469 | if (curlist < 0) { |
3470 | // list is not present |
3471 | return FAIL; |
3472 | } |
3473 | qi->qf_curlist = curlist; |
3474 | } |
3475 | return OK; |
3476 | } |
3477 | |
3478 | // Jump to the first entry if there is one. |
3479 | static void qf_jump_first(qf_info_T *qi, unsigned save_qfid, int forceit) |
3480 | FUNC_ATTR_NONNULL_ALL |
3481 | { |
3482 | if (qf_restore_list(qi, save_qfid) == FAIL) { |
3483 | return; |
3484 | } |
3485 | // Autocommands might have cleared the list, check for that |
3486 | if (!qf_list_empty(qi, qi->qf_curlist)) { |
3487 | qf_jump(qi, 0, 0, forceit); |
3488 | } |
3489 | } |
3490 | |
3491 | /* |
3492 | * Return TRUE when using ":vimgrep" for ":grep". |
3493 | */ |
3494 | int grep_internal(cmdidx_T cmdidx) |
3495 | { |
3496 | return (cmdidx == CMD_grep |
3497 | || cmdidx == CMD_lgrep |
3498 | || cmdidx == CMD_grepadd |
3499 | || cmdidx == CMD_lgrepadd) |
3500 | && STRCMP("internal" , |
3501 | *curbuf->b_p_gp == NUL ? p_gp : curbuf->b_p_gp) == 0; |
3502 | } |
3503 | |
3504 | /* |
3505 | * Used for ":make", ":lmake", ":grep", ":lgrep", ":grepadd", and ":lgrepadd" |
3506 | */ |
3507 | void ex_make(exarg_T *eap) |
3508 | { |
3509 | char_u *fname; |
3510 | win_T *wp = NULL; |
3511 | qf_info_T *qi = &ql_info; |
3512 | int res; |
3513 | char_u *au_name = NULL; |
3514 | char_u *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc; |
3515 | |
3516 | /* Redirect ":grep" to ":vimgrep" if 'grepprg' is "internal". */ |
3517 | if (grep_internal(eap->cmdidx)) { |
3518 | ex_vimgrep(eap); |
3519 | return; |
3520 | } |
3521 | |
3522 | switch (eap->cmdidx) { |
3523 | case CMD_make: au_name = (char_u *)"make" ; break; |
3524 | case CMD_lmake: au_name = (char_u *)"lmake" ; break; |
3525 | case CMD_grep: au_name = (char_u *)"grep" ; break; |
3526 | case CMD_lgrep: au_name = (char_u *)"lgrep" ; break; |
3527 | case CMD_grepadd: au_name = (char_u *)"grepadd" ; break; |
3528 | case CMD_lgrepadd: au_name = (char_u *)"lgrepadd" ; break; |
3529 | default: break; |
3530 | } |
3531 | if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name, |
3532 | curbuf->b_fname, true, curbuf)) { |
3533 | if (aborting()) { |
3534 | return; |
3535 | } |
3536 | } |
3537 | |
3538 | if (eap->cmdidx == CMD_lmake || eap->cmdidx == CMD_lgrep |
3539 | || eap->cmdidx == CMD_lgrepadd) |
3540 | wp = curwin; |
3541 | |
3542 | autowrite_all(); |
3543 | fname = get_mef_name(); |
3544 | if (fname == NULL) |
3545 | return; |
3546 | os_remove((char *)fname); // in case it's not unique |
3547 | |
3548 | // If 'shellpipe' empty: don't redirect to 'errorfile'. |
3549 | const size_t len = (STRLEN(p_shq) * 2 + STRLEN(eap->arg) + 1 |
3550 | + (*p_sp == NUL |
3551 | ? 0 |
3552 | : STRLEN(p_sp) + STRLEN(fname) + 3)); |
3553 | char *const cmd = xmalloc(len); |
3554 | snprintf(cmd, len, "%s%s%s" , (char *)p_shq, (char *)eap->arg, |
3555 | (char *)p_shq); |
3556 | if (*p_sp != NUL) { |
3557 | append_redir(cmd, len, (char *) p_sp, (char *) fname); |
3558 | } |
3559 | // Output a newline if there's something else than the :make command that |
3560 | // was typed (in which case the cursor is in column 0). |
3561 | if (msg_col == 0) { |
3562 | msg_didout = false; |
3563 | } |
3564 | msg_start(); |
3565 | MSG_PUTS(":!" ); |
3566 | msg_outtrans((char_u *)cmd); // show what we are doing |
3567 | |
3568 | do_shell((char_u *)cmd, 0); |
3569 | |
3570 | |
3571 | res = qf_init(wp, fname, (eap->cmdidx != CMD_make |
3572 | && eap->cmdidx != CMD_lmake) ? p_gefm : p_efm, |
3573 | (eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd), |
3574 | qf_cmdtitle(*eap->cmdlinep), enc); |
3575 | if (wp != NULL) { |
3576 | qi = GET_LOC_LIST(wp); |
3577 | if (qi == NULL) { |
3578 | goto cleanup; |
3579 | } |
3580 | } |
3581 | if (res >= 0) { |
3582 | qf_list_changed(qi, qi->qf_curlist); |
3583 | } |
3584 | // Remember the current quickfix list identifier, so that we can |
3585 | // check for autocommands changing the current quickfix list. |
3586 | unsigned save_qfid = qi->qf_lists[qi->qf_curlist].qf_id; |
3587 | if (au_name != NULL) { |
3588 | apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, curbuf->b_fname, true, |
3589 | curbuf); |
3590 | } |
3591 | if (res > 0 && !eap->forceit && qflist_valid(wp, save_qfid)) { |
3592 | // display the first error |
3593 | qf_jump_first(qi, save_qfid, false); |
3594 | } |
3595 | |
3596 | cleanup: |
3597 | os_remove((char *)fname); |
3598 | xfree(fname); |
3599 | xfree(cmd); |
3600 | } |
3601 | |
3602 | /* |
3603 | * Return the name for the errorfile, in allocated memory. |
3604 | * Find a new unique name when 'makeef' contains "##". |
3605 | * Returns NULL for error. |
3606 | */ |
3607 | static char_u *get_mef_name(void) |
3608 | { |
3609 | char_u *p; |
3610 | char_u *name; |
3611 | static int start = -1; |
3612 | static int off = 0; |
3613 | |
3614 | if (*p_mef == NUL) { |
3615 | name = vim_tempname(); |
3616 | if (name == NULL) |
3617 | EMSG(_(e_notmp)); |
3618 | return name; |
3619 | } |
3620 | |
3621 | for (p = p_mef; *p; ++p) |
3622 | if (p[0] == '#' && p[1] == '#') |
3623 | break; |
3624 | |
3625 | if (*p == NUL) |
3626 | return vim_strsave(p_mef); |
3627 | |
3628 | /* Keep trying until the name doesn't exist yet. */ |
3629 | for (;; ) { |
3630 | if (start == -1) { |
3631 | start = (int)os_get_pid(); |
3632 | } else { |
3633 | off += 19; |
3634 | } |
3635 | name = xmalloc(STRLEN(p_mef) + 30); |
3636 | STRCPY(name, p_mef); |
3637 | sprintf((char *)name + (p - p_mef), "%d%d" , start, off); |
3638 | STRCAT(name, p + 2); |
3639 | // Don't accept a symbolic link, it's a security risk. |
3640 | FileInfo file_info; |
3641 | bool file_or_link_found = os_fileinfo_link((char *)name, &file_info); |
3642 | if (!file_or_link_found) { |
3643 | break; |
3644 | } |
3645 | xfree(name); |
3646 | } |
3647 | return name; |
3648 | } |
3649 | |
3650 | /// Returns the number of valid entries in the current quickfix/location list. |
3651 | size_t qf_get_size(exarg_T *eap) |
3652 | FUNC_ATTR_NONNULL_ALL |
3653 | { |
3654 | qf_info_T *qi = &ql_info; |
3655 | if (eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) { |
3656 | // Location list. |
3657 | qi = GET_LOC_LIST(curwin); |
3658 | if (qi == NULL) { |
3659 | return 0; |
3660 | } |
3661 | } |
3662 | |
3663 | int prev_fnum = 0; |
3664 | size_t sz = 0; |
3665 | qfline_T *qfp; |
3666 | size_t i; |
3667 | assert(qi->qf_lists[qi->qf_curlist].qf_count >= 0); |
3668 | for (i = 0, qfp = qi->qf_lists[qi->qf_curlist].qf_start; |
3669 | i < (size_t)qi->qf_lists[qi->qf_curlist].qf_count && qfp != NULL; |
3670 | i++, qfp = qfp->qf_next) { |
3671 | if (!qfp->qf_valid) { |
3672 | continue; |
3673 | } |
3674 | |
3675 | if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo) { |
3676 | // Count all valid entries. |
3677 | sz++; |
3678 | } else if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum) { |
3679 | // Count the number of files. |
3680 | sz++; |
3681 | prev_fnum = qfp->qf_fnum; |
3682 | } |
3683 | } |
3684 | |
3685 | return sz; |
3686 | } |
3687 | |
3688 | /// Returns the current index of the quickfix/location list. |
3689 | /// Returns 0 if there is an error. |
3690 | size_t qf_get_cur_idx(exarg_T *eap) |
3691 | FUNC_ATTR_NONNULL_ALL |
3692 | { |
3693 | qf_info_T *qi = &ql_info; |
3694 | |
3695 | if (eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) { |
3696 | // Location list. |
3697 | qi = GET_LOC_LIST(curwin); |
3698 | if (qi == NULL) { |
3699 | return 0; |
3700 | } |
3701 | } |
3702 | |
3703 | assert(qi->qf_lists[qi->qf_curlist].qf_index >= 0); |
3704 | return (size_t)qi->qf_lists[qi->qf_curlist].qf_index; |
3705 | } |
3706 | |
3707 | /// Returns the current index in the quickfix/location list, |
3708 | /// counting only valid entries. |
3709 | /// Returns 1 if there are no valid entries. |
3710 | int qf_get_cur_valid_idx(exarg_T *eap) |
3711 | FUNC_ATTR_NONNULL_ALL |
3712 | { |
3713 | qf_info_T *qi = &ql_info; |
3714 | |
3715 | if (eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) { |
3716 | // Location list. |
3717 | qi = GET_LOC_LIST(curwin); |
3718 | if (qi == NULL) { |
3719 | return 1; |
3720 | } |
3721 | } |
3722 | |
3723 | qf_list_T *qfl = &qi->qf_lists[qi->qf_curlist]; |
3724 | |
3725 | // Check if the list has valid errors. |
3726 | if (qfl->qf_count <= 0 || qfl->qf_nonevalid) { |
3727 | return 1; |
3728 | } |
3729 | |
3730 | int prev_fnum = 0; |
3731 | int eidx = 0; |
3732 | qfline_T *qfp; |
3733 | size_t i; |
3734 | assert(qfl->qf_index >= 0); |
3735 | for (i = 1, qfp = qfl->qf_start; |
3736 | i <= (size_t)qfl->qf_index && qfp != NULL; |
3737 | i++, qfp = qfp->qf_next) { |
3738 | if (!qfp->qf_valid) { |
3739 | continue; |
3740 | } |
3741 | |
3742 | if (eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) { |
3743 | if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum) { |
3744 | // Count the number of files. |
3745 | eidx++; |
3746 | prev_fnum = qfp->qf_fnum; |
3747 | } |
3748 | } else { |
3749 | eidx++; |
3750 | } |
3751 | } |
3752 | |
3753 | return eidx != 0 ? eidx : 1; |
3754 | } |
3755 | |
3756 | /// Get the 'n'th valid error entry in the quickfix or location list. |
3757 | /// |
3758 | /// Used by :cdo, :ldo, :cfdo and :lfdo commands. |
3759 | /// For :cdo and :ldo, returns the 'n'th valid error entry. |
3760 | /// For :cfdo and :lfdo, returns the 'n'th valid file entry. |
3761 | static size_t qf_get_nth_valid_entry(qf_info_T *qi, size_t n, bool fdo) |
3762 | FUNC_ATTR_NONNULL_ALL |
3763 | { |
3764 | qf_list_T *qfl = &qi->qf_lists[qi->qf_curlist]; |
3765 | |
3766 | // Check if the list has valid errors. |
3767 | if (qfl->qf_count <= 0 || qfl->qf_nonevalid) { |
3768 | return 1; |
3769 | } |
3770 | |
3771 | int prev_fnum = 0; |
3772 | size_t eidx = 0; |
3773 | size_t i; |
3774 | qfline_T *qfp; |
3775 | assert(qfl->qf_count >= 0); |
3776 | for (i = 1, qfp = qfl->qf_start; |
3777 | i <= (size_t)qfl->qf_count && qfp != NULL; |
3778 | i++, qfp = qfp->qf_next) { |
3779 | if (qfp->qf_valid) { |
3780 | if (fdo) { |
3781 | if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum) { |
3782 | // Count the number of files. |
3783 | eidx++; |
3784 | prev_fnum = qfp->qf_fnum; |
3785 | } |
3786 | } else { |
3787 | eidx++; |
3788 | } |
3789 | } |
3790 | |
3791 | if (eidx == n) { |
3792 | break; |
3793 | } |
3794 | } |
3795 | |
3796 | return i <= (size_t)qfl->qf_count ? i : 1; |
3797 | } |
3798 | |
3799 | /* |
3800 | * ":cc", ":crewind", ":cfirst" and ":clast". |
3801 | * ":ll", ":lrewind", ":lfirst" and ":llast". |
3802 | * ":cdo", ":ldo", ":cfdo" and ":lfdo". |
3803 | */ |
3804 | void ex_cc(exarg_T *eap) |
3805 | { |
3806 | qf_info_T *qi = &ql_info; |
3807 | |
3808 | if (eap->cmdidx == CMD_ll |
3809 | || eap->cmdidx == CMD_lrewind |
3810 | || eap->cmdidx == CMD_lfirst |
3811 | || eap->cmdidx == CMD_llast |
3812 | || eap->cmdidx == CMD_ldo |
3813 | || eap->cmdidx == CMD_lfdo) { |
3814 | qi = GET_LOC_LIST(curwin); |
3815 | if (qi == NULL) { |
3816 | EMSG(_(e_loclist)); |
3817 | return; |
3818 | } |
3819 | } |
3820 | |
3821 | int errornr; |
3822 | if (eap->addr_count > 0) { |
3823 | errornr = (int)eap->line2; |
3824 | } else if (eap->cmdidx == CMD_cc || eap->cmdidx == CMD_ll) { |
3825 | errornr = 0; |
3826 | } else if (eap->cmdidx == CMD_crewind || eap->cmdidx == CMD_lrewind |
3827 | || eap->cmdidx == CMD_cfirst || eap->cmdidx == CMD_lfirst) { |
3828 | errornr = 1; |
3829 | } else { |
3830 | errornr = 32767; |
3831 | } |
3832 | |
3833 | // For cdo and ldo commands, jump to the nth valid error. |
3834 | // For cfdo and lfdo commands, jump to the nth valid file entry. |
3835 | if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo |
3836 | || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) { |
3837 | size_t n; |
3838 | if (eap->addr_count > 0) { |
3839 | assert(eap->line1 >= 0); |
3840 | n = (size_t)eap->line1; |
3841 | } else { |
3842 | n = 1; |
3843 | } |
3844 | size_t valid_entry = qf_get_nth_valid_entry(qi, n, |
3845 | eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo); |
3846 | assert(valid_entry <= INT_MAX); |
3847 | errornr = (int)valid_entry; |
3848 | } |
3849 | |
3850 | qf_jump(qi, 0, errornr, eap->forceit); |
3851 | } |
3852 | |
3853 | /* |
3854 | * ":cnext", ":cnfile", ":cNext" and ":cprevious". |
3855 | * ":lnext", ":lNext", ":lprevious", ":lnfile", ":lNfile" and ":lpfile". |
3856 | * ":cdo", ":ldo", ":cfdo" and ":lfdo". |
3857 | */ |
3858 | void ex_cnext(exarg_T *eap) |
3859 | { |
3860 | qf_info_T *qi = &ql_info; |
3861 | |
3862 | if (eap->cmdidx == CMD_lnext |
3863 | || eap->cmdidx == CMD_lNext |
3864 | || eap->cmdidx == CMD_lprevious |
3865 | || eap->cmdidx == CMD_lnfile |
3866 | || eap->cmdidx == CMD_lNfile |
3867 | || eap->cmdidx == CMD_lpfile |
3868 | || eap->cmdidx == CMD_ldo |
3869 | || eap->cmdidx == CMD_lfdo) { |
3870 | qi = GET_LOC_LIST(curwin); |
3871 | if (qi == NULL) { |
3872 | EMSG(_(e_loclist)); |
3873 | return; |
3874 | } |
3875 | } |
3876 | |
3877 | int errornr; |
3878 | if (eap->addr_count > 0 |
3879 | && (eap->cmdidx != CMD_cdo && eap->cmdidx != CMD_ldo |
3880 | && eap->cmdidx != CMD_cfdo && eap->cmdidx != CMD_lfdo)) { |
3881 | errornr = (int)eap->line2; |
3882 | } else { |
3883 | errornr = 1; |
3884 | } |
3885 | |
3886 | qf_jump(qi, (eap->cmdidx == CMD_cnext || eap->cmdidx == CMD_lnext |
3887 | || eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo) |
3888 | ? FORWARD |
3889 | : (eap->cmdidx == CMD_cnfile || eap->cmdidx == CMD_lnfile |
3890 | || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) |
3891 | ? FORWARD_FILE |
3892 | : (eap->cmdidx == CMD_cpfile || eap->cmdidx == CMD_lpfile |
3893 | || eap->cmdidx == CMD_cNfile || eap->cmdidx == CMD_lNfile) |
3894 | ? BACKWARD_FILE |
3895 | : BACKWARD, |
3896 | errornr, eap->forceit); |
3897 | } |
3898 | |
3899 | /* |
3900 | * ":cfile"/":cgetfile"/":caddfile" commands. |
3901 | * ":lfile"/":lgetfile"/":laddfile" commands. |
3902 | */ |
3903 | void ex_cfile(exarg_T *eap) |
3904 | { |
3905 | win_T *wp = NULL; |
3906 | qf_info_T *qi = &ql_info; |
3907 | char_u *au_name = NULL; |
3908 | |
3909 | switch (eap->cmdidx) { |
3910 | case CMD_cfile: au_name = (char_u *)"cfile" ; break; |
3911 | case CMD_cgetfile: au_name = (char_u *)"cgetfile" ; break; |
3912 | case CMD_caddfile: au_name = (char_u *)"caddfile" ; break; |
3913 | case CMD_lfile: au_name = (char_u *)"lfile" ; break; |
3914 | case CMD_lgetfile: au_name = (char_u *)"lgetfile" ; break; |
3915 | case CMD_laddfile: au_name = (char_u *)"laddfile" ; break; |
3916 | default: break; |
3917 | } |
3918 | if (au_name != NULL) |
3919 | apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name, NULL, FALSE, curbuf); |
3920 | if (*eap->arg != NUL) |
3921 | set_string_option_direct((char_u *)"ef" , -1, eap->arg, OPT_FREE, 0); |
3922 | |
3923 | char_u *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc; |
3924 | |
3925 | if (eap->cmdidx == CMD_lfile |
3926 | || eap->cmdidx == CMD_lgetfile |
3927 | || eap->cmdidx == CMD_laddfile) { |
3928 | wp = curwin; |
3929 | } |
3930 | |
3931 | // This function is used by the :cfile, :cgetfile and :caddfile |
3932 | // commands. |
3933 | // :cfile always creates a new quickfix list and jumps to the |
3934 | // first error. |
3935 | // :cgetfile creates a new quickfix list but doesn't jump to the |
3936 | // first error. |
3937 | // :caddfile adds to an existing quickfix list. If there is no |
3938 | // quickfix list then a new list is created. |
3939 | int res = qf_init(wp, p_ef, p_efm, (eap->cmdidx != CMD_caddfile |
3940 | && eap->cmdidx != CMD_laddfile), |
3941 | qf_cmdtitle(*eap->cmdlinep), enc); |
3942 | if (wp != NULL) { |
3943 | qi = GET_LOC_LIST(wp); |
3944 | if (qi == NULL) { |
3945 | return; |
3946 | } |
3947 | } |
3948 | if (res >= 0) { |
3949 | qf_list_changed(qi, qi->qf_curlist); |
3950 | } |
3951 | unsigned save_qfid = qi->qf_lists[qi->qf_curlist].qf_id; |
3952 | if (au_name != NULL) { |
3953 | apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, NULL, false, curbuf); |
3954 | } |
3955 | // Jump to the first error for a new list and if autocmds didn't free the |
3956 | // list. |
3957 | if (res > 0 && (eap->cmdidx == CMD_cfile || eap->cmdidx == CMD_lfile) |
3958 | && qflist_valid(wp, save_qfid)) { |
3959 | // display the first error |
3960 | qf_jump_first(qi, save_qfid, eap->forceit); |
3961 | } |
3962 | } |
3963 | |
3964 | /// Return the vimgrep autocmd name. |
3965 | static char_u *vgr_get_auname(cmdidx_T cmdidx) |
3966 | { |
3967 | switch (cmdidx) { |
3968 | case CMD_vimgrep: return (char_u *)"vimgrep" ; |
3969 | case CMD_lvimgrep: return (char_u *)"lvimgrep" ; |
3970 | case CMD_vimgrepadd: return (char_u *)"vimgrepadd" ; |
3971 | case CMD_lvimgrepadd: return (char_u *)"lvimgrepadd" ; |
3972 | case CMD_grep: return (char_u *)"grep" ; |
3973 | case CMD_lgrep: return (char_u *)"lgrep" ; |
3974 | case CMD_grepadd: return (char_u *)"grepadd" ; |
3975 | case CMD_lgrepadd: return (char_u *)"lgrepadd" ; |
3976 | default: return NULL; |
3977 | } |
3978 | } |
3979 | |
3980 | /// Initialize the regmatch used by vimgrep for pattern "s". |
3981 | static void vgr_init_regmatch(regmmatch_T *regmatch, char_u *s) |
3982 | { |
3983 | // Get the search pattern: either white-separated or enclosed in //. |
3984 | regmatch->regprog = NULL; |
3985 | |
3986 | if (s == NULL || *s == NUL) { |
3987 | // Pattern is empty, use last search pattern. |
3988 | if (last_search_pat() == NULL) { |
3989 | EMSG(_(e_noprevre)); |
3990 | return; |
3991 | } |
3992 | regmatch->regprog = vim_regcomp(last_search_pat(), RE_MAGIC); |
3993 | } else { |
3994 | regmatch->regprog = vim_regcomp(s, RE_MAGIC); |
3995 | } |
3996 | |
3997 | regmatch->rmm_ic = p_ic; |
3998 | regmatch->rmm_maxcol = 0; |
3999 | } |
4000 | |
4001 | |
4002 | /// Display a file name when vimgrep is running. |
4003 | static void vgr_display_fname(char_u *fname) |
4004 | { |
4005 | msg_start(); |
4006 | char_u *p = msg_strtrunc(fname, true); |
4007 | if (p == NULL) { |
4008 | msg_outtrans(fname); |
4009 | } else { |
4010 | msg_outtrans(p); |
4011 | xfree(p); |
4012 | } |
4013 | msg_clr_eos(); |
4014 | msg_didout = false; // overwrite this message |
4015 | msg_nowait = true; // don't wait for this message |
4016 | msg_col = 0; |
4017 | ui_flush(); |
4018 | } |
4019 | |
4020 | /// Load a dummy buffer to search for a pattern using vimgrep. |
4021 | static buf_T *vgr_load_dummy_buf(char_u *fname, char_u *dirname_start, |
4022 | char_u *dirname_now) |
4023 | { |
4024 | char_u *save_ei = NULL; |
4025 | |
4026 | // Don't do Filetype autocommands to avoid loading syntax and |
4027 | // indent scripts, a great speed improvement. |
4028 | long save_mls = p_mls; |
4029 | p_mls = 0; |
4030 | |
4031 | // Load file into a buffer, so that 'fileencoding' is detected, |
4032 | // autocommands applied, etc. |
4033 | buf_T *buf = load_dummy_buffer(fname, dirname_start, dirname_now); |
4034 | |
4035 | p_mls = save_mls; |
4036 | au_event_restore(save_ei); |
4037 | |
4038 | return buf; |
4039 | } |
4040 | |
4041 | /// Check whether a quickfix/location list is valid. Autocmds may remove or |
4042 | /// change a quickfix list when vimgrep is running. If the list is not found, |
4043 | /// create a new list. |
4044 | static bool vgr_qflist_valid(win_T *wp, qf_info_T *qi, unsigned qfid, |
4045 | char_u *title) |
4046 | { |
4047 | // Verify that the quickfix/location list was not freed by an autocmd |
4048 | if (!qflist_valid(wp, qfid)) { |
4049 | if (wp != NULL) { |
4050 | // An autocmd has freed the location list |
4051 | EMSG(_(e_loc_list_changed)); |
4052 | return false; |
4053 | } else { |
4054 | // Quickfix list is not found, create a new one. |
4055 | qf_new_list(qi, title); |
4056 | return true; |
4057 | } |
4058 | } |
4059 | if (qf_restore_list(qi, qfid) == FAIL) { |
4060 | return false; |
4061 | } |
4062 | |
4063 | return true; |
4064 | } |
4065 | |
4066 | |
4067 | /// Search for a pattern in all the lines in a buffer and add the matching lines |
4068 | /// to a quickfix list. |
4069 | static bool vgr_match_buflines(qf_info_T *qi, char_u *fname, buf_T *buf, |
4070 | regmmatch_T *regmatch, long tomatch, |
4071 | int duplicate_name, int flags) |
4072 | { |
4073 | bool found_match = false; |
4074 | |
4075 | for (long lnum = 1; lnum <= buf->b_ml.ml_line_count && tomatch > 0; lnum++) { |
4076 | colnr_T col = 0; |
4077 | while (vim_regexec_multi(regmatch, curwin, buf, lnum, col, NULL, |
4078 | NULL) > 0) { |
4079 | // Pass the buffer number so that it gets used even for a |
4080 | // dummy buffer, unless duplicate_name is set, then the |
4081 | // buffer will be wiped out below. |
4082 | if (qf_add_entry(qi, |
4083 | qi->qf_curlist, |
4084 | NULL, // dir |
4085 | fname, |
4086 | NULL, |
4087 | duplicate_name ? 0 : buf->b_fnum, |
4088 | ml_get_buf(buf, regmatch->startpos[0].lnum + lnum, |
4089 | false), |
4090 | regmatch->startpos[0].lnum + lnum, |
4091 | regmatch->startpos[0].col + 1, |
4092 | false, // vis_col |
4093 | NULL, // search pattern |
4094 | 0, // nr |
4095 | 0, // type |
4096 | true // valid |
4097 | ) == FAIL) { |
4098 | got_int = true; |
4099 | break; |
4100 | } |
4101 | found_match = true; |
4102 | if (--tomatch == 0) { |
4103 | break; |
4104 | } |
4105 | if ((flags & VGR_GLOBAL) == 0 || regmatch->endpos[0].lnum > 0) { |
4106 | break; |
4107 | } |
4108 | col = regmatch->endpos[0].col + (col == regmatch->endpos[0].col); |
4109 | if (col > (colnr_T)STRLEN(ml_get_buf(buf, lnum, false))) { |
4110 | break; |
4111 | } |
4112 | } |
4113 | line_breakcheck(); |
4114 | if (got_int) { |
4115 | break; |
4116 | } |
4117 | } |
4118 | |
4119 | return found_match; |
4120 | } |
4121 | |
4122 | /// Jump to the first match and update the directory. |
4123 | static void vgr_jump_to_match(qf_info_T *qi, int forceit, int *redraw_for_dummy, |
4124 | buf_T *first_match_buf, char_u *target_dir) |
4125 | { |
4126 | buf_T *buf = curbuf; |
4127 | qf_jump(qi, 0, 0, forceit); |
4128 | if (buf != curbuf) { |
4129 | // If we jumped to another buffer redrawing will already be |
4130 | // taken care of. |
4131 | *redraw_for_dummy = false; |
4132 | } |
4133 | |
4134 | // Jump to the directory used after loading the buffer. |
4135 | if (curbuf == first_match_buf && target_dir != NULL) { |
4136 | exarg_T ea = { |
4137 | .arg = target_dir, |
4138 | .cmdidx = CMD_lcd, |
4139 | }; |
4140 | ex_cd(&ea); |
4141 | } |
4142 | } |
4143 | |
4144 | /* |
4145 | * ":vimgrep {pattern} file(s)" |
4146 | * ":vimgrepadd {pattern} file(s)" |
4147 | * ":lvimgrep {pattern} file(s)" |
4148 | * ":lvimgrepadd {pattern} file(s)" |
4149 | */ |
4150 | void ex_vimgrep(exarg_T *eap) |
4151 | { |
4152 | regmmatch_T regmatch; |
4153 | int fcount; |
4154 | char_u **fnames; |
4155 | char_u *fname; |
4156 | char_u *s; |
4157 | char_u *p; |
4158 | int fi; |
4159 | qf_info_T *qi = &ql_info; |
4160 | win_T *wp = NULL; |
4161 | buf_T *buf; |
4162 | int duplicate_name = FALSE; |
4163 | int using_dummy; |
4164 | int redraw_for_dummy = FALSE; |
4165 | int found_match; |
4166 | buf_T *first_match_buf = NULL; |
4167 | time_t seconds = 0; |
4168 | aco_save_T aco; |
4169 | int flags = 0; |
4170 | long tomatch; |
4171 | char_u *dirname_start = NULL; |
4172 | char_u *dirname_now = NULL; |
4173 | char_u *target_dir = NULL; |
4174 | char_u *au_name = NULL; |
4175 | |
4176 | au_name = vgr_get_auname(eap->cmdidx); |
4177 | if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name, |
4178 | curbuf->b_fname, true, curbuf)) { |
4179 | if (aborting()) { |
4180 | return; |
4181 | } |
4182 | } |
4183 | |
4184 | if (eap->cmdidx == CMD_lgrep |
4185 | || eap->cmdidx == CMD_lvimgrep |
4186 | || eap->cmdidx == CMD_lgrepadd |
4187 | || eap->cmdidx == CMD_lvimgrepadd) { |
4188 | qi = ll_get_or_alloc_list(curwin); |
4189 | wp = curwin; |
4190 | } |
4191 | |
4192 | if (eap->addr_count > 0) |
4193 | tomatch = eap->line2; |
4194 | else |
4195 | tomatch = MAXLNUM; |
4196 | |
4197 | /* Get the search pattern: either white-separated or enclosed in // */ |
4198 | regmatch.regprog = NULL; |
4199 | char_u *title = vim_strsave(qf_cmdtitle(*eap->cmdlinep)); |
4200 | p = skip_vimgrep_pat(eap->arg, &s, &flags); |
4201 | if (p == NULL) { |
4202 | EMSG(_(e_invalpat)); |
4203 | goto theend; |
4204 | } |
4205 | |
4206 | vgr_init_regmatch(®match, s); |
4207 | if (regmatch.regprog == NULL) { |
4208 | goto theend; |
4209 | } |
4210 | |
4211 | p = skipwhite(p); |
4212 | if (*p == NUL) { |
4213 | EMSG(_("E683: File name missing or invalid pattern" )); |
4214 | goto theend; |
4215 | } |
4216 | |
4217 | if ((eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd |
4218 | && eap->cmdidx != CMD_vimgrepadd && eap->cmdidx != CMD_lvimgrepadd) |
4219 | || qi->qf_curlist == qi->qf_listcount) { |
4220 | // make place for a new list |
4221 | qf_new_list(qi, title); |
4222 | } |
4223 | |
4224 | /* parse the list of arguments */ |
4225 | if (get_arglist_exp(p, &fcount, &fnames, true) == FAIL) |
4226 | goto theend; |
4227 | if (fcount == 0) { |
4228 | EMSG(_(e_nomatch)); |
4229 | goto theend; |
4230 | } |
4231 | |
4232 | dirname_start = xmalloc(MAXPATHL); |
4233 | dirname_now = xmalloc(MAXPATHL); |
4234 | |
4235 | /* Remember the current directory, because a BufRead autocommand that does |
4236 | * ":lcd %:p:h" changes the meaning of short path names. */ |
4237 | os_dirname(dirname_start, MAXPATHL); |
4238 | |
4239 | // Remember the current quickfix list identifier, so that we can check for |
4240 | // autocommands changing the current quickfix list. |
4241 | unsigned save_qfid = qi->qf_lists[qi->qf_curlist].qf_id; |
4242 | |
4243 | seconds = (time_t)0; |
4244 | for (fi = 0; fi < fcount && !got_int && tomatch > 0; fi++) { |
4245 | fname = path_try_shorten_fname(fnames[fi]); |
4246 | if (time(NULL) > seconds) { |
4247 | /* Display the file name every second or so, show the user we are |
4248 | * working on it. */ |
4249 | seconds = time(NULL); |
4250 | vgr_display_fname(fname); |
4251 | } |
4252 | |
4253 | buf = buflist_findname_exp(fnames[fi]); |
4254 | if (buf == NULL || buf->b_ml.ml_mfp == NULL) { |
4255 | /* Remember that a buffer with this name already exists. */ |
4256 | duplicate_name = (buf != NULL); |
4257 | using_dummy = TRUE; |
4258 | redraw_for_dummy = TRUE; |
4259 | |
4260 | buf = vgr_load_dummy_buf(fname, dirname_start, dirname_now); |
4261 | } else { |
4262 | // Use existing, loaded buffer. |
4263 | using_dummy = false; |
4264 | } |
4265 | |
4266 | // Check whether the quickfix list is still valid. When loading a |
4267 | // buffer above, autocommands might have changed the quickfix list. |
4268 | if (!vgr_qflist_valid(wp, qi, save_qfid, *eap->cmdlinep)) { |
4269 | FreeWild(fcount, fnames); |
4270 | goto theend; |
4271 | } |
4272 | save_qfid = qi->qf_lists[qi->qf_curlist].qf_id; |
4273 | |
4274 | if (buf == NULL) { |
4275 | if (!got_int) |
4276 | smsg(_("Cannot open file \"%s\"" ), fname); |
4277 | } else { |
4278 | // Try for a match in all lines of the buffer. |
4279 | // For ":1vimgrep" look for first match only. |
4280 | found_match = vgr_match_buflines(qi, fname, buf, ®match, tomatch, |
4281 | duplicate_name, flags); |
4282 | |
4283 | if (using_dummy) { |
4284 | if (found_match && first_match_buf == NULL) |
4285 | first_match_buf = buf; |
4286 | if (duplicate_name) { |
4287 | /* Never keep a dummy buffer if there is another buffer |
4288 | * with the same name. */ |
4289 | wipe_dummy_buffer(buf, dirname_start); |
4290 | buf = NULL; |
4291 | } else if (!cmdmod.hide |
4292 | || buf->b_p_bh[0] == 'u' /* "unload" */ |
4293 | || buf->b_p_bh[0] == 'w' /* "wipe" */ |
4294 | || buf->b_p_bh[0] == 'd') { /* "delete" */ |
4295 | /* When no match was found we don't need to remember the |
4296 | * buffer, wipe it out. If there was a match and it |
4297 | * wasn't the first one or we won't jump there: only |
4298 | * unload the buffer. |
4299 | * Ignore 'hidden' here, because it may lead to having too |
4300 | * many swap files. */ |
4301 | if (!found_match) { |
4302 | wipe_dummy_buffer(buf, dirname_start); |
4303 | buf = NULL; |
4304 | } else if (buf != first_match_buf || (flags & VGR_NOJUMP)) { |
4305 | unload_dummy_buffer(buf, dirname_start); |
4306 | // Keeping the buffer, remove the dummy flag. |
4307 | buf->b_flags &= ~BF_DUMMY; |
4308 | buf = NULL; |
4309 | } |
4310 | } |
4311 | |
4312 | if (buf != NULL) { |
4313 | // Keeping the buffer, remove the dummy flag. |
4314 | buf->b_flags &= ~BF_DUMMY; |
4315 | |
4316 | // If the buffer is still loaded we need to use the |
4317 | // directory we jumped to below. |
4318 | if (buf == first_match_buf |
4319 | && target_dir == NULL |
4320 | && STRCMP(dirname_start, dirname_now) != 0) { |
4321 | target_dir = vim_strsave(dirname_now); |
4322 | } |
4323 | |
4324 | /* The buffer is still loaded, the Filetype autocommands |
4325 | * need to be done now, in that buffer. And the modelines |
4326 | * need to be done (again). But not the window-local |
4327 | * options! */ |
4328 | aucmd_prepbuf(&aco, buf); |
4329 | apply_autocmds(EVENT_FILETYPE, buf->b_p_ft, |
4330 | buf->b_fname, TRUE, buf); |
4331 | do_modelines(OPT_NOWIN); |
4332 | aucmd_restbuf(&aco); |
4333 | } |
4334 | } |
4335 | } |
4336 | } |
4337 | |
4338 | FreeWild(fcount, fnames); |
4339 | |
4340 | qi->qf_lists[qi->qf_curlist].qf_nonevalid = FALSE; |
4341 | qi->qf_lists[qi->qf_curlist].qf_ptr = qi->qf_lists[qi->qf_curlist].qf_start; |
4342 | qi->qf_lists[qi->qf_curlist].qf_index = 1; |
4343 | qf_list_changed(qi, qi->qf_curlist); |
4344 | |
4345 | qf_update_buffer(qi, NULL); |
4346 | |
4347 | if (au_name != NULL) |
4348 | apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, |
4349 | curbuf->b_fname, TRUE, curbuf); |
4350 | |
4351 | // The QuickFixCmdPost autocmd may free the quickfix list. Check the list |
4352 | // is still valid. |
4353 | if (!qflist_valid(wp, save_qfid)) { |
4354 | goto theend; |
4355 | } |
4356 | |
4357 | if (qf_restore_list(qi, save_qfid) == FAIL) { |
4358 | goto theend; |
4359 | } |
4360 | |
4361 | /* Jump to first match. */ |
4362 | if (qi->qf_lists[qi->qf_curlist].qf_count > 0) { |
4363 | if ((flags & VGR_NOJUMP) == 0) { |
4364 | vgr_jump_to_match(qi, eap->forceit, &redraw_for_dummy, first_match_buf, |
4365 | target_dir); |
4366 | } |
4367 | } else |
4368 | EMSG2(_(e_nomatch2), s); |
4369 | |
4370 | /* If we loaded a dummy buffer into the current window, the autocommands |
4371 | * may have messed up things, need to redraw and recompute folds. */ |
4372 | if (redraw_for_dummy) { |
4373 | foldUpdateAll(curwin); |
4374 | } |
4375 | |
4376 | theend: |
4377 | xfree(title); |
4378 | xfree(dirname_now); |
4379 | xfree(dirname_start); |
4380 | xfree(target_dir); |
4381 | vim_regfree(regmatch.regprog); |
4382 | } |
4383 | |
4384 | /* |
4385 | * Restore current working directory to "dirname_start" if they differ, taking |
4386 | * into account whether it is set locally or globally. |
4387 | */ |
4388 | static void restore_start_dir(char_u *dirname_start) |
4389 | { |
4390 | char_u *dirname_now = xmalloc(MAXPATHL); |
4391 | |
4392 | os_dirname(dirname_now, MAXPATHL); |
4393 | if (STRCMP(dirname_start, dirname_now) != 0) { |
4394 | /* If the directory has changed, change it back by building up an |
4395 | * appropriate ex command and executing it. */ |
4396 | exarg_T ea = { |
4397 | .arg = dirname_start, |
4398 | .cmdidx = (curwin->w_localdir == NULL) ? CMD_cd : CMD_lcd, |
4399 | }; |
4400 | ex_cd(&ea); |
4401 | } |
4402 | xfree(dirname_now); |
4403 | } |
4404 | |
4405 | /* |
4406 | * Load file "fname" into a dummy buffer and return the buffer pointer, |
4407 | * placing the directory resulting from the buffer load into the |
4408 | * "resulting_dir" pointer. "resulting_dir" must be allocated by the caller |
4409 | * prior to calling this function. Restores directory to "dirname_start" prior |
4410 | * to returning, if autocmds or the 'autochdir' option have changed it. |
4411 | * |
4412 | * If creating the dummy buffer does not fail, must call unload_dummy_buffer() |
4413 | * or wipe_dummy_buffer() later! |
4414 | * |
4415 | * Returns NULL if it fails. |
4416 | */ |
4417 | static buf_T * |
4418 | load_dummy_buffer ( |
4419 | char_u *fname, |
4420 | char_u *dirname_start, /* in: old directory */ |
4421 | char_u *resulting_dir /* out: new directory */ |
4422 | ) |
4423 | { |
4424 | buf_T *newbuf; |
4425 | bufref_T newbufref; |
4426 | bufref_T newbuf_to_wipe; |
4427 | int failed = true; |
4428 | aco_save_T aco; |
4429 | int readfile_result; |
4430 | |
4431 | // Allocate a buffer without putting it in the buffer list. |
4432 | newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); |
4433 | if (newbuf == NULL) { |
4434 | return NULL; |
4435 | } |
4436 | set_bufref(&newbufref, newbuf); |
4437 | |
4438 | /* Init the options. */ |
4439 | buf_copy_options(newbuf, BCO_ENTER | BCO_NOHELP); |
4440 | |
4441 | /* need to open the memfile before putting the buffer in a window */ |
4442 | if (ml_open(newbuf) == OK) { |
4443 | // Make sure this buffer isn't wiped out by autocommands. |
4444 | newbuf->b_locked++; |
4445 | // set curwin/curbuf to buf and save a few things |
4446 | aucmd_prepbuf(&aco, newbuf); |
4447 | |
4448 | /* Need to set the filename for autocommands. */ |
4449 | (void)setfname(curbuf, fname, NULL, FALSE); |
4450 | |
4451 | /* Create swap file now to avoid the ATTENTION message. */ |
4452 | check_need_swap(TRUE); |
4453 | |
4454 | /* Remove the "dummy" flag, otherwise autocommands may not |
4455 | * work. */ |
4456 | curbuf->b_flags &= ~BF_DUMMY; |
4457 | |
4458 | newbuf_to_wipe.br_buf = NULL; |
4459 | readfile_result = readfile(fname, NULL, (linenr_T)0, (linenr_T)0, |
4460 | (linenr_T)MAXLNUM, NULL, |
4461 | READ_NEW | READ_DUMMY); |
4462 | newbuf->b_locked--; |
4463 | if (readfile_result == OK |
4464 | && !got_int |
4465 | && !(curbuf->b_flags & BF_NEW)) { |
4466 | failed = FALSE; |
4467 | if (curbuf != newbuf) { |
4468 | // Bloody autocommands changed the buffer! Can happen when |
4469 | // using netrw and editing a remote file. Use the current |
4470 | // buffer instead, delete the dummy one after restoring the |
4471 | // window stuff. |
4472 | set_bufref(&newbuf_to_wipe, newbuf); |
4473 | newbuf = curbuf; |
4474 | } |
4475 | } |
4476 | |
4477 | // Restore curwin/curbuf and a few other things. |
4478 | aucmd_restbuf(&aco); |
4479 | if (newbuf_to_wipe.br_buf != NULL && bufref_valid(&newbuf_to_wipe)) { |
4480 | wipe_buffer(newbuf_to_wipe.br_buf, false); |
4481 | } |
4482 | |
4483 | // Add back the "dummy" flag, otherwise buflist_findname_file_id() |
4484 | // won't skip it. |
4485 | newbuf->b_flags |= BF_DUMMY; |
4486 | } |
4487 | |
4488 | /* |
4489 | * When autocommands/'autochdir' option changed directory: go back. |
4490 | * Let the caller know what the resulting dir was first, in case it is |
4491 | * important. |
4492 | */ |
4493 | os_dirname(resulting_dir, MAXPATHL); |
4494 | restore_start_dir(dirname_start); |
4495 | |
4496 | if (!bufref_valid(&newbufref)) { |
4497 | return NULL; |
4498 | } |
4499 | if (failed) { |
4500 | wipe_dummy_buffer(newbuf, dirname_start); |
4501 | return NULL; |
4502 | } |
4503 | return newbuf; |
4504 | } |
4505 | |
4506 | /* |
4507 | * Wipe out the dummy buffer that load_dummy_buffer() created. Restores |
4508 | * directory to "dirname_start" prior to returning, if autocmds or the |
4509 | * 'autochdir' option have changed it. |
4510 | */ |
4511 | static void wipe_dummy_buffer(buf_T *buf, char_u *dirname_start) |
4512 | { |
4513 | if (curbuf != buf) { /* safety check */ |
4514 | cleanup_T cs; |
4515 | |
4516 | /* Reset the error/interrupt/exception state here so that aborting() |
4517 | * returns FALSE when wiping out the buffer. Otherwise it doesn't |
4518 | * work when got_int is set. */ |
4519 | enter_cleanup(&cs); |
4520 | |
4521 | wipe_buffer(buf, FALSE); |
4522 | |
4523 | /* Restore the error/interrupt/exception state if not discarded by a |
4524 | * new aborting error, interrupt, or uncaught exception. */ |
4525 | leave_cleanup(&cs); |
4526 | /* When autocommands/'autochdir' option changed directory: go back. */ |
4527 | restore_start_dir(dirname_start); |
4528 | } |
4529 | } |
4530 | |
4531 | /* |
4532 | * Unload the dummy buffer that load_dummy_buffer() created. Restores |
4533 | * directory to "dirname_start" prior to returning, if autocmds or the |
4534 | * 'autochdir' option have changed it. |
4535 | */ |
4536 | static void unload_dummy_buffer(buf_T *buf, char_u *dirname_start) |
4537 | { |
4538 | if (curbuf != buf) { /* safety check */ |
4539 | close_buffer(NULL, buf, DOBUF_UNLOAD, FALSE); |
4540 | |
4541 | /* When autocommands/'autochdir' option changed directory: go back. */ |
4542 | restore_start_dir(dirname_start); |
4543 | } |
4544 | } |
4545 | |
4546 | /// Add each quickfix error to list "list" as a dictionary. |
4547 | /// If qf_idx is -1, use the current list. Otherwise, use the specified list. |
4548 | int get_errorlist(const qf_info_T *qi_arg, win_T *wp, int qf_idx, list_T *list) |
4549 | { |
4550 | const qf_info_T *qi = qi_arg; |
4551 | char_u buf[2]; |
4552 | qfline_T *qfp; |
4553 | int i; |
4554 | int bufnum; |
4555 | |
4556 | if (qi == NULL) { |
4557 | qi = &ql_info; |
4558 | if (wp != NULL) { |
4559 | qi = GET_LOC_LIST(wp); |
4560 | if (qi == NULL) { |
4561 | return FAIL; |
4562 | } |
4563 | } |
4564 | } |
4565 | |
4566 | if (qf_idx == INVALID_QFIDX) { |
4567 | qf_idx = qi->qf_curlist; |
4568 | } |
4569 | |
4570 | if (qf_idx >= qi->qf_listcount |
4571 | || qi->qf_lists[qf_idx].qf_count == 0) { |
4572 | return FAIL; |
4573 | } |
4574 | |
4575 | qfp = qi->qf_lists[qf_idx].qf_start; |
4576 | for (i = 1; !got_int && i <= qi->qf_lists[qf_idx].qf_count; i++) { |
4577 | // Handle entries with a non-existing buffer number. |
4578 | bufnum = qfp->qf_fnum; |
4579 | if (bufnum != 0 && (buflist_findnr(bufnum) == NULL)) |
4580 | bufnum = 0; |
4581 | |
4582 | dict_T *const dict = tv_dict_alloc(); |
4583 | tv_list_append_dict(list, dict); |
4584 | |
4585 | buf[0] = qfp->qf_type; |
4586 | buf[1] = NUL; |
4587 | if (tv_dict_add_nr(dict, S_LEN("bufnr" ), (varnumber_T)bufnum) == FAIL |
4588 | || (tv_dict_add_nr(dict, S_LEN("lnum" ), (varnumber_T)qfp->qf_lnum) |
4589 | == FAIL) |
4590 | || (tv_dict_add_nr(dict, S_LEN("col" ), (varnumber_T)qfp->qf_col) |
4591 | == FAIL) |
4592 | || (tv_dict_add_nr(dict, S_LEN("vcol" ), (varnumber_T)qfp->qf_viscol) |
4593 | == FAIL) |
4594 | || (tv_dict_add_nr(dict, S_LEN("nr" ), (varnumber_T)qfp->qf_nr) == FAIL) |
4595 | || tv_dict_add_str(dict, S_LEN("module" ), |
4596 | (qfp->qf_module == NULL |
4597 | ? "" |
4598 | : (const char *)qfp->qf_module)) == FAIL |
4599 | || tv_dict_add_str(dict, S_LEN("pattern" ), |
4600 | (qfp->qf_pattern == NULL |
4601 | ? "" |
4602 | : (const char *)qfp->qf_pattern)) == FAIL |
4603 | || tv_dict_add_str(dict, S_LEN("text" ), |
4604 | (qfp->qf_text == NULL |
4605 | ? "" |
4606 | : (const char *)qfp->qf_text)) == FAIL |
4607 | || tv_dict_add_str(dict, S_LEN("type" ), (const char *)buf) == FAIL |
4608 | || (tv_dict_add_nr(dict, S_LEN("valid" ), (varnumber_T)qfp->qf_valid) |
4609 | == FAIL)) { |
4610 | // tv_dict_add* fail only if key already exist, but this is a newly |
4611 | // allocated dictionary which is thus guaranteed to have no existing keys. |
4612 | assert(false); |
4613 | } |
4614 | |
4615 | qfp = qfp->qf_next; |
4616 | if (qfp == NULL) { |
4617 | break; |
4618 | } |
4619 | } |
4620 | return OK; |
4621 | } |
4622 | |
4623 | /// Flags used by getqflist()/getloclist() to determine which fields to return. |
4624 | enum { |
4625 | QF_GETLIST_NONE = 0x0, |
4626 | QF_GETLIST_TITLE = 0x1, |
4627 | QF_GETLIST_ITEMS = 0x2, |
4628 | QF_GETLIST_NR = 0x4, |
4629 | QF_GETLIST_WINID = 0x8, |
4630 | QF_GETLIST_CONTEXT = 0x10, |
4631 | QF_GETLIST_ID = 0x20, |
4632 | QF_GETLIST_IDX = 0x40, |
4633 | QF_GETLIST_SIZE = 0x80, |
4634 | QF_GETLIST_TICK = 0x100, |
4635 | QF_GETLIST_ALL = 0x1FF |
4636 | }; |
4637 | |
4638 | /// Parse text from 'di' and return the quickfix list items. |
4639 | /// Existing quickfix lists are not modified. |
4640 | static int qf_get_list_from_lines(dict_T *what, dictitem_T *di, dict_T *retdict) |
4641 | { |
4642 | int status = FAIL; |
4643 | char_u *errorformat = p_efm; |
4644 | dictitem_T *efm_di; |
4645 | |
4646 | // Only a List value is supported |
4647 | if (di->di_tv.v_type == VAR_LIST && di->di_tv.vval.v_list != NULL) { |
4648 | // If errorformat is supplied then use it, otherwise use the 'efm' |
4649 | // option setting |
4650 | if ((efm_di = tv_dict_find(what, S_LEN("efm" ))) != NULL) { |
4651 | if (efm_di->di_tv.v_type != VAR_STRING |
4652 | || efm_di->di_tv.vval.v_string == NULL) { |
4653 | return FAIL; |
4654 | } |
4655 | errorformat = efm_di->di_tv.vval.v_string; |
4656 | } |
4657 | |
4658 | list_T *l = tv_list_alloc(kListLenMayKnow); |
4659 | qf_info_T *const qi = ll_new_list(); |
4660 | |
4661 | if (qf_init_ext(qi, 0, NULL, NULL, &di->di_tv, errorformat, |
4662 | true, (linenr_T)0, (linenr_T)0, NULL, NULL) > 0) { |
4663 | (void)get_errorlist(qi, NULL, 0, l); |
4664 | qf_free(qi, 0); |
4665 | } |
4666 | xfree(qi); |
4667 | |
4668 | tv_dict_add_list(retdict, S_LEN("items" ), l); |
4669 | status = OK; |
4670 | } |
4671 | |
4672 | return status; |
4673 | } |
4674 | |
4675 | /// Return the quickfix/location list window identifier in the current tabpage. |
4676 | static int qf_winid(qf_info_T *qi) |
4677 | { |
4678 | // The quickfix window can be opened even if the quickfix list is not set |
4679 | // using ":copen". This is not true for location lists. |
4680 | if (qi == NULL) { |
4681 | return 0; |
4682 | } |
4683 | win_T *win = qf_find_win(qi); |
4684 | if (win != NULL) { |
4685 | return win->handle; |
4686 | } |
4687 | return 0; |
4688 | } |
4689 | |
4690 | /// Convert the keys in 'what' to quickfix list property flags. |
4691 | static int qf_getprop_keys2flags(dict_T *what) |
4692 | { |
4693 | int flags = QF_GETLIST_NONE; |
4694 | |
4695 | if (tv_dict_find(what, S_LEN("all" )) != NULL) { |
4696 | flags |= QF_GETLIST_ALL; |
4697 | } |
4698 | if (tv_dict_find(what, S_LEN("title" )) != NULL) { |
4699 | flags |= QF_GETLIST_TITLE; |
4700 | } |
4701 | if (tv_dict_find(what, S_LEN("nr" )) != NULL) { |
4702 | flags |= QF_GETLIST_NR; |
4703 | } |
4704 | if (tv_dict_find(what, S_LEN("winid" )) != NULL) { |
4705 | flags |= QF_GETLIST_WINID; |
4706 | } |
4707 | if (tv_dict_find(what, S_LEN("context" )) != NULL) { |
4708 | flags |= QF_GETLIST_CONTEXT; |
4709 | } |
4710 | if (tv_dict_find(what, S_LEN("id" )) != NULL) { |
4711 | flags |= QF_GETLIST_ID; |
4712 | } |
4713 | if (tv_dict_find(what, S_LEN("items" )) != NULL) { |
4714 | flags |= QF_GETLIST_ITEMS; |
4715 | } |
4716 | if (tv_dict_find(what, S_LEN("idx" )) != NULL) { |
4717 | flags |= QF_GETLIST_IDX; |
4718 | } |
4719 | if (tv_dict_find(what, S_LEN("size" )) != NULL) { |
4720 | flags |= QF_GETLIST_SIZE; |
4721 | } |
4722 | if (tv_dict_find(what, S_LEN("changedtick" )) != NULL) { |
4723 | flags |= QF_GETLIST_TICK; |
4724 | } |
4725 | |
4726 | return flags; |
4727 | } |
4728 | |
4729 | /// Return the quickfix list index based on 'nr' or 'id' in 'what'. |
4730 | /// |
4731 | /// If 'nr' and 'id' are not present in 'what' then return the current |
4732 | /// quickfix list index. |
4733 | /// If 'nr' is zero then return the current quickfix list index. |
4734 | /// If 'nr' is '$' then return the last quickfix list index. |
4735 | /// If 'id' is present then return the index of the quickfix list with that id. |
4736 | /// If 'id' is zero then return the quickfix list index specified by 'nr'. |
4737 | /// Return -1, if quickfix list is not present or if the stack is empty. |
4738 | static int qf_getprop_qfidx(qf_info_T *qi, dict_T *what) |
4739 | { |
4740 | dictitem_T *di = NULL; |
4741 | |
4742 | int qf_idx = qi->qf_curlist; // default is the current list |
4743 | if ((di = tv_dict_find(what, S_LEN("nr" ))) != NULL) { |
4744 | // Use the specified quickfix/location list |
4745 | if (di->di_tv.v_type == VAR_NUMBER) { |
4746 | // for zero use the current list |
4747 | if (di->di_tv.vval.v_number != 0) { |
4748 | qf_idx = (int)di->di_tv.vval.v_number - 1; |
4749 | if (qf_idx < 0 || qf_idx >= qi->qf_listcount) { |
4750 | qf_idx = INVALID_QFIDX; |
4751 | } |
4752 | } |
4753 | } else if (di->di_tv.v_type == VAR_STRING |
4754 | && strequal((const char *)di->di_tv.vval.v_string, "$" )) { |
4755 | // Get the last quickfix list number |
4756 | qf_idx = qi->qf_listcount - 1; |
4757 | } else { |
4758 | qf_idx = INVALID_QFIDX; |
4759 | } |
4760 | } |
4761 | |
4762 | if ((di = tv_dict_find(what, S_LEN("id" ))) != NULL) { |
4763 | // Look for a list with the specified id |
4764 | if (di->di_tv.v_type == VAR_NUMBER) { |
4765 | // For zero, use the current list or the list specifed by 'nr' |
4766 | if (di->di_tv.vval.v_number != 0) { |
4767 | qf_idx = qf_id2nr(qi, (unsigned)di->di_tv.vval.v_number); |
4768 | } |
4769 | } else { |
4770 | qf_idx = INVALID_QFIDX; |
4771 | } |
4772 | } |
4773 | |
4774 | return qf_idx; |
4775 | } |
4776 | |
4777 | /// Return default values for quickfix list properties in retdict. |
4778 | static int qf_getprop_defaults(qf_info_T *qi, int flags, dict_T *retdict) |
4779 | { |
4780 | int status = OK; |
4781 | |
4782 | if (flags & QF_GETLIST_TITLE) { |
4783 | status = tv_dict_add_str(retdict, S_LEN("title" ), (const char *)"" ); |
4784 | } |
4785 | if ((status == OK) && (flags & QF_GETLIST_ITEMS)) { |
4786 | list_T *l = tv_list_alloc(kListLenMayKnow); |
4787 | status = tv_dict_add_list(retdict, S_LEN("items" ), l); |
4788 | } |
4789 | if ((status == OK) && (flags & QF_GETLIST_NR)) { |
4790 | status = tv_dict_add_nr(retdict, S_LEN("nr" ), 0); |
4791 | } |
4792 | if ((status == OK) && (flags & QF_GETLIST_WINID)) { |
4793 | status = tv_dict_add_nr(retdict, S_LEN("winid" ), qf_winid(qi)); |
4794 | } |
4795 | if ((status == OK) && (flags & QF_GETLIST_CONTEXT)) { |
4796 | status = tv_dict_add_str(retdict, S_LEN("context" ), (const char *)"" ); |
4797 | } |
4798 | if ((status == OK) && (flags & QF_GETLIST_ID)) { |
4799 | status = tv_dict_add_nr(retdict, S_LEN("id" ), 0); |
4800 | } |
4801 | if ((status == OK) && (flags & QF_GETLIST_IDX)) { |
4802 | status = tv_dict_add_nr(retdict, S_LEN("idx" ), 0); |
4803 | } |
4804 | if ((status == OK) && (flags & QF_GETLIST_SIZE)) { |
4805 | status = tv_dict_add_nr(retdict, S_LEN("size" ), 0); |
4806 | } |
4807 | if ((status == OK) && (flags & QF_GETLIST_TICK)) { |
4808 | status = tv_dict_add_nr(retdict, S_LEN("changedtick" ), 0); |
4809 | } |
4810 | |
4811 | return status; |
4812 | } |
4813 | |
4814 | /// Return the quickfix list title as 'title' in retdict |
4815 | static int qf_getprop_title(qf_info_T *qi, int qf_idx, dict_T *retdict) |
4816 | { |
4817 | return tv_dict_add_str(retdict, S_LEN("title" ), |
4818 | (const char *)qi->qf_lists[qf_idx].qf_title); |
4819 | } |
4820 | |
4821 | /// Return the quickfix list items/entries as 'items' in retdict |
4822 | static int qf_getprop_items(qf_info_T *qi, int qf_idx, dict_T *retdict) |
4823 | { |
4824 | list_T *l = tv_list_alloc(kListLenMayKnow); |
4825 | get_errorlist(qi, NULL, qf_idx, l); |
4826 | tv_dict_add_list(retdict, S_LEN("items" ), l); |
4827 | |
4828 | return OK; |
4829 | } |
4830 | |
4831 | /// Return the quickfix list context (if any) as 'context' in retdict. |
4832 | static int qf_getprop_ctx(qf_info_T *qi, int qf_idx, dict_T *retdict) |
4833 | { |
4834 | int status; |
4835 | |
4836 | if (qi->qf_lists[qf_idx].qf_ctx != NULL) { |
4837 | dictitem_T *di = tv_dict_item_alloc_len(S_LEN("context" )); |
4838 | tv_copy(qi->qf_lists[qf_idx].qf_ctx, &di->di_tv); |
4839 | status = tv_dict_add(retdict, di); |
4840 | if (status == FAIL) { |
4841 | tv_dict_item_free(di); |
4842 | } |
4843 | } else { |
4844 | status = tv_dict_add_str(retdict, S_LEN("context" ), "" ); |
4845 | } |
4846 | |
4847 | return status; |
4848 | } |
4849 | |
4850 | /// Return the quickfix list index as 'idx' in retdict |
4851 | static int qf_getprop_idx(qf_info_T *qi, int qf_idx, dict_T *retdict) |
4852 | { |
4853 | int idx = qi->qf_lists[qf_idx].qf_index; |
4854 | if (qi->qf_lists[qf_idx].qf_count == 0) { |
4855 | // For empty lists, qf_index is set to 1 |
4856 | idx = 0; |
4857 | } |
4858 | return tv_dict_add_nr(retdict, S_LEN("idx" ), idx); |
4859 | } |
4860 | |
4861 | /// Return quickfix/location list details (title) as a dictionary. |
4862 | /// 'what' contains the details to return. If 'list_idx' is -1, |
4863 | /// then current list is used. Otherwise the specified list is used. |
4864 | int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict) |
4865 | { |
4866 | qf_info_T *qi = &ql_info; |
4867 | dictitem_T *di = NULL; |
4868 | int status = OK; |
4869 | int qf_idx; |
4870 | |
4871 | if ((di = tv_dict_find(what, S_LEN("lines" ))) != NULL) { |
4872 | return qf_get_list_from_lines(what, di, retdict); |
4873 | } |
4874 | |
4875 | if (wp != NULL) { |
4876 | qi = GET_LOC_LIST(wp); |
4877 | } |
4878 | |
4879 | int flags = qf_getprop_keys2flags(what); |
4880 | |
4881 | if (qi != NULL && qi->qf_listcount != 0) { |
4882 | qf_idx = qf_getprop_qfidx(qi, what); |
4883 | } |
4884 | |
4885 | // List is not present or is empty |
4886 | if (qi == NULL || qi->qf_listcount == 0 || qf_idx == INVALID_QFIDX) { |
4887 | return qf_getprop_defaults(qi, flags, retdict); |
4888 | } |
4889 | |
4890 | if (flags & QF_GETLIST_TITLE) { |
4891 | status = qf_getprop_title(qi, qf_idx, retdict); |
4892 | } |
4893 | if ((status == OK) && (flags & QF_GETLIST_NR)) { |
4894 | status = tv_dict_add_nr(retdict, S_LEN("nr" ), qf_idx + 1); |
4895 | } |
4896 | if ((status == OK) && (flags & QF_GETLIST_WINID)) { |
4897 | status = tv_dict_add_nr(retdict, S_LEN("winid" ), qf_winid(qi)); |
4898 | } |
4899 | if ((status == OK) && (flags & QF_GETLIST_ITEMS)) { |
4900 | status = qf_getprop_items(qi, qf_idx, retdict); |
4901 | } |
4902 | if ((status == OK) && (flags & QF_GETLIST_CONTEXT)) { |
4903 | status = qf_getprop_ctx(qi, qf_idx, retdict); |
4904 | } |
4905 | if ((status == OK) && (flags & QF_GETLIST_ID)) { |
4906 | status = tv_dict_add_nr(retdict, S_LEN("id" ), qi->qf_lists[qf_idx].qf_id); |
4907 | } |
4908 | if ((status == OK) && (flags & QF_GETLIST_IDX)) { |
4909 | status = qf_getprop_idx(qi, qf_idx, retdict); |
4910 | } |
4911 | if ((status == OK) && (flags & QF_GETLIST_SIZE)) { |
4912 | status = tv_dict_add_nr(retdict, S_LEN("size" ), |
4913 | qi->qf_lists[qf_idx].qf_count); |
4914 | } |
4915 | if ((status == OK) && (flags & QF_GETLIST_TICK)) { |
4916 | status = tv_dict_add_nr(retdict, S_LEN("changedtick" ), |
4917 | qi->qf_lists[qf_idx].qf_changedtick); |
4918 | } |
4919 | |
4920 | return status; |
4921 | } |
4922 | |
4923 | /// Add list of entries to quickfix/location list. Each list entry is |
4924 | /// a dictionary with item information. |
4925 | static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, |
4926 | char_u *title, int action) |
4927 | { |
4928 | dict_T *d; |
4929 | qfline_T *old_last = NULL; |
4930 | int retval = OK; |
4931 | bool did_bufnr_emsg = false; |
4932 | |
4933 | if (action == ' ' || qf_idx == qi->qf_listcount) { |
4934 | // make place for a new list |
4935 | qf_new_list(qi, title); |
4936 | qf_idx = qi->qf_curlist; |
4937 | } else if (action == 'a' && qi->qf_lists[qf_idx].qf_count > 0) { |
4938 | // Adding to existing list, use last entry. |
4939 | old_last = qi->qf_lists[qf_idx].qf_last; |
4940 | } else if (action == 'r') { |
4941 | qf_free_items(qi, qf_idx); |
4942 | qf_store_title(qi, qf_idx, title); |
4943 | } |
4944 | |
4945 | TV_LIST_ITER_CONST(list, li, { |
4946 | if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT) { |
4947 | continue; // Skip non-dict items. |
4948 | } |
4949 | |
4950 | d = TV_LIST_ITEM_TV(li)->vval.v_dict; |
4951 | if (d == NULL) { |
4952 | continue; |
4953 | } |
4954 | |
4955 | char *const filename = tv_dict_get_string(d, "filename" , true); |
4956 | char *const module = tv_dict_get_string(d, "module" , true); |
4957 | int bufnum = (int)tv_dict_get_number(d, "bufnr" ); |
4958 | long lnum = (long)tv_dict_get_number(d, "lnum" ); |
4959 | int col = (int)tv_dict_get_number(d, "col" ); |
4960 | char_u vcol = (char_u)tv_dict_get_number(d, "vcol" ); |
4961 | int nr = (int)tv_dict_get_number(d, "nr" ); |
4962 | const char *type_str = tv_dict_get_string(d, "type" , false); |
4963 | const char_u type = (char_u)(uint8_t)(type_str == NULL ? NUL : *type_str); |
4964 | char *const pattern = tv_dict_get_string(d, "pattern" , true); |
4965 | char *text = tv_dict_get_string(d, "text" , true); |
4966 | if (text == NULL) { |
4967 | text = xcalloc(1, 1); |
4968 | } |
4969 | bool valid = true; |
4970 | if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL)) { |
4971 | valid = false; |
4972 | } |
4973 | |
4974 | /* Mark entries with non-existing buffer number as not valid. Give the |
4975 | * error message only once. */ |
4976 | if (bufnum != 0 && (buflist_findnr(bufnum) == NULL)) { |
4977 | if (!did_bufnr_emsg) { |
4978 | did_bufnr_emsg = TRUE; |
4979 | EMSGN(_("E92: Buffer %" PRId64 " not found" ), bufnum); |
4980 | } |
4981 | valid = false; |
4982 | bufnum = 0; |
4983 | } |
4984 | |
4985 | // If the 'valid' field is present it overrules the detected value. |
4986 | if (tv_dict_find(d, "valid" , -1) != NULL) { |
4987 | valid = (int)tv_dict_get_number(d, "valid" ); |
4988 | } |
4989 | |
4990 | int status = qf_add_entry(qi, |
4991 | qf_idx, |
4992 | NULL, // dir |
4993 | (char_u *)filename, |
4994 | (char_u *)module, |
4995 | bufnum, |
4996 | (char_u *)text, |
4997 | lnum, |
4998 | col, |
4999 | vcol, // vis_col |
5000 | (char_u *)pattern, // search pattern |
5001 | nr, |
5002 | type, |
5003 | valid); |
5004 | |
5005 | xfree(filename); |
5006 | xfree(module); |
5007 | xfree(pattern); |
5008 | xfree(text); |
5009 | |
5010 | if (status == FAIL) { |
5011 | retval = FAIL; |
5012 | break; |
5013 | } |
5014 | }); |
5015 | |
5016 | if (qi->qf_lists[qf_idx].qf_index == 0) { |
5017 | // no valid entry |
5018 | qi->qf_lists[qf_idx].qf_nonevalid = true; |
5019 | } else { |
5020 | qi->qf_lists[qf_idx].qf_nonevalid = false; |
5021 | } |
5022 | if (action != 'a') { |
5023 | qi->qf_lists[qf_idx].qf_ptr = qi->qf_lists[qf_idx].qf_start; |
5024 | if (qi->qf_lists[qf_idx].qf_count > 0) { |
5025 | qi->qf_lists[qf_idx].qf_index = 1; |
5026 | } |
5027 | } |
5028 | |
5029 | // Don't update the cursor in quickfix window when appending entries |
5030 | qf_update_buffer(qi, old_last); |
5031 | |
5032 | return retval; |
5033 | } |
5034 | |
5035 | // Get the quickfix list index from 'nr' or 'id' |
5036 | static int qf_setprop_get_qfidx( |
5037 | const qf_info_T *qi, |
5038 | const dict_T *what, |
5039 | int action, |
5040 | bool *newlist) |
5041 | FUNC_ATTR_NONNULL_ALL |
5042 | { |
5043 | dictitem_T *di; |
5044 | int qf_idx = qi->qf_curlist; // default is the current list |
5045 | |
5046 | if ((di = tv_dict_find(what, S_LEN("nr" ))) != NULL) { |
5047 | // Use the specified quickfix/location list |
5048 | if (di->di_tv.v_type == VAR_NUMBER) { |
5049 | // for zero use the current list |
5050 | if (di->di_tv.vval.v_number != 0) { |
5051 | qf_idx = (int)di->di_tv.vval.v_number - 1; |
5052 | } |
5053 | |
5054 | if ((action == ' ' || action == 'a') && qf_idx == qi->qf_listcount) { |
5055 | // When creating a new list, accept qf_idx pointing to the next |
5056 | // non-available list and add the new list at the end of the |
5057 | // stack. |
5058 | *newlist = true; |
5059 | qf_idx = qi->qf_listcount > 0 ? qi->qf_listcount - 1 : 0; |
5060 | } else if (qf_idx < 0 || qf_idx >= qi->qf_listcount) { |
5061 | return INVALID_QFIDX; |
5062 | } else if (action != ' ') { |
5063 | *newlist = false; // use the specified list |
5064 | } |
5065 | } else if (di->di_tv.v_type == VAR_STRING |
5066 | && strequal((const char *)di->di_tv.vval.v_string, "$" )) { |
5067 | if (qi->qf_listcount > 0) { |
5068 | qf_idx = qi->qf_listcount - 1; |
5069 | } else if (*newlist) { |
5070 | qf_idx = 0; |
5071 | } else { |
5072 | return INVALID_QFIDX; |
5073 | } |
5074 | } else { |
5075 | return INVALID_QFIDX; |
5076 | } |
5077 | } |
5078 | |
5079 | if (!*newlist && (di = tv_dict_find(what, S_LEN("id" ))) != NULL) { |
5080 | // Use the quickfix/location list with the specified id |
5081 | if (di->di_tv.v_type != VAR_NUMBER) { |
5082 | return INVALID_QFIDX; |
5083 | } |
5084 | return qf_id2nr(qi, (unsigned)di->di_tv.vval.v_number); |
5085 | } |
5086 | |
5087 | return qf_idx; |
5088 | } |
5089 | |
5090 | // Set the quickfix list title. |
5091 | static int qf_setprop_title(qf_info_T *qi, int qf_idx, const dict_T *what, |
5092 | const dictitem_T *di) |
5093 | FUNC_ATTR_NONNULL_ALL |
5094 | { |
5095 | if (di->di_tv.v_type != VAR_STRING) { |
5096 | return FAIL; |
5097 | } |
5098 | |
5099 | xfree(qi->qf_lists[qf_idx].qf_title); |
5100 | qi->qf_lists[qf_idx].qf_title = |
5101 | (char_u *)tv_dict_get_string(what, "title" , true); |
5102 | if (qf_idx == qi->qf_curlist) { |
5103 | qf_update_win_titlevar(qi); |
5104 | } |
5105 | |
5106 | return OK; |
5107 | } |
5108 | |
5109 | // Set quickfix list items/entries. |
5110 | static int qf_setprop_items(qf_info_T *qi, int qf_idx, dictitem_T *di, |
5111 | int action) |
5112 | FUNC_ATTR_NONNULL_ALL |
5113 | { |
5114 | if (di->di_tv.v_type != VAR_LIST) { |
5115 | return FAIL; |
5116 | } |
5117 | |
5118 | char_u *title_save = vim_strsave(qi->qf_lists[qf_idx].qf_title); |
5119 | const int retval = qf_add_entries(qi, qf_idx, di->di_tv.vval.v_list, |
5120 | title_save, |
5121 | action == ' ' ? 'a' : action); |
5122 | xfree(title_save); |
5123 | |
5124 | return retval; |
5125 | } |
5126 | |
5127 | // Set quickfix list items/entries from a list of lines. |
5128 | static int qf_setprop_items_from_lines( |
5129 | qf_info_T *qi, |
5130 | int qf_idx, |
5131 | const dict_T *what, |
5132 | dictitem_T *di, |
5133 | int action) |
5134 | FUNC_ATTR_NONNULL_ALL |
5135 | { |
5136 | char_u *errorformat = p_efm; |
5137 | dictitem_T *efm_di; |
5138 | int retval = FAIL; |
5139 | |
5140 | // Use the user supplied errorformat settings (if present) |
5141 | if ((efm_di = tv_dict_find(what, S_LEN("efm" ))) != NULL) { |
5142 | if (efm_di->di_tv.v_type != VAR_STRING |
5143 | || efm_di->di_tv.vval.v_string == NULL) { |
5144 | return FAIL; |
5145 | } |
5146 | errorformat = efm_di->di_tv.vval.v_string; |
5147 | } |
5148 | |
5149 | // Only a List value is supported |
5150 | if (di->di_tv.v_type != VAR_LIST || di->di_tv.vval.v_list == NULL) { |
5151 | return FAIL; |
5152 | } |
5153 | |
5154 | if (action == 'r') { |
5155 | qf_free_items(qi, qf_idx); |
5156 | } |
5157 | if (qf_init_ext(qi, qf_idx, NULL, NULL, &di->di_tv, errorformat, |
5158 | false, (linenr_T)0, (linenr_T)0, NULL, NULL) > 0) { |
5159 | retval = OK; |
5160 | } |
5161 | |
5162 | return retval; |
5163 | } |
5164 | |
5165 | // Set quickfix list context. |
5166 | static int qf_setprop_context(qf_info_T *qi, int qf_idx, dictitem_T *di) |
5167 | FUNC_ATTR_NONNULL_ALL |
5168 | { |
5169 | tv_free(qi->qf_lists[qf_idx].qf_ctx); |
5170 | typval_T *ctx = xcalloc(1, sizeof(typval_T)); |
5171 | tv_copy(&di->di_tv, ctx); |
5172 | qi->qf_lists[qf_idx].qf_ctx = ctx; |
5173 | |
5174 | return OK; |
5175 | } |
5176 | |
5177 | // Set quickfix/location list properties (title, items, context). |
5178 | // Also used to add items from parsing a list of lines. |
5179 | // Used by the setqflist() and setloclist() VimL functions. |
5180 | static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action, |
5181 | char_u *title) |
5182 | FUNC_ATTR_NONNULL_ALL |
5183 | { |
5184 | dictitem_T *di; |
5185 | int retval = FAIL; |
5186 | bool newlist = action == ' ' || qi->qf_curlist == qi->qf_listcount; |
5187 | int qf_idx = qf_setprop_get_qfidx(qi, what, action, &newlist); |
5188 | if (qf_idx == INVALID_QFIDX) { // List not found |
5189 | return FAIL; |
5190 | } |
5191 | |
5192 | if (newlist) { |
5193 | qi->qf_curlist = qf_idx; |
5194 | qf_new_list(qi, title); |
5195 | qf_idx = qi->qf_curlist; |
5196 | } |
5197 | |
5198 | if ((di = tv_dict_find(what, S_LEN("title" ))) != NULL) { |
5199 | retval = qf_setprop_title(qi, qf_idx, what, di); |
5200 | } |
5201 | if ((di = tv_dict_find(what, S_LEN("items" ))) != NULL) { |
5202 | retval = qf_setprop_items(qi, qf_idx, di, action); |
5203 | } |
5204 | if ((di = tv_dict_find(what, S_LEN("lines" ))) != NULL) { |
5205 | retval = qf_setprop_items_from_lines(qi, qf_idx, what, di, action); |
5206 | } |
5207 | if ((di = tv_dict_find(what, S_LEN("context" ))) != NULL) { |
5208 | retval = qf_setprop_context(qi, qf_idx, di); |
5209 | } |
5210 | |
5211 | if (retval == OK) { |
5212 | qf_list_changed(qi, qf_idx); |
5213 | } |
5214 | |
5215 | return retval; |
5216 | } |
5217 | |
5218 | // Find the non-location list window with the specified location list. |
5219 | static win_T * find_win_with_ll(qf_info_T *qi) |
5220 | { |
5221 | FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { |
5222 | if ((wp->w_llist == qi) && !bt_quickfix(wp->w_buffer)) { |
5223 | return wp; |
5224 | } |
5225 | } |
5226 | |
5227 | return NULL; |
5228 | } |
5229 | |
5230 | // Free the entire quickfix/location list stack. |
5231 | // If the quickfix/location list window is open, then clear it. |
5232 | static void qf_free_stack(win_T *wp, qf_info_T *qi) |
5233 | { |
5234 | win_T *qfwin = qf_find_win(qi); |
5235 | |
5236 | if (qfwin != NULL) { |
5237 | // If the quickfix/location list window is open, then clear it |
5238 | if (qi->qf_curlist < qi->qf_listcount) { |
5239 | qf_free(qi, qi->qf_curlist); |
5240 | } |
5241 | qf_update_buffer(qi, NULL); |
5242 | } |
5243 | |
5244 | win_T *llwin = NULL; |
5245 | win_T *orig_wp = wp; |
5246 | if (wp != NULL && IS_LL_WINDOW(wp)) { |
5247 | // If in the location list window, then use the non-location list |
5248 | // window with this location list (if present) |
5249 | llwin = find_win_with_ll(qi); |
5250 | if (llwin != NULL) { |
5251 | wp = llwin; |
5252 | } |
5253 | } |
5254 | |
5255 | qf_free_all(wp); |
5256 | if (wp == NULL) { |
5257 | // quickfix list |
5258 | qi->qf_curlist = 0; |
5259 | qi->qf_listcount = 0; |
5260 | } else if (IS_LL_WINDOW(orig_wp)) { |
5261 | // If the location list window is open, then create a new empty location |
5262 | // list |
5263 | qf_info_T *new_ll = ll_new_list(); |
5264 | |
5265 | // first free the list reference in the location list window |
5266 | ll_free_all(&orig_wp->w_llist_ref); |
5267 | |
5268 | orig_wp->w_llist_ref = new_ll; |
5269 | if (llwin != NULL) { |
5270 | llwin->w_llist = new_ll; |
5271 | new_ll->qf_refcount++; |
5272 | } |
5273 | } |
5274 | } |
5275 | |
5276 | // Populate the quickfix list with the items supplied in the list |
5277 | // of dictionaries. "title" will be copied to w:quickfix_title |
5278 | // "action" is 'a' for add, 'r' for replace. Otherwise create a new list. |
5279 | int set_errorlist(win_T *wp, list_T *list, int action, char_u *title, |
5280 | dict_T *what) |
5281 | { |
5282 | qf_info_T *qi = &ql_info; |
5283 | int retval = OK; |
5284 | |
5285 | if (wp != NULL) { |
5286 | qi = ll_get_or_alloc_list(wp); |
5287 | } |
5288 | |
5289 | if (action == 'f') { |
5290 | // Free the entire quickfix or location list stack |
5291 | qf_free_stack(wp, qi); |
5292 | } else if (what != NULL) { |
5293 | retval = qf_set_properties(qi, what, action, title); |
5294 | } else { |
5295 | retval = qf_add_entries(qi, qi->qf_curlist, list, title, action); |
5296 | if (retval == OK) { |
5297 | qf_list_changed(qi, qi->qf_curlist); |
5298 | } |
5299 | } |
5300 | |
5301 | return retval; |
5302 | } |
5303 | |
5304 | /// Mark the context as in use for all the lists in a quickfix stack. |
5305 | static bool mark_quickfix_ctx(qf_info_T *qi, int copyID) |
5306 | { |
5307 | bool abort = false; |
5308 | |
5309 | for (int i = 0; i < LISTCOUNT && !abort; i++) { |
5310 | typval_T *ctx = qi->qf_lists[i].qf_ctx; |
5311 | if (ctx != NULL && ctx->v_type != VAR_NUMBER |
5312 | && ctx->v_type != VAR_STRING && ctx->v_type != VAR_FLOAT) { |
5313 | abort = set_ref_in_item(ctx, copyID, NULL, NULL); |
5314 | } |
5315 | } |
5316 | |
5317 | return abort; |
5318 | } |
5319 | |
5320 | /// Mark the context of the quickfix list and the location lists (if present) as |
5321 | /// "in use". So that garbage collection doesn't free the context. |
5322 | bool set_ref_in_quickfix(int copyID) |
5323 | { |
5324 | bool abort = mark_quickfix_ctx(&ql_info, copyID); |
5325 | if (abort) { |
5326 | return abort; |
5327 | } |
5328 | |
5329 | FOR_ALL_TAB_WINDOWS(tp, win) { |
5330 | if (win->w_llist != NULL) { |
5331 | abort = mark_quickfix_ctx(win->w_llist, copyID); |
5332 | if (abort) { |
5333 | return abort; |
5334 | } |
5335 | } |
5336 | |
5337 | if (IS_LL_WINDOW(win) && (win->w_llist_ref->qf_refcount == 1)) { |
5338 | // In a location list window and none of the other windows is |
5339 | // referring to this location list. Mark the location list |
5340 | // context as still in use. |
5341 | abort = mark_quickfix_ctx(win->w_llist_ref, copyID); |
5342 | if (abort) { |
5343 | return abort; |
5344 | } |
5345 | } |
5346 | } |
5347 | |
5348 | return abort; |
5349 | } |
5350 | |
5351 | /* |
5352 | * ":[range]cbuffer [bufnr]" command. |
5353 | * ":[range]caddbuffer [bufnr]" command. |
5354 | * ":[range]cgetbuffer [bufnr]" command. |
5355 | * ":[range]lbuffer [bufnr]" command. |
5356 | * ":[range]laddbuffer [bufnr]" command. |
5357 | * ":[range]lgetbuffer [bufnr]" command. |
5358 | */ |
5359 | void ex_cbuffer(exarg_T *eap) |
5360 | { |
5361 | buf_T *buf = NULL; |
5362 | qf_info_T *qi = &ql_info; |
5363 | const char *au_name = NULL; |
5364 | win_T *wp = NULL; |
5365 | |
5366 | switch (eap->cmdidx) { |
5367 | case CMD_cbuffer: |
5368 | au_name = "cbuffer" ; |
5369 | break; |
5370 | case CMD_cgetbuffer: |
5371 | au_name = "cgetbuffer" ; |
5372 | break; |
5373 | case CMD_caddbuffer: |
5374 | au_name = "caddbuffer" ; |
5375 | break; |
5376 | case CMD_lbuffer: |
5377 | au_name = "lbuffer" ; |
5378 | break; |
5379 | case CMD_lgetbuffer: |
5380 | au_name = "lgetbuffer" ; |
5381 | break; |
5382 | case CMD_laddbuffer: |
5383 | au_name = "laddbuffer" ; |
5384 | break; |
5385 | default: |
5386 | break; |
5387 | } |
5388 | |
5389 | if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, (char_u *)au_name, |
5390 | curbuf->b_fname, true, curbuf)) { |
5391 | if (aborting()) { |
5392 | return; |
5393 | } |
5394 | } |
5395 | |
5396 | // Must come after autocommands. |
5397 | if (eap->cmdidx == CMD_lbuffer |
5398 | || eap->cmdidx == CMD_lgetbuffer |
5399 | || eap->cmdidx == CMD_laddbuffer) { |
5400 | qi = ll_get_or_alloc_list(curwin); |
5401 | wp = curwin; |
5402 | } |
5403 | |
5404 | if (*eap->arg == NUL) |
5405 | buf = curbuf; |
5406 | else if (*skipwhite(skipdigits(eap->arg)) == NUL) |
5407 | buf = buflist_findnr(atoi((char *)eap->arg)); |
5408 | if (buf == NULL) |
5409 | EMSG(_(e_invarg)); |
5410 | else if (buf->b_ml.ml_mfp == NULL) |
5411 | EMSG(_("E681: Buffer is not loaded" )); |
5412 | else { |
5413 | if (eap->addr_count == 0) { |
5414 | eap->line1 = 1; |
5415 | eap->line2 = buf->b_ml.ml_line_count; |
5416 | } |
5417 | if (eap->line1 < 1 || eap->line1 > buf->b_ml.ml_line_count |
5418 | || eap->line2 < 1 || eap->line2 > buf->b_ml.ml_line_count) { |
5419 | EMSG(_(e_invrange)); |
5420 | } else { |
5421 | char_u *qf_title = qf_cmdtitle(*eap->cmdlinep); |
5422 | |
5423 | if (buf->b_sfname) { |
5424 | vim_snprintf((char *)IObuff, IOSIZE, "%s (%s)" , |
5425 | (char *)qf_title, (char *)buf->b_sfname); |
5426 | qf_title = IObuff; |
5427 | } |
5428 | |
5429 | int res = qf_init_ext(qi, qi->qf_curlist, NULL, buf, NULL, p_efm, |
5430 | (eap->cmdidx != CMD_caddbuffer |
5431 | && eap->cmdidx != CMD_laddbuffer), |
5432 | eap->line1, eap->line2, qf_title, NULL); |
5433 | if (res >= 0) { |
5434 | qf_list_changed(qi, qi->qf_curlist); |
5435 | } |
5436 | // Remember the current quickfix list identifier, so that we can |
5437 | // check for autocommands changing the current quickfix list. |
5438 | unsigned save_qfid = qi->qf_lists[qi->qf_curlist].qf_id; |
5439 | if (au_name != NULL) { |
5440 | const buf_T *const curbuf_old = curbuf; |
5441 | apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name, |
5442 | curbuf->b_fname, true, curbuf); |
5443 | if (curbuf != curbuf_old) { |
5444 | // Autocommands changed buffer, don't jump now, "qi" may |
5445 | // be invalid. |
5446 | res = 0; |
5447 | } |
5448 | } |
5449 | // Jump to the first error for new list and if autocmds didn't |
5450 | // free the list. |
5451 | if (res > 0 && (eap->cmdidx == CMD_cbuffer || eap->cmdidx == CMD_lbuffer) |
5452 | && qflist_valid(wp, save_qfid)) { |
5453 | // display the first error |
5454 | qf_jump_first(qi, save_qfid, eap->forceit); |
5455 | } |
5456 | } |
5457 | } |
5458 | } |
5459 | |
5460 | /* |
5461 | * ":cexpr {expr}", ":cgetexpr {expr}", ":caddexpr {expr}" command. |
5462 | * ":lexpr {expr}", ":lgetexpr {expr}", ":laddexpr {expr}" command. |
5463 | */ |
5464 | void ex_cexpr(exarg_T *eap) |
5465 | { |
5466 | qf_info_T *qi = &ql_info; |
5467 | const char *au_name = NULL; |
5468 | win_T *wp = NULL; |
5469 | |
5470 | switch (eap->cmdidx) { |
5471 | case CMD_cexpr: |
5472 | au_name = "cexpr" ; |
5473 | break; |
5474 | case CMD_cgetexpr: |
5475 | au_name = "cgetexpr" ; |
5476 | break; |
5477 | case CMD_caddexpr: |
5478 | au_name = "caddexpr" ; |
5479 | break; |
5480 | case CMD_lexpr: |
5481 | au_name = "lexpr" ; |
5482 | break; |
5483 | case CMD_lgetexpr: |
5484 | au_name = "lgetexpr" ; |
5485 | break; |
5486 | case CMD_laddexpr: |
5487 | au_name = "laddexpr" ; |
5488 | break; |
5489 | default: |
5490 | break; |
5491 | } |
5492 | if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, (char_u *)au_name, |
5493 | curbuf->b_fname, true, curbuf)) { |
5494 | if (aborting()) { |
5495 | return; |
5496 | } |
5497 | } |
5498 | |
5499 | if (eap->cmdidx == CMD_lexpr |
5500 | || eap->cmdidx == CMD_lgetexpr |
5501 | || eap->cmdidx == CMD_laddexpr) { |
5502 | qi = ll_get_or_alloc_list(curwin); |
5503 | wp = curwin; |
5504 | } |
5505 | |
5506 | /* Evaluate the expression. When the result is a string or a list we can |
5507 | * use it to fill the errorlist. */ |
5508 | typval_T tv; |
5509 | if (eval0(eap->arg, &tv, NULL, true) != FAIL) { |
5510 | if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL) |
5511 | || tv.v_type == VAR_LIST) { |
5512 | int res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, &tv, p_efm, |
5513 | (eap->cmdidx != CMD_caddexpr |
5514 | && eap->cmdidx != CMD_laddexpr), |
5515 | (linenr_T)0, (linenr_T)0, |
5516 | qf_cmdtitle(*eap->cmdlinep), NULL); |
5517 | if (res >= 0) { |
5518 | qf_list_changed(qi, qi->qf_curlist); |
5519 | } |
5520 | // Remember the current quickfix list identifier, so that we can |
5521 | // check for autocommands changing the current quickfix list. |
5522 | unsigned save_qfid = qi->qf_lists[qi->qf_curlist].qf_id; |
5523 | if (au_name != NULL) { |
5524 | apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name, |
5525 | curbuf->b_fname, true, curbuf); |
5526 | } |
5527 | // Jump to the first error for a new list and if autocmds didn't |
5528 | // free the list. |
5529 | if (res > 0 |
5530 | && (eap->cmdidx == CMD_cexpr || eap->cmdidx == CMD_lexpr) |
5531 | && qflist_valid(wp, save_qfid)) { |
5532 | // display the first error |
5533 | qf_jump_first(qi, save_qfid, eap->forceit); |
5534 | } |
5535 | } else { |
5536 | EMSG(_("E777: String or List expected" )); |
5537 | } |
5538 | tv_clear(&tv); |
5539 | } |
5540 | } |
5541 | |
5542 | /* |
5543 | * ":helpgrep {pattern}" |
5544 | */ |
5545 | void ex_helpgrep(exarg_T *eap) |
5546 | { |
5547 | regmatch_T regmatch; |
5548 | char_u *save_cpo; |
5549 | char_u *p; |
5550 | int fcount; |
5551 | char_u **fnames; |
5552 | FILE *fd; |
5553 | int fi; |
5554 | long lnum; |
5555 | char_u *lang; |
5556 | qf_info_T *qi = &ql_info; |
5557 | int new_qi = FALSE; |
5558 | char_u *au_name = NULL; |
5559 | |
5560 | /* Check for a specified language */ |
5561 | lang = check_help_lang(eap->arg); |
5562 | |
5563 | switch (eap->cmdidx) { |
5564 | case CMD_helpgrep: au_name = (char_u *)"helpgrep" ; break; |
5565 | case CMD_lhelpgrep: au_name = (char_u *)"lhelpgrep" ; break; |
5566 | default: break; |
5567 | } |
5568 | if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name, |
5569 | curbuf->b_fname, true, curbuf)) { |
5570 | if (aborting()) { |
5571 | return; |
5572 | } |
5573 | } |
5574 | |
5575 | /* Make 'cpoptions' empty, the 'l' flag should not be used here. */ |
5576 | save_cpo = p_cpo; |
5577 | p_cpo = empty_option; |
5578 | |
5579 | if (eap->cmdidx == CMD_lhelpgrep) { |
5580 | win_T *wp = NULL; |
5581 | |
5582 | // If the current window is a help window, then use it |
5583 | if (bt_help(curwin->w_buffer)) { |
5584 | wp = curwin; |
5585 | } else { |
5586 | // Find an existing help window |
5587 | FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) { |
5588 | if (bt_help(wp2->w_buffer)) { |
5589 | wp = wp2; |
5590 | break; |
5591 | } |
5592 | } |
5593 | } |
5594 | |
5595 | if (wp == NULL) { // Help window not found |
5596 | qi = NULL; |
5597 | } else { |
5598 | qi = wp->w_llist; |
5599 | } |
5600 | if (qi == NULL) { |
5601 | /* Allocate a new location list for help text matches */ |
5602 | qi = ll_new_list(); |
5603 | new_qi = TRUE; |
5604 | } |
5605 | } |
5606 | |
5607 | regmatch.regprog = vim_regcomp(eap->arg, RE_MAGIC + RE_STRING); |
5608 | regmatch.rm_ic = FALSE; |
5609 | if (regmatch.regprog != NULL) { |
5610 | // Create a new quickfix list. |
5611 | qf_new_list(qi, qf_cmdtitle(*eap->cmdlinep)); |
5612 | |
5613 | // Go through all the directories in 'runtimepath' |
5614 | p = p_rtp; |
5615 | while (*p != NUL && !got_int) { |
5616 | copy_option_part(&p, NameBuff, MAXPATHL, "," ); |
5617 | |
5618 | /* Find all "*.txt" and "*.??x" files in the "doc" directory. */ |
5619 | add_pathsep((char *)NameBuff); |
5620 | STRCAT(NameBuff, "doc/*.\\(txt\\|??x\\)" ); |
5621 | |
5622 | // Note: We cannot just do `&NameBuff` because it is a statically sized array |
5623 | // so `NameBuff == &NameBuff` according to C semantics. |
5624 | char_u *buff_list[1] = {NameBuff}; |
5625 | if (gen_expand_wildcards(1, buff_list, &fcount, |
5626 | &fnames, EW_FILE|EW_SILENT) == OK |
5627 | && fcount > 0) { |
5628 | for (fi = 0; fi < fcount && !got_int; fi++) { |
5629 | // Skip files for a different language. |
5630 | if (lang != NULL |
5631 | && STRNICMP(lang, fnames[fi] + STRLEN(fnames[fi]) - 3, 2) != 0 |
5632 | && !(STRNICMP(lang, "en" , 2) == 0 |
5633 | && STRNICMP("txt" , fnames[fi] |
5634 | + STRLEN(fnames[fi]) - 3, 3) == 0)) { |
5635 | continue; |
5636 | } |
5637 | fd = os_fopen((char *)fnames[fi], "r" ); |
5638 | if (fd != NULL) { |
5639 | lnum = 1; |
5640 | while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int) { |
5641 | char_u *line = IObuff; |
5642 | if (vim_regexec(®match, line, (colnr_T)0)) { |
5643 | int l = (int)STRLEN(line); |
5644 | |
5645 | /* remove trailing CR, LF, spaces, etc. */ |
5646 | while (l > 0 && line[l - 1] <= ' ') |
5647 | line[--l] = NUL; |
5648 | |
5649 | if (qf_add_entry(qi, |
5650 | qi->qf_curlist, |
5651 | NULL, // dir |
5652 | fnames[fi], |
5653 | NULL, |
5654 | 0, |
5655 | line, |
5656 | lnum, |
5657 | (int)(regmatch.startp[0] - line) |
5658 | + 1, // col |
5659 | false, // vis_col |
5660 | NULL, // search pattern |
5661 | 0, // nr |
5662 | 1, // type |
5663 | true) // valid |
5664 | == FAIL) { |
5665 | got_int = true; |
5666 | if (line != IObuff) { |
5667 | xfree(line); |
5668 | } |
5669 | break; |
5670 | } |
5671 | } |
5672 | if (line != IObuff) |
5673 | xfree(line); |
5674 | ++lnum; |
5675 | line_breakcheck(); |
5676 | } |
5677 | fclose(fd); |
5678 | } |
5679 | } |
5680 | FreeWild(fcount, fnames); |
5681 | } |
5682 | } |
5683 | |
5684 | vim_regfree(regmatch.regprog); |
5685 | |
5686 | qi->qf_lists[qi->qf_curlist].qf_nonevalid = FALSE; |
5687 | qi->qf_lists[qi->qf_curlist].qf_ptr = |
5688 | qi->qf_lists[qi->qf_curlist].qf_start; |
5689 | qi->qf_lists[qi->qf_curlist].qf_index = 1; |
5690 | } |
5691 | |
5692 | if (p_cpo == empty_option) |
5693 | p_cpo = save_cpo; |
5694 | else |
5695 | /* Darn, some plugin changed the value. */ |
5696 | free_string_option(save_cpo); |
5697 | |
5698 | qf_list_changed(qi, qi->qf_curlist); |
5699 | qf_update_buffer(qi, NULL); |
5700 | |
5701 | if (au_name != NULL) { |
5702 | apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, |
5703 | curbuf->b_fname, true, curbuf); |
5704 | if (!new_qi && IS_LL_STACK(qi) && qf_find_buf(qi) == NULL) { |
5705 | // autocommands made "qi" invalid |
5706 | return; |
5707 | } |
5708 | } |
5709 | |
5710 | /* Jump to first match. */ |
5711 | if (qi->qf_lists[qi->qf_curlist].qf_count > 0) |
5712 | qf_jump(qi, 0, 0, FALSE); |
5713 | else |
5714 | EMSG2(_(e_nomatch2), eap->arg); |
5715 | |
5716 | if (eap->cmdidx == CMD_lhelpgrep) { |
5717 | /* If the help window is not opened or if it already points to the |
5718 | * correct location list, then free the new location list. */ |
5719 | if (!bt_help(curwin->w_buffer) || curwin->w_llist == qi) { |
5720 | if (new_qi) { |
5721 | ll_free_all(&qi); |
5722 | } |
5723 | } else if (curwin->w_llist == NULL) { |
5724 | curwin->w_llist = qi; |
5725 | } |
5726 | } |
5727 | } |
5728 | |
5729 | |