diff options
Diffstat (limited to 'src/nvim/popupmenu.c')
-rw-r--r-- | src/nvim/popupmenu.c | 273 |
1 files changed, 210 insertions, 63 deletions
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index 86f3611ec5..ddcb819054 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -17,13 +17,16 @@ #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" #include "nvim/ex_cmds_defs.h" #include "nvim/extmark.h" #include "nvim/extmark_defs.h" +#include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/gettext_defs.h" #include "nvim/globals.h" @@ -47,6 +50,7 @@ #include "nvim/plines.h" #include "nvim/popupmenu.h" #include "nvim/pos_defs.h" +#include "nvim/search.h" #include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/types_defs.h" @@ -140,7 +144,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i || (State == MODE_CMDLINE && ui_has(kUIWildmenu)); } - pum_rl = (curwin->w_p_rl && State != MODE_CMDLINE); + pum_rl = State != MODE_CMDLINE && curwin->w_p_rl; do { // Mark the pum as visible already here, @@ -434,6 +438,114 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i pum_redraw(); } +/// Computes attributes of text on the popup menu. +/// 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) + || (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; + } + + char *leader = State == MODE_CMDLINE ? cmdline_compl_pattern() + : ins_compl_leader(); + if (leader == NULL || *leader == NUL) { + return NULL; + } + + 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; + size_t leader_len = strlen(leader); + + garray_T *ga = NULL; + bool matched_start = false; + + if (in_fuzzy) { + ga = fuzzy_match_str_with_pos(text, leader); + } else { + matched_start = mb_strnicmp(text, leader, leader_len) == 0; + } + + const char *ptr = text; + int cell_idx = 0; + uint32_t char_pos = 0; + + while (*ptr != NUL) { + int new_attr = win_hl_attr(curwin, (int)hlf); + + if (ga != NULL) { + // 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); + break; + } + } + } else if (matched_start && ptr < text + leader_len) { + new_attr = win_hl_attr(curwin, hlf == HLF_PSI ? HLF_PMSI : HLF_PMNI); + } + + if (user_hlattr > 0) { + new_attr = hl_combine_attr(new_attr, user_hlattr); + } + + int char_cells = utf_ptr2cells(ptr); + for (int i = 0; i < char_cells; i++) { + attrs[cell_idx + i] = new_attr; + } + cell_idx += char_cells; + + MB_PTR_ADV(ptr); + char_pos++; + } + + if (ga != NULL) { + ga_clear(ga); + xfree(ga); + } + return attrs; +} + +/// Displays text on the popup menu with specific attributes. +static void pum_grid_puts_with_attrs(int col, int cells, const char *text, int textlen, + const int *attrs) +{ + const int col_start = col; + const char *ptr = text; + + // Render text with proper attributes + while (*ptr != NUL && (textlen < 0 || ptr < text + textlen)) { + int char_len = utfc_ptr2len(ptr); + int attr = attrs[pum_rl ? (col_start + cells - col - 1) : (col - col_start)]; + grid_line_puts(col, ptr, char_len, attr); + col += utf_ptr2cells(ptr); + ptr += char_len; + } +} + +static inline void pum_align_order(int *order) +{ + bool is_default = cia_flags == 0; + order[0] = is_default ? CPT_ABBR : cia_flags / 100; + order[1] = is_default ? CPT_KIND : (cia_flags / 10) % 10; + order[2] = is_default ? CPT_MENU : cia_flags % 10; +} + +static inline char *pum_get_item(int index, int type) +{ + switch (type) { + case CPT_ABBR: + return pum_array[index].pum_text; + case CPT_KIND: + return pum_array[index].pum_kind; + case CPT_MENU: + return pum_array[index].pum_extra; + } + return NULL; +} + /// Redraw the popup menu, using "pum_first" and "pum_selected". void pum_redraw(void) { @@ -445,11 +557,9 @@ void pum_redraw(void) int thumb_height = 1; int n; -#define HA(hlf) (win_hl_attr(curwin, (hlf))) - // "word" "kind" "extra text" - const int attrsNorm[3] = { HA(HLF_PNI), HA(HLF_PNK), HA(HLF_PNX) }; - const int attrsSel[3] = { HA(HLF_PSI), HA(HLF_PSK), HA(HLF_PSX) }; -#undef HA + // "word" "kind" "extra text" + const hlf_T hlfsNorm[3] = { HLF_PNI, HLF_PNK, HLF_PNX }; + const hlf_T hlfsSel[3] = { HLF_PSI, HLF_PSK, HLF_PSX }; int grid_width = pum_width; int col_off = 0; @@ -516,8 +626,9 @@ void pum_redraw(void) for (int i = 0; i < pum_height; i++) { int idx = i + pum_first; - const int *const attrs = (idx == pum_selected) ? attrsSel : attrsNorm; - int attr = attrs[0]; // start with "word" highlight + const hlf_T *const hlfs = (idx == pum_selected) ? hlfsSel : hlfsNorm; + hlf_T hlf = hlfs[0]; // start with "word" highlight + int attr = win_hl_attr(curwin, (int)hlf); grid_line_start(&pum_grid, row); @@ -531,27 +642,27 @@ void pum_redraw(void) } // Display each entry, use two spaces for a Tab. - // Do this 3 times: - // 0 - main text - // 1 - kind - // 2 - extra info + // Do this 3 times and order from p_cia int grid_col = col_off; int totwidth = 0; - - for (int round = 0; round < 3; round++) { - attr = attrs[round]; + int order[3]; + int items_width_array[3] = { pum_base_width, pum_kind_width, pum_extra_width }; + pum_align_order(order); + int basic_width = items_width_array[order[0]]; // first item width + bool last_isabbr = order[2] == CPT_ABBR; + for (int j = 0; j < 3; j++) { + int item_type = order[j]; + hlf = hlfs[item_type]; + attr = win_hl_attr(curwin, (int)hlf); + if (pum_array[idx].pum_user_hlattr > 0) { + attr = hl_combine_attr(attr, pum_array[idx].pum_user_hlattr); + } + if (item_type == CPT_KIND && pum_array[idx].pum_user_kind_hlattr > 0) { + attr = hl_combine_attr(attr, pum_array[idx].pum_user_kind_hlattr); + } int width = 0; char *s = NULL; - - switch (round) { - case 0: - p = pum_array[idx].pum_text; break; - case 1: - p = pum_array[idx].pum_kind; break; - case 2: - p = pum_array[idx].pum_extra; break; - } - + p = pum_get_item(idx, item_type); if (p != NULL) { for (;; MB_PTR_ADV(p)) { if (s == NULL) { @@ -573,36 +684,51 @@ void pum_redraw(void) *p = saved; } + int user_hlattr = pum_array[idx].pum_user_hlattr; + int *attrs = pum_compute_text_attrs(st, hlf, user_hlattr); + if (pum_rl) { char *rt = reverse_text(st); char *rt_start = rt; - int size = vim_strsize(rt); + int cells = vim_strsize(rt); - if (size > pum_width) { + if (cells > pum_width) { do { - size -= utf_ptr2cells(rt); + cells -= utf_ptr2cells(rt); MB_PTR_ADV(rt); - } while (size > pum_width); + } while (cells > pum_width); - if (size < 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) = '<'; - size++; + cells++; } } - grid_line_puts(grid_col - size + 1, rt, -1, attr); + + 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); + } + xfree(rt_start); xfree(st); grid_col -= width; } else { - // use grid_line_puts() to truncate the text - grid_line_puts(grid_col, st, -1, attr); + 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; } + xfree(attrs); + if (*p != TAB) { break; } @@ -625,31 +751,31 @@ void pum_redraw(void) } } - if (round > 0) { - n = pum_kind_width + 1; + if (j > 0) { + n = items_width_array[order[1]] + (last_isabbr ? 0 : 1); } else { - n = 1; + n = order[j] == CPT_ABBR ? 1 : 0; } + bool next_isempty = false; + if (j + 1 < 3) { + next_isempty = pum_get_item(idx, order[j + 1]) == NULL; + } // Stop when there is nothing more to display. - if ((round == 2) - || ((round == 1) - && (pum_array[idx].pum_extra == NULL)) - || ((round == 0) - && (pum_array[idx].pum_kind == NULL) - && (pum_array[idx].pum_extra == NULL)) - || (pum_base_width + n >= pum_width)) { + if ((j == 2) + || (next_isempty && (j == 1 || (j == 0 && pum_get_item(idx, order[j + 2]) == NULL))) + || (basic_width + n >= pum_width)) { break; } if (pum_rl) { - grid_line_fill(col_off - pum_base_width - n + 1, grid_col + 1, schar_from_ascii(' '), attr); - grid_col = col_off - pum_base_width - n + 1; + grid_line_fill(col_off - basic_width - n + 1, grid_col + 1, schar_from_ascii(' '), attr); + grid_col = col_off - basic_width - n; } else { - grid_line_fill(grid_col, col_off + pum_base_width + n, schar_from_ascii(' '), attr); - grid_col = col_off + pum_base_width + n; + grid_line_fill(grid_col, col_off + basic_width + n, schar_from_ascii(' '), attr); + grid_col = col_off + basic_width + n; } - totwidth = pum_base_width + n; + totwidth = basic_width + n; } if (pum_rl) { @@ -699,7 +825,7 @@ static void pum_preview_set_text(buf_T *buf, char *info, linenr_T *lnum, int *ma } // delete the empty last line ml_delete_buf(buf, buf->b_ml.ml_line_count, false); - if (strstr(p_cot, "popup") != NULL) { + if (get_cot_flags() & COT_POPUP) { extmark_splice(buf, 1, 0, 1, 0, 0, buf->b_ml.ml_line_count, 0, inserted_bytes, kExtmarkNoUndo); } } @@ -794,7 +920,8 @@ static bool pum_set_selected(int n, int repeat) int prev_selected = pum_selected; pum_selected = n; - bool use_float = strstr(p_cot, "popup") != NULL; + 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(); @@ -860,7 +987,7 @@ static bool pum_set_selected(int n, int repeat) if ((pum_array[pum_selected].pum_info != NULL) && (Rows > 10) && (repeat <= 1) - && (vim_strchr(p_cot, 'p') != NULL)) { + && (cur_cot_flags & COT_ANY_PREVIEW)) { win_T *curwin_save = curwin; tabpage_T *curtab_save = curtab; @@ -1142,12 +1269,14 @@ void pum_set_event_info(dict_T *dict) static void pum_position_at_mouse(int min_width) { int min_row = 0; + int min_col = 0; int max_row = Rows; int max_col = Columns; if (mouse_grid > 1) { win_T *wp = get_win_by_grid_handle(mouse_grid); if (wp != NULL) { min_row = -wp->w_winrow; + min_col = -wp->w_wincol; max_row = MAX(Rows - wp->w_winrow, wp->w_grid.rows); max_col = MAX(Columns - wp->w_wincol, wp->w_grid.cols); } @@ -1160,8 +1289,10 @@ static void pum_position_at_mouse(int min_width) } else { pum_anchor_grid = mouse_grid; } - if (max_row - mouse_row > pum_size) { - // Enough space below the mouse row. + + if (max_row - mouse_row > pum_size || max_row - mouse_row > mouse_row - min_row) { + // Enough space below the mouse row, + // or there is more space below the mouse row than above. pum_above = false; pum_row = mouse_row + 1; if (pum_height > max_row - pum_row) { @@ -1176,16 +1307,29 @@ static void pum_position_at_mouse(int min_width) pum_row = min_row; } } - if (max_col - mouse_col >= pum_base_width - || max_col - mouse_col > min_width) { - // Enough space to show at mouse column. - pum_col = mouse_col; + + if (pum_rl) { + if (mouse_col - min_col + 1 >= pum_base_width + || mouse_col - min_col + 1 > min_width) { + // Enough space to show at mouse column. + pum_col = mouse_col; + } else { + // Not enough space, left align with window. + pum_col = min_col + MIN(pum_base_width, min_width) - 1; + } + pum_width = pum_col - min_col + 1; } else { - // Not enough space, right align with window. - pum_col = max_col - (pum_base_width > min_width ? min_width : pum_base_width); + if (max_col - mouse_col >= pum_base_width + || max_col - mouse_col > min_width) { + // Enough space to show at mouse column. + pum_col = mouse_col; + } else { + // Not enough space, right align with window. + pum_col = max_col - MIN(pum_base_width, min_width); + } + pum_width = max_col - pum_col; } - pum_width = max_col - pum_col; if (pum_width > pum_base_width + 1) { pum_width = pum_base_width + 1; } @@ -1204,7 +1348,7 @@ static void pum_select_mouse_pos(void) int idx = mouse_row - pum_row; - if (idx < 0 || idx >= pum_size) { + if (idx < 0 || idx >= pum_height) { pum_selected = -1; } else if (*pum_array[idx].pum_text != NUL) { pum_selected = idx; @@ -1267,6 +1411,7 @@ void pum_show_popupmenu(vimmenu_T *menu) pum_compute_size(); pum_scrollbar = 0; pum_height = pum_size; + pum_rl = curwin->w_p_rl; pum_position_at_mouse(20); pum_selected = -1; @@ -1346,7 +1491,9 @@ void pum_make_popup(const char *path_name, int use_mouse_pos) // Hack: set mouse position at the cursor so that the menu pops up // around there. mouse_row = curwin->w_grid.row_offset + curwin->w_wrow; - mouse_col = curwin->w_grid.col_offset + curwin->w_wcol; + mouse_col = curwin->w_grid.col_offset + + (curwin->w_p_rl ? curwin->w_width_inner - curwin->w_wcol - 1 + : curwin->w_wcol); if (ui_has(kUIMultigrid)) { mouse_grid = curwin->w_grid.target->handle; } else if (curwin->w_grid.target != &default_grid) { |