Merge pull request #11943 from yatli/master

[RDY] API/UI: Allow UI to set PUM position and size, and pass the position to CompleteChanged
This commit is contained in:
Matthieu Coudron 2020-04-27 21:12:12 +02:00 committed by GitHub
commit c7d3630e21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 231 additions and 29 deletions

View File

@ -2341,6 +2341,22 @@ nvim_ui_pum_set_height({height}) *nvim_ui_pum_set_height()*
Parameters: ~
{height} Popupmenu height, must be greater than zero.
*nvim_ui_pum_set_bounds()*
nvim_ui_pum_set_bounds({width}, {height}, {row}, {col})
Tells Nvim the geometry of the popumenu, to align floating
windows with an external popup menu. Note that this method
is not to be confused with |nvim_ui_pum_set_height()|, which
sets the number of visible items in the popup menu, while
this function sets the bounding box of the popup menu,
including visual decorations such as boarders and sliders.
Parameters: ~
{width} Popupmenu width.
{height} Popupmenu height.
{row} Popupmenu row.
{height} Popupmenu height.
nvim_ui_set_option({name}, {value}) *nvim_ui_set_option()*
TODO: Documentation

View File

@ -109,7 +109,12 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
UI *ui = xcalloc(1, sizeof(UI));
ui->width = (int)width;
ui->height = (int)height;
ui->pum_height = 0;
ui->pum_nlines = 0;
ui->pum_pos = false;
ui->pum_width = 0.0;
ui->pum_height = 0.0;
ui->pum_row = -1.0;
ui->pum_col = -1.0;
ui->rgb = true;
ui->override = false;
ui->grid_resize = remote_ui_grid_resize;
@ -340,7 +345,56 @@ void nvim_ui_pum_set_height(uint64_t channel_id, Integer height, Error *err)
"It must support the ext_popupmenu option");
return;
}
ui->pum_height = (int)height;
ui->pum_nlines = (int)height;
}
/// Tells Nvim the geometry of the popumenu, to align floating windows with an
/// external popup menu.
///
/// Note that this method is not to be confused with |nvim_ui_pum_set_height()|,
/// which sets the number of visible items in the popup menu, while this
/// function sets the bounding box of the popup menu, including visual
/// decorations such as boarders and sliders. Floats need not use the same font
/// size, nor be anchored to exact grid corners, so one can set floating-point
/// numbers to the popup menu geometry.
///
/// @param channel_id
/// @param width Popupmenu width.
/// @param height Popupmenu height.
/// @param row Popupmenu row.
/// @param col Popupmenu height.
/// @param[out] err Error details, if any.
void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height,
Float row, Float col, Error *err)
FUNC_API_SINCE(7) FUNC_API_REMOTE_ONLY
{
if (!pmap_has(uint64_t)(connected_uis, channel_id)) {
api_set_error(err, kErrorTypeException,
"UI not attached to channel: %" PRId64, channel_id);
return;
}
UI *ui = pmap_get(uint64_t)(connected_uis, channel_id);
if (!ui->ui_ext[kUIPopupmenu]) {
api_set_error(err, kErrorTypeValidation,
"UI must support the ext_popupmenu option");
return;
}
if (width <= 0) {
api_set_error(err, kErrorTypeValidation, "Expected width > 0");
return;
} else if (height <= 0) {
api_set_error(err, kErrorTypeValidation, "Expected height > 0");
return;
}
ui->pum_row = (double)row;
ui->pum_col = (double)col;
ui->pum_width = (double)width;
ui->pum_height = (double)height;
ui->pum_pos = true;
}
/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush().

View File

@ -1634,6 +1634,28 @@ int tv_dict_add_nr(dict_T *const d, const char *const key,
return OK;
}
/// Add a floating point number entry to dictionary
///
/// @param[out] d Dictionary to add entry to.
/// @param[in] key Key to add.
/// @param[in] key_len Key length.
/// @param[in] nr Floating point number to add.
///
/// @return OK in case of success, FAIL when key already exists.
int tv_dict_add_float(dict_T *const d, const char *const key,
const size_t key_len, const float_T nr)
{
dictitem_T *const item = tv_dict_item_alloc_len(key, key_len);
item->di_tv.v_type = VAR_FLOAT;
item->di_tv.vval.v_float = nr;
if (tv_dict_add(d, item) == FAIL) {
tv_dict_item_free(item);
return FAIL;
}
return OK;
}
/// Add a special entry to dictionary
///
/// @param[out] d Dictionary to add entry to.

View File

