mirror of
https://github.com/neovim/neovim.git
synced 2026-05-27 07:18:36 +00:00
feat(ui): use vim.ui.select for :tselect, z= #39478
Problem: `:tselect` and `z=` (spell suggest) have their own bespoke select menus. Solution: - Delegate to `vim.ui.select` instead. - Bonus: - `:tselect` gains mouse support. `print_tag_list` didn't suport mouseclick. This causes some minor regressions, which are not blockers: - `z=` no longer draws the list right-left if 'rightleft' is set. - TODO: can/should `vim.ui.select` / `vim.fn.inputlist()` handle that? - `:tselect` - No "column" headings (`# pri kind tag file`). - No highlighting: (HLF_T: tag name, HLF_D: file, HLF_CM: extra fields). - TODO: can `vim.ui.select()` support highlighted chunks (`[[text, hl_id], ...]`) ? fix https://github.com/neovim/neovim/issues/25814 fix https://github.com/neovim/neovim/issues/31987
This commit is contained in:
@@ -5165,12 +5165,13 @@ vim.ui.progress_status() *vim.ui.progress_status()*
|
|||||||
|
|
||||||
vim.ui.select({items}, {opts}, {on_choice}) *vim.ui.select()*
|
vim.ui.select({items}, {opts}, {on_choice}) *vim.ui.select()*
|
||||||
Prompts the user to pick from a list of items, allowing arbitrary
|
Prompts the user to pick from a list of items, allowing arbitrary
|
||||||
(potentially asynchronous) work until `on_choice`.
|
(potentially asynchronous) work until `on_choice`. This is the standard
|
||||||
|
"picker" interface, used by |z=|, |:tselect|, etc.
|
||||||
|
|
||||||
Plugins may override `vim.ui.select` to provide a custom "picker"
|
Plugins may override `vim.ui.select` to provide a custom picker; they are
|
||||||
interface; they are expected to call the `format_item` and `preview_item`
|
expected to call the `format_item` and `preview_item` handlers (if any)
|
||||||
handlers (if any) provided by the caller. They may also use the `kind`
|
provided by the caller. They may also use the `kind` hint (if provided by
|
||||||
hint (if provided by the caller) to decide how to handle some items.
|
the caller) to decide how to handle some items.
|
||||||
|
|
||||||
Note: the default `vim.ui.select` currently doesn't support preview.
|
Note: the default `vim.ui.select` currently doesn't support preview.
|
||||||
|
|
||||||
|
|||||||
@@ -216,7 +216,10 @@ TUI
|
|||||||
|
|
||||||
UI
|
UI
|
||||||
|
|
||||||
• todo
|
• |:tselect| delegates to |vim.ui.select()| instead of a bespoke internal
|
||||||
|
selection routine.
|
||||||
|
• |z=| (spell suggest) delegates to |vim.ui.select()| instead of a bespoke
|
||||||
|
internal selection routine.
|
||||||
|
|
||||||
VIMSCRIPT
|
VIMSCRIPT
|
||||||
|
|
||||||
|
|||||||
36
runtime/lua/vim/_core/spell.lua
Normal file
36
runtime/lua/vim/_core/spell.lua
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
local select_blocking = require('vim._core.ui').select_blocking
|
||||||
|
local N_ = vim.fn.gettext
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
--- @class vim._core.spell.Suggestion
|
||||||
|
--- @field word string The suggested replacement.
|
||||||
|
--- @field extra? string Text replaced when wider than the bad span.
|
||||||
|
--- @field score integer Primary score.
|
||||||
|
--- @field altscore? integer Secondary score (only set when 'spellsuggest' contains "double" or "best").
|
||||||
|
--- @field salscore? boolean True if the score came from sound-alike comparison (only set alongside `altscore`).
|
||||||
|
|
||||||
|
--- Called from `spell_suggest()` (`z=`) to let the user pick from `items` via
|
||||||
|
--- |vim.ui.select()|.
|
||||||
|
---
|
||||||
|
--- @param items vim._core.spell.Suggestion[]
|
||||||
|
--- @param bad string The misspelled word being replaced.
|
||||||
|
--- @return integer? # 1-based index of the chosen suggestion, or nil if cancelled.
|
||||||
|
function M.suggest_select(items, bad)
|
||||||
|
return select_blocking(items, {
|
||||||
|
prompt = N_('Change "%s" to:'):format(bad),
|
||||||
|
kind = 'spell',
|
||||||
|
format_item = function(s)
|
||||||
|
local extra = s.extra and (' < "' .. s.extra .. '"') or ''
|
||||||
|
local score = ''
|
||||||
|
if vim.o.verbose > 0 then
|
||||||
|
score = s.altscore
|
||||||
|
and (' (%s%d - %d)'):format(s.salscore and 's ' or '', s.score, s.altscore)
|
||||||
|
or (' (%d)'):format(s.score)
|
||||||
|
end
|
||||||
|
return ('"%s"%s%s'):format(s.word, extra, score)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
44
runtime/lua/vim/_core/tag.lua
Normal file
44
runtime/lua/vim/_core/tag.lua
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
local select_blocking = require('vim._core.ui').select_blocking
|
||||||
|
local N_ = vim.fn.gettext
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
--- @class vim._core.tag.Match
|
||||||
|
--- @field tag string
|
||||||
|
--- @field kind? string
|
||||||
|
--- @field pri string Priority code, e.g. "FSC" — see `:h tag-priority`.
|
||||||
|
--- @field file string
|
||||||
|
--- @field extra? string
|
||||||
|
--- @field cur boolean True if this is the currently-active tagstack match.
|
||||||
|
|
||||||
|
--- Called from `do_tag()` (`:tselect`, ambiguous `:tag`, etc.) to let the user
|
||||||
|
--- pick from `matches` via |vim.ui.select()|.
|
||||||
|
---
|
||||||
|
--- @param items vim._core.tag.Match[] One per matching tag.
|
||||||
|
--- @return integer? # 1-based index of the chosen tag, or nil if cancelled.
|
||||||
|
function M.select(items)
|
||||||
|
local taglen = 18
|
||||||
|
for _, m in ipairs(items) do
|
||||||
|
taglen = math.max(taglen, vim.fn.strdisplaywidth(m.tag) + 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
return select_blocking(items, {
|
||||||
|
prompt = N_('Type number and <Enter> (q or empty cancels):'),
|
||||||
|
kind = 'tag',
|
||||||
|
format_item = function(m)
|
||||||
|
local marker = m.cur and '>' or ' '
|
||||||
|
local kind = m.kind or ''
|
||||||
|
local extra = m.extra and (' ' .. m.extra) or ''
|
||||||
|
return ('%s %s %-4s %-' .. taglen .. 's %s%s'):format(
|
||||||
|
marker,
|
||||||
|
m.pri,
|
||||||
|
kind,
|
||||||
|
m.tag,
|
||||||
|
m.file,
|
||||||
|
extra
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
23
runtime/lua/vim/_core/ui.lua
Normal file
23
runtime/lua/vim/_core/ui.lua
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
--- Wait for |vim.ui.select()| and return the selected index. The default vim.ui.select impl
|
||||||
|
--- (inputlist()) is synchronous, but this also handles async pickers (fzf-lua, telescope, …).
|
||||||
|
---
|
||||||
|
--- @param items table Items to choose from.
|
||||||
|
--- @param opts table Forwarded to |vim.ui.select()|.
|
||||||
|
--- @return integer? # 1-based index of the chosen item, or nil if cancelled/interrupted.
|
||||||
|
function M.select_blocking(items, opts)
|
||||||
|
local choice ---@type integer?
|
||||||
|
local done = false
|
||||||
|
vim.ui.select(items, opts or {}, function(_, idx)
|
||||||
|
choice = idx
|
||||||
|
done = true
|
||||||
|
end)
|
||||||
|
-- vim.wait returns false on timeout (math.huge means never) or interrupt (-2).
|
||||||
|
vim.wait(math.huge, function()
|
||||||
|
return done
|
||||||
|
end)
|
||||||
|
return choice
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@@ -26,11 +26,11 @@ local M = vim._defer_require('vim.ui', {
|
|||||||
---@field kind? string
|
---@field kind? string
|
||||||
|
|
||||||
--- Prompts the user to pick from a list of items, allowing arbitrary (potentially asynchronous)
|
--- Prompts the user to pick from a list of items, allowing arbitrary (potentially asynchronous)
|
||||||
--- work until `on_choice`.
|
--- work until `on_choice`. This is the standard "picker" interface, used by |z=|, |:tselect|, etc.
|
||||||
---
|
---
|
||||||
--- Plugins may override `vim.ui.select` to provide a custom "picker" interface; they are expected
|
--- Plugins may override `vim.ui.select` to provide a custom picker; they are expected to call the
|
||||||
--- to call the `format_item` and `preview_item` handlers (if any) provided by the caller. They may
|
--- `format_item` and `preview_item` handlers (if any) provided by the caller. They may also use the
|
||||||
--- also use the `kind` hint (if provided by the caller) to decide how to handle some items.
|
--- `kind` hint (if provided by the caller) to decide how to handle some items.
|
||||||
---
|
---
|
||||||
--- Note: the default `vim.ui.select` currently doesn't support preview.
|
--- Note: the default `vim.ui.select` currently doesn't support preview.
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "nvim/hashtab_defs.h"
|
#include "nvim/hashtab_defs.h"
|
||||||
#include "nvim/highlight_defs.h"
|
#include "nvim/highlight_defs.h"
|
||||||
#include "nvim/input.h"
|
#include "nvim/input.h"
|
||||||
|
#include "nvim/lua/executor.h"
|
||||||
#include "nvim/macros_defs.h"
|
#include "nvim/macros_defs.h"
|
||||||
#include "nvim/mbyte.h"
|
#include "nvim/mbyte.h"
|
||||||
#include "nvim/mbyte_defs.h"
|
#include "nvim/mbyte_defs.h"
|
||||||
@@ -432,6 +433,68 @@ int spell_check_sps(void)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Let the user pick a spell suggestion. Delegates to `vim.ui.select()`.
|
||||||
|
///
|
||||||
|
/// @return 1-based index of the chosen suggestion, or 0 if cancelled.
|
||||||
|
static int select_spell_suggestion(suginfo_T *sug)
|
||||||
|
{
|
||||||
|
typval_T items_tv;
|
||||||
|
tv_list_alloc_ret(&items_tv, sug->su_ga.ga_len);
|
||||||
|
|
||||||
|
for (int i = 0; i < sug->su_ga.ga_len; i++) {
|
||||||
|
suggest_T *stp = &SUG(sug->su_ga, i);
|
||||||
|
|
||||||
|
dict_T *d = tv_dict_alloc();
|
||||||
|
|
||||||
|
// The suggested word may replace only part of the bad word; append the
|
||||||
|
// unreplaced tail to form the user-visible "word".
|
||||||
|
int el = sug->su_badlen - stp->st_orglen;
|
||||||
|
if (el > 0) {
|
||||||
|
char *word = xmallocz((size_t)stp->st_wordlen + (size_t)el);
|
||||||
|
memcpy(word, stp->st_word, (size_t)stp->st_wordlen);
|
||||||
|
memcpy(word + stp->st_wordlen, sug->su_badptr + stp->st_orglen, (size_t)el);
|
||||||
|
tv_dict_add_allocated_str(d, S_LEN("word"), word);
|
||||||
|
} else {
|
||||||
|
tv_dict_add_str(d, S_LEN("word"), stp->st_word);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The suggestion may replace MORE than su_badlen of the bad text;
|
||||||
|
// capture that wider span as `extra`.
|
||||||
|
if (sug->su_badlen < stp->st_orglen) {
|
||||||
|
char *extra = xstrnsave(sug->su_badptr, (size_t)stp->st_orglen);
|
||||||
|
tv_dict_add_allocated_str(d, S_LEN("extra"), extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass raw scoring data; Lua decides whether/how to render.
|
||||||
|
// `altscore`/`salscore` are only meaningful with SPS_DOUBLE|SPS_BEST.
|
||||||
|
tv_dict_add_nr(d, S_LEN("score"), stp->st_score);
|
||||||
|
if (sps_flags & (SPS_DOUBLE | SPS_BEST)) {
|
||||||
|
tv_dict_add_nr(d, S_LEN("altscore"), stp->st_altscore);
|
||||||
|
tv_dict_add_bool(d, S_LEN("salscore"),
|
||||||
|
stp->st_salscore ? kBoolVarTrue : kBoolVarFalse);
|
||||||
|
}
|
||||||
|
|
||||||
|
typval_T item = { .v_type = VAR_DICT, .vval.v_dict = d };
|
||||||
|
tv_list_append_tv(items_tv.vval.v_list, &item);
|
||||||
|
}
|
||||||
|
|
||||||
|
typval_T bad_tv = { .v_type = VAR_STRING,
|
||||||
|
.vval.v_string = xstrnsave(sug->su_badptr, (size_t)sug->su_badlen) };
|
||||||
|
typval_T lua_args[] = { items_tv, bad_tv, { .v_type = VAR_UNKNOWN } };
|
||||||
|
typval_T rettv = TV_INITIAL_VALUE;
|
||||||
|
nlua_call_vimfn("vim._core.spell", "suggest_select", lua_args, &rettv);
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
if (rettv.v_type == VAR_NUMBER) {
|
||||||
|
idx = (int)rettv.vval.v_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
tv_clear(&items_tv);
|
||||||
|
tv_clear(&bad_tv);
|
||||||
|
tv_clear(&rettv);
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
/// "z=": Find badly spelled word under or after the cursor.
|
/// "z=": Find badly spelled word under or after the cursor.
|
||||||
/// Give suggestions for the properly spelled word.
|
/// Give suggestions for the properly spelled word.
|
||||||
/// In Visual mode use the highlighted word as the bad word.
|
/// In Visual mode use the highlighted word as the bad word.
|
||||||
@@ -439,7 +502,6 @@ int spell_check_sps(void)
|
|||||||
void spell_suggest(int count)
|
void spell_suggest(int count)
|
||||||
{
|
{
|
||||||
pos_T prev_cursor = curwin->w_cursor;
|
pos_T prev_cursor = curwin->w_cursor;
|
||||||
bool mouse_used = false;
|
|
||||||
int badlen = 0;
|
int badlen = 0;
|
||||||
int msg_scroll_save = msg_scroll;
|
int msg_scroll_save = msg_scroll;
|
||||||
const int wo_spell_save = curwin->w_p_spell;
|
const int wo_spell_save = curwin->w_p_spell;
|
||||||
@@ -512,91 +574,17 @@ void spell_suggest(int count)
|
|||||||
true, need_cap, true);
|
true, need_cap, true);
|
||||||
|
|
||||||
int selected = count;
|
int selected = count;
|
||||||
msg_ext_set_kind("confirm");
|
|
||||||
if (GA_EMPTY(&sug.su_ga)) {
|
if (GA_EMPTY(&sug.su_ga)) {
|
||||||
|
msg_ext_set_kind("wmsg");
|
||||||
msg(_("No suggestions"), 0);
|
msg(_("No suggestions"), 0);
|
||||||
} else if (count > 0) {
|
} else if (count > 0) {
|
||||||
if (count > sug.su_ga.ga_len) {
|
if (count > sug.su_ga.ga_len) {
|
||||||
smsg(0, _("Only %" PRId64 " suggestions"),
|
msg_ext_set_kind("wmsg");
|
||||||
(int64_t)sug.su_ga.ga_len);
|
smsg(0, _("Only %" PRId64 " suggestions"), (int64_t)sug.su_ga.ga_len);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// When 'rightleft' is set the list is drawn right-left.
|
// Ask the user (via vim.ui.select) to pick a suggestion.
|
||||||
cmdmsg_rl = curwin->w_p_rl;
|
selected = select_spell_suggestion(&sug);
|
||||||
|
|
||||||
// List the suggestions.
|
|
||||||
msg_start();
|
|
||||||
msg_row = Rows - 1; // for when 'cmdheight' > 1
|
|
||||||
lines_left = Rows; // avoid more prompt
|
|
||||||
char *fmt = _("Change \"%.*s\" to:");
|
|
||||||
if (cmdmsg_rl && strncmp(fmt, "Change", 6) == 0) {
|
|
||||||
// And now the rabbit from the high hat: Avoid showing the
|
|
||||||
// untranslated message rightleft.
|
|
||||||
fmt = ":ot \"%.*s\" egnahC";
|
|
||||||
}
|
|
||||||
vim_snprintf(IObuff, IOSIZE, fmt, sug.su_badlen, sug.su_badptr);
|
|
||||||
msg_puts(IObuff);
|
|
||||||
msg_clr_eos();
|
|
||||||
msg_putchar('\n');
|
|
||||||
|
|
||||||
msg_scroll = true;
|
|
||||||
for (int i = 0; i < sug.su_ga.ga_len; i++) {
|
|
||||||
suggest_T *stp = &SUG(sug.su_ga, i);
|
|
||||||
|
|
||||||
// The suggested word may replace only part of the bad word, add
|
|
||||||
// the not replaced part. But only when it's not getting too long.
|
|
||||||
char wcopy[MAXWLEN + 2];
|
|
||||||
xstrlcpy(wcopy, stp->st_word, MAXWLEN + 1);
|
|
||||||
int el = sug.su_badlen - stp->st_orglen;
|
|
||||||
if (el > 0 && stp->st_wordlen + el <= MAXWLEN) {
|
|
||||||
assert(sug.su_badptr != NULL);
|
|
||||||
xmemcpyz(wcopy + stp->st_wordlen, sug.su_badptr + stp->st_orglen, (size_t)el);
|
|
||||||
}
|
|
||||||
vim_snprintf(IObuff, IOSIZE, "%2d", i + 1);
|
|
||||||
if (cmdmsg_rl) {
|
|
||||||
rl_mirror_ascii(IObuff, NULL);
|
|
||||||
}
|
|
||||||
msg_puts(IObuff);
|
|
||||||
|
|
||||||
vim_snprintf(IObuff, IOSIZE, " \"%s\"", wcopy);
|
|
||||||
msg_puts(IObuff);
|
|
||||||
|
|
||||||
// The word may replace more than "su_badlen".
|
|
||||||
if (sug.su_badlen < stp->st_orglen) {
|
|
||||||
vim_snprintf(IObuff, IOSIZE, _(" < \"%.*s\""),
|
|
||||||
stp->st_orglen, sug.su_badptr);
|
|
||||||
msg_puts(IObuff);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_verbose > 0) {
|
|
||||||
// Add the score.
|
|
||||||
if (sps_flags & (SPS_DOUBLE | SPS_BEST)) {
|
|
||||||
vim_snprintf(IObuff, IOSIZE, " (%s%d - %d)",
|
|
||||||
stp->st_salscore ? "s " : "",
|
|
||||||
stp->st_score, stp->st_altscore);
|
|
||||||
} else {
|
|
||||||
vim_snprintf(IObuff, IOSIZE, " (%d)",
|
|
||||||
stp->st_score);
|
|
||||||
}
|
|
||||||
if (cmdmsg_rl) {
|
|
||||||
// Mirror the numbers, but keep the leading space.
|
|
||||||
rl_mirror_ascii(IObuff + 1, NULL);
|
|
||||||
}
|
|
||||||
msg_advance(30);
|
|
||||||
msg_puts(IObuff);
|
|
||||||
}
|
|
||||||
if (!ui_has(kUIMessages) || i < sug.su_ga.ga_len - 1) {
|
|
||||||
msg_putchar('\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdmsg_rl = false;
|
|
||||||
msg_col = 0;
|
|
||||||
// Ask for choice.
|
|
||||||
selected = prompt_for_input(NULL, 0, false, &mouse_used);
|
|
||||||
if (mouse_used) {
|
|
||||||
selected = sug.su_ga.ga_len + 1 - (cmdline_row - mouse_row);
|
|
||||||
}
|
|
||||||
|
|
||||||
lines_left = Rows; // avoid more prompt
|
lines_left = Rows; // avoid more prompt
|
||||||
// don't delay for 'smd' in normal_cmd()
|
// don't delay for 'smd' in normal_cmd()
|
||||||
|
|||||||
232
src/nvim/tag.c
232
src/nvim/tag.c
@@ -38,6 +38,7 @@
|
|||||||
#include "nvim/highlight_defs.h"
|
#include "nvim/highlight_defs.h"
|
||||||
#include "nvim/input.h"
|
#include "nvim/input.h"
|
||||||
#include "nvim/insexpand.h"
|
#include "nvim/insexpand.h"
|
||||||
|
#include "nvim/lua/executor.h"
|
||||||
#include "nvim/macros_defs.h"
|
#include "nvim/macros_defs.h"
|
||||||
#include "nvim/mark.h"
|
#include "nvim/mark.h"
|
||||||
#include "nvim/mark_defs.h"
|
#include "nvim/mark_defs.h"
|
||||||
@@ -651,26 +652,13 @@ void do_tag(char *tag, int type, int count, int forceit, bool verbose)
|
|||||||
}
|
}
|
||||||
g_do_tagpreview = 0;
|
g_do_tagpreview = 0;
|
||||||
} else {
|
} else {
|
||||||
bool ask_for_selection = false;
|
|
||||||
|
|
||||||
if (type == DT_TAG && *tag != NUL) {
|
if (type == DT_TAG && *tag != NUL) {
|
||||||
// If a count is supplied to the ":tag <name>" command, then
|
// If a count is supplied to the ":tag <name>" command, then
|
||||||
// jump to count'th matching tag.
|
// jump to count'th matching tag.
|
||||||
cur_match = count > 0 ? count - 1 : 0;
|
cur_match = count > 0 ? count - 1 : 0;
|
||||||
} else if (type == DT_SELECT || (type == DT_JUMP && num_matches > 1)) {
|
} else if (type == DT_SELECT || (type == DT_JUMP && num_matches > 1)) {
|
||||||
print_tag_list(new_tag, use_tagstack, num_matches, matches);
|
// Ask the user (via vim.ui.select) to pick a tag.
|
||||||
ask_for_selection = true;
|
int i = select_tag_match(new_tag, use_tagstack, num_matches, matches);
|
||||||
} else if (type == DT_LTAG) {
|
|
||||||
if (add_llist_tags(tag, num_matches, matches) == FAIL) {
|
|
||||||
goto end_do_tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_match = 0; // Jump to the first tag
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ask_for_selection) {
|
|
||||||
// Ask to select a tag from the list.
|
|
||||||
int i = prompt_for_input(NULL, 0, false, NULL);
|
|
||||||
if (i <= 0 || i > num_matches || got_int) {
|
if (i <= 0 || i > num_matches || got_int) {
|
||||||
// no valid choice: don't change anything
|
// no valid choice: don't change anything
|
||||||
if (use_tagstack) {
|
if (use_tagstack) {
|
||||||
@@ -680,6 +668,12 @@ void do_tag(char *tag, int type, int count, int forceit, bool verbose)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cur_match = i - 1;
|
cur_match = i - 1;
|
||||||
|
} else if (type == DT_LTAG) {
|
||||||
|
if (add_llist_tags(tag, num_matches, matches) == FAIL) {
|
||||||
|
goto end_do_tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_match = 0; // Jump to the first tag
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cur_match >= num_matches) {
|
if (cur_match >= num_matches) {
|
||||||
@@ -797,179 +791,57 @@ end_do_tag:
|
|||||||
xfree(tofree);
|
xfree(tofree);
|
||||||
}
|
}
|
||||||
|
|
||||||
// List all the matching tags.
|
/// Let the user pick from `matches`. Delegates to `vim.ui.select()`.
|
||||||
static void print_tag_list(bool new_tag, bool use_tagstack, int num_matches, char **matches)
|
///
|
||||||
|
/// @return 1-based index of the chosen tag, or 0 if cancelled.
|
||||||
|
static int select_tag_match(bool new_tag, bool use_tagstack, int num_matches, char **matches)
|
||||||
{
|
{
|
||||||
taggy_T *tagstack = curwin->w_tagstack;
|
taggy_T *tagstack = curwin->w_tagstack;
|
||||||
int tagstackidx = curwin->w_tagstackidx;
|
int tagstackidx = curwin->w_tagstackidx;
|
||||||
tagptrs_T tagp;
|
|
||||||
|
|
||||||
// Assume that the first match indicates how long the tags can
|
typval_T items_tv;
|
||||||
// be, and align the file names to that.
|
tv_list_alloc_ret(&items_tv, num_matches);
|
||||||
parse_match(matches[0], &tagp);
|
|
||||||
int taglen = MAX((int)(tagp.tagname_end - tagp.tagname + 2), 18);
|
|
||||||
if (taglen > Columns - 25) {
|
|
||||||
taglen = MAXCOL;
|
|
||||||
}
|
|
||||||
if (msg_col == 0) {
|
|
||||||
msg_didout = false; // overwrite previous message
|
|
||||||
}
|
|
||||||
msg_ext_set_kind("confirm");
|
|
||||||
msg_start();
|
|
||||||
msg_puts_hl(_(" # pri kind tag"), HLF_T, false);
|
|
||||||
msg_clr_eos();
|
|
||||||
taglen_advance(taglen);
|
|
||||||
msg_puts_hl(_("file\n"), HLF_T, false);
|
|
||||||
|
|
||||||
for (int i = 0; i < num_matches && !got_int; i++) {
|
for (int i = 0; i < num_matches; i++) {
|
||||||
parse_match(matches[i], &tagp);
|
|
||||||
if (!new_tag && (
|
|
||||||
(g_do_tagpreview != 0
|
|
||||||
&& i == ptag_entry.cur_match)
|
|
||||||
|| (use_tagstack
|
|
||||||
&& i == tagstack[tagstackidx].cur_match))) {
|
|
||||||
*IObuff = '>';
|
|
||||||
} else {
|
|
||||||
*IObuff = ' ';
|
|
||||||
}
|
|
||||||
vim_snprintf(IObuff + 1, IOSIZE - 1,
|
|
||||||
"%2d %s ", i + 1,
|
|
||||||
mt_names[matches[i][0] & MT_MASK]);
|
|
||||||
msg_puts(IObuff);
|
|
||||||
if (tagp.tagkind != NULL) {
|
|
||||||
msg_outtrans_len(tagp.tagkind, (int)(tagp.tagkind_end - tagp.tagkind), 0, false);
|
|
||||||
}
|
|
||||||
msg_advance(13);
|
|
||||||
msg_outtrans_len(tagp.tagname, (int)(tagp.tagname_end - tagp.tagname), HLF_T, false);
|
|
||||||
msg_putchar(' ');
|
|
||||||
taglen_advance(taglen);
|
|
||||||
|
|
||||||
// Find out the actual file name. If it is long, truncate
|
|
||||||
// it and put "..." in the middle
|
|
||||||
const char *p = tag_full_fname(&tagp);
|
|
||||||
if (p != NULL) {
|
|
||||||
msg_outtrans(p, HLF_D, false);
|
|
||||||
XFREE_CLEAR(p);
|
|
||||||
}
|
|
||||||
if (msg_col > 0) {
|
|
||||||
msg_putchar('\n');
|
|
||||||
}
|
|
||||||
if (got_int) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
msg_advance(15);
|
|
||||||
|
|
||||||
// print any extra fields
|
|
||||||
const char *command_end = tagp.command_end;
|
|
||||||
if (command_end != NULL) {
|
|
||||||
p = command_end + 3;
|
|
||||||
while (*p && *p != '\r' && *p != '\n') {
|
|
||||||
while (*p == TAB) {
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip "file:" without a value (static tag)
|
|
||||||
if (strncmp(p, "file:", 5) == 0 && ascii_isspace(p[5])) {
|
|
||||||
p += 5;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// skip "kind:<kind>" and "<kind>"
|
|
||||||
if (p == tagp.tagkind
|
|
||||||
|| (p + 5 == tagp.tagkind
|
|
||||||
&& strncmp(p, "kind:", 5) == 0)) {
|
|
||||||
p = tagp.tagkind_end;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// print all other extra fields
|
|
||||||
int hl_id = HLF_CM;
|
|
||||||
while (*p && *p != '\r' && *p != '\n') {
|
|
||||||
if (msg_col + ptr2cells(p) >= Columns) {
|
|
||||||
msg_putchar('\n');
|
|
||||||
if (got_int) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
msg_advance(15);
|
|
||||||
}
|
|
||||||
p = msg_outtrans_one(p, hl_id, false);
|
|
||||||
if (*p == TAB) {
|
|
||||||
msg_puts_hl(" ", hl_id, false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (*p == ':') {
|
|
||||||
hl_id = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (msg_col > 15) {
|
|
||||||
msg_putchar('\n');
|
|
||||||
if (got_int) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
msg_advance(15);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (p = tagp.command;
|
|
||||||
*p && *p != '\r' && *p != '\n';
|
|
||||||
p++) {}
|
|
||||||
command_end = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put the info (in several lines) at column 15.
|
|
||||||
// Don't display "/^" and "?^".
|
|
||||||
p = tagp.command;
|
|
||||||
if (*p == '/' || *p == '?') {
|
|
||||||
p++;
|
|
||||||
if (*p == '^') {
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Remove leading whitespace from pattern
|
|
||||||
while (p != command_end && ascii_isspace(*p)) {
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (p != command_end) {
|
|
||||||
if (msg_col + (*p == TAB ? 1 : ptr2cells(p)) > Columns) {
|
|
||||||
msg_putchar('\n');
|
|
||||||
}
|
|
||||||
if (got_int) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
msg_advance(15);
|
|
||||||
|
|
||||||
// skip backslash used for escaping a command char or
|
|
||||||
// a backslash
|
|
||||||
if (*p == '\\' && (*(p + 1) == *tagp.command
|
|
||||||
|| *(p + 1) == '\\')) {
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*p == TAB) {
|
|
||||||
msg_putchar(' ');
|
|
||||||
p++;
|
|
||||||
} else {
|
|
||||||
p = msg_outtrans_one(p, 0, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't display the "$/;\"" and "$?;\""
|
|
||||||
if (p == command_end - 2 && *p == '$'
|
|
||||||
&& *(p + 1) == *tagp.command) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// don't display matching '/' or '?'
|
|
||||||
if (p == command_end - 1 && *p == *tagp.command
|
|
||||||
&& (*p == '/' || *p == '?')) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (msg_col && (!ui_has(kUIMessages) || i < num_matches - 1)) {
|
|
||||||
msg_putchar('\n');
|
|
||||||
}
|
|
||||||
os_breakcheck();
|
os_breakcheck();
|
||||||
|
if (got_int) {
|
||||||
|
tv_clear(&items_tv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
tagptrs_T tagp;
|
||||||
|
parse_match(matches[i], &tagp);
|
||||||
|
|
||||||
|
bool cur = !new_tag
|
||||||
|
&& ((g_do_tagpreview != 0 && i == ptag_entry.cur_match)
|
||||||
|
|| (use_tagstack && i == tagstack[tagstackidx].cur_match));
|
||||||
|
|
||||||
|
dict_T *d = tv_dict_alloc();
|
||||||
|
tv_dict_add_str_len(d, S_LEN("tag"), tagp.tagname, (int)(tagp.tagname_end - tagp.tagname));
|
||||||
|
tv_dict_add_str(d, S_LEN("pri"), mt_names[matches[i][0] & MT_MASK]);
|
||||||
|
if (tagp.tagkind != NULL) {
|
||||||
|
tv_dict_add_str_len(d, S_LEN("kind"), tagp.tagkind, (int)(tagp.tagkind_end - tagp.tagkind));
|
||||||
|
}
|
||||||
|
char *fname = tag_full_fname(&tagp);
|
||||||
|
tv_dict_add_str(d, S_LEN("file"), fname != NULL ? fname : "");
|
||||||
|
xfree(fname);
|
||||||
|
tv_dict_add_bool(d, S_LEN("cur"), cur ? kBoolVarTrue : kBoolVarFalse);
|
||||||
|
|
||||||
|
typval_T item = { .v_type = VAR_DICT, .vval.v_dict = d };
|
||||||
|
tv_list_append_tv(items_tv.vval.v_list, &item);
|
||||||
}
|
}
|
||||||
if (got_int) {
|
|
||||||
got_int = false; // only stop the listing
|
typval_T lua_args[] = { items_tv, { .v_type = VAR_UNKNOWN } };
|
||||||
|
typval_T rettv = TV_INITIAL_VALUE;
|
||||||
|
nlua_call_vimfn("vim._core.tag", "select", lua_args, &rettv);
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
if (rettv.v_type == VAR_NUMBER) {
|
||||||
|
idx = (int)rettv.vval.v_number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tv_clear(&items_tv);
|
||||||
|
tv_clear(&rettv);
|
||||||
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add the matching tags to the location list for the current
|
/// Add the matching tags to the location list for the current
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ local Screen = require('test.functional.ui.screen')
|
|||||||
local uv = vim.uv
|
local uv = vim.uv
|
||||||
|
|
||||||
local eq = t.eq
|
local eq = t.eq
|
||||||
local pcall_err = t.pcall_err
|
|
||||||
local matches = t.matches
|
local matches = t.matches
|
||||||
local feed = n.feed
|
local feed = n.feed
|
||||||
local eval = n.eval
|
local eval = n.eval
|
||||||
@@ -231,10 +230,13 @@ describe('vim._core', function()
|
|||||||
'vim._core.options',
|
'vim._core.options',
|
||||||
'vim._core.server',
|
'vim._core.server',
|
||||||
'vim._core.shared',
|
'vim._core.shared',
|
||||||
|
'vim._core.spell',
|
||||||
'vim._core.stringbuffer',
|
'vim._core.stringbuffer',
|
||||||
'vim._core.system',
|
'vim._core.system',
|
||||||
'vim._core.table',
|
'vim._core.table',
|
||||||
|
'vim._core.tag',
|
||||||
'vim._core.time',
|
'vim._core.time',
|
||||||
|
'vim._core.ui',
|
||||||
'vim._core.ui2',
|
'vim._core.ui2',
|
||||||
'vim._core.util',
|
'vim._core.util',
|
||||||
'vim._core.vimfn',
|
'vim._core.vimfn',
|
||||||
|
|||||||
168
test/functional/lua/ui_select_spec.lua
Normal file
168
test/functional/lua/ui_select_spec.lua
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
-- Tests for vim.ui.select(), including integration with builtins (:tselect, z=).
|
||||||
|
|
||||||
|
local t = require('test.testutil')
|
||||||
|
local n = require('test.functional.testnvim')()
|
||||||
|
local clear = n.clear
|
||||||
|
local exec_lua = n.exec_lua
|
||||||
|
local api = n.api
|
||||||
|
local eq = t.eq
|
||||||
|
local write_file = t.write_file
|
||||||
|
|
||||||
|
before_each(clear)
|
||||||
|
|
||||||
|
describe('vim.ui.select()', function()
|
||||||
|
it('can select an item', function()
|
||||||
|
local result = exec_lua [[
|
||||||
|
local items = {
|
||||||
|
{ name = 'Item 1' },
|
||||||
|
{ name = 'Item 2' },
|
||||||
|
}
|
||||||
|
local opts = {
|
||||||
|
format_item = function(entry)
|
||||||
|
return entry.name
|
||||||
|
end
|
||||||
|
}
|
||||||
|
local selected
|
||||||
|
local cb = function(item)
|
||||||
|
selected = item
|
||||||
|
end
|
||||||
|
-- inputlist would require input and block the test;
|
||||||
|
local choices
|
||||||
|
vim.fn.inputlist = function(x)
|
||||||
|
choices = x
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
vim.ui.select(items, opts, cb)
|
||||||
|
vim.wait(100, function() return selected ~= nil end)
|
||||||
|
return {selected, choices}
|
||||||
|
]]
|
||||||
|
eq({ name = 'Item 1' }, result[1])
|
||||||
|
eq({
|
||||||
|
'Select one of:',
|
||||||
|
'1: Item 1',
|
||||||
|
'2: Item 2',
|
||||||
|
}, result[2])
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('via :tselect', function()
|
||||||
|
it('passes items and applies the chosen index', function()
|
||||||
|
-- Create dummy source files so the jump succeeds.
|
||||||
|
write_file('XselTagA.c', 'int foo;\n')
|
||||||
|
write_file('XselTagB.c', 'int foo = 1;\n')
|
||||||
|
finally(function()
|
||||||
|
os.remove('XselTagA.c')
|
||||||
|
os.remove('XselTagB.c')
|
||||||
|
os.remove('XselTags')
|
||||||
|
end)
|
||||||
|
write_file(
|
||||||
|
'XselTags',
|
||||||
|
'!_TAG_FILE_FORMAT\t2\t/extended format/\n'
|
||||||
|
.. 'foo\tXselTagA.c\t/^int foo;$/;"\tv\n'
|
||||||
|
.. 'foo\tXselTagB.c\t/^int foo = 1;$/;"\tv\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
local got = exec_lua(function()
|
||||||
|
vim.opt.tags = 'XselTags'
|
||||||
|
local captured ---@type table?
|
||||||
|
vim.ui.select = function(items, opts, on_choice)
|
||||||
|
captured = { items = items, kind = opts.kind }
|
||||||
|
-- Pick the second match.
|
||||||
|
on_choice(items[2], 2)
|
||||||
|
end
|
||||||
|
vim.cmd('tselect foo')
|
||||||
|
return {
|
||||||
|
kind = captured and captured.kind,
|
||||||
|
nitems = captured and #captured.items,
|
||||||
|
item1_tag = captured and captured.items[1].tag,
|
||||||
|
item2_file = captured and captured.items[2].file,
|
||||||
|
bufname = vim.fn.fnamemodify(vim.api.nvim_buf_get_name(0), ':t'),
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
eq('tag', got.kind)
|
||||||
|
eq(2, got.nitems)
|
||||||
|
eq('foo', got.item1_tag)
|
||||||
|
eq('XselTagB.c', got.item2_file)
|
||||||
|
-- Picking item 2 should land us in XselTagB.c.
|
||||||
|
eq('XselTagB.c', got.bufname)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('keeps the buffer unchanged when the user cancels', function()
|
||||||
|
write_file('XselTagA.c', 'int foo;\n')
|
||||||
|
write_file('XselTagB.c', 'int foo = 1;\n')
|
||||||
|
finally(function()
|
||||||
|
os.remove('XselTagA.c')
|
||||||
|
os.remove('XselTagB.c')
|
||||||
|
os.remove('XselTags')
|
||||||
|
end)
|
||||||
|
write_file(
|
||||||
|
'XselTags',
|
||||||
|
'!_TAG_FILE_FORMAT\t2\t/extended format/\n'
|
||||||
|
.. 'foo\tXselTagA.c\t/^int foo;$/;"\tv\n'
|
||||||
|
.. 'foo\tXselTagB.c\t/^int foo = 1;$/;"\tv\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
api.nvim_set_option_value('tags', 'XselTags', {})
|
||||||
|
|
||||||
|
local before = api.nvim_buf_get_name(0)
|
||||||
|
exec_lua(function()
|
||||||
|
vim.ui.select = function(_, _, on_choice)
|
||||||
|
on_choice(nil, nil)
|
||||||
|
end
|
||||||
|
vim.cmd('tselect foo')
|
||||||
|
end)
|
||||||
|
|
||||||
|
eq(before, api.nvim_buf_get_name(0))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('via z=', function()
|
||||||
|
it('passes items and applies the chosen suggestion', function()
|
||||||
|
api.nvim_set_option_value('spell', true, {})
|
||||||
|
api.nvim_set_option_value('spelllang', 'en_us', {})
|
||||||
|
|
||||||
|
api.nvim_buf_set_lines(0, 0, -1, false, { 'helo' })
|
||||||
|
|
||||||
|
local got = exec_lua(function()
|
||||||
|
vim.cmd('normal! gg0')
|
||||||
|
local captured ---@type table?
|
||||||
|
vim.ui.select = function(items, opts, on_choice)
|
||||||
|
captured = { items = items, kind = opts.kind, prompt = opts.prompt }
|
||||||
|
-- Pick the first suggestion.
|
||||||
|
on_choice(items[1], 1)
|
||||||
|
end
|
||||||
|
vim.cmd('normal! z=')
|
||||||
|
return {
|
||||||
|
kind = captured and captured.kind,
|
||||||
|
prompt = captured and captured.prompt,
|
||||||
|
item1_word = captured and captured.items[1].word,
|
||||||
|
line = vim.api.nvim_buf_get_lines(0, 0, -1, false)[1],
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
eq('spell', got.kind)
|
||||||
|
-- prompt should contain the misspelled word
|
||||||
|
t.matches('helo', got.prompt)
|
||||||
|
-- The first suggestion replaced the bad word.
|
||||||
|
t.neq('helo', got.line)
|
||||||
|
eq(got.item1_word, got.line)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('keeps the word unchanged when the user cancels', function()
|
||||||
|
api.nvim_set_option_value('spell', true, {})
|
||||||
|
api.nvim_set_option_value('spelllang', 'en_us', {})
|
||||||
|
|
||||||
|
api.nvim_buf_set_lines(0, 0, -1, false, { 'helo' })
|
||||||
|
|
||||||
|
exec_lua(function()
|
||||||
|
vim.cmd('normal! gg0')
|
||||||
|
vim.ui.select = function(_, _, on_choice)
|
||||||
|
on_choice(nil, nil)
|
||||||
|
end
|
||||||
|
vim.cmd('normal! z=')
|
||||||
|
end)
|
||||||
|
|
||||||
|
eq('helo', api.nvim_buf_get_lines(0, 0, -1, false)[1])
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
@@ -2,7 +2,6 @@ local t = require('test.testutil')
|
|||||||
local n = require('test.functional.testnvim')()
|
local n = require('test.functional.testnvim')()
|
||||||
|
|
||||||
local eq = t.eq
|
local eq = t.eq
|
||||||
local pcall_err = t.pcall_err
|
|
||||||
local ok = t.ok
|
local ok = t.ok
|
||||||
local exec_lua = n.exec_lua
|
local exec_lua = n.exec_lua
|
||||||
local clear = n.clear
|
local clear = n.clear
|
||||||
@@ -17,41 +16,6 @@ describe('vim.ui', function()
|
|||||||
clear({ args_rm = { '-u' }, args = { '--clean' } })
|
clear({ args_rm = { '-u' }, args = { '--clean' } })
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('select()', function()
|
|
||||||
it('can select an item', function()
|
|
||||||
local result = exec_lua [[
|
|
||||||
local items = {
|
|
||||||
{ name = 'Item 1' },
|
|
||||||
{ name = 'Item 2' },
|
|
||||||
}
|
|
||||||
local opts = {
|
|
||||||
format_item = function(entry)
|
|
||||||
return entry.name
|
|
||||||
end
|
|
||||||
}
|
|
||||||
local selected
|
|
||||||
local cb = function(item)
|
|
||||||
selected = item
|
|
||||||
end
|
|
||||||
-- inputlist would require input and block the test;
|
|
||||||
local choices
|
|
||||||
vim.fn.inputlist = function(x)
|
|
||||||
choices = x
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
vim.ui.select(items, opts, cb)
|
|
||||||
vim.wait(100, function() return selected ~= nil end)
|
|
||||||
return {selected, choices}
|
|
||||||
]]
|
|
||||||
eq({ name = 'Item 1' }, result[1])
|
|
||||||
eq({
|
|
||||||
'Select one of:',
|
|
||||||
'1: Item 1',
|
|
||||||
'2: Item 2',
|
|
||||||
}, result[2])
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
describe('input()', function()
|
describe('input()', function()
|
||||||
it('can input text', function()
|
it('can input text', function()
|
||||||
local result = exec_lua [[
|
local result = exec_lua [[
|
||||||
|
|||||||
@@ -400,7 +400,8 @@ describe('ui/ext_messages', function()
|
|||||||
{
|
{
|
||||||
content = { { '' } },
|
content = { { '' } },
|
||||||
pos = 0,
|
pos = 0,
|
||||||
prompt = 'Type number and <Enter> (q or empty cancels): ',
|
-- Default vim.ui.select uses this prompt.
|
||||||
|
prompt = 'Type number and <Enter> or click with the mouse (q or empty cancels): ',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
-- Message depends on runtimepath, only test the static text...
|
-- Message depends on runtimepath, only test the static text...
|
||||||
@@ -408,13 +409,12 @@ describe('ui/ext_messages', function()
|
|||||||
for _, msg in ipairs(screen.messages) do
|
for _, msg in ipairs(screen.messages) do
|
||||||
eq(false, msg.history)
|
eq(false, msg.history)
|
||||||
eq('confirm', msg.kind)
|
eq('confirm', msg.kind)
|
||||||
eq(' # pri kind tag', msg.content[1][2])
|
local text = '' -- Concatenate all chunks.
|
||||||
eq('\n ', msg.content[2][2])
|
for _, chunk in ipairs(msg.content) do
|
||||||
eq('file\n', msg.content[3][2])
|
text = text .. (#chunk >= 2 and chunk[2] or chunk[1])
|
||||||
eq('> 1 F ', msg.content[4][2])
|
end
|
||||||
eq('help.txt', msg.content[5][2])
|
t.matches('^Type number and <Enter> %(q or empty cancels%):\n', text)
|
||||||
eq(' \n ', msg.content[6][2])
|
t.matches('1: > F%s+help%.txt%s+', text)
|
||||||
eq('\n *help.txt*', msg.content[#msg.content][2])
|
|
||||||
end
|
end
|
||||||
screen.messages = {}
|
screen.messages = {}
|
||||||
end,
|
end,
|
||||||
@@ -1286,7 +1286,7 @@ stack traceback:
|
|||||||
},
|
},
|
||||||
messages = {
|
messages = {
|
||||||
{
|
{
|
||||||
content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Hullo"' } },
|
content = { { 'Change "helllo" to:\n1: "Hello"\n2: "Hallo"\n3: "Hullo"' } },
|
||||||
kind = 'confirm',
|
kind = 'confirm',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -466,13 +466,12 @@ func Test_spellsuggest_option_number()
|
|||||||
call assert_equal('A baord', getline(1))
|
call assert_equal('A baord', getline(1))
|
||||||
|
|
||||||
let a = execute('norm $z=')
|
let a = execute('norm $z=')
|
||||||
|
" Nvim: z= goes through vim.ui.select().
|
||||||
call assert_equal(
|
call assert_equal(
|
||||||
\ "\n"
|
\ "\n"
|
||||||
\ .. "Change \"baord\" to:\n"
|
\ .. "Change \"baord\" to:\n"
|
||||||
\ .. " 1 \"board\"\n"
|
\ .. "1: \"board\"\n"
|
||||||
\ .. " 2 \"bard\"\n"
|
\ .. "2: \"bard\"\n"
|
||||||
"\ Nvim: Prompt message is sent to cmdline prompt.
|
|
||||||
"\ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a)
|
|
||||||
\ , a)
|
\ , a)
|
||||||
|
|
||||||
set spell spellsuggest=0
|
set spell spellsuggest=0
|
||||||
@@ -505,14 +504,13 @@ func Test_spellsuggest_option_expr()
|
|||||||
new
|
new
|
||||||
call setline(1, 'baord')
|
call setline(1, 'baord')
|
||||||
let a = execute('norm z=')
|
let a = execute('norm z=')
|
||||||
|
" Nvim: z= goes through vim.ui.select().
|
||||||
call assert_equal(
|
call assert_equal(
|
||||||
\ "\n"
|
\ "\n"
|
||||||
\ .. "Change \"baord\" to:\n"
|
\ .. "Change \"baord\" to:\n"
|
||||||
\ .. " 1 \"BARD\"\n"
|
\ .. "1: \"BARD\"\n"
|
||||||
\ .. " 2 \"BOARD\"\n"
|
\ .. "2: \"BOARD\"\n"
|
||||||
\ .. " 3 \"BROAD\"\n"
|
\ .. "3: \"BROAD\"\n"
|
||||||
"\ Nvim: Prompt message is sent to cmdline prompt.
|
|
||||||
"\ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a)
|
|
||||||
\ , a)
|
\ , a)
|
||||||
|
|
||||||
" With verbose, z= should show the score i.e. word length with
|
" With verbose, z= should show the score i.e. word length with
|
||||||
@@ -522,11 +520,9 @@ func Test_spellsuggest_option_expr()
|
|||||||
call assert_equal(
|
call assert_equal(
|
||||||
\ "\n"
|
\ "\n"
|
||||||
\ .. "Change \"baord\" to:\n"
|
\ .. "Change \"baord\" to:\n"
|
||||||
\ .. " 1 \"BARD\" (4 - 0)\n"
|
\ .. "1: \"BARD\" (4 - 0)\n"
|
||||||
\ .. " 2 \"BOARD\" (5 - 0)\n"
|
\ .. "2: \"BOARD\" (5 - 0)\n"
|
||||||
\ .. " 3 \"BROAD\" (5 - 0)\n"
|
\ .. "3: \"BROAD\" (5 - 0)\n"
|
||||||
"\ Nvim: Prompt message is sent to cmdline prompt.
|
|
||||||
"\ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a)
|
|
||||||
\ , a)
|
\ , a)
|
||||||
|
|
||||||
set spell& spellsuggest& verbose&
|
set spell& spellsuggest& verbose&
|
||||||
|
|||||||
@@ -1239,17 +1239,12 @@ func Test_tselect_listing()
|
|||||||
|
|
||||||
call feedkeys("\<CR>", "t")
|
call feedkeys("\<CR>", "t")
|
||||||
let l = split(execute("tselect first"), "\n")
|
let l = split(execute("tselect first"), "\n")
|
||||||
|
" Nvim: :tselect goes through vim.ui.select().
|
||||||
let expected =<< [DATA]
|
let expected =<< [DATA]
|
||||||
# pri kind tag file
|
Type number and <Enter> (q or empty cancels):
|
||||||
1 FS v first Xfoo
|
1: FS v first Xfoo
|
||||||
typeref:typename:int
|
2: FS v first Xfoo
|
||||||
1
|
|
||||||
2 FS v first Xfoo
|
|
||||||
typeref:typename:char
|
|
||||||
2
|
|
||||||
[DATA]
|
[DATA]
|
||||||
" Type number and <Enter> (q or empty cancels):
|
|
||||||
" Nvim: Prompt message is sent to cmdline prompt.
|
|
||||||
|
|
||||||
call assert_equal(expected, l)
|
call assert_equal(expected, l)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user