Skip to content

Commit

Permalink
fix: queue successive calls to input and select (#139)
Browse files Browse the repository at this point in the history
  • Loading branch information
stevearc committed Jan 21, 2024
1 parent 7237cdf commit 9de702f
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 11 deletions.
4 changes: 2 additions & 2 deletions lua/dressing/input.lua
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ end

---@param opts string|dressing.InputOptions
---@param on_confirm fun(text?: string)
local function show_input(opts, on_confirm)
local show_input = util.make_queued_async_fn(2, function(opts, on_confirm)
vim.validate({
on_confirm = { on_confirm, "function", false },
})
Expand Down Expand Up @@ -499,7 +499,7 @@ local function show_input(opts, on_confirm)
end
close_completion_window()
apply_highlight()
end
end)

setmetatable(M, {
-- use schedule_wrap to avoid a bug when vim opens
Expand Down
5 changes: 3 additions & 2 deletions lua/dressing/select/init.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
local global_config = require("dressing.config")
local patch = require("dressing.patch")
local util = require("dressing.util")

local M = {}

Expand Down Expand Up @@ -33,7 +34,7 @@ end
-- (see https://github.com/stevearc/dressing.nvim/issues/15)
-- also to prevent focus problems for providers
-- (see https://github.com/stevearc/dressing.nvim/issues/59)
local select = vim.schedule_wrap(function(items, opts, on_choice)
local select = vim.schedule_wrap(util.make_queued_async_fn(3, function(items, opts, on_choice)
vim.validate({
items = {
items,
Expand Down Expand Up @@ -83,7 +84,7 @@ local select = vim.schedule_wrap(function(items, opts, on_choice)
on_choice(...)
end)
)
end)
end))

setmetatable(M, {
__call = function(_, ...)
Expand Down
34 changes: 34 additions & 0 deletions lua/dressing/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,38 @@ M.schedule_wrap_before_vimenter = function(func)
end
end

---Wrap an async function so that if called multiple times only one will execute concurrently
---@param callback_arg_num integer The position of the callback argument in the function
---@param fn function
M.make_queued_async_fn = function(callback_arg_num, fn)
local queue = {}
local consuming = false

local function consume()
if #queue == 0 then
consuming = false
return
end
consuming = true
local args = table.remove(queue, 1)
fn(vim.F.unpack_len(args))
end

return function(...)
local args = vim.F.pack_len(...)
local cb = args[callback_arg_num]
args[callback_arg_num] = function(...)
local cb_args = vim.F.pack_len(...)
vim.schedule(function()
cb(vim.F.unpack_len(cb_args))
vim.schedule(consume)
end)
end
table.insert(queue, args)
if not consuming then
consume()
end
end
end

return M
22 changes: 15 additions & 7 deletions tests/input_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -122,16 +122,24 @@ a.describe("input modal", function()
assert(not startinsert_called, "Got 'true' expected 'false'")
end)

a.it("cancels first callback if second input is opened", function()
local tx, rx = channel.oneshot()
vim.ui.input({}, tx)
a.it("queues successive calls to vim.ui.input", function()
local tx1, rx1 = channel.oneshot()
local tx2, rx2 = channel.oneshot()
vim.ui.input({}, tx1)
vim.ui.input({}, tx2)
util.feedkeys({
"i", -- HACK have to do this because :startinsert doesn't work in tests,
"my text",
"first text<CR>",
})
vim.ui.input({}, function() end)
local ret = rx()
assert(ret == nil, string.format("Got '%s' expected nil", ret))
local ret = rx1()
assert(ret == "first text", string.format("Got '%s' expected 'first text'", ret))
a.util.sleep(10)
util.feedkeys({
"i", -- HACK have to do this because :startinsert doesn't work in tests,
"second text<CR>",
})
ret = rx2()
assert(ret == "second text", string.format("Got '%s' expected 'second text'", ret))
end)

a.it("supports completion", function()
Expand Down

0 comments on commit 9de702f

Please sign in to comment.