# HG changeset patch # User Christian Brabandt # Date 1498160703 -7200 # Node ID f5add45f98488daa918fe9f6cb35964692b25cf2 # Parent edd5aab186706a82b329ccf5e3803a5d426ebcd7 patch 8.0.0657: cannot get and set quickfix list items commit https://github.com/vim/vim/commit/6a8958db259d4444da6e6956e54a6513c1af8860 Author: Bram Moolenaar Date: Thu Jun 22 21:33:20 2017 +0200 patch 8.0.0657: cannot get and set quickfix list items Problem: Cannot get and set quickfix list items. Solution: Add the "items" argument to getqflist() and setqflist(). (Yegappan Lakshmanan) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4586,6 +4586,7 @@ getqflist([{what}]) *getqflist()* returns only the items listed in {what} as a dictionary. The following string items are supported in {what}: context get the context stored with |setqflist()| + items quickfix list entries nr get information for this quickfix list; zero means the current quickfix list and '$' means the last quickfix list @@ -4602,6 +4603,7 @@ getqflist([{what}]) *getqflist()* The returned dictionary contains the following entries: context context information stored with |setqflist()| + items quickfix list entries nr quickfix list number title quickfix list title text winid quickfix |window-ID| (if opened) @@ -6998,6 +7000,8 @@ setqflist({list} [, {action}[, {what}]]) argument is ignored. The following items can be specified in {what}: context any Vim type can be stored as a context + items list of quickfix entries. Same as the {list} + argument. nr list number in the quickfix stack; zero means the current quickfix list and '$' means the last quickfix list diff --git a/src/quickfix.c b/src/quickfix.c --- a/src/quickfix.c +++ b/src/quickfix.c @@ -47,19 +47,32 @@ struct qfline_S */ #define LISTCOUNT 10 +/* + * Quickfix/Location list definition + * Contains a list of entries (qfline_T). qf_start points to the first entry + * and qf_last points to the last entry. qf_count contains the list size. + * + * Usually the list contains one or more entries. But an empty list can be + * created using setqflist()/setloclist() with a title and/or user context + * information and entries can be added later using setqflist()/setloclist(). + */ typedef struct qf_list_S { qfline_T *qf_start; /* pointer to the first error */ qfline_T *qf_last; /* pointer to the last error */ qfline_T *qf_ptr; /* pointer to the current error */ - int qf_count; /* number of errors (0 means no error list) */ + int qf_count; /* number of errors (0 means empty list) */ int qf_index; /* current index in the error list */ int qf_nonevalid; /* TRUE if not a single valid entry found */ char_u *qf_title; /* title derived from the command that created - * the error list */ + * the error list or set by setqflist */ typval_T *qf_ctx; /* context set by setqflist/setloclist */ } qf_list_T; +/* + * Quickfix/Location list stack definition + * Contains a list of quickfix/location lists (qf_list_T) + */ struct qf_info_S { /* @@ -1347,6 +1360,9 @@ qf_init_end: static void qf_store_title(qf_info_T *qi, int qf_idx, char_u *title) { + vim_free(qi->qf_lists[qf_idx].qf_title); + qi->qf_lists[qf_idx].qf_title = NULL; + if (title != NULL) { char_u *p = alloc((int)STRLEN(title) + 2); @@ -2735,10 +2751,11 @@ qf_history(exarg_T *eap) } /* - * Free all the entries in the error list "idx". + * Free all the entries in the error list "idx". Note that other information + * associated with the list like context and title are not freed. */ static void -qf_free(qf_info_T *qi, int idx) +qf_free_items(qf_info_T *qi, int idx) { qfline_T *qfp; qfline_T *qfpnext; @@ -2763,10 +2780,7 @@ qf_free(qf_info_T *qi, int idx) qi->qf_lists[idx].qf_start = qfpnext; --qi->qf_lists[idx].qf_count; } - vim_free(qi->qf_lists[idx].qf_title); - qi->qf_lists[idx].qf_title = NULL; - free_tv(qi->qf_lists[idx].qf_ctx); - qi->qf_lists[idx].qf_ctx = NULL; + qi->qf_lists[idx].qf_index = 0; qi->qf_lists[idx].qf_start = NULL; qi->qf_lists[idx].qf_last = NULL; @@ -2783,6 +2797,21 @@ qf_free(qf_info_T *qi, int idx) } /* + * Free error list "idx". Frees all the entries in the quickfix list, + * associated context information and the title. + */ + static void +qf_free(qf_info_T *qi, int idx) +{ + qf_free_items(qi, idx); + + vim_free(qi->qf_lists[idx].qf_title); + qi->qf_lists[idx].qf_title = NULL; + free_tv(qi->qf_lists[idx].qf_ctx); + qi->qf_lists[idx].qf_ctx = NULL; +} + +/* * qf_mark_adjust: adjust marks */ void @@ -4698,13 +4727,11 @@ get_errorlist_properties(win_T *wp, dict } else if ((di->di_tv.v_type == VAR_STRING) && (STRCMP(di->di_tv.vval.v_string, "$") == 0)) { - { - /* Get the last quickfix list number */ - if (qi->qf_listcount > 0) - qf_idx = qi->qf_listcount - 1; - else - qf_idx = -1; /* Quickfix stack is empty */ - } + /* Get the last quickfix list number */ + if (qi->qf_listcount > 0) + qf_idx = qi->qf_listcount - 1; + else + qf_idx = -1; /* Quickfix stack is empty */ flags |= QF_GETLIST_NR; } else @@ -4724,6 +4751,9 @@ get_errorlist_properties(win_T *wp, dict if (dict_find(what, (char_u *)"context", -1) != NULL) flags |= QF_GETLIST_CONTEXT; + + if (dict_find(what, (char_u *)"items", -1) != NULL) + flags |= QF_GETLIST_ITEMS; } if (flags & QF_GETLIST_TITLE) @@ -4743,6 +4773,15 @@ get_errorlist_properties(win_T *wp, dict if (win != NULL) status = dict_add_nr_str(retdict, "winid", win->w_id, NULL); } + if ((status == OK) && (flags & QF_GETLIST_ITEMS)) + { + list_T *l = list_alloc(); + if (l != NULL) + { + (void)get_errorlist(wp, qf_idx, l); + dict_add_list(retdict, "items", l); + } + } if ((status == OK) && (flags & QF_GETLIST_CONTEXT)) { @@ -4802,7 +4841,7 @@ qf_add_entries( #endif else if (action == 'r') { - qf_free(qi, qf_idx); + qf_free_items(qi, qf_idx); qf_store_title(qi, qf_idx, title); } @@ -4915,15 +4954,27 @@ qf_set_properties(qf_info_T *qi, dict_T /* for zero use the current list */ if (di->di_tv.vval.v_number != 0) qf_idx = di->di_tv.vval.v_number - 1; - if (qf_idx < 0 || qf_idx >= qi->qf_listcount) + + if ((action == ' ' || action == 'a') && + qf_idx == qi->qf_listcount) + /* + * When creating a new list, accept qf_idx pointing to the next + * non-available list + */ + newlist = TRUE; + else if (qf_idx < 0 || qf_idx >= qi->qf_listcount) return FAIL; + else + newlist = FALSE; /* use the specified list */ } else if (di->di_tv.v_type == VAR_STRING && STRCMP(di->di_tv.vval.v_string, "$") == 0 && qi->qf_listcount > 0) + { qf_idx = qi->qf_listcount - 1; + newlist = FALSE; + } else return FAIL; - newlist = FALSE; /* use the specified list */ } if (newlist) @@ -4944,6 +4995,17 @@ qf_set_properties(qf_info_T *qi, dict_T retval = OK; } } + if ((di = dict_find(what, (char_u *)"items", -1)) != NULL) + { + if (di->di_tv.v_type == VAR_LIST) + { + char_u *title_save = vim_strsave(qi->qf_lists[qf_idx].qf_title); + + retval = qf_add_entries(qi, qf_idx, di->di_tv.vval.v_list, + title_save, action == ' ' ? 'a' : action); + vim_free(title_save); + } + } if ((di = dict_find(what, (char_u *)"context", -1)) != NULL) { 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 @@ -1835,6 +1835,73 @@ func Xproperty_tests(cchar) call test_garbagecollect_now() let m = g:Xgetlist({'context' : 1}) call assert_equal(["red", "blue", "green"], m.context) + + " Test for setting/getting items + Xexpr "" + let qfprev = g:Xgetlist({'nr':0}) + call g:Xsetlist([], ' ', {'title':'Green', + \ 'items' : [{'filename':'F1', 'lnum':10}]}) + let qfcur = g:Xgetlist({'nr':0}) + call assert_true(qfcur.nr == qfprev.nr + 1) + let l = g:Xgetlist({'items':1}) + call assert_equal('F1', bufname(l.items[0].bufnr)) + call assert_equal(10, l.items[0].lnum) + call g:Xsetlist([], 'a', {'items' : [{'filename':'F2', 'lnum':20}, + \ {'filename':'F2', 'lnum':30}]}) + let l = g:Xgetlist({'items':1}) + call assert_equal('F2', bufname(l.items[2].bufnr)) + call assert_equal(30, l.items[2].lnum) + call g:Xsetlist([], 'r', {'items' : [{'filename':'F3', 'lnum':40}]}) + let l = g:Xgetlist({'items':1}) + call assert_equal('F3', bufname(l.items[0].bufnr)) + call assert_equal(40, l.items[0].lnum) + call g:Xsetlist([], 'r', {'items' : []}) + let l = g:Xgetlist({'items':1}) + call assert_equal(0, len(l.items)) + + " Save and restore the quickfix stack + call g:Xsetlist([], 'f') + call assert_equal(0, g:Xgetlist({'nr':'$'}).nr) + Xexpr "File1:10:Line1" + Xexpr "File2:20:Line2" + Xexpr "File3:30:Line3" + let last_qf = g:Xgetlist({'nr':'$'}).nr + call assert_equal(3, last_qf) + let qstack = [] + for i in range(1, last_qf) + let qstack = add(qstack, g:Xgetlist({'nr':i, 'all':1})) + endfor + call g:Xsetlist([], 'f') + for i in range(len(qstack)) + call g:Xsetlist([], ' ', qstack[i]) + endfor + call assert_equal(3, g:Xgetlist({'nr':'$'}).nr) + call assert_equal(10, g:Xgetlist({'nr':1, 'items':1}).items[0].lnum) + call assert_equal(20, g:Xgetlist({'nr':2, 'items':1}).items[0].lnum) + call assert_equal(30, g:Xgetlist({'nr':3, 'items':1}).items[0].lnum) + call g:Xsetlist([], 'f') + + " Swap two quickfix lists + Xexpr "File1:10:Line10" + Xexpr "File2:20:Line20" + Xexpr "File3:30:Line30" + call g:Xsetlist([], 'r', {'nr':1,'title':'Colors','context':['Colors']}) + call g:Xsetlist([], 'r', {'nr':2,'title':'Fruits','context':['Fruits']}) + let l1=g:Xgetlist({'nr':1,'all':1}) + let l2=g:Xgetlist({'nr':2,'all':1}) + let l1.nr=2 + let l2.nr=1 + call g:Xsetlist([], 'r', l1) + call g:Xsetlist([], 'r', l2) + let newl1=g:Xgetlist({'nr':1,'all':1}) + let newl2=g:Xgetlist({'nr':2,'all':1}) + call assert_equal(':Fruits', newl1.title) + call assert_equal(['Fruits'], newl1.context) + call assert_equal('Line20', newl1.items[0].text) + call assert_equal(':Colors', newl2.title) + call assert_equal(['Colors'], newl2.context) + call assert_equal('Line10', newl2.items[0].text) + call g:Xsetlist([], 'f') endfunc func Test_qf_property() diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -765,6 +765,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 657, +/**/ 656, /**/ 655,