Skip to content

Commit 923b53d

Browse files
committed
Pretty diff expressions
1 parent 1d6f71e commit 923b53d

File tree

2 files changed

+115
-32
lines changed

2 files changed

+115
-32
lines changed

src/ast/printer.rs

+72-16
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,73 @@ impl<'d> ToDisplayTree<'d> for Type<'_> {
156156
}
157157
}
158158

159+
impl<'d> ToDisplayTree<'d> for ExprKind<'_> {
160+
fn to_display_tree(&self, a: &'d Arenas<'d>) -> DisplayTree<'d> {
161+
enum Symbol {
162+
Deref,
163+
Ref(Mutability),
164+
}
165+
/// Remove the refs and derefs in front of this expression.
166+
fn strip_symbols<'a>(e: &'a ExprKind<'a>, symbols: &mut Vec<Symbol>) -> &'a ExprKind<'a> {
167+
match e {
168+
ExprKind::Ref(mtbl, e) => {
169+
symbols.push(Symbol::Ref(*mtbl));
170+
strip_symbols(&e.kind, symbols)
171+
}
172+
ExprKind::Deref(e) => {
173+
symbols.push(Symbol::Deref);
174+
strip_symbols(&e.kind, symbols)
175+
}
176+
_ => &e,
177+
}
178+
}
179+
let mut symbols = Vec::new();
180+
let e = strip_symbols(self, &mut symbols);
181+
let leaf = match e {
182+
ExprKind::Scrutinee => "s".to_display_tree(a),
183+
ExprKind::Abstract { .. } => "e".to_display_tree(a),
184+
ExprKind::Field(e, n) => {
185+
let needs_parens = matches!(e.kind, ExprKind::Deref(..));
186+
let (before, after) = if needs_parens { ("(", ")") } else { ("", "") };
187+
DisplayTree::sep_by(
188+
a,
189+
"",
190+
[
191+
before.to_display_tree(a),
192+
e.to_display_tree(a),
193+
after.to_display_tree(a),
194+
".".to_display_tree(a),
195+
format!("{n}").to_display_tree(a),
196+
],
197+
)
198+
}
199+
ExprKind::Ref(..) | ExprKind::Deref(..) => unreachable!(),
200+
};
201+
// We cleverly diff expressions: expressions tend to start the same then diverge; so we
202+
// want to show that the innermost expressions are the same and the surrounding `&`/`*`
203+
// differ. To do this, we extract the list of `&`/`*` and add them to the same list, with
204+
// `Suffix` compare mode.
205+
DisplayTree::sep_by_compare_mode(
206+
a,
207+
"",
208+
symbols
209+
.iter()
210+
.map(|s| match s {
211+
Symbol::Deref => "*".to_display_tree(a),
212+
Symbol::Ref(mutable) => format!("&{mutable}").to_display_tree(a),
213+
})
214+
.chain([leaf]),
215+
CompareMode::Suffix,
216+
)
217+
}
218+
}
219+
220+
impl<'d> ToDisplayTree<'d> for Expression<'_> {
221+
fn to_display_tree(&self, a: &'d Arenas<'d>) -> DisplayTree<'d> {
222+
self.kind.to_display_tree(a)
223+
}
224+
}
225+
159226
impl<'d> ToDisplayTree<'d> for BindingAssignments<'_> {
160227
fn to_display_tree(&self, a: &'d Arenas<'d>) -> DisplayTree<'d> {
161228
DisplayTree::sep_by(
@@ -227,7 +294,7 @@ impl<'a> TypingPredicate<'a> {
227294
.pat
228295
.to_display_tree(a)
229296
.sep_then(a, ": ", self.expr.ty)
230-
.sep_then(a, " = ", self.expr.to_string())
297+
.sep_then(a, " = ", self.expr)
231298
.preceded(a, "let "),
232299
PredicateStyle::Sequent {
233300
ty: toi,
@@ -385,26 +452,15 @@ impl Display for Type<'_> {
385452

386453
impl Display for Expression<'_> {
387454
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
388-
write!(f, "{}", self.kind)
455+
let a = &Arenas::default();
456+
write!(f, "{}", self.to_display_tree(a))
389457
}
390458
}
391459

392460
impl Display for ExprKind<'_> {
393461
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
394-
match self {
395-
ExprKind::Scrutinee => write!(f, "s"),
396-
ExprKind::Abstract { .. } => write!(f, "e"),
397-
ExprKind::Ref(mutable, e) => write!(f, "&{mutable}{e}"),
398-
ExprKind::Deref(e) => write!(f, "*{e}"),
399-
ExprKind::Field(e, n) => {
400-
let needs_parens = matches!(e.kind, ExprKind::Deref(..));
401-
if needs_parens {
402-
write!(f, "({e}).{n}")
403-
} else {
404-
write!(f, "{e}.{n}")
405-
}
406-
}
407-
}
462+
let a = &Arenas::default();
463+
write!(f, "{}", self.to_display_tree(a))
408464
}
409465
}
410466

src/ast/printer/display_tree.rs

+43-16
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ use itertools::Itertools;
55
use crate::*;
66
use DisplayTreeKind::*;
77

