Hi Gary,
On Sun, Mar 20, 2016 at 10:32 PM, Gary Johnson <[email protected]> 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 [email protected].
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