linked lists
This commit is contained in:
parent
8eab2502c7
commit
430c62c120
12 changed files with 783 additions and 544 deletions
|
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::HashMap, iter, mem, path::PathBuf, sync::Arc};
|
||||
use std::{collections::HashMap, path::PathBuf, sync::Arc};
|
||||
|
||||
use crate::tui::{
|
||||
filter::Filter,
|
||||
|
|
@ -8,9 +8,10 @@ use crate::tui::{
|
|||
view::LogView,
|
||||
},
|
||||
model::LogEntry,
|
||||
processing::{FilterList, IntoLogStream, LogStream},
|
||||
processing::Cursor,
|
||||
widgets::last_error::LastError,
|
||||
};
|
||||
use dumpster::sync::Gc;
|
||||
use tui_widget_list::ListState;
|
||||
|
||||
pub mod filters;
|
||||
|
|
@ -18,11 +19,9 @@ pub mod input;
|
|||
pub mod view;
|
||||
|
||||
pub struct LogViewer {
|
||||
pub stack: Vec<LogView>,
|
||||
curr: LogView,
|
||||
cache: HashMap<Vec<usize>, LogView>,
|
||||
|
||||
pub root_stream: Box<dyn LogStream>,
|
||||
pub view: LogView,
|
||||
|
||||
pub last_height: usize,
|
||||
pub last_offset: usize,
|
||||
|
|
@ -30,21 +29,18 @@ pub struct LogViewer {
|
|||
pub last_fields_height: usize,
|
||||
|
||||
pub footer_list: ListState,
|
||||
filters: Filters,
|
||||
pub filters: Filters,
|
||||
pub input_state: InputState,
|
||||
}
|
||||
|
||||
impl LogViewer {
|
||||
pub fn new(stream: impl LogStream, filters_path: PathBuf, error: LastError) -> Self {
|
||||
pub fn new(start: Gc<LogEntry>, filters_path: PathBuf, error: LastError) -> Self {
|
||||
let filters = Filters::new(filters_path, error);
|
||||
let stream = stream.with_filters(filters.get());
|
||||
Self {
|
||||
stack: Vec::new(),
|
||||
curr: LogView {
|
||||
iter: stream.clone(),
|
||||
view: LogView {
|
||||
cursor: Cursor::new(start),
|
||||
selection_offset: 0,
|
||||
},
|
||||
root_stream: stream.clone(),
|
||||
cache: HashMap::new(),
|
||||
footer_list: ListState::default(),
|
||||
|
||||
|
|
@ -58,169 +54,20 @@ impl LogViewer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn filtered_root_stream(&self) -> Box<dyn LogStream> {
|
||||
self.root_stream.with_filters(self.filters.get())
|
||||
}
|
||||
|
||||
fn update_filters(&mut self, old_filters: &FilterList) {
|
||||
self.cache.clear();
|
||||
let offsets_list: Vec<_> = self
|
||||
.stack
|
||||
.iter()
|
||||
.map(|i| (i.selected(old_filters), i.selection_offset))
|
||||
.chain(iter::once((
|
||||
self.curr.selected(old_filters),
|
||||
self.curr.selection_offset,
|
||||
)))
|
||||
.collect();
|
||||
|
||||
// let mut log = File::options()
|
||||
// .create(true)
|
||||
// .append(true)
|
||||
// .open("./log")
|
||||
// .unwrap();
|
||||
|
||||
// writeln!(&mut log, "active filters: {:?}", self.filters.get()).unwrap();
|
||||
|
||||
// writeln!(
|
||||
// &mut log,
|
||||
// "{:?}",
|
||||
// offsets_list.iter().map(|i| i.1).collect::<Vec<_>>()
|
||||
// )
|
||||
// .unwrap();
|
||||
|
||||
let find_elem_in_stream =
|
||||
|stream: &dyn LogStream, elem: &Arc<LogEntry>| -> Option<Box<dyn LogStream>> {
|
||||
let mut temp_stream = stream.clone();
|
||||
while let Some((curr, _)) = temp_stream.next(self.filters.get()) {
|
||||
if Arc::ptr_eq(&curr, elem) {
|
||||
return Some(temp_stream);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
};
|
||||
|
||||
let mut current_stream = self.filtered_root_stream();
|
||||
let mut new_stack = Vec::<LogView>::new();
|
||||
let mut missing = false;
|
||||
|
||||
for (elem, old_offset) in offsets_list {
|
||||
let Some((elem, inline_depth)) = elem else {
|
||||
// writeln!(&mut log, "no elem").unwrap();
|
||||
break;
|
||||
};
|
||||
// writeln!(
|
||||
// &mut log,
|
||||
// "reconstruction {:?} prev at {old_offset:?}",
|
||||
// elem.message_or_name()
|
||||
// )
|
||||
// .unwrap();
|
||||
|
||||
// find the nearest stream in which this element can be found
|
||||
let mut curr = current_stream.as_ref();
|
||||
let mut parents = new_stack.iter().rev();
|
||||
let mut found_in_toplevel = true;
|
||||
let mut stream = loop {
|
||||
// writeln!(&mut log, "find in stream").unwrap();
|
||||
if let Some(stream) = find_elem_in_stream(curr, &elem) {
|
||||
// writeln!(&mut log, "found (toplevel={found_in_toplevel})").unwrap();
|
||||
break stream;
|
||||
}
|
||||
found_in_toplevel = false;
|
||||
|
||||
if let Some(parent) = parents.next() {
|
||||
// writeln!(&mut log, "searching parent").unwrap();
|
||||
curr = parent.iter.as_ref();
|
||||
continue;
|
||||
}
|
||||
|
||||
// writeln!(&mut log, "no more parents").unwrap();
|
||||
missing = true;
|
||||
|
||||
// TODO: better guess at how far down we need to go
|
||||
// now its just 0
|
||||
|
||||
break current_stream;
|
||||
};
|
||||
|
||||
let offset = if found_in_toplevel {
|
||||
let mut offset = 0;
|
||||
for _ in 0..old_offset {
|
||||
if stream.prev(self.filters.get()).is_none() {
|
||||
break;
|
||||
}
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
offset
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
// writeln!(&mut log, "new offset: {offset:?}").unwrap();
|
||||
|
||||
let nested_stream = elem.from_start(inline_depth);
|
||||
new_stack.push(LogView {
|
||||
iter: stream,
|
||||
selection_offset: offset,
|
||||
});
|
||||
|
||||
// If we found a missing entry, don't try deeper entries,
|
||||
// they won't exist
|
||||
if missing {
|
||||
break;
|
||||
} else if let Some(nested_stream) = nested_stream {
|
||||
current_stream = nested_stream.with_filters(self.filters.get());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// writeln!(
|
||||
// &mut log,
|
||||
// "{:?}",
|
||||
// new_stack
|
||||
// .iter()
|
||||
// .map(|i| (
|
||||
// i.iter
|
||||
// .clone()
|
||||
// .next(self.filters.get())
|
||||
// .map(|i| i.0.message_or_name()),
|
||||
// i.selection_offset
|
||||
// ))
|
||||
// .collect::<Vec<_>>()
|
||||
// )
|
||||
// .unwrap();
|
||||
|
||||
// Take the top of the stack as `curr`, unless
|
||||
// the new stack is empty, then just reset the current view.
|
||||
if let Some(curr) = new_stack.pop() {
|
||||
// writeln!(&mut log, "popped new curr off stack").unwrap();
|
||||
self.curr = curr;
|
||||
} else {
|
||||
// writeln!(&mut log, "reconstructing root, somehow").unwrap();
|
||||
self.curr = LogView {
|
||||
iter: self.filtered_root_stream().clone(),
|
||||
selection_offset: self.stack.first().unwrap_or(&self.curr).selection_offset,
|
||||
};
|
||||
}
|
||||
self.stack = new_stack;
|
||||
}
|
||||
|
||||
pub fn add_filter(&mut self, filter: Arc<Filter>) {
|
||||
let old_filters = self.filters.clone();
|
||||
self.filters.push(Arc::clone(&filter));
|
||||
self.update_filters(old_filters.get());
|
||||
if !self.view.cursor.update_with_parents(&self.filters) {
|
||||
panic!("no log entries");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_num_items(&mut self, num_visible_items: usize) {
|
||||
while self.curr.selection_offset >= num_visible_items {
|
||||
if self.curr.selection_offset == 0 {
|
||||
while self.view.selection_offset >= num_visible_items {
|
||||
if self.view.selection_offset == 0 {
|
||||
break;
|
||||
}
|
||||
self.curr.iter.next(self.filters.get());
|
||||
self.curr.selection_offset -= 1;
|
||||
self.view.cursor.next(&self.filters);
|
||||
self.view.selection_offset -= 1;
|
||||
}
|
||||
self.last_height = num_visible_items;
|
||||
}
|
||||
|
|
@ -229,7 +76,7 @@ impl LogViewer {
|
|||
if let Some((selected, _)) = self.selected() {
|
||||
let ret = match selected.as_ref() {
|
||||
LogEntry::Single { .. } => Default::default(),
|
||||
LogEntry::Sub { sub_entries, .. } => sub_entries.last().and_then(|i| {
|
||||
LogEntry::Sub { children, .. } => children.last_child.as_ref().and_then(|i| {
|
||||
i.all_fields()
|
||||
.get_key_value("return")
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
|
|
@ -247,32 +94,33 @@ impl LogViewer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn items(&mut self, max: usize) -> Option<(Vec<(Arc<LogEntry>, usize)>, usize)> {
|
||||
let mut temp_iter = self.curr.iter.clone();
|
||||
pub fn items(&mut self, max: usize) -> Option<(Vec<(Gc<LogEntry>, usize)>, usize)> {
|
||||
let mut temp_iter = self.view.cursor.clone();
|
||||
let mut res = Vec::new();
|
||||
for _ in 0..max {
|
||||
let Some(i) = temp_iter.next(self.filters.get()) else {
|
||||
if !temp_iter.next(&self.filters) {
|
||||
break;
|
||||
};
|
||||
res.push(i);
|
||||
}
|
||||
// TODO: inline depth
|
||||
res.push((temp_iter.curr(), 0));
|
||||
}
|
||||
|
||||
if res.len() > 0 && self.curr.selection_offset > res.len() - 1 {
|
||||
self.curr.selection_offset = res.len() - 1;
|
||||
if !res.is_empty() && self.view.selection_offset > res.len() - 1 {
|
||||
self.view.selection_offset = res.len() - 1;
|
||||
}
|
||||
|
||||
Some((res, self.curr.selection_offset))
|
||||
Some((res, self.view.selection_offset))
|
||||
}
|
||||
|
||||
pub fn click(&mut self, row: u16) {
|
||||
if row as usize >= self.last_offset {
|
||||
let row_in_list = row as usize - self.last_offset;
|
||||
if row_in_list < self.last_height {
|
||||
if self.curr.selection_offset == row_in_list {
|
||||
if self.view.selection_offset == row_in_list {
|
||||
self.input_state = InputState::None;
|
||||
self.enter();
|
||||
} else {
|
||||
self.curr.selection_offset = row_in_list;
|
||||
self.view.selection_offset = row_in_list;
|
||||
self.input_state = InputState::Target(InputTarget::This);
|
||||
}
|
||||
}
|
||||
|
|
@ -288,8 +136,8 @@ impl LogViewer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn selected(&self) -> Option<(Arc<LogEntry>, usize)> {
|
||||
self.curr.selected(self.filters.get())
|
||||
pub fn selected(&self) -> Option<(Gc<LogEntry>, usize)> {
|
||||
self.view.selected(&self.filters)
|
||||
}
|
||||
|
||||
pub fn prev(&mut self) {
|
||||
|
|
@ -299,10 +147,10 @@ impl LogViewer {
|
|||
self.input_state = InputState::Target(InputTarget::Fields(None));
|
||||
}
|
||||
_ => {
|
||||
if self.curr.selection_offset == 0 {
|
||||
let _ = self.curr.iter.prev(self.filters.get());
|
||||
if self.view.selection_offset == 0 {
|
||||
self.view.cursor.prev(&self.filters);
|
||||
} else {
|
||||
self.curr.selection_offset -= 1;
|
||||
self.view.selection_offset -= 1;
|
||||
}
|
||||
self.input_state = InputState::None;
|
||||
}
|
||||
|
|
@ -316,23 +164,23 @@ impl LogViewer {
|
|||
self.input_state = InputState::Target(InputTarget::Fields(None));
|
||||
}
|
||||
_ => {
|
||||
self.curr.selection_offset += 1;
|
||||
self.view.selection_offset += 1;
|
||||
self.input_state = InputState::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn page_down(&mut self) {
|
||||
self.curr.selection_offset += self.last_height;
|
||||
self.view.selection_offset += self.last_height;
|
||||
self.input_state.reset();
|
||||
}
|
||||
|
||||
pub fn page_up(&mut self) {
|
||||
for _ in 0..self.last_height {
|
||||
if self.curr.selection_offset == 0 {
|
||||
let _ = self.curr.iter.prev(self.filters.get());
|
||||
if self.view.selection_offset == 0 {
|
||||
let _ = self.view.cursor.prev(&self.filters);
|
||||
} else {
|
||||
self.curr.selection_offset -= 1;
|
||||
self.view.selection_offset -= 1;
|
||||
}
|
||||
}
|
||||
self.input_state.reset();
|
||||
|
|
@ -345,67 +193,62 @@ impl LogViewer {
|
|||
self.input_state = InputState::Target(InputTarget::Fields(None));
|
||||
}
|
||||
_ => {
|
||||
self.curr.selection_offset = 0;
|
||||
while self.curr.iter.prev(self.filters.get()).is_some() {}
|
||||
self.view.selection_offset = 0;
|
||||
while self.view.cursor.prev(&self.filters) {}
|
||||
self.input_state = InputState::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(&self) -> Vec<usize> {
|
||||
self.stack.iter().map(|i| i.selection_offset).collect()
|
||||
}
|
||||
|
||||
pub fn back(&mut self) {
|
||||
self.cache.insert(self.path(), self.curr.clone());
|
||||
if let Some(stack) = self.stack.pop() {
|
||||
self.curr = stack;
|
||||
}
|
||||
self.view.cursor.exit(&self.filters);
|
||||
// self.cache.insert(self.path(), self.curr.clone());
|
||||
self.input_state.reset();
|
||||
}
|
||||
|
||||
pub fn undo(&mut self) {
|
||||
let old_filters = self.filters.clone();
|
||||
self.filters.undo();
|
||||
self.update_filters(old_filters.get());
|
||||
}
|
||||
|
||||
pub fn redo(&mut self) {
|
||||
let old_filters = self.filters.clone();
|
||||
self.filters.redo();
|
||||
self.update_filters(old_filters.get());
|
||||
}
|
||||
|
||||
pub fn enter(&mut self) {
|
||||
match self.input_state {
|
||||
InputState::None => {
|
||||
let Some((s, _)) = self.selected() else {
|
||||
return;
|
||||
};
|
||||
let Some(i) = s.from_start(0).map(|i| i.with_filters(self.filters.get())) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if i.clone().next(self.filters.get()).is_none() {
|
||||
return;
|
||||
}
|
||||
if i.clone()
|
||||
.next(self.filters.get())
|
||||
.is_some_and(|(i, _)| i.is_return())
|
||||
{
|
||||
return;
|
||||
for _ in 0..(self.view.selection_offset + 1) {
|
||||
self.view.cursor.next(&self.filters);
|
||||
}
|
||||
|
||||
self.stack.push(mem::replace(
|
||||
&mut self.curr,
|
||||
LogView {
|
||||
iter: i,
|
||||
selection_offset: 0,
|
||||
},
|
||||
));
|
||||
if let Some(cached_view) = self.cache.get(&self.path()) {
|
||||
self.curr = cached_view.clone();
|
||||
}
|
||||
self.view.cursor.enter(&self.filters);
|
||||
// let Some((s, _)) = self.selected() else {
|
||||
// return;
|
||||
// };
|
||||
// let Some(i) = s.from_start(0).map(|i| i.with_filters(self.filters.get())) else {
|
||||
// return;
|
||||
// };
|
||||
//
|
||||
// if i.clone().next(self.filters.get()).is_none() {
|
||||
// return;
|
||||
// }
|
||||
// if i.clone()
|
||||
// .next(self.filters.get())
|
||||
// .is_some_and(|(i, _)| i.is_return())
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// self.stack.push(mem::replace(
|
||||
// &mut self.curr,
|
||||
// LogView {
|
||||
// cursor: i,
|
||||
// selection_offset: 0,
|
||||
// },
|
||||
// ));
|
||||
// if let Some(cached_view) = self.cache.get(&self.path()) {
|
||||
// self.curr = cached_view.clone();
|
||||
// }
|
||||
}
|
||||
InputState::Target(InputTarget::Fields(None)) => {
|
||||
self.input_state =
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue