Skip to main content

lattice_diff/
options.rs

1//! D-fix.5 (2026-06-26): diff-presentation options, owned by the diff
2//! subsystem ([[feedback_mode_owns_its_surface]]) rather than host
3//! core. Self-register via `linkme` like every other `options!` block;
4//! the host's `init_from_linkme()` walks the global slice at boot, so
5//! linking `lattice-diff` into the binary (it always is — `install`
6//! runs in the Phase-B list) picks these up automatically.
7//!
8//! Both drive the universal `UnchangedFoldSource` (vimdiff
9//! `foldmethod=diff` + `diffopt context:N`): in any diff session the
10//! unchanged code between hunks is folded away so only the changes (±
11//! `context` lines) show, on BOTH sides in lockstep.
12//!
13//! Bound to the `display` group (visual-presentation toggles, next to
14//! line numbers / wrap / whitespace). User customizes via
15//! `:set ui.diff.context=3` / `:set ui.diff.fold-unchanged` /
16//! `:customize display`.
17
18/// Validator for `ui.diff.context`: a non-negative line count. `0`
19/// keeps zero context (only the changed lines show); the upper bound
20/// guards against a fat-fingered value that would fold nothing
21/// (`context >= file length` ⇒ the whole file is "kept").
22fn validate_diff_context(n: &i64) -> Result<(), String> {
23    if *n >= 0 && *n <= 1000 {
24        Ok(())
25    } else {
26        Err(format!(
27            "ui.diff.context must be in range [0, 1000], got {n}"
28        ))
29    }
30}
31
32lattice_config::options! {
33    group = lattice_config::Display;
34
35    /// Fold the unchanged regions of a diff so only the changes (±
36    /// [`UiDiffContext`] lines) remain visible — vimdiff's
37    /// `foldmethod=diff`, VS Code's "Collapse Unchanged Regions".
38    ///
39    /// `true` (default) — every diff session gets a closed
40    /// `UnchangedFoldSource` on each side; `zR` / `zo` expand a region
41    /// to read the surrounding context, `zM` re-collapses.
42    ///
43    /// `false` — diffs open fully expanded (the unchanged code stays
44    /// visible). Hunk folds (`za` on a change) are unaffected — those
45    /// are a separate, open-by-default source.
46    #[name("ui.diff.fold-unchanged")]
47    pub UiDiffFoldUnchanged: bool = true;
48
49    /// Number of unchanged context lines to keep visible above and
50    /// below each change when [`UiDiffFoldUnchanged`] folds a diff.
51    /// Default `6` — vimdiff's `diffopt` `context:6`. Mirrors VS
52    /// Code's `diffEditor.hideUnchangedRegions.contextLineCount`.
53    ///
54    /// `0` collapses right up to the change boundary; larger values
55    /// leave more surrounding code visible. Unchanged gaps shorter than
56    /// the per-fold floor (2 lines) are never folded regardless, so
57    /// tiny inter-hunk gaps stay visible (VS Code's `minimumLineCount`).
58    #[name("ui.diff.context")]
59    #[validate(validate_diff_context)]
60    pub UiDiffContext: i64 = 6;
61
62    /// Tint whole rows by what the diff did to them — added rows on an
63    /// add background, removed rows on a remove background — rather
64    /// than colouring only the leading `+` / `-`.
65    ///
66    /// `true` (default) — what MG.21a made unconditional, and what
67    /// every diff-showing surface has looked like since.
68    ///
69    /// `false` — foreground colouring only. For low-contrast themes
70    /// where a full-row wash fights the syntax colours underneath, and
71    /// for terminals whose background handling makes the tint muddy.
72    ///
73    /// **Lives here rather than under `magit.*`, which is where
74    /// MG.22's design fragment first put it.** The mechanism turned out
75    /// to be generic: `Editor::diff_signs_from_spans` derives the tint
76    /// from whatever spans a mode publishes, so every diff-showing
77    /// buffer shares it, magit's among them. An option named for one
78    /// consumer would have understated what it turns off.
79    #[name("ui.diff.line-backgrounds")]
80    pub UiDiffLineBackgrounds: bool = true;
81}