diff --git a/README.md b/README.md index c082ba3fe..156682033 100644 --- a/README.md +++ b/README.md @@ -70,8 +70,6 @@ neogit.setup { disable_context_highlighting = false, -- Disables signs for sections/items/hunks disable_signs = false, - -- Do not ask to confirm the commit - just do it when the buffer is closed. - disable_commit_confirmation = false, -- Changes what mode the Commit Editor starts in. `true` will leave nvim in normal mode, `false` will change nvim to -- insert mode, and `"auto"` will change nvim to insert mode IF the commit message is empty, otherwise leaving it in -- normal mode. diff --git a/lua/neogit/buffers/commit_editor/init.lua b/lua/neogit/buffers/commit_editor/init.lua index 49ffb6aab..623a4bc57 100644 --- a/lua/neogit/buffers/commit_editor/init.lua +++ b/lua/neogit/buffers/commit_editor/init.lua @@ -1,6 +1,9 @@ local Buffer = require("neogit.lib.buffer") local config = require("neogit.config") local input = require("neogit.lib.input") +local util = require("neogit.lib.util") + +local pad = util.pad_right local M = {} @@ -30,7 +33,9 @@ function M.new(filename, on_unload) end function M:open() - local should_commit = false + local mapping = config.get_reversed_commit_editor_maps() + local aborted = false + self.buffer = Buffer.create { name = self.filename, filetype = "NeogitCommitMessage", @@ -39,38 +44,60 @@ function M:open() kind = config.values.commit_editor.kind, modifiable = true, readonly = false, - autocmds = { - ["BufUnload"] = function(o) - local buf = Buffer.create { - name = o.buf, - } - if not should_commit and buf:get_option("modified") then - if - not config.values.disable_commit_confirmation - and not input.get_confirmation("Are you sure you want to commit?") - then - -- Clear the buffer, without filling the register - buf:clear() - buf:write() - end + after = function(buffer) + local padding = util.max_length(util.flatten(vim.tbl_values(mapping))) + local pad_mapping = function(name) + return pad(mapping[name][1], padding) + end + + -- stylua: ignore + local help_lines = { + "# Neogit Commands:", + string.format("# %s close", pad_mapping("Close")), + string.format("# %s tell Git to make it happen", pad_mapping("Submit")), + string.format("# %s tell Git that you changed your mind, i.e. abort", pad_mapping("Abort")), + "#" + } + + help_lines = util.filter_map(help_lines, function(line) + if not line:match("") then -- mapping will be if user unbinds key + return line end + end) - if self.on_unload and not should_commit then - self.on_unload(0) + local line = vim.fn.search("# Changes to be committed:") - 2 + buffer:set_lines(line, line, false, help_lines) + buffer:write() + buffer:move_cursor(1) + end, + autocmds = { + ["BufUnload"] = function() + if self.on_unload then + self.on_unload(aborted and 1 or 0) end - require("neogit.process").defer_show_preview_buffers() + if not aborted then + require("neogit.process").defer_show_preview_buffers() + end end, }, mappings = { n = { - ["q"] = function(buffer) - if not buffer:get_option("modified") then - buffer:close(true) - elseif input.get_confirmation("Commit message hasn't been saved. Abort?") then - should_commit = true - buffer:close(true) + [mapping["Close"]] = function(buffer) + if buffer:get_option("modified") and input.get_confirmation("Save changes?") then + buffer:write() end + + buffer:close(true) + end, + [mapping["Submit"]] = function(buffer) + buffer:write() + buffer:close(true) + end, + [mapping["Abort"]] = function(buffer) + aborted = true + buffer:write() + buffer:close(true) end, }, }, diff --git a/lua/neogit/buffers/description_editor.lua b/lua/neogit/buffers/description_editor/init.lua similarity index 89% rename from lua/neogit/buffers/description_editor.lua rename to lua/neogit/buffers/description_editor/init.lua index b181401d2..355db8673 100644 --- a/lua/neogit/buffers/description_editor.lua +++ b/lua/neogit/buffers/description_editor/init.lua @@ -3,10 +3,10 @@ local config = require("neogit.config") local M = {} -function M.new(filename, on_close) +function M.new(filename, on_unload) local instance = { filename = filename, - on_close = on_close, + on_unload = on_unload, buffer = nil, } @@ -26,7 +26,7 @@ function M:open() readonly = false, autocmds = { ["BufUnload"] = function() - self.on_close() + self.on_unload() vim.cmd("silent w!") require("neogit.process").defer_show_preview_buffers() end, diff --git a/lua/neogit/buffers/merge_editor.lua b/lua/neogit/buffers/merge_editor/init.lua similarity index 89% rename from lua/neogit/buffers/merge_editor.lua rename to lua/neogit/buffers/merge_editor/init.lua index ae0d0a280..02c59f3c2 100644 --- a/lua/neogit/buffers/merge_editor.lua +++ b/lua/neogit/buffers/merge_editor/init.lua @@ -3,10 +3,10 @@ local config = require("neogit.config") local M = {} -function M.new(filename, on_close) +function M.new(filename, on_unload) local instance = { filename = filename, - on_close = on_close, + on_unload = on_unload, buffer = nil, } @@ -26,7 +26,7 @@ function M:open() readonly = false, autocmds = { ["BufUnload"] = function() - self.on_close() + self.on_unload() vim.cmd("silent w!") require("neogit.process").defer_show_preview_buffers() end, diff --git a/lua/neogit/buffers/rebase_editor/init.lua b/lua/neogit/buffers/rebase_editor/init.lua index 4a0cead16..7b7b2c8f3 100644 --- a/lua/neogit/buffers/rebase_editor/init.lua +++ b/lua/neogit/buffers/rebase_editor/init.lua @@ -31,10 +31,10 @@ local function line_action(action) end end -function M.new(filename, on_close) +function M.new(filename, on_unload) local instance = { filename = filename, - on_close = on_close, + on_unload = on_unload, buffer = nil, } @@ -99,8 +99,8 @@ function M:open() end, autocmds = { ["BufUnload"] = function() - if self.on_close then - self.on_close(aborted and 1 or 0) + if self.on_unload then + self.on_unload(aborted and 1 or 0) end if not aborted then diff --git a/lua/neogit/buffers/tag_editor.lua b/lua/neogit/buffers/tag_editor/init.lua similarity index 89% rename from lua/neogit/buffers/tag_editor.lua rename to lua/neogit/buffers/tag_editor/init.lua index 6aeceb754..4cfa2c19b 100644 --- a/lua/neogit/buffers/tag_editor.lua +++ b/lua/neogit/buffers/tag_editor/init.lua @@ -3,10 +3,10 @@ local config = require("neogit.config") local M = {} -function M.new(filename, on_close) +function M.new(filename, on_unload) local instance = { filename = filename, - on_close = on_close, + on_unload = on_unload, buffer = nil, } @@ -26,7 +26,7 @@ function M:open() readonly = false, autocmds = { ["BufUnload"] = function() - self.on_close() + self.on_unload() vim.cmd("silent w!") require("neogit.process").defer_show_preview_buffers() end, diff --git a/lua/neogit/config.lua b/lua/neogit/config.lua index 8239efebc..910fcaf8a 100644 --- a/lua/neogit/config.lua +++ b/lua/neogit/config.lua @@ -58,6 +58,18 @@ function M.get_reversed_rebase_editor_maps() return reversed_rebase_editor_maps end + +local reversed_commit_editor_maps +---@return table +--- Returns a map of commands, mapped to the list of keys which trigger them. +function M.get_reversed_commit_editor_maps() + if not reversed_commit_editor_maps then + reversed_commit_editor_maps = get_reversed_maps(M.values.mappings.commit_editor) + end + + return reversed_commit_editor_maps +end + ---@alias WindowKind ---|"split" Open in a split ---| "vsplit" Open in a vertical split @@ -106,13 +118,16 @@ end ---@alias NeogitConfigMappingsPopup "HelpPopup" | "DiffPopup" | "PullPopup" | "RebasePopup" | "MergePopup" | "PushPopup" | "CommitPopup" | "LogPopup" | "RevertPopup" | "StashPopup" | "IgnorePopup" | "CherryPickPopup" | "BranchPopup" | "FetchPopup" | "ResetPopup" | "RemotePopup" | "TagPopup" | false ----@alias NeogitConfigMappingsRebaseEditor "Pick" | "Reword" | "Edit" | "Squash" | "Fixup" | "Execute" | "Drop" | "Break" | "MoveUp" | "MoveDown" | "Close" | "OpenCommit" | "Submit" | "Abort" | false +---@alias NeogitConfigMappingsRebaseEditor "Pick" | "Reword" | "Edit" | "Squash" | "Fixup" | "Execute" | "Drop" | "Break" | "MoveUp" | "MoveDown" | "Close" | "OpenCommit" | "Submit" | "Abort" | false | fun() +--- +---@alias NeogitConfigMappingsCommitEditor "Close" | "Submit" | "Abort" | false | fun() ---@class NeogitConfigMappings Consult the config file or documentation for values ---@field finder? { [string]: NeogitConfigMappingsFinder } A dictionary that uses finder commands to set multiple keybinds ---@field status? { [string]: NeogitConfigMappingsStatus } A dictionary that uses status commands to set a single keybind ---@field popup? { [string]: NeogitConfigMappingsPopup } A dictionary that uses popup commands to set a single keybind ---@field rebase_editor? { [string]: NeogitConfigMappingsRebaseEditor } A dictionary that uses Rebase editor commands to set a single keybind +---@field commit_editor? { [string]: NeogitConfigMappingsCommitEditor } A dictionary that uses Commit editor commands to set a single keybind ---@alias NeogitGraphStyle "ascii" | "unicode" @@ -123,7 +138,6 @@ end ---@field disable_context_highlighting? boolean Disable context highlights based on cursor position ---@field disable_signs? boolean Special signs to draw for sections etc. in Neogit ---@field git_services? table Templartes to use when opening a pull request for a branch ----@field disable_commit_confirmation? boolean Disable commit confirmations ---@field fetch_after_checkout? boolean Perform a fetch if the newly checked out branch has an upstream or pushRemote set ---@field telescope_sorter? function The sorter telescope will use ---@field disable_insert_on_commit? boolean|"auto" Disable automatically entering insert mode in commit dialogues @@ -163,7 +177,6 @@ function M.get_default_values() disable_hint = false, disable_context_highlighting = false, disable_signs = false, - disable_commit_confirmation = false, graph_style = "ascii", filewatcher = { interval = 1000, @@ -177,7 +190,7 @@ function M.get_default_values() ["bitbucket.org"] = "https://bitbucket.org/${owner}/${repository}/pull-requests/new?source=${branch_name}&t=1", ["gitlab.com"] = "https://gitlab.com/${owner}/${repository}/merge_requests/new?merge_request[source_branch]=${branch_name}", }, - disable_insert_on_commit = true, + disable_insert_on_commit = "auto", use_per_project_settings = true, remember_settings = true, fetch_after_checkout = false, @@ -290,6 +303,11 @@ function M.get_default_values() "NeogitCommitPopup--allow-empty", }, mappings = { + commit_editor = { + ["q"] = "Close", + [""] = "Submit", + [""] = "Abort", + }, rebase_editor = { ["p"] = "Pick", ["r"] = "Reword", @@ -673,7 +691,7 @@ function M.validate_config() and validate_type( command, string.format("mappings.rebase_editor['%s']", key), - { "string", "boolean" } + { "string", "boolean", "function" } ) then if type(command) == "string" and not vim.tbl_contains(valid_rebase_editor_commands, command) then @@ -693,13 +711,48 @@ function M.validate_config() end end end + + local valid_commit_editor_commands = { + false, + } + + for _, cmd in pairs(M.get_default_values().mappings.commit_editor) do + table.insert(valid_commit_editor_commands, cmd) + end + + if validate_type(config.mappings.commit_editor, "mappings.commit_editor", "table") then + for key, command in pairs(config.mappings.commit_editor) do + if + validate_type(key, "mappings.commit_editor -> " .. vim.inspect(key), "string") + and validate_type( + command, + string.format("mappings.commit_editor['%s']", key), + { "string", "boolean", "function" } + ) + then + if type(command) == "string" and not vim.tbl_contains(valid_commit_editor_commands, command) then + local valid_commit_editor_commands = util.map(valid_commit_editor_commands, function(command) + return vim.inspect(command) + end) + + err( + string.format("mappings.commit_editor['%s']", key), + string.format( + "Expected a valid commit_editor command, got '%s'. Valid commit_editor commands: { %s }", + command, + table.concat(valid_commit_editor_commands, ", ") + ) + ) + end + end + end + end end if validate_type(config, "base config", "table") then validate_type(config.disable_hint, "disable_hint", "boolean") validate_type(config.disable_context_highlighting, "disable_context_highlighting", "boolean") validate_type(config.disable_signs, "disable_signs", "boolean") - validate_type(config.disable_commit_confirmation, "disable_commit_confirmation", "boolean") validate_type(config.telescope_sorter, "telescope_sorter", "function") validate_type(config.use_per_project_settings, "use_per_project_settings", "boolean") validate_type(config.remember_settings, "remember_settings", "boolean")