Skip to main content

snapper_fmt/
diff.rs

1use imara_diff::{Algorithm, BasicLineDiffPrinter, Diff, InternedInput, UnifiedDiffConfig};
2
3/// Produce a unified diff between original and formatted text using
4/// imara-diff's histogram algorithm (same as git's default).
5///
6/// Returns an empty string if the texts are identical.
7pub fn unified_diff(path: &str, original: &str, formatted: &str) -> String {
8    let input = InternedInput::new(original, formatted);
9    let mut diff = Diff::compute(Algorithm::Histogram, &input);
10    diff.postprocess_lines(&input);
11
12    let mut config = UnifiedDiffConfig::default();
13    config.context_len(3);
14
15    let body = diff
16        .unified_diff(&BasicLineDiffPrinter(&input.interner), config, &input)
17        .to_string();
18
19    if body.is_empty() {
20        return String::new();
21    }
22    format!("--- a/{}\n+++ b/{}\n{}", path, path, body)
23}
24
25/// Print a unified diff to stdout. No-op if texts are identical.
26pub fn print_diff(path: &str, original: &str, formatted: &str) {
27    let diff = unified_diff(path, original, formatted);
28    if !diff.is_empty() {
29        print!("{}", diff);
30    }
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36
37    #[test]
38    fn identical_texts_produce_empty_diff() {
39        let text = "Hello world.\nLine two.\n";
40        assert!(unified_diff("test.md", text, text).is_empty());
41    }
42
43    #[test]
44    fn split_lines_show_correctly() {
45        let old = "First.\nSecond line. Third line.\nFourth.\n";
46        let new = "First.\nSecond line.\nThird line.\nFourth.\n";
47        let diff = unified_diff("test.md", old, new);
48        assert!(diff.contains("-Second line. Third line."));
49        assert!(diff.contains("+Second line."));
50        assert!(diff.contains("+Third line."));
51        assert!(diff.contains(" First."));
52        assert!(diff.contains(" Fourth."));
53    }
54
55    #[test]
56    fn preserves_structural_context() {
57        let old = "# Heading\n\nHello world. This is a test.\n\n# Other\n";
58        let new = "# Heading\n\nHello world.\nThis is a test.\n\n# Other\n";
59        let diff = unified_diff("test.md", old, new);
60        assert!(diff.contains(" # Heading"));
61        assert!(diff.contains("-Hello world. This is a test."));
62        assert!(diff.contains("+Hello world."));
63    }
64
65    #[test]
66    fn file_headers_present() {
67        let diff = unified_diff("foo.org", "a\n", "b\n");
68        assert!(diff.starts_with("--- a/foo.org\n+++ b/foo.org\n"));
69    }
70}