]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.1688: cannot store custom data in quickfix list v9.0.1688
authorTom Praschan <13141438+tom-anders@users.noreply.github.com>
Fri, 11 Aug 2023 21:26:12 +0000 (23:26 +0200)
committerChristian Brabandt <cb@256bit.org>
Fri, 11 Aug 2023 21:26:12 +0000 (23:26 +0200)
Problem: cannot store custom data in quickfix list
Solution: add `user_data` field for the quickfix list

closes: #11818

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Tom Praschan <13141438+tom-anders@users.noreply.github.com>
runtime/doc/builtin.txt
src/quickfix.c
src/testdir/test_quickfix.vim
src/version.c

index e74b39740e81bc27cec66c000426a266563ba554..979f46f2dc79a658dd3c371892e0abc74b12784a 100644 (file)
@@ -4002,6 +4002,9 @@ getqflist([{what}])                                       *getqflist()*
                        text    description of the error
                        type    type of the error, 'E', '1', etc.
                        valid   |TRUE|: recognized error message
+                       user_data
+                               custom data associated with the item, can be
+                               any type.
 
                When there is no error list or it's empty, an empty list is
                returned. Quickfix list entries with a non-existing buffer
@@ -8298,6 +8301,8 @@ setqflist({list} [, {action} [, {what}]])         *setqflist()*
                    text        description of the error
                    type        single-character error type, 'E', 'W', etc.
                    valid       recognized error message
+                   user_data   custom data associated with the item, can be
+                               any type.
 
                The "col", "vcol", "nr", "type" and "text" entries are
                optional.  Either "lnum" or "pattern" entry can be used to
index 4501d11f157be7e123eb09e005b25e89b2e6e7a7..6aa828b35238c8a0022f1ff0c2fa3b89d47a685e 100644 (file)
@@ -43,6 +43,7 @@ struct qfline_S
     char_u     qf_cleared;     // set to TRUE if line has been deleted
     char_u     qf_type;        // type of the error (mostly 'E'); 1 for
                                // :helpgrep
+    typval_T   qf_user_data;   // custom user data associated with this item
     char_u     qf_valid;       // valid error message detected
 };
 
@@ -82,6 +83,7 @@ typedef struct qf_list_S
     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
+    int                qf_has_user_data; // TRUE if at least one item has user_data attached
     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
@@ -168,7 +170,7 @@ static efm_T        *fmt_start = NULL; // cached across qf_parse_line() calls
 static callback_T qftf_cb;
 
 static void    qf_new_list(qf_info_T *qi, char_u *qf_title);
-static int     qf_add_entry(qf_list_T *qfl, char_u *dir, char_u *fname, char_u *module, int bufnum, char_u *mesg, long lnum, long end_lnum, int col, int end_col, int vis_col, char_u *pattern, int nr, int type, int valid);
+static int     qf_add_entry(qf_list_T *qfl, char_u *dir, char_u *fname, char_u *module, int bufnum, char_u *mesg, long lnum, long end_lnum, int col, int end_col, int vis_col, char_u *pattern, int nr, int type, typval_T *user_data, int valid);
 static void    qf_free(qf_list_T *qfl);
 static char_u  *qf_types(int, int);
 static int     qf_get_fnum(qf_list_T *qfl, char_u *, char_u *);
@@ -951,6 +953,7 @@ typedef struct {
     char_u     *pattern;
     int                enr;
     int                type;
+    typval_T   *user_data;
     int                valid;
 } qffields_T;
 
@@ -1720,6 +1723,7 @@ qf_init_process_nextline(
                fields->pattern,
                fields->enr,
                fields->type,
+               fields->user_data,
                fields->valid);
 }
 
@@ -1965,6 +1969,7 @@ qf_new_list(qf_info_T *qi, char_u *qf_title)
     qf_store_title(qfl, qf_title);
     qfl->qfl_type = qi->qfl_type;
     qfl->qf_id = ++last_qf_id;
