aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/popupmenu.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/popupmenu.c')
-rw-r--r--src/nvim/popupmenu.c282
1 files changed, 235 insertions, 47 deletions
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index f009722357..e34d6fd97f 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -8,35 +8,47 @@
#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/charset.h"
#include "nvim/drawscreen.h"
#include "nvim/eval/typval.h"
#include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/getchar.h"
-#include "nvim/gettext.h"
+#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
+#include "nvim/highlight_defs.h"
#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/menu.h"
#include "nvim/message.h"
#include "nvim/move.h"
#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"
#include "nvim/state_defs.h"
#include "nvim/strings.h"
+#include "nvim/types_defs.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
+#include "nvim/ui_defs.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
+#include "nvim/winfloat.h"
static pumitem_T *pum_array = NULL; // items of displayed pum
static int pum_size; // nr of items in "pum_array"
@@ -53,7 +65,8 @@ static bool pum_rl; // true when popupmenu is drawn 'rightleft'
static int pum_anchor_grid; // grid where position is defined
static int pum_row; // top row of pum
-static int pum_col; // left column of pum
+static int pum_col; // left column of pum, right column if 'rightleft'
+static int pum_left_col; // left column of pum, before padding or scrollbar
static bool pum_above; // pum is drawn above cursor line
static bool pum_is_visible = false;
@@ -73,21 +86,20 @@ static void pum_compute_size(void)
pum_kind_width = 0;
pum_extra_width = 0;
for (int i = 0; i < pum_size; i++) {
- int w;
if (pum_array[i].pum_text != NULL) {
- w = vim_strsize(pum_array[i].pum_text);
+ int w = vim_strsize(pum_array[i].pum_text);
if (pum_base_width < w) {
pum_base_width = w;
}
}
if (pum_array[i].pum_kind != NULL) {
- w = vim_strsize(pum_array[i].pum_kind) + 1;
+ int w = vim_strsize(pum_array[i].pum_kind) + 1;
if (pum_kind_width < w) {
pum_kind_width = w;
}
}
if (pum_array[i].pum_extra != NULL) {
- w = vim_strsize(pum_array[i].pum_extra) + 1;
+ int w = vim_strsize(pum_array[i].pum_extra) + 1;
if (pum_extra_width < w) {
pum_extra_width = w;
}
@@ -460,14 +472,14 @@ void pum_redraw(void)
grid_assign_handle(&pum_grid);
- bool moved = ui_comp_put_grid(&pum_grid, pum_row, pum_col - col_off,
+ pum_left_col = pum_col - col_off;
+ bool moved = ui_comp_put_grid(&pum_grid, pum_row, pum_left_col,
pum_height, grid_width, false, true);
bool invalid_grid = moved || pum_invalid;
pum_invalid = false;
must_redraw_pum = false;
- if (!pum_grid.chars
- || pum_grid.rows != pum_height || pum_grid.cols != grid_width) {
+ if (!pum_grid.chars || pum_grid.rows != pum_height || pum_grid.cols != grid_width) {
grid_alloc(&pum_grid, pum_height, grid_width, !invalid_grid, false);
ui_call_grid_resize(pum_grid.handle, pum_grid.cols, pum_grid.rows);
} else if (invalid_grid) {
@@ -476,9 +488,8 @@ void pum_redraw(void)
if (ui_has(kUIMultigrid)) {
const char *anchor = pum_above ? "SW" : "NW";
int row_off = pum_above ? -pum_height : 0;
- ui_call_win_float_pos(pum_grid.handle, -1, cstr_as_string((char *)anchor),
- pum_anchor_grid, pum_row - row_off, pum_col - col_off,
- false, pum_grid.zindex);
+ ui_call_win_float_pos(pum_grid.handle, -1, cstr_as_string(anchor), pum_anchor_grid,
+ pum_row - row_off, pum_left_col, false, pum_grid.zindex);
}
// Never display more than we have
@@ -625,19 +636,19 @@ void pum_redraw(void)
}
if (pum_rl) {
- grid_line_fill(col_off - pum_base_width - n + 1, grid_col + 1, ' ', attr);
+ 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;
} else {
- grid_line_fill(grid_col, col_off + pum_base_width + n, ' ', attr);
+ grid_line_fill(grid_col, col_off + pum_base_width + n, schar_from_ascii(' '), attr);
grid_col = col_off + pum_base_width + n;
}
totwidth = pum_base_width + n;
}
if (pum_rl) {
- grid_line_fill(col_off - pum_width + 1, grid_col + 1, ' ', attr);
+ grid_line_fill(col_off - pum_width + 1, grid_col + 1, schar_from_ascii(' '), attr);
} else {
- grid_line_fill(grid_col, col_off + pum_width, ' ', attr);
+ grid_line_fill(grid_col, col_off + pum_width, schar_from_ascii(' '), attr);
}
if (pum_scrollbar > 0) {
@@ -654,6 +665,142 @@ void pum_redraw(void)
}
}
+/// create a floating preview window for info
+/// @return NULL when no enough room to show
+static win_T *pum_create_float_preview(bool enter)
+{
+ WinConfig config = WIN_CONFIG_INIT;
+ config.relative = kFloatRelativeEditor;
+ // when pum_above is SW otherwise is NW
+ config.anchor = pum_above ? kFloatAnchorSouth : 0;
+ int col = pum_col + pum_width + pum_scrollbar + 1;
+ // TODO(glepnir): support config align border by using completepopup
+ // align menu
+ config.row = pum_row;
+ int right_extra = Columns - col;
+ if (right_extra > 0) {
+ config.width = right_extra;
+ config.col = col - 1;
+ } else if (pum_col - 2 > 0) {
+ config.width = pum_col - 2;
+ config.col = pum_col - config.width - 1;
+ } else {
+ return NULL;
+ }
+ config.height = pum_height;
+ config.style = kWinStyleMinimal;
+ config.hide = true;
+ Error err = ERROR_INIT;
+ win_T *wp = win_new_float(NULL, true, config, &err);
+ // TODO(glepnir): remove win_enter usage
+ if (enter) {
+ win_enter(wp, false);
+ }
+
+ // create a new buffer
+ Buffer b = nvim_create_buf(false, true, &err);
+ if (!b) {
+ win_free(wp, NULL);
+ return NULL;
+ }
+ buf_T *buf = find_buffer_by_handle(b, &err);
+ set_string_option_direct_in_buf(buf, kOptBufhidden, "wipe", OPT_LOCAL, 0);
+ wp->w_float_is_info = true;
+ wp->w_p_diff = false;
+ buf->b_p_bl = false;
+ win_set_buf(wp, buf, true, &err);
+ return wp;
+}
+
+/// set info text to preview buffer.
+static void pum_preview_set_text(buf_T *buf, char *info, linenr_T *lnum, int *max_width)
+{
+ 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);
+ text_width = (int)mb_string2cells(p);
+ if (text_width > *max_width) {
+ *max_width = text_width;
+ }
+ *e = '\n';
+ p = e + 1;
+ }
+ // delete the empty last line
+ ml_delete_buf(buf, buf->b_ml.ml_line_count, false);
+}
+
+/// adjust floating preview window width and height
+static void pum_adjust_float_position(win_T *wp, int height, int width)
+{
+ // when floating window in right and right no enough room to show
+ // but left has enough room, adjust floating window to left.
+ if (wp->w_config.width < width && wp->w_config.col > pum_col) {
+ if ((pum_col - 2) > width) {
+ wp->w_config.width = width;
+ wp->w_config.col = pum_col - width - 1;
+ }
+ }
+ wp->w_config.width = MIN(wp->w_config.width, width);
+ wp->w_config.height = MIN(Rows, height);
+ wp->w_config.hide = false;
+ win_config_float(wp, wp->w_config);
+}
+
+/// used in nvim_complete_set
+win_T *pum_set_info(int pum_idx, char *info)
+{
+ if (!pum_is_visible || pum_idx < 0 || pum_idx > pum_size) {
+ return NULL;
+ }
+ pum_array[pum_idx].pum_info = xstrdup(info);
+ compl_set_info(pum_idx);
+ bool use_float = strstr(p_cot, "popup") != NULL ? true : false;
+ if (pum_idx != pum_selected || !use_float) {
+ return NULL;
+ }
+
+ block_autocmds();
+ RedrawingDisabled++;
+ no_u_sync++;
+ win_T *wp = win_float_find_preview();
+ if (wp == NULL) {
+ wp = pum_create_float_preview(false);
+ // no enough room to show
+ if (!wp) {
+ return NULL;
+ }
+ } else {
+ // clean exist buffer
+ while (!buf_is_empty(wp->w_buffer)) {
+ ml_delete_buf(wp->w_buffer, 1, false);
+ }
+ }
+ no_u_sync--;
+ RedrawingDisabled--;
+
+ linenr_T lnum = 0;
+ int max_info_width = 0;
+ pum_preview_set_text(wp->w_buffer, info, &lnum, &max_info_width);
+ redraw_later(wp, UPD_SOME_VALID);
+
+ if (wp->w_p_wrap) {
+ lnum += plines_win(wp, lnum, true);
+ }
+ pum_adjust_float_position(wp, lnum, max_info_width);
+ unblock_autocmds();
+ return wp;
+}
+
/// Set the index of the currently selected item. The menu will scroll when
/// necessary. When "n" is out of range don't scroll.
/// This may be repeated when the preview window is used:
@@ -668,8 +815,9 @@ void pum_redraw(void)
/// menu must be recomputed.
static bool pum_set_selected(int n, int repeat)
{
- int resized = false;
+ bool resized = false;
int context = pum_height / 2;
+ int prev_selected = pum_selected;
pum_selected = n;
@@ -718,6 +866,10 @@ static bool pum_set_selected(int n, int repeat)
pum_first = pum_selected + context - pum_height + 1;
}
}
+ // adjust for the number of lines displayed
+ if (pum_first > pum_size - pum_height) {
+ pum_first = pum_size - pum_height;
+ }
// Show extra info in the preview window if there is something and
// 'completeopt' contains "preview".
@@ -730,6 +882,11 @@ static bool pum_set_selected(int n, int repeat)
&& (vim_strchr(p_cot, 'p') != NULL)) {
win_T *curwin_save = curwin;
tabpage_T *curtab_save = curtab;
+ bool use_float = strstr(p_cot, "popup") != NULL ? true : false;
+
+ if (use_float) {
+ block_autocmds();
+ }
// Open a preview window. 3 lines by default. Prefer
// 'previewheight' if set and smaller.
@@ -742,12 +899,26 @@ static bool pum_set_selected(int n, int repeat)
// Prevent undo sync here, if an autocommand syncs undo weird
// things can happen to the undo tree.
no_u_sync++;
- resized = prepare_tagpreview(false);
+
+ if (!use_float) {
+ resized = prepare_tagpreview(false);
+ } else {
+ win_T *wp = win_float_find_preview();
+ if (wp) {
+ win_enter(wp, false);
+ } else {
+ wp = pum_create_float_preview(true);
+ if (wp) {
+ resized = true;
+ }
+ }
+ }
+
no_u_sync--;
RedrawingDisabled--;
g_do_tagpreview = 0;
- if (curwin->w_p_pvw) {
+ if (curwin->w_p_pvw || curwin->w_float_is_info) {
int res = OK;
if (!resized
&& (curbuf->b_nwindows == 1)
@@ -767,32 +938,21 @@ static bool pum_set_selected(int n, int repeat)
if (res == OK) {
// Edit a new, empty buffer. Set options for a "wipeout"
// buffer.
- set_option_value_give_err("swf", BOOLEAN_OPTVAL(false), OPT_LOCAL);
- set_option_value_give_err("bl", BOOLEAN_OPTVAL(false), OPT_LOCAL);
- set_option_value_give_err("bt", STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL);
- set_option_value_give_err("bh", STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL);
- set_option_value_give_err("diff", BOOLEAN_OPTVAL(false), OPT_LOCAL);
+ set_option_value_give_err(kOptSwapfile, BOOLEAN_OPTVAL(false), OPT_LOCAL);
+ set_option_value_give_err(kOptBuflisted, BOOLEAN_OPTVAL(false), OPT_LOCAL);
+ set_option_value_give_err(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL);
+ set_option_value_give_err(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL);
+ set_option_value_give_err(kOptDiff, BOOLEAN_OPTVAL(false), OPT_LOCAL);
}
}
if (res == OK) {
linenr_T lnum = 0;
-
- for (char *p = pum_array[pum_selected].pum_info; *p != NUL;) {
- char *e = vim_strchr(p, '\n');
- if (e == NULL) {
- ml_append(lnum++, p, 0, false);
- break;
- }
- *e = NUL;
- ml_append(lnum++, p, (int)(e - p + 1), false);
- *e = '\n';
- p = e + 1;
- }
-
+ int max_info_width = 0;
+ pum_preview_set_text(curbuf, pum_array[pum_selected].pum_info, &lnum, &max_info_width);
// Increase the height of the preview window to show the
// text, but no more than 'previewheight' lines.
- if (repeat == 0) {
+ if (repeat == 0 && !use_float) {
if (lnum > p_pvh) {
lnum = (linenr_T)p_pvh;
}
@@ -805,9 +965,22 @@ static bool pum_set_selected(int n, int repeat)
curbuf->b_changed = false;
curbuf->b_p_ma = false;
+ if (pum_selected != prev_selected) {
+ curwin->w_topline = 1;
+ } else if (curwin->w_topline > curbuf->b_ml.ml_line_count) {
+ curwin->w_topline = curbuf->b_ml.ml_line_count;
+ }
curwin->w_cursor.lnum = 1;
curwin->w_cursor.col = 0;
+ if (use_float) {
+ if (curwin->w_p_wrap) {
+ lnum += plines_win(curwin, lnum, true);
+ }
+ // adjust floating window by actually height and max info text width
+ pum_adjust_float_position(curwin, lnum, max_info_width);
+ }
+
if ((curwin != curwin_save && win_valid(curwin_save))
|| (curtab != curtab_save && valid_tabpage(curtab_save))) {
if (curtab != curtab_save && valid_tabpage(curtab_save)) {
@@ -829,7 +1002,7 @@ static bool pum_set_selected(int n, int repeat)
// update the view on the buffer. Only go back to
// the window when needed, otherwise it will always be
// redrawn.
- if (resized) {
+ if (resized && win_valid(curwin_save)) {
no_u_sync++;
win_enter(curwin_save, true);
no_u_sync--;
@@ -858,6 +1031,10 @@ static bool pum_set_selected(int n, int repeat)
}
}
}
+
+ if (use_float) {
+ unblock_autocmds();
+ }
}
}
@@ -892,6 +1069,10 @@ void pum_check_clear(void)
}
pum_is_drawn = false;
pum_external = false;
+ win_T *wp = win_float_find_preview();
+ if (wp != NULL) {
+ win_close(wp, false, false);
+ }
}
}
@@ -964,13 +1145,13 @@ void pum_set_event_info(dict_T *dict)
r = (double)pum_row;
c = (double)pum_col;
}
- (void)tv_dict_add_float(dict, S_LEN("height"), h);
- (void)tv_dict_add_float(dict, S_LEN("width"), w);
- (void)tv_dict_add_float(dict, S_LEN("row"), r);
- (void)tv_dict_add_float(dict, S_LEN("col"), c);
- (void)tv_dict_add_nr(dict, S_LEN("size"), pum_size);
- (void)tv_dict_add_bool(dict, S_LEN("scrollbar"),
- pum_scrollbar ? kBoolVarTrue : kBoolVarFalse);
+ tv_dict_add_float(dict, S_LEN("height"), h);
+ tv_dict_add_float(dict, S_LEN("width"), w);
+ tv_dict_add_float(dict, S_LEN("row"), r);
+ tv_dict_add_float(dict, S_LEN("col"), c);
+ tv_dict_add_nr(dict, S_LEN("size"), pum_size);
+ tv_dict_add_bool(dict, S_LEN("scrollbar"),
+ pum_scrollbar ? kBoolVarTrue : kBoolVarFalse);
}
static void pum_position_at_mouse(int min_width)
@@ -986,7 +1167,14 @@ static void pum_position_at_mouse(int min_width)
max_col = MAX(Columns - wp->w_wincol, wp->w_grid.cols);
}
}
- pum_anchor_grid = mouse_grid;
+ if (pum_grid.handle != 0 && mouse_grid == pum_grid.handle) {
+ // Repositioning the menu by right-clicking on itself
+ mouse_grid = pum_anchor_grid;
+ mouse_row += pum_row;
+ mouse_col += pum_left_col;
+ } else {
+ pum_anchor_grid = mouse_grid;
+ }
if (max_row - mouse_row > pum_size) {
// Enough space below the mouse row.
pum_above = false;