claude-baseline-1752171490
This commit is contained in:
parent
ac6f7354b6
commit
91c24b34a6
|
@ -98,3 +98,6 @@ SEPARATED HUNK #4: Final test with better hunk separation!
|
||||||
|
|
||||||
MULTI-EDIT TEST #4: Testing hunk navigation with ]h and [h!
|
MULTI-EDIT TEST #4: Testing hunk navigation with ]h and [h!
|
||||||
|
|
||||||
|
## PERSISTENCE TEST
|
||||||
|
This tests our new stash-based persistence system that doesn't create git commits!
|
||||||
|
|
||||||
|
|
|
@ -30,17 +30,13 @@ function M.handle_claude_edit(stash_ref, pre_edit_ref)
|
||||||
-- Initialize review session
|
-- Initialize review session
|
||||||
M.current_review = {
|
M.current_review = {
|
||||||
stash_ref = stash_ref,
|
stash_ref = stash_ref,
|
||||||
pre_edit_ref = pre_edit_ref, -- Store the pre-edit commit reference
|
pre_edit_ref = pre_edit_ref, -- Store the pre-edit commit reference
|
||||||
timestamp = os.time(),
|
timestamp = os.time(),
|
||||||
changed_files = changed_files,
|
changed_files = changed_files,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Notify user about changes
|
-- Notify user about changes
|
||||||
vim.notify(string.format(
|
vim.notify(string.format('Claude made changes to %d file(s): %s', #changed_files, table.concat(changed_files, ', ')), vim.log.levels.INFO)
|
||||||
'Claude made changes to %d file(s): %s',
|
|
||||||
#changed_files,
|
|
||||||
table.concat(changed_files, ', ')
|
|
||||||
), vim.log.levels.INFO)
|
|
||||||
|
|
||||||
vim.notify('Use <leader>dd to open diffview, <leader>df for fugitive, <leader>dc to clear review', vim.log.levels.INFO)
|
vim.notify('Use <leader>dd to open diffview, <leader>df for fugitive, <leader>dc to clear review', vim.log.levels.INFO)
|
||||||
|
|
||||||
|
@ -70,14 +66,11 @@ function M.handle_claude_stashes(baseline_ref)
|
||||||
timestamp = os.time(),
|
timestamp = os.time(),
|
||||||
claude_stashes = claude_stashes,
|
claude_stashes = claude_stashes,
|
||||||
current_stash_index = 0, -- Show cumulative view by default
|
current_stash_index = 0, -- Show cumulative view by default
|
||||||
is_stash_based = true
|
is_stash_based = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Notify user about changes
|
-- Notify user about changes
|
||||||
vim.notify(string.format(
|
vim.notify(string.format('Found %d Claude stash(es). Use <leader>dd for cumulative view, <leader>dh to browse.', #claude_stashes), vim.log.levels.INFO)
|
||||||
'Found %d Claude stash(es). Use <leader>dd for cumulative view, <leader>dh to browse.',
|
|
||||||
#claude_stashes
|
|
||||||
), vim.log.levels.INFO)
|
|
||||||
|
|
||||||
-- Automatically open cumulative stash view
|
-- Automatically open cumulative stash view
|
||||||
M.open_cumulative_stash_view()
|
M.open_cumulative_stash_view()
|
||||||
|
@ -91,7 +84,7 @@ end
|
||||||
|
|
||||||
-- Get list of files changed in the stash
|
-- Get list of files changed in the stash
|
||||||
function M.get_changed_files(stash_ref)
|
function M.get_changed_files(stash_ref)
|
||||||
local utils = require('nvim-claude.utils')
|
local utils = require 'nvim-claude.utils'
|
||||||
local cmd = string.format('git stash show %s --name-only', stash_ref)
|
local cmd = string.format('git stash show %s --name-only', stash_ref)
|
||||||
local result = utils.exec(cmd)
|
local result = utils.exec(cmd)
|
||||||
|
|
||||||
|
@ -100,7 +93,7 @@ function M.get_changed_files(stash_ref)
|
||||||
end
|
end
|
||||||
|
|
||||||
local files = {}
|
local files = {}
|
||||||
for line in result:gmatch('[^\n]+') do
|
for line in result:gmatch '[^\n]+' do
|
||||||
if line ~= '' then
|
if line ~= '' then
|
||||||
table.insert(files, line)
|
table.insert(files, line)
|
||||||
end
|
end
|
||||||
|
@ -110,7 +103,7 @@ end
|
||||||
|
|
||||||
-- Get list of files changed since baseline
|
-- Get list of files changed since baseline
|
||||||
function M.get_changed_files_since_baseline(baseline_ref)
|
function M.get_changed_files_since_baseline(baseline_ref)
|
||||||
local utils = require('nvim-claude.utils')
|
local utils = require 'nvim-claude.utils'
|
||||||
local cmd = string.format('git diff --name-only %s', baseline_ref)
|
local cmd = string.format('git diff --name-only %s', baseline_ref)
|
||||||
local result = utils.exec(cmd)
|
local result = utils.exec(cmd)
|
||||||
|
|
||||||
|
@ -119,7 +112,7 @@ function M.get_changed_files_since_baseline(baseline_ref)
|
||||||
end
|
end
|
||||||
|
|
||||||
local files = {}
|
local files = {}
|
||||||
for line in result:gmatch('[^\n]+') do
|
for line in result:gmatch '[^\n]+' do
|
||||||
if line ~= '' then
|
if line ~= '' then
|
||||||
table.insert(files, line)
|
table.insert(files, line)
|
||||||
end
|
end
|
||||||
|
@ -129,7 +122,7 @@ end
|
||||||
|
|
||||||
-- Get Claude stashes (only stashes with [claude-edit] messages)
|
-- Get Claude stashes (only stashes with [claude-edit] messages)
|
||||||
function M.get_claude_stashes()
|
function M.get_claude_stashes()
|
||||||
local utils = require('nvim-claude.utils')
|
local utils = require 'nvim-claude.utils'
|
||||||
local cmd = 'git stash list'
|
local cmd = 'git stash list'
|
||||||
local result = utils.exec(cmd)
|
local result = utils.exec(cmd)
|
||||||
|
|
||||||
|
@ -138,13 +131,13 @@ function M.get_claude_stashes()
|
||||||
end
|
end
|
||||||
|
|
||||||
local stashes = {}
|
local stashes = {}
|
||||||
for line in result:gmatch('[^\n]+') do
|
for line in result:gmatch '[^\n]+' do
|
||||||
if line ~= '' and line:match('%[claude%-edit%]') then
|
if line ~= '' and line:match '%[claude%-edit%]' then
|
||||||
local stash_ref = line:match('^(stash@{%d+})')
|
local stash_ref = line:match '^(stash@{%d+})'
|
||||||
if stash_ref then
|
if stash_ref then
|
||||||
table.insert(stashes, {
|
table.insert(stashes, {
|
||||||
ref = stash_ref,
|
ref = stash_ref,
|
||||||
message = line:match(': (.+)$') or line
|
message = line:match ': (.+)$' or line,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -179,8 +172,8 @@ end
|
||||||
function M.open_diffview()
|
function M.open_diffview()
|
||||||
if not M.current_review then
|
if not M.current_review then
|
||||||
-- Try to recover stash-based session from baseline
|
-- Try to recover stash-based session from baseline
|
||||||
local utils = require('nvim-claude.utils')
|
local utils = require 'nvim-claude.utils'
|
||||||
local baseline_ref = utils.read_file('/tmp/claude-baseline-commit')
|
local baseline_ref = utils.read_file '/tmp/claude-baseline-commit'
|
||||||
|
|
||||||
-- If no baseline file, but we have Claude stashes, use HEAD as baseline
|
-- If no baseline file, but we have Claude stashes, use HEAD as baseline
|
||||||
local claude_stashes = M.get_claude_stashes()
|
local claude_stashes = M.get_claude_stashes()
|
||||||
|
@ -197,7 +190,7 @@ function M.open_diffview()
|
||||||
timestamp = os.time(),
|
timestamp = os.time(),
|
||||||
claude_stashes = claude_stashes,
|
claude_stashes = claude_stashes,
|
||||||
current_stash_index = 0, -- Show cumulative view by default
|
current_stash_index = 0, -- Show cumulative view by default
|
||||||
is_stash_based = true
|
is_stash_based = true,
|
||||||
}
|
}
|
||||||
vim.notify(string.format('Recovered Claude stash session with %d stashes', #claude_stashes), vim.log.levels.INFO)
|
vim.notify(string.format('Recovered Claude stash session with %d stashes', #claude_stashes), vim.log.levels.INFO)
|
||||||
end
|
end
|
||||||
|
@ -314,26 +307,28 @@ end
|
||||||
|
|
||||||
-- Telescope picker for changed files
|
-- Telescope picker for changed files
|
||||||
function M.telescope_changed_files()
|
function M.telescope_changed_files()
|
||||||
local pickers = require('telescope.pickers')
|
local pickers = require 'telescope.pickers'
|
||||||
local finders = require('telescope.finders')
|
local finders = require 'telescope.finders'
|
||||||
local conf = require('telescope.config').values
|
local conf = require('telescope.config').values
|
||||||
|
|
||||||
pickers.new({}, {
|
pickers
|
||||||
prompt_title = 'Claude Changed Files',
|
.new({}, {
|
||||||
finder = finders.new_table({
|
prompt_title = 'Claude Changed Files',
|
||||||
results = M.current_review.changed_files,
|
finder = finders.new_table {
|
||||||
}),
|
results = M.current_review.changed_files,
|
||||||
sorter = conf.generic_sorter({}),
|
},
|
||||||
attach_mappings = function(_, map)
|
sorter = conf.generic_sorter {},
|
||||||
map('i', '<CR>', function(prompt_bufnr)
|
attach_mappings = function(_, map)
|
||||||
local selection = require('telescope.actions.state').get_selected_entry()
|
map('i', '<CR>', function(prompt_bufnr)
|
||||||
require('telescope.actions').close(prompt_bufnr)
|
local selection = require('telescope.actions.state').get_selected_entry()
|
||||||
vim.cmd('edit ' .. selection[1])
|
require('telescope.actions').close(prompt_bufnr)
|
||||||
M.open_diffview()
|
vim.cmd('edit ' .. selection[1])
|
||||||
end)
|
M.open_diffview()
|
||||||
return true
|
end)
|
||||||
end,
|
return true
|
||||||
}):find()
|
end,
|
||||||
|
})
|
||||||
|
:find()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Clear review session
|
-- Clear review session
|
||||||
|
@ -343,7 +338,7 @@ function M.clear_review()
|
||||||
|
|
||||||
-- Close diffview if it's open
|
-- Close diffview if it's open
|
||||||
pcall(function()
|
pcall(function()
|
||||||
vim.cmd('DiffviewClose')
|
vim.cmd 'DiffviewClose'
|
||||||
end)
|
end)
|
||||||
|
|
||||||
vim.notify('Claude review session cleared', vim.log.levels.INFO)
|
vim.notify('Claude review session cleared', vim.log.levels.INFO)
|
||||||
|
@ -354,7 +349,7 @@ end
|
||||||
|
|
||||||
-- Accept all Claude changes (update baseline)
|
-- Accept all Claude changes (update baseline)
|
||||||
function M.accept_changes()
|
function M.accept_changes()
|
||||||
local utils = require('nvim-claude.utils')
|
local utils = require 'nvim-claude.utils'
|
||||||
|
|
||||||
-- Get project root
|
-- Get project root
|
||||||
local git_root = utils.get_project_root()
|
local git_root = utils.get_project_root()
|
||||||
|
@ -380,7 +375,7 @@ function M.accept_changes()
|
||||||
local commit_cmd = string.format('cd "%s" && git commit -m "%s" --allow-empty', git_root, commit_msg)
|
local commit_cmd = string.format('cd "%s" && git commit -m "%s" --allow-empty', git_root, commit_msg)
|
||||||
local commit_result, commit_err = utils.exec(commit_cmd)
|
local commit_result, commit_err = utils.exec(commit_cmd)
|
||||||
|
|
||||||
if commit_err and not commit_err:match('nothing to commit') then
|
if commit_err and not commit_err:match 'nothing to commit' then
|
||||||
vim.notify('Failed to create new baseline: ' .. commit_err, vim.log.levels.ERROR)
|
vim.notify('Failed to create new baseline: ' .. commit_err, vim.log.levels.ERROR)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -394,7 +389,7 @@ function M.accept_changes()
|
||||||
|
|
||||||
-- Close diffview
|
-- Close diffview
|
||||||
pcall(function()
|
pcall(function()
|
||||||
vim.cmd('DiffviewClose')
|
vim.cmd 'DiffviewClose'
|
||||||
end)
|
end)
|
||||||
|
|
||||||
vim.notify('All Claude changes accepted! New baseline created.', vim.log.levels.INFO)
|
vim.notify('All Claude changes accepted! New baseline created.', vim.log.levels.INFO)
|
||||||
|
@ -402,7 +397,7 @@ end
|
||||||
|
|
||||||
-- Decline all Claude changes (reset to baseline)
|
-- Decline all Claude changes (reset to baseline)
|
||||||
function M.decline_changes()
|
function M.decline_changes()
|
||||||
local utils = require('nvim-claude.utils')
|
local utils = require 'nvim-claude.utils'
|
||||||
|
|
||||||
-- Get baseline commit
|
-- Get baseline commit
|
||||||
local baseline_file = '/tmp/claude-baseline-commit'
|
local baseline_file = '/tmp/claude-baseline-commit'
|
||||||
|
@ -436,11 +431,11 @@ function M.decline_changes()
|
||||||
|
|
||||||
-- Close diffview
|
-- Close diffview
|
||||||
pcall(function()
|
pcall(function()
|
||||||
vim.cmd('DiffviewClose')
|
vim.cmd 'DiffviewClose'
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- Refresh buffers
|
-- Refresh buffers
|
||||||
vim.cmd('checktime')
|
vim.cmd 'checktime'
|
||||||
|
|
||||||
vim.notify('All Claude changes declined! Reset to baseline.', vim.log.levels.INFO)
|
vim.notify('All Claude changes declined! Reset to baseline.', vim.log.levels.INFO)
|
||||||
end
|
end
|
||||||
|
@ -547,8 +542,8 @@ end
|
||||||
|
|
||||||
-- Telescope picker for Claude stashes
|
-- Telescope picker for Claude stashes
|
||||||
function M.telescope_claude_stashes()
|
function M.telescope_claude_stashes()
|
||||||
local pickers = require('telescope.pickers')
|
local pickers = require 'telescope.pickers'
|
||||||
local finders = require('telescope.finders')
|
local finders = require 'telescope.finders'
|
||||||
local conf = require('telescope.config').values
|
local conf = require('telescope.config').values
|
||||||
|
|
||||||
local stashes = M.current_review.claude_stashes
|
local stashes = M.current_review.claude_stashes
|
||||||
|
@ -562,29 +557,31 @@ function M.telescope_claude_stashes()
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
pickers.new({}, {
|
pickers
|
||||||
prompt_title = 'Claude Stash History',
|
.new({}, {
|
||||||
finder = finders.new_table({
|
prompt_title = 'Claude Stash History',
|
||||||
results = stash_entries,
|
finder = finders.new_table {
|
||||||
entry_maker = function(entry)
|
results = stash_entries,
|
||||||
return {
|
entry_maker = function(entry)
|
||||||
value = entry.value,
|
return {
|
||||||
display = entry.display,
|
value = entry.value,
|
||||||
ordinal = entry.ordinal,
|
display = entry.display,
|
||||||
}
|
ordinal = entry.ordinal,
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
sorter = conf.generic_sorter {},
|
||||||
|
attach_mappings = function(_, map)
|
||||||
|
map('i', '<CR>', function(prompt_bufnr)
|
||||||
|
local selection = require('telescope.actions.state').get_selected_entry()
|
||||||
|
require('telescope.actions').close(prompt_bufnr)
|
||||||
|
M.current_review.current_stash_index = selection.value
|
||||||
|
M.view_specific_stash(selection.value)
|
||||||
|
end)
|
||||||
|
return true
|
||||||
end,
|
end,
|
||||||
}),
|
})
|
||||||
sorter = conf.generic_sorter({}),
|
:find()
|
||||||
attach_mappings = function(_, map)
|
|
||||||
map('i', '<CR>', function(prompt_bufnr)
|
|
||||||
local selection = require('telescope.actions.state').get_selected_entry()
|
|
||||||
require('telescope.actions').close(prompt_bufnr)
|
|
||||||
M.current_review.current_stash_index = selection.value
|
|
||||||
M.view_specific_stash(selection.value)
|
|
||||||
end)
|
|
||||||
return true
|
|
||||||
end,
|
|
||||||
}):find()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Generate combined patch from all Claude stashes
|
-- Generate combined patch from all Claude stashes
|
||||||
|
@ -594,7 +591,7 @@ function M.generate_claude_patch()
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local utils = require('nvim-claude.utils')
|
local utils = require 'nvim-claude.utils'
|
||||||
local baseline_ref = M.current_review.baseline_ref
|
local baseline_ref = M.current_review.baseline_ref
|
||||||
|
|
||||||
-- Generate diff from baseline to current working directory
|
-- Generate diff from baseline to current working directory
|
||||||
|
@ -613,8 +610,8 @@ end
|
||||||
function M.open_unified_view()
|
function M.open_unified_view()
|
||||||
if not M.current_review then
|
if not M.current_review then
|
||||||
-- Try to recover stash-based session from baseline
|
-- Try to recover stash-based session from baseline
|
||||||
local utils = require('nvim-claude.utils')
|
local utils = require 'nvim-claude.utils'
|
||||||
local baseline_ref = utils.read_file('/tmp/claude-baseline-commit')
|
local baseline_ref = utils.read_file '/tmp/claude-baseline-commit'
|
||||||
|
|
||||||
-- If no baseline file, but we have Claude stashes, use HEAD as baseline
|
-- If no baseline file, but we have Claude stashes, use HEAD as baseline
|
||||||
local claude_stashes = M.get_claude_stashes()
|
local claude_stashes = M.get_claude_stashes()
|
||||||
|
@ -631,7 +628,7 @@ function M.open_unified_view()
|
||||||
timestamp = os.time(),
|
timestamp = os.time(),
|
||||||
claude_stashes = claude_stashes,
|
claude_stashes = claude_stashes,
|
||||||
current_stash_index = 0,
|
current_stash_index = 0,
|
||||||
is_stash_based = true
|
is_stash_based = true,
|
||||||
}
|
}
|
||||||
vim.notify(string.format('Recovered Claude stash session with %d stashes', #claude_stashes), vim.log.levels.INFO)
|
vim.notify(string.format('Recovered Claude stash session with %d stashes', #claude_stashes), vim.log.levels.INFO)
|
||||||
end
|
end
|
||||||
|
@ -670,7 +667,6 @@ function M.open_unified_view()
|
||||||
vim.notify('Claude unified diff opened. Use ]h/[h to navigate hunks', vim.log.levels.INFO)
|
vim.notify('Claude unified diff opened. Use ]h/[h to navigate hunks', vim.log.levels.INFO)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Accept hunk at cursor position
|
-- Accept hunk at cursor position
|
||||||
function M.accept_hunk_at_cursor()
|
function M.accept_hunk_at_cursor()
|
||||||
-- Get current buffer and check if we're in a diff view
|
-- Get current buffer and check if we're in a diff view
|
||||||
|
@ -678,10 +674,7 @@ function M.accept_hunk_at_cursor()
|
||||||
local filetype = vim.bo.filetype
|
local filetype = vim.bo.filetype
|
||||||
|
|
||||||
-- Check for various diff view types
|
-- Check for various diff view types
|
||||||
local is_diff_view = bufname:match('diffview://') or
|
local is_diff_view = bufname:match 'diffview://' or bufname:match 'Claude Unified Diff' or filetype == 'diff' or filetype == 'git'
|
||||||
bufname:match('Claude Unified Diff') or
|
|
||||||
filetype == 'diff' or
|
|
||||||
filetype == 'git'
|
|
||||||
|
|
||||||
if not is_diff_view then
|
if not is_diff_view then
|
||||||
vim.notify('This command only works in diff views', vim.log.levels.WARN)
|
vim.notify('This command only works in diff views', vim.log.levels.WARN)
|
||||||
|
@ -710,10 +703,7 @@ function M.reject_hunk_at_cursor()
|
||||||
local filetype = vim.bo.filetype
|
local filetype = vim.bo.filetype
|
||||||
|
|
||||||
-- Check for various diff view types
|
-- Check for various diff view types
|
||||||
local is_diff_view = bufname:match('diffview://') or
|
local is_diff_view = bufname:match 'diffview://' or bufname:match 'Claude Unified Diff' or filetype == 'diff' or filetype == 'git'
|
||||||
bufname:match('Claude Unified Diff') or
|
|
||||||
filetype == 'diff' or
|
|
||||||
filetype == 'git'
|
|
||||||
|
|
||||||
if not is_diff_view then
|
if not is_diff_view then
|
||||||
vim.notify('This command only works in diff views', vim.log.levels.WARN)
|
vim.notify('This command only works in diff views', vim.log.levels.WARN)
|
||||||
|
@ -731,7 +721,10 @@ function M.reject_hunk_at_cursor()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
vim.notify(string.format('Rejected hunk in %s at lines %d-%d', hunk_info.file, hunk_info.old_start, hunk_info.old_start + hunk_info.old_count - 1), vim.log.levels.INFO)
|
vim.notify(
|
||||||
|
string.format('Rejected hunk in %s at lines %d-%d', hunk_info.file, hunk_info.old_start, hunk_info.old_start + hunk_info.old_count - 1),
|
||||||
|
vim.log.levels.INFO
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Find hunk information at given line in diff buffer
|
-- Find hunk information at given line in diff buffer
|
||||||
|
@ -743,14 +736,14 @@ function M.find_hunk_at_line(lines, target_line)
|
||||||
|
|
||||||
for i, line in ipairs(lines) do
|
for i, line in ipairs(lines) do
|
||||||
-- File header
|
-- File header
|
||||||
if line:match('^diff %-%-git') or line:match('^diff %-%-cc') then
|
if line:match '^diff %-%-git' or line:match '^diff %-%-cc' then
|
||||||
current_file = line:match('b/(.+)$')
|
current_file = line:match 'b/(.+)$'
|
||||||
elseif line:match('^%+%+%+ b/(.+)') then
|
elseif line:match '^%+%+%+ b/(.+)' then
|
||||||
current_file = line:match('^%+%+%+ b/(.+)')
|
current_file = line:match '^%+%+%+ b/(.+)'
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Hunk header
|
-- Hunk header
|
||||||
if line:match('^@@') then
|
if line:match '^@@' then
|
||||||
-- If we were in a hunk that included target line, return it
|
-- If we were in a hunk that included target line, return it
|
||||||
if in_hunk and hunk_start_line and target_line >= hunk_start_line and target_line < i then
|
if in_hunk and hunk_start_line and target_line >= hunk_start_line and target_line < i then
|
||||||
return M.parse_hunk_info(hunk_lines, current_file, hunk_start_line)
|
return M.parse_hunk_info(hunk_lines, current_file, hunk_start_line)
|
||||||
|
@ -759,10 +752,10 @@ function M.find_hunk_at_line(lines, target_line)
|
||||||
-- Start new hunk
|
-- Start new hunk
|
||||||
in_hunk = true
|
in_hunk = true
|
||||||
hunk_start_line = i
|
hunk_start_line = i
|
||||||
hunk_lines = {line}
|
hunk_lines = { line }
|
||||||
elseif in_hunk then
|
elseif in_hunk then
|
||||||
-- Collect hunk lines
|
-- Collect hunk lines
|
||||||
if line:match('^[%+%-%s]') then
|
if line:match '^[%+%-%s]' then
|
||||||
table.insert(hunk_lines, line)
|
table.insert(hunk_lines, line)
|
||||||
else
|
else
|
||||||
-- End of hunk
|
-- End of hunk
|
||||||
|
@ -784,12 +777,16 @@ end
|
||||||
|
|
||||||
-- Parse hunk information from diff lines
|
-- Parse hunk information from diff lines
|
||||||
function M.parse_hunk_info(hunk_lines, file, start_line)
|
function M.parse_hunk_info(hunk_lines, file, start_line)
|
||||||
if #hunk_lines == 0 then return nil end
|
if #hunk_lines == 0 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
local header = hunk_lines[1]
|
local header = hunk_lines[1]
|
||||||
local old_start, old_count, new_start, new_count = header:match('^@@ %-(%d+),?(%d*) %+(%d+),?(%d*) @@')
|
local old_start, old_count, new_start, new_count = header:match '^@@ %-(%d+),?(%d*) %+(%d+),?(%d*) @@'
|
||||||
|
|
||||||
if not old_start then return nil end
|
if not old_start then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
file = file,
|
file = file,
|
||||||
|
@ -798,20 +795,20 @@ function M.parse_hunk_info(hunk_lines, file, start_line)
|
||||||
new_start = tonumber(new_start),
|
new_start = tonumber(new_start),
|
||||||
new_count = tonumber(new_count) or 1,
|
new_count = tonumber(new_count) or 1,
|
||||||
lines = hunk_lines,
|
lines = hunk_lines,
|
||||||
buffer_start_line = start_line
|
buffer_start_line = start_line,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Apply a specific hunk to the working directory
|
-- Apply a specific hunk to the working directory
|
||||||
function M.apply_hunk(hunk_info)
|
function M.apply_hunk(hunk_info)
|
||||||
local utils = require('nvim-claude.utils')
|
local utils = require 'nvim-claude.utils'
|
||||||
|
|
||||||
-- Create a patch with just this hunk
|
-- Create a patch with just this hunk
|
||||||
local patch_lines = {
|
local patch_lines = {
|
||||||
'diff --git a/' .. hunk_info.file .. ' b/' .. hunk_info.file,
|
'diff --git a/' .. hunk_info.file .. ' b/' .. hunk_info.file,
|
||||||
'index 0000000..0000000 100644',
|
'index 0000000..0000000 100644',
|
||||||
'--- a/' .. hunk_info.file,
|
'--- a/' .. hunk_info.file,
|
||||||
'+++ b/' .. hunk_info.file
|
'+++ b/' .. hunk_info.file,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Add hunk lines
|
-- Add hunk lines
|
||||||
|
@ -849,7 +846,8 @@ function M.apply_hunk(hunk_info)
|
||||||
vim.notify(string.format('Applied hunk to %s', hunk_info.file), vim.log.levels.INFO)
|
vim.notify(string.format('Applied hunk to %s', hunk_info.file), vim.log.levels.INFO)
|
||||||
|
|
||||||
-- Refresh the buffer if it's open
|
-- Refresh the buffer if it's open
|
||||||
vim.cmd('checktime')
|
vim.cmd 'checktime'
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,7 @@ local M = {}
|
||||||
M.pre_edit_commit = nil
|
M.pre_edit_commit = nil
|
||||||
|
|
||||||
function M.setup()
|
function M.setup()
|
||||||
-- Auto-cleanup old Claude commits on startup
|
-- Setup persistence layer on startup
|
||||||
vim.defer_fn(function()
|
|
||||||
M.cleanup_old_commits()
|
|
||||||
end, 200)
|
|
||||||
|
|
||||||
-- Create baseline on startup if we're in a git repo
|
|
||||||
vim.defer_fn(function()
|
vim.defer_fn(function()
|
||||||
M.create_startup_baseline()
|
M.create_startup_baseline()
|
||||||
end, 500)
|
end, 500)
|
||||||
|
@ -27,6 +22,7 @@ end
|
||||||
function M.post_tool_use_hook()
|
function M.post_tool_use_hook()
|
||||||
-- Run directly without vim.schedule for testing
|
-- Run directly without vim.schedule for testing
|
||||||
local utils = require 'nvim-claude.utils'
|
local utils = require 'nvim-claude.utils'
|
||||||
|
local persistence = require 'nvim-claude.inline-diff-persistence'
|
||||||
|
|
||||||
-- Refresh all buffers to show Claude's changes
|
-- Refresh all buffers to show Claude's changes
|
||||||
vim.cmd 'checktime'
|
vim.cmd 'checktime'
|
||||||
|
@ -53,26 +49,15 @@ function M.post_tool_use_hook()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get the baseline commit reference first
|
-- Get the stash reference from pre-hook
|
||||||
local baseline_ref = utils.read_file '/tmp/claude-baseline-commit'
|
local stash_ref = persistence.current_stash_ref
|
||||||
if baseline_ref then
|
if not stash_ref then
|
||||||
baseline_ref = baseline_ref:gsub('%s+', '') -- trim whitespace
|
-- If no pre-hook stash, create one now
|
||||||
|
stash_ref = persistence.create_stash('nvim-claude: changes detected ' .. os.date('%Y-%m-%d %H:%M:%S'))
|
||||||
|
persistence.current_stash_ref = stash_ref
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create a stash of Claude's changes (but keep them in working directory)
|
if stash_ref then
|
||||||
local timestamp = os.date '%Y-%m-%d %H:%M:%S'
|
|
||||||
local stash_msg = string.format('[claude-edit] %s', timestamp)
|
|
||||||
|
|
||||||
-- Use git stash create to create stash without removing changes
|
|
||||||
local stash_cmd = string.format('cd "%s" && git stash create -u', git_root)
|
|
||||||
local stash_hash, stash_err = utils.exec(stash_cmd)
|
|
||||||
|
|
||||||
if not stash_err and stash_hash and stash_hash ~= '' then
|
|
||||||
-- Store the stash with a message
|
|
||||||
stash_hash = stash_hash:gsub('%s+', '') -- trim whitespace
|
|
||||||
local store_cmd = string.format('cd "%s" && git stash store -m "%s" %s', git_root, stash_msg, stash_hash)
|
|
||||||
utils.exec(store_cmd)
|
|
||||||
|
|
||||||
-- Check if any modified files are currently open in buffers
|
-- Check if any modified files are currently open in buffers
|
||||||
local inline_diff = require 'nvim-claude.inline-diff'
|
local inline_diff = require 'nvim-claude.inline-diff'
|
||||||
local opened_inline = false
|
local opened_inline = false
|
||||||
|
@ -85,9 +70,9 @@ function M.post_tool_use_hook()
|
||||||
if vim.api.nvim_buf_is_valid(buf) and vim.api.nvim_buf_is_loaded(buf) then
|
if vim.api.nvim_buf_is_valid(buf) and vim.api.nvim_buf_is_loaded(buf) then
|
||||||
local buf_name = vim.api.nvim_buf_get_name(buf)
|
local buf_name = vim.api.nvim_buf_get_name(buf)
|
||||||
if buf_name == full_path or buf_name:match('/' .. file:gsub('([^%w])', '%%%1') .. '$') then
|
if buf_name == full_path or buf_name:match('/' .. file:gsub('([^%w])', '%%%1') .. '$') then
|
||||||
-- Get the original content (from baseline)
|
-- Get the original content from stash
|
||||||
local baseline_cmd = string.format('cd "%s" && git show %s:%s 2>/dev/null', git_root, baseline_ref or 'HEAD', file)
|
local stash_cmd = string.format('cd "%s" && git show %s:%s 2>/dev/null', git_root, stash_ref, file)
|
||||||
local original_content, orig_err = utils.exec(baseline_cmd)
|
local original_content, orig_err = utils.exec(stash_cmd)
|
||||||
|
|
||||||
if not orig_err and original_content then
|
if not orig_err and original_content then
|
||||||
-- Get current content
|
-- Get current content
|
||||||
|
@ -116,14 +101,12 @@ function M.post_tool_use_hook()
|
||||||
|
|
||||||
-- If no inline diff was shown, fall back to regular diff review
|
-- If no inline diff was shown, fall back to regular diff review
|
||||||
if not opened_inline then
|
if not opened_inline then
|
||||||
-- Trigger diff review - show Claude stashes against baseline
|
-- Trigger diff review with stash reference
|
||||||
local ok, diff_review = pcall(require, 'nvim-claude.diff-review')
|
local ok, diff_review = pcall(require, 'nvim-claude.diff-review')
|
||||||
if ok then
|
if ok then
|
||||||
diff_review.handle_claude_stashes(baseline_ref)
|
diff_review.handle_claude_edit(stash_ref, nil)
|
||||||
else
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -132,6 +115,7 @@ function M.test_inline_diff()
|
||||||
vim.notify('Testing inline diff manually...', vim.log.levels.INFO)
|
vim.notify('Testing inline diff manually...', vim.log.levels.INFO)
|
||||||
|
|
||||||
local utils = require 'nvim-claude.utils'
|
local utils = require 'nvim-claude.utils'
|
||||||
|
local persistence = require 'nvim-claude.inline-diff-persistence'
|
||||||
local git_root = utils.get_project_root()
|
local git_root = utils.get_project_root()
|
||||||
|
|
||||||
if not git_root then
|
if not git_root then
|
||||||
|
@ -163,12 +147,20 @@ function M.test_inline_diff()
|
||||||
if inline_diff.original_content[bufnr] then
|
if inline_diff.original_content[bufnr] then
|
||||||
original_content = inline_diff.original_content[bufnr]
|
original_content = inline_diff.original_content[bufnr]
|
||||||
vim.notify('Using updated baseline from memory (length: ' .. #original_content .. ')', vim.log.levels.INFO)
|
vim.notify('Using updated baseline from memory (length: ' .. #original_content .. ')', vim.log.levels.INFO)
|
||||||
else
|
elseif persistence.current_stash_ref then
|
||||||
-- Fall back to git baseline
|
-- Try to get from stash
|
||||||
local baseline_ref = utils.read_file '/tmp/claude-baseline-commit' or 'HEAD'
|
local stash_cmd = string.format('cd "%s" && git show %s:%s 2>/dev/null', git_root, persistence.current_stash_ref, relative_path)
|
||||||
baseline_ref = baseline_ref:gsub('%s+', '')
|
local git_err
|
||||||
|
original_content, git_err = utils.exec(stash_cmd)
|
||||||
|
|
||||||
local baseline_cmd = string.format('cd "%s" && git show %s:%s 2>/dev/null', git_root, baseline_ref, relative_path)
|
if git_err then
|
||||||
|
vim.notify('Failed to get stash content: ' .. git_err, vim.log.levels.ERROR)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
vim.notify('Using stash baseline: ' .. persistence.current_stash_ref, vim.log.levels.INFO)
|
||||||
|
else
|
||||||
|
-- Fall back to HEAD
|
||||||
|
local baseline_cmd = string.format('cd "%s" && git show HEAD:%s 2>/dev/null', git_root, relative_path)
|
||||||
local git_err
|
local git_err
|
||||||
original_content, git_err = utils.exec(baseline_cmd)
|
original_content, git_err = utils.exec(baseline_cmd)
|
||||||
|
|
||||||
|
@ -176,7 +168,7 @@ function M.test_inline_diff()
|
||||||
vim.notify('Failed to get baseline content: ' .. git_err, vim.log.levels.ERROR)
|
vim.notify('Failed to get baseline content: ' .. git_err, vim.log.levels.ERROR)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
vim.notify('Using git baseline', vim.log.levels.INFO)
|
vim.notify('Using HEAD as baseline', vim.log.levels.INFO)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get current content
|
-- Get current content
|
||||||
|
@ -187,65 +179,33 @@ function M.test_inline_diff()
|
||||||
inline_diff.show_inline_diff(bufnr, original_content, current_content)
|
inline_diff.show_inline_diff(bufnr, original_content, current_content)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create baseline on Neovim startup
|
-- Create baseline on Neovim startup (now just sets up persistence)
|
||||||
function M.create_startup_baseline()
|
function M.create_startup_baseline()
|
||||||
local utils = require 'nvim-claude.utils'
|
local persistence = require 'nvim-claude.inline-diff-persistence'
|
||||||
|
|
||||||
-- Check if we're in a git repository
|
-- Setup persistence autocmds
|
||||||
local git_root = utils.get_project_root()
|
persistence.setup_autocmds()
|
||||||
if not git_root then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check if we already have a baseline
|
-- Try to restore any saved diffs
|
||||||
local baseline_file = '/tmp/claude-baseline-commit'
|
persistence.restore_diffs()
|
||||||
local existing_baseline = utils.read_file(baseline_file)
|
|
||||||
|
|
||||||
-- If we have a valid baseline, keep it
|
|
||||||
if existing_baseline and existing_baseline ~= '' then
|
|
||||||
existing_baseline = existing_baseline:gsub('%s+', '')
|
|
||||||
local check_cmd = string.format('cd "%s" && git rev-parse --verify %s^{commit} 2>/dev/null', git_root, existing_baseline)
|
|
||||||
local check_result, check_err = utils.exec(check_cmd)
|
|
||||||
|
|
||||||
if check_result and not check_err and check_result:match '^%x+' then
|
|
||||||
-- Baseline is valid
|
|
||||||
vim.notify('Claude baseline loaded: ' .. existing_baseline:sub(1, 7), vim.log.levels.INFO)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create new baseline of current state
|
|
||||||
local timestamp = os.time()
|
|
||||||
local commit_msg = string.format('claude-baseline-%d (startup)', timestamp)
|
|
||||||
|
|
||||||
-- Stage all current changes
|
|
||||||
local add_cmd = string.format('cd "%s" && git add -A', git_root)
|
|
||||||
utils.exec(add_cmd)
|
|
||||||
|
|
||||||
-- Create baseline commit
|
|
||||||
local commit_cmd = string.format('cd "%s" && git commit -m "%s" --allow-empty', git_root, commit_msg)
|
|
||||||
local commit_result, commit_err = utils.exec(commit_cmd)
|
|
||||||
|
|
||||||
if not commit_err or commit_err:match 'nothing to commit' then
|
|
||||||
-- Get the commit hash
|
|
||||||
local hash_cmd = string.format('cd "%s" && git rev-parse HEAD', git_root)
|
|
||||||
local commit_hash, hash_err = utils.exec(hash_cmd)
|
|
||||||
|
|
||||||
if not hash_err and commit_hash then
|
|
||||||
commit_hash = commit_hash:gsub('%s+', '')
|
|
||||||
utils.write_file(baseline_file, commit_hash)
|
|
||||||
vim.notify('Claude baseline created: ' .. commit_hash:sub(1, 7), vim.log.levels.INFO)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Manual hook testing
|
-- Manual hook testing
|
||||||
function M.test_hooks()
|
function M.test_hooks()
|
||||||
vim.notify('=== Testing nvim-claude hooks ===', vim.log.levels.INFO)
|
vim.notify('=== Testing nvim-claude hooks ===', vim.log.levels.INFO)
|
||||||
|
|
||||||
-- Test pre-tool-use hook
|
local persistence = require 'nvim-claude.inline-diff-persistence'
|
||||||
vim.notify('1. Testing pre-tool-use hook (creating snapshot)...', vim.log.levels.INFO)
|
|
||||||
M.pre_tool_use_hook()
|
-- Test creating a stash
|
||||||
|
vim.notify('1. Creating test stash...', vim.log.levels.INFO)
|
||||||
|
local stash_ref = persistence.create_stash('nvim-claude: test stash')
|
||||||
|
|
||||||
|
if stash_ref then
|
||||||
|
persistence.current_stash_ref = stash_ref
|
||||||
|
vim.notify('Stash created: ' .. stash_ref, vim.log.levels.INFO)
|
||||||
|
else
|
||||||
|
vim.notify('Failed to create stash', vim.log.levels.ERROR)
|
||||||
|
end
|
||||||
|
|
||||||
-- Simulate making a change
|
-- Simulate making a change
|
||||||
vim.notify('2. Make some changes to test files now...', vim.log.levels.INFO)
|
vim.notify('2. Make some changes to test files now...', vim.log.levels.INFO)
|
||||||
|
@ -386,6 +346,12 @@ function M.setup_commands()
|
||||||
local inline_diff = require 'nvim-claude.inline-diff'
|
local inline_diff = require 'nvim-claude.inline-diff'
|
||||||
inline_diff.original_content[bufnr] = current_content
|
inline_diff.original_content[bufnr] = current_content
|
||||||
|
|
||||||
|
-- Save updated state
|
||||||
|
local persistence = require 'nvim-claude.inline-diff-persistence'
|
||||||
|
if persistence.current_stash_ref then
|
||||||
|
persistence.save_state({ stash_ref = persistence.current_stash_ref })
|
||||||
|
end
|
||||||
|
|
||||||
vim.notify('Baseline updated to current buffer state', vim.log.levels.INFO)
|
vim.notify('Baseline updated to current buffer state', vim.log.levels.INFO)
|
||||||
end, {
|
end, {
|
||||||
desc = 'Update Claude baseline to current buffer state',
|
desc = 'Update Claude baseline to current buffer state',
|
||||||
|
@ -448,15 +414,8 @@ function M.setup_commands()
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Cleanup old Claude commits and temp files
|
-- Cleanup old temp files (no longer cleans up commits)
|
||||||
function M.cleanup_old_commits()
|
function M.cleanup_old_files()
|
||||||
local utils = require 'nvim-claude.utils'
|
|
||||||
|
|
||||||
local git_root = utils.get_project_root()
|
|
||||||
if not git_root then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Clean up old temp files
|
-- Clean up old temp files
|
||||||
local temp_files = {
|
local temp_files = {
|
||||||
'/tmp/claude-pre-edit-commit',
|
'/tmp/claude-pre-edit-commit',
|
||||||
|
@ -470,31 +429,6 @@ function M.cleanup_old_commits()
|
||||||
vim.fn.delete(file)
|
vim.fn.delete(file)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Clean up old Claude commits (keep only the last 5)
|
|
||||||
local log_cmd =
|
|
||||||
string.format('cd "%s" && git log --oneline --grep="claude-" --grep="claude-baseline" --grep="claude-pre-edit" --all --max-count=10', git_root)
|
|
||||||
local log_result = utils.exec(log_cmd)
|
|
||||||
|
|
||||||
if log_result and log_result ~= '' then
|
|
||||||
local commits = {}
|
|
||||||
for line in log_result:gmatch '[^\n]+' do
|
|
||||||
local hash = line:match '^(%w+)'
|
|
||||||
if hash then
|
|
||||||
table.insert(commits, hash)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Keep only the last 5 Claude commits, remove the rest
|
|
||||||
-- DISABLED: This was causing rebases that broke the workflow
|
|
||||||
-- if #commits > 5 then
|
|
||||||
-- for i = 6, #commits do
|
|
||||||
-- local reset_cmd = string.format('cd "%s" && git rebase --onto %s^ %s', git_root, commits[i], commits[i])
|
|
||||||
-- utils.exec(reset_cmd)
|
|
||||||
-- end
|
|
||||||
-- vim.notify('Cleaned up old Claude commits', vim.log.levels.DEBUG)
|
|
||||||
-- end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
@ -0,0 +1,266 @@
|
||||||
|
-- Persistence layer for inline diffs
|
||||||
|
-- Manages saving/loading diff state across neovim sessions without polluting git history
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
local utils = require('nvim-claude.utils')
|
||||||
|
|
||||||
|
-- State file location
|
||||||
|
M.state_file = vim.fn.stdpath('data') .. '/nvim-claude-inline-diff-state.json'
|
||||||
|
|
||||||
|
-- Save current diff state
|
||||||
|
function M.save_state(diff_data)
|
||||||
|
-- Structure:
|
||||||
|
-- {
|
||||||
|
-- version: 1,
|
||||||
|
-- timestamp: <unix_timestamp>,
|
||||||
|
-- stash_ref: "stash@{0}",
|
||||||
|
-- files: {
|
||||||
|
-- "/path/to/file": {
|
||||||
|
-- original_content: "...",
|
||||||
|
-- hunks: [...],
|
||||||
|
-- applied_hunks: {...}
|
||||||
|
-- }
|
||||||
|
-- }
|
||||||
|
-- }
|
||||||
|
|
||||||
|
local state = {
|
||||||
|
version = 1,
|
||||||
|
timestamp = os.time(),
|
||||||
|
stash_ref = diff_data.stash_ref,
|
||||||
|
files = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Collect state from all buffers with active diffs
|
||||||
|
local inline_diff = require('nvim-claude.inline-diff')
|
||||||
|
for file_path, bufnr in pairs(inline_diff.diff_files) do
|
||||||
|
if inline_diff.active_diffs[bufnr] then
|
||||||
|
local diff = inline_diff.active_diffs[bufnr]
|
||||||
|
state.files[file_path] = {
|
||||||
|
original_content = inline_diff.original_content[bufnr],
|
||||||
|
hunks = diff.hunks,
|
||||||
|
applied_hunks = diff.applied_hunks or {},
|
||||||
|
new_content = diff.new_content
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Save to file
|
||||||
|
local success, err = utils.write_json(M.state_file, state)
|
||||||
|
if not success then
|
||||||
|
vim.notify('Failed to save inline diff state: ' .. err, vim.log.levels.ERROR)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Load saved diff state
|
||||||
|
function M.load_state()
|
||||||
|
if not utils.file_exists(M.state_file) then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local state, err = utils.read_json(M.state_file)
|
||||||
|
if not state then
|
||||||
|
vim.notify('Failed to load inline diff state: ' .. err, vim.log.levels.ERROR)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Validate version
|
||||||
|
if state.version ~= 1 then
|
||||||
|
vim.notify('Incompatible inline diff state version', vim.log.levels.WARN)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if stash still exists
|
||||||
|
if state.stash_ref then
|
||||||
|
local cmd = string.format('git stash list | grep -q "%s"', state.stash_ref:gsub("{", "\\{"):gsub("}", "\\}"))
|
||||||
|
local result = os.execute(cmd)
|
||||||
|
if result ~= 0 then
|
||||||
|
vim.notify('Saved stash no longer exists: ' .. state.stash_ref, vim.log.levels.WARN)
|
||||||
|
M.clear_state()
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return state
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Clear saved state
|
||||||
|
function M.clear_state()
|
||||||
|
if utils.file_exists(M.state_file) then
|
||||||
|
os.remove(M.state_file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Restore diffs from saved state
|
||||||
|
function M.restore_diffs()
|
||||||
|
local state = M.load_state()
|
||||||
|
if not state then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local inline_diff = require('nvim-claude.inline-diff')
|
||||||
|
local restored_count = 0
|
||||||
|
|
||||||
|
-- Restore diffs for each file
|
||||||
|
for file_path, file_state in pairs(state.files) do
|
||||||
|
-- Check if file exists and hasn't changed since the diff was created
|
||||||
|
if utils.file_exists(file_path) then
|
||||||
|
-- Read current content
|
||||||
|
local current_content = utils.read_file(file_path)
|
||||||
|
|
||||||
|
-- Check if the file matches what we expect (either original or with applied changes)
|
||||||
|
-- This handles the case where some hunks were accepted
|
||||||
|
if current_content then
|
||||||
|
-- Find or create buffer for this file
|
||||||
|
local bufnr = vim.fn.bufnr(file_path)
|
||||||
|
if bufnr == -1 then
|
||||||
|
-- File not loaded, we'll restore when it's opened
|
||||||
|
-- Store in a pending restores table
|
||||||
|
M.pending_restores = M.pending_restores or {}
|
||||||
|
M.pending_restores[file_path] = file_state
|
||||||
|
else
|
||||||
|
-- Restore the diff visualization
|
||||||
|
inline_diff.original_content[bufnr] = file_state.original_content
|
||||||
|
inline_diff.diff_files[file_path] = bufnr
|
||||||
|
inline_diff.active_diffs[bufnr] = {
|
||||||
|
hunks = file_state.hunks,
|
||||||
|
new_content = file_state.new_content,
|
||||||
|
current_hunk = 1,
|
||||||
|
applied_hunks = file_state.applied_hunks or {}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Apply visualization
|
||||||
|
inline_diff.apply_diff_visualization(bufnr)
|
||||||
|
inline_diff.setup_inline_keymaps(bufnr)
|
||||||
|
|
||||||
|
restored_count = restored_count + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if restored_count > 0 then
|
||||||
|
vim.notify(string.format('Restored inline diffs for %d file(s)', restored_count), vim.log.levels.INFO)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Store the stash reference for future operations
|
||||||
|
M.current_stash_ref = state.stash_ref
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check for pending restores when a buffer is loaded
|
||||||
|
function M.check_pending_restore(bufnr)
|
||||||
|
if not M.pending_restores then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local file_path = vim.api.nvim_buf_get_name(bufnr)
|
||||||
|
local file_state = M.pending_restores[file_path]
|
||||||
|
|
||||||
|
if file_state then
|
||||||
|
local inline_diff = require('nvim-claude.inline-diff')
|
||||||
|
|
||||||
|
-- Restore the diff for this buffer
|
||||||
|
inline_diff.original_content[bufnr] = file_state.original_content
|
||||||
|
inline_diff.diff_files[file_path] = bufnr
|
||||||
|
inline_diff.active_diffs[bufnr] = {
|
||||||
|
hunks = file_state.hunks,
|
||||||
|
new_content = file_state.new_content,
|
||||||
|
current_hunk = 1,
|
||||||
|
applied_hunks = file_state.applied_hunks or {}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Apply visualization
|
||||||
|
inline_diff.apply_diff_visualization(bufnr)
|
||||||
|
inline_diff.setup_inline_keymaps(bufnr)
|
||||||
|
|
||||||
|
-- Remove from pending
|
||||||
|
M.pending_restores[file_path] = nil
|
||||||
|
|
||||||
|
vim.notify('Restored inline diff for ' .. vim.fn.fnamemodify(file_path, ':~:.'), vim.log.levels.INFO)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Create a stash of current changes (instead of baseline commit)
|
||||||
|
function M.create_stash(message)
|
||||||
|
message = message or 'nvim-claude: pre-edit state'
|
||||||
|
|
||||||
|
-- Check if there are changes to stash
|
||||||
|
local status = utils.exec('git status --porcelain')
|
||||||
|
if not status or status == '' then
|
||||||
|
-- No changes, but we still need to track current state
|
||||||
|
-- Create an empty stash by making a tiny change
|
||||||
|
local temp_file = '.nvim-claude-temp'
|
||||||
|
utils.write_file(temp_file, 'temp')
|
||||||
|
utils.exec('git add ' .. temp_file)
|
||||||
|
utils.exec(string.format('git stash push -m "%s" -- %s', message, temp_file))
|
||||||
|
os.remove(temp_file)
|
||||||
|
else
|
||||||
|
-- Stash all current changes
|
||||||
|
local cmd = string.format('git stash push -m "%s" --include-untracked', message)
|
||||||
|
local result, err = utils.exec(cmd)
|
||||||
|
if err and not err:match('Saved working directory') then
|
||||||
|
vim.notify('Failed to create stash: ' .. err, vim.log.levels.ERROR)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get the stash reference
|
||||||
|
local stash_list = utils.exec('git stash list -n 1')
|
||||||
|
if stash_list then
|
||||||
|
local stash_ref = stash_list:match('^(stash@{%d+})')
|
||||||
|
return stash_ref
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Setup autocmds for persistence
|
||||||
|
function M.setup_autocmds()
|
||||||
|
local group = vim.api.nvim_create_augroup('NvimClaudeInlineDiffPersistence', { clear = true })
|
||||||
|
|
||||||
|
-- Save state before exiting vim
|
||||||
|
vim.api.nvim_create_autocmd('VimLeavePre', {
|
||||||
|
group = group,
|
||||||
|
callback = function()
|
||||||
|
local inline_diff = require('nvim-claude.inline-diff')
|
||||||
|
-- Only save if there are active diffs
|
||||||
|
local has_active_diffs = false
|
||||||
|
for _, diff in pairs(inline_diff.active_diffs) do
|
||||||
|
if diff then
|
||||||
|
has_active_diffs = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if has_active_diffs and M.current_stash_ref then
|
||||||
|
M.save_state({ stash_ref = M.current_stash_ref })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Check for pending restores when buffers are loaded
|
||||||
|
vim.api.nvim_create_autocmd('BufReadPost', {
|
||||||
|
group = group,
|
||||||
|
callback = function(ev)
|
||||||
|
M.check_pending_restore(ev.buf)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Auto-restore on VimEnter
|
||||||
|
vim.api.nvim_create_autocmd('VimEnter', {
|
||||||
|
group = group,
|
||||||
|
once = true,
|
||||||
|
callback = function()
|
||||||
|
-- Delay slightly to ensure everything is loaded
|
||||||
|
vim.defer_fn(function()
|
||||||
|
M.restore_diffs()
|
||||||
|
end, 100)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
|
@ -349,12 +349,14 @@ function M.accept_current_hunk(bufnr)
|
||||||
local hunk = diff_data.hunks[diff_data.current_hunk]
|
local hunk = diff_data.hunks[diff_data.current_hunk]
|
||||||
if not hunk then return end
|
if not hunk then return end
|
||||||
|
|
||||||
-- Update the baseline to include this accepted change
|
|
||||||
M.update_baseline_after_accept(bufnr, hunk)
|
|
||||||
|
|
||||||
-- Mark as applied (the changes are already in the buffer)
|
-- Mark as applied (the changes are already in the buffer)
|
||||||
diff_data.applied_hunks[diff_data.current_hunk] = true
|
diff_data.applied_hunks[diff_data.current_hunk] = true
|
||||||
|
|
||||||
|
-- Update in-memory baseline to current state
|
||||||
|
local current_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||||
|
local current_content = table.concat(current_lines, '\n')
|
||||||
|
M.original_content[bufnr] = current_content
|
||||||
|
|
||||||
-- Remove this hunk from the diff data since it's accepted
|
-- Remove this hunk from the diff data since it's accepted
|
||||||
table.remove(diff_data.hunks, diff_data.current_hunk)
|
table.remove(diff_data.hunks, diff_data.current_hunk)
|
||||||
|
|
||||||
|
@ -365,6 +367,10 @@ function M.accept_current_hunk(bufnr)
|
||||||
|
|
||||||
vim.notify(string.format('Accepted hunk - %d hunks remaining', #diff_data.hunks), vim.log.levels.INFO)
|
vim.notify(string.format('Accepted hunk - %d hunks remaining', #diff_data.hunks), vim.log.levels.INFO)
|
||||||
|
|
||||||
|
-- Save state for persistence
|
||||||
|
local persistence = require('nvim-claude.inline-diff-persistence')
|
||||||
|
persistence.save_state({ stash_ref = persistence.current_stash_ref })
|
||||||
|
|
||||||
if #diff_data.hunks == 0 then
|
if #diff_data.hunks == 0 then
|
||||||
-- No more hunks to review
|
-- No more hunks to review
|
||||||
vim.notify('All hunks processed! Closing inline diff.', vim.log.levels.INFO)
|
vim.notify('All hunks processed! Closing inline diff.', vim.log.levels.INFO)
|
||||||
|
@ -387,67 +393,17 @@ function M.reject_current_hunk(bufnr)
|
||||||
-- Revert the hunk by applying original content
|
-- Revert the hunk by applying original content
|
||||||
M.revert_hunk_changes(bufnr, hunk)
|
M.revert_hunk_changes(bufnr, hunk)
|
||||||
|
|
||||||
-- Create a new baseline commit with the rejected changes reverted
|
-- Save the buffer to ensure changes are on disk
|
||||||
local utils = require('nvim-claude.utils')
|
vim.api.nvim_buf_call(bufnr, function()
|
||||||
local git_root = utils.get_project_root()
|
if vim.bo.modified then
|
||||||
|
vim.cmd('write')
|
||||||
if git_root then
|
|
||||||
-- Save the buffer to ensure changes are on disk
|
|
||||||
vim.api.nvim_buf_call(bufnr, function()
|
|
||||||
if vim.bo.modified then
|
|
||||||
vim.cmd('write')
|
|
||||||
vim.notify('Buffer saved', vim.log.levels.INFO)
|
|
||||||
else
|
|
||||||
vim.notify('Buffer already saved', vim.log.levels.INFO)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Stage only the current file (now with hunk reverted)
|
|
||||||
local file_path = vim.api.nvim_buf_get_name(bufnr)
|
|
||||||
local relative_path = file_path:gsub('^' .. git_root .. '/', '')
|
|
||||||
|
|
||||||
-- Create a new baseline commit with only this file
|
|
||||||
local timestamp = os.time()
|
|
||||||
local commit_msg = string.format('claude-baseline-%d (rejected changes)', timestamp)
|
|
||||||
|
|
||||||
-- Use git commit with only the specific file
|
|
||||||
local commit_cmd = string.format('cd "%s" && git add "%s" && git commit -m "%s" -- "%s"',
|
|
||||||
git_root, relative_path, commit_msg, relative_path)
|
|
||||||
local commit_result, commit_err = utils.exec(commit_cmd)
|
|
||||||
|
|
||||||
vim.notify('Commit command: ' .. commit_cmd, vim.log.levels.DEBUG)
|
|
||||||
vim.notify('Commit result: ' .. (commit_result or 'nil'), vim.log.levels.DEBUG)
|
|
||||||
|
|
||||||
if commit_result and (commit_result:match('1 file changed') or commit_result:match('create mode') or commit_result:match('nothing to commit')) then
|
|
||||||
-- Get the new commit hash
|
|
||||||
local hash_cmd = string.format('cd "%s" && git rev-parse HEAD', git_root)
|
|
||||||
local commit_hash, hash_err = utils.exec(hash_cmd)
|
|
||||||
|
|
||||||
if not hash_err and commit_hash then
|
|
||||||
commit_hash = commit_hash:gsub('%s+', '')
|
|
||||||
-- Update the baseline file
|
|
||||||
utils.write_file('/tmp/claude-baseline-commit', commit_hash)
|
|
||||||
|
|
||||||
-- Update in-memory baseline
|
|
||||||
local current_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
|
||||||
local current_content = table.concat(current_lines, '\n')
|
|
||||||
M.original_content[bufnr] = current_content
|
|
||||||
|
|
||||||
if commit_result:match('nothing to commit') then
|
|
||||||
vim.notify('No changes to commit after rejection, baseline updated', vim.log.levels.INFO)
|
|
||||||
else
|
|
||||||
vim.notify('Baseline commit created after rejection: ' .. commit_hash:sub(1, 7), vim.log.levels.INFO)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
vim.notify('Failed to get commit hash: ' .. (hash_err or 'unknown'), vim.log.levels.ERROR)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
vim.notify('Failed to create baseline commit for rejection', vim.log.levels.ERROR)
|
|
||||||
if commit_err then
|
|
||||||
vim.notify('Error: ' .. commit_err, vim.log.levels.ERROR)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end)
|
||||||
|
|
||||||
|
-- Update in-memory baseline to current state (with rejected changes)
|
||||||
|
local current_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||||
|
local current_content = table.concat(current_lines, '\n')
|
||||||
|
M.original_content[bufnr] = current_content
|
||||||
|
|
||||||
-- Remove this hunk from the diff data since it's rejected
|
-- Remove this hunk from the diff data since it's rejected
|
||||||
table.remove(diff_data.hunks, diff_data.current_hunk)
|
table.remove(diff_data.hunks, diff_data.current_hunk)
|
||||||
|
@ -459,6 +415,10 @@ function M.reject_current_hunk(bufnr)
|
||||||
|
|
||||||
vim.notify(string.format('Rejected hunk - %d hunks remaining', #diff_data.hunks), vim.log.levels.INFO)
|
vim.notify(string.format('Rejected hunk - %d hunks remaining', #diff_data.hunks), vim.log.levels.INFO)
|
||||||
|
|
||||||
|
-- Save state for persistence
|
||||||
|
local persistence = require('nvim-claude.inline-diff-persistence')
|
||||||
|
persistence.save_state({ stash_ref = persistence.current_stash_ref })
|
||||||
|
|
||||||
if #diff_data.hunks == 0 then
|
if #diff_data.hunks == 0 then
|
||||||
-- No more hunks to review
|
-- No more hunks to review
|
||||||
vim.notify('All hunks processed! Closing inline diff.', vim.log.levels.INFO)
|
vim.notify('All hunks processed! Closing inline diff.', vim.log.levels.INFO)
|
||||||
|
@ -612,6 +572,22 @@ function M.close_inline_diff(bufnr, keep_baseline)
|
||||||
M.original_content[bufnr] = nil
|
M.original_content[bufnr] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Check if all diffs are closed
|
||||||
|
local has_active_diffs = false
|
||||||
|
for _, diff in pairs(M.active_diffs) do
|
||||||
|
if diff then
|
||||||
|
has_active_diffs = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If no more active diffs, clear persistence state
|
||||||
|
if not has_active_diffs then
|
||||||
|
local persistence = require('nvim-claude.inline-diff-persistence')
|
||||||
|
persistence.clear_state()
|
||||||
|
persistence.current_stash_ref = nil
|
||||||
|
end
|
||||||
|
|
||||||
vim.notify('Inline diff closed', vim.log.levels.INFO)
|
vim.notify('Inline diff closed', vim.log.levels.INFO)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -620,71 +596,11 @@ function M.has_active_diff(bufnr)
|
||||||
return M.active_diffs[bufnr] ~= nil
|
return M.active_diffs[bufnr] ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Update baseline content after accepting a hunk
|
-- Update baseline content after accepting a hunk (deprecated - no longer creates commits)
|
||||||
function M.update_baseline_after_accept(bufnr, hunk)
|
function M.update_baseline_after_accept(bufnr, hunk)
|
||||||
local utils = require('nvim-claude.utils')
|
-- This function is deprecated but kept for compatibility
|
||||||
local git_root = utils.get_project_root()
|
-- The baseline update is now handled directly in accept_current_hunk
|
||||||
|
vim.notify('update_baseline_after_accept is deprecated', vim.log.levels.DEBUG)
|
||||||
if not git_root then
|
|
||||||
vim.notify('Not in a git repository', vim.log.levels.ERROR)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Save the buffer to ensure changes are on disk
|
|
||||||
vim.api.nvim_buf_call(bufnr, function()
|
|
||||||
if vim.bo.modified then
|
|
||||||
vim.cmd('write')
|
|
||||||
vim.notify('Buffer saved', vim.log.levels.INFO)
|
|
||||||
else
|
|
||||||
vim.notify('Buffer already saved', vim.log.levels.INFO)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Stage only the current file
|
|
||||||
local file_path = vim.api.nvim_buf_get_name(bufnr)
|
|
||||||
local relative_path = file_path:gsub('^' .. git_root .. '/', '')
|
|
||||||
|
|
||||||
-- Create a new baseline commit with only this file
|
|
||||||
local timestamp = os.time()
|
|
||||||
local commit_msg = string.format('claude-baseline-%d (accepted changes)', timestamp)
|
|
||||||
|
|
||||||
-- Use git commit with only the specific file
|
|
||||||
local commit_cmd = string.format('cd "%s" && git add "%s" && git commit -m "%s" -- "%s"',
|
|
||||||
git_root, relative_path, commit_msg, relative_path)
|
|
||||||
local commit_result, commit_err = utils.exec(commit_cmd)
|
|
||||||
|
|
||||||
vim.notify('Commit command: ' .. commit_cmd, vim.log.levels.INFO)
|
|
||||||
vim.notify('Commit result: ' .. (commit_result or 'nil'), vim.log.levels.INFO)
|
|
||||||
|
|
||||||
if commit_result and (commit_result:match('1 file changed') or commit_result:match('create mode') or commit_result:match('nothing to commit')) then
|
|
||||||
-- Commit was successful or there was nothing to commit (file already at desired state)
|
|
||||||
local hash_cmd = string.format('cd "%s" && git rev-parse HEAD', git_root)
|
|
||||||
local commit_hash, hash_err = utils.exec(hash_cmd)
|
|
||||||
|
|
||||||
if not hash_err and commit_hash then
|
|
||||||
commit_hash = commit_hash:gsub('%s+', '')
|
|
||||||
-- Update the baseline file
|
|
||||||
utils.write_file('/tmp/claude-baseline-commit', commit_hash)
|
|
||||||
|
|
||||||
-- Update in-memory baseline
|
|
||||||
local current_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
|
||||||
local current_content = table.concat(current_lines, '\n')
|
|
||||||
M.original_content[bufnr] = current_content
|
|
||||||
|
|
||||||
if commit_result:match('nothing to commit') then
|
|
||||||
vim.notify('No changes to commit, baseline updated to current state', vim.log.levels.INFO)
|
|
||||||
else
|
|
||||||
vim.notify('Baseline commit created: ' .. commit_hash:sub(1, 7), vim.log.levels.INFO)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
vim.notify('Failed to get commit hash: ' .. (hash_err or 'unknown'), vim.log.levels.ERROR)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
vim.notify('Failed to create baseline commit', vim.log.levels.ERROR)
|
|
||||||
if commit_err then
|
|
||||||
vim.notify('Error: ' .. commit_err, vim.log.levels.ERROR)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Test keymap functionality
|
-- Test keymap functionality
|
||||||
|
|
Loading…
Reference in New Issue