+    qfl->qf_has_user_data = FALSE;
 }
 
 /*
@@ -2145,6 +2150,7 @@ qf_add_entry(
     char_u     *pattern,       // search pattern
     int                nr,             // error number
     int                type,           // type character
+    typval_T   *user_data,     // custom user data or NULL
     int                valid)          // valid entry
 {
     qfline_T   *qfp;
@@ -2173,6 +2179,13 @@ qf_add_entry(
     qfp->qf_col = col;
     qfp->qf_end_col = end_col;
     qfp->qf_viscol = vis_col;
+    if (user_data == NULL || user_data->v_type == VAR_UNKNOWN)
+        qfp->qf_user_data.v_type = VAR_UNKNOWN;
+    else
+    {
+       copy_tv(user_data, &qfp->qf_user_data);
+       qfl->qf_has_user_data = TRUE;
+    }
     if (pattern == NULL || *pattern == NUL)
        qfp->qf_pattern = NULL;
     else if ((qfp->qf_pattern = vim_strsave(pattern)) == NULL)
@@ -2335,6 +2348,7 @@ copy_loclist_entries(qf_list_T *from_qfl, qf_list_T *to_qfl)
                    from_qfp->qf_pattern,
                    from_qfp->qf_nr,
                    0,
+                   &from_qfp->qf_user_data,
                    from_qfp->qf_valid) == QF_FAIL)
            return FAIL;
 
@@ -2360,6 +2374,7 @@ copy_loclist(qf_list_T *from_qfl, qf_list_T *to_qfl)
     // Some of the fields are populated by qf_add_entry()
     to_qfl->qfl_type = from_qfl->qfl_type;
     to_qfl->qf_nonevalid = from_qfl->qf_nonevalid;
+    to_qfl->qf_has_user_data = from_qfl->qf_has_user_data;
     to_qfl->qf_count = 0;
     to_qfl->qf_index = 0;
     to_qfl->qf_start = NULL;
@@ -3957,6 +3972,7 @@ qf_free_items(qf_list_T *qfl)
            vim_free(qfp->qf_module);
            vim_free(qfp->qf_text);
            vim_free(qfp->qf_pattern);
+           clear_tv(&qfp->qf_user_data);
            stop = (qfp == qfpnext);
            vim_free(qfp);
            if (stop)
@@ -6085,6 +6101,7 @@ vgr_match_buflines(
                            NULL,       // search pattern
                            0,          // nr
                            0,          // type
+                           NULL,       // user_data
                            TRUE        // valid
                            ) == QF_FAIL)
                {
@@ -6131,6 +6148,7 @@ vgr_match_buflines(
                            NULL,       // search pattern
                            0,          // nr
                            0,          // type
+                           NULL,       // user_data
                            TRUE        // valid
                            ) == QF_FAIL)
                {
@@ -6771,6 +6789,8 @@ get_qfline_items(qfline_T *qfp, list_T *list)
            || dict_add_string(dict, "pattern", qfp->qf_pattern) == FAIL
            || dict_add_string(dict, "text", qfp->qf_text) == FAIL
            || dict_add_string(dict, "type", buf) == FAIL
+           || (qfp->qf_user_data.v_type != VAR_UNKNOWN
+               && dict_add_tv(dict, "user_data", &qfp->qf_user_data) == FAIL )
            || dict_add_number(dict, "valid", (long)qfp->qf_valid) == FAIL)
        return FAIL;
 
@@ -7305,6 +7325,9 @@ qf_add_entry_from_dict(
     text = dict_get_string(d, "text", TRUE);
     if (text == NULL)
        text = vim_strsave((char_u *)"");
+    typval_T user_data;
+    user_data.v_type = VAR_UNKNOWN;
+    dict_get_tv(d, "user_data", &user_data);
 
     valid = TRUE;
     if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL))
@@ -7341,6 +7364,7 @@ qf_add_entry_from_dict(
                        pattern,        // search pattern
                        nr,
                        type == NULL ? NUL : *type,
+                       &user_data,
                        valid);
 
     vim_free(filename);
@@ -7348,6 +7372,7 @@ qf_add_entry_from_dict(
     vim_free(pattern);
     vim_free(text);
     vim_free(type);
+    clear_tv(&user_data);
 
     if (valid)
        *valid_entry = TRUE;
@@ -7805,6 +7830,27 @@ set_errorlist(
     return retval;
 }
 
+static int mark_quickfix_user_data(qf_info_T *qi, int copyID)
+{
+    int abort = FALSE;
+    for (int i = 0; i < LISTCOUNT && !abort; ++i)
+    {
+       qf_list_T *qfl = &qi->qf_lists[i];
+       if (!qfl->qf_has_user_data)
+           continue;
+       qfline_T *qfp;
+       int j;
+       FOR_ALL_QFL_ITEMS(qfl, qfp, j)
+       {
+           typval_T* user_data = &qfp->qf_user_data;
+           if (user_data != NULL && user_data->v_type != VAR_NUMBER
+               && user_data->v_type != VAR_STRING && user_data->v_type != VAR_FLOAT)
+               abort = abort || set_ref_in_item(user_data, copyID, NULL, NULL);
+       }
+    }
+    return abort;
+}
+
 /*
  * Mark the quickfix context and callback function as in use for all the lists
  * in a quickfix stack.
@@ -7846,6 +7892,10 @@ set_ref_in_quickfix(int copyID)
     if (abort)
        return abort;
 
+    abort = mark_quickfix_user_data(&ql_info, copyID);
+    if (abort)
+       return abort;
+
     abort = set_ref_in_callback(&qftf_cb, copyID);
     if (abort)
        return abort;
@@ -7857,6 +7907,10 @@ set_ref_in_quickfix(int copyID)
            abort = mark_quickfix_ctx(win->w_llist, copyID);
            if (abort)
                return abort;
+
+           abort = mark_quickfix_user_data(win->w_llist, copyID);
+           if (abort)
+               return abort;
        }
        if (IS_LL_WINDOW(win) && (win->w_llist_ref->qf_refcount == 1))
        {
@@ -8225,6 +8279,7 @@ hgr_search_file(
                        NULL,   // search pattern
                        0,      // nr
                        1,      // type
+                       NULL,   // user_data
                        TRUE    // valid
                        ) == QF_FAIL)
            {
index b7d290275ba6b38f2d2eb1d97926dd8f4c8f20b6..33ce700f0be585a0cf995065ade551ced64ff6c7 100644 (file)
@@ -1808,13 +1808,23 @@ func SetXlistTests(cchar, bnum)
   call s:setup_commands(a:cchar)
 
   call g:Xsetlist([{'bufnr': a:bnum, 'lnum': 1},
-             \  {'bufnr': a:bnum, 'lnum': 2, 'end_lnum': 3, 'col': 4, 'end_col': 5}])
+        \  {'bufnr': a:bnum, 'lnum': 2, 'end_lnum': 3, 'col': 4, 'end_col': 5, 'user_data': {'6': [7, 8]}}])
   let l = g:Xgetlist()
   call assert_equal(2, len(l))
   call assert_equal(2, l[1].lnum)
   call assert_equal(3, l[1].end_lnum)
   call assert_equal(4, l[1].col)
   call assert_equal(5, l[1].end_col)
+  call assert_equal({'6': [7, 8]}, l[1].user_data)
+
+  " Test that user_data is garbage collected
+  call g:Xsetlist([{'user_data': ['high', 5]},
+        \  {'user_data': {'this': [7, 'eight'], 'is': ['a', 'dictionary']}}])
+  call test_garbagecollect_now()
+  let l = g:Xgetlist()
+  call assert_equal(2, len(l))
+  call assert_equal(['high', 5], l[0].user_data)
+  call assert_equal({'this': [7, 'eight'], 'is': ['a', 'dictionary']}, l[1].user_data)
 
   Xnext
   call g:Xsetlist([{'bufnr': a:bnum, 'lnum': 3}], 'a')
index dc054dfd7a046529afab1b9c7e85823effe233cd..99f0013ee119709b19b80908fb9accfa736ac81f 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1688,
 /**/
     1687,
 /**/