Skip to content

Commit aa7aa7a

Browse files
authored
Fix file history for all sizes (#1738)
1 parent d0f15d5 commit aa7aa7a

File tree

1 file changed

+86
-69
lines changed

1 file changed

+86
-69
lines changed

src/components/file_revlog.rs

Lines changed: 86 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use asyncgit::{
1919
diff_contains_file, get_commits_info, CommitId, RepoPathRef,
2020
},
2121
AsyncDiff, AsyncGitNotification, AsyncLog, DiffParams, DiffType,
22-
FetchStatus,
2322
};
2423
use chrono::{DateTime, Local};
2524
use crossbeam_channel::Sender;
@@ -123,7 +122,10 @@ impl FileRevlogComponent {
123122
&self.sender,
124123
Some(filter),
125124
));
126-
self.table_state.get_mut().select(Some(0));
125+
126+
self.items.clear();
127+
self.set_selection(open_request.selection.unwrap_or(0));
128+
127129
self.show()?;
128130

129131
self.diff.focus(false);
@@ -146,20 +148,9 @@ impl FileRevlogComponent {
146148
///
147149
pub fn update(&mut self) -> Result<()> {
148150
if let Some(ref mut git_log) = self.git_log {
149-
let log_changed =
150-
git_log.fetch()? == FetchStatus::Started;
151-
152-
let table_state = self.table_state.take();
153-
let start = table_state.selected().unwrap_or(0);
154-
self.table_state.set(table_state);
155-
156-
if self.items.needs_data(start, git_log.count()?)
157-
|| log_changed
158-
{
159-
self.fetch_commits()?;
160-
self.set_open_selection();
161-
}
151+
git_log.fetch()?;
162152

153+
self.fetch_commits_if_needed()?;
163154
self.update_diff()?;
164155
}
165156

@@ -220,47 +211,26 @@ impl FileRevlogComponent {
220211
Ok(())
221212
}
222213

223-
fn fetch_commits(&mut self) -> Result<()> {
214+
fn fetch_commits(
215+
&mut self,
216+
new_offset: usize,
217+
new_max_offset: usize,
218+
) -> Result<()> {
224219
if let Some(git_log) = &mut self.git_log {
225-
let table_state = self.table_state.take();
220+
let amount = new_max_offset
221+
.saturating_sub(new_offset)
222+
.max(SLICE_SIZE);
226223

227224
let commits = get_commits_info(
228225
&self.repo_path.borrow(),
229-
&git_log.get_slice(0, SLICE_SIZE)?,
226+
&git_log.get_slice(new_offset, amount)?,
230227
self.current_width.get(),
231228
);
232229

233230
if let Ok(commits) = commits {
234-
// 2023-04-12
235-
//
236-
// There is an issue with how windowing works in `self.items` and
237-
// `self.table_state`. Because of that issue, we currently have to pass
238-
// `0` as the first argument to `set_items`. If we did not do that, the
239-
// offset that is kept separately in `self.items` and `self.table_state`
240-
// would get out of sync, resulting in the table showing the wrong rows.
241-
//
242-
// The offset determines the number of rows `render_stateful_widget`
243-
// skips when rendering a table. When `set_items` is called, it clears
244-
// its internal `Vec` of items and sets `index_offset` based on the
245-
// parameter passed. Unfortunately, there is no way for us to pass this
246-
// information, `index_offset`, to `render_stateful_widget`. Because of
247-
// that, `render_stateful_widget` assumes that the rows provided by
248-
// `Table` are 0-indexed while in reality they are
249-
// `index_offset`-indexed.
250-
//
251-
// This fix makes the `FileRevlog` unable to show histories that have
252-
// more than `SLICE_SIZE` items, but since it is broken for larger
253-
// histories anyway, this seems acceptable for the time being.
254-
//
255-
// This issue can only properly be fixed upstream, in `tui-rs`. See
256-
// [tui-issue].
257-
//
258-
// [gitui-issue]: https://github.com/extrawurst/gitui/issues/1560
259-
// [tui-issue]: https://github.com/fdehau/tui-rs/issues/626
260-
self.items.set_items(0, commits, &None);
231+
self.items.set_items(new_offset, commits, &None);
261232
}
262233

263-
self.table_state.set(table_state);
264234
self.count_total = git_log.count()?;
265235
}
266236

@@ -273,7 +243,10 @@ impl FileRevlogComponent {
273243
let commit_id = table_state.selected().and_then(|selected| {
274244
self.items
275245
.iter()
276-
.nth(selected)
246+
.nth(
247+
selected
248+
.saturating_sub(self.items.index_offset()),
249+
)
277250
.as_ref()
278251
.map(|entry| entry.id)
279252
});
@@ -345,10 +318,12 @@ impl FileRevlogComponent {
345318
})
346319
}
347320

348-
fn move_selection(&mut self, scroll_type: ScrollType) -> bool {
349-
let mut table_state = self.table_state.take();
350-
351-
let old_selection = table_state.selected().unwrap_or(0);
321+
fn move_selection(
322+
&mut self,
323+
scroll_type: ScrollType,
324+
) -> Result<()> {
325+
let old_selection =
326+
self.table_state.get_mut().selected().unwrap_or(0);
352327
let max_selection = self.get_max_selection();
353328
let height_in_items = self.current_height.get() / 2;
354329

@@ -372,20 +347,40 @@ impl FileRevlogComponent {
372347
self.queue.push(InternalEvent::Update(NeedsUpdate::DIFF));
373348
}
374349

375-
table_state.select(Some(new_selection));
376-
self.table_state.set(table_state);
350+
self.set_selection(new_selection);
351+
self.fetch_commits_if_needed()?;
377352

378-
needs_update
353+
Ok(())
354+
}
355+
356+
fn set_selection(&mut self, selection: usize) {
357+
let height_in_items =
358+
(self.current_height.get().saturating_sub(2)) / 2;
359+
360+
let offset = *self.table_state.get_mut().offset_mut();
361+
let min_offset = selection
362+
.saturating_sub(height_in_items.saturating_sub(1));
363+
364+
let new_offset = offset.clamp(min_offset, selection);
365+
366+
*self.table_state.get_mut().offset_mut() = new_offset;
367+
self.table_state.get_mut().select(Some(selection));
379368
}
380369

381-
fn set_open_selection(&mut self) {
382-
if let Some(selection) =
383-
self.open_request.as_ref().and_then(|req| req.selection)
384-
{
385-
let mut table_state = self.table_state.take();
386-
table_state.select(Some(selection));
387-
self.table_state.set(table_state);
370+
fn fetch_commits_if_needed(&mut self) -> Result<()> {
371+
let selection =
372+
self.table_state.get_mut().selected().unwrap_or(0);
373+
let offset = *self.table_state.get_mut().offset_mut();
374+
let height_in_items =
375+
(self.current_height.get().saturating_sub(2)) / 2;
376+
let new_max_offset =
377+
selection.saturating_add(height_in_items);
378+
379+
if self.items.needs_data(offset, new_max_offset) {
380+
self.fetch_commits(offset, new_max_offset)?;
388381
}
382+
383+
Ok(())
389384
}
390385

391386
fn get_selection(&self) -> Option<usize> {
@@ -423,10 +418,32 @@ impl FileRevlogComponent {
423418
.border_style(self.theme.block(true)),
424419
);
425420

426-
let mut table_state = self.table_state.take();
421+
let table_state = self.table_state.take();
422+
// We have to adjust the table state for drawing to account for the fact
423+
// that `self.items` not necessarily starts at index 0.
424+
//
425+
// When a user scrolls down, items outside of the current view are removed
426+
// when new data is fetched. Let’s have a look at an example: if the item at
427+
// index 50 is the first item in the current view and `self.items` has been
428+
// freshly fetched, the current offset is 50 and `self.items[0]` is the item
429+
// at index 50. Subtracting the current offset from the selected index
430+
// yields the correct index in `self.items`, in this case 0.
431+
let mut adjusted_table_state = TableState::default()
432+
.with_selected(table_state.selected().map(|selected| {
433+
selected.saturating_sub(self.items.index_offset())
434+
}))
435+
.with_offset(
436+
table_state
437+
.offset()
438+
.saturating_sub(self.items.index_offset()),
439+
);
427440

428441
f.render_widget(Clear, area);
429-
f.render_stateful_widget(table, area, &mut table_state);
442+
f.render_stateful_widget(
443+
table,
444+
area,
445+
&mut adjusted_table_state,
446+
);
430447

431448
draw_scrollbar(
432449
f,
@@ -545,36 +562,36 @@ impl Component for FileRevlogComponent {
545562
}
546563
} else if key_match(key, self.key_config.keys.move_up)
547564
{
548-
self.move_selection(ScrollType::Up);
565+
self.move_selection(ScrollType::Up)?;
549566
} else if key_match(
550567
key,
551568
self.key_config.keys.move_down,
552569
) {
553-
self.move_selection(ScrollType::Down);
570+
self.move_selection(ScrollType::Down)?;
554571
} else if key_match(
555572
key,
556573
self.key_config.keys.shift_up,
557574
) || key_match(
558575
key,
559576
self.key_config.keys.home,
560577
) {
561-
self.move_selection(ScrollType::Home);
578+
self.move_selection(ScrollType::Home)?;
562579
} else if key_match(
563580
key,
564581
self.key_config.keys.shift_down,
565582
) || key_match(
566583
key,
567584
self.key_config.keys.end,
568585
) {
569-
self.move_selection(ScrollType::End);
586+
self.move_selection(ScrollType::End)?;
570587
} else if key_match(key, self.key_config.keys.page_up)
571588
{
572-
self.move_selection(ScrollType::PageUp);
589+
self.move_selection(ScrollType::PageUp)?;
573590
} else if key_match(
574591
key,
575592
self.key_config.keys.page_down,
576593
) {
577-
self.move_selection(ScrollType::PageDown);
594+
self.move_selection(ScrollType::PageDown)?;
578595
}
579596
}
580597

0 commit comments

Comments
 (0)