Repository: rust-analyzer/rust-analyzer
Pull request #5178 (2020-07-01 16:35:00)

Fold multiline calls


bors r+
🤖


Patch:
diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs
index aad5a8e4db..c22a5d17b3 100644
--- a/crates/ra_ide/src/display/structure.rs
+++ b/crates/ra_ide/src/display/structure.rs
@@ -173,12 +173,19 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
 
 #[cfg(test)]
 mod tests {
+    use expect::{expect, Expect};
+
     use super::*;
-    use insta::assert_debug_snapshot;
+
+    fn check(ra_fixture: &str, expect: Expect) {
+        let file = SourceFile::parse(ra_fixture).ok().unwrap();
+        let structure = file_structure(&file);
+        expect.assert_debug_eq(&structure)
+    }
 
     #[test]
     fn test_file_structure() {
-        let file = SourceFile::parse(
+        check(
             r#"
 struct Foo {
     x: i32
@@ -223,216 +230,211 @@ fn obsolete() {}
 #[deprecated(note = "for awhile")]
 fn very_obsolete() {}
 "#,
-        )
-        .ok()
-        .unwrap();
-        let structure = file_structure(&file);
-        assert_debug_snapshot!(structure,
-        @r###"
-        [
-            StructureNode {
-                parent: None,
-                label: "Foo",
-                navigation_range: 8..11,
-                node_range: 1..26,
-                kind: STRUCT_DEF,
-                detail: None,
-                deprecated: false,
-            },
-            StructureNode {
-                parent: Some(
-                    0,
-                ),
-                label: "x",
-                navigation_range: 18..19,
-                node_range: 18..24,
-                kind: RECORD_FIELD_DEF,
-                detail: Some(
-                    "i32",
-                ),
-                deprecated: false,
-            },
-            StructureNode {
-                parent: None,
-                label: "m",
-                navigation_range: 32..33,
-                node_range: 28..158,
-                kind: MODULE,
-                detail: None,
-                deprecated: false,
-            },
-            StructureNode {
-                parent: Some(
-                    2,
-                ),
-                label: "bar1",
-                navigation_range: 43..47,
-                node_range: 40..52,
-                kind: FN_DEF,
-                detail: Some(
-                    "fn()",
-                ),
-                deprecated: false,
-            },
-            StructureNode {
-                parent: Some(
-                    2,
-                ),
-                label: "bar2",
-                navigation_range: 60..64,
-                node_range: 57..81,
-                kind: FN_DEF,
-                detail: Some(
-                    "fn<T>(t: T) -> T",
-                ),
-                deprecated: false,
-            },
-            StructureNode {
-                parent: Some(
-                    2,
-                ),
-                label: "bar3",
-                navigation_range: 89..93,
-                node_range: 86..156,
-                kind: FN_DEF,
-                detail: Some(
-                    "fn<A, B>(a: A, b: B) -> Vec< u32 >",
-                ),
-                deprecated: false,
-            },
-            StructureNode {
-                parent: None,
-                label: "E",
-                navigation_range: 165..166,
-                node_range: 160..180,
-                kind: ENUM_DEF,
-                detail: None,
-                deprecated: false,
-            },
-            StructureNode {
-                parent: Some(
-                    6,
-                ),
-                label: "X",
-                navigation_range: 169..170,
-                node_range: 169..170,
-                kind: ENUM_VARIANT,
-                detail: None,
-                deprecated: false,
-            },
-            StructureNode {
-                parent: Some(
-                    6,
-                ),
-                label: "Y",
-                navigation_range: 172..173,
-                node_range: 172..178,
-                kind: ENUM_VARIANT,
-                detail: None,
-                deprecated: false,
-            },
-            StructureNode {
-                parent: None,
-                label: "T",
-                navigation_range: 186..187,
-                node_range: 181..193,
-                kind: TYPE_ALIAS_DEF,
-                detail: Some(
-                    "()",
-                ),
-                deprecated: false,
-            },
-            StructureNode {
-                parent: None,
-                label: "S",
-                navigation_range: 201..202,
-                node_range: 194..213,
-                kind: STATIC_DEF,
-                detail: Some(
-                    "i32",
-                ),
-                deprecated: false,
-            },
-            StructureNode {
-                parent: None,
-                label: "C",
-                navigation_range: 220..221,
-                node_range: 214..232,
-                kind: CONST_DEF,
-                detail: Some(
-                    "i32",
-                ),
-                deprecated: false,
-            },
-            StructureNode {
-                parent: None,
-                label: "impl E",
-                navigation_range: 239..240,
-                node_range: 234..243,
-                kind: IMPL_DEF,
-                detail: None,
-                deprecated: false,
-            },
-            StructureNode {
-                parent: None,
-                label: "impl fmt::Debug for E",
-                navigation_range: 265..266,
-                node_range: 245..269,
-                kind: IMPL_DEF,
-                detail: None,
-                deprecated: false,
-            },
-            StructureNode {
-                parent: None,
-                label: "mc",
-                navigation_range: 284..286,
-                node_range: 271..303,
-                kind: MACRO_CALL,
-                detail: None,
-                deprecated: false,
-            },
-            StructureNode {
-                parent: None,
-                label: "mcexp",
-                navigation_range: 334..339,
-                node_range: 305..356,
-                kind: MACRO_CALL,
-                detail: None,
-                deprecated: false,
-            },
-            StructureNode {
-                parent: None,
-                label: "mcexp",
-                navigation_range: 387..392,
-                node_range: 358..409,
-                kind: MACRO_CALL,
-                detail: None,
-                deprecated: false,
-            },
-            StructureNode {
-                parent: None,
-                label: "obsolete",
-                navigation_range: 428..436,
-                node_range: 411..441,
-                kind: FN_DEF,
-                detail: Some(
-                    "fn()",
-                ),
-                deprecated: true,
-            },
-            StructureNode {
-                parent: None,
-                label: "very_obsolete",
-                navigation_range: 481..494,
-                node_range: 443..499,
-                kind: FN_DEF,
-                detail: Some(
-                    "fn()",
-                ),
-                deprecated: true,
-            },
-        ]
-        "###
-                );
+            expect![[r#"
+                [
+                    StructureNode {
+                        parent: None,
+                        label: "Foo",
+                        navigation_range: 8..11,
+                        node_range: 1..26,
+                        kind: STRUCT_DEF,
+                        detail: None,
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: Some(
+                            0,
+                        ),
+                        label: "x",
+                        navigation_range: 18..19,
+                        node_range: 18..24,
+                        kind: RECORD_FIELD_DEF,
+                        detail: Some(
+                            "i32",
+                        ),
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: None,
+                        label: "m",
+                        navigation_range: 32..33,
+                        node_range: 28..158,
+                        kind: MODULE,
+                        detail: None,
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: Some(
+                            2,
+                        ),
+                        label: "bar1",
+                        navigation_range: 43..47,
+                        node_range: 40..52,
+                        kind: FN_DEF,
+                        detail: Some(
+                            "fn()",
+                        ),
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: Some(
+                            2,
+                        ),
+                        label: "bar2",
+                        navigation_range: 60..64,
+                        node_range: 57..81,
+                        kind: FN_DEF,
+                        detail: Some(
+                            "fn<T>(t: T) -> T",
+                        ),
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: Some(
+                            2,
+                        ),
+                        label: "bar3",
+                        navigation_range: 89..93,
+                        node_range: 86..156,
+                        kind: FN_DEF,
+                        detail: Some(
+                            "fn<A, B>(a: A, b: B) -> Vec< u32 >",
+                        ),
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: None,
+                        label: "E",
+                        navigation_range: 165..166,
+                        node_range: 160..180,
+                        kind: ENUM_DEF,
+                        detail: None,
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: Some(
+                            6,
+                        ),
+                        label: "X",
+                        navigation_range: 169..170,
+                        node_range: 169..170,
+                        kind: ENUM_VARIANT,
+                        detail: None,
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: Some(
+                            6,
+                        ),
+                        label: "Y",
+                        navigation_range: 172..173,
+                        node_range: 172..178,
+                        kind: ENUM_VARIANT,
+                        detail: None,
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: None,
+                        label: "T",
+                        navigation_range: 186..187,
+                        node_range: 181..193,
+                        kind: TYPE_ALIAS_DEF,
+                        detail: Some(
+                            "()",
+                        ),
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: None,
+                        label: "S",
+                        navigation_range: 201..202,
+                        node_range: 194..213,
+                        kind: STATIC_DEF,
+                        detail: Some(
+                            "i32",
+                        ),
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: None,
+                        label: "C",
+                        navigation_range: 220..221,
+                        node_range: 214..232,
+                        kind: CONST_DEF,
+                        detail: Some(
+                            "i32",
+                        ),
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: None,
+                        label: "impl E",
+                        navigation_range: 239..240,
+                        node_range: 234..243,
+                        kind: IMPL_DEF,
+                        detail: None,
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: None,
+                        label: "impl fmt::Debug for E",
+                        navigation_range: 265..266,
+                        node_range: 245..269,
+                        kind: IMPL_DEF,
+                        detail: None,
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: None,
+                        label: "mc",
+                        navigation_range: 284..286,
+                        node_range: 271..303,
+                        kind: MACRO_CALL,
+                        detail: None,
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: None,
+                        label: "mcexp",
+                        navigation_range: 334..339,
+                        node_range: 305..356,
+                        kind: MACRO_CALL,
+                        detail: None,
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: None,
+                        label: "mcexp",
+                        navigation_range: 387..392,
+                        node_range: 358..409,
+                        kind: MACRO_CALL,
+                        detail: None,
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: None,
+                        label: "obsolete",
+                        navigation_range: 428..436,
+                        node_range: 411..441,
+                        kind: FN_DEF,
+                        detail: Some(
+                            "fn()",
+                        ),
+                        deprecated: true,
+                    },
+                    StructureNode {
+                        parent: None,
+                        label: "very_obsolete",
+                        navigation_range: 481..494,
+                        node_range: 443..499,
+                        kind: FN_DEF,
+                        detail: Some(
+                            "fn()",
+                        ),
+                        deprecated: true,
+                    },
+                ]
+            "#]],
+        );
     }
 }
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs
index 043515f541..54a47aac06 100644
--- a/crates/ra_ide/src/expand_macro.rs
+++ b/crates/ra_ide/src/expand_macro.rs
@@ -2,9 +2,7 @@ use hir::Semantics;
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
     algo::{find_node_at_offset, SyntaxRewriter},
-    ast, AstNode, NodeOrToken, SyntaxKind,
-    SyntaxKind::*,
-    SyntaxNode, WalkEvent, T,
+    ast, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent, T,
 };
 
 use crate::FilePosition;
@@ -67,6 +65,8 @@ fn expand_macro_recur(
 // FIXME: It would also be cool to share logic here and in the mbe tests,
 // which are pretty unreadable at the moment.
 fn insert_whitespaces(syn: SyntaxNode) -> String {
+    use SyntaxKind::*;
+
     let mut res = String::new();
     let mut token_iter = syn
         .preorder_with_tokens()
@@ -120,164 +120,175 @@ fn insert_whitespaces(syn: SyntaxNode) -> String {
 
 #[cfg(test)]
 mod tests {
-    use expect::{expect, Expect};
+    use insta::assert_snapshot;
 
     use crate::mock_analysis::analysis_and_position;
 
-    fn check(ra_fixture: &str, expect: Expect) {
-        let (analysis, pos) = analysis_and_position(ra_fixture);
-        let expansion = analysis.expand_macro(pos).unwrap().unwrap();
-        let actual = format!("{}\n{}", expansion.name, expansion.expansion);
-        expect.assert_eq(&actual);
+    use super::*;
+
+    fn check_expand_macro(fixture: &str) -> ExpandedMacro {
+        let (analysis, pos) = analysis_and_position(fixture);
+        analysis.expand_macro(pos).unwrap().unwrap()
     }
 
     #[test]
     fn macro_expand_recursive_expansion() {
-        check(
+        let res = check_expand_macro(
             r#"
-macro_rules! bar {
-    () => { fn  b() {} }
-}
-macro_rules! foo {
-    () => { bar!(); }
-}
-macro_rules! baz {
-    () => { foo!(); }
-}
-f<|>oo!();
-"#,
-            expect![[r#"
-                foo
-                fn b(){}
-            "#]],
+        //- /lib.rs
+        macro_rules! bar {
+            () => { fn  b() {} }
+        }
+        macro_rules! foo {
+            () => { bar!(); }
+        }
+        macro_rules! baz {
+            () => { foo!(); }
+        }
+        f<|>oo!();
+        "#,
         );
+
+        assert_eq!(res.name, "foo");
+        assert_snapshot!(res.expansion, @r###"
+fn b(){}
+"###);
     }
 
     #[test]
     fn macro_expand_multiple_lines() {
-        check(
+        let res = check_expand_macro(
             r#"
-macro_rules! foo {
-    () => {
-        fn some_thing() -> u32 {
-            let a = 0;
-            a + 10
+        //- /lib.rs
+        macro_rules! foo {
+            () => {
+                fn some_thing() -> u32 {
+                    let a = 0;
+                    a + 10
+                }
+            }
         }
-    }
-}
-f<|>oo!();
+        f<|>oo!();
         "#,
-            expect![[r#"
-            foo
-            fn some_thing() -> u32 {
-              let a = 0;
-              a+10
-            }"#]],
         );
+
+        assert_eq!(res.name, "foo");
+        assert_snapshot!(res.expansion, @r###"
+fn some_thing() -> u32 {
+  let a = 0;
+  a+10
+}
+"###);
     }
 
     #[test]
     fn macro_expand_match_ast() {
-        check(
+        let res = check_expand_macro(
             r#"
-macro_rules! match_ast {
-    (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
-    (match ($node:expr) {
-        $( ast::$ast:ident($it:ident) => $res:block, )*
-        _ => $catch_all:expr $(,)?
-    }) => {{
-        $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )*
-        { $catch_all }
-    }};
-}
+        //- /lib.rs
+        macro_rules! match_ast {
+            (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
 
-fn main() {
-    mat<|>ch_ast! {
-        match container {
-            ast::TraitDef(it) => {},
-            ast::ImplDef(it) => {},
-            _ => { continue },
+            (match ($node:expr) {
+                $( ast::$ast:ident($it:ident) => $res:block, )*
+                _ => $catch_all:expr $(,)?
+            }) => {{
+                $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )*
+                { $catch_all }
+            }};
         }
+
+        fn main() {
+            mat<|>ch_ast! {
+                match container {
+                    ast::TraitDef(it) => {},
+                    ast::ImplDef(it) => {},
+                    _ => { continue },
+                }
+            }
+        }
+        "#,
+        );
+
+        assert_eq!(res.name, "match_ast");
+        assert_snapshot!(res.expansion, @r###"
+{
+  if let Some(it) = ast::TraitDef::cast(container.clone()){}
+  else if let Some(it) = ast::ImplDef::cast(container.clone()){}
+  else {
+    {
+      continue
     }
+  }
 }
-"#,
-            expect![[r#"
-       match_ast
-       {
-         if let Some(it) = ast::TraitDef::cast(container.clone()){}
-         else if let Some(it) = ast::ImplDef::cast(container.clone()){}
-         else {
-           {
-             continue
-           }
-         }
-       }"#]],
-        );
+"###);
     }
 
     #[test]
     fn macro_expand_match_ast_inside_let_statement() {
-        check(
+        let res = check_expand_macro(
             r#"
-macro_rules! match_ast {
-    (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
-    (match ($node:expr) {}) => {{}};
-}
+        //- /lib.rs
+        macro_rules! match_ast {
+            (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
+            (match ($node:expr) {}) => {{}};
+        }
 
-fn main() {
-    let p = f(|it| {
-        let res = mat<|>ch_ast! { match c {}};
-        Some(res)
-    })?;
-}
-"#,
-            expect![[r#"
-                match_ast
-                {}
-            "#]],
+        fn main() {
+            let p = f(|it| {
+                let res = mat<|>ch_ast! { match c {}};
+                Some(res)
+            })?;
+        }
+        "#,
         );
+
+        assert_eq!(res.name, "match_ast");
+        assert_snapshot!(res.expansion, @r###"{}"###);
     }
 
     #[test]
     fn macro_expand_inner_macro_fail_to_expand() {
-        check(
+        let res = check_expand_macro(
             r#"
-macro_rules! bar {
-    (BAD) => {};
-}
-macro_rules! foo {
-    () => {bar!()};
-}
+        //- /lib.rs
+        macro_rules! bar {
+            (BAD) => {};
+        }
+        macro_rules! foo {
+            () => {bar!()};
+        }
 
-fn main() {
-    let res = fo<|>o!();
-}
-"#,
-            expect![[r#"
-                foo
-            "#]],
+        fn main() {
+            let res = fo<|>o!();
+        }
+        "#,
         );
+
+        assert_eq!(res.name, "foo");
+        assert_snapshot!(res.expansion, @r###""###);
     }
 
     #[test]
     fn macro_expand_with_dollar_crate() {
-        check(
+        let res = check_expand_macro(
             r#"
-#[macro_export]
-macro_rules! bar {
-    () => {0};
-}
-macro_rules! foo {
-    () => {$crate::bar!()};
-}
+        //- /lib.rs
+        #[macro_export]
+        macro_rules! bar {
+            () => {0};
+        }
+        macro_rules! foo {
+            () => {$crate::bar!()};
+        }
 
-fn main() {
-    let res = fo<|>o!();
-}
-"#,
-            expect![[r#"
-                foo
-                0 "#]],
+        fn main() {
+            let res = fo<|>o!();
+        }
+        "#,
         );
+
+        assert_eq!(res.name, "foo");
+        assert_snapshot!(res.expansion, @r###"0"###);
     }
 }
diff --git a/crates/ra_ide/src/folding_ranges.rs b/crates/ra_ide/src/folding_ranges.rs
index 8657377ded..5cec689f8b 100644
--- a/crates/ra_ide/src/folding_ranges.rs
+++ b/crates/ra_ide/src/folding_ranges.rs
@@ -15,6 +15,7 @@ pub enum FoldKind {
     Imports,
     Mods,
     Block,
+    ArgList,
 }
 
 #[derive(Debug)]
@@ -83,6 +84,7 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
     match kind {
         COMMENT => Some(FoldKind::Comment),
         USE_ITEM => Some(FoldKind::Imports),
+        ARG_LIST => Some(FoldKind::ArgList),
         RECORD_FIELD_DEF_LIST
         | RECORD_FIELD_PAT_LIST
         | ITEM_LIST
@@ -196,89 +198,85 @@ fn contiguous_range_for_comment(
 
 #[cfg(test)]
 mod tests {
+    use test_utils::extract_tags;
+
     use super::*;
-    use test_utils::extract_ranges;
 
-    fn do_check(text: &str, fold_kinds: &[FoldKind]) {
-        let (ranges, text) = extract_ranges(text, "fold");
+    fn check(ra_fixture: &str) {
+        let (ranges, text) = extract_tags(ra_fixture, "fold");
+
         let parse = SourceFile::parse(&text);
         let folds = folding_ranges(&parse.tree());
-
         assert_eq!(
             folds.len(),
             ranges.len(),
             "The amount of folds is different than the expected amount"
         );
-        assert_eq!(
-            folds.len(),
-            fold_kinds.len(),
-            "The amount of fold kinds is different than the expected amount"
-        );
-        for ((fold, range), fold_kind) in
-            folds.iter().zip(ranges.into_iter()).zip(fold_kinds.iter())
-        {
+
+        for (fold, (range, attr)) in folds.iter().zip(ranges.into_iter()) {
             assert_eq!(fold.range.start(), range.start());
             assert_eq!(fold.range.end(), range.end());
-            assert_eq!(&fold.kind, fold_kind);
+
+            let kind = match fold.kind {
+                FoldKind::Comment => "comment",
+                FoldKind::Imports => "imports",
+                FoldKind::Mods => "mods",
+                FoldKind::Block => "block",
+                FoldKind::ArgList => "arglist",
+            };
+            assert_eq!(kind, &attr.unwrap());
         }
     }
 
     #[test]
     fn test_fold_comments() {
-        let text = r#"
-<fold>// Hello
+        check(
+            r#"
+<fold comment>// Hello
 // this is a multiline
 // comment
 //</fold>
 
 // But this is not
 
-fn main() <fold>{
-    <fold>// We should
+fn main() <fold block>{
+    <fold comment>// We should
     // also
     // fold
     // this one.</fold>
-    <fold>//! But this one is different
+    <fold comment>//! But this one is different
     //! because it has another flavor</fold>
-    <fold>/* As does this
+    <fold comment>/* As does this
     multiline comment */</fold>
-}</fold>"#;
-
-        let fold_kinds = &[
-            FoldKind::Comment,
-            FoldKind::Block,
-            FoldKind::Comment,
-            FoldKind::Comment,
-            FoldKind::Comment,
-        ];
-        do_check(text, fold_kinds);
+}</fold>"#,
+        );
     }
 
     #[test]
     fn test_fold_imports() {
-        let text = r#"
-<fold>use std::<fold>{
+        check(
+            r#"
+<fold imports>use std::<fold block>{
     str,
     vec,
     io as iop
 }</fold>;</fold>
 
-fn main() <fold>{
-}</fold>"#;
-
-        let folds = &[FoldKind::Imports, FoldKind::Block, FoldKind::Block];
-        do_check(text, folds);
+fn main() <fold block>{
+}</fold>"#,
+        );
     }
 
     #[test]
     fn test_fold_mods() {
-        let text = r#"
+        check(
+            r#"
 
 pub mod foo;
-<fold>mod after_pub;
+<fold mods>mod after_pub;
 mod after_pub_next;</fold>
 
-<fold>mod before_pub;
+<fold mods>mod before_pub;
 mod before_pub_next;</fold>
 pub mod bar;
 
@@ -286,90 +284,93 @@ mod not_folding_single;
 pub mod foobar;
 pub not_folding_single_next;
 
-<fold>#[cfg(test)]
+<fold mods>#[cfg(test)]
 mod with_attribute;
 mod with_attribute_next;</fold>
 
-fn main() <fold>{
-}</fold>"#;
-
-        let folds = &[FoldKind::Mods, FoldKind::Mods, FoldKind::Mods, FoldKind::Block];
-        do_check(text, folds);
+fn main() <fold block>{
+}</fold>"#,
+        );
     }
 
     #[test]
     fn test_fold_import_groups() {
-        let text = r#"
-<fold>use std::str;
+        check(
+            r#"
+<fold imports>use std::str;
 use std::vec;
 use std::io as iop;</fold>
 
-<fold>use std::mem;
+<fold imports>use std::mem;
 use std::f64;</fold>
 
 use std::collections::HashMap;
 // Some random comment
 use std::collections::VecDeque;
 
-fn main() <fold>{
-}</fold>"#;
-
-        let folds = &[FoldKind::Imports, FoldKind::Imports, FoldKind::Block];
-        do_check(text, folds);
+fn main() <fold block>{
+}</fold>"#,
+        );
     }
 
     #[test]
     fn test_fold_import_and_groups() {
-        let text = r#"
-<fold>use std::str;
+        check(
+            r#"
+<fold imports>use std::str;
 use std::vec;
 use std::io as iop;</fold>
 
-<fold>use std::mem;
+<fold imports>use std::mem;
 use std::f64;</fold>
 
-<fold>use std::collections::<fold>{
+<fold imports>use std::collections::<fold block>{
     HashMap,
     VecDeque,
 }</fold>;</fold>
 // Some random comment
 
-fn main() <fold>{
-}</fold>"#;
-
-        let folds = &[
-            FoldKind::Imports,
-            FoldKind::Imports,
-            FoldKind::Imports,
-            FoldKind::Block,
-            FoldKind::Block,
-        ];
-        do_check(text, folds);
+fn main() <fold block>{
+}</fold>"#,
+        );
     }
 
     #[test]
     fn test_folds_macros() {
-        let text = r#"
-macro_rules! foo <fold>{
+        check(
+            r#"
+macro_rules! foo <fold block>{
     ($($tt:tt)*) => { $($tt)* }
 }</fold>
-"#;
-
-        let folds = &[FoldKind::Block];
-        do_check(text, folds);
+"#,
+        );
     }
 
     #[test]
     fn test_fold_match_arms() {
-        let text = r#"
-fn main() <fold>{
-    match 0 <fold>{
+        check(
+            r#"
+fn main() <fold block>{
+    match 0 <fold block>{
         0 => 0,
         _ => 1,
     }</fold>
-}</fold>"#;
+}</fold>"#,
+        );
+    }
 
-        let folds = &[FoldKind::Block, FoldKind::Block];
-        do_check(text, folds);
+    #[test]
+    fn fold_big_calls() {
+        check(
+            r#"
+fn main() <fold block>{
+    frobnicate<fold arglist>(
+        1,
+        2,
+        3,
+    )</fold>
+}</fold>
+        "#,
+        )
     }
 }
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index a0a58f689d..95dd8e408d 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -352,7 +352,7 @@ pub(crate) fn folding_range(
     let kind = match fold.kind {
         FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment),
         FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports),
-        FoldKind::Mods | FoldKind::Block => None,
+        FoldKind::Mods | FoldKind::Block | FoldKind::ArgList => None,
     };
 
     let range = range(line_index, fold.range);
@@ -685,32 +685,27 @@ pub(crate) fn runnable(
 
 #[cfg(test)]
 mod tests {
-    use test_utils::extract_ranges;
+    use ra_ide::Analysis;
 
     use super::*;
 
     #[test]
     fn conv_fold_line_folding_only_fixup() {
-        let text = r#"<fold>mod a;
+        let text = r#"mod a;
 mod b;
-mod c;</fold>
+mod c;
 
-fn main() <fold>{
-    if cond <fold>{
+fn main() {
+    if cond {
         a::do_a();
-    }</fold> else <fold>{
+    } else {
         b::do_b();
-    }</fold>
-}</fold>"#;
-
-        let (ranges, text) = extract_ranges(text, "fold");
-        assert_eq!(ranges.len(), 4);
-        let folds = vec![
-            Fold { range: ranges[0], kind: FoldKind::Mods },
-            Fold { range: ranges[1], kind: FoldKind::Block },
-            Fold { range: ranges[2], kind: FoldKind::Block },
-            Fold { range: ranges[3], kind: FoldKind::Block },
-        ];
+    }
+}"#;
+
+        let (analysis, file_id) = Analysis::from_single_file(text.to_string());
+        let folds = analysis.folding_ranges(file_id).unwrap();
+        assert_eq!(folds.len(), 4);
 
         let line_index = LineIndex::new(&text);
         let converted: Vec<lsp_types::FoldingRange> =


Test patch:
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index fba5f42810..e4aa894ace 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -118,8 +118,8 @@ pub fn extract_range_or_offset(text: &str) -> (RangeOrOffset, String) {
 }
 
 /// Extracts ranges, marked with `<tag> </tag>` pairs from the `text`
-pub fn extract_ranges(mut text: &str, tag: &str) -> (Vec<TextRange>, String) {
-    let open = format!("<{}>", tag);
+pub fn extract_tags(mut text: &str, tag: &str) -> (Vec<(TextRange, Option<String>)>, String) {
+    let open = format!("<{}", tag);
     let close = format!("</{}>", tag);
     let mut ranges = Vec::new();
     let mut res = String::new();
@@ -134,22 +134,35 @@ pub fn extract_ranges(mut text: &str, tag: &str) -> (Vec<TextRange>, String) {
                 res.push_str(&text[..i]);
                 text = &text[i..];
                 if text.starts_with(&open) {
-                    text = &text[open.len()..];
+                    let close_open = text.find('>').unwrap();
+                    let attr = text[open.len()..close_open].trim();
+                    let attr = if attr.is_empty() { None } else { Some(attr.to_string()) };
+                    text = &text[close_open + '>'.len_utf8()..];
                     let from = TextSize::of(&res);
-                    stack.push(from);
+                    stack.push((from, attr));
                 } else if text.starts_with(&close) {
                     text = &text[close.len()..];
-                    let from = stack.pop().unwrap_or_else(|| panic!("unmatched </{}>", tag));
+                    let (from, attr) =
+                        stack.pop().unwrap_or_else(|| panic!("unmatched </{}>", tag));
                     let to = TextSize::of(&res);
-                    ranges.push(TextRange::new(from, to));
+                    ranges.push((TextRange::new(from, to), attr));
+                } else {
+                    res.push('<');
+                    text = &text['<'.len_utf8()..];
                 }
             }
         }
     }
     assert!(stack.is_empty(), "unmatched <{}>", tag);
-    ranges.sort_by_key(|r| (r.start(), r.end()));
+    ranges.sort_by_key(|r| (r.0.start(), r.0.end()));
     (ranges, res)
 }
+#[test]
+fn test_extract_tags() {
+    let (tags, text) = extract_tags(r#"<tag fn>fn <tag>main</tag>() {}</tag>"#, "tag");
+    let actual = tags.into_iter().map(|(range, attr)| (&text[range], attr)).collect::<Vec<_>>();
+    assert_eq!(actual, vec![("fn main() {}", Some("fn".into())), ("main", None),]);
+}
 
 /// Inserts `<|>` marker into the `text` at `offset`.
 pub fn add_cursor(text: &str, offset: TextSize) -> String {