diff options
-rw-r--r-- | src/nvim/quickfix.c | 127 | ||||
-rw-r--r-- | src/nvim/testdir/test_quickfix.vim | 84 | ||||
-rw-r--r-- | src/nvim/version.c | 2 |
3 files changed, 148 insertions, 65 deletions
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 39f95d19ae..f4654628fb 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -48,8 +48,6 @@ struct dir_stack_T { char_u *dirname; }; -static struct dir_stack_T *dir_stack = NULL; - /* * For each error the next struct is allocated and linked in a list. */ @@ -97,6 +95,15 @@ struct qf_info_S { int qf_listcount; /* current number of lists */ int qf_curlist; /* current error list */ qf_list_T qf_lists[LISTCOUNT]; + + int qf_dir_curlist; ///< error list for qf_dir_stack + struct dir_stack_T *qf_dir_stack; + char_u *qf_directory; + struct dir_stack_T *qf_file_stack; + char_u *qf_currfile; + bool qf_multiline; + bool qf_multiignore; + bool qf_multiscan; }; static qf_info_T ql_info; /* global quickfix list */ @@ -464,25 +471,20 @@ qf_init_ext ( int enr = 0; FILE *fd = NULL; qfline_T *old_last = NULL; - efm_T *fmt_first = NULL; + static efm_T *fmt_first = NULL; efm_T *fmt_ptr; efm_T *fmt_start = NULL; char_u *efm; + static char_u *last_efm = NULL; char_u *ptr; size_t len; int i; int idx = 0; - bool multiline = false; - bool multiignore = false; - bool multiscan = false; int retval = -1; // default: return error flag - char_u *directory = NULL; - char_u *currfile = NULL; char_u *tail = NULL; char_u *p_buf = NULL; char_u *p_str = NULL; listitem_T *p_li = NULL; - struct dir_stack_T *file_stack = NULL; regmatch_T regmatch; namebuf = xmalloc(CMDBUFFSIZE + 1); @@ -513,7 +515,33 @@ qf_init_ext ( else efm = errorformat; - fmt_first = parse_efm_option(efm); + // If we are not adding or adding to another list: clear the state. + if (newlist || qi->qf_curlist != qi->qf_dir_curlist) { + qi->qf_dir_curlist = qi->qf_curlist; + qf_clean_dir_stack(&qi->qf_dir_stack); + qi->qf_directory = NULL; + qf_clean_dir_stack(&qi->qf_file_stack); + qi->qf_currfile = NULL; + qi->qf_multiline = false; + qi->qf_multiignore = false; + qi->qf_multiscan = false; + } + + // If the errorformat didn't change between calls, then reuse the previously + // parsed values. + if (last_efm == NULL || (STRCMP(last_efm, efm) != 0)) { + // free the previously parsed data + xfree(last_efm); + last_efm = NULL; + free_efm_list(&fmt_first); + + // parse the current 'efm' + fmt_first = parse_efm_option(efm); + if (fmt_first != NULL) { + last_efm = vim_strsave(efm); + } + } + if (fmt_first == NULL) { /* nothing found */ goto error2; } @@ -696,11 +724,11 @@ qf_init_ext ( restofline: for (; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next) { idx = fmt_ptr->prefix; - if (multiscan && vim_strchr((char_u *)"OPQ", idx) == NULL) + if (qi->qf_multiscan && vim_strchr((char_u *)"OPQ", idx) == NULL) continue; namebuf[0] = NUL; pattern[0] = NUL; - if (!multiscan) + if (!qi->qf_multiscan) errmsg[0] = NUL; lnum = 0; col = 0; @@ -713,7 +741,7 @@ restofline: int r = vim_regexec(®match, linebuf, (colnr_T)0); fmt_ptr->prog = regmatch.regprog; if (r) { - if ((idx == 'C' || idx == 'Z') && !multiline) { + if ((idx == 'C' || idx == 'Z') && !qi->qf_multiline) { continue; } if (vim_strchr((char_u *)"EWI", idx) != NULL) { @@ -759,7 +787,7 @@ restofline: continue; type = *regmatch.startp[i]; } - if (fmt_ptr->flags == '+' && !multiscan) { // %+ + if (fmt_ptr->flags == '+' && !qi->qf_multiscan) { // %+ if (linelen > errmsglen) { // linelen + null terminator errmsg = xrealloc(errmsg, linelen + 1); @@ -820,7 +848,7 @@ restofline: break; } } - multiscan = false; + qi->qf_multiscan = false; if (fmt_ptr == NULL || idx == 'D' || idx == 'X') { if (fmt_ptr != NULL) { @@ -829,10 +857,12 @@ restofline: EMSG(_("E379: Missing or empty directory name")); goto error2; } - if ((directory = qf_push_dir(namebuf, &dir_stack)) == NULL) + qi->qf_directory = qf_push_dir(namebuf, &qi->qf_dir_stack, false); + if (qi->qf_directory == NULL) { goto error2; + } } else if (idx == 'X') /* leave directory */ - directory = qf_pop_dir(&dir_stack); + qi->qf_directory = qf_pop_dir(&qi->qf_dir_stack); } namebuf[0] = NUL; // no match found, remove file name lnum = 0; // don't jump to this line @@ -844,7 +874,7 @@ restofline: // copy whole line to error message STRLCPY(errmsg, linebuf, linelen + 1); if (fmt_ptr == NULL) { - multiline = multiignore = false; + qi->qf_multiline = qi->qf_multiignore = false; } } else if (fmt_ptr != NULL) { /* honor %> item */ @@ -852,14 +882,14 @@ restofline: fmt_start = fmt_ptr; if (vim_strchr((char_u *)"AEWI", idx) != NULL) { - multiline = true; // start of a multi-line message - multiignore = false; // reset continuation + qi->qf_multiline = true; // start of a multi-line message + qi->qf_multiignore = false; // reset continuation } else if (vim_strchr((char_u *)"CZ", idx) != NULL) { /* continuation of multi-line msg */ qfline_T *qfprev = qi->qf_lists[qi->qf_curlist].qf_last; if (qfprev == NULL) goto error2; - if (*errmsg && !multiignore) { + if (*errmsg && !qi->qf_multiignore) { size_t len = STRLEN(qfprev->qf_text); qfprev->qf_text = xrealloc(qfprev->qf_text, len + STRLEN(errmsg) + 2); qfprev->qf_text[len] = '\n'; @@ -875,12 +905,13 @@ restofline: qfprev->qf_col = col; qfprev->qf_viscol = use_viscol; if (!qfprev->qf_fnum) - qfprev->qf_fnum = qf_get_fnum(directory, + qfprev->qf_fnum = qf_get_fnum(qi, qi->qf_directory, *namebuf - || directory ? namebuf : currfile - && valid ? currfile : 0); + || qi->qf_directory + ? namebuf : qi->qf_currfile + && valid ? qi->qf_currfile : 0); if (idx == 'Z') { - multiline = multiignore = false; + qi->qf_multiline = qi->qf_multiignore = false; } line_breakcheck(); continue; @@ -889,31 +920,32 @@ restofline: valid = false; if (*namebuf == NUL || os_path_exists(namebuf)) { if (*namebuf && idx == 'P') { - currfile = qf_push_dir(namebuf, &file_stack); + qi->qf_currfile = qf_push_dir(namebuf, &qi->qf_file_stack, true); } else if (idx == 'Q') { - currfile = qf_pop_dir(&file_stack); + qi->qf_currfile = qf_pop_dir(&qi->qf_file_stack); } *namebuf = NUL; if (tail && *tail) { STRMOVE(IObuff, skipwhite(tail)); - multiscan = true; + qi->qf_multiscan = true; goto restofline; } } } if (fmt_ptr->flags == '-') { // generally exclude this line - if (multiline) { - multiignore = true; // also exclude continuation lines + if (qi->qf_multiline) { + // also exclude continuation lines + qi->qf_multiignore = true; } continue; } } if (qf_add_entry(qi, - directory, - (*namebuf || directory) + qi->qf_directory, + (*namebuf || qi->qf_directory) ? namebuf - : ((currfile && valid) ? currfile : (char_u *)NULL), + : ((qi->qf_currfile && valid) ? qi->qf_currfile : (char_u *)NULL), 0, errmsg, lnum, @@ -952,9 +984,6 @@ error2: qf_init_end: if (fd != NULL) fclose(fd); - free_efm_list(&fmt_first); - qf_clean_dir_stack(&dir_stack); - qf_clean_dir_stack(&file_stack); xfree(namebuf); xfree(errmsg); xfree(pattern); @@ -1071,7 +1100,7 @@ static int qf_add_entry(qf_info_T *qi, char_u *dir, char_u *fname, int bufnum, buf->b_has_qf_entry = true; } } else - qfp->qf_fnum = qf_get_fnum(dir, fname); + qfp->qf_fnum = qf_get_fnum(qi, dir, fname); qfp->qf_text = vim_strsave(mesg); qfp->qf_lnum = lnum; qfp->qf_col = col; @@ -1245,7 +1274,7 @@ void copy_loclist(win_T *from, win_T *to) // Get buffer number for file "dir.name". // Also sets the b_has_qf_entry flag. -static int qf_get_fnum(char_u *directory, char_u *fname) +static int qf_get_fnum(qf_info_T *qi, char_u *directory, char_u *fname) { char_u *ptr; buf_T *buf; @@ -1267,7 +1296,7 @@ static int qf_get_fnum(char_u *directory, char_u *fname) */ if (!os_path_exists(ptr)) { xfree(ptr); - directory = qf_guess_filepath(fname); + directory = qf_guess_filepath(qi, fname); if (directory) ptr = (char_u *)concat_fnames((char *)directory, (char *)fname, TRUE); else @@ -1288,7 +1317,7 @@ static int qf_get_fnum(char_u *directory, char_u *fname) // Push dirbuf onto the directory stack and return pointer to actual dir or // NULL on error. -static char_u *qf_push_dir(char_u *dirbuf, struct dir_stack_T **stackptr) +static char_u *qf_push_dir(char_u *dirbuf, struct dir_stack_T **stackptr, bool is_file_stack) { struct dir_stack_T *ds_ptr; @@ -1301,7 +1330,7 @@ static char_u *qf_push_dir(char_u *dirbuf, struct dir_stack_T **stackptr) /* store directory on the stack */ if (vim_isAbsName(dirbuf) || (*stackptr)->next == NULL - || (*stackptr && dir_stack != *stackptr)) + || (*stackptr && is_file_stack)) (*stackptr)->dirname = vim_strsave(dirbuf); else { /* Okay we don't have an absolute path. @@ -1403,17 +1432,17 @@ static void qf_clean_dir_stack(struct dir_stack_T **stackptr) * Then qf_push_dir thinks we are in ./aa/bb, but we are in ./bb. * qf_guess_filepath will return NULL. */ -static char_u *qf_guess_filepath(char_u *filename) +static char_u *qf_guess_filepath(qf_info_T *qi, char_u *filename) { struct dir_stack_T *ds_ptr; struct dir_stack_T *ds_tmp; char_u *fullname; /* no dirs on the stack - there's nothing we can do */ - if (dir_stack == NULL) + if (qi->qf_dir_stack == NULL) return NULL; - ds_ptr = dir_stack->next; + ds_ptr = qi->qf_dir_stack->next; fullname = NULL; while (ds_ptr) { xfree(fullname); @@ -1429,15 +1458,14 @@ static char_u *qf_guess_filepath(char_u *filename) xfree(fullname); /* clean up all dirs we already left */ - while (dir_stack->next != ds_ptr) { - ds_tmp = dir_stack->next; - dir_stack->next = dir_stack->next->next; + while (qi->qf_dir_stack->next != ds_ptr) { + ds_tmp = qi->qf_dir_stack->next; + qi->qf_dir_stack->next = qi->qf_dir_stack->next->next; xfree(ds_tmp->dirname); xfree(ds_tmp); } return ds_ptr==NULL ? NULL : ds_ptr->dirname; - } /// When loading a file from the quickfix, the auto commands may modify it. @@ -2146,6 +2174,9 @@ static void qf_free(qf_info_T *qi, int idx) qi->qf_lists[idx].qf_ptr = NULL; qi->qf_lists[idx].qf_title = NULL; qi->qf_lists[idx].qf_index = 0; + + qf_clean_dir_stack(&qi->qf_dir_stack); + qf_clean_dir_stack(&qi->qf_file_stack); } /* diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 3bd7a8c4fb..2c6a3600e1 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -10,6 +10,7 @@ function! s:setup_commands(cchar) if a:cchar == 'c' command! -nargs=* -bang Xlist <mods>clist<bang> <args> command! -nargs=* Xgetexpr <mods>cgetexpr <args> + command! -nargs=* Xaddexpr <mods>caddexpr <args> command! -nargs=* Xolder <mods>colder <args> command! -nargs=* Xnewer <mods>cnewer <args> command! -nargs=* Xopen <mods>copen <args> @@ -33,6 +34,7 @@ function! s:setup_commands(cchar) else command! -nargs=* -bang Xlist <mods>llist<bang> <args> command! -nargs=* Xgetexpr <mods>lgetexpr <args> + command! -nargs=* Xaddexpr <mods>laddexpr <args> command! -nargs=* Xolder <mods>lolder <args> command! -nargs=* Xnewer <mods>lnewer <args> command! -nargs=* Xopen <mods>lopen <args> @@ -661,21 +663,25 @@ function! s:dir_stack_tests(cchar) let save_efm=&efm set efm=%DEntering\ dir\ '%f',%f:%l:%m,%XLeaving\ dir\ '%f' - let l = "Entering dir 'dir1/a'\n" . - \ 'habits2.txt:1:Nine Healthy Habits' . "\n" . - \ "Entering dir 'b'\n" . - \ 'habits3.txt:2:0 Hours of television' . "\n" . - \ 'habits2.txt:7:5 Small meals' . "\n" . - \ "Entering dir 'dir1/c'\n" . - \ 'habits4.txt:3:1 Hour of exercise' . "\n" . - \ "Leaving dir 'dir1/c'\n" . - \ "Leaving dir 'dir1/a'\n" . - \ 'habits1.txt:4:2 Liters of water' . "\n" . - \ "Entering dir 'dir2'\n" . - \ 'habits5.txt:5:3 Cups of hot green tea' . "\n" - \ "Leaving dir 'dir2'\n" - - Xgetexpr l + let lines = ["Entering dir 'dir1/a'", + \ 'habits2.txt:1:Nine Healthy Habits', + \ "Entering dir 'b'", + \ 'habits3.txt:2:0 Hours of television', + \ 'habits2.txt:7:5 Small meals', + \ "Entering dir 'dir1/c'", + \ 'habits4.txt:3:1 Hour of exercise', + \ "Leaving dir 'dir1/c'", + \ "Leaving dir 'dir1/a'", + \ 'habits1.txt:4:2 Liters of water', + \ "Entering dir 'dir2'", + \ 'habits5.txt:5:3 Cups of hot green tea', + \ "Leaving dir 'dir2'" + \] + + Xexpr "" + for l in lines + Xaddexpr l + endfor let qf = g:Xgetlist() @@ -762,7 +768,10 @@ function! Test_efm2() \ "(67,3) warning: 's' already defined" \] set efm=%+P[%f],(%l\\,%c)%*[\ ]%t%*[^:]:\ %m,%-Q - cgetexpr lines + cexpr "" + for l in lines + caddexpr l + endfor let l = getqflist() call assert_equal(9, len(l)) call assert_equal(21, l[2].lnum) @@ -1220,3 +1229,46 @@ function! Test_grep() call s:test_xgrep('c') call s:test_xgrep('l') endfunction + +function! Test_two_windows() + " Use one 'errorformat' for two windows. Add an expression to each of them, + " make sure they each keep their own state. + set efm=%DEntering\ dir\ '%f',%f:%l:%m,%XLeaving\ dir\ '%f' + call mkdir('Xone/a', 'p') + call mkdir('Xtwo/a', 'p') + let lines = ['1', '2', 'one one one', '4', 'two two two', '6', '7'] + call writefile(lines, 'Xone/a/one.txt') + call writefile(lines, 'Xtwo/a/two.txt') + + new one + let one_id = win_getid() + lexpr "" + new two + let two_id = win_getid() + lexpr "" + + laddexpr "Entering dir 'Xtwo/a'" + call win_gotoid(one_id) + laddexpr "Entering dir 'Xone/a'" + call win_gotoid(two_id) + laddexpr 'two.txt:5:two two two' + call win_gotoid(one_id) + laddexpr 'one.txt:3:one one one' + + let loc_one = getloclist(one_id) +echo string(loc_one) + call assert_equal('Xone/a/one.txt', bufname(loc_one[1].bufnr)) + call assert_equal(3, loc_one[1].lnum) + + let loc_two = getloclist(two_id) +echo string(loc_two) + call assert_equal('Xtwo/a/two.txt', bufname(loc_two[1].bufnr)) + call assert_equal(5, loc_two[1].lnum) + + call win_gotoid(one_id) + bwipe! + call win_gotoid(two_id) + bwipe! + call delete('Xone', 'rf') + call delete('Xtwo', 'rf') +endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index 576843e61d..17bb4aa356 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -460,7 +460,7 @@ static int included_patches[] = { // 1983 NA // 1982 NA // 1981, - // 1980, + 1980, // 1979, // 1978, // 1977, |