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