Skip to content

Commit 09f0514

Browse files
committed
feat: add and handle diff_opts.on_unsaved_changes option
1 parent 985b4b1 commit 09f0514

File tree

3 files changed

+55
-6
lines changed

3 files changed

+55
-6
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ For deep technical details, see [ARCHITECTURE.md](./ARCHITECTURE.md).
273273
vertical_split = true,
274274
open_in_current_tab = true,
275275
keep_terminal_focus = false, -- If true, moves focus back to terminal after diff opens
276+
on_unsaved_changes = "error"
276277
},
277278
},
278279
keys = {

lua/claudecode/config.lua

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ M.defaults = {
2424
vertical_split = true,
2525
open_in_current_tab = true, -- Use current tab instead of creating new tab
2626
keep_terminal_focus = false, -- If true, moves focus back to terminal after diff opens
27+
on_unsaved_changes = "error", -- "error", "discard" - How to handle unsaved changes when creating diffs
2728
},
2829
models = {
2930
{ name = "Claude Opus 4.1 (Latest)", value = "opus" },
@@ -110,6 +111,16 @@ function M.validate(config)
110111
assert(type(config.diff_opts.open_in_current_tab) == "boolean", "diff_opts.open_in_current_tab must be a boolean")
111112
assert(type(config.diff_opts.keep_terminal_focus) == "boolean", "diff_opts.keep_terminal_focus must be a boolean")
112113

114+
local valid_behaviors = { "error", "discard" }
115+
local is_valid_behavior = false
116+
for _, behavior in ipairs(valid_behaviors) do
117+
if config.diff_opts.on_unsaved_changes == behavior then
118+
is_valid_behavior = true
119+
break
120+
end
121+
end
122+
assert(is_valid_behavior, "diff_opts.on_unsaved_changes must be one of: " .. table.concat(valid_behaviors, ", "))
123+
113124
-- Validate env
114125
assert(type(config.env) == "table", "env must be a table")
115126
for key, value in pairs(config.env) do

lua/claudecode/diff.lua

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,29 @@ local function is_buffer_dirty(file_path)
112112
return is_dirty, nil
113113
end
114114

115+
---Discard unsaved changes in a buffer by reloading from disk.
116+
---@param file_path string The file path whose buffer changes should be discarded
117+
---@return boolean success True if changes were discarded successfully
118+
---@return string? error Error message if discard failed
119+
local function discard_buffer_changes(file_path)
120+
local bufnr = vim.fn.bufnr(file_path)
121+
if bufnr == -1 then
122+
return false, "Buffer for " .. file_path .. " is not available"
123+
end
124+
125+
local discard_success, discard_error = pcall(function()
126+
vim.api.nvim_buf_call(bufnr, function()
127+
vim.cmd("edit!") -- Force reload from disk, discarding changes
128+
end)
129+
end)
130+
131+
if not discard_success then
132+
return false, "Discard error: " .. tostring(discard_error)
133+
end
134+
135+
return true, nil
136+
end
137+
115138
---Setup the diff module
116139
---@param user_config ClaudeCodeConfig? The configuration passed from init.lua
117140
function M.setup(user_config)
@@ -712,15 +735,29 @@ function M._setup_blocking_diff(params, resolution_callback)
712735
local old_file_exists = vim.fn.filereadable(params.old_file_path) == 1
713736
local is_new_file = not old_file_exists
714737

715-
-- Step 1.5: Check if the file buffer has unsaved changes
738+
-- Step 1.5: Handle unsaved changes based on configuration
716739
if old_file_exists then
717740
local is_dirty = is_buffer_dirty(params.old_file_path)
718741
if is_dirty then
719-
error({
720-
code = -32000,
721-
message = "Cannot create diff: file has unsaved changes",
722-
data = "Please save (:w) or discard (:e!) changes to " .. params.old_file_path .. " before creating diff",
723-
})
742+
local behavior = config and config.diff_opts and config.diff_opts.on_unsaved_changes or "error"
743+
744+
if behavior == "error" then
745+
error({
746+
code = -32000,
747+
message = "Cannot create diff: file has unsaved changes",
748+
data = "Please save (:w) or discard (:e!) changes to " .. params.old_file_path .. " before creating diff",
749+
})
750+
elseif behavior == "discard" then
751+
-- Discard unsaved changes using the extracted function
752+
local discard_success, discard_err = discard_buffer_changes(params.old_file_path)
753+
if not discard_success then
754+
error({
755+
code = -32000,
756+
message = "Failed to discard unsaved changes before creating diff",
757+
data = discard_err,
758+
})
759+
end
760+
end
724761
end
725762
end
726763

0 commit comments

Comments
 (0)