diff options
-rw-r--r-- | runtime/doc/eval.txt | 4 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 81 | ||||
-rw-r--r-- | src/nvim/testdir/test_quickfix.vim | 67 |
3 files changed, 134 insertions, 18 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 7c1e524b66..ae62498d35 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4328,6 +4328,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 @@ -4344,6 +4345,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) @@ -6892,6 +6894,8 @@ setqflist({list} [, {action}[, {what}]]) *setqflist()* 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/nvim/quickfix.c b/src/nvim/quickfix.c index 1d368bf87a..224e43008d 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -76,18 +76,25 @@ struct qfline_S { */ #define LISTCOUNT 10 +/// Quickfix/Location list definition +/// +/// 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_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 - typval_T *qf_ctx; // context set by setqflist/setloclist + 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 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 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 { /* * Count of references to this list. Used only for location lists. @@ -1185,6 +1192,9 @@ qf_init_end: static void qf_store_title(qf_info_T *qi, int qf_idx, char_u *title) { + xfree(qi->qf_lists[qf_idx].qf_title); + qi->qf_lists[qf_idx].qf_title = NULL; + if (title != NULL) { size_t len = STRLEN(title) + 1; char_u *p = xmallocz(len); @@ -2396,8 +2406,9 @@ void qf_history(exarg_T *eap) } } -/// Free all the entries in the error list "idx". -static void qf_free(qf_info_T *qi, int 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_items(qf_info_T *qi, int idx) { qfline_T *qfp; qfline_T *qfpnext; @@ -2421,12 +2432,9 @@ static void qf_free(qf_info_T *qi, int idx) qi->qf_lists[idx].qf_start = qfpnext; qi->qf_lists[idx].qf_count--; } - xfree(qi->qf_lists[idx].qf_title); + qi->qf_lists[idx].qf_start = NULL; qi->qf_lists[idx].qf_ptr = NULL; - qi->qf_lists[idx].qf_title = NULL; - tv_free(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; @@ -2442,6 +2450,18 @@ static void qf_free(qf_info_T *qi, int idx) qi->qf_multiscan = false; } +/// 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); + + xfree(qi->qf_lists[idx].qf_title); + qi->qf_lists[idx].qf_title = NULL; + tv_free(qi->qf_lists[idx].qf_ctx); + qi->qf_lists[idx].qf_ctx = NULL; +} + /* * qf_mark_adjust: adjust marks */ @@ -4150,6 +4170,10 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) if (tv_dict_find(what, S_LEN("context")) != NULL) { flags |= QF_GETLIST_CONTEXT; } + + if (tv_dict_find(what, S_LEN("items")) != NULL) { + flags |= QF_GETLIST_ITEMS; + } } if (flags & QF_GETLIST_TITLE) { @@ -4168,6 +4192,11 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) status = tv_dict_add_nr(retdict, S_LEN("winid"), win->handle); } } + if ((status == OK) && (flags & QF_GETLIST_ITEMS)) { + list_T *l = tv_list_alloc(); + (void)get_errorlist(wp, qf_idx, l); + tv_dict_add_list(retdict, S_LEN("items"), l); + } if ((status == OK) && (flags & QF_GETLIST_CONTEXT)) { if (qi->qf_lists[qf_idx].qf_ctx != NULL) { @@ -4204,7 +4233,7 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, // Adding to existing list, use last entry. old_last = qi->qf_lists[qf_idx].qf_last; } else if (action == 'r') { - qf_free(qi, qf_idx); + qf_free_items(qi, qf_idx); qf_store_title(qi, qf_idx, title); } @@ -4312,17 +4341,24 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action) if (di->di_tv.vval.v_number != 0) { qf_idx = (int)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 && strequal((const char *)di->di_tv.vval.v_string, "$") && qi->qf_listcount > 0) { qf_idx = qi->qf_listcount - 1; + newlist = false; } else { return FAIL; } - newlist = false; // use the specified list } if (newlist) { @@ -4341,6 +4377,15 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action) retval = OK; } } + if ((di = tv_dict_find(what, S_LEN("items"))) != 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); + xfree(title_save); + } + } if ((di = tv_dict_find(what, S_LEN("context"))) != NULL) { tv_free(qi->qf_lists[qf_idx].qf_ctx); diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 828176e6be..95a7d29089 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -1817,6 +1817,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() |