// Assist: extract_function
//
-// Extracts selected statements into new function.
+// Extracts selected statements and comments into new function.
//
// ```
// fn main() {
// let n = 1;
// $0let m = n + 2;
+// // calculate
// let k = m + n;$0
// let g = 3;
// }
//
// fn $0fun_name(n: i32) {
// let m = n + 2;
+// // calculate
// let k = m + n;
// }
// ```
return None;
}
- println!("initial node: {:?}", node);
-
let node = match node {
syntax::NodeOrToken::Node(n) => n,
syntax::NodeOrToken::Token(t) => t.parent()?,
};
- println!("next node: {:?}", node);
let body = extraction_target(&node, range)?;
- println!("body: {:?}", body);
let container_info = body.analyze_container(&ctx.sema)?;
let (locals_used, self_param) = body.analyze(&ctx.sema);
};
}
- println!("node: {:?}", node);
-
// Covering element returned the parent block of one or multiple statements that have been selected
if let Some(stmt_list) = ast::StmtList::cast(node.clone()) {
if let Some(block_expr) = stmt_list.syntax().parent().and_then(ast::BlockExpr::cast) {
}
// Extract the full statements.
- println!("stmt_list: {:?}", stmt_list);
- println!("selection_range: {:?}", selection_range);
return Some(FunctionBody::from_range(stmt_list, selection_range));
}
fn from_range(parent: ast::StmtList, selected: TextRange) -> FunctionBody {
let full_body = parent.syntax().children_with_tokens();
- for st in parent.syntax().children_with_tokens() {
- println!("Statement: {:?}", &st);
- }
let mut text_range = full_body
- .map(|stmt| stmt.text_range())
- .filter(|&stmt| selected.intersect(stmt).filter(|it| !it.is_empty()).is_some())
+ .filter(|it| ast::Stmt::can_cast(it.kind()) || it.kind() == COMMENT)
+ .map(|element| element.text_range())
+ .filter(|&range| selected.intersect(range).filter(|it| !it.is_empty()).is_some())
.reduce(|acc, stmt| acc.cover(stmt));
- println!("from_range text_range first: {:?}", text_range);
if let Some(tail_range) = parent
.tail_expr()
.map(|it| it.syntax().text_range())
Some(text_range) => text_range.cover(tail_range),
None => tail_range,
});
-
- println!("from_range text_range second: {:?}", text_range);
}
Self::Span { parent, text_range: text_range.unwrap_or(selected) }
}
let stmt = make::expr_stmt(action);
let block = make::block_expr(iter::once(stmt.into()), None);
let controlflow_break_path = make::path_from_text("ControlFlow::Break");
- let condition = make::condition(
+ let condition = make::expr_let(
+ make::tuple_struct_pat(
+ controlflow_break_path,
+ iter::once(make::wildcard_pat().into()),
+ )
+ .into(),
call_expr,
- Some(
- make::tuple_struct_pat(
- controlflow_break_path,
- iter::once(make::wildcard_pat().into()),
- )
- .into(),
- ),
);
- make::expr_if(condition, block, None)
+ make::expr_if(condition.into(), block, None)
}
FlowHandler::IfOption { action } => {
let path = make::ext::ident_path("Some");
let value_pat = make::ext::simple_ident_pat(make::name("value"));
let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into()));
- let cond = make::condition(call_expr, Some(pattern.into()));
+ let cond = make::expr_let(pattern.into(), call_expr);
let value = make::expr_path(make::ext::ident_path("value"));
let action_expr = action.make_result_handler(Some(value));
let action_stmt = make::expr_stmt(action_expr);
let then = make::block_expr(iter::once(action_stmt.into()), None);
- make::expr_if(cond, then, None)
+ make::expr_if(cond.into(), then, None)
}
FlowHandler::MatchOption { none } => {
let some_name = "value";
FlowHandler::from_ret_ty(fun, &ret_ty)
};
- println!("making body: {:?}", fun.body);
let block = match &fun.body {
FunctionBody::Expr(expr) => {
let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax());
.syntax()
.children_with_tokens()
.filter(|it| text_range.contains_range(it.text_range()))
- .map(|it| match it {
- syntax::NodeOrToken::Node(n) => {
- println!("Found node: {:?}", n);
- let node_rewritten = rewrite_body_segment(ctx, &fun.params, &handler, &n);
-
- syntax::NodeOrToken::Node(node_rewritten)
- }
- syntax::NodeOrToken::Token(t) => {
- println!("Found token: {:?}", t);
- syntax::NodeOrToken::Token(t)
- }
+ .map(|it| match &it {
+ syntax::NodeOrToken::Node(n) => syntax::NodeOrToken::Node(
+ rewrite_body_segment(ctx, &fun.params, &handler, &n),
+ ),
+ _ => it,
})
.collect();
let mut tail_expr = match &elements.last() {
- Some(element) => match element {
- syntax::NodeOrToken::Node(node) if ast::Expr::can_cast(node.kind()) => {
- ast::Expr::cast(node.clone())
- }
- _ => None,
- },
- None => None,
+ Some(syntax::NodeOrToken::Node(node)) if ast::Expr::can_cast(node.kind()) => {
+ ast::Expr::cast(node.clone())
+ }
+ _ => None,
};
match tail_expr {
};
let body_indent = IndentLevel(1);
- let elements: Vec<SyntaxElement> = elements
+ let elements = elements
.into_iter()
.map(|node_or_token| match &node_or_token {
syntax::NodeOrToken::Node(node) => match ast::Stmt::cast(node.clone()) {
let ast_node = indented.syntax().clone_subtree();
syntax::NodeOrToken::Node(ast_node)
}
- None => node_or_token,
+ _ => node_or_token,
},
_ => node_or_token,
})
.collect::<Vec<SyntaxElement>>();
let tail_expr = tail_expr.map(|expr| expr.dedent(old_indent).indent(body_indent));
- for element in &elements {
- println!("element: {:?}", element);
- }
-
- make::block_expr_full(elements, tail_expr)
-
- // make::block_expr(parent.statements().into_iter(), tail_expr)
+ make::hacky_block_expr_with_comments(elements, tail_expr)
}
};
);
}
+ #[test]
+ fn extract_does_not_tear_body_apart() {
+ check_assist(
+ extract_function,
+ r#"
+fn foo() {
+ $0foo();
+}$0
+"#,
+ r#"
+fn foo() {
+ fun_name();
+}
+
+fn $0fun_name() {
+ foo();
+}
+"#,
+ );
+ }
+
#[test]
fn extract_does_not_wrap_res_in_res() {
check_assist(
extract_function,
r#"
fn func() {
- let i = 0;
- $0
+ let i = 0;$0
let a = 0;
// comment here!
let x = 0;$0
r#"
fn func() {
let i = 0;
-
fun_name();
}
);
}
+ // FIXME: we do want to preserve whitespace
#[test]
fn extract_function_does_not_preserve_whitespace() {
check_assist(