diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2018-09-09 11:29:18 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-09-09 11:29:18 +0200 |
commit | a8bd4de28a83d31f854684490c715be0df4044d6 (patch) | |
tree | 8bc8375653f9585d769b9b10a732dbe0494a622c /src | |
parent | 50eadfe2e991a0bb5fe0f98377d91702f0a20492 (diff) | |
parent | 40e894f59570a6192aabbe4fe34c456bd00ae871 (diff) | |
download | rneovim-a8bd4de28a83d31f854684490c715be0df4044d6.tar.gz rneovim-a8bd4de28a83d31f854684490c715be0df4044d6.tar.bz2 rneovim-a8bd4de28a83d31f854684490c715be0df4044d6.zip |
Merge #8969 from janlazo/vim-8.0.1006
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/eval.c | 2 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 189 | ||||
-rw-r--r-- | src/nvim/testdir/test_quickfix.vim | 123 |
3 files changed, 242 insertions, 72 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d750a47588..309c0fc062 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9943,7 +9943,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, if (what_arg->v_type == VAR_UNKNOWN) { tv_list_alloc_ret(rettv, kListLenMayKnow); if (is_qf || wp != NULL) { - (void)get_errorlist(wp, -1, rettv->vval.v_list); + (void)get_errorlist(NULL, wp, -1, rettv->vval.v_list); } } else { tv_dict_alloc_ret(rettv); diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 263b8b3a77..ba2f2ba969 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -82,6 +82,7 @@ struct qfline_S { /// 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 { + unsigned qf_id; ///< Unique identifier for this list 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 @@ -116,7 +117,8 @@ struct qf_info_S { qf_list_T qf_lists[LISTCOUNT]; }; -static qf_info_T ql_info; /* global quickfix list */ +static qf_info_T ql_info; // global quickfix list +static unsigned last_qf_id = 0; // Last Used quickfix list id #define FMT_PATTERNS 10 /* maximum number of % recognized */ @@ -1224,6 +1226,7 @@ static void qf_new_list(qf_info_T *qi, char_u *qf_title) qi->qf_curlist = qi->qf_listcount++; memset(&qi->qf_lists[qi->qf_curlist], 0, (size_t)(sizeof(qf_list_T))); qf_store_title(qi, qi->qf_curlist, qf_title); + qi->qf_lists[qi->qf_curlist].qf_id = ++last_qf_id; } /* @@ -1467,6 +1470,9 @@ void copy_loclist(win_T *from, win_T *to) to_qfl->qf_index = from_qfl->qf_index; /* current index in the list */ + // Assign a new ID for the location list + to_qfl->qf_id = ++last_qf_id; + /* When no valid entries are present in the list, qf_ptr points to * the first item in the list */ if (to_qfl->qf_nonevalid) { @@ -2414,7 +2420,7 @@ static void qf_free_items(qf_info_T *qi, int idx) while (qfl->qf_count && qfl->qf_start != NULL) { qfp = qfl->qf_start; qfpnext = qfp->qf_next; - if (qfl->qf_title != NULL && !stop) { + if (!stop) { xfree(qfp->qf_text); stop = (qfp == qfpnext); xfree(qfp->qf_pattern); @@ -2458,6 +2464,7 @@ static void qf_free(qf_info_T *qi, int idx) qfl->qf_title = NULL; tv_free(qfl->qf_ctx); qfl->qf_ctx = NULL; + qfl->qf_id = 0; } /* @@ -4031,18 +4038,22 @@ static void unload_dummy_buffer(buf_T *buf, char_u *dirname_start) /// Add each quickfix error to list "list" as a dictionary. /// If qf_idx is -1, use the current list. Otherwise, use the specified list. -int get_errorlist(win_T *wp, int qf_idx, list_T *list) +int get_errorlist(const qf_info_T *qi_arg, win_T *wp, int qf_idx, list_T *list) { - qf_info_T *qi = &ql_info; + const qf_info_T *qi = qi_arg; char_u buf[2]; qfline_T *qfp; int i; int bufnum; - if (wp != NULL) { - qi = GET_LOC_LIST(wp); - if (qi == NULL) - return FAIL; + if (qi == NULL) { + qi = &ql_info; + if (wp != NULL) { + qi = GET_LOC_LIST(wp); + if (qi == NULL) { + return FAIL; + } + } } if (qf_idx == -1) { @@ -4106,9 +4117,48 @@ enum { QF_GETLIST_NR = 0x4, QF_GETLIST_WINID = 0x8, QF_GETLIST_CONTEXT = 0x10, + QF_GETLIST_ID = 0x20, QF_GETLIST_ALL = 0xFF }; +// Parse text from 'di' and return the quickfix list items +static int qf_get_list_from_lines(dict_T *what, dictitem_T *di, dict_T *retdict) +{ + int status = FAIL; + char_u *errorformat = p_efm; + dictitem_T *efm_di; + + // Only a List value is supported + if (di->di_tv.v_type == VAR_LIST && di->di_tv.vval.v_list != NULL) { + // If errorformat is supplied then use it, otherwise use the 'efm' + // option setting + if ((efm_di = tv_dict_find(what, S_LEN("efm"))) != NULL) { + if (efm_di->di_tv.v_type != VAR_STRING + || efm_di->di_tv.vval.v_string == NULL) { + return FAIL; + } + errorformat = efm_di->di_tv.vval.v_string; + } + + list_T *l = tv_list_alloc(kListLenMayKnow); + qf_info_T *qi = xmalloc(sizeof(*qi)); + memset(qi, 0, sizeof(*qi)); + qi->qf_refcount++; + + if (qf_init_ext(qi, 0, NULL, NULL, &di->di_tv, errorformat, + true, (linenr_T)0, (linenr_T)0, NULL, NULL) > 0) { + (void)get_errorlist(qi, NULL, 0, l); + qf_free(qi, 0); + } + xfree(qi); + + tv_dict_add_list(retdict, S_LEN("items"), l); + status = OK; + } + + return status; +} + /// Return quickfix/location list details (title) as a /// dictionary. 'what' contains the details to return. If 'list_idx' is -1, /// then current list is used. Otherwise the specified list is used. @@ -4117,17 +4167,22 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) qf_info_T *qi = &ql_info; dictitem_T *di; + if ((di = tv_dict_find(what, S_LEN("lines"))) != NULL) { + return qf_get_list_from_lines(what, di, retdict); + } + if (wp != NULL) { qi = GET_LOC_LIST(wp); - if (qi == NULL) { - // If querying for the size of the location list, return 0 - if (((di = tv_dict_find(what, S_LEN("nr"))) != NULL) - && (di->di_tv.v_type == VAR_STRING) - && strequal((const char *)di->di_tv.vval.v_string, "$")) { - return tv_dict_add_nr(retdict, S_LEN("nr"), 0); - } - return FAIL; + } + // List is not present or is empty + if (qi == NULL || qi->qf_listcount == 0) { + // If querying for the size of the list, return 0 + if (((di = tv_dict_find(what, S_LEN("nr"))) != NULL) + && (di->di_tv.v_type == VAR_STRING) + && (STRCMP(di->di_tv.vval.v_string, "$") == 0)) { + return tv_dict_add_nr(retdict, S_LEN("nr"), 0); } + return FAIL; } int status = OK; @@ -4143,44 +4198,51 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) if (qf_idx < 0 || qf_idx >= qi->qf_listcount) { return FAIL; } - } else if (qi->qf_listcount == 0) { // stack is empty - return FAIL; } - flags |= QF_GETLIST_NR; } else if (di->di_tv.v_type == VAR_STRING && strequal((const char *)di->di_tv.vval.v_string, "$")) { // 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; + qf_idx = qi->qf_listcount - 1; } else { return FAIL; } + flags |= QF_GETLIST_NR; } - if (qf_idx != -1) { - if (tv_dict_find(what, S_LEN("all")) != NULL) { - flags |= QF_GETLIST_ALL; - } - - if (tv_dict_find(what, S_LEN("title")) != NULL) { - flags |= QF_GETLIST_TITLE; - } - - if (tv_dict_find(what, S_LEN("winid")) != NULL) { - flags |= QF_GETLIST_WINID; - } - - if (tv_dict_find(what, S_LEN("context")) != NULL) { - flags |= QF_GETLIST_CONTEXT; + if ((di = tv_dict_find(what, S_LEN("id"))) != NULL) { + // Look for a list with the specified id + if (di->di_tv.v_type == VAR_NUMBER) { + // For zero, use the current list or the list specifed by 'nr' + if (di->di_tv.vval.v_number != 0) { + for (qf_idx = 0; qf_idx < qi->qf_listcount; qf_idx++) { + if (qi->qf_lists[qf_idx].qf_id == di->di_tv.vval.v_number) { + break; + } + } + if (qf_idx == qi->qf_listcount) { + return FAIL; // List not found + } + } + flags |= QF_GETLIST_ID; + } else { + return FAIL; } + } - if (tv_dict_find(what, S_LEN("items")) != NULL) { - flags |= QF_GETLIST_ITEMS; - } + if (tv_dict_find(what, S_LEN("all")) != NULL) { + flags |= QF_GETLIST_ALL; + } + if (tv_dict_find(what, S_LEN("title")) != NULL) { + flags |= QF_GETLIST_TITLE; + } + if (tv_dict_find(what, S_LEN("winid")) != NULL) { + flags |= QF_GETLIST_WINID; + } + 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) { @@ -4201,7 +4263,7 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) } if ((status == OK) && (flags & QF_GETLIST_ITEMS)) { list_T *l = tv_list_alloc(kListLenMayKnow); - (void)get_errorlist(wp, qf_idx, l); + (void)get_errorlist(qi, NULL, qf_idx, l); tv_dict_add_list(retdict, S_LEN("items"), l); } @@ -4218,6 +4280,10 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) } } + if ((status == OK) && (flags & QF_GETLIST_ID)) { + status = tv_dict_add_nr(retdict, S_LEN("id"), qi->qf_lists[qf_idx].qf_id); + } + return status; } @@ -4336,6 +4402,7 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action, dictitem_T *di; int retval = FAIL; int newlist = false; + char_u *errorformat = p_efm; if (action == ' ' || qi->qf_curlist == qi->qf_listcount) { newlist = true; @@ -4374,6 +4441,22 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action, } } + if (!newlist && (di = tv_dict_find(what, S_LEN("id"))) != NULL) { + // Use the quickfix/location list with the specified id + if (di->di_tv.v_type == VAR_NUMBER) { + for (qf_idx = 0; qf_idx < qi->qf_listcount; qf_idx++) { + if (qi->qf_lists[qf_idx].qf_id == di->di_tv.vval.v_number) { + break; + } + } + if (qf_idx == qi->qf_listcount) { + return FAIL; // List not found + } + } else { + return FAIL; + } + } + if (newlist) { qi->qf_curlist = qf_idx; qf_new_list(qi, title); @@ -4401,16 +4484,20 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action, } } - if ((di = tv_dict_find(what, S_LEN("text"))) != NULL) { - // Only string and list values are supported - if ((di->di_tv.v_type == VAR_STRING - && di->di_tv.vval.v_string != NULL) - || (di->di_tv.v_type == VAR_LIST - && di->di_tv.vval.v_list != NULL)) { + if ((di = tv_dict_find(what, S_LEN("efm"))) != NULL) { + if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL) { + return FAIL; + } + errorformat = di->di_tv.vval.v_string; + } + + if ((di = tv_dict_find(what, S_LEN("lines"))) != NULL) { + // Only a List value is supported + if (di->di_tv.v_type == VAR_LIST && di->di_tv.vval.v_list != NULL) { if (action == 'r') { qf_free_items(qi, qf_idx); } - if (qf_init_ext(qi, qf_idx, NULL, NULL, &di->di_tv, p_efm, + if (qf_init_ext(qi, qf_idx, NULL, NULL, &di->di_tv, errorformat, false, (linenr_T)0, (linenr_T)0, NULL, NULL) > 0) { retval = OK; } diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index f603f46d63..7a53db7605 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -1879,8 +1879,9 @@ func Xproperty_tests(cchar) 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 + let save_id = l1.id + let l1.id=l2.id + let l2.id=save_id call g:Xsetlist([], 'r', l1) call g:Xsetlist([], 'r', l2) let newl1=g:Xgetlist({'nr':1,'all':1}) @@ -2280,27 +2281,39 @@ func Xsetexpr_tests(cchar) call s:setup_commands(a:cchar) let t = ["File1:10:Line10", "File1:20:Line20"] - call g:Xsetlist([], ' ', {'text' : t}) - call g:Xsetlist([], 'a', {'text' : "File1:30:Line30"}) + call g:Xsetlist([], ' ', {'lines' : t}) + call g:Xsetlist([], 'a', {'lines' : ["File1:30:Line30"]}) let l = g:Xgetlist() call assert_equal(3, len(l)) call assert_equal(20, l[1].lnum) call assert_equal('Line30', l[2].text) - call g:Xsetlist([], 'r', {'text' : "File2:5:Line5"}) + call g:Xsetlist([], 'r', {'lines' : ["File2:5:Line5"]}) let l = g:Xgetlist() call assert_equal(1, len(l)) call assert_equal('Line5', l[0].text) - call assert_equal(-1, g:Xsetlist([], 'a', {'text' : 10})) + call assert_equal(-1, g:Xsetlist([], 'a', {'lines' : 10})) + call assert_equal(-1, g:Xsetlist([], 'a', {'lines' : "F1:10:L10"})) call g:Xsetlist([], 'f') " Add entries to multiple lists - call g:Xsetlist([], 'a', {'nr' : 1, 'text' : ["File1:10:Line10"]}) - call g:Xsetlist([], 'a', {'nr' : 2, 'text' : ["File2:20:Line20"]}) - call g:Xsetlist([], 'a', {'nr' : 1, 'text' : ["File1:15:Line15"]}) - call g:Xsetlist([], 'a', {'nr' : 2, 'text' : ["File2:25:Line25"]}) + call g:Xsetlist([], 'a', {'nr' : 1, 'lines' : ["File1:10:Line10"]}) + call g:Xsetlist([], 'a', {'nr' : 2, 'lines' : ["File2:20:Line20"]}) + call g:Xsetlist([], 'a', {'nr' : 1, 'lines' : ["File1:15:Line15"]}) + call g:Xsetlist([], 'a', {'nr' : 2, 'lines' : ["File2:25:Line25"]}) call assert_equal('Line15', g:Xgetlist({'nr':1, 'items':1}).items[1].text) call assert_equal('Line25', g:Xgetlist({'nr':2, 'items':1}).items[1].text) + + " Adding entries using a custom efm + set efm& + call g:Xsetlist([], ' ', {'efm' : '%f#%l#%m', + \ 'lines' : ["F1#10#L10", "F2#20#L20"]}) + call assert_equal(20, g:Xgetlist({'items':1}).items[1].lnum) + call g:Xsetlist([], 'a', {'efm' : '%f#%l#%m', 'lines' : ["F3:30:L30"]}) + call assert_equal('F3:30:L30', g:Xgetlist({'items':1}).items[2].text) + call assert_equal(20, g:Xgetlist({'items':1}).items[1].lnum) + call assert_equal(-1, g:Xsetlist([], 'a', {'efm' : [], + \ 'lines' : ['F1:10:L10']})) endfunc func Test_setexpr() @@ -2315,16 +2328,16 @@ func Xmultidirstack_tests(cchar) call g:Xsetlist([], 'f') Xexpr "" | Xexpr "" - call g:Xsetlist([], 'a', {'nr' : 1, 'text' : "Entering dir 'Xone/a'"}) - call g:Xsetlist([], 'a', {'nr' : 2, 'text' : "Entering dir 'Xtwo/a'"}) - call g:Xsetlist([], 'a', {'nr' : 1, 'text' : "one.txt:3:one one one"}) - call g:Xsetlist([], 'a', {'nr' : 2, 'text' : "two.txt:5:two two two"}) + call g:Xsetlist([], 'a', {'nr' : 1, 'lines' : ["Entering dir 'Xone/a'"]}) + call g:Xsetlist([], 'a', {'nr' : 2, 'lines' : ["Entering dir 'Xtwo/a'"]}) + call g:Xsetlist([], 'a', {'nr' : 1, 'lines' : ["one.txt:3:one one one"]}) + call g:Xsetlist([], 'a', {'nr' : 2, 'lines' : ["two.txt:5:two two two"]}) let l1 = g:Xgetlist({'nr':1, 'items':1}) let l2 = g:Xgetlist({'nr':2, 'items':1}) - call assert_equal('Xone/a/one.txt', bufname(l1.items[1].bufnr)) + call assert_equal(expand('Xone/a/one.txt'), bufname(l1.items[1].bufnr)) call assert_equal(3, l1.items[1].lnum) - call assert_equal('Xtwo/a/two.txt', bufname(l2.items[1].bufnr)) + call assert_equal(expand('Xtwo/a/two.txt'), bufname(l2.items[1].bufnr)) call assert_equal(5, l2.items[1].lnum) endfunc @@ -2352,10 +2365,10 @@ func Xmultifilestack_tests(cchar) call g:Xsetlist([], 'f') Xexpr "" | Xexpr "" - call g:Xsetlist([], 'a', {'nr' : 1, 'text' : "[one.txt]"}) - call g:Xsetlist([], 'a', {'nr' : 2, 'text' : "[two.txt]"}) - call g:Xsetlist([], 'a', {'nr' : 1, 'text' : "(3,5) one one one"}) - call g:Xsetlist([], 'a', {'nr' : 2, 'text' : "(5,9) two two two"}) + call g:Xsetlist([], 'a', {'nr' : 1, 'lines' : ["[one.txt]"]}) + call g:Xsetlist([], 'a', {'nr' : 2, 'lines' : ["[two.txt]"]}) + call g:Xsetlist([], 'a', {'nr' : 1, 'lines' : ["(3,5) one one one"]}) + call g:Xsetlist([], 'a', {'nr' : 2, 'lines' : ["(5,9) two two two"]}) let l1 = g:Xgetlist({'nr':1, 'items':1}) let l2 = g:Xgetlist({'nr':2, 'items':1}) @@ -2501,3 +2514,73 @@ func Test_add_qf() call XaddQf_tests('c') call XaddQf_tests('l') endfunc + +" Test for getting the quickfix list items from some text without modifying +" the quickfix stack +func XgetListFromLines(cchar) + call s:setup_commands(a:cchar) + call g:Xsetlist([], 'f') + + let l = g:Xgetlist({'lines' : ["File2:20:Line20", "File2:30:Line30"]}).items + call assert_equal(2, len(l)) + call assert_equal(30, l[1].lnum) + + call assert_equal({}, g:Xgetlist({'lines' : 10})) + call assert_equal({}, g:Xgetlist({'lines' : 'File1:10:Line10'})) + call assert_equal([], g:Xgetlist({'lines' : []}).items) + call assert_equal([], g:Xgetlist({'lines' : [10, 20]}).items) + + " Parse text using a custom efm + set efm& + let l = g:Xgetlist({'lines':['File3#30#Line30'], 'efm' : '%f#%l#%m'}).items + call assert_equal('Line30', l[0].text) + let l = g:Xgetlist({'lines':['File3:30:Line30'], 'efm' : '%f-%l-%m'}).items + call assert_equal('File3:30:Line30', l[0].text) + let l = g:Xgetlist({'lines':['File3:30:Line30'], 'efm' : [1,2]}) + call assert_equal({}, l) + call assert_fails("call g:Xgetlist({'lines':['abc'], 'efm':'%2'})", 'E376:') + call assert_fails("call g:Xgetlist({'lines':['abc'], 'efm':''})", 'E378:') + + " Make sure that the quickfix stack is not modified + call assert_equal(0, g:Xgetlist({'nr' : '$'}).nr) +endfunc + +func Test_get_list_from_lines() + call XgetListFromLines('c') + call XgetListFromLines('l') +endfunc + +" Tests for the quickfix list id +func Xqfid_tests(cchar) + call s:setup_commands(a:cchar) + + call g:Xsetlist([], 'f') + call assert_equal({}, g:Xgetlist({'id':0})) + Xexpr '' + let start_id = g:Xgetlist({'id' : 0}).id + Xexpr '' | Xexpr '' + Xolder + call assert_equal(start_id, g:Xgetlist({'id':0, 'nr':1}).id) + call assert_equal(start_id + 1, g:Xgetlist({'id':0, 'nr':0}).id) + call assert_equal(start_id + 2, g:Xgetlist({'id':0, 'nr':'$'}).id) + call assert_equal({}, g:Xgetlist({'id':0, 'nr':99})) + call assert_equal(2, g:Xgetlist({'id':start_id + 1, 'nr':0}).nr) + call assert_equal({}, g:Xgetlist({'id':99, 'nr':0})) + call assert_equal({}, g:Xgetlist({'id':"abc", 'nr':0})) + + call g:Xsetlist([], 'a', {'id':start_id, 'context':[1,2]}) + call assert_equal([1,2], g:Xgetlist({'nr':1, 'context':1}).context) + call g:Xsetlist([], 'a', {'id':start_id+1, 'lines':['F1:10:L10']}) + call assert_equal('L10', g:Xgetlist({'nr':2, 'items':1}).items[0].text) + call assert_equal(-1, g:Xsetlist([], 'a', {'id':999, 'title':'Vim'})) + call assert_equal(-1, g:Xsetlist([], 'a', {'id':'abc', 'title':'Vim'})) + + let qfid = g:Xgetlist({'id':0, 'nr':0}) + call g:Xsetlist([], 'f') + call assert_equal({}, g:Xgetlist({'id':qfid, 'nr':0})) +endfunc + +func Test_qf_id() + call Xqfid_tests('c') + call Xqfid_tests('l') +endfunc |