diff --git a/Cargo.lock b/Cargo.lock index 58c1d566..8dbf10b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1781,9 +1781,9 @@ dependencies = [ [[package]] name = "ctutils" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03bb0e1cc970d482d121d9a1744999169b69a07470b3d644a7894e53fcaf4574" +checksum = "38d28fb6aa7dd680b3ffd1a3e64e115c177bdba63c0ae7f9cd48129ff05c4fa0" dependencies = [ "cmov", ] diff --git a/crates/tests/fixtures/nvim/focus.lua b/crates/tests/fixtures/nvim/focus.lua index bf1fb435..cd324aa9 100644 --- a/crates/tests/fixtures/nvim/focus.lua +++ b/crates/tests/fixtures/nvim/focus.lua @@ -14,7 +14,9 @@ package.loaded['argon.client'] = { local focus = require('argon.focus') local stage = 1 -local exit_keys = { '', '', '', '', '' } +local exit_keys = { '', '', '', '', '', '' } +local pending_command = 'let g:argon_focus_pending = 1' +local pending_replaced = false local function checked(callback) local ok, err = pcall(callback) @@ -26,6 +28,10 @@ end vim.api.nvim_create_autocmd('CmdlineEnter', { callback = function() + -- The pending command line stays open until the GUI replaces it. + if stage == 6 and not pending_replaced then + return + end local exit_key = assert(exit_keys[stage]) vim.schedule(function() vim.api.nvim_input(exit_key) @@ -33,6 +39,17 @@ vim.api.nvim_create_autocmd('CmdlineEnter', { end, }) +vim.api.nvim_create_autocmd('CmdlineChanged', { + callback = function() + if stage == 6 and not pending_replaced and vim.fn.getcmdline() == pending_command then + pending_replaced = true + vim.schedule(function() + focus.editor('let g:argon_focus_replaced = 1') + end) + end + end, +}) + vim.api.nvim_create_autocmd('CmdlineLeave', { callback = function() vim.defer_fn(function() @@ -57,9 +74,14 @@ vim.api.nvim_create_autocmd('CmdlineLeave', { assert(gui_focus_count == 3, 'interrupting a GUI command should focus the GUI') stage = 5 vim.api.nvim_input(':let g:argon_ordinary_command = 1') - else + elseif stage == 5 then assert(vim.g.argon_ordinary_command == nil) assert(gui_focus_count == 3, 'cancelling an ordinary command should remain in Neovim') + stage = 6 + vim.api.nvim_input(':' .. pending_command) + elseif vim.g.argon_focus_replaced == 1 then + assert(vim.g.argon_focus_pending == nil, 'a GUI command should abandon a pending command line') + assert(gui_focus_count == 4, 'executing a GUI command should focus the GUI') vim.cmd('quitall!') end end) diff --git a/crates/tests/src/nvim.rs b/crates/tests/src/nvim.rs index 9beb2179..92425e14 100644 --- a/crates/tests/src/nvim.rs +++ b/crates/tests/src/nvim.rs @@ -23,8 +23,9 @@ mod tests { #[tokio::test] async fn gui_commands_respect_return_focus_configuration() { let mut command = crate::nvim_command(); + // Sourced rather than run with `-l`, which exits before typeahead is processed. command - .arg("-l") + .arg("-S") .arg(crate::repository_root().join("crates/tests/fixtures/nvim/focus.lua")); let child = command.spawn().expect("start headless Neovim"); crate::finish_nvim(child).await; diff --git a/lua/argon/focus.lua b/lua/argon/focus.lua index 3d56fdff..69a75698 100644 --- a/lua/argon/focus.lua +++ b/lua/argon/focus.lua @@ -56,14 +56,15 @@ end ---@param opts? { return_to_gui?: boolean } function M.editor(command, opts) opts = opts or {} - local mode = vim.api.nvim_get_mode().mode - local keys = mode:sub(1, 1) == 't' and ':' or ':' + -- reaches Normal mode from any mode. A fed would instead + -- execute a pending command line, and an error there drops the keys after it. + local keys = ':' if type(command) == 'string' then keys = keys .. command end -- Wait for this command line to be entered before watching it leave. The - -- leading may itself leave a pre-existing command line or prompt. + -- leading may itself leave a pre-existing command line. vim.api.nvim_clear_autocmds({ group = gui_command_group }) if opts.return_to_gui ~= false then return_to_gui_after_command()