Skip to content

Commit 706cdf9

Browse files
authored
feat: file and status tab support pageup and pagedown (#2496)
1 parent ee5c243 commit 706cdf9

File tree

5 files changed

+99
-4
lines changed

5 files changed

+99
-4
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
* execute git-hooks directly if possible (on *nix) else use sh instead of bash (without reading SHELL variable) [[@Joshix](https://github.com/Joshix-1)] ([#2483](https://github.com/extrawurst/gitui/pull/2483))
1010

1111
### Added
12+
* Files and status tab support pageUp and pageDown [[@fatpandac](https://github.com/fatpandac)] ([#1951](https://github.com/extrawurst/gitui/issues/1951))
1213
* support loading custom syntax highlighting themes from a file [[@acuteenvy](https://github.com/acuteenvy)] ([#2565](https://github.com/gitui-org/gitui/pull/2565))
1314
* Select syntax highlighting theme out of the defaults from syntect [[@vasilismanol](https://github.com/vasilismanol)] ([#1931](https://github.com/extrawurst/gitui/issues/1931))
1415
* new command-line option to override the default log file path (`--logfile`) [[@acuteenvy](https://github.com/acuteenvy)] ([#2539](https://github.com/gitui-org/gitui/pull/2539))

filetreelist/src/filetree.rs

+53-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{
22
error::Result, filetreeitems::FileTreeItems,
33
tree_iter::TreeIterator, TreeItemInfo,
44
};
5-
use std::{collections::BTreeSet, path::Path};
5+
use std::{cell::Cell, collections::BTreeSet, path::Path};
66

77
///
88
#[derive(Copy, Clone, Debug)]
@@ -30,6 +30,7 @@ pub struct FileTree {
3030
selection: Option<usize>,
3131
// caches the absolute selection translated to visual index
3232
visual_selection: Option<VisualSelection>,
33+
pub window_height: Cell<Option<usize>>,
3334
}
3435

3536
impl FileTree {
@@ -42,6 +43,7 @@ impl FileTree {
4243
items: FileTreeItems::new(list, collapsed)?,
4344
selection: if list.is_empty() { None } else { Some(0) },
4445
visual_selection: None,
46+
window_height: None.into(),
4547
};
4648
new_self.visual_selection = new_self.calc_visual_selection();
4749

@@ -112,6 +114,18 @@ impl FileTree {
112114
}
113115
}
114116

117+
fn selection_page_updown(
118+
&self,
119+
range: impl Iterator<Item = usize>,
120+
) -> Option<usize> {
121+
let page_size = self.window_height.get().unwrap_or(0);
122+
123+
range
124+
.filter(|index| self.is_visible_index(*index))
125+
.take(page_size)
126+
.last()
127+
}
128+
115129
///
116130
pub fn move_selection(&mut self, dir: MoveSelection) -> bool {
117131
self.selection.is_some_and(|selection| {
@@ -130,9 +144,13 @@ impl FileTree {
130144
Self::selection_start(selection)
131145
}
132146
MoveSelection::End => self.selection_end(selection),
133-
MoveSelection::PageDown | MoveSelection::PageUp => {
134-
None
147+
MoveSelection::PageUp => {
148+
self.selection_page_updown((0..=selection).rev())
135149
}
150+
MoveSelection::PageDown => self
151+
.selection_page_updown(
152+
selection..(self.items.len()),
153+
),
136154
};
137155

138156
let changed_index =
@@ -514,4 +532,36 @@ mod test {
514532
assert_eq!(s.count, 3);
515533
assert_eq!(s.index, 2);
516534
}
535+
536+
#[test]
537+
fn test_selection_page_updown() {
538+
let items = vec![
539+
Path::new("a/b/c"), //
540+
Path::new("a/b/c2"), //
541+
Path::new("a/d"), //
542+
Path::new("a/e"), //
543+
];
544+
545+
//0 a/
546+
//1 b/
547+
//2 c
548+
//3 c2
549+
//4 d
550+
//5 e
551+
552+
let mut tree =
553+
FileTree::new(&items, &BTreeSet::new()).unwrap();
554+
555+
tree.window_height.set(Some(3));
556+
557+
tree.selection = Some(0);
558+
assert!(tree.move_selection(MoveSelection::PageDown));
559+
assert_eq!(tree.selection, Some(2));
560+
assert!(tree.move_selection(MoveSelection::PageDown));
561+
assert_eq!(tree.selection, Some(4));
562+
assert!(tree.move_selection(MoveSelection::PageUp));
563+
assert_eq!(tree.selection, Some(2));
564+
assert!(tree.move_selection(MoveSelection::PageUp));
565+
assert_eq!(tree.selection, Some(0));
566+
}
517567
}

src/components/revision_files.rs

+2
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ impl RevisionFilesComponent {
275275
let tree_height = usize::from(area.height.saturating_sub(2));
276276
let tree_width = usize::from(area.width);
277277

278+
self.tree.window_height.set(Some(tree_height));
279+
278280
self.tree.visual_selection().map_or_else(
279281
|| {
280282
self.scroll.reset();

src/components/status_tree.rs

+10
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ impl DrawableComponent for StatusTreeComponent {
351351
.map(|idx| idx.saturating_sub(selection_offset))
352352
.unwrap_or_default();
353353
let tree_height = r.height.saturating_sub(2) as usize;
354+
self.tree.window_height.set(Some(tree_height));
354355

355356
self.scroll_top.set(ui::calc_scroll_top(
356357
self.scroll_top.get(),
@@ -504,6 +505,15 @@ impl Component for StatusTreeComponent {
504505
|| key_match(e, self.key_config.keys.shift_down)
505506
{
506507
Ok(self.move_selection(MoveSelection::End).into())
508+
} else if key_match(e, self.key_config.keys.page_up) {
509+
Ok(self
510+
.move_selection(MoveSelection::PageUp)
511+
.into())
512+
} else if key_match(e, self.key_config.keys.page_down)
513+
{
514+
Ok(self
515+
.move_selection(MoveSelection::PageDown)
516+
.into())
507517
} else if key_match(e, self.key_config.keys.move_left)
508518
{
509519
Ok(self

src/components/utils/statustree.rs

+33-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use super::filetree::{
33
};
44
use anyhow::Result;
55
use asyncgit::StatusItem;
6-
use std::{cmp, collections::BTreeSet};
6+
use std::{cell::Cell, cmp, collections::BTreeSet};
77

88
//TODO: use new `filetreelist` crate
99

@@ -16,6 +16,8 @@ pub struct StatusTree {
1616
// some folders may be folded up, this allows jumping
1717
// over folders which are folded into their parent
1818
pub available_selections: Vec<usize>,
19+
20+
pub window_height: Cell<Option<usize>>,
1921
}
2022

2123
///
@@ -27,6 +29,8 @@ pub enum MoveSelection {
2729
Right,
2830
Home,
2931
End,
32+
PageDown,
33+
PageUp,
3034
}
3135

3236
#[derive(Copy, Clone, Debug)]
@@ -143,6 +147,15 @@ impl StatusTree {
143147
}
144148
MoveSelection::Home => SelectionChange::new(0, false),
145149
MoveSelection::End => self.selection_end(),
150+
MoveSelection::PageUp => self.selection_page_updown(
151+
selection,
152+
(0..=selection).rev(),
153+
),
154+
MoveSelection::PageDown => self
155+
.selection_page_updown(
156+
selection,
157+
selection..(self.tree.len()),
158+
),
146159
};
147160

148161
let changed_index =
@@ -283,6 +296,25 @@ impl StatusTree {
283296
SelectionChange::new(new_index, false)
284297
}
285298

299+
fn selection_page_updown(
300+
&self,
301+
current_index: usize,
302+
range: impl Iterator<Item = usize>,
303+
) -> SelectionChange {
304+
let page_size = self.window_height.get().unwrap_or(0);
305+
306+
let new_index = range
307+
.filter(|index| {
308+
self.available_selections.contains(index)
309+
&& self.is_visible_index(*index)
310+
})
311+
.take(page_size)
312+
.last()
313+
.unwrap_or(current_index);
314+
315+
SelectionChange::new(new_index, false)
316+
}
317+
286318
fn is_visible_index(&self, idx: usize) -> bool {
287319
self.tree[idx].info.visible
288320
}

0 commit comments

Comments
 (0)