8+
/// How to compare two separated lists of different lengths.
9+
#[derive(Clone, Copy, PartialEq, Eq)]
10+
pub enum CompareMode {
11+
/// We consider the whole subtree to differ.
12+
None,
13+
/// The first elements are diffed together until exhaustion.
14+
Prefix,
15+
/// The last elements are diffed together until exhaustion.
16+
Suffix,
17+
}
18+
819
/// A structured representation of a string to be displayed. Used to compute structured diffs.
920
#[derive(Clone, Copy)]
1021
enum DisplayTreeKind<'a> {
@@ -17,10 +28,8 @@ enum DisplayTreeKind<'a> {
1728
sep: &'a str,
1829
/// The children.
1930
children: &'a [DisplayTree<'a>],
20-
/// If the lengths differ and this is `true`, the first elements are diffed together until
21-
/// exhaustion. If the lengths differ and this is `false`, we consider the whole subtree to
22-
/// differ.
23-
compare_common_prefix: bool,
31+
/// How to compare elements if the lengths differ.
32+
compare_mode: CompareMode,
2433
},
2534
}
2635

@@ -119,11 +128,11 @@ impl<'a> DisplayTree<'a> {
119128
Self::leaf_noalloc(a.alloc_str(s))
120129
}
121130

122-
fn mk_separated(
131+
pub fn sep_by_compare_mode(
123132
a: &'a Arenas<'a>,
124133
sep: &str,
125134
children: impl IntoIterator<Item: ToDisplayTree<'a>>,
126-
compare_common_prefix: bool,
135+
compare_mode: CompareMode,
127136
) -> Self {
128137
let children = children
129138
.into_iter()
@@ -132,7 +141,7 @@ impl<'a> DisplayTree<'a> {
132141
Self::new_from_kind(Separated {
133142
sep: a.alloc_str(sep),
134143
children: a.bump.alloc_slice_copy(&children),
135-
compare_common_prefix,
144+
compare_mode,
136145
})
137146
}
138147

@@ -141,15 +150,15 @@ impl<'a> DisplayTree<'a> {
141150
sep: &str,
142151
children: impl IntoIterator<Item: ToDisplayTree<'a>>,
143152
) -> Self {
144-
Self::mk_separated(a, sep, children, false)
153+
Self::sep_by_compare_mode(a, sep, children, CompareMode::None)
145154
}
146155

147156
pub fn sep_by_compare_prefix(
148157
a: &'a Arenas<'a>,
149158
sep: &str,
150159
children: impl IntoIterator<Item: ToDisplayTree<'a>>,
151160
) -> Self {
152-
Self::mk_separated(a, sep, children, true)
161+
Self::sep_by_compare_mode(a, sep, children, CompareMode::Prefix)
153162
}
154163

155164
/// Concatenates `self` and `x`, separated by `sep`.
@@ -235,26 +244,44 @@ impl<'a> DisplayTree<'a> {
235244
Separated {
236245
sep,
237246
children: c1,
238-
compare_common_prefix: ccp1,
247+
compare_mode: ccp1,
239248
},
240249
Separated {
241250
sep: sep2,
242251
children: c2,
243-
compare_common_prefix: ccp2,
252+
compare_mode: _, // we only look at one of tham
244253
},
245254
) if strip_markup(sep) == strip_markup(sep2)
246-
&& (c1.len() == c2.len() || ccp1 || ccp2) =>
255+
&& (c1.len() == c2.len() || ccp1 != CompareMode::None) =>
247256
{
257+
use std::iter::repeat_n;
258+
// Pad the two lists so they have the same length; pad at the start or end
259+
// depending on how we want to match the lists up.
260+
let len1 = c1.len();
261+
let len2 = c2.len();
262+
let len = std::cmp::max(len1, len2);
263+
let (prefix1, prefix2, suffix1, suffix2) = match ccp1 {
264+
CompareMode::None => (0, 0, 0, 0),
265+
CompareMode::Prefix => (0, 0, len - len1, len - len2),
266+
CompareMode::Suffix => (len - len1, len - len2, 0, 0),
267+
};
268+
let c1 = repeat_n(None, prefix1)
269+
.chain(c1.iter().copied().map(Some))
270+
.chain(repeat_n(None, suffix1));
271+
let c2 = repeat_n(None, prefix2)
272+
.chain(c2.iter().copied().map(Some))
273+
.chain(repeat_n(None, suffix2));
274+
248275
let mut is_first = true;
249276
let mut any_diff = false;
250-
for either_or_both in c1.iter().copied().zip_longest(c2.iter().copied()) {
251-
if !is_first && !either_or_both.is_right() {
277+
for (l, r) in c1.zip(c2) {
278+
if !is_first && !l.is_none() {
252279
write!(left, "{sep}")?;
253280
}
254-
if !is_first && !either_or_both.is_left() {
281+
if !is_first && !r.is_none() {
255282
write!(right, "{sep}")?;
256283
}
257-
let (c1, c2) = either_or_both.or_default();
284+
let (c1, c2) = (l.unwrap_or_default(), r.unwrap_or_default());
258285
any_diff |= c1.diff_display_inner(&c2, left, right)?;
259286
is_first = false;
260287
}

0 commit comments

Comments
 (0)