diff options
author | Matthieu Coudron <mattator@gmail.com> | 2020-04-27 21:12:12 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-27 21:12:12 +0200 |
commit | c7d3630e214012667e855c30cde2808a5fe59650 (patch) | |
tree | cc2024053865052d34e7e8ebebbd45a4064d29ee | |
parent | d90a92bcd3c18111abb62a57192c9e151839a7f4 (diff) | |
parent | e34684b2ad02e759dec39c0f0958c7882120ecdc (diff) | |
download | rneovim-c7d3630e214012667e855c30cde2808a5fe59650.tar.gz rneovim-c7d3630e214012667e855c30cde2808a5fe59650.tar.bz2 rneovim-c7d3630e214012667e855c30cde2808a5fe59650.zip |
Merge pull request #11943 from yatli/master
[RDY] API/UI: Allow UI to set PUM position and size, and pass the position to CompleteChanged
-rw-r--r-- | runtime/doc/api.txt | 16 | ||||
-rw-r--r-- | src/nvim/api/ui.c | 58 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 22 | ||||
-rw-r--r-- | src/nvim/generators/gen_api_dispatch.lua | 6 | ||||
-rw-r--r-- | src/nvim/popupmnu.c | 15 | ||||
-rw-r--r-- | src/nvim/testdir/test_popup.vim | 14 | ||||
-rw-r--r-- | src/nvim/ui.c | 17 | ||||
-rw-r--r-- | src/nvim/ui.h | 7 | ||||
-rw-r--r-- | test/functional/ui/popupmenu_spec.lua | 85 | ||||
-rw-r--r-- | test/unit/eval/typval_spec.lua | 20 |
10 files changed, 231 insertions, 29 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 39b6c6417d..a2e0c56f85 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -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 diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 75ee05761b..717713b948 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -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(). diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index c8b5fc294c..773e493d0b 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -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. diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index e861cfda35..6e80ad0e5c 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -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 diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index da34d85c00..532bf68190 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -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); diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index e5696f4cbb..bb0ed6e00c 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -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, \ } diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 3a5aa95ad3..685da77b39 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -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(); diff --git a/src/nvim/ui.h b/src/nvim/ui.h index 8867b5ee24..d00243d35f 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -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 diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 11c3f4123e..c1c5d1ce2e 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -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) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 1651eb9bcc..ea86ccbf1c 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -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}) |