2
2
-- Manages different diff providers (native Neovim and diffview.nvim) with automatic detection and fallback.
3
3
local M = {}
4
4
5
- -- Internal state with safe defaults
6
5
local diff_config = {
7
6
diff_provider = " auto" ,
8
7
diff_opts = {
@@ -29,7 +28,7 @@ function M.get_current_provider()
29
28
if M .is_diffview_available () then
30
29
return " diffview"
31
30
else
32
- vim .notify (" diffview.nvim not found, falling back to native diff" , vim .log .levels .WARN )
31
+ vim .notify (" diffview.nvim not found, falling back to native diff" , vim .log .levels .WARN ) -- Explain fallback
33
32
return " native"
34
33
end
35
34
else
40
39
--- Setup the diff module with configuration
41
40
-- @param user_diff_config table|nil User configuration for diff functionality
42
41
function M .setup (user_diff_config )
43
- -- Simple setup without config module dependency for now
44
42
if user_diff_config then
45
43
diff_config = {
46
44
diff_provider = user_diff_config .diff_provider or diff_config .diff_provider ,
70
68
-- @param filename string Base filename for the temporary file
71
69
-- @return string|nil, string|nil The temporary file path and error message
72
70
function M ._create_temp_file (content , filename )
73
- local tmp_dir = vim .fn .tempname () .. " _claudecode_diff"
74
- local ok , err = pcall (vim .fn .mkdir , tmp_dir , " p" )
75
- if not ok then
76
- return nil , " Failed to create temporary directory: " .. tostring (err )
71
+ local base_dir_cache = vim .fn .stdpath (" cache" ) .. " /claudecode_diffs"
72
+ local mkdir_ok_cache , mkdir_err_cache = pcall (vim .fn .mkdir , base_dir_cache , " p" )
73
+
74
+ local final_base_dir
75
+ if mkdir_ok_cache then
76
+ final_base_dir = base_dir_cache
77
+ else
78
+ local base_dir_temp = vim .fn .stdpath (" temp" ) .. " /claudecode_diffs"
79
+ local mkdir_ok_temp , mkdir_err_temp = pcall (vim .fn .mkdir , base_dir_temp , " p" )
80
+ if not mkdir_ok_temp then
81
+ local err_to_report = mkdir_err_temp or mkdir_err_cache or " unknown error creating base temp dir"
82
+ return nil , " Failed to create base temporary directory: " .. tostring (err_to_report )
83
+ end
84
+ final_base_dir = base_dir_temp
85
+ end
86
+
87
+ local session_id_base = vim .fn .fnamemodify (vim .fn .tempname (), " :t" ) .. " _" .. tostring (os.time ()) .. " _" .. tostring (math.random (1000 , 9999 ))
88
+ local session_id = session_id_base :gsub (" [^A-Za-z0-9_-]" , " " )
89
+ if session_id == " " then -- Fallback if all characters were problematic, ensuring a directory can be made.
90
+ session_id = " claudecode_session"
77
91
end
78
92
79
- local tmp_file = tmp_dir .. " /" .. filename
93
+ local tmp_session_dir = final_base_dir .. " /" .. session_id
94
+ local mkdir_session_ok , mkdir_session_err = pcall (vim .fn .mkdir , tmp_session_dir , " p" )
95
+ if not mkdir_session_ok then
96
+ return nil , " Failed to create temporary session directory: " .. tostring (mkdir_session_err )
97
+ end
98
+
99
+ local tmp_file = tmp_session_dir .. " /" .. filename
80
100
local file = io.open (tmp_file , " w" )
81
101
if not file then
82
102
return nil , " Failed to create temporary file: " .. tmp_file
@@ -105,82 +125,40 @@ end
105
125
-- @param tab_name string Name for the diff tab/view
106
126
-- @return table Result with provider, tab_name, and success status
107
127
function M ._open_native_diff (old_file_path , new_file_path , new_file_contents , tab_name )
108
- -- Create temporary file for new content
109
128
local new_filename = vim .fn .fnamemodify (new_file_path , " :t" ) .. " .new"
110
129
local tmp_file , err = M ._create_temp_file (new_file_contents , new_filename )
111
130
if not tmp_file then
112
131
return { provider = " native" , tab_name = tab_name , success = false , error = err }
113
132
end
114
133
115
- -- Choose whether to open in current tab or new tab based on configuration
116
134
if diff_config and diff_config .diff_opts and diff_config .diff_opts .open_in_current_tab then
117
- -- Save current buffer info for restoration later if needed
118
135
local original_buf = vim .api .nvim_get_current_buf ()
119
- local _ = vim .api .nvim_buf_get_name (original_buf ) -- unused for now but may be needed later
120
-
121
- -- Open the original file in the current buffer
136
+ local _ = vim .api .nvim_buf_get_name (original_buf ) -- Storing original buffer name, though not currently used, might be useful for future enhancements.
122
137
vim .cmd (" edit " .. vim .fn .fnameescape (old_file_path ))
123
138
else
124
- -- Create a new tab for the diff (old behavior)
125
139
vim .cmd (" tabnew" )
126
-
127
- -- Set the tab name
128
140
vim .api .nvim_buf_set_name (0 , tab_name )
129
-
130
- -- Open the original file
131
141
vim .cmd (" edit " .. vim .fn .fnameescape (old_file_path ))
132
142
end
133
143
134
- -- Enable diff mode
135
144
vim .cmd (" diffthis" )
136
145
137
- -- Create split based on configuration
138
146
if diff_config and diff_config .diff_opts and diff_config .diff_opts .vertical_split then
139
147
vim .cmd (" vertical split" )
140
148
else
141
149
vim .cmd (" split" )
142
150
end
143
151
144
- -- Open the temporary file with new content
145
152
vim .cmd (" edit " .. vim .fn .fnameescape (tmp_file ))
146
153
vim .api .nvim_buf_set_name (0 , new_file_path .. " (New)" )
147
154
148
- -- Configure the new content buffer
149
155
local new_buf = vim .api .nvim_get_current_buf ()
150
156
vim .api .nvim_set_option_value (" buftype" , " nofile" , { buf = new_buf })
151
157
vim .api .nvim_set_option_value (" bufhidden" , " wipe" , { buf = new_buf })
152
158
vim .api .nvim_set_option_value (" swapfile" , false , { buf = new_buf })
153
159
154
- -- Enable diff mode
155
160
vim .cmd (" diffthis" )
156
161
157
- -- Set up buffer-local keymaps for diff navigation and exit (only in current tab mode)
158
- if diff_config and diff_config .diff_opts and diff_config .diff_opts .open_in_current_tab then
159
- local function setup_diff_keymaps ()
160
- -- Map <leader>dq to quit diff mode
161
- vim .keymap .set (" n" , " <leader>dq" , function ()
162
- vim .cmd (" diffoff!" )
163
- vim .cmd (" wincmd o" ) -- Close other windows (the diff split)
164
- M ._cleanup_temp_file (tmp_file )
165
- vim .notify (" Diff mode exited" , vim .log .levels .INFO )
166
- end , { buffer = true , desc = " Exit diff mode" })
167
-
168
- -- Map <leader>da to accept all changes (replace current buffer with new content)
169
- vim .keymap .set (" n" , " <leader>da" , function ()
170
- vim .cmd (" diffoff!" )
171
- vim .cmd (" wincmd o" ) -- Close other windows
172
- -- Load the new content into the current buffer
173
- local lines = vim .split (new_file_contents , " \n " )
174
- vim .api .nvim_buf_set_lines (0 , 0 , - 1 , false , lines )
175
- M ._cleanup_temp_file (tmp_file )
176
- vim .notify (" All changes accepted" , vim .log .levels .INFO )
177
- end , { buffer = true , desc = " Accept all changes" })
178
- end
179
-
180
- setup_diff_keymaps ()
181
- end
182
-
183
- -- Set up autocmd for cleanup when buffers are deleted
184
162
local cleanup_group = vim .api .nvim_create_augroup (" ClaudeCodeDiffCleanup" , { clear = false })
185
163
vim .api .nvim_create_autocmd ({ " BufDelete" , " BufWipeout" }, {
186
164
group = cleanup_group ,
@@ -191,18 +169,6 @@ function M._open_native_diff(old_file_path, new_file_path, new_file_contents, ta
191
169
once = true ,
192
170
})
193
171
194
- -- Show diff info with helpful keymaps
195
- vim .defer_fn (function ()
196
- local message
197
- if diff_config and diff_config .diff_opts and diff_config .diff_opts .open_in_current_tab then
198
- message =
199
- string.format (" Diff: %s | Use ]c/[c to navigate, <leader>da to accept all, <leader>dq to exit" , tab_name )
200
- else
201
- message = string.format (" Diff: %s | Use ]c/[c to navigate changes, close tab when done" , tab_name )
202
- end
203
- vim .notify (message , vim .log .levels .INFO )
204
- end , 100 )
205
-
206
172
return {
207
173
provider = " native" ,
208
174
tab_name = tab_name ,
218
184
-- @param tab_name string Name for the diff tab/view
219
185
-- @return table Result with provider, tab_name, and success status
220
186
function M ._open_diffview_diff (old_file_path , new_file_path , new_file_contents , tab_name )
221
- -- For now, fall back to native implementation
222
- -- This will be properly implemented in Phase 4
187
+ -- TODO: Implement full diffview.nvim integration (Phase 4)
188
+ -- For now, fall back to native implementation. This notification informs the user about the current behavior.
223
189
vim .notify (" diffview.nvim integration not yet implemented, using native diff" , vim .log .levels .INFO )
224
190
return M ._open_native_diff (old_file_path , new_file_path , new_file_contents , tab_name )
225
191
end
0 commit comments