From addbc3b4406fe5fb6377de80cc2fbb4fd04661e6 Mon Sep 17 00:00:00 2001 From: Sidharth Menon Date: Wed, 7 Oct 2026 04:23:18 -0700 Subject: [PATCH] Keep a block's brackets when its folds merge by lines A block holding one statement folds twice over the same lines: the block from the shared query, with its opener and closer, and the statement from the context query, with none. merge_by_lines kept the innermost node's syntax, the statement's None, so the fold lost its brackets and frontends drew no rail for it, as for every one-statement `for` body. The merged fold now keeps the merged node's opener and closer when its own node has none. AI-assisted: Claude Code. Agent-Session: 3c0a7f53-4e2b-4d6b-b99f-471c7e752571 --- crates/diffr-core/src/parse/fold_tests.rs | 8 +++++++- crates/diffr-core/src/parse/folds.rs | 6 +++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/diffr-core/src/parse/fold_tests.rs b/crates/diffr-core/src/parse/fold_tests.rs index 03d5c6f74..62b47f23d 100644 --- a/crates/diffr-core/src/parse/fold_tests.rs +++ b/crates/diffr-core/src/parse/fold_tests.rs @@ -325,7 +325,8 @@ mod folds { // A function whose body is one `match`: the block the shared query // folds and the `match` the context query folds cover the same // lines, so they are one fold. It is the `match`'s, the innermost - // node whose extent that region is, and it carries both tags. + // node whose extent that region is, and it carries both tags and + // the block's braces, since the `match` has no opener of its own. let src = "fn f(x: u32) -> u32 {\n match x {\n 1 => 2,\n _ => 3,\n }\n}\n"; let params = crate::config::Config::from_toml("") @@ -349,6 +350,11 @@ mod folds { "{:?}", same_lines[0].tags ); + let syntax = same_lines[0].syntax.expect("the block's opener and closer"); + assert_eq!( + text(src, &syntax), + "\n match x {\n 1 => 2,\n _ => 3,\n }\n" + ); } #[test] diff --git a/crates/diffr-core/src/parse/folds.rs b/crates/diffr-core/src/parse/folds.rs index 8e6a48c99..d422566fc 100644 --- a/crates/diffr-core/src/parse/folds.rs +++ b/crates/diffr-core/src/parse/folds.rs @@ -232,7 +232,10 @@ fn depth(node: tree_sitter::Node<'_>) -> usize { /// Merge folds of different nodes that cover the same whole lines, such as a /// Rust `Self { … }` tail expression and the `{ … }` field list inside it. /// The merged fold keeps the innermost node's range, indent and syntax (the -/// deepest among equal ranges), plus every fold's tags and relations. +/// deepest among equal ranges), plus every fold's tags and relations. When +/// the innermost node has no opener, as a lone statement in a block has none, +/// the fold keeps the opener and closer of the node it merged: they delimit +/// the same lines, so frontends still find the brackets and the rail. /// A fold with no whole line merges with nothing. fn merge_by_lines( mut folds: Vec<(usize, usize, FoldMetadata)>, @@ -275,6 +278,7 @@ fn merge_by_lines( } else { (owner, held, metadata) }; + kept.syntax = kept.syntax.or(lost.syntax); kept.tags.extend(lost.tags); kept.tags.sort(); kept.tags.dedup();