@ -237,6 +237,12 @@ for i = 1, #functions do
(j - 1)..'].type == kObjectTypeInteger && args.items['..(j - 1)..'].data.integer >= 0) {')
output:write('\n '..converted..' = (handle_T)args.items['..(j - 1)..'].data.integer;')
end
if rt:match('^Float$') then
-- accept integers for Floats
output:write('\n } else if (args.items['..
(j - 1)..'].type == kObjectTypeInteger) {')
output:write('\n '..converted..' = (Float)args.items['..(j - 1)..'].data.integer;')
end
-- accept empty lua tables as empty dictionarys
if rt:match('^Dictionary') then
output:write('\n } else if (args.items['..(j - 1)..'].type == kObjectTypeArray && args.items['..(j - 1)..'].data.array.size == 0) {') --luacheck: ignore 631

View File

@ -908,10 +908,17 @@ void pum_set_event_info(dict_T *dict)
if (!pum_visible()) {
return;
}
tv_dict_add_nr(dict, S_LEN("height"), pum_height);
tv_dict_add_nr(dict, S_LEN("width"), pum_width);
tv_dict_add_nr(dict, S_LEN("row"), pum_row);
tv_dict_add_nr(dict, S_LEN("col"), pum_col);
double w, h, r, c;
if (!ui_pum_get_pos(&w, &h, &r, &c)) {
w = (double)pum_width;
h = (double)pum_height;
r = (double)pum_row;
c = (double)pum_col;
}
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_special(dict, S_LEN("scrollbar"),
pum_scrollbar ? kSpecialVarTrue : kSpecialVarFalse);

View File

@ -979,9 +979,9 @@ func Test_CompleteChanged()
call cursor(4, 1)
call feedkeys("Sf\<C-N>", 'tx')
call assert_equal({'completed_item': {}, 'width': 15,
\ 'height': 2, 'size': 2,
\ 'col': 0, 'row': 4, 'scrollbar': v:false}, g:event)
call assert_equal({'completed_item': {}, 'width': 15.0,
\ 'height': 2.0, 'size': 2,
\ 'col': 0.0, 'row': 4.0, 'scrollbar': v:false}, g:event)
call feedkeys("a\<C-N>\<C-N>\<C-E>", 'tx')
call assert_equal('foo', g:word)
call feedkeys("a\<C-N>\<C-N>\<C-N>\<C-E>", 'tx')
@ -1009,10 +1009,10 @@ func Test_pum_getpos()
setlocal completefunc=UserDefinedComplete
let d = {
\ 'height': 5,
\ 'width': 15,
\ 'row': 1,
\ 'col': 0,
\ 'height': 5.0,
\ 'width': 15.0,
\ 'row': 1.0,
\ 'col': 0.0,
\ 'size': 5,
\ 'scrollbar': v:false,
\ }

View File

