Skip to content

Commit 7a5bda9

Browse files
committed
fix: scroll horizontally when navigating through highlights
1 parent f3b522b commit 7a5bda9

8 files changed

Lines changed: 123 additions & 23 deletions

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ mod output_widget;
88
mod props;
99
mod rura;
1010
mod rura_widget;
11+
mod search_widget;
1112
mod theme;
1213
mod uicmd;
13-
mod search_widget;
1414

1515
use crate::app::App;
1616
use crate::config::load_config;

src/output_widget.rs

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ pub struct OutputWidget {
3030
key_bindings: KeyBindings,
3131
output_height: u16,
3232
error_pane_placement: ErrorPanePlacement,
33-
visible_range: Range<usize>,
33+
visible_range_x: Range<usize>,
34+
visible_range_y: Range<usize>,
3435
highlight: String,
3536
highlight_positions: Vec<(usize, Range<usize>)>,
3637
highlight_index: usize,
@@ -56,7 +57,8 @@ impl OutputWidget {
5657
error_pane_placement,
5758
highlight: String::new(),
5859
highlight_positions: vec![],
59-
visible_range: 0..0,
60+
visible_range_x: 0..0,
61+
visible_range_y: 0..0,
6062
highlight_index: 0,
6163
}
6264
}
@@ -73,10 +75,9 @@ impl OutputWidget {
7375
pub fn highlight_next(&mut self) {
7476
if !self.highlight_positions.is_empty() {
7577
self.highlight_index = (self.highlight_index + 1) % self.highlight_positions.len();
76-
let (line, _) = self.highlight_positions[self.highlight_index];
77-
if !self.visible_range.contains(&line) {
78-
self.offset.y = line.saturating_sub(self.visible_range.len() / 2);
79-
}
78+
79+
let (line, range) = self.highlight_positions[self.highlight_index].clone();
80+
self.adjust_viewport_for_highlight(line, range);
8081
}
8182
}
8283

@@ -88,11 +89,8 @@ impl OutputWidget {
8889
self.highlight_index = self.highlight_index.saturating_sub(1);
8990
}
9091

91-
let (line, _) = self.highlight_positions[self.highlight_index];
92-
93-
if !self.visible_range.contains(&line) {
94-
self.offset.y = line.saturating_sub(self.visible_range.len() / 2);
95-
}
92+
let (line, range) = self.highlight_positions[self.highlight_index].clone();
93+
self.adjust_viewport_for_highlight(line, range);
9694
}
9795
}
9896

