aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/api.txt15
-rw-r--r--runtime/doc/builtin.txt2
-rw-r--r--runtime/doc/news.txt3
-rw-r--r--runtime/doc/options.txt4
-rw-r--r--runtime/lua/vim/_meta/api.lua10
-rw-r--r--runtime/lua/vim/_meta/api_keysets.lua3
-rw-r--r--runtime/lua/vim/_meta/options.lua4
-rw-r--r--runtime/lua/vim/_meta/vimfn.lua2
-rw-r--r--src/nvim/api/keysets_defs.h5
-rw-r--r--src/nvim/api/vim.c26
-rw-r--r--src/nvim/buffer_defs.h3
-rw-r--r--src/nvim/eval.lua2
-rw-r--r--src/nvim/insexpand.c28
-rw-r--r--src/nvim/options.lua4
-rw-r--r--src/nvim/optionstr.c2
-rw-r--r--src/nvim/popupmenu.c210
-rw-r--r--src/nvim/popupmenu.h1
-rw-r--r--src/nvim/window.c2
-rw-r--r--src/nvim/winfloat.c10
-rw-r--r--test/functional/ui/popupmenu_spec.lua204
20 files changed, 519 insertions, 21 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 48bbdc33df..72ac357ac0 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -700,6 +700,21 @@ nvim_chan_send({chan}, {data}) *nvim_chan_send()*
• {chan} id of the channel
• {data} data to write. 8-bit clean: can contain NUL bytes.
+nvim_complete_set({index}, {*opts}) *nvim_complete_set()*
+ Set info for the completion candidate index. if the info was shown in a
+ window, then the window and buffer ids are returned for further
+ customization. If the text was not shown, an empty dict is returned.
+
+ Parameters: ~
+ • {index} the completion candidate index
+ • {opts} Optional parameters.
+ • info: (string) info text.
+
+ Return: ~
+ Dictionary containing these keys:
+ • winid: (number) floating window id
+ • bufnr: (number) buffer id in floating window
+
nvim_create_buf({listed}, {scratch}) *nvim_create_buf()*
Creates a new, empty, unnamed buffer.
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index 714320279a..98201c0eed 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -802,6 +802,8 @@ complete_info([{what}]) *complete_info()*
no item is selected when using the <Up> or
<Down> keys)
inserted Inserted string. [NOT IMPLEMENTED YET]
+ preview_winid Info floating preview window id.
+ preview_bufnr Info floating preview buffer id.
*complete_info_mode*
mode values are:
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index ad2de7a40a..406ac879eb 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -252,6 +252,9 @@ The following new APIs and features were added.
• |vim.text.hexencode()| and |vim.text.hexdecode()| convert strings to and
from byte representations.
+• 'completeopt' option supports "popup" flags to show extra information in
+ in floating window.
+
==============================================================================
CHANGED FEATURES *news-changed*
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index fda60eaab2..970f687c99 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1516,6 +1516,10 @@ A jump table for the options with a short description can be found at |Q_op|.
select one from the menu. Only works in combination with
"menu" or "menuone".
+ popup Show extra information about the currently selected
+ completion in a popup window. Only works in combination
+ with "menu" or "menuone". Overrides "preview".
+
*'completeslash'* *'csl'*
'completeslash' 'csl' string (default "")
local to buffer
diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index 231e1c3404..c8afbd58dd 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -773,6 +773,16 @@ function vim.api.nvim_command(command) end
--- @return string
function vim.api.nvim_command_output(command) end
+--- Set info for the completion candidate index. if the info was shown in a
+--- window, then the window and buffer ids are returned for further
+--- customization. If the text was not shown, an empty dict is returned.
+---
+--- @param index integer the completion candidate index
+--- @param opts vim.api.keyset.complete_set Optional parameters.
+--- • info: (string) info text.
+--- @return table<string,any>
+function vim.api.nvim_complete_set(index, opts) end
+
--- Create or get an autocommand group `autocmd-groups`.
--- To get an existing group id, do:
---
diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua
index f64cdb8afd..4ec8b03d30 100644
--- a/runtime/lua/vim/_meta/api_keysets.lua
+++ b/runtime/lua/vim/_meta/api_keysets.lua
@@ -68,6 +68,9 @@ error('Cannot require a meta file')
--- @class vim.api.keyset.cmd_opts
--- @field output? boolean
+--- @class vim.api.keyset.complete_set
+--- @field info? string
+
--- @class vim.api.keyset.context
--- @field types? any[]
diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index 5e65ca6b1b..c908d7ae54 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -1061,6 +1061,10 @@ vim.bo.cfu = vim.bo.completefunc
--- select one from the menu. Only works in combination with
--- "menu" or "menuone".
---
+--- popup Show extra information about the currently selected
+--- completion in a popup window. Only works in combination
+--- with "menu" or "menuone". Overrides "preview".
+---
--- @type string
vim.o.completeopt = "menu,preview"
vim.o.cot = vim.o.completeopt
diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
index ead5d8d13b..59d9836688 100644
--- a/runtime/lua/vim/_meta/vimfn.lua
+++ b/runtime/lua/vim/_meta/vimfn.lua
@@ -1024,6 +1024,8 @@ function vim.fn.complete_check() end
--- no item is selected when using the <Up> or
--- <Down> keys)
--- inserted Inserted string. [NOT IMPLEMENTED YET]
+--- preview_winid Info floating preview window id.
+--- preview_bufnr Info floating preview buffer id.
---
--- *complete_info_mode*
--- mode values are:
diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h
index c0daa0ca74..fa9a2cb013 100644
--- a/src/nvim/api/keysets_defs.h
+++ b/src/nvim/api/keysets_defs.h
@@ -346,3 +346,8 @@ typedef struct {
LuaRef on_input;
Boolean force_crlf;
} Dict(open_term);
+
+typedef struct {
+ OptionalKeys is_set__complete_set_;
+ String info;
+} Dict(complete_set);
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 2f3d527b9e..91f908bb88 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -2300,3 +2300,29 @@ void nvim_error_event(uint64_t channel_id, Integer lvl, String data)
// if we fork nvim processes as async workers
ELOG("async error on channel %" PRId64 ": %s", channel_id, data.size ? data.data : "");
}
+
+/// Set info for the completion candidate index.
+/// if the info was shown in a window, then the
+/// window and buffer ids are returned for further
+/// customization. If the text was not shown, an
+/// empty dict is returned.
+///
+/// @param index the completion candidate index
+/// @param opts Optional parameters.
+/// - info: (string) info text.
+/// @return Dictionary containing these keys:
+/// - winid: (number) floating window id
+/// - bufnr: (number) buffer id in floating window
+Dictionary nvim_complete_set(Integer index, Dict(complete_set) *opts)
+ FUNC_API_SINCE(12)
+{
+ Dictionary rv = ARRAY_DICT_INIT;
+ if (HAS_KEY(opts, complete_set, info)) {
+ win_T *wp = pum_set_info((int)index, opts->info.data);
+ if (wp) {
+ PUT(rv, "winid", WINDOW_OBJ(wp->handle));
+ PUT(rv, "bufnr", BUFFER_OBJ(wp->w_buffer->handle));
+ }
+ }
+ return rv;
+}
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index beb3ec95b8..dc93243fad 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -1286,7 +1286,8 @@ struct window_S {
ScreenGrid w_grid; // the grid specific to the window
ScreenGrid w_grid_alloc; // the grid specific to the window
bool w_pos_changed; // true if window position changed
- bool w_floating; ///< whether the window is floating
+ bool w_floating; ///< whether the window is floating
+ bool w_float_is_info; // the floating window is info float
FloatConfig w_float_config;
// w_fraction is the fractional row of the cursor within the window, from
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index a86ca9c1ea..984935d009 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -1373,6 +1373,8 @@ M.funcs = {
no item is selected when using the <Up> or
<Down> keys)
inserted Inserted string. [NOT IMPLEMENTED YET]
+ preview_winid Info floating preview window id.
+ preview_bufnr Info floating preview buffer id.
*complete_info_mode*
mode values are:
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 8791aeb10c..54651eb48d 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -63,6 +63,7 @@
#include "nvim/undo.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
+#include "nvim/winfloat.h"
// Definitions used for CTRL-X submode.
// Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[]
@@ -1290,6 +1291,28 @@ void ins_compl_show_pum(void)
}
}
+/// used for set or update info
+void compl_set_info(int pum_idx)
+{
+ compl_T *comp = compl_first_match;
+ char *pum_text = compl_match_array[pum_idx].pum_text;
+
+ while (comp != NULL) {
+ if (pum_text == comp->cp_str
+ || pum_text == comp->cp_text[CPT_ABBR]) {
+ comp->cp_text[CPT_INFO] = compl_match_array[pum_idx].pum_info;
+
+ // if comp is current match update completed_item value
+ if (comp == compl_curr_match) {
+ dict_T *dict = ins_compl_dict_alloc(compl_curr_match);
+ set_vim_var_dict(VV_COMPLETED_ITEM, dict);
+ }
+ break;
+ }
+ comp = comp->cp_next;
+ }
+}
+
#define DICT_FIRST (1) ///< use just first element in "dict"
#define DICT_EXACT (2) ///< "dict" is the exact name of a file
@@ -2813,6 +2836,11 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
ret = tv_dict_add_nr(retdict, S_LEN("selected"),
(compl_curr_match != NULL)
? compl_curr_match->cp_number - 1 : -1);
+ win_T *wp = win_float_find_preview();
+ if (wp != NULL) {
+ tv_dict_add_nr(retdict, S_LEN("preview_winid"), wp->handle);
+ tv_dict_add_nr(retdict, S_LEN("preview_bufnr"), wp->w_buffer->handle);
+ }
}
(void)ret;
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index edd7423e69..2621cc2eb1 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -1450,6 +1450,10 @@ return {
noselect Do not select a match in the menu, force the user to
select one from the menu. Only works in combination with
"menu" or "menuone".
+
+ popup Show extra information about the currently selected
+ completion in a popup window. Only works in combination
+ with "menu" or "menuone". Overrides "preview".
]=],
expand_cb = 'expand_set_completeopt',
full_name = 'completeopt',
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index 002cc68a3b..1aab9d4344 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -116,7 +116,7 @@ static char *(p_fdm_values[]) = { "manual", "expr", "marker", "indent",
"syntax", "diff", NULL };
static char *(p_fcl_values[]) = { "all", NULL };
static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview", "noinsert", "noselect",
- NULL };
+ "popup", NULL };
#ifdef BACKSLASH_IN_FILENAME
static char *(p_csl_values[]) = { "slash", "backslash", NULL };
#endif
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index 56fc16a82e..a3e92fb595 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -8,8 +8,11 @@
#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"
@@ -29,6 +32,8 @@
#include "nvim/move.h"
#include "nvim/option.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"
@@ -37,6 +42,7 @@
#include "nvim/ui_compositor.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"
@@ -654,6 +660,142 @@ void pum_redraw(void)
}
}
+/// create a floting preview window for info
+/// @return NULL when no enough room to show
+static win_T *pum_create_float_preview(bool enter)
+{
+ FloatConfig config = FLOAT_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_FREE | 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_float_config.width < width && wp->w_float_config.col > pum_col) {
+ if ((pum_col - 2) > width) {
+ wp->w_float_config.width = width;
+ wp->w_float_config.col = pum_col - width - 1;
+ }
+ }
+ wp->w_float_config.width = MIN(wp->w_float_config.width, width);
+ wp->w_float_config.height = MIN(Rows, height);
+ wp->w_float_config.hide = false;
+ win_config_float(wp, wp->w_float_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, (linenr_T)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:
@@ -670,6 +812,7 @@ static bool pum_set_selected(int n, int repeat)
{
int resized = false;
int context = pum_height / 2;
+ int prev_selected = pum_selected;
pum_selected = n;
@@ -718,6 +861,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 +877,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 +894,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)
@@ -777,22 +943,11 @@ static bool pum_set_selected(int n, int repeat)
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 +960,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 floting 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 +997,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 +1026,10 @@ static bool pum_set_selected(int n, int repeat)
}
}
}
+
+ if (use_float) {
+ unblock_autocmds();
+ }
}
}
@@ -892,6 +1064,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);
+ }
}
}
diff --git a/src/nvim/popupmenu.h b/src/nvim/popupmenu.h
index 24a3f8713a..dc741d1b77 100644
--- a/src/nvim/popupmenu.h
+++ b/src/nvim/popupmenu.h
@@ -2,6 +2,7 @@
#include <stdbool.h>
+#include "nvim/buffer_defs.h"
#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
#include "nvim/grid_defs.h"
#include "nvim/macros_defs.h"
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 98c62d6f44..618a3f760e 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -4978,7 +4978,7 @@ void free_wininfo(wininfo_T *wip, buf_T *bp)
/// Remove window 'wp' from the window list and free the structure.
///
/// @param tp tab page "win" is in, NULL for current
-static void win_free(win_T *wp, tabpage_T *tp)
+void win_free(win_T *wp, tabpage_T *tp)
{
pmap_del(int)(&window_handles, wp->handle, NULL);
clearFolding(wp);
diff --git a/src/nvim/winfloat.c b/src/nvim/winfloat.c
index 0eea21eb9d..4efff3cfab 100644
--- a/src/nvim/winfloat.c
+++ b/src/nvim/winfloat.c
@@ -286,3 +286,13 @@ bool win_float_valid(const win_T *win)
}
return false;
}
+
+win_T *win_float_find_preview(void)
+{
+ for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) {
+ if (wp->w_float_is_info) {
+ return wp;
+ }
+ }
+ return NULL;
+}
diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua
index 042b7dbabf..956173b86e 100644
--- a/test/functional/ui/popupmenu_spec.lua
+++ b/test/functional/ui/popupmenu_spec.lua
@@ -1431,7 +1431,7 @@ describe('builtin popupmenu', function()
feed('<C-N>')
screen:expect([[
1info |
- |
+ {1:~ }|
{1:~ }|
{3:[Scratch] [Preview] }|
one^ |
@@ -1469,6 +1469,208 @@ describe('builtin popupmenu', function()
end)
end
+ describe("floating window preview #popup", function()
+ it('pum popup preview', function()
+ --row must > 10
+ screen:try_resize(30, 11)
+ exec([[
+ funct Omni_test(findstart, base)
+ if a:findstart
+ return col(".") - 1
+ endif
+ return [#{word: "one", info: "1info"}, #{word: "two", info: "2info"}, #{word: "three"}]
+ endfunc
+ set omnifunc=Omni_test
+ set completeopt=menu,popup
+
+ funct Set_info()
+ let comp_info = complete_info()
+ if comp_info['selected'] == 2
+ call nvim_complete_set(comp_info['selected'], {"info": "3info"})
+ endif
+ endfunc
+ autocmd CompleteChanged * call Set_info()
+ ]])
+ feed('Gi<C-x><C-o>')
+
+ --floating preview in right
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [3:------------------------------]|
+ ## grid 2
+ one^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ {2:-- }{5:match 1 of 3} |
+ ## grid 4
+ {n:1info}|
+ {n: }|
+ ## grid 5
+ {s:one }|
+ {n:two }|
+ {n:three }|
+ ]], float_pos={
+ [5] = {{id = -1}, "NW", 2, 1, 0, false, 100};
+ [4] = {{id = 1001}, "NW", 1, 1, 15, true, 50};
+ }}
+ else
+ screen:expect{grid=[[
+ one^ |
+ {s:one }{n:1info}{1: }|
+ {n:two }{1: }|
+ {n:three }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- }{5:match 1 of 3} |
+ ]], unchanged = true}
+ end
+
+ -- test nvim_complete_set_info
+ feed('<C-N><C-N>')
+ helpers.sleep(10)
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [3:------------------------------]|
+ ## grid 2
+ three^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ {2:-- }{5:match 3 of 3} |
+ ## grid 4
+ {n:3info}|
+ {n: }|
+ ## grid 5
+ {n:one }|
+ {n:two }|
+ {s:three }|
+ ]], float_pos={
+ [5] = {{id = -1}, "NW", 2, 1, 0, false, 100};
+ [4] = {{id = 1001}, "NW", 1, 1, 15, true, 50};
+ }}
+ else
+ screen:expect{grid=[[
+ three^ |
+ {n:one 3info}{1: }|
+ {n:two }{1: }|
+ {s:three }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- }{5:match 3 of 3} |
+ ]]}
+ end
+ -- make sure info has set
+ feed('<C-y>')
+ eq('3info', exec_lua('return vim.v.completed_item.info'))
+
+ -- preview in left
+ feed('<ESC>cc')
+ insert(('test'):rep(5))
+ feed('i<C-x><C-o>')
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [3:------------------------------]|
+ ## grid 2
+ itesttesttesttesttesone^t |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ {2:-- }{5:match 1 of 3} |
+ ## grid 5
+ {s: one }|
+ {n: two }|
+ {n: three }|
+ ## grid 6
+ {n:1info}|
+ {n: }|
+ ]], float_pos={
+ [5] = {{id = -1}, "NW", 2, 1, 19, false, 100};
+ [6] = {{id = 1002}, "NW", 1, 1, 1, true, 50};
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 23, linecount = 1, sum_scroll_delta = 0};
+ [6] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
+ }}
+ else
+ screen:expect{grid=[[
+ itesttesttesttesttesone^t |
+ {1:~}{n:1info}{1: }{s: one }{1: }|
+ {1:~}{n: }{1: }{n: two }{1: }|
+ {1:~ }{n: three }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- }{5:match 1 of 3} |
+ ]]}
+ end
+ end)
+ end)
+
it('with vsplits', function()
screen:try_resize(32, 8)
insert('aaa aab aac\n')