Skip to content

Commit 8757559

Browse files
Add input validation to Go to line popup
Add a context struct for the go to line popup to keep the max line number allowed Add support for negative values for the go to line popup input (go to the -n-th to last line) Make the go to line input box red when invalid values are provided Add an error message to the Go to line popup when invalid values are used Allow arbitrarily large values in the Go to line input box
1 parent ccdf0f6 commit 8757559

File tree

5 files changed

+74
-24
lines changed

5 files changed

+74
-24
lines changed

src/app.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -675,8 +675,8 @@ impl App {
675675
StackablePopupOpen::CompareCommits(param) => {
676676
self.compare_commits_popup.open(param)?;
677677
}
678-
StackablePopupOpen::GotoLine => {
679-
self.goto_line_popup.open();
678+
StackablePopupOpen::GotoLine(param) => {
679+
self.goto_line_popup.open(param);
680680
}
681681
}
682682

src/popups/blame_file.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ use ratatui::{
3030
};
3131
use std::path::Path;
3232

33+
use super::{goto_line::GotoLineContext, GotoLineOpen};
34+
3335
static NO_COMMIT_ID: &str = "0000000";
3436
static NO_AUTHOR: &str = "<no author>";
3537
static MIN_AUTHOR_WIDTH: usize = 3;
@@ -333,7 +335,11 @@ impl Component for BlameFilePopup {
333335
self.hide_stacked(true);
334336
self.visible = true;
335337
self.queue.push(InternalEvent::OpenPopup(
336-
StackablePopupOpen::GotoLine,
338+
StackablePopupOpen::GotoLine(GotoLineOpen {
339+
context: GotoLineContext {
340+
max_line: self.get_max_line_number(),
341+
},
342+
}),
337343
));
338344
}
339345

src/popups/goto_line.rs

+62-18
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::{
1111

1212
use ratatui::{
1313
layout::Rect,
14+
style::{Color, Style},
1415
widgets::{Block, Clear, Paragraph},
1516
Frame,
1617
};
@@ -19,27 +20,44 @@ use anyhow::Result;
1920

2021
use crossterm::event::{Event, KeyCode};
2122

23+
#[derive(Debug)]
24+
pub struct GotoLineContext {
25+
pub max_line: usize,
26+
}
27+
28+
#[derive(Debug)]
29+
pub struct GotoLineOpen {
30+
pub context: GotoLineContext,
31+
}
32+
2233
pub struct GotoLinePopup {
2334
visible: bool,
24-
line: String,
35+
input: String,
36+
line_number: usize,
2537
key_config: SharedKeyConfig,
2638
queue: Queue,
2739
theme: SharedTheme,
40+
invalid_input: bool,
41+
context: GotoLineContext,
2842
}
2943

3044
impl GotoLinePopup {
3145
pub fn new(env: &Environment) -> Self {
3246
Self {
3347
visible: false,
34-
line: String::new(),
48+
input: String::new(),
3549
key_config: env.key_config.clone(),
3650
queue: env.queue.clone(),
3751
theme: env.theme.clone(),
52+
invalid_input: false,
53+
context: GotoLineContext { max_line: 0 },
54+
line_number: 0,
3855
}
3956
}
4057

41-
pub fn open(&mut self) {
58+
pub fn open(&mut self, open: GotoLineOpen) {
4259
self.visible = true;
60+
self.context = open.context;
4361
}
4462
}
4563

@@ -63,41 +81,67 @@ impl Component for GotoLinePopup {
6381
if let Event::Key(key) = event {
6482
if key_match(key, self.key_config.keys.exit_popup) {
6583
self.visible = false;
66-
self.line.clear();
84+
self.input.clear();
6785
self.queue.push(InternalEvent::PopupStackPop);
6886
} else if let KeyCode::Char(c) = key.code {
69-
if c.is_ascii_digit() {
70-
// I'd assume it's unusual for people to blame
71-
// files with milions of lines
72-
if self.line.len() < 6 {
73-
self.line.push(c);
74-
}
87+
if c.is_ascii_digit() || c == '-' {
88+
self.input.push(c);
7589
}
7690
} else if key.code == KeyCode::Backspace {
77-
self.line.pop();
91+
self.input.pop();
7892
} else if key_match(key, self.key_config.keys.enter) {
7993
self.visible = false;
80-
if !self.line.is_empty() {
94+
if self.invalid_input {
95+
self.queue.push(InternalEvent::ShowErrorMsg(
96+
format!("Invalid input: only numbers between -{0} and {0} (included) are allowed",self.context.max_line))
97+
,
98+
);
99+
} else if !self.input.is_empty() {
81100
self.queue.push(InternalEvent::GotoLine(
82-
self.line.parse::<usize>()?,
101+
self.line_number,
83102
));
84103
}
85104
self.queue.push(InternalEvent::PopupStackPop);
86-
self.line.clear();
105+
self.input.clear();
106+
self.invalid_input = false;
107+
}
108+
}
109+
match self.input.parse::<isize>() {
110+
Ok(input) => {
111+
if input.unsigned_abs() > self.context.max_line {
112+
self.invalid_input = true;
113+
} else {
114+
self.invalid_input = false;
115+
self.line_number = if input > 0 {
116+
input.unsigned_abs()
117+
} else {
118+
self.context.max_line
119+
- input.unsigned_abs()
120+
}
121+
}
122+
}
123+
Err(_) => {
124+
if !self.input.is_empty() {
125+
self.invalid_input = true;
126+
}
87127
}
88-
return Ok(EventState::Consumed);
89128
}
129+
return Ok(EventState::Consumed);
90130
}
91-
92131
Ok(EventState::NotConsumed)
93132
}
94133
}
95134

96135
impl DrawableComponent for GotoLinePopup {
97136
fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> {
98137
if self.is_visible() {
99-
let input = Paragraph::new(self.line.as_str())
100-
.style(self.theme.text(true, false))
138+
let style = if self.invalid_input {
139+
Style::default().fg(Color::Red)
140+
} else {
141+
self.theme.text(true, false)
142+
};
143+
let input = Paragraph::new(self.input.as_str())
144+
.style(style)
101145
.block(Block::bordered().title("Go to Line"));
102146

103147
let input_area = ui::centered_rect_absolute(15, 3, area);

src/popups/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ pub use externaleditor::ExternalEditorPopup;
3535
pub use fetch::FetchPopup;
3636
pub use file_revlog::{FileRevOpen, FileRevlogPopup};
3737
pub use fuzzy_find::FuzzyFindPopup;
38-
pub use goto_line::GotoLinePopup;
38+
pub use goto_line::{GotoLineOpen, GotoLinePopup};
3939
pub use help::HelpPopup;
4040
pub use inspect_commit::{InspectCommitOpen, InspectCommitPopup};
4141
pub use log_search::LogSearchPopupPopup;

src/queue.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{
22
components::FuzzyFinderTarget,
33
popups::{
44
AppOption, BlameFileOpen, FileRevOpen, FileTreeOpen,
5-
InspectCommitOpen,
5+
GotoLineOpen, InspectCommitOpen,
66
},
77
tabs::StashingOptions,
88
};
@@ -69,7 +69,7 @@ pub enum StackablePopupOpen {
6969
///
7070
CompareCommits(InspectCommitOpen),
7171
///
72-
GotoLine,
72+
GotoLine(GotoLineOpen),
7373
}
7474

7575
pub enum AppTabs {

0 commit comments

Comments
 (0)