mirror of
https://github.com/neovim/neovim.git
synced 2026-07-15 13:50:33 +00:00
fix(ui): z=, tselect with async vim.ui.select
Problem:
After 55ceb31, z= and tselect don't work if `vim.ui.select` is an async
provider (especially terminal buffers).
Solution:
Drop the `vim.wait()` approach, use an async approach.
fix #39506
This commit is contained in:
@@ -6,7 +6,8 @@ local uv = vim.uv
|
||||
local N_ = vim.fn.gettext
|
||||
|
||||
--- Parsed ex command arguments for builtin commands, passed from C via `nlua_call_excmd`.
|
||||
--- Inherits fields from user command args: args, bang, line1, line2, range, count, reg, smods.
|
||||
--- Inherits fields from user command args: name, args, bang, line1, line2, range, count, reg, smods.
|
||||
--- Note: For builtin commands `name` is the canonical command name.
|
||||
--- @class vim._core.ExCmdArgs : vim.api.keyset.create_user_command.command_args
|
||||
|
||||
local M = {}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
local select_blocking = require('vim._core.ui').select_blocking
|
||||
local N_ = vim.fn.gettext
|
||||
|
||||
local M = {}
|
||||
@@ -10,13 +9,15 @@ local M = {}
|
||||
--- @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`).
|
||||
|
||||
--- Implements `spell_suggest()` (`z=`) vim.ui.select().
|
||||
--- Implements `spell_suggest()` (`z=`) via vim.ui.select().
|
||||
---
|
||||
--- async: returns immediately, the chosen suggestion is applied later
|
||||
--- by re-running `:normal! [idx]z=` from `on_choice`.
|
||||
---
|
||||
--- @param items vim._core.spell.Suggestion[]
|
||||
--- @param bad string The misspelled word being replaced.
|
||||
--- @return integer? # Selected item (1-indexed), or nil if cancelled.
|
||||
function M.select_suggest(items, bad)
|
||||
return select_blocking(items, {
|
||||
vim.ui.select(items, {
|
||||
prompt = N_('Change "%s" to:'):format(bad),
|
||||
kind = 'spell',
|
||||
format_item = function(s)
|
||||
@@ -29,7 +30,14 @@ function M.select_suggest(items, bad)
|
||||
end
|
||||
return ('"%s"%s%s'):format(s.word, extra, score)
|
||||
end,
|
||||
})
|
||||
}, function(_, idx)
|
||||
if not idx then
|
||||
return
|
||||
end
|
||||
-- Queue ":normal! [idx]z=" as user input, so the recursive spell_suggest runs via the normal
|
||||
-- input-dispatch loop. Using vim.schedule + vim.cmd can hang bc of "Press ENTER".
|
||||
vim.fn.feedkeys(vim.keycode(('<Cmd>normal! %dz=<CR>'):format(idx)), 'in')
|
||||
end)
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
local select_blocking = require('vim._core.ui').select_blocking
|
||||
local N_ = vim.fn.gettext
|
||||
|
||||
local M = {}
|
||||
@@ -13,31 +12,47 @@ local M = {}
|
||||
|
||||
--- Implements `do_tag()` (`:tselect`, ambiguous `:tag`, …) via vim.ui.select().
|
||||
---
|
||||
--- @param items vim._core.tag.Match[] One per matching tag.
|
||||
--- @return integer? # Selected item (1-indexed), or nil if cancelled.
|
||||
function M.select_tag(items)
|
||||
--- async: returns immediately, the chosen tag is applied later by re-running
|
||||
--- `:[mods] [idx]tag {tagname}` (or `stag`) from `on_choice`.
|
||||
---
|
||||
--- @param eap vim._core.ExCmdArgs Original :tselect/:stselect/… invocation.
|
||||
--- @param extra { items: vim._core.tag.Match[], tagname: string }
|
||||
function M.select_tag(eap, extra)
|
||||
local items, tagname = extra.items, extra.tagname
|
||||
-- :stag/:stselect/:stjump need a split when re-invoked.
|
||||
local stag = eap.name:sub(1, 1) == 's'
|
||||
-- `eap.mods` is the raw modifier string (e.g. ":vert silent").
|
||||
local mods_str = eap.mods ~= '' and (eap.mods .. ' ') or ''
|
||||
|
||||
local taglen = 18
|
||||
for _, m in ipairs(items) do
|
||||
taglen = math.max(taglen, vim.fn.strdisplaywidth(m.tag) + 2)
|
||||
end
|
||||
|
||||
return select_blocking(items, {
|
||||
vim.ui.select(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
|
||||
m.extra and (' ' .. m.extra) or ''
|
||||
)
|
||||
end,
|
||||
})
|
||||
}, function(_, idx)
|
||||
if not idx then
|
||||
return
|
||||
end
|
||||
-- Queue ":[mods] [idx](s)tag {tagname}" as user input, so the recursive do_tag runs via the
|
||||
-- normal input-dispatch loop. Using vim.schedule + vim.cmd can hang bc of "Press ENTER".
|
||||
local cmd = stag and 'stag' or 'tag'
|
||||
vim.fn.feedkeys(vim.keycode(('<Cmd>%s%d%s %s<CR>'):format(mods_str, idx, cmd, tagname)), 'in')
|
||||
end)
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
Reference in New Issue
Block a user