diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2025-02-05 23:09:29 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2025-02-05 23:09:29 +0000 |
commit | d5f194ce780c95821a855aca3c19426576d28ae0 (patch) | |
tree | d45f461b19f9118ad2bb1f440a7a08973ad18832 /src/nvim/popupmenu.c | |
parent | c5d770d311841ea5230426cc4c868e8db27300a8 (diff) | |
parent | 44740e561fc93afe3ebecfd3618bda2d2abeafb0 (diff) | |
download | rneovim-rahm.tar.gz rneovim-rahm.tar.bz2 rneovim-rahm.zip |
Diffstat (limited to 'src/nvim/popupmenu.c')
-rw-r--r-- | src/nvim/popupmenu.c | 368 |
1 files changed, 159 insertions, 209 deletions
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index 7df6a1a5d7..d1c6f647fd 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -4,22 +4,19 @@ #include <assert.h> #include <stdbool.h> +#include <stdint.h> #include <string.h> #include "nvim/api/buffer.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/api/vim.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" -#include "nvim/buffer_updates.h" -#include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" #include "nvim/drawscreen.h" -#include "nvim/edit.h" #include "nvim/errors.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" @@ -27,6 +24,7 @@ #include "nvim/extmark.h" #include "nvim/extmark_defs.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" #include "nvim/gettext_defs.h" #include "nvim/globals.h" @@ -35,6 +33,7 @@ #include "nvim/highlight_defs.h" #include "nvim/insexpand.h" #include "nvim/keycodes.h" +#include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" @@ -46,7 +45,6 @@ #include "nvim/option.h" #include "nvim/option_defs.h" #include "nvim/option_vars.h" -#include "nvim/optionstr.h" #include "nvim/plines.h" #include "nvim/popupmenu.h" #include "nvim/pos_defs.h" @@ -234,12 +232,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i } // Figure out the size and position of the pum. - if (size < PUM_DEF_HEIGHT) { - pum_height = size; - } else { - pum_height = PUM_DEF_HEIGHT; - } - + pum_height = MIN(size, PUM_DEF_HEIGHT); if (p_ph > 0 && pum_height > p_ph) { pum_height = (int)p_ph; } @@ -256,11 +249,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i context_lines = 0; } else { // Leave two lines of context if possible - if (curwin->w_wrow - curwin->w_cline_row >= 2) { - context_lines = 2; - } else { - context_lines = curwin->w_wrow - curwin->w_cline_row; - } + context_lines = MIN(2, curwin->w_wrow - curwin->w_cline_row); } if (pum_win_row - min_row >= size + context_lines) { @@ -285,20 +274,13 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i } else { // Leave two lines of context if possible validate_cheight(curwin); - if (curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow >= 3) { - context_lines = 3; - } else { - context_lines = curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow; - } + int cline_visible_offset = curwin->w_cline_row + + curwin->w_cline_height - curwin->w_wrow; + context_lines = MIN(3, cline_visible_offset); } pum_row = pum_win_row + context_lines; - if (size > below_row - pum_row) { - pum_height = below_row - pum_row; - } else { - pum_height = size; - } - + pum_height = MIN(below_row - pum_row, size); if (p_ph > 0 && pum_height > p_ph) { pum_height = (int)p_ph; } @@ -353,15 +335,10 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i pum_width = max_col - pum_col - pum_scrollbar; } - if (pum_width > max_width + pum_kind_width + pum_extra_width + 1 - && pum_width > p_pw) { - // the width is more than needed for the items, make it - // narrower - pum_width = max_width + pum_kind_width + pum_extra_width + 1; - - if (pum_width < p_pw) { - pum_width = (int)p_pw; - } + int content_width = max_width + pum_kind_width + pum_extra_width + 1; + if (pum_width > content_width && pum_width > p_pw) { + // Reduce width to fit item + pum_width = MAX(content_width, (int)p_pw); } else if (((cursor_col - min_col > p_pw || cursor_col - min_col > max_width) && !pum_rl) || (pum_rl && (cursor_col < max_col - p_pw @@ -373,13 +350,10 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i pum_col = max_col - 1; } } else if (!pum_rl) { - if (win_start_col > max_col - max_width - pum_scrollbar - && max_width <= p_pw) { + int right_edge_col = max_col - max_width - pum_scrollbar; + if (win_start_col > right_edge_col && max_width <= p_pw) { // use full width to end of the screen - pum_col = max_col - max_width - pum_scrollbar; - if (pum_col < min_col) { - pum_col = min_col; - } + pum_col = MAX(min_col, right_edge_col); } } @@ -400,12 +374,8 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i pum_width = max_col - pum_col - 1; } } - } else if (pum_width > max_width + pum_kind_width + pum_extra_width + 1 - && pum_width > p_pw) { - pum_width = max_width + pum_kind_width + pum_extra_width + 1; - if (pum_width < p_pw) { - pum_width = (int)p_pw; - } + } else if (pum_width > content_width && pum_width > p_pw) { + pum_width = MAX(content_width, (int)p_pw); } } } else if (max_col - min_col < def_width) { @@ -442,7 +412,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i /// Returns attributes for every cell, or NULL if all attributes are the same. static int *pum_compute_text_attrs(char *text, hlf_T hlf, int user_hlattr) { - if ((hlf != HLF_PSI && hlf != HLF_PNI) + if (*text == NUL || (hlf != HLF_PSI && hlf != HLF_PNI) || (win_hl_attr(curwin, HLF_PMSI) == win_hl_attr(curwin, HLF_PSI) && win_hl_attr(curwin, HLF_PMNI) == win_hl_attr(curwin, HLF_PNI))) { return NULL; @@ -456,7 +426,7 @@ static int *pum_compute_text_attrs(char *text, hlf_T hlf, int user_hlattr) int *attrs = xmalloc(sizeof(int) * (size_t)vim_strsize(text)); bool in_fuzzy = State == MODE_CMDLINE ? cmdline_compl_is_fuzzy() - : (get_cot_flags() & COT_FUZZY) != 0; + : (get_cot_flags() & kOptCotFlagFuzzy) != 0; size_t leader_len = strlen(leader); garray_T *ga = NULL; @@ -471,6 +441,7 @@ static int *pum_compute_text_attrs(char *text, hlf_T hlf, int user_hlattr) const char *ptr = text; int cell_idx = 0; uint32_t char_pos = 0; + bool is_select = hlf == HLF_PSI; while (*ptr != NUL) { int new_attr = win_hl_attr(curwin, (int)hlf); @@ -479,14 +450,14 @@ static int *pum_compute_text_attrs(char *text, hlf_T hlf, int user_hlattr) // Handle fuzzy matching for (int i = 0; i < ga->ga_len; i++) { if (char_pos == ((uint32_t *)ga->ga_data)[i]) { - new_attr = win_hl_attr(curwin, hlf == HLF_PSI ? HLF_PMSI : HLF_PMNI); + new_attr = win_hl_attr(curwin, is_select ? HLF_PMSI : HLF_PMNI); new_attr = hl_combine_attr(win_hl_attr(curwin, HLF_PMNI), new_attr); new_attr = hl_combine_attr(win_hl_attr(curwin, (int)hlf), new_attr); break; } } } else if (matched_start && ptr < text + leader_len) { - new_attr = win_hl_attr(curwin, hlf == HLF_PSI ? HLF_PMSI : HLF_PMNI); + new_attr = win_hl_attr(curwin, is_select ? HLF_PMSI : HLF_PMNI); new_attr = hl_combine_attr(win_hl_attr(curwin, HLF_PMNI), new_attr); new_attr = hl_combine_attr(win_hl_attr(curwin, (int)hlf), new_attr); } @@ -552,6 +523,15 @@ static inline char *pum_get_item(int index, int type) return NULL; } +static inline int pum_user_attr_combine(int idx, int type, int attr) +{ + int user_attr[] = { + pum_array[idx].pum_user_abbr_hlattr, + pum_array[idx].pum_user_kind_hlattr, + }; + return user_attr[type] > 0 ? hl_combine_attr(attr, user_attr[type]) : attr; +} + /// Redraw the popup menu, using "pum_first" and "pum_selected". void pum_redraw(void) { @@ -615,19 +595,16 @@ void pum_redraw(void) pum_row - row_off, pum_left_col, false, pum_grid.zindex); } + int scroll_range = pum_size - pum_height; // Never display more than we have - if (pum_first > pum_size - pum_height) { - pum_first = pum_size - pum_height; - } + pum_first = MIN(pum_first, scroll_range); if (pum_scrollbar) { thumb_height = pum_height * pum_height / pum_size; if (thumb_height == 0) { thumb_height = 1; } - thumb_pos = (pum_first * (pum_height - thumb_height) - + (pum_size - pum_height) / 2) - / (pum_size - pum_height); + thumb_pos = (pum_first * (pum_height - thumb_height) + scroll_range / 2) / scroll_range; } for (int i = 0; i < pum_height; i++) { @@ -665,13 +642,8 @@ void pum_redraw(void) attr = win_hl_attr(curwin, (int)hlf); attr = hl_combine_attr(win_hl_attr(curwin, HLF_PNI), attr); orig_attr = attr; - int user_abbr_hlattr = pum_array[idx].pum_user_abbr_hlattr; - int user_kind_hlattr = pum_array[idx].pum_user_kind_hlattr; - if (item_type == CPT_ABBR && user_abbr_hlattr > 0) { - attr = hl_combine_attr(attr, user_abbr_hlattr); - } - if (item_type == CPT_KIND && user_kind_hlattr > 0) { - attr = hl_combine_attr(attr, user_kind_hlattr); + if (item_type < 2) { // try combine attr with user custom + attr = pum_user_attr_combine(idx, item_type, attr); } int width = 0; char *s = NULL; @@ -682,89 +654,87 @@ void pum_redraw(void) s = p; } int w = ptr2cells(p); + if (*p != NUL && *p != TAB && totwidth + w <= pum_width) { + width += w; + continue; + } - if ((*p == NUL) || (*p == TAB) || (totwidth + w > pum_width)) { - // Display the text that fits or comes before a Tab. - // First convert it to printable characters. - char *st; - char saved = *p; - - if (saved != NUL) { - *p = NUL; - } - st = transstr(s, true); - if (saved != NUL) { - *p = saved; - } + // Display the text that fits or comes before a Tab. + // First convert it to printable characters. + char saved = *p; - int *attrs = NULL; - if (item_type == CPT_ABBR) { - attrs = pum_compute_text_attrs(st, hlf, user_abbr_hlattr); - } + if (saved != NUL) { + *p = NUL; + } + char *st = transstr(s, true); + if (saved != NUL) { + *p = saved; + } - if (pum_rl) { - char *rt = reverse_text(st); - char *rt_start = rt; - int cells = vim_strsize(rt); - - if (cells > pum_width) { - do { - cells -= utf_ptr2cells(rt); - MB_PTR_ADV(rt); - } while (cells > pum_width); - - if (cells < pum_width) { - // Most left character requires 2-cells but only 1 cell - // is available on screen. Put a '<' on the left of the - // pum item - *(--rt) = '<'; - cells++; - } - } + int *attrs = NULL; + if (item_type == CPT_ABBR) { + attrs = pum_compute_text_attrs(st, hlf, + pum_array[idx].pum_user_abbr_hlattr); + } - if (attrs == NULL) { - grid_line_puts(grid_col - cells + 1, rt, -1, attr); - } else { - pum_grid_puts_with_attrs(grid_col - cells + 1, cells, rt, -1, attrs); + if (pum_rl) { + char *rt = reverse_text(st); + char *rt_start = rt; + int cells = vim_strsize(rt); + + if (cells > pum_width) { + do { + cells -= utf_ptr2cells(rt); + MB_PTR_ADV(rt); + } while (cells > pum_width); + + if (cells < pum_width) { + // Most left character requires 2-cells but only 1 cell is available on + // screen. Put a '<' on the left of the pum item. + *(--rt) = '<'; + cells++; } + } - xfree(rt_start); - xfree(st); - grid_col -= width; + if (attrs == NULL) { + grid_line_puts(grid_col - cells + 1, rt, -1, attr); } else { - if (attrs == NULL) { - grid_line_puts(grid_col, st, -1, attr); - } else { - pum_grid_puts_with_attrs(grid_col, vim_strsize(st), st, -1, attrs); - } - - xfree(st); - grid_col += width; + pum_grid_puts_with_attrs(grid_col - cells + 1, cells, rt, -1, attrs); } - if (attrs != NULL) { - XFREE_CLEAR(attrs); + xfree(rt_start); + xfree(st); + grid_col -= width; + } else { + if (attrs == NULL) { + grid_line_puts(grid_col, st, -1, attr); + } else { + pum_grid_puts_with_attrs(grid_col, vim_strsize(st), st, -1, attrs); } - if (*p != TAB) { - break; - } + xfree(st); + grid_col += width; + } - // Display two spaces for a Tab. - if (pum_rl) { - grid_line_puts(grid_col - 1, " ", 2, attr); - grid_col -= 2; - } else { - grid_line_puts(grid_col, " ", 2, attr); - grid_col += 2; - } - totwidth += 2; - // start text at next char - s = NULL; - width = 0; + if (attrs != NULL) { + XFREE_CLEAR(attrs); + } + + if (*p != TAB) { + break; + } + + // Display two spaces for a Tab. + if (pum_rl) { + grid_line_puts(grid_col - 1, " ", 2, attr); + grid_col -= 2; } else { - width += w; + grid_line_puts(grid_col, " ", 2, attr); + grid_col += 2; } + totwidth += 2; + s = NULL; // start text at next char + width = 0; } } @@ -817,36 +787,41 @@ void pum_redraw(void) } } -/// set info text to preview buffer. +/// Set the informational text in the preview buffer when the completion +/// item does not include a dedicated preview or popup window. +/// +/// @param[in] buf Buffer where the text will be set. +/// @param[in] info Informational text to display in the preview buffer. +/// @param[in] lnum Where to start the text. Incremented for each added line. +/// @param[out] max_width Maximum width of the displayed text. static void pum_preview_set_text(buf_T *buf, char *info, linenr_T *lnum, int *max_width) { - bcount_t inserted_bytes = 0; - for (char *p = info; *p != NUL;) { - int text_width = 0; - char *e = vim_strchr(p, '\n'); - if (e == NULL) { - ml_append_buf(buf, (*lnum)++, p, 0, false); - text_width = (int)mb_string2cells(p); - if (text_width > *max_width) { - *max_width = text_width; - } - break; - } - *e = NUL; - ml_append_buf(buf, (*lnum)++, p, (int)(e - p + 1), false); - inserted_bytes += (bcount_t)strlen(p) + 1; - text_width = (int)mb_string2cells(p); - if (text_width > *max_width) { - *max_width = text_width; - } - *e = '\n'; - p = e + 1; + Error err = ERROR_INIT; + Arena arena = ARENA_EMPTY; + Array replacement = ARRAY_DICT_INIT; + char *token = NULL; + char *line = os_strtok(info, "\n", &token); + buf->b_p_ma = true; + while (line != NULL) { + ADD(replacement, STRING_OBJ(cstr_to_string(line))); + (*lnum)++; + (*max_width) = MAX(*max_width, (int)mb_string2cells(line)); + line = os_strtok(NULL, "\n", &token); } - // delete the empty last line - ml_delete_buf(buf, buf->b_ml.ml_line_count, false); - if (get_cot_flags() & COT_POPUP) { - extmark_splice(buf, 1, 0, 1, 0, 0, buf->b_ml.ml_line_count, 0, inserted_bytes, kExtmarkNoUndo); + + int original_textlock = textlock; + if (textlock > 0) { + textlock = 0; } + nvim_buf_set_lines(0, buf->handle, 0, -1, false, replacement, &arena, &err); + textlock = original_textlock; + if (ERROR_SET(&err)) { + emsg(err.msg); + api_clear_error(&err); + } + arena_mem_free(arena_finish(&arena)); + api_free_array(replacement); + buf->b_p_ma = false; } /// adjust floating info preview window position @@ -880,7 +855,7 @@ static void pum_adjust_info_position(win_T *wp, int height, int width) /// Used for nvim__complete_set /// /// @param selected the selected compl item. -/// @parma info Info string. +/// @param info Info string. /// @return a win_T pointer. win_T *pum_set_info(int selected, char *info) { @@ -896,14 +871,6 @@ win_T *pum_set_info(int selected, char *info) if (!wp) { return NULL; } - } else { - // clean exist buffer - linenr_T count = wp->w_buffer->b_ml.ml_line_count; - while (!buf_is_empty(wp->w_buffer)) { - ml_delete_buf(wp->w_buffer, 1, false); - } - bcount_t deleted_bytes = get_region_bytecount(wp->w_buffer, 1, count, 0, 0); - extmark_splice(wp->w_buffer, 1, 0, count, 0, deleted_bytes, 1, 0, 0, kExtmarkNoUndo); } linenr_T lnum = 0; int max_info_width = 0; @@ -939,13 +906,17 @@ static bool pum_set_selected(int n, int repeat) int prev_selected = pum_selected; pum_selected = n; + int scroll_offset = pum_selected - pum_height; unsigned cur_cot_flags = get_cot_flags(); - bool use_float = (cur_cot_flags & COT_POPUP) != 0; - // when new leader add and info window is shown and no selected we still - // need use the first index item to update the info float window position. - bool force_select = use_float && pum_selected < 0 && win_float_find_preview(); - if (force_select) { - pum_selected = 0; + bool use_float = (cur_cot_flags & kOptCotFlagPopup) != 0; + + // Close the floating preview window if 'selected' is -1, indicating a return to the original + // state. It is also closed when the selected item has no corresponding info item. + if (use_float && (pum_selected < 0 || pum_array[pum_selected].pum_info == NULL)) { + win_T *wp = win_float_find_preview(); + if (wp) { + win_close(wp, true, true); + } } if ((pum_selected >= 0) && (pum_selected < pum_size)) { @@ -962,41 +933,28 @@ static bool pum_set_selected(int n, int repeat) } else { pum_first = pum_selected; } - } else if (pum_first < pum_selected - pum_height + 5) { + } else if (pum_first < scroll_offset + 5) { // scroll up; when we did a jump it's probably a PageDown then // scroll a whole page - if (pum_first < pum_selected - pum_height + 1 + 2) { - pum_first += pum_height - 2; - if (pum_first < pum_selected - pum_height + 1) { - pum_first = pum_selected - pum_height + 1; - } + if (pum_first < scroll_offset + 3) { + pum_first = MAX(pum_first + pum_height - 2, scroll_offset + 1); } else { - pum_first = pum_selected - pum_height + 1; + pum_first = scroll_offset + 1; } } // Give a few lines of context when possible. - if (context > 3) { - context = 3; - } + context = MIN(context, 3); if (pum_height > 2) { if (pum_first > pum_selected - context) { - // scroll down - pum_first = pum_selected - context; - - if (pum_first < 0) { - pum_first = 0; - } + pum_first = MAX(pum_selected - context, 0); // scroll down } else if (pum_first < pum_selected + context - pum_height + 1) { - // scroll up - pum_first = pum_selected + context - pum_height + 1; + pum_first = pum_selected + context - pum_height + 1; // up } } // adjust for the number of lines displayed - if (pum_first > pum_size - pum_height) { - pum_first = pum_size - pum_height; - } + pum_first = MIN(pum_first, pum_size - pum_height); // Show extra info in the preview window if there is something and // 'completeopt' contains "preview". @@ -1006,7 +964,7 @@ static bool pum_set_selected(int n, int repeat) if ((pum_array[pum_selected].pum_info != NULL) && (Rows > 10) && (repeat <= 1) - && (cur_cot_flags & COT_ANY_PREVIEW)) { + && (cur_cot_flags & (kOptCotFlagPreview | kOptCotFlagPopup))) { win_T *curwin_save = curwin; tabpage_T *curtab_save = curtab; @@ -1050,7 +1008,8 @@ static bool pum_set_selected(int n, int repeat) && (curbuf->b_nwindows == 1) && (curbuf->b_fname == NULL) && bt_nofile(curbuf) - && (curbuf->b_p_bh[0] == 'w')) { + && (curbuf->b_p_bh[0] == 'w') + && !use_float) { // Already a "wipeout" buffer, make it empty. while (!buf_is_empty(curbuf)) { ml_delete(1, false); @@ -1079,9 +1038,7 @@ static bool pum_set_selected(int n, int repeat) // Increase the height of the preview window to show the // text, but no more than 'previewheight' lines. if (repeat == 0 && !use_float) { - if (lnum > p_pvh) { - lnum = (linenr_T)p_pvh; - } + lnum = MIN(lnum, (linenr_T)p_pvh); if (curwin->w_height < lnum) { win_setheight((int)lnum); @@ -1164,11 +1121,6 @@ static bool pum_set_selected(int n, int repeat) } } - // restore before selected value - if (force_select) { - pum_selected = n; - } - return resized; } @@ -1349,9 +1301,7 @@ static void pum_position_at_mouse(int min_width) pum_width = max_col - pum_col; } - if (pum_width > pum_base_width + 1) { - pum_width = pum_base_width + 1; - } + pum_width = MIN(pum_width, pum_base_width + 1); } /// Select the pum entry at the mouse position. |