errors
This commit is contained in:
parent
d8e445b5f7
commit
4a7817a239
4 changed files with 82 additions and 8 deletions
|
|
@ -1,20 +1,20 @@
|
||||||
use ratatui_themes::{Theme, ThemeName};
|
use ratatui_themes::{Theme, ThemeName};
|
||||||
use regex::bytes::Regex;
|
|
||||||
use std::{
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
fs::{self, DirEntry},
|
fs::{self, DirEntry},
|
||||||
io,
|
io,
|
||||||
ops::ControlFlow,
|
ops::ControlFlow,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::exit,
|
process::exit,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
|
time::Instant,
|
||||||
};
|
};
|
||||||
use tui_widget_list::{ListBuilder, ListView};
|
use tui_widget_list::{ListBuilder, ListView};
|
||||||
|
|
||||||
use crate::tui::{
|
use crate::tui::{
|
||||||
filter::FilterKind,
|
filter::FilterKind,
|
||||||
log_viewer::{InputState, InputTarget, LogViewer},
|
log_viewer::{InputState, InputTarget, LogViewer},
|
||||||
model::pretty_print_value,
|
widgets::{hyperlink::Hyperlink, items::Items, last_error::LastError},
|
||||||
widgets::{hyperlink::Hyperlink, items::Items, line_text::Highlighted},
|
|
||||||
};
|
};
|
||||||
use crate::tui::{
|
use crate::tui::{
|
||||||
filter::{Filter, Matcher},
|
filter::{Filter, Matcher},
|
||||||
|
|
@ -27,7 +27,7 @@ use ratatui::{
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
crossterm::event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
|
crossterm::event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
|
||||||
layout::{Constraint, HorizontalAlignment, Layout, Rect},
|
layout::{Constraint, HorizontalAlignment, Layout, Rect},
|
||||||
style::Style,
|
style::{self, Style},
|
||||||
text::Line,
|
text::Line,
|
||||||
widgets::{
|
widgets::{
|
||||||
Block, Clear, List, ListItem, ListState, Padding, Paragraph, StatefulWidget, Widget, Wrap,
|
Block, Clear, List, ListItem, ListState, Padding, Paragraph, StatefulWidget, Widget, Wrap,
|
||||||
|
|
@ -116,6 +116,8 @@ struct App {
|
||||||
compiler_root: Option<PathBuf>,
|
compiler_root: Option<PathBuf>,
|
||||||
current_file: Option<LogfileReader>,
|
current_file: Option<LogfileReader>,
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
|
|
||||||
|
last_error: LastError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
|
@ -126,6 +128,7 @@ impl App {
|
||||||
logs_dir,
|
logs_dir,
|
||||||
compiler_root,
|
compiler_root,
|
||||||
theme,
|
theme,
|
||||||
|
last_error: LastError::new(),
|
||||||
};
|
};
|
||||||
res.replace_tab(res.choose_file());
|
res.replace_tab(res.choose_file());
|
||||||
res
|
res
|
||||||
|
|
@ -356,6 +359,8 @@ impl App {
|
||||||
|
|
||||||
fn run(mut self, mut terminal: DefaultTerminal) -> io::Result<()> {
|
fn run(mut self, mut terminal: DefaultTerminal) -> io::Result<()> {
|
||||||
loop {
|
loop {
|
||||||
|
self.last_error.check_expired();
|
||||||
|
|
||||||
terminal.draw(|frame| frame.render_widget(&mut self, frame.area()))?;
|
terminal.draw(|frame| frame.render_widget(&mut self, frame.area()))?;
|
||||||
|
|
||||||
if let Event::Key(key) = event::read()? {
|
if let Event::Key(key) = event::read()? {
|
||||||
|
|
@ -376,12 +381,14 @@ impl App {
|
||||||
let highlighted = Style::new().fg(palette.accent).bg(palette.selection);
|
let highlighted = Style::new().fg(palette.accent).bg(palette.selection);
|
||||||
let border = Style::new().fg(palette.fg).bg(palette.bg);
|
let border = Style::new().fg(palette.fg).bg(palette.bg);
|
||||||
let border_highlighted = Style::new().fg(palette.secondary).bg(palette.bg);
|
let border_highlighted = Style::new().fg(palette.secondary).bg(palette.bg);
|
||||||
|
let error = Style::new().fg(palette.error).bg(palette.bg);
|
||||||
|
|
||||||
Styles {
|
Styles {
|
||||||
default,
|
default,
|
||||||
highlighted,
|
highlighted,
|
||||||
border,
|
border,
|
||||||
border_highlighted,
|
border_highlighted,
|
||||||
|
error,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -400,11 +407,12 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Styles {
|
pub struct Styles {
|
||||||
default: Style,
|
default: Style,
|
||||||
highlighted: Style,
|
highlighted: Style,
|
||||||
border: Style,
|
border: Style,
|
||||||
border_highlighted: Style,
|
border_highlighted: Style,
|
||||||
|
error: Style,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for &mut App {
|
impl Widget for &mut App {
|
||||||
|
|
@ -471,6 +479,8 @@ impl Widget for &mut App {
|
||||||
.style(styles.default)
|
.style(styles.default)
|
||||||
.render(middle, buf);
|
.render(middle, buf);
|
||||||
|
|
||||||
|
let [right, error] =
|
||||||
|
Layout::vertical([Constraint::Length(1), Constraint::Length(1)]).areas(right);
|
||||||
Paragraph::new("").style(styles.default).render(right, buf);
|
Paragraph::new("").style(styles.default).render(right, buf);
|
||||||
|
|
||||||
for tab in &mut self.tabs {
|
for tab in &mut self.tabs {
|
||||||
|
|
@ -533,6 +543,7 @@ impl Widget for &mut App {
|
||||||
.get(idx)
|
.get(idx)
|
||||||
.map(|(a, b)| (a.clone(), b.clone()))
|
.map(|(a, b)| (a.clone(), b.clone()))
|
||||||
}),
|
}),
|
||||||
|
self.last_error.clone(),
|
||||||
)
|
)
|
||||||
.styled_ref(&styles)
|
.styled_ref(&styles)
|
||||||
.render(main_area, buf);
|
.render(main_area, buf);
|
||||||
|
|
@ -575,6 +586,8 @@ impl Widget for &mut App {
|
||||||
Paragraph::new(HELP_TEXT).render(popup_area, buf);
|
Paragraph::new(HELP_TEXT).render(popup_area, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.last_error.clone().styled(&styles).render(error, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::rc::Rc;
|
use std::{cell::OnceCell, rc::Rc};
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ratatui::widgets::{List, ListItem, Widget};
|
use ratatui::widgets::{List, ListItem, Widget};
|
||||||
|
|
@ -8,6 +8,7 @@ use crate::tui::{
|
||||||
log_viewer::{FieldMatcher, InputState, InputTarget},
|
log_viewer::{FieldMatcher, InputState, InputTarget},
|
||||||
model::LogEntry,
|
model::LogEntry,
|
||||||
widgets::{
|
widgets::{
|
||||||
|
last_error::LastError,
|
||||||
line_text::Highlighted,
|
line_text::Highlighted,
|
||||||
styled::{IntoStyled, Styled},
|
styled::{IntoStyled, Styled},
|
||||||
},
|
},
|
||||||
|
|
@ -92,6 +93,7 @@ pub struct Items<'a> {
|
||||||
input_state: &'a InputState,
|
input_state: &'a InputState,
|
||||||
|
|
||||||
selected_footer_field: Option<(String, String)>,
|
selected_footer_field: Option<(String, String)>,
|
||||||
|
last_error: LastError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Items<'a> {
|
impl<'a> Items<'a> {
|
||||||
|
|
@ -100,12 +102,14 @@ impl<'a> Items<'a> {
|
||||||
selected_offset: usize,
|
selected_offset: usize,
|
||||||
input_state: &'a InputState,
|
input_state: &'a InputState,
|
||||||
selected_footer_field: Option<(String, String)>,
|
selected_footer_field: Option<(String, String)>,
|
||||||
|
last_error: LastError,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
items,
|
items,
|
||||||
selected_offset,
|
selected_offset,
|
||||||
input_state,
|
input_state,
|
||||||
selected_footer_field,
|
selected_footer_field,
|
||||||
|
last_error,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,6 +124,7 @@ impl Widget for Styled<'_, &Items<'_>> {
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
let ts = TreeState::from_items(&self.items);
|
let ts = TreeState::from_items(&self.items);
|
||||||
|
let regex_cache = OnceCell::new();
|
||||||
|
|
||||||
let list = List::new(
|
let list = List::new(
|
||||||
self.inner
|
self.inner
|
||||||
|
|
@ -161,8 +166,13 @@ impl Widget for Styled<'_, &Items<'_>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FieldMatcher::Regex(r) => {
|
FieldMatcher::Regex(r) => {
|
||||||
if let Ok(regex) = Regex::new(r)
|
if let Ok(regex) = regex_cache.get_or_init(|| {
|
||||||
&& let Some(start_offset) = line.message.find(msg)
|
let regex = Regex::new(r);
|
||||||
|
if let Err(e) = ®ex {
|
||||||
|
self.last_error.set(format!("{e}"));
|
||||||
|
}
|
||||||
|
regex
|
||||||
|
}) && let Some(start_offset) = line.message.find(msg)
|
||||||
&& let Some(m) = regex.find(msg)
|
&& let Some(m) = regex.find(msg)
|
||||||
{
|
{
|
||||||
let from = start_offset + m.start();
|
let from = start_offset + m.start();
|
||||||
|
|
|
||||||
50
src/tui/widgets/last_error.rs
Normal file
50
src/tui/widgets/last_error.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
use std::{cell::RefCell, rc::Rc, time::Instant};
|
||||||
|
|
||||||
|
use ratatui::widgets::{Paragraph, Widget};
|
||||||
|
|
||||||
|
use crate::tui::widgets::styled::Styled;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LastError {
|
||||||
|
inner: Rc<RefCell<Option<(String, Instant)>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LastError {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Rc::new(RefCell::new(None)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&self, s: String) {
|
||||||
|
*self.inner.borrow_mut() = Some((s, Instant::now()));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_expired(&self) {
|
||||||
|
let inner = self.inner.borrow();
|
||||||
|
if let Some((_, time)) = *inner {
|
||||||
|
if Instant::now().duration_since(time).as_secs_f64() > 1.0 {
|
||||||
|
drop(inner);
|
||||||
|
*self.inner.borrow_mut() = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for Styled<'_, LastError> {
|
||||||
|
fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer)
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Paragraph::new(
|
||||||
|
self.inner
|
||||||
|
.inner
|
||||||
|
.borrow()
|
||||||
|
.as_ref()
|
||||||
|
.map(|i| i.0.replace("\n", ""))
|
||||||
|
.unwrap_or_default(),
|
||||||
|
)
|
||||||
|
.style(self.styles.error)
|
||||||
|
.render(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod hyperlink;
|
pub mod hyperlink;
|
||||||
pub mod items;
|
pub mod items;
|
||||||
|
pub mod last_error;
|
||||||
pub mod line_text;
|
pub mod line_text;
|
||||||
pub mod styled;
|
pub mod styled;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue