Auto merge of #15809 - Young-Flash:extract_variable_in_place, r=HKalbasi
feat: make extract_variable assist in place
![extract_variable_without_select](https://github.com/rust-lang/rust-analyzer/assets/71162630/96be2de4-42c9-4b24-b3e1-8b3e3a2da1d9)
close https://github.com/rust-lang/rust-analyzer/issues/15796
diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs
index 014c231..e7c884d 100644
--- a/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/crates/ide-assists/src/handlers/extract_variable.rs
@@ -29,22 +29,31 @@
// }
// ```
pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
- if ctx.has_empty_selection() {
- return None;
- }
-
- let node = match ctx.covering_element() {
- NodeOrToken::Node(it) => it,
- NodeOrToken::Token(it) if it.kind() == COMMENT => {
- cov_mark::hit!(extract_var_in_comment_is_not_applicable);
+ let node = if ctx.has_empty_selection() {
+ if let Some(expr_stmt) = ctx.find_node_at_offset::<ast::ExprStmt>() {
+ expr_stmt.syntax().clone()
+ } else if let Some(expr) = ctx.find_node_at_offset::<ast::Expr>() {
+ expr.syntax().ancestors().find_map(valid_target_expr)?.syntax().clone()
+ } else {
return None;
}
- NodeOrToken::Token(it) => it.parent()?,
+ } else {
+ match ctx.covering_element() {
+ NodeOrToken::Node(it) => it,
+ NodeOrToken::Token(it) if it.kind() == COMMENT => {
+ cov_mark::hit!(extract_var_in_comment_is_not_applicable);
+ return None;
+ }
+ NodeOrToken::Token(it) => it.parent()?,
+ }
};
+
let node = node.ancestors().take_while(|anc| anc.text_range() == node.text_range()).last()?;
+ let range = node.text_range();
+
let to_extract = node
.descendants()
- .take_while(|it| ctx.selection_trimmed().contains_range(it.text_range()))
+ .take_while(|it| range.contains_range(it.text_range()))
.find_map(valid_target_expr)?;
let ty = ctx.sema.type_of_expr(&to_extract).map(TypeInfo::adjusted);
@@ -236,6 +245,138 @@
use super::*;
#[test]
+ fn test_extract_var_simple_without_select() {
+ check_assist(
+ extract_variable,
+ r#"
+fn main() -> i32 {
+ if true {
+ 1
+ } else {
+ 2
+ }$0
+}
+"#,
+ r#"
+fn main() -> i32 {
+ let $0var_name = if true {
+ 1
+ } else {
+ 2
+ };
+ var_name
+}
+"#,
+ );
+
+ check_assist(
+ extract_variable,
+ r#"
+fn foo() -> i32 { 1 }
+fn main() {
+ foo();$0
+}
+"#,
+ r#"
+fn foo() -> i32 { 1 }
+fn main() {
+ let $0foo = foo();
+}
+"#,
+ );
+
+ check_assist(
+ extract_variable,
+ r#"
+fn main() {
+ let a = Some(2);
+ a.is_some();$0
+}
+"#,
+ r#"
+fn main() {
+ let a = Some(2);
+ let $0is_some = a.is_some();
+}
+"#,
+ );
+
+ check_assist(
+ extract_variable,
+ r#"
+fn main() {
+ "hello"$0;
+}
+"#,
+ r#"
+fn main() {
+ let $0var_name = "hello";
+}
+"#,
+ );
+
+ check_assist(
+ extract_variable,
+ r#"
+fn main() {
+ 1 + 2$0;
+}
+"#,
+ r#"
+fn main() {
+ let $0var_name = 1 + 2;
+}
+"#,
+ );
+
+ check_assist(
+ extract_variable,
+ r#"
+fn main() {
+ match () {
+ () if true => 1,
+ _ => 2,
+ };$0
+}
+"#,
+ r#"
+fn main() {
+ let $0var_name = match () {
+ () if true => 1,
+ _ => 2,
+ };
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn test_extract_var_unit_expr_without_select_not_applicable() {
+ check_assist_not_applicable(
+ extract_variable,
+ r#"
+fn foo() {}
+fn main() {
+ foo()$0;
+}
+"#,
+ );
+
+ check_assist_not_applicable(
+ extract_variable,
+ r#"
+fn foo() {
+ let mut i = 3;
+ if i >= 0 {
+ i += 1;
+ } else {
+ i -= 1;
+ }$0
+}"#,
+ );
+ }
+
+ #[test]
fn test_extract_var_simple() {
check_assist(
extract_variable,