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()),
)?,
}),
InputTarget::This => lv
InputTarget::This => {
if lv
.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!(),
}
}

View file

@ -102,7 +102,7 @@ impl LogViewer {
break;
}
// 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 {

View file

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

View file

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

View file

@ -1,4 +1,4 @@
use std::mem;
use std::{iter, mem};
use dumpster::sync::Gc;
use itertools::Itertools;
@ -62,14 +62,13 @@ impl Cursor {
let old = self.clone();
// if the current one is inlined
if self.curr_is_inlined(filters) {
// try going one back
self.prev(filters);
self.next(filters);
while self.curr_is_inlined(filters) {
self.enter_start_internal(filters);
self.curr.continue_in_parent = true;
}
// 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
if self.next(filters) {
return true;
@ -98,6 +97,15 @@ impl Cursor {
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>> {
self.parents.last().map(|i| i.entry.clone())
}
@ -118,7 +126,11 @@ impl Cursor {
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 {
return false;
};
@ -132,7 +144,11 @@ impl Cursor {
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 {
return false;
};
@ -223,7 +239,7 @@ impl Cursor {
return true;
}
self.enter_end_internal();
self.enter_end_internal(filters);
self.curr.continue_in_parent = true;
self.update(filters)
}
@ -237,14 +253,14 @@ impl Cursor {
return true;
}
self.enter_start_internal();
self.enter_start_internal(filters);
self.curr.continue_in_parent = true;
self.update(filters)
}
pub fn enter(&mut self, filters: &Filters) -> bool {
let before = self.clone();
if !self.enter_start_internal() {
if !self.enter_start_internal(filters) {
return false;
}

View file

@ -107,6 +107,7 @@ impl LogFileEntryGenerator {
struct Inner {
pub path: PathBuf,
#[allow(unused)]
pub jh: Option<JoinHandle<()>>,
first: Option<Gc<LogEntry>>,
}
@ -435,4 +436,42 @@ mod tests {
assert_eq!(c.curr().message_or_name(), Some("foo".to_string()));
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 itertools::Itertools;
use ratatui::widgets::{List, ListItem, Widget};
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> {
items: Vec<(Gc<LogEntry>, usize)>,
selected_offset: usize,
@ -132,18 +59,25 @@ impl Widget for Styled<'_, &Items<'_>> {
where
Self: Sized,
{
let ts = TreeState::from_items(&self.items);
let regex_cache = OnceCell::new();
let list = List::new(
self.inner
.items
.iter()
.map(|(entry, _)| entry)
.enumerate()
.zip(ts.tree_prefixes)
.map(|((idx, entry), tree)| {
let line_text = entry.line_text(tree, self.filters);
.map(|(idx, (entry, depth))| {
let prefix = if *depth == 0 {
"".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);
if idx == self.selected_offset