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}