@@ -19,7 +19,6 @@ use asyncgit::{
19
19
diff_contains_file, get_commits_info, CommitId , RepoPathRef ,
20
20
} ,
21
21
AsyncDiff , AsyncGitNotification , AsyncLog , DiffParams , DiffType ,
22
- FetchStatus ,
23
22
} ;
24
23
use chrono:: { DateTime , Local } ;
25
24
use crossbeam_channel:: Sender ;
@@ -126,7 +125,10 @@ impl FileRevlogComponent {
126
125
& self . sender ,
127
126
Some ( filter) ,
128
127
) ) ;
129
- self . table_state . get_mut ( ) . select ( Some ( 0 ) ) ;
128
+
129
+ self . items . clear ( ) ;
130
+ self . set_selection ( open_request. selection . unwrap_or ( 0 ) ) ;
131
+
130
132
self . show ( ) ?;
131
133
132
134
self . diff . focus ( false ) ;
@@ -149,20 +151,9 @@ impl FileRevlogComponent {
149
151
///
150
152
pub fn update ( & mut self ) -> Result < ( ) > {
151
153
if let Some ( ref mut git_log) = self . git_log {
152
- let log_changed =
153
- git_log. fetch ( ) ? == FetchStatus :: Started ;
154
-
155
- let table_state = self . table_state . take ( ) ;
156
- let start = table_state. selected ( ) . unwrap_or ( 0 ) ;
157
- self . table_state . set ( table_state) ;
158
-
159
- if self . items . needs_data ( start, git_log. count ( ) ?)
160
- || log_changed
161
- {
162
- self . fetch_commits ( ) ?;
163
- self . set_open_selection ( ) ;
164
- }
154
+ git_log. fetch ( ) ?;
165
155
156
+ self . fetch_commits_if_needed ( ) ?;
166
157
self . update_diff ( ) ?;
167
158
}
168
159
@@ -225,45 +216,18 @@ impl FileRevlogComponent {
225
216
226
217
fn fetch_commits ( & mut self ) -> Result < ( ) > {
227
218
if let Some ( git_log) = & mut self . git_log {
228
- let table_state = self . table_state . take ( ) ;
219
+ let offset = self . table_state . get_mut ( ) . offset ( ) ;
229
220
230
221
let commits = get_commits_info (
231
222
& self . repo_path . borrow ( ) ,
232
- & git_log. get_slice ( 0 , SLICE_SIZE ) ?,
223
+ & git_log. get_slice ( offset , SLICE_SIZE ) ?,
233
224
self . current_width . get ( ) ,
234
225
) ;
235
226
236
227
if let Ok ( commits) = commits {
237
- // 2023-04-12
238
- //
239
- // There is an issue with how windowing works in `self.items` and
240
- // `self.table_state`. Because of that issue, we currently have to pass
241
- // `0` as the first argument to `set_items`. If we did not do that, the
242
- // offset that is kept separately in `self.items` and `self.table_state`
243
- // would get out of sync, resulting in the table showing the wrong rows.
244
- //
245
- // The offset determines the number of rows `render_stateful_widget`
246
- // skips when rendering a table. When `set_items` is called, it clears
247
- // its internal `Vec` of items and sets `index_offset` based on the
248
- // parameter passed. Unfortunately, there is no way for us to pass this
249
- // information, `index_offset`, to `render_stateful_widget`. Because of
250
- // that, `render_stateful_widget` assumes that the rows provided by
251
- // `Table` are 0-indexed while in reality they are
252
- // `index_offset`-indexed.
253
- //
254
- // This fix makes the `FileRevlog` unable to show histories that have
255
- // more than `SLICE_SIZE` items, but since it is broken for larger
256
- // histories anyway, this seems acceptable for the time being.
257
- //
258
- // This issue can only properly be fixed upstream, in `tui-rs`. See
259
- // [tui-issue].
260
- //
261
- // [gitui-issue]: https://github.com/extrawurst/gitui/issues/1560
262
- // [tui-issue]: https://github.com/fdehau/tui-rs/issues/626
263
- self . items . set_items ( 0 , commits) ;
228
+ self . items . set_items ( offset, commits) ;
264
229
}
265
230
266
- self . table_state . set ( table_state) ;
267
231
self . count_total = git_log. count ( ) ?;
268
232
}
269
233
@@ -276,7 +240,7 @@ impl FileRevlogComponent {
276
240
let commit_id = table_state. selected ( ) . and_then ( |selected| {
277
241
self . items
278
242
. iter ( )
279
- . nth ( selected)
243
+ . nth ( selected. saturating_sub ( table_state . offset ( ) ) )
280
244
. as_ref ( )
281
245
. map ( |entry| entry. id )
282
246
} ) ;
@@ -348,10 +312,12 @@ impl FileRevlogComponent {
348
312
} )
349
313
}
350
314
351
- fn move_selection ( & mut self , scroll_type : ScrollType ) -> bool {
352
- let mut table_state = self . table_state . take ( ) ;
353
-
354
- let old_selection = table_state. selected ( ) . unwrap_or ( 0 ) ;
315
+ fn move_selection (
316
+ & mut self ,
317
+ scroll_type : ScrollType ,
318
+ ) -> Result < ( ) > {
319
+ let old_selection =
320
+ self . table_state . get_mut ( ) . selected ( ) . unwrap_or ( 0 ) ;
355
321
let max_selection = self . get_max_selection ( ) ;
356
322
let height_in_items = self . current_height . get ( ) / 2 ;
357
323
@@ -375,20 +341,34 @@ impl FileRevlogComponent {
375
341
self . queue . push ( InternalEvent :: Update ( NeedsUpdate :: DIFF ) ) ;
376
342
}
377
343
378
- table_state. select ( Some ( new_selection) ) ;
379
- self . table_state . set ( table_state) ;
344
+ self . set_selection ( new_selection) ;
345
+ self . fetch_commits_if_needed ( ) ?;
346
+
347
+ Ok ( ( ) )
348
+ }
349
+
350
+ fn set_selection ( & mut self , selection : usize ) {
351
+ let height_in_items = self . current_height . get ( ) / 2 ;
352
+ let new_offset = selection. saturating_sub ( height_in_items) ;
380
353
381
- needs_update
354
+ * self . table_state . get_mut ( ) . offset_mut ( ) = new_offset;
355
+ self . table_state . get_mut ( ) . select ( Some ( selection) ) ;
382
356
}
383
357
384
- fn set_open_selection ( & mut self ) {
385
- if let Some ( selection) =
386
- self . open_request . as_ref ( ) . and_then ( |req| req. selection )
387
- {
388
- let mut table_state = self . table_state . take ( ) ;
389
- table_state. select ( Some ( selection) ) ;
390
- self . table_state . set ( table_state) ;
358
+ fn fetch_commits_if_needed ( & mut self ) -> Result < ( ) > {
359
+ let selection =
360
+ self . table_state . get_mut ( ) . selected ( ) . unwrap_or ( 0 ) ;
361
+ let height_in_items = self . current_height . get ( ) / 2 ;
362
+ let new_offset = selection. saturating_sub ( height_in_items) ;
363
+
364
+ if self . items . needs_data (
365
+ new_offset,
366
+ selection. saturating_add ( height_in_items) ,
367
+ ) {
368
+ self . fetch_commits ( ) ?;
391
369
}
370
+
371
+ Ok ( ( ) )
392
372
}
393
373
394
374
fn get_selection ( & self ) -> Option < usize > {
@@ -426,10 +406,28 @@ impl FileRevlogComponent {
426
406
. border_style ( self . theme . block ( true ) ) ,
427
407
) ;
428
408
429
- let mut table_state = self . table_state . take ( ) ;
409
+ let table_state = self . table_state . take ( ) ;
410
+ // We have to adjust the table state for drawing to account for the fact
411
+ // that `self.items` not necessarily starts at index 0.
412
+ //
413
+ // When a user scrolls down, items outside of the current view are removed
414
+ // when new data is fetched. Let’s have a look at an example: if the item at
415
+ // index 50 is the first item in the current view and `self.items` has been
416
+ // freshly fetched, the current offset is 50 and `self.items[0]` is the item
417
+ // at index 50. Subtracting the current offset from the selected index
418
+ // yields the correct index in `self.items`, in this case 0.
419
+ let mut adjusted_table_state = TableState :: default ( )
420
+ . with_selected ( table_state. selected ( ) . map ( |selected| {
421
+ selected. saturating_sub ( table_state. offset ( ) )
422
+ } ) )
423
+ . with_offset ( 0 ) ;
430
424
431
425
f. render_widget ( Clear , area) ;
432
- f. render_stateful_widget ( table, area, & mut table_state) ;
426
+ f. render_stateful_widget (
427
+ table,
428
+ area,
429
+ & mut adjusted_table_state,
430
+ ) ;
433
431
434
432
draw_scrollbar (
435
433
f,
@@ -548,36 +546,36 @@ impl Component for FileRevlogComponent {
548
546
}
549
547
} else if key_match ( key, self . key_config . keys . move_up )
550
548
{
551
- self . move_selection ( ScrollType :: Up ) ;
549
+ self . move_selection ( ScrollType :: Up ) ? ;
552
550
} else if key_match (
553
551
key,
554
552
self . key_config . keys . move_down ,
555
553
) {
556
- self . move_selection ( ScrollType :: Down ) ;
554
+ self . move_selection ( ScrollType :: Down ) ? ;
557
555
} else if key_match (
558
556
key,
559
557
self . key_config . keys . shift_up ,
560
558
) || key_match (
561
559
key,
562
560
self . key_config . keys . home ,
563
561
) {
564
- self . move_selection ( ScrollType :: Home ) ;
562
+ self . move_selection ( ScrollType :: Home ) ? ;
565
563
} else if key_match (
566
564
key,
567
565
self . key_config . keys . shift_down ,
568
566
) || key_match (
569
567
key,
570
568
self . key_config . keys . end ,
571
569
) {
572
- self . move_selection ( ScrollType :: End ) ;
570
+ self . move_selection ( ScrollType :: End ) ? ;
573
571
} else if key_match ( key, self . key_config . keys . page_up )
574
572
{
575
- self . move_selection ( ScrollType :: PageUp ) ;
573
+ self . move_selection ( ScrollType :: PageUp ) ? ;
576
574
} else if key_match (
577
575
key,
578
576
self . key_config . keys . page_down ,
579
577
) {
580
- self . move_selection ( ScrollType :: PageDown ) ;
578
+ self . move_selection ( ScrollType :: PageDown ) ? ;
581
579
}
582
580
}
583
581
0 commit comments