fix some (but far from all) visual bugs

This commit is contained in:
Jana Dönszelmann 2026-03-31 17:03:41 +02:00
parent d0bc7e952c
commit b668a894c7
No known key found for this signature in database
7 changed files with 114 additions and 104 deletions

View file

@ -90,9 +90,17 @@ impl Matcher {
lv.selected().and_then(|(i, _)| i.message_or_name()), lv.selected().and_then(|(i, _)| i.message_or_name()),
)?, )?,
}), }),
InputTarget::This => lv InputTarget::This => {
if lv
.selected() .selected()
.map(|(i, _)| Self::Specific { hash: i.hash() }), .is_none_or(|(i, _)| !i.can_be_inlined(&lv.filters))
{
return None;
}
lv.selected()
.map(|(i, _)| Self::Specific { hash: i.hash() })
}
InputTarget::Surround => todo!(), InputTarget::Surround => todo!(),
} }
} }

View file

@ -102,7 +102,7 @@ impl LogViewer {
break; break;
} }
// TODO: inline depth // TODO: inline depth
res.push((temp_iter.curr(), 0)); res.push((temp_iter.curr(), temp_iter.inline_depth()));
} }
if !res.is_empty() && self.view.selection_offset > res.len() - 1 { if !res.is_empty() && self.view.selection_offset > res.len() - 1 {

View file

@ -641,9 +641,9 @@ impl Widget for &mut App {
{ {
let full_file_path = canonical_rustc_root.join(&file); let full_file_path = canonical_rustc_root.join(&file);
Hyperlink::new( Hyperlink::new(
Line::from(format!("In file: {}", file.display())) Line::from(format!("In file: {}:{line}", file.display()))
.style(styles.default), .style(styles.default),
format!("file://{}:{line}", full_file_path.display()), format!("file://{}", full_file_path.display()),
) )
.render(first_line, buf); .render(first_line, buf);
} else { } else {

View file

@ -115,9 +115,14 @@ impl LogEntry {
false false
} }
pub fn can_be_inlined(&self, filters: &Filters) -> bool {
self.can_enter(filters)
}
pub fn is_inlined(&self, filters: &Filters) -> bool { pub fn is_inlined(&self, filters: &Filters) -> bool {
for f in filters.get() { for f in filters.get() {
if let FilterKind::Inline = f.kind if let FilterKind::Inline = f.kind
&& self.can_be_inlined(filters)
&& f.matcher.matches(self) && f.matcher.matches(self)
{ {
return true; return true;
@ -177,7 +182,7 @@ impl LogEntry {
.all_children_cache .all_children_cache
.lock() .lock()
.unwrap() .unwrap()
.get(&id(&first_child)) .get(&id(first_child))
.copied() .copied()
}; };
if let Some(cached) = cached { if let Some(cached) = cached {
@ -194,7 +199,7 @@ impl LogEntry {
.all_children_cache .all_children_cache
.lock() .lock()
.unwrap() .unwrap()
.insert(id(&first_child), count); .insert(id(first_child), count);
count count
} }
@ -255,6 +260,16 @@ impl LogEntry {
} }
} }
pub fn has_only_return(&self) -> bool {
matches!(self, Self::Sub { children, .. } if children.first_child.as_ref().is_some_and(|i| i.is_return()))
}
pub fn can_enter(&self, filters: &Filters) -> bool {
matches!(self, Self::Sub { children, .. } if children.first_child.is_some())
&& !self.has_only_return()
&& self.all_children(filters) != 0
}
pub fn line_text(&self, tree: String, filters: &Filters) -> LineText { pub fn line_text(&self, tree: String, filters: &Filters) -> LineText {
const NO_MESSAGE: &str = "<no message>"; const NO_MESSAGE: &str = "<no message>";
const SPACES_BEFORE: &str = " "; const SPACES_BEFORE: &str = " ";
@ -281,14 +296,12 @@ impl LogEntry {
self.message_or_name(), self.message_or_name(),
tree, tree,
), ),
LogEntry::Sub { LogEntry::Sub { enter, .. } => {
enter, children, ..
} => {
if let Some(val) = enter.all_fields().fields.get("name") { if let Some(val) = enter.all_fields().fields.get("name") {
let (prefix, sym) = if children.first_child.is_none() let (prefix, sym) = if self.has_only_return() {
|| children.first_child.as_ref().is_some_and(|i| i.is_return())
{
(SPACES_BEFORE.to_string(), "") (SPACES_BEFORE.to_string(), "")
} else if !self.can_enter(filters) {
(SPACES_BEFORE.to_string(), "")
} else { } else {
( (
format!( format!(

View file

@ -1,4 +1,4 @@
use std::mem; use std::{iter, mem};
use dumpster::sync::Gc; use dumpster::sync::Gc;
use itertools::Itertools; use itertools::Itertools;
@ -62,14 +62,13 @@ impl Cursor {
let old = self.clone(); let old = self.clone();
// if the current one is inlined // if the current one is inlined
if self.curr_is_inlined(filters) { while self.curr_is_inlined(filters) {
// try going one back self.enter_start_internal(filters);
self.prev(filters); self.curr.continue_in_parent = true;
self.next(filters);
} }
// if the current one is removed... // if the current one is removed...
if self.curr_is_removed(filters) || self.curr_is_inlined(filters) { if self.curr_is_removed(filters) {
// try going forwards to the next visible node // try going forwards to the next visible node
if self.next(filters) { if self.next(filters) {
return true; return true;
@ -98,6 +97,15 @@ impl Cursor {
self.curr.entry.clone() self.curr.entry.clone()
} }
pub fn inline_depth(&self) -> usize {
self.parents
.iter()
.chain(iter::once(&self.curr))
.rev()
.take_while(|i| i.continue_in_parent)
.count()
}
pub fn parent(&self) -> Option<Gc<LogEntry>> { pub fn parent(&self) -> Option<Gc<LogEntry>> {
self.parents.last().map(|i| i.entry.clone()) self.parents.last().map(|i| i.entry.clone())
} }
@ -118,7 +126,11 @@ impl Cursor {
true true
} }
fn enter_start_internal(&mut self) -> bool { fn enter_start_internal(&mut self, filters: &Filters) -> bool {
if !self.curr().can_enter(filters) {
return false;
}
let Some(new) = self.curr().first_child() else { let Some(new) = self.curr().first_child() else {
return false; return false;
}; };
@ -132,7 +144,11 @@ impl Cursor {
true true
} }
fn enter_end_internal(&mut self) -> bool { fn enter_end_internal(&mut self, filters: &Filters) -> bool {
if !self.curr().can_enter(filters) {
return false;
}
let Some(new) = self.curr().last_child() else { let Some(new) = self.curr().last_child() else {
return false; return false;
}; };
@ -223,7 +239,7 @@ impl Cursor {
return true; return true;
} }
self.enter_end_internal(); self.enter_end_internal(filters);
self.curr.continue_in_parent = true; self.curr.continue_in_parent = true;
self.update(filters) self.update(filters)
} }
@ -237,14 +253,14 @@ impl Cursor {
return true; return true;
} }
self.enter_start_internal(); self.enter_start_internal(filters);
self.curr.continue_in_parent = true; self.curr.continue_in_parent = true;
self.update(filters) self.update(filters)
} }
pub fn enter(&mut self, filters: &Filters) -> bool { pub fn enter(&mut self, filters: &Filters) -> bool {
let before = self.clone(); let before = self.clone();
if !self.enter_start_internal() { if !self.enter_start_internal(filters) {
return false; return false;
} }

View file

@ -107,6 +107,7 @@ impl LogFileEntryGenerator {
struct Inner { struct Inner {
pub path: PathBuf, pub path: PathBuf,
#[allow(unused)]
pub jh: Option<JoinHandle<()>>, pub jh: Option<JoinHandle<()>>,
first: Option<Gc<LogEntry>>, first: Option<Gc<LogEntry>>,
} }
@ -435,4 +436,42 @@ mod tests {
assert_eq!(c.curr().message_or_name(), Some("foo".to_string())); assert_eq!(c.curr().message_or_name(), Some("foo".to_string()));
assert!(!c.prev(&f)); assert!(!c.prev(&f));
} }
#[test]
fn inline_depth() {
let mut c = parse(
&[
with_fields(r#"{"message": "enter", "name": "nest1"}"#),
with_fields(r#"{"message": "enter", "name": "nest2"}"#),
with_fields(r#"{"message": "baz"}"#),
with_fields(r#"{"message": "exit"}"#),
with_fields(r#"{"message": "exit"}"#),
]
.join("\n"),
);
let mut f = filters();
c.enter(&f);
c.enter(&f);
assert_eq!(c.curr().message_or_name(), Some("baz".to_string()));
c.exit(&f);
c.exit(&f);
f.push(Arc::new(Filter {
matcher: Matcher::Field {
name: "name".to_string(),
value: MatcherValue::Exact("nest1".to_string()),
},
kind: FilterKind::Inline,
}));
f.push(Arc::new(Filter {
matcher: Matcher::Field {
name: "name".to_string(),
value: MatcherValue::Exact("nest2".to_string()),
},
kind: FilterKind::Inline,
}));
c.update_with_parents(&f);
assert_eq!(c.curr().message_or_name(), Some("baz".to_string()));
}
} }

View file

@ -1,7 +1,6 @@
use std::{cell::OnceCell, iter}; use std::cell::OnceCell;
use dumpster::sync::Gc; use dumpster::sync::Gc;
use itertools::Itertools;
use ratatui::widgets::{List, ListItem, Widget}; use ratatui::widgets::{List, ListItem, Widget};
use regex::Regex; use regex::Regex;
@ -18,78 +17,6 @@ use crate::tui::{
}, },
}; };
pub struct TreeState {
tree_prefixes: Vec<String>,
}
impl TreeState {
pub fn from_items(items: &[(Gc<LogEntry>, usize)]) -> Self {
let mut res = Vec::new();
let mut curr = String::new();
let mut prev_depth = 0;
for (depth, next_depth) in items
.iter()
.map(|i| i.1 as isize)
.chain(iter::once(-1isize))
.tuple_windows()
{
if depth > prev_depth {
if depth > 1 {
let _ = curr.pop();
let _ = curr.pop();
curr.push('│');
curr.push(' ');
}
for _ in prev_depth..depth.saturating_sub(2) {
curr.push(' ');
curr.push(' ');
}
if next_depth < depth {
curr.push('└');
curr.push('─');
} else {
curr.push('├');
curr.push('─');
}
} else if depth == prev_depth && next_depth < prev_depth {
let _ = curr.pop();
let _ = curr.pop();
curr.push('└');
curr.push('─');
} else if depth < prev_depth {
for _ in depth..prev_depth {
let _ = curr.pop();
let _ = curr.pop();
let _ = curr.pop();
let _ = curr.pop();
}
if depth > 0 {
let _ = curr.pop();
let _ = curr.pop();
if next_depth < depth {
curr.push('└');
curr.push('─');
} else {
curr.push('├');
curr.push('─');
}
}
}
prev_depth = depth;
if depth != 0 {
res.push(format!("{curr} "));
} else {
res.push(String::new());
}
}
Self { tree_prefixes: res }
}
}
pub struct Items<'a> { pub struct Items<'a> {
items: Vec<(Gc<LogEntry>, usize)>, items: Vec<(Gc<LogEntry>, usize)>,
selected_offset: usize, selected_offset: usize,
@ -132,18 +59,25 @@ impl Widget for Styled<'_, &Items<'_>> {
where where
Self: Sized, Self: Sized,
{ {
let ts = TreeState::from_items(&self.items);
let regex_cache = OnceCell::new(); let regex_cache = OnceCell::new();
let list = List::new( let list = List::new(
self.inner self.inner
.items .items
.iter() .iter()
.map(|(entry, _)| entry)
.enumerate() .enumerate()
.zip(ts.tree_prefixes) .map(|(idx, (entry, depth))| {
.map(|((idx, entry), tree)| { let prefix = if *depth == 0 {
let line_text = entry.line_text(tree, self.filters); "".to_string()
} else {
let mut res = String::new();
for _ in 0..*depth - 1 {
res.push_str(" ");
}
res.push_str("- ");
res
};
let line_text = entry.line_text(prefix, self.filters);
let mut line = line_text.styled(&self.styles); let mut line = line_text.styled(&self.styles);
if idx == self.selected_offset if idx == self.selected_offset