From 04147cfbb6638f77a3a3b20fab0f1a639cf52586 Mon Sep 17 00:00:00 2001 From: ketan0 Date: Mon, 21 Sep 2026 00:21:18 -0700 Subject: [PATCH] Reveal unchanged context groups without nested context folds Decide grouping before collapsing members, preserving their existing visibility. Verify opening a group reveals all source lines on both sides. AI-assisted implementation using Codex. --- plugins/context/src/lib.rs | 26 +++++++++++++++++++++----- src/plugin/tests/context.rs | 9 +++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/plugins/context/src/lib.rs b/plugins/context/src/lib.rs index d3df92baa..5497a9ea0 100644 --- a/plugins/context/src/lib.rs +++ b/plugins/context/src/lib.rs @@ -13,7 +13,7 @@ //! A stretch is cut at region edges. The part in one list of siblings //! collapses when it is at least `MIN_GAP` lines long or is the whole //! stretch; a shorter sliver stays open. When a part spans several -//! siblings, each collapses and a group wraps them in one row on each side, +//! siblings, only their enclosing group collapses into one row on each side, //! provided the two sides' siblings match one for one: leaves sharing an //! `alignment_id`, or folds sharing a fold state. A fold in it collapses only when every //! region on the other side in its fold state lies wholly inside the @@ -407,6 +407,16 @@ impl Plugin for Context { if lines < MIN_GAP && lines != total { continue; } + // One context fold is enough for a group. Leave its members' + // visibility intact so expanding it reveals code directly. + let grouped = same_shape + && part.len() >= 2 + && part.iter().all(|member| match member { + Member::Whole { + id, state: Some(_), .. + } => hides_only_this(*id), + _ => true, + }); let mut members = Vec::new(); // The rhs leaves and pieces the lhs members pair with. let mut rhs_members = Vec::new(); @@ -418,28 +428,34 @@ impl Plugin for Context { Member::Whole { id, lines, state, .. } => { - draft.collapse(*id, unchanged_label(*lines))?; + if !grouped { + draft.collapse(*id, unchanged_label(*lines))?; + } members.push(*id); let Some(state) = state else { rhs_members.extend(rhs_leaf_ids.get(id)); continue; }; for rhs_id in rhs_by_state.get(state).into_iter().flatten() { - if let Some(rhs_lines) = rhs_fold_lines.get(rhs_id) { + if let Some(rhs_lines) = + rhs_fold_lines.get(rhs_id).filter(|_| !grouped) + { draft.collapse(*rhs_id, unchanged_label(*rhs_lines))?; } } } Member::Part { id, start, end, .. } => { let piece = draft.cut_lines(*id, *start, *end)?; - draft.collapse(piece, unchanged_label(end - start))?; + if !grouped { + draft.collapse(piece, unchanged_label(end - start))?; + } members.push(piece); rhs_members.extend(draft.paired_leaf(piece)?); } } } // A fold left open breaks the run, so nothing is grouped. - if same_shape && members.len() == part.len() && members.len() >= 2 { + if grouped { members.reverse(); // The rhs leaves and pieces, then the rhs folds. members.extend(rhs_members); diff --git a/src/plugin/tests/context.rs b/src/plugin/tests/context.rs index beae09445..7eea0bf0e 100644 --- a/src/plugin/tests/context.rs +++ b/src/plugin/tests/context.rs @@ -123,6 +123,15 @@ fn a_stretch_over_whole_folds_collapses_as_one_group() { "the two sides' groups open and close together" ); assert_eq!(open_lines(&lhs.regions), BTreeSet::from([0, 1, 2, 16, 17])); + // Opening the context group reveals its entire unchanged stretch on + // either side, rather than another layer of context folds. + for source in [lhs, rhs] { + let mut regions = source.regions.clone(); + for region in &mut regions { + region.visibility.collapsed = false; + } + assert_eq!(open_lines(®ions), (0..18).collect()); + } } #[test]