Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go to idx #330

Merged
merged 3 commits into from
Mar 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions bacon.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,13 @@ analyzer = "cargo_json"
command = ["cargo", "check"]
need_stdout = false
default_watch = false
watch = ["src"]

[jobs.check-all]
command = [
"cargo", "check",
"--all-targets",
]
need_stdout = false
allow_warnings = true

[jobs.nightly]
command = [
Expand Down
2 changes: 0 additions & 2 deletions src/analysis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ mod eslint;
mod item_accumulator;
mod line_analysis;
mod line_analyzer;
mod line_pattern;
mod line_type;
mod nextest;
mod python;
Expand All @@ -18,7 +17,6 @@ pub use {
item_accumulator::*,
line_analysis::*,
line_analyzer::*,
line_pattern::*,
line_type::*,
stats::*,
};
1 change: 1 addition & 0 deletions src/conf/keybindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ impl Default for KeyBindings {
bindings.set(key!(i), JobRef::Initial);
bindings.set(key!(p), Internal::TogglePause);
bindings.set(key!('/'), Internal::FocusSearch);
bindings.set(key!(':'), Internal::FocusGoto);
bindings.set(key!(enter), Internal::Validate);
bindings.set(key!(tab), Internal::NextMatch);
bindings.set(key!(backtab), Internal::PreviousMatch);
Expand Down
4 changes: 4 additions & 0 deletions src/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub enum Internal {
Back, // leave help, clear search, go to previous job, leave, etc.
CopyUnstyledOutput,
FocusSearch,
FocusGoto,
Help,
NextMatch,
NoOp, // no operation, can be used to clear a binding
Expand Down Expand Up @@ -53,6 +54,7 @@ impl Internal {
Self::Back => "back to previous page or job".to_string(),
Self::CopyUnstyledOutput => "copy current job's output".to_string(),
Self::FocusSearch => "focus search".to_string(),
Self::FocusGoto => "focus goto".to_string(),
Self::Help => "help".to_string(),
Self::NextMatch => "next match".to_string(),
Self::NoOp => "no operation".to_string(),
Expand Down Expand Up @@ -100,6 +102,7 @@ impl fmt::Display for Internal {
Self::ToggleWrap => write!(f, "toggle-wrap"),
Self::Unpause => write!(f, "unpause"),
Self::FocusSearch => write!(f, "focus-search"),
Self::FocusGoto => write!(f, "focus-goto"),
Self::Validate => write!(f, "validate"),
Self::NextMatch => write!(f, "next-match"),
Self::PreviousMatch => write!(f, "previous-match"),
Expand Down Expand Up @@ -139,6 +142,7 @@ impl std::str::FromStr for Internal {
"unpause" => Ok(Self::Unpause),
"toggle-pause" => Ok(Self::TogglePause),
"focus-search" => Ok(Self::FocusSearch),
"focus-goto" => Ok(Self::FocusGoto),
"validate" => Ok(Self::Validate),
"next-match" => Ok(Self::NextMatch),
"previous-match" => Ok(Self::PreviousMatch),
Expand Down
25 changes: 25 additions & 0 deletions src/search/goto_idx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use crate::*;

pub fn search_item_idx<'i, I>(
idx: usize,
lines: I,
) -> Vec<Found>
where
I: IntoIterator<Item = &'i Line>,
{
for (line_idx, line) in lines.into_iter().enumerate() {
if line.item_idx == idx && !line.content.strings.is_empty() {
let end_byte_in_string = line.content.strings[0].raw.len();
return vec![Found {
line_idx,
trange: TRange {
string_idx: 0,
start_byte_in_string: 0,
end_byte_in_string,
},
continued: None,
}];
}
}
vec![]
}
File renamed without changes.
51 changes: 51 additions & 0 deletions src/search/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
mod goto_idx;
mod line_pattern;
mod search_pattern;

pub use {
goto_idx::*,
line_pattern::*,
search_pattern::*,
};

use crate::*;

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SearchMode {
Pattern,
ItemIdx,
}

/// position in a [TLine] of a found pattern
#[derive(Debug, PartialEq, Eq)]
pub struct Found {
/// The index of the first line containing the pattern
pub line_idx: usize,
/// The range of the pattern in the line
pub trange: TRange,
/// If the pattern goes over a line wrap, the range of the pattern in the next line
pub continued: Option<TRange>,
}

pub const CSI_FOUND: &str = "\u{1b}[1m\u{1b}[38;5;208m"; // bold, orange foreground
pub const CSI_FOUND_SELECTED: &str = "\u{1b}[1m\u{1b}[30m\u{1b}[48;5;208m"; // bold, orange background

pub enum Search {
Pattern(Pattern),
ItemIdx(usize),
}

impl Search {
pub fn search_lines<'i, I>(
&self,
lines: I,
) -> Vec<Found>
where
I: IntoIterator<Item = &'i Line>,
{
match self {
Self::Pattern(pattern) => pattern.search_lines(lines),
Self::ItemIdx(idx) => search_item_idx(*idx, lines),
}
}
}
14 changes: 0 additions & 14 deletions src/search.rs → src/search/search_pattern.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,9 @@
use crate::*;

/// position in a [TLine] of a found pattern
#[derive(Debug, PartialEq, Eq)]
pub struct Found {
/// The index of the first line containing the pattern
pub line_idx: usize,
/// The range of the pattern in the line
pub trange: TRange,
/// If the pattern goes over a line wrap, the range of the pattern in the next line
pub continued: Option<TRange>,
}

pub struct Pattern {
pub pattern: String, // might change later
}

pub const CSI_FOUND: &str = "\u{1b}[1m\u{1b}[38;5;208m"; // bold, orange foreground
pub const CSI_FOUND_SELECTED: &str = "\u{1b}[1m\u{1b}[30m\u{1b}[48;5;208m"; // bold, orange background

impl Pattern {
// Current limitations:
// - a match can't span over more than 2 lines. This is probably fine.
Expand Down
3 changes: 3 additions & 0 deletions src/tui/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@ fn run_mission(
Internal::FocusSearch => {
state.focus_search();
}
Internal::FocusGoto => {
state.focus_goto();
}
Internal::Help => {
state.toggle_help();
}
Expand Down
18 changes: 12 additions & 6 deletions src/tui/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,11 @@ impl<'s> AppState<'s> {
})
}
pub fn focus_search(&mut self) {
self.search.set_focus(true);
self.search.focus_with_mode(SearchMode::Pattern);
self.show_selected_found();
}
pub fn focus_goto(&mut self) {
self.search.focus_with_mode(SearchMode::ItemIdx);
self.show_selected_found();
}
// Handle the "back" operation, return true if it did (thus consuming the action)
Expand Down Expand Up @@ -178,7 +182,7 @@ impl<'s> AppState<'s> {
// Handle the "validate" operation, return true if it did (thus consuming the action)
pub fn validate(&mut self) -> bool {
if self.search.focused() {
self.search.set_focus(false);
self.search.unfocus();
true
} else {
false
Expand Down Expand Up @@ -206,9 +210,9 @@ impl<'s> AppState<'s> {
return;
}
let founds = if self.search.input_has_content() {
let pattern = self.search.pattern();
let search = self.search.search();
let lines = self.lines_to_draw();
pattern.search_lines(lines)
search.search_lines(lines)
} else {
Vec::new()
};
Expand All @@ -226,8 +230,8 @@ impl<'s> AppState<'s> {
// account as we're only adding lines in the raw output where there's
// no filtering
let lines = self.lines_to_draw_unfiltered();
let pattern = self.search.pattern();
let new_founds = pattern.search_lines(&lines[line_count_before..]);
let search = self.search.search();
let new_founds = search.search_lines(&lines[line_count_before..]);
self.search.extend_founds(new_founds);
}
pub fn add_line(
Expand Down Expand Up @@ -464,6 +468,8 @@ impl<'s> AppState<'s> {
self.summary ^= true;
self.try_scroll_to_last_top_item();
self.search.touch();
self.update_search();
self.show_selected_found();
}
pub fn toggle_backtrace(
&mut self,
Expand Down
53 changes: 45 additions & 8 deletions src/tui/search_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use {

/// Search related state, part of the app state
pub struct SearchState {
mode: SearchMode,
/// the search input field
input: InputField,
/// whether the app state is up to date with the search
Expand All @@ -23,6 +24,7 @@ impl Default for SearchState {
search_input.set_focus(false);
let founds = Default::default();
Self {
mode: SearchMode::Pattern,
input: search_input,
up_to_date: true,
founds,
Expand All @@ -48,11 +50,18 @@ impl SearchState {
pub fn must_be_drawn(&self) -> bool {
self.focused() || !self.input.is_empty()
}
pub fn set_focus(
pub fn focus_with_mode(
&mut self,
f: bool,
mode: SearchMode,
) {
self.input.set_focus(f);
if mode != self.mode {
self.input.clear();
}
self.mode = mode;
self.input.set_focus(true);
}
pub fn unfocus(&mut self) {
self.input.set_focus(false);
}
pub fn focused(&self) -> bool {
self.input.focused()
Expand Down Expand Up @@ -96,9 +105,25 @@ impl SearchState {
}
false
}
pub fn pattern(&self) -> Pattern {
Pattern {
pattern: self.input.get_content(),
pub fn search(&self) -> Search {
match self.mode {
SearchMode::Pattern => Search::Pattern(Pattern {
pattern: self.input.get_content(),
}),
SearchMode::ItemIdx => Search::ItemIdx(self.input.get_content().parse().unwrap_or(0)),
}
}
pub fn is_invalid(&self) -> bool {
match self.mode {
SearchMode::Pattern => false,
SearchMode::ItemIdx => {
if self.input.is_empty() {
false
} else {
let n = self.input.get_content().parse::<usize>();
n.is_err()
}
}
}
}
pub fn set_founds(
Expand Down Expand Up @@ -137,7 +162,15 @@ impl SearchState {
width: u16, // must be > 1
) -> Result<()> {
goto_line(w, y)?;
draw(w, CSI_FOUND, "/")?;
draw(
w,
CSI_FOUND,
if self.mode == SearchMode::ItemIdx {
":"
} else {
"/"
},
)?;
self.input.change_area(x + 1, y, width - 1);
self.input.display_on(w)?;
Ok(())
Expand All @@ -148,7 +181,11 @@ impl SearchState {
) {
if self.input_has_content() {
if self.founds.is_empty() {
t_line.add_tstring(CSI_FOUND, "no match");
if self.mode == SearchMode::ItemIdx && self.is_invalid() {
t_line.add_tstring(CSI_FOUND, "integer expected");
} else {
t_line.add_tstring(CSI_FOUND, "no match");
}
} else {
t_line.add_tstring(
CSI_FOUND,
Expand Down