# HG changeset patch # User Christian Brabandt # Date 1513623606 -3600 # Node ID 6479dadcf21405c6450aafab3a412b4affc8b98d # Parent 60b09974895d43bf61b2cd142dfc5a882fe95309 patch 8.0.1406: difficult to track changes to a quickfix list commit https://github.com/vim/vim/commit/b254af312d1696b12367085acfbe41a41b7f1ea5 Author: Bram Moolenaar Date: Mon Dec 18 19:48:58 2017 +0100 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 https://github.com/vim/vim/issues/2460) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4674,6 +4674,8 @@ getqflist([{what}]) *getqflist()* If the optional {what} dictionary argument is supplied, then returns only the items listed in {what} as a dictionary. The 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()| efm errorformat to use when parsing "lines". If not present, then the 'errorformat' option @@ -4707,6 +4709,8 @@ getqflist([{what}]) *getqflist()* "items" with the list of 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()|. If not present, set to "". id quickfix list ID |quickfix-ID|. If not diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -64,6 +64,14 @@ When a window with a location list is sp location list. When there are no longer any references to a location list, 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 similar to the quickfix commands, replacing the 'c' prefix in the quickfix command with 'l'. diff --git a/src/quickfix.c b/src/quickfix.c --- a/src/quickfix.c +++ b/src/quickfix.c @@ -76,6 +76,7 @@ typedef struct qf_list_S int qf_multiline; int qf_multiignore; int qf_multiscan; + long qf_changedtick; } qf_list_T; /* @@ -1668,6 +1669,7 @@ copy_loclist(win_T *from, win_T *to) /* Assign a new ID for the location list */ 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 * the first item in the list */ @@ -2965,6 +2967,7 @@ qf_free(qf_info_T *qi, int idx) free_tv(qfl->qf_ctx); qfl->qf_ctx = NULL; qfl->qf_id = 0; + qfl->qf_changedtick = 0L; } /* @@ -3604,6 +3607,12 @@ qf_fill_buffer(qf_info_T *qi, buf_T *buf 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". */ @@ -3713,6 +3722,8 @@ ex_make(exarg_T *eap) *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 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 && 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 if (au_name != NULL) apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, NULL, FALSE, curbuf); #endif 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 */ } } @@ -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_ptr = qi->qf_lists[qi->qf_curlist].qf_start; qi->qf_lists[qi->qf_curlist].qf_index = 1; + qf_list_changed(qi, qi->qf_curlist); qf_update_buffer(qi, NULL); @@ -4780,7 +4794,8 @@ enum { QF_GETLIST_ID = 0x20, QF_GETLIST_IDX = 0x40, 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 *wha if (dict_find(what, (char_u *)"size", -1) != NULL) flags |= QF_GETLIST_SIZE; + if (dict_find(what, (char_u *)"changedtick", -1) != NULL) + flags |= QF_GETLIST_TICK; + if (qi != NULL && qi->qf_listcount != 0) { qf_idx = qi->qf_curlist; /* default is the current list */ @@ -4963,6 +4981,8 @@ qf_get_properties(win_T *wp, dict_T *wha status = dict_add_nr_str(retdict, "idx", 0L, NULL); if ((status == OK) && (flags & QF_GETLIST_SIZE)) 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; } @@ -5035,6 +5055,10 @@ qf_get_properties(win_T *wp, dict_T *wha status = dict_add_nr_str(retdict, "size", 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; } @@ -5304,6 +5328,9 @@ qf_set_properties(qf_info_T *qi, dict_T retval = OK; } + if (retval == OK) + qf_list_changed(qi, qf_idx); + return retval; } @@ -5407,7 +5434,11 @@ set_errorlist( else if (what != NULL) retval = qf_set_properties(qi, what, action, title); else + { retval = qf_add_entries(qi, qi->qf_curlist, list, title, action); + if (retval == OK) + qf_list_changed(qi, qi->qf_curlist); + } return retval; } @@ -5540,6 +5571,8 @@ ex_cbuffer(exarg_T *eap) && eap->cmdidx != CMD_laddbuffer), eap->line1, eap->line2, qf_title, NULL); + if (res >= 0) + qf_list_changed(qi, qi->qf_curlist); #ifdef FEAT_AUTOCMD if (au_name != NULL) apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, @@ -5609,6 +5642,8 @@ ex_cexpr(exarg_T *eap) && eap->cmdidx != CMD_laddexpr), (linenr_T)0, (linenr_T)0, *eap->cmdlinep, NULL); + if (res >= 0) + qf_list_changed(qi, qi->qf_curlist); #ifdef FEAT_AUTOCMD if (au_name != NULL) apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, @@ -5829,6 +5864,7 @@ ex_helpgrep(exarg_T *eap) /* Darn, some plugin changed the value. */ free_string_option(save_cpo); + qf_list_changed(qi, qi->qf_curlist); qf_update_buffer(qi, NULL); #ifdef FEAT_AUTOCMD diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -2132,6 +2132,8 @@ func Test_Autocmd() call delete('Xtest') call delete('Xempty') + au! QuickFixCmdPre + au! QuickFixCmdPost endfunc 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('', g:Xgetlist({'title' : 0}).title) 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 Xexpr "" @@ -2908,6 +2911,7 @@ func Xgetlist_empty_tests(cchar) call assert_equal(0, g:Xgetlist({'size' : 0}).size) call assert_notequal('', g:Xgetlist({'title' : 0}).title) 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 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('', g:Xgetlist({'id' : qfid, 'title' : 0}).title) 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 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('', g:Xgetlist({'nr' : 5, 'title' : 0}).title) 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 func Test_getqflist() call Xgetlist_empty_tests('c') call Xgetlist_empty_tests('l') 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 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -772,6 +772,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1406, +/**/ 1405, /**/ 1404,