0
0
mirror of https://github.com/vim/vim.git synced 2025-07-04 23:07:33 -04:00

patch 8.0.1406: difficult to track changes to a quickfix list

Problem:    Difficult to track changes to a quickfix list.
Solution:   Add a "changedtick" value. (Yegappan Lakshmanan, closes #2460)
This commit is contained in:
Bram Moolenaar 2017-12-18 19:48:58 +01:00
parent c9e649ae81
commit b254af312d
5 changed files with 120 additions and 6 deletions

View File

@ -4674,6 +4674,8 @@ getqflist([{what}]) *getqflist()*
If the optional {what} dictionary argument is supplied, then If the optional {what} dictionary argument is supplied, then
returns only the items listed in {what} as a dictionary. The returns only the items listed in {what} as a dictionary. The
following string items are supported in {what}: following string items are supported in {what}:
changedtick get the total number of changes made
to the list
context get the context stored with |setqflist()| context get the context stored with |setqflist()|
efm errorformat to use when parsing "lines". If efm errorformat to use when parsing "lines". If
not present, then the 'errorformat' option not present, then the 'errorformat' option
@ -4707,6 +4709,8 @@ getqflist([{what}]) *getqflist()*
"items" with the list of entries. "items" with the list of entries.
The returned dictionary contains the following entries: The returned dictionary contains the following entries:
changedtick total number of changes made to the
list |quickfix-changedtick|
context context information stored with |setqflist()|. context context information stored with |setqflist()|.
If not present, set to "". If not present, set to "".
id quickfix list ID |quickfix-ID|. If not id quickfix list ID |quickfix-ID|. If not

View File

@ -64,6 +64,14 @@ When a window with a location list is split, the new window gets a copy of the
location list. When there are no longer any references to a location list, location list. When there are no longer any references to a location list,
the location list is destroyed. the location list is destroyed.
*quickfix-changedtick*
Every quickfix and location list has a read-only changedtick variable that
tracks the total number of changes made to the list. Every time the quickfix
list is modified, this count is incremented. This can be used to perform an
action only when the list has changed. The getqflist() and getloclist()
functions can be used to query the current value of changedtick. You cannot
change the changedtick variable.
The following quickfix commands can be used. The location list commands are The following quickfix commands can be used. The location list commands are
similar to the quickfix commands, replacing the 'c' prefix in the quickfix similar to the quickfix commands, replacing the 'c' prefix in the quickfix
command with 'l'. command with 'l'.

View File

@ -76,6 +76,7 @@ typedef struct qf_list_S
int qf_multiline; int qf_multiline;
int qf_multiignore; int qf_multiignore;
int qf_multiscan; int qf_multiscan;
long qf_changedtick;
} qf_list_T; } qf_list_T;
/* /*
@ -1668,6 +1669,7 @@ copy_loclist(win_T *from, win_T *to)
/* Assign a new ID for the location list */ /* Assign a new ID for the location list */
to_qfl->qf_id = ++last_qf_id; to_qfl->qf_id = ++last_qf_id;
to_qfl->qf_changedtick = 0L;
/* When no valid entries are present in the list, qf_ptr points to /* When no valid entries are present in the list, qf_ptr points to
* the first item in the list */ * the first item in the list */
@ -2965,6 +2967,7 @@ qf_free(qf_info_T *qi, int idx)
free_tv(qfl->qf_ctx); free_tv(qfl->qf_ctx);
qfl->qf_ctx = NULL; qfl->qf_ctx = NULL;
qfl->qf_id = 0; qfl->qf_id = 0;
qfl->qf_changedtick = 0L;
} }
/* /*
@ -3604,6 +3607,12 @@ qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last)
KeyTyped = old_KeyTyped; KeyTyped = old_KeyTyped;
} }
static void
qf_list_changed(qf_info_T *qi, int qf_idx)
{
qi->qf_lists[qf_idx].qf_changedtick++;
}
/* /*
* Return TRUE when using ":vimgrep" for ":grep". * Return TRUE when using ":vimgrep" for ":grep".
*/ */
@ -3713,6 +3722,8 @@ ex_make(exarg_T *eap)
*eap->cmdlinep, enc); *eap->cmdlinep, enc);
if (wp != NULL) if (wp != NULL)
qi = GET_LOC_LIST(wp); qi = GET_LOC_LIST(wp);
if (res >= 0 && qi != NULL)
qf_list_changed(qi, qi->qf_curlist);
#ifdef FEAT_AUTOCMD #ifdef FEAT_AUTOCMD
if (au_name != NULL) if (au_name != NULL)
{ {
@ -4105,14 +4116,16 @@ ex_cfile(exarg_T *eap)
*/ */
res = qf_init(wp, p_ef, p_efm, (eap->cmdidx != CMD_caddfile res = qf_init(wp, p_ef, p_efm, (eap->cmdidx != CMD_caddfile
&& eap->cmdidx != CMD_laddfile), *eap->cmdlinep, enc); && eap->cmdidx != CMD_laddfile), *eap->cmdlinep, enc);
if (wp != NULL)
qi = GET_LOC_LIST(wp);
if (res >= 0 && qi != NULL)
qf_list_changed(qi, qi->qf_curlist);
#ifdef FEAT_AUTOCMD #ifdef FEAT_AUTOCMD
if (au_name != NULL) if (au_name != NULL)
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, NULL, FALSE, curbuf); apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, NULL, FALSE, curbuf);
#endif #endif
if (res > 0 && (eap->cmdidx == CMD_cfile || eap->cmdidx == CMD_lfile)) if (res > 0 && (eap->cmdidx == CMD_cfile || eap->cmdidx == CMD_lfile))
{ {
if (wp != NULL)
qi = GET_LOC_LIST(wp);
qf_jump(qi, 0, 0, eap->forceit); /* display first error */ qf_jump(qi, 0, 0, eap->forceit); /* display first error */
} }
} }
@ -4469,6 +4482,7 @@ ex_vimgrep(exarg_T *eap)
qi->qf_lists[qi->qf_curlist].qf_nonevalid = FALSE; 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_ptr = qi->qf_lists[qi->qf_curlist].qf_start;
qi->qf_lists[qi->qf_curlist].qf_index = 1; qi->qf_lists[qi->qf_curlist].qf_index = 1;
qf_list_changed(qi, qi->qf_curlist);
qf_update_buffer(qi, NULL); qf_update_buffer(qi, NULL);
@ -4780,7 +4794,8 @@ enum {
QF_GETLIST_ID = 0x20, QF_GETLIST_ID = 0x20,
QF_GETLIST_IDX = 0x40, QF_GETLIST_IDX = 0x40,
QF_GETLIST_SIZE = 0x80, QF_GETLIST_SIZE = 0x80,
QF_GETLIST_ALL = 0xFF QF_GETLIST_TICK = 0x100,
QF_GETLIST_ALL = 0x1FF
}; };
/* /*
@ -4896,6 +4911,9 @@ qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
if (dict_find(what, (char_u *)"size", -1) != NULL) if (dict_find(what, (char_u *)"size", -1) != NULL)
flags |= QF_GETLIST_SIZE; flags |= QF_GETLIST_SIZE;
if (dict_find(what, (char_u *)"changedtick", -1) != NULL)
flags |= QF_GETLIST_TICK;
if (qi != NULL && qi->qf_listcount != 0) if (qi != NULL && qi->qf_listcount != 0)
{ {
qf_idx = qi->qf_curlist; /* default is the current list */ qf_idx = qi->qf_curlist; /* default is the current list */
@ -4963,6 +4981,8 @@ qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
status = dict_add_nr_str(retdict, "idx", 0L, NULL); status = dict_add_nr_str(retdict, "idx", 0L, NULL);
if ((status == OK) && (flags & QF_GETLIST_SIZE)) if ((status == OK) && (flags & QF_GETLIST_SIZE))
status = dict_add_nr_str(retdict, "size", 0L, NULL); status = dict_add_nr_str(retdict, "size", 0L, NULL);
if ((status == OK) && (flags & QF_GETLIST_TICK))
status = dict_add_nr_str(retdict, "changedtick", 0L, NULL);
return status; return status;
} }
@ -5035,6 +5055,10 @@ qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
status = dict_add_nr_str(retdict, "size", status = dict_add_nr_str(retdict, "size",
qi->qf_lists[qf_idx].qf_count, NULL); qi->qf_lists[qf_idx].qf_count, NULL);
if ((status == OK) && (flags & QF_GETLIST_TICK))
status = dict_add_nr_str(retdict, "changedtick",
qi->qf_lists[qf_idx].qf_changedtick, NULL);
return status; return status;
} }
@ -5304,6 +5328,9 @@ qf_set_properties(qf_info_T *qi, dict_T *what, int action, char_u *title)
retval = OK; retval = OK;
} }
if (retval == OK)
qf_list_changed(qi, qf_idx);
return retval; return retval;
} }
@ -5407,7 +5434,11 @@ set_errorlist(
else if (what != NULL) else if (what != NULL)
retval = qf_set_properties(qi, what, action, title); retval = qf_set_properties(qi, what, action, title);
else else
{
retval = qf_add_entries(qi, qi->qf_curlist, list, title, action); retval = qf_add_entries(qi, qi->qf_curlist, list, title, action);
if (retval == OK)
qf_list_changed(qi, qi->qf_curlist);
}
return retval; return retval;
} }
@ -5540,6 +5571,8 @@ ex_cbuffer(exarg_T *eap)
&& eap->cmdidx != CMD_laddbuffer), && eap->cmdidx != CMD_laddbuffer),
eap->line1, eap->line2, eap->line1, eap->line2,
qf_title, NULL); qf_title, NULL);
if (res >= 0)
qf_list_changed(qi, qi->qf_curlist);
#ifdef FEAT_AUTOCMD #ifdef FEAT_AUTOCMD
if (au_name != NULL) if (au_name != NULL)
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
@ -5609,6 +5642,8 @@ ex_cexpr(exarg_T *eap)
&& eap->cmdidx != CMD_laddexpr), && eap->cmdidx != CMD_laddexpr),
(linenr_T)0, (linenr_T)0, *eap->cmdlinep, (linenr_T)0, (linenr_T)0, *eap->cmdlinep,
NULL); NULL);
if (res >= 0)
qf_list_changed(qi, qi->qf_curlist);
#ifdef FEAT_AUTOCMD #ifdef FEAT_AUTOCMD
if (au_name != NULL) if (au_name != NULL)
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
@ -5829,6 +5864,7 @@ ex_helpgrep(exarg_T *eap)
/* Darn, some plugin changed the value. */ /* Darn, some plugin changed the value. */
free_string_option(save_cpo); free_string_option(save_cpo);
qf_list_changed(qi, qi->qf_curlist);
qf_update_buffer(qi, NULL); qf_update_buffer(qi, NULL);
#ifdef FEAT_AUTOCMD #ifdef FEAT_AUTOCMD

