Skip to content

Commit 021fe2c

Browse files
committed
fix: Stay at next hunk/file after staging
Hunk operations (stage/unstage/reset) stay at the same index position, effectively moving to the next hunk. When a file is fully staged or unstaged, jump to the next file of the same type rather than the first.
1 parent 508f28e commit 021fe2c

1 file changed

Lines changed: 59 additions & 61 deletions

File tree

  • lua/vgit/features/screens/ProjectDiffScreen

lua/vgit/features/screens/ProjectDiffScreen/init.lua

Lines changed: 59 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,38 @@ function ProjectDiffScreen:move_to(query_fn)
8787
return self.status_list_view:move_to(query_fn)
8888
end
8989

90+
-- Find the next file of given entry_type after current filename
91+
function ProjectDiffScreen:find_next_file(filename, target_entry_type)
92+
local next_filename = nil
93+
local found_current = false
94+
self.status_list_view:each_status(function(status, entry_type)
95+
if entry_type == target_entry_type then
96+
if found_current and not next_filename then
97+
next_filename = status.filename
98+
end
99+
if status.filename == filename then
100+
found_current = true
101+
end
102+
end
103+
end)
104+
return next_filename
105+
end
106+
107+
-- Restore cursor to same hunk index after staging/unstaging/resetting a hunk
108+
function ProjectDiffScreen:restore_hunk_position(filename, entry_type, hunk_index)
109+
local new_entry = self.model:get_entry()
110+
if new_entry
111+
and new_entry.type == entry_type
112+
and new_entry.status.filename == filename then
113+
local diff = self.model:get_diff()
114+
if diff and diff.marks and #diff.marks > 0 then
115+
local target = math.min(hunk_index, #diff.marks)
116+
local hunk_alignment = project_diff_preview_setting:get('hunk_alignment')
117+
self.diff_view:move_to_hunk(target, hunk_alignment)
118+
end
119+
end
120+
end
121+
90122
function ProjectDiffScreen:stage_hunk()
91123
local entry = self.model:get_entry()
92124
if not entry then return end
@@ -97,6 +129,8 @@ function ProjectDiffScreen:stage_hunk()
97129
if not hunk then return end
98130

99131
local filename = entry.status.filename
132+
local next_file = self:find_next_file(filename, 'unstaged')
133+
100134
local _, err = self.model:stage_hunk(filename, hunk)
101135
if err then
102136
console.debug.error(err)
@@ -112,35 +146,21 @@ function ProjectDiffScreen:stage_hunk()
112146
end)
113147

114148
if has_unstaged then
115-
-- Stay on the unstaged entry for this file
116149
self:move_to(function(status, entry_type)
117150
return status.filename == filename and entry_type == 'unstaged'
118151
end)
152+
elseif next_file then
153+
self:move_to(function(status, entry_type)
154+
return status.filename == next_file and entry_type == 'unstaged'
155+
end)
119156
else
120-
-- File fully staged - jump to next unstaged file, else this file's staged
121-
local found = self:move_to(function(_, entry_type)
122-
return entry_type == 'unstaged'
157+
self:move_to(function(status)
158+
return status.filename == filename
123159
end)
124-
if not found then
125-
self:move_to(function(status)
126-
return status.filename == filename
127-
end)
128-
end
129160
end
130161
end)
131162

132-
-- If still on the same file, stay at same hunk index position
133-
local new_entry = self.model:get_entry()
134-
if new_entry
135-
and new_entry.type == 'unstaged'
136-
and new_entry.status.filename == filename then
137-
local diff = self.model:get_diff()
138-
if diff and diff.marks and #diff.marks > 0 then
139-
local target = math.min(hunk_index, #diff.marks)
140-
local hunk_alignment = project_diff_preview_setting:get('hunk_alignment')
141-
self.diff_view:move_to_hunk(target, hunk_alignment)
142-
end
143-
end
163+
self:restore_hunk_position(filename, 'unstaged', hunk_index)
144164
end
145165

146166
function ProjectDiffScreen:unstage_hunk()
@@ -153,6 +173,8 @@ function ProjectDiffScreen:unstage_hunk()
153173
if not hunk then return end
154174

155175
local filename = entry.status.filename
176+
local next_file = self:find_next_file(filename, 'staged')
177+
156178
local _, err = self.model:unstage_hunk(filename, hunk)
157179
if err then
158180
console.debug.error(err)
@@ -168,35 +190,21 @@ function ProjectDiffScreen:unstage_hunk()
168190
end)
169191

