help overview and input rework
This commit is contained in:
parent
a6d501977c
commit
f733c65bcf
3 changed files with 275 additions and 214 deletions
|
|
@ -59,7 +59,7 @@ enum Preset {
|
||||||
compiler_root: Option<PathBuf>,
|
compiler_root: Option<PathBuf>,
|
||||||
|
|
||||||
/// Path where the compiler source code lives, for links in the TUI to work.
|
/// Path where the compiler source code lives, for links in the TUI to work.
|
||||||
#[arg(default_value_t = Theme(ThemeName::OneDarkPro))]
|
#[arg(default_value_t = Theme(ThemeName::Dracula))]
|
||||||
#[arg(long = "theme")]
|
#[arg(long = "theme")]
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,25 @@ impl Clone for LogView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum InputTarget {
|
||||||
|
Fields,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum InputState {
|
||||||
|
None,
|
||||||
|
Target(InputTarget),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputState {
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
*self = Self::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn target(&mut self, target: InputTarget) {
|
||||||
|
*self = Self::Target(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct LogViewer {
|
pub struct LogViewer {
|
||||||
stack: Vec<LogView>,
|
stack: Vec<LogView>,
|
||||||
curr: LogView,
|
curr: LogView,
|
||||||
|
|
@ -40,8 +59,9 @@ pub struct LogViewer {
|
||||||
|
|
||||||
pub root_stream: Box<dyn LogStream>,
|
pub root_stream: Box<dyn LogStream>,
|
||||||
pub last_height: usize,
|
pub last_height: usize,
|
||||||
pub footer_selected: bool,
|
|
||||||
pub footer_list: ListState,
|
pub footer_list: ListState,
|
||||||
|
|
||||||
|
pub input_state: InputState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LogViewer {
|
impl LogViewer {
|
||||||
|
|
@ -55,9 +75,9 @@ impl LogViewer {
|
||||||
root_stream: stream.clone(),
|
root_stream: stream.clone(),
|
||||||
cache: HashMap::new(),
|
cache: HashMap::new(),
|
||||||
footer_list: ListState::default(),
|
footer_list: ListState::default(),
|
||||||
footer_selected: false,
|
|
||||||
last_height: 0,
|
last_height: 0,
|
||||||
filters: Vec::new(),
|
filters: Vec::new(),
|
||||||
|
input_state: InputState::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -224,36 +244,35 @@ impl LogViewer {
|
||||||
self.curr.selected()
|
self.curr.selected()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_footer_select(&mut self) {
|
|
||||||
self.footer_list.select(Some(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prev(&mut self) {
|
pub fn prev(&mut self) {
|
||||||
if self.footer_selected {
|
match self.input_state {
|
||||||
self.footer_list.previous();
|
InputState::None => {
|
||||||
} else {
|
if self.curr.selection_offset == 0 {
|
||||||
if self.curr.selection_offset == 0 {
|
let _ = self.curr.iter.prev();
|
||||||
let _ = self.curr.iter.prev();
|
} else {
|
||||||
} else {
|
self.curr.selection_offset -= 1;
|
||||||
self.curr.selection_offset -= 1;
|
}
|
||||||
|
}
|
||||||
|
InputState::Target(InputTarget::Fields) => {
|
||||||
|
self.footer_list.previous();
|
||||||
}
|
}
|
||||||
self.update_footer_select();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next(&mut self) {
|
pub fn next(&mut self) {
|
||||||
if self.footer_selected {
|
match self.input_state {
|
||||||
self.footer_list.next();
|
InputState::None => {
|
||||||
} else {
|
self.curr.selection_offset += 1;
|
||||||
self.curr.selection_offset += 1;
|
}
|
||||||
self.update_footer_select();
|
InputState::Target(InputTarget::Fields) => {
|
||||||
|
self.footer_list.next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn page_down(&mut self) {
|
pub fn page_down(&mut self) {
|
||||||
self.curr.selection_offset += self.last_height;
|
self.curr.selection_offset += self.last_height;
|
||||||
self.footer_selected = false;
|
self.input_state.reset();
|
||||||
self.update_footer_select();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn page_up(&mut self) {
|
pub fn page_up(&mut self) {
|
||||||
|
|
@ -264,17 +283,18 @@ impl LogViewer {
|
||||||
self.curr.selection_offset -= 1;
|
self.curr.selection_offset -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.footer_selected = false;
|
self.input_state.reset();
|
||||||
self.update_footer_select();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn home(&mut self) {
|
pub fn home(&mut self) {
|
||||||
if self.footer_selected {
|
match self.input_state {
|
||||||
self.footer_list.select(Some(0));
|
InputState::None => {
|
||||||
} else {
|
self.curr.selection_offset = 0;
|
||||||
self.curr.selection_offset = 0;
|
while self.curr.iter.prev().is_some() {}
|
||||||
while self.curr.iter.prev().is_some() {}
|
}
|
||||||
self.update_footer_select();
|
InputState::Target(InputTarget::Fields) => {
|
||||||
|
self.footer_list.select(Some(0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,34 +307,33 @@ impl LogViewer {
|
||||||
if let Some(stack) = self.stack.pop() {
|
if let Some(stack) = self.stack.pop() {
|
||||||
self.curr = stack;
|
self.curr = stack;
|
||||||
}
|
}
|
||||||
self.footer_selected = false;
|
self.input_state.reset();
|
||||||
self.update_footer_select();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn switch_focus(&mut self) {
|
|
||||||
self.footer_selected = !self.footer_selected;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enter(&mut self) {
|
pub fn enter(&mut self) {
|
||||||
if !self.footer_selected {
|
match self.input_state {
|
||||||
let Some((s, _)) = self.selected() else {
|
InputState::None => {
|
||||||
return;
|
let Some((s, _)) = self.selected() else {
|
||||||
};
|
return;
|
||||||
let Some(i) = s.from_start(0) else {
|
};
|
||||||
return;
|
let Some(i) = s.from_start(0) else {
|
||||||
};
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
self.stack.push(mem::replace(
|
self.stack.push(mem::replace(
|
||||||
&mut self.curr,
|
&mut self.curr,
|
||||||
LogView {
|
LogView {
|
||||||
iter: Box::new(i),
|
iter: Box::new(i),
|
||||||
selection_offset: 0,
|
selection_offset: 0,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
if let Some(cached_view) = self.cache.get(&self.path()) {
|
if let Some(cached_view) = self.cache.get(&self.path()) {
|
||||||
self.curr = cached_view.clone();
|
self.curr = cached_view.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InputState::Target(InputTarget::Fields) => {
|
||||||
|
self.footer_list.next();
|
||||||
}
|
}
|
||||||
self.update_footer_select();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
364
src/tui/mod.rs
364
src/tui/mod.rs
|
|
@ -3,6 +3,7 @@ use ratatui_themes::{Theme, ThemeName};
|
||||||
use std::{
|
use std::{
|
||||||
fs::{self, DirEntry},
|
fs::{self, DirEntry},
|
||||||
io,
|
io,
|
||||||
|
ops::ControlFlow,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::exit,
|
process::exit,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
|
|
@ -11,7 +12,7 @@ use tui_widget_list::{ListBuilder, ListView};
|
||||||
|
|
||||||
use crate::tui::{
|
use crate::tui::{
|
||||||
filter::{FilterKind, WipMatcher},
|
filter::{FilterKind, WipMatcher},
|
||||||
log_viewer::LogViewer,
|
log_viewer::{InputState, InputTarget, LogViewer},
|
||||||
};
|
};
|
||||||
use crate::tui::{
|
use crate::tui::{
|
||||||
filter::{FilterSelection, WipFilter},
|
filter::{FilterSelection, WipFilter},
|
||||||
|
|
@ -20,7 +21,7 @@ use crate::tui::{
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
DefaultTerminal,
|
DefaultTerminal,
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
crossterm::event::{self, Event, KeyCode, KeyModifiers},
|
crossterm::event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
|
||||||
layout::{Constraint, HorizontalAlignment, Layout, Rect},
|
layout::{Constraint, HorizontalAlignment, Layout, Rect},
|
||||||
style::Style,
|
style::Style,
|
||||||
text::{Line, Text},
|
text::{Line, Text},
|
||||||
|
|
@ -58,6 +59,7 @@ enum Tab {
|
||||||
filter: WipFilter,
|
filter: WipFilter,
|
||||||
},
|
},
|
||||||
Empty,
|
Empty,
|
||||||
|
Help,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
|
|
@ -68,31 +70,33 @@ impl Tab {
|
||||||
(Tab::LogViewer(_), Some(path)) => format!("logs of {}", path.display()),
|
(Tab::LogViewer(_), Some(path)) => format!("logs of {}", path.display()),
|
||||||
(Tab::LogViewer(_), None) => "logs".to_string(),
|
(Tab::LogViewer(_), None) => "logs".to_string(),
|
||||||
(Tab::CreateFilter { .. }, _) => "create filter".to_string(),
|
(Tab::CreateFilter { .. }, _) => "create filter".to_string(),
|
||||||
|
(Tab::Help, _) => "help".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_filter(lv: &mut LogViewer, kind: Option<FilterKind>) -> WipFilter {
|
fn initialize_filter(lv: &mut LogViewer, kind: Option<FilterKind>) -> WipFilter {
|
||||||
let matcher = if lv.footer_selected {
|
todo!()
|
||||||
let footer_fields = lv.footer_fields();
|
// let matcher = if lv.fields_selected {
|
||||||
let (key, value) = footer_fields
|
// let footer_fields = lv.footer_fields();
|
||||||
.get(lv.footer_list.selected.unwrap_or(0))
|
// let (key, value) = footer_fields
|
||||||
.map_or((None, None), |(k, v)| (Some(k), Some(v)));
|
// .get(lv.footer_list.selected.unwrap_or(0))
|
||||||
Some(WipMatcher::Field {
|
// .map_or((None, None), |(k, v)| (Some(k), Some(v)));
|
||||||
name: key.cloned(),
|
// Some(WipMatcher::Field {
|
||||||
value: value.cloned(),
|
// name: key.cloned(),
|
||||||
})
|
// value: value.cloned(),
|
||||||
} else {
|
// })
|
||||||
Some(WipMatcher::Specific {
|
// } else {
|
||||||
hash: lv.selected().map(|(i, _)| i.hash()),
|
// Some(WipMatcher::Specific {
|
||||||
})
|
// hash: lv.selected().map(|(i, _)| i.hash()),
|
||||||
};
|
// })
|
||||||
|
// };
|
||||||
|
|
||||||
WipFilter {
|
// WipFilter {
|
||||||
matcher,
|
// matcher,
|
||||||
kind,
|
// kind,
|
||||||
selection: filter::FilterSelection::Kind,
|
// selection: filter::FilterSelection::Kind,
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
struct App {
|
struct App {
|
||||||
|
|
@ -166,6 +170,92 @@ impl App {
|
||||||
self.tabs.last_mut().unwrap()
|
self.tabs.last_mut().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_current_tab_keycode(&mut self, key: KeyEvent) {
|
||||||
|
match self.tabs.last_mut().unwrap() {
|
||||||
|
Tab::Help => {}
|
||||||
|
Tab::Empty => {}
|
||||||
|
Tab::FileChooser {
|
||||||
|
files,
|
||||||
|
state,
|
||||||
|
last_height,
|
||||||
|
} => match key.code {
|
||||||
|
KeyCode::Char('j') | KeyCode::Down => state.select_next(),
|
||||||
|
KeyCode::Char('k') | KeyCode::Up => state.select_previous(),
|
||||||
|
KeyCode::PageUp => state.scroll_up_by(*last_height as u16),
|
||||||
|
KeyCode::PageDown => state.scroll_down_by(*last_height as u16),
|
||||||
|
KeyCode::Char('G') | KeyCode::Home => state.select_first(),
|
||||||
|
KeyCode::Char('g') | KeyCode::End => state.select_last(),
|
||||||
|
KeyCode::Enter => {
|
||||||
|
if let Some(selected) = state.selected()
|
||||||
|
&& let Some(selected) = files.get(selected)
|
||||||
|
{
|
||||||
|
match LogfileReader::new(&selected.path()) {
|
||||||
|
Ok(i) => {
|
||||||
|
self.current_file = Some(i.clone());
|
||||||
|
self.replace_tab(Tab::LogViewer(LogViewer::new(i.iter())));
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
panic!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
Tab::LogViewer(lv) => match key.code {
|
||||||
|
KeyCode::Char('j') | KeyCode::Down => lv.next(),
|
||||||
|
KeyCode::Char('k') | KeyCode::Up => lv.prev(),
|
||||||
|
KeyCode::PageUp => lv.page_up(),
|
||||||
|
KeyCode::PageDown => lv.page_down(),
|
||||||
|
KeyCode::Char('G') | KeyCode::Home => lv.home(),
|
||||||
|
KeyCode::Char('g') | KeyCode::End => todo!(),
|
||||||
|
KeyCode::Backspace | KeyCode::Left => lv.back(),
|
||||||
|
KeyCode::Right => lv.enter(),
|
||||||
|
KeyCode::Enter => lv.enter(),
|
||||||
|
KeyCode::Char('f') => {
|
||||||
|
lv.input_state.target(InputTarget::Fields);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
Tab::CreateFilter { filter } => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_generic_keycode(
|
||||||
|
&mut self,
|
||||||
|
key: KeyEvent,
|
||||||
|
terminal: &mut DefaultTerminal,
|
||||||
|
) -> ControlFlow<()> {
|
||||||
|
let num_tabs = self.tabs.len();
|
||||||
|
match key.code {
|
||||||
|
KeyCode::Char('q') => return ControlFlow::Break(()),
|
||||||
|
KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||||
|
return ControlFlow::Break(());
|
||||||
|
}
|
||||||
|
KeyCode::Char('z') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||||
|
ratatui::restore();
|
||||||
|
let self_pid = nix::unistd::getpid();
|
||||||
|
let _ = nix::sys::signal::kill(self_pid, nix::sys::signal::SIGTSTP);
|
||||||
|
*terminal = ratatui::init();
|
||||||
|
}
|
||||||
|
KeyCode::Char('?') => {
|
||||||
|
if matches!(self.current_tab(), Tab::Help) {
|
||||||
|
self.pop_tab();
|
||||||
|
} else {
|
||||||
|
self.push_tab(Tab::Help);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyCode::Esc if num_tabs > 1 => {
|
||||||
|
self.pop_tab();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.handle_current_tab_keycode(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
}
|
||||||
|
|
||||||
fn run(mut self, mut terminal: DefaultTerminal) -> io::Result<()> {
|
fn run(mut self, mut terminal: DefaultTerminal) -> io::Result<()> {
|
||||||
loop {
|
loop {
|
||||||
terminal.draw(|frame| frame.render_widget(&mut self, frame.area()))?;
|
terminal.draw(|frame| frame.render_widget(&mut self, frame.area()))?;
|
||||||
|
|
@ -173,147 +263,48 @@ impl App {
|
||||||
if let Event::Key(key) = event::read()? {
|
if let Event::Key(key) = event::read()? {
|
||||||
// to initialize, but we then do get manually for borrow reasons
|
// to initialize, but we then do get manually for borrow reasons
|
||||||
self.current_tab();
|
self.current_tab();
|
||||||
let num_tabs = self.tabs.len();
|
|
||||||
match (key.code, self.tabs.last_mut().unwrap()) {
|
|
||||||
(KeyCode::Char('q'), _) => return Ok(()),
|
|
||||||
(KeyCode::Char('c'), _) if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
(KeyCode::Char('z'), _) if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
|
||||||
ratatui::restore();
|
|
||||||
let self_pid = nix::unistd::getpid();
|
|
||||||
let _ = nix::sys::signal::kill(self_pid, nix::sys::signal::SIGTSTP);
|
|
||||||
terminal = ratatui::init();
|
|
||||||
}
|
|
||||||
(KeyCode::Esc, _) if num_tabs > 1 => {
|
|
||||||
self.pop_tab();
|
|
||||||
}
|
|
||||||
(KeyCode::Char('j') | KeyCode::Down, tab) => match tab {
|
|
||||||
Tab::FileChooser { state, .. } => state.select_next(),
|
|
||||||
Tab::LogViewer(lv) => lv.next(),
|
|
||||||
Tab::Empty => {}
|
|
||||||
Tab::CreateFilter { filter } => {
|
|
||||||
filter.selection.next();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(KeyCode::Char('k') | KeyCode::Up, tab) => match tab {
|
|
||||||
Tab::FileChooser { state, .. } => state.select_previous(),
|
|
||||||
Tab::LogViewer(lv) => {
|
|
||||||
lv.prev();
|
|
||||||
}
|
|
||||||
Tab::Empty => {}
|
|
||||||
Tab::CreateFilter { filter } => {
|
|
||||||
filter.selection.prev();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(KeyCode::PageDown, tab) => match tab {
|
|
||||||
Tab::FileChooser {
|
|
||||||
state, last_height, ..
|
|
||||||
} => state.scroll_down_by(*last_height as u16),
|
|
||||||
Tab::LogViewer(lv) => {
|
|
||||||
lv.page_down();
|
|
||||||
}
|
|
||||||
Tab::Empty => {}
|
|
||||||
Tab::CreateFilter { .. } => {}
|
|
||||||
},
|
|
||||||
(KeyCode::PageUp, tab) => match tab {
|
|
||||||
Tab::FileChooser {
|
|
||||||
state, last_height, ..
|
|
||||||
} => state.scroll_up_by(*last_height as u16),
|
|
||||||
Tab::LogViewer(lv) => {
|
|
||||||
lv.page_up();
|
|
||||||
}
|
|
||||||
Tab::Empty => {}
|
|
||||||
Tab::CreateFilter { .. } => {}
|
|
||||||
},
|
|
||||||
(KeyCode::Char('G') | KeyCode::Home, tab) => match tab {
|
|
||||||
Tab::FileChooser { state, .. } => state.select_first(),
|
|
||||||
Tab::LogViewer(lv) => {
|
|
||||||
lv.home();
|
|
||||||
}
|
|
||||||
Tab::Empty => {}
|
|
||||||
Tab::CreateFilter { .. } => {}
|
|
||||||
},
|
|
||||||
(KeyCode::Char('g') | KeyCode::End, tab) => match tab {
|
|
||||||
Tab::FileChooser { state, .. } => state.select_last(),
|
|
||||||
Tab::LogViewer(_) => {}
|
|
||||||
Tab::Empty => {}
|
|
||||||
Tab::CreateFilter { .. } => {}
|
|
||||||
},
|
|
||||||
(KeyCode::Backspace | KeyCode::Left | KeyCode::Esc, Tab::LogViewer(lv)) => {
|
|
||||||
lv.back();
|
|
||||||
}
|
|
||||||
(KeyCode::Backspace, Tab::CreateFilter { filter }) => {
|
|
||||||
filter.clear();
|
|
||||||
}
|
|
||||||
(KeyCode::Right, Tab::CreateFilter { filter }) => {
|
|
||||||
filter.right();
|
|
||||||
}
|
|
||||||
(KeyCode::Left, Tab::CreateFilter { filter }) => {
|
|
||||||
filter.left();
|
|
||||||
}
|
|
||||||
(KeyCode::Right, Tab::LogViewer(lv)) => lv.enter(),
|
|
||||||
(KeyCode::Tab, Tab::LogViewer(lv)) => {
|
|
||||||
lv.switch_focus();
|
|
||||||
}
|
|
||||||
(KeyCode::Char('r'), Tab::LogViewer(lv)) => {
|
|
||||||
let filter = initialize_filter(lv, Some(FilterKind::Remove));
|
|
||||||
self.push_tab(Tab::CreateFilter { filter });
|
|
||||||
}
|
|
||||||
(KeyCode::Char('i'), Tab::LogViewer(lv)) => {
|
|
||||||
let filter = initialize_filter(lv, Some(FilterKind::Inline));
|
|
||||||
self.push_tab(Tab::CreateFilter { filter });
|
|
||||||
}
|
|
||||||
(KeyCode::Enter, tab) => match tab {
|
|
||||||
Tab::FileChooser { files, state, .. } => {
|
|
||||||
if let Some(selected) = state.selected()
|
|
||||||
&& let Some(selected) = files.get(selected)
|
|
||||||
{
|
|
||||||
match LogfileReader::new(&selected.path()) {
|
|
||||||
Ok(i) => {
|
|
||||||
self.current_file = Some(i.clone());
|
|
||||||
self.replace_tab(Tab::LogViewer(LogViewer::new(i.iter())));
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
panic!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Tab::LogViewer(lv) => {
|
|
||||||
if lv.footer_selected {
|
|
||||||
let filter = initialize_filter(lv, None);
|
|
||||||
self.push_tab(Tab::CreateFilter { filter });
|
|
||||||
} else {
|
|
||||||
lv.enter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Tab::Empty => {}
|
|
||||||
Tab::CreateFilter { filter } => {
|
|
||||||
if let FilterSelection::Confirm = filter.selection {
|
|
||||||
let filter_clone = filter.clone();
|
|
||||||
if let Some(lv) = self.tabs.iter_mut().rev().find_map(|i| {
|
|
||||||
if let Tab::LogViewer(lv) = i {
|
|
||||||
Some(lv)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}) && let Some(filter) = filter_clone.validate()
|
|
||||||
{
|
|
||||||
lv.add_filter(Rc::new(filter));
|
|
||||||
self.pop_tab();
|
|
||||||
|
|
||||||
if let Tab::LogViewer(lv) = self.current_tab() {
|
// always active
|
||||||
lv.footer_selected = false;
|
if self.handle_generic_keycode(key, &mut terminal).is_break() {
|
||||||
}
|
break Ok(());
|
||||||
}
|
|
||||||
} else {
|
|
||||||
filter.selection.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// match (key.code, self.tabs.last_mut().unwrap()) {
|
||||||
|
// (KeyCode::Tab, Tab::LogViewer(lv)) => {
|
||||||
|
// lv.switch_focus();
|
||||||
|
// }
|
||||||
|
// (KeyCode::Char('r'), Tab::LogViewer(lv)) => {
|
||||||
|
// let filter = initialize_filter(lv, Some(FilterKind::Remove));
|
||||||
|
// self.push_tab(Tab::CreateFilter { filter });
|
||||||
|
// }
|
||||||
|
// (KeyCode::Char('i'), Tab::LogViewer(lv)) => {
|
||||||
|
// let filter = initialize_filter(lv, Some(FilterKind::Inline));
|
||||||
|
// self.push_tab(Tab::CreateFilter { filter });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if let FilterSelection::Confirm = filter.selection {
|
||||||
|
// let filter_clone = filter.clone();
|
||||||
|
// if let Some(lv) = self.tabs.iter_mut().rev().find_map(|i| {
|
||||||
|
// if let Tab::LogViewer(lv) = i {
|
||||||
|
// Some(lv)
|
||||||
|
// } else {
|
||||||
|
// None
|
||||||
|
// }
|
||||||
|
// }) && let Some(filter) = filter_clone.validate()
|
||||||
|
// {
|
||||||
|
// lv.add_filter(Rc::new(filter));
|
||||||
|
// self.pop_tab();
|
||||||
|
|
||||||
|
// if let Tab::LogViewer(lv) = self.current_tab() {
|
||||||
|
// lv.footer_selected = false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// filter.selection.next();
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// _ => {}
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -351,8 +342,13 @@ impl Widget for &mut App {
|
||||||
.areas(popup_area);
|
.areas(popup_area);
|
||||||
|
|
||||||
let (footer_focused, header_focused) = match self.current_tab() {
|
let (footer_focused, header_focused) = match self.current_tab() {
|
||||||
|
Tab::Help => (false, false),
|
||||||
Tab::FileChooser { .. } => (false, true),
|
Tab::FileChooser { .. } => (false, true),
|
||||||
Tab::LogViewer(lv) => (lv.footer_selected, !lv.footer_selected),
|
Tab::LogViewer(lv) => {
|
||||||
|
let target_fields =
|
||||||
|
matches!(lv.input_state, InputState::Target(InputTarget::Fields));
|
||||||
|
(target_fields, !target_fields)
|
||||||
|
}
|
||||||
Tab::Empty => (false, false),
|
Tab::Empty => (false, false),
|
||||||
Tab::CreateFilter { .. } => (false, false),
|
Tab::CreateFilter { .. } => (false, false),
|
||||||
};
|
};
|
||||||
|
|
@ -496,6 +492,52 @@ impl Widget for &mut App {
|
||||||
StatefulWidget::render(list, footer_area, buf, &mut lv.footer_list);
|
StatefulWidget::render(list, footer_area, buf, &mut lv.footer_list);
|
||||||
}
|
}
|
||||||
Tab::Empty => {}
|
Tab::Empty => {}
|
||||||
|
Tab::Help => {
|
||||||
|
Clear.render(popup_area, buf);
|
||||||
|
let popup_area = {
|
||||||
|
let block = Block::bordered()
|
||||||
|
.title_top("help")
|
||||||
|
.style(default)
|
||||||
|
.padding(Padding::symmetric(3, 1))
|
||||||
|
.border_style(border_selected);
|
||||||
|
let inner = block.inner(popup_area);
|
||||||
|
block.render(popup_area, buf);
|
||||||
|
inner
|
||||||
|
};
|
||||||
|
|
||||||
|
Paragraph::new(
|
||||||
|
"
|
||||||
|
Generic:
|
||||||
|
? show help
|
||||||
|
<esc> cancel focus or close tab
|
||||||
|
u undo
|
||||||
|
r redo
|
||||||
|
pgdwn&pgup move page
|
||||||
|
down&up move single
|
||||||
|
j&k move single
|
||||||
|
Home/G move to start
|
||||||
|
|
||||||
|
<- / backspace / h exit nested view
|
||||||
|
-> / enter / l enter nested view
|
||||||
|
|
||||||
|
───────────────────────────────────────────────────────
|
||||||
|
targeting logs:
|
||||||
|
|
||||||
|
f fields
|
||||||
|
p prefix
|
||||||
|
r regex
|
||||||
|
c current
|
||||||
|
s surrounding element
|
||||||
|
|
||||||
|
───────────────────────────────────────────────────────
|
||||||
|
perform action on selected target:
|
||||||
|
|
||||||
|
d delete
|
||||||
|
i inline
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.render(popup_area, buf);
|
||||||
|
}
|
||||||
Tab::CreateFilter { filter } => {
|
Tab::CreateFilter { filter } => {
|
||||||
Clear.render(popup_area, buf);
|
Clear.render(popup_area, buf);
|
||||||
let popup_area = {
|
let popup_area = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue