feat: Add fetch/push commands and log keybinds, make long-running operations non-blocking

Convert blocking jj commands to async using vim.fn.jobstart() to prevent UI
freezing during fetch, push, rebase, squash, and other heavy operations.

Changes:
- Implement execute_command_async() in runner with proper error handling
  - Captures stdout from job and passes to success callback
  - Supports stdin input for commands that require it
  - Includes silent flag to suppress error notifications
  - Replicates sync version's error handling behavior

- Convert blocking operations to async:
  - handle_log_fetch, handle_log_push_all, handle_log_new
  - handle_log_edit, handle_log_abandon, handle_log_describe
  - describe.execute_describe, all init.lua commands

- Add push/fetch as public commands with CLI support:
  - M.fetch() - fetch from remote
  - M.push(opts) - push all changes or specific bookmark
  - CLI: :J push, :J push <bookmark>, :J fetch

- Implement handle_log_push_bookmark() for revision under cursor:
  - Retrieves and strips modified bookmark indicator (*)
  - Pushes specific bookmark to remote
  - Keybind: Shift+p in log buffer

- Update README with new keybinds:
  - a: abandon revision under cursor
  - f: fetch from remote
  - p: push all changes
  - Shift+p: push revision's bookmark

- Add Lua API docs for push command options
This commit is contained in:
NicolasGB
2025-11-27 20:58:34 +01:00
committed by Nicolas GB
parent 8f1a60342a
commit e22bf5ec4e
6 changed files with 282 additions and 71 deletions
+44 -1
View File
@@ -29,5 +29,48 @@ function M.execute_command(cmd, error_prefix, input, silent)
return output, success
end
return M
--- Execute a system command asynchronously
--- @param cmd string The command to execute
--- @param on_success function|nil Callback on success, receives output as parameter
--- @param error_prefix string|nil Optional error message prefix
--- @param input string|nil Optional input to pass to stdin
--- @param silent boolean|nil Optional to silent the notification
function M.execute_command_async(cmd, on_success, error_prefix, input, silent)
local output_lines = {}
local job_id = vim.fn.jobstart(cmd, {
on_stdout = function(_, data)
for _, line in ipairs(data) do
if line ~= "" then
table.insert(output_lines, line)
end
end
end,
on_exit = function(_, exit_code)
local output = table.concat(output_lines, "\n")
if exit_code == 0 then
if on_success then
on_success(output)
end
else
local error_message
if error_prefix then
error_message = string.format("%s: %s", error_prefix, output)
else
error_message = output
end
if not silent then
vim.notify(error_message, vim.log.levels.ERROR, { title = "JJ" })
end
end
end,
})
-- Send stdin if provided
if input then
vim.fn.chansend(job_id, input)
vim.fn.chanclose(job_id, "stdin")
end
end
return M