ast::{
self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner,
},
- TextSize, T,
+ T,
};
use stdx::{format_to, SepBy};
// }
//
// impl<T: Clone> Ctx<T> {
-// fn new(data: T) -> Self { Self { data } }
+// fn $0new(data: T) -> Self { Self { data } }
// }
//
// ```
let impl_def = find_struct_impl(&ctx, &strukt)?;
let target = strukt.syntax().text_range();
- acc.add(AssistId("add_new"), "Add default constructor", target, |edit| {
+ acc.add(AssistId("add_new"), "Add default constructor", target, |builder| {
let mut buf = String::with_capacity(512);
if impl_def.is_some() {
buf.push('\n');
}
- let vis = strukt.visibility().map(|v| format!("{} ", v));
- let vis = vis.as_deref().unwrap_or("");
+ let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
let params = field_list
.fields()
.filter_map(|f| {
- Some(format!(
- "{}: {}",
- f.name()?.syntax().text(),
- f.ascribed_type()?.syntax().text()
- ))
+ Some(format!("{}: {}", f.name()?.syntax(), f.ascribed_type()?.syntax()))
})
.sep_by(", ");
let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", ");
format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
- let (start_offset, end_offset) = impl_def
+ let start_offset = impl_def
.and_then(|impl_def| {
buf.push('\n');
let start = impl_def
.text_range()
.end();
- Some((start, TextSize::of("\n")))
+ Some(start)
})
.unwrap_or_else(|| {
buf = generate_impl_text(&strukt, &buf);
- let start = strukt.syntax().text_range().end();
-
- (start, TextSize::of("\n}\n"))
+ strukt.syntax().text_range().end()
});
- edit.set_cursor(start_offset + TextSize::of(&buf) - end_offset);
- edit.insert(start_offset, buf);
+ match ctx.config.snippet_cap {
+ None => builder.insert(start_offset, buf),
+ Some(cap) => {
+ buf = buf.replace("fn new", "fn $0new");
+ builder.insert_snippet(cap, start_offset, buf);
+ }
+ }
})
}
"struct Foo {}
impl Foo {
- fn new() -> Self { Self { } }<|>
+ fn $0new() -> Self { Self { } }
}
",
);
"struct Foo<T: Clone> {}
impl<T: Clone> Foo<T> {
- fn new() -> Self { Self { } }<|>
+ fn $0new() -> Self { Self { } }
}
",
);
"struct Foo<'a, T: Foo<'a>> {}
impl<'a, T: Foo<'a>> Foo<'a, T> {
- fn new() -> Self { Self { } }<|>
+ fn $0new() -> Self { Self { } }
}
",
);
"struct Foo { baz: String }
impl Foo {
- fn new(baz: String) -> Self { Self { baz } }<|>
+ fn $0new(baz: String) -> Self { Self { baz } }
}
",
);
"struct Foo { baz: String, qux: Vec<i32> }
impl Foo {
- fn new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }<|>
+ fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
}
",
);
"struct Foo { pub baz: String, pub qux: Vec<i32> }
impl Foo {
- fn new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }<|>
+ fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
}
",
);
"struct Foo {}
impl Foo {
- fn new() -> Self { Self { } }<|>
+ fn $0new() -> Self { Self { } }
}
",
);
"struct Foo {}
impl Foo {
- fn new() -> Self { Self { } }<|>
+ fn $0new() -> Self { Self { } }
fn qux(&self) {}
}
"struct Foo {}
impl Foo {
- fn new() -> Self { Self { } }<|>
+ fn $0new() -> Self { Self { } }
fn qux(&self) {}
fn baz() -> i32 {
"pub struct Foo {}
impl Foo {
- pub fn new() -> Self { Self { } }<|>
+ pub fn $0new() -> Self { Self { } }
}
",
);
"pub(crate) struct Foo {}
impl Foo {
- pub(crate) fn new() -> Self { Self { } }<|>
+ pub(crate) fn $0new() -> Self { Self { } }
}
",
);
}
impl<T> Source<T> {
- pub fn new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }<|>
+ pub fn $0new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
Source { file_id: self.file_id, ast: f(self.ast) }
use ra_syntax::{
ast::{self, BlockExpr, Expr, LoopBodyOwner},
- AstNode,
- SyntaxKind::{COMMENT, WHITESPACE},
- SyntaxNode, TextSize,
+ AstNode, SyntaxNode,
};
use crate::{AssistContext, AssistId, Assists};
// ```
// ->
// ```
-// fn foo() -> Result<i32, > { Ok(42i32) }
+// fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
// ```
pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
- let fn_def = ctx.find_node_at_offset::<ast::FnDef>();
- let fn_def = &mut fn_def?;
- let ret_type = &fn_def.ret_type()?.type_ref()?;
- if ret_type.syntax().text().to_string().starts_with("Result<") {
+ let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
+ // FIXME: extend to lambdas as well
+ let fn_def = ret_type.syntax().parent().and_then(ast::FnDef::cast)?;
+
+ let type_ref = &ret_type.type_ref()?;
+ if type_ref.syntax().text().to_string().starts_with("Result<") {
return None;
}
let block_expr = &fn_def.body()?;
- let cursor_in_ret_type =
- fn_def.ret_type()?.syntax().text_range().contains_range(ctx.frange.range);
- if !cursor_in_ret_type {
- return None;
- }
acc.add(
AssistId("change_return_type_to_result"),
"Change return type to Result",
- ret_type.syntax().text_range(),
- |edit| {
+ type_ref.syntax().text_range(),
+ |builder| {
let mut tail_return_expr_collector = TailReturnCollector::new();
tail_return_expr_collector.collect_jump_exprs(block_expr, false);
tail_return_expr_collector.collect_tail_exprs(block_expr);
for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap {
- edit.replace_node_and_indent(&ret_expr_arg, format!("Ok({})", ret_expr_arg));
+ builder.replace_node_and_indent(&ret_expr_arg, format!("Ok({})", ret_expr_arg));
}
- edit.replace_node_and_indent(ret_type.syntax(), format!("Result<{}, >", ret_type));
- if let Some(node_start) = result_insertion_offset(&ret_type) {
- edit.set_cursor(node_start + TextSize::of(&format!("Result<{}, ", ret_type)));
+ match ctx.config.snippet_cap {
+ Some(cap) => {
+ let snippet = format!("Result<{}, ${{0:_}}>", type_ref);
+ builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet)
+ }
+ None => builder
+ .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)),
}
},
)
}
}
-fn result_insertion_offset(ret_type: &ast::TypeRef) -> Option<TextSize> {
- let non_ws_child = ret_type
- .syntax()
- .children_with_tokens()
- .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?;
- Some(non_ws_child.text_range().start())
-}
-
#[cfg(test)]
mod tests {
-
use crate::tests::{check_assist, check_assist_not_applicable};
use super::*;
let test = "test";
return 42i32;
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
let test = "test";
return Ok(42i32);
}"#,
let test = "test";
return 42i32;
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
let test = "test";
return Ok(42i32);
}"#,
let test = "test";
return 42i32;
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
let test = "test";
return Ok(42i32);
}"#,
let test = "test";
42i32
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
let test = "test";
Ok(42i32)
}"#,
r#"fn foo() -> i32<|> {
42i32
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
Ok(42i32)
}"#,
);
24i32
}
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
if true {
Ok(42i32)
} else {
24i32
}
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
if true {
if false {
Ok(1)
24i32.await
}
}"#,
- r#"async fn foo() -> Result<i32, <|>> {
+ r#"async fn foo() -> Result<i32, ${0:_}> {
if true {
if false {
Ok(1.await)
r#"fn foo() -> [i32;<|> 3] {
[1, 2, 3]
}"#,
- r#"fn foo() -> Result<[i32; 3], <|>> {
+ r#"fn foo() -> Result<[i32; 3], ${0:_}> {
Ok([1, 2, 3])
}"#,
);
24 as i32
}
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
if true {
if false {
Ok(1 as i32)
_ => 24i32,
}
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
let my_var = 5;
match my_var {
5 => Ok(42i32),
my_var
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
let my_var = 5;
loop {
println!("test");
my_var
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
let my_var = let x = loop {
break 1;
};
res
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
let my_var = 5;
let res = match my_var {
5 => 42i32,
res
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
let my_var = 5;
let res = if my_var == 5 {
42i32
},
}
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
let my_var = 5;
match my_var {
5 => {
}
53i32
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
let test = "test";
if test == "test" {
return Ok(24i32);
the_field
}"#,
- r#"fn foo(the_field: u32) -> Result<u32, <|>> {
+ r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
let true_closure = || {
return true;
};
t.unwrap_or_else(|| the_field)
}"#,
- r#"fn foo(the_field: u32) -> Result<u32, <|>> {
+ r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
let true_closure = || {
return true;
};
i += 1;
}
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
let test = "test";
if test == "test" {
return Ok(24i32);
}
}
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
let test = "test";
if test == "test" {
return Ok(24i32);
}
}
}"#,
- r#"fn foo() -> Result<i32, <|>> {
+ r#"fn foo() -> Result<i32, ${0:_}> {
let test = "test";
let other = 5;
if test == "test" {
the_field
}"#,
- r#"fn foo(the_field: u32) -> Result<u32, <|>> {
+ r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
if the_field < 5 {
let mut i = 0;
loop {
the_field
}"#,
- r#"fn foo(the_field: u32) -> Result<u32, <|>> {
+ r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
if the_field < 5 {
let mut i = 0;
the_field
}"#,
- r#"fn foo(the_field: u32) -> Result<u32, <|>> {
+ r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
if the_field < 5 {
let mut i = 0;
the_field
}"#,
- r#"fn foo(the_field: u32) -> Result<u32, <|>> {
+ r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
if the_field < 5 {
let mut i = 0;