mirror of
https://github.com/neovim/neovim.git
synced 2024-09-17 20:58:20 -04:00
feat(ui): config popupmenu by use completepopup
This commit is contained in:
parent
f3677c71f0
commit
7e3b565ca8
@ -143,6 +143,8 @@ OPTIONS
|
||||
|
||||
• 'completeopt' flag "fuzzy" enables |fuzzy-matching| during |ins-completion|.
|
||||
• 'tabclose' controls which tab page to focus when closing a tab page.
|
||||
• Added new option 'completepopup' for config completion window and info
|
||||
preview floating window.
|
||||
|
||||
PERFORMANCE
|
||||
|
||||
|
@ -1571,6 +1571,19 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
list of alternatives, but not how the candidates are
|
||||
collected (using different completion types).
|
||||
|
||||
*'completepopup'* *'cpp'*
|
||||
'completepopup' 'cpp' string (default "")
|
||||
global
|
||||
This option is used to set the completion window and info floating
|
||||
preview window. The option is a comma-separated list of values:
|
||||
`align`, `border`, `title`, `titlepos`, `footer`, `footerpos`.
|
||||
see |nvim_open_win()|.
|
||||
When the "align" value is "item" then the info floating preview window
|
||||
is positioned close to the selected item. Changing the selection will
|
||||
also move the popup. When "align" is "menu" then the floating preview
|
||||
window is aligned with the top of the menu if the menu is below the text,
|
||||
and the bottom of the menu otherwise.
|
||||
|
||||
*'completeslash'* *'csl'*
|
||||
'completeslash' 'csl' string (default "")
|
||||
local to buffer
|
||||
|
@ -607,7 +607,6 @@ These legacy Vim features are not yet implemented:
|
||||
|
||||
- *:gui*
|
||||
- *:gvim*
|
||||
- *'completepopup'*
|
||||
- *'previewpopup'*
|
||||
|
||||
==============================================================================
|
||||
|
16
runtime/lua/vim/_meta/options.lua
generated
16
runtime/lua/vim/_meta/options.lua
generated
@ -1103,6 +1103,22 @@ vim.bo.cot = vim.bo.completeopt
|
||||
vim.go.completeopt = vim.o.completeopt
|
||||
vim.go.cot = vim.go.completeopt
|
||||
|
||||
--- This option is used to set the completion window and info floating
|
||||
--- preview window. The option is a comma-separated list of values:
|
||||
--- `align`, `border`, `title`, `titlepos`, `footer`, `footerpos`.
|
||||
--- see `nvim_open_win()`.
|
||||
--- When the "align" value is "item" then the info floating preview window
|
||||
--- is positioned close to the selected item. Changing the selection will
|
||||
--- also move the popup. When "align" is "menu" then the floating preview
|
||||
--- window is aligned with the top of the menu if the menu is below the text,
|
||||
--- and the bottom of the menu otherwise.
|
||||
---
|
||||
--- @type string
|
||||
vim.o.completepopup = ""
|
||||
vim.o.cpp = vim.o.completepopup
|
||||
vim.go.completepopup = vim.o.completepopup
|
||||
vim.go.cpp = vim.go.completepopup
|
||||
|
||||
--- only for MS-Windows
|
||||
--- When this option is set it overrules 'shellslash' for completion:
|
||||
--- - When this option is set to "slash", a forward slash is used for path
|
||||
|
@ -836,8 +836,8 @@ static bool parse_float_bufpos(Array bufpos, lpos_T *out)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void parse_bordertext(Object bordertext, BorderTextType bordertext_type, WinConfig *fconfig,
|
||||
Error *err)
|
||||
void parse_bordertext(Object bordertext, BorderTextType bordertext_type, WinConfig *fconfig,
|
||||
Error *err)
|
||||
{
|
||||
if (bordertext.type != kObjectTypeString && bordertext.type != kObjectTypeArray) {
|
||||
api_set_error(err, kErrorTypeValidation, "title/footer must be string or array");
|
||||
@ -891,8 +891,8 @@ static void parse_bordertext(Object bordertext, BorderTextType bordertext_type,
|
||||
*is_present = true;
|
||||
}
|
||||
|
||||
static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertext_type,
|
||||
WinConfig *fconfig, Error *err)
|
||||
bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertext_type, WinConfig *fconfig,
|
||||
Error *err)
|
||||
{
|
||||
AlignTextPos *align;
|
||||
switch (bordertext_type) {
|
||||
@ -931,7 +931,7 @@ static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertex
|
||||
return true;
|
||||
}
|
||||
|
||||
static void parse_border_style(Object style, WinConfig *fconfig, Error *err)
|
||||
void parse_border_style(Object style, WinConfig *fconfig, Error *err)
|
||||
{
|
||||
struct {
|
||||
const char *name;
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "nvim/api/keysets_defs.h" // IWYU pragma: keep
|
||||
#include "nvim/api/private/defs.h" // IWYU pragma: keep
|
||||
#include "nvim/buffer_defs.h" // IWYU pragma: keep
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "api/win_config.h.generated.h"
|
||||
|
@ -112,6 +112,7 @@
|
||||
#include "nvim/statusline.h"
|
||||
#include "nvim/strings.h"
|
||||
#include "nvim/syntax.h"
|
||||
#include "nvim/syntax_defs.h"
|
||||
#include "nvim/terminal.h"
|
||||
#include "nvim/types_defs.h"
|
||||
#include "nvim/ui.h"
|
||||
@ -628,7 +629,9 @@ int update_screen(void)
|
||||
win_grid_alloc(wp);
|
||||
|
||||
if (wp->w_redr_border || wp->w_redr_type >= UPD_NOT_VALID) {
|
||||
win_redr_border(wp);
|
||||
// win_redr_border(wp);
|
||||
grid_draw_border(&wp->w_grid_alloc, wp->w_config, wp->w_border_adj, (int)wp->w_p_winbl,
|
||||
wp->w_ns_hl_attr);
|
||||
}
|
||||
|
||||
if (wp->w_redr_type != 0) {
|
||||
@ -707,112 +710,6 @@ void end_search_hl(void)
|
||||
screen_search_hl.rm.regprog = NULL;
|
||||
}
|
||||
|
||||
static void win_redr_bordertext(win_T *wp, VirtText vt, int col, BorderTextType bt)
|
||||
{
|
||||
for (size_t i = 0; i < kv_size(vt);) {
|
||||
int attr = -1;
|
||||
char *text = next_virt_text_chunk(vt, &i, &attr);
|
||||
if (text == NULL) {
|
||||
break;
|
||||
}
|
||||
if (attr == -1) { // No highlight specified.
|
||||
attr = wp->w_ns_hl_attr[bt == kBorderTextTitle ? HLF_BTITLE : HLF_BFOOTER];
|
||||
}
|
||||
attr = hl_apply_winblend(wp, attr);
|
||||
col += grid_line_puts(col, text, -1, attr);
|
||||
}
|
||||
}
|
||||
|
||||
int win_get_bordertext_col(int total_col, int text_width, AlignTextPos align)
|
||||
{
|
||||
switch (align) {
|
||||
case kAlignLeft:
|
||||
return 1;
|
||||
case kAlignCenter:
|
||||
return MAX((total_col - text_width) / 2 + 1, 1);
|
||||
case kAlignRight:
|
||||
return MAX(total_col - text_width + 1, 1);
|
||||
}
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
static void win_redr_border(win_T *wp)
|
||||
{
|
||||
wp->w_redr_border = false;
|
||||
if (!(wp->w_floating && wp->w_config.border)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScreenGrid *grid = &wp->w_grid_alloc;
|
||||
|
||||
schar_T chars[8];
|
||||
for (int i = 0; i < 8; i++) {
|
||||
chars[i] = schar_from_str(wp->w_config.border_chars[i]);
|
||||
}
|
||||
int *attrs = wp->w_config.border_attr;
|
||||
|
||||
int *adj = wp->w_border_adj;
|
||||
int irow = wp->w_height_inner + wp->w_winbar_height;
|
||||
int icol = wp->w_width_inner;
|
||||
|
||||
if (adj[0]) {
|
||||
grid_line_start(grid, 0);
|
||||
if (adj[3]) {
|
||||
grid_line_put_schar(0, chars[0], attrs[0]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < icol; i++) {
|
||||
grid_line_put_schar(i + adj[3], chars[1], attrs[1]);
|
||||
}
|
||||
|
||||
if (wp->w_config.title) {
|
||||
int title_col = win_get_bordertext_col(icol, wp->w_config.title_width,
|
||||
wp->w_config.title_pos);
|
||||
win_redr_bordertext(wp, wp->w_config.title_chunks, title_col, kBorderTextTitle);
|
||||
}
|
||||
if (adj[1]) {
|
||||
grid_line_put_schar(icol + adj[3], chars[2], attrs[2]);
|
||||
}
|
||||
grid_line_flush();
|
||||
}
|
||||
|
||||
for (int i = 0; i < irow; i++) {
|
||||
if (adj[3]) {
|
||||
grid_line_start(grid, i + adj[0]);
|
||||
grid_line_put_schar(0, chars[7], attrs[7]);
|
||||
grid_line_flush();
|
||||
}
|
||||
if (adj[1]) {
|
||||
int ic = (i == 0 && !adj[0] && chars[2]) ? 2 : 3;
|
||||
grid_line_start(grid, i + adj[0]);
|
||||
grid_line_put_schar(icol + adj[3], chars[ic], attrs[ic]);
|
||||
grid_line_flush();
|
||||
}
|
||||
}
|
||||
|
||||
if (adj[2]) {
|
||||
grid_line_start(grid, irow + adj[0]);
|
||||
if (adj[3]) {
|
||||
grid_line_put_schar(0, chars[6], attrs[6]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < icol; i++) {
|
||||
int ic = (i == 0 && !adj[3] && chars[6]) ? 6 : 5;
|
||||
grid_line_put_schar(i + adj[3], chars[ic], attrs[ic]);
|
||||
}
|
||||
|
||||
if (wp->w_config.footer) {
|
||||
int footer_col = win_get_bordertext_col(icol, wp->w_config.footer_width,
|
||||
wp->w_config.footer_pos);
|
||||
win_redr_bordertext(wp, wp->w_config.footer_chunks, footer_col, kBorderTextFooter);
|
||||
}
|
||||
if (adj[1]) {
|
||||
grid_line_put_schar(icol + adj[3], chars[4], attrs[4]);
|
||||
}
|
||||
grid_line_flush();
|
||||
}
|
||||
}
|
||||
|
||||
/// Set cursor to its position in the current window.
|
||||
void setcursor(void)
|
||||
{
|
||||
|
102
src/nvim/grid.c
102
src/nvim/grid.c
@ -1067,6 +1067,108 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
|
||||
}
|
||||
}
|
||||
|
||||
static void grid_draw_bordertext(VirtText vt, int col, int winbl, const int *hl_attr,
|
||||
BorderTextType bt)
|
||||
{
|
||||
for (size_t i = 0; i < kv_size(vt);) {
|
||||
int attr = -1;
|
||||
char *text = next_virt_text_chunk(vt, &i, &attr);
|
||||
if (text == NULL) {
|
||||
break;
|
||||
}
|
||||
if (attr == -1) { // No highlight specified.
|
||||
attr = hl_attr[bt == kBorderTextTitle ? HLF_BTITLE : HLF_BFOOTER];
|
||||
}
|
||||
attr = hl_apply_winblend(winbl, attr);
|
||||
col += grid_line_puts(col, text, -1, attr);
|
||||
}
|
||||
}
|
||||
|
||||
static int get_bordertext_col(int total_col, int text_width, AlignTextPos align)
|
||||
{
|
||||
switch (align) {
|
||||
case kAlignLeft:
|
||||
return 1;
|
||||
case kAlignCenter:
|
||||
return MAX((total_col - text_width) / 2 + 1, 1);
|
||||
case kAlignRight:
|
||||
return MAX(total_col - text_width + 1, 1);
|
||||
}
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
/// draw border on floating window grid
|
||||
void grid_draw_border(ScreenGrid *grid, WinConfig config, int *adj, int winbl, int *hl_attr)
|
||||
{
|
||||
int *attrs = config.border_attr;
|
||||
schar_T chars[8];
|
||||
if (!hl_attr) {
|
||||
hl_attr = hl_attr_active;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
chars[i] = schar_from_str(config.border_chars[i]);
|
||||
}
|
||||
|
||||
int irow = grid->rows - adj[0] - adj[2];
|
||||
int icol = grid->cols - adj[1] - adj[3];
|
||||
|
||||
if (adj[0]) {
|
||||
grid_line_start(grid, 0);
|
||||
if (adj[3]) {
|
||||
grid_line_put_schar(0, chars[0], attrs[0]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < icol; i++) {
|
||||
grid_line_put_schar(i + adj[3], chars[1], attrs[1]);
|
||||
}
|
||||
|
||||
if (config.title) {
|
||||
int title_col = get_bordertext_col(icol, config.title_width, config.title_pos);
|
||||
grid_draw_bordertext(config.title_chunks, title_col, winbl, hl_attr, kBorderTextTitle);
|
||||
}
|
||||
if (adj[1]) {
|
||||
grid_line_put_schar(icol + adj[3], chars[2], attrs[2]);
|
||||
}
|
||||
grid_line_flush();
|
||||
}
|
||||
|
||||
for (int i = 0; i < irow; i++) {
|
||||
if (adj[3]) {
|
||||
grid_line_start(grid, i + adj[0]);
|
||||
grid_line_put_schar(0, chars[7], attrs[7]);
|
||||
grid_line_flush();
|
||||
}
|
||||
if (adj[1]) {
|
||||
int ic = (i == 0 && !adj[0] && chars[2]) ? 2 : 3;
|
||||
grid_line_start(grid, i + adj[0]);
|
||||
grid_line_put_schar(icol + adj[3], chars[ic], attrs[ic]);
|
||||
grid_line_flush();
|
||||
}
|
||||
}
|
||||
|
||||
if (adj[2]) {
|
||||
grid_line_start(grid, irow + adj[0]);
|
||||
if (adj[3]) {
|
||||
grid_line_put_schar(0, chars[6], attrs[6]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < icol; i++) {
|
||||
int ic = (i == 0 && !adj[3] && chars[6]) ? 6 : 5;
|
||||
grid_line_put_schar(i + adj[3], chars[ic], attrs[ic]);
|
||||
}
|
||||
|
||||
if (config.footer) {
|
||||
int footer_col = get_bordertext_col(icol, config.footer_width, config.footer_pos);
|
||||
grid_draw_bordertext(config.footer_chunks, footer_col, winbl, hl_attr, kBorderTextFooter);
|
||||
}
|
||||
if (adj[1]) {
|
||||
grid_line_put_schar(icol + adj[3], chars[4], attrs[4]);
|
||||
}
|
||||
grid_line_flush();
|
||||
}
|
||||
}
|
||||
|
||||
static void linecopy(ScreenGrid *grid, int to, int from, int col, int width)
|
||||
{
|
||||
unsigned off_to = (unsigned)(grid->line_offset[to] + (size_t)col);
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h> // IWYU pragma: keep
|
||||
|
||||
#include "nvim/buffer_defs.h" // IWYU pragma: keep
|
||||
#include "nvim/grid_defs.h" // IWYU pragma: keep
|
||||
#include "nvim/macros_defs.h"
|
||||
#include "nvim/pos_defs.h"
|
||||
|
@ -332,12 +332,12 @@ int hl_get_ui_attr(int ns_id, int idx, int final_id, bool optional)
|
||||
/// @param attr The original attribute code.
|
||||
///
|
||||
/// @return The attribute code with 'winblend' applied.
|
||||
int hl_apply_winblend(win_T *wp, int attr)
|
||||
int hl_apply_winblend(int winbl, int attr)
|
||||
{
|
||||
HlEntry entry = attr_entry(attr);
|
||||
// if blend= attribute is not set, 'winblend' value overrides it.
|
||||
if (entry.attr.hl_blend == -1 && wp->w_p_winbl > 0) {
|
||||
entry.attr.hl_blend = (int)wp->w_p_winbl;
|
||||
if (entry.attr.hl_blend == -1 && winbl > 0) {
|
||||
entry.attr.hl_blend = winbl;
|
||||
attr = get_attr_entry(entry);
|
||||
}
|
||||
return attr;
|
||||
@ -379,7 +379,7 @@ void update_window_hl(win_T *wp, bool invalid)
|
||||
}
|
||||
|
||||
if (wp->w_floating) {
|
||||
wp->w_hl_attr_normal = hl_apply_winblend(wp, wp->w_hl_attr_normal);
|
||||
wp->w_hl_attr_normal = hl_apply_winblend((int)wp->w_p_winbl, wp->w_hl_attr_normal);
|
||||
}
|
||||
|
||||
wp->w_config.shadow = false;
|
||||
@ -390,7 +390,7 @@ void update_window_hl(win_T *wp, bool invalid)
|
||||
attr = hl_get_ui_attr(ns_id, HLF_BORDER,
|
||||
wp->w_config.border_hl_ids[i], false);
|
||||
}
|
||||
attr = hl_apply_winblend(wp, attr);
|
||||
attr = hl_apply_winblend((int)wp->w_p_winbl, attr);
|
||||
if (syn_attr2entry(attr).hl_blend > 0) {
|
||||
wp->w_config.shadow = true;
|
||||
}
|
||||
@ -411,7 +411,7 @@ void update_window_hl(win_T *wp, bool invalid)
|
||||
}
|
||||
|
||||
if (wp->w_floating) {
|
||||
wp->w_hl_attr_normalnc = hl_apply_winblend(wp, wp->w_hl_attr_normalnc);
|
||||
wp->w_hl_attr_normalnc = hl_apply_winblend((int)wp->w_p_winbl, wp->w_hl_attr_normalnc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -443,6 +443,7 @@ EXTERN unsigned cot_flags; ///< flags from 'completeopt'
|
||||
#define COT_NOINSERT 0x020 // false: select & insert, true: noinsert
|
||||
#define COT_NOSELECT 0x040 // false: select & insert, true: noselect
|
||||
#define COT_FUZZY 0x080 // true: fuzzy match enabled
|
||||
EXTERN char *p_cpp; ///< 'completepopup'
|
||||
#ifdef BACKSLASH_IN_FILENAME
|
||||
EXTERN char *p_csl; ///< 'completeslash'
|
||||
#endif
|
||||
|
@ -1480,6 +1480,28 @@ return {
|
||||
type = 'string',
|
||||
varname = 'p_cot',
|
||||
},
|
||||
{
|
||||
abbreviation = 'cpp',
|
||||
cb = 'did_set_completepopup',
|
||||
defaults = { if_true = '' },
|
||||
desc = [=[
|
||||
This option is used to set the completion window and info floating
|
||||
preview window. The option is a comma-separated list of values:
|
||||
`align`, `border`, `title`, `titlepos`, `footer`, `footerpos`.
|
||||
see |nvim_open_win()|.
|
||||
When the "align" value is "item" then the info floating preview window
|
||||
is positioned close to the selected item. Changing the selection will
|
||||
also move the popup. When "align" is "menu" then the floating preview
|
||||
window is aligned with the top of the menu if the menu is below the text,
|
||||
and the bottom of the menu otherwise.
|
||||
]=],
|
||||
full_name = 'completepopup',
|
||||
list = 'onecomma',
|
||||
scope = { 'global' },
|
||||
short_desc = N_('options for completion window'),
|
||||
type = 'string',
|
||||
varname = 'p_cpp',
|
||||
},
|
||||
{
|
||||
abbreviation = 'csl',
|
||||
cb = 'did_set_completeslash',
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "nvim/cmdexpand_defs.h"
|
||||
#include "nvim/cursor.h"
|
||||
#include "nvim/cursor_shape.h"
|
||||
#include "nvim/decoration.h"
|
||||
#include "nvim/diff.h"
|
||||
#include "nvim/digraph.h"
|
||||
#include "nvim/drawscreen.h"
|
||||
@ -38,6 +39,7 @@
|
||||
#include "nvim/option_vars.h"
|
||||
#include "nvim/optionstr.h"
|
||||
#include "nvim/os/os.h"
|
||||
#include "nvim/popupmenu.h"
|
||||
#include "nvim/pos_defs.h"
|
||||
#include "nvim/regexp.h"
|
||||
#include "nvim/regexp_defs.h"
|
||||
@ -1027,6 +1029,23 @@ int expand_set_completeopt(optexpand_T *args, int *numMatches, char ***matches)
|
||||
matches);
|
||||
}
|
||||
|
||||
const char *did_set_completepopup(optset_T *args FUNC_ATTR_UNUSED)
|
||||
{
|
||||
WinConfig config = WIN_CONFIG_INIT;
|
||||
int adj[4];
|
||||
bool result = parse_completepopup(&config, adj);
|
||||
if (config.title) {
|
||||
clear_virttext(&config.title_chunks);
|
||||
}
|
||||
if (config.footer) {
|
||||
clear_virttext(&config.footer_chunks);
|
||||
}
|
||||
if (!result) {
|
||||
return e_invarg;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef BACKSLASH_IN_FILENAME
|
||||
/// The 'completeslash' option is changed.
|
||||
const char *did_set_completeslash(optset_T *args)
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/api/vim.h"
|
||||
#include "nvim/api/win_config.h"
|
||||
#include "nvim/ascii_defs.h"
|
||||
#include "nvim/autocmd.h"
|
||||
#include "nvim/buffer.h"
|
||||
@ -18,6 +19,7 @@
|
||||
#include "nvim/change.h"
|
||||
#include "nvim/charset.h"
|
||||
#include "nvim/cmdexpand.h"
|
||||
#include "nvim/decoration.h"
|
||||
#include "nvim/drawscreen.h"
|
||||
#include "nvim/edit.h"
|
||||
#include "nvim/errors.h"
|
||||
@ -33,6 +35,7 @@
|
||||
#include "nvim/grid.h"
|
||||
#include "nvim/highlight.h"
|
||||
#include "nvim/highlight_defs.h"
|
||||
#include "nvim/highlight_group.h"
|
||||
#include "nvim/insexpand.h"
|
||||
#include "nvim/keycodes.h"
|
||||
#include "nvim/mbyte.h"
|
||||
@ -80,6 +83,9 @@ static int pum_col; // left column of pum, right column if 'righ
|
||||
static int pum_left_col; // left column of pum, before padding or scrollbar
|
||||
static bool pum_above; // pum is drawn above cursor line
|
||||
|
||||
static PumInfoAlign pum_align = kInfoAlignMenu; // float preview align
|
||||
static bool pum_has_border = false; // pum grid has border
|
||||
|
||||
static bool pum_is_visible = false;
|
||||
static bool pum_is_drawn = false;
|
||||
static bool pum_external = false;
|
||||
@ -234,11 +240,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 = size < PUM_DEF_HEIGHT ? size : PUM_DEF_HEIGHT;
|
||||
|
||||
if (p_ph > 0 && pum_height > p_ph) {
|
||||
pum_height = (int)p_ph;
|
||||
@ -566,17 +568,30 @@ void pum_redraw(void)
|
||||
}
|
||||
}
|
||||
|
||||
WinConfig fconfig = WIN_CONFIG_INIT;
|
||||
int border_adj[4];
|
||||
parse_completepopup(&fconfig, border_adj);
|
||||
pum_has_border = fconfig.border;
|
||||
// has border
|
||||
int pum_extra_border = fconfig.border ? 2 : 0;
|
||||
// avoid border out of screen
|
||||
if (pum_extra_border && pum_col + pum_extra_border + pum_width > Columns) {
|
||||
pum_col -= 2;
|
||||
}
|
||||
|
||||
grid_assign_handle(&pum_grid);
|
||||
|
||||
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);
|
||||
pum_height + pum_extra_border, grid_width + pum_extra_border, 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) {
|
||||
grid_alloc(&pum_grid, pum_height, grid_width, !invalid_grid, false);
|
||||
grid_alloc(&pum_grid, pum_height + pum_extra_border, grid_width + pum_extra_border,
|
||||
!invalid_grid, false);
|
||||
ui_call_grid_resize(pum_grid.handle, pum_grid.cols, pum_grid.rows);
|
||||
} else if (invalid_grid) {
|
||||
grid_invalidate(&pum_grid);
|
||||
@ -588,6 +603,19 @@ void pum_redraw(void)
|
||||
pum_row - row_off, pum_left_col, false, pum_grid.zindex);
|
||||
}
|
||||
|
||||
if (fconfig.border) {
|
||||
grid_draw_border(&pum_grid, fconfig, border_adj, 0, NULL);
|
||||
row++;
|
||||
col_off++;
|
||||
|
||||
if (fconfig.title) {
|
||||
clear_virttext(&fconfig.title_chunks);
|
||||
}
|
||||
if (fconfig.footer) {
|
||||
clear_virttext(&fconfig.footer_chunks);
|
||||
}
|
||||
}
|
||||
|
||||
// Never display more than we have
|
||||
if (pum_first > pum_size - pum_height) {
|
||||
pum_first = pum_size - pum_height;
|
||||
@ -816,9 +844,8 @@ static void pum_preview_set_text(buf_T *buf, char *info, linenr_T *lnum, int *ma
|
||||
/// adjust floating info preview window position
|
||||
static void pum_adjust_info_position(win_T *wp, int height, int width)
|
||||
{
|
||||
int col = pum_col + pum_width + pum_scrollbar + 1;
|
||||
// TODO(glepnir): support config align border by using completepopup
|
||||
// align menu
|
||||
int extra_width = pum_has_border ? 2 : 0;
|
||||
int col = pum_col + pum_width + pum_scrollbar + 1 + extra_width;
|
||||
int right_extra = Columns - col;
|
||||
int left_extra = pum_col - 2;
|
||||
|
||||
@ -835,7 +862,12 @@ static void pum_adjust_info_position(win_T *wp, int height, int width)
|
||||
}
|
||||
// when pum_above is SW otherwise is NW
|
||||
wp->w_config.anchor = pum_above ? kFloatAnchorSouth : 0;
|
||||
wp->w_config.row = pum_above ? pum_row + height : pum_row;
|
||||
if (pum_align == kInfoAlignMenu) {
|
||||
wp->w_config.row = (pum_above ? pum_row + height : pum_row) + (extra_width > 0 ? 1 : 0);
|
||||
} else {
|
||||
wp->w_config.row = pum_row + pum_selected - pum_first + 1 + (pum_above
|
||||
&& pum_has_border ? 1 : 0);
|
||||
}
|
||||
wp->w_config.height = MIN(Rows, height);
|
||||
wp->w_config.hide = false;
|
||||
win_config_float(wp, wp->w_config);
|
||||
@ -1491,3 +1523,106 @@ void pum_make_popup(const char *path_name, int use_mouse_pos)
|
||||
pum_show_popupmenu(menu);
|
||||
}
|
||||
}
|
||||
|
||||
static bool pum_parse_title(WinConfig *fconfig, BorderTextType bt, const char *s, size_t len,
|
||||
bool pos)
|
||||
{
|
||||
Error err = ERROR_INIT;
|
||||
char *data = xmemdupz(s, len);
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
bool result = true;
|
||||
if (pos) {
|
||||
result = parse_bordertext_pos(cstr_as_string(data), bt, fconfig, &err);
|
||||
} else {
|
||||
parse_bordertext(CSTR_AS_OBJ(data), bt, fconfig, &err);
|
||||
}
|
||||
xfree(data);
|
||||
if (ERROR_SET(&err)) {
|
||||
emsg(err.msg);
|
||||
api_clear_error(&err);
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool parse_completepopup(WinConfig *fconfig, int *border_adj)
|
||||
{
|
||||
const char *p = p_cpp;
|
||||
Error err = ERROR_INIT;
|
||||
|
||||
while (*p != NUL) {
|
||||
const char *s = p;
|
||||
|
||||
const char *e = vim_strchr(p, ':');
|
||||
if (e == NULL || e[1] == NUL) {
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
p = vim_strchr(e, ',');
|
||||
if (p == NULL) {
|
||||
p = e + strlen(e);
|
||||
}
|
||||
|
||||
size_t len = (size_t)(p - e - 1);
|
||||
if (strncmp(s, "border:", 7) == 0) {
|
||||
char *style = xmemdupz(e + 1, len);
|
||||
if (!style) {
|
||||
return false;
|
||||
}
|
||||
parse_border_style(CSTR_AS_OBJ(style), fconfig, &err);
|
||||
xfree(style);
|
||||
if (ERROR_SET(&err)) {
|
||||
emsg(err.msg);
|
||||
api_clear_error(&err);
|
||||
return false;
|
||||
}
|
||||
|
||||
int border_attr = win_hl_attr(curwin, HLF_BORDER);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
fconfig->border_attr[i] = fconfig->border_hl_ids[i]
|
||||
? hl_get_ui_attr(0, HLF_BORDER, fconfig->border_hl_ids[i], false)
|
||||
: border_attr;
|
||||
if (i < 4 && fconfig->border_chars[2 * i + 1][0]) {
|
||||
border_adj[i] = 1;
|
||||
}
|
||||
}
|
||||
} else if (strncmp(s, "title:", 6) == 0) {
|
||||
if (!pum_parse_title(fconfig, kBorderTextTitle, e + 1, len, false)) {
|
||||
return false;
|
||||
}
|
||||
fconfig->title = true;
|
||||
} else if (strncmp(s, "titlepos:", 9) == 0) {
|
||||
if (!pum_parse_title(fconfig, kBorderTextTitle, e + 1, len, true)) {
|
||||
return false;
|
||||
}
|
||||
} else if (strncmp(s, "footer:", 7) == 0) {
|
||||
if (!pum_parse_title(fconfig, kBorderTextFooter, e + 1, len, false)) {
|
||||
return false;
|
||||
}
|
||||
fconfig->footer = true;
|
||||
} else if (strncmp(s, "footerpos:", 10) == 0) {
|
||||
if (!pum_parse_title(fconfig, kBorderTextFooter, e + 1, len, true)) {
|
||||
return false;
|
||||
}
|
||||
} else if (strncmp(s, "align:", 6) == 0) {
|
||||
const char *arg = e + 1;
|
||||
if (strncmp(arg, "item", 4) == 0) {
|
||||
pum_align = kInfoAlignItem;
|
||||
} else if (strncmp(arg, "menu", 4) == 0) {
|
||||
pum_align = kInfoAlignMenu;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*p == ',') {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -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"
|
||||
@ -29,6 +30,11 @@ EXTERN struct {
|
||||
bool finish;
|
||||
} pum_want;
|
||||
|
||||
typedef enum {
|
||||
kInfoAlignMenu = 0,
|
||||
kInfoAlignItem,
|
||||
} PumInfoAlign;
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "popupmenu.h.generated.h"
|
||||
#endif
|
||||
|
@ -1173,6 +1173,7 @@ describe('builtin popupmenu', function()
|
||||
[6] = { foreground = Screen.colors.White, background = Screen.colors.Red },
|
||||
[7] = { background = Screen.colors.Yellow }, -- Search
|
||||
[8] = { foreground = Screen.colors.Red },
|
||||
[9] = { foreground = Screen.colors.Fuchsia, bold = true },
|
||||
ks = { foreground = Screen.colors.Red, background = Screen.colors.Grey },
|
||||
kn = { foreground = Screen.colors.Red, background = Screen.colors.Plum1 },
|
||||
xs = { foreground = Screen.colors.Black, background = Screen.colors.Grey },
|
||||
@ -5012,6 +5013,316 @@ describe('builtin popupmenu', function()
|
||||
feed('<C-E><Esc>')
|
||||
end)
|
||||
end
|
||||
|
||||
describe('#completepopup', function()
|
||||
before_each(function()
|
||||
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", info: "3info"}]
|
||||
endfunc
|
||||
set omnifunc=Omni_test
|
||||
set completeopt-=preview
|
||||
set completepopup=border:rounded
|
||||
]])
|
||||
end)
|
||||
|
||||
it('can set border', function()
|
||||
feed('Gi<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
|
||||
one^ |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
## grid 3
|
||||
{2:-- }{5:match 1 of 3} |
|
||||
## grid 4
|
||||
╭───────────────╮|
|
||||
│{s:one }│|
|
||||
│{n:two }│|
|
||||
│{n:three }│|
|
||||
╰───────────────╯|
|
||||
]],
|
||||
float_pos = {
|
||||
[4] = { -1, 'NW', 2, 1, 0, false, 100 },
|
||||
},
|
||||
win_viewport = {
|
||||
[2] = {
|
||||
win = 1000,
|
||||
topline = 0,
|
||||
botline = 2,
|
||||
curline = 0,
|
||||
curcol = 3,
|
||||
linecount = 1,
|
||||
sum_scroll_delta = 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
else
|
||||
screen:expect {
|
||||
grid = [[
|
||||
one^ |
|
||||
╭───────────────╮{1: }|
|
||||
│{s:one }│{1: }|
|
||||
│{n:two }│{1: }|
|
||||
│{n:three }│{1: }|
|
||||
╰───────────────╯{1: }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{2:-- }{5:match 1 of 3} |
|
||||
]],
|
||||
}
|
||||
end
|
||||
-- avoid out of screen
|
||||
feed(('a'):rep(25) .. '<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
|
||||
oneaaaaaaaaaaaaaaaaaaaaaaaaaon|
|
||||
e^ |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
## grid 3
|
||||
{2:-- }{5:match 1 of 3} |
|
||||
## grid 4
|
||||
╭──────╮|
|
||||
│{s: one }│|
|
||||
│{n: two }│|
|
||||
│{n: three}│|
|
||||
╰──────╯|
|
||||
]],
|
||||
float_pos = {
|
||||
[4] = { -1, 'NW', 2, 2, 22, false, 100 },
|
||||
},
|
||||
win_viewport = {
|
||||
[2] = {
|
||||
win = 1000,
|
||||
topline = 0,
|
||||
botline = 2,
|
||||
curline = 0,
|
||||
curcol = 31,
|
||||
linecount = 1,
|
||||
sum_scroll_delta = 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
else
|
||||
screen:expect {
|
||||
grid = [[
|
||||
oneaaaaaaaaaaaaaaaaaaaaaaaaaon|
|
||||
e^ |
|
||||
{1:~ }╭──────╮|
|
||||
{1:~ }│{s: one }│|
|
||||
{1:~ }│{n: two }│|
|
||||
{1:~ }│{n: three}│|
|
||||
{1:~ }╰──────╯|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{2:-- }{5:match 1 of 3} |
|
||||
]],
|
||||
}
|
||||
end
|
||||
end)
|
||||
|
||||
it('can set title', function()
|
||||
command('set completepopup=border:rounded,title:Omni,titlepos:right')
|
||||
feed('Gi<C-x><C-o>')
|
||||
if multigrid then
|
||||
screen:expect({
|
||||
grid = [[
|
||||
## grid 1
|
||||
[2:------------------------------]|*10
|
||||
[3:------------------------------]|
|
||||
## grid 2
|
||||
one^ |
|
||||
{1:~ }|*9
|
||||
## grid 3
|
||||
{2:-- }{5:match 1 of 3} |
|
||||
## grid 4
|
||||
╭───────────{9:Omni}╮|
|
||||
│{s:one }│|
|
||||
│{n:two }│|
|
||||
│{n:three }│|
|
||||
╰───────────────╯|
|
||||
]],
|
||||
float_pos = {
|
||||
[4] = { -1, 'NW', 2, 1, 0, false, 100 },
|
||||
},
|
||||
win_viewport = {
|
||||
[2] = {
|
||||
win = 1000,
|
||||
topline = 0,
|
||||
botline = 2,
|
||||
curline = 0,
|
||||
curcol = 3,
|
||||
linecount = 1,
|
||||
sum_scroll_delta = 0,
|
||||
},
|
||||
},
|
||||
win_viewport_margins = {
|
||||
[2] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1000,
|
||||
},
|
||||
},
|
||||
})
|
||||
else
|
||||
screen:expect({
|
||||
grid = [[
|
||||
one^ |
|
||||
╭───────────{9:Omni}╮{1: }|
|
||||
│{s:one }│{1: }|
|
||||
│{n:two }│{1: }|
|
||||
│{n:three }│{1: }|
|
||||
╰───────────────╯{1: }|
|
||||
{1:~ }|*4
|
||||
{2:-- }{5:match 1 of 3} |
|
||||
]],
|
||||
})
|
||||
end
|
||||
end)
|
||||
|
||||
it('align', function()
|
||||
command('set completepopup=align:item,border:rounded,title:Omni, completeopt+=popup')
|
||||
feed('<ESC>S<C-X><C-O><C-N>')
|
||||
if multigrid then
|
||||
screen:expect({
|
||||
grid = [[
|
||||
## grid 1
|
||||
[2:------------------------------]|*10
|
||||
[3:------------------------------]|
|
||||
## grid 2
|
||||
two^ |
|
||||
{1:~ }|*9
|
||||
## grid 3
|
||||
{2:-- }{5:match 2 of 3} |
|
||||
## grid 4
|
||||
{n:2info}|
|
||||
{n: }|
|
||||
## grid 5
|
||||
╭{9:Omni}───────────╮|
|
||||
│{n:one }│|
|
||||
│{s:two }│|
|
||||
│{n:three }│|
|
||||
╰───────────────╯|
|
||||
]],
|
||||
float_pos = {
|
||||
[5] = { -1, 'NW', 2, 1, 0, false, 100 },
|
||||
[4] = { 1001, 'NW', 1, 3, 17, false, 50 },
|
||||
},
|
||||
win_viewport = {
|
||||
[2] = {
|
||||
win = 1000,
|
||||
topline = 0,
|
||||
botline = 2,
|
||||
curline = 0,
|
||||
curcol = 3,
|
||||
linecount = 1,
|
||||
sum_scroll_delta = 0,
|
||||
},
|
||||
[4] = {
|
||||
win = 1001,
|
||||
topline = 0,
|
||||
botline = 2,
|
||||
curline = 0,
|
||||
curcol = 0,
|
||||
linecount = 1,
|
||||
sum_scroll_delta = 0,
|
||||
},
|
||||
},
|
||||
win_viewport_margins = {
|
||||
[2] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1000,
|
||||
},
|
||||
[4] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1001,
|
||||
},
|
||||
},
|
||||
})
|
||||
else
|
||||
screen:expect({
|
||||
grid = [[
|
||||
two^ |
|
||||
╭{9:Omni}───────────╮{1: }|
|
||||
│{n:one }│{1: }|
|
||||
│{s:two }│{n:2info}{1: }|
|
||||
│{n:three }│{n: }{1: }|
|
||||
╰───────────────╯{1: }|
|
||||
{1:~ }|*4
|
||||
{2:-- }{5:match 2 of 3} |
|
||||
]],
|
||||
})
|
||||
end
|
||||
end)
|
||||
|
||||
it('wrong value in option', function()
|
||||
eq(
|
||||
'Vim(set):invalid border style "foo"',
|
||||
pcall_err(command, 'set completepopup=border:foo')
|
||||
)
|
||||
eq(
|
||||
'Vim(set):E474: Invalid argument: completepopup=align:foo',
|
||||
pcall_err(command, 'set completepopup=align:foo')
|
||||
)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
describe('with ext_multigrid', function()
|
||||
|
Loading…
Reference in New Issue
Block a user