diff options
Diffstat (limited to 'src/nvim/popupmenu.c')
-rw-r--r-- | src/nvim/popupmenu.c | 167 |
1 files changed, 140 insertions, 27 deletions
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index 324254a188..18446e749b 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -25,6 +25,7 @@ #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" @@ -48,6 +49,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" @@ -141,7 +143,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, @@ -435,6 +437,84 @@ 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) +{ + char *leader = ins_compl_leader(); + + if (leader == NULL || *leader == 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; + } + + int *attrs = xmalloc(sizeof(int) * (size_t)vim_strsize(text)); + size_t leader_len = strlen(leader); + const bool in_fuzzy = (get_cot_flags() & COT_FUZZY) != 0; + + garray_T *ga = NULL; + bool matched_start = false; + + if (in_fuzzy) { + ga = fuzzy_match_str_with_pos(text, leader); + } else { + matched_start = strncmp(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); + } + + 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; + } +} + /// Redraw the popup menu, using "pum_first" and "pum_selected". void pum_redraw(void) { @@ -446,11 +526,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; @@ -517,8 +595,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); @@ -540,7 +619,8 @@ void pum_redraw(void) int totwidth = 0; for (int round = 0; round < 3; round++) { - attr = attrs[round]; + hlf = hlfs[round]; + attr = win_hl_attr(curwin, (int)hlf); int width = 0; char *s = NULL; @@ -574,36 +654,50 @@ void pum_redraw(void) *p = saved; } + int *attrs = pum_compute_text_attrs(st, hlf); + 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; } @@ -645,7 +739,7 @@ void pum_redraw(void) 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_col = col_off - pum_base_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; @@ -1144,12 +1238,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); } @@ -1162,6 +1258,7 @@ 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. pum_above = false; @@ -1178,16 +1275,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; } @@ -1269,6 +1379,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; @@ -1348,7 +1459,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) { |