@@ -132,7 +130,7 @@ impl OutputWidget {
132130
// find the first match in the visible range otherwise start from the beginning
133131
match positions
134132
.iter()
135-
.find_position(|(line, _range)| line >= &self.visible_range.start)
133+
.find_position(|(line, _range)| line >= &self.visible_range_y.start)
136134
{
137135
Some((z, _)) => self.highlight_index = z,
138136
None => self.highlight_index = 0,
@@ -142,16 +140,29 @@ impl OutputWidget {
142140

143141
// focus on the first match
144142
if !self.highlight_positions.is_empty() {
145-
let (line, _) = self.highlight_positions[self.highlight_index];
146-
if !self.visible_range.contains(&line) {
147-
self.offset.y = line.saturating_sub(self.visible_range.len() / 2);
148-
}
143+
let (line, range) = self.highlight_positions[self.highlight_index].clone();
144+
self.adjust_viewport_for_highlight(line, range);
149145
}
150146
} else {
151147
self.highlight_positions = vec![];
152148
}
153149
}
154150

151+
fn adjust_viewport_for_highlight(&mut self, line_num: usize, range: Range<usize>) {
152+
if !self.visible_range_y.contains(&line_num) {
153+
self.offset.y = line_num.saturating_sub(self.visible_range_y.len() / 2);
154+
}
155+
156+
if !self.visible_range_x.contains(&range.start) {
157+
if range.start < self.visible_range_x.len() {
158+
// scroll fully to the left if highligh is in the first "horizontal "page"
159+
self.offset.x = 0;
160+
} else {
161+
self.offset.x = range.start.saturating_sub(self.visible_range_x.len() / 4);
162+
}
163+
}
164+
}
165+
155166
pub fn output_len(&self) -> usize {
156167
self.output.lines.len()
157168
}
@@ -302,19 +313,20 @@ impl Widget for &mut OutputWidget {
302313

303314
let height = output_content_area.height.min(output_len as u16);
304315

305-
let visible_range: Range<usize> = if height >= output_len as u16 {
316+
let visible_lines: Range<usize> = if height >= output_len as u16 {
306317
0..output_len
307318
} else {
308319
let from = (self.offset.y as usize).min(output_len);
309320
let to = (self.offset.y as usize + height as usize).min(output_len);
310321
from..to
311322
};
312323

313-
self.visible_range = visible_range.clone();
324+
self.visible_range_x = self.offset.x..self.offset.x + output_content_area.width as usize;
325+
self.visible_range_y = self.offset.y..self.offset.y + output_content_area.height as usize;
314326

315327
let output = self.main_output();
316328

317-
let line_nums = visible_range
329+
let line_nums = visible_lines
318330
.clone()
319331
.flat_map(|i| {
320332
let visual_line_count = if self.wrap {
@@ -336,12 +348,12 @@ impl Widget for &mut OutputWidget {
336348

337349
let output_par = {
338350
let mut par = if !self.highlight_positions.is_empty() {
339-
let lines = (&output.lines[visible_range.clone()])
351+
let lines = (&output.lines[visible_lines.clone()])
340352
.iter()
341353
.enumerate()
342354
.map(|(line_index, line)| {
343355
// todo simplify
344-
let logical_line_num = line_index + visible_range.start;
356+
let logical_line_num = line_index + visible_lines.start;
345357

346358
let (current_match_line, current_match_range) =
347359
self.highlight_positions.get(self.highlight_index).unwrap();
@@ -386,7 +398,7 @@ impl Widget for &mut OutputWidget {
386398
.scroll((0, self.offset.x as u16)) // todo
387399
.block(Block::default())
388400
} else {
389-
Paragraph::new(output.lines[visible_range].join("\n"))
401+
Paragraph::new(output.lines[visible_lines].join("\n"))
390402
.scroll((0, self.offset.x as u16)) // todo
391403
.block(Block::default())
392404
};
@@ -619,6 +631,34 @@ mod tests {
619631
assert_snapshot!("highlight another highlight", terminal.backend());
620632
}
621633

634+
#[test]
635+
fn highlighting_horizontal_scroll() {
636+
let mut terminal = Terminal::new(TestBackend::new(15, 6)).unwrap();
637+
638+
let mut widget = OutputWidget::default();
639+
640+
let out = vec![
641+
" hl1 ",
642+
" hl2 hl3 ",
643+
" hl4 hl5 ",
644+
];
645+
646+
widget.handle_command_output(Output::ok_stdin(&out.join("\n")));
647+
terminal
648+
.draw(|frame| widget.render(frame.area(), frame.buffer_mut()))
649+
.unwrap();
650+
assert_snapshot!("highlight horizontal base", terminal.backend());
651+
652+
widget.highlight("hl", false);
653+
for i in 1..6 {
654+
widget.highlight_next();
655+
terminal
656+
.draw(|frame| widget.render(frame.area(), frame.buffer_mut()))
657+
.unwrap();
658+
assert_snapshot!(format!("highlight {i}"), terminal.backend());
659+
}
660+
}
661+
622662
#[test]
623663
fn split_line_into_parts_by_ranges_test() {
624664
let str = "01234567890123456789";
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
source: src/output_widget.rs
3+
expression: terminal.backend()
4+
---
5+
"1 "
6+
"2 hl2 hl3 "
7+
"3 hl5 "
8+
" "
9+
" "
10+
" "
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
source: src/output_widget.rs
3+
expression: terminal.backend()
4+
---
5+
"1 "
6+
"2 hl2 hl3 "
7+
"3 hl5 "
8+
" "
9+
" "
10+
" "
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
source: src/output_widget.rs
3+
expression: terminal.backend()
4+
---
5+
"1 hl1 "
6+
"2 "
7+
"3 hl4 "
8+
" "
9+
" "
10+
" "
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
source: src/output_widget.rs
3+
expression: terminal.backend()
4+
---
5+
"1 "
6+
"2 l2 hl3 "
7+
"3 hl5 "
8+
" "
9+
" "
10+
" "
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
source: src/output_widget.rs
3+
expression: terminal.backend()
4+
---
5+
"1 hl1 "
6+
"2 "
7+
"3 hl4 "
8+
" "
9+
" "
10+
" "
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
source: src/output_widget.rs
3+
expression: terminal.backend()
4+
---
5+
"1 hl1 "
6+
"2 "
7+
"3 hl4 "
8+
" "
9+
" "
10+
" "

0 commit comments

Comments
 (0)