blob: 79c9fdf39e05b6a91da75c49d863900a7dacf743 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * quickfix.c: functions for quickfix mode, using a file with error messages
12 */
13
14#include "vim.h"
15
16#if defined(FEAT_QUICKFIX) || defined(PROTO)
17
18struct dir_stack_T
19{
20 struct dir_stack_T *next;
21 char_u *dirname;
22};
23
Bram Moolenaar071d4272004-06-13 20:20:40 +000024/*
Bram Moolenaar68b76a62005-03-25 21:53:48 +000025 * For each error the next struct is allocated and linked in a list.
Bram Moolenaar071d4272004-06-13 20:20:40 +000026 */
Bram Moolenaar68b76a62005-03-25 21:53:48 +000027typedef struct qfline_S qfline_T;
28struct qfline_S
Bram Moolenaar071d4272004-06-13 20:20:40 +000029{
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +020030 qfline_T *qf_next; // pointer to next error in the list
31 qfline_T *qf_prev; // pointer to previous error in the list
32 linenr_T qf_lnum; // line number where the error occurred
thinca6864efa2021-06-19 20:45:20 +020033 linenr_T qf_end_lnum; // line number when the error has range or zero
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +020034 int qf_fnum; // file number for the line
35 int qf_col; // column where the error occurred
thinca6864efa2021-06-19 20:45:20 +020036 int qf_end_col; // column when the error has range or zero
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +020037 int qf_nr; // error number
38 char_u *qf_module; // module name for this error
Austin Chang29822992024-10-03 10:50:05 +020039 char_u *qf_fname; // different filename if there're hard links
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +020040 char_u *qf_pattern; // search pattern for the error
41 char_u *qf_text; // description of the error
thinca6864efa2021-06-19 20:45:20 +020042 char_u qf_viscol; // set to TRUE if qf_col and qf_end_col is
43 // screen column
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +020044 char_u qf_cleared; // set to TRUE if line has been deleted
45 char_u qf_type; // type of the error (mostly 'E'); 1 for
46 // :helpgrep
Tom Praschanca6ac992023-08-11 23:26:12 +020047 typval_T qf_user_data; // custom user data associated with this item
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +020048 char_u qf_valid; // valid error message detected
Bram Moolenaar071d4272004-06-13 20:20:40 +000049};
50
51/*
52 * There is a stack of error lists.
53 */
54#define LISTCOUNT 10
Bram Moolenaara2aa8a22018-04-24 13:55:00 +020055#define INVALID_QFIDX (-1)
Bram Moolenaaree8188f2019-02-05 21:23:04 +010056#define INVALID_QFBUFNR (0)
Bram Moolenaar071d4272004-06-13 20:20:40 +000057
Bram Moolenaar6a8958d2017-06-22 21:33:20 +020058/*
Bram Moolenaar2d67d302018-11-16 18:46:02 +010059 * Quickfix list type.
60 */
61typedef enum
62{
63 QFLT_QUICKFIX, // Quickfix list - global list
64 QFLT_LOCATION, // Location list - per window list
65 QFLT_INTERNAL // Internal - Temporary list used by getqflist()/getloclist()
66} qfltype_T;
67
68/*
Bram Moolenaar6a8958d2017-06-22 21:33:20 +020069 * Quickfix/Location list definition
70 * Contains a list of entries (qfline_T). qf_start points to the first entry
71 * and qf_last points to the last entry. qf_count contains the list size.
72 *
73 * Usually the list contains one or more entries. But an empty list can be
74 * created using setqflist()/setloclist() with a title and/or user context
75 * information and entries can be added later using setqflist()/setloclist().
76 */
Bram Moolenaard12f5c12006-01-25 22:10:52 +000077typedef struct qf_list_S
Bram Moolenaar071d4272004-06-13 20:20:40 +000078{
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +020079 int_u qf_id; // Unique identifier for this list
Bram Moolenaar2d67d302018-11-16 18:46:02 +010080 qfltype_T qfl_type;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +020081 qfline_T *qf_start; // pointer to the first error
82 qfline_T *qf_last; // pointer to the last error
83 qfline_T *qf_ptr; // pointer to the current error
84 int qf_count; // number of errors (0 means empty list)
85 int qf_index; // current index in the error list
86 int qf_nonevalid; // TRUE if not a single valid entry found
Tom Praschanca6ac992023-08-11 23:26:12 +020087 int qf_has_user_data; // TRUE if at least one item has user_data attached
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +020088 char_u *qf_title; // title derived from the command that created
89 // the error list or set by setqflist
90 typval_T *qf_ctx; // context set by setqflist/setloclist
=?UTF-8?q?Dundar=20G=C3=B6c?=b8366582022-04-14 20:43:56 +010091 callback_T qf_qftf_cb; // 'quickfixtextfunc' callback function
Bram Moolenaara7df8c72017-07-19 13:23:06 +020092
93 struct dir_stack_T *qf_dir_stack;
94 char_u *qf_directory;
95 struct dir_stack_T *qf_file_stack;
96 char_u *qf_currfile;
97 int qf_multiline;
98 int qf_multiignore;
99 int qf_multiscan;
Bram Moolenaarb254af32017-12-18 19:48:58 +0100100 long qf_changedtick;
Bram Moolenaard12f5c12006-01-25 22:10:52 +0000101} qf_list_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000102
Bram Moolenaar6a8958d2017-06-22 21:33:20 +0200103/*
104 * Quickfix/Location list stack definition
105 * Contains a list of quickfix/location lists (qf_list_T)
106 */
Bram Moolenaard12f5c12006-01-25 22:10:52 +0000107struct qf_info_S
108{
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200109 // Count of references to this list. Used only for location lists.
110 // When a location list window reference this list, qf_refcount
111 // will be 2. Otherwise, qf_refcount will be 1. When qf_refcount
112 // reaches 0, the list is freed.
Bram Moolenaard12f5c12006-01-25 22:10:52 +0000113 int qf_refcount;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200114 int qf_listcount; // current number of lists
115 int qf_curlist; // current error list
Bram Moolenaard12f5c12006-01-25 22:10:52 +0000116 qf_list_T qf_lists[LISTCOUNT];
Bram Moolenaar2d67d302018-11-16 18:46:02 +0100117 qfltype_T qfl_type; // type of list
Bram Moolenaaree8188f2019-02-05 21:23:04 +0100118 int qf_bufnr; // quickfix window buffer number
Bram Moolenaard12f5c12006-01-25 22:10:52 +0000119};
120
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200121static qf_info_T ql_info; // global quickfix list
122static int_u last_qf_id = 0; // Last used quickfix list id
Bram Moolenaar071d4272004-06-13 20:20:40 +0000123
Yegappan Lakshmananb7318002023-10-25 20:50:28 +0200124#define FMT_PATTERNS 14 // maximum number of % recognized
Bram Moolenaar071d4272004-06-13 20:20:40 +0000125
126/*
127 * Structure used to hold the info of one part of 'errorformat'
128 */
Bram Moolenaar01265852006-03-20 21:50:15 +0000129typedef struct efm_S efm_T;
130struct efm_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000131{
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200132 regprog_T *prog; // pre-formatted part of 'errorformat'
133 efm_T *next; // pointer to next (NULL if last)
134 char_u addr[FMT_PATTERNS]; // indices of used % patterns
135 char_u prefix; // prefix of this format line:
136 // 'D' enter directory
137 // 'X' leave directory
138 // 'A' start of multi-line message
139 // 'E' error message
140 // 'W' warning message
141 // 'I' informational message
Bram Moolenaare9283662020-06-07 14:10:47 +0200142 // 'N' note message
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200143 // 'C' continuation line
144 // 'Z' end of multi-line message
145 // 'G' general, unspecific message
146 // 'P' push file (partial) message
147 // 'Q' pop/quit file (partial) message
148 // 'O' overread (partial) message
149 char_u flags; // additional flags given in prefix
150 // '-' do not include this line
151 // '+' include whole line in message
152 int conthere; // %> used
Bram Moolenaar071d4272004-06-13 20:20:40 +0000153};
154
Bram Moolenaar9f84ded2018-10-20 20:54:02 +0200155// List of location lists to be deleted.
156// Used to delay the deletion of locations lists by autocmds.
157typedef struct qf_delq_S
158{
159 struct qf_delq_S *next;
160 qf_info_T *qi;
161} qf_delq_T;
162static qf_delq_T *qf_delq_head = NULL;
163
164// Counter to prevent autocmds from freeing up location lists when they are
165// still being used.
166static int quickfix_busy = 0;
167
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200168static efm_T *fmt_start = NULL; // cached across qf_parse_line() calls
Bram Moolenaar63bed3d2016-11-12 15:36:54 +0100169
Bram Moolenaard43906d2020-07-20 21:31:32 +0200170// callback function for 'quickfixtextfunc'
171static callback_T qftf_cb;
172
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100173static void qf_new_list(qf_info_T *qi, char_u *qf_title);
Tom Praschanca6ac992023-08-11 23:26:12 +0200174static int qf_add_entry(qf_list_T *qfl, char_u *dir, char_u *fname, char_u *module, int bufnum, char_u *mesg, long lnum, long end_lnum, int col, int end_col, int vis_col, char_u *pattern, int nr, int type, typval_T *user_data, int valid);
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +0200175static void qf_free(qf_list_T *qfl);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100176static char_u *qf_types(int, int);
Bram Moolenaar0398e002019-03-21 21:12:49 +0100177static int qf_get_fnum(qf_list_T *qfl, char_u *, char_u *);
Bram Moolenaar361c8f02016-07-02 15:41:47 +0200178static char_u *qf_push_dir(char_u *, struct dir_stack_T **, int is_file_stack);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100179static char_u *qf_pop_dir(struct dir_stack_T **);
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +0200180static char_u *qf_guess_filepath(qf_list_T *qfl, char_u *);
Bram Moolenaar5843f5f2019-08-20 20:13:45 +0200181static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, int newwin);
Yegappan Lakshmananf8412c92022-10-13 11:59:22 +0100182static void qf_fmt_text(garray_T *gap, char_u *text);
183static void qf_range_text(garray_T *gap, qfline_T *qfp);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100184static int qf_win_pos_update(qf_info_T *qi, int old_qf_index);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100185static win_T *qf_find_win(qf_info_T *qi);
186static buf_T *qf_find_buf(qf_info_T *qi);
Bram Moolenaar864293a2016-06-02 13:40:04 +0200187static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last);
Bram Moolenaar7ba5a7e2020-06-08 19:20:27 +0200188static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int qf_winid);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100189static buf_T *load_dummy_buffer(char_u *fname, char_u *dirname_start, char_u *resulting_dir);
190static void wipe_dummy_buffer(buf_T *buf, char_u *dirname_start);
191static void unload_dummy_buffer(buf_T *buf, char_u *dirname_start);
192static qf_info_T *ll_get_or_alloc_list(win_T *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000193
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200194// Quickfix window check helper macro
kylo252ae6f1d82022-02-16 19:24:07 +0000195#define IS_QF_WINDOW(wp) (bt_quickfix((wp)->w_buffer) && (wp)->w_llist_ref == NULL)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200196// Location list window check helper macro
kylo252ae6f1d82022-02-16 19:24:07 +0000197#define IS_LL_WINDOW(wp) (bt_quickfix((wp)->w_buffer) && (wp)->w_llist_ref != NULL)
Bram Moolenaar4d77c652018-08-18 19:59:54 +0200198
199// Quickfix and location list stack check helper macros
kylo252ae6f1d82022-02-16 19:24:07 +0000200#define IS_QF_STACK(qi) ((qi)->qfl_type == QFLT_QUICKFIX)
201#define IS_LL_STACK(qi) ((qi)->qfl_type == QFLT_LOCATION)
202#define IS_QF_LIST(qfl) ((qfl)->qfl_type == QFLT_QUICKFIX)
203#define IS_LL_LIST(qfl) ((qfl)->qfl_type == QFLT_LOCATION)
Bram Moolenaar4d77c652018-08-18 19:59:54 +0200204
Bram Moolenaard12f5c12006-01-25 22:10:52 +0000205/*
206 * Return location list for window 'wp'
207 * For location list window, return the referenced location list
208 */
kylo252ae6f1d82022-02-16 19:24:07 +0000209#define GET_LOC_LIST(wp) (IS_LL_WINDOW(wp) ? (wp)->w_llist_ref : (wp)->w_llist)
Bram Moolenaard12f5c12006-01-25 22:10:52 +0000210
Bram Moolenaar95946f12019-03-31 15:31:59 +0200211// Macro to loop through all the items in a quickfix list
212// Quickfix item index starts from 1, so i below starts at 1
Bram Moolenaara16123a2019-03-28 20:31:07 +0100213#define FOR_ALL_QFL_ITEMS(qfl, qfp, i) \
kylo252ae6f1d82022-02-16 19:24:07 +0000214 for ((i) = 1, (qfp) = (qfl)->qf_start; \
215 !got_int && (i) <= (qfl)->qf_count && (qfp) != NULL; \
216 ++(i), (qfp) = (qfp)->qf_next)
Bram Moolenaara16123a2019-03-28 20:31:07 +0100217
Bram Moolenaar071d4272004-06-13 20:20:40 +0000218/*
Bram Moolenaar6dd4a532017-05-28 07:56:36 +0200219 * Looking up a buffer can be slow if there are many. Remember the last one
220 * to make this a lot faster if there are multiple matches in the same file.
221 */
Bram Moolenaar45e5fd12017-06-04 14:58:02 +0200222static char_u *qf_last_bufname = NULL;
223static bufref_T qf_last_bufref = {NULL, 0, 0};
Bram Moolenaar6dd4a532017-05-28 07:56:36 +0200224
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +0100225static garray_T qfga;
226
227/*
228 * Get a growarray to buffer text in. Shared between various commands to avoid
229 * many alloc/free calls.
230 */
231 static garray_T *
232qfga_get(void)
233{
234 static int initialized = FALSE;
235
236 if (!initialized)
237 {
238 initialized = TRUE;
239 ga_init2(&qfga, 1, 256);
240 }
241
Yegappan Lakshmanand8cd6f72022-10-16 11:30:48 +0100242 // Reset the length to zero. Retain ga_data from previous use to avoid
243 // many alloc/free calls.
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +0100244 qfga.ga_len = 0;
245
246 return &qfga;
247}
248
Bram Moolenaar6dd4a532017-05-28 07:56:36 +0200249/*
Yegappan Lakshmanand8cd6f72022-10-16 11:30:48 +0100250 * The "qfga" grow array buffer is reused across multiple quickfix commands as
251 * a temporary buffer to reduce the number of alloc/free calls. But if the
252 * buffer size is large, then to avoid holding on to that memory, clear the
253 * grow array. Otherwise just reset the grow array length.
254 */
255 static void
256qfga_clear(void)
257{
258 if (qfga.ga_maxlen > 1000)
259 ga_clear(&qfga);
260 else
261 qfga.ga_len = 0;
262}
263
264/*
Bram Moolenaar6be8c8e2016-04-30 13:17:09 +0200265 * Maximum number of bytes allowed per line while reading a errorfile.
266 */
267#define LINE_MAXLEN 4096
268
haya14busae023d492022-02-08 18:09:29 +0000269/*
270 * Patterns used. Keep in sync with qf_parse_fmt[].
271 */
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200272static struct fmtpattern
273{
274 char_u convchar;
275 char *pattern;
276} fmt_pat[FMT_PATTERNS] =
Bram Moolenaar18cebf42018-05-08 22:31:37 +0200277 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200278 {'f', ".\\+"}, // only used when at end
Yegappan Lakshmananb7318002023-10-25 20:50:28 +0200279 {'b', "\\d\\+"}, // 1
280 {'n', "\\d\\+"}, // 2
281 {'l', "\\d\\+"}, // 3
282 {'e', "\\d\\+"}, // 4
283 {'c', "\\d\\+"}, // 5
284 {'k', "\\d\\+"}, // 6
285 {'t', "."}, // 7
286#define FMT_PATTERN_M 8
287 {'m', ".\\+"}, // 8
288#define FMT_PATTERN_R 9
289 {'r', ".*"}, // 9
290 {'p', "[- .]*"}, // 10
291 {'v', "\\d\\+"}, // 11
292 {'s', ".\\+"}, // 12
293 {'o', ".\\+"} // 13
Bram Moolenaar18cebf42018-05-08 22:31:37 +0200294 };
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200295
296/*
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200297 * Convert an errorformat pattern to a regular expression pattern.
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200298 * See fmt_pat definition above for the list of supported patterns. The
299 * pattern specifier is supplied in "efmpat". The converted pattern is stored
300 * in "regpat". Returns a pointer to the location after the pattern.
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200301 */
302 static char_u *
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200303efmpat_to_regpat(
304 char_u *efmpat,
305 char_u *regpat,
306 efm_T *efminfo,
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200307 int idx,
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100308 int round)
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200309{
310 char_u *srcptr;
311
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200312 if (efminfo->addr[idx])
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200313 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200314 // Each errorformat pattern can occur only once
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000315 semsg(_(e_too_many_chr_in_format_string), *efmpat);
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200316 return NULL;
317 }
haya14busae023d492022-02-08 18:09:29 +0000318 if ((idx && idx < FMT_PATTERN_R
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200319 && vim_strchr((char_u *)"DXOPQ", efminfo->prefix) != NULL)
haya14busae023d492022-02-08 18:09:29 +0000320 || (idx == FMT_PATTERN_R
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200321 && vim_strchr((char_u *)"OPQ", efminfo->prefix) == NULL))
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200322 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000323 semsg(_(e_unexpected_chr_in_format_str), *efmpat);
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200324 return NULL;
325 }
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200326 efminfo->addr[idx] = (char_u)++round;
327 *regpat++ = '\\';
328 *regpat++ = '(';
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200329#ifdef BACKSLASH_IN_FILENAME
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200330 if (*efmpat == 'f')
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200331 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200332 // Also match "c:" in the file name, even when
333 // checking for a colon next: "%f:".
334 // "\%(\a:\)\="
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200335 STRCPY(regpat, "\\%(\\a:\\)\\=");
336 regpat += 10;
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200337 }
338#endif
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200339 if (*efmpat == 'f' && efmpat[1] != NUL)
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200340 {
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200341 if (efmpat[1] != '\\' && efmpat[1] != '%')
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200342 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200343 // A file name may contain spaces, but this isn't
344 // in "\f". For "%f:%l:%m" there may be a ":" in
345 // the file name. Use ".\{-1,}x" instead (x is
346 // the next character), the requirement that :999:
347 // follows should work.
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200348 STRCPY(regpat, ".\\{-1,}");
349 regpat += 7;
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200350 }
351 else
352 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200353 // File name followed by '\\' or '%': include as
354 // many file name chars as possible.
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200355 STRCPY(regpat, "\\f\\+");
356 regpat += 4;
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200357 }
358 }
359 else
360 {
361 srcptr = (char_u *)fmt_pat[idx].pattern;
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200362 while ((*regpat = *srcptr++) != NUL)
363 ++regpat;
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200364 }
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200365 *regpat++ = '\\';
366 *regpat++ = ')';
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200367
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200368 return regpat;
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200369}
370
371/*
372 * Convert a scanf like format in 'errorformat' to a regular expression.
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200373 * Returns a pointer to the location after the pattern.
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200374 */
375 static char_u *
376scanf_fmt_to_regpat(
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200377 char_u **pefmp,
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200378 char_u *efm,
379 int len,
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100380 char_u *regpat)
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200381{
382 char_u *efmp = *pefmp;
383
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200384 if (*efmp == '[' || *efmp == '\\')
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200385 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200386 if ((*regpat++ = *efmp) == '[') // %*[^a-z0-9] etc.
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200387 {
388 if (efmp[1] == '^')
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200389 *regpat++ = *++efmp;
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200390 if (efmp < efm + len)
391 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200392 *regpat++ = *++efmp; // could be ']'
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200393 while (efmp < efm + len
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200394 && (*regpat++ = *++efmp) != ']')
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200395 // skip ;
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200396 if (efmp == efm + len)
397 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000398 emsg(_(e_missing_rsb_in_format_string));
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200399 return NULL;
400 }
401 }
402 }
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200403 else if (efmp < efm + len) // %*\D, %*\s etc.
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200404 *regpat++ = *++efmp;
405 *regpat++ = '\\';
406 *regpat++ = '+';
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200407 }
408 else
409 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200410 // TODO: scanf()-like: %*ud, %*3c, %*f, ... ?
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000411 semsg(_(e_unsupported_chr_in_format_string), *efmp);
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200412 return NULL;
413 }
414
415 *pefmp = efmp;
416
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200417 return regpat;
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200418}
419
420/*
421 * Analyze/parse an errorformat prefix.
422 */
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200423 static char_u *
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100424efm_analyze_prefix(char_u *efmp, efm_T *efminfo)
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200425{
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200426 if (vim_strchr((char_u *)"+-", *efmp) != NULL)
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200427 efminfo->flags = *efmp++;
Bram Moolenaare9283662020-06-07 14:10:47 +0200428 if (vim_strchr((char_u *)"DXAEWINCZGOPQ", *efmp) != NULL)
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200429 efminfo->prefix = *efmp;
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200430 else
431 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000432 semsg(_(e_invalid_chr_in_format_string_prefix), *efmp);
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200433 return NULL;
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200434 }
435
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200436 return efmp;
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200437}
438
439/*
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200440 * Converts a 'errorformat' string part in 'efm' to a regular expression
441 * pattern. The resulting regex pattern is returned in "regpat". Additional
442 * information about the 'erroformat' pattern is returned in "fmt_ptr".
443 * Returns OK or FAIL.
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200444 */
445 static int
446efm_to_regpat(
Bram Moolenaaref6b8de2017-09-14 13:57:37 +0200447 char_u *efm,
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200448 int len,
Bram Moolenaaref6b8de2017-09-14 13:57:37 +0200449 efm_T *fmt_ptr,
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100450 char_u *regpat)
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200451{
452 char_u *ptr;
453 char_u *efmp;
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200454 int round;
455 int idx = 0;
456
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200457 // Build a regexp pattern for a 'errorformat' option part
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200458 ptr = regpat;
459 *ptr++ = '^';
460 round = 0;
461 for (efmp = efm; efmp < efm + len; ++efmp)
462 {
463 if (*efmp == '%')
464 {
465 ++efmp;
466 for (idx = 0; idx < FMT_PATTERNS; ++idx)
467 if (fmt_pat[idx].convchar == *efmp)
468 break;
469 if (idx < FMT_PATTERNS)
470 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100471 ptr = efmpat_to_regpat(efmp, ptr, fmt_ptr, idx, round);
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200472 if (ptr == NULL)
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200473 return FAIL;
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200474 round++;
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200475 }
476 else if (*efmp == '*')
477 {
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200478 ++efmp;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100479 ptr = scanf_fmt_to_regpat(&efmp, efm, len, ptr);
Bram Moolenaar6bff7192018-05-20 15:41:17 +0200480 if (ptr == NULL)
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200481 return FAIL;
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200482 }
483 else if (vim_strchr((char_u *)"%\\.^$~[", *efmp) != NULL)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200484 *ptr++ = *efmp; // regexp magic characters
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200485 else if (*efmp == '#')
486 *ptr++ = '*';
487 else if (*efmp == '>')
488 fmt_ptr->conthere = TRUE;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200489 else if (efmp == efm + 1) // analyse prefix
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200490 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200491 // prefix is allowed only at the beginning of the errorformat
492 // option part
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100493 efmp = efm_analyze_prefix(efmp, fmt_ptr);
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200494 if (efmp == NULL)
495 return FAIL;
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200496 }
497 else
498 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000499 semsg(_(e_invalid_chr_in_format_string), *efmp);
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200500 return FAIL;
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200501 }
502 }
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200503 else // copy normal character
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200504 {
505 if (*efmp == '\\' && efmp + 1 < efm + len)
506 ++efmp;
507 else if (vim_strchr((char_u *)".*^$~[", *efmp) != NULL)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200508 *ptr++ = '\\'; // escape regexp atoms
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200509 if (*efmp)
510 *ptr++ = *efmp;
511 }
512 }
513 *ptr++ = '$';
514 *ptr = NUL;
515
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200516 return OK;
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200517}
518
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200519/*
520 * Free the 'errorformat' information list
521 */
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200522 static void
523free_efm_list(efm_T **efm_first)
524{
525 efm_T *efm_ptr;
526
527 for (efm_ptr = *efm_first; efm_ptr != NULL; efm_ptr = *efm_first)
528 {
529 *efm_first = efm_ptr->next;
530 vim_regfree(efm_ptr->prog);
531 vim_free(efm_ptr);
532 }
Bram Moolenaar63bed3d2016-11-12 15:36:54 +0100533 fmt_start = NULL;
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200534}
535
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200536/*
537 * Compute the size of the buffer used to convert a 'errorformat' pattern into
538 * a regular expression pattern.
539 */
540 static int
541efm_regpat_bufsz(char_u *efm)
542{
543 int sz;
544 int i;
545
546 sz = (FMT_PATTERNS * 3) + ((int)STRLEN(efm) << 2);
547 for (i = FMT_PATTERNS; i > 0; )
548 sz += (int)STRLEN(fmt_pat[--i].pattern);
549#ifdef BACKSLASH_IN_FILENAME
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200550 sz += 12; // "%f" can become twelve chars longer (see efm_to_regpat)
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200551#else
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200552 sz += 2; // "%f" can become two chars longer
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200553#endif
554
555 return sz;
556}
557
558/*
559 * Return the length of a 'errorformat' option part (separated by ",").
560 */
561 static int
562efm_option_part_len(char_u *efm)
563{
564 int len;
565
566 for (len = 0; efm[len] != NUL && efm[len] != ','; ++len)
567 if (efm[len] == '\\' && efm[len + 1] != NUL)
568 ++len;
569
570 return len;
571}
572
573/*
574 * Parse the 'errorformat' option. Multiple parts in the 'errorformat' option
575 * are parsed and converted to regular expressions. Returns information about
576 * the parsed 'errorformat' option.
577 */
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200578 static efm_T *
579parse_efm_option(char_u *efm)
580{
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200581 efm_T *fmt_ptr = NULL;
582 efm_T *fmt_first = NULL;
583 efm_T *fmt_last = NULL;
584 char_u *fmtstr = NULL;
585 int len;
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200586 int sz;
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200587
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200588 // Each part of the format string is copied and modified from errorformat
589 // to regex prog. Only a few % characters are allowed.
Bram Moolenaare87e6dd2016-07-17 19:25:04 +0200590
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200591 // Get some space to modify the format string into.
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200592 sz = efm_regpat_bufsz(efm);
Yegappan Lakshmanan5a2d4a32022-02-26 10:31:32 +0000593 if ((fmtstr = alloc_id(sz, aid_qf_efm_fmtstr)) == NULL)
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200594 goto parse_efm_error;
595
596 while (efm[0] != NUL)
597 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200598 // Allocate a new eformat structure and put it at the end of the list
Yegappan Lakshmanan5a2d4a32022-02-26 10:31:32 +0000599 fmt_ptr = ALLOC_CLEAR_ONE_ID(efm_T, aid_qf_efm_fmtpart);
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200600 if (fmt_ptr == NULL)
601 goto parse_efm_error;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200602 if (fmt_first == NULL) // first one
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200603 fmt_first = fmt_ptr;
604 else
605 fmt_last->next = fmt_ptr;
606 fmt_last = fmt_ptr;
607
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200608 // Isolate one part in the 'errorformat' option
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200609 len = efm_option_part_len(efm);
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200610
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100611 if (efm_to_regpat(efm, len, fmt_ptr, fmtstr) == FAIL)
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200612 goto parse_efm_error;
613 if ((fmt_ptr->prog = vim_regcomp(fmtstr, RE_MAGIC + RE_STRING)) == NULL)
614 goto parse_efm_error;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200615 // Advance to next part
616 efm = skip_to_option_part(efm + len); // skip comma and spaces
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200617 }
618
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200619 if (fmt_first == NULL) // nothing found
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000620 emsg(_(e_errorformat_contains_no_pattern));
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200621
622 goto parse_efm_end;
623
624parse_efm_error:
625 free_efm_list(&fmt_first);
626
627parse_efm_end:
628 vim_free(fmtstr);
Bram Moolenaar688e3d12016-06-26 22:05:54 +0200629
630 return fmt_first;
631}
632
Bram Moolenaare0d37972016-07-15 22:36:01 +0200633enum {
634 QF_FAIL = 0,
635 QF_OK = 1,
636 QF_END_OF_INPUT = 2,
Bram Moolenaare87e6dd2016-07-17 19:25:04 +0200637 QF_NOMEM = 3,
Bram Moolenaar18cebf42018-05-08 22:31:37 +0200638 QF_IGNORE_LINE = 4,
639 QF_MULTISCAN = 5,
Yegappan Lakshmanan6d24a512022-08-27 20:59:57 +0100640 QF_ABORT = 6
Bram Moolenaare0d37972016-07-15 22:36:01 +0200641};
642
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200643/*
644 * State information used to parse lines and add entries to a quickfix/location
645 * list.
646 */
Bram Moolenaare0d37972016-07-15 22:36:01 +0200647typedef struct {
648 char_u *linebuf;
649 int linelen;
650 char_u *growbuf;
651 int growbufsiz;
652 FILE *fd;
653 typval_T *tv;
654 char_u *p_str;
655 listitem_T *p_li;
656 buf_T *buf;
657 linenr_T buflnum;
658 linenr_T lnumlast;
Bram Moolenaar2c7292d2017-03-05 17:43:31 +0100659 vimconv_T vc;
Bram Moolenaare0d37972016-07-15 22:36:01 +0200660} qfstate_T;
661
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200662/*
663 * Allocate more memory for the line buffer used for parsing lines.
664 */
Bram Moolenaare0d37972016-07-15 22:36:01 +0200665 static char_u *
666qf_grow_linebuf(qfstate_T *state, int newsz)
667{
Bram Moolenaar18cebf42018-05-08 22:31:37 +0200668 char_u *p;
669
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200670 // If the line exceeds LINE_MAXLEN exclude the last
671 // byte since it's not a NL character.
Bram Moolenaare0d37972016-07-15 22:36:01 +0200672 state->linelen = newsz > LINE_MAXLEN ? LINE_MAXLEN - 1 : newsz;
673 if (state->growbuf == NULL)
674 {
Yegappan Lakshmanan5a2d4a32022-02-26 10:31:32 +0000675 state->growbuf = alloc_id(state->linelen + 1, aid_qf_linebuf);
Bram Moolenaare0d37972016-07-15 22:36:01 +0200676 if (state->growbuf == NULL)
677 return NULL;
678 state->growbufsiz = state->linelen;
679 }
680 else if (state->linelen > state->growbufsiz)
681 {
Bram Moolenaar18cebf42018-05-08 22:31:37 +0200682 if ((p = vim_realloc(state->growbuf, state->linelen + 1)) == NULL)
Bram Moolenaare0d37972016-07-15 22:36:01 +0200683 return NULL;
Bram Moolenaar18cebf42018-05-08 22:31:37 +0200684 state->growbuf = p;
Bram Moolenaare0d37972016-07-15 22:36:01 +0200685 state->growbufsiz = state->linelen;
686 }
687 return state->growbuf;
688}
689
690/*
691 * Get the next string (separated by newline) from state->p_str.
692 */
693 static int
694qf_get_next_str_line(qfstate_T *state)
695{
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200696 // Get the next line from the supplied string
Bram Moolenaare0d37972016-07-15 22:36:01 +0200697 char_u *p_str = state->p_str;
698 char_u *p;
699 int len;
700
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200701 if (*p_str == NUL) // Reached the end of the string
Bram Moolenaare0d37972016-07-15 22:36:01 +0200702 return QF_END_OF_INPUT;
703
704 p = vim_strchr(p_str, '\n');
705 if (p != NULL)
706 len = (int)(p - p_str) + 1;
707 else
708 len = (int)STRLEN(p_str);
709
710 if (len > IOSIZE - 2)
711 {
712 state->linebuf = qf_grow_linebuf(state, len);
713 if (state->linebuf == NULL)
714 return QF_NOMEM;
715 }
716 else
717 {
718 state->linebuf = IObuff;
719 state->linelen = len;
720 }
721 vim_strncpy(state->linebuf, p_str, state->linelen);
722
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200723 // Increment using len in order to discard the rest of the
724 // line if it exceeds LINE_MAXLEN.
Bram Moolenaare0d37972016-07-15 22:36:01 +0200725 p_str += len;
726 state->p_str = p_str;
727
728 return QF_OK;
729}
730
731/*
Yegappan Lakshmanan5a2d4a32022-02-26 10:31:32 +0000732 * Get the next string from the List item state->p_li.
Bram Moolenaare0d37972016-07-15 22:36:01 +0200733 */
734 static int
735qf_get_next_list_line(qfstate_T *state)
736{
737 listitem_T *p_li = state->p_li;
738 int len;
739
740 while (p_li != NULL
741 && (p_li->li_tv.v_type != VAR_STRING
742 || p_li->li_tv.vval.v_string == NULL))
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200743 p_li = p_li->li_next; // Skip non-string items
Bram Moolenaare0d37972016-07-15 22:36:01 +0200744
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200745 if (p_li == NULL) // End of the list
Bram Moolenaare0d37972016-07-15 22:36:01 +0200746 {
747 state->p_li = NULL;
748 return QF_END_OF_INPUT;
749 }
750
751 len = (int)STRLEN(p_li->li_tv.vval.v_string);
752 if (len > IOSIZE - 2)
753 {
754 state->linebuf = qf_grow_linebuf(state, len);
755 if (state->linebuf == NULL)
756 return QF_NOMEM;
757 }
758 else
759 {
760 state->linebuf = IObuff;
761 state->linelen = len;
762 }
763
764 vim_strncpy(state->linebuf, p_li->li_tv.vval.v_string, state->linelen);
765
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200766 state->p_li = p_li->li_next; // next item
Bram Moolenaare0d37972016-07-15 22:36:01 +0200767 return QF_OK;
768}
769
770/*
771 * Get the next string from state->buf.
772 */
773 static int
774qf_get_next_buf_line(qfstate_T *state)
775{
776 char_u *p_buf = NULL;
777 int len;
778
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200779 // Get the next line from the supplied buffer
Bram Moolenaare0d37972016-07-15 22:36:01 +0200780 if (state->buflnum > state->lnumlast)
781 return QF_END_OF_INPUT;
782
783 p_buf = ml_get_buf(state->buf, state->buflnum, FALSE);
zeertzjq94b7c322024-03-12 21:50:32 +0100784 len = ml_get_buf_len(state->buf, state->buflnum);
Bram Moolenaare0d37972016-07-15 22:36:01 +0200785 state->buflnum += 1;
786
Bram Moolenaare0d37972016-07-15 22:36:01 +0200787 if (len > IOSIZE - 2)
788 {
789 state->linebuf = qf_grow_linebuf(state, len);
790 if (state->linebuf == NULL)
791 return QF_NOMEM;
792 }
793 else
794 {
795 state->linebuf = IObuff;
796 state->linelen = len;
797 }
798 vim_strncpy(state->linebuf, p_buf, state->linelen);
799
800 return QF_OK;
801}
802
803/*
804 * Get the next string from file state->fd.
805 */
806 static int
807qf_get_next_file_line(qfstate_T *state)
808{
809 int discard;
810 int growbuflen;
811
812 if (fgets((char *)IObuff, IOSIZE, state->fd) == NULL)
813 return QF_END_OF_INPUT;
814
815 discard = FALSE;
816 state->linelen = (int)STRLEN(IObuff);
Bram Moolenaar796aa9c2016-08-02 21:41:28 +0200817 if (state->linelen == IOSIZE - 1 && !(IObuff[state->linelen - 1] == '\n'))
Bram Moolenaare0d37972016-07-15 22:36:01 +0200818 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200819 // The current line exceeds IObuff, continue reading using
820 // growbuf until EOL or LINE_MAXLEN bytes is read.
Bram Moolenaare0d37972016-07-15 22:36:01 +0200821 if (state->growbuf == NULL)
822 {
823 state->growbufsiz = 2 * (IOSIZE - 1);
Yegappan Lakshmanan5a2d4a32022-02-26 10:31:32 +0000824 state->growbuf = alloc_id(state->growbufsiz, aid_qf_linebuf);
Bram Moolenaare0d37972016-07-15 22:36:01 +0200825 if (state->growbuf == NULL)
826 return QF_NOMEM;
827 }
828
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200829 // Copy the read part of the line, excluding null-terminator
Bram Moolenaare0d37972016-07-15 22:36:01 +0200830 memcpy(state->growbuf, IObuff, IOSIZE - 1);
831 growbuflen = state->linelen;
832
833 for (;;)
834 {
Bram Moolenaar18cebf42018-05-08 22:31:37 +0200835 char_u *p;
836
Bram Moolenaare0d37972016-07-15 22:36:01 +0200837 if (fgets((char *)state->growbuf + growbuflen,
838 state->growbufsiz - growbuflen, state->fd) == NULL)
839 break;
840 state->linelen = (int)STRLEN(state->growbuf + growbuflen);
841 growbuflen += state->linelen;
Bram Moolenaar796aa9c2016-08-02 21:41:28 +0200842 if ((state->growbuf)[growbuflen - 1] == '\n')
Bram Moolenaare0d37972016-07-15 22:36:01 +0200843 break;
844 if (state->growbufsiz == LINE_MAXLEN)
845 {
846 discard = TRUE;
847 break;
848 }
849
850 state->growbufsiz = 2 * state->growbufsiz < LINE_MAXLEN
851 ? 2 * state->growbufsiz : LINE_MAXLEN;
Bram Moolenaar18cebf42018-05-08 22:31:37 +0200852 if ((p = vim_realloc(state->growbuf, state->growbufsiz)) == NULL)
Bram Moolenaare0d37972016-07-15 22:36:01 +0200853 return QF_NOMEM;
Bram Moolenaar18cebf42018-05-08 22:31:37 +0200854 state->growbuf = p;
Bram Moolenaare0d37972016-07-15 22:36:01 +0200855 }
856
857 while (discard)
858 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200859 // The current line is longer than LINE_MAXLEN, continue
860 // reading but discard everything until EOL or EOF is
861 // reached.
Bram Moolenaare0d37972016-07-15 22:36:01 +0200862 if (fgets((char *)IObuff, IOSIZE, state->fd) == NULL
863 || (int)STRLEN(IObuff) < IOSIZE - 1
Bram Moolenaar59941cb2020-09-05 17:03:40 +0200864 || IObuff[IOSIZE - 2] == '\n')
Bram Moolenaare0d37972016-07-15 22:36:01 +0200865 break;
866 }
867
868 state->linebuf = state->growbuf;
869 state->linelen = growbuflen;
870 }
871 else
872 state->linebuf = IObuff;
873
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200874 // Convert a line if it contains a non-ASCII character.
Bram Moolenaarb6fa30c2017-03-29 14:19:25 +0200875 if (state->vc.vc_type != CONV_NONE && has_non_ascii(state->linebuf))
876 {
Bram Moolenaar2c7292d2017-03-05 17:43:31 +0100877 char_u *line;
878
879 line = string_convert(&state->vc, state->linebuf, &state->linelen);
880 if (line != NULL)
881 {
882 if (state->linelen < IOSIZE)
883 {
884 STRCPY(state->linebuf, line);
885 vim_free(line);
886 }
887 else
888 {
889 vim_free(state->growbuf);
890 state->linebuf = state->growbuf = line;
891 state->growbufsiz = state->linelen < LINE_MAXLEN
892 ? state->linelen : LINE_MAXLEN;
893 }
894 }
895 }
Bram Moolenaar2c7292d2017-03-05 17:43:31 +0100896
Bram Moolenaare0d37972016-07-15 22:36:01 +0200897 return QF_OK;
898}
899
900/*
901 * Get the next string from a file/buffer/list/string.
902 */
903 static int
904qf_get_nextline(qfstate_T *state)
905{
906 int status = QF_FAIL;
907
908 if (state->fd == NULL)
909 {
910 if (state->tv != NULL)
911 {
912 if (state->tv->v_type == VAR_STRING)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200913 // Get the next line from the supplied string
Bram Moolenaare0d37972016-07-15 22:36:01 +0200914 status = qf_get_next_str_line(state);
915 else if (state->tv->v_type == VAR_LIST)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200916 // Get the next line from the supplied list
Bram Moolenaare0d37972016-07-15 22:36:01 +0200917 status = qf_get_next_list_line(state);
918 }
919 else
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200920 // Get the next line from the supplied buffer
Bram Moolenaare0d37972016-07-15 22:36:01 +0200921 status = qf_get_next_buf_line(state);
922 }
923 else
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200924 // Get the next line from the supplied file
Bram Moolenaare0d37972016-07-15 22:36:01 +0200925 status = qf_get_next_file_line(state);
926
927 if (status != QF_OK)
928 return status;
929
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200930 // remove newline/CR from the line
Bram Moolenaare0d37972016-07-15 22:36:01 +0200931 if (state->linelen > 0 && state->linebuf[state->linelen - 1] == '\n')
Bram Moolenaar796aa9c2016-08-02 21:41:28 +0200932 {
Bram Moolenaare0d37972016-07-15 22:36:01 +0200933 state->linebuf[state->linelen - 1] = NUL;
934#ifdef USE_CRNL
Bram Moolenaar796aa9c2016-08-02 21:41:28 +0200935 if (state->linelen > 1 && state->linebuf[state->linelen - 2] == '\r')
936 state->linebuf[state->linelen - 2] = NUL;
Bram Moolenaare0d37972016-07-15 22:36:01 +0200937#endif
Bram Moolenaar796aa9c2016-08-02 21:41:28 +0200938 }
Bram Moolenaare0d37972016-07-15 22:36:01 +0200939
Bram Moolenaare0d37972016-07-15 22:36:01 +0200940 remove_bom(state->linebuf);
Bram Moolenaare0d37972016-07-15 22:36:01 +0200941
942 return QF_OK;
943}
944
Bram Moolenaare87e6dd2016-07-17 19:25:04 +0200945typedef struct {
946 char_u *namebuf;
Yegappan Lakshmananb7318002023-10-25 20:50:28 +0200947 int bnr;
Bram Moolenaard76ce852018-05-01 15:02:04 +0200948 char_u *module;
Bram Moolenaare87e6dd2016-07-17 19:25:04 +0200949 char_u *errmsg;
950 int errmsglen;
951 long lnum;
thinca6864efa2021-06-19 20:45:20 +0200952 long end_lnum;
Bram Moolenaare87e6dd2016-07-17 19:25:04 +0200953 int col;
thinca6864efa2021-06-19 20:45:20 +0200954 int end_col;
Bram Moolenaare87e6dd2016-07-17 19:25:04 +0200955 char_u use_viscol;
956 char_u *pattern;
957 int enr;
958 int type;
Tom Praschanca6ac992023-08-11 23:26:12 +0200959 typval_T *user_data;
Bram Moolenaare87e6dd2016-07-17 19:25:04 +0200960 int valid;
961} qffields_T;
962
963/*
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200964 * Parse the match for filename ('%f') pattern in regmatch.
965 * Return the matched value in "fields->namebuf".
966 */
967 static int
968qf_parse_fmt_f(regmatch_T *rmp, int midx, qffields_T *fields, int prefix)
969{
970 int c;
971
972 if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL)
973 return QF_FAIL;
974
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200975 // Expand ~/file and $HOME/file to full path.
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200976 c = *rmp->endp[midx];
977 *rmp->endp[midx] = NUL;
978 expand_env(rmp->startp[midx], fields->namebuf, CMDBUFFSIZE);
979 *rmp->endp[midx] = c;
980
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +0200981 // For separate filename patterns (%O, %P and %Q), the specified file
982 // should exist.
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200983 if (vim_strchr((char_u *)"OPQ", prefix) != NULL
984 && mch_getperm(fields->namebuf) == -1)
985 return QF_FAIL;
986
987 return QF_OK;
988}
989
990/*
zeertzjqb0221812023-10-26 23:15:44 +0200991 * Parse the match for buffer number ('%b') pattern in regmatch.
992 * Return the matched value in "fields->bnr".
Bram Moolenaarde3b3672018-08-07 21:54:41 +0200993 */
994 static int
Yegappan Lakshmananb7318002023-10-25 20:50:28 +0200995qf_parse_fmt_b(regmatch_T *rmp, int midx, qffields_T *fields)
996{
997 if (rmp->startp[midx] == NULL)
998 return QF_FAIL;
999 int bnr = (int)atol((char *)rmp->startp[midx]);
1000 if (buflist_findnr(bnr) == NULL)
1001 return QF_FAIL;
1002 fields->bnr = bnr;
1003 return QF_OK;
1004}
1005
1006/*
1007 * Parse the match for error number ('%n') pattern in regmatch.
1008 * Return the matched value in "fields->enr".
1009 */
1010 static int
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001011qf_parse_fmt_n(regmatch_T *rmp, int midx, qffields_T *fields)
1012{
1013 if (rmp->startp[midx] == NULL)
1014 return QF_FAIL;
1015 fields->enr = (int)atol((char *)rmp->startp[midx]);
1016 return QF_OK;
1017}
1018
1019/*
haya14busae023d492022-02-08 18:09:29 +00001020 * Parse the match for line number ('%l') pattern in regmatch.
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001021 * Return the matched value in "fields->lnum".
1022 */
1023 static int
1024qf_parse_fmt_l(regmatch_T *rmp, int midx, qffields_T *fields)
1025{
1026 if (rmp->startp[midx] == NULL)
1027 return QF_FAIL;
1028 fields->lnum = atol((char *)rmp->startp[midx]);
1029 return QF_OK;
1030}
1031
1032/*
haya14busae023d492022-02-08 18:09:29 +00001033 * Parse the match for end line number ('%e') pattern in regmatch.
1034 * Return the matched value in "fields->end_lnum".
1035 */
1036 static int
1037qf_parse_fmt_e(regmatch_T *rmp, int midx, qffields_T *fields)
1038{
1039 if (rmp->startp[midx] == NULL)
1040 return QF_FAIL;
1041 fields->end_lnum = atol((char *)rmp->startp[midx]);
1042 return QF_OK;
1043}
1044
1045/*
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001046 * Parse the match for column number ('%c') pattern in regmatch.
1047 * Return the matched value in "fields->col".
1048 */
1049 static int
1050qf_parse_fmt_c(regmatch_T *rmp, int midx, qffields_T *fields)
1051{
1052 if (rmp->startp[midx] == NULL)
1053 return QF_FAIL;
1054 fields->col = (int)atol((char *)rmp->startp[midx]);
1055 return QF_OK;
1056}
1057
1058/*
haya14busae023d492022-02-08 18:09:29 +00001059 * Parse the match for end column number ('%k') pattern in regmatch.
1060 * Return the matched value in "fields->end_col".
1061 */
1062 static int
1063qf_parse_fmt_k(regmatch_T *rmp, int midx, qffields_T *fields)
1064{
1065 if (rmp->startp[midx] == NULL)
1066 return QF_FAIL;
1067 fields->end_col = (int)atol((char *)rmp->startp[midx]);
1068 return QF_OK;
1069}
1070
1071/*
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001072 * Parse the match for error type ('%t') pattern in regmatch.
1073 * Return the matched value in "fields->type".
1074 */
1075 static int
1076qf_parse_fmt_t(regmatch_T *rmp, int midx, qffields_T *fields)
1077{
1078 if (rmp->startp[midx] == NULL)
1079 return QF_FAIL;
1080 fields->type = *rmp->startp[midx];
1081 return QF_OK;
1082}
1083
1084/*
Bram Moolenaarf4140482020-02-15 23:06:45 +01001085 * Copy a non-error line into the error string. Return the matched line in
1086 * "fields->errmsg".
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001087 */
1088 static int
Bram Moolenaarf4140482020-02-15 23:06:45 +01001089copy_nonerror_line(char_u *linebuf, int linelen, qffields_T *fields)
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001090{
1091 char_u *p;
1092
1093 if (linelen >= fields->errmsglen)
1094 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001095 // linelen + null terminator
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001096 if ((p = vim_realloc(fields->errmsg, linelen + 1)) == NULL)
1097 return QF_NOMEM;
1098 fields->errmsg = p;
1099 fields->errmsglen = linelen + 1;
1100 }
Bram Moolenaarf4140482020-02-15 23:06:45 +01001101 // copy whole line to error message
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001102 vim_strncpy(fields->errmsg, linebuf, linelen);
Bram Moolenaarf4140482020-02-15 23:06:45 +01001103
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001104 return QF_OK;
1105}
1106
1107/*
1108 * Parse the match for error message ('%m') pattern in regmatch.
1109 * Return the matched value in "fields->errmsg".
1110 */
1111 static int
1112qf_parse_fmt_m(regmatch_T *rmp, int midx, qffields_T *fields)
1113{
1114 char_u *p;
1115 int len;
1116
1117 if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL)
1118 return QF_FAIL;
1119 len = (int)(rmp->endp[midx] - rmp->startp[midx]);
1120 if (len >= fields->errmsglen)
1121 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001122 // len + null terminator
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001123 if ((p = vim_realloc(fields->errmsg, len + 1)) == NULL)
1124 return QF_NOMEM;
1125 fields->errmsg = p;
1126 fields->errmsglen = len + 1;
1127 }
1128 vim_strncpy(fields->errmsg, rmp->startp[midx], len);
1129 return QF_OK;
1130}
1131
1132/*
1133 * Parse the match for rest of a single-line file message ('%r') pattern.
1134 * Return the matched value in "tail".
1135 */
1136 static int
1137qf_parse_fmt_r(regmatch_T *rmp, int midx, char_u **tail)
1138{
1139 if (rmp->startp[midx] == NULL)
1140 return QF_FAIL;
1141 *tail = rmp->startp[midx];
1142 return QF_OK;
1143}
1144
1145/*
1146 * Parse the match for the pointer line ('%p') pattern in regmatch.
1147 * Return the matched value in "fields->col".
1148 */
1149 static int
1150qf_parse_fmt_p(regmatch_T *rmp, int midx, qffields_T *fields)
1151{
1152 char_u *match_ptr;
1153
1154 if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL)
1155 return QF_FAIL;
1156 fields->col = 0;
1157 for (match_ptr = rmp->startp[midx]; match_ptr != rmp->endp[midx];
1158 ++match_ptr)
1159 {
1160 ++fields->col;
1161 if (*match_ptr == TAB)
1162 {
1163 fields->col += 7;
1164 fields->col -= fields->col % 8;
1165 }
1166 }
1167 ++fields->col;
1168 fields->use_viscol = TRUE;
1169 return QF_OK;
1170}
1171
1172/*
1173 * Parse the match for the virtual column number ('%v') pattern in regmatch.
1174 * Return the matched value in "fields->col".
1175 */
1176 static int
1177qf_parse_fmt_v(regmatch_T *rmp, int midx, qffields_T *fields)
1178{
1179 if (rmp->startp[midx] == NULL)
1180 return QF_FAIL;
1181 fields->col = (int)atol((char *)rmp->startp[midx]);
1182 fields->use_viscol = TRUE;
1183 return QF_OK;
1184}
1185
1186/*
1187 * Parse the match for the search text ('%s') pattern in regmatch.
1188 * Return the matched value in "fields->pattern".
1189 */
1190 static int
1191qf_parse_fmt_s(regmatch_T *rmp, int midx, qffields_T *fields)
1192{
1193 int len;
1194
1195 if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL)
1196 return QF_FAIL;
1197 len = (int)(rmp->endp[midx] - rmp->startp[midx]);
1198 if (len > CMDBUFFSIZE - 5)
1199 len = CMDBUFFSIZE - 5;
1200 STRCPY(fields->pattern, "^\\V");
1201 STRNCAT(fields->pattern, rmp->startp[midx], len);
1202 fields->pattern[len + 3] = '\\';
1203 fields->pattern[len + 4] = '$';
1204 fields->pattern[len + 5] = NUL;
1205 return QF_OK;
1206}
1207
1208/*
1209 * Parse the match for the module ('%o') pattern in regmatch.
1210 * Return the matched value in "fields->module".
1211 */
1212 static int
1213qf_parse_fmt_o(regmatch_T *rmp, int midx, qffields_T *fields)
1214{
1215 int len;
1216
1217 if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL)
1218 return QF_FAIL;
1219 len = (int)(rmp->endp[midx] - rmp->startp[midx]);
1220 if (len > CMDBUFFSIZE)
1221 len = CMDBUFFSIZE;
1222 STRNCAT(fields->module, rmp->startp[midx], len);
1223 return QF_OK;
1224}
1225
1226/*
1227 * 'errorformat' format pattern parser functions.
1228 * The '%f' and '%r' formats are parsed differently from other formats.
1229 * See qf_parse_match() for details.
haya14busae023d492022-02-08 18:09:29 +00001230 * Keep in sync with fmt_pat[].
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001231 */
1232static int (*qf_parse_fmt[FMT_PATTERNS])(regmatch_T *, int, qffields_T *) =
1233{
haya14busae023d492022-02-08 18:09:29 +00001234 NULL, // %f
Yegappan Lakshmananb7318002023-10-25 20:50:28 +02001235 qf_parse_fmt_b,
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001236 qf_parse_fmt_n,
1237 qf_parse_fmt_l,
haya14busae023d492022-02-08 18:09:29 +00001238 qf_parse_fmt_e,
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001239 qf_parse_fmt_c,
haya14busae023d492022-02-08 18:09:29 +00001240 qf_parse_fmt_k,
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001241 qf_parse_fmt_t,
1242 qf_parse_fmt_m,
haya14busae023d492022-02-08 18:09:29 +00001243 NULL, // %r
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001244 qf_parse_fmt_p,
1245 qf_parse_fmt_v,
1246 qf_parse_fmt_s,
1247 qf_parse_fmt_o
1248};
1249
1250/*
1251 * Parse the error format pattern matches in "regmatch" and set the values in
1252 * "fields". fmt_ptr contains the 'efm' format specifiers/prefixes that have a
1253 * match. Returns QF_OK if all the matches are successfully parsed. On
1254 * failure, returns QF_FAIL or QF_NOMEM.
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001255 */
1256 static int
1257qf_parse_match(
1258 char_u *linebuf,
1259 int linelen,
1260 efm_T *fmt_ptr,
1261 regmatch_T *regmatch,
1262 qffields_T *fields,
1263 int qf_multiline,
1264 int qf_multiscan,
1265 char_u **tail)
1266{
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001267 int idx = fmt_ptr->prefix;
1268 int i;
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001269 int midx;
1270 int status;
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001271
1272 if ((idx == 'C' || idx == 'Z') && !qf_multiline)
1273 return QF_FAIL;
Bram Moolenaare9283662020-06-07 14:10:47 +02001274 if (vim_strchr((char_u *)"EWIN", idx) != NULL)
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001275 fields->type = idx;
1276 else
1277 fields->type = 0;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001278
1279 // Extract error message data from matched line.
1280 // We check for an actual submatch, because "\[" and "\]" in
1281 // the 'errorformat' may cause the wrong submatch to be used.
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001282 for (i = 0; i < FMT_PATTERNS; i++)
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001283 {
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001284 status = QF_OK;
1285 midx = (int)fmt_ptr->addr[i];
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001286 if (i == 0 && midx > 0) // %f
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001287 status = qf_parse_fmt_f(regmatch, midx, fields, idx);
haya14busae023d492022-02-08 18:09:29 +00001288 else if (i == FMT_PATTERN_M)
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001289 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001290 if (fmt_ptr->flags == '+' && !qf_multiscan) // %+
Bram Moolenaarf4140482020-02-15 23:06:45 +01001291 status = copy_nonerror_line(linebuf, linelen, fields);
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001292 else if (midx > 0) // %m
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001293 status = qf_parse_fmt_m(regmatch, midx, fields);
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001294 }
haya14busae023d492022-02-08 18:09:29 +00001295 else if (i == FMT_PATTERN_R && midx > 0) // %r
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001296 status = qf_parse_fmt_r(regmatch, midx, tail);
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001297 else if (midx > 0) // others
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001298 status = (qf_parse_fmt[i])(regmatch, midx, fields);
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001299
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001300 if (status != QF_OK)
1301 return status;
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001302 }
1303
1304 return QF_OK;
1305}
1306
1307/*
1308 * Parse an error line in 'linebuf' using a single error format string in
1309 * 'fmt_ptr->prog' and return the matching values in 'fields'.
1310 * Returns QF_OK if the efm format matches completely and the fields are
1311 * successfully copied. Otherwise returns QF_FAIL or QF_NOMEM.
1312 */
1313 static int
1314qf_parse_get_fields(
1315 char_u *linebuf,
1316 int linelen,
1317 efm_T *fmt_ptr,
1318 qffields_T *fields,
1319 int qf_multiline,
1320 int qf_multiscan,
1321 char_u **tail)
1322{
1323 regmatch_T regmatch;
1324 int status = QF_FAIL;
1325 int r;
1326
1327 if (qf_multiscan &&
1328 vim_strchr((char_u *)"OPQ", fmt_ptr->prefix) == NULL)
1329 return QF_FAIL;
1330
1331 fields->namebuf[0] = NUL;
Yegappan Lakshmananb7318002023-10-25 20:50:28 +02001332 fields->bnr = 0;
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001333 fields->module[0] = NUL;
1334 fields->pattern[0] = NUL;
1335 if (!qf_multiscan)
1336 fields->errmsg[0] = NUL;
1337 fields->lnum = 0;
thinca6864efa2021-06-19 20:45:20 +02001338 fields->end_lnum = 0;
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001339 fields->col = 0;
thinca6864efa2021-06-19 20:45:20 +02001340 fields->end_col = 0;
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001341 fields->use_viscol = FALSE;
1342 fields->enr = -1;
1343 fields->type = 0;
1344 *tail = NULL;
1345
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001346 // Always ignore case when looking for a matching error.
Bram Moolenaar8b62e312018-05-13 15:29:04 +02001347 regmatch.rm_ic = TRUE;
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001348 regmatch.regprog = fmt_ptr->prog;
1349 r = vim_regexec(&regmatch, linebuf, (colnr_T)0);
1350 fmt_ptr->prog = regmatch.regprog;
1351 if (r)
1352 status = qf_parse_match(linebuf, linelen, fmt_ptr, &regmatch,
1353 fields, qf_multiline, qf_multiscan, tail);
1354
1355 return status;
1356}
1357
1358/*
1359 * Parse directory error format prefixes (%D and %X).
1360 * Push and pop directories from the directory stack when scanning directory
1361 * names.
1362 */
1363 static int
1364qf_parse_dir_pfx(int idx, qffields_T *fields, qf_list_T *qfl)
1365{
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001366 if (idx == 'D') // enter directory
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001367 {
1368 if (*fields->namebuf == NUL)
1369 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001370 emsg(_(e_missing_or_empty_directory_name));
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001371 return QF_FAIL;
1372 }
1373 qfl->qf_directory =
1374 qf_push_dir(fields->namebuf, &qfl->qf_dir_stack, FALSE);
1375 if (qfl->qf_directory == NULL)
1376 return QF_FAIL;
1377 }
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001378 else if (idx == 'X') // leave directory
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001379 qfl->qf_directory = qf_pop_dir(&qfl->qf_dir_stack);
1380
1381 return QF_OK;
1382}
1383
1384/*
1385 * Parse global file name error format prefixes (%O, %P and %Q).
1386 */
1387 static int
1388qf_parse_file_pfx(
1389 int idx,
1390 qffields_T *fields,
1391 qf_list_T *qfl,
1392 char_u *tail)
1393{
1394 fields->valid = FALSE;
1395 if (*fields->namebuf == NUL || mch_getperm(fields->namebuf) >= 0)
1396 {
1397 if (*fields->namebuf && idx == 'P')
1398 qfl->qf_currfile =
1399 qf_push_dir(fields->namebuf, &qfl->qf_file_stack, TRUE);
1400 else if (idx == 'Q')
1401 qfl->qf_currfile = qf_pop_dir(&qfl->qf_file_stack);
1402 *fields->namebuf = NUL;
1403 if (tail && *tail)
1404 {
1405 STRMOVE(IObuff, skipwhite(tail));
1406 qfl->qf_multiscan = TRUE;
1407 return QF_MULTISCAN;
1408 }
1409 }
1410
1411 return QF_OK;
1412}
1413
1414/*
1415 * Parse a non-error line (a line which doesn't match any of the error
1416 * format in 'efm').
1417 */
1418 static int
1419qf_parse_line_nomatch(char_u *linebuf, int linelen, qffields_T *fields)
1420{
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001421 fields->namebuf[0] = NUL; // no match found, remove file name
1422 fields->lnum = 0; // don't jump to this line
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001423 fields->valid = FALSE;
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001424
Bram Moolenaarf4140482020-02-15 23:06:45 +01001425 return copy_nonerror_line(linebuf, linelen, fields);
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001426}
1427
1428/*
1429 * Parse multi-line error format prefixes (%C and %Z)
1430 */
1431 static int
1432qf_parse_multiline_pfx(
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001433 int idx,
1434 qf_list_T *qfl,
1435 qffields_T *fields)
1436{
1437 char_u *ptr;
1438 int len;
1439
1440 if (!qfl->qf_multiignore)
1441 {
1442 qfline_T *qfprev = qfl->qf_last;
1443
1444 if (qfprev == NULL)
1445 return QF_FAIL;
1446 if (*fields->errmsg && !qfl->qf_multiignore)
1447 {
1448 len = (int)STRLEN(qfprev->qf_text);
Yegappan Lakshmanan5a2d4a32022-02-26 10:31:32 +00001449 ptr = alloc_id(len + STRLEN(fields->errmsg) + 2,
1450 aid_qf_multiline_pfx);
1451 if (ptr == NULL)
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001452 return QF_FAIL;
1453 STRCPY(ptr, qfprev->qf_text);
1454 vim_free(qfprev->qf_text);
1455 qfprev->qf_text = ptr;
1456 *(ptr += len) = '\n';
1457 STRCPY(++ptr, fields->errmsg);
1458 }
1459 if (qfprev->qf_nr == -1)
1460 qfprev->qf_nr = fields->enr;
1461 if (vim_isprintc(fields->type) && !qfprev->qf_type)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001462 // only printable chars allowed
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001463 qfprev->qf_type = fields->type;
1464
1465 if (!qfprev->qf_lnum)
1466 qfprev->qf_lnum = fields->lnum;
haya14busae023d492022-02-08 18:09:29 +00001467 if (!qfprev->qf_end_lnum)
1468 qfprev->qf_end_lnum = fields->end_lnum;
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001469 if (!qfprev->qf_col)
Bram Moolenaarc95940c2020-10-20 14:59:12 +02001470 {
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001471 qfprev->qf_col = fields->col;
Bram Moolenaarc95940c2020-10-20 14:59:12 +02001472 qfprev->qf_viscol = fields->use_viscol;
1473 }
haya14busae023d492022-02-08 18:09:29 +00001474 if (!qfprev->qf_end_col)
1475 qfprev->qf_end_col = fields->end_col;
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001476 if (!qfprev->qf_fnum)
Bram Moolenaar0398e002019-03-21 21:12:49 +01001477 qfprev->qf_fnum = qf_get_fnum(qfl,
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001478 qfl->qf_directory,
1479 *fields->namebuf || qfl->qf_directory != NULL
1480 ? fields->namebuf
1481 : qfl->qf_currfile != NULL && fields->valid
1482 ? qfl->qf_currfile : 0);
1483 }
1484 if (idx == 'Z')
1485 qfl->qf_multiline = qfl->qf_multiignore = FALSE;
1486 line_breakcheck();
1487
1488 return QF_IGNORE_LINE;
1489}
1490
1491/*
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001492 * Parse a line and get the quickfix fields.
1493 * Return the QF_ status.
1494 */
1495 static int
1496qf_parse_line(
Bram Moolenaar0398e002019-03-21 21:12:49 +01001497 qf_list_T *qfl,
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001498 char_u *linebuf,
1499 int linelen,
1500 efm_T *fmt_first,
1501 qffields_T *fields)
1502{
1503 efm_T *fmt_ptr;
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001504 int idx = 0;
1505 char_u *tail = NULL;
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001506 int status;
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001507
Bram Moolenaare333e792018-04-08 13:27:39 +02001508restofline:
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001509 // If there was no %> item start at the first pattern
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001510 if (fmt_start == NULL)
1511 fmt_ptr = fmt_first;
1512 else
1513 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001514 // Otherwise start from the last used pattern
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001515 fmt_ptr = fmt_start;
1516 fmt_start = NULL;
1517 }
1518
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001519 // Try to match each part of 'errorformat' until we find a complete
1520 // match or no match.
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001521 fields->valid = TRUE;
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001522 for ( ; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next)
1523 {
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001524 idx = fmt_ptr->prefix;
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001525 status = qf_parse_get_fields(linebuf, linelen, fmt_ptr, fields,
1526 qfl->qf_multiline, qfl->qf_multiscan, &tail);
1527 if (status == QF_NOMEM)
1528 return status;
1529 if (status == QF_OK)
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001530 break;
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001531 }
Bram Moolenaara7df8c72017-07-19 13:23:06 +02001532 qfl->qf_multiscan = FALSE;
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001533
1534 if (fmt_ptr == NULL || idx == 'D' || idx == 'X')
1535 {
1536 if (fmt_ptr != NULL)
1537 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001538 // 'D' and 'X' directory specifiers
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001539 status = qf_parse_dir_pfx(idx, fields, qfl);
1540 if (status != QF_OK)
1541 return status;
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001542 }
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001543
1544 status = qf_parse_line_nomatch(linebuf, linelen, fields);
1545 if (status != QF_OK)
1546 return status;
1547
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001548 if (fmt_ptr == NULL)
Bram Moolenaara7df8c72017-07-19 13:23:06 +02001549 qfl->qf_multiline = qfl->qf_multiignore = FALSE;
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001550 }
1551 else if (fmt_ptr != NULL)
1552 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001553 // honor %> item
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001554 if (fmt_ptr->conthere)
1555 fmt_start = fmt_ptr;
1556
Bram Moolenaare9283662020-06-07 14:10:47 +02001557 if (vim_strchr((char_u *)"AEWIN", idx) != NULL)
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001558 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001559 qfl->qf_multiline = TRUE; // start of a multi-line message
1560 qfl->qf_multiignore = FALSE;// reset continuation
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001561 }
1562 else if (vim_strchr((char_u *)"CZ", idx) != NULL)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001563 { // continuation of multi-line msg
Bram Moolenaar0398e002019-03-21 21:12:49 +01001564 status = qf_parse_multiline_pfx(idx, qfl, fields);
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001565 if (status != QF_OK)
1566 return status;
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001567 }
1568 else if (vim_strchr((char_u *)"OPQ", idx) != NULL)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001569 { // global file names
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001570 status = qf_parse_file_pfx(idx, fields, qfl, tail);
1571 if (status == QF_MULTISCAN)
1572 goto restofline;
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001573 }
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001574 if (fmt_ptr->flags == '-') // generally exclude this line
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001575 {
Bram Moolenaara7df8c72017-07-19 13:23:06 +02001576 if (qfl->qf_multiline)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001577 // also exclude continuation lines
Bram Moolenaara7df8c72017-07-19 13:23:06 +02001578 qfl->qf_multiignore = TRUE;
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001579 return QF_IGNORE_LINE;
1580 }
1581 }
1582
1583 return QF_OK;
1584}
1585
Bram Moolenaar6be8c8e2016-04-30 13:17:09 +02001586/*
Bram Moolenaar019dfe62018-10-07 14:38:49 +02001587 * Returns TRUE if the specified quickfix/location stack is empty
1588 */
1589 static int
1590qf_stack_empty(qf_info_T *qi)
1591{
1592 return qi == NULL || qi->qf_listcount <= 0;
1593}
1594
1595/*
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001596 * Returns TRUE if the specified quickfix/location list is empty.
1597 */
1598 static int
Bram Moolenaar0398e002019-03-21 21:12:49 +01001599qf_list_empty(qf_list_T *qfl)
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001600{
Bram Moolenaar0398e002019-03-21 21:12:49 +01001601 return qfl == NULL || qfl->qf_count <= 0;
1602}
1603
1604/*
Bram Moolenaar3ff33112019-05-03 21:56:35 +02001605 * Returns TRUE if the specified quickfix/location list is not empty and
1606 * has valid entries.
1607 */
1608 static int
1609qf_list_has_valid_entries(qf_list_T *qfl)
1610{
1611 return !qf_list_empty(qfl) && !qfl->qf_nonevalid;
1612}
1613
1614/*
Bram Moolenaar0398e002019-03-21 21:12:49 +01001615 * Return a pointer to a list in the specified quickfix stack
1616 */
1617 static qf_list_T *
1618qf_get_list(qf_info_T *qi, int idx)
1619{
1620 return &qi->qf_lists[idx];
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001621}
1622
Bram Moolenaarde3b3672018-08-07 21:54:41 +02001623/*
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02001624 * Allocate the fields used for parsing lines and populating a quickfix list.
1625 */
1626 static int
1627qf_alloc_fields(qffields_T *pfields)
1628{
1629 pfields->namebuf = alloc_id(CMDBUFFSIZE + 1, aid_qf_namebuf);
1630 pfields->module = alloc_id(CMDBUFFSIZE + 1, aid_qf_module);
1631 pfields->errmsglen = CMDBUFFSIZE + 1;
1632 pfields->errmsg = alloc_id(pfields->errmsglen, aid_qf_errmsg);
1633 pfields->pattern = alloc_id(CMDBUFFSIZE + 1, aid_qf_pattern);
1634 if (pfields->namebuf == NULL || pfields->errmsg == NULL
1635 || pfields->pattern == NULL || pfields->module == NULL)
1636 return FAIL;
1637
1638 return OK;
1639}
1640
1641/*
1642 * Free the fields used for parsing lines and populating a quickfix list.
1643 */
1644 static void
1645qf_free_fields(qffields_T *pfields)
1646{
1647 vim_free(pfields->namebuf);
1648 vim_free(pfields->module);
1649 vim_free(pfields->errmsg);
1650 vim_free(pfields->pattern);
1651}
1652
1653/*
1654 * Setup the state information used for parsing lines and populating a
1655 * quickfix list.
1656 */
1657 static int
1658qf_setup_state(
1659 qfstate_T *pstate,
1660 char_u *enc,
1661 char_u *efile,
1662 typval_T *tv,
1663 buf_T *buf,
1664 linenr_T lnumfirst,
1665 linenr_T lnumlast)
1666{
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02001667 pstate->vc.vc_type = CONV_NONE;
1668 if (enc != NULL && *enc != NUL)
1669 convert_setup(&pstate->vc, enc, p_enc);
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02001670
1671 if (efile != NULL && (pstate->fd = mch_fopen((char *)efile, "r")) == NULL)
1672 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02001673 semsg(_(e_cant_open_errorfile_str), efile);
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02001674 return FAIL;
1675 }
1676
1677 if (tv != NULL)
1678 {
1679 if (tv->v_type == VAR_STRING)
1680 pstate->p_str = tv->vval.v_string;
1681 else if (tv->v_type == VAR_LIST)
1682 pstate->p_li = tv->vval.v_list->lv_first;
1683 pstate->tv = tv;
1684 }
1685 pstate->buf = buf;
1686 pstate->buflnum = lnumfirst;
1687 pstate->lnumlast = lnumlast;
1688
1689 return OK;
1690}
1691
1692/*
1693 * Cleanup the state information used for parsing lines and populating a
1694 * quickfix list.
1695 */
1696 static void
1697qf_cleanup_state(qfstate_T *pstate)
1698{
1699 if (pstate->fd != NULL)
1700 fclose(pstate->fd);
1701
1702 vim_free(pstate->growbuf);
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02001703 if (pstate->vc.vc_type != CONV_NONE)
1704 convert_setup(&pstate->vc, NULL, NULL);
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02001705}
1706
1707/*
Bram Moolenaar95946f12019-03-31 15:31:59 +02001708 * Process the next line from a file/buffer/list/string and add it
1709 * to the quickfix list 'qfl'.
1710 */
1711 static int
1712qf_init_process_nextline(
1713 qf_list_T *qfl,
1714 efm_T *fmt_first,
1715 qfstate_T *state,
1716 qffields_T *fields)
1717{
1718 int status;
1719
1720 // Get the next line from a file/buffer/list/string
1721 status = qf_get_nextline(state);
1722 if (status != QF_OK)
1723 return status;
1724
1725 status = qf_parse_line(qfl, state->linebuf, state->linelen,
1726 fmt_first, fields);
1727 if (status != QF_OK)
1728 return status;
1729
1730 return qf_add_entry(qfl,
1731 qfl->qf_directory,
1732 (*fields->namebuf || qfl->qf_directory != NULL)
1733 ? fields->namebuf
1734 : ((qfl->qf_currfile != NULL && fields->valid)
1735 ? qfl->qf_currfile : (char_u *)NULL),
1736 fields->module,
Yegappan Lakshmananb7318002023-10-25 20:50:28 +02001737 fields->bnr,
Bram Moolenaar95946f12019-03-31 15:31:59 +02001738 fields->errmsg,
1739 fields->lnum,
thinca6864efa2021-06-19 20:45:20 +02001740 fields->end_lnum,
Bram Moolenaar95946f12019-03-31 15:31:59 +02001741 fields->col,
thinca6864efa2021-06-19 20:45:20 +02001742 fields->end_col,
Bram Moolenaar95946f12019-03-31 15:31:59 +02001743 fields->use_viscol,
1744 fields->pattern,
1745 fields->enr,
1746 fields->type,
Tom Praschanca6ac992023-08-11 23:26:12 +02001747 fields->user_data,
Bram Moolenaar95946f12019-03-31 15:31:59 +02001748 fields->valid);
1749}
1750
1751/*
Bram Moolenaar86b68352004-12-27 21:59:20 +00001752 * Read the errorfile "efile" into memory, line by line, building the error
1753 * list.
Bram Moolenaar864293a2016-06-02 13:40:04 +02001754 * Alternative: when "efile" is NULL read errors from buffer "buf".
1755 * Alternative: when "tv" is not NULL get errors from the string or list.
Bram Moolenaar86b68352004-12-27 21:59:20 +00001756 * Always use 'errorformat' from "buf" if there is a local value.
Bram Moolenaar7fd73202010-07-25 16:58:46 +02001757 * Then "lnumfirst" and "lnumlast" specify the range of lines to use.
1758 * Set the title of the list to "qf_title".
Bram Moolenaar86b68352004-12-27 21:59:20 +00001759 * Return -1 for error, number of errors for success.
1760 */
1761 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01001762qf_init_ext(
1763 qf_info_T *qi,
Bram Moolenaara7df8c72017-07-19 13:23:06 +02001764 int qf_idx,
Bram Moolenaar05540972016-01-30 20:31:25 +01001765 char_u *efile,
1766 buf_T *buf,
1767 typval_T *tv,
1768 char_u *errorformat,
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001769 int newlist, // TRUE: start a new error list
1770 linenr_T lnumfirst, // first line number to use
1771 linenr_T lnumlast, // last line number to use
Bram Moolenaar2c7292d2017-03-05 17:43:31 +01001772 char_u *qf_title,
1773 char_u *enc)
Bram Moolenaar86b68352004-12-27 21:59:20 +00001774{
Bram Moolenaara7df8c72017-07-19 13:23:06 +02001775 qf_list_T *qfl;
Bram Moolenaar2c7292d2017-03-05 17:43:31 +01001776 qfstate_T state;
1777 qffields_T fields;
Bram Moolenaar864293a2016-06-02 13:40:04 +02001778 qfline_T *old_last = NULL;
Bram Moolenaar86f100dc2017-06-28 21:26:27 +02001779 int adding = FALSE;
Bram Moolenaar361c8f02016-07-02 15:41:47 +02001780 static efm_T *fmt_first = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001781 char_u *efm;
Bram Moolenaar361c8f02016-07-02 15:41:47 +02001782 static char_u *last_efm = NULL;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001783 int retval = -1; // default: return error flag
Bram Moolenaare0d37972016-07-15 22:36:01 +02001784 int status;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001785
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001786 // Do not used the cached buffer, it may have been wiped out.
Bram Moolenaard23a8232018-02-10 18:45:26 +01001787 VIM_CLEAR(qf_last_bufname);
Bram Moolenaar6dd4a532017-05-28 07:56:36 +02001788
Bram Moolenaara80faa82020-04-12 19:37:17 +02001789 CLEAR_FIELD(state);
1790 CLEAR_FIELD(fields);
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02001791 if ((qf_alloc_fields(&fields) == FAIL) ||
1792 (qf_setup_state(&state, enc, efile, tv, buf,
1793 lnumfirst, lnumlast) == FAIL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001794 goto qf_init_end;
1795
Bram Moolenaara7df8c72017-07-19 13:23:06 +02001796 if (newlist || qf_idx == qi->qf_listcount)
1797 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001798 // make place for a new list
Bram Moolenaar94116152012-11-28 17:41:59 +01001799 qf_new_list(qi, qf_title);
Bram Moolenaara7df8c72017-07-19 13:23:06 +02001800 qf_idx = qi->qf_curlist;
Bram Moolenaar9afe5e92019-03-22 14:16:06 +01001801 qfl = qf_get_list(qi, qf_idx);
Bram Moolenaara7df8c72017-07-19 13:23:06 +02001802 }
Bram Moolenaar86f100dc2017-06-28 21:26:27 +02001803 else
Bram Moolenaar864293a2016-06-02 13:40:04 +02001804 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001805 // Adding to existing list, use last entry.
Bram Moolenaar2b946c92016-11-12 18:14:44 +01001806 adding = TRUE;
Bram Moolenaar9afe5e92019-03-22 14:16:06 +01001807 qfl = qf_get_list(qi, qf_idx);
1808 if (!qf_list_empty(qfl))
1809 old_last = qfl->qf_last;
Bram Moolenaar86f100dc2017-06-28 21:26:27 +02001810 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001812 // Use the local value of 'errorformat' if it's set.
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00001813 if (errorformat == p_efm && tv == NULL && *buf->b_p_efm != NUL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00001814 efm = buf->b_p_efm;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001815 else
1816 efm = errorformat;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001817
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001818 // If the errorformat didn't change between calls, then reuse the
1819 // previously parsed values.
Bram Moolenaar361c8f02016-07-02 15:41:47 +02001820 if (last_efm == NULL || (STRCMP(last_efm, efm) != 0))
1821 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001822 // free the previously parsed data
Bram Moolenaard23a8232018-02-10 18:45:26 +01001823 VIM_CLEAR(last_efm);
Bram Moolenaar361c8f02016-07-02 15:41:47 +02001824 free_efm_list(&fmt_first);
1825
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001826 // parse the current 'efm'
Bram Moolenaar361c8f02016-07-02 15:41:47 +02001827 fmt_first = parse_efm_option(efm);
1828 if (fmt_first != NULL)
1829 last_efm = vim_strsave(efm);
1830 }
1831
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001832 if (fmt_first == NULL) // nothing found
Bram Moolenaar071d4272004-06-13 20:20:40 +00001833 goto error2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001834
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001835 // got_int is reset here, because it was probably set when killing the
1836 // ":make" command, but we still want to read the errorfile then.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001837 got_int = FALSE;
1838
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001839 // Read the lines in the error file one by one.
1840 // Try to recognize one of the error formats in each line.
Bram Moolenaar86b68352004-12-27 21:59:20 +00001841 while (!got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001842 {
Bram Moolenaar95946f12019-03-31 15:31:59 +02001843 status = qf_init_process_nextline(qfl, fmt_first, &state, &fields);
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001844 if (status == QF_NOMEM) // memory alloc failure
Bram Moolenaare0d37972016-07-15 22:36:01 +02001845 goto qf_init_end;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001846 if (status == QF_END_OF_INPUT) // end of input
Bram Moolenaare0d37972016-07-15 22:36:01 +02001847 break;
Bram Moolenaare87e6dd2016-07-17 19:25:04 +02001848 if (status == QF_FAIL)
1849 goto error2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001850
Bram Moolenaar071d4272004-06-13 20:20:40 +00001851 line_breakcheck();
1852 }
Bram Moolenaare0d37972016-07-15 22:36:01 +02001853 if (state.fd == NULL || !ferror(state.fd))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001854 {
Bram Moolenaara7df8c72017-07-19 13:23:06 +02001855 if (qfl->qf_index == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001856 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001857 // no valid entry found
Bram Moolenaara7df8c72017-07-19 13:23:06 +02001858 qfl->qf_ptr = qfl->qf_start;
1859 qfl->qf_index = 1;
1860 qfl->qf_nonevalid = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001861 }
1862 else
1863 {
Bram Moolenaara7df8c72017-07-19 13:23:06 +02001864 qfl->qf_nonevalid = FALSE;
1865 if (qfl->qf_ptr == NULL)
1866 qfl->qf_ptr = qfl->qf_start;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001867 }
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001868 // return number of matches
Bram Moolenaara7df8c72017-07-19 13:23:06 +02001869 retval = qfl->qf_count;
Bram Moolenaarbcf77722016-06-28 21:11:32 +02001870 goto qf_init_end;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001871 }
Bram Moolenaard8e44472021-07-21 22:20:33 +02001872 emsg(_(e_error_while_reading_errorfile));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001873error2:
Bram Moolenaar2b946c92016-11-12 18:14:44 +01001874 if (!adding)
1875 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001876 // Error when creating a new list. Free the new list
Bram Moolenaar108e7b42018-10-11 17:39:12 +02001877 qf_free(qfl);
Bram Moolenaar2b946c92016-11-12 18:14:44 +01001878 qi->qf_listcount--;
1879 if (qi->qf_curlist > 0)
1880 --qi->qf_curlist;
1881 }
Bram Moolenaarbcf77722016-06-28 21:11:32 +02001882qf_init_end:
Bram Moolenaara7df8c72017-07-19 13:23:06 +02001883 if (qf_idx == qi->qf_curlist)
1884 qf_update_buffer(qi, old_last);
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02001885 qf_cleanup_state(&state);
1886 qf_free_fields(&fields);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001887
1888 return retval;
1889}
1890
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001891/*
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02001892 * Read the errorfile "efile" into memory, line by line, building the error
1893 * list. Set the error list's title to qf_title.
1894 * Return -1 for error, number of errors for success.
1895 */
1896 int
1897qf_init(win_T *wp,
1898 char_u *efile,
1899 char_u *errorformat,
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001900 int newlist, // TRUE: start a new error list
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02001901 char_u *qf_title,
1902 char_u *enc)
1903{
1904 qf_info_T *qi = &ql_info;
1905
1906 if (wp != NULL)
1907 {
1908 qi = ll_get_or_alloc_list(wp);
1909 if (qi == NULL)
1910 return FAIL;
1911 }
1912
1913 return qf_init_ext(qi, qi->qf_curlist, efile, curbuf, NULL, errorformat,
1914 newlist, (linenr_T)0, (linenr_T)0, qf_title, enc);
1915}
1916
1917/*
Bram Moolenaar18cebf42018-05-08 22:31:37 +02001918 * Set the title of the specified quickfix list. Frees the previous title.
1919 * Prepends ':' to the title.
1920 */
Bram Moolenaarfb604092014-07-23 15:55:00 +02001921 static void
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02001922qf_store_title(qf_list_T *qfl, char_u *title)
Bram Moolenaarfb604092014-07-23 15:55:00 +02001923{
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02001924 VIM_CLEAR(qfl->qf_title);
Bram Moolenaar6a8958d2017-06-22 21:33:20 +02001925
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00001926 if (title == NULL)
1927 return;
Bram Moolenaarfb604092014-07-23 15:55:00 +02001928
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00001929 char_u *p = alloc_id(STRLEN(title) + 2, aid_qf_title);
1930
1931 qfl->qf_title = p;
1932 if (p != NULL)
1933 STRCPY(p, title);
Bram Moolenaarfb604092014-07-23 15:55:00 +02001934}
1935
Bram Moolenaar071d4272004-06-13 20:20:40 +00001936/*
Bram Moolenaar8b62e312018-05-13 15:29:04 +02001937 * The title of a quickfix/location list is set, by default, to the command
1938 * that created the quickfix list with the ":" prefix.
1939 * Create a quickfix list title string by prepending ":" to a user command.
1940 * Returns a pointer to a static buffer with the title.
1941 */
1942 static char_u *
1943qf_cmdtitle(char_u *cmd)
1944{
1945 static char_u qftitle_str[IOSIZE];
1946
1947 vim_snprintf((char *)qftitle_str, IOSIZE, ":%s", (char *)cmd);
1948 return qftitle_str;
1949}
1950
1951/*
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01001952 * Return a pointer to the current list in the specified quickfix stack
1953 */
1954 static qf_list_T *
1955qf_get_curlist(qf_info_T *qi)
1956{
Bram Moolenaar0398e002019-03-21 21:12:49 +01001957 return qf_get_list(qi, qi->qf_curlist);
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01001958}
1959
1960/*
Bram Moolenaar55b69262017-08-13 13:42:01 +02001961 * Prepare for adding a new quickfix list. If the current list is in the
1962 * middle of the stack, then all the following lists are freed and then
1963 * the new list is added.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001964 */
1965 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01001966qf_new_list(qf_info_T *qi, char_u *qf_title)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001967{
1968 int i;
Bram Moolenaar108e7b42018-10-11 17:39:12 +02001969 qf_list_T *qfl;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001970
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001971 // If the current entry is not the last entry, delete entries beyond
1972 // the current entry. This makes it possible to browse in a tree-like
Bram Moolenaar32aa1022019-11-02 22:54:41 +01001973 // way with ":grep".
Bram Moolenaard12f5c12006-01-25 22:10:52 +00001974 while (qi->qf_listcount > qi->qf_curlist + 1)
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02001975 qf_free(&qi->qf_lists[--qi->qf_listcount]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001976
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02001977 // When the stack is full, remove to oldest entry
1978 // Otherwise, add a new entry.
Bram Moolenaard12f5c12006-01-25 22:10:52 +00001979 if (qi->qf_listcount == LISTCOUNT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001980 {
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02001981 qf_free(&qi->qf_lists[0]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001982 for (i = 1; i < LISTCOUNT; ++i)
Bram Moolenaard12f5c12006-01-25 22:10:52 +00001983 qi->qf_lists[i - 1] = qi->qf_lists[i];
1984 qi->qf_curlist = LISTCOUNT - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001985 }
1986 else
Bram Moolenaard12f5c12006-01-25 22:10:52 +00001987 qi->qf_curlist = qi->qf_listcount++;
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01001988 qfl = qf_get_curlist(qi);
Bram Moolenaara80faa82020-04-12 19:37:17 +02001989 CLEAR_POINTER(qfl);
Bram Moolenaar108e7b42018-10-11 17:39:12 +02001990 qf_store_title(qfl, qf_title);
Bram Moolenaar2d67d302018-11-16 18:46:02 +01001991 qfl->qfl_type = qi->qfl_type;
Bram Moolenaar108e7b42018-10-11 17:39:12 +02001992 qfl->qf_id = ++last_qf_id;
Tom Praschanca6ac992023-08-11 23:26:12 +02001993 qfl->qf_has_user_data = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001994}
1995
Bram Moolenaard12f5c12006-01-25 22:10:52 +00001996/*
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02001997 * Queue location list stack delete request.
1998 */
1999 static void
2000locstack_queue_delreq(qf_info_T *qi)
2001{
2002 qf_delq_T *q;
2003
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002004 q = ALLOC_ONE(qf_delq_T);
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00002005 if (q == NULL)
2006 return;
2007
2008 q->qi = qi;
2009 q->next = qf_delq_head;
2010 qf_delq_head = q;
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02002011}
2012
2013/*
Bram Moolenaaree8188f2019-02-05 21:23:04 +01002014 * Return the global quickfix stack window buffer number.
2015 */
2016 int
2017qf_stack_get_bufnr(void)
2018{
2019 return ql_info.qf_bufnr;
2020}
2021
2022/*
2023 * Wipe the quickfix window buffer (if present) for the specified
2024 * quickfix/location list.
2025 */
2026 static void
2027wipe_qf_buffer(qf_info_T *qi)
2028{
2029 buf_T *qfbuf;
2030
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00002031 if (qi->qf_bufnr == INVALID_QFBUFNR)
2032 return;
2033
2034 qfbuf = buflist_findnr(qi->qf_bufnr);
2035 if (qfbuf != NULL && qfbuf->b_nwindows == 0)
Bram Moolenaaree8188f2019-02-05 21:23:04 +01002036 {
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00002037 // If the quickfix buffer is not loaded in any window, then
2038 // wipe the buffer.
2039 close_buffer(NULL, qfbuf, DOBUF_WIPE, FALSE, FALSE);
2040 qi->qf_bufnr = INVALID_QFBUFNR;
Bram Moolenaaree8188f2019-02-05 21:23:04 +01002041 }
2042}
2043
2044/*
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02002045 * Free a location list stack
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002046 */
2047 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01002048ll_free_all(qf_info_T **pqi)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00002049{
2050 int i;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002051 qf_info_T *qi;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00002052
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002053 qi = *pqi;
2054 if (qi == NULL)
2055 return;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002056 *pqi = NULL; // Remove reference to this list
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002057
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01002058 // If the location list is still in use, then queue the delete request
2059 // to be processed later.
2060 if (quickfix_busy > 0)
2061 {
2062 locstack_queue_delreq(qi);
2063 return;
2064 }
2065
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002066 qi->qf_refcount--;
2067 if (qi->qf_refcount < 1)
2068 {
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02002069 // No references to this location list.
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01002070 // If the quickfix window buffer is loaded, then wipe it
2071 wipe_qf_buffer(qi);
Bram Moolenaaree8188f2019-02-05 21:23:04 +01002072
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01002073 for (i = 0; i < qi->qf_listcount; ++i)
Bram Moolenaar0398e002019-03-21 21:12:49 +01002074 qf_free(qf_get_list(qi, i));
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01002075 vim_free(qi);
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002076 }
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00002077}
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002078
Bram Moolenaar18cebf42018-05-08 22:31:37 +02002079/*
2080 * Free all the quickfix/location lists in the stack.
2081 */
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002082 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002083qf_free_all(win_T *wp)
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002084{
2085 int i;
2086 qf_info_T *qi = &ql_info;
2087
2088 if (wp != NULL)
2089 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002090 // location list
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002091 ll_free_all(&wp->w_llist);
2092 ll_free_all(&wp->w_llist_ref);
2093 }
2094 else
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002095 // quickfix list
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002096 for (i = 0; i < qi->qf_listcount; ++i)
Bram Moolenaar0398e002019-03-21 21:12:49 +01002097 qf_free(qf_get_list(qi, i));
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002098}
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00002099
Bram Moolenaar071d4272004-06-13 20:20:40 +00002100/*
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02002101 * Delay freeing of location list stacks when the quickfix code is running.
2102 * Used to avoid problems with autocmds freeing location list stacks when the
2103 * quickfix code is still referencing the stack.
2104 * Must always call decr_quickfix_busy() exactly once after this.
2105 */
2106 static void
2107incr_quickfix_busy(void)
2108{
2109 quickfix_busy++;
2110}
2111
2112/*
2113 * Safe to free location list stacks. Process any delayed delete requests.
2114 */
2115 static void
2116decr_quickfix_busy(void)
2117{
2118 if (--quickfix_busy == 0)
2119 {
2120 // No longer referencing the location lists. Process all the pending
2121 // delete requests.
2122 while (qf_delq_head != NULL)
2123 {
2124 qf_delq_T *q = qf_delq_head;
2125
2126 qf_delq_head = q->next;
2127 ll_free_all(&q->qi);
2128 vim_free(q);
2129 }
2130 }
2131#ifdef ABORT_ON_INTERNAL_ERROR
2132 if (quickfix_busy < 0)
2133 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002134 emsg("quickfix_busy has become negative");
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02002135 abort();
2136 }
2137#endif
2138}
2139
2140#if defined(EXITFREE) || defined(PROTO)
2141 void
2142check_quickfix_busy(void)
2143{
2144 if (quickfix_busy != 0)
2145 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002146 semsg("quickfix_busy not zero on exit: %ld", (long)quickfix_busy);
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02002147# ifdef ABORT_ON_INTERNAL_ERROR
2148 abort();
2149# endif
2150 }
2151}
2152#endif
2153
2154/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002155 * Add an entry to the end of the list of errors.
Yegappan Lakshmanan9c9be052022-02-24 12:33:17 +00002156 * Returns QF_OK on success or QF_FAIL on a memory allocation failure.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002157 */
2158 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01002159qf_add_entry(
Bram Moolenaar0398e002019-03-21 21:12:49 +01002160 qf_list_T *qfl, // quickfix list entry
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002161 char_u *dir, // optional directory name
2162 char_u *fname, // file name or NULL
2163 char_u *module, // module name or NULL
2164 int bufnum, // buffer number or zero
2165 char_u *mesg, // message
2166 long lnum, // line number
thinca6864efa2021-06-19 20:45:20 +02002167 long end_lnum, // line number for end
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002168 int col, // column
thinca6864efa2021-06-19 20:45:20 +02002169 int end_col, // column for end
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002170 int vis_col, // using visual column
2171 char_u *pattern, // search pattern
2172 int nr, // error number
2173 int type, // type character
Tom Praschanca6ac992023-08-11 23:26:12 +02002174 typval_T *user_data, // custom user data or NULL
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002175 int valid) // valid entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00002176{
Austin Chang29822992024-10-03 10:50:05 +02002177 buf_T *buf;
Bram Moolenaar68b76a62005-03-25 21:53:48 +00002178 qfline_T *qfp;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002179 qfline_T **lastp; // pointer to qf_last or NULL
Austin Chang29822992024-10-03 10:50:05 +02002180 char_u *fullname = NULL;
2181 char_u *p = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002182
Yegappan Lakshmanan5a2d4a32022-02-26 10:31:32 +00002183 if ((qfp = ALLOC_ONE_ID(qfline_T, aid_qf_qfline)) == NULL)
Bram Moolenaar95946f12019-03-31 15:31:59 +02002184 return QF_FAIL;
Bram Moolenaar48b66fb2007-02-04 01:58:18 +00002185 if (bufnum != 0)
Bram Moolenaar2f095a42016-06-03 19:05:49 +02002186 {
Austin Chang29822992024-10-03 10:50:05 +02002187 buf = buflist_findnr(bufnum);
Bram Moolenaar2f095a42016-06-03 19:05:49 +02002188
Bram Moolenaar48b66fb2007-02-04 01:58:18 +00002189 qfp->qf_fnum = bufnum;
Bram Moolenaar2f095a42016-06-03 19:05:49 +02002190 if (buf != NULL)
Bram Moolenaarc1542742016-07-20 21:44:37 +02002191 buf->b_has_qf_entry |=
Bram Moolenaar2d67d302018-11-16 18:46:02 +01002192 IS_QF_LIST(qfl) ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY;
Bram Moolenaar2f095a42016-06-03 19:05:49 +02002193 }
Bram Moolenaar48b66fb2007-02-04 01:58:18 +00002194 else
Austin Chang29822992024-10-03 10:50:05 +02002195 {
Bram Moolenaar0398e002019-03-21 21:12:49 +01002196 qfp->qf_fnum = qf_get_fnum(qfl, dir, fname);
Austin Chang29822992024-10-03 10:50:05 +02002197 buf = buflist_findnr(qfp->qf_fnum);
2198 }
2199 if (fname != NULL)
2200 fullname = fix_fname(fname);
2201 qfp->qf_fname = NULL;
2202 if (buf != NULL &&
2203 buf->b_ffname != NULL && fullname != NULL)
2204 {
2205 if (fnamecmp(fullname, buf->b_ffname) != 0)
2206 {
2207 p = shorten_fname1(fullname);
2208 if (p != NULL)
2209 qfp->qf_fname = vim_strsave(p);
2210 }
2211 }
2212 vim_free(fullname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002213 if ((qfp->qf_text = vim_strsave(mesg)) == NULL)
2214 {
2215 vim_free(qfp);
Bram Moolenaar95946f12019-03-31 15:31:59 +02002216 return QF_FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002217 }
2218 qfp->qf_lnum = lnum;
thinca6864efa2021-06-19 20:45:20 +02002219 qfp->qf_end_lnum = end_lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002220 qfp->qf_col = col;
thinca6864efa2021-06-19 20:45:20 +02002221 qfp->qf_end_col = end_col;
Bram Moolenaar05159a02005-02-26 23:04:13 +00002222 qfp->qf_viscol = vis_col;
Tom Praschanca6ac992023-08-11 23:26:12 +02002223 if (user_data == NULL || user_data->v_type == VAR_UNKNOWN)
Yegappan Lakshmanand4e4ecb2023-08-27 18:35:45 +02002224 qfp->qf_user_data.v_type = VAR_UNKNOWN;
Tom Praschanca6ac992023-08-11 23:26:12 +02002225 else
2226 {
2227 copy_tv(user_data, &qfp->qf_user_data);
2228 qfl->qf_has_user_data = TRUE;
2229 }
Bram Moolenaar68b76a62005-03-25 21:53:48 +00002230 if (pattern == NULL || *pattern == NUL)
2231 qfp->qf_pattern = NULL;
2232 else if ((qfp->qf_pattern = vim_strsave(pattern)) == NULL)
2233 {
2234 vim_free(qfp->qf_text);
2235 vim_free(qfp);
Bram Moolenaar95946f12019-03-31 15:31:59 +02002236 return QF_FAIL;
Bram Moolenaar68b76a62005-03-25 21:53:48 +00002237 }
Bram Moolenaard76ce852018-05-01 15:02:04 +02002238 if (module == NULL || *module == NUL)
2239 qfp->qf_module = NULL;
2240 else if ((qfp->qf_module = vim_strsave(module)) == NULL)
2241 {
2242 vim_free(qfp->qf_text);
2243 vim_free(qfp->qf_pattern);
2244 vim_free(qfp);
Bram Moolenaar95946f12019-03-31 15:31:59 +02002245 return QF_FAIL;
Bram Moolenaard76ce852018-05-01 15:02:04 +02002246 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002247 qfp->qf_nr = nr;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002248 if (type != 1 && !vim_isprintc(type)) // only printable chars allowed
Bram Moolenaar071d4272004-06-13 20:20:40 +00002249 type = 0;
2250 qfp->qf_type = type;
2251 qfp->qf_valid = valid;
2252
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02002253 lastp = &qfl->qf_last;
Bram Moolenaar0398e002019-03-21 21:12:49 +01002254 if (qf_list_empty(qfl)) // first element in the list
Bram Moolenaar071d4272004-06-13 20:20:40 +00002255 {
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02002256 qfl->qf_start = qfp;
2257 qfl->qf_ptr = qfp;
2258 qfl->qf_index = 0;
Bram Moolenaar83e6d7a2016-06-02 22:08:05 +02002259 qfp->qf_prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002260 }
2261 else
2262 {
Bram Moolenaar83e6d7a2016-06-02 22:08:05 +02002263 qfp->qf_prev = *lastp;
2264 (*lastp)->qf_next = qfp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002265 }
Bram Moolenaar83e6d7a2016-06-02 22:08:05 +02002266 qfp->qf_next = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002267 qfp->qf_cleared = FALSE;
Bram Moolenaar83e6d7a2016-06-02 22:08:05 +02002268 *lastp = qfp;
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02002269 ++qfl->qf_count;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002270 if (qfl->qf_index == 0 && qfp->qf_valid) // first valid entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00002271 {
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02002272 qfl->qf_index = qfl->qf_count;
2273 qfl->qf_ptr = qfp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002274 }
2275
Bram Moolenaar95946f12019-03-31 15:31:59 +02002276 return QF_OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002277}
2278
2279/*
Bram Moolenaar2d67d302018-11-16 18:46:02 +01002280 * Allocate a new quickfix/location list stack
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002281 */
Bram Moolenaar8b6144b2006-02-08 09:20:24 +00002282 static qf_info_T *
Bram Moolenaar2d67d302018-11-16 18:46:02 +01002283qf_alloc_stack(qfltype_T qfltype)
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002284{
Bram Moolenaar8b6144b2006-02-08 09:20:24 +00002285 qf_info_T *qi;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002286
Yegappan Lakshmanan5a2d4a32022-02-26 10:31:32 +00002287 qi = ALLOC_CLEAR_ONE_ID(qf_info_T, aid_qf_qfinfo);
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00002288 if (qi == NULL)
2289 return NULL;
2290
2291 qi->qf_refcount++;
2292 qi->qfl_type = qfltype;
2293 qi->qf_bufnr = INVALID_QFBUFNR;
Bram Moolenaar8b6144b2006-02-08 09:20:24 +00002294 return qi;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002295}
2296
2297/*
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02002298 * Return the location list stack for window 'wp'.
2299 * If not present, allocate a location list stack
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002300 */
2301 static qf_info_T *
Bram Moolenaar05540972016-01-30 20:31:25 +01002302ll_get_or_alloc_list(win_T *wp)
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002303{
2304 if (IS_LL_WINDOW(wp))
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002305 // For a location list window, use the referenced location list
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002306 return wp->w_llist_ref;
2307
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002308 // For a non-location list window, w_llist_ref should not point to a
2309 // location list.
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002310 ll_free_all(&wp->w_llist_ref);
2311
2312 if (wp->w_llist == NULL)
Bram Moolenaar2d67d302018-11-16 18:46:02 +01002313 wp->w_llist = qf_alloc_stack(QFLT_LOCATION); // new location list
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002314 return wp->w_llist;
2315}
2316
2317/*
Bram Moolenaar87f59b02019-04-04 14:04:11 +02002318 * Get the quickfix/location list stack to use for the specified Ex command.
2319 * For a location list command, returns the stack for the current window. If
2320 * the location list is not found, then returns NULL and prints an error
2321 * message if 'print_emsg' is TRUE.
2322 */
2323 static qf_info_T *
2324qf_cmd_get_stack(exarg_T *eap, int print_emsg)
2325{
2326 qf_info_T *qi = &ql_info;
2327
2328 if (is_loclist_cmd(eap->cmdidx))
2329 {
2330 qi = GET_LOC_LIST(curwin);
2331 if (qi == NULL)
2332 {
2333 if (print_emsg)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002334 emsg(_(e_no_location_list));
Bram Moolenaar87f59b02019-04-04 14:04:11 +02002335 return NULL;
2336 }
2337 }
2338
2339 return qi;
2340}
2341
2342/*
2343 * Get the quickfix/location list stack to use for the specified Ex command.
2344 * For a location list command, returns the stack for the current window.
2345 * If the location list is not present, then allocates a new one.
2346 * Returns NULL if the allocation fails. For a location list command, sets
2347 * 'pwinp' to curwin.
2348 */
2349 static qf_info_T *
2350qf_cmd_get_or_alloc_stack(exarg_T *eap, win_T **pwinp)
2351{
2352 qf_info_T *qi = &ql_info;
2353
2354 if (is_loclist_cmd(eap->cmdidx))
2355 {
2356 qi = ll_get_or_alloc_list(curwin);
2357 if (qi == NULL)
2358 return NULL;
2359 *pwinp = curwin;
2360 }
2361
2362 return qi;
2363}
2364
2365/*
Bram Moolenaar09037502018-09-25 22:08:14 +02002366 * Copy location list entries from 'from_qfl' to 'to_qfl'.
2367 */
2368 static int
Bram Moolenaar0398e002019-03-21 21:12:49 +01002369copy_loclist_entries(qf_list_T *from_qfl, qf_list_T *to_qfl)
Bram Moolenaar09037502018-09-25 22:08:14 +02002370{
2371 int i;
2372 qfline_T *from_qfp;
2373 qfline_T *prevp;
2374
2375 // copy all the location entries in this list
Bram Moolenaara16123a2019-03-28 20:31:07 +01002376 FOR_ALL_QFL_ITEMS(from_qfl, from_qfp, i)
Bram Moolenaar09037502018-09-25 22:08:14 +02002377 {
Bram Moolenaar0398e002019-03-21 21:12:49 +01002378 if (qf_add_entry(to_qfl,
Bram Moolenaar09037502018-09-25 22:08:14 +02002379 NULL,
2380 NULL,
2381 from_qfp->qf_module,
2382 0,
2383 from_qfp->qf_text,
2384 from_qfp->qf_lnum,
thinca6864efa2021-06-19 20:45:20 +02002385 from_qfp->qf_end_lnum,
Bram Moolenaar09037502018-09-25 22:08:14 +02002386 from_qfp->qf_col,
thinca6864efa2021-06-19 20:45:20 +02002387 from_qfp->qf_end_col,
Bram Moolenaar09037502018-09-25 22:08:14 +02002388 from_qfp->qf_viscol,
2389 from_qfp->qf_pattern,
2390 from_qfp->qf_nr,
2391 0,
Tom Praschanca6ac992023-08-11 23:26:12 +02002392 &from_qfp->qf_user_data,
Bram Moolenaar95946f12019-03-31 15:31:59 +02002393 from_qfp->qf_valid) == QF_FAIL)
Bram Moolenaar09037502018-09-25 22:08:14 +02002394 return FAIL;
2395
2396 // qf_add_entry() will not set the qf_num field, as the
2397 // directory and file names are not supplied. So the qf_fnum
2398 // field is copied here.
2399 prevp = to_qfl->qf_last;
2400 prevp->qf_fnum = from_qfp->qf_fnum; // file number
2401 prevp->qf_type = from_qfp->qf_type; // error type
2402 if (from_qfl->qf_ptr == from_qfp)
2403 to_qfl->qf_ptr = prevp; // current location
2404 }
2405
2406 return OK;
2407}
2408
2409/*
2410 * Copy the specified location list 'from_qfl' to 'to_qfl'.
2411 */
2412 static int
Bram Moolenaar0398e002019-03-21 21:12:49 +01002413copy_loclist(qf_list_T *from_qfl, qf_list_T *to_qfl)
Bram Moolenaar09037502018-09-25 22:08:14 +02002414{
2415 // Some of the fields are populated by qf_add_entry()
Bram Moolenaar2d67d302018-11-16 18:46:02 +01002416 to_qfl->qfl_type = from_qfl->qfl_type;
Bram Moolenaar09037502018-09-25 22:08:14 +02002417 to_qfl->qf_nonevalid = from_qfl->qf_nonevalid;
Tom Praschanca6ac992023-08-11 23:26:12 +02002418 to_qfl->qf_has_user_data = from_qfl->qf_has_user_data;
Bram Moolenaar09037502018-09-25 22:08:14 +02002419 to_qfl->qf_count = 0;
2420 to_qfl->qf_index = 0;
2421 to_qfl->qf_start = NULL;
2422 to_qfl->qf_last = NULL;
2423 to_qfl->qf_ptr = NULL;
2424 if (from_qfl->qf_title != NULL)
2425 to_qfl->qf_title = vim_strsave(from_qfl->qf_title);
2426 else
2427 to_qfl->qf_title = NULL;
2428 if (from_qfl->qf_ctx != NULL)
2429 {
2430 to_qfl->qf_ctx = alloc_tv();
2431 if (to_qfl->qf_ctx != NULL)
2432 copy_tv(from_qfl->qf_ctx, to_qfl->qf_ctx);
2433 }
2434 else
2435 to_qfl->qf_ctx = NULL;
=?UTF-8?q?Dundar=20G=C3=B6c?=b8366582022-04-14 20:43:56 +01002436 if (from_qfl->qf_qftf_cb.cb_name != NULL)
2437 copy_callback(&to_qfl->qf_qftf_cb, &from_qfl->qf_qftf_cb);
Bram Moolenaar858ba062020-05-31 23:11:59 +02002438 else
=?UTF-8?q?Dundar=20G=C3=B6c?=b8366582022-04-14 20:43:56 +01002439 to_qfl->qf_qftf_cb.cb_name = NULL;
Bram Moolenaar09037502018-09-25 22:08:14 +02002440
2441 if (from_qfl->qf_count)
Bram Moolenaar0398e002019-03-21 21:12:49 +01002442 if (copy_loclist_entries(from_qfl, to_qfl) == FAIL)
Bram Moolenaar09037502018-09-25 22:08:14 +02002443 return FAIL;
2444
2445 to_qfl->qf_index = from_qfl->qf_index; // current index in the list
2446
2447 // Assign a new ID for the location list
2448 to_qfl->qf_id = ++last_qf_id;
2449 to_qfl->qf_changedtick = 0L;
2450
2451 // When no valid entries are present in the list, qf_ptr points to
2452 // the first item in the list
2453 if (to_qfl->qf_nonevalid)
2454 {
2455 to_qfl->qf_ptr = to_qfl->qf_start;
2456 to_qfl->qf_index = 1;
2457 }
2458
2459 return OK;
2460}
2461
2462/*
2463 * Copy the location list stack 'from' window to 'to' window.
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002464 */
2465 void
Bram Moolenaar09037502018-09-25 22:08:14 +02002466copy_loclist_stack(win_T *from, win_T *to)
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002467{
2468 qf_info_T *qi;
2469 int idx;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002470
Bram Moolenaar09037502018-09-25 22:08:14 +02002471 // When copying from a location list window, copy the referenced
2472 // location list. For other windows, copy the location list for
2473 // that window.
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002474 if (IS_LL_WINDOW(from))
2475 qi = from->w_llist_ref;
2476 else
2477 qi = from->w_llist;
2478
Bram Moolenaar09037502018-09-25 22:08:14 +02002479 if (qi == NULL) // no location list to copy
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002480 return;
2481
Bram Moolenaar09037502018-09-25 22:08:14 +02002482 // allocate a new location list
Bram Moolenaar2d67d302018-11-16 18:46:02 +01002483 if ((to->w_llist = qf_alloc_stack(QFLT_LOCATION)) == NULL)
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002484 return;
2485
2486 to->w_llist->qf_listcount = qi->qf_listcount;
2487
Bram Moolenaar09037502018-09-25 22:08:14 +02002488 // Copy the location lists one at a time
Bram Moolenaarde3b3672018-08-07 21:54:41 +02002489 for (idx = 0; idx < qi->qf_listcount; ++idx)
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002490 {
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002491 to->w_llist->qf_curlist = idx;
2492
Bram Moolenaar0398e002019-03-21 21:12:49 +01002493 if (copy_loclist(qf_get_list(qi, idx),
2494 qf_get_list(to->w_llist, idx)) == FAIL)
Bram Moolenaar8f77c5a2017-04-30 14:21:00 +02002495 {
Bram Moolenaar09037502018-09-25 22:08:14 +02002496 qf_free_all(to);
2497 return;
Bram Moolenaar730d2c02013-06-30 13:33:58 +02002498 }
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002499 }
2500
Bram Moolenaar09037502018-09-25 22:08:14 +02002501 to->w_llist->qf_curlist = qi->qf_curlist; // current list
Bram Moolenaard12f5c12006-01-25 22:10:52 +00002502}
2503
2504/*
Bram Moolenaar7618e002016-11-13 15:09:26 +01002505 * Get buffer number for file "directory/fname".
Bram Moolenaar2f095a42016-06-03 19:05:49 +02002506 * Also sets the b_has_qf_entry flag.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002507 */
2508 static int
Bram Moolenaar0398e002019-03-21 21:12:49 +01002509qf_get_fnum(qf_list_T *qfl, char_u *directory, char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002510{
Bram Moolenaar82404332016-07-10 17:00:38 +02002511 char_u *ptr = NULL;
Bram Moolenaar2f095a42016-06-03 19:05:49 +02002512 buf_T *buf;
Bram Moolenaar82404332016-07-10 17:00:38 +02002513 char_u *bufname;
Bram Moolenaar2f095a42016-06-03 19:05:49 +02002514
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002515 if (fname == NULL || *fname == NUL) // no file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00002516 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002517
Bram Moolenaare60acc12011-05-10 16:41:25 +02002518#ifdef VMS
Bram Moolenaar2f095a42016-06-03 19:05:49 +02002519 vms_remove_version(fname);
Bram Moolenaare60acc12011-05-10 16:41:25 +02002520#endif
2521#ifdef BACKSLASH_IN_FILENAME
Bram Moolenaar2f095a42016-06-03 19:05:49 +02002522 if (directory != NULL)
2523 slash_adjust(directory);
2524 slash_adjust(fname);
Bram Moolenaare60acc12011-05-10 16:41:25 +02002525#endif
Bram Moolenaar2f095a42016-06-03 19:05:49 +02002526 if (directory != NULL && !vim_isAbsName(fname)
2527 && (ptr = concat_fnames(directory, fname, TRUE)) != NULL)
2528 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002529 // Here we check if the file really exists.
2530 // This should normally be true, but if make works without
2531 // "leaving directory"-messages we might have missed a
2532 // directory change.
Bram Moolenaar2f095a42016-06-03 19:05:49 +02002533 if (mch_getperm(ptr) < 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002534 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002535 vim_free(ptr);
Bram Moolenaar108e7b42018-10-11 17:39:12 +02002536 directory = qf_guess_filepath(qfl, fname);
Bram Moolenaar2f095a42016-06-03 19:05:49 +02002537 if (directory)
2538 ptr = concat_fnames(directory, fname, TRUE);
2539 else
2540 ptr = vim_strsave(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002541 }
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002542 // Use concatenated directory name and file name
Bram Moolenaar82404332016-07-10 17:00:38 +02002543 bufname = ptr;
2544 }
2545 else
2546 bufname = fname;
2547
2548 if (qf_last_bufname != NULL && STRCMP(bufname, qf_last_bufname) == 0
Bram Moolenaarb25f9a92016-07-10 18:21:50 +02002549 && bufref_valid(&qf_last_bufref))
Bram Moolenaar82404332016-07-10 17:00:38 +02002550 {
Bram Moolenaarb25f9a92016-07-10 18:21:50 +02002551 buf = qf_last_bufref.br_buf;
Bram Moolenaar2f095a42016-06-03 19:05:49 +02002552 vim_free(ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002553 }
Bram Moolenaar2f095a42016-06-03 19:05:49 +02002554 else
Bram Moolenaar82404332016-07-10 17:00:38 +02002555 {
2556 vim_free(qf_last_bufname);
2557 buf = buflist_new(bufname, NULL, (linenr_T)0, BLN_NOOPT);
2558 if (bufname == ptr)
2559 qf_last_bufname = bufname;
2560 else
2561 qf_last_bufname = vim_strsave(bufname);
Bram Moolenaarb25f9a92016-07-10 18:21:50 +02002562 set_bufref(&qf_last_bufref, buf);
Bram Moolenaar82404332016-07-10 17:00:38 +02002563 }
Bram Moolenaar2f095a42016-06-03 19:05:49 +02002564 if (buf == NULL)
2565 return 0;
Bram Moolenaar82404332016-07-10 17:00:38 +02002566
Bram Moolenaarc1542742016-07-20 21:44:37 +02002567 buf->b_has_qf_entry =
Bram Moolenaar2d67d302018-11-16 18:46:02 +01002568 IS_QF_LIST(qfl) ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY;
Bram Moolenaar2f095a42016-06-03 19:05:49 +02002569 return buf->b_fnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002570}
2571
2572/*
Bram Moolenaar38df43b2016-06-20 21:41:12 +02002573 * Push dirbuf onto the directory stack and return pointer to actual dir or
2574 * NULL on error.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002575 */
2576 static char_u *
Bram Moolenaar361c8f02016-07-02 15:41:47 +02002577qf_push_dir(char_u *dirbuf, struct dir_stack_T **stackptr, int is_file_stack)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002578{
2579 struct dir_stack_T *ds_new;
2580 struct dir_stack_T *ds_ptr;
2581
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002582 // allocate new stack element and hook it in
Yegappan Lakshmanan5a2d4a32022-02-26 10:31:32 +00002583 ds_new = ALLOC_ONE_ID(struct dir_stack_T, aid_qf_dirstack);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002584 if (ds_new == NULL)
2585 return NULL;
2586
2587 ds_new->next = *stackptr;
2588 *stackptr = ds_new;
2589
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002590 // store directory on the stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00002591 if (vim_isAbsName(dirbuf)
2592 || (*stackptr)->next == NULL
=?UTF-8?q?Dundar=20G=C3=B6c?=dd410372022-05-15 13:59:11 +01002593 || is_file_stack)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002594 (*stackptr)->dirname = vim_strsave(dirbuf);
2595 else
2596 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002597 // Okay we don't have an absolute path.
2598 // dirbuf must be a subdir of one of the directories on the stack.
2599 // Let's search...
Bram Moolenaar071d4272004-06-13 20:20:40 +00002600 ds_new = (*stackptr)->next;
2601 (*stackptr)->dirname = NULL;
2602 while (ds_new)
2603 {
2604 vim_free((*stackptr)->dirname);
2605 (*stackptr)->dirname = concat_fnames(ds_new->dirname, dirbuf,
2606 TRUE);
2607 if (mch_isdir((*stackptr)->dirname) == TRUE)
2608 break;
2609
2610 ds_new = ds_new->next;
2611 }
2612
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002613 // clean up all dirs we already left
Bram Moolenaar071d4272004-06-13 20:20:40 +00002614 while ((*stackptr)->next != ds_new)
2615 {
2616 ds_ptr = (*stackptr)->next;
2617 (*stackptr)->next = (*stackptr)->next->next;
2618 vim_free(ds_ptr->dirname);
2619 vim_free(ds_ptr);
2620 }
2621
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002622 // Nothing found -> it must be on top level
Bram Moolenaar071d4272004-06-13 20:20:40 +00002623 if (ds_new == NULL)
2624 {
2625 vim_free((*stackptr)->dirname);
2626 (*stackptr)->dirname = vim_strsave(dirbuf);
2627 }
2628 }
2629
2630 if ((*stackptr)->dirname != NULL)
2631 return (*stackptr)->dirname;
2632 else
2633 {
2634 ds_ptr = *stackptr;
2635 *stackptr = (*stackptr)->next;
2636 vim_free(ds_ptr);
2637 return NULL;
2638 }
2639}
2640
Bram Moolenaar071d4272004-06-13 20:20:40 +00002641/*
2642 * pop dirbuf from the directory stack and return previous directory or NULL if
2643 * stack is empty
2644 */
2645 static char_u *
Bram Moolenaar05540972016-01-30 20:31:25 +01002646qf_pop_dir(struct dir_stack_T **stackptr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002647{
2648 struct dir_stack_T *ds_ptr;
2649
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002650 // TODO: Should we check if dirbuf is the directory on top of the stack?
2651 // What to do if it isn't?
Bram Moolenaar071d4272004-06-13 20:20:40 +00002652
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002653 // pop top element and free it
Bram Moolenaar071d4272004-06-13 20:20:40 +00002654 if (*stackptr != NULL)
2655 {
2656 ds_ptr = *stackptr;
2657 *stackptr = (*stackptr)->next;
2658 vim_free(ds_ptr->dirname);
2659 vim_free(ds_ptr);
2660 }
2661
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002662 // return NEW top element as current dir or NULL if stack is empty
Bram Moolenaar071d4272004-06-13 20:20:40 +00002663 return *stackptr ? (*stackptr)->dirname : NULL;
2664}
2665
2666/*
2667 * clean up directory stack
2668 */
2669 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01002670qf_clean_dir_stack(struct dir_stack_T **stackptr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002671{
2672 struct dir_stack_T *ds_ptr;
2673
2674 while ((ds_ptr = *stackptr) != NULL)
2675 {
2676 *stackptr = (*stackptr)->next;
2677 vim_free(ds_ptr->dirname);
2678 vim_free(ds_ptr);
2679 }
2680}
2681
2682/*
2683 * Check in which directory of the directory stack the given file can be
2684 * found.
Bram Moolenaaraa23b372015-09-08 18:46:31 +02002685 * Returns a pointer to the directory name or NULL if not found.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002686 * Cleans up intermediate directory entries.
2687 *
2688 * TODO: How to solve the following problem?
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02002689 * If we have this directory tree:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002690 * ./
2691 * ./aa
2692 * ./aa/bb
2693 * ./bb
2694 * ./bb/x.c
2695 * and make says:
2696 * making all in aa
2697 * making all in bb
2698 * x.c:9: Error
2699 * Then qf_push_dir thinks we are in ./aa/bb, but we are in ./bb.
2700 * qf_guess_filepath will return NULL.
2701 */
2702 static char_u *
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02002703qf_guess_filepath(qf_list_T *qfl, char_u *filename)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002704{
2705 struct dir_stack_T *ds_ptr;
2706 struct dir_stack_T *ds_tmp;
2707 char_u *fullname;
2708
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002709 // no dirs on the stack - there's nothing we can do
Bram Moolenaara7df8c72017-07-19 13:23:06 +02002710 if (qfl->qf_dir_stack == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002711 return NULL;
2712
Bram Moolenaara7df8c72017-07-19 13:23:06 +02002713 ds_ptr = qfl->qf_dir_stack->next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002714 fullname = NULL;
2715 while (ds_ptr)
2716 {
2717 vim_free(fullname);
2718 fullname = concat_fnames(ds_ptr->dirname, filename, TRUE);
2719
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002720 // If concat_fnames failed, just go on. The worst thing that can happen
2721 // is that we delete the entire stack.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002722 if ((fullname != NULL) && (mch_getperm(fullname) >= 0))
2723 break;
2724
2725 ds_ptr = ds_ptr->next;
2726 }
2727
2728 vim_free(fullname);
2729
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002730 // clean up all dirs we already left
Bram Moolenaara7df8c72017-07-19 13:23:06 +02002731 while (qfl->qf_dir_stack->next != ds_ptr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002732 {
Bram Moolenaara7df8c72017-07-19 13:23:06 +02002733 ds_tmp = qfl->qf_dir_stack->next;
2734 qfl->qf_dir_stack->next = qfl->qf_dir_stack->next->next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002735 vim_free(ds_tmp->dirname);
2736 vim_free(ds_tmp);
2737 }
2738
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02002739 return ds_ptr == NULL ? NULL : ds_ptr->dirname;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002740}
2741
2742/*
Bram Moolenaar3c097222017-12-21 20:54:49 +01002743 * Returns TRUE if a quickfix/location list with the given identifier exists.
2744 */
2745 static int
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02002746qflist_valid(win_T *wp, int_u qf_id)
Bram Moolenaar3c097222017-12-21 20:54:49 +01002747{
2748 qf_info_T *qi = &ql_info;
2749 int i;
2750
2751 if (wp != NULL)
2752 {
Bram Moolenaar2c7080b2021-02-06 19:19:42 +01002753 if (!win_valid(wp))
2754 return FALSE;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002755 qi = GET_LOC_LIST(wp); // Location list
Bram Moolenaar3c097222017-12-21 20:54:49 +01002756 if (qi == NULL)
2757 return FALSE;
2758 }
2759
2760 for (i = 0; i < qi->qf_listcount; ++i)
2761 if (qi->qf_lists[i].qf_id == qf_id)
2762 return TRUE;
2763
2764 return FALSE;
2765}
2766
2767/*
Bram Moolenaar531b9a32018-07-03 16:54:23 +02002768 * When loading a file from the quickfix, the autocommands may modify it.
Bram Moolenaarffec3c52016-03-23 20:55:42 +01002769 * This may invalidate the current quickfix entry. This function checks
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02002770 * whether an entry is still present in the quickfix list.
Bram Moolenaarffec3c52016-03-23 20:55:42 +01002771 * Similar to location list.
2772 */
2773 static int
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02002774is_qf_entry_present(qf_list_T *qfl, qfline_T *qf_ptr)
Bram Moolenaarffec3c52016-03-23 20:55:42 +01002775{
Bram Moolenaarffec3c52016-03-23 20:55:42 +01002776 qfline_T *qfp;
2777 int i;
2778
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002779 // Search for the entry in the current list
Bram Moolenaara16123a2019-03-28 20:31:07 +01002780 FOR_ALL_QFL_ITEMS(qfl, qfp, i)
2781 if (qfp == qf_ptr)
Bram Moolenaarffec3c52016-03-23 20:55:42 +01002782 break;
2783
Bram Moolenaar95946f12019-03-31 15:31:59 +02002784 if (i > qfl->qf_count) // Entry is not found
Bram Moolenaarffec3c52016-03-23 20:55:42 +01002785 return FALSE;
2786
2787 return TRUE;
2788}
2789
2790/*
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002791 * Get the next valid entry in the current quickfix/location list. The search
Bram Moolenaar9cb03712017-09-20 22:43:02 +02002792 * starts from the current entry. Returns NULL on failure.
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002793 */
2794 static qfline_T *
2795get_next_valid_entry(
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02002796 qf_list_T *qfl,
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002797 qfline_T *qf_ptr,
2798 int *qf_index,
2799 int dir)
2800{
2801 int idx;
2802 int old_qf_fnum;
2803
2804 idx = *qf_index;
2805 old_qf_fnum = qf_ptr->qf_fnum;
2806
2807 do
2808 {
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02002809 if (idx == qfl->qf_count || qf_ptr->qf_next == NULL)
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002810 return NULL;
2811 ++idx;
2812 qf_ptr = qf_ptr->qf_next;
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02002813 } while ((!qfl->qf_nonevalid && !qf_ptr->qf_valid)
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002814 || (dir == FORWARD_FILE && qf_ptr->qf_fnum == old_qf_fnum));
2815
2816 *qf_index = idx;
2817 return qf_ptr;
2818}
2819
2820/*
2821 * Get the previous valid entry in the current quickfix/location list. The
Bram Moolenaar9cb03712017-09-20 22:43:02 +02002822 * search starts from the current entry. Returns NULL on failure.
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002823 */
2824 static qfline_T *
2825get_prev_valid_entry(
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02002826 qf_list_T *qfl,
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002827 qfline_T *qf_ptr,
2828 int *qf_index,
2829 int dir)
2830{
2831 int idx;
2832 int old_qf_fnum;
2833
2834 idx = *qf_index;
2835 old_qf_fnum = qf_ptr->qf_fnum;
2836
2837 do
2838 {
2839 if (idx == 1 || qf_ptr->qf_prev == NULL)
2840 return NULL;
2841 --idx;
2842 qf_ptr = qf_ptr->qf_prev;
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02002843 } while ((!qfl->qf_nonevalid && !qf_ptr->qf_valid)
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002844 || (dir == BACKWARD_FILE && qf_ptr->qf_fnum == old_qf_fnum));
2845
2846 *qf_index = idx;
2847 return qf_ptr;
2848}
2849
2850/*
2851 * Get the n'th (errornr) previous/next valid entry from the current entry in
2852 * the quickfix list.
2853 * dir == FORWARD or FORWARD_FILE: next valid entry
2854 * dir == BACKWARD or BACKWARD_FILE: previous valid entry
2855 */
2856 static qfline_T *
2857get_nth_valid_entry(
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02002858 qf_list_T *qfl,
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002859 int errornr,
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02002860 int dir,
2861 int *new_qfidx)
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002862{
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02002863 qfline_T *qf_ptr = qfl->qf_ptr;
2864 int qf_idx = qfl->qf_index;
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002865 qfline_T *prev_qf_ptr;
2866 int prev_index;
Bram Moolenaar1d423ef2022-01-02 21:26:16 +00002867 char *err = e_no_more_items;
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002868
2869 while (errornr--)
2870 {
2871 prev_qf_ptr = qf_ptr;
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02002872 prev_index = qf_idx;
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002873
2874 if (dir == FORWARD || dir == FORWARD_FILE)
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02002875 qf_ptr = get_next_valid_entry(qfl, qf_ptr, &qf_idx, dir);
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002876 else
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02002877 qf_ptr = get_prev_valid_entry(qfl, qf_ptr, &qf_idx, dir);
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002878 if (qf_ptr == NULL)
2879 {
2880 qf_ptr = prev_qf_ptr;
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02002881 qf_idx = prev_index;
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002882 if (err != NULL)
2883 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002884 emsg(_(err));
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002885 return NULL;
2886 }
2887 break;
2888 }
2889
2890 err = NULL;
2891 }
2892
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02002893 *new_qfidx = qf_idx;
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002894 return qf_ptr;
2895}
2896
2897/*
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02002898 * Get n'th (errornr) quickfix entry from the current entry in the quickfix
2899 * list 'qfl'. Returns a pointer to the new entry and the index in 'new_qfidx'
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002900 */
2901 static qfline_T *
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02002902get_nth_entry(qf_list_T *qfl, int errornr, int *new_qfidx)
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002903{
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02002904 qfline_T *qf_ptr = qfl->qf_ptr;
2905 int qf_idx = qfl->qf_index;
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002906
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02002907 // New error number is less than the current error number
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002908 while (errornr < qf_idx && qf_idx > 1 && qf_ptr->qf_prev != NULL)
2909 {
2910 --qf_idx;
2911 qf_ptr = qf_ptr->qf_prev;
2912 }
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02002913 // New error number is greater than the current error number
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02002914 while (errornr > qf_idx && qf_idx < qfl->qf_count &&
2915 qf_ptr->qf_next != NULL)
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002916 {
2917 ++qf_idx;
2918 qf_ptr = qf_ptr->qf_next;
2919 }
2920
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02002921 *new_qfidx = qf_idx;
2922 return qf_ptr;
2923}
2924
2925/*
Bram Moolenaar4b96df52020-01-26 22:00:26 +01002926 * Get a entry specified by 'errornr' and 'dir' from the current
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02002927 * quickfix/location list. 'errornr' specifies the index of the entry and 'dir'
2928 * specifies the direction (FORWARD/BACKWARD/FORWARD_FILE/BACKWARD_FILE).
2929 * Returns a pointer to the entry and the index of the new entry is stored in
2930 * 'new_qfidx'.
2931 */
2932 static qfline_T *
2933qf_get_entry(
2934 qf_list_T *qfl,
2935 int errornr,
2936 int dir,
2937 int *new_qfidx)
2938{
2939 qfline_T *qf_ptr = qfl->qf_ptr;
2940 int qfidx = qfl->qf_index;
2941
2942 if (dir != 0) // next/prev valid entry
2943 qf_ptr = get_nth_valid_entry(qfl, errornr, dir, &qfidx);
2944 else if (errornr != 0) // go to specified number
2945 qf_ptr = get_nth_entry(qfl, errornr, &qfidx);
2946
2947 *new_qfidx = qfidx;
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002948 return qf_ptr;
2949}
2950
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002951/*
Yegappan Lakshmanan78a61062021-12-08 20:03:31 +00002952 * Find a window displaying a Vim help file in the current tab page.
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02002953 */
2954 static win_T *
2955qf_find_help_win(void)
2956{
2957 win_T *wp;
2958
2959 FOR_ALL_WINDOWS(wp)
2960 if (bt_help(wp->w_buffer))
2961 return wp;
2962
2963 return NULL;
2964}
2965
2966/*
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01002967 * Set the location list for the specified window to 'qi'.
2968 */
2969 static void
2970win_set_loclist(win_T *wp, qf_info_T *qi)
2971{
2972 wp->w_llist = qi;
2973 qi->qf_refcount++;
2974}
2975
2976/*
Bram Moolenaarb2443732018-11-11 22:50:27 +01002977 * Find a help window or open one. If 'newwin' is TRUE, then open a new help
2978 * window.
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002979 */
2980 static int
Bram Moolenaarb2443732018-11-11 22:50:27 +01002981jump_to_help_window(qf_info_T *qi, int newwin, int *opened_window)
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002982{
2983 win_T *wp;
2984 int flags;
2985
Bram Moolenaare1004402020-10-24 20:49:43 +02002986 if (cmdmod.cmod_tab != 0 || newwin)
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002987 wp = NULL;
2988 else
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02002989 wp = qf_find_help_win();
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002990 if (wp != NULL && wp->w_buffer->b_nwindows > 0)
2991 win_enter(wp, TRUE);
2992 else
2993 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02002994 // Split off help window; put it at far top if no position
2995 // specified, the current window is vertically split and narrow.
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002996 flags = WSP_HELP;
Bram Moolenaare1004402020-10-24 20:49:43 +02002997 if (cmdmod.cmod_split == 0 && curwin->w_width != Columns
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02002998 && curwin->w_width < 80)
2999 flags |= WSP_TOP;
Bram Moolenaarb2443732018-11-11 22:50:27 +01003000 // If the user asks to open a new window, then copy the location list.
3001 // Otherwise, don't copy the location list.
3002 if (IS_LL_STACK(qi) && !newwin)
3003 flags |= WSP_NEWLOC;
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02003004
3005 if (win_split(0, flags) == FAIL)
3006 return FAIL;
3007
3008 *opened_window = TRUE;
3009
3010 if (curwin->w_height < p_hh)
3011 win_setheight((int)p_hh);
3012
Bram Moolenaarb2443732018-11-11 22:50:27 +01003013 // When using location list, the new window should use the supplied
3014 // location list. If the user asks to open a new window, then the new
3015 // window will get a copy of the location list.
3016 if (IS_LL_STACK(qi) && !newwin)
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01003017 win_set_loclist(curwin, qi);
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02003018 }
3019
3020 if (!p_im)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003021 restart_edit = 0; // don't want insert mode in help file
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02003022
3023 return OK;
3024}
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02003025
3026/*
Yegappan Lakshmanan78a61062021-12-08 20:03:31 +00003027 * Find a non-quickfix window using the given location list stack in the
3028 * current tabpage.
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003029 * Returns NULL if a matching window is not found.
3030 */
3031 static win_T *
3032qf_find_win_with_loclist(qf_info_T *ll)
3033{
3034 win_T *wp;
3035
3036 FOR_ALL_WINDOWS(wp)
3037 if (wp->w_llist == ll && !bt_quickfix(wp->w_buffer))
3038 return wp;
3039
3040 return NULL;
3041}
3042
3043/*
Yegappan Lakshmanan78a61062021-12-08 20:03:31 +00003044 * Find a window containing a normal buffer in the current tab page.
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003045 */
3046 static win_T *
3047qf_find_win_with_normal_buf(void)
3048{
3049 win_T *wp;
3050
3051 FOR_ALL_WINDOWS(wp)
Bram Moolenaar91335e52018-08-01 17:53:12 +02003052 if (bt_normal(wp->w_buffer))
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003053 return wp;
3054
3055 return NULL;
3056}
3057
3058/*
3059 * Go to a window in any tabpage containing the specified file. Returns TRUE
3060 * if successfully jumped to the window. Otherwise returns FALSE.
3061 */
3062 static int
3063qf_goto_tabwin_with_file(int fnum)
3064{
3065 tabpage_T *tp;
3066 win_T *wp;
3067
3068 FOR_ALL_TAB_WINDOWS(tp, wp)
3069 if (wp->w_buffer->b_fnum == fnum)
3070 {
3071 goto_tabpage_win(tp, wp);
3072 return TRUE;
3073 }
3074
3075 return FALSE;
3076}
3077
3078/*
3079 * Create a new window to show a file above the quickfix window. Called when
3080 * only the quickfix window is present.
3081 */
3082 static int
3083qf_open_new_file_win(qf_info_T *ll_ref)
3084{
3085 int flags;
3086
3087 flags = WSP_ABOVE;
3088 if (ll_ref != NULL)
3089 flags |= WSP_NEWLOC;
3090 if (win_split(0, flags) == FAIL)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003091 return FAIL; // not enough room for window
3092 p_swb = empty_option; // don't split again
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003093 swb_flags = 0;
3094 RESET_BINDING(curwin);
3095 if (ll_ref != NULL)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003096 // The new window should use the location list from the
3097 // location list window
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01003098 win_set_loclist(curwin, ll_ref);
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003099 return OK;
3100}
3101
3102/*
3103 * Go to a window that shows the right buffer. If the window is not found, go
3104 * to the window just above the location list window. This is used for opening
3105 * a file from a location window and not from a quickfix window. If some usable
3106 * window is previously found, then it is supplied in 'use_win'.
3107 */
3108 static void
3109qf_goto_win_with_ll_file(win_T *use_win, int qf_fnum, qf_info_T *ll_ref)
3110{
3111 win_T *win = use_win;
3112
3113 if (win == NULL)
3114 {
Yegappan Lakshmanan78a61062021-12-08 20:03:31 +00003115 // Find the window showing the selected file in the current tab page.
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003116 FOR_ALL_WINDOWS(win)
3117 if (win->w_buffer->b_fnum == qf_fnum)
3118 break;
3119 if (win == NULL)
3120 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003121 // Find a previous usable window
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003122 win = curwin;
3123 do
3124 {
Bram Moolenaar91335e52018-08-01 17:53:12 +02003125 if (bt_normal(win->w_buffer))
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003126 break;
3127 if (win->w_prev == NULL)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003128 win = lastwin; // wrap around the top
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003129 else
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003130 win = win->w_prev; // go to previous window
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003131 } while (win != curwin);
3132 }
3133 }
3134 win_goto(win);
3135
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003136 // If the location list for the window is not set, then set it
3137 // to the location list from the location window
Bram Moolenaar0398e002019-03-21 21:12:49 +01003138 if (win->w_llist == NULL && ll_ref != NULL)
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01003139 win_set_loclist(win, ll_ref);
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003140}
3141
3142/*
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02003143 * Go to a window that contains the specified buffer 'qf_fnum'. If a window is
3144 * not found, then go to the window just above the quickfix window. This is
3145 * used for opening a file from a quickfix window and not from a location
3146 * window.
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003147 */
3148 static void
3149qf_goto_win_with_qfl_file(int qf_fnum)
3150{
3151 win_T *win;
3152 win_T *altwin;
3153
3154 win = curwin;
3155 altwin = NULL;
3156 for (;;)
3157 {
3158 if (win->w_buffer->b_fnum == qf_fnum)
3159 break;
3160 if (win->w_prev == NULL)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003161 win = lastwin; // wrap around the top
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003162 else
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003163 win = win->w_prev; // go to previous window
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003164
3165 if (IS_QF_WINDOW(win))
3166 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003167 // Didn't find it, go to the window before the quickfix
Bram Moolenaar539aa6b2019-11-17 18:09:38 +01003168 // window, unless 'switchbuf' contains 'uselast': in this case we
3169 // try to jump to the previously used window first.
Sean Dewar4bb505e2024-03-05 20:39:07 +01003170 if ((swb_flags & SWB_USELAST) && win_valid(prevwin) &&
3171 !prevwin->w_p_wfb)
Bram Moolenaar539aa6b2019-11-17 18:09:38 +01003172 win = prevwin;
3173 else if (altwin != NULL)
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003174 win = altwin;
3175 else if (curwin->w_prev != NULL)
3176 win = curwin->w_prev;
3177 else
3178 win = curwin->w_next;
3179 break;
3180 }
3181
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003182 // Remember a usable window.
Sean Dewar4bb505e2024-03-05 20:39:07 +01003183 if (altwin == NULL && !win->w_p_pvw && !win->w_p_wfb &&
3184 bt_normal(win->w_buffer))
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003185 altwin = win;
3186 }
3187
3188 win_goto(win);
3189}
3190
3191/*
3192 * Find a suitable window for opening a file (qf_fnum) from the
3193 * quickfix/location list and jump to it. If the file is already opened in a
Bram Moolenaarb2443732018-11-11 22:50:27 +01003194 * window, jump to it. Otherwise open a new window to display the file. If
3195 * 'newwin' is TRUE, then always open a new window. This is called from either
3196 * a quickfix or a location list window.
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003197 */
3198 static int
Bram Moolenaarb2443732018-11-11 22:50:27 +01003199qf_jump_to_usable_window(int qf_fnum, int newwin, int *opened_window)
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003200{
Bram Moolenaarf9ae1542019-11-18 22:02:16 +01003201 win_T *usable_wp = NULL;
3202 int usable_win = FALSE;
Bram Moolenaarb2443732018-11-11 22:50:27 +01003203 qf_info_T *ll_ref = NULL;
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003204
Bram Moolenaarb2443732018-11-11 22:50:27 +01003205 // If opening a new window, then don't use the location list referred by
3206 // the current window. Otherwise two windows will refer to the same
3207 // location list.
3208 if (!newwin)
3209 ll_ref = curwin->w_llist_ref;
3210
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003211 if (ll_ref != NULL)
3212 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003213 // Find a non-quickfix window with this location list
Bram Moolenaarf9ae1542019-11-18 22:02:16 +01003214 usable_wp = qf_find_win_with_loclist(ll_ref);
3215 if (usable_wp != NULL)
3216 usable_win = TRUE;
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003217 }
3218
3219 if (!usable_win)
3220 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003221 // Locate a window showing a normal buffer
Bram Moolenaarf9ae1542019-11-18 22:02:16 +01003222 win_T *win = qf_find_win_with_normal_buf();
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003223 if (win != NULL)
Bram Moolenaarf9ae1542019-11-18 22:02:16 +01003224 usable_win = TRUE;
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003225 }
3226
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003227 // If no usable window is found and 'switchbuf' contains "usetab"
3228 // then search in other tabs.
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003229 if (!usable_win && (swb_flags & SWB_USETAB))
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003230 usable_win = qf_goto_tabwin_with_file(qf_fnum);
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003231
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003232 // If there is only one window and it is the quickfix window, create a
3233 // new one above the quickfix window.
Bram Moolenaarb2443732018-11-11 22:50:27 +01003234 if ((ONE_WINDOW && bt_quickfix(curbuf)) || !usable_win || newwin)
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003235 {
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003236 if (qf_open_new_file_win(ll_ref) != OK)
3237 return FAIL;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003238 *opened_window = TRUE; // close it when fail
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003239 }
3240 else
3241 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003242 if (curwin->w_llist_ref != NULL) // In a location window
Bram Moolenaarf9ae1542019-11-18 22:02:16 +01003243 qf_goto_win_with_ll_file(usable_wp, qf_fnum, ll_ref);
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003244 else // In a quickfix window
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003245 qf_goto_win_with_qfl_file(qf_fnum);
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003246 }
3247
3248 return OK;
3249}
3250
3251/*
3252 * Edit the selected file or help file.
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003253 * Returns OK if successfully edited the file, FAIL on failing to open the
Yegappan Lakshmanan6d24a512022-08-27 20:59:57 +01003254 * buffer and QF_ABORT if the quickfix/location list was freed by an autocmd
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003255 * when opening the buffer.
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003256 */
3257 static int
3258qf_jump_edit_buffer(
3259 qf_info_T *qi,
3260 qfline_T *qf_ptr,
3261 int forceit,
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01003262 int prev_winid,
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003263 int *opened_window)
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003264{
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01003265 qf_list_T *qfl = qf_get_curlist(qi);
Bram Moolenaar4d170af2020-09-13 22:21:22 +02003266 int old_changedtick = qfl->qf_changedtick;
Bram Moolenaar2d67d302018-11-16 18:46:02 +01003267 qfltype_T qfl_type = qfl->qfl_type;
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003268 int retval = OK;
Bram Moolenaarb6f14802018-10-21 18:47:43 +02003269 int old_qf_curlist = qi->qf_curlist;
3270 int save_qfid = qfl->qf_id;
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003271
3272 if (qf_ptr->qf_type == 1)
3273 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003274 // Open help file (do_ecmd() will set b_help flag, readfile() will
3275 // set b_p_ro flag).
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003276 if (!can_abandon(curbuf, forceit))
3277 {
3278 no_write_message();
Bram Moolenaarb6f14802018-10-21 18:47:43 +02003279 return FAIL;
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003280 }
Bram Moolenaarb6f14802018-10-21 18:47:43 +02003281
3282 retval = do_ecmd(qf_ptr->qf_fnum, NULL, NULL, NULL, (linenr_T)1,
3283 ECMD_HIDE + ECMD_SET_HELP,
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01003284 prev_winid == curwin->w_id ? curwin : NULL);
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003285 }
3286 else
Colin Kennedy21570352024-03-03 16:16:47 +01003287 {
Sean Dewar4bb505e2024-03-05 20:39:07 +01003288 int fnum = qf_ptr->qf_fnum;
3289
3290 if (!forceit && curwin->w_p_wfb && curbuf->b_fnum != fnum)
Colin Kennedy21570352024-03-03 16:16:47 +01003291 {
3292 if (qi->qfl_type == QFLT_LOCATION)
3293 {
Sean Dewar4bb505e2024-03-05 20:39:07 +01003294 // Location lists cannot split or reassign their window
3295 // so 'winfixbuf' windows must fail
3296 emsg(_(e_winfixbuf_cannot_go_to_buffer));
3297 return FAIL;
Colin Kennedy21570352024-03-03 16:16:47 +01003298 }
3299
Sean Dewar4bb505e2024-03-05 20:39:07 +01003300 if (win_valid(prevwin) && !prevwin->w_p_wfb &&
3301 !bt_quickfix(prevwin->w_buffer))
Colin Kennedy21570352024-03-03 16:16:47 +01003302 {
Sean Dewar4bb505e2024-03-05 20:39:07 +01003303 // 'winfixbuf' is set; attempt to change to a window without it
3304 // that isn't a quickfix/location list window.
3305 win_goto(prevwin);
3306 }
3307 if (curwin->w_p_wfb)
3308 {
3309 // Split the window, which will be 'nowinfixbuf', and set curwin
3310 // to that
3311 if (win_split(0, 0) == OK)
3312 *opened_window = TRUE;
3313
3314 if (curwin->w_p_wfb)
3315 {
3316 // Autocommands set 'winfixbuf' or sent us to another window
Sean Dewar769eb2d2024-03-07 21:37:50 +01003317 // with it set, or we failed to split the window. Give up,
3318 // but don't return immediately, as they may have messed
3319 // with the list.
Sean Dewar4bb505e2024-03-05 20:39:07 +01003320 emsg(_(e_winfixbuf_cannot_go_to_buffer));
3321 retval = FAIL;
3322 }
Colin Kennedy21570352024-03-03 16:16:47 +01003323 }
3324 }
3325
Sean Dewar4bb505e2024-03-05 20:39:07 +01003326 if (retval == OK)
3327 {
3328 retval = buflist_getfile(fnum,
3329 (linenr_T)1, GETF_SETMARK | GETF_SWITCH, forceit);
3330 }
Colin Kennedy21570352024-03-03 16:16:47 +01003331 }
Bram Moolenaar3c097222017-12-21 20:54:49 +01003332
Bram Moolenaarb6f14802018-10-21 18:47:43 +02003333 // If a location list, check whether the associated window is still
3334 // present.
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01003335 if (qfl_type == QFLT_LOCATION)
Bram Moolenaarb6f14802018-10-21 18:47:43 +02003336 {
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01003337 win_T *wp = win_id2wp(prev_winid);
Bram Moolenaarb4ad3b02022-03-30 10:57:45 +01003338
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01003339 if (wp == NULL && curwin->w_llist != qi)
3340 {
Bram Moolenaard82a47d2022-01-05 20:24:39 +00003341 emsg(_(e_current_window_was_closed));
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01003342 *opened_window = FALSE;
Yegappan Lakshmanan6d24a512022-08-27 20:59:57 +01003343 return QF_ABORT;
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01003344 }
Bram Moolenaarb6f14802018-10-21 18:47:43 +02003345 }
3346
Bram Moolenaar2d67d302018-11-16 18:46:02 +01003347 if (qfl_type == QFLT_QUICKFIX && !qflist_valid(NULL, save_qfid))
Bram Moolenaarb6f14802018-10-21 18:47:43 +02003348 {
Bram Moolenaar4d170af2020-09-13 22:21:22 +02003349 emsg(_(e_current_quickfix_list_was_changed));
Yegappan Lakshmanan6d24a512022-08-27 20:59:57 +01003350 return QF_ABORT;
Bram Moolenaarb6f14802018-10-21 18:47:43 +02003351 }
3352
Bram Moolenaar4d170af2020-09-13 22:21:22 +02003353 // Check if the list was changed. The pointers may happen to be identical,
3354 // thus also check qf_changedtick.
Bram Moolenaarb6f14802018-10-21 18:47:43 +02003355 if (old_qf_curlist != qi->qf_curlist
Bram Moolenaar4d170af2020-09-13 22:21:22 +02003356 || old_changedtick != qfl->qf_changedtick
Bram Moolenaarb6f14802018-10-21 18:47:43 +02003357 || !is_qf_entry_present(qfl, qf_ptr))
3358 {
Bram Moolenaar2d67d302018-11-16 18:46:02 +01003359 if (qfl_type == QFLT_QUICKFIX)
Bram Moolenaar4d170af2020-09-13 22:21:22 +02003360 emsg(_(e_current_quickfix_list_was_changed));
Bram Moolenaarb6f14802018-10-21 18:47:43 +02003361 else
Bram Moolenaar4d170af2020-09-13 22:21:22 +02003362 emsg(_(e_current_location_list_was_changed));
Yegappan Lakshmanan6d24a512022-08-27 20:59:57 +01003363 return QF_ABORT;
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003364 }
3365
3366 return retval;
3367}
3368
3369/*
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02003370 * Go to the error line in the current file using either line/column number or
3371 * a search pattern.
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003372 */
3373 static void
3374qf_jump_goto_line(
3375 linenr_T qf_lnum,
3376 int qf_col,
3377 char_u qf_viscol,
3378 char_u *qf_pattern)
3379{
3380 linenr_T i;
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003381
3382 if (qf_pattern == NULL)
3383 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003384 // Go to line with error, unless qf_lnum is 0.
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003385 i = qf_lnum;
3386 if (i > 0)
3387 {
3388 if (i > curbuf->b_ml.ml_line_count)
3389 i = curbuf->b_ml.ml_line_count;
3390 curwin->w_cursor.lnum = i;
3391 }
3392 if (qf_col > 0)
3393 {
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003394 curwin->w_cursor.coladd = 0;
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003395 if (qf_viscol == TRUE)
Bram Moolenaarc45eb772019-01-31 14:27:04 +01003396 coladvance(qf_col - 1);
3397 else
3398 curwin->w_cursor.col = qf_col - 1;
Bram Moolenaar2dfcef42018-08-15 22:29:51 +02003399 curwin->w_set_curswant = TRUE;
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003400 check_cursor();
3401 }
3402 else
3403 beginline(BL_WHITE | BL_FIX);
3404 }
3405 else
3406 {
3407 pos_T save_cursor;
3408
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003409 // Move the cursor to the first line in the buffer
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003410 save_cursor = curwin->w_cursor;
3411 curwin->w_cursor.lnum = 0;
John Marriott8c85a2a2024-05-20 19:18:26 +02003412 if (!do_search(NULL, '/', '/', qf_pattern, STRLEN(qf_pattern), (long)1, SEARCH_KEEP, NULL))
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003413 curwin->w_cursor = save_cursor;
3414 }
3415}
3416
3417/*
3418 * Display quickfix list index and size message
3419 */
3420 static void
3421qf_jump_print_msg(
3422 qf_info_T *qi,
3423 int qf_index,
3424 qfline_T *qf_ptr,
3425 buf_T *old_curbuf,
3426 linenr_T old_lnum)
3427{
3428 linenr_T i;
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01003429 garray_T *gap;
3430
3431 gap = qfga_get();
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003432
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003433 // Update the screen before showing the message, unless the screen
3434 // scrolled up.
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003435 if (!msg_scrolled)
3436 update_topline_redraw();
Yegappan Lakshmananf8412c92022-10-13 11:59:22 +01003437 vim_snprintf((char *)IObuff, IOSIZE, _("(%d of %d)%s%s: "), qf_index,
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01003438 qf_get_curlist(qi)->qf_count,
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003439 qf_ptr->qf_cleared ? _(" (line deleted)") : "",
3440 (char *)qf_types(qf_ptr->qf_type, qf_ptr->qf_nr));
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003441 // Add the message, skipping leading whitespace and newlines.
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01003442 ga_concat(gap, IObuff);
3443 qf_fmt_text(gap, skipwhite(qf_ptr->qf_text));
Shane Harper5bf04282023-06-07 19:09:57 +01003444 ga_append(gap, NUL);
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003445
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003446 // Output the message. Overwrite to avoid scrolling when the 'O'
3447 // flag is present in 'shortmess'; But when not jumping, print the
3448 // whole message.
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003449 i = msg_scroll;
3450 if (curbuf == old_curbuf && curwin->w_cursor.lnum == old_lnum)
3451 msg_scroll = TRUE;
3452 else if (!msg_scrolled && shortmess(SHM_OVERALL))
3453 msg_scroll = FALSE;
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01003454 msg_attr_keep((char *)gap->ga_data, 0, TRUE);
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003455 msg_scroll = i;
Yegappan Lakshmanand8cd6f72022-10-16 11:30:48 +01003456
3457 qfga_clear();
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003458}
3459
3460/*
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003461 * Find a usable window for opening a file from the quickfix/location list. If
Bram Moolenaarb2443732018-11-11 22:50:27 +01003462 * a window is not found then open a new window. If 'newwin' is TRUE, then open
3463 * a new window.
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003464 * Returns OK if successfully jumped or opened a window. Returns FAIL if not
3465 * able to jump/open a window. Returns NOTDONE if a file is not associated
Yegappan Lakshmanan6d24a512022-08-27 20:59:57 +01003466 * with the entry. Returns QF_ABORT if the quickfix/location list was modified
3467 * by an autocmd.
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003468 */
3469 static int
Bram Moolenaarb2443732018-11-11 22:50:27 +01003470qf_jump_open_window(
3471 qf_info_T *qi,
3472 qfline_T *qf_ptr,
3473 int newwin,
3474 int *opened_window)
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003475{
Bram Moolenaar4d170af2020-09-13 22:21:22 +02003476 qf_list_T *qfl = qf_get_curlist(qi);
3477 int old_changedtick = qfl->qf_changedtick;
3478 int old_qf_curlist = qi->qf_curlist;
3479 qfltype_T qfl_type = qfl->qfl_type;
3480
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003481 // For ":helpgrep" find a help window or open one.
Bram Moolenaare1004402020-10-24 20:49:43 +02003482 if (qf_ptr->qf_type == 1 && (!bt_help(curwin->w_buffer)
3483 || cmdmod.cmod_tab != 0))
Bram Moolenaarb2443732018-11-11 22:50:27 +01003484 if (jump_to_help_window(qi, newwin, opened_window) == FAIL)
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003485 return FAIL;
Bram Moolenaar4d170af2020-09-13 22:21:22 +02003486 if (old_qf_curlist != qi->qf_curlist
3487 || old_changedtick != qfl->qf_changedtick
3488 || !is_qf_entry_present(qfl, qf_ptr))
3489 {
3490 if (qfl_type == QFLT_QUICKFIX)
3491 emsg(_(e_current_quickfix_list_was_changed));
3492 else
3493 emsg(_(e_current_location_list_was_changed));
Yegappan Lakshmanan6d24a512022-08-27 20:59:57 +01003494 return QF_ABORT;
Bram Moolenaar4d170af2020-09-13 22:21:22 +02003495 }
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003496
3497 // If currently in the quickfix window, find another window to show the
3498 // file in.
3499 if (bt_quickfix(curbuf) && !*opened_window)
3500 {
3501 // If there is no file specified, we don't know where to go.
3502 // But do advance, otherwise ":cn" gets stuck.
3503 if (qf_ptr->qf_fnum == 0)
3504 return NOTDONE;
3505
Bram Moolenaarb2443732018-11-11 22:50:27 +01003506 if (qf_jump_to_usable_window(qf_ptr->qf_fnum, newwin,
3507 opened_window) == FAIL)
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003508 return FAIL;
3509 }
Bram Moolenaar4d170af2020-09-13 22:21:22 +02003510 if (old_qf_curlist != qi->qf_curlist
3511 || old_changedtick != qfl->qf_changedtick
3512 || !is_qf_entry_present(qfl, qf_ptr))
3513 {
3514 if (qfl_type == QFLT_QUICKFIX)
3515 emsg(_(e_current_quickfix_list_was_changed));
3516 else
3517 emsg(_(e_current_location_list_was_changed));
Yegappan Lakshmanan6d24a512022-08-27 20:59:57 +01003518 return QF_ABORT;
Bram Moolenaar4d170af2020-09-13 22:21:22 +02003519 }
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003520
3521 return OK;
3522}
3523
3524/*
3525 * Edit a selected file from the quickfix/location list and jump to a
3526 * particular line/column, adjust the folds and display a message about the
3527 * jump.
3528 * Returns OK on success and FAIL on failing to open the file/buffer. Returns
Yegappan Lakshmanan6d24a512022-08-27 20:59:57 +01003529 * QF_ABORT if the quickfix/location list is freed by an autocmd when opening
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003530 * the file.
3531 */
3532 static int
3533qf_jump_to_buffer(
3534 qf_info_T *qi,
3535 int qf_index,
3536 qfline_T *qf_ptr,
3537 int forceit,
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01003538 int prev_winid,
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003539 int *opened_window,
3540 int openfold,
3541 int print_message)
3542{
3543 buf_T *old_curbuf;
3544 linenr_T old_lnum;
3545 int retval = OK;
3546
3547 // If there is a file name, read the wanted file if needed, and check
3548 // autowrite etc.
3549 old_curbuf = curbuf;
3550 old_lnum = curwin->w_cursor.lnum;
3551
3552 if (qf_ptr->qf_fnum != 0)
3553 {
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01003554 retval = qf_jump_edit_buffer(qi, qf_ptr, forceit, prev_winid,
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003555 opened_window);
3556 if (retval != OK)
3557 return retval;
3558 }
3559
3560 // When not switched to another buffer, still need to set pc mark
3561 if (curbuf == old_curbuf)
3562 setpcmark();
3563
3564 qf_jump_goto_line(qf_ptr->qf_lnum, qf_ptr->qf_col, qf_ptr->qf_viscol,
3565 qf_ptr->qf_pattern);
3566
3567#ifdef FEAT_FOLDING
3568 if ((fdo_flags & FDO_QUICKFIX) && openfold)
3569 foldOpenCursor();
3570#endif
3571 if (print_message)
3572 qf_jump_print_msg(qi, qf_index, qf_ptr, old_curbuf, old_lnum);
3573
3574 return retval;
3575}
3576
3577/*
Bram Moolenaar5b69c222019-01-11 14:50:06 +01003578 * Jump to a quickfix line and try to use an existing window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003579 */
3580 void
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02003581qf_jump(qf_info_T *qi,
3582 int dir,
3583 int errornr,
3584 int forceit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003585{
Bram Moolenaarb2443732018-11-11 22:50:27 +01003586 qf_jump_newwin(qi, dir, errornr, forceit, FALSE);
3587}
3588
3589/*
Bram Moolenaar5b69c222019-01-11 14:50:06 +01003590 * Jump to a quickfix line.
3591 * If dir == 0 go to entry "errornr".
3592 * If dir == FORWARD go "errornr" valid entries forward.
3593 * If dir == BACKWARD go "errornr" valid entries backward.
3594 * If dir == FORWARD_FILE go "errornr" valid entries files backward.
3595 * If dir == BACKWARD_FILE go "errornr" valid entries files backward
3596 * else if "errornr" is zero, redisplay the same line
3597 * If 'forceit' is TRUE, then can discard changes to the current buffer.
Bram Moolenaarb2443732018-11-11 22:50:27 +01003598 * If 'newwin' is TRUE, then open the file in a new window.
3599 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02003600 static void
Bram Moolenaarb2443732018-11-11 22:50:27 +01003601qf_jump_newwin(qf_info_T *qi,
3602 int dir,
3603 int errornr,
3604 int forceit,
3605 int newwin)
3606{
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02003607 qf_list_T *qfl;
Bram Moolenaar68b76a62005-03-25 21:53:48 +00003608 qfline_T *qf_ptr;
3609 qfline_T *old_qf_ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003610 int qf_index;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003611 int old_qf_index;
Bram Moolenaar71fe80d2006-01-22 23:25:56 +00003612 char_u *old_swb = p_swb;
Bram Moolenaar446cb832008-06-24 21:56:24 +00003613 unsigned old_swb_flags = swb_flags;
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01003614 int prev_winid;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003615 int opened_window = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003616 int print_message = TRUE;
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003617 int old_KeyTyped = KeyTyped; // getting file may reset it
Bram Moolenaar9cb03712017-09-20 22:43:02 +02003618 int retval = OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003619
Bram Moolenaar8b6144b2006-02-08 09:20:24 +00003620 if (qi == NULL)
3621 qi = &ql_info;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00003622
Bram Moolenaar0398e002019-03-21 21:12:49 +01003623 if (qf_stack_empty(qi) || qf_list_empty(qf_get_curlist(qi)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003624 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02003625 emsg(_(e_no_errors));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003626 return;
3627 }
3628
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01003629 incr_quickfix_busy();
3630
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01003631 qfl = qf_get_curlist(qi);
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02003632
3633 qf_ptr = qfl->qf_ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003634 old_qf_ptr = qf_ptr;
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02003635 qf_index = qfl->qf_index;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003636 old_qf_index = qf_index;
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003637
3638 qf_ptr = qf_get_entry(qfl, errornr, dir, &qf_index);
3639 if (qf_ptr == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003640 {
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003641 qf_ptr = old_qf_ptr;
3642 qf_index = old_qf_index;
3643 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003644 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003645
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02003646 qfl->qf_index = qf_index;
Wei-Chung Wen1557b162021-07-15 13:13:39 +02003647 qfl->qf_ptr = qf_ptr;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00003648 if (qf_win_pos_update(qi, old_qf_index))
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003649 // No need to print the error message if it's visible in the error
3650 // window
Bram Moolenaar071d4272004-06-13 20:20:40 +00003651 print_message = FALSE;
3652
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01003653 prev_winid = curwin->w_id;
3654
Bram Moolenaarb2443732018-11-11 22:50:27 +01003655 retval = qf_jump_open_window(qi, qf_ptr, newwin, &opened_window);
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003656 if (retval == FAIL)
3657 goto failed;
Yegappan Lakshmanan6d24a512022-08-27 20:59:57 +01003658 if (retval == QF_ABORT)
3659 {
3660 qi = NULL;
3661 qf_ptr = NULL;
3662 goto theend;
3663 }
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003664 if (retval == NOTDONE)
3665 goto theend;
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00003666
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01003667 retval = qf_jump_to_buffer(qi, qf_index, qf_ptr, forceit, prev_winid,
Bram Moolenaard570ab92019-09-03 23:20:05 +02003668 &opened_window, old_KeyTyped, print_message);
Yegappan Lakshmanan6d24a512022-08-27 20:59:57 +01003669 if (retval == QF_ABORT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003670 {
Yegappan Lakshmanan6d24a512022-08-27 20:59:57 +01003671 // Quickfix/location list was modified by an autocmd
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003672 qi = NULL;
3673 qf_ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003674 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003675
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003676 if (retval != OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003677 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003678 if (opened_window)
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003679 win_close(curwin, TRUE); // Close opened window
Bram Moolenaar0899d692016-03-19 13:35:03 +01003680 if (qf_ptr != NULL && qf_ptr->qf_fnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003681 {
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003682 // Couldn't open file, so put index back where it was. This could
3683 // happen if the file was readonly and we changed something.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003684failed:
Bram Moolenaar071d4272004-06-13 20:20:40 +00003685 qf_ptr = old_qf_ptr;
3686 qf_index = old_qf_index;
3687 }
3688 }
3689theend:
Bram Moolenaar0899d692016-03-19 13:35:03 +01003690 if (qi != NULL)
3691 {
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02003692 qfl->qf_ptr = qf_ptr;
3693 qfl->qf_index = qf_index;
Bram Moolenaar0899d692016-03-19 13:35:03 +01003694 }
Bram Moolenaarf9ae1542019-11-18 22:02:16 +01003695 if (p_swb != old_swb && p_swb == empty_option)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003696 {
Bram Moolenaar6dae96e2018-09-24 21:50:12 +02003697 // Restore old 'switchbuf' value, but not when an autocommand or
3698 // modeline has changed the value.
Bram Moolenaarf9ae1542019-11-18 22:02:16 +01003699 p_swb = old_swb;
3700 swb_flags = old_swb_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003701 }
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01003702 decr_quickfix_busy();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003703}
3704
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003705// Highlight attributes used for displaying entries from the quickfix list.
Bram Moolenaarde3b3672018-08-07 21:54:41 +02003706static int qfFileAttr;
3707static int qfSepAttr;
3708static int qfLineAttr;
3709
3710/*
3711 * Display information about a single entry from the quickfix/location list.
3712 * Used by ":clist/:llist" commands.
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02003713 * 'cursel' will be set to TRUE for the currently selected entry in the
3714 * quickfix list.
Bram Moolenaarde3b3672018-08-07 21:54:41 +02003715 */
3716 static void
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02003717qf_list_entry(qfline_T *qfp, int qf_idx, int cursel)
Bram Moolenaarde3b3672018-08-07 21:54:41 +02003718{
3719 char_u *fname;
3720 buf_T *buf;
3721 int filter_entry;
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01003722 garray_T *gap;
Bram Moolenaarde3b3672018-08-07 21:54:41 +02003723
3724 fname = NULL;
3725 if (qfp->qf_module != NULL && *qfp->qf_module != NUL)
3726 vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", qf_idx,
3727 (char *)qfp->qf_module);
Bram Moolenaarebfec1c2023-01-22 21:14:53 +00003728 else
3729 {
Bram Moolenaarde3b3672018-08-07 21:54:41 +02003730 if (qfp->qf_fnum != 0
3731 && (buf = buflist_findnr(qfp->qf_fnum)) != NULL)
3732 {
Austin Chang29822992024-10-03 10:50:05 +02003733 if (qfp->qf_fname == NULL)
3734 fname = buf->b_fname;
3735 else
3736 fname = qfp->qf_fname;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003737 if (qfp->qf_type == 1) // :helpgrep
Bram Moolenaarde3b3672018-08-07 21:54:41 +02003738 fname = gettail(fname);
3739 }
3740 if (fname == NULL)
3741 sprintf((char *)IObuff, "%2d", qf_idx);
3742 else
3743 vim_snprintf((char *)IObuff, IOSIZE, "%2d %s",
3744 qf_idx, (char *)fname);
3745 }
3746
3747 // Support for filtering entries using :filter /pat/ clist
3748 // Match against the module name, file name, search pattern and
3749 // text of the entry.
3750 filter_entry = TRUE;
3751 if (qfp->qf_module != NULL && *qfp->qf_module != NUL)
3752 filter_entry &= message_filtered(qfp->qf_module);
3753 if (filter_entry && fname != NULL)
3754 filter_entry &= message_filtered(fname);
3755 if (filter_entry && qfp->qf_pattern != NULL)
3756 filter_entry &= message_filtered(qfp->qf_pattern);
3757 if (filter_entry)
3758 filter_entry &= message_filtered(qfp->qf_text);
3759 if (filter_entry)
3760 return;
3761
3762 msg_putchar('\n');
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02003763 msg_outtrans_attr(IObuff, cursel ? HL_ATTR(HLF_QFL) : qfFileAttr);
Bram Moolenaarde3b3672018-08-07 21:54:41 +02003764
3765 if (qfp->qf_lnum != 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003766 msg_puts_attr(":", qfSepAttr);
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01003767 gap = qfga_get();
Shane Harper5bf04282023-06-07 19:09:57 +01003768 if (qfp->qf_lnum != 0)
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01003769 qf_range_text(gap, qfp);
3770 ga_concat(gap, qf_types(qfp->qf_type, qfp->qf_nr));
3771 ga_append(gap, NUL);
3772 msg_puts_attr((char *)gap->ga_data, qfLineAttr);
Bram Moolenaar32526b32019-01-19 17:43:09 +01003773 msg_puts_attr(":", qfSepAttr);
Bram Moolenaarde3b3672018-08-07 21:54:41 +02003774 if (qfp->qf_pattern != NULL)
3775 {
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01003776 gap = qfga_get();
3777 qf_fmt_text(gap, qfp->qf_pattern);
Shane Harper5bf04282023-06-07 19:09:57 +01003778 ga_append(gap, NUL);
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01003779 msg_puts((char *)gap->ga_data);
Bram Moolenaar32526b32019-01-19 17:43:09 +01003780 msg_puts_attr(":", qfSepAttr);
Bram Moolenaarde3b3672018-08-07 21:54:41 +02003781 }
Bram Moolenaar32526b32019-01-19 17:43:09 +01003782 msg_puts(" ");
Bram Moolenaarde3b3672018-08-07 21:54:41 +02003783
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01003784 // Remove newlines and leading whitespace from the text. For an
3785 // unrecognized line keep the indent, the compiler may mark a word
3786 // with ^^^^.
3787 gap = qfga_get();
3788 qf_fmt_text(gap, (fname != NULL || qfp->qf_lnum != 0)
Shane Harper5bf04282023-06-07 19:09:57 +01003789 ? skipwhite(qfp->qf_text) : qfp->qf_text);
3790 ga_append(gap, NUL);
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01003791 msg_prt_line((char_u *)gap->ga_data, FALSE);
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003792 out_flush(); // show one line at a time
Bram Moolenaarde3b3672018-08-07 21:54:41 +02003793}
3794
3795/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003796 * ":clist": list all errors
Bram Moolenaard12f5c12006-01-25 22:10:52 +00003797 * ":llist": list all locations
Bram Moolenaar071d4272004-06-13 20:20:40 +00003798 */
3799 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003800qf_list(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003801{
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02003802 qf_list_T *qfl;
Bram Moolenaar68b76a62005-03-25 21:53:48 +00003803 qfline_T *qfp;
3804 int i;
3805 int idx1 = 1;
3806 int idx2 = -1;
Bram Moolenaar68b76a62005-03-25 21:53:48 +00003807 char_u *arg = eap->arg;
Bram Moolenaare8fea072016-07-01 14:48:27 +02003808 int plus = FALSE;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003809 int all = eap->forceit; // if not :cl!, only show
3810 // recognised errors
Bram Moolenaar87f59b02019-04-04 14:04:11 +02003811 qf_info_T *qi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003812
Bram Moolenaar87f59b02019-04-04 14:04:11 +02003813 if ((qi = qf_cmd_get_stack(eap, TRUE)) == NULL)
3814 return;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00003815
Bram Moolenaar0398e002019-03-21 21:12:49 +01003816 if (qf_stack_empty(qi) || qf_list_empty(qf_get_curlist(qi)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003817 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02003818 emsg(_(e_no_errors));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003819 return;
3820 }
Bram Moolenaare8fea072016-07-01 14:48:27 +02003821 if (*arg == '+')
3822 {
3823 ++arg;
3824 plus = TRUE;
3825 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003826 if (!get_list_range(&arg, &idx1, &idx2) || *arg != NUL)
3827 {
Bram Moolenaar74409f62022-01-01 15:58:22 +00003828 semsg(_(e_trailing_characters_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003829 return;
3830 }
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01003831 qfl = qf_get_curlist(qi);
Bram Moolenaare8fea072016-07-01 14:48:27 +02003832 if (plus)
3833 {
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02003834 i = qfl->qf_index;
Bram Moolenaare8fea072016-07-01 14:48:27 +02003835 idx2 = i + idx1;
3836 idx1 = i;
3837 }
3838 else
3839 {
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02003840 i = qfl->qf_count;
Bram Moolenaare8fea072016-07-01 14:48:27 +02003841 if (idx1 < 0)
3842 idx1 = (-idx1 > i) ? 0 : idx1 + i + 1;
3843 if (idx2 < 0)
3844 idx2 = (-idx2 > i) ? 0 : idx2 + i + 1;
3845 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003846
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003847 // Shorten all the file names, so that it is easy to read
Bram Moolenaara796d462018-05-01 14:30:36 +02003848 shorten_fnames(FALSE);
3849
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02003850 // Get the attributes for the different quickfix highlight items. Note
3851 // that this depends on syntax items defined in the qf.vim syntax file
Bram Moolenaar93a32e22017-11-23 22:05:45 +01003852 qfFileAttr = syn_name2attr((char_u *)"qfFileName");
3853 if (qfFileAttr == 0)
3854 qfFileAttr = HL_ATTR(HLF_D);
3855 qfSepAttr = syn_name2attr((char_u *)"qfSeparator");
3856 if (qfSepAttr == 0)
3857 qfSepAttr = HL_ATTR(HLF_D);
3858 qfLineAttr = syn_name2attr((char_u *)"qfLineNr");
3859 if (qfLineAttr == 0)
3860 qfLineAttr = HL_ATTR(HLF_N);
3861
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02003862 if (qfl->qf_nonevalid)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003863 all = TRUE;
Bram Moolenaar95946f12019-03-31 15:31:59 +02003864 FOR_ALL_QFL_ITEMS(qfl, qfp, i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003865 {
3866 if ((qfp->qf_valid || all) && idx1 <= i && i <= idx2)
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02003867 qf_list_entry(qfp, i, i == qfl->qf_index);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00003868
Bram Moolenaar071d4272004-06-13 20:20:40 +00003869 ui_breakcheck();
3870 }
Yegappan Lakshmanand8cd6f72022-10-16 11:30:48 +01003871 qfga_clear();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003872}
3873
3874/*
3875 * Remove newlines and leading whitespace from an error message.
Yegappan Lakshmananf8412c92022-10-13 11:59:22 +01003876 * Add the result to the grow array "gap".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003877 */
3878 static void
Yegappan Lakshmananf8412c92022-10-13 11:59:22 +01003879qf_fmt_text(garray_T *gap, char_u *text)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003880{
Bram Moolenaar071d4272004-06-13 20:20:40 +00003881 char_u *p = text;
Yegappan Lakshmananf8412c92022-10-13 11:59:22 +01003882 while (*p != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003883 {
3884 if (*p == '\n')
3885 {
Yegappan Lakshmananf8412c92022-10-13 11:59:22 +01003886 ga_append(gap, ' ');
Bram Moolenaar071d4272004-06-13 20:20:40 +00003887 while (*++p != NUL)
Bram Moolenaar1c465442017-03-12 20:10:05 +01003888 if (!VIM_ISWHITE(*p) && *p != '\n')
Bram Moolenaar071d4272004-06-13 20:20:40 +00003889 break;
3890 }
3891 else
Yegappan Lakshmananf8412c92022-10-13 11:59:22 +01003892 ga_append(gap, *p++);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003893 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003894}
3895
Bram Moolenaar18cebf42018-05-08 22:31:37 +02003896/*
Yegappan Lakshmananf8412c92022-10-13 11:59:22 +01003897 * Add the range information from the lnum, col, end_lnum, and end_col values
3898 * of a quickfix entry to the grow array "gap".
thinca6864efa2021-06-19 20:45:20 +02003899 */
3900 static void
Yegappan Lakshmananf8412c92022-10-13 11:59:22 +01003901qf_range_text(garray_T *gap, qfline_T *qfp)
thinca6864efa2021-06-19 20:45:20 +02003902{
Yegappan Lakshmananf8412c92022-10-13 11:59:22 +01003903 char_u *buf = IObuff;
3904 int bufsize = IOSIZE;
thinca6864efa2021-06-19 20:45:20 +02003905 int len;
Yegappan Lakshmananf8412c92022-10-13 11:59:22 +01003906
thinca6864efa2021-06-19 20:45:20 +02003907 vim_snprintf((char *)buf, bufsize, "%ld", qfp->qf_lnum);
3908 len = (int)STRLEN(buf);
3909
3910 if (qfp->qf_end_lnum > 0 && qfp->qf_lnum != qfp->qf_end_lnum)
3911 {
Shane Harper5bf04282023-06-07 19:09:57 +01003912 vim_snprintf((char *)buf + len, bufsize - len, "-%ld",
3913 qfp->qf_end_lnum);
thinca6864efa2021-06-19 20:45:20 +02003914 len += (int)STRLEN(buf + len);
3915 }
3916 if (qfp->qf_col > 0)
3917 {
3918 vim_snprintf((char *)buf + len, bufsize - len, " col %d", qfp->qf_col);
3919 len += (int)STRLEN(buf + len);
3920 if (qfp->qf_end_col > 0 && qfp->qf_col != qfp->qf_end_col)
3921 {
Shane Harper5bf04282023-06-07 19:09:57 +01003922 vim_snprintf((char *)buf + len, bufsize - len, "-%d",
3923 qfp->qf_end_col);
thinca6864efa2021-06-19 20:45:20 +02003924 len += (int)STRLEN(buf + len);
3925 }
3926 }
Yegappan Lakshmananf8412c92022-10-13 11:59:22 +01003927
3928 ga_concat_len(gap, buf, len);
thinca6864efa2021-06-19 20:45:20 +02003929}
3930
3931/*
Bram Moolenaar18cebf42018-05-08 22:31:37 +02003932 * Display information (list number, list size and the title) about a
3933 * quickfix/location list.
3934 */
Bram Moolenaarf6acffb2016-07-16 16:54:24 +02003935 static void
3936qf_msg(qf_info_T *qi, int which, char *lead)
3937{
3938 char *title = (char *)qi->qf_lists[which].qf_title;
3939 int count = qi->qf_lists[which].qf_count;
3940 char_u buf[IOSIZE];
3941
3942 vim_snprintf((char *)buf, IOSIZE, _("%serror list %d of %d; %d errors "),
3943 lead,
3944 which + 1,
3945 qi->qf_listcount,
3946 count);
3947
3948 if (title != NULL)
3949 {
Bram Moolenaar16ec3c92016-07-18 22:22:39 +02003950 size_t len = STRLEN(buf);
3951
3952 if (len < 34)
3953 {
3954 vim_memset(buf + len, ' ', 34 - len);
3955 buf[34] = NUL;
3956 }
3957 vim_strcat(buf, (char_u *)title, IOSIZE);
Bram Moolenaarf6acffb2016-07-16 16:54:24 +02003958 }
3959 trunc_string(buf, buf, Columns - 1, IOSIZE);
Bram Moolenaar32526b32019-01-19 17:43:09 +01003960 msg((char *)buf);
Bram Moolenaarf6acffb2016-07-16 16:54:24 +02003961}
3962
Bram Moolenaar071d4272004-06-13 20:20:40 +00003963/*
3964 * ":colder [count]": Up in the quickfix stack.
3965 * ":cnewer [count]": Down in the quickfix stack.
Bram Moolenaard12f5c12006-01-25 22:10:52 +00003966 * ":lolder [count]": Up in the location list stack.
3967 * ":lnewer [count]": Down in the location list stack.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003968 */
3969 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003970qf_age(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003971{
Bram Moolenaar87f59b02019-04-04 14:04:11 +02003972 qf_info_T *qi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003973 int count;
3974
Bram Moolenaar87f59b02019-04-04 14:04:11 +02003975 if ((qi = qf_cmd_get_stack(eap, TRUE)) == NULL)
3976 return;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00003977
Bram Moolenaar071d4272004-06-13 20:20:40 +00003978 if (eap->addr_count != 0)
3979 count = eap->line2;
3980 else
3981 count = 1;
3982 while (count--)
3983 {
Bram Moolenaard12f5c12006-01-25 22:10:52 +00003984 if (eap->cmdidx == CMD_colder || eap->cmdidx == CMD_lolder)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003985 {
Bram Moolenaard12f5c12006-01-25 22:10:52 +00003986 if (qi->qf_curlist == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003987 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003988 emsg(_(e_at_bottom_of_quickfix_stack));
Bram Moolenaar82e803b2013-05-11 15:50:33 +02003989 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003990 }
Bram Moolenaard12f5c12006-01-25 22:10:52 +00003991 --qi->qf_curlist;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003992 }
3993 else
3994 {
Bram Moolenaard12f5c12006-01-25 22:10:52 +00003995 if (qi->qf_curlist >= qi->qf_listcount - 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003996 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003997 emsg(_(e_at_top_of_quickfix_stack));
Bram Moolenaar82e803b2013-05-11 15:50:33 +02003998 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003999 }
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004000 ++qi->qf_curlist;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004001 }
4002 }
Bram Moolenaarf6acffb2016-07-16 16:54:24 +02004003 qf_msg(qi, qi->qf_curlist, "");
Bram Moolenaar864293a2016-06-02 13:40:04 +02004004 qf_update_buffer(qi, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004005}
4006
Bram Moolenaar18cebf42018-05-08 22:31:37 +02004007/*
4008 * Display the information about all the quickfix/location lists in the stack
4009 */
Bram Moolenaarf6acffb2016-07-16 16:54:24 +02004010 void
4011qf_history(exarg_T *eap)
4012{
Bram Moolenaar87f59b02019-04-04 14:04:11 +02004013 qf_info_T *qi = qf_cmd_get_stack(eap, FALSE);
Bram Moolenaarf6acffb2016-07-16 16:54:24 +02004014 int i;
4015
Bram Moolenaar8ffc7c82019-05-05 21:00:26 +02004016 if (eap->addr_count > 0)
4017 {
4018 if (qi == NULL)
4019 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004020 emsg(_(e_no_location_list));
Bram Moolenaar8ffc7c82019-05-05 21:00:26 +02004021 return;
4022 }
4023
4024 // Jump to the specified quickfix list
4025 if (eap->line2 > 0 && eap->line2 <= qi->qf_listcount)
4026 {
4027 qi->qf_curlist = eap->line2 - 1;
4028 qf_msg(qi, qi->qf_curlist, "");
4029 qf_update_buffer(qi, NULL);
4030 }
4031 else
Bram Moolenaar108010a2021-06-27 22:03:33 +02004032 emsg(_(e_invalid_range));
Bram Moolenaar8ffc7c82019-05-05 21:00:26 +02004033
4034 return;
4035 }
4036
Bram Moolenaar5b69c222019-01-11 14:50:06 +01004037 if (qf_stack_empty(qi))
Bram Moolenaar32526b32019-01-19 17:43:09 +01004038 msg(_("No entries"));
Bram Moolenaarf6acffb2016-07-16 16:54:24 +02004039 else
4040 for (i = 0; i < qi->qf_listcount; ++i)
4041 qf_msg(qi, i, i == qi->qf_curlist ? "> " : " ");
4042}
4043
Bram Moolenaar071d4272004-06-13 20:20:40 +00004044/*
Bram Moolenaar6a8958d2017-06-22 21:33:20 +02004045 * Free all the entries in the error list "idx". Note that other information
4046 * associated with the list like context and title are not freed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004047 */
4048 static void
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02004049qf_free_items(qf_list_T *qfl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004050{
Bram Moolenaar68b76a62005-03-25 21:53:48 +00004051 qfline_T *qfp;
Bram Moolenaar83e6d7a2016-06-02 22:08:05 +02004052 qfline_T *qfpnext;
Bram Moolenaar81484f42012-12-05 15:16:47 +01004053 int stop = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004054
Bram Moolenaara7df8c72017-07-19 13:23:06 +02004055 while (qfl->qf_count && qfl->qf_start != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004056 {
Bram Moolenaara7df8c72017-07-19 13:23:06 +02004057 qfp = qfl->qf_start;
Bram Moolenaar83e6d7a2016-06-02 22:08:05 +02004058 qfpnext = qfp->qf_next;
Bram Moolenaar7adf06f2017-08-27 15:23:41 +02004059 if (!stop)
Bram Moolenaarc83a44b2012-11-28 15:25:34 +01004060 {
Austin Chang29822992024-10-03 10:50:05 +02004061 vim_free(qfp->qf_fname);
Bram Moolenaard76ce852018-05-01 15:02:04 +02004062 vim_free(qfp->qf_module);
Bram Moolenaar83e6d7a2016-06-02 22:08:05 +02004063 vim_free(qfp->qf_text);
Bram Moolenaar83e6d7a2016-06-02 22:08:05 +02004064 vim_free(qfp->qf_pattern);
Tom Praschanca6ac992023-08-11 23:26:12 +02004065 clear_tv(&qfp->qf_user_data);
Bram Moolenaard76ce852018-05-01 15:02:04 +02004066 stop = (qfp == qfpnext);
Bram Moolenaar83e6d7a2016-06-02 22:08:05 +02004067 vim_free(qfp);
Bram Moolenaar81484f42012-12-05 15:16:47 +01004068 if (stop)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02004069 // Somehow qf_count may have an incorrect value, set it to 1
4070 // to avoid crashing when it's wrong.
4071 // TODO: Avoid qf_count being incorrect.
Bram Moolenaara7df8c72017-07-19 13:23:06 +02004072 qfl->qf_count = 1;
Christian Brabandt567cae22023-11-19 16:19:27 +01004073 else
4074 qfl->qf_start = qfpnext;
Bram Moolenaarc83a44b2012-11-28 15:25:34 +01004075 }
Bram Moolenaara7df8c72017-07-19 13:23:06 +02004076 --qfl->qf_count;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004077 }
Bram Moolenaar6a8958d2017-06-22 21:33:20 +02004078
Bram Moolenaara7df8c72017-07-19 13:23:06 +02004079 qfl->qf_index = 0;
4080 qfl->qf_start = NULL;
4081 qfl->qf_last = NULL;
4082 qfl->qf_ptr = NULL;
4083 qfl->qf_nonevalid = TRUE;
Bram Moolenaar361c8f02016-07-02 15:41:47 +02004084
Bram Moolenaara7df8c72017-07-19 13:23:06 +02004085 qf_clean_dir_stack(&qfl->qf_dir_stack);
4086 qfl->qf_directory = NULL;
4087 qf_clean_dir_stack(&qfl->qf_file_stack);
4088 qfl->qf_currfile = NULL;
4089 qfl->qf_multiline = FALSE;
4090 qfl->qf_multiignore = FALSE;
4091 qfl->qf_multiscan = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004092}
4093
4094/*
Bram Moolenaar6a8958d2017-06-22 21:33:20 +02004095 * Free error list "idx". Frees all the entries in the quickfix list,
4096 * associated context information and the title.
4097 */
4098 static void
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02004099qf_free(qf_list_T *qfl)
Bram Moolenaar6a8958d2017-06-22 21:33:20 +02004100{
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02004101 qf_free_items(qfl);
Bram Moolenaar6a8958d2017-06-22 21:33:20 +02004102
Bram Moolenaard23a8232018-02-10 18:45:26 +01004103 VIM_CLEAR(qfl->qf_title);
Bram Moolenaara7df8c72017-07-19 13:23:06 +02004104 free_tv(qfl->qf_ctx);
4105 qfl->qf_ctx = NULL;
=?UTF-8?q?Dundar=20G=C3=B6c?=b8366582022-04-14 20:43:56 +01004106 free_callback(&qfl->qf_qftf_cb);
Bram Moolenaara539f4f2017-08-30 20:33:55 +02004107 qfl->qf_id = 0;
Bram Moolenaarb254af32017-12-18 19:48:58 +01004108 qfl->qf_changedtick = 0L;
Bram Moolenaar6a8958d2017-06-22 21:33:20 +02004109}
4110
4111/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00004112 * qf_mark_adjust: adjust marks
4113 */
4114 void
Bram Moolenaar05540972016-01-30 20:31:25 +01004115qf_mark_adjust(
Bram Moolenaaref6b8de2017-09-14 13:57:37 +02004116 win_T *wp,
4117 linenr_T line1,
4118 linenr_T line2,
4119 long amount,
4120 long amount_after)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004121{
Bram Moolenaar68b76a62005-03-25 21:53:48 +00004122 int i;
4123 qfline_T *qfp;
4124 int idx;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004125 qf_info_T *qi = &ql_info;
Bram Moolenaar2f095a42016-06-03 19:05:49 +02004126 int found_one = FALSE;
Bram Moolenaarc1542742016-07-20 21:44:37 +02004127 int buf_has_flag = wp == NULL ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004128
Bram Moolenaarc1542742016-07-20 21:44:37 +02004129 if (!(curbuf->b_has_qf_entry & buf_has_flag))
Bram Moolenaar2f095a42016-06-03 19:05:49 +02004130 return;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004131 if (wp != NULL)
4132 {
4133 if (wp->w_llist == NULL)
4134 return;
4135 qi = wp->w_llist;
4136 }
4137
4138 for (idx = 0; idx < qi->qf_listcount; ++idx)
Bram Moolenaar0398e002019-03-21 21:12:49 +01004139 {
4140 qf_list_T *qfl = qf_get_list(qi, idx);
4141
4142 if (!qf_list_empty(qfl))
Bram Moolenaara16123a2019-03-28 20:31:07 +01004143 FOR_ALL_QFL_ITEMS(qfl, qfp, i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004144 if (qfp->qf_fnum == curbuf->b_fnum)
4145 {
Bram Moolenaar2f095a42016-06-03 19:05:49 +02004146 found_one = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004147 if (qfp->qf_lnum >= line1 && qfp->qf_lnum <= line2)
4148 {
4149 if (amount == MAXLNUM)
4150 qfp->qf_cleared = TRUE;
4151 else
4152 qfp->qf_lnum += amount;
4153 }
4154 else if (amount_after && qfp->qf_lnum > line2)
4155 qfp->qf_lnum += amount_after;
4156 }
Bram Moolenaar0398e002019-03-21 21:12:49 +01004157 }
Bram Moolenaar2f095a42016-06-03 19:05:49 +02004158
4159 if (!found_one)
Bram Moolenaarc1542742016-07-20 21:44:37 +02004160 curbuf->b_has_qf_entry &= ~buf_has_flag;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004161}
4162
4163/*
4164 * Make a nice message out of the error character and the error number:
4165 * char number message
4166 * e or E 0 " error"
4167 * w or W 0 " warning"
4168 * i or I 0 " info"
Bram Moolenaare9283662020-06-07 14:10:47 +02004169 * n or N 0 " note"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004170 * 0 0 ""
4171 * other 0 " c"
4172 * e or E n " error n"
4173 * w or W n " warning n"
4174 * i or I n " info n"
Bram Moolenaare9283662020-06-07 14:10:47 +02004175 * n or N n " note n"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004176 * 0 n " error n"
4177 * other n " c n"
4178 * 1 x "" :helpgrep
4179 */
4180 static char_u *
Bram Moolenaar05540972016-01-30 20:31:25 +01004181qf_types(int c, int nr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004182{
4183 static char_u buf[20];
4184 static char_u cc[3];
4185 char_u *p;
4186
4187 if (c == 'W' || c == 'w')
4188 p = (char_u *)" warning";
4189 else if (c == 'I' || c == 'i')
4190 p = (char_u *)" info";
Bram Moolenaare9283662020-06-07 14:10:47 +02004191 else if (c == 'N' || c == 'n')
4192 p = (char_u *)" note";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004193 else if (c == 'E' || c == 'e' || (c == 0 && nr > 0))
4194 p = (char_u *)" error";
4195 else if (c == 0 || c == 1)
4196 p = (char_u *)"";
4197 else
4198 {
4199 cc[0] = ' ';
4200 cc[1] = c;
4201 cc[2] = NUL;
4202 p = cc;
4203 }
4204
4205 if (nr <= 0)
4206 return p;
4207
4208 sprintf((char *)buf, "%s %3d", (char *)p, nr);
4209 return buf;
4210}
4211
Bram Moolenaar071d4272004-06-13 20:20:40 +00004212/*
Bram Moolenaar0a08c632018-07-25 22:36:52 +02004213 * When "split" is FALSE: Open the entry/result under the cursor.
4214 * When "split" is TRUE: Open the entry/result under the cursor in a new window.
4215 */
4216 void
4217qf_view_result(int split)
4218{
4219 qf_info_T *qi = &ql_info;
4220
Bram Moolenaar0a08c632018-07-25 22:36:52 +02004221 if (IS_LL_WINDOW(curwin))
4222 qi = GET_LOC_LIST(curwin);
4223
Bram Moolenaar0398e002019-03-21 21:12:49 +01004224 if (qf_list_empty(qf_get_curlist(qi)))
Bram Moolenaar0a08c632018-07-25 22:36:52 +02004225 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02004226 emsg(_(e_no_errors));
Bram Moolenaar0a08c632018-07-25 22:36:52 +02004227 return;
4228 }
4229
4230 if (split)
4231 {
Bram Moolenaarb2443732018-11-11 22:50:27 +01004232 // Open the selected entry in a new window
4233 qf_jump_newwin(qi, 0, (long)curwin->w_cursor.lnum, FALSE, TRUE);
4234 do_cmdline_cmd((char_u *) "clearjumps");
Bram Moolenaar0a08c632018-07-25 22:36:52 +02004235 return;
4236 }
4237
4238 do_cmdline_cmd((char_u *)(IS_LL_WINDOW(curwin) ? ".ll" : ".cc"));
4239}
4240
4241/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00004242 * ":cwindow": open the quickfix window if we have errors to display,
4243 * close it if not.
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004244 * ":lwindow": open the location list window if we have locations to display,
4245 * close it if not.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004246 */
4247 void
Bram Moolenaar05540972016-01-30 20:31:25 +01004248ex_cwindow(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004249{
Bram Moolenaar87f59b02019-04-04 14:04:11 +02004250 qf_info_T *qi;
Bram Moolenaar108e7b42018-10-11 17:39:12 +02004251 qf_list_T *qfl;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004252 win_T *win;
4253
Bram Moolenaar87f59b02019-04-04 14:04:11 +02004254 if ((qi = qf_cmd_get_stack(eap, TRUE)) == NULL)
4255 return;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004256
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01004257 qfl = qf_get_curlist(qi);
Bram Moolenaar108e7b42018-10-11 17:39:12 +02004258
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02004259 // Look for an existing quickfix window.
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004260 win = qf_find_win(qi);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004261
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02004262 // If a quickfix window is open but we have no errors to display,
4263 // close the window. If a quickfix window is not open, then open
4264 // it if we have errors; otherwise, leave it closed.
Bram Moolenaar019dfe62018-10-07 14:38:49 +02004265 if (qf_stack_empty(qi)
Bram Moolenaar108e7b42018-10-11 17:39:12 +02004266 || qfl->qf_nonevalid
Bram Moolenaar9afe5e92019-03-22 14:16:06 +01004267 || qf_list_empty(qfl))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004268 {
4269 if (win != NULL)
4270 ex_cclose(eap);
4271 }
4272 else if (win == NULL)
4273 ex_copen(eap);
4274}
4275
4276/*
4277 * ":cclose": close the window showing the list of errors.
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004278 * ":lclose": close the window showing the location list
Bram Moolenaar071d4272004-06-13 20:20:40 +00004279 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004280 void
Bram Moolenaar05540972016-01-30 20:31:25 +01004281ex_cclose(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004282{
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004283 win_T *win = NULL;
Bram Moolenaar87f59b02019-04-04 14:04:11 +02004284 qf_info_T *qi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004285
Bram Moolenaar87f59b02019-04-04 14:04:11 +02004286 if ((qi = qf_cmd_get_stack(eap, FALSE)) == NULL)
4287 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004288
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02004289 // Find existing quickfix window and close it.
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004290 win = qf_find_win(qi);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004291 if (win != NULL)
4292 win_close(win, FALSE);
4293}
4294
4295/*
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02004296 * Set "w:quickfix_title" if "qi" has a title.
4297 */
4298 static void
4299qf_set_title_var(qf_list_T *qfl)
4300{
4301 if (qfl->qf_title != NULL)
4302 set_internal_string_var((char_u *)"w:quickfix_title", qfl->qf_title);
4303}
4304
4305/*
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004306 * Goto a quickfix or location list window (if present).
4307 * Returns OK if the window is found, FAIL otherwise.
4308 */
4309 static int
4310qf_goto_cwindow(qf_info_T *qi, int resize, int sz, int vertsplit)
4311{
4312 win_T *win;
4313
4314 win = qf_find_win(qi);
4315 if (win == NULL)
4316 return FAIL;
4317
4318 win_goto(win);
4319 if (resize)
4320 {
4321 if (vertsplit)
4322 {
4323 if (sz != win->w_width)
4324 win_setwidth(sz);
4325 }
Bram Moolenaar1142a312019-10-16 14:51:39 +02004326 else if (sz != win->w_height && win->w_height
4327 + win->w_status_height + tabline_height() < cmdline_row)
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004328 win_setheight(sz);
4329 }
4330
4331 return OK;
4332}
4333
4334/*
Bram Moolenaard82a81c2019-03-02 07:57:18 +01004335 * Set options for the buffer in the quickfix or location list window.
4336 */
4337 static void
4338qf_set_cwindow_options(void)
4339{
4340 // switch off 'swapfile'
Bram Moolenaar31e5c602022-04-15 13:53:33 +01004341 set_option_value_give_err((char_u *)"swf", 0L, NULL, OPT_LOCAL);
4342 set_option_value_give_err((char_u *)"bt",
4343 0L, (char_u *)"quickfix", OPT_LOCAL);
4344 set_option_value_give_err((char_u *)"bh", 0L, (char_u *)"hide", OPT_LOCAL);
Bram Moolenaard82a81c2019-03-02 07:57:18 +01004345 RESET_BINDING(curwin);
4346#ifdef FEAT_DIFF
4347 curwin->w_p_diff = FALSE;
4348#endif
4349#ifdef FEAT_FOLDING
Bram Moolenaar31e5c602022-04-15 13:53:33 +01004350 set_option_value_give_err((char_u *)"fdm", 0L, (char_u *)"manual",
Bram Moolenaard82a81c2019-03-02 07:57:18 +01004351 OPT_LOCAL);
4352#endif
4353}
4354
4355/*
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004356 * Open a new quickfix or location list window, load the quickfix buffer and
4357 * set the appropriate options for the window.
4358 * Returns FAIL if the window could not be opened.
4359 */
4360 static int
4361qf_open_new_cwindow(qf_info_T *qi, int height)
4362{
4363 buf_T *qf_buf;
4364 win_T *oldwin = curwin;
4365 tabpage_T *prevtab = curtab;
4366 int flags = 0;
4367 win_T *win;
4368
4369 qf_buf = qf_find_buf(qi);
4370
4371 // The current window becomes the previous window afterwards.
4372 win = curwin;
4373
Bram Moolenaare1004402020-10-24 20:49:43 +02004374 if (IS_QF_STACK(qi) && cmdmod.cmod_split == 0)
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004375 // Create the new quickfix window at the very bottom, except when
4376 // :belowright or :aboveleft is used.
4377 win_goto(lastwin);
4378 // Default is to open the window below the current window
Bram Moolenaare1004402020-10-24 20:49:43 +02004379 if (cmdmod.cmod_split == 0)
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004380 flags = WSP_BELOW;
4381 flags |= WSP_NEWLOC;
4382 if (win_split(height, flags) == FAIL)
4383 return FAIL; // not enough room for window
4384 RESET_BINDING(curwin);
4385
4386 if (IS_LL_STACK(qi))
4387 {
4388 // For the location list window, create a reference to the
Bram Moolenaareeb1b9c2019-02-10 22:59:04 +01004389 // location list stack from the window 'win'.
4390 curwin->w_llist_ref = qi;
4391 qi->qf_refcount++;
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004392 }
4393
4394 if (oldwin != curwin)
4395 oldwin = NULL; // don't store info when in another window
4396 if (qf_buf != NULL)
4397 {
4398 // Use the existing quickfix buffer
Bram Moolenaar9e40c4b2020-11-23 20:15:08 +01004399 if (do_ecmd(qf_buf->b_fnum, NULL, NULL, NULL, ECMD_ONE,
Bram Moolenaar1d30fde2021-10-20 21:58:42 +01004400 ECMD_HIDE + ECMD_OLDBUF + ECMD_NOWINENTER, oldwin) == FAIL)
Bram Moolenaar9e40c4b2020-11-23 20:15:08 +01004401 return FAIL;
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004402 }
4403 else
4404 {
4405 // Create a new quickfix buffer
Bram Moolenaar1d30fde2021-10-20 21:58:42 +01004406 if (do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE + ECMD_NOWINENTER,
4407 oldwin) == FAIL)
Bram Moolenaar9e40c4b2020-11-23 20:15:08 +01004408 return FAIL;
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004409
Bram Moolenaaree8188f2019-02-05 21:23:04 +01004410 // save the number of the new buffer
4411 qi->qf_bufnr = curbuf->b_fnum;
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004412 }
4413
Bram Moolenaard82a81c2019-03-02 07:57:18 +01004414 // Set the options for the quickfix buffer/window (if not already done)
4415 // Do this even if the quickfix buffer was already present, as an autocmd
4416 // might have previously deleted (:bdelete) the quickfix buffer.
Bram Moolenaar61eeeea2019-06-15 21:56:17 +02004417 if (!bt_quickfix(curbuf))
Bram Moolenaard82a81c2019-03-02 07:57:18 +01004418 qf_set_cwindow_options();
4419
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004420 // Only set the height when still in the same tab page and there is no
4421 // window to the side.
4422 if (curtab == prevtab && curwin->w_width == Columns)
4423 win_setheight(height);
4424 curwin->w_p_wfh = TRUE; // set 'winfixheight'
4425 if (win_valid(win))
4426 prevwin = win;
4427
4428 return OK;
4429}
4430
4431/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00004432 * ":copen": open a window that shows the list of errors.
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004433 * ":lopen": open a window that shows the location list.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004434 */
4435 void
Bram Moolenaar05540972016-01-30 20:31:25 +01004436ex_copen(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004437{
Bram Moolenaar87f59b02019-04-04 14:04:11 +02004438 qf_info_T *qi;
Bram Moolenaar108e7b42018-10-11 17:39:12 +02004439 qf_list_T *qfl;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004440 int height;
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004441 int status = FAIL;
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02004442 int lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004443
Bram Moolenaar87f59b02019-04-04 14:04:11 +02004444 if ((qi = qf_cmd_get_stack(eap, TRUE)) == NULL)
4445 return;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004446
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02004447 incr_quickfix_busy();
4448
Bram Moolenaar071d4272004-06-13 20:20:40 +00004449 if (eap->addr_count != 0)
4450 height = eap->line2;
4451 else
4452 height = QF_WINHEIGHT;
4453
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004454 reset_VIsual_and_resel(); // stop Visual mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00004455#ifdef FEAT_GUI
4456 need_mouse_correct = TRUE;
4457#endif
4458
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004459 // Find an existing quickfix window, or open a new one.
Bram Moolenaare1004402020-10-24 20:49:43 +02004460 if (cmdmod.cmod_tab == 0)
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004461 status = qf_goto_cwindow(qi, eap->addr_count != 0, height,
Bram Moolenaare1004402020-10-24 20:49:43 +02004462 cmdmod.cmod_split & WSP_VERT);
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004463 if (status == FAIL)
4464 if (qf_open_new_cwindow(qi, height) == FAIL)
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02004465 {
4466 decr_quickfix_busy();
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004467 return;
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02004468 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004469
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01004470 qfl = qf_get_curlist(qi);
Bram Moolenaar108e7b42018-10-11 17:39:12 +02004471 qf_set_title_var(qfl);
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02004472 // Save the current index here, as updating the quickfix buffer may free
4473 // the quickfix list
4474 lnum = qfl->qf_index;
Bram Moolenaar81278ef2015-05-04 12:34:22 +02004475
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004476 // Fill the buffer with the quickfix list.
Bram Moolenaar7ba5a7e2020-06-08 19:20:27 +02004477 qf_fill_buffer(qfl, curbuf, NULL, curwin->w_id);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004478
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02004479 decr_quickfix_busy();
4480
4481 curwin->w_cursor.lnum = lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004482 curwin->w_cursor.col = 0;
4483 check_cursor();
Bram Moolenaar476c0db2018-09-19 21:56:02 +02004484 update_topline(); // scroll to show the line
Bram Moolenaar071d4272004-06-13 20:20:40 +00004485}
4486
4487/*
Bram Moolenaardcb17002016-07-07 18:58:59 +02004488 * Move the cursor in the quickfix window to "lnum".
4489 */
4490 static void
4491qf_win_goto(win_T *win, linenr_T lnum)
4492{
4493 win_T *old_curwin = curwin;
4494
4495 curwin = win;
4496 curbuf = win->w_buffer;
4497 curwin->w_cursor.lnum = lnum;
4498 curwin->w_cursor.col = 0;
Bram Moolenaardcb17002016-07-07 18:58:59 +02004499 curwin->w_cursor.coladd = 0;
Bram Moolenaardcb17002016-07-07 18:58:59 +02004500 curwin->w_curswant = 0;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02004501 update_topline(); // scroll to show the line
Bram Moolenaara4d158b2022-08-14 14:17:45 +01004502 redraw_later(UPD_VALID);
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02004503 curwin->w_redr_status = TRUE; // update ruler
Bram Moolenaardcb17002016-07-07 18:58:59 +02004504 curwin = old_curwin;
4505 curbuf = curwin->w_buffer;
4506}
4507
4508/*
Bram Moolenaar537ef082016-07-09 17:56:19 +02004509 * :cbottom/:lbottom commands.
Bram Moolenaardcb17002016-07-07 18:58:59 +02004510 */
4511 void
Bram Moolenaar39665952018-08-15 20:59:48 +02004512ex_cbottom(exarg_T *eap)
Bram Moolenaardcb17002016-07-07 18:58:59 +02004513{
Bram Moolenaar87f59b02019-04-04 14:04:11 +02004514 qf_info_T *qi;
Bram Moolenaar537ef082016-07-09 17:56:19 +02004515 win_T *win;
Bram Moolenaardcb17002016-07-07 18:58:59 +02004516
Bram Moolenaar87f59b02019-04-04 14:04:11 +02004517 if ((qi = qf_cmd_get_stack(eap, TRUE)) == NULL)
4518 return;
Bram Moolenaar537ef082016-07-09 17:56:19 +02004519
4520 win = qf_find_win(qi);
Bram Moolenaardcb17002016-07-07 18:58:59 +02004521 if (win != NULL && win->w_cursor.lnum != win->w_buffer->b_ml.ml_line_count)
4522 qf_win_goto(win, win->w_buffer->b_ml.ml_line_count);
4523}
4524
4525/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00004526 * Return the number of the current entry (line number in the quickfix
4527 * window).
4528 */
4529 linenr_T
Bram Moolenaar05540972016-01-30 20:31:25 +01004530qf_current_entry(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004531{
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004532 qf_info_T *qi = &ql_info;
4533
4534 if (IS_LL_WINDOW(wp))
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02004535 // In the location list window, use the referenced location list
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004536 qi = wp->w_llist_ref;
4537
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01004538 return qf_get_curlist(qi)->qf_index;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004539}
4540
4541/*
4542 * Update the cursor position in the quickfix window to the current error.
4543 * Return TRUE if there is a quickfix window.
4544 */
4545 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01004546qf_win_pos_update(
4547 qf_info_T *qi,
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02004548 int old_qf_index) // previous qf_index or zero
Bram Moolenaar071d4272004-06-13 20:20:40 +00004549{
4550 win_T *win;
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01004551 int qf_index = qf_get_curlist(qi)->qf_index;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004552
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02004553 // Put the cursor on the current error in the quickfix window, so that
4554 // it's viewable.
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004555 win = qf_find_win(qi);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004556 if (win != NULL
4557 && qf_index <= win->w_buffer->b_ml.ml_line_count
4558 && old_qf_index != qf_index)
4559 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004560 if (qf_index > old_qf_index)
4561 {
Bram Moolenaardcb17002016-07-07 18:58:59 +02004562 win->w_redraw_top = old_qf_index;
4563 win->w_redraw_bot = qf_index;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004564 }
4565 else
4566 {
Bram Moolenaardcb17002016-07-07 18:58:59 +02004567 win->w_redraw_top = qf_index;
4568 win->w_redraw_bot = old_qf_index;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004569 }
Bram Moolenaardcb17002016-07-07 18:58:59 +02004570 qf_win_goto(win, qf_index);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004571 }
4572 return win != NULL;
4573}
4574
4575/*
Bram Moolenaar9c102382006-05-03 21:26:49 +00004576 * Check whether the given window is displaying the specified quickfix/location
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02004577 * stack.
Bram Moolenaar9c102382006-05-03 21:26:49 +00004578 */
4579 static int
Bram Moolenaar05540972016-01-30 20:31:25 +01004580is_qf_win(win_T *win, qf_info_T *qi)
Bram Moolenaar9c102382006-05-03 21:26:49 +00004581{
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02004582 // A window displaying the quickfix buffer will have the w_llist_ref field
4583 // set to NULL.
4584 // A window displaying a location list buffer will have the w_llist_ref
4585 // pointing to the location list.
Christian Brabandtfc682992023-09-03 20:20:52 +02004586 if (buf_valid(win->w_buffer) && bt_quickfix(win->w_buffer))
Bram Moolenaar4d77c652018-08-18 19:59:54 +02004587 if ((IS_QF_STACK(qi) && win->w_llist_ref == NULL)
4588 || (IS_LL_STACK(qi) && win->w_llist_ref == qi))
Bram Moolenaar9c102382006-05-03 21:26:49 +00004589 return TRUE;
4590
4591 return FALSE;
4592}
4593
4594/*
Yegappan Lakshmanan78a61062021-12-08 20:03:31 +00004595 * Find a window displaying the quickfix/location stack 'qi' in the current tab
4596 * page.
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004597 */
4598 static win_T *
Bram Moolenaar05540972016-01-30 20:31:25 +01004599qf_find_win(qf_info_T *qi)
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004600{
4601 win_T *win;
4602
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004603 FOR_ALL_WINDOWS(win)
Bram Moolenaar9c102382006-05-03 21:26:49 +00004604 if (is_qf_win(win, qi))
Bram Moolenaar2ec364e2018-01-27 11:52:13 +01004605 return win;
4606 return NULL;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004607}
4608
4609/*
Bram Moolenaar9c102382006-05-03 21:26:49 +00004610 * Find a quickfix buffer.
Yegappan Lakshmanan78a61062021-12-08 20:03:31 +00004611 * Searches in windows opened in all the tab pages.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004612 */
4613 static buf_T *
Bram Moolenaar05540972016-01-30 20:31:25 +01004614qf_find_buf(qf_info_T *qi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004615{
Bram Moolenaar9c102382006-05-03 21:26:49 +00004616 tabpage_T *tp;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004617 win_T *win;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004618
Bram Moolenaaree8188f2019-02-05 21:23:04 +01004619 if (qi->qf_bufnr != INVALID_QFBUFNR)
4620 {
4621 buf_T *qfbuf;
4622 qfbuf = buflist_findnr(qi->qf_bufnr);
4623 if (qfbuf != NULL)
4624 return qfbuf;
4625 // buffer is no longer present
4626 qi->qf_bufnr = INVALID_QFBUFNR;
4627 }
4628
Bram Moolenaar9c102382006-05-03 21:26:49 +00004629 FOR_ALL_TAB_WINDOWS(tp, win)
4630 if (is_qf_win(win, qi))
4631 return win->w_buffer;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004632
Bram Moolenaar9c102382006-05-03 21:26:49 +00004633 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004634}
4635
4636/*
Bram Moolenaard43906d2020-07-20 21:31:32 +02004637 * Process the 'quickfixtextfunc' option value.
Yegappan Lakshmanan777175b2021-11-18 22:08:57 +00004638 * Returns OK or FAIL.
Bram Moolenaard43906d2020-07-20 21:31:32 +02004639 */
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00004640 char *
Yegappan Lakshmananaf936912023-02-20 12:16:39 +00004641did_set_quickfixtextfunc(optset_T *args UNUSED)
Bram Moolenaard43906d2020-07-20 21:31:32 +02004642{
Yegappan Lakshmananf2e30d02023-01-30 13:04:42 +00004643 if (option_set_callback_func(p_qftf, &qftf_cb) == FAIL)
4644 return e_invalid_argument;
4645
4646 return NULL;
Bram Moolenaard43906d2020-07-20 21:31:32 +02004647}
4648
4649/*
Bram Moolenaar530bed92020-12-16 21:02:56 +01004650 * Update the w:quickfix_title variable in the quickfix/location list window in
4651 * all the tab pages.
Bram Moolenaard823fa92016-08-12 16:29:27 +02004652 */
4653 static void
4654qf_update_win_titlevar(qf_info_T *qi)
4655{
Bram Moolenaar530bed92020-12-16 21:02:56 +01004656 qf_list_T *qfl = qf_get_curlist(qi);
4657 tabpage_T *tp;
Bram Moolenaard823fa92016-08-12 16:29:27 +02004658 win_T *win;
Bram Moolenaar530bed92020-12-16 21:02:56 +01004659 win_T *save_curwin = curwin;
Bram Moolenaard823fa92016-08-12 16:29:27 +02004660
Bram Moolenaar530bed92020-12-16 21:02:56 +01004661 FOR_ALL_TAB_WINDOWS(tp, win)
Bram Moolenaard823fa92016-08-12 16:29:27 +02004662 {
Bram Moolenaar530bed92020-12-16 21:02:56 +01004663 if (is_qf_win(win, qi))
4664 {
4665 curwin = win;
4666 qf_set_title_var(qfl);
4667 }
Bram Moolenaard823fa92016-08-12 16:29:27 +02004668 }
Bram Moolenaar530bed92020-12-16 21:02:56 +01004669 curwin = save_curwin;
Bram Moolenaard823fa92016-08-12 16:29:27 +02004670}
4671
4672/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00004673 * Find the quickfix buffer. If it exists, update the contents.
4674 */
4675 static void
Bram Moolenaar864293a2016-06-02 13:40:04 +02004676qf_update_buffer(qf_info_T *qi, qfline_T *old_last)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004677{
4678 buf_T *buf;
Bram Moolenaarc95e3262011-08-10 18:36:54 +02004679 win_T *win;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004680 aco_save_T aco;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004681
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02004682 // Check if a buffer for the quickfix list exists. Update it.
Bram Moolenaard12f5c12006-01-25 22:10:52 +00004683 buf = qf_find_buf(qi);
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00004684 if (buf == NULL)
4685 return;
4686
4687 linenr_T old_line_count = buf->b_ml.ml_line_count;
4688 int qf_winid = 0;
4689
4690 if (IS_LL_STACK(qi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004691 {
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00004692 if (curwin->w_llist == qi)
4693 win = curwin;
4694 else
Yegappan Lakshmananad52f962021-06-19 18:22:53 +02004695 {
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00004696 // Find the file window (non-quickfix) with this location list
4697 win = qf_find_win_with_loclist(qi);
4698 if (win == NULL)
4699 // File window is not found. Find the location list window.
4700 win = qf_find_win(qi);
4701 if (win == NULL)
4702 return;
Yegappan Lakshmananad52f962021-06-19 18:22:53 +02004703 }
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00004704 qf_winid = win->w_id;
4705 }
Bram Moolenaar864293a2016-06-02 13:40:04 +02004706
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00004707 // autocommands may cause trouble
4708 incr_quickfix_busy();
Bram Moolenaard0fab102022-10-20 16:03:33 +01004709
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00004710 int do_fill = TRUE;
4711 if (old_last == NULL)
4712 {
4713 // set curwin/curbuf to buf and save a few things
4714 aucmd_prepbuf(&aco, buf);
4715 if (curbuf != buf)
4716 do_fill = FALSE; // failed to find a window for "buf"
4717 }
4718
4719 if (do_fill)
4720 {
4721 qf_update_win_titlevar(qi);
4722
4723 qf_fill_buffer(qf_get_curlist(qi), buf, old_last, qf_winid);
4724 ++CHANGEDTICK(buf);
4725
Bram Moolenaar864293a2016-06-02 13:40:04 +02004726 if (old_last == NULL)
4727 {
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00004728 (void)qf_win_pos_update(qi, 0);
4729
4730 // restore curwin/curbuf and a few other things
4731 aucmd_restbuf(&aco);
Bram Moolenaare76062c2022-11-28 18:51:43 +00004732 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004733 }
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00004734
4735 // Only redraw when added lines are visible. This avoids flickering
4736 // when the added lines are not visible.
4737 if ((win = qf_find_win(qi)) != NULL && old_line_count < win->w_botline)
4738 redraw_buf_later(buf, UPD_NOT_VALID);
4739
4740 // always called after incr_quickfix_busy()
4741 decr_quickfix_busy();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004742}
4743
Bram Moolenaar81278ef2015-05-04 12:34:22 +02004744/*
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02004745 * Add an error line to the quickfix buffer.
4746 */
4747 static int
Bram Moolenaar858ba062020-05-31 23:11:59 +02004748qf_buf_add_line(
Bram Moolenaar858ba062020-05-31 23:11:59 +02004749 buf_T *buf, // quickfix window buffer
4750 linenr_T lnum,
4751 qfline_T *qfp,
Bram Moolenaar7ba5a7e2020-06-08 19:20:27 +02004752 char_u *dirname,
Bram Moolenaar8ec92c92020-09-29 22:47:03 +02004753 int first_bufline,
Bram Moolenaar00e260b2020-06-11 19:35:52 +02004754 char_u *qftf_str)
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02004755{
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02004756 buf_T *errbuf;
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01004757 garray_T *gap;
Yegappan Lakshmananf8412c92022-10-13 11:59:22 +01004758
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01004759 gap = qfga_get();
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02004760
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00004761 // If the 'quickfixtextfunc' function returned a non-empty custom string
Bram Moolenaard43906d2020-07-20 21:31:32 +02004762 // for this entry, then use it.
4763 if (qftf_str != NULL && *qftf_str != NUL)
Bram Moolenaar2f7bfe62022-11-13 12:54:50 +00004764 {
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01004765 ga_concat(gap, qftf_str);
Bram Moolenaar2f7bfe62022-11-13 12:54:50 +00004766 }
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02004767 else
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02004768 {
Bram Moolenaar858ba062020-05-31 23:11:59 +02004769 if (qfp->qf_module != NULL)
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01004770 ga_concat(gap, qfp->qf_module);
Bram Moolenaar858ba062020-05-31 23:11:59 +02004771 else if (qfp->qf_fnum != 0
4772 && (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL
4773 && errbuf->b_fname != NULL)
4774 {
4775 if (qfp->qf_type == 1) // :helpgrep
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01004776 ga_concat(gap, gettail(errbuf->b_fname));
Bram Moolenaar858ba062020-05-31 23:11:59 +02004777 else
4778 {
Bram Moolenaar8ec92c92020-09-29 22:47:03 +02004779 // Shorten the file name if not done already.
4780 // For optimization, do this only for the first entry in a
4781 // buffer.
4782 if (first_bufline && (errbuf->b_sfname == NULL
4783 || mch_isFullName(errbuf->b_sfname)))
Bram Moolenaar858ba062020-05-31 23:11:59 +02004784 {
4785 if (*dirname == NUL)
4786 mch_dirname(dirname, MAXPATHL);
4787 shorten_buf_fname(errbuf, dirname, FALSE);
4788 }
Austin Chang29822992024-10-03 10:50:05 +02004789 if (qfp->qf_fname == NULL)
4790 ga_concat(gap, errbuf->b_fname);
4791 else
4792 ga_concat(gap, qfp->qf_fname);
Bram Moolenaar858ba062020-05-31 23:11:59 +02004793 }
Bram Moolenaar858ba062020-05-31 23:11:59 +02004794 }
Bram Moolenaar858ba062020-05-31 23:11:59 +02004795
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01004796 ga_append(gap, '|');
Bram Moolenaar858ba062020-05-31 23:11:59 +02004797
4798 if (qfp->qf_lnum > 0)
4799 {
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01004800 qf_range_text(gap, qfp);
4801 ga_concat(gap, qf_types(qfp->qf_type, qfp->qf_nr));
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02004802 }
Bram Moolenaar858ba062020-05-31 23:11:59 +02004803 else if (qfp->qf_pattern != NULL)
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01004804 qf_fmt_text(gap, qfp->qf_pattern);
4805 ga_append(gap, '|');
4806 ga_append(gap, ' ');
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02004807
Bram Moolenaar858ba062020-05-31 23:11:59 +02004808 // Remove newlines and leading whitespace from the text.
4809 // For an unrecognized line keep the indent, the compiler may
4810 // mark a word with ^^^^.
Bram Moolenaar2f7bfe62022-11-13 12:54:50 +00004811 qf_fmt_text(gap, gap->ga_len > 3 ? skipwhite(qfp->qf_text)
4812 : qfp->qf_text);
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02004813 }
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02004814
Shane Harper5bf04282023-06-07 19:09:57 +01004815 ga_append(gap, NUL);
Bram Moolenaar2f7bfe62022-11-13 12:54:50 +00004816 if (ml_append_buf(buf, lnum, gap->ga_data, gap->ga_len, FALSE) == FAIL)
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02004817 return FAIL;
4818
4819 return OK;
4820}
4821
Bram Moolenaard43906d2020-07-20 21:31:32 +02004822/*
4823 * Call the 'quickfixtextfunc' function to get the list of lines to display in
4824 * the quickfix window for the entries 'start_idx' to 'end_idx'.
4825 */
Bram Moolenaar00e260b2020-06-11 19:35:52 +02004826 static list_T *
4827call_qftf_func(qf_list_T *qfl, int qf_winid, long start_idx, long end_idx)
4828{
Bram Moolenaard43906d2020-07-20 21:31:32 +02004829 callback_T *cb = &qftf_cb;
Bram Moolenaar00e260b2020-06-11 19:35:52 +02004830 list_T *qftf_list = NULL;
Bram Moolenaard6c67622022-08-24 20:07:22 +01004831 static int recursive = FALSE;
4832
4833 if (recursive)
4834 return NULL; // this doesn't work properly recursively
4835 recursive = TRUE;
Bram Moolenaar00e260b2020-06-11 19:35:52 +02004836
4837 // If 'quickfixtextfunc' is set, then use the user-supplied function to get
4838 // the text to display. Use the local value of 'quickfixtextfunc' if it is
4839 // set.
=?UTF-8?q?Dundar=20G=C3=B6c?=b8366582022-04-14 20:43:56 +01004840 if (qfl->qf_qftf_cb.cb_name != NULL)
4841 cb = &qfl->qf_qftf_cb;
4842 if (cb->cb_name != NULL)
Bram Moolenaar00e260b2020-06-11 19:35:52 +02004843 {
4844 typval_T args[1];
4845 dict_T *d;
Bram Moolenaard43906d2020-07-20 21:31:32 +02004846 typval_T rettv;
Bram Moolenaar00e260b2020-06-11 19:35:52 +02004847
4848 // create the dict argument
4849 if ((d = dict_alloc_lock(VAR_FIXED)) == NULL)
Bram Moolenaard6c67622022-08-24 20:07:22 +01004850 {
4851 recursive = FALSE;
Bram Moolenaar00e260b2020-06-11 19:35:52 +02004852 return NULL;
Bram Moolenaard6c67622022-08-24 20:07:22 +01004853 }
Bram Moolenaar00e260b2020-06-11 19:35:52 +02004854 dict_add_number(d, "quickfix", (long)IS_QF_LIST(qfl));
4855 dict_add_number(d, "winid", (long)qf_winid);
4856 dict_add_number(d, "id", (long)qfl->qf_id);
4857 dict_add_number(d, "start_idx", start_idx);
4858 dict_add_number(d, "end_idx", end_idx);
4859 ++d->dv_refcount;
4860 args[0].v_type = VAR_DICT;
4861 args[0].vval.v_dict = d;
4862
Bram Moolenaard43906d2020-07-20 21:31:32 +02004863 qftf_list = NULL;
4864 if (call_callback(cb, 0, &rettv, 1, args) != FAIL)
4865 {
4866 if (rettv.v_type == VAR_LIST)
4867 {
4868 qftf_list = rettv.vval.v_list;
4869 qftf_list->lv_refcount++;
4870 }
4871 clear_tv(&rettv);
4872 }
4873 dict_unref(d);
Bram Moolenaar00e260b2020-06-11 19:35:52 +02004874 }
4875
Bram Moolenaard6c67622022-08-24 20:07:22 +01004876 recursive = FALSE;
Bram Moolenaar00e260b2020-06-11 19:35:52 +02004877 return qftf_list;
4878}
4879
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02004880/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00004881 * Fill current buffer with quickfix errors, replacing any previous contents.
4882 * curbuf must be the quickfix buffer!
Bram Moolenaar864293a2016-06-02 13:40:04 +02004883 * If "old_last" is not NULL append the items after this one.
4884 * When "old_last" is NULL then "buf" must equal "curbuf"! Because
4885 * ml_delete() is used and autocommands will be triggered.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004886 */
4887 static void
Bram Moolenaar7ba5a7e2020-06-08 19:20:27 +02004888qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int qf_winid)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004889{
Bram Moolenaar68b76a62005-03-25 21:53:48 +00004890 linenr_T lnum;
4891 qfline_T *qfp;
Bram Moolenaar68b76a62005-03-25 21:53:48 +00004892 int old_KeyTyped = KeyTyped;
Bram Moolenaar00e260b2020-06-11 19:35:52 +02004893 list_T *qftf_list = NULL;
4894 listitem_T *qftf_li = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004895
Bram Moolenaar864293a2016-06-02 13:40:04 +02004896 if (old_last == NULL)
4897 {
zeertzjqc7a8eb52024-05-08 20:22:40 +02004898 win_T *wp;
4899 tabpage_T *tp;
4900
Bram Moolenaar864293a2016-06-02 13:40:04 +02004901 if (buf != curbuf)
4902 {
Bram Moolenaar95f09602016-11-10 20:01:45 +01004903 internal_error("qf_fill_buffer()");
Bram Moolenaar864293a2016-06-02 13:40:04 +02004904 return;
4905 }
4906
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02004907 // delete all existing lines
Christian Brabandtf0d3d4a2024-02-15 20:15:04 +01004908 //
4909 // Note: we cannot store undo information, because
4910 // qf buffer is usually not allowed to be modified.
4911 //
4912 // So we need to clean up undo information
4913 // otherwise autocommands may invalidate the undo stack
Bram Moolenaar864293a2016-06-02 13:40:04 +02004914 while ((curbuf->b_ml.ml_flags & ML_EMPTY) == 0)
Bram Moolenaarca70c072020-05-30 20:30:46 +02004915 (void)ml_delete((linenr_T)1);
Christian Brabandtf0d3d4a2024-02-15 20:15:04 +01004916
zeertzjqc7a8eb52024-05-08 20:22:40 +02004917 FOR_ALL_TAB_WINDOWS(tp, wp)
4918 if (wp->w_buffer == curbuf)
4919 wp->w_skipcol = 0;
4920
Christian Brabandtf0d3d4a2024-02-15 20:15:04 +01004921 // Remove all undo information
Christian Brabandt9071ed82024-02-15 20:17:37 +01004922 u_clearallandblockfree(curbuf);
Bram Moolenaar864293a2016-06-02 13:40:04 +02004923 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004924
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02004925 // Check if there is anything to display
Bram Moolenaar4f1b0832022-08-29 20:45:16 +01004926 if (qfl != NULL && qfl->qf_start != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004927 {
Bram Moolenaar108e7b42018-10-11 17:39:12 +02004928 char_u dirname[MAXPATHL];
Bram Moolenaard43906d2020-07-20 21:31:32 +02004929 int invalid_val = FALSE;
Bram Moolenaar8ec92c92020-09-29 22:47:03 +02004930 int prev_bufnr = -1;
Bram Moolenaara796d462018-05-01 14:30:36 +02004931
4932 *dirname = NUL;
4933
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02004934 // Add one line for each error
Bram Moolenaar2ce77902020-11-14 13:15:24 +01004935 if (old_last == NULL)
Bram Moolenaar864293a2016-06-02 13:40:04 +02004936 {
Bram Moolenaar108e7b42018-10-11 17:39:12 +02004937 qfp = qfl->qf_start;
Bram Moolenaar864293a2016-06-02 13:40:04 +02004938 lnum = 0;
4939 }
4940 else
4941 {
Bram Moolenaar2ce77902020-11-14 13:15:24 +01004942 if (old_last->qf_next != NULL)
4943 qfp = old_last->qf_next;
4944 else
4945 qfp = old_last;
Bram Moolenaar864293a2016-06-02 13:40:04 +02004946 lnum = buf->b_ml.ml_line_count;
4947 }
Bram Moolenaar00e260b2020-06-11 19:35:52 +02004948
4949 qftf_list = call_qftf_func(qfl, qf_winid, (long)(lnum + 1),
4950 (long)qfl->qf_count);
4951 if (qftf_list != NULL)
4952 qftf_li = qftf_list->lv_first;
4953
Bram Moolenaar108e7b42018-10-11 17:39:12 +02004954 while (lnum < qfl->qf_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004955 {
Bram Moolenaar00e260b2020-06-11 19:35:52 +02004956 char_u *qftf_str = NULL;
4957
Bram Moolenaard43906d2020-07-20 21:31:32 +02004958 // Use the text supplied by the user defined function (if any).
4959 // If the returned value is not string, then ignore the rest
4960 // of the returned values and use the default.
4961 if (qftf_li != NULL && !invalid_val)
4962 {
Bram Moolenaar00e260b2020-06-11 19:35:52 +02004963 qftf_str = tv_get_string_chk(&qftf_li->li_tv);
Bram Moolenaard43906d2020-07-20 21:31:32 +02004964 if (qftf_str == NULL)
4965 invalid_val = TRUE;
4966 }
Bram Moolenaar00e260b2020-06-11 19:35:52 +02004967
Bram Moolenaar8ec92c92020-09-29 22:47:03 +02004968 if (qf_buf_add_line(buf, lnum, qfp, dirname,
4969 prev_bufnr != qfp->qf_fnum, qftf_str) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004970 break;
Bram Moolenaar6053f2d2018-05-21 16:56:38 +02004971
Bram Moolenaar8ec92c92020-09-29 22:47:03 +02004972 prev_bufnr = qfp->qf_fnum;
Bram Moolenaar864293a2016-06-02 13:40:04 +02004973 ++lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004974 qfp = qfp->qf_next;
Bram Moolenaar83e6d7a2016-06-02 22:08:05 +02004975 if (qfp == NULL)
4976 break;
Bram Moolenaar00e260b2020-06-11 19:35:52 +02004977
4978 if (qftf_li != NULL)
4979 qftf_li = qftf_li->li_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004980 }
Bram Moolenaar864293a2016-06-02 13:40:04 +02004981
4982 if (old_last == NULL)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02004983 // Delete the empty line which is now at the end
Bram Moolenaarca70c072020-05-30 20:30:46 +02004984 (void)ml_delete(lnum + 1);
Yegappan Lakshmanand8cd6f72022-10-16 11:30:48 +01004985
4986 qfga_clear();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004987 }
4988
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02004989 // correct cursor position
Bram Moolenaar071d4272004-06-13 20:20:40 +00004990 check_lnums(TRUE);
4991
Bram Moolenaar864293a2016-06-02 13:40:04 +02004992 if (old_last == NULL)
4993 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02004994 // Set the 'filetype' to "qf" each time after filling the buffer.
4995 // This resembles reading a file into a buffer, it's more logical when
4996 // using autocommands.
Bram Moolenaar18141832017-06-25 21:17:25 +02004997 ++curbuf_lock;
Bram Moolenaar31e5c602022-04-15 13:53:33 +01004998 set_option_value_give_err((char_u *)"ft",
4999 0L, (char_u *)"qf", OPT_LOCAL);
Bram Moolenaar864293a2016-06-02 13:40:04 +02005000 curbuf->b_p_ma = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005001
zeertzjq5bf6c212024-03-31 18:41:27 +02005002 curbuf->b_keep_filetype = TRUE; // don't detect 'filetype'
Bram Moolenaar864293a2016-06-02 13:40:04 +02005003 apply_autocmds(EVENT_BUFREADPOST, (char_u *)"quickfix", NULL,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005004 FALSE, curbuf);
Bram Moolenaar864293a2016-06-02 13:40:04 +02005005 apply_autocmds(EVENT_BUFWINENTER, (char_u *)"quickfix", NULL,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005006 FALSE, curbuf);
zeertzjq5bf6c212024-03-31 18:41:27 +02005007 curbuf->b_keep_filetype = FALSE;
Bram Moolenaar18141832017-06-25 21:17:25 +02005008 --curbuf_lock;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01005009
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02005010 // make sure it will be redrawn
Bram Moolenaara4d158b2022-08-14 14:17:45 +01005011 redraw_curbuf_later(UPD_NOT_VALID);
Bram Moolenaar864293a2016-06-02 13:40:04 +02005012 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005013
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02005014 // Restore KeyTyped, setting 'filetype' may reset it.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005015 KeyTyped = old_KeyTyped;
5016}
5017
Bram Moolenaar18cebf42018-05-08 22:31:37 +02005018/*
5019 * For every change made to the quickfix list, update the changed tick.
5020 */
Bram Moolenaarb254af32017-12-18 19:48:58 +01005021 static void
Bram Moolenaar108e7b42018-10-11 17:39:12 +02005022qf_list_changed(qf_list_T *qfl)
Bram Moolenaarb254af32017-12-18 19:48:58 +01005023{
Bram Moolenaar108e7b42018-10-11 17:39:12 +02005024 qfl->qf_changedtick++;
Bram Moolenaarb254af32017-12-18 19:48:58 +01005025}
5026
Bram Moolenaar071d4272004-06-13 20:20:40 +00005027/*
Bram Moolenaar531b9a32018-07-03 16:54:23 +02005028 * Return the quickfix/location list number with the given identifier.
5029 * Returns -1 if list is not found.
5030 */
5031 static int
5032qf_id2nr(qf_info_T *qi, int_u qfid)
5033{
5034 int qf_idx;
5035
5036 for (qf_idx = 0; qf_idx < qi->qf_listcount; qf_idx++)
5037 if (qi->qf_lists[qf_idx].qf_id == qfid)
5038 return qf_idx;
5039 return INVALID_QFIDX;
5040}
5041
5042/*
Bram Moolenaar38efd1d2018-08-09 21:52:24 +02005043 * If the current list is not "save_qfid" and we can find the list with that ID
5044 * then make it the current list.
5045 * This is used when autocommands may have changed the current list.
Bram Moolenaar90f1e2b2018-08-11 13:36:56 +02005046 * Returns OK if successfully restored the list. Returns FAIL if the list with
5047 * the specified identifier (save_qfid) is not found in the stack.
Bram Moolenaar38efd1d2018-08-09 21:52:24 +02005048 */
Bram Moolenaar90f1e2b2018-08-11 13:36:56 +02005049 static int
Bram Moolenaar38efd1d2018-08-09 21:52:24 +02005050qf_restore_list(qf_info_T *qi, int_u save_qfid)
5051{
5052 int curlist;
5053
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00005054 if (qf_get_curlist(qi)->qf_id == save_qfid)
5055 return OK;
5056
5057 curlist = qf_id2nr(qi, save_qfid);
5058 if (curlist < 0)
5059 // list is not present
5060 return FAIL;
5061 qi->qf_curlist = curlist;
Bram Moolenaar90f1e2b2018-08-11 13:36:56 +02005062 return OK;
Bram Moolenaar38efd1d2018-08-09 21:52:24 +02005063}
5064
5065/*
Bram Moolenaar8d8a65e2018-08-07 19:48:08 +02005066 * Jump to the first entry if there is one.
5067 */
5068 static void
5069qf_jump_first(qf_info_T *qi, int_u save_qfid, int forceit)
5070{
Bram Moolenaar90f1e2b2018-08-11 13:36:56 +02005071 if (qf_restore_list(qi, save_qfid) == FAIL)
5072 return;
Bram Moolenaar8d8a65e2018-08-07 19:48:08 +02005073
Colin Kennedy21570352024-03-03 16:16:47 +01005074
5075 if (!check_can_set_curbuf_forceit(forceit))
5076 return;
5077
5078
Bram Moolenaar90f1e2b2018-08-11 13:36:56 +02005079 // Autocommands might have cleared the list, check for that.
Bram Moolenaar0398e002019-03-21 21:12:49 +01005080 if (!qf_list_empty(qf_get_curlist(qi)))
Bram Moolenaar8d8a65e2018-08-07 19:48:08 +02005081 qf_jump(qi, 0, 0, forceit);
5082}
5083
5084/*
Bram Moolenaar86b68352004-12-27 21:59:20 +00005085 * Return TRUE when using ":vimgrep" for ":grep".
5086 */
5087 int
Bram Moolenaar05540972016-01-30 20:31:25 +01005088grep_internal(cmdidx_T cmdidx)
Bram Moolenaar86b68352004-12-27 21:59:20 +00005089{
Bram Moolenaar754b5602006-02-09 23:53:20 +00005090 return ((cmdidx == CMD_grep
5091 || cmdidx == CMD_lgrep
5092 || cmdidx == CMD_grepadd
5093 || cmdidx == CMD_lgrepadd)
Bram Moolenaar86b68352004-12-27 21:59:20 +00005094 && STRCMP("internal",
5095 *curbuf->b_p_gp == NUL ? p_gp : curbuf->b_p_gp) == 0);
5096}
5097
5098/*
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005099 * Return the make/grep autocmd name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005100 */
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005101 static char_u *
5102make_get_auname(cmdidx_T cmdidx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005103{
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005104 switch (cmdidx)
Bram Moolenaard88e02d2011-04-28 17:27:09 +02005105 {
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005106 case CMD_make: return (char_u *)"make";
5107 case CMD_lmake: return (char_u *)"lmake";
5108 case CMD_grep: return (char_u *)"grep";
5109 case CMD_lgrep: return (char_u *)"lgrep";
5110 case CMD_grepadd: return (char_u *)"grepadd";
5111 case CMD_lgrepadd: return (char_u *)"lgrepadd";
5112 default: return NULL;
Bram Moolenaard88e02d2011-04-28 17:27:09 +02005113 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005114}
5115
5116/*
5117 * Return the name for the errorfile, in allocated memory.
5118 * Find a new unique name when 'makeef' contains "##".
5119 * Returns NULL for error.
5120 */
5121 static char_u *
Bram Moolenaar05540972016-01-30 20:31:25 +01005122get_mef_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005123{
5124 char_u *p;
5125 char_u *name;
5126 static int start = -1;
5127 static int off = 0;
5128#ifdef HAVE_LSTAT
Bram Moolenaar8767f522016-07-01 17:17:39 +02005129 stat_T sb;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005130#endif
5131
5132 if (*p_mef == NUL)
5133 {
Bram Moolenaare5c421c2015-03-31 13:33:08 +02005134 name = vim_tempname('e', FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005135 if (name == NULL)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00005136 emsg(_(e_cant_get_temp_file_name));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005137 return name;
5138 }
5139
5140 for (p = p_mef; *p; ++p)
5141 if (p[0] == '#' && p[1] == '#')
5142 break;
5143
5144 if (*p == NUL)
5145 return vim_strsave(p_mef);
5146
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005147 // Keep trying until the name doesn't exist yet.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005148 for (;;)
5149 {
5150 if (start == -1)
5151 start = mch_get_pid();
5152 else
5153 off += 19;
5154
Yegappan Lakshmanan5a2d4a32022-02-26 10:31:32 +00005155 name = alloc_id(STRLEN(p_mef) + 30, aid_qf_mef_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005156 if (name == NULL)
5157 break;
5158 STRCPY(name, p_mef);
5159 sprintf((char *)name + (p - p_mef), "%d%d", start, off);
5160 STRCAT(name, p + 2);
5161 if (mch_getperm(name) < 0
5162#ifdef HAVE_LSTAT
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005163 // Don't accept a symbolic link, it's a security risk.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005164 && mch_lstat((char *)name, &sb) < 0
5165#endif
5166 )
5167 break;
5168 vim_free(name);
5169 }
5170 return name;
5171}
5172
5173/*
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005174 * Form the complete command line to invoke 'make'/'grep'. Quote the command
5175 * using 'shellquote' and append 'shellpipe'. Echo the fully formed command.
5176 */
5177 static char_u *
5178make_get_fullcmd(char_u *makecmd, char_u *fname)
5179{
5180 char_u *cmd;
5181 unsigned len;
5182
5183 len = (unsigned)STRLEN(p_shq) * 2 + (unsigned)STRLEN(makecmd) + 1;
5184 if (*p_sp != NUL)
5185 len += (unsigned)STRLEN(p_sp) + (unsigned)STRLEN(fname) + 3;
Yegappan Lakshmanan5a2d4a32022-02-26 10:31:32 +00005186 cmd = alloc_id(len, aid_qf_makecmd);
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005187 if (cmd == NULL)
5188 return NULL;
5189 sprintf((char *)cmd, "%s%s%s", (char *)p_shq, (char *)makecmd,
5190 (char *)p_shq);
5191
5192 // If 'shellpipe' empty: don't redirect to 'errorfile'.
5193 if (*p_sp != NUL)
5194 append_redir(cmd, len, p_sp, fname);
5195
5196 // Display the fully formed command. Output a newline if there's something
5197 // else than the :make command that was typed (in which case the cursor is
5198 // in column 0).
5199 if (msg_col == 0)
5200 msg_didout = FALSE;
5201 msg_start();
Bram Moolenaar32526b32019-01-19 17:43:09 +01005202 msg_puts(":!");
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005203 msg_outtrans(cmd); // show what we are doing
5204
5205 return cmd;
5206}
5207
5208/*
5209 * Used for ":make", ":lmake", ":grep", ":lgrep", ":grepadd", and ":lgrepadd"
5210 */
5211 void
5212ex_make(exarg_T *eap)
5213{
5214 char_u *fname;
5215 char_u *cmd;
5216 char_u *enc = NULL;
5217 win_T *wp = NULL;
5218 qf_info_T *qi = &ql_info;
5219 int res;
5220 char_u *au_name = NULL;
5221 int_u save_qfid;
Yegappan Lakshmanan2f87a992022-03-02 20:29:35 +00005222 char_u *errorformat = p_efm;
5223 int newlist = TRUE;
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005224
5225 // Redirect ":grep" to ":vimgrep" if 'grepprg' is "internal".
5226 if (grep_internal(eap->cmdidx))
5227 {
5228 ex_vimgrep(eap);
5229 return;
5230 }
5231
5232 au_name = make_get_auname(eap->cmdidx);
5233 if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
5234 curbuf->b_fname, TRUE, curbuf))
5235 {
5236#ifdef FEAT_EVAL
5237 if (aborting())
5238 return;
5239#endif
5240 }
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005241 enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc;
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005242
5243 if (is_loclist_cmd(eap->cmdidx))
5244 wp = curwin;
5245
5246 autowrite_all();
5247 fname = get_mef_name();
5248 if (fname == NULL)
5249 return;
5250 mch_remove(fname); // in case it's not unique
5251
5252 cmd = make_get_fullcmd(eap->arg, fname);
5253 if (cmd == NULL)
Yegappan Lakshmanan5a2d4a32022-02-26 10:31:32 +00005254 {
5255 vim_free(fname);
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005256 return;
Yegappan Lakshmanan5a2d4a32022-02-26 10:31:32 +00005257 }
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005258
5259 // let the shell know if we are redirecting output or not
5260 do_shell(cmd, *p_sp != NUL ? SHELL_DOOUT : 0);
5261
5262#ifdef AMIGA
5263 out_flush();
5264 // read window status report and redraw before message
5265 (void)char_avail();
5266#endif
5267
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02005268 incr_quickfix_busy();
5269
Yegappan Lakshmanan2f87a992022-03-02 20:29:35 +00005270 if (eap->cmdidx != CMD_make && eap->cmdidx != CMD_lmake)
5271 errorformat = p_gefm;
5272 if (eap->cmdidx == CMD_grepadd || eap->cmdidx == CMD_lgrepadd)
5273 newlist = FALSE;
5274
5275 res = qf_init(wp, fname, errorformat, newlist, qf_cmdtitle(*eap->cmdlinep),
5276 enc);
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005277 if (wp != NULL)
5278 {
5279 qi = GET_LOC_LIST(wp);
5280 if (qi == NULL)
5281 goto cleanup;
5282 }
5283 if (res >= 0)
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01005284 qf_list_changed(qf_get_curlist(qi));
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005285
5286 // Remember the current quickfix list identifier, so that we can
5287 // check for autocommands changing the current quickfix list.
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01005288 save_qfid = qf_get_curlist(qi)->qf_id;
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005289 if (au_name != NULL)
5290 apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
5291 curbuf->b_fname, TRUE, curbuf);
5292 if (res > 0 && !eap->forceit && qflist_valid(wp, save_qfid))
5293 // display the first error
5294 qf_jump_first(qi, save_qfid, FALSE);
5295
5296cleanup:
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02005297 decr_quickfix_busy();
Bram Moolenaarb434ae22018-09-28 23:09:55 +02005298 mch_remove(fname);
5299 vim_free(fname);
5300 vim_free(cmd);
5301}
5302
5303/*
Bram Moolenaar25190db2019-05-04 15:05:28 +02005304 * Returns the number of entries in the current quickfix/location list.
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005305 */
5306 int
Bram Moolenaar05540972016-01-30 20:31:25 +01005307qf_get_size(exarg_T *eap)
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005308{
Bram Moolenaar87f59b02019-04-04 14:04:11 +02005309 qf_info_T *qi;
Bram Moolenaar25190db2019-05-04 15:05:28 +02005310
5311 if ((qi = qf_cmd_get_stack(eap, FALSE)) == NULL)
5312 return 0;
5313 return qf_get_curlist(qi)->qf_count;
5314}
5315
5316/*
5317 * Returns the number of valid entries in the current quickfix/location list.
5318 */
5319 int
5320qf_get_valid_size(exarg_T *eap)
5321{
5322 qf_info_T *qi;
Bram Moolenaar108e7b42018-10-11 17:39:12 +02005323 qf_list_T *qfl;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005324 qfline_T *qfp;
5325 int i, sz = 0;
5326 int prev_fnum = 0;
5327
Bram Moolenaar87f59b02019-04-04 14:04:11 +02005328 if ((qi = qf_cmd_get_stack(eap, FALSE)) == NULL)
5329 return 0;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005330
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01005331 qfl = qf_get_curlist(qi);
Bram Moolenaara16123a2019-03-28 20:31:07 +01005332 FOR_ALL_QFL_ITEMS(qfl, qfp, i)
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005333 {
5334 if (qfp->qf_valid)
5335 {
5336 if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02005337 sz++; // Count all valid entries
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005338 else if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum)
5339 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02005340 // Count the number of files
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005341 sz++;
5342 prev_fnum = qfp->qf_fnum;
5343 }
5344 }
5345 }
5346
5347 return sz;
5348}
5349
5350/*
5351 * Returns the current index of the quickfix/location list.
5352 * Returns 0 if there is an error.
5353 */
5354 int
Bram Moolenaar05540972016-01-30 20:31:25 +01005355qf_get_cur_idx(exarg_T *eap)
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005356{
Bram Moolenaar87f59b02019-04-04 14:04:11 +02005357 qf_info_T *qi;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005358
Bram Moolenaar87f59b02019-04-04 14:04:11 +02005359 if ((qi = qf_cmd_get_stack(eap, FALSE)) == NULL)
5360 return 0;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005361
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01005362 return qf_get_curlist(qi)->qf_index;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005363}
5364
5365/*
5366 * Returns the current index in the quickfix/location list (counting only valid
5367 * entries). If no valid entries are in the list, then returns 1.
5368 */
5369 int
Bram Moolenaar05540972016-01-30 20:31:25 +01005370qf_get_cur_valid_idx(exarg_T *eap)
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005371{
Bram Moolenaar87f59b02019-04-04 14:04:11 +02005372 qf_info_T *qi;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005373 qf_list_T *qfl;
5374 qfline_T *qfp;
5375 int i, eidx = 0;
5376 int prev_fnum = 0;
5377
Bram Moolenaar87f59b02019-04-04 14:04:11 +02005378 if ((qi = qf_cmd_get_stack(eap, FALSE)) == NULL)
5379 return 1;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005380
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01005381 qfl = qf_get_curlist(qi);
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005382 qfp = qfl->qf_start;
5383
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02005384 // check if the list has valid errors
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005385 if (!qf_list_has_valid_entries(qfl))
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005386 return 1;
5387
5388 for (i = 1; i <= qfl->qf_index && qfp!= NULL; i++, qfp = qfp->qf_next)
5389 {
5390 if (qfp->qf_valid)
5391 {
5392 if (eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
5393 {
5394 if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum)
5395 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02005396 // Count the number of files
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005397 eidx++;
5398 prev_fnum = qfp->qf_fnum;
5399 }
5400 }
5401 else
5402 eidx++;
5403 }
5404 }
5405
5406 return eidx ? eidx : 1;
5407}
5408
5409/*
5410 * Get the 'n'th valid error entry in the quickfix or location list.
5411 * Used by :cdo, :ldo, :cfdo and :lfdo commands.
5412 * For :cdo and :ldo returns the 'n'th valid error entry.
5413 * For :cfdo and :lfdo returns the 'n'th valid file entry.
5414 */
5415 static int
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02005416qf_get_nth_valid_entry(qf_list_T *qfl, int n, int fdo)
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005417{
Bram Moolenaar95946f12019-03-31 15:31:59 +02005418 qfline_T *qfp;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005419 int i, eidx;
5420 int prev_fnum = 0;
5421
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02005422 // check if the list has valid errors
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005423 if (!qf_list_has_valid_entries(qfl))
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005424 return 1;
5425
Bram Moolenaar95946f12019-03-31 15:31:59 +02005426 eidx = 0;
5427 FOR_ALL_QFL_ITEMS(qfl, qfp, i)
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005428 {
5429 if (qfp->qf_valid)
5430 {
5431 if (fdo)
5432 {
5433 if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum)
5434 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02005435 // Count the number of files
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005436 eidx++;
5437 prev_fnum = qfp->qf_fnum;
5438 }
5439 }
5440 else
5441 eidx++;
5442 }
5443
5444 if (eidx == n)
5445 break;
5446 }
5447
5448 if (i <= qfl->qf_count)
5449 return i;
5450 else
5451 return 1;
5452}
5453
5454/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00005455 * ":cc", ":crewind", ":cfirst" and ":clast".
Bram Moolenaard12f5c12006-01-25 22:10:52 +00005456 * ":ll", ":lrewind", ":lfirst" and ":llast".
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005457 * ":cdo", ":ldo", ":cfdo" and ":lfdo"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005458 */
5459 void
Bram Moolenaar05540972016-01-30 20:31:25 +01005460ex_cc(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005461{
Bram Moolenaar87f59b02019-04-04 14:04:11 +02005462 qf_info_T *qi;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005463 int errornr;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00005464
Bram Moolenaar87f59b02019-04-04 14:04:11 +02005465 if ((qi = qf_cmd_get_stack(eap, TRUE)) == NULL)
5466 return;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00005467
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005468 if (eap->addr_count > 0)
5469 errornr = (int)eap->line2;
5470 else
5471 {
Bram Moolenaar39665952018-08-15 20:59:48 +02005472 switch (eap->cmdidx)
5473 {
5474 case CMD_cc: case CMD_ll:
5475 errornr = 0;
5476 break;
5477 case CMD_crewind: case CMD_lrewind: case CMD_cfirst:
5478 case CMD_lfirst:
5479 errornr = 1;
5480 break;
5481 default:
5482 errornr = 32767;
5483 }
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005484 }
5485
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02005486 // For cdo and ldo commands, jump to the nth valid error.
5487 // For cfdo and lfdo commands, jump to the nth valid file entry.
Bram Moolenaar55b69262017-08-13 13:42:01 +02005488 if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo
5489 || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01005490 errornr = qf_get_nth_valid_entry(qf_get_curlist(qi),
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005491 eap->addr_count > 0 ? (int)eap->line1 : 1,
5492 eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo);
5493
5494 qf_jump(qi, 0, errornr, eap->forceit);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005495}
5496
5497/*
5498 * ":cnext", ":cnfile", ":cNext" and ":cprevious".
Bram Moolenaard12f5c12006-01-25 22:10:52 +00005499 * ":lnext", ":lNext", ":lprevious", ":lnfile", ":lNfile" and ":lpfile".
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005500 * Also, used by ":cdo", ":ldo", ":cfdo" and ":lfdo" commands.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005501 */
5502 void
Bram Moolenaar05540972016-01-30 20:31:25 +01005503ex_cnext(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005504{
Bram Moolenaar87f59b02019-04-04 14:04:11 +02005505 qf_info_T *qi;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005506 int errornr;
Bram Moolenaar39665952018-08-15 20:59:48 +02005507 int dir;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00005508
Bram Moolenaar87f59b02019-04-04 14:04:11 +02005509 if ((qi = qf_cmd_get_stack(eap, TRUE)) == NULL)
5510 return;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00005511
Bram Moolenaar55b69262017-08-13 13:42:01 +02005512 if (eap->addr_count > 0
5513 && (eap->cmdidx != CMD_cdo && eap->cmdidx != CMD_ldo
5514 && eap->cmdidx != CMD_cfdo && eap->cmdidx != CMD_lfdo))
Bram Moolenaaraa23b372015-09-08 18:46:31 +02005515 errornr = (int)eap->line2;
5516 else
5517 errornr = 1;
5518
Bram Moolenaar39665952018-08-15 20:59:48 +02005519 // Depending on the command jump to either next or previous entry/file.
5520 switch (eap->cmdidx)
5521 {
5522 case CMD_cnext: case CMD_lnext: case CMD_cdo: case CMD_ldo:
5523 dir = FORWARD;
5524 break;
5525 case CMD_cprevious: case CMD_lprevious: case CMD_cNext:
5526 case CMD_lNext:
5527 dir = BACKWARD;
5528 break;
5529 case CMD_cnfile: case CMD_lnfile: case CMD_cfdo: case CMD_lfdo:
5530 dir = FORWARD_FILE;
5531 break;
5532 case CMD_cpfile: case CMD_lpfile: case CMD_cNfile: case CMD_lNfile:
5533 dir = BACKWARD_FILE;
5534 break;
5535 default:
5536 dir = FORWARD;
5537 break;
5538 }
5539
5540 qf_jump(qi, dir, errornr, eap->forceit);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005541}
5542
5543/*
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005544 * Find the first entry in the quickfix list 'qfl' from buffer 'bnr'.
5545 * The index of the entry is stored in 'errornr'.
5546 * Returns NULL if an entry is not found.
5547 */
5548 static qfline_T *
5549qf_find_first_entry_in_buf(qf_list_T *qfl, int bnr, int *errornr)
5550{
5551 qfline_T *qfp = NULL;
5552 int idx = 0;
5553
5554 // Find the first entry in this file
5555 FOR_ALL_QFL_ITEMS(qfl, qfp, idx)
5556 if (qfp->qf_fnum == bnr)
5557 break;
5558
5559 *errornr = idx;
5560 return qfp;
5561}
5562
5563/*
5564 * Find the first quickfix entry on the same line as 'entry'. Updates 'errornr'
5565 * with the error number for the first entry. Assumes the entries are sorted in
5566 * the quickfix list by line number.
5567 */
5568 static qfline_T *
5569qf_find_first_entry_on_line(qfline_T *entry, int *errornr)
5570{
5571 while (!got_int
5572 && entry->qf_prev != NULL
5573 && entry->qf_fnum == entry->qf_prev->qf_fnum
5574 && entry->qf_lnum == entry->qf_prev->qf_lnum)
5575 {
5576 entry = entry->qf_prev;
5577 --*errornr;
5578 }
5579
5580 return entry;
5581}
5582
5583/*
5584 * Find the last quickfix entry on the same line as 'entry'. Updates 'errornr'
5585 * with the error number for the last entry. Assumes the entries are sorted in
5586 * the quickfix list by line number.
5587 */
5588 static qfline_T *
5589qf_find_last_entry_on_line(qfline_T *entry, int *errornr)
5590{
5591 while (!got_int &&
5592 entry->qf_next != NULL
5593 && entry->qf_fnum == entry->qf_next->qf_fnum
5594 && entry->qf_lnum == entry->qf_next->qf_lnum)
5595 {
5596 entry = entry->qf_next;
5597 ++*errornr;
5598 }
5599
5600 return entry;
5601}
5602
5603/*
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005604 * Returns TRUE if the specified quickfix entry is
5605 * after the given line (linewise is TRUE)
5606 * or after the line and column.
5607 */
5608 static int
5609qf_entry_after_pos(qfline_T *qfp, pos_T *pos, int linewise)
5610{
5611 if (linewise)
5612 return qfp->qf_lnum > pos->lnum;
5613 else
5614 return (qfp->qf_lnum > pos->lnum ||
5615 (qfp->qf_lnum == pos->lnum && qfp->qf_col > pos->col));
5616}
5617
5618/*
5619 * Returns TRUE if the specified quickfix entry is
5620 * before the given line (linewise is TRUE)
5621 * or before the line and column.
5622 */
5623 static int
5624qf_entry_before_pos(qfline_T *qfp, pos_T *pos, int linewise)
5625{
5626 if (linewise)
5627 return qfp->qf_lnum < pos->lnum;
5628 else
5629 return (qfp->qf_lnum < pos->lnum ||
5630 (qfp->qf_lnum == pos->lnum && qfp->qf_col < pos->col));
5631}
5632
5633/*
5634 * Returns TRUE if the specified quickfix entry is
5635 * on or after the given line (linewise is TRUE)
5636 * or on or after the line and column.
5637 */
5638 static int
5639qf_entry_on_or_after_pos(qfline_T *qfp, pos_T *pos, int linewise)
5640{
5641 if (linewise)
5642 return qfp->qf_lnum >= pos->lnum;
5643 else
5644 return (qfp->qf_lnum > pos->lnum ||
5645 (qfp->qf_lnum == pos->lnum && qfp->qf_col >= pos->col));
5646}
5647
5648/*
5649 * Returns TRUE if the specified quickfix entry is
5650 * on or before the given line (linewise is TRUE)
5651 * or on or before the line and column.
5652 */
5653 static int
5654qf_entry_on_or_before_pos(qfline_T *qfp, pos_T *pos, int linewise)
5655{
5656 if (linewise)
5657 return qfp->qf_lnum <= pos->lnum;
5658 else
5659 return (qfp->qf_lnum < pos->lnum ||
5660 (qfp->qf_lnum == pos->lnum && qfp->qf_col <= pos->col));
5661}
5662
5663/*
5664 * Find the first quickfix entry after position 'pos' in buffer 'bnr'.
5665 * If 'linewise' is TRUE, returns the entry after the specified line and treats
5666 * multiple entries on a single line as one. Otherwise returns the entry after
5667 * the specified line and column.
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005668 * 'qfp' points to the very first entry in the buffer and 'errornr' is the
5669 * index of the very first entry in the quickfix list.
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005670 * Returns NULL if an entry is not found after 'pos'.
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005671 */
5672 static qfline_T *
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005673qf_find_entry_after_pos(
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005674 int bnr,
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005675 pos_T *pos,
5676 int linewise,
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005677 qfline_T *qfp,
5678 int *errornr)
5679{
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005680 if (qf_entry_after_pos(qfp, pos, linewise))
Bram Moolenaar32aa1022019-11-02 22:54:41 +01005681 // First entry is after position 'pos'
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005682 return qfp;
5683
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005684 // Find the entry just before or at the position 'pos'
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005685 while (qfp->qf_next != NULL
5686 && qfp->qf_next->qf_fnum == bnr
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005687 && qf_entry_on_or_before_pos(qfp->qf_next, pos, linewise))
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005688 {
5689 qfp = qfp->qf_next;
5690 ++*errornr;
5691 }
5692
5693 if (qfp->qf_next == NULL || qfp->qf_next->qf_fnum != bnr)
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005694 // No entries found after position 'pos'
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005695 return NULL;
5696
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005697 // Use the entry just after position 'pos'
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005698 qfp = qfp->qf_next;
5699 ++*errornr;
5700
5701 return qfp;
5702}
5703
5704/*
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005705 * Find the first quickfix entry before position 'pos' in buffer 'bnr'.
5706 * If 'linewise' is TRUE, returns the entry before the specified line and
5707 * treats multiple entries on a single line as one. Otherwise returns the entry
5708 * before the specified line and column.
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005709 * 'qfp' points to the very first entry in the buffer and 'errornr' is the
5710 * index of the very first entry in the quickfix list.
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005711 * Returns NULL if an entry is not found before 'pos'.
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005712 */
5713 static qfline_T *
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005714qf_find_entry_before_pos(
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005715 int bnr,
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005716 pos_T *pos,
5717 int linewise,
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005718 qfline_T *qfp,
5719 int *errornr)
5720{
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005721 // Find the entry just before the position 'pos'
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005722 while (qfp->qf_next != NULL
5723 && qfp->qf_next->qf_fnum == bnr
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005724 && qf_entry_before_pos(qfp->qf_next, pos, linewise))
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005725 {
5726 qfp = qfp->qf_next;
5727 ++*errornr;
5728 }
5729
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005730 if (qf_entry_on_or_after_pos(qfp, pos, linewise))
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005731 return NULL;
5732
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005733 if (linewise)
5734 // If multiple entries are on the same line, then use the first entry
5735 qfp = qf_find_first_entry_on_line(qfp, errornr);
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005736
5737 return qfp;
5738}
5739
5740/*
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005741 * Find a quickfix entry in 'qfl' closest to position 'pos' in buffer 'bnr' in
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005742 * the direction 'dir'.
5743 */
5744 static qfline_T *
5745qf_find_closest_entry(
5746 qf_list_T *qfl,
5747 int bnr,
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005748 pos_T *pos,
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005749 int dir,
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005750 int linewise,
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005751 int *errornr)
5752{
5753 qfline_T *qfp;
5754
5755 *errornr = 0;
5756
5757 // Find the first entry in this file
5758 qfp = qf_find_first_entry_in_buf(qfl, bnr, errornr);
5759 if (qfp == NULL)
5760 return NULL; // no entry in this file
5761
5762 if (dir == FORWARD)
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005763 qfp = qf_find_entry_after_pos(bnr, pos, linewise, qfp, errornr);
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005764 else
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005765 qfp = qf_find_entry_before_pos(bnr, pos, linewise, qfp, errornr);
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005766
5767 return qfp;
5768}
5769
5770/*
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005771 * Get the nth quickfix entry below the specified entry. Searches forward in
5772 * the list. If linewise is TRUE, then treat multiple entries on a single line
5773 * as one.
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005774 */
Bram Moolenaar64416122019-06-07 21:37:13 +02005775 static void
Bram Moolenaard6a98a32019-11-12 22:59:51 +01005776qf_get_nth_below_entry(qfline_T *entry_arg, int n, int linewise, int *errornr)
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005777{
Bram Moolenaard6a98a32019-11-12 22:59:51 +01005778 qfline_T *entry = entry_arg;
5779
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005780 while (n-- > 0 && !got_int)
5781 {
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005782 int first_errornr = *errornr;
5783
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005784 if (linewise)
5785 // Treat all the entries on the same line in this file as one
5786 entry = qf_find_last_entry_on_line(entry, errornr);
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005787
5788 if (entry->qf_next == NULL
5789 || entry->qf_next->qf_fnum != entry->qf_fnum)
5790 {
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005791 if (linewise)
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005792 *errornr = first_errornr;
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005793 break;
5794 }
5795
5796 entry = entry->qf_next;
5797 ++*errornr;
5798 }
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005799}
5800
5801/*
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005802 * Get the nth quickfix entry above the specified entry. Searches backwards in
5803 * the list. If linewise is TRUE, then treat multiple entries on a single line
5804 * as one.
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005805 */
Bram Moolenaar64416122019-06-07 21:37:13 +02005806 static void
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005807qf_get_nth_above_entry(qfline_T *entry, int n, int linewise, int *errornr)
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005808{
5809 while (n-- > 0 && !got_int)
5810 {
5811 if (entry->qf_prev == NULL
5812 || entry->qf_prev->qf_fnum != entry->qf_fnum)
5813 break;
5814
5815 entry = entry->qf_prev;
5816 --*errornr;
5817
5818 // If multiple entries are on the same line, then use the first entry
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005819 if (linewise)
5820 entry = qf_find_first_entry_on_line(entry, errornr);
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005821 }
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005822}
5823
5824/*
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005825 * Find the n'th quickfix entry adjacent to position 'pos' in buffer 'bnr' in
5826 * the specified direction. Returns the error number in the quickfix list or 0
5827 * if an entry is not found.
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005828 */
5829 static int
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005830qf_find_nth_adj_entry(
5831 qf_list_T *qfl,
5832 int bnr,
5833 pos_T *pos,
5834 int n,
5835 int dir,
5836 int linewise)
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005837{
5838 qfline_T *adj_entry;
5839 int errornr;
5840
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005841 // Find an entry closest to the specified position
5842 adj_entry = qf_find_closest_entry(qfl, bnr, pos, dir, linewise, &errornr);
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005843 if (adj_entry == NULL)
5844 return 0;
5845
5846 if (--n > 0)
5847 {
5848 // Go to the n'th entry in the current buffer
5849 if (dir == FORWARD)
Bram Moolenaar64416122019-06-07 21:37:13 +02005850 qf_get_nth_below_entry(adj_entry, n, linewise, &errornr);
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005851 else
Bram Moolenaar64416122019-06-07 21:37:13 +02005852 qf_get_nth_above_entry(adj_entry, n, linewise, &errornr);
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005853 }
5854
5855 return errornr;
5856}
5857
5858/*
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005859 * Jump to a quickfix entry in the current file nearest to the current line or
5860 * current line/col.
5861 * ":cabove", ":cbelow", ":labove", ":lbelow", ":cafter", ":cbefore",
5862 * ":lafter" and ":lbefore" commands
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005863 */
5864 void
5865ex_cbelow(exarg_T *eap)
5866{
5867 qf_info_T *qi;
5868 qf_list_T *qfl;
5869 int dir;
5870 int buf_has_flag;
5871 int errornr = 0;
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005872 pos_T pos;
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005873
5874 if (eap->addr_count > 0 && eap->line2 <= 0)
5875 {
Bram Moolenaar108010a2021-06-27 22:03:33 +02005876 emsg(_(e_invalid_range));
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005877 return;
5878 }
5879
5880 // Check whether the current buffer has any quickfix entries
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005881 if (eap->cmdidx == CMD_cabove || eap->cmdidx == CMD_cbelow
5882 || eap->cmdidx == CMD_cbefore || eap->cmdidx == CMD_cafter)
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005883 buf_has_flag = BUF_HAS_QF_ENTRY;
5884 else
5885 buf_has_flag = BUF_HAS_LL_ENTRY;
5886 if (!(curbuf->b_has_qf_entry & buf_has_flag))
5887 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02005888 emsg(_(e_no_errors));
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005889 return;
5890 }
5891
5892 if ((qi = qf_cmd_get_stack(eap, TRUE)) == NULL)
5893 return;
5894
5895 qfl = qf_get_curlist(qi);
5896 // check if the list has valid errors
5897 if (!qf_list_has_valid_entries(qfl))
5898 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02005899 emsg(_(e_no_errors));
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005900 return;
5901 }
5902
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005903 if (eap->cmdidx == CMD_cbelow
5904 || eap->cmdidx == CMD_lbelow
5905 || eap->cmdidx == CMD_cafter
5906 || eap->cmdidx == CMD_lafter)
5907 // Forward motion commands
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005908 dir = FORWARD;
5909 else
5910 dir = BACKWARD;
5911
Bram Moolenaarcf6a55c2019-05-05 15:02:30 +02005912 pos = curwin->w_cursor;
5913 // A quickfix entry column number is 1 based whereas cursor column
5914 // number is 0 based. Adjust the column number.
5915 pos.col++;
5916 errornr = qf_find_nth_adj_entry(qfl, curbuf->b_fnum, &pos,
5917 eap->addr_count > 0 ? eap->line2 : 0, dir,
5918 eap->cmdidx == CMD_cbelow
5919 || eap->cmdidx == CMD_lbelow
5920 || eap->cmdidx == CMD_cabove
5921 || eap->cmdidx == CMD_labove);
Bram Moolenaar3ff33112019-05-03 21:56:35 +02005922
5923 if (errornr > 0)
5924 qf_jump(qi, 0, errornr, FALSE);
5925 else
5926 emsg(_(e_no_more_items));
5927}
5928
5929/*
Bram Moolenaara16123a2019-03-28 20:31:07 +01005930 * Return the autocmd name for the :cfile Ex commands
5931 */
5932 static char_u *
5933cfile_get_auname(cmdidx_T cmdidx)
5934{
5935 switch (cmdidx)
5936 {
5937 case CMD_cfile: return (char_u *)"cfile";
5938 case CMD_cgetfile: return (char_u *)"cgetfile";
5939 case CMD_caddfile: return (char_u *)"caddfile";
5940 case CMD_lfile: return (char_u *)"lfile";
5941 case CMD_lgetfile: return (char_u *)"lgetfile";
5942 case CMD_laddfile: return (char_u *)"laddfile";
5943 default: return NULL;
5944 }
5945}
5946
5947/*
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00005948 * ":cfile"/":cgetfile"/":caddfile" commands.
Bram Moolenaard12f5c12006-01-25 22:10:52 +00005949 * ":lfile"/":lgetfile"/":laddfile" commands.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005950 */
5951 void
Bram Moolenaar05540972016-01-30 20:31:25 +01005952ex_cfile(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005953{
Bram Moolenaar2c7292d2017-03-05 17:43:31 +01005954 char_u *enc = NULL;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00005955 win_T *wp = NULL;
Bram Moolenaar8b6144b2006-02-08 09:20:24 +00005956 qf_info_T *qi = &ql_info;
Bram Moolenaar8ec1f852012-03-07 20:13:49 +01005957 char_u *au_name = NULL;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02005958 int_u save_qfid = 0; // init for gcc
Bram Moolenaar1ed22762017-11-28 18:03:44 +01005959 int res;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00005960
Bram Moolenaara16123a2019-03-28 20:31:07 +01005961 au_name = cfile_get_auname(eap->cmdidx);
Bram Moolenaar6a0cc912019-10-26 16:48:44 +02005962 if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
5963 NULL, FALSE, curbuf))
5964 {
5965#ifdef FEAT_EVAL
5966 if (aborting())
5967 return;
5968#endif
5969 }
Bram Moolenaara16123a2019-03-28 20:31:07 +01005970
Bram Moolenaar2c7292d2017-03-05 17:43:31 +01005971 enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc;
Bram Moolenaar9028b102010-07-11 16:58:51 +02005972#ifdef FEAT_BROWSE
Bram Moolenaare1004402020-10-24 20:49:43 +02005973 if (cmdmod.cmod_flags & CMOD_BROWSE)
Bram Moolenaar9028b102010-07-11 16:58:51 +02005974 {
5975 char_u *browse_file = do_browse(0, (char_u *)_("Error file"), eap->arg,
Bram Moolenaarc36651b2018-04-29 12:22:56 +02005976 NULL, NULL,
5977 (char_u *)_(BROWSE_FILTER_ALL_FILES), NULL);
Bram Moolenaar9028b102010-07-11 16:58:51 +02005978 if (browse_file == NULL)
5979 return;
5980 set_string_option_direct((char_u *)"ef", -1, browse_file, OPT_FREE, 0);
5981 vim_free(browse_file);
5982 }
5983 else
5984#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005985 if (*eap->arg != NUL)
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00005986 set_string_option_direct((char_u *)"ef", -1, eap->arg, OPT_FREE, 0);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00005987
Bram Moolenaar39665952018-08-15 20:59:48 +02005988 if (is_loclist_cmd(eap->cmdidx))
Bram Moolenaar14a4deb2017-12-19 16:48:55 +01005989 wp = curwin;
5990
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02005991 incr_quickfix_busy();
5992
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02005993 // This function is used by the :cfile, :cgetfile and :caddfile
5994 // commands.
Colin Kennedy21570352024-03-03 16:16:47 +01005995 // :cfile always creates a new quickfix list and may jump to the
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02005996 // first error.
5997 // :cgetfile creates a new quickfix list but doesn't jump to the
5998 // first error.
5999 // :caddfile adds to an existing quickfix list. If there is no
6000 // quickfix list then a new list is created.
Bram Moolenaar1ed22762017-11-28 18:03:44 +01006001 res = qf_init(wp, p_ef, p_efm, (eap->cmdidx != CMD_caddfile
Bram Moolenaar8b62e312018-05-13 15:29:04 +02006002 && eap->cmdidx != CMD_laddfile),
6003 qf_cmdtitle(*eap->cmdlinep), enc);
Bram Moolenaarb254af32017-12-18 19:48:58 +01006004 if (wp != NULL)
Bram Moolenaar531b9a32018-07-03 16:54:23 +02006005 {
Bram Moolenaarb254af32017-12-18 19:48:58 +01006006 qi = GET_LOC_LIST(wp);
Bram Moolenaar531b9a32018-07-03 16:54:23 +02006007 if (qi == NULL)
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02006008 {
6009 decr_quickfix_busy();
Bram Moolenaar531b9a32018-07-03 16:54:23 +02006010 return;
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02006011 }
Bram Moolenaar531b9a32018-07-03 16:54:23 +02006012 }
6013 if (res >= 0)
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01006014 qf_list_changed(qf_get_curlist(qi));
6015 save_qfid = qf_get_curlist(qi)->qf_id;
Bram Moolenaar1ed22762017-11-28 18:03:44 +01006016 if (au_name != NULL)
6017 apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, NULL, FALSE, curbuf);
Bram Moolenaar0549a1e2018-02-11 15:02:48 +01006018
Bram Moolenaar531b9a32018-07-03 16:54:23 +02006019 // Jump to the first error for a new list and if autocmds didn't
6020 // free the list.
6021 if (res > 0 && (eap->cmdidx == CMD_cfile || eap->cmdidx == CMD_lfile)
6022 && qflist_valid(wp, save_qfid))
Bram Moolenaar8d8a65e2018-08-07 19:48:08 +02006023 // display the first error
6024 qf_jump_first(qi, save_qfid, eap->forceit);
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02006025
6026 decr_quickfix_busy();
Bram Moolenaare1bb8792018-04-06 22:58:23 +02006027}
6028
6029/*
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006030 * Return the vimgrep autocmd name.
6031 */
6032 static char_u *
6033vgr_get_auname(cmdidx_T cmdidx)
6034{
6035 switch (cmdidx)
6036 {
6037 case CMD_vimgrep: return (char_u *)"vimgrep";
6038 case CMD_lvimgrep: return (char_u *)"lvimgrep";
6039 case CMD_vimgrepadd: return (char_u *)"vimgrepadd";
6040 case CMD_lvimgrepadd: return (char_u *)"lvimgrepadd";
6041 case CMD_grep: return (char_u *)"grep";
6042 case CMD_lgrep: return (char_u *)"lgrep";
6043 case CMD_grepadd: return (char_u *)"grepadd";
6044 case CMD_lgrepadd: return (char_u *)"lgrepadd";
6045 default: return NULL;
6046 }
6047}
6048
6049/*
6050 * Initialize the regmatch used by vimgrep for pattern "s".
6051 */
6052 static void
6053vgr_init_regmatch(regmmatch_T *regmatch, char_u *s)
6054{
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006055 // Get the search pattern: either white-separated or enclosed in //
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006056 regmatch->regprog = NULL;
6057
6058 if (s == NULL || *s == NUL)
6059 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006060 // Pattern is empty, use last search pattern.
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006061 if (last_search_pat() == NULL)
6062 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02006063 emsg(_(e_no_previous_regular_expression));
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006064 return;
6065 }
6066 regmatch->regprog = vim_regcomp(last_search_pat(), RE_MAGIC);
6067 }
6068 else
6069 regmatch->regprog = vim_regcomp(s, RE_MAGIC);
6070
6071 regmatch->rmm_ic = p_ic;
6072 regmatch->rmm_maxcol = 0;
6073}
6074
6075/*
6076 * Display a file name when vimgrep is running.
6077 */
6078 static void
6079vgr_display_fname(char_u *fname)
6080{
6081 char_u *p;
6082
6083 msg_start();
6084 p = msg_strtrunc(fname, TRUE);
6085 if (p == NULL)
6086 msg_outtrans(fname);
6087 else
6088 {
6089 msg_outtrans(p);
6090 vim_free(p);
6091 }
6092 msg_clr_eos();
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006093 msg_didout = FALSE; // overwrite this message
6094 msg_nowait = TRUE; // don't wait for this message
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006095 msg_col = 0;
6096 out_flush();
6097}
6098
6099/*
6100 * Load a dummy buffer to search for a pattern using vimgrep.
6101 */
6102 static buf_T *
6103vgr_load_dummy_buf(
6104 char_u *fname,
6105 char_u *dirname_start,
6106 char_u *dirname_now)
6107{
6108 int save_mls;
6109#if defined(FEAT_SYN_HL)
6110 char_u *save_ei = NULL;
6111#endif
6112 buf_T *buf;
6113
6114#if defined(FEAT_SYN_HL)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006115 // Don't do Filetype autocommands to avoid loading syntax and
6116 // indent scripts, a great speed improvement.
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006117 save_ei = au_event_disable(",Filetype");
6118#endif
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006119 // Don't use modelines here, it's useless.
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006120 save_mls = p_mls;
6121 p_mls = 0;
6122
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006123 // Load file into a buffer, so that 'fileencoding' is detected,
6124 // autocommands applied, etc.
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006125 buf = load_dummy_buffer(fname, dirname_start, dirname_now);
6126
6127 p_mls = save_mls;
6128#if defined(FEAT_SYN_HL)
6129 au_event_restore(save_ei);
6130#endif
6131
6132 return buf;
6133}
6134
6135/*
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01006136 * Check whether a quickfix/location list is valid. Autocmds may remove or
6137 * change a quickfix list when vimgrep is running. If the list is not found,
6138 * create a new list.
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006139 */
6140 static int
6141vgr_qflist_valid(
Bram Moolenaare1bb8792018-04-06 22:58:23 +02006142 win_T *wp,
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006143 qf_info_T *qi,
Bram Moolenaare1bb8792018-04-06 22:58:23 +02006144 int_u qfid,
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006145 char_u *title)
6146{
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006147 // Verify that the quickfix/location list was not freed by an autocmd
Bram Moolenaare1bb8792018-04-06 22:58:23 +02006148 if (!qflist_valid(wp, qfid))
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006149 {
Bram Moolenaare1bb8792018-04-06 22:58:23 +02006150 if (wp != NULL)
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006151 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006152 // An autocmd has freed the location list.
Bram Moolenaar4d170af2020-09-13 22:21:22 +02006153 emsg(_(e_current_location_list_was_changed));
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006154 return FALSE;
6155 }
Bram Moolenaare1bb8792018-04-06 22:58:23 +02006156 else
6157 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006158 // Quickfix list is not found, create a new one.
Bram Moolenaare1bb8792018-04-06 22:58:23 +02006159 qf_new_list(qi, title);
6160 return TRUE;
6161 }
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006162 }
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006163
Bram Moolenaar90f1e2b2018-08-11 13:36:56 +02006164 if (qf_restore_list(qi, qfid) == FAIL)
6165 return FALSE;
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006166
6167 return TRUE;
6168}
6169
6170/*
6171 * Search for a pattern in all the lines in a buffer and add the matching lines
6172 * to a quickfix list.
6173 */
6174 static int
6175vgr_match_buflines(
Bram Moolenaar9afe5e92019-03-22 14:16:06 +01006176 qf_list_T *qfl,
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006177 char_u *fname,
6178 buf_T *buf,
Yegappan Lakshmananbb01a1e2021-04-26 21:17:52 +02006179 char_u *spat,
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006180 regmmatch_T *regmatch,
Bram Moolenaar1c299432018-10-28 14:36:09 +01006181 long *tomatch,
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006182 int duplicate_name,
6183 int flags)
6184{
6185 int found_match = FALSE;
6186 long lnum;
6187 colnr_T col;
Bram Moolenaar551c1ae2021-05-03 18:57:05 +02006188 int pat_len = (int)STRLEN(spat);
Bram Moolenaarcaf642c2023-04-29 21:38:04 +01006189 if (pat_len > MAX_FUZZY_MATCHES)
6190 pat_len = MAX_FUZZY_MATCHES;
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006191
Bram Moolenaar1c299432018-10-28 14:36:09 +01006192 for (lnum = 1; lnum <= buf->b_ml.ml_line_count && *tomatch > 0; ++lnum)
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006193 {
6194 col = 0;
Yegappan Lakshmananbb01a1e2021-04-26 21:17:52 +02006195 if (!(flags & VGR_FUZZY))
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006196 {
Yegappan Lakshmananbb01a1e2021-04-26 21:17:52 +02006197 // Regular expression match
6198 while (vim_regexec_multi(regmatch, curwin, buf, lnum,
Bram Moolenaarcaf642c2023-04-29 21:38:04 +01006199 col, NULL) > 0)
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006200 {
Yegappan Lakshmananbb01a1e2021-04-26 21:17:52 +02006201 // Pass the buffer number so that it gets used even for a
6202 // dummy buffer, unless duplicate_name is set, then the
6203 // buffer will be wiped out below.
6204 if (qf_add_entry(qfl,
6205 NULL, // dir
6206 fname,
6207 NULL,
6208 duplicate_name ? 0 : buf->b_fnum,
6209 ml_get_buf(buf,
6210 regmatch->startpos[0].lnum + lnum, FALSE),
6211 regmatch->startpos[0].lnum + lnum,
thinca6864efa2021-06-19 20:45:20 +02006212 regmatch->endpos[0].lnum + lnum,
Yegappan Lakshmananbb01a1e2021-04-26 21:17:52 +02006213 regmatch->startpos[0].col + 1,
thinca6864efa2021-06-19 20:45:20 +02006214 regmatch->endpos[0].col + 1,
Yegappan Lakshmananbb01a1e2021-04-26 21:17:52 +02006215 FALSE, // vis_col
6216 NULL, // search pattern
6217 0, // nr
6218 0, // type
Tom Praschanca6ac992023-08-11 23:26:12 +02006219 NULL, // user_data
Yegappan Lakshmananbb01a1e2021-04-26 21:17:52 +02006220 TRUE // valid
6221 ) == QF_FAIL)
6222 {
6223 got_int = TRUE;
6224 break;
6225 }
6226 found_match = TRUE;
6227 if (--*tomatch == 0)
6228 break;
6229 if ((flags & VGR_GLOBAL) == 0
6230 || regmatch->endpos[0].lnum > 0)
6231 break;
6232 col = regmatch->endpos[0].col
6233 + (col == regmatch->endpos[0].col);
zeertzjq94b7c322024-03-12 21:50:32 +01006234 if (col > ml_get_buf_len(buf, lnum))
Yegappan Lakshmananbb01a1e2021-04-26 21:17:52 +02006235 break;
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006236 }
Yegappan Lakshmananbb01a1e2021-04-26 21:17:52 +02006237 }
6238 else
6239 {
6240 char_u *str = ml_get_buf(buf, lnum, FALSE);
zeertzjq8c55d602024-03-13 20:42:26 +01006241 colnr_T linelen = ml_get_buf_len(buf, lnum);
Yegappan Lakshmananbb01a1e2021-04-26 21:17:52 +02006242 int score;
6243 int_u matches[MAX_FUZZY_MATCHES];
K.Takataeeec2542021-06-02 13:28:16 +02006244 int_u sz = ARRAY_LENGTH(matches);
Yegappan Lakshmananbb01a1e2021-04-26 21:17:52 +02006245
6246 // Fuzzy string match
Bram Moolenaarcaf642c2023-04-29 21:38:04 +01006247 CLEAR_FIELD(matches);
Yegappan Lakshmananbb01a1e2021-04-26 21:17:52 +02006248 while (fuzzy_match(str + col, spat, FALSE, &score, matches, sz) > 0)
6249 {
6250 // Pass the buffer number so that it gets used even for a
6251 // dummy buffer, unless duplicate_name is set, then the
6252 // buffer will be wiped out below.
6253 if (qf_add_entry(qfl,
6254 NULL, // dir
6255 fname,
6256 NULL,
6257 duplicate_name ? 0 : buf->b_fnum,
6258 str,
6259 lnum,
thinca6864efa2021-06-19 20:45:20 +02006260 0,
Yegappan Lakshmananbb01a1e2021-04-26 21:17:52 +02006261 matches[0] + col + 1,
thinca6864efa2021-06-19 20:45:20 +02006262 0,
Yegappan Lakshmananbb01a1e2021-04-26 21:17:52 +02006263 FALSE, // vis_col
6264 NULL, // search pattern
6265 0, // nr
6266 0, // type
Tom Praschanca6ac992023-08-11 23:26:12 +02006267 NULL, // user_data
Yegappan Lakshmananbb01a1e2021-04-26 21:17:52 +02006268 TRUE // valid
6269 ) == QF_FAIL)
6270 {
6271 got_int = TRUE;
6272 break;
6273 }
6274 found_match = TRUE;
6275 if (--*tomatch == 0)
6276 break;
6277 if ((flags & VGR_GLOBAL) == 0)
6278 break;
6279 col = matches[pat_len - 1] + col + 1;
zeertzjq8c55d602024-03-13 20:42:26 +01006280 if (col > linelen)
Yegappan Lakshmananbb01a1e2021-04-26 21:17:52 +02006281 break;
6282 }
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006283 }
6284 line_breakcheck();
6285 if (got_int)
6286 break;
6287 }
6288
6289 return found_match;
6290}
6291
6292/*
6293 * Jump to the first match and update the directory.
6294 */
6295 static void
6296vgr_jump_to_match(
6297 qf_info_T *qi,
6298 int forceit,
6299 int *redraw_for_dummy,
6300 buf_T *first_match_buf,
6301 char_u *target_dir)
6302{
6303 buf_T *buf;
6304
6305 buf = curbuf;
6306 qf_jump(qi, 0, 0, forceit);
6307 if (buf != curbuf)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006308 // If we jumped to another buffer redrawing will already be
6309 // taken care of.
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006310 *redraw_for_dummy = FALSE;
6311
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006312 // Jump to the directory used after loading the buffer.
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006313 if (curbuf == first_match_buf && target_dir != NULL)
6314 {
6315 exarg_T ea;
6316
Bram Moolenaara80faa82020-04-12 19:37:17 +02006317 CLEAR_FIELD(ea);
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006318 ea.arg = target_dir;
6319 ea.cmdidx = CMD_lcd;
6320 ex_cd(&ea);
6321 }
6322}
6323
6324/*
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006325 * :vimgrep command arguments
Bram Moolenaar86b68352004-12-27 21:59:20 +00006326 */
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006327typedef struct
Bram Moolenaar86b68352004-12-27 21:59:20 +00006328{
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006329 long tomatch; // maximum number of matches to find
6330 char_u *spat; // search pattern
6331 int flags; // search modifier
6332 char_u **fnames; // list of files to search
6333 int fcount; // number of files
6334 regmmatch_T regmatch; // compiled search pattern
6335 char_u *qf_title; // quickfix list title
6336} vgr_args_T;
6337
6338/*
6339 * Process :vimgrep command arguments. The command syntax is:
6340 *
6341 * :{count}vimgrep /{pattern}/[g][j] {file} ...
6342 */
6343 static int
6344vgr_process_args(
6345 exarg_T *eap,
6346 vgr_args_T *args)
6347{
Bram Moolenaar748bf032005-02-02 23:04:36 +00006348 char_u *p;
Bram Moolenaar7c626922005-02-07 22:01:03 +00006349
Yegappan Lakshmanan960dcbd2023-03-07 17:45:11 +00006350 CLEAR_POINTER(args);
Bram Moolenaar86b68352004-12-27 21:59:20 +00006351
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006352 args->regmatch.regprog = NULL;
6353 args->qf_title = vim_strsave(qf_cmdtitle(*eap->cmdlinep));
Bram Moolenaara6557602006-02-04 22:43:20 +00006354
Bram Moolenaar1f35bf92006-03-07 22:38:47 +00006355 if (eap->addr_count > 0)
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006356 args->tomatch = eap->line2;
Bram Moolenaar1f35bf92006-03-07 22:38:47 +00006357 else
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006358 args->tomatch = MAXLNUM;
Bram Moolenaar1f35bf92006-03-07 22:38:47 +00006359
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006360 // Get the search pattern: either white-separated or enclosed in //
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006361 p = skip_vimgrep_pat(eap->arg, &args->spat, &args->flags);
Bram Moolenaar748bf032005-02-02 23:04:36 +00006362 if (p == NULL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00006363 {
Bram Moolenaar74409f62022-01-01 15:58:22 +00006364 emsg(_(e_invalid_search_pattern_or_delimiter));
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006365 return FAIL;
Bram Moolenaar81695252004-12-29 20:58:21 +00006366 }
Bram Moolenaar60abe752013-03-07 16:32:54 +01006367
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006368 vgr_init_regmatch(&args->regmatch, args->spat);
6369 if (args->regmatch.regprog == NULL)
6370 return FAIL;
Bram Moolenaar86b68352004-12-27 21:59:20 +00006371
6372 p = skipwhite(p);
6373 if (*p == NUL)
6374 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00006375 emsg(_(e_file_name_missing_or_invalid_pattern));
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006376 return FAIL;
Bram Moolenaar86b68352004-12-27 21:59:20 +00006377 }
6378
Bram Moolenaarf8c6a172021-01-30 18:09:06 +01006379 // Parse the list of arguments, wildcards have already been expanded.
Christian Brabandt0b226f62021-12-01 10:54:24 +00006380 if ((get_arglist_exp(p, &args->fcount, &args->fnames, TRUE) == FAIL) ||
6381 args->fcount == 0)
Bram Moolenaar86b68352004-12-27 21:59:20 +00006382 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00006383 emsg(_(e_no_match));
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006384 return FAIL;
Bram Moolenaar86b68352004-12-27 21:59:20 +00006385 }
6386
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006387 return OK;
6388}
6389
6390/*
Bram Moolenaar8ce4b7e2020-08-07 18:12:18 +02006391 * Return TRUE if "buf" had an existing swap file, the current swap file does
6392 * not end in ".swp".
6393 */
6394 static int
6395existing_swapfile(buf_T *buf)
6396{
Bram Moolenaar997cd1a2020-08-31 22:16:08 +02006397 if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL)
Bram Moolenaar8ce4b7e2020-08-07 18:12:18 +02006398 {
6399 char_u *fname = buf->b_ml.ml_mfp->mf_fname;
6400 size_t len = STRLEN(fname);
6401
6402 return fname[len - 1] != 'p' || fname[len - 2] != 'w';
6403 }
6404 return FALSE;
6405}
6406
6407/*
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006408 * Search for a pattern in a list of files and populate the quickfix list with
6409 * the matches.
6410 */
6411 static int
6412vgr_process_files(
6413 win_T *wp,
6414 qf_info_T *qi,
6415 vgr_args_T *cmd_args,
6416 int *redraw_for_dummy,
6417 buf_T **first_match_buf,
6418 char_u **target_dir)
6419{
6420 int status = FAIL;
6421 int_u save_qfid = qf_get_curlist(qi)->qf_id;
6422 time_t seconds = 0;
6423 char_u *fname;
6424 int fi;
6425 buf_T *buf;
6426 int duplicate_name = FALSE;
6427 int using_dummy;
6428 char_u *dirname_start = NULL;
6429 char_u *dirname_now = NULL;
6430 int found_match;
6431 aco_save_T aco;
6432
Bram Moolenaarb86a3432016-01-10 16:00:53 +01006433 dirname_start = alloc_id(MAXPATHL, aid_qf_dirname_start);
6434 dirname_now = alloc_id(MAXPATHL, aid_qf_dirname_now);
Bram Moolenaard9462e32011-04-11 21:35:11 +02006435 if (dirname_start == NULL || dirname_now == NULL)
6436 goto theend;
6437
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006438 // Remember the current directory, because a BufRead autocommand that does
6439 // ":lcd %:p:h" changes the meaning of short path names.
Bram Moolenaard089d9b2007-09-30 12:02:55 +00006440 mch_dirname(dirname_start, MAXPATHL);
6441
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006442 seconds = (time_t)0;
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006443 for (fi = 0; fi < cmd_args->fcount && !got_int && cmd_args->tomatch > 0;
6444 ++fi)
Bram Moolenaar86b68352004-12-27 21:59:20 +00006445 {
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006446 fname = shorten_fname1(cmd_args->fnames[fi]);
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006447 if (time(NULL) > seconds)
6448 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006449 // Display the file name every second or so, show the user we are
6450 // working on it.
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006451 seconds = time(NULL);
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006452 vgr_display_fname(fname);
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006453 }
6454
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006455 buf = buflist_findname_exp(cmd_args->fnames[fi]);
Bram Moolenaar81695252004-12-29 20:58:21 +00006456 if (buf == NULL || buf->b_ml.ml_mfp == NULL)
6457 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006458 // Remember that a buffer with this name already exists.
Bram Moolenaar81695252004-12-29 20:58:21 +00006459 duplicate_name = (buf != NULL);
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006460 using_dummy = TRUE;
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006461 *redraw_for_dummy = TRUE;
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006462
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006463 buf = vgr_load_dummy_buf(fname, dirname_start, dirname_now);
Bram Moolenaar81695252004-12-29 20:58:21 +00006464 }
6465 else
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006466 // Use existing, loaded buffer.
Bram Moolenaar81695252004-12-29 20:58:21 +00006467 using_dummy = FALSE;
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006468
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006469 // Check whether the quickfix list is still valid. When loading a
6470 // buffer above, autocommands might have changed the quickfix list.
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006471 if (!vgr_qflist_valid(wp, qi, save_qfid, cmd_args->qf_title))
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006472 goto theend;
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006473
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01006474 save_qfid = qf_get_curlist(qi)->qf_id;
Bram Moolenaar321a9ec2012-12-12 15:55:20 +01006475
Bram Moolenaar81695252004-12-29 20:58:21 +00006476 if (buf == NULL)
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006477 {
6478 if (!got_int)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006479 smsg(_("Cannot open file \"%s\""), fname);
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006480 }
Bram Moolenaar86b68352004-12-27 21:59:20 +00006481 else
6482 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006483 // Try for a match in all lines of the buffer.
6484 // For ":1vimgrep" look for first match only.
Bram Moolenaar9afe5e92019-03-22 14:16:06 +01006485 found_match = vgr_match_buflines(qf_get_curlist(qi),
Yegappan Lakshmananbb01a1e2021-04-26 21:17:52 +02006486 fname, buf, cmd_args->spat, &cmd_args->regmatch,
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006487 &cmd_args->tomatch, duplicate_name, cmd_args->flags);
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006488
Bram Moolenaar81695252004-12-29 20:58:21 +00006489 if (using_dummy)
6490 {
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006491 if (found_match && *first_match_buf == NULL)
6492 *first_match_buf = buf;
Bram Moolenaar81695252004-12-29 20:58:21 +00006493 if (duplicate_name)
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006494 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006495 // Never keep a dummy buffer if there is another buffer
6496 // with the same name.
Bram Moolenaar7f51a822012-04-25 18:57:21 +02006497 wipe_dummy_buffer(buf, dirname_start);
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006498 buf = NULL;
6499 }
Bram Moolenaare1004402020-10-24 20:49:43 +02006500 else if ((cmdmod.cmod_flags & CMOD_HIDE) == 0
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006501 || buf->b_p_bh[0] == 'u' // "unload"
6502 || buf->b_p_bh[0] == 'w' // "wipe"
6503 || buf->b_p_bh[0] == 'd') // "delete"
Bram Moolenaar81695252004-12-29 20:58:21 +00006504 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006505 // When no match was found we don't need to remember the
6506 // buffer, wipe it out. If there was a match and it
6507 // wasn't the first one or we won't jump there: only
6508 // unload the buffer.
6509 // Ignore 'hidden' here, because it may lead to having too
6510 // many swap files.
Bram Moolenaar81695252004-12-29 20:58:21 +00006511 if (!found_match)
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006512 {
Bram Moolenaar7f51a822012-04-25 18:57:21 +02006513 wipe_dummy_buffer(buf, dirname_start);
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006514 buf = NULL;
6515 }
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006516 else if (buf != *first_match_buf
Bram Moolenaar8ce4b7e2020-08-07 18:12:18 +02006517 || (cmd_args->flags & VGR_NOJUMP)
6518 || existing_swapfile(buf))
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006519 {
Bram Moolenaar7f51a822012-04-25 18:57:21 +02006520 unload_dummy_buffer(buf, dirname_start);
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006521 // Keeping the buffer, remove the dummy flag.
Bram Moolenaar015102e2016-07-16 18:24:56 +02006522 buf->b_flags &= ~BF_DUMMY;
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006523 buf = NULL;
6524 }
Bram Moolenaar81695252004-12-29 20:58:21 +00006525 }
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006526
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006527 if (buf != NULL)
6528 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006529 // Keeping the buffer, remove the dummy flag.
Bram Moolenaar015102e2016-07-16 18:24:56 +02006530 buf->b_flags &= ~BF_DUMMY;
6531
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006532 // If the buffer is still loaded we need to use the
6533 // directory we jumped to below.
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006534 if (buf == *first_match_buf
6535 && *target_dir == NULL
Bram Moolenaard089d9b2007-09-30 12:02:55 +00006536 && STRCMP(dirname_start, dirname_now) != 0)
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006537 *target_dir = vim_strsave(dirname_now);
Bram Moolenaard089d9b2007-09-30 12:02:55 +00006538
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006539 // The buffer is still loaded, the Filetype autocommands
6540 // need to be done now, in that buffer. And the modelines
6541 // need to be done (again). But not the window-local
6542 // options!
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006543 aucmd_prepbuf(&aco, buf);
Bram Moolenaare76062c2022-11-28 18:51:43 +00006544 if (curbuf == buf)
6545 {
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01006546#if defined(FEAT_SYN_HL)
Bram Moolenaare76062c2022-11-28 18:51:43 +00006547 apply_autocmds(EVENT_FILETYPE, buf->b_p_ft,
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006548 buf->b_fname, TRUE, buf);
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00006549#endif
Bram Moolenaare76062c2022-11-28 18:51:43 +00006550 do_modelines(OPT_NOWIN);
6551 aucmd_restbuf(&aco);
6552 }
Bram Moolenaarfaa959a2006-02-20 21:37:40 +00006553 }
Bram Moolenaar81695252004-12-29 20:58:21 +00006554 }
Bram Moolenaar86b68352004-12-27 21:59:20 +00006555 }
6556 }
6557
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006558 status = OK;
6559
6560theend:
6561 vim_free(dirname_now);
6562 vim_free(dirname_start);
6563 return status;
6564}
6565
6566/*
6567 * ":vimgrep {pattern} file(s)"
6568 * ":vimgrepadd {pattern} file(s)"
6569 * ":lvimgrep {pattern} file(s)"
6570 * ":lvimgrepadd {pattern} file(s)"
6571 */
6572 void
6573ex_vimgrep(exarg_T *eap)
6574{
6575 vgr_args_T args;
6576 qf_info_T *qi;
6577 qf_list_T *qfl;
6578 int_u save_qfid;
6579 win_T *wp = NULL;
6580 int redraw_for_dummy = FALSE;
6581 buf_T *first_match_buf = NULL;
6582 char_u *target_dir = NULL;
6583 char_u *au_name = NULL;
6584 int status;
6585
Colin Kennedy21570352024-03-03 16:16:47 +01006586 if (!check_can_set_curbuf_forceit(eap->forceit))
6587 return;
6588
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006589 au_name = vgr_get_auname(eap->cmdidx);
6590 if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
6591 curbuf->b_fname, TRUE, curbuf))
6592 {
6593#ifdef FEAT_EVAL
6594 if (aborting())
6595 return;
6596#endif
6597 }
6598
6599 qi = qf_cmd_get_or_alloc_stack(eap, &wp);
6600 if (qi == NULL)
6601 return;
6602
6603 if (vgr_process_args(eap, &args) == FAIL)
6604 goto theend;
6605
6606 if ((eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd
6607 && eap->cmdidx != CMD_vimgrepadd
6608 && eap->cmdidx != CMD_lvimgrepadd)
6609 || qf_stack_empty(qi))
6610 // make place for a new list
6611 qf_new_list(qi, args.qf_title);
6612
6613 incr_quickfix_busy();
6614
6615 status = vgr_process_files(wp, qi, &args, &redraw_for_dummy,
6616 &first_match_buf, &target_dir);
6617 if (status != OK)
6618 {
6619 FreeWild(args.fcount, args.fnames);
6620 decr_quickfix_busy();
6621 goto theend;
6622 }
6623
6624 FreeWild(args.fcount, args.fnames);
Bram Moolenaar86b68352004-12-27 21:59:20 +00006625
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01006626 qfl = qf_get_curlist(qi);
Bram Moolenaar108e7b42018-10-11 17:39:12 +02006627 qfl->qf_nonevalid = FALSE;
6628 qfl->qf_ptr = qfl->qf_start;
6629 qfl->qf_index = 1;
6630 qf_list_changed(qfl);
Bram Moolenaar86b68352004-12-27 21:59:20 +00006631
Bram Moolenaar864293a2016-06-02 13:40:04 +02006632 qf_update_buffer(qi, NULL);
Bram Moolenaar86b68352004-12-27 21:59:20 +00006633
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006634 // Remember the current quickfix list identifier, so that we can check for
6635 // autocommands changing the current quickfix list.
6636 save_qfid = qf_get_curlist(qi)->qf_id;
6637
Bram Moolenaarf9393ef2006-04-24 19:47:27 +00006638 if (au_name != NULL)
6639 apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
6640 curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006641 // The QuickFixCmdPost autocmd may free the quickfix list. Check the list
6642 // is still valid.
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02006643 if (!qflist_valid(wp, save_qfid)
6644 || qf_restore_list(qi, save_qfid) == FAIL)
6645 {
6646 decr_quickfix_busy();
Bram Moolenaar3c097222017-12-21 20:54:49 +01006647 goto theend;
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02006648 }
Bram Moolenaar531b9a32018-07-03 16:54:23 +02006649
zeertzjq8c55d602024-03-13 20:42:26 +01006650 // Jump to first match.
Bram Moolenaar0398e002019-03-21 21:12:49 +01006651 if (!qf_list_empty(qf_get_curlist(qi)))
Bram Moolenaar05159a02005-02-26 23:04:13 +00006652 {
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006653 if ((args.flags & VGR_NOJUMP) == 0)
Bram Moolenaar75b0a882018-03-24 14:01:56 +01006654 vgr_jump_to_match(qi, eap->forceit, &redraw_for_dummy,
6655 first_match_buf, target_dir);
Bram Moolenaar05159a02005-02-26 23:04:13 +00006656 }
Bram Moolenaar81695252004-12-29 20:58:21 +00006657 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00006658 semsg(_(e_no_match_str_2), args.spat);
Bram Moolenaar86b68352004-12-27 21:59:20 +00006659
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02006660 decr_quickfix_busy();
6661
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006662 // If we loaded a dummy buffer into the current window, the autocommands
6663 // may have messed up things, need to redraw and recompute folds.
Bram Moolenaar1042fa32007-09-16 11:27:42 +00006664 if (redraw_for_dummy)
6665 {
6666#ifdef FEAT_FOLDING
6667 foldUpdateAll(curwin);
6668#else
Bram Moolenaara4d158b2022-08-14 14:17:45 +01006669 redraw_later(UPD_NOT_VALID);
Bram Moolenaar1042fa32007-09-16 11:27:42 +00006670#endif
6671 }
6672
Bram Moolenaar86b68352004-12-27 21:59:20 +00006673theend:
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006674 vim_free(args.qf_title);
Bram Moolenaard089d9b2007-09-30 12:02:55 +00006675 vim_free(target_dir);
Bram Moolenaard6a98a32019-11-12 22:59:51 +01006676 vim_regfree(args.regmatch.regprog);
Bram Moolenaar86b68352004-12-27 21:59:20 +00006677}
6678
6679/*
Bram Moolenaar7f51a822012-04-25 18:57:21 +02006680 * Restore current working directory to "dirname_start" if they differ, taking
6681 * into account whether it is set locally or globally.
6682 */
6683 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01006684restore_start_dir(char_u *dirname_start)
Bram Moolenaar7f51a822012-04-25 18:57:21 +02006685{
6686 char_u *dirname_now = alloc(MAXPATHL);
6687
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00006688 if (dirname_now == NULL)
6689 return;
Bram Moolenaar7f51a822012-04-25 18:57:21 +02006690
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00006691 mch_dirname(dirname_now, MAXPATHL);
6692 if (STRCMP(dirname_start, dirname_now) != 0)
6693 {
6694 // If the directory has changed, change it back by building up an
6695 // appropriate ex command and executing it.
6696 exarg_T ea;
6697
6698 CLEAR_FIELD(ea);
6699 ea.arg = dirname_start;
6700 ea.cmdidx = (curwin->w_localdir == NULL) ? CMD_cd : CMD_lcd;
6701 ex_cd(&ea);
Bram Moolenaar7f51a822012-04-25 18:57:21 +02006702 }
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00006703 vim_free(dirname_now);
Bram Moolenaar7f51a822012-04-25 18:57:21 +02006704}
6705
6706/*
6707 * Load file "fname" into a dummy buffer and return the buffer pointer,
6708 * placing the directory resulting from the buffer load into the
6709 * "resulting_dir" pointer. "resulting_dir" must be allocated by the caller
6710 * prior to calling this function. Restores directory to "dirname_start" prior
6711 * to returning, if autocmds or the 'autochdir' option have changed it.
6712 *
6713 * If creating the dummy buffer does not fail, must call unload_dummy_buffer()
6714 * or wipe_dummy_buffer() later!
6715 *
Bram Moolenaar81695252004-12-29 20:58:21 +00006716 * Returns NULL if it fails.
Bram Moolenaar81695252004-12-29 20:58:21 +00006717 */
6718 static buf_T *
Bram Moolenaar05540972016-01-30 20:31:25 +01006719load_dummy_buffer(
6720 char_u *fname,
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006721 char_u *dirname_start, // in: old directory
6722 char_u *resulting_dir) // out: new directory
Bram Moolenaar81695252004-12-29 20:58:21 +00006723{
6724 buf_T *newbuf;
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02006725 bufref_T newbufref;
6726 bufref_T newbuf_to_wipe;
Bram Moolenaar81695252004-12-29 20:58:21 +00006727 int failed = TRUE;
Bram Moolenaar81695252004-12-29 20:58:21 +00006728 aco_save_T aco;
Bram Moolenaar4fb921e2017-12-18 15:33:00 +01006729 int readfile_result;
Bram Moolenaar81695252004-12-29 20:58:21 +00006730
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006731 // Allocate a buffer without putting it in the buffer list.
Bram Moolenaar81695252004-12-29 20:58:21 +00006732 newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY);
6733 if (newbuf == NULL)
6734 return NULL;
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02006735 set_bufref(&newbufref, newbuf);
Bram Moolenaar81695252004-12-29 20:58:21 +00006736
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006737 // Init the options.
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00006738 buf_copy_options(newbuf, BCO_ENTER | BCO_NOHELP);
6739
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006740 // need to open the memfile before putting the buffer in a window
Bram Moolenaarf061e0b2009-06-24 15:32:01 +00006741 if (ml_open(newbuf) == OK)
Bram Moolenaar81695252004-12-29 20:58:21 +00006742 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006743 // Make sure this buffer isn't wiped out by autocommands.
Bram Moolenaar4fb921e2017-12-18 15:33:00 +01006744 ++newbuf->b_locked;
6745
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006746 // set curwin/curbuf to buf and save a few things
Bram Moolenaarf061e0b2009-06-24 15:32:01 +00006747 aucmd_prepbuf(&aco, newbuf);
Bram Moolenaare76062c2022-11-28 18:51:43 +00006748 if (curbuf == newbuf)
Bram Moolenaar81695252004-12-29 20:58:21 +00006749 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00006750 // Need to set the filename for autocommands.
6751 (void)setfname(curbuf, fname, NULL, FALSE);
6752
6753 // Create swap file now to avoid the ATTENTION message.
6754 check_need_swap(TRUE);
6755
6756 // Remove the "dummy" flag, otherwise autocommands may not
6757 // work.
6758 curbuf->b_flags &= ~BF_DUMMY;
6759
6760 newbuf_to_wipe.br_buf = NULL;
6761 readfile_result = readfile(fname, NULL,
6762 (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM,
6763 NULL, READ_NEW | READ_DUMMY);
6764 --newbuf->b_locked;
6765 if (readfile_result == OK
6766 && !got_int
6767 && !(curbuf->b_flags & BF_NEW))
Bram Moolenaar81695252004-12-29 20:58:21 +00006768 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00006769 failed = FALSE;
6770 if (curbuf != newbuf)
6771 {
6772 // Bloody autocommands changed the buffer! Can happen when
6773 // using netrw and editing a remote file. Use the current
6774 // buffer instead, delete the dummy one after restoring the
6775 // window stuff.
6776 set_bufref(&newbuf_to_wipe, newbuf);
6777 newbuf = curbuf;
6778 }
Bram Moolenaar81695252004-12-29 20:58:21 +00006779 }
Bram Moolenaare76062c2022-11-28 18:51:43 +00006780
6781 // restore curwin/curbuf and a few other things
6782 aucmd_restbuf(&aco);
Bram Moolenaar81695252004-12-29 20:58:21 +00006783
Bram Moolenaar84497cd2022-11-28 20:34:52 +00006784 if (newbuf_to_wipe.br_buf != NULL && bufref_valid(&newbuf_to_wipe))
6785 wipe_buffer(newbuf_to_wipe.br_buf, FALSE);
6786 }
Bram Moolenaarea3f2e72016-07-10 20:27:32 +02006787
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006788 // Add back the "dummy" flag, otherwise buflist_findname_stat() won't
6789 // skip it.
Bram Moolenaarea3f2e72016-07-10 20:27:32 +02006790 newbuf->b_flags |= BF_DUMMY;
Bram Moolenaarf061e0b2009-06-24 15:32:01 +00006791 }
Bram Moolenaar81695252004-12-29 20:58:21 +00006792
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006793 // When autocommands/'autochdir' option changed directory: go back.
6794 // Let the caller know what the resulting dir was first, in case it is
6795 // important.
Bram Moolenaar7f51a822012-04-25 18:57:21 +02006796 mch_dirname(resulting_dir, MAXPATHL);
6797 restore_start_dir(dirname_start);
6798
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02006799 if (!bufref_valid(&newbufref))
Bram Moolenaar81695252004-12-29 20:58:21 +00006800 return NULL;
6801 if (failed)
6802 {
Bram Moolenaar7f51a822012-04-25 18:57:21 +02006803 wipe_dummy_buffer(newbuf, dirname_start);
Bram Moolenaar81695252004-12-29 20:58:21 +00006804 return NULL;
6805 }
6806 return newbuf;
6807}
6808
6809/*
Bram Moolenaar7f51a822012-04-25 18:57:21 +02006810 * Wipe out the dummy buffer that load_dummy_buffer() created. Restores
6811 * directory to "dirname_start" prior to returning, if autocmds or the
6812 * 'autochdir' option have changed it.
Bram Moolenaar81695252004-12-29 20:58:21 +00006813 */
6814 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01006815wipe_dummy_buffer(buf_T *buf, char_u *dirname_start)
Bram Moolenaar81695252004-12-29 20:58:21 +00006816{
Bram Moolenaar2573af32020-03-14 17:21:34 +01006817 // If any autocommand opened a window on the dummy buffer, close that
6818 // window. If we can't close them all then give up.
6819 while (buf->b_nwindows > 0)
6820 {
6821 int did_one = FALSE;
6822 win_T *wp;
6823
6824 if (firstwin->w_next != NULL)
Bram Moolenaar00d253e2020-04-06 22:13:01 +02006825 FOR_ALL_WINDOWS(wp)
Bram Moolenaar2573af32020-03-14 17:21:34 +01006826 if (wp->w_buffer == buf)
6827 {
6828 if (win_close(wp, FALSE) == OK)
6829 did_one = TRUE;
6830 break;
6831 }
6832 if (!did_one)
6833 return;
6834 }
6835
6836 if (curbuf != buf && buf->b_nwindows == 0) // safety check
Bram Moolenaard68071d2006-05-02 22:08:30 +00006837 {
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01006838#if defined(FEAT_EVAL)
Bram Moolenaard68071d2006-05-02 22:08:30 +00006839 cleanup_T cs;
6840
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006841 // Reset the error/interrupt/exception state here so that aborting()
6842 // returns FALSE when wiping out the buffer. Otherwise it doesn't
6843 // work when got_int is set.
Bram Moolenaard68071d2006-05-02 22:08:30 +00006844 enter_cleanup(&cs);
6845#endif
6846
Bram Moolenaar1cfb9bb2020-12-22 11:40:45 +01006847 wipe_buffer(buf, TRUE);
Bram Moolenaard68071d2006-05-02 22:08:30 +00006848
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01006849#if defined(FEAT_EVAL)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006850 // Restore the error/interrupt/exception state if not discarded by a
6851 // new aborting error, interrupt, or uncaught exception.
Bram Moolenaard68071d2006-05-02 22:08:30 +00006852 leave_cleanup(&cs);
6853#endif
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006854 // When autocommands/'autochdir' option changed directory: go back.
Bram Moolenaar7f51a822012-04-25 18:57:21 +02006855 restore_start_dir(dirname_start);
Bram Moolenaard68071d2006-05-02 22:08:30 +00006856 }
Bram Moolenaar81695252004-12-29 20:58:21 +00006857}
6858
6859/*
Bram Moolenaar7f51a822012-04-25 18:57:21 +02006860 * Unload the dummy buffer that load_dummy_buffer() created. Restores
6861 * directory to "dirname_start" prior to returning, if autocmds or the
6862 * 'autochdir' option have changed it.
Bram Moolenaar81695252004-12-29 20:58:21 +00006863 */
6864 static void
Bram Moolenaar05540972016-01-30 20:31:25 +01006865unload_dummy_buffer(buf_T *buf, char_u *dirname_start)
Bram Moolenaar81695252004-12-29 20:58:21 +00006866{
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00006867 if (curbuf == buf) // safety check
6868 return;
Bram Moolenaar7f51a822012-04-25 18:57:21 +02006869
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00006870 close_buffer(NULL, buf, DOBUF_UNLOAD, FALSE, TRUE);
6871
6872 // When autocommands/'autochdir' option changed directory: go back.
6873 restore_start_dir(dirname_start);
Bram Moolenaar81695252004-12-29 20:58:21 +00006874}
6875
Bram Moolenaar05159a02005-02-26 23:04:13 +00006876#if defined(FEAT_EVAL) || defined(PROTO)
6877/*
Bram Moolenaar4b96df52020-01-26 22:00:26 +01006878 * Copy the specified quickfix entry items into a new dict and append the dict
Bram Moolenaara16123a2019-03-28 20:31:07 +01006879 * to 'list'. Returns OK on success.
6880 */
6881 static int
6882get_qfline_items(qfline_T *qfp, list_T *list)
6883{
6884 int bufnum;
6885 dict_T *dict;
6886 char_u buf[2];
6887
6888 // Handle entries with a non-existing buffer number.
6889 bufnum = qfp->qf_fnum;
6890 if (bufnum != 0 && (buflist_findnr(bufnum) == NULL))
6891 bufnum = 0;
6892
6893 if ((dict = dict_alloc()) == NULL)
6894 return FAIL;
6895 if (list_append_dict(list, dict) == FAIL)
6896 return FAIL;
6897
6898 buf[0] = qfp->qf_type;
6899 buf[1] = NUL;
6900 if (dict_add_number(dict, "bufnr", (long)bufnum) == FAIL
thinca6864efa2021-06-19 20:45:20 +02006901 || dict_add_number(dict, "lnum", (long)qfp->qf_lnum) == FAIL
6902 || dict_add_number(dict, "end_lnum", (long)qfp->qf_end_lnum) == FAIL
6903 || dict_add_number(dict, "col", (long)qfp->qf_col) == FAIL
6904 || dict_add_number(dict, "end_col", (long)qfp->qf_end_col) == FAIL
6905 || dict_add_number(dict, "vcol", (long)qfp->qf_viscol) == FAIL
6906 || dict_add_number(dict, "nr", (long)qfp->qf_nr) == FAIL
Bram Moolenaara16123a2019-03-28 20:31:07 +01006907 || dict_add_string(dict, "module", qfp->qf_module) == FAIL
6908 || dict_add_string(dict, "pattern", qfp->qf_pattern) == FAIL
6909 || dict_add_string(dict, "text", qfp->qf_text) == FAIL
6910 || dict_add_string(dict, "type", buf) == FAIL
Tom Praschanca6ac992023-08-11 23:26:12 +02006911 || (qfp->qf_user_data.v_type != VAR_UNKNOWN
6912 && dict_add_tv(dict, "user_data", &qfp->qf_user_data) == FAIL )
Bram Moolenaara16123a2019-03-28 20:31:07 +01006913 || dict_add_number(dict, "valid", (long)qfp->qf_valid) == FAIL)
6914 return FAIL;
6915
6916 return OK;
6917}
6918
6919/*
Bram Moolenaar05159a02005-02-26 23:04:13 +00006920 * Add each quickfix error to list "list" as a dictionary.
Bram Moolenaard823fa92016-08-12 16:29:27 +02006921 * If qf_idx is -1, use the current list. Otherwise, use the specified list.
Bram Moolenaar858ba062020-05-31 23:11:59 +02006922 * If eidx is not 0, then return only the specified entry. Otherwise return
6923 * all the entries.
Bram Moolenaar05159a02005-02-26 23:04:13 +00006924 */
Bram Moolenaare677df82019-09-02 22:31:11 +02006925 static int
Bram Moolenaar858ba062020-05-31 23:11:59 +02006926get_errorlist(
6927 qf_info_T *qi_arg,
6928 win_T *wp,
6929 int qf_idx,
6930 int eidx,
6931 list_T *list)
Bram Moolenaar05159a02005-02-26 23:04:13 +00006932{
Bram Moolenaar7adf06f2017-08-27 15:23:41 +02006933 qf_info_T *qi = qi_arg;
Bram Moolenaar0398e002019-03-21 21:12:49 +01006934 qf_list_T *qfl;
Bram Moolenaar68b76a62005-03-25 21:53:48 +00006935 qfline_T *qfp;
6936 int i;
Bram Moolenaar05159a02005-02-26 23:04:13 +00006937
Bram Moolenaar7adf06f2017-08-27 15:23:41 +02006938 if (qi == NULL)
Bram Moolenaar17c7c012006-01-26 22:25:15 +00006939 {
Bram Moolenaar7adf06f2017-08-27 15:23:41 +02006940 qi = &ql_info;
6941 if (wp != NULL)
6942 {
6943 qi = GET_LOC_LIST(wp);
6944 if (qi == NULL)
6945 return FAIL;
6946 }
Bram Moolenaar17c7c012006-01-26 22:25:15 +00006947 }
6948
Bram Moolenaar858ba062020-05-31 23:11:59 +02006949 if (eidx < 0)
6950 return OK;
6951
Bram Moolenaar29ce4092018-04-28 21:56:44 +02006952 if (qf_idx == INVALID_QFIDX)
Bram Moolenaard823fa92016-08-12 16:29:27 +02006953 qf_idx = qi->qf_curlist;
6954
Bram Moolenaar0398e002019-03-21 21:12:49 +01006955 if (qf_idx >= qi->qf_listcount)
Bram Moolenaar05159a02005-02-26 23:04:13 +00006956 return FAIL;
Bram Moolenaar05159a02005-02-26 23:04:13 +00006957
Bram Moolenaar0398e002019-03-21 21:12:49 +01006958 qfl = qf_get_list(qi, qf_idx);
6959 if (qf_list_empty(qfl))
6960 return FAIL;
6961
Bram Moolenaara16123a2019-03-28 20:31:07 +01006962 FOR_ALL_QFL_ITEMS(qfl, qfp, i)
Bram Moolenaar05159a02005-02-26 23:04:13 +00006963 {
Bram Moolenaar858ba062020-05-31 23:11:59 +02006964 if (eidx > 0)
6965 {
6966 if (eidx == i)
6967 return get_qfline_items(qfp, list);
6968 }
6969 else if (get_qfline_items(qfp, list) == FAIL)
Bram Moolenaar05159a02005-02-26 23:04:13 +00006970 return FAIL;
Bram Moolenaar05159a02005-02-26 23:04:13 +00006971 }
Bram Moolenaara16123a2019-03-28 20:31:07 +01006972
Bram Moolenaar05159a02005-02-26 23:04:13 +00006973 return OK;
6974}
Bram Moolenaar68b76a62005-03-25 21:53:48 +00006975
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02006976// Flags used by getqflist()/getloclist() to determine which fields to return.
Bram Moolenaard823fa92016-08-12 16:29:27 +02006977enum {
6978 QF_GETLIST_NONE = 0x0,
6979 QF_GETLIST_TITLE = 0x1,
6980 QF_GETLIST_ITEMS = 0x2,
6981 QF_GETLIST_NR = 0x4,
6982 QF_GETLIST_WINID = 0x8,
Bram Moolenaar8f77c5a2017-04-30 14:21:00 +02006983 QF_GETLIST_CONTEXT = 0x10,
Bram Moolenaara539f4f2017-08-30 20:33:55 +02006984 QF_GETLIST_ID = 0x20,
Bram Moolenaarfc2b2702017-09-15 22:43:07 +02006985 QF_GETLIST_IDX = 0x40,
6986 QF_GETLIST_SIZE = 0x80,
Bram Moolenaarb254af32017-12-18 19:48:58 +01006987 QF_GETLIST_TICK = 0x100,
Bram Moolenaarc9cc9c72018-09-02 15:18:42 +02006988 QF_GETLIST_FILEWINID = 0x200,
Bram Moolenaar647e24b2019-03-17 16:39:46 +01006989 QF_GETLIST_QFBUFNR = 0x400,
Bram Moolenaard43906d2020-07-20 21:31:32 +02006990 QF_GETLIST_QFTF = 0x800,
6991 QF_GETLIST_ALL = 0xFFF,
Bram Moolenaard823fa92016-08-12 16:29:27 +02006992};
6993
6994/*
Bram Moolenaar353eeea2018-04-16 18:04:57 +02006995 * Parse text from 'di' and return the quickfix list items.
6996 * Existing quickfix lists are not modified.
Bram Moolenaar7adf06f2017-08-27 15:23:41 +02006997 */
6998 static int
Bram Moolenaar36538222017-09-02 19:51:44 +02006999qf_get_list_from_lines(dict_T *what, dictitem_T *di, dict_T *retdict)
Bram Moolenaar7adf06f2017-08-27 15:23:41 +02007000{
7001 int status = FAIL;
7002 qf_info_T *qi;
Bram Moolenaar36538222017-09-02 19:51:44 +02007003 char_u *errorformat = p_efm;
7004 dictitem_T *efm_di;
7005 list_T *l;
Bram Moolenaar7adf06f2017-08-27 15:23:41 +02007006
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007007 // Only a List value is supported
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00007008 if (di->di_tv.v_type != VAR_LIST || di->di_tv.vval.v_list == NULL)
7009 return FAIL;
7010
7011 // If errorformat is supplied then use it, otherwise use the 'efm'
7012 // option setting
7013 if ((efm_di = dict_find(what, (char_u *)"efm", -1)) != NULL)
Bram Moolenaar7adf06f2017-08-27 15:23:41 +02007014 {
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00007015 if (efm_di->di_tv.v_type != VAR_STRING ||
7016 efm_di->di_tv.vval.v_string == NULL)
Bram Moolenaarda732532017-08-31 20:58:02 +02007017 return FAIL;
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00007018 errorformat = efm_di->di_tv.vval.v_string;
Bram Moolenaar7adf06f2017-08-27 15:23:41 +02007019 }
7020
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00007021 l = list_alloc();
7022 if (l == NULL)
7023 return FAIL;
7024
7025 qi = qf_alloc_stack(QFLT_INTERNAL);
7026 if (qi != NULL)
7027 {
7028 if (qf_init_ext(qi, 0, NULL, NULL, &di->di_tv, errorformat,
7029 TRUE, (linenr_T)0, (linenr_T)0, NULL, NULL) > 0)
7030 {
7031 (void)get_errorlist(qi, NULL, 0, 0, l);
7032 qf_free(&qi->qf_lists[0]);
7033 }
7034 free(qi);
7035 }
7036 dict_add_list(retdict, "items", l);
7037 status = OK;
7038
Bram Moolenaar7adf06f2017-08-27 15:23:41 +02007039 return status;
7040}
7041
7042/*
Bram Moolenaar2ec364e2018-01-27 11:52:13 +01007043 * Return the quickfix/location list window identifier in the current tabpage.
7044 */
7045 static int
7046qf_winid(qf_info_T *qi)
7047{
7048 win_T *win;
7049
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007050 // The quickfix window can be opened even if the quickfix list is not set
7051 // using ":copen". This is not true for location lists.
Bram Moolenaar2ec364e2018-01-27 11:52:13 +01007052 if (qi == NULL)
7053 return 0;
7054 win = qf_find_win(qi);
7055 if (win != NULL)
7056 return win->w_id;
7057 return 0;
7058}
7059
7060/*
Bram Moolenaar647e24b2019-03-17 16:39:46 +01007061 * Returns the number of the buffer displayed in the quickfix/location list
Yegappan Lakshmanan56150da2021-12-09 09:27:06 +00007062 * window. If there is no buffer associated with the list or the buffer is
7063 * wiped out, then returns 0.
Bram Moolenaar647e24b2019-03-17 16:39:46 +01007064 */
7065 static int
7066qf_getprop_qfbufnr(qf_info_T *qi, dict_T *retdict)
7067{
Yegappan Lakshmanan56150da2021-12-09 09:27:06 +00007068 int bufnum = 0;
7069
7070 if (qi != NULL && buflist_findnr(qi->qf_bufnr) != NULL)
7071 bufnum = qi->qf_bufnr;
7072
7073 return dict_add_number(retdict, "qfbufnr", bufnum);
Bram Moolenaar647e24b2019-03-17 16:39:46 +01007074}
7075
7076/*
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007077 * Convert the keys in 'what' to quickfix list property flags.
Bram Moolenaar68b76a62005-03-25 21:53:48 +00007078 */
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007079 static int
Bram Moolenaarc9cc9c72018-09-02 15:18:42 +02007080qf_getprop_keys2flags(dict_T *what, int loclist)
Bram Moolenaard823fa92016-08-12 16:29:27 +02007081{
Bram Moolenaard823fa92016-08-12 16:29:27 +02007082 int flags = QF_GETLIST_NONE;
7083
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01007084 if (dict_has_key(what, "all"))
Bram Moolenaarc9cc9c72018-09-02 15:18:42 +02007085 {
Bram Moolenaara539f4f2017-08-30 20:33:55 +02007086 flags |= QF_GETLIST_ALL;
Bram Moolenaarc9cc9c72018-09-02 15:18:42 +02007087 if (!loclist)
7088 // File window ID is applicable only to location list windows
7089 flags &= ~ QF_GETLIST_FILEWINID;
7090 }
Bram Moolenaard823fa92016-08-12 16:29:27 +02007091
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01007092 if (dict_has_key(what, "title"))
Bram Moolenaara539f4f2017-08-30 20:33:55 +02007093 flags |= QF_GETLIST_TITLE;
Bram Moolenaard823fa92016-08-12 16:29:27 +02007094
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01007095 if (dict_has_key(what, "nr"))
Bram Moolenaara6d48492017-12-12 22:45:31 +01007096 flags |= QF_GETLIST_NR;
7097
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01007098 if (dict_has_key(what, "winid"))
Bram Moolenaara539f4f2017-08-30 20:33:55 +02007099 flags |= QF_GETLIST_WINID;
Bram Moolenaard823fa92016-08-12 16:29:27 +02007100
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01007101 if (dict_has_key(what, "context"))
Bram Moolenaara539f4f2017-08-30 20:33:55 +02007102 flags |= QF_GETLIST_CONTEXT;
Bram Moolenaar6a8958d2017-06-22 21:33:20 +02007103
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01007104 if (dict_has_key(what, "id"))
Bram Moolenaara6d48492017-12-12 22:45:31 +01007105 flags |= QF_GETLIST_ID;
7106
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01007107 if (dict_has_key(what, "items"))
Bram Moolenaara539f4f2017-08-30 20:33:55 +02007108 flags |= QF_GETLIST_ITEMS;
Bram Moolenaar8f77c5a2017-04-30 14:21:00 +02007109
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01007110 if (dict_has_key(what, "idx"))
Bram Moolenaarfc2b2702017-09-15 22:43:07 +02007111 flags |= QF_GETLIST_IDX;
7112
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01007113 if (dict_has_key(what, "size"))
Bram Moolenaarfc2b2702017-09-15 22:43:07 +02007114 flags |= QF_GETLIST_SIZE;
7115
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01007116 if (dict_has_key(what, "changedtick"))
Bram Moolenaarb254af32017-12-18 19:48:58 +01007117 flags |= QF_GETLIST_TICK;
7118
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01007119 if (loclist && dict_has_key(what, "filewinid"))
Bram Moolenaarc9cc9c72018-09-02 15:18:42 +02007120 flags |= QF_GETLIST_FILEWINID;
7121
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01007122 if (dict_has_key(what, "qfbufnr"))
Bram Moolenaar647e24b2019-03-17 16:39:46 +01007123 flags |= QF_GETLIST_QFBUFNR;
7124
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01007125 if (dict_has_key(what, "quickfixtextfunc"))
Bram Moolenaard43906d2020-07-20 21:31:32 +02007126 flags |= QF_GETLIST_QFTF;
7127
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007128 return flags;
7129}
Bram Moolenaara6d48492017-12-12 22:45:31 +01007130
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007131/*
7132 * Return the quickfix list index based on 'nr' or 'id' in 'what'.
7133 * If 'nr' and 'id' are not present in 'what' then return the current
7134 * quickfix list index.
7135 * If 'nr' is zero then return the current quickfix list index.
7136 * If 'nr' is '$' then return the last quickfix list index.
7137 * If 'id' is present then return the index of the quickfix list with that id.
7138 * If 'id' is zero then return the quickfix list index specified by 'nr'.
7139 * Return -1, if quickfix list is not present or if the stack is empty.
7140 */
7141 static int
7142qf_getprop_qfidx(qf_info_T *qi, dict_T *what)
7143{
7144 int qf_idx;
7145 dictitem_T *di;
7146
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007147 qf_idx = qi->qf_curlist; // default is the current list
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007148 if ((di = dict_find(what, (char_u *)"nr", -1)) != NULL)
7149 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007150 // Use the specified quickfix/location list
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007151 if (di->di_tv.v_type == VAR_NUMBER)
Bram Moolenaara6d48492017-12-12 22:45:31 +01007152 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007153 // for zero use the current list
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007154 if (di->di_tv.vval.v_number != 0)
Bram Moolenaara6d48492017-12-12 22:45:31 +01007155 {
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007156 qf_idx = di->di_tv.vval.v_number - 1;
7157 if (qf_idx < 0 || qf_idx >= qi->qf_listcount)
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007158 qf_idx = INVALID_QFIDX;
Bram Moolenaara6d48492017-12-12 22:45:31 +01007159 }
Bram Moolenaara6d48492017-12-12 22:45:31 +01007160 }
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007161 else if (di->di_tv.v_type == VAR_STRING
7162 && di->di_tv.vval.v_string != NULL
7163 && STRCMP(di->di_tv.vval.v_string, "$") == 0)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007164 // Get the last quickfix list number
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007165 qf_idx = qi->qf_listcount - 1;
7166 else
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007167 qf_idx = INVALID_QFIDX;
Bram Moolenaara6d48492017-12-12 22:45:31 +01007168 }
7169
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007170 if ((di = dict_find(what, (char_u *)"id", -1)) != NULL)
7171 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007172 // Look for a list with the specified id
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007173 if (di->di_tv.v_type == VAR_NUMBER)
7174 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007175 // For zero, use the current list or the list specified by 'nr'
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007176 if (di->di_tv.vval.v_number != 0)
7177 qf_idx = qf_id2nr(qi, di->di_tv.vval.v_number);
7178 }
7179 else
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007180 qf_idx = INVALID_QFIDX;
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007181 }
7182
7183 return qf_idx;
7184}
7185
7186/*
7187 * Return default values for quickfix list properties in retdict.
7188 */
7189 static int
Bram Moolenaar2d67d302018-11-16 18:46:02 +01007190qf_getprop_defaults(qf_info_T *qi, int flags, int locstack, dict_T *retdict)
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007191{
7192 int status = OK;
7193
7194 if (flags & QF_GETLIST_TITLE)
Bram Moolenaare0be1672018-07-08 16:50:37 +02007195 status = dict_add_string(retdict, "title", (char_u *)"");
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007196 if ((status == OK) && (flags & QF_GETLIST_ITEMS))
7197 {
7198 list_T *l = list_alloc();
7199 if (l != NULL)
7200 status = dict_add_list(retdict, "items", l);
7201 else
7202 status = FAIL;
7203 }
7204 if ((status == OK) && (flags & QF_GETLIST_NR))
Bram Moolenaare0be1672018-07-08 16:50:37 +02007205 status = dict_add_number(retdict, "nr", 0);
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007206 if ((status == OK) && (flags & QF_GETLIST_WINID))
Bram Moolenaare0be1672018-07-08 16:50:37 +02007207 status = dict_add_number(retdict, "winid", qf_winid(qi));
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007208 if ((status == OK) && (flags & QF_GETLIST_CONTEXT))
Bram Moolenaare0be1672018-07-08 16:50:37 +02007209 status = dict_add_string(retdict, "context", (char_u *)"");
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007210 if ((status == OK) && (flags & QF_GETLIST_ID))
Bram Moolenaare0be1672018-07-08 16:50:37 +02007211 status = dict_add_number(retdict, "id", 0);
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007212 if ((status == OK) && (flags & QF_GETLIST_IDX))
Bram Moolenaare0be1672018-07-08 16:50:37 +02007213 status = dict_add_number(retdict, "idx", 0);
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007214 if ((status == OK) && (flags & QF_GETLIST_SIZE))
Bram Moolenaare0be1672018-07-08 16:50:37 +02007215 status = dict_add_number(retdict, "size", 0);
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007216 if ((status == OK) && (flags & QF_GETLIST_TICK))
Bram Moolenaare0be1672018-07-08 16:50:37 +02007217 status = dict_add_number(retdict, "changedtick", 0);
Bram Moolenaar2d67d302018-11-16 18:46:02 +01007218 if ((status == OK) && locstack && (flags & QF_GETLIST_FILEWINID))
Bram Moolenaarc9cc9c72018-09-02 15:18:42 +02007219 status = dict_add_number(retdict, "filewinid", 0);
Bram Moolenaar647e24b2019-03-17 16:39:46 +01007220 if ((status == OK) && (flags & QF_GETLIST_QFBUFNR))
7221 status = qf_getprop_qfbufnr(qi, retdict);
Bram Moolenaard43906d2020-07-20 21:31:32 +02007222 if ((status == OK) && (flags & QF_GETLIST_QFTF))
7223 status = dict_add_string(retdict, "quickfixtextfunc", (char_u *)"");
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007224
7225 return status;
7226}
7227
7228/*
7229 * Return the quickfix list title as 'title' in retdict
7230 */
7231 static int
Bram Moolenaar108e7b42018-10-11 17:39:12 +02007232qf_getprop_title(qf_list_T *qfl, dict_T *retdict)
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007233{
Bram Moolenaar108e7b42018-10-11 17:39:12 +02007234 return dict_add_string(retdict, "title", qfl->qf_title);
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007235}
7236
7237/*
Bram Moolenaarc9cc9c72018-09-02 15:18:42 +02007238 * Returns the identifier of the window used to display files from a location
7239 * list. If there is no associated window, then returns 0. Useful only when
7240 * called from a location list window.
7241 */
7242 static int
7243qf_getprop_filewinid(win_T *wp, qf_info_T *qi, dict_T *retdict)
7244{
7245 int winid = 0;
7246
7247 if (wp != NULL && IS_LL_WINDOW(wp))
7248 {
7249 win_T *ll_wp = qf_find_win_with_loclist(qi);
7250 if (ll_wp != NULL)
7251 winid = ll_wp->w_id;
7252 }
7253
7254 return dict_add_number(retdict, "filewinid", winid);
7255}
7256
7257/*
Bram Moolenaar858ba062020-05-31 23:11:59 +02007258 * Return the quickfix list items/entries as 'items' in retdict.
7259 * If eidx is not 0, then return the item at the specified index.
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007260 */
7261 static int
Bram Moolenaar858ba062020-05-31 23:11:59 +02007262qf_getprop_items(qf_info_T *qi, int qf_idx, int eidx, dict_T *retdict)
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007263{
7264 int status = OK;
7265 list_T *l = list_alloc();
7266 if (l != NULL)
7267 {
Bram Moolenaar858ba062020-05-31 23:11:59 +02007268 (void)get_errorlist(qi, NULL, qf_idx, eidx, l);
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007269 dict_add_list(retdict, "items", l);
7270 }
7271 else
7272 status = FAIL;
7273
7274 return status;
7275}
7276
7277/*
7278 * Return the quickfix list context (if any) as 'context' in retdict.
7279 */
7280 static int
Bram Moolenaar108e7b42018-10-11 17:39:12 +02007281qf_getprop_ctx(qf_list_T *qfl, dict_T *retdict)
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007282{
7283 int status;
7284 dictitem_T *di;
7285
Bram Moolenaar108e7b42018-10-11 17:39:12 +02007286 if (qfl->qf_ctx != NULL)
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007287 {
7288 di = dictitem_alloc((char_u *)"context");
7289 if (di != NULL)
7290 {
Bram Moolenaar108e7b42018-10-11 17:39:12 +02007291 copy_tv(qfl->qf_ctx, &di->di_tv);
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007292 status = dict_add(retdict, di);
7293 if (status == FAIL)
7294 dictitem_free(di);
7295 }
7296 else
7297 status = FAIL;
7298 }
7299 else
Bram Moolenaare0be1672018-07-08 16:50:37 +02007300 status = dict_add_string(retdict, "context", (char_u *)"");
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007301
7302 return status;
7303}
7304
7305/*
Bram Moolenaar858ba062020-05-31 23:11:59 +02007306 * Return the current quickfix list index as 'idx' in retdict.
7307 * If a specific entry index (eidx) is supplied, then use that.
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007308 */
7309 static int
Bram Moolenaar858ba062020-05-31 23:11:59 +02007310qf_getprop_idx(qf_list_T *qfl, int eidx, dict_T *retdict)
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007311{
Bram Moolenaar858ba062020-05-31 23:11:59 +02007312 if (eidx == 0)
7313 {
7314 eidx = qfl->qf_index;
7315 if (qf_list_empty(qfl))
7316 // For empty lists, current index is set to 0
7317 eidx = 0;
7318 }
7319 return dict_add_number(retdict, "idx", eidx);
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007320}
7321
7322/*
Bram Moolenaard43906d2020-07-20 21:31:32 +02007323 * Return the 'quickfixtextfunc' function of a quickfix/location list
7324 */
7325 static int
7326qf_getprop_qftf(qf_list_T *qfl, dict_T *retdict)
7327{
7328 int status;
7329
=?UTF-8?q?Dundar=20G=C3=B6c?=b8366582022-04-14 20:43:56 +01007330 if (qfl->qf_qftf_cb.cb_name != NULL)
Bram Moolenaard43906d2020-07-20 21:31:32 +02007331 {
7332 typval_T tv;
7333
=?UTF-8?q?Dundar=20G=C3=B6c?=b8366582022-04-14 20:43:56 +01007334 put_callback(&qfl->qf_qftf_cb, &tv);
Bram Moolenaard43906d2020-07-20 21:31:32 +02007335 status = dict_add_tv(retdict, "quickfixtextfunc", &tv);
7336 clear_tv(&tv);
7337 }
7338 else
7339 status = dict_add_string(retdict, "quickfixtextfunc", (char_u *)"");
7340
7341 return status;
7342}
7343
7344/*
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007345 * Return quickfix/location list details (title) as a
7346 * dictionary. 'what' contains the details to return. If 'list_idx' is -1,
7347 * then current list is used. Otherwise the specified list is used.
7348 */
Bram Moolenaare677df82019-09-02 22:31:11 +02007349 static int
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007350qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
7351{
7352 qf_info_T *qi = &ql_info;
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02007353 qf_list_T *qfl;
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007354 int status = OK;
Bram Moolenaar108e7b42018-10-11 17:39:12 +02007355 int qf_idx = INVALID_QFIDX;
Bram Moolenaar858ba062020-05-31 23:11:59 +02007356 int eidx = 0;
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007357 dictitem_T *di;
7358 int flags = QF_GETLIST_NONE;
7359
7360 if ((di = dict_find(what, (char_u *)"lines", -1)) != NULL)
7361 return qf_get_list_from_lines(what, di, retdict);
7362
7363 if (wp != NULL)
7364 qi = GET_LOC_LIST(wp);
7365
Bram Moolenaarc9cc9c72018-09-02 15:18:42 +02007366 flags = qf_getprop_keys2flags(what, (wp != NULL));
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007367
Bram Moolenaar019dfe62018-10-07 14:38:49 +02007368 if (!qf_stack_empty(qi))
Bram Moolenaar353eeea2018-04-16 18:04:57 +02007369 qf_idx = qf_getprop_qfidx(qi, what);
7370
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007371 // List is not present or is empty
Bram Moolenaar019dfe62018-10-07 14:38:49 +02007372 if (qf_stack_empty(qi) || qf_idx == INVALID_QFIDX)
Bram Moolenaar2d67d302018-11-16 18:46:02 +01007373 return qf_getprop_defaults(qi, flags, wp != NULL, retdict);
Bram Moolenaara6d48492017-12-12 22:45:31 +01007374
Bram Moolenaar0398e002019-03-21 21:12:49 +01007375 qfl = qf_get_list(qi, qf_idx);
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02007376
Bram Moolenaar858ba062020-05-31 23:11:59 +02007377 // If an entry index is specified, use that
7378 if ((di = dict_find(what, (char_u *)"idx", -1)) != NULL)
7379 {
7380 if (di->di_tv.v_type != VAR_NUMBER)
7381 return FAIL;
7382 eidx = di->di_tv.vval.v_number;
7383 }
7384
Bram Moolenaard823fa92016-08-12 16:29:27 +02007385 if (flags & QF_GETLIST_TITLE)
Bram Moolenaar108e7b42018-10-11 17:39:12 +02007386 status = qf_getprop_title(qfl, retdict);
Bram Moolenaard823fa92016-08-12 16:29:27 +02007387 if ((status == OK) && (flags & QF_GETLIST_NR))
Bram Moolenaare0be1672018-07-08 16:50:37 +02007388 status = dict_add_number(retdict, "nr", qf_idx + 1);
Bram Moolenaard823fa92016-08-12 16:29:27 +02007389 if ((status == OK) && (flags & QF_GETLIST_WINID))
Bram Moolenaare0be1672018-07-08 16:50:37 +02007390 status = dict_add_number(retdict, "winid", qf_winid(qi));
Bram Moolenaar6a8958d2017-06-22 21:33:20 +02007391 if ((status == OK) && (flags & QF_GETLIST_ITEMS))
Bram Moolenaar858ba062020-05-31 23:11:59 +02007392 status = qf_getprop_items(qi, qf_idx, eidx, retdict);
Bram Moolenaar8f77c5a2017-04-30 14:21:00 +02007393 if ((status == OK) && (flags & QF_GETLIST_CONTEXT))
Bram Moolenaar108e7b42018-10-11 17:39:12 +02007394 status = qf_getprop_ctx(qfl, retdict);
Bram Moolenaara539f4f2017-08-30 20:33:55 +02007395 if ((status == OK) && (flags & QF_GETLIST_ID))
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02007396 status = dict_add_number(retdict, "id", qfl->qf_id);
Bram Moolenaarfc2b2702017-09-15 22:43:07 +02007397 if ((status == OK) && (flags & QF_GETLIST_IDX))
Bram Moolenaar858ba062020-05-31 23:11:59 +02007398 status = qf_getprop_idx(qfl, eidx, retdict);
Bram Moolenaarfc2b2702017-09-15 22:43:07 +02007399 if ((status == OK) && (flags & QF_GETLIST_SIZE))
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02007400 status = dict_add_number(retdict, "size", qfl->qf_count);
Bram Moolenaarb254af32017-12-18 19:48:58 +01007401 if ((status == OK) && (flags & QF_GETLIST_TICK))
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02007402 status = dict_add_number(retdict, "changedtick", qfl->qf_changedtick);
Bram Moolenaarc9cc9c72018-09-02 15:18:42 +02007403 if ((status == OK) && (wp != NULL) && (flags & QF_GETLIST_FILEWINID))
7404 status = qf_getprop_filewinid(wp, qi, retdict);
Bram Moolenaar647e24b2019-03-17 16:39:46 +01007405 if ((status == OK) && (flags & QF_GETLIST_QFBUFNR))
7406 status = qf_getprop_qfbufnr(qi, retdict);
Bram Moolenaard43906d2020-07-20 21:31:32 +02007407 if ((status == OK) && (flags & QF_GETLIST_QFTF))
7408 status = qf_getprop_qftf(qfl, retdict);
Bram Moolenaarb254af32017-12-18 19:48:58 +01007409
Bram Moolenaard823fa92016-08-12 16:29:27 +02007410 return status;
7411}
7412
7413/*
Bram Moolenaar6f6ef7c2018-08-28 22:07:44 +02007414 * Add a new quickfix entry to list at 'qf_idx' in the stack 'qi' from the
Bram Moolenaar9752c722018-12-22 16:49:34 +01007415 * items in the dict 'd'. If it is a valid error entry, then set 'valid_entry'
7416 * to TRUE.
Bram Moolenaar6f6ef7c2018-08-28 22:07:44 +02007417 */
7418 static int
7419qf_add_entry_from_dict(
Bram Moolenaar0398e002019-03-21 21:12:49 +01007420 qf_list_T *qfl,
Bram Moolenaar6f6ef7c2018-08-28 22:07:44 +02007421 dict_T *d,
Bram Moolenaar9752c722018-12-22 16:49:34 +01007422 int first_entry,
7423 int *valid_entry)
Bram Moolenaar6f6ef7c2018-08-28 22:07:44 +02007424{
7425 static int did_bufnr_emsg;
7426 char_u *filename, *module, *pattern, *text, *type;
thinca6864efa2021-06-19 20:45:20 +02007427 int bufnum, valid, status, col, end_col, vcol, nr;
7428 long lnum, end_lnum;
Bram Moolenaar6f6ef7c2018-08-28 22:07:44 +02007429
7430 if (first_entry)
7431 did_bufnr_emsg = FALSE;
7432
Bram Moolenaard61efa52022-07-23 09:52:04 +01007433 filename = dict_get_string(d, "filename", TRUE);
7434 module = dict_get_string(d, "module", TRUE);
7435 bufnum = (int)dict_get_number(d, "bufnr");
7436 lnum = (int)dict_get_number(d, "lnum");
7437 end_lnum = (int)dict_get_number(d, "end_lnum");
7438 col = (int)dict_get_number(d, "col");
7439 end_col = (int)dict_get_number(d, "end_col");
7440 vcol = (int)dict_get_number(d, "vcol");
7441 nr = (int)dict_get_number(d, "nr");
7442 type = dict_get_string(d, "type", TRUE);
7443 pattern = dict_get_string(d, "pattern", TRUE);
7444 text = dict_get_string(d, "text", TRUE);
Bram Moolenaar6f6ef7c2018-08-28 22:07:44 +02007445 if (text == NULL)
7446 text = vim_strsave((char_u *)"");
Tom Praschanca6ac992023-08-11 23:26:12 +02007447 typval_T user_data;
7448 user_data.v_type = VAR_UNKNOWN;
7449 dict_get_tv(d, "user_data", &user_data);
Bram Moolenaar6f6ef7c2018-08-28 22:07:44 +02007450
7451 valid = TRUE;
7452 if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL))
7453 valid = FALSE;
7454
7455 // Mark entries with non-existing buffer number as not valid. Give the
7456 // error message only once.
7457 if (bufnum != 0 && (buflist_findnr(bufnum) == NULL))
7458 {
7459 if (!did_bufnr_emsg)
7460 {
7461 did_bufnr_emsg = TRUE;
Bram Moolenaare1242042021-12-16 20:56:57 +00007462 semsg(_(e_buffer_nr_not_found), bufnum);
Bram Moolenaar6f6ef7c2018-08-28 22:07:44 +02007463 }
7464 valid = FALSE;
7465 bufnum = 0;
7466 }
7467
7468 // If the 'valid' field is present it overrules the detected value.
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01007469 if (dict_has_key(d, "valid"))
Bram Moolenaard61efa52022-07-23 09:52:04 +01007470 valid = (int)dict_get_bool(d, "valid", FALSE);
Bram Moolenaar6f6ef7c2018-08-28 22:07:44 +02007471
Bram Moolenaar0398e002019-03-21 21:12:49 +01007472 status = qf_add_entry(qfl,
Bram Moolenaar6f6ef7c2018-08-28 22:07:44 +02007473 NULL, // dir
7474 filename,
7475 module,
7476 bufnum,
7477 text,
7478 lnum,
thinca6864efa2021-06-19 20:45:20 +02007479 end_lnum,
Bram Moolenaar6f6ef7c2018-08-28 22:07:44 +02007480 col,
thinca6864efa2021-06-19 20:45:20 +02007481 end_col,
Bram Moolenaar6f6ef7c2018-08-28 22:07:44 +02007482 vcol, // vis_col
7483 pattern, // search pattern
7484 nr,
7485 type == NULL ? NUL : *type,
Tom Praschanca6ac992023-08-11 23:26:12 +02007486 &user_data,
Bram Moolenaar6f6ef7c2018-08-28 22:07:44 +02007487 valid);
7488
7489 vim_free(filename);
7490 vim_free(module);
7491 vim_free(pattern);
7492 vim_free(text);
7493 vim_free(type);
Tom Praschanca6ac992023-08-11 23:26:12 +02007494 clear_tv(&user_data);
Bram Moolenaar6f6ef7c2018-08-28 22:07:44 +02007495
Bram Moolenaar9752c722018-12-22 16:49:34 +01007496 if (valid)
7497 *valid_entry = TRUE;
7498
Bram Moolenaar6f6ef7c2018-08-28 22:07:44 +02007499 return status;
7500}
7501
7502/*
Bram Moolenaard823fa92016-08-12 16:29:27 +02007503 * Add list of entries to quickfix/location list. Each list entry is
7504 * a dictionary with item information.
7505 */
7506 static int
7507qf_add_entries(
7508 qf_info_T *qi,
Bram Moolenaara3921f42017-06-04 15:30:34 +02007509 int qf_idx,
Bram Moolenaard823fa92016-08-12 16:29:27 +02007510 list_T *list,
7511 char_u *title,
7512 int action)
Bram Moolenaar68b76a62005-03-25 21:53:48 +00007513{
Bram Moolenaar0398e002019-03-21 21:12:49 +01007514 qf_list_T *qfl = qf_get_list(qi, qf_idx);
Bram Moolenaar68b76a62005-03-25 21:53:48 +00007515 listitem_T *li;
7516 dict_T *d;
Bram Moolenaar864293a2016-06-02 13:40:04 +02007517 qfline_T *old_last = NULL;
Bram Moolenaar68b76a62005-03-25 21:53:48 +00007518 int retval = OK;
Bram Moolenaar9752c722018-12-22 16:49:34 +01007519 int valid_entry = FALSE;
Bram Moolenaar68b76a62005-03-25 21:53:48 +00007520
Bram Moolenaara3921f42017-06-04 15:30:34 +02007521 if (action == ' ' || qf_idx == qi->qf_listcount)
7522 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007523 // make place for a new list
Bram Moolenaar94116152012-11-28 17:41:59 +01007524 qf_new_list(qi, title);
Bram Moolenaara3921f42017-06-04 15:30:34 +02007525 qf_idx = qi->qf_curlist;
Bram Moolenaar0398e002019-03-21 21:12:49 +01007526 qfl = qf_get_list(qi, qf_idx);
Bram Moolenaara3921f42017-06-04 15:30:34 +02007527 }
Bram Moolenaar0398e002019-03-21 21:12:49 +01007528 else if (action == 'a' && !qf_list_empty(qfl))
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007529 // Adding to existing list, use last entry.
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02007530 old_last = qfl->qf_last;
Bram Moolenaar35c54e52005-05-20 21:25:31 +00007531 else if (action == 'r')
Bram Moolenaarfb604092014-07-23 15:55:00 +02007532 {
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02007533 qf_free_items(qfl);
7534 qf_store_title(qfl, title);
Bram Moolenaarfb604092014-07-23 15:55:00 +02007535 }
Bram Moolenaar68b76a62005-03-25 21:53:48 +00007536
Bram Moolenaaraeea7212020-04-02 18:50:46 +02007537 FOR_ALL_LIST_ITEMS(list, li)
Bram Moolenaar68b76a62005-03-25 21:53:48 +00007538 {
7539 if (li->li_tv.v_type != VAR_DICT)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007540 continue; // Skip non-dict items
Bram Moolenaar68b76a62005-03-25 21:53:48 +00007541
7542 d = li->li_tv.vval.v_dict;
7543 if (d == NULL)
7544 continue;
7545
Bram Moolenaar0398e002019-03-21 21:12:49 +01007546 retval = qf_add_entry_from_dict(qfl, d, li == list->lv_first,
Bram Moolenaar9752c722018-12-22 16:49:34 +01007547 &valid_entry);
Bram Moolenaar95946f12019-03-31 15:31:59 +02007548 if (retval == QF_FAIL)
Bram Moolenaar68b76a62005-03-25 21:53:48 +00007549 break;
Bram Moolenaar68b76a62005-03-25 21:53:48 +00007550 }
7551
Bram Moolenaar9752c722018-12-22 16:49:34 +01007552 // Check if any valid error entries are added to the list.
7553 if (valid_entry)
7554 qfl->qf_nonevalid = FALSE;
7555 else if (qfl->qf_index == 0)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007556 // no valid entry
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02007557 qfl->qf_nonevalid = TRUE;
Bram Moolenaar9752c722018-12-22 16:49:34 +01007558
7559 // If not appending to the list, set the current error to the first entry
Bram Moolenaarb6fa30c2017-03-29 14:19:25 +02007560 if (action != 'a')
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02007561 qfl->qf_ptr = qfl->qf_start;
Bram Moolenaar9752c722018-12-22 16:49:34 +01007562
7563 // Update the current error index if not appending to the list or if the
7564 // list was empty before and it is not empty now.
Bram Moolenaar0398e002019-03-21 21:12:49 +01007565 if ((action != 'a' || qfl->qf_index == 0) && !qf_list_empty(qfl))
Bram Moolenaar9752c722018-12-22 16:49:34 +01007566 qfl->qf_index = 1;
Bram Moolenaar68b76a62005-03-25 21:53:48 +00007567
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007568 // Don't update the cursor in quickfix window when appending entries
Bram Moolenaar864293a2016-06-02 13:40:04 +02007569 qf_update_buffer(qi, old_last);
Bram Moolenaar68b76a62005-03-25 21:53:48 +00007570
7571 return retval;
7572}
Bram Moolenaard823fa92016-08-12 16:29:27 +02007573
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007574/*
7575 * Get the quickfix list index from 'nr' or 'id'
7576 */
Bram Moolenaard823fa92016-08-12 16:29:27 +02007577 static int
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007578qf_setprop_get_qfidx(
7579 qf_info_T *qi,
7580 dict_T *what,
7581 int action,
7582 int *newlist)
Bram Moolenaard823fa92016-08-12 16:29:27 +02007583{
7584 dictitem_T *di;
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007585 int qf_idx = qi->qf_curlist; // default is the current list
Bram Moolenaar2b529bb2016-08-27 13:35:35 +02007586
Bram Moolenaard823fa92016-08-12 16:29:27 +02007587 if ((di = dict_find(what, (char_u *)"nr", -1)) != NULL)
7588 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007589 // Use the specified quickfix/location list
Bram Moolenaard823fa92016-08-12 16:29:27 +02007590 if (di->di_tv.v_type == VAR_NUMBER)
7591 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007592 // for zero use the current list
Bram Moolenaar6e62da32017-05-28 08:16:25 +02007593 if (di->di_tv.vval.v_number != 0)
7594 qf_idx = di->di_tv.vval.v_number - 1;
Bram Moolenaar6a8958d2017-06-22 21:33:20 +02007595
Bram Moolenaar55b69262017-08-13 13:42:01 +02007596 if ((action == ' ' || action == 'a') && qf_idx == qi->qf_listcount)
7597 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007598 // When creating a new list, accept qf_idx pointing to the next
7599 // non-available list and add the new list at the end of the
7600 // stack.
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007601 *newlist = TRUE;
Bram Moolenaar019dfe62018-10-07 14:38:49 +02007602 qf_idx = qf_stack_empty(qi) ? 0 : qi->qf_listcount - 1;
Bram Moolenaar55b69262017-08-13 13:42:01 +02007603 }
Bram Moolenaar6a8958d2017-06-22 21:33:20 +02007604 else if (qf_idx < 0 || qf_idx >= qi->qf_listcount)
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007605 return INVALID_QFIDX;
Bram Moolenaar55b69262017-08-13 13:42:01 +02007606 else if (action != ' ')
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007607 *newlist = FALSE; // use the specified list
Bram Moolenaar55b69262017-08-13 13:42:01 +02007608 }
7609 else if (di->di_tv.v_type == VAR_STRING
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007610 && di->di_tv.vval.v_string != NULL
7611 && STRCMP(di->di_tv.vval.v_string, "$") == 0)
Bram Moolenaar6a8958d2017-06-22 21:33:20 +02007612 {
Bram Moolenaar019dfe62018-10-07 14:38:49 +02007613 if (!qf_stack_empty(qi))
Bram Moolenaar55b69262017-08-13 13:42:01 +02007614 qf_idx = qi->qf_listcount - 1;
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007615 else if (*newlist)
Bram Moolenaar55b69262017-08-13 13:42:01 +02007616 qf_idx = 0;
7617 else
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007618 return INVALID_QFIDX;
Bram Moolenaar6a8958d2017-06-22 21:33:20 +02007619 }
Bram Moolenaard823fa92016-08-12 16:29:27 +02007620 else
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007621 return INVALID_QFIDX;
Bram Moolenaar2b529bb2016-08-27 13:35:35 +02007622 }
7623
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007624 if (!*newlist && (di = dict_find(what, (char_u *)"id", -1)) != NULL)
Bram Moolenaara539f4f2017-08-30 20:33:55 +02007625 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007626 // Use the quickfix/location list with the specified id
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007627 if (di->di_tv.v_type != VAR_NUMBER)
7628 return INVALID_QFIDX;
7629
7630 return qf_id2nr(qi, di->di_tv.vval.v_number);
Bram Moolenaara539f4f2017-08-30 20:33:55 +02007631 }
7632
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007633 return qf_idx;
7634}
7635
7636/*
7637 * Set the quickfix list title.
7638 */
7639 static int
7640qf_setprop_title(qf_info_T *qi, int qf_idx, dict_T *what, dictitem_T *di)
7641{
Bram Moolenaar0398e002019-03-21 21:12:49 +01007642 qf_list_T *qfl = qf_get_list(qi, qf_idx);
Bram Moolenaar108e7b42018-10-11 17:39:12 +02007643
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007644 if (di->di_tv.v_type != VAR_STRING)
7645 return FAIL;
7646
Bram Moolenaar108e7b42018-10-11 17:39:12 +02007647 vim_free(qfl->qf_title);
Bram Moolenaard61efa52022-07-23 09:52:04 +01007648 qfl->qf_title = dict_get_string(what, "title", TRUE);
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007649 if (qf_idx == qi->qf_curlist)
7650 qf_update_win_titlevar(qi);
7651
7652 return OK;
7653}
7654
7655/*
7656 * Set quickfix list items/entries.
7657 */
7658 static int
7659qf_setprop_items(qf_info_T *qi, int qf_idx, dictitem_T *di, int action)
7660{
7661 int retval = FAIL;
7662 char_u *title_save;
7663
7664 if (di->di_tv.v_type != VAR_LIST)
7665 return FAIL;
7666
7667 title_save = vim_strsave(qi->qf_lists[qf_idx].qf_title);
7668 retval = qf_add_entries(qi, qf_idx, di->di_tv.vval.v_list,
7669 title_save, action == ' ' ? 'a' : action);
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007670 vim_free(title_save);
7671
7672 return retval;
7673}
7674
7675/*
7676 * Set quickfix list items/entries from a list of lines.
7677 */
7678 static int
7679qf_setprop_items_from_lines(
7680 qf_info_T *qi,
7681 int qf_idx,
7682 dict_T *what,
7683 dictitem_T *di,
7684 int action)
7685{
7686 char_u *errorformat = p_efm;
7687 dictitem_T *efm_di;
7688 int retval = FAIL;
7689
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007690 // Use the user supplied errorformat settings (if present)
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007691 if ((efm_di = dict_find(what, (char_u *)"efm", -1)) != NULL)
7692 {
7693 if (efm_di->di_tv.v_type != VAR_STRING ||
7694 efm_di->di_tv.vval.v_string == NULL)
7695 return FAIL;
7696 errorformat = efm_di->di_tv.vval.v_string;
7697 }
7698
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007699 // Only a List value is supported
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007700 if (di->di_tv.v_type != VAR_LIST || di->di_tv.vval.v_list == NULL)
7701 return FAIL;
7702
7703 if (action == 'r')
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02007704 qf_free_items(&qi->qf_lists[qf_idx]);
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007705 if (qf_init_ext(qi, qf_idx, NULL, NULL, &di->di_tv, errorformat,
Bram Moolenaar287153c2020-11-29 14:20:27 +01007706 FALSE, (linenr_T)0, (linenr_T)0, NULL, NULL) >= 0)
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007707 retval = OK;
7708
7709 return retval;
7710}
7711
7712/*
7713 * Set quickfix list context.
7714 */
7715 static int
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02007716qf_setprop_context(qf_list_T *qfl, dictitem_T *di)
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007717{
7718 typval_T *ctx;
7719
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02007720 free_tv(qfl->qf_ctx);
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007721 ctx = alloc_tv();
7722 if (ctx != NULL)
7723 copy_tv(&di->di_tv, ctx);
Bram Moolenaarfe15b7d2018-09-18 22:50:06 +02007724 qfl->qf_ctx = ctx;
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007725
7726 return OK;
7727}
7728
7729/*
Bram Moolenaar5b69c222019-01-11 14:50:06 +01007730 * Set the current index in the specified quickfix list
7731 */
7732 static int
7733qf_setprop_curidx(qf_info_T *qi, qf_list_T *qfl, dictitem_T *di)
7734{
7735 int denote = FALSE;
7736 int newidx;
7737 int old_qfidx;
7738 qfline_T *qf_ptr;
7739
7740 // If the specified index is '$', then use the last entry
7741 if (di->di_tv.v_type == VAR_STRING
7742 && di->di_tv.vval.v_string != NULL
7743 && STRCMP(di->di_tv.vval.v_string, "$") == 0)
7744 newidx = qfl->qf_count;
7745 else
7746 {
7747 // Otherwise use the specified index
7748 newidx = tv_get_number_chk(&di->di_tv, &denote);
7749 if (denote)
7750 return FAIL;
7751 }
7752
7753 if (newidx < 1) // sanity check
7754 return FAIL;
7755 if (newidx > qfl->qf_count)
7756 newidx = qfl->qf_count;
7757
7758 old_qfidx = qfl->qf_index;
7759 qf_ptr = get_nth_entry(qfl, newidx, &newidx);
7760 if (qf_ptr == NULL)
7761 return FAIL;
7762 qfl->qf_ptr = qf_ptr;
7763 qfl->qf_index = newidx;
7764
7765 // If the current list is modified and it is displayed in the quickfix
7766 // window, then Update it.
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01007767 if (qf_get_curlist(qi)->qf_id == qfl->qf_id)
Bram Moolenaar5b69c222019-01-11 14:50:06 +01007768 qf_win_pos_update(qi, old_qfidx);
7769
7770 return OK;
7771}
7772
7773/*
Bram Moolenaar858ba062020-05-31 23:11:59 +02007774 * Set the current index in the specified quickfix list
7775 */
7776 static int
7777qf_setprop_qftf(qf_info_T *qi UNUSED, qf_list_T *qfl, dictitem_T *di)
7778{
Bram Moolenaard43906d2020-07-20 21:31:32 +02007779 callback_T cb;
7780
=?UTF-8?q?Dundar=20G=C3=B6c?=b8366582022-04-14 20:43:56 +01007781 free_callback(&qfl->qf_qftf_cb);
Bram Moolenaard43906d2020-07-20 21:31:32 +02007782 cb = get_callback(&di->di_tv);
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00007783 if (cb.cb_name == NULL || *cb.cb_name == NUL)
7784 return OK;
7785
7786 set_callback(&qfl->qf_qftf_cb, &cb);
7787 if (cb.cb_free_name)
7788 vim_free(cb.cb_name);
Bram Moolenaar858ba062020-05-31 23:11:59 +02007789
7790 return OK;
7791}
7792
7793/*
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007794 * Set quickfix/location list properties (title, items, context).
7795 * Also used to add items from parsing a list of lines.
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02007796 * Used by the setqflist() and setloclist() Vim script functions.
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007797 */
7798 static int
7799qf_set_properties(qf_info_T *qi, dict_T *what, int action, char_u *title)
7800{
7801 dictitem_T *di;
7802 int retval = FAIL;
7803 int qf_idx;
7804 int newlist = FALSE;
Bram Moolenaar108e7b42018-10-11 17:39:12 +02007805 qf_list_T *qfl;
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007806
Bram Moolenaar019dfe62018-10-07 14:38:49 +02007807 if (action == ' ' || qf_stack_empty(qi))
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007808 newlist = TRUE;
7809
7810 qf_idx = qf_setprop_get_qfidx(qi, what, action, &newlist);
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007811 if (qf_idx == INVALID_QFIDX) // List not found
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007812 return FAIL;
7813
Bram Moolenaar2b529bb2016-08-27 13:35:35 +02007814 if (newlist)
7815 {
Bram Moolenaar55b69262017-08-13 13:42:01 +02007816 qi->qf_curlist = qf_idx;
Bram Moolenaarae338332017-08-11 20:25:26 +02007817 qf_new_list(qi, title);
Bram Moolenaar2b529bb2016-08-27 13:35:35 +02007818 qf_idx = qi->qf_curlist;
Bram Moolenaard823fa92016-08-12 16:29:27 +02007819 }
7820
Bram Moolenaar0398e002019-03-21 21:12:49 +01007821 qfl = qf_get_list(qi, qf_idx);
Bram Moolenaard823fa92016-08-12 16:29:27 +02007822 if ((di = dict_find(what, (char_u *)"title", -1)) != NULL)
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007823 retval = qf_setprop_title(qi, qf_idx, what, di);
Bram Moolenaar6a8958d2017-06-22 21:33:20 +02007824 if ((di = dict_find(what, (char_u *)"items", -1)) != NULL)
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007825 retval = qf_setprop_items(qi, qf_idx, di, action);
Bram Moolenaar2c809b72017-09-01 18:34:02 +02007826 if ((di = dict_find(what, (char_u *)"lines", -1)) != NULL)
Bram Moolenaara2aa8a22018-04-24 13:55:00 +02007827 retval = qf_setprop_items_from_lines(qi, qf_idx, what, di, action);
Bram Moolenaar8f77c5a2017-04-30 14:21:00 +02007828 if ((di = dict_find(what, (char_u *)"context", -1)) != NULL)
Bram Moolenaar108e7b42018-10-11 17:39:12 +02007829 retval = qf_setprop_context(qfl, di);
Bram Moolenaar5b69c222019-01-11 14:50:06 +01007830 if ((di = dict_find(what, (char_u *)"idx", -1)) != NULL)
7831 retval = qf_setprop_curidx(qi, qfl, di);
Bram Moolenaar858ba062020-05-31 23:11:59 +02007832 if ((di = dict_find(what, (char_u *)"quickfixtextfunc", -1)) != NULL)
7833 retval = qf_setprop_qftf(qi, qfl, di);
Bram Moolenaar8f77c5a2017-04-30 14:21:00 +02007834
Bram Moolenaar287153c2020-11-29 14:20:27 +01007835 if (newlist || retval == OK)
Bram Moolenaar108e7b42018-10-11 17:39:12 +02007836 qf_list_changed(qfl);
Bram Moolenaar287153c2020-11-29 14:20:27 +01007837 if (newlist)
7838 qf_update_buffer(qi, NULL);
Bram Moolenaarb254af32017-12-18 19:48:58 +01007839
Bram Moolenaard823fa92016-08-12 16:29:27 +02007840 return retval;
7841}
7842
Bram Moolenaar69f40be2017-04-02 15:15:49 +02007843/*
Bram Moolenaar69f40be2017-04-02 15:15:49 +02007844 * Free the entire quickfix/location list stack.
7845 * If the quickfix/location list window is open, then clear it.
7846 */
Bram Moolenaarb6fa30c2017-03-29 14:19:25 +02007847 static void
7848qf_free_stack(win_T *wp, qf_info_T *qi)
7849{
Bram Moolenaar69f40be2017-04-02 15:15:49 +02007850 win_T *qfwin = qf_find_win(qi);
7851 win_T *llwin = NULL;
Bram Moolenaar69f40be2017-04-02 15:15:49 +02007852
7853 if (qfwin != NULL)
7854 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007855 // If the quickfix/location list window is open, then clear it
Bram Moolenaar69f40be2017-04-02 15:15:49 +02007856 if (qi->qf_curlist < qi->qf_listcount)
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01007857 qf_free(qf_get_curlist(qi));
Bram Moolenaar69f40be2017-04-02 15:15:49 +02007858 qf_update_buffer(qi, NULL);
7859 }
7860
7861 if (wp != NULL && IS_LL_WINDOW(wp))
7862 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007863 // If in the location list window, then use the non-location list
7864 // window with this location list (if present)
Bram Moolenaaree8188f2019-02-05 21:23:04 +01007865 llwin = qf_find_win_with_loclist(qi);
Bram Moolenaar69f40be2017-04-02 15:15:49 +02007866 if (llwin != NULL)
7867 wp = llwin;
7868 }
7869
Bram Moolenaarb6fa30c2017-03-29 14:19:25 +02007870 qf_free_all(wp);
7871 if (wp == NULL)
7872 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007873 // quickfix list
Bram Moolenaarb6fa30c2017-03-29 14:19:25 +02007874 qi->qf_curlist = 0;
7875 qi->qf_listcount = 0;
7876 }
Bram Moolenaaree8188f2019-02-05 21:23:04 +01007877 else if (qfwin != NULL)
Bram Moolenaar69f40be2017-04-02 15:15:49 +02007878 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007879 // If the location list window is open, then create a new empty
7880 // location list
Bram Moolenaar2d67d302018-11-16 18:46:02 +01007881 qf_info_T *new_ll = qf_alloc_stack(QFLT_LOCATION);
Bram Moolenaar99895ea2017-04-20 22:44:47 +02007882
Bram Moolenaar95946f12019-03-31 15:31:59 +02007883 if (new_ll != NULL)
7884 {
7885 new_ll->qf_bufnr = qfwin->w_buffer->b_fnum;
Bram Moolenaard788f6f2017-04-23 17:19:43 +02007886
Bram Moolenaar95946f12019-03-31 15:31:59 +02007887 // first free the list reference in the location list window
7888 ll_free_all(&qfwin->w_llist_ref);
7889
7890 qfwin->w_llist_ref = new_ll;
7891 if (wp != qfwin)
7892 win_set_loclist(wp, new_ll);
7893 }
Bram Moolenaar69f40be2017-04-02 15:15:49 +02007894 }
Bram Moolenaarb6fa30c2017-03-29 14:19:25 +02007895}
7896
Bram Moolenaard823fa92016-08-12 16:29:27 +02007897/*
7898 * Populate the quickfix list with the items supplied in the list
7899 * of dictionaries. "title" will be copied to w:quickfix_title.
7900 * "action" is 'a' for add, 'r' for replace. Otherwise create a new list.
Bram Moolenaar90049492020-07-01 13:04:05 +02007901 * When "what" is not NULL then only set some properties.
Bram Moolenaard823fa92016-08-12 16:29:27 +02007902 */
7903 int
7904set_errorlist(
7905 win_T *wp,
7906 list_T *list,
7907 int action,
7908 char_u *title,
7909 dict_T *what)
7910{
7911 qf_info_T *qi = &ql_info;
7912 int retval = OK;
7913
7914 if (wp != NULL)
7915 {
7916 qi = ll_get_or_alloc_list(wp);
7917 if (qi == NULL)
7918 return FAIL;
7919 }
7920
Bram Moolenaarb6fa30c2017-03-29 14:19:25 +02007921 if (action == 'f')
7922 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02007923 // Free the entire quickfix or location list stack
Bram Moolenaarb6fa30c2017-03-29 14:19:25 +02007924 qf_free_stack(wp, qi);
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02007925 return OK;
Bram Moolenaarb6fa30c2017-03-29 14:19:25 +02007926 }
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02007927
Bram Moolenaarbe7a50c2020-06-30 22:11:44 +02007928 // A dict argument cannot be specified with a non-empty list argument
Bram Moolenaar90049492020-07-01 13:04:05 +02007929 if (list->lv_len != 0 && what != NULL)
Bram Moolenaarbe7a50c2020-06-30 22:11:44 +02007930 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00007931 semsg(_(e_invalid_argument_str),
Bram Moolenaarbe7a50c2020-06-30 22:11:44 +02007932 _("cannot have both a list and a \"what\" argument"));
7933 return FAIL;
7934 }
7935
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02007936 incr_quickfix_busy();
7937
7938 if (what != NULL)
Bram Moolenaarae338332017-08-11 20:25:26 +02007939 retval = qf_set_properties(qi, what, action, title);
Bram Moolenaard823fa92016-08-12 16:29:27 +02007940 else
Bram Moolenaarb254af32017-12-18 19:48:58 +01007941 {
Bram Moolenaara3921f42017-06-04 15:30:34 +02007942 retval = qf_add_entries(qi, qi->qf_curlist, list, title, action);
Bram Moolenaarb254af32017-12-18 19:48:58 +01007943 if (retval == OK)
Bram Moolenaar4aa47b22019-03-13 06:51:53 +01007944 qf_list_changed(qf_get_curlist(qi));
Bram Moolenaarb254af32017-12-18 19:48:58 +01007945 }
Bram Moolenaard823fa92016-08-12 16:29:27 +02007946
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02007947 decr_quickfix_busy();
7948
Bram Moolenaard823fa92016-08-12 16:29:27 +02007949 return retval;
7950}
Bram Moolenaar8f77c5a2017-04-30 14:21:00 +02007951
Tom Praschanca6ac992023-08-11 23:26:12 +02007952static int mark_quickfix_user_data(qf_info_T *qi, int copyID)
7953{
7954 int abort = FALSE;
7955 for (int i = 0; i < LISTCOUNT && !abort; ++i)
7956 {
7957 qf_list_T *qfl = &qi->qf_lists[i];
7958 if (!qfl->qf_has_user_data)
7959 continue;
7960 qfline_T *qfp;
7961 int j;
7962 FOR_ALL_QFL_ITEMS(qfl, qfp, j)
7963 {
7964 typval_T* user_data = &qfp->qf_user_data;
7965 if (user_data != NULL && user_data->v_type != VAR_NUMBER
7966 && user_data->v_type != VAR_STRING && user_data->v_type != VAR_FLOAT)
7967 abort = abort || set_ref_in_item(user_data, copyID, NULL, NULL);
7968 }
7969 }
7970 return abort;
7971}
7972
Bram Moolenaar18cebf42018-05-08 22:31:37 +02007973/*
Yegappan Lakshmanan6ae8fae2021-12-12 16:26:44 +00007974 * Mark the quickfix context and callback function as in use for all the lists
7975 * in a quickfix stack.
Bram Moolenaar18cebf42018-05-08 22:31:37 +02007976 */
Bram Moolenaar8f77c5a2017-04-30 14:21:00 +02007977 static int
7978mark_quickfix_ctx(qf_info_T *qi, int copyID)
7979{
7980 int i;
7981 int abort = FALSE;
7982 typval_T *ctx;
Yegappan Lakshmanan6ae8fae2021-12-12 16:26:44 +00007983 callback_T *cb;
Bram Moolenaar8f77c5a2017-04-30 14:21:00 +02007984
7985 for (i = 0; i < LISTCOUNT && !abort; ++i)
7986 {
7987 ctx = qi->qf_lists[i].qf_ctx;
Bram Moolenaar55b69262017-08-13 13:42:01 +02007988 if (ctx != NULL && ctx->v_type != VAR_NUMBER
7989 && ctx->v_type != VAR_STRING && ctx->v_type != VAR_FLOAT)
Yegappan Lakshmanan6ae8fae2021-12-12 16:26:44 +00007990 abort = abort || set_ref_in_item(ctx, copyID, NULL, NULL);
7991
=?UTF-8?q?Dundar=20G=C3=B6c?=b8366582022-04-14 20:43:56 +01007992 cb = &qi->qf_lists[i].qf_qftf_cb;
Yegappan Lakshmanan6ae8fae2021-12-12 16:26:44 +00007993 abort = abort || set_ref_in_callback(cb, copyID);
Bram Moolenaar8f77c5a2017-04-30 14:21:00 +02007994 }
7995
7996 return abort;
7997}
7998
7999/*
8000 * Mark the context of the quickfix list and the location lists (if present) as
Bram Moolenaar353eeea2018-04-16 18:04:57 +02008001 * "in use". So that garbage collection doesn't free the context.
Bram Moolenaar8f77c5a2017-04-30 14:21:00 +02008002 */
8003 int
8004set_ref_in_quickfix(int copyID)
8005{
8006 int abort = FALSE;
8007 tabpage_T *tp;
8008 win_T *win;
8009
8010 abort = mark_quickfix_ctx(&ql_info, copyID);
8011 if (abort)
8012 return abort;
8013
Tom Praschanca6ac992023-08-11 23:26:12 +02008014 abort = mark_quickfix_user_data(&ql_info, copyID);
8015 if (abort)
8016 return abort;
8017
Yegappan Lakshmanan6ae8fae2021-12-12 16:26:44 +00008018 abort = set_ref_in_callback(&qftf_cb, copyID);
8019 if (abort)
8020 return abort;
8021
Bram Moolenaar8f77c5a2017-04-30 14:21:00 +02008022 FOR_ALL_TAB_WINDOWS(tp, win)
8023 {
8024 if (win->w_llist != NULL)
8025 {
8026 abort = mark_quickfix_ctx(win->w_llist, copyID);
8027 if (abort)
8028 return abort;
Tom Praschanca6ac992023-08-11 23:26:12 +02008029
8030 abort = mark_quickfix_user_data(win->w_llist, copyID);
8031 if (abort)
8032 return abort;
Bram Moolenaar8f77c5a2017-04-30 14:21:00 +02008033 }
Bram Moolenaar12237442017-12-19 12:38:52 +01008034 if (IS_LL_WINDOW(win) && (win->w_llist_ref->qf_refcount == 1))
8035 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008036 // In a location list window and none of the other windows is
8037 // referring to this location list. Mark the location list
8038 // context as still in use.
Bram Moolenaar12237442017-12-19 12:38:52 +01008039 abort = mark_quickfix_ctx(win->w_llist_ref, copyID);
8040 if (abort)
8041 return abort;
zeertzjqbe4bd182024-09-14 10:32:31 +02008042
8043 abort = mark_quickfix_user_data(win->w_llist_ref, copyID);
8044 if (abort)
8045 return abort;
Bram Moolenaar12237442017-12-19 12:38:52 +01008046 }
Bram Moolenaar8f77c5a2017-04-30 14:21:00 +02008047 }
8048
8049 return abort;
8050}
Bram Moolenaar05159a02005-02-26 23:04:13 +00008051#endif
8052
Bram Moolenaar81695252004-12-29 20:58:21 +00008053/*
Bram Moolenaara16123a2019-03-28 20:31:07 +01008054 * Return the autocmd name for the :cbuffer Ex commands
8055 */
8056 static char_u *
8057cbuffer_get_auname(cmdidx_T cmdidx)
8058{
8059 switch (cmdidx)
8060 {
8061 case CMD_cbuffer: return (char_u *)"cbuffer";
8062 case CMD_cgetbuffer: return (char_u *)"cgetbuffer";
8063 case CMD_caddbuffer: return (char_u *)"caddbuffer";
8064 case CMD_lbuffer: return (char_u *)"lbuffer";
8065 case CMD_lgetbuffer: return (char_u *)"lgetbuffer";
8066 case CMD_laddbuffer: return (char_u *)"laddbuffer";
8067 default: return NULL;
8068 }
8069}
8070
8071/*
8072 * Process and validate the arguments passed to the :cbuffer, :caddbuffer,
8073 * :cgetbuffer, :lbuffer, :laddbuffer, :lgetbuffer Ex commands.
8074 */
8075 static int
8076cbuffer_process_args(
8077 exarg_T *eap,
8078 buf_T **bufp,
8079 linenr_T *line1,
8080 linenr_T *line2)
8081{
8082 buf_T *buf = NULL;
8083
8084 if (*eap->arg == NUL)
8085 buf = curbuf;
8086 else if (*skipwhite(skipdigits(eap->arg)) == NUL)
8087 buf = buflist_findnr(atoi((char *)eap->arg));
8088
8089 if (buf == NULL)
8090 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00008091 emsg(_(e_invalid_argument));
Bram Moolenaara16123a2019-03-28 20:31:07 +01008092 return FAIL;
8093 }
8094
8095 if (buf->b_ml.ml_mfp == NULL)
8096 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00008097 emsg(_(e_buffer_is_not_loaded));
Bram Moolenaara16123a2019-03-28 20:31:07 +01008098 return FAIL;
8099 }
8100
8101 if (eap->addr_count == 0)
8102 {
8103 eap->line1 = 1;
8104 eap->line2 = buf->b_ml.ml_line_count;
8105 }
8106
8107 if (eap->line1 < 1 || eap->line1 > buf->b_ml.ml_line_count
8108 || eap->line2 < 1 || eap->line2 > buf->b_ml.ml_line_count)
8109 {
Bram Moolenaar108010a2021-06-27 22:03:33 +02008110 emsg(_(e_invalid_range));
Bram Moolenaara16123a2019-03-28 20:31:07 +01008111 return FAIL;
8112 }
8113
8114 *line1 = eap->line1;
8115 *line2 = eap->line2;
8116 *bufp = buf;
8117
8118 return OK;
8119}
8120
8121/*
Bram Moolenaar86b68352004-12-27 21:59:20 +00008122 * ":[range]cbuffer [bufnr]" command.
Bram Moolenaara6557602006-02-04 22:43:20 +00008123 * ":[range]caddbuffer [bufnr]" command.
Bram Moolenaardb552d602006-03-23 22:59:57 +00008124 * ":[range]cgetbuffer [bufnr]" command.
Bram Moolenaard12f5c12006-01-25 22:10:52 +00008125 * ":[range]lbuffer [bufnr]" command.
Bram Moolenaara6557602006-02-04 22:43:20 +00008126 * ":[range]laddbuffer [bufnr]" command.
Bram Moolenaardb552d602006-03-23 22:59:57 +00008127 * ":[range]lgetbuffer [bufnr]" command.
Bram Moolenaar86b68352004-12-27 21:59:20 +00008128 */
8129 void
Bram Moolenaar05540972016-01-30 20:31:25 +01008130ex_cbuffer(exarg_T *eap)
Bram Moolenaar86b68352004-12-27 21:59:20 +00008131{
8132 buf_T *buf = NULL;
Bram Moolenaar87f59b02019-04-04 14:04:11 +02008133 qf_info_T *qi;
Bram Moolenaar04c4ce62016-09-01 15:45:58 +02008134 char_u *au_name = NULL;
Bram Moolenaar1ed22762017-11-28 18:03:44 +01008135 int res;
Bram Moolenaar531b9a32018-07-03 16:54:23 +02008136 int_u save_qfid;
8137 win_T *wp = NULL;
Bram Moolenaara16123a2019-03-28 20:31:07 +01008138 char_u *qf_title;
8139 linenr_T line1;
8140 linenr_T line2;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00008141
Bram Moolenaara16123a2019-03-28 20:31:07 +01008142 au_name = cbuffer_get_auname(eap->cmdidx);
Bram Moolenaar21662be2016-11-06 14:46:44 +01008143 if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
Bram Moolenaara16123a2019-03-28 20:31:07 +01008144 curbuf->b_fname, TRUE, curbuf))
Bram Moolenaar04c4ce62016-09-01 15:45:58 +02008145 {
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01008146#ifdef FEAT_EVAL
Bram Moolenaar21662be2016-11-06 14:46:44 +01008147 if (aborting())
Bram Moolenaar04c4ce62016-09-01 15:45:58 +02008148 return;
Bram Moolenaar04c4ce62016-09-01 15:45:58 +02008149#endif
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01008150 }
Bram Moolenaar04c4ce62016-09-01 15:45:58 +02008151
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008152 // Must come after autocommands.
Bram Moolenaar87f59b02019-04-04 14:04:11 +02008153 qi = qf_cmd_get_or_alloc_stack(eap, &wp);
8154 if (qi == NULL)
8155 return;
Bram Moolenaaraaf6e432017-12-19 16:41:14 +01008156
Bram Moolenaara16123a2019-03-28 20:31:07 +01008157 if (cbuffer_process_args(eap, &buf, &line1, &line2) == FAIL)
8158 return;
8159
8160 qf_title = qf_cmdtitle(*eap->cmdlinep);
8161
8162 if (buf->b_sfname)
Bram Moolenaar86b68352004-12-27 21:59:20 +00008163 {
Bram Moolenaara16123a2019-03-28 20:31:07 +01008164 vim_snprintf((char *)IObuff, IOSIZE, "%s (%s)",
8165 (char *)qf_title, (char *)buf->b_sfname);
8166 qf_title = IObuff;
Bram Moolenaar86b68352004-12-27 21:59:20 +00008167 }
Bram Moolenaara16123a2019-03-28 20:31:07 +01008168
8169 incr_quickfix_busy();
8170
8171 res = qf_init_ext(qi, qi->qf_curlist, NULL, buf, NULL, p_efm,
8172 (eap->cmdidx != CMD_caddbuffer
8173 && eap->cmdidx != CMD_laddbuffer),
8174 line1, line2,
8175 qf_title, NULL);
8176 if (qf_stack_empty(qi))
8177 {
8178 decr_quickfix_busy();
8179 return;
8180 }
8181 if (res >= 0)
8182 qf_list_changed(qf_get_curlist(qi));
8183
8184 // Remember the current quickfix list identifier, so that we can
8185 // check for autocommands changing the current quickfix list.
8186 save_qfid = qf_get_curlist(qi)->qf_id;
8187 if (au_name != NULL)
8188 {
8189 buf_T *curbuf_old = curbuf;
8190
8191 apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, curbuf->b_fname,
8192 TRUE, curbuf);
8193 if (curbuf != curbuf_old)
8194 // Autocommands changed buffer, don't jump now, "qi" may
8195 // be invalid.
8196 res = 0;
8197 }
8198 // Jump to the first error for a new list and if autocmds didn't
8199 // free the list.
8200 if (res > 0 && (eap->cmdidx == CMD_cbuffer ||
8201 eap->cmdidx == CMD_lbuffer)
8202 && qflist_valid(wp, save_qfid))
8203 // display the first error
8204 qf_jump_first(qi, save_qfid, eap->forceit);
8205
8206 decr_quickfix_busy();
Bram Moolenaar86b68352004-12-27 21:59:20 +00008207}
8208
Bram Moolenaar1e015462005-09-25 22:16:38 +00008209#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar86b68352004-12-27 21:59:20 +00008210/*
Bram Moolenaara16123a2019-03-28 20:31:07 +01008211 * Return the autocmd name for the :cexpr Ex commands.
8212 */
Bram Moolenaar5f7d4c02021-05-05 21:31:39 +02008213 char_u *
Bram Moolenaara16123a2019-03-28 20:31:07 +01008214cexpr_get_auname(cmdidx_T cmdidx)
8215{
8216 switch (cmdidx)
8217 {
8218 case CMD_cexpr: return (char_u *)"cexpr";
8219 case CMD_cgetexpr: return (char_u *)"cgetexpr";
8220 case CMD_caddexpr: return (char_u *)"caddexpr";
8221 case CMD_lexpr: return (char_u *)"lexpr";
8222 case CMD_lgetexpr: return (char_u *)"lgetexpr";
8223 case CMD_laddexpr: return (char_u *)"laddexpr";
8224 default: return NULL;
8225 }
8226}
8227
Bram Moolenaar5f7d4c02021-05-05 21:31:39 +02008228 int
8229trigger_cexpr_autocmd(int cmdidx)
8230{
8231 char_u *au_name = cexpr_get_auname(cmdidx);
8232
8233 if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
8234 curbuf->b_fname, TRUE, curbuf))
8235 {
8236 if (aborting())
8237 return FAIL;
8238 }
8239 return OK;
8240}
8241
8242 int
8243cexpr_core(exarg_T *eap, typval_T *tv)
8244{
8245 qf_info_T *qi;
8246 win_T *wp = NULL;
8247
8248 qi = qf_cmd_get_or_alloc_stack(eap, &wp);
8249 if (qi == NULL)
8250 return FAIL;
8251
8252 if ((tv->v_type == VAR_STRING && tv->vval.v_string != NULL)
8253 || (tv->v_type == VAR_LIST && tv->vval.v_list != NULL))
8254 {
8255 int res;
8256 int_u save_qfid;
8257 char_u *au_name = cexpr_get_auname(eap->cmdidx);
8258
8259 incr_quickfix_busy();
8260 res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, tv, p_efm,
8261 (eap->cmdidx != CMD_caddexpr
8262 && eap->cmdidx != CMD_laddexpr),
8263 (linenr_T)0, (linenr_T)0,
8264 qf_cmdtitle(*eap->cmdlinep), NULL);
8265 if (qf_stack_empty(qi))
8266 {
8267 decr_quickfix_busy();
8268 return FAIL;
8269 }
8270 if (res >= 0)
8271 qf_list_changed(qf_get_curlist(qi));
8272
8273 // Remember the current quickfix list identifier, so that we can
8274 // check for autocommands changing the current quickfix list.
8275 save_qfid = qf_get_curlist(qi)->qf_id;
8276 if (au_name != NULL)
8277 apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
8278 curbuf->b_fname, TRUE, curbuf);
8279
8280 // Jump to the first error for a new list and if autocmds didn't
8281 // free the list.
8282 if (res > 0 && (eap->cmdidx == CMD_cexpr || eap->cmdidx == CMD_lexpr)
8283 && qflist_valid(wp, save_qfid))
8284 // display the first error
8285 qf_jump_first(qi, save_qfid, eap->forceit);
8286 decr_quickfix_busy();
8287 return OK;
8288 }
8289
Bram Moolenaar677658a2022-01-05 16:09:06 +00008290 emsg(_(e_string_or_list_expected));
Bram Moolenaar5f7d4c02021-05-05 21:31:39 +02008291 return FAIL;
8292}
8293
Bram Moolenaara16123a2019-03-28 20:31:07 +01008294/*
Bram Moolenaardb552d602006-03-23 22:59:57 +00008295 * ":cexpr {expr}", ":cgetexpr {expr}", ":caddexpr {expr}" command.
8296 * ":lexpr {expr}", ":lgetexpr {expr}", ":laddexpr {expr}" command.
Bram Moolenaar5f7d4c02021-05-05 21:31:39 +02008297 * Also: ":caddexpr", ":cgetexpr", "laddexpr" and "laddexpr".
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00008298 */
8299 void
Bram Moolenaar05540972016-01-30 20:31:25 +01008300ex_cexpr(exarg_T *eap)
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00008301{
8302 typval_T *tv;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00008303
Bram Moolenaar5f7d4c02021-05-05 21:31:39 +02008304 if (trigger_cexpr_autocmd(eap->cmdidx) == FAIL)
Bram Moolenaar87f59b02019-04-04 14:04:11 +02008305 return;
Bram Moolenaar3c097222017-12-21 20:54:49 +01008306
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008307 // Evaluate the expression. When the result is a string or a list we can
8308 // use it to fill the errorlist.
Bram Moolenaarb171fb12020-06-24 20:34:03 +02008309 tv = eval_expr(eap->arg, eap);
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00008310 if (tv == NULL)
8311 return;
8312
8313 (void)cexpr_core(eap, tv);
8314 free_tv(tv);
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00008315}
Bram Moolenaar1e015462005-09-25 22:16:38 +00008316#endif
Bram Moolenaar87e25fd2005-07-27 21:13:01 +00008317
8318/*
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008319 * Get the location list for ":lhelpgrep"
8320 */
8321 static qf_info_T *
8322hgr_get_ll(int *new_ll)
8323{
8324 win_T *wp;
8325 qf_info_T *qi;
8326
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008327 // If the current window is a help window, then use it
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008328 if (bt_help(curwin->w_buffer))
8329 wp = curwin;
8330 else
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008331 // Find an existing help window
Bram Moolenaar7a2b0e52018-05-10 18:55:28 +02008332 wp = qf_find_help_win();
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008333
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008334 if (wp == NULL) // Help window not found
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008335 qi = NULL;
8336 else
8337 qi = wp->w_llist;
8338
8339 if (qi == NULL)
8340 {
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008341 // Allocate a new location list for help text matches
Bram Moolenaar2d67d302018-11-16 18:46:02 +01008342 if ((qi = qf_alloc_stack(QFLT_LOCATION)) == NULL)
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008343 return NULL;
8344 *new_ll = TRUE;
8345 }
8346
8347 return qi;
8348}
8349
8350/*
8351 * Search for a pattern in a help file.
8352 */
8353 static void
8354hgr_search_file(
Bram Moolenaar9afe5e92019-03-22 14:16:06 +01008355 qf_list_T *qfl,
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008356 char_u *fname,
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008357 vimconv_T *p_vc,
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008358 regmatch_T *p_regmatch)
8359{
8360 FILE *fd;
8361 long lnum;
8362
8363 fd = mch_fopen((char *)fname, "r");
8364 if (fd == NULL)
8365 return;
8366
8367 lnum = 1;
8368 while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int)
8369 {
8370 char_u *line = IObuff;
Bram Moolenaara12a1612019-01-24 16:39:02 +01008371
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008372 // Convert a line if 'encoding' is not utf-8 and
8373 // the line contains a non-ASCII character.
Yegappan Lakshmanandf1bbea2022-03-05 14:35:12 +00008374 if (p_vc->vc_type != CONV_NONE && has_non_ascii(IObuff))
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008375 {
8376 line = string_convert(p_vc, IObuff, NULL);
8377 if (line == NULL)
8378 line = IObuff;
8379 }
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008380
8381 if (vim_regexec(p_regmatch, line, (colnr_T)0))
8382 {
8383 int l = (int)STRLEN(line);
8384
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008385 // remove trailing CR, LF, spaces, etc.
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008386 while (l > 0 && line[l - 1] <= ' ')
8387 line[--l] = NUL;
8388
Bram Moolenaar9afe5e92019-03-22 14:16:06 +01008389 if (qf_add_entry(qfl,
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008390 NULL, // dir
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008391 fname,
Bram Moolenaard76ce852018-05-01 15:02:04 +02008392 NULL,
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008393 0,
8394 line,
8395 lnum,
thinca6864efa2021-06-19 20:45:20 +02008396 0,
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008397 (int)(p_regmatch->startp[0] - line)
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008398 + 1, // col
thinca6864efa2021-06-19 20:45:20 +02008399 (int)(p_regmatch->endp[0] - line)
8400 + 1, // end_col
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008401 FALSE, // vis_col
8402 NULL, // search pattern
8403 0, // nr
8404 1, // type
Tom Praschanca6ac992023-08-11 23:26:12 +02008405 NULL, // user_data
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008406 TRUE // valid
Bram Moolenaar95946f12019-03-31 15:31:59 +02008407 ) == QF_FAIL)
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008408 {
8409 got_int = TRUE;
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008410 if (line != IObuff)
8411 vim_free(line);
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008412 break;
8413 }
8414 }
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008415 if (line != IObuff)
8416 vim_free(line);
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008417 ++lnum;
8418 line_breakcheck();
8419 }
8420 fclose(fd);
8421}
8422
8423/*
8424 * Search for a pattern in all the help files in the doc directory under
8425 * the given directory.
8426 */
8427 static void
8428hgr_search_files_in_dir(
Bram Moolenaar9afe5e92019-03-22 14:16:06 +01008429 qf_list_T *qfl,
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008430 char_u *dirname,
Bram Moolenaara12a1612019-01-24 16:39:02 +01008431 regmatch_T *p_regmatch,
8432 vimconv_T *p_vc
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008433#ifdef FEAT_MULTI_LANG
8434 , char_u *lang
8435#endif
8436 )
8437{
8438 int fcount;
8439 char_u **fnames;
8440 int fi;
8441
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008442 // Find all "*.txt" and "*.??x" files in the "doc" directory.
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008443 add_pathsep(dirname);
8444 STRCAT(dirname, "doc/*.\\(txt\\|??x\\)");
8445 if (gen_expand_wildcards(1, &dirname, &fcount,
8446 &fnames, EW_FILE|EW_SILENT) == OK
8447 && fcount > 0)
8448 {
8449 for (fi = 0; fi < fcount && !got_int; ++fi)
8450 {
8451#ifdef FEAT_MULTI_LANG
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008452 // Skip files for a different language.
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008453 if (lang != NULL
8454 && STRNICMP(lang, fnames[fi]
Bram Moolenaard76ce852018-05-01 15:02:04 +02008455 + STRLEN(fnames[fi]) - 3, 2) != 0
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008456 && !(STRNICMP(lang, "en", 2) == 0
8457 && STRNICMP("txt", fnames[fi]
8458 + STRLEN(fnames[fi]) - 3, 3) == 0))
8459 continue;
8460#endif
8461
Bram Moolenaar9afe5e92019-03-22 14:16:06 +01008462 hgr_search_file(qfl, fnames[fi], p_vc, p_regmatch);
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008463 }
8464 FreeWild(fcount, fnames);
8465 }
8466}
8467
8468/*
Bram Moolenaar18cebf42018-05-08 22:31:37 +02008469 * Search for a pattern in all the help files in the 'runtimepath'
8470 * and add the matches to a quickfix list.
Bram Moolenaarc631f2d2018-08-21 21:58:13 +02008471 * 'lang' is the language specifier. If supplied, then only matches in the
Bram Moolenaar18cebf42018-05-08 22:31:37 +02008472 * specified language are found.
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008473 */
8474 static void
Bram Moolenaar9afe5e92019-03-22 14:16:06 +01008475hgr_search_in_rtp(qf_list_T *qfl, regmatch_T *p_regmatch, char_u *lang)
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008476{
8477 char_u *p;
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008478
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008479 vimconv_T vc;
8480
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008481 // Help files are in utf-8 or latin1, convert lines when 'encoding'
8482 // differs.
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008483 vc.vc_type = CONV_NONE;
8484 if (!enc_utf8)
8485 convert_setup(&vc, (char_u *)"utf-8", p_enc);
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008486
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008487 // Go through all the directories in 'runtimepath'
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008488 p = p_rtp;
8489 while (*p != NUL && !got_int)
8490 {
8491 copy_option_part(&p, NameBuff, MAXPATHL, ",");
8492
Bram Moolenaar9afe5e92019-03-22 14:16:06 +01008493 hgr_search_files_in_dir(qfl, NameBuff, p_regmatch, &vc
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008494#ifdef FEAT_MULTI_LANG
8495 , lang
8496#endif
8497 );
8498 }
8499
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008500 if (vc.vc_type != CONV_NONE)
8501 convert_setup(&vc, NULL, NULL);
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008502}
8503
8504/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008505 * ":helpgrep {pattern}"
8506 */
8507 void
Bram Moolenaar05540972016-01-30 20:31:25 +01008508ex_helpgrep(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008509{
8510 regmatch_T regmatch;
8511 char_u *save_cpo;
Bram Moolenaar4791fcd2022-02-23 12:06:00 +00008512 int save_cpo_allocated;
Bram Moolenaard12f5c12006-01-25 22:10:52 +00008513 qf_info_T *qi = &ql_info;
Bram Moolenaar8b6144b2006-02-08 09:20:24 +00008514 int new_qi = FALSE;
Bram Moolenaar73633f82012-01-20 13:39:07 +01008515 char_u *au_name = NULL;
Bram Moolenaarc631f2d2018-08-21 21:58:13 +02008516 char_u *lang = NULL;
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01008517 int updated = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008518
Bram Moolenaar73633f82012-01-20 13:39:07 +01008519 switch (eap->cmdidx)
8520 {
8521 case CMD_helpgrep: au_name = (char_u *)"helpgrep"; break;
8522 case CMD_lhelpgrep: au_name = (char_u *)"lhelpgrep"; break;
8523 default: break;
8524 }
Bram Moolenaar21662be2016-11-06 14:46:44 +01008525 if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
8526 curbuf->b_fname, TRUE, curbuf))
Bram Moolenaar73633f82012-01-20 13:39:07 +01008527 {
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01008528#ifdef FEAT_EVAL
Bram Moolenaar21662be2016-11-06 14:46:44 +01008529 if (aborting())
Bram Moolenaar73633f82012-01-20 13:39:07 +01008530 return;
Bram Moolenaar73633f82012-01-20 13:39:07 +01008531#endif
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01008532 }
Bram Moolenaar73633f82012-01-20 13:39:07 +01008533
Bram Moolenaar39665952018-08-15 20:59:48 +02008534 if (is_loclist_cmd(eap->cmdidx))
Bram Moolenaar8b6144b2006-02-08 09:20:24 +00008535 {
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008536 qi = hgr_get_ll(&new_qi);
Bram Moolenaar8b6144b2006-02-08 09:20:24 +00008537 if (qi == NULL)
Bram Moolenaar2225ebb2018-04-24 15:48:11 +02008538 return;
Bram Moolenaar8b6144b2006-02-08 09:20:24 +00008539 }
8540
Bram Moolenaara16123a2019-03-28 20:31:07 +01008541 // Make 'cpoptions' empty, the 'l' flag should not be used here.
8542 save_cpo = p_cpo;
Bram Moolenaar4791fcd2022-02-23 12:06:00 +00008543 save_cpo_allocated = is_option_allocated("cpo");
Bram Moolenaara16123a2019-03-28 20:31:07 +01008544 p_cpo = empty_option;
8545
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02008546 incr_quickfix_busy();
8547
Bram Moolenaarc631f2d2018-08-21 21:58:13 +02008548#ifdef FEAT_MULTI_LANG
8549 // Check for a specified language
8550 lang = check_help_lang(eap->arg);
8551#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008552 regmatch.regprog = vim_regcomp(eap->arg, RE_MAGIC + RE_STRING);
8553 regmatch.rm_ic = FALSE;
8554 if (regmatch.regprog != NULL)
8555 {
Bram Moolenaar108e7b42018-10-11 17:39:12 +02008556 qf_list_T *qfl;
8557
Bram Moolenaarc631f2d2018-08-21 21:58:13 +02008558 // create a new quickfix list
Bram Moolenaar8b62e312018-05-13 15:29:04 +02008559 qf_new_list(qi, qf_cmdtitle(*eap->cmdlinep));
Bram Moolenaar9afe5e92019-03-22 14:16:06 +01008560 qfl = qf_get_curlist(qi);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008561
Bram Moolenaar9afe5e92019-03-22 14:16:06 +01008562 hgr_search_in_rtp(qfl, &regmatch, lang);
Bram Moolenaar10b7b392012-01-10 16:28:45 +01008563
Bram Moolenaar473de612013-06-08 18:19:48 +02008564 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008565
Bram Moolenaar108e7b42018-10-11 17:39:12 +02008566 qfl->qf_nonevalid = FALSE;
8567 qfl->qf_ptr = qfl->qf_start;
8568 qfl->qf_index = 1;
8569 qf_list_changed(qfl);
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01008570 updated = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008571 }
8572
Bram Moolenaar9c24ccc2008-07-14 21:05:15 +00008573 if (p_cpo == empty_option)
8574 p_cpo = save_cpo;
8575 else
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01008576 {
8577 // Darn, some plugin changed the value. If it's still empty it was
8578 // changed and restored, need to restore in the complicated way.
8579 if (*p_cpo == NUL)
Bram Moolenaar31e5c602022-04-15 13:53:33 +01008580 set_option_value_give_err((char_u *)"cpo", 0L, save_cpo, 0);
Bram Moolenaar4791fcd2022-02-23 12:06:00 +00008581 if (save_cpo_allocated)
8582 free_string_option(save_cpo);
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01008583 }
8584
8585 if (updated)
8586 // This may open a window and source scripts, do this after 'cpo' was
8587 // restored.
8588 qf_update_buffer(qi, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008589
Bram Moolenaar73633f82012-01-20 13:39:07 +01008590 if (au_name != NULL)
8591 {
8592 apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
8593 curbuf->b_fname, TRUE, curbuf);
Bram Moolenaarec98e932020-06-08 19:35:59 +02008594 // When adding a location list to an existing location list stack,
8595 // if the autocmd made the stack invalid, then just return.
8596 if (!new_qi && IS_LL_STACK(qi) && qf_find_win_with_loclist(qi) == NULL)
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02008597 {
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02008598 decr_quickfix_busy();
Bram Moolenaar73633f82012-01-20 13:39:07 +01008599 return;
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02008600 }
Bram Moolenaar73633f82012-01-20 13:39:07 +01008601 }
Bram Moolenaar73633f82012-01-20 13:39:07 +01008602
Bram Moolenaar00bf8cd2018-10-07 20:26:20 +02008603 // Jump to first match.
Bram Moolenaar0398e002019-03-21 21:12:49 +01008604 if (!qf_list_empty(qf_get_curlist(qi)))
Bram Moolenaar8b6144b2006-02-08 09:20:24 +00008605 qf_jump(qi, 0, 0, FALSE);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00008606 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00008607 semsg(_(e_no_match_str_2), eap->arg);
Bram Moolenaar8b6144b2006-02-08 09:20:24 +00008608
Bram Moolenaar9f84ded2018-10-20 20:54:02 +02008609 decr_quickfix_busy();
8610
Bram Moolenaar8b6144b2006-02-08 09:20:24 +00008611 if (eap->cmdidx == CMD_lhelpgrep)
8612 {
Bram Moolenaarc631f2d2018-08-21 21:58:13 +02008613 // If the help window is not opened or if it already points to the
8614 // correct location list, then free the new location list.
Bram Moolenaard28cc3f2017-07-27 22:03:50 +02008615 if (!bt_help(curwin->w_buffer) || curwin->w_llist == qi)
Bram Moolenaar8b6144b2006-02-08 09:20:24 +00008616 {
8617 if (new_qi)
8618 ll_free_all(&qi);
8619 }
Yegappan Lakshmanan9c9be052022-02-24 12:33:17 +00008620 else if (curwin->w_llist == NULL && new_qi)
8621 // current window didn't have a location list associated with it
8622 // before. Associate the new location list now.
Bram Moolenaar8b6144b2006-02-08 09:20:24 +00008623 curwin->w_llist = qi;
8624 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008625}
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01008626
8627# if defined(EXITFREE) || defined(PROTO)
8628 void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +00008629free_quickfix(void)
Yegappan Lakshmanan975a6652022-10-14 13:11:13 +01008630{
8631 win_T *win;
8632 tabpage_T *tab;
8633
8634 qf_free_all(NULL);
8635 // Free all location lists
8636 FOR_ALL_TAB_WINDOWS(tab, win)
8637 qf_free_all(win);
8638
8639 ga_clear(&qfga);
8640}
8641# endif
8642
Bram Moolenaar63d9e732019-12-05 21:10:38 +01008643#endif // FEAT_QUICKFIX
Bram Moolenaare677df82019-09-02 22:31:11 +02008644
8645#if defined(FEAT_EVAL) || defined(PROTO)
8646# ifdef FEAT_QUICKFIX
8647 static void
8648get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv)
8649{
8650 if (what_arg->v_type == VAR_UNKNOWN)
8651 {
8652 if (rettv_list_alloc(rettv) == OK)
8653 if (is_qf || wp != NULL)
Bram Moolenaar858ba062020-05-31 23:11:59 +02008654 (void)get_errorlist(NULL, wp, -1, 0, rettv->vval.v_list);
Bram Moolenaare677df82019-09-02 22:31:11 +02008655 }
8656 else
8657 {
8658 if (rettv_dict_alloc(rettv) == OK)
8659 if (is_qf || (wp != NULL))
8660 {
8661 if (what_arg->v_type == VAR_DICT)
8662 {
8663 dict_T *d = what_arg->vval.v_dict;
8664
8665 if (d != NULL)
8666 qf_get_properties(wp, d, rettv->vval.v_dict);
8667 }
8668 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00008669 emsg(_(e_dictionary_required));
Bram Moolenaare677df82019-09-02 22:31:11 +02008670 }
8671 }
8672}
8673# endif
8674
8675/*
8676 * "getloclist()" function
8677 */
8678 void
8679f_getloclist(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
8680{
8681# ifdef FEAT_QUICKFIX
8682 win_T *wp;
8683
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02008684 if (in_vim9script()
8685 && (check_for_number_arg(argvars, 0) == FAIL
8686 || check_for_opt_dict_arg(argvars, 1) == FAIL))
8687 return;
8688
Bram Moolenaare677df82019-09-02 22:31:11 +02008689 wp = find_win_by_nr_or_id(&argvars[0]);
8690 get_qf_loc_list(FALSE, wp, &argvars[1], rettv);
8691# endif
8692}
8693
8694/*
8695 * "getqflist()" function
8696 */
8697 void
8698f_getqflist(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
8699{
8700# ifdef FEAT_QUICKFIX
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02008701 if (in_vim9script() && check_for_opt_dict_arg(argvars, 0) == FAIL)
8702 return;
8703
Bram Moolenaare677df82019-09-02 22:31:11 +02008704 get_qf_loc_list(TRUE, NULL, &argvars[0], rettv);
8705# endif
8706}
8707
8708/*
8709 * Used by "setqflist()" and "setloclist()" functions
8710 */
8711 static void
8712set_qf_ll_list(
8713 win_T *wp UNUSED,
8714 typval_T *list_arg UNUSED,
8715 typval_T *action_arg UNUSED,
8716 typval_T *what_arg UNUSED,
8717 typval_T *rettv)
8718{
8719# ifdef FEAT_QUICKFIX
Bram Moolenaare677df82019-09-02 22:31:11 +02008720 char_u *act;
8721 int action = 0;
8722 static int recursive = 0;
8723# endif
8724
8725 rettv->vval.v_number = -1;
8726
8727# ifdef FEAT_QUICKFIX
8728 if (list_arg->v_type != VAR_LIST)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00008729 emsg(_(e_list_required));
Bram Moolenaare677df82019-09-02 22:31:11 +02008730 else if (recursive != 0)
Bram Moolenaar74409f62022-01-01 15:58:22 +00008731 emsg(_(e_autocommand_caused_recursive_behavior));
Bram Moolenaare677df82019-09-02 22:31:11 +02008732 else
8733 {
8734 list_T *l = list_arg->vval.v_list;
Bram Moolenaar90049492020-07-01 13:04:05 +02008735 dict_T *what = NULL;
Bram Moolenaare677df82019-09-02 22:31:11 +02008736 int valid_dict = TRUE;
8737
8738 if (action_arg->v_type == VAR_STRING)
8739 {
8740 act = tv_get_string_chk(action_arg);
8741 if (act == NULL)
8742 return; // type error; errmsg already given
8743 if ((*act == 'a' || *act == 'r' || *act == ' ' || *act == 'f') &&
8744 act[1] == NUL)
8745 action = *act;
8746 else
Bram Moolenaard82a47d2022-01-05 20:24:39 +00008747 semsg(_(e_invalid_action_str_1), act);
Bram Moolenaare677df82019-09-02 22:31:11 +02008748 }
8749 else if (action_arg->v_type == VAR_UNKNOWN)
8750 action = ' ';
8751 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00008752 emsg(_(e_string_required));
Bram Moolenaare677df82019-09-02 22:31:11 +02008753
8754 if (action_arg->v_type != VAR_UNKNOWN
8755 && what_arg->v_type != VAR_UNKNOWN)
8756 {
Bram Moolenaar90049492020-07-01 13:04:05 +02008757 if (what_arg->v_type == VAR_DICT && what_arg->vval.v_dict != NULL)
8758 what = what_arg->vval.v_dict;
Bram Moolenaare677df82019-09-02 22:31:11 +02008759 else
8760 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00008761 emsg(_(e_dictionary_required));
Bram Moolenaare677df82019-09-02 22:31:11 +02008762 valid_dict = FALSE;
8763 }
8764 }
8765
8766 ++recursive;
Bram Moolenaar90049492020-07-01 13:04:05 +02008767 if (l != NULL && action && valid_dict
8768 && set_errorlist(wp, l, action,
Bram Moolenaare677df82019-09-02 22:31:11 +02008769 (char_u *)(wp == NULL ? ":setqflist()" : ":setloclist()"),
Bram Moolenaar90049492020-07-01 13:04:05 +02008770 what) == OK)
Bram Moolenaare677df82019-09-02 22:31:11 +02008771 rettv->vval.v_number = 0;
8772 --recursive;
8773 }
8774# endif
8775}
8776
8777/*
8778 * "setloclist()" function
8779 */
8780 void
8781f_setloclist(typval_T *argvars, typval_T *rettv)
8782{
8783 win_T *win;
8784
8785 rettv->vval.v_number = -1;
8786
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02008787 if (in_vim9script()
8788 && (check_for_number_arg(argvars, 0) == FAIL
8789 || check_for_list_arg(argvars, 1) == FAIL
8790 || check_for_opt_string_arg(argvars, 2) == FAIL
8791 || (argvars[2].v_type != VAR_UNKNOWN
8792 && check_for_opt_dict_arg(argvars, 3) == FAIL)))
8793 return;
8794
Bram Moolenaare677df82019-09-02 22:31:11 +02008795 win = find_win_by_nr_or_id(&argvars[0]);
8796 if (win != NULL)
8797 set_qf_ll_list(win, &argvars[1], &argvars[2], &argvars[3], rettv);
8798}
8799
8800/*
8801 * "setqflist()" function
8802 */
8803 void
8804f_setqflist(typval_T *argvars, typval_T *rettv)
8805{
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02008806 if (in_vim9script()
8807 && (check_for_list_arg(argvars, 0) == FAIL
8808 || check_for_opt_string_arg(argvars, 1) == FAIL
8809 || (argvars[1].v_type != VAR_UNKNOWN
8810 && check_for_opt_dict_arg(argvars, 2) == FAIL)))
8811 return;
8812
Bram Moolenaare677df82019-09-02 22:31:11 +02008813 set_qf_ll_list(NULL, &argvars[0], &argvars[1], &argvars[2], rettv);
8814}
8815#endif