From 2cd19c98aed98d0074eb1f04d38de9577e63afe1 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 2 Jan 2025 01:08:48 +0100 Subject: [PATCH] cli: use new diff technology too --- src/analyses/compute_rules.rs | 18 +++++++------- src/ast/printer.rs | 40 +++++++++++++++++++++---------- src/ast/printer/display_tree.rs | 2 +- src/cli.rs | 42 ++++++++++++++++++++++++--------- src/wasm.rs | 6 ++--- 5 files changed, 71 insertions(+), 37 deletions(-) diff --git a/src/analyses/compute_rules.rs b/src/analyses/compute_rules.rs index 567aaa0..2fcc681 100644 --- a/src/analyses/compute_rules.rs +++ b/src/analyses/compute_rules.rs @@ -337,7 +337,7 @@ impl RenderablePredicate<'_> { } /// Intermediate representation used in the display process. -pub(crate) struct RenderableTypingRule<'a> { +struct RenderableTypingRule<'a> { name: Rule, preconditions: Vec>, postconditions: Vec>, @@ -353,7 +353,7 @@ impl<'a> TypingRule<'a> { cstrs } - pub(crate) fn make_renderable( + fn make_renderable( &'a self, _a: &'a Arenas<'a>, style: PredicateStyle, @@ -444,19 +444,21 @@ impl<'a> TypingRule<'a> { options: self.options, }) } + pub fn display_to_tree<'d>( + &self, + a: &'d Arenas<'d>, + style: PredicateStyle, + ) -> Result, IncompatibleStyle> { + Ok(self.make_renderable(a, style)?.display_to_tree(a, style)) + } pub fn display(&self, style: PredicateStyle) -> Result { let a = &Arenas::default(); - Ok(self.make_renderable(a, style)?.display(style)) + Ok(self.display_to_tree(a, style)?.to_string()) } } impl<'a> RenderableTypingRule<'a> { - pub fn display(&self, style: PredicateStyle) -> String { - let a = &Arenas::default(); - self.display_to_tree(a, style).to_string() - } - pub fn display_to_tree<'d>(&self, a: &'d Arenas<'d>, style: PredicateStyle) -> DisplayTree<'d> { let preconditions = DisplayTree::sep_by( a, diff --git a/src/ast/printer.rs b/src/ast/printer.rs index 0583e9b..02f9192 100644 --- a/src/ast/printer.rs +++ b/src/ast/printer.rs @@ -6,7 +6,7 @@ use crate::*; pub mod display_tree; pub use display_tree::*; -pub trait Style { +pub trait Style: Display + AsRef { fn green(&self) -> String; fn red(&self) -> String; fn comment(&self) -> String; @@ -14,60 +14,74 @@ pub trait Style { fn tooltip(&self, text: &str) -> String; fn inherited_ref(&self) -> String; fn code(&self) -> String; + + fn wrap_in_tag(&self, tag_name: &str, tag_args: Option<(&str, &str)>) -> String { + let tag_args = tag_args + .map(|(k, v)| format!("{k}=\"{v}\"")) + .unwrap_or_default(); + format!("<{tag_name} {tag_args}>{self}") + } + fn span_style(&self, style: &str) -> String { + self.wrap_in_tag("span", Some(("style", style))) + } + fn apply_colorize<'a>(&'a self, f: impl Fn(&'a str) -> colored::ColoredString) -> String { + // Apply line-by-line so that we can split by line later without messing up escape codes. + self.as_ref().lines().map(|line| f(line)).join("\n") + } } impl> Style for T { fn green(&self) -> String { if cfg!(target_arch = "wasm32") { - format!("{self}") + self.span_style("color: green") } else { use colored::Colorize; - <_ as Colorize>::green(self.as_ref()).to_string() + self.apply_colorize(<_ as Colorize>::green) } } fn red(&self) -> String { if cfg!(target_arch = "wasm32") { - format!("{self}") + self.span_style("color: red") } else { use colored::Colorize; - <_ as Colorize>::red(self.as_ref()).to_string() + self.apply_colorize(<_ as Colorize>::red) } } fn dimmed(&self) -> String { if cfg!(target_arch = "wasm32") { - format!("{self}") + self.span_style("opacity: 0.5") } else { use colored::Colorize; - <_ as Colorize>::dimmed(self.as_ref()).to_string() + self.apply_colorize(<_ as Colorize>::dimmed) } } fn comment(&self) -> String { if cfg!(target_arch = "wasm32") { - format!("{self}") + self.span_style("color: dimgray") } else { use colored::Colorize; - <_ as Colorize>::dimmed(self.as_ref()).to_string() + self.apply_colorize(<_ as Colorize>::dimmed) } } fn tooltip(&self, text: &str) -> String { if cfg!(target_arch = "wasm32") { - format!("{self}") + self.wrap_in_tag("span", Some(("title", text))) } else { self.to_string() } } fn inherited_ref(&self) -> String { if cfg!(target_arch = "wasm32") { - format!("{self}") + self.wrap_in_tag("span", Some(("class", "inherited-ref"))) } else { use colored::Colorize; - <_ as Colorize>::dimmed(self.as_ref()).to_string() + self.apply_colorize(<_ as Colorize>::dimmed) } .tooltip("inherited reference") } fn code(&self) -> String { if cfg!(target_arch = "wasm32") { - format!("{self}") + self.wrap_in_tag("code", None) } else { format!("`{self}`") } diff --git a/src/ast/printer/display_tree.rs b/src/ast/printer/display_tree.rs index fdbb485..7cd8ca2 100644 --- a/src/ast/printer/display_tree.rs +++ b/src/ast/printer/display_tree.rs @@ -31,7 +31,7 @@ pub struct DisplayTree<'a> { /// Compute the length of this string as displayed on the screen (i.e. ignoring html tags or ansi /// escapes, depending on context). -fn len_ignoring_markup(s: &str) -> usize { +pub(crate) fn len_ignoring_markup(s: &str) -> usize { if cfg!(target_arch = "wasm32") { // Compute string length skipping html tags. let mut in_tag = false; diff --git a/src/cli.rs b/src/cli.rs index 1ea821f..a15ea69 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -106,23 +106,26 @@ impl CliState { right: RuleOptions, ) -> Result { let style = self.predicate_style; - let arenas = &Arenas::default(); + let a = &Arenas::default(); let type_of_interest = style.type_of_interest(); - let joint_rules = compute_joint_rules(arenas, type_of_interest, left, right); + let joint_rules = compute_joint_rules(a, type_of_interest, left, right); let mut out = String::new(); for joint_rule in joint_rules { - let (left, right) = joint_rule.left_and_right(); + let (left, right) = joint_rule.as_ref().left_and_right(); let left = left - .map(|r| r.display(style)) - .transpose()? + .map(|r| r.display_to_tree(a, style).unwrap()) .unwrap_or_default(); let right = right - .map(|r| r.display(style)) - .transpose()? + .map(|r| r.display_to_tree(a, style).unwrap()) .unwrap_or_default(); - out += &DiffState::side_by_side(&left, &right); - let _ = writeln!(&mut out); + let (mut left, mut right, has_diff) = left.diff_display_has_diff(&right); + if !has_diff { + left = left.dimmed(); + right = right.dimmed(); + } + out += &side_by_side(&left, &right); + out += "\n"; } Ok(out) } @@ -413,6 +416,19 @@ pub fn display_rules( Ok(out) } +fn side_by_side(left: &str, right: &str) -> String { + let mut out = String::new(); + for x in left.lines().zip_longest(right.lines()) { + let (l, r) = x.or("", ""); + let pad = 80usize + .checked_sub(len_ignoring_markup(l)) + .unwrap_or_default(); + let pad = " ".repeat(pad); + let _ = writeln!(&mut out, " {l}{pad} | {r}"); + } + out +} + #[derive(Clone, Copy, PartialEq, Eq)] enum DiffState { Both, @@ -439,13 +455,17 @@ impl DiffState { use DiffState::*; let mut out = String::new(); for x in left.lines().zip_longest(right.lines()) { - let (l, r) = x.or(" ", " "); + let (l, r) = x.or("", ""); + let pad = 80usize + .checked_sub(len_ignoring_markup(l)) + .unwrap_or_default(); + let pad = " ".repeat(pad); let same = l == r; let l_state = if same { Both } else { Old }; let r_state = if same { Both } else { New }; let l = l_state.color_line(l); let r = r_state.color_line(r); - let _ = writeln!(&mut out, " {l:80} | {r}"); + let _ = writeln!(&mut out, " {l}{pad} | {r}"); } out } diff --git a/src/wasm.rs b/src/wasm.rs index ae3035a..47d6936 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -278,12 +278,10 @@ pub fn display_joint_rules_js( .map(|joint_rule| { let (left, right) = joint_rule.as_ref().left_and_right(); let left = left - .map(|r| r.make_renderable(a, style).unwrap()) - .map(|r| r.display_to_tree(a, style)) + .map(|r| r.display_to_tree(a, style).unwrap()) .unwrap_or_default(); let right = right - .map(|r| r.make_renderable(a, style).unwrap()) - .map(|r| r.display_to_tree(a, style)) + .map(|r| r.display_to_tree(a, style).unwrap()) .unwrap_or_default(); let (mut left, mut right, has_diff) = left.diff_display_has_diff(&right); if !has_diff {