feat(hunks): stage selected hunks
This commit is contained in:
+112
-35
@@ -621,20 +621,6 @@ local function hunk_at(hunks, row)
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param state ow.Git.Hunks.BufState
|
||||
---@param buf integer
|
||||
---@param row integer 1-indexed cursor line
|
||||
---@return ow.Git.Hunks.Hunk?
|
||||
local function staged_hunk_at(state, buf, row)
|
||||
local line_count = vim.api.nvim_buf_line_count(buf)
|
||||
for _, s in ipairs(staged_signs(state, line_count)) do
|
||||
if s.row == row - 1 then
|
||||
return s.hunk
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param buf integer?
|
||||
---@return integer buf
|
||||
---@return ow.Git.Hunks.BufState? state
|
||||
@@ -748,17 +734,20 @@ end
|
||||
---@param h ow.Git.Hunks.Hunk
|
||||
---@param old_lines string[]
|
||||
---@param rel string
|
||||
---@return string patch
|
||||
---@param include_header boolean?
|
||||
---@param context integer?
|
||||
---@return string[] patch lines
|
||||
---@return boolean zero_context
|
||||
local function build_patch(h, old_lines, rel)
|
||||
local function build_patch_lines(h, old_lines, rel, include_header, context)
|
||||
context = context or PATCH_CONTEXT
|
||||
local old_before, new_before = hunk_offsets(h)
|
||||
local pre = {}
|
||||
for i = math.max(old_before - PATCH_CONTEXT + 1, 1), old_before do
|
||||
for i = math.max(old_before - context + 1, 1), old_before do
|
||||
pre[#pre + 1] = old_lines[i] or ""
|
||||
end
|
||||
local post = {}
|
||||
local after = old_before + h.old_count
|
||||
for i = after + 1, math.min(after + PATCH_CONTEXT, #old_lines) do
|
||||
for i = after + 1, math.min(after + context, #old_lines) do
|
||||
post[#post + 1] = old_lines[i] or ""
|
||||
end
|
||||
local old_n = #pre + h.old_count + #post
|
||||
@@ -786,9 +775,29 @@ local function build_patch(h, old_lines, rel)
|
||||
for _, l in ipairs(post) do
|
||||
body[#body + 1] = " " .. l
|
||||
end
|
||||
local lines = { "--- a/" .. rel, "+++ b/" .. rel }
|
||||
local lines = {}
|
||||
if include_header ~= false then
|
||||
lines = { "--- a/" .. rel, "+++ b/" .. rel }
|
||||
end
|
||||
vim.list_extend(lines, body)
|
||||
return table.concat(lines, "\n") .. "\n", #pre == 0 and #post == 0
|
||||
return lines, #pre == 0 and #post == 0
|
||||
end
|
||||
|
||||
---@param hunks ow.Git.Hunks.Hunk[]
|
||||
---@param old_lines string[]
|
||||
---@param rel string
|
||||
---@param context integer?
|
||||
---@return string patch
|
||||
---@return boolean zero_context
|
||||
local function build_patch(hunks, old_lines, rel, context)
|
||||
local lines = { "--- a/" .. rel, "+++ b/" .. rel }
|
||||
local zero_context = false
|
||||
for _, h in ipairs(hunks) do
|
||||
local body, zero = build_patch_lines(h, old_lines, rel, false, context)
|
||||
vim.list_extend(lines, body)
|
||||
zero_context = zero_context or zero
|
||||
end
|
||||
return table.concat(lines, "\n") .. "\n", zero_context
|
||||
end
|
||||
|
||||
---@param state ow.Git.Hunks.BufState
|
||||
@@ -818,27 +827,95 @@ local function apply_patch(state, buf, patch, zero_context)
|
||||
})
|
||||
end
|
||||
|
||||
---@param h ow.Git.Hunks.Hunk
|
||||
---@return integer first 1-indexed buffer line
|
||||
---@return integer last 1-indexed buffer line
|
||||
local function hunk_range(h)
|
||||
if h.type == "delete" then
|
||||
local line = math.max(h.new_start, 1)
|
||||
return line, line
|
||||
end
|
||||
return h.new_start, h.new_start + h.new_count - 1
|
||||
end
|
||||
|
||||
---@param h ow.Git.Hunks.Hunk
|
||||
---@param first integer
|
||||
---@param last integer
|
||||
---@return boolean
|
||||
local function hunk_overlaps(h, first, last)
|
||||
local h_first, h_last = hunk_range(h)
|
||||
return h_first <= last and h_last >= first
|
||||
end
|
||||
|
||||
---@param state ow.Git.Hunks.BufState
|
||||
---@param buf integer
|
||||
---@param first integer
|
||||
---@param last integer
|
||||
---@return ow.Git.Hunks.Hunk[]
|
||||
local function staged_hunks_in_range(state, buf, first, last)
|
||||
local out = {}
|
||||
local seen = {}
|
||||
local line_count = vim.api.nvim_buf_line_count(buf)
|
||||
for _, s in ipairs(staged_signs(state, line_count)) do
|
||||
local line = s.row + 1
|
||||
if line >= first and line <= last and not seen[s.hunk] then
|
||||
seen[s.hunk] = true
|
||||
table.insert(out, s.hunk)
|
||||
end
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
---@param first integer
|
||||
---@param last integer
|
||||
---@param buf? integer
|
||||
function M.toggle_stage(buf)
|
||||
function M.toggle_stage_range(first, last, buf)
|
||||
buf = resolve_buf(buf)
|
||||
local state = states[buf]
|
||||
if not state then
|
||||
if not state or not state.index then
|
||||
return
|
||||
end
|
||||
if first > last then
|
||||
first, last = last, first
|
||||
end
|
||||
local unstaged = {}
|
||||
for _, h in ipairs(state.hunks) do
|
||||
if hunk_overlaps(h, first, last) then
|
||||
table.insert(unstaged, h)
|
||||
end
|
||||
end
|
||||
if #unstaged > 0 then
|
||||
local context = #unstaged > 1 and 0 or nil
|
||||
local patch, zero =
|
||||
build_patch(unstaged, state.index, state.rel, context)
|
||||
apply_patch(state, buf, patch, zero)
|
||||
return
|
||||
end
|
||||
local staged = staged_hunks_in_range(state, buf, first, last)
|
||||
if #staged > 0 then
|
||||
local inverted = {}
|
||||
for _, h in ipairs(staged) do
|
||||
table.insert(inverted, invert(h))
|
||||
end
|
||||
local context = #inverted > 1 and 0 or nil
|
||||
local patch, zero =
|
||||
build_patch(inverted, state.index, state.rel, context)
|
||||
apply_patch(state, buf, patch, zero)
|
||||
return
|
||||
end
|
||||
util.warning("git hunks: no hunk in selection")
|
||||
end
|
||||
|
||||
---@param buf? integer
|
||||
function M.toggle_stage(buf)
|
||||
local row = vim.api.nvim_win_get_cursor(0)[1]
|
||||
local unstaged = hunk_at(state.hunks, row)
|
||||
if unstaged and state.index then
|
||||
local patch, zero = build_patch(unstaged, state.index, state.rel)
|
||||
apply_patch(state, buf, patch, zero)
|
||||
return
|
||||
end
|
||||
local staged = staged_hunk_at(state, buf, row)
|
||||
if staged and state.index then
|
||||
local patch, zero = build_patch(invert(staged), state.index, state.rel)
|
||||
apply_patch(state, buf, patch, zero)
|
||||
return
|
||||
end
|
||||
util.warning("git hunks: no hunk at cursor")
|
||||
M.toggle_stage_range(row, row, buf)
|
||||
end
|
||||
|
||||
---@param buf? integer
|
||||
function M.toggle_stage_selection(buf)
|
||||
local cursor_line = vim.api.nvim_win_get_cursor(0)[1]
|
||||
M.toggle_stage_range(vim.fn.line("v"), cursor_line, buf)
|
||||
end
|
||||
|
||||
---@param buf? integer
|
||||
|
||||
Reference in New Issue
Block a user