mirror of
https://codeberg.org/icewind/logsmash.git
synced 2026-06-03 10:04:12 +02:00
add copy keybind
This commit is contained in:
parent
c56a77f3d0
commit
5b86279b46
11 changed files with 231 additions and 101 deletions
22
Cargo.lock
generated
22
Cargo.lock
generated
|
|
@ -329,6 +329,26 @@ dependencies = [
|
|||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "1.0.0-beta.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7abbfc297053be59290e3152f8cbcd52c8642e0728b69ee187d991d4c1af08d"
|
||||
dependencies = [
|
||||
"derive_more-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more-impl"
|
||||
version = "1.0.0-beta.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bba3e9872d7c58ce7ef0fcf1844fcc3e23ef2a58377b50df35dd98e42a5726e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
|
|
@ -532,7 +552,9 @@ name = "logsmash"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"base64",
|
||||
"clap",
|
||||
"derive_more",
|
||||
"hdrhistogram",
|
||||
"itertools",
|
||||
"log",
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ tinystr = { version = "0.7.6", features = ["serde"] }
|
|||
time = { version = "0.3.36", features = ["serde", "serde-well-known"] }
|
||||
hdrhistogram = "7.5.4"
|
||||
ahash = "0.8.11"
|
||||
base64 = "0.21.7"
|
||||
derive_more = { version = "1.0.0-beta.6", features = ["from"] }
|
||||
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 3
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
use crate::logfile::LogFile;
|
||||
use crate::logline::LogLine;
|
||||
use crate::matcher::MatchResult;
|
||||
use crate::timegraph::TimeGraph;
|
||||
use logsmash_data::StatementList;
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Mutex;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
pub struct App {
|
||||
|
|
@ -14,6 +16,7 @@ pub struct App {
|
|||
pub error_count: usize,
|
||||
pub all: LogMatch,
|
||||
pub unmatched: LogMatch,
|
||||
pub log_file: Mutex<LogFile>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
|
|
@ -25,6 +28,10 @@ impl App {
|
|||
};
|
||||
self.matches.len() + 1 + unmatched_line_count
|
||||
}
|
||||
|
||||
pub fn get_line(&self, index: usize) -> Option<String> {
|
||||
self.log_file.lock().unwrap().nth(index)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LogMatch {
|
||||
|
|
@ -63,7 +70,7 @@ fn group_lines<I: Iterator<Item = usize>>(all_lines: &[LogLine], indices: I) ->
|
|||
let mut map: BTreeMap<u64, Vec<usize>> = BTreeMap::new();
|
||||
|
||||
for (i, line) in indices.map(|i| (i, &all_lines[i])) {
|
||||
map.entry(line.index()).or_default().push(i);
|
||||
map.entry(line.identity()).or_default().push(i);
|
||||
}
|
||||
|
||||
let mut list: Vec<_> = map
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::error::ReadError;
|
||||
use itertools::Either;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::io::{BufRead, BufReader, Seek};
|
||||
use zip::ZipArchive;
|
||||
|
||||
pub enum LogFile {
|
||||
|
|
@ -37,4 +37,22 @@ impl LogFile {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nth(&mut self, index: usize) -> Option<String> {
|
||||
match self {
|
||||
LogFile::Plain(file) => {
|
||||
file.rewind().unwrap();
|
||||
file.lines().nth(index).transpose().ok().flatten()
|
||||
}
|
||||
LogFile::Zip(zip) => {
|
||||
let file = zip.by_index(0).expect("failed to open zip content again");
|
||||
BufReader::new(file)
|
||||
.lines()
|
||||
.nth(index)
|
||||
.transpose()
|
||||
.ok()
|
||||
.flatten()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ use tinystr::TinyAsciiStr;
|
|||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LogLine {
|
||||
#[serde(default)]
|
||||
pub index: usize,
|
||||
pub version: TinyAsciiStr<16>,
|
||||
pub level: LogLevel,
|
||||
pub message: String,
|
||||
|
|
@ -17,20 +19,11 @@ pub struct LogLine {
|
|||
}
|
||||
|
||||
impl LogLine {
|
||||
pub fn index(&self) -> u64 {
|
||||
pub fn identity(&self) -> u64 {
|
||||
let mut hasher = AHasher::default();
|
||||
self.message.hash(&mut hasher);
|
||||
self.level.hash(&mut hasher);
|
||||
self.exception
|
||||
.as_ref()
|
||||
.map(|e| e.exception.as_str())
|
||||
.hash(&mut hasher);
|
||||
self.exception
|
||||
.as_ref()
|
||||
.map(|e| e.file.as_str())
|
||||
.hash(&mut hasher);
|
||||
self.app.hash(&mut hasher);
|
||||
self.exception.as_ref().map(|e| e.line).hash(&mut hasher);
|
||||
self.exception.hash(&mut hasher);
|
||||
self.app.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
}
|
||||
|
|
@ -71,3 +64,12 @@ pub struct Exception {
|
|||
pub line: usize,
|
||||
pub previous: Option<Box<Exception>>,
|
||||
}
|
||||
|
||||
impl Hash for Exception {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.message.hash(state);
|
||||
self.exception.hash(state);
|
||||
self.file.hash(state);
|
||||
self.line.hash(state);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
22
src/main.rs
22
src/main.rs
|
|
@ -4,12 +4,14 @@ use crate::logfile::LogFile;
|
|||
use crate::logline::LogLine;
|
||||
use crate::matcher::{MatchResult, Matcher};
|
||||
use crate::ui::run_ui;
|
||||
use base64::prelude::*;
|
||||
use clap::Parser;
|
||||
use logsmash_data::{default_apps, get_statements, SourceDefinition};
|
||||
use main_error::MainResult;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::iter::once;
|
||||
use std::sync::Mutex;
|
||||
|
||||
mod app;
|
||||
mod error;
|
||||
|
|
@ -58,25 +60,26 @@ fn main() -> MainResult {
|
|||
let mut unmatched_counts: HashMap<String, Vec<usize>> = HashMap::new();
|
||||
let mut parsed_lines = Vec::with_capacity(1024);
|
||||
let mut unmatched_lines = Vec::with_capacity(256);
|
||||
let mut i = 0;
|
||||
for line in lines {
|
||||
let mut parsed_index = 0;
|
||||
for (index, line) in lines.enumerate() {
|
||||
if line.starts_with('{') {
|
||||
let parsed = match serde_json::from_str::<LogLine>(&line) {
|
||||
let mut parsed = match serde_json::from_str::<LogLine>(&line) {
|
||||
Ok(parsed) => parsed,
|
||||
Err(_) => {
|
||||
error_count += 1;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
parsed.index = index;
|
||||
if let Some(index) = matcher.match_log(&parsed) {
|
||||
counts.entry(index).or_default().push(i);
|
||||
counts.entry(index).or_default().push(parsed_index);
|
||||
} else if let Some(entry) = unmatched_counts.get_mut(parsed.app.as_str()) {
|
||||
entry.push(i)
|
||||
entry.push(parsed_index)
|
||||
} else {
|
||||
unmatched_lines.push(i);
|
||||
unmatched_lines.push(parsed_index);
|
||||
}
|
||||
parsed_lines.push(parsed);
|
||||
i += 1;
|
||||
parsed_index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -105,9 +108,14 @@ fn main() -> MainResult {
|
|||
unmatched,
|
||||
all,
|
||||
error_count,
|
||||
log_file: Mutex::new(log_file),
|
||||
};
|
||||
|
||||
run_ui(app)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_osc(text: &str) {
|
||||
print!("\x1B]52;c;{}\x07", BASE64_STANDARD.encode(text))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@ fn test_matcher() {
|
|||
message: "Not allowed to rename a shared album".into(),
|
||||
exception: None,
|
||||
time: OffsetDateTime::now_utc(),
|
||||
index: 0,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
|
|
@ -174,6 +175,7 @@ fn test_matcher() {
|
|||
message: "Not allowed to rename an album".into(),
|
||||
exception: None,
|
||||
time: OffsetDateTime::now_utc(),
|
||||
index: 0,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
|
|
@ -185,6 +187,7 @@ fn test_matcher() {
|
|||
message: "You are not allowed to edit link shares that you don't own".into(),
|
||||
exception: None,
|
||||
time: OffsetDateTime::now_utc(),
|
||||
index: 0,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
|
|
@ -196,6 +199,7 @@ fn test_matcher() {
|
|||
message: "You are not allowed to edit link shares that you don't own".into(),
|
||||
exception: None,
|
||||
time: OffsetDateTime::now_utc(),
|
||||
index: 0,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
|
|
@ -208,6 +212,7 @@ fn test_matcher() {
|
|||
message: "Unsupported query value for mimetype: %/text, only values in the format \"mime/type\" or \"mime/%\" are supported".into(),
|
||||
exception: None,
|
||||
time: OffsetDateTime::now_utc(),
|
||||
index: 0,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
|
@ -227,6 +232,7 @@ fn test_matcher() {
|
|||
previous: None,
|
||||
}),
|
||||
time: OffsetDateTime::now_utc(),
|
||||
index: 0,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ fn help(page: UiPage) -> &'static str {
|
|||
match page {
|
||||
UiPage::MatchList => "«Q» Exit - «Enter» Select",
|
||||
UiPage::Match => "«Q» Exit - «Enter» Select - «Esc» Back",
|
||||
UiPage::Logs => "«Q» Exit - «Esc» Back",
|
||||
UiPage::Logs => "«Q» Exit - «Esc» Back - «C» Copy log line",
|
||||
UiPage::Log => "«Q» Exit - «Esc» Back - «R» Toggle raw - «C» Copy log line",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ use crate::ui::footer::footer;
|
|||
use crate::ui::histogram::UiHistogram;
|
||||
use crate::ui::match_list::match_list;
|
||||
use crate::ui::raw_logs::raw_logs;
|
||||
use crate::ui::single_log::single_log;
|
||||
use crate::ui::single_match::grouped_lines;
|
||||
use crate::ui::state::{UiEvent, UiState};
|
||||
use crate::ui::state::{LogState, LogsState, MatchListState, MatchState, UiEvent, UiState};
|
||||
use ratatui::crossterm::event::{Event, KeyCode, KeyModifiers};
|
||||
use ratatui::crossterm::terminal::{
|
||||
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
|
||||
|
|
@ -21,6 +22,7 @@ mod footer;
|
|||
mod histogram;
|
||||
mod match_list;
|
||||
mod raw_logs;
|
||||
mod single_log;
|
||||
mod single_match;
|
||||
mod state;
|
||||
pub mod style;
|
||||
|
|
@ -58,7 +60,10 @@ fn handle_events() -> io::Result<Option<UiEvent>> {
|
|||
KeyCode::Up => Some(UiEvent::Up(1)),
|
||||
KeyCode::PageDown => Some(UiEvent::Down(10)),
|
||||
KeyCode::PageUp => Some(UiEvent::Up(10)),
|
||||
KeyCode::End => Some(UiEvent::Down(usize::MAX)),
|
||||
KeyCode::Home => Some(UiEvent::Up(usize::MAX)),
|
||||
KeyCode::Enter | KeyCode::Right => Some(UiEvent::Select),
|
||||
KeyCode::Char('c') => Some(UiEvent::Copy),
|
||||
_ => None,
|
||||
});
|
||||
}
|
||||
|
|
@ -80,10 +85,10 @@ fn ui(frame: &mut Frame, app: &App, state: &mut UiState) {
|
|||
|
||||
match state {
|
||||
UiState::Quit => {}
|
||||
UiState::MatchList {
|
||||
UiState::MatchList(MatchListState {
|
||||
table_state,
|
||||
scroll_state,
|
||||
} => {
|
||||
}) => {
|
||||
let selected = table_state.selected().unwrap_or(0);
|
||||
let histogram = if selected == 0 {
|
||||
&app.all.histogram
|
||||
|
|
@ -113,12 +118,12 @@ fn ui(frame: &mut Frame, app: &App, state: &mut UiState) {
|
|||
);
|
||||
frame.render_widget(footer(app, page), layout[2]);
|
||||
}
|
||||
UiState::Match {
|
||||
UiState::Match(MatchState {
|
||||
result,
|
||||
table_state,
|
||||
scroll_state,
|
||||
..
|
||||
} => {
|
||||
}) => {
|
||||
let selected_group = &result.grouped[table_state.selected().unwrap_or_default()];
|
||||
let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight)
|
||||
.begin_symbol(Some("↑"))
|
||||
|
|
@ -140,12 +145,12 @@ fn ui(frame: &mut Frame, app: &App, state: &mut UiState) {
|
|||
);
|
||||
frame.render_widget(footer(app, page), layout[2]);
|
||||
}
|
||||
UiState::Logs {
|
||||
UiState::Logs(LogsState {
|
||||
lines,
|
||||
table_state,
|
||||
scroll_state,
|
||||
..
|
||||
} => {
|
||||
}) => {
|
||||
let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight)
|
||||
.begin_symbol(Some("↑"))
|
||||
.end_symbol(Some("↓"));
|
||||
|
|
@ -164,5 +169,9 @@ fn ui(frame: &mut Frame, app: &App, state: &mut UiState) {
|
|||
);
|
||||
frame.render_widget(footer(app, page), layout[2]);
|
||||
}
|
||||
UiState::Log(LogState { log, .. }) => {
|
||||
frame.render_widget(single_log(app, log), layout[0].union(layout[1]));
|
||||
frame.render_widget(footer(app, page), layout[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
7
src/ui/single_log.rs
Normal file
7
src/ui/single_log.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
use crate::app::App;
|
||||
use crate::logline::LogLine;
|
||||
use ratatui::widgets::{Paragraph, Wrap};
|
||||
|
||||
pub fn single_log<'a>(_app: &App, line: &'a LogLine) -> Paragraph<'a> {
|
||||
Paragraph::new(line.display()).wrap(Wrap::default())
|
||||
}
|
||||
194
src/ui/state.rs
194
src/ui/state.rs
|
|
@ -1,70 +1,108 @@
|
|||
use crate::app::{App, LogMatch};
|
||||
use crate::copy_osc;
|
||||
use crate::logline::LogLine;
|
||||
use derive_more::From;
|
||||
use ratatui::widgets::{ScrollbarState, TableState};
|
||||
use table_state::TableStateExt;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, From)]
|
||||
pub enum UiState<'a> {
|
||||
MatchList {
|
||||
table_state: TableState,
|
||||
scroll_state: ScrollbarState,
|
||||
},
|
||||
Match {
|
||||
result: &'a LogMatch,
|
||||
table_state: TableState,
|
||||
scroll_state: ScrollbarState,
|
||||
previous: Box<UiState<'a>>,
|
||||
},
|
||||
Logs {
|
||||
lines: &'a [usize],
|
||||
table_state: TableState,
|
||||
scroll_state: ScrollbarState,
|
||||
previous: Box<UiState<'a>>,
|
||||
},
|
||||
MatchList(MatchListState),
|
||||
Match(MatchState<'a>),
|
||||
Logs(LogsState<'a>),
|
||||
Log(LogState<'a>),
|
||||
Quit,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MatchListState {
|
||||
pub table_state: TableState,
|
||||
pub scroll_state: ScrollbarState,
|
||||
}
|
||||
|
||||
impl MatchListState {
|
||||
fn selected(&self) -> usize {
|
||||
self.table_state.selected().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MatchState<'a> {
|
||||
pub result: &'a LogMatch,
|
||||
pub table_state: TableState,
|
||||
pub scroll_state: ScrollbarState,
|
||||
pub previous: Box<UiState<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> MatchState<'a> {
|
||||
fn selected(&self) -> usize {
|
||||
self.table_state.selected().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LogsState<'a> {
|
||||
pub lines: &'a [usize],
|
||||
pub table_state: TableState,
|
||||
pub scroll_state: ScrollbarState,
|
||||
pub previous: Box<UiState<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> LogsState<'a> {
|
||||
fn selected(&self) -> usize {
|
||||
self.table_state.selected().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LogState<'a> {
|
||||
pub log: &'a LogLine,
|
||||
pub previous: Box<UiState<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> UiState<'a> {
|
||||
pub fn new(app: &App) -> Self {
|
||||
let mut table_state = TableState::default();
|
||||
table_state.select(Some(0));
|
||||
UiState::MatchList {
|
||||
UiState::MatchList(MatchListState {
|
||||
table_state,
|
||||
scroll_state: ScrollbarState::new(app.match_lines()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn page(&self) -> UiPage {
|
||||
match self {
|
||||
UiState::Quit | UiState::MatchList { .. } => UiPage::MatchList,
|
||||
UiState::Match { .. } => UiPage::Match,
|
||||
UiState::Logs { .. } => UiPage::Logs,
|
||||
UiState::Quit | UiState::MatchList(_) => UiPage::MatchList,
|
||||
UiState::Match(_) => UiPage::Match,
|
||||
UiState::Logs(_) => UiPage::Logs,
|
||||
UiState::Log(_) => UiPage::Log,
|
||||
}
|
||||
}
|
||||
|
||||
fn table_state(&mut self) -> Option<&mut TableState> {
|
||||
match self {
|
||||
UiState::MatchList { table_state, .. } => Some(table_state),
|
||||
UiState::Match { table_state, .. } => Some(table_state),
|
||||
UiState::Logs { table_state, .. } => Some(table_state),
|
||||
UiState::Quit => None,
|
||||
UiState::MatchList(state) => Some(&mut state.table_state),
|
||||
UiState::Match(state) => Some(&mut state.table_state),
|
||||
UiState::Logs(state) => Some(&mut state.table_state),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn scroll_state(&mut self) -> Option<&mut ScrollbarState> {
|
||||
match self {
|
||||
UiState::MatchList { scroll_state, .. } => Some(scroll_state),
|
||||
UiState::Match { scroll_state, .. } => Some(scroll_state),
|
||||
UiState::Logs { scroll_state, .. } => Some(scroll_state),
|
||||
UiState::Quit => None,
|
||||
UiState::MatchList(state) => Some(&mut state.scroll_state),
|
||||
UiState::Match(state) => Some(&mut state.scroll_state),
|
||||
UiState::Logs(state) => Some(&mut state.scroll_state),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn table_count(&self, app: &App) -> usize {
|
||||
fn row_count(&self, app: &App) -> usize {
|
||||
match self {
|
||||
UiState::MatchList { .. } => app.match_lines(),
|
||||
UiState::Match { result, .. } => result.grouped.len(),
|
||||
UiState::Logs { lines, .. } => lines.len(),
|
||||
UiState::Quit => 0,
|
||||
UiState::MatchList(_) => app.match_lines(),
|
||||
UiState::Match(state) => state.result.grouped.len(),
|
||||
UiState::Logs(state) => state.lines.len(),
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,9 +110,9 @@ impl<'a> UiState<'a> {
|
|||
match (self, event) {
|
||||
(UiState::Quit, _) => UiState::Quit,
|
||||
(_, UiEvent::Quit) => UiState::Quit,
|
||||
(UiState::MatchList { .. }, UiEvent::Back) => UiState::Quit,
|
||||
(UiState::MatchList(_), UiEvent::Back) => UiState::Quit,
|
||||
(mut state, UiEvent::Down(step)) => {
|
||||
let count = state.table_count(app);
|
||||
let count = state.row_count(app);
|
||||
if let Some(table_state) = state.table_state() {
|
||||
let pos = table_state.down(count, step);
|
||||
let scroll_state = state.scroll_state().unwrap();
|
||||
|
|
@ -83,7 +121,7 @@ impl<'a> UiState<'a> {
|
|||
state
|
||||
}
|
||||
(mut state, UiEvent::Up(step)) => {
|
||||
let count = state.table_count(app);
|
||||
let count = state.row_count(app);
|
||||
if let Some(table_state) = state.table_state() {
|
||||
let pos = table_state.up(count, step);
|
||||
let scroll_state = state.scroll_state().unwrap();
|
||||
|
|
@ -91,14 +129,8 @@ impl<'a> UiState<'a> {
|
|||
}
|
||||
state
|
||||
}
|
||||
(
|
||||
UiState::MatchList {
|
||||
table_state: prev_state,
|
||||
scroll_state: prev_scroll,
|
||||
},
|
||||
UiEvent::Select,
|
||||
) => {
|
||||
let selected = prev_state.selected().unwrap_or(0);
|
||||
(UiState::MatchList(state), UiEvent::Select) => {
|
||||
let selected = state.selected();
|
||||
let mut table_state = TableState::default();
|
||||
table_state.select(Some(0));
|
||||
|
||||
|
|
@ -109,45 +141,59 @@ impl<'a> UiState<'a> {
|
|||
} else {
|
||||
&app.matches[selected - 1]
|
||||
};
|
||||
UiState::Match {
|
||||
UiState::Match(MatchState {
|
||||
result,
|
||||
table_state,
|
||||
scroll_state: ScrollbarState::new(result.count()),
|
||||
previous: Box::new(UiState::MatchList {
|
||||
table_state: prev_state,
|
||||
scroll_state: prev_scroll,
|
||||
}),
|
||||
}
|
||||
previous: Box::new(state.into()),
|
||||
})
|
||||
}
|
||||
(
|
||||
UiState::Match {
|
||||
table_state: prev_state,
|
||||
scroll_state: prev_scroll,
|
||||
previous,
|
||||
result,
|
||||
},
|
||||
UiEvent::Select,
|
||||
) => {
|
||||
let selected = prev_state.selected().unwrap_or(0);
|
||||
(UiState::Match(state), UiEvent::Select) => {
|
||||
let selected = state.selected();
|
||||
let mut table_state = TableState::default();
|
||||
table_state.select(Some(0));
|
||||
|
||||
let lines = result.grouped[selected].lines.as_slice();
|
||||
UiState::Logs {
|
||||
let lines = state.result.grouped[selected].lines.as_slice();
|
||||
UiState::Logs(LogsState {
|
||||
lines,
|
||||
table_state,
|
||||
scroll_state: ScrollbarState::new(lines.len()),
|
||||
previous: Box::new(UiState::Match {
|
||||
table_state: prev_state,
|
||||
scroll_state: prev_scroll,
|
||||
previous,
|
||||
result,
|
||||
}),
|
||||
}
|
||||
previous: Box::new(state.into()),
|
||||
})
|
||||
}
|
||||
(UiState::Match { previous, .. } | UiState::Logs { previous, .. }, UiEvent::Back) => {
|
||||
*previous
|
||||
(UiState::Logs(state), UiEvent::Select) => {
|
||||
let selected = state.selected();
|
||||
let mut table_state = TableState::default();
|
||||
table_state.select(Some(0));
|
||||
|
||||
let line = state.lines[selected];
|
||||
let log = &app.lines[line];
|
||||
UiState::Log(LogState {
|
||||
log,
|
||||
previous: Box::new(state.into()),
|
||||
})
|
||||
}
|
||||
(UiState::Logs(state), UiEvent::Copy) => {
|
||||
let selected = state.selected();
|
||||
let mut table_state = TableState::default();
|
||||
table_state.select(Some(0));
|
||||
|
||||
let line = &app.lines[state.lines[selected]];
|
||||
let raw = app.get_line(line.index).unwrap_or_default();
|
||||
copy_osc(&raw);
|
||||
UiState::Logs(state)
|
||||
}
|
||||
(UiState::Log(state), UiEvent::Copy) => {
|
||||
let raw = app.get_line(state.log.index).unwrap_or_default();
|
||||
copy_osc(&raw);
|
||||
UiState::Log(state)
|
||||
}
|
||||
(
|
||||
UiState::Match(MatchState { previous, .. })
|
||||
| UiState::Logs(LogsState { previous, .. })
|
||||
| UiState::Log(LogState { previous, .. }),
|
||||
UiEvent::Back,
|
||||
) => *previous,
|
||||
(state, _) => state,
|
||||
}
|
||||
}
|
||||
|
|
@ -159,12 +205,14 @@ pub enum UiEvent {
|
|||
Up(usize),
|
||||
Down(usize),
|
||||
Select,
|
||||
Copy,
|
||||
}
|
||||
|
||||
pub enum UiPage {
|
||||
MatchList,
|
||||
Match,
|
||||
Logs,
|
||||
Log,
|
||||
}
|
||||
|
||||
mod table_state {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue