Merge pull request #20407 from A4-Tacks/remove-doc-for-gen-trait

Remove doc comments for generate_trait_from_impl
diff --git a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
index 5a223e1..9f9ced9 100644
--- a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
+++ b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
@@ -220,7 +220,7 @@
             }
 
             let body = if wrap_body_in_block {
-                make::block_expr([], Some(body))
+                make::block_expr([], Some(body.reset_indent().indent(1.into())))
             } else {
                 ast::BlockExpr::cast(body.syntax().clone()).unwrap()
             };
@@ -971,6 +971,32 @@
 }
 "#,
         );
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore: copy
+fn foo() {
+    {
+        let closure = |$0| match () {
+            () => {},
+        };
+        closure();
+    }
+}
+"#,
+            r#"
+fn foo() {
+    {
+        fn closure() {
+            match () {
+                () => {},
+            }
+        }
+        closure();
+    }
+}
+"#,
+        );
     }
 
     #[test]
diff --git a/crates/ide-assists/src/handlers/merge_imports.rs b/crates/ide-assists/src/handlers/merge_imports.rs
index 9ba73d2..42bc058 100644
--- a/crates/ide-assists/src/handlers/merge_imports.rs
+++ b/crates/ide-assists/src/handlers/merge_imports.rs
@@ -49,8 +49,9 @@
             SyntaxElement::Node(n) => n,
             SyntaxElement::Token(t) => t.parent()?,
         };
-        let mut selected_nodes =
-            parent_node.children().filter(|it| selection_range.contains_range(it.text_range()));
+        let mut selected_nodes = parent_node.children().filter(|it| {
+            selection_range.intersect(it.text_range()).is_some_and(|it| !it.is_empty())
+        });
 
         let first_selected = selected_nodes.next()?;
         let edits = match_ast! {
@@ -678,6 +679,25 @@
     }
 
     #[test]
+    fn merge_partial_selection_uses() {
+        cov_mark::check!(merge_with_selected_use_item_neighbors);
+        check_assist(
+            merge_imports,
+            r"
+use std::fmt::Error;
+$0use std::fmt::Display;
+use std::fmt::Debug;
+use std::fmt::Write;
+use$0 std::fmt::Result;
+",
+            r"
+use std::fmt::Error;
+use std::fmt::{Debug, Display, Result, Write};
+",
+        );
+    }
+
+    #[test]
     fn merge_selection_use_trees() {
         cov_mark::check!(merge_with_selected_use_tree_neighbors);
         check_assist(
diff --git a/crates/ide-assists/src/handlers/unwrap_tuple.rs b/crates/ide-assists/src/handlers/unwrap_tuple.rs
index 46f3e85..2345075 100644
--- a/crates/ide-assists/src/handlers/unwrap_tuple.rs
+++ b/crates/ide-assists/src/handlers/unwrap_tuple.rs
@@ -1,3 +1,6 @@
+use std::iter;
+
+use either::Either;
 use syntax::{
     AstNode, T,
     ast::{self, edit::AstNodeEdit},
@@ -24,11 +27,16 @@
 // ```
 pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let let_kw = ctx.find_token_syntax_at_offset(T![let])?;
-    let let_stmt = let_kw.parent().and_then(ast::LetStmt::cast)?;
-    let indent_level = let_stmt.indent_level().0 as usize;
-    let pat = let_stmt.pat()?;
-    let ty = let_stmt.ty();
-    let init = let_stmt.initializer()?;
+    let let_stmt = let_kw.parent().and_then(Either::<ast::LetStmt, ast::LetExpr>::cast)?;
+    let mut indent_level = let_stmt.indent_level();
+    let pat = either::for_both!(&let_stmt, it => it.pat())?;
+    let (ty, init, prefix, suffix) = match &let_stmt {
+        Either::Left(let_stmt) => (let_stmt.ty(), let_stmt.initializer()?, "", ";"),
+        Either::Right(let_expr) => {
+            indent_level = indent_level + 1;
+            (None, let_expr.expr()?, "&& ", "")
+        }
+    };
 
     // This only applies for tuple patterns, types, and initializers.
     let tuple_pat = match pat {
@@ -60,25 +68,19 @@
         "Unwrap tuple",
         let_kw.text_range(),
         |edit| {
-            let indents = "    ".repeat(indent_level);
+            let mut decls = String::new();
 
             // If there is an ascribed type, insert that type for each declaration,
             // otherwise, omit that type.
-            if let Some(tys) = tuple_ty {
-                let mut zipped_decls = String::new();
-                for (pat, ty, expr) in
-                    itertools::izip!(tuple_pat.fields(), tys.fields(), tuple_init.fields())
-                {
-                    zipped_decls.push_str(&format!("{indents}let {pat}: {ty} = {expr};\n"))
-                }
-                edit.replace(parent.text_range(), zipped_decls.trim());
-            } else {
-                let mut zipped_decls = String::new();
-                for (pat, expr) in itertools::izip!(tuple_pat.fields(), tuple_init.fields()) {
-                    zipped_decls.push_str(&format!("{indents}let {pat} = {expr};\n"));
-                }
-                edit.replace(parent.text_range(), zipped_decls.trim());
+            let tys =
+                tuple_ty.into_iter().flat_map(|it| it.fields().map(Some)).chain(iter::repeat(None));
+            for (pat, ty, expr) in itertools::izip!(tuple_pat.fields(), tys, tuple_init.fields()) {
+                let ty = ty.map_or_else(String::new, |ty| format!(": {ty}"));
+                decls.push_str(&format!("{prefix}let {pat}{ty} = {expr}{suffix}\n{indent_level}"))
             }
+
+            let s = decls.trim();
+            edit.replace(parent.text_range(), s.strip_prefix(prefix).unwrap_or(s));
         },
     )
 }
@@ -124,6 +126,28 @@
     }
 
     #[test]
+    fn unwrap_tuples_in_let_expr() {
+        check_assist(
+            unwrap_tuple,
+            r#"
+fn main() {
+    if $0let (foo, bar) = ("Foo", "Bar") {
+        code();
+    }
+}
+"#,
+            r#"
+fn main() {
+    if let foo = "Foo"
+        && let bar = "Bar" {
+        code();
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
     fn unwrap_tuple_with_types() {
         check_assist(
             unwrap_tuple,