Skip to content

Commit

Permalink
Add input validation to Go to line popup
Browse files Browse the repository at this point in the history
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
  • Loading branch information
andrea-berling authored and extrawurst committed Sep 17, 2024
1 parent e1291f0 commit 4855ca6
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 24 deletions.
4 changes: 2 additions & 2 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -675,8 +675,8 @@ impl App {
StackablePopupOpen::CompareCommits(param) => {
self.compare_commits_popup.open(param)?;
}
StackablePopupOpen::GotoLine => {
self.goto_line_popup.open();
StackablePopupOpen::GotoLine(param) => {
self.goto_line_popup.open(param);
}
}

Expand Down
8 changes: 7 additions & 1 deletion src/popups/blame_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ use ratatui::{
};
use std::path::Path;

use super::{goto_line::GotoLineContext, GotoLineOpen};

static NO_COMMIT_ID: &str = "0000000";
static NO_AUTHOR: &str = "<no author>";
static MIN_AUTHOR_WIDTH: usize = 3;
Expand Down Expand Up @@ -333,7 +335,11 @@ impl Component for BlameFilePopup {
self.hide_stacked(true);
self.visible = true;
self.queue.push(InternalEvent::OpenPopup(
StackablePopupOpen::GotoLine,
StackablePopupOpen::GotoLine(GotoLineOpen {
context: GotoLineContext {
max_line: self.get_max_line_number(),
},
}),
));
}

Expand Down
80 changes: 62 additions & 18 deletions src/popups/goto_line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{

use ratatui::{
layout::Rect,
style::{Color, Style},
widgets::{Block, Clear, Paragraph},
Frame,
};
Expand All @@ -19,27 +20,44 @@ use anyhow::Result;

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

#[derive(Debug)]
pub struct GotoLineContext {
pub max_line: usize,
}

#[derive(Debug)]
pub struct GotoLineOpen {
pub context: GotoLineContext,
}

pub struct GotoLinePopup {
visible: bool,
line: String,
input: String,
line_number: usize,
key_config: SharedKeyConfig,
queue: Queue,
theme: SharedTheme,
invalid_input: bool,
context: GotoLineContext,
}

impl GotoLinePopup {
pub fn new(env: &Environment) -> Self {
Self {
visible: false,
line: String::new(),
input: String::new(),
key_config: env.key_config.clone(),
queue: env.queue.clone(),
theme: env.theme.clone(),
invalid_input: false,
context: GotoLineContext { max_line: 0 },
line_number: 0,
}
}

pub fn open(&mut self) {
pub fn open(&mut self, open: GotoLineOpen) {
self.visible = true;
self.context = open.context;
}
}

Expand All @@ -63,41 +81,67 @@ impl Component for GotoLinePopup {
if let Event::Key(key) = event {
if key_match(key, self.key_config.keys.exit_popup) {
self.visible = false;
self.line.clear();
self.input.clear();
self.queue.push(InternalEvent::PopupStackPop);
} else if let KeyCode::Char(c) = key.code {
if c.is_ascii_digit() {
// I'd assume it's unusual for people to blame
// files with milions of lines
if self.line.len() < 6 {
self.line.push(c);
}
if c.is_ascii_digit() || c == '-' {
self.input.push(c);
}
} else if key.code == KeyCode::Backspace {
self.line.pop();
self.input.pop();
} else if key_match(key, self.key_config.keys.enter) {
self.visible = false;
if !self.line.is_empty() {
if self.invalid_input {
self.queue.push(InternalEvent::ShowErrorMsg(
format!("Invalid input: only numbers between -{0} and {0} (included) are allowed",self.context.max_line))
,
);
} else if !self.input.is_empty() {
self.queue.push(InternalEvent::GotoLine(
self.line.parse::<usize>()?,
self.line_number,
));
}
self.queue.push(InternalEvent::PopupStackPop);
self.line.clear();
self.input.clear();
self.invalid_input = false;
}
}
match self.input.parse::<isize>() {
Ok(input) => {
if input.unsigned_abs() > self.context.max_line {
self.invalid_input = true;
} else {
self.invalid_input = false;
self.line_number = if input > 0 {
input.unsigned_abs()
} else {
self.context.max_line
- input.unsigned_abs()
}
}
}
Err(_) => {
if !self.input.is_empty() {
self.invalid_input = true;
}
}
return Ok(EventState::Consumed);
}
return Ok(EventState::Consumed);
}

Ok(EventState::NotConsumed)
}
}

impl DrawableComponent for GotoLinePopup {
fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> {
if self.is_visible() {
let input = Paragraph::new(self.line.as_str())
.style(self.theme.text(true, false))
let style = if self.invalid_input {
Style::default().fg(Color::Red)
} else {
self.theme.text(true, false)
};
let input = Paragraph::new(self.input.as_str())
.style(style)
.block(Block::bordered().title("Go to Line"));

let input_area = ui::centered_rect_absolute(15, 3, area);
Expand Down
2 changes: 1 addition & 1 deletion src/popups/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub use externaleditor::ExternalEditorPopup;
pub use fetch::FetchPopup;
pub use file_revlog::{FileRevOpen, FileRevlogPopup};
pub use fuzzy_find::FuzzyFindPopup;
pub use goto_line::GotoLinePopup;
pub use goto_line::{GotoLineOpen, GotoLinePopup};
pub use help::HelpPopup;
pub use inspect_commit::{InspectCommitOpen, InspectCommitPopup};
pub use log_search::LogSearchPopupPopup;
Expand Down
4 changes: 2 additions & 2 deletions src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
components::FuzzyFinderTarget,
popups::{
AppOption, BlameFileOpen, FileRevOpen, FileTreeOpen,
InspectCommitOpen,
GotoLineOpen, InspectCommitOpen,
},
tabs::StashingOptions,
};
Expand Down Expand Up @@ -69,7 +69,7 @@ pub enum StackablePopupOpen {
///
CompareCommits(InspectCommitOpen),
///
GotoLine,
GotoLine(GotoLineOpen),
}

pub enum AppTabs {
Expand Down

0 comments on commit 4855ca6

Please sign in to comment.