170192
if has_staged then
171-
-- Stay on the staged entry for this file
172193
self:move_to(function(status, entry_type)
173194
return status.filename == filename and entry_type == 'staged'
174195
end)
196+
elseif next_file then
197+
self:move_to(function(status, entry_type)
198+
return status.filename == next_file and entry_type == 'staged'
199+
end)
175200
else
176-
-- File fully unstaged - jump to next staged file, else this file's unstaged
177-
local found = self:move_to(function(_, entry_type)
178-
return entry_type == 'staged'
201+
self:move_to(function(status)
202+
return status.filename == filename
179203
end)
180-
if not found then
181-
self:move_to(function(status)
182-
return status.filename == filename
183-
end)
184-
end
185204
end
186205
end)
187206

188-
-- If still on the same file, stay at same hunk index position
189-
local new_entry = self.model:get_entry()
190-
if new_entry
191-
and new_entry.type == 'staged'
192-
and new_entry.status.filename == filename then
193-
local diff = self.model:get_diff()
194-
if diff and diff.marks and #diff.marks > 0 then
195-
local target = math.min(hunk_index, #diff.marks)
196-
local hunk_alignment = project_diff_preview_setting:get('hunk_alignment')
197-
self.diff_view:move_to_hunk(target, hunk_alignment)
198-
end
199-
end
207+
self:restore_hunk_position(filename, 'staged', hunk_index)
200208
end
201209

202210
function ProjectDiffScreen:reset_hunk()
@@ -209,6 +217,8 @@ function ProjectDiffScreen:reset_hunk()
209217
if not hunk then return end
210218

211219
local filename = entry.status.filename
220+
local next_file = self:find_next_file(filename, 'unstaged')
221+
212222
loop.free_textlock()
213223
local decision = console.input('Are you sure you want to discard this hunk? (y/N) '):lower()
214224
if decision ~= 'yes' and decision ~= 'y' then return end
@@ -233,30 +243,18 @@ function ProjectDiffScreen:reset_hunk()
233243
self:move_to(function(status, entry_type)
234244
return status.filename == filename and entry_type == 'unstaged'
235245
end)
246+
elseif next_file then
247+
self:move_to(function(status, entry_type)
248+
return status.filename == next_file and entry_type == 'unstaged'
249+
end)
236250
else
237-
local found = self:move_to(function(_, entry_type)
238-
return entry_type == 'unstaged'
251+
self:move_to(function(status)
252+
return status.filename == filename
239253
end)
240-
if not found then
241-
self:move_to(function(status)
242-
return status.filename == filename
243-
end)
244-
end
245254
end
246255
end)
247256

248-
-- If still on the same file, stay at same hunk index position
249-
local new_entry = self.model:get_entry()
250-
if new_entry
251-
and new_entry.type == 'unstaged'
252-
and new_entry.status.filename == filename then
253-
local diff = self.model:get_diff()
254-
if diff and diff.marks and #diff.marks > 0 then
255-
local target = math.min(hunk_index, #diff.marks)
256-
local hunk_alignment = project_diff_preview_setting:get('hunk_alignment')
257-
self.diff_view:move_to_hunk(target, hunk_alignment)
258-
end
259-
end
257+
self:restore_hunk_position(filename, 'unstaged', hunk_index)
260258
end
261259

262260
function ProjectDiffScreen:stage_file()

0 commit comments

Comments
 (0)