patch 9.1.0785: cannot preserve error position when setting quickfix list
Problem: cannot preserve error position when setting quickfix lists
Solution: Add the 'u' action for setqflist()/setloclist() and try
to keep the closes target position (Jeremy Fleischman)
fixes: #15839
closes: #15841
Signed-off-by: Jeremy Fleischman <jeremyfleischman@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/quickfix.c b/src/quickfix.c
index 79c9fdf..1008dd5 100644
--- a/src/quickfix.c
+++ b/src/quickfix.c
@@ -190,6 +190,7 @@
static void wipe_dummy_buffer(buf_T *buf, char_u *dirname_start);
static void unload_dummy_buffer(buf_T *buf, char_u *dirname_start);
static qf_info_T *ll_get_or_alloc_list(win_T *);
+static int entry_is_closer_to_target(qfline_T *entry, qfline_T *other_entry, int target_fnum, int target_lnum, int target_col);
// Quickfix window check helper macro
#define IS_QF_WINDOW(wp) (bt_quickfix((wp)->w_buffer) && (wp)->w_llist_ref == NULL)
@@ -7500,6 +7501,62 @@
}
/*
+ * Check if `entry` is closer to the target than `other_entry`.
+ *
+ * Only returns TRUE if `entry` is definitively closer. If it's further
+ * away, or there's not enough information to tell, return FALSE.
+ */
+ static int
+entry_is_closer_to_target(
+ qfline_T *entry,
+ qfline_T *other_entry,
+ int target_fnum,
+ int target_lnum,
+ int target_col)
+{
+ // First, compare entries to target file.
+ if (!target_fnum)
+ // Without a target file, we can't know which is closer.
+ return FALSE;
+
+ int is_target_file = entry->qf_fnum && entry->qf_fnum == target_fnum;
+ int other_is_target_file = other_entry->qf_fnum && other_entry->qf_fnum == target_fnum;
+ if (!is_target_file && other_is_target_file)
+ return FALSE;
+ else if (is_target_file && !other_is_target_file)
+ return TRUE;
+
+ // Both entries are pointing at the exact same file. Now compare line
+ // numbers.
+ if (!target_lnum)
+ // Without a target line number, we can't know which is closer.
+ return FALSE;
+
+ int line_distance = entry->qf_lnum ? labs(entry->qf_lnum - target_lnum) : INT_MAX;
+ int other_line_distance = other_entry->qf_lnum ? labs(other_entry->qf_lnum - target_lnum) : INT_MAX;
+ if (line_distance > other_line_distance)
+ return FALSE;
+ else if (line_distance < other_line_distance)
+ return TRUE;
+
+ // Both entries are pointing at the exact same line number (or no line
+ // number at all). Now compare columns.
+ if (!target_col)
+ // Without a target column, we can't know which is closer.
+ return FALSE;
+
+ int column_distance = entry->qf_col ? abs(entry->qf_col - target_col) : INT_MAX;
+ int other_column_distance = other_entry->qf_col ? abs(other_entry->qf_col - target_col): INT_MAX;
+ if (column_distance > other_column_distance)
+ return FALSE;
+ else if (column_distance < other_column_distance)
+ return TRUE;
+
+ // It's a complete tie! The exact same file, line, and column.
+ return FALSE;
+}
+
+/*
* Add list of entries to quickfix/location list. Each list entry is
* a dictionary with item information.
*/
@@ -7518,21 +7575,54 @@
int retval = OK;
int valid_entry = FALSE;
+ // If there's an entry selected in the quickfix list, remember its location
+ // (file, line, column), so we can select the nearest entry in the updated
+ // quickfix list.
+ int prev_fnum = 0;
+ int prev_lnum = 0;
+ int prev_col = 0;
+ if (qfl->qf_ptr)
+ {
+ prev_fnum = qfl->qf_ptr->qf_fnum;
+ prev_lnum = qfl->qf_ptr->qf_lnum;
+ prev_col = qfl->qf_ptr->qf_col;
+ }
+
+ int select_first_entry = FALSE;
+ int select_nearest_entry = FALSE;
+
if (action == ' ' || qf_idx == qi->qf_listcount)
{
+ select_first_entry = TRUE;
// make place for a new list
qf_new_list(qi, title);
qf_idx = qi->qf_curlist;
qfl = qf_get_list(qi, qf_idx);
}
- else if (action == 'a' && !qf_list_empty(qfl))
- // Adding to existing list, use last entry.
- old_last = qfl->qf_last;
+ else if (action == 'a')
+ {
+ if (qf_list_empty(qfl))
+ // Appending to empty list, select first entry.
+ select_first_entry = TRUE;
+ else
+ // Adding to existing list, use last entry.
+ old_last = qfl->qf_last;
+ }
else if (action == 'r')
{
+ select_first_entry = TRUE;
qf_free_items(qfl);
qf_store_title(qfl, title);
}
+ else if (action == 'u')
+ {
+ select_nearest_entry = TRUE;
+ qf_free_items(qfl);
+ qf_store_title(qfl, title);
+ }
+
+ qfline_T *entry_to_select = NULL;
+ int entry_to_select_index = 0;
FOR_ALL_LIST_ITEMS(list, li)
{
@@ -7547,6 +7637,18 @@
&valid_entry);
if (retval == QF_FAIL)
break;
+
+ qfline_T *entry = qfl->qf_last;
+ if (
+ (select_first_entry && entry_to_select == NULL) ||
+ (select_nearest_entry &&
+ (entry_to_select == NULL ||
+ entry_is_closer_to_target(entry, entry_to_select, prev_fnum,
+ prev_lnum, prev_col))))
+ {
+ entry_to_select = entry;
+ entry_to_select_index = qfl->qf_count;
+ }
}
// Check if any valid error entries are added to the list.
@@ -7556,14 +7658,12 @@
// no valid entry
qfl->qf_nonevalid = TRUE;
- // If not appending to the list, set the current error to the first entry
- if (action != 'a')
- qfl->qf_ptr = qfl->qf_start;
-
- // Update the current error index if not appending to the list or if the
- // list was empty before and it is not empty now.
- if ((action != 'a' || qfl->qf_index == 0) && !qf_list_empty(qfl))
- qfl->qf_index = 1;
+ // Set the current error.
+ if (entry_to_select)
+ {
+ qfl->qf_ptr = entry_to_select;
+ qfl->qf_index = entry_to_select_index;
+ }
// Don't update the cursor in quickfix window when appending entries
qf_update_buffer(qi, old_last);
@@ -7700,7 +7800,7 @@
if (di->di_tv.v_type != VAR_LIST || di->di_tv.vval.v_list == NULL)
return FAIL;
- if (action == 'r')
+ if (action == 'r' || action == 'u')
qf_free_items(&qi->qf_lists[qf_idx]);
if (qf_init_ext(qi, qf_idx, NULL, NULL, &di->di_tv, errorformat,
FALSE, (linenr_T)0, (linenr_T)0, NULL, NULL) >= 0)
@@ -7897,8 +7997,8 @@
/*
* Populate the quickfix list with the items supplied in the list
* of dictionaries. "title" will be copied to w:quickfix_title.
- * "action" is 'a' for add, 'r' for replace. Otherwise create a new list.
- * When "what" is not NULL then only set some properties.
+ * "action" is 'a' for add, 'r' for replace, 'u' for update. Otherwise
+ * create a new list. When "what" is not NULL then only set some properties.
*/
int
set_errorlist(
@@ -8740,7 +8840,7 @@
act = tv_get_string_chk(action_arg);
if (act == NULL)
return; // type error; errmsg already given
- if ((*act == 'a' || *act == 'r' || *act == ' ' || *act == 'f') &&
+ if ((*act == 'a' || *act == 'r' || *act == 'u' || *act == ' ' || *act == 'f') &&
act[1] == NUL)
action = *act;
else