Hi Gary, On Sun, Mar 20, 2016 at 10:32 PM, Gary Johnson <garyj...@spocom.com> wrote: > On 2016-03-13, Yegappan Lakshmanan wrote: >> Hi Bram, >> >> On Sun, Mar 13, 2016 at 11:44 AM, Bram Moolenaar wrote: >> > >> > Yegappan Lakshmanan wrote: >> > >> >> >> I am attaching a patch to add the ":cfilter" and ":lfilter" commands to >> >> >> filter entries matching a pattern from the quickfix/location lists. >> >> >> The :cfilter command creates a new quickfix list with entries matching >> >> >> the specified regex pattern. When "!" is supplied, creates a list with >> >> >> entries not matching the pattern. >> >> > >> >> > The help text is very brief. Can you add a useful example? >> >> > >> >> >> >> I have added additional description and examples to the help text. >> >> I have also added tests for the new commands. The updated patch is >> >> attached. >> > >> > Thanks. The help could be a bit more specific about what happens. I >> > guess the new quickfix list is added at the end and becomes the current >> > one. What happens after: >> > :colder 5 >> > :cfilter >> > >> >> An updated patch with additional description and tests is attached. > > This looks like a great feature. I was unclear, though, about the > meaning of this sentence from the description in :help. > > Only the text of the valid entries is used for the match. > > While ":help getqflist() refers to the "text" entry as the > "description of the error", ":help errorformat" refers to %m as the > "error message". The latter has been around longer, so I always > think of that portion of the quickfix window contents as the > "message" rather than the "text". > > Since the existing :help entries are not consistent, perhaps that > sentence should be changed to something like: > > Only the text or error message portion of the valid entries is > used for the match. >
I have updated the help for ":cfilter" to include the above description. I have also modified the patch to match the full path of the file name of the valid entries. Now the ":cfilter" command tries to match the supplied pattern in both the error message and the full path of the file name of valid entries. The updated patch is attached. - Yegappan -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to vim_dev+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 76ff93f..48ebff6 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1143,6 +1143,7 @@ tag command action ~ |:center| :ce[nter] format lines at the center |:cexpr| :cex[pr] read errors from expr and jump to first |:cfile| :cf[ile] read file with error messages and jump to first +|:cfilter| :cfilt[er] filter entries in the quickfix list |:cfirst| :cfir[st] go to the specified error, default first one |:cgetbuffer| :cgetb[uffer] get errors from buffer |:cgetexpr| :cgete[xpr] get errors from expr @@ -1300,6 +1301,7 @@ tag command action ~ |:lcscope| :lcs[cope] like ":cscope" but uses location list |:ldo| :ld[o] execute command in valid location list entries |:lfdo| :lfd[o] execute command in each file in location list +|:lfilter| :lfilt[er] filter entries in the location list |:left| :le[ft] left align lines |:leftabove| :lefta[bove] make split window appear left or above |:let| :let assign a value to a variable or option diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index 0ca314d..fd36c04 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -257,6 +257,26 @@ location list command, it will be aborted. :lad[dexpr] {expr} Same as ":caddexpr", except the location list for the current window is used instead of the quickfix list. + *:cfilt* *:cfilter* +:cfilt[er][!] {pattern} Create a new quickfix list with entries in the current + quickfix list matching {pattern}. Only the text or + error message of the valid entries and the full path of + their filenames are used for the match. See |vimgrep| + for an explanation of {pattern}. With [!], entries not + matching {pattern} are used. When no matches are found, + an empty quickfix list is created. The new quickfix + list is added after the current list and the following + lists (if any) are freed. + Examples: > + :cfilter Line\d\+ + :cfilter /will allocate/ + :cfilter // + :cfilter! /fname_expand/ +< + *:lfilt* *:lfilter* +:lfilt[er][!] {pattern} Same as ":cfilter", except the location list for the + current window is used instead of the quickfix list. + *:cl* *:clist* :cl[ist] [from] [, [to]] List all errors that are valid |quickfix-valid|. diff --git a/src/ex_cmds.h b/src/ex_cmds.h index 34defea..fff235c 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -279,6 +279,9 @@ EX(CMD_cexpr, "cexpr", ex_cexpr, EX(CMD_cfile, "cfile", ex_cfile, TRLBAR|FILE1|BANG, ADDR_LINES), +EX(CMD_cfilter, "cfilter", ex_cfilter, + BANG|EXTRA|NEEDARG|NOTRLCOM|NOTADR, + ADDR_LINES), EX(CMD_cfdo, "cfdo", ex_listdo, BANG|NEEDARG|EXTRA|NOTRLCOM|RANGE|NOTADR|DFLALL, ADDR_QUICKFIX), @@ -750,6 +753,9 @@ EX(CMD_lexpr, "lexpr", ex_cexpr, EX(CMD_lfile, "lfile", ex_cfile, TRLBAR|FILE1|BANG, ADDR_LINES), +EX(CMD_lfilter, "lfilter", ex_cfilter, + BANG|EXTRA|NEEDARG|NOTRLCOM|NOTADR, + ADDR_LINES), EX(CMD_lfdo, "lfdo", ex_listdo, BANG|NEEDARG|EXTRA|NOTRLCOM|RANGE|NOTADR|DFLALL, ADDR_QUICKFIX), diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 26f4219..c6e0c56 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -120,6 +120,7 @@ static int getargopt(exarg_T *eap); # define ex_cc ex_ni # define ex_cnext ex_ni # define ex_cfile ex_ni +# define ex_cfilter ex_ni # define qf_list ex_ni # define qf_age ex_ni # define ex_helpgrep ex_ni diff --git a/src/proto/quickfix.pro b/src/proto/quickfix.pro index cc60edd..0957927 100644 --- a/src/proto/quickfix.pro +++ b/src/proto/quickfix.pro @@ -29,5 +29,6 @@ int get_errorlist(win_T *wp, list_T *list); int set_errorlist(win_T *wp, list_T *list, int action, char_u *title); void ex_cbuffer(exarg_T *eap); void ex_cexpr(exarg_T *eap); +void ex_cfilter(exarg_T *eap); void ex_helpgrep(exarg_T *eap); /* vim: set ft=c : */ diff --git a/src/quickfix.c b/src/quickfix.c index 06e50da..9bd61a3 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -4186,6 +4186,144 @@ ex_cexpr(exarg_T *eap) #endif /* + * ":cfilter {pattern}" + * ":lfilter {pattern}" + */ + void +ex_cfilter(exarg_T *eap) +{ + qf_info_T *qi = &ql_info; + regmatch_T regmatch; + char_u *save_cmd; + char_u *p; + char_u *s; + int i; + int qf_count; + qfline_T *qfp; + int add_entry; + int matched; + qfline_T *prevp = NULL; + buf_T *buf = NULL; + + if (eap->cmdidx == CMD_lfilter) + { + qi = GET_LOC_LIST(curwin); + if (qi == NULL) + { + EMSG(_(e_loclist)); + return; + } + } + + if (qi->qf_curlist >= qi->qf_listcount + || qi->qf_lists[qi->qf_curlist].qf_count == 0) + { + EMSG(_(e_quickfix)); + return; + } + + if (qi->qf_lists[qi->qf_curlist].qf_nonevalid) + return; + + /* skip_vimgrep_pat changes the command line, so save it */ + save_cmd = vim_strsave(*eap->cmdlinep); + + /* Get the search pattern: either white-separated or enclosed in // */ + regmatch.regprog = NULL; + p = skip_vimgrep_pat(eap->arg, &s, NULL); + if (p == NULL) + { + EMSG(_(e_invalpat)); + goto theend; + } + + if (s != NULL && *s == NUL) + { + /* Pattern is empty, use last search pattern. */ + if (last_search_pat() == NULL) + { + EMSG(_(e_noprevre)); + goto theend; + } + s = last_search_pat(); + } + + regmatch.regprog = vim_regcomp(s, RE_MAGIC + RE_STRING); + regmatch.rm_ic = p_ic; + + if (regmatch.regprog == NULL) + goto theend; + + qfp = qi->qf_lists[qi->qf_curlist].qf_start; + qf_count = qi->qf_lists[qi->qf_curlist].qf_count; + + /* create a new quickfix list */ + qf_new_list(qi, save_cmd); + + for (i = 0; !got_int && i < qf_count; i++, qfp = qfp->qf_next) + { + if (!qfp->qf_valid) + continue; /* search in only valid entries */ + + matched = FALSE; + if (vim_regexec(®match, qfp->qf_text, (colnr_T)0)) + matched = TRUE; + else if (qfp->qf_fnum != 0) + { + /* Try matching the file name */ + buf = buflist_findnr(qfp->qf_fnum); + if (buf != NULL && buf->b_ffname != NULL) + if (vim_regexec(®match, buf->b_ffname, (colnr_T)0)) + matched = TRUE; + } + + add_entry = FALSE; + if (matched) + { + if (!eap->forceit) + add_entry = TRUE; + } else if (eap->forceit) + add_entry = TRUE; + + if (add_entry) + { + if (qf_add_entry(qi, &prevp, + NULL, /* dir */ + NULL, /* file name */ + qfp->qf_fnum, + qfp->qf_text, + qfp->qf_lnum, + qfp->qf_col, + qfp->qf_viscol, + qfp->qf_pattern, + qfp->qf_nr, + qfp->qf_type, + qfp->qf_valid + ) == FAIL) + break; + } + } + + if (qi->qf_lists[qi->qf_curlist].qf_index == 0) + /* no valid entry */ + qi->qf_lists[qi->qf_curlist].qf_nonevalid = TRUE; + else + qi->qf_lists[qi->qf_curlist].qf_nonevalid = FALSE; + qi->qf_lists[qi->qf_curlist].qf_ptr = + qi->qf_lists[qi->qf_curlist].qf_start; + qi->qf_lists[qi->qf_curlist].qf_index = 1; + + vim_regfree(regmatch.regprog); + +#ifdef FEAT_WINDOWS + qf_update_buffer(qi); +#endif + +theend: + vim_free(save_cmd); +} + +/* * ":helpgrep {pattern}" */ void diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index 07c465d..ad29713 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -637,3 +637,69 @@ function! Test_efm1() call delete('Xerrorfile2') call delete('Xtestfile') endfunction + +" Tests for the :cfilter and :lfilter commands. +function XfilterTests(cchar) + let Xfilter = a:cchar . 'filter' + let Xolder = a:cchar . 'older' + let Xgetexpr = a:cchar . 'getexpr' + if a:cchar == 'c' + let Xgetlist = 'getqflist()' + else + let Xgetlist = 'getloclist(0)' + endif + + " With an empty list, command should return error + exe Xgetexpr . ' []' + exe 'silent! ' . Xfilter . ' /Pattern/' + call assert_true(v:errmsg ==# 'E42: No Errors') + + " Populate the list + exe Xgetexpr . " ['Xtestfile1:1:3:a simple line of text', + \ 'Dummy line of text 1', + \ 'Xtestfile2:2:2:another line of TEXT', + \ 'Dummy line of text 2', + \ 'Xtestfile3:3:1:third line of Text']" + + exe Xfilter . ' /third/' + exe 'let l = ' . Xgetlist + call assert_equal('third line of Text', l[0].text) + + exe Xolder + exe Xfilter . ' /abc/' + exe 'let l = ' . Xgetlist + call assert_true(len(l) == 0) + + exe Xolder + exe Xfilter . ' /\ctext/' + exe 'let l = ' . Xgetlist + call assert_true(len(l) == 3 && + \ l[0].text ==# 'a simple line of text' && + \ l[1].text ==# 'another line of TEXT' && + \ l[2].text ==# 'third line of Text') + + let @/ = "TEXT" + exe Xolder + exe Xfilter . ' //' + exe 'let l = ' . Xgetlist + call assert_true(len(l) == 1 && l[0].text ==# 'another line of TEXT') + + exe Xolder + exe Xfilter . '! text' + exe 'let l = ' . Xgetlist + call assert_true(len(l) == 2 && + \ l[0].text ==# 'another line of TEXT' && + \ l[1].text ==# 'third line of Text') + + exe 'silent! ' . Xfilter . ' /Pattern' + call assert_true(v:errmsg ==# 'E682: Invalid search pattern or delimiter') + + let @/ = "" + exe 'silent! ' . Xfilter . ' //' + call assert_true(v:errmsg ==# 'E35: No previous regular expression', v:errmsg) +endfunction + +function Test_cfilter() + call XfilterTests('c') + call XfilterTests('l') +endfunction