diff options
author | Bram Moolenaar <Bram@vim.org> | 2019-05-03 21:56:35 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2019-05-03 21:56:35 +0200 |
commit | 3ff33114d70fc0f7e9c3187c5fec9028f6499cf3 (patch) | |
tree | 62a9dedf831bf246b93c1a8c3715669c77f6040a /src/quickfix.c | |
parent | 12e91862c14a1af44b537d478e8a5021893044fe (diff) |
patch 8.1.1256: cannot navigate through errors relative to the cursorv8.1.1256
Problem: Cannot navigate through errors relative to the cursor.
Solution: Add :cabove, :cbelow, :labove and :lbelow. (Yegappan Lakshmanan,
closes #4316)
Diffstat (limited to 'src/quickfix.c')
-rw-r--r-- | src/quickfix.c | 311 |
1 files changed, 308 insertions, 3 deletions
diff --git a/src/quickfix.c b/src/quickfix.c index f909343879..e8c7829616 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -177,6 +177,7 @@ static buf_T *load_dummy_buffer(char_u *fname, char_u *dirname_start, char_u *re 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 char_u *e_no_more_items = (char_u *)N_("E553: No more items"); // Quickfix window check helper macro #define IS_QF_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref == NULL) @@ -1494,6 +1495,16 @@ qf_list_empty(qf_list_T *qfl) } /* + * Returns TRUE if the specified quickfix/location list is not empty and + * has valid entries. + */ + static int +qf_list_has_valid_entries(qf_list_T *qfl) +{ + return !qf_list_empty(qfl) && !qfl->qf_nonevalid; +} + +/* * Return a pointer to a list in the specified quickfix stack */ static qf_list_T * @@ -2700,7 +2711,6 @@ get_nth_valid_entry( int qf_idx = qfl->qf_index; qfline_T *prev_qf_ptr; int prev_index; - static char_u *e_no_more_items = (char_u *)N_("E553: No more items"); char_u *err = e_no_more_items; while (errornr--) @@ -4886,7 +4896,7 @@ qf_get_cur_valid_idx(exarg_T *eap) qfp = qfl->qf_start; // check if the list has valid errors - if (qfl->qf_count <= 0 || qfl->qf_nonevalid) + if (!qf_list_has_valid_entries(qfl)) return 1; for (i = 1; i <= qfl->qf_index && qfp!= NULL; i++, qfp = qfp->qf_next) @@ -4924,7 +4934,7 @@ qf_get_nth_valid_entry(qf_list_T *qfl, int n, int fdo) int prev_fnum = 0; // check if the list has valid errors - if (qfl->qf_count <= 0 || qfl->qf_nonevalid) + if (!qf_list_has_valid_entries(qfl)) return 1; eidx = 0; @@ -5045,6 +5055,301 @@ ex_cnext(exarg_T *eap) } /* + * Find the first entry in the quickfix list 'qfl' from buffer 'bnr'. + * The index of the entry is stored in 'errornr'. + * Returns NULL if an entry is not found. + */ + static qfline_T * +qf_find_first_entry_in_buf(qf_list_T *qfl, int bnr, int *errornr) +{ + qfline_T *qfp = NULL; + int idx = 0; + + // Find the first entry in this file + FOR_ALL_QFL_ITEMS(qfl, qfp, idx) + if (qfp->qf_fnum == bnr) + break; + + *errornr = idx; + return qfp; +} + +/* + * Find the first quickfix entry on the same line as 'entry'. Updates 'errornr' + * with the error number for the first entry. Assumes the entries are sorted in + * the quickfix list by line number. + */ + static qfline_T * +qf_find_first_entry_on_line(qfline_T *entry, int *errornr) +{ + while (!got_int + && entry->qf_prev != NULL + && entry->qf_fnum == entry->qf_prev->qf_fnum + && entry->qf_lnum == entry->qf_prev->qf_lnum) + { + entry = entry->qf_prev; + --*errornr; + } + + return entry; +} + +/* + * Find the last quickfix entry on the same line as 'entry'. Updates 'errornr' + * with the error number for the last entry. Assumes the entries are sorted in + * the quickfix list by line number. + */ + static qfline_T * +qf_find_last_entry_on_line(qfline_T *entry, int *errornr) +{ + while (!got_int && + entry->qf_next != NULL + && entry->qf_fnum == entry->qf_next->qf_fnum + && entry->qf_lnum == entry->qf_next->qf_lnum) + { + entry = entry->qf_next; + ++*errornr; + } + + return entry; +} + +/* + * Find the first quickfix entry below line 'lnum' in buffer 'bnr'. + * 'qfp' points to the very first entry in the buffer and 'errornr' is the + * index of the very first entry in the quickfix list. + * Returns NULL if an entry is not found after 'lnum'. + */ + static qfline_T * +qf_find_entry_on_next_line( + int bnr, + linenr_T lnum, + qfline_T *qfp, + int *errornr) +{ + if (qfp->qf_lnum > lnum) + // First entry is after line 'lnum' + return qfp; + + // Find the entry just before or at the line 'lnum' + while (qfp->qf_next != NULL + && qfp->qf_next->qf_fnum == bnr + && qfp->qf_next->qf_lnum <= lnum) + { + qfp = qfp->qf_next; + ++*errornr; + } + + if (qfp->qf_next == NULL || qfp->qf_next->qf_fnum != bnr) + // No entries found after 'lnum' + return NULL; + + // Use the entry just after line 'lnum' + qfp = qfp->qf_next; + ++*errornr; + + return qfp; +} + +/* + * Find the first quickfix entry before line 'lnum' in buffer 'bnr'. + * 'qfp' points to the very first entry in the buffer and 'errornr' is the + * index of the very first entry in the quickfix list. + * Returns NULL if an entry is not found before 'lnum'. + */ + static qfline_T * +qf_find_entry_on_prev_line( + int bnr, + linenr_T lnum, + qfline_T *qfp, + int *errornr) +{ + // Find the entry just before the line 'lnum' + while (qfp->qf_next != NULL + && qfp->qf_next->qf_fnum == bnr + && qfp->qf_next->qf_lnum < lnum) + { + qfp = qfp->qf_next; + ++*errornr; + } + + if (qfp->qf_lnum >= lnum) // entry is after 'lnum' + return NULL; + + // If multiple entries are on the same line, then use the first entry + qfp = qf_find_first_entry_on_line(qfp, errornr); + + return qfp; +} + +/* + * Find a quickfix entry in 'qfl' closest to line 'lnum' in buffer 'bnr' in + * the direction 'dir'. + */ + static qfline_T * +qf_find_closest_entry( + qf_list_T *qfl, + int bnr, + linenr_T lnum, + int dir, + int *errornr) +{ + qfline_T *qfp; + + *errornr = 0; + + // Find the first entry in this file + qfp = qf_find_first_entry_in_buf(qfl, bnr, errornr); + if (qfp == NULL) + return NULL; // no entry in this file + + if (dir == FORWARD) + qfp = qf_find_entry_on_next_line(bnr, lnum, qfp, errornr); + else + qfp = qf_find_entry_on_prev_line(bnr, lnum, qfp, errornr); + + return qfp; +} + +/* + * Get the nth quickfix entry below the specified entry treating multiple + * entries on a single line as one. Searches forward in the list. + */ + static qfline_T * +qf_get_nth_below_entry(qfline_T *entry, int *errornr, int n) +{ + while (n-- > 0 && !got_int) + { + qfline_T *first_entry = entry; + int first_errornr = *errornr; + + // Treat all the entries on the same line in this file as one + entry = qf_find_last_entry_on_line(entry, errornr); + + if (entry->qf_next == NULL + || entry->qf_next->qf_fnum != entry->qf_fnum) + { + // If multiple entries are on the same line, then use the first + // entry + entry = first_entry; + *errornr = first_errornr; + break; + } + + entry = entry->qf_next; + ++*errornr; + } + + return entry; +} + +/* + * Get the nth quickfix entry above the specified entry treating multiple + * entries on a single line as one. Searches backwards in the list. + */ + static qfline_T * +qf_get_nth_above_entry(qfline_T *entry, int *errornr, int n) +{ + while (n-- > 0 && !got_int) + { + if (entry->qf_prev == NULL + || entry->qf_prev->qf_fnum != entry->qf_fnum) + break; + + entry = entry->qf_prev; + --*errornr; + + // If multiple entries are on the same line, then use the first entry + entry = qf_find_first_entry_on_line(entry, errornr); + } + + return entry; +} + +/* + * Find the n'th quickfix entry adjacent to line 'lnum' in buffer 'bnr' in the + * specified direction. + * Returns the error number in the quickfix list or 0 if an entry is not found. + */ + static int +qf_find_nth_adj_entry(qf_list_T *qfl, int bnr, linenr_T lnum, int n, int dir) +{ + qfline_T *adj_entry; + int errornr; + + // Find an entry closest to the specified line + adj_entry = qf_find_closest_entry(qfl, bnr, lnum, dir, &errornr); + if (adj_entry == NULL) + return 0; + + if (--n > 0) + { + // Go to the n'th entry in the current buffer + if (dir == FORWARD) + adj_entry = qf_get_nth_below_entry(adj_entry, &errornr, n); + else + adj_entry = qf_get_nth_above_entry(adj_entry, &errornr, n); + } + + return errornr; +} + +/* + * Jump to a quickfix entry in the current file nearest to the current line. + * ":cabove", ":cbelow", ":labove" and ":lbelow" commands + */ + void +ex_cbelow(exarg_T *eap) +{ + qf_info_T *qi; + qf_list_T *qfl; + int dir; + int buf_has_flag; + int errornr = 0; + + if (eap->addr_count > 0 && eap->line2 <= 0) + { + emsg(_(e_invrange)); + return; + } + + // Check whether the current buffer has any quickfix entries + if (eap->cmdidx == CMD_cabove || eap->cmdidx == CMD_cbelow) + buf_has_flag = BUF_HAS_QF_ENTRY; + else + buf_has_flag = BUF_HAS_LL_ENTRY; + if (!(curbuf->b_has_qf_entry & buf_has_flag)) + { + emsg(_(e_quickfix)); + return; + } + + if ((qi = qf_cmd_get_stack(eap, TRUE)) == NULL) + return; + + qfl = qf_get_curlist(qi); + // check if the list has valid errors + if (!qf_list_has_valid_entries(qfl)) + { + emsg(_(e_quickfix)); + return; + } + + if (eap->cmdidx == CMD_cbelow || eap->cmdidx == CMD_lbelow) + dir = FORWARD; + else + dir = BACKWARD; + + errornr = qf_find_nth_adj_entry(qfl, curbuf->b_fnum, curwin->w_cursor.lnum, + eap->addr_count > 0 ? eap->line2 : 0, dir); + + if (errornr > 0) + qf_jump(qi, 0, errornr, FALSE); + else + emsg(_(e_no_more_items)); +} + +/* * Return the autocmd name for the :cfile Ex commands */ static char_u * |