Hi,

On Mon, Mar 21, 2016 at 7:05 AM, Yegappan Lakshmanan
<yegapp...@gmail.com> 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.
>

An updated patch that supports both 'ignorecase' and 'smartcase'
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/eval.txt b/runtime/doc/eval.txt
index f1425ba..7a5b109 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -3603,7 +3603,7 @@ function({name} [, {arglist}] [, {dict}])
                        let Broken = function(dict.Func, [arg], dict)
 <
                When {arglist} or {dict} is present this creates a partial.
-               That mans the argument list and/or the dictionary is stored in
+               That means the argument list and/or the dictionary is stored in
                the Funcref and will be used when the Funcref is called.
                
                The arguments are passed to the function in front of other
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 a4c1208..1532c70 100644
--- a/runtime/doc/quickfix.txt
+++ b/runtime/doc/quickfix.txt
@@ -261,6 +261,27 @@ processing a quickfix or 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}.  Both 'ignorecase' and
+                       'smartcase' are used.
+                       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 e5de379..ed427e7 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 00762bd..4bfe5d4 100644
--- a/src/quickfix.c
+++ b/src/quickfix.c
@@ -4233,6 +4233,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 = ignorecase(s);
+
+    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(&regmatch, 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(&regmatch, 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 8da1b6f..196b770 100644
--- a/src/testdir/test_quickfix.vim
+++ b/src/testdir/test_quickfix.vim
@@ -697,3 +697,82 @@ func Test_cgetexpr_works()
   " this must not crash Vim
   cgetexpr [$x]
 endfunc
+
+" 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')
+
+  " Search for text in the filenames
+  exe Xolder
+  exe Xfilter . ' Xtestfile2'
+  exe 'let l = ' . Xgetlist
+  call assert_true(len(l) == 1 &&
+                         \ l[0].text ==# 'another line of TEXT')
+
+  exe Xolder
+  exe Xfilter . '! Xtestfile'
+  exe 'let l = ' . Xgetlist
+  call assert_true(len(l) == 0)
+
+  exe Xolder
+  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

Reply via email to