View File

@ -2132,6 +2132,8 @@ func Test_Autocmd()
call delete('Xtest') call delete('Xtest')
call delete('Xempty') call delete('Xempty')
au! QuickFixCmdPre
au! QuickFixCmdPost
endfunc endfunc
func Test_Autocmd_Exception() func Test_Autocmd_Exception()
@ -2896,7 +2898,8 @@ func Xgetlist_empty_tests(cchar)
call assert_equal(0, g:Xgetlist({'size' : 0}).size) call assert_equal(0, g:Xgetlist({'size' : 0}).size)
call assert_equal('', g:Xgetlist({'title' : 0}).title) call assert_equal('', g:Xgetlist({'title' : 0}).title)
call assert_equal(0, g:Xgetlist({'winid' : 0}).winid) call assert_equal(0, g:Xgetlist({'winid' : 0}).winid)
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0}, g:Xgetlist({'all' : 0})) call assert_equal(0, g:Xgetlist({'changedtick' : 0}).changedtick)
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, 'changedtick': 0}, g:Xgetlist({'all' : 0}))
" Empty quickfix list " Empty quickfix list
Xexpr "" Xexpr ""
@ -2908,6 +2911,7 @@ func Xgetlist_empty_tests(cchar)
call assert_equal(0, g:Xgetlist({'size' : 0}).size) call assert_equal(0, g:Xgetlist({'size' : 0}).size)
call assert_notequal('', g:Xgetlist({'title' : 0}).title) call assert_notequal('', g:Xgetlist({'title' : 0}).title)
call assert_equal(0, g:Xgetlist({'winid' : 0}).winid) call assert_equal(0, g:Xgetlist({'winid' : 0}).winid)
call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
let qfid = g:Xgetlist({'id' : 0}).id let qfid = g:Xgetlist({'id' : 0}).id
call g:Xsetlist([], 'f') call g:Xsetlist([], 'f')
@ -2921,7 +2925,8 @@ func Xgetlist_empty_tests(cchar)
call assert_equal(0, g:Xgetlist({'id' : qfid, 'size' : 0}).size) call assert_equal(0, g:Xgetlist({'id' : qfid, 'size' : 0}).size)
call assert_equal('', g:Xgetlist({'id' : qfid, 'title' : 0}).title) call assert_equal('', g:Xgetlist({'id' : qfid, 'title' : 0}).title)
call assert_equal(0, g:Xgetlist({'id' : qfid, 'winid' : 0}).winid) call assert_equal(0, g:Xgetlist({'id' : qfid, 'winid' : 0}).winid)
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0}, g:Xgetlist({'id' : qfid, 'all' : 0})) call assert_equal(0, g:Xgetlist({'id' : qfid, 'changedtick' : 0}).changedtick)
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, 'changedtick' : 0}, g:Xgetlist({'id' : qfid, 'all' : 0}))
" Non-existing quickfix list number " Non-existing quickfix list number
call assert_equal('', g:Xgetlist({'nr' : 5, 'context' : 0}).context) call assert_equal('', g:Xgetlist({'nr' : 5, 'context' : 0}).context)
@ -2932,10 +2937,69 @@ func Xgetlist_empty_tests(cchar)
call assert_equal(0, g:Xgetlist({'nr' : 5, 'size' : 0}).size) call assert_equal(0, g:Xgetlist({'nr' : 5, 'size' : 0}).size)
call assert_equal('', g:Xgetlist({'nr' : 5, 'title' : 0}).title) call assert_equal('', g:Xgetlist({'nr' : 5, 'title' : 0}).title)
call assert_equal(0, g:Xgetlist({'nr' : 5, 'winid' : 0}).winid) call assert_equal(0, g:Xgetlist({'nr' : 5, 'winid' : 0}).winid)
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0}, g:Xgetlist({'nr' : 5, 'all' : 0})) call assert_equal(0, g:Xgetlist({'nr' : 5, 'changedtick' : 0}).changedtick)
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, 'changedtick' : 0}, g:Xgetlist({'nr' : 5, 'all' : 0}))
endfunc endfunc
func Test_getqflist() func Test_getqflist()
call Xgetlist_empty_tests('c') call Xgetlist_empty_tests('c')
call Xgetlist_empty_tests('l') call Xgetlist_empty_tests('l')
endfunc endfunc
" Tests for the quickfix/location list changedtick
func Xqftick_tests(cchar)
call s:setup_commands(a:cchar)
call g:Xsetlist([], 'f')
Xexpr "F1:10:Line10"
let qfid = g:Xgetlist({'id' : 0}).id
call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
Xaddexpr "F2:20:Line20\nF2:21:Line21"
call assert_equal(2, g:Xgetlist({'changedtick' : 0}).changedtick)
call g:Xsetlist([], 'a', {'lines' : ["F3:30:Line30", "F3:31:Line31"]})
call assert_equal(3, g:Xgetlist({'changedtick' : 0}).changedtick)
call g:Xsetlist([], 'r', {'lines' : ["F4:40:Line40"]})
call assert_equal(4, g:Xgetlist({'changedtick' : 0}).changedtick)
call g:Xsetlist([], 'a', {'title' : 'New Title'})
call assert_equal(5, g:Xgetlist({'changedtick' : 0}).changedtick)
enew!
call append(0, ["F5:50:L50", "F6:60:L60"])
Xaddbuffer
call assert_equal(6, g:Xgetlist({'changedtick' : 0}).changedtick)
enew!
call g:Xsetlist([], 'a', {'context' : {'bus' : 'pci'}})
call assert_equal(7, g:Xgetlist({'changedtick' : 0}).changedtick)
call g:Xsetlist([{'filename' : 'F7', 'lnum' : 10, 'text' : 'L7'},
\ {'filename' : 'F7', 'lnum' : 11, 'text' : 'L11'}], 'a')
call assert_equal(8, g:Xgetlist({'changedtick' : 0}).changedtick)
call g:Xsetlist([{'filename' : 'F7', 'lnum' : 10, 'text' : 'L7'},
\ {'filename' : 'F7', 'lnum' : 11, 'text' : 'L11'}], ' ')
call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
call g:Xsetlist([{'filename' : 'F7', 'lnum' : 10, 'text' : 'L7'},
\ {'filename' : 'F7', 'lnum' : 11, 'text' : 'L11'}], 'r')
call assert_equal(2, g:Xgetlist({'changedtick' : 0}).changedtick)
call writefile(["F8:80:L80", "F8:81:L81"], "Xone")
Xfile Xone
call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
Xaddfile Xone
call assert_equal(2, g:Xgetlist({'changedtick' : 0}).changedtick)
" Test case for updating a non-current quickfix list
call g:Xsetlist([], 'f')
Xexpr "F1:1:L1"
Xexpr "F2:2:L2"
call g:Xsetlist([], 'a', {'nr' : 1, "lines" : ["F10:10:L10"]})
call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
call assert_equal(2, g:Xgetlist({'nr' : 1, 'changedtick' : 0}).changedtick)
call delete("Xone")
endfunc
func Test_qf_tick()
call Xqftick_tests('c')
call Xqftick_tests('l')
endfunc

View File

@ -771,6 +771,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1406,
/**/ /**/
1405, 1405,
/**/ /**/