@@ -87,6 +87,38 @@ function ProjectDiffScreen:move_to(query_fn)
8787 return self .status_list_view :move_to (query_fn )
8888end
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+
90122function 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 )
144164end
145165
146166function 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 )
200208end
201209
202210function 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 )
260258end
261259
262260function ProjectDiffScreen :stage_file ()
0 commit comments