changeset 13062:6479dadcf214 v8.0.1406

patch 8.0.1406: difficult to track changes to a quickfix list commit https://github.com/vim/vim/commit/b254af312d1696b12367085acfbe41a41b7f1ea5 Author: Bram Moolenaar <Bram@vim.org> 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)
author Christian Brabandt <cb@256bit.org>
date Mon, 18 Dec 2017 20:00:06 +0100
parents 60b09974895d
children c1e813afafdd
files runtime/doc/eval.txt runtime/doc/quickfix.txt src/quickfix.c src/testdir/test_quickfix.vim src/version.c
diffstat 5 files changed, 120 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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'.
--- 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
--- 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
--- 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,