diff options
-rw-r--r-- | crates/ra_assists/src/handlers/add_explicit_type.rs | 5 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/add_from_impl_for_enum.rs | 7 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/add_function.rs | 137 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/replace_unwrap_with_match.rs | 2 | ||||
-rw-r--r-- | crates/ra_assists/src/tests/generated.rs | 2 | ||||
-rw-r--r-- | crates/ra_assists/src/utils.rs | 30 | ||||
-rw-r--r-- | crates/ra_syntax/src/algo.rs | 36 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/edit.rs | 44 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/make.rs | 36 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 3 | ||||
-rw-r--r-- | crates/stdx/src/lib.rs | 8 | ||||
-rw-r--r-- | docs/user/assists.md | 2 |
12 files changed, 205 insertions, 107 deletions
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index 0c7d5e355..770049212 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs | |||
@@ -25,9 +25,8 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio | |||
25 | let stmt = ctx.find_node_at_offset::<LetStmt>()?; | 25 | let stmt = ctx.find_node_at_offset::<LetStmt>()?; |
26 | let module = ctx.sema.scope(stmt.syntax()).module()?; | 26 | let module = ctx.sema.scope(stmt.syntax()).module()?; |
27 | let expr = stmt.initializer()?; | 27 | let expr = stmt.initializer()?; |
28 | let pat = stmt.pat()?; | ||
29 | // Must be a binding | 28 | // Must be a binding |
30 | let pat = match pat { | 29 | let pat = match stmt.pat()? { |
31 | ast::Pat::BindPat(bind_pat) => bind_pat, | 30 | ast::Pat::BindPat(bind_pat) => bind_pat, |
32 | _ => return None, | 31 | _ => return None, |
33 | }; | 32 | }; |
@@ -46,7 +45,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio | |||
46 | // Assist not applicable if the type has already been specified | 45 | // Assist not applicable if the type has already been specified |
47 | // and it has no placeholders | 46 | // and it has no placeholders |
48 | let ascribed_ty = stmt.ascribed_type(); | 47 | let ascribed_ty = stmt.ascribed_type(); |
49 | if let Some(ref ty) = ascribed_ty { | 48 | if let Some(ty) = &ascribed_ty { |
50 | if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() { | 49 | if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() { |
51 | return None; | 50 | return None; |
52 | } | 51 | } |
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs index 6a49b7dbd..eb57b7231 100644 --- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs +++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs | |||
@@ -1,6 +1,5 @@ | |||
1 | use ra_ide_db::RootDatabase; | 1 | use ra_ide_db::RootDatabase; |
2 | use ra_syntax::ast::{self, AstNode, NameOwner}; | 2 | use ra_syntax::ast::{self, AstNode, NameOwner}; |
3 | use stdx::format_to; | ||
4 | use test_utils::tested_by; | 3 | use test_utils::tested_by; |
5 | 4 | ||
6 | use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; | 5 | use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; |
@@ -35,7 +34,7 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> | |||
35 | } | 34 | } |
36 | let field_type = field_list.fields().next()?.type_ref()?; | 35 | let field_type = field_list.fields().next()?.type_ref()?; |
37 | let path = match field_type { | 36 | let path = match field_type { |
38 | ast::TypeRef::PathType(p) => p, | 37 | ast::TypeRef::PathType(it) => it, |
39 | _ => return None, | 38 | _ => return None, |
40 | }; | 39 | }; |
41 | 40 | ||
@@ -51,9 +50,7 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> | |||
51 | target, | 50 | target, |
52 | |edit| { | 51 | |edit| { |
53 | let start_offset = variant.parent_enum().syntax().text_range().end(); | 52 | let start_offset = variant.parent_enum().syntax().text_range().end(); |
54 | let mut buf = String::new(); | 53 | let buf = format!( |
55 | format_to!( | ||
56 | buf, | ||
57 | r#" | 54 | r#" |
58 | 55 | ||
59 | impl From<{0}> for {1} {{ | 56 | impl From<{0}> for {1} {{ |
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index de016ae4e..a0709630d 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs | |||
@@ -4,13 +4,13 @@ use ra_syntax::{ | |||
4 | ast::{ | 4 | ast::{ |
5 | self, | 5 | self, |
6 | edit::{AstNodeEdit, IndentLevel}, | 6 | edit::{AstNodeEdit, IndentLevel}, |
7 | ArgListOwner, AstNode, ModuleItemOwner, | 7 | make, ArgListOwner, AstNode, ModuleItemOwner, |
8 | }, | 8 | }, |
9 | SyntaxKind, SyntaxNode, TextSize, | 9 | SyntaxKind, SyntaxNode, TextSize, |
10 | }; | 10 | }; |
11 | use rustc_hash::{FxHashMap, FxHashSet}; | 11 | use rustc_hash::{FxHashMap, FxHashSet}; |
12 | 12 | ||
13 | use crate::{AssistContext, AssistId, Assists}; | 13 | use crate::{assist_config::SnippetCap, utils::render_snippet, AssistContext, AssistId, Assists}; |
14 | 14 | ||
15 | // Assist: add_function | 15 | // Assist: add_function |
16 | // | 16 | // |
@@ -33,7 +33,7 @@ use crate::{AssistContext, AssistId, Assists}; | |||
33 | // } | 33 | // } |
34 | // | 34 | // |
35 | // fn bar(arg: &str, baz: Baz) { | 35 | // fn bar(arg: &str, baz: Baz) { |
36 | // todo!() | 36 | // ${0:todo!()} |
37 | // } | 37 | // } |
38 | // | 38 | // |
39 | // ``` | 39 | // ``` |
@@ -58,21 +58,36 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
58 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; | 58 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; |
59 | 59 | ||
60 | let target = call.syntax().text_range(); | 60 | let target = call.syntax().text_range(); |
61 | acc.add(AssistId("add_function"), "Add function", target, |edit| { | 61 | acc.add(AssistId("add_function"), "Add function", target, |builder| { |
62 | let function_template = function_builder.render(); | 62 | let function_template = function_builder.render(); |
63 | edit.set_file(function_template.file); | 63 | builder.set_file(function_template.file); |
64 | edit.set_cursor(function_template.cursor_offset); | 64 | let new_fn = function_template.to_string(ctx.config.snippet_cap); |
65 | edit.insert(function_template.insert_offset, function_template.fn_def.to_string()); | 65 | match ctx.config.snippet_cap { |
66 | Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), | ||
67 | None => builder.insert(function_template.insert_offset, new_fn), | ||
68 | } | ||
66 | }) | 69 | }) |
67 | } | 70 | } |
68 | 71 | ||
69 | struct FunctionTemplate { | 72 | struct FunctionTemplate { |
70 | insert_offset: TextSize, | 73 | insert_offset: TextSize, |
71 | cursor_offset: TextSize, | 74 | placeholder_expr: ast::MacroCall, |
72 | fn_def: ast::SourceFile, | 75 | leading_ws: String, |
76 | fn_def: ast::FnDef, | ||
77 | trailing_ws: String, | ||
73 | file: FileId, | 78 | file: FileId, |
74 | } | 79 | } |
75 | 80 | ||
81 | impl FunctionTemplate { | ||
82 | fn to_string(&self, cap: Option<SnippetCap>) -> String { | ||
83 | let f = match cap { | ||
84 | Some(cap) => render_snippet(cap, self.fn_def.syntax(), self.placeholder_expr.syntax()), | ||
85 | None => self.fn_def.to_string(), | ||
86 | }; | ||
87 | format!("{}{}{}", self.leading_ws, f, self.trailing_ws) | ||
88 | } | ||
89 | } | ||
90 | |||
76 | struct FunctionBuilder { | 91 | struct FunctionBuilder { |
77 | target: GeneratedFunctionTarget, | 92 | target: GeneratedFunctionTarget, |
78 | fn_name: ast::Name, | 93 | fn_name: ast::Name, |
@@ -110,35 +125,41 @@ impl FunctionBuilder { | |||
110 | } | 125 | } |
111 | 126 | ||
112 | fn render(self) -> FunctionTemplate { | 127 | fn render(self) -> FunctionTemplate { |
113 | let placeholder_expr = ast::make::expr_todo(); | 128 | let placeholder_expr = make::expr_todo(); |
114 | let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr)); | 129 | let fn_body = make::block_expr(vec![], Some(placeholder_expr)); |
115 | let mut fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body); | 130 | let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None }; |
116 | if self.needs_pub { | 131 | let mut fn_def = |
117 | fn_def = ast::make::add_pub_crate_modifier(fn_def); | 132 | make::fn_def(visibility, self.fn_name, self.type_params, self.params, fn_body); |
118 | } | 133 | let leading_ws; |
119 | 134 | let trailing_ws; | |
120 | let (fn_def, insert_offset) = match self.target { | 135 | |
136 | let insert_offset = match self.target { | ||
121 | GeneratedFunctionTarget::BehindItem(it) => { | 137 | GeneratedFunctionTarget::BehindItem(it) => { |
122 | let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def); | 138 | let indent = IndentLevel::from_node(&it); |
123 | let indented = with_leading_blank_line.indent(IndentLevel::from_node(&it)); | 139 | leading_ws = format!("\n\n{}", indent); |
124 | (indented, it.text_range().end()) | 140 | fn_def = fn_def.indent(indent); |
141 | trailing_ws = String::new(); | ||
142 | it.text_range().end() | ||
125 | } | 143 | } |
126 | GeneratedFunctionTarget::InEmptyItemList(it) => { | 144 | GeneratedFunctionTarget::InEmptyItemList(it) => { |
127 | let indent_once = IndentLevel(1); | ||
128 | let indent = IndentLevel::from_node(it.syntax()); | 145 | let indent = IndentLevel::from_node(it.syntax()); |
129 | let fn_def = ast::make::add_leading_newlines(1, fn_def); | 146 | leading_ws = format!("\n{}", indent + 1); |
130 | let fn_def = fn_def.indent(indent_once); | 147 | fn_def = fn_def.indent(indent + 1); |
131 | let fn_def = ast::make::add_trailing_newlines(1, fn_def); | 148 | trailing_ws = format!("\n{}", indent); |
132 | let fn_def = fn_def.indent(indent); | 149 | it.syntax().text_range().start() + TextSize::of('{') |
133 | (fn_def, it.syntax().text_range().start() + TextSize::of('{')) | ||
134 | } | 150 | } |
135 | }; | 151 | }; |
136 | 152 | ||
137 | let placeholder_expr = | 153 | let placeholder_expr = |
138 | fn_def.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | 154 | fn_def.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); |
139 | let cursor_offset_from_fn_start = placeholder_expr.syntax().text_range().start(); | 155 | FunctionTemplate { |
140 | let cursor_offset = insert_offset + cursor_offset_from_fn_start; | 156 | insert_offset, |
141 | FunctionTemplate { insert_offset, cursor_offset, fn_def, file: self.file } | 157 | placeholder_expr, |
158 | leading_ws, | ||
159 | fn_def, | ||
160 | trailing_ws, | ||
161 | file: self.file, | ||
162 | } | ||
142 | } | 163 | } |
143 | } | 164 | } |
144 | 165 | ||
@@ -158,7 +179,7 @@ impl GeneratedFunctionTarget { | |||
158 | 179 | ||
159 | fn fn_name(call: &ast::Path) -> Option<ast::Name> { | 180 | fn fn_name(call: &ast::Path) -> Option<ast::Name> { |
160 | let name = call.segment()?.syntax().to_string(); | 181 | let name = call.segment()?.syntax().to_string(); |
161 | Some(ast::make::name(&name)) | 182 | Some(make::name(&name)) |
162 | } | 183 | } |
163 | 184 | ||
164 | /// Computes the type variables and arguments required for the generated function | 185 | /// Computes the type variables and arguments required for the generated function |
@@ -180,8 +201,8 @@ fn fn_args( | |||
180 | }); | 201 | }); |
181 | } | 202 | } |
182 | deduplicate_arg_names(&mut arg_names); | 203 | deduplicate_arg_names(&mut arg_names); |
183 | let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| ast::make::param(name, ty)); | 204 | let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| make::param(name, ty)); |
184 | Some((None, ast::make::param_list(params))) | 205 | Some((None, make::param_list(params))) |
185 | } | 206 | } |
186 | 207 | ||
187 | /// Makes duplicate argument names unique by appending incrementing numbers. | 208 | /// Makes duplicate argument names unique by appending incrementing numbers. |
@@ -316,7 +337,7 @@ fn foo() { | |||
316 | } | 337 | } |
317 | 338 | ||
318 | fn bar() { | 339 | fn bar() { |
319 | <|>todo!() | 340 | ${0:todo!()} |
320 | } | 341 | } |
321 | ", | 342 | ", |
322 | ) | 343 | ) |
@@ -343,7 +364,7 @@ impl Foo { | |||
343 | } | 364 | } |
344 | 365 | ||
345 | fn bar() { | 366 | fn bar() { |
346 | <|>todo!() | 367 | ${0:todo!()} |
347 | } | 368 | } |
348 | ", | 369 | ", |
349 | ) | 370 | ) |
@@ -367,7 +388,7 @@ fn foo1() { | |||
367 | } | 388 | } |
368 | 389 | ||
369 | fn bar() { | 390 | fn bar() { |
370 | <|>todo!() | 391 | ${0:todo!()} |
371 | } | 392 | } |
372 | 393 | ||
373 | fn foo2() {} | 394 | fn foo2() {} |
@@ -393,7 +414,7 @@ mod baz { | |||
393 | } | 414 | } |
394 | 415 | ||
395 | fn bar() { | 416 | fn bar() { |
396 | <|>todo!() | 417 | ${0:todo!()} |
397 | } | 418 | } |
398 | } | 419 | } |
399 | ", | 420 | ", |
@@ -419,7 +440,7 @@ fn foo() { | |||
419 | } | 440 | } |
420 | 441 | ||
421 | fn bar(baz: Baz) { | 442 | fn bar(baz: Baz) { |
422 | <|>todo!() | 443 | ${0:todo!()} |
423 | } | 444 | } |
424 | ", | 445 | ", |
425 | ); | 446 | ); |
@@ -452,7 +473,7 @@ impl Baz { | |||
452 | } | 473 | } |
453 | 474 | ||
454 | fn bar(baz: Baz) { | 475 | fn bar(baz: Baz) { |
455 | <|>todo!() | 476 | ${0:todo!()} |
456 | } | 477 | } |
457 | ", | 478 | ", |
458 | ) | 479 | ) |
@@ -473,7 +494,7 @@ fn foo() { | |||
473 | } | 494 | } |
474 | 495 | ||
475 | fn bar(arg: &str) { | 496 | fn bar(arg: &str) { |
476 | <|>todo!() | 497 | ${0:todo!()} |
477 | } | 498 | } |
478 | "#, | 499 | "#, |
479 | ) | 500 | ) |
@@ -494,7 +515,7 @@ fn foo() { | |||
494 | } | 515 | } |
495 | 516 | ||
496 | fn bar(arg: char) { | 517 | fn bar(arg: char) { |
497 | <|>todo!() | 518 | ${0:todo!()} |
498 | } | 519 | } |
499 | "#, | 520 | "#, |
500 | ) | 521 | ) |
@@ -515,7 +536,7 @@ fn foo() { | |||
515 | } | 536 | } |
516 | 537 | ||
517 | fn bar(arg: i32) { | 538 | fn bar(arg: i32) { |
518 | <|>todo!() | 539 | ${0:todo!()} |
519 | } | 540 | } |
520 | ", | 541 | ", |
521 | ) | 542 | ) |
@@ -536,7 +557,7 @@ fn foo() { | |||
536 | } | 557 | } |
537 | 558 | ||
538 | fn bar(arg: u8) { | 559 | fn bar(arg: u8) { |
539 | <|>todo!() | 560 | ${0:todo!()} |
540 | } | 561 | } |
541 | ", | 562 | ", |
542 | ) | 563 | ) |
@@ -561,7 +582,7 @@ fn foo() { | |||
561 | } | 582 | } |
562 | 583 | ||
563 | fn bar(x: u8) { | 584 | fn bar(x: u8) { |
564 | <|>todo!() | 585 | ${0:todo!()} |
565 | } | 586 | } |
566 | ", | 587 | ", |
567 | ) | 588 | ) |
@@ -584,7 +605,7 @@ fn foo() { | |||
584 | } | 605 | } |
585 | 606 | ||
586 | fn bar(worble: ()) { | 607 | fn bar(worble: ()) { |
587 | <|>todo!() | 608 | ${0:todo!()} |
588 | } | 609 | } |
589 | ", | 610 | ", |
590 | ) | 611 | ) |
@@ -613,7 +634,7 @@ fn baz() { | |||
613 | } | 634 | } |
614 | 635 | ||
615 | fn bar(foo: impl Foo) { | 636 | fn bar(foo: impl Foo) { |
616 | <|>todo!() | 637 | ${0:todo!()} |
617 | } | 638 | } |
618 | ", | 639 | ", |
619 | ) | 640 | ) |
@@ -640,7 +661,7 @@ fn foo() { | |||
640 | } | 661 | } |
641 | 662 | ||
642 | fn bar(baz: &Baz) { | 663 | fn bar(baz: &Baz) { |
643 | <|>todo!() | 664 | ${0:todo!()} |
644 | } | 665 | } |
645 | ", | 666 | ", |
646 | ) | 667 | ) |
@@ -669,7 +690,7 @@ fn foo() { | |||
669 | } | 690 | } |
670 | 691 | ||
671 | fn bar(baz: Baz::Bof) { | 692 | fn bar(baz: Baz::Bof) { |
672 | <|>todo!() | 693 | ${0:todo!()} |
673 | } | 694 | } |
674 | ", | 695 | ", |
675 | ) | 696 | ) |
@@ -692,7 +713,7 @@ fn foo<T>(t: T) { | |||
692 | } | 713 | } |
693 | 714 | ||
694 | fn bar<T>(t: T) { | 715 | fn bar<T>(t: T) { |
695 | <|>todo!() | 716 | ${0:todo!()} |
696 | } | 717 | } |
697 | ", | 718 | ", |
698 | ) | 719 | ) |
@@ -723,7 +744,7 @@ fn foo() { | |||
723 | } | 744 | } |
724 | 745 | ||
725 | fn bar(arg: fn() -> Baz) { | 746 | fn bar(arg: fn() -> Baz) { |
726 | <|>todo!() | 747 | ${0:todo!()} |
727 | } | 748 | } |
728 | ", | 749 | ", |
729 | ) | 750 | ) |
@@ -748,7 +769,7 @@ fn foo() { | |||
748 | } | 769 | } |
749 | 770 | ||
750 | fn bar(closure: impl Fn(i64) -> i64) { | 771 | fn bar(closure: impl Fn(i64) -> i64) { |
751 | <|>todo!() | 772 | ${0:todo!()} |
752 | } | 773 | } |
753 | ", | 774 | ", |
754 | ) | 775 | ) |
@@ -769,7 +790,7 @@ fn foo() { | |||
769 | } | 790 | } |
770 | 791 | ||
771 | fn bar(baz: ()) { | 792 | fn bar(baz: ()) { |
772 | <|>todo!() | 793 | ${0:todo!()} |
773 | } | 794 | } |
774 | ", | 795 | ", |
775 | ) | 796 | ) |
@@ -794,7 +815,7 @@ fn foo() { | |||
794 | } | 815 | } |
795 | 816 | ||
796 | fn bar(baz_1: Baz, baz_2: Baz) { | 817 | fn bar(baz_1: Baz, baz_2: Baz) { |
797 | <|>todo!() | 818 | ${0:todo!()} |
798 | } | 819 | } |
799 | ", | 820 | ", |
800 | ) | 821 | ) |
@@ -819,7 +840,7 @@ fn foo() { | |||
819 | } | 840 | } |
820 | 841 | ||
821 | fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) { | 842 | fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) { |
822 | <|>todo!() | 843 | ${0:todo!()} |
823 | } | 844 | } |
824 | "#, | 845 | "#, |
825 | ) | 846 | ) |
@@ -839,7 +860,7 @@ fn foo() { | |||
839 | r" | 860 | r" |
840 | mod bar { | 861 | mod bar { |
841 | pub(crate) fn my_fn() { | 862 | pub(crate) fn my_fn() { |
842 | <|>todo!() | 863 | ${0:todo!()} |
843 | } | 864 | } |
844 | } | 865 | } |
845 | 866 | ||
@@ -878,7 +899,7 @@ fn bar() { | |||
878 | } | 899 | } |
879 | 900 | ||
880 | fn baz(foo: foo::Foo) { | 901 | fn baz(foo: foo::Foo) { |
881 | <|>todo!() | 902 | ${0:todo!()} |
882 | } | 903 | } |
883 | ", | 904 | ", |
884 | ) | 905 | ) |
@@ -902,7 +923,7 @@ mod bar { | |||
902 | fn something_else() {} | 923 | fn something_else() {} |
903 | 924 | ||
904 | pub(crate) fn my_fn() { | 925 | pub(crate) fn my_fn() { |
905 | <|>todo!() | 926 | ${0:todo!()} |
906 | } | 927 | } |
907 | } | 928 | } |
908 | 929 | ||
@@ -930,7 +951,7 @@ fn foo() { | |||
930 | mod bar { | 951 | mod bar { |
931 | mod baz { | 952 | mod baz { |
932 | pub(crate) fn my_fn() { | 953 | pub(crate) fn my_fn() { |
933 | <|>todo!() | 954 | ${0:todo!()} |
934 | } | 955 | } |
935 | } | 956 | } |
936 | } | 957 | } |
@@ -959,7 +980,7 @@ fn main() { | |||
959 | 980 | ||
960 | 981 | ||
961 | pub(crate) fn bar() { | 982 | pub(crate) fn bar() { |
962 | <|>todo!() | 983 | ${0:todo!()} |
963 | }", | 984 | }", |
964 | ) | 985 | ) |
965 | } | 986 | } |
diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs index c4b56f6e9..b379b55a8 100644 --- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs | |||
@@ -51,7 +51,7 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
51 | let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); | 51 | let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); |
52 | let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); | 52 | let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); |
53 | 53 | ||
54 | let unreachable_call = make::unreachable_macro_call().into(); | 54 | let unreachable_call = make::expr_unreachable(); |
55 | let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); | 55 | let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); |
56 | 56 | ||
57 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); | 57 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); |
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index 32fbcdef4..1d82c245d 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs | |||
@@ -78,7 +78,7 @@ fn foo() { | |||
78 | } | 78 | } |
79 | 79 | ||
80 | fn bar(arg: &str, baz: Baz) { | 80 | fn bar(arg: &str, baz: Baz) { |
81 | todo!() | 81 | ${0:todo!()} |
82 | } | 82 | } |
83 | 83 | ||
84 | "#####, | 84 | "#####, |
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index f3fc92ebf..8a26a6808 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs | |||
@@ -1,18 +1,44 @@ | |||
1 | //! Assorted functions shared by several assists. | 1 | //! Assorted functions shared by several assists. |
2 | pub(crate) mod insert_use; | 2 | pub(crate) mod insert_use; |
3 | 3 | ||
4 | use std::iter; | 4 | use std::{iter, ops}; |
5 | 5 | ||
6 | use hir::{Adt, Crate, Semantics, Trait, Type}; | 6 | use hir::{Adt, Crate, Semantics, Trait, Type}; |
7 | use ra_ide_db::RootDatabase; | 7 | use ra_ide_db::RootDatabase; |
8 | use ra_syntax::{ | 8 | use ra_syntax::{ |
9 | ast::{self, make, NameOwner}, | 9 | ast::{self, make, NameOwner}, |
10 | AstNode, T, | 10 | AstNode, SyntaxNode, T, |
11 | }; | 11 | }; |
12 | use rustc_hash::FxHashSet; | 12 | use rustc_hash::FxHashSet; |
13 | 13 | ||
14 | use crate::assist_config::SnippetCap; | ||
15 | |||
14 | pub(crate) use insert_use::insert_use_statement; | 16 | pub(crate) use insert_use::insert_use_statement; |
15 | 17 | ||
18 | pub(crate) fn render_snippet( | ||
19 | _cap: SnippetCap, | ||
20 | node: &SyntaxNode, | ||
21 | placeholder: &SyntaxNode, | ||
22 | ) -> String { | ||
23 | assert!(placeholder.ancestors().any(|it| it == *node)); | ||
24 | let range = placeholder.text_range() - node.text_range().start(); | ||
25 | let range: ops::Range<usize> = range.into(); | ||
26 | |||
27 | let mut placeholder = placeholder.to_string(); | ||
28 | escape(&mut placeholder); | ||
29 | let tab_stop = format!("${{0:{}}}", placeholder); | ||
30 | |||
31 | let mut buf = node.to_string(); | ||
32 | buf.replace_range(range, &tab_stop); | ||
33 | return buf; | ||
34 | |||
35 | fn escape(buf: &mut String) { | ||
36 | stdx::replace(buf, '{', r"\{"); | ||
37 | stdx::replace(buf, '}', r"\}"); | ||
38 | stdx::replace(buf, '$', r"\$"); | ||
39 | } | ||
40 | } | ||
41 | |||
16 | pub fn get_missing_assoc_items( | 42 | pub fn get_missing_assoc_items( |
17 | sema: &Semantics<RootDatabase>, | 43 | sema: &Semantics<RootDatabase>, |
18 | impl_def: &ast::ImplDef, | 44 | impl_def: &ast::ImplDef, |
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index 2a8dac757..664894d1f 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs | |||
@@ -266,6 +266,15 @@ impl<'a> SyntaxRewriter<'a> { | |||
266 | let replacement = Replacement::Single(with.clone().into()); | 266 | let replacement = Replacement::Single(with.clone().into()); |
267 | self.replacements.insert(what, replacement); | 267 | self.replacements.insert(what, replacement); |
268 | } | 268 | } |
269 | pub fn replace_with_many<T: Clone + Into<SyntaxElement>>( | ||
270 | &mut self, | ||
271 | what: &T, | ||
272 | with: Vec<SyntaxElement>, | ||
273 | ) { | ||
274 | let what = what.clone().into(); | ||
275 | let replacement = Replacement::Many(with); | ||
276 | self.replacements.insert(what, replacement); | ||
277 | } | ||
269 | pub fn replace_ast<T: AstNode>(&mut self, what: &T, with: &T) { | 278 | pub fn replace_ast<T: AstNode>(&mut self, what: &T, with: &T) { |
270 | self.replace(what.syntax(), with.syntax()) | 279 | self.replace(what.syntax(), with.syntax()) |
271 | } | 280 | } |
@@ -302,31 +311,41 @@ impl<'a> SyntaxRewriter<'a> { | |||
302 | 311 | ||
303 | fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { | 312 | fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { |
304 | // FIXME: this could be made much faster. | 313 | // FIXME: this could be made much faster. |
305 | let new_children = | 314 | let mut new_children = Vec::new(); |
306 | node.children_with_tokens().flat_map(|it| self.rewrite_self(&it)).collect::<Vec<_>>(); | 315 | for child in node.children_with_tokens() { |
316 | self.rewrite_self(&mut new_children, &child); | ||
317 | } | ||
307 | with_children(node, new_children) | 318 | with_children(node, new_children) |
308 | } | 319 | } |
309 | 320 | ||
310 | fn rewrite_self( | 321 | fn rewrite_self( |
311 | &self, | 322 | &self, |
323 | acc: &mut Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, | ||
312 | element: &SyntaxElement, | 324 | element: &SyntaxElement, |
313 | ) -> Option<NodeOrToken<rowan::GreenNode, rowan::GreenToken>> { | 325 | ) { |
314 | if let Some(replacement) = self.replacement(&element) { | 326 | if let Some(replacement) = self.replacement(&element) { |
315 | return match replacement { | 327 | match replacement { |
316 | Replacement::Single(NodeOrToken::Node(it)) => { | 328 | Replacement::Single(NodeOrToken::Node(it)) => { |
317 | Some(NodeOrToken::Node(it.green().clone())) | 329 | acc.push(NodeOrToken::Node(it.green().clone())) |
318 | } | 330 | } |
319 | Replacement::Single(NodeOrToken::Token(it)) => { | 331 | Replacement::Single(NodeOrToken::Token(it)) => { |
320 | Some(NodeOrToken::Token(it.green().clone())) | 332 | acc.push(NodeOrToken::Token(it.green().clone())) |
321 | } | 333 | } |
322 | Replacement::Delete => None, | 334 | Replacement::Many(replacements) => { |
335 | acc.extend(replacements.iter().map(|it| match it { | ||
336 | NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()), | ||
337 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), | ||
338 | })) | ||
339 | } | ||
340 | Replacement::Delete => (), | ||
323 | }; | 341 | }; |
342 | return; | ||
324 | } | 343 | } |
325 | let res = match element { | 344 | let res = match element { |
326 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), | 345 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), |
327 | NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()), | 346 | NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()), |
328 | }; | 347 | }; |
329 | Some(res) | 348 | acc.push(res) |
330 | } | 349 | } |
331 | } | 350 | } |
332 | 351 | ||
@@ -341,6 +360,7 @@ impl ops::AddAssign for SyntaxRewriter<'_> { | |||
341 | enum Replacement { | 360 | enum Replacement { |
342 | Delete, | 361 | Delete, |
343 | Single(SyntaxElement), | 362 | Single(SyntaxElement), |
363 | Many(Vec<SyntaxElement>), | ||
344 | } | 364 | } |
345 | 365 | ||
346 | fn with_children( | 366 | fn with_children( |
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 24a1e1d91..29eb3fcb9 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs | |||
@@ -1,7 +1,10 @@ | |||
1 | //! This module contains functions for editing syntax trees. As the trees are | 1 | //! This module contains functions for editing syntax trees. As the trees are |
2 | //! immutable, all function here return a fresh copy of the tree, instead of | 2 | //! immutable, all function here return a fresh copy of the tree, instead of |
3 | //! doing an in-place modification. | 3 | //! doing an in-place modification. |
4 | use std::{iter, ops::RangeInclusive}; | 4 | use std::{ |
5 | fmt, iter, | ||
6 | ops::{self, RangeInclusive}, | ||
7 | }; | ||
5 | 8 | ||
6 | use arrayvec::ArrayVec; | 9 | use arrayvec::ArrayVec; |
7 | 10 | ||
@@ -437,6 +440,28 @@ impl From<u8> for IndentLevel { | |||
437 | } | 440 | } |
438 | } | 441 | } |
439 | 442 | ||
443 | impl fmt::Display for IndentLevel { | ||
444 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
445 | let spaces = " "; | ||
446 | let buf; | ||
447 | let len = self.0 as usize * 4; | ||
448 | let indent = if len <= spaces.len() { | ||
449 | &spaces[..len] | ||
450 | } else { | ||
451 | buf = iter::repeat(' ').take(len).collect::<String>(); | ||
452 | &buf | ||
453 | }; | ||
454 | fmt::Display::fmt(indent, f) | ||
455 | } | ||
456 | } | ||
457 | |||
458 | impl ops::Add<u8> for IndentLevel { | ||
459 | type Output = IndentLevel; | ||
460 | fn add(self, rhs: u8) -> IndentLevel { | ||
461 | IndentLevel(self.0 + rhs) | ||
462 | } | ||
463 | } | ||
464 | |||
440 | impl IndentLevel { | 465 | impl IndentLevel { |
441 | pub fn from_node(node: &SyntaxNode) -> IndentLevel { | 466 | pub fn from_node(node: &SyntaxNode) -> IndentLevel { |
442 | let first_token = match node.first_token() { | 467 | let first_token = match node.first_token() { |
@@ -453,6 +478,14 @@ impl IndentLevel { | |||
453 | IndentLevel(0) | 478 | IndentLevel(0) |
454 | } | 479 | } |
455 | 480 | ||
481 | /// XXX: this intentionally doesn't change the indent of the very first token. | ||
482 | /// Ie, in something like | ||
483 | /// ``` | ||
484 | /// fn foo() { | ||
485 | /// 92 | ||
486 | /// } | ||
487 | /// ``` | ||
488 | /// if you indent the block, the `{` token would stay put. | ||
456 | fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { | 489 | fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { |
457 | let mut rewriter = SyntaxRewriter::default(); | 490 | let mut rewriter = SyntaxRewriter::default(); |
458 | node.descendants_with_tokens() | 491 | node.descendants_with_tokens() |
@@ -463,12 +496,7 @@ impl IndentLevel { | |||
463 | text.contains('\n') | 496 | text.contains('\n') |
464 | }) | 497 | }) |
465 | .for_each(|ws| { | 498 | .for_each(|ws| { |
466 | let new_ws = make::tokens::whitespace(&format!( | 499 | let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self,)); |
467 | "{}{:width$}", | ||
468 | ws.syntax().text(), | ||
469 | "", | ||
470 | width = self.0 as usize * 4 | ||
471 | )); | ||
472 | rewriter.replace(ws.syntax(), &new_ws) | 500 | rewriter.replace(ws.syntax(), &new_ws) |
473 | }); | 501 | }); |
474 | rewriter.rewrite(&node) | 502 | rewriter.rewrite(&node) |
@@ -485,7 +513,7 @@ impl IndentLevel { | |||
485 | }) | 513 | }) |
486 | .for_each(|ws| { | 514 | .for_each(|ws| { |
487 | let new_ws = make::tokens::whitespace( | 515 | let new_ws = make::tokens::whitespace( |
488 | &ws.syntax().text().replace(&format!("\n{:1$}", "", self.0 as usize * 4), "\n"), | 516 | &ws.syntax().text().replace(&format!("\n{}", self), "\n"), |
489 | ); | 517 | ); |
490 | rewriter.replace(ws.syntax(), &new_ws) | 518 | rewriter.replace(ws.syntax(), &new_ws) |
491 | }); | 519 | }); |
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index d0e960fb4..da0eb0926 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs | |||
@@ -1,5 +1,9 @@ | |||
1 | //! This module contains free-standing functions for creating AST fragments out | 1 | //! This module contains free-standing functions for creating AST fragments out |
2 | //! of smaller pieces. | 2 | //! of smaller pieces. |
3 | //! | ||
4 | //! Note that all functions here intended to be stupid constructors, which just | ||
5 | //! assemble a finish node from immediate children. If you want to do something | ||
6 | //! smarter than that, it probably doesn't belong in this module. | ||
3 | use itertools::Itertools; | 7 | use itertools::Itertools; |
4 | use stdx::format_to; | 8 | use stdx::format_to; |
5 | 9 | ||
@@ -95,6 +99,9 @@ pub fn expr_empty_block() -> ast::Expr { | |||
95 | pub fn expr_unimplemented() -> ast::Expr { | 99 | pub fn expr_unimplemented() -> ast::Expr { |
96 | expr_from_text("unimplemented!()") | 100 | expr_from_text("unimplemented!()") |
97 | } | 101 | } |
102 | pub fn expr_unreachable() -> ast::Expr { | ||
103 | expr_from_text("unreachable!()") | ||
104 | } | ||
98 | pub fn expr_todo() -> ast::Expr { | 105 | pub fn expr_todo() -> ast::Expr { |
99 | expr_from_text("todo!()") | 106 | expr_from_text("todo!()") |
100 | } | 107 | } |
@@ -264,10 +271,6 @@ pub fn token(kind: SyntaxKind) -> SyntaxToken { | |||
264 | .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) | 271 | .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) |
265 | } | 272 | } |
266 | 273 | ||
267 | pub fn unreachable_macro_call() -> ast::MacroCall { | ||
268 | ast_from_text(&format!("unreachable!()")) | ||
269 | } | ||
270 | |||
271 | pub fn param(name: String, ty: String) -> ast::Param { | 274 | pub fn param(name: String, ty: String) -> ast::Param { |
272 | ast_from_text(&format!("fn f({}: {}) {{ }}", name, ty)) | 275 | ast_from_text(&format!("fn f({}: {}) {{ }}", name, ty)) |
273 | } | 276 | } |
@@ -277,7 +280,12 @@ pub fn param_list(pats: impl IntoIterator<Item = ast::Param>) -> ast::ParamList | |||
277 | ast_from_text(&format!("fn f({}) {{ }}", args)) | 280 | ast_from_text(&format!("fn f({}) {{ }}", args)) |
278 | } | 281 | } |
279 | 282 | ||
283 | pub fn visibility_pub_crate() -> ast::Visibility { | ||
284 | ast_from_text("pub(crate) struct S") | ||
285 | } | ||
286 | |||
280 | pub fn fn_def( | 287 | pub fn fn_def( |
288 | visibility: Option<ast::Visibility>, | ||
281 | fn_name: ast::Name, | 289 | fn_name: ast::Name, |
282 | type_params: Option<ast::TypeParamList>, | 290 | type_params: Option<ast::TypeParamList>, |
283 | params: ast::ParamList, | 291 | params: ast::ParamList, |
@@ -285,21 +293,11 @@ pub fn fn_def( | |||
285 | ) -> ast::FnDef { | 293 | ) -> ast::FnDef { |
286 | let type_params = | 294 | let type_params = |
287 | if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() }; | 295 | if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() }; |
288 | ast_from_text(&format!("fn {}{}{} {}", fn_name, type_params, params, body)) | 296 | let visibility = match visibility { |
289 | } | 297 | None => String::new(), |
290 | 298 | Some(it) => format!("{} ", it), | |
291 | pub fn add_leading_newlines(amount_of_newlines: usize, t: impl AstNode) -> ast::SourceFile { | 299 | }; |
292 | let newlines = "\n".repeat(amount_of_newlines); | 300 | ast_from_text(&format!("{}fn {}{}{} {}", visibility, fn_name, type_params, params, body)) |
293 | ast_from_text(&format!("{}{}", newlines, t.syntax())) | ||
294 | } | ||
295 | |||
296 | pub fn add_trailing_newlines(amount_of_newlines: usize, t: impl AstNode) -> ast::SourceFile { | ||
297 | let newlines = "\n".repeat(amount_of_newlines); | ||
298 | ast_from_text(&format!("{}{}", t.syntax(), newlines)) | ||
299 | } | ||
300 | |||
301 | pub fn add_pub_crate_modifier(fn_def: ast::FnDef) -> ast::FnDef { | ||
302 | ast_from_text(&format!("pub(crate) {}", fn_def)) | ||
303 | } | 301 | } |
304 | 302 | ||
305 | fn ast_from_text<N: AstNode>(text: &str) -> N { | 303 | fn ast_from_text<N: AstNode>(text: &str) -> N { |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 2b1a3378f..af54f81b7 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -639,7 +639,7 @@ fn main() <fold>{ | |||
639 | } | 639 | } |
640 | 640 | ||
641 | pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> { | 641 | pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> { |
642 | let res = if assist.source_change.is_snippet { | 642 | let res = if assist.source_change.cursor_position.is_none() { |
643 | lsp_ext::CodeAction { | 643 | lsp_ext::CodeAction { |
644 | title: assist.label, | 644 | title: assist.label, |
645 | kind: Some(String::new()), | 645 | kind: Some(String::new()), |
@@ -647,6 +647,7 @@ pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_e | |||
647 | command: None, | 647 | command: None, |
648 | } | 648 | } |
649 | } else { | 649 | } else { |
650 | assert!(!assist.source_change.is_snippet); | ||
650 | let source_change = source_change(&world, assist.source_change)?; | 651 | let source_change = source_change(&world, assist.source_change)?; |
651 | let arg = serde_json::to_value(source_change)?; | 652 | let arg = serde_json::to_value(source_change)?; |
652 | let title = assist.label; | 653 | let title = assist.label; |
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index 0f34ce70e..71a57fba2 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs | |||
@@ -116,3 +116,11 @@ pub fn to_lower_snake_case(s: &str) -> String { | |||
116 | } | 116 | } |
117 | buf | 117 | buf |
118 | } | 118 | } |
119 | |||
120 | pub fn replace(buf: &mut String, from: char, to: &str) { | ||
121 | if !buf.contains(from) { | ||
122 | return; | ||
123 | } | ||
124 | // FIXME: do this in place. | ||
125 | *buf = buf.replace(from, to) | ||
126 | } | ||
diff --git a/docs/user/assists.md b/docs/user/assists.md index c72b50a4d..10ab67b2e 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md | |||
@@ -77,7 +77,7 @@ fn foo() { | |||
77 | } | 77 | } |
78 | 78 | ||
79 | fn bar(arg: &str, baz: Baz) { | 79 | fn bar(arg: &str, baz: Baz) { |
80 | todo!() | 80 | ${0:todo!()} |
81 | } | 81 | } |
82 | 82 | ||
83 | ``` | 83 | ``` |