From 8ac661eaa9c546a158350e22c48b1b302dae0d50 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Sat, 21 Jun 2025 15:44:52 +0800 Subject: [PATCH 1/3] feat: support folding multiline arg list & fn body in one folding range --- crates/ide/src/folding_ranges.rs | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs index 9bd8504733..a9072773a1 100755 --- a/crates/ide/src/folding_ranges.rs +++ b/crates/ide/src/folding_ranges.rs @@ -47,6 +47,7 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec { let mut res = vec![]; let mut visited_comments = FxHashSet::default(); let mut visited_nodes = FxHashSet::default(); + let mut merged_fn_bodies = FxHashSet::default(); // regions can be nested, here is a LIFO buffer let mut region_starts: Vec = vec![]; @@ -59,6 +60,31 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec { NodeOrToken::Token(token) => token.text().contains('\n'), }; if is_multiline { + // for the arg list, we need to special handle + if matches!(element.kind(), ARG_LIST | PARAM_LIST) { + if let NodeOrToken::Node(node) = &element { + if let Some(fn_node) = node.parent().and_then(ast::Fn::cast) { + if let Some(body) = fn_node.body() { + // just add a big fold combine the params and body + res.push(Fold { + range: TextRange::new( + node.text_range().start(), + body.syntax().text_range().end(), + ), + kind: FoldKind::ArgList, + }); + merged_fn_bodies.insert(body.syntax().text_range()); + continue; + } + } + } + } + // skip the merged function body + if matches!(element.kind(), BLOCK_EXPR) + && merged_fn_bodies.contains(&element.text_range()) + { + continue; + } res.push(Fold { range: element.text_range(), kind }); continue; } @@ -291,6 +317,7 @@ mod tests { use super::*; + #[track_caller] fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) { let (ranges, text) = extract_tags(ra_fixture, "fold"); @@ -544,7 +571,7 @@ const _: S = S { fn foo( x: i32, y: String, -) {} +) {} "#, ) } From 5ae67747f5030eed238bbf9325350363cbf31418 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Mon, 30 Jun 2025 17:26:20 +0800 Subject: [PATCH 2/3] internal: add `FoldKind::Function` --- crates/ide/src/folding_ranges.rs | 28 ++++++++++++++---------- crates/rust-analyzer/src/lsp/to_proto.rs | 3 ++- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs index a9072773a1..7ac160dac2 100755 --- a/crates/ide/src/folding_ranges.rs +++ b/crates/ide/src/folding_ranges.rs @@ -23,6 +23,7 @@ pub enum FoldKind { WhereClause, ReturnType, MatchArm, + Function, // region: item runs Modules, Consts, @@ -60,18 +61,25 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec { NodeOrToken::Token(token) => token.text().contains('\n'), }; if is_multiline { - // for the arg list, we need to special handle - if matches!(element.kind(), ARG_LIST | PARAM_LIST) { + // for the func with multiline param list + if matches!(element.kind(), FN) { if let NodeOrToken::Node(node) = &element { - if let Some(fn_node) = node.parent().and_then(ast::Fn::cast) { + if let Some(fn_node) = ast::Fn::cast(node.clone()) { + if !fn_node + .param_list() + .map(|param_list| param_list.syntax().text().contains_char('\n')) + .unwrap_or(false) + { + continue; + } + if let Some(body) = fn_node.body() { - // just add a big fold combine the params and body res.push(Fold { range: TextRange::new( node.text_range().start(), - body.syntax().text_range().end(), + node.text_range().end(), ), - kind: FoldKind::ArgList, + kind: FoldKind::Function, }); merged_fn_bodies.insert(body.syntax().text_range()); continue; @@ -79,12 +87,6 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec { } } } - // skip the merged function body - if matches!(element.kind(), BLOCK_EXPR) - && merged_fn_bodies.contains(&element.text_range()) - { - continue; - } res.push(Fold { range: element.text_range(), kind }); continue; } @@ -178,6 +180,7 @@ fn fold_kind(kind: SyntaxKind) -> Option { ARG_LIST | PARAM_LIST | GENERIC_ARG_LIST | GENERIC_PARAM_LIST => Some(FoldKind::ArgList), ARRAY_EXPR => Some(FoldKind::Array), RET_TYPE => Some(FoldKind::ReturnType), + FN => Some(FoldKind::Function), WHERE_CLAUSE => Some(FoldKind::WhereClause), ASSOC_ITEM_LIST | RECORD_FIELD_LIST @@ -349,6 +352,7 @@ mod tests { FoldKind::WhereClause => "whereclause", FoldKind::ReturnType => "returntype", FoldKind::MatchArm => "matcharm", + FoldKind::Function => "function", FoldKind::TraitAliases => "traitaliases", FoldKind::ExternCrates => "externcrates", }; diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index 8a848fb848..292be1d531 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -911,7 +911,8 @@ pub(crate) fn folding_range( | FoldKind::Array | FoldKind::TraitAliases | FoldKind::ExternCrates - | FoldKind::MatchArm => None, + | FoldKind::MatchArm + | FoldKind::Function => None, }; let range = range(line_index, fold.range); From d94a83226437cfe3d4314cb4b477c5806147ac5e Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Mon, 30 Jun 2025 17:27:07 +0800 Subject: [PATCH 3/3] test: add test case for func with multiline param list --- crates/ide/src/folding_ranges.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs index 7ac160dac2..c081796d07 100755 --- a/crates/ide/src/folding_ranges.rs +++ b/crates/ide/src/folding_ranges.rs @@ -360,6 +360,23 @@ mod tests { } } + #[test] + fn test_fold_func_with_multiline_param_list() { + check( + r#" +fn func( + a: i32, + b: i32, + c: i32, +) { + + + +} +"#, + ); + } + #[test] fn test_fold_comments() { check( @@ -572,10 +589,10 @@ const _: S = S { fn fold_multiline_params() { check( r#" -fn foo( +fn foo( x: i32, y: String, -) {} +) {} "#, ) }