From b34e08d8155800272cfd03fd3afbbc9dc837762d Mon Sep 17 00:00:00 2001 From: NicolasGB Date: Fri, 21 Nov 2025 16:59:51 +0100 Subject: [PATCH] fix: describe command reopening log buffer without flicker - Close terminal buffer before opening describe editor - Capture log state before closing to detect if we should reopen - Add on_unload callback to editor to handle log reopening - Suppress redraws during buffer transitions with lazyredraw - Remove duplicate hardcoded status keymaps from terminal.lua (were referencing undefined functions) - Fix resolve_keymaps_from_specs to validate handlers exist before creating keymaps --- lua/jj/cmd.lua | 57 +++++++++++++++++++++++++++++++----------- lua/jj/core/buffer.lua | 2 +- lua/jj/ui/editor.lua | 13 +++++++++- lua/jj/ui/terminal.lua | 12 +++------ 4 files changed, 59 insertions(+), 25 deletions(-) diff --git a/lua/jj/cmd.lua b/lua/jj/cmd.lua index e968501..f9e705d 100644 --- a/lua/jj/cmd.lua +++ b/lua/jj/cmd.lua @@ -64,7 +64,7 @@ local function resolve_keymaps_from_specs(cfg, specs) for key, spec in pairs(specs) do local lhs = cfg[key] - if lhs then + if lhs and spec.handler then if type(lhs) == "table" then for _, key_lhs in ipairs(lhs) do table.insert( @@ -172,8 +172,12 @@ function M.describe(description, revset, opts) local status_files = parser.get_status_files(status_result) local old_description = vim.trim(old_description_raw) - local first_line = old_description:match("^[^\n]*") or "" - local text = { first_line } + -- Split description into lines to preserve multiline descriptions + local description_lines = vim.split(old_description, "\n") + local text = {} + for _, line in ipairs(description_lines) do + table.insert(text, line) + end table.insert(text, "") -- Empty line to separate from user input table.insert(text, "JJ: Change ID: " .. revset) table.insert(text, "JJ: This commit contains the following changes:") @@ -183,6 +187,12 @@ function M.describe(description, revset, opts) table.insert(text, "JJ:") -- blank line table.insert(text, 'JJ: Lines starting with "JJ:" (like this one) will be removed') + -- Check if we're coming from the log view so we can reopen it after editing + local open_log_on_close = terminal.state.buf_cmd == "log" + + -- Close the terminal buffer before opening editor + terminal.close_terminal_buffer() + editor.open_editor(text, function(buf_lines) local user_lines = {} for _, line in ipairs(buf_lines) do @@ -193,9 +203,17 @@ function M.describe(description, revset, opts) -- Join lines and trim leading/trailing whitespace local trimmed_description = table.concat(user_lines, "\n"):gsub("^%s+", ""):gsub("%s+$", "") execute_describe(trimmed_description, revset) + -- Once editing is done, reopen the log if we came from there + end, function() + if open_log_on_close then + vim.schedule(function() + vim.o.lazyredraw = true + M.log({}) + vim.o.lazyredraw = false + vim.cmd("redraw!") + end) + end end, describe_editor_keymaps()) - - terminal.close_terminal_buffer() else -- Use input mode local merged_opts = vim.tbl_deep_extend("force", default_describe_opts, opts or {}) @@ -279,8 +297,6 @@ end -- Resolve status keymaps from config, filtering out nil values --- @return jj.core.buffer.keymap[] local function status_keymaps() - -- Reduce repetition by declaring a specification table. - -- Each entry maps the config key name to: local cfg = M.config.keymaps.status or {} local specs = { open_file = { @@ -407,14 +423,6 @@ function M.squash() end end ---- @class jj.cmd.log_opts ---- @field summary? boolean ---- @field reversed? boolean ---- @field no_graph? boolean ---- @field limit? uinteger ---- @field revisions? string - -local default_log_opts = { summary = false, reversed = false, no_graph = false, limit = 20 } --- --- Create a new change relative to the revision under the cursor in a jj log buffer. --- Behavior: @@ -595,6 +603,17 @@ local function log_keymaps() return resolve_keymaps_from_specs(cfg, specs) end +--- @class jj.cmd.log_opts +--- @field summary? boolean +--- @field reversed? boolean +--- @field no_graph? boolean +--- @field limit? uinteger +--- @field revisions? string +--- @field raw_flags? string + +---@type jj.cmd.log_opts +local default_log_opts = { summary = false, reversed = false, no_graph = false, limit = 20, raw_flats = nil } + -- Jujutsu log --- @param opts? jj.cmd.log_opts function M.log(opts) @@ -605,6 +624,11 @@ function M.log(opts) local cmd = "jj log" local merged_opts = vim.tbl_extend("force", default_log_opts, opts or {}) + -- If a raw has been given simply execute it as is + if merged_opts.raw then + return terminal.run(string.format("%s %s", cmd, merged_opts.raw), log_keymaps()) + end + for key, value in pairs(merged_opts) do key = key:gsub("_", "-") if key == "limit" and value then @@ -808,6 +832,9 @@ function M.j(args) redo = function() M.redo() end, + log = function() + M.log({ raw_flags = remaining_args_str ~= "" and remaining_args_str or nil }) + end, } if handlers[subcommand] then diff --git a/lua/jj/core/buffer.lua b/lua/jj/core/buffer.lua index 822b31e..e06eeaf 100644 --- a/lua/jj/core/buffer.lua +++ b/lua/jj/core/buffer.lua @@ -297,4 +297,4 @@ function M.start_insert(buf) vim.cmd("startinsert") end -return M +return M \ No newline at end of file diff --git a/lua/jj/ui/editor.lua b/lua/jj/ui/editor.lua index 7038dd9..278fc77 100644 --- a/lua/jj/ui/editor.lua +++ b/lua/jj/ui/editor.lua @@ -51,8 +51,9 @@ end ---@param initial_text string[] Lines to initialize the buffer with ---@param on_done fun(buf: string[])? Optional callback called with user text on buffer write +---@param on_unload? fun()? Optional callback called when the buffer is closed ---@param keymaps? jj.core.buffer.keymap[] Optional keymaps for the buffer -function M.open_editor(initial_text, on_done, keymaps) +function M.open_editor(initial_text, on_done, on_unload, keymaps) -- Initialize highlight groups once init_highlights() @@ -156,6 +157,16 @@ function M.open_editor(initial_text, on_done, keymaps) vim.bo[buf].modified = false end, }) + + -- Register the on_close callback + if on_unload then + vim.api.nvim_create_autocmd("BufWipeout", { + buffer = buf, + callback = function() + on_unload() + end, + }) + end end return M diff --git a/lua/jj/ui/terminal.lua b/lua/jj/ui/terminal.lua index f5f9893..04d1792 100644 --- a/lua/jj/ui/terminal.lua +++ b/lua/jj/ui/terminal.lua @@ -158,6 +158,7 @@ function M.run_floating(cmd) { modes = { "n", "v" }, lhs = "i", rhs = function() end }, { modes = { "n", "v" }, lhs = "c", rhs = function() end }, { modes = { "n", "v" }, lhs = "a", rhs = function() end }, + { modes = { "n", "v" }, lhs = "u", rhs = function() end }, { modes = { "n", "v" }, lhs = "q", @@ -291,6 +292,7 @@ function M.run(cmd, keymaps) { modes = { "n", "v" }, lhs = "i", rhs = function() end }, { modes = { "n", "v" }, lhs = "c", rhs = function() end }, { modes = { "n", "v" }, lhs = "a", rhs = function() end }, + { modes = { "n", "v" }, lhs = "u", rhs = function() end }, -- Close terminal buffer { modes = { "n", "v" }, @@ -327,14 +329,8 @@ function M.run(cmd, keymaps) end end - -- Add Enter key mapping for status buffers to open files - if cmd[2] == "st" or cmd[2] == "status" then - new_command_keymaps = { - { modes = "n", lhs = "", rhs = handle_status_enter, opts = { desc = "Open file under cursor" } }, - { modes = "n", lhs = "X", rhs = handle_status_restore, opts = { desc = "Restore file under cursor" } }, - } - end - + -- Status keymaps are already handled in cmd.lua via status_keymaps() + -- No need to duplicate them here if #new_command_keymaps > 0 then buffer.set_keymaps(state.buf, new_command_keymaps) vim.b[state.buf].jj_command_keymaps = new_command_keymaps