To: vim_dev@googlegroups.com Subject: Patch 8.0.1406 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.0.1406 Problem: Difficult to track changes to a quickfix list. Solution: Add a "changedtick" value. (Yegappan Lakshmanan, closes #2460) Files: runtime/doc/eval.txt, runtime/doc/quickfix.txt, src/quickfix.c, src/testdir/test_quickfix.vim *** ../vim-8.0.1405/runtime/doc/eval.txt 2017-12-16 18:26:56.618992550 +0100 --- runtime/doc/eval.txt 2017-12-18 19:45:00.179246715 +0100 *************** *** 4662,4667 **** --- 4674,4681 ---- 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 *************** *** 4695,4700 **** --- 4709,4716 ---- "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 *** ../vim-8.0.1405/runtime/doc/quickfix.txt 2017-12-10 15:25:12.506523247 +0100 --- runtime/doc/quickfix.txt 2017-12-18 19:45:00.179246715 +0100 *************** *** 1,4 **** ! *quickfix.txt* For Vim version 8.0. Last change: 2017 Sep 13 VIM REFERENCE MANUAL by Bram Moolenaar --- 1,4 ---- ! *quickfix.txt* For Vim version 8.0. Last change: 2017 Dec 13 VIM REFERENCE MANUAL by Bram Moolenaar *************** *** 64,69 **** --- 64,77 ---- 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'. *************** *** 363,369 **** The |setqflist()| and the |setloclist()| functions can be used to associate a context with a quickfix and a location list respectively. The |getqflist()| and the |getloclist()| functions can be used to retrieve the context of a ! quickifx and a location list respectively. This is useful for a Vim plugin dealing with multiple quickfix/location lists. Examples: > --- 371,377 ---- The |setqflist()| and the |setloclist()| functions can be used to associate a context with a quickfix and a location list respectively. The |getqflist()| and the |getloclist()| functions can be used to retrieve the context of a ! quickfix and a location list respectively. This is useful for a Vim plugin dealing with multiple quickfix/location lists. Examples: > *************** *** 376,388 **** echo getloclist(2, {'id' : qfid, 'context' : 1}) < *quickfix-parse* ! You can parse a list of lines using 'erroformat' without creating or modifying ! a quickfix list using the |getqflist()| function. Examples: > echo getqflist({'lines' : ["F1:10:Line10", "F2:20:Line20"]}) echo getqflist({'lines' : systemlist('grep -Hn quickfix *')}) This returns a dictionary where the 'items' key contains the list of quickfix entries parsed from lines. The following shows how to use a custom ! 'errorformat' to parse the lines without modifying the 'erroformat' option: > echo getqflist({'efm' : '%f#%l#%m', 'lines' : ['F1#10#Line']}) < --- 384,396 ---- echo getloclist(2, {'id' : qfid, 'context' : 1}) < *quickfix-parse* ! You can parse a list of lines using 'errorformat' without creating or ! modifying a quickfix list using the |getqflist()| function. Examples: > echo getqflist({'lines' : ["F1:10:Line10", "F2:20:Line20"]}) echo getqflist({'lines' : systemlist('grep -Hn quickfix *')}) This returns a dictionary where the 'items' key contains the list of quickfix entries parsed from lines. The following shows how to use a custom ! 'errorformat' to parse the lines without modifying the 'errorformat' option: > echo getqflist({'efm' : '%f#%l#%m', 'lines' : ['F1#10#Line']}) < *** ../vim-8.0.1405/src/quickfix.c 2017-12-18 15:32:55.274906330 +0100 --- src/quickfix.c 2017-12-18 19:45:00.179246715 +0100 *************** *** 76,81 **** --- 76,82 ---- int qf_multiline; int qf_multiignore; int qf_multiscan; + long qf_changedtick; } qf_list_T; /* *************** *** 1668,1673 **** --- 1669,1675 ---- /* 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,2970 **** --- 2967,2973 ---- free_tv(qfl->qf_ctx); qfl->qf_ctx = NULL; qfl->qf_id = 0; + qfl->qf_changedtick = 0L; } /* *************** *** 3604,3609 **** --- 3607,3618 ---- 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,3718 **** --- 3722,3729 ---- *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,4118 **** */ res = qf_init(wp, p_ef, p_efm, (eap->cmdidx != CMD_caddfile && eap->cmdidx != CMD_laddfile), *eap->cmdlinep, enc); #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 */ } } --- 4116,4131 ---- */ 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)) { qf_jump(qi, 0, 0, eap->forceit); /* display first error */ } } *************** *** 4469,4474 **** --- 4482,4488 ---- 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,4786 **** QF_GETLIST_ID = 0x20, QF_GETLIST_IDX = 0x40, QF_GETLIST_SIZE = 0x80, ! QF_GETLIST_ALL = 0xFF }; /* --- 4794,4801 ---- QF_GETLIST_ID = 0x20, QF_GETLIST_IDX = 0x40, QF_GETLIST_SIZE = 0x80, ! QF_GETLIST_TICK = 0x100, ! QF_GETLIST_ALL = 0x1FF }; /* *************** *** 4896,4901 **** --- 4911,4919 ---- 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,4968 **** --- 4981,4988 ---- 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,5040 **** --- 5055,5064 ---- 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,5309 **** --- 5328,5336 ---- retval = OK; } + if (retval == OK) + qf_list_changed(qi, qf_idx); + return retval; } *************** *** 5407,5413 **** --- 5434,5444 ---- 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,5545 **** --- 5571,5578 ---- && 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,5614 **** --- 5642,5649 ---- && 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,5834 **** --- 5864,5870 ---- /* 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 *** ../vim-8.0.1405/src/testdir/test_quickfix.vim 2017-12-12 22:45:07.141808185 +0100 --- src/testdir/test_quickfix.vim 2017-12-18 19:45:00.179246715 +0100 *************** *** 2132,2137 **** --- 2132,2139 ---- call delete('Xtest') call delete('Xempty') + au! QuickFixCmdPre + au! QuickFixCmdPost endfunc func Test_Autocmd_Exception() *************** *** 2896,2902 **** 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})) " Empty quickfix list Xexpr "" --- 2898,2905 ---- 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(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,2913 **** --- 2911,2917 ---- 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,2927 **** 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})) " Non-existing quickfix list number call assert_equal('', g:Xgetlist({'nr' : 5, 'context' : 0}).context) --- 2925,2932 ---- 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(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,2941 **** 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})) endfunc func Test_getqflist() call Xgetlist_empty_tests('c') call Xgetlist_empty_tests('l') endfunc --- 2937,3005 ---- 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(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 *** ../vim-8.0.1405/src/version.c 2017-12-18 18:14:43.455768463 +0100 --- src/version.c 2017-12-18 19:45:40.418952831 +0100 *************** *** 773,774 **** --- 773,776 ---- { /* Add new patch number below this line */ + /**/ + 1406, /**/ -- hundred-and-one symptoms of being an internet addict: 124. You begin conversations with, "Who is your internet service provider?" /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///