diff options
author | zeertzjq <zeertzjq@outlook.com> | 2024-10-15 18:53:59 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-15 18:53:59 +0800 |
commit | 84623dbe93777c0a8e7ddf57470ddeb2ea738069 (patch) | |
tree | 0856b7556667a3efc5c7ec6219a9a5b3f47d487d /src/nvim/quickfix.c | |
parent | e0a5c3bb581752569df4490b48cb54e7c1ab0613 (diff) | |
download | rneovim-84623dbe93777c0a8e7ddf57470ddeb2ea738069.tar.gz rneovim-84623dbe93777c0a8e7ddf57470ddeb2ea738069.tar.bz2 rneovim-84623dbe93777c0a8e7ddf57470ddeb2ea738069.zip |
vim-patch:9.1.0785: cannot preserve error position when setting quickfix list (#30820)
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: vim/vim#15839
closes: vim/vim#15841
https://github.com/vim/vim/commit/27fbf6e5e8bee5c6b61819a5e82a0b50b265f0b0
Co-authored-by: Jeremy Fleischman <jeremyfleischman@gmail.com>
Diffstat (limited to 'src/nvim/quickfix.c')
-rw-r--r-- | src/nvim/quickfix.c | 130 |
1 files changed, 111 insertions, 19 deletions
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index ddf2a7247f..5bd81ce469 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -6462,6 +6462,64 @@ static int qf_add_entry_from_dict(qf_list_T *qfl, dict_T *d, bool first_entry, b return status; } +/// 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 bool 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; + } + + bool is_target_file = entry->qf_fnum && entry->qf_fnum == target_fnum; + bool 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 + ? abs(entry->qf_lnum - target_lnum) : INT_MAX; + int other_line_distance = other_entry->qf_lnum + ? abs(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. static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, char *title, int action) @@ -6471,19 +6529,48 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, char *title, int retval = OK; bool 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; + } + + bool select_first_entry = false; + bool 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; + TV_LIST_ITER_CONST(list, li, { if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT) { continue; // Skip non-dict items. @@ -6498,6 +6585,16 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, char *title, 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. @@ -6507,16 +6604,10 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, char *title, 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 @@ -6632,7 +6723,7 @@ static int qf_setprop_items_from_lines(qf_info_T *qi, int qf_idx, const dict_T * 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, @@ -6789,10 +6880,11 @@ static void qf_free_stack(win_T *wp, qf_info_T *qi) } } -// 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. +/// 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, 'u' for update. Otherwise +/// create a new list. +/// When "what" is not NULL then only set some properties. int set_errorlist(win_T *wp, list_T *list, int action, char *title, dict_T *what) { qf_info_T *qi = &ql_info; @@ -7433,7 +7525,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) return; } const char *const act = tv_get_string_chk(action_arg); - if ((*act == 'a' || *act == 'r' || *act == ' ' || *act == 'f') + if ((*act == 'a' || *act == 'r' || *act == 'u' || *act == ' ' || *act == 'f') && act[1] == NUL) { action = *act; } else { |