@ -226,7 +226,7 @@ int ui_pum_get_height(void)
{
int pum_height = 0;
for (size_t i = 1; i < ui_count; i++) {
int ui_pum_height = uis[i]->pum_height;
int ui_pum_height = uis[i]->pum_nlines;
if (ui_pum_height) {
pum_height =
pum_height != 0 ? MIN(pum_height, ui_pum_height) : ui_pum_height;
@ -235,6 +235,21 @@ int ui_pum_get_height(void)
return pum_height;
}
bool ui_pum_get_pos(double *pwidth, double *pheight, double *prow, double *pcol)
{
for (size_t i = 1; i < ui_count; i++) {
if (!uis[i]->pum_pos) {
continue;
}
*pwidth = uis[i]->pum_width;
*pheight = uis[i]->pum_height;
*prow = uis[i]->pum_row;
*pcol = uis[i]->pum_col;
return true;
}
return false;
}
static void ui_refresh_event(void **argv)
{
ui_refresh();

View File

@ -53,7 +53,12 @@ struct ui_t {
bool ui_ext[kUIExtCount]; ///< Externalized UI capabilities.
int width;
int height;
int pum_height;
int pum_nlines; /// actual nr. lines shown in PUM
bool pum_pos; /// UI reports back pum position?
double pum_row;
double pum_col;
double pum_height;
double pum_width;
void *data;
#ifdef INCLUDE_GENERATED_DECLARATIONS

View File

@ -8,7 +8,7 @@ local command = helpers.command
local funcs = helpers.funcs
local get_pathsep = helpers.get_pathsep
local eq = helpers.eq
local matches = helpers.matches
local pcall_err = helpers.pcall_err
describe('ui/ext_popupmenu', function()
local screen
@ -382,7 +382,7 @@ describe('ui/ext_popupmenu', function()
end
describe('pum_set_height', function()
it('can be set pum height', function()
it('can set pum height', function()
source_complete_month()
local month_expected = {
{'January', '', '', ''},
@ -421,22 +421,79 @@ describe('ui/ext_popupmenu', function()
end)
it('an error occurs if set 0 or less', function()
local ok, err, _
ok, _ = pcall(meths.ui_pum_set_height, 1)
eq(ok, true)
ok, err = pcall(meths.ui_pum_set_height, 0)
eq(ok, false)
matches('.*: Expected pum height > 0', err)
meths.ui_pum_set_height(1)
eq('Expected pum height > 0',
pcall_err(meths.ui_pum_set_height, 0))
end)
it('an error occurs when ext_popupmenu is false', function()
local ok, err, _
ok, _ = pcall(meths.ui_pum_set_height, 1)
eq(ok, true)
meths.ui_pum_set_height(1)
screen:set_option('ext_popupmenu', false)
ok, err = pcall(meths.ui_pum_set_height, 1)
eq(ok, false)
matches('.*: It must support the ext_popupmenu option', err)
eq('It must support the ext_popupmenu option',
pcall_err(meths.ui_pum_set_height, 1))
end)
end)
describe('pum_set_bounds', function()
it('can set pum bounds', function()
source_complete_month()
local month_expected = {
{'January', '', '', ''},
{'February', '', '', ''},
{'March', '', '', ''},
{'April', '', '', ''},
{'May', '', '', ''},
{'June', '', '', ''},
{'July', '', '', ''},
{'August', '', '', ''},
{'September', '', '', ''},
{'October', '', '', ''},
{'November', '', '', ''},
{'December', '', '', ''},
}
local pum_height = 6
feed('o<C-r>=TestCompleteMonth()<CR>')
meths.ui_pum_set_height(pum_height)
-- set bounds w h r c
meths.ui_pum_set_bounds(10.5, 5.2, 6.3, 7.4)
feed('<PageDown>')
-- pos becomes pum_height-2 because it is subtracting 2 to keep some
-- context in ins_compl_key2count()
screen:expect{grid=[[
|
January^ |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{2:-- INSERT --} |
]], popupmenu={
items=month_expected,
pos=pum_height-2,
anchor={1,1,0},
}}
end)
it('no error occurs if row or col set less than 0', function()
meths.ui_pum_set_bounds(1.0, 1.0, 0.0, 1.5)
meths.ui_pum_set_bounds(1.0, 1.0, -1.0, 0.0)
meths.ui_pum_set_bounds(1.0, 1.0, 0.0, -1.0)
end)
it('an error occurs if width or height set 0 or less', function()
meths.ui_pum_set_bounds(1.0, 1.0, 0.0, 1.5)
eq('Expected width > 0',
pcall_err(meths.ui_pum_set_bounds, 0.0, 1.0, 1.0, 0.0))
eq('Expected height > 0',
pcall_err(meths.ui_pum_set_bounds, 1.0, -1.0, 1.0, 0.0))
end)
it('an error occurs when ext_popupmenu is false', function()
meths.ui_pum_set_bounds(1.0, 1.0, 0.0, 1.5)
screen:set_option('ext_popupmenu', false)
eq('UI must support the ext_popupmenu option',
pcall_err(meths.ui_pum_set_bounds, 1.0, 1.0, 0.0, 1.5))
end)
end)

View File

@ -2026,6 +2026,26 @@ describe('typval.c', function()
alloc_log:check({})
end)
end)
describe('float()', function()
itp('works', function()
local d = dict({test=10})
alloc_log:clear()
eq({test=10}, dct2tbl(d))
eq(OK, lib.tv_dict_add_float(d, 'testt', 3, 1.5))
local dis = dict_items(d)
alloc_log:check({a.di(dis.tes, 'tes')})
eq({test=10, tes=1.5}, dct2tbl(d))
eq(FAIL, check_emsg(function() return lib.tv_dict_add_float(d, 'testt', 3, 1.5) end,
'E685: Internal error: hash_add()'))
alloc_log:clear()
lib.emsg_skip = lib.emsg_skip + 1
eq(FAIL, check_emsg(function() return lib.tv_dict_add_float(d, 'testt', 3, 1.5) end,
nil))
lib.emsg_skip = lib.emsg_skip - 1
alloc_log:clear_tmp_allocs()
alloc_log:check({})
end)
end)
describe('str()', function()
itp('works', function()
local d = dict({test=10})