diff options
author | Benjamin Coenen <[email protected]> | 2020-05-06 08:57:00 +0100 |
---|---|---|
committer | Benjamin Coenen <[email protected]> | 2020-05-06 08:57:00 +0100 |
commit | c4d128e454448191c4b21d8e151c673e4c42376e (patch) | |
tree | 17cf5bbf429642c52708cd0d3c1d8885b63543f0 /crates/ra_ide/src | |
parent | 0bf02f5ccac99c91f10ef46bb06ff2ea316c382c (diff) | |
parent | 30eb458b4fa8adcecd8cbf731bd1cfa9a7a8b88b (diff) |
Merge branch 'master' of github.com:rust-analyzer/rust-analyzer into fix_4311
Diffstat (limited to 'crates/ra_ide/src')
-rw-r--r-- | crates/ra_ide/src/assists.rs | 19 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_qualified_path.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_trait_impl.rs | 46 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/completion_context.rs | 4 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/completion_item.rs | 4 | ||||
-rw-r--r-- | crates/ra_ide/src/diagnostics.rs | 36 | ||||
-rw-r--r-- | crates/ra_ide/src/join_lines.rs | 6 | ||||
-rw-r--r-- | crates/ra_ide/src/references/rename.rs | 42 | ||||
-rw-r--r-- | crates/ra_ide/src/runnables.rs | 43 | ||||
-rw-r--r-- | crates/ra_ide/src/source_change.rs | 4 | ||||
-rw-r--r-- | crates/ra_ide/src/ssr.rs | 32 | ||||
-rw-r--r-- | crates/ra_ide/src/test_utils.rs | 6 | ||||
-rw-r--r-- | crates/ra_ide/src/typing.rs | 9 | ||||
-rw-r--r-- | crates/ra_ide/src/typing/on_enter.rs | 5 |
14 files changed, 169 insertions, 89 deletions
diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs index 2b5d11681..389339a03 100644 --- a/crates/ra_ide/src/assists.rs +++ b/crates/ra_ide/src/assists.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_assists::{resolved_assists, AssistAction, AssistLabel}; | 3 | use ra_assists::{resolved_assists, AssistAction}; |
4 | use ra_db::{FilePosition, FileRange}; | 4 | use ra_db::{FilePosition, FileRange}; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | 6 | ||
@@ -21,27 +21,22 @@ pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> { | |||
21 | .into_iter() | 21 | .into_iter() |
22 | .map(|assist| { | 22 | .map(|assist| { |
23 | let file_id = frange.file_id; | 23 | let file_id = frange.file_id; |
24 | let assist_label = &assist.label; | ||
25 | Assist { | 24 | Assist { |
26 | id: assist_label.id, | 25 | id: assist.label.id, |
27 | label: assist_label.label.clone(), | 26 | label: assist.label.label.clone(), |
28 | group_label: assist.group_label.map(|it| it.0), | 27 | group_label: assist.label.group.map(|it| it.0), |
29 | source_change: action_to_edit(assist.action, file_id, assist_label), | 28 | source_change: action_to_edit(assist.action, file_id, assist.label.label.clone()), |
30 | } | 29 | } |
31 | }) | 30 | }) |
32 | .collect() | 31 | .collect() |
33 | } | 32 | } |
34 | 33 | ||
35 | fn action_to_edit( | 34 | fn action_to_edit(action: AssistAction, file_id: FileId, label: String) -> SourceChange { |
36 | action: AssistAction, | ||
37 | file_id: FileId, | ||
38 | assist_label: &AssistLabel, | ||
39 | ) -> SourceChange { | ||
40 | let file_id = match action.file { | 35 | let file_id = match action.file { |
41 | ra_assists::AssistFile::TargetFile(it) => it, | 36 | ra_assists::AssistFile::TargetFile(it) => it, |
42 | _ => file_id, | 37 | _ => file_id, |
43 | }; | 38 | }; |
44 | let file_edit = SourceFileEdit { file_id, edit: action.edit }; | 39 | let file_edit = SourceFileEdit { file_id, edit: action.edit }; |
45 | SourceChange::source_file_edit(assist_label.label.clone(), file_edit) | 40 | SourceChange::source_file_edit(label, file_edit) |
46 | .with_cursor_opt(action.cursor_position.map(|offset| FilePosition { offset, file_id })) | 41 | .with_cursor_opt(action.cursor_position.map(|offset| FilePosition { offset, file_id })) |
47 | } | 42 | } |
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs index d9ea92ef8..7fcd22525 100644 --- a/crates/ra_ide/src/completion/complete_qualified_path.rs +++ b/crates/ra_ide/src/completion/complete_qualified_path.rs | |||
@@ -84,7 +84,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
84 | }); | 84 | }); |
85 | 85 | ||
86 | // Iterate assoc types separately | 86 | // Iterate assoc types separately |
87 | ty.iterate_impl_items(ctx.db, krate, |item| { | 87 | ty.iterate_assoc_items(ctx.db, krate, |item| { |
88 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | 88 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { |
89 | return None; | 89 | return None; |
90 | } | 90 | } |
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index ee32d1ff6..039df03e0 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs | |||
@@ -32,7 +32,7 @@ | |||
32 | //! ``` | 32 | //! ``` |
33 | 33 | ||
34 | use hir::{self, Docs, HasSource}; | 34 | use hir::{self, Docs, HasSource}; |
35 | use ra_assists::utils::get_missing_impl_items; | 35 | use ra_assists::utils::get_missing_assoc_items; |
36 | use ra_syntax::{ | 36 | use ra_syntax::{ |
37 | ast::{self, edit, ImplDef}, | 37 | ast::{self, edit, ImplDef}, |
38 | AstNode, SyntaxKind, SyntaxNode, TextRange, T, | 38 | AstNode, SyntaxKind, SyntaxNode, TextRange, T, |
@@ -50,7 +50,7 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext | |||
50 | if let Some((trigger, impl_def)) = completion_match(ctx) { | 50 | if let Some((trigger, impl_def)) = completion_match(ctx) { |
51 | match trigger.kind() { | 51 | match trigger.kind() { |
52 | SyntaxKind::NAME_REF => { | 52 | SyntaxKind::NAME_REF => { |
53 | get_missing_impl_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { | 53 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { |
54 | hir::AssocItem::Function(fn_item) => { | 54 | hir::AssocItem::Function(fn_item) => { |
55 | add_function_impl(&trigger, acc, ctx, &fn_item) | 55 | add_function_impl(&trigger, acc, ctx, &fn_item) |
56 | } | 56 | } |
@@ -64,34 +64,40 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext | |||
64 | } | 64 | } |
65 | 65 | ||
66 | SyntaxKind::FN_DEF => { | 66 | SyntaxKind::FN_DEF => { |
67 | for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( | 67 | for missing_fn in |
68 | |item| match item { | 68 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { |
69 | hir::AssocItem::Function(fn_item) => Some(fn_item), | 69 | match item { |
70 | _ => None, | 70 | hir::AssocItem::Function(fn_item) => Some(fn_item), |
71 | }, | 71 | _ => None, |
72 | ) { | 72 | } |
73 | }) | ||
74 | { | ||
73 | add_function_impl(&trigger, acc, ctx, &missing_fn); | 75 | add_function_impl(&trigger, acc, ctx, &missing_fn); |
74 | } | 76 | } |
75 | } | 77 | } |
76 | 78 | ||
77 | SyntaxKind::TYPE_ALIAS_DEF => { | 79 | SyntaxKind::TYPE_ALIAS_DEF => { |
78 | for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( | 80 | for missing_fn in |
79 | |item| match item { | 81 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { |
80 | hir::AssocItem::TypeAlias(type_item) => Some(type_item), | 82 | match item { |
81 | _ => None, | 83 | hir::AssocItem::TypeAlias(type_item) => Some(type_item), |
82 | }, | 84 | _ => None, |
83 | ) { | 85 | } |
86 | }) | ||
87 | { | ||
84 | add_type_alias_impl(&trigger, acc, ctx, &missing_fn); | 88 | add_type_alias_impl(&trigger, acc, ctx, &missing_fn); |
85 | } | 89 | } |
86 | } | 90 | } |
87 | 91 | ||
88 | SyntaxKind::CONST_DEF => { | 92 | SyntaxKind::CONST_DEF => { |
89 | for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( | 93 | for missing_fn in |
90 | |item| match item { | 94 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { |
91 | hir::AssocItem::Const(const_item) => Some(const_item), | 95 | match item { |
92 | _ => None, | 96 | hir::AssocItem::Const(const_item) => Some(const_item), |
93 | }, | 97 | _ => None, |
94 | ) { | 98 | } |
99 | }) | ||
100 | { | ||
95 | add_const_impl(&trigger, acc, ctx, &missing_fn); | 101 | add_const_impl(&trigger, acc, ctx, &missing_fn); |
96 | } | 102 | } |
97 | } | 103 | } |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index dd87bd119..b6b9627de 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -9,7 +9,7 @@ use ra_syntax::{ | |||
9 | SyntaxKind::*, | 9 | SyntaxKind::*, |
10 | SyntaxNode, SyntaxToken, TextRange, TextSize, | 10 | SyntaxNode, SyntaxToken, TextRange, TextSize, |
11 | }; | 11 | }; |
12 | use ra_text_edit::AtomTextEdit; | 12 | use ra_text_edit::Indel; |
13 | 13 | ||
14 | use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; | 14 | use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; |
15 | 15 | ||
@@ -76,7 +76,7 @@ impl<'a> CompletionContext<'a> { | |||
76 | // actual completion. | 76 | // actual completion. |
77 | let file_with_fake_ident = { | 77 | let file_with_fake_ident = { |
78 | let parse = db.parse(position.file_id); | 78 | let parse = db.parse(position.file_id); |
79 | let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); | 79 | let edit = Indel::insert(position.offset, "intellijRulezz".to_string()); |
80 | parse.reparse(&edit).tree() | 80 | parse.reparse(&edit).tree() |
81 | }; | 81 | }; |
82 | let fake_ident_token = | 82 | let fake_ident_token = |
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs index 5936fb8f7..383b23ac4 100644 --- a/crates/ra_ide/src/completion/completion_item.rs +++ b/crates/ra_ide/src/completion/completion_item.rs | |||
@@ -62,8 +62,8 @@ impl fmt::Debug for CompletionItem { | |||
62 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 62 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
63 | let mut s = f.debug_struct("CompletionItem"); | 63 | let mut s = f.debug_struct("CompletionItem"); |
64 | s.field("label", &self.label()).field("source_range", &self.source_range()); | 64 | s.field("label", &self.label()).field("source_range", &self.source_range()); |
65 | if self.text_edit().as_atoms().len() == 1 { | 65 | if self.text_edit().as_indels().len() == 1 { |
66 | let atom = &self.text_edit().as_atoms()[0]; | 66 | let atom = &self.text_edit().as_indels()[0]; |
67 | s.field("delete", &atom.delete); | 67 | s.field("delete", &atom.delete); |
68 | s.field("insert", &atom.insert); | 68 | s.field("insert", &atom.insert); |
69 | } else { | 69 | } else { |
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index a6b4c2c28..87a0b80f1 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -64,7 +64,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
64 | .unwrap_or_else(|| RelativePath::new("")) | 64 | .unwrap_or_else(|| RelativePath::new("")) |
65 | .join(&d.candidate); | 65 | .join(&d.candidate); |
66 | let create_file = FileSystemEdit::CreateFile { source_root, path }; | 66 | let create_file = FileSystemEdit::CreateFile { source_root, path }; |
67 | let fix = SourceChange::file_system_edit("create module", create_file); | 67 | let fix = SourceChange::file_system_edit("Create module", create_file); |
68 | res.borrow_mut().push(Diagnostic { | 68 | res.borrow_mut().push(Diagnostic { |
69 | range: sema.diagnostics_range(d).range, | 69 | range: sema.diagnostics_range(d).range, |
70 | message: d.message(), | 70 | message: d.message(), |
@@ -92,7 +92,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
92 | algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); | 92 | algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); |
93 | 93 | ||
94 | Some(SourceChange::source_file_edit_from( | 94 | Some(SourceChange::source_file_edit_from( |
95 | "fill struct fields", | 95 | "Fill struct fields", |
96 | file_id, | 96 | file_id, |
97 | builder.finish(), | 97 | builder.finish(), |
98 | )) | 98 | )) |
@@ -117,7 +117,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
117 | let node = d.ast(db); | 117 | let node = d.ast(db); |
118 | let replacement = format!("Ok({})", node.syntax()); | 118 | let replacement = format!("Ok({})", node.syntax()); |
119 | let edit = TextEdit::replace(node.syntax().text_range(), replacement); | 119 | let edit = TextEdit::replace(node.syntax().text_range(), replacement); |
120 | let fix = SourceChange::source_file_edit_from("wrap with ok", file_id, edit); | 120 | let fix = SourceChange::source_file_edit_from("Wrap with ok", file_id, edit); |
121 | res.borrow_mut().push(Diagnostic { | 121 | res.borrow_mut().push(Diagnostic { |
122 | range: sema.diagnostics_range(d).range, | 122 | range: sema.diagnostics_range(d).range, |
123 | message: d.message(), | 123 | message: d.message(), |
@@ -199,7 +199,7 @@ fn check_struct_shorthand_initialization( | |||
199 | message: "Shorthand struct initialization".to_string(), | 199 | message: "Shorthand struct initialization".to_string(), |
200 | severity: Severity::WeakWarning, | 200 | severity: Severity::WeakWarning, |
201 | fix: Some(SourceChange::source_file_edit( | 201 | fix: Some(SourceChange::source_file_edit( |
202 | "use struct shorthand initialization", | 202 | "Use struct shorthand initialization", |
203 | SourceFileEdit { file_id, edit }, | 203 | SourceFileEdit { file_id, edit }, |
204 | )), | 204 | )), |
205 | }); | 205 | }); |
@@ -241,7 +241,11 @@ mod tests { | |||
241 | diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before)); | 241 | diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before)); |
242 | let mut fix = diagnostic.fix.unwrap(); | 242 | let mut fix = diagnostic.fix.unwrap(); |
243 | let edit = fix.source_file_edits.pop().unwrap().edit; | 243 | let edit = fix.source_file_edits.pop().unwrap().edit; |
244 | let actual = edit.apply(&before); | 244 | let actual = { |
245 | let mut actual = before.to_string(); | ||
246 | edit.apply(&mut actual); | ||
247 | actual | ||
248 | }; | ||
245 | assert_eq_text!(after, &actual); | 249 | assert_eq_text!(after, &actual); |
246 | } | 250 | } |
247 | 251 | ||
@@ -256,7 +260,11 @@ mod tests { | |||
256 | let mut fix = diagnostic.fix.unwrap(); | 260 | let mut fix = diagnostic.fix.unwrap(); |
257 | let edit = fix.source_file_edits.pop().unwrap().edit; | 261 | let edit = fix.source_file_edits.pop().unwrap().edit; |
258 | let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); | 262 | let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); |
259 | let actual = edit.apply(&target_file_contents); | 263 | let actual = { |
264 | let mut actual = target_file_contents.to_string(); | ||
265 | edit.apply(&mut actual); | ||
266 | actual | ||
267 | }; | ||
260 | 268 | ||
261 | // Strip indent and empty lines from `after`, to match the behaviour of | 269 | // Strip indent and empty lines from `after`, to match the behaviour of |
262 | // `parse_fixture` called from `analysis_and_position`. | 270 | // `parse_fixture` called from `analysis_and_position`. |
@@ -288,7 +296,11 @@ mod tests { | |||
288 | let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); | 296 | let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); |
289 | let mut fix = diagnostic.fix.unwrap(); | 297 | let mut fix = diagnostic.fix.unwrap(); |
290 | let edit = fix.source_file_edits.pop().unwrap().edit; | 298 | let edit = fix.source_file_edits.pop().unwrap().edit; |
291 | let actual = edit.apply(&before); | 299 | let actual = { |
300 | let mut actual = before.to_string(); | ||
301 | edit.apply(&mut actual); | ||
302 | actual | ||
303 | }; | ||
292 | assert_eq_text!(after, &actual); | 304 | assert_eq_text!(after, &actual); |
293 | } | 305 | } |
294 | 306 | ||
@@ -606,7 +618,7 @@ mod tests { | |||
606 | range: 0..8, | 618 | range: 0..8, |
607 | fix: Some( | 619 | fix: Some( |
608 | SourceChange { | 620 | SourceChange { |
609 | label: "create module", | 621 | label: "Create module", |
610 | source_file_edits: [], | 622 | source_file_edits: [], |
611 | file_system_edits: [ | 623 | file_system_edits: [ |
612 | CreateFile { | 624 | CreateFile { |
@@ -655,17 +667,17 @@ mod tests { | |||
655 | range: 224..233, | 667 | range: 224..233, |
656 | fix: Some( | 668 | fix: Some( |
657 | SourceChange { | 669 | SourceChange { |
658 | label: "fill struct fields", | 670 | label: "Fill struct fields", |
659 | source_file_edits: [ | 671 | source_file_edits: [ |
660 | SourceFileEdit { | 672 | SourceFileEdit { |
661 | file_id: FileId( | 673 | file_id: FileId( |
662 | 1, | 674 | 1, |
663 | ), | 675 | ), |
664 | edit: TextEdit { | 676 | edit: TextEdit { |
665 | atoms: [ | 677 | indels: [ |
666 | AtomTextEdit { | 678 | Indel { |
667 | delete: 3..9, | ||
668 | insert: "{a:42, b: ()}", | 679 | insert: "{a:42, b: ()}", |
680 | delete: 3..9, | ||
669 | }, | 681 | }, |
670 | ], | 682 | ], |
671 | }, | 683 | }, |
diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs index 63fd6b3e4..d3af780c4 100644 --- a/crates/ra_ide/src/join_lines.rs +++ b/crates/ra_ide/src/join_lines.rs | |||
@@ -569,7 +569,11 @@ fn foo() { | |||
569 | let (sel, before) = extract_range(before); | 569 | let (sel, before) = extract_range(before); |
570 | let parse = SourceFile::parse(&before); | 570 | let parse = SourceFile::parse(&before); |
571 | let result = join_lines(&parse.tree(), sel); | 571 | let result = join_lines(&parse.tree(), sel); |
572 | let actual = result.apply(&before); | 572 | let actual = { |
573 | let mut actual = before.to_string(); | ||
574 | result.apply(&mut actual); | ||
575 | actual | ||
576 | }; | ||
573 | assert_eq_text!(after, &actual); | 577 | assert_eq_text!(after, &actual); |
574 | } | 578 | } |
575 | 579 | ||
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index 916edaef2..0398d53bc 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs | |||
@@ -122,7 +122,7 @@ fn rename_mod( | |||
122 | source_file_edits.extend(ref_edits); | 122 | source_file_edits.extend(ref_edits); |
123 | } | 123 | } |
124 | 124 | ||
125 | Some(SourceChange::from_edits("rename", source_file_edits, file_system_edits)) | 125 | Some(SourceChange::from_edits("Rename", source_file_edits, file_system_edits)) |
126 | } | 126 | } |
127 | 127 | ||
128 | fn rename_reference( | 128 | fn rename_reference( |
@@ -141,7 +141,7 @@ fn rename_reference( | |||
141 | return None; | 141 | return None; |
142 | } | 142 | } |
143 | 143 | ||
144 | Some(RangeInfo::new(range, SourceChange::source_file_edits("rename", edit))) | 144 | Some(RangeInfo::new(range, SourceChange::source_file_edits("Rename", edit))) |
145 | } | 145 | } |
146 | 146 | ||
147 | #[cfg(test)] | 147 | #[cfg(test)] |
@@ -530,17 +530,17 @@ mod tests { | |||
530 | RangeInfo { | 530 | RangeInfo { |
531 | range: 4..7, | 531 | range: 4..7, |
532 | info: SourceChange { | 532 | info: SourceChange { |
533 | label: "rename", | 533 | label: "Rename", |
534 | source_file_edits: [ | 534 | source_file_edits: [ |
535 | SourceFileEdit { | 535 | SourceFileEdit { |
536 | file_id: FileId( | 536 | file_id: FileId( |
537 | 2, | 537 | 2, |
538 | ), | 538 | ), |
539 | edit: TextEdit { | 539 | edit: TextEdit { |
540 | atoms: [ | 540 | indels: [ |
541 | AtomTextEdit { | 541 | Indel { |
542 | delete: 4..7, | ||
543 | insert: "foo2", | 542 | insert: "foo2", |
543 | delete: 4..7, | ||
544 | }, | 544 | }, |
545 | ], | 545 | ], |
546 | }, | 546 | }, |
@@ -582,17 +582,17 @@ mod tests { | |||
582 | RangeInfo { | 582 | RangeInfo { |
583 | range: 4..7, | 583 | range: 4..7, |
584 | info: SourceChange { | 584 | info: SourceChange { |
585 | label: "rename", | 585 | label: "Rename", |
586 | source_file_edits: [ | 586 | source_file_edits: [ |
587 | SourceFileEdit { | 587 | SourceFileEdit { |
588 | file_id: FileId( | 588 | file_id: FileId( |
589 | 1, | 589 | 1, |
590 | ), | 590 | ), |
591 | edit: TextEdit { | 591 | edit: TextEdit { |
592 | atoms: [ | 592 | indels: [ |
593 | AtomTextEdit { | 593 | Indel { |
594 | delete: 4..7, | ||
595 | insert: "foo2", | 594 | insert: "foo2", |
595 | delete: 4..7, | ||
596 | }, | 596 | }, |
597 | ], | 597 | ], |
598 | }, | 598 | }, |
@@ -665,17 +665,17 @@ mod tests { | |||
665 | RangeInfo { | 665 | RangeInfo { |
666 | range: 8..11, | 666 | range: 8..11, |
667 | info: SourceChange { | 667 | info: SourceChange { |
668 | label: "rename", | 668 | label: "Rename", |
669 | source_file_edits: [ | 669 | source_file_edits: [ |
670 | SourceFileEdit { | 670 | SourceFileEdit { |
671 | file_id: FileId( | 671 | file_id: FileId( |
672 | 2, | 672 | 2, |
673 | ), | 673 | ), |
674 | edit: TextEdit { | 674 | edit: TextEdit { |
675 | atoms: [ | 675 | indels: [ |
676 | AtomTextEdit { | 676 | Indel { |
677 | delete: 8..11, | ||
678 | insert: "foo2", | 677 | insert: "foo2", |
678 | delete: 8..11, | ||
679 | }, | 679 | }, |
680 | ], | 680 | ], |
681 | }, | 681 | }, |
@@ -685,10 +685,10 @@ mod tests { | |||
685 | 1, | 685 | 1, |
686 | ), | 686 | ), |
687 | edit: TextEdit { | 687 | edit: TextEdit { |
688 | atoms: [ | 688 | indels: [ |
689 | AtomTextEdit { | 689 | Indel { |
690 | delete: 27..30, | ||
691 | insert: "foo2", | 690 | insert: "foo2", |
691 | delete: 27..30, | ||
692 | }, | 692 | }, |
693 | ], | 693 | ], |
694 | }, | 694 | }, |
@@ -720,13 +720,13 @@ mod tests { | |||
720 | if let Some(change) = source_change { | 720 | if let Some(change) = source_change { |
721 | for edit in change.info.source_file_edits { | 721 | for edit in change.info.source_file_edits { |
722 | file_id = Some(edit.file_id); | 722 | file_id = Some(edit.file_id); |
723 | for atom in edit.edit.as_atoms() { | 723 | for indel in edit.edit.as_indels() { |
724 | text_edit_builder.replace(atom.delete, atom.insert.clone()); | 724 | text_edit_builder.replace(indel.delete, indel.insert.clone()); |
725 | } | 725 | } |
726 | } | 726 | } |
727 | } | 727 | } |
728 | let result = | 728 | let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string(); |
729 | text_edit_builder.finish().apply(&*analysis.file_text(file_id.unwrap()).unwrap()); | 729 | text_edit_builder.finish().apply(&mut result); |
730 | assert_eq_text!(expected, &*result); | 730 | assert_eq_text!(expected, &*result); |
731 | } | 731 | } |
732 | } | 732 | } |
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 38637c19c..fa8a9d92c 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs | |||
@@ -9,6 +9,7 @@ use ra_syntax::{ | |||
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crate::FileId; | 11 | use crate::FileId; |
12 | use ast::DocCommentsOwner; | ||
12 | use std::fmt::Display; | 13 | use std::fmt::Display; |
13 | 14 | ||
14 | #[derive(Debug)] | 15 | #[derive(Debug)] |
@@ -37,6 +38,7 @@ pub enum RunnableKind { | |||
37 | Test { test_id: TestId, attr: TestAttr }, | 38 | Test { test_id: TestId, attr: TestAttr }, |
38 | TestMod { path: String }, | 39 | TestMod { path: String }, |
39 | Bench { test_id: TestId }, | 40 | Bench { test_id: TestId }, |
41 | DocTest { test_id: TestId }, | ||
40 | Bin, | 42 | Bin, |
41 | } | 43 | } |
42 | 44 | ||
@@ -81,6 +83,8 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Run | |||
81 | RunnableKind::Test { test_id, attr } | 83 | RunnableKind::Test { test_id, attr } |
82 | } else if fn_def.has_atom_attr("bench") { | 84 | } else if fn_def.has_atom_attr("bench") { |
83 | RunnableKind::Bench { test_id } | 85 | RunnableKind::Bench { test_id } |
86 | } else if has_doc_test(&fn_def) { | ||
87 | RunnableKind::DocTest { test_id } | ||
84 | } else { | 88 | } else { |
85 | return None; | 89 | return None; |
86 | } | 90 | } |
@@ -117,6 +121,10 @@ fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool { | |||
117 | .any(|attribute_text| attribute_text.contains("test")) | 121 | .any(|attribute_text| attribute_text.contains("test")) |
118 | } | 122 | } |
119 | 123 | ||
124 | fn has_doc_test(fn_def: &ast::FnDef) -> bool { | ||
125 | fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) | ||
126 | } | ||
127 | |||
120 | fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { | 128 | fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { |
121 | let has_test_function = module | 129 | let has_test_function = module |
122 | .item_list()? | 130 | .item_list()? |
@@ -195,6 +203,41 @@ mod tests { | |||
195 | } | 203 | } |
196 | 204 | ||
197 | #[test] | 205 | #[test] |
206 | fn test_runnables_doc_test() { | ||
207 | let (analysis, pos) = analysis_and_position( | ||
208 | r#" | ||
209 | //- /lib.rs | ||
210 | <|> //empty | ||
211 | fn main() {} | ||
212 | |||
213 | /// ``` | ||
214 | /// let x = 5; | ||
215 | /// ``` | ||
216 | fn foo() {} | ||
217 | "#, | ||
218 | ); | ||
219 | let runnables = analysis.runnables(pos.file_id).unwrap(); | ||
220 | assert_debug_snapshot!(&runnables, | ||
221 | @r###" | ||
222 | [ | ||
223 | Runnable { | ||
224 | range: 1..21, | ||
225 | kind: Bin, | ||
226 | }, | ||
227 | Runnable { | ||
228 | range: 22..64, | ||
229 | kind: DocTest { | ||
230 | test_id: Path( | ||
231 | "foo", | ||
232 | ), | ||
233 | }, | ||
234 | }, | ||
235 | ] | ||
236 | "### | ||
237 | ); | ||
238 | } | ||
239 | |||
240 | #[test] | ||
198 | fn test_runnables_module() { | 241 | fn test_runnables_module() { |
199 | let (analysis, pos) = analysis_and_position( | 242 | let (analysis, pos) = analysis_and_position( |
200 | r#" | 243 | r#" |
diff --git a/crates/ra_ide/src/source_change.rs b/crates/ra_ide/src/source_change.rs index 71b0e8f75..10afd7825 100644 --- a/crates/ra_ide/src/source_change.rs +++ b/crates/ra_ide/src/source_change.rs | |||
@@ -35,8 +35,10 @@ impl SourceChange { | |||
35 | /// Creates a new SourceChange with the given label, | 35 | /// Creates a new SourceChange with the given label, |
36 | /// containing only the given `SourceFileEdits`. | 36 | /// containing only the given `SourceFileEdits`. |
37 | pub(crate) fn source_file_edits<L: Into<String>>(label: L, edits: Vec<SourceFileEdit>) -> Self { | 37 | pub(crate) fn source_file_edits<L: Into<String>>(label: L, edits: Vec<SourceFileEdit>) -> Self { |
38 | let label = label.into(); | ||
39 | assert!(label.starts_with(char::is_uppercase)); | ||
38 | SourceChange { | 40 | SourceChange { |
39 | label: label.into(), | 41 | label: label, |
40 | source_file_edits: edits, | 42 | source_file_edits: edits, |
41 | file_system_edits: vec![], | 43 | file_system_edits: vec![], |
42 | cursor_position: None, | 44 | cursor_position: None, |
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs index 7b93ff2d2..8bf52d0fa 100644 --- a/crates/ra_ide/src/ssr.rs +++ b/crates/ra_ide/src/ssr.rs | |||
@@ -401,16 +401,22 @@ fn render_replace( | |||
401 | ignored_comments: &Vec<Comment>, | 401 | ignored_comments: &Vec<Comment>, |
402 | template: &SsrTemplate, | 402 | template: &SsrTemplate, |
403 | ) -> String { | 403 | ) -> String { |
404 | let mut builder = TextEditBuilder::default(); | 404 | let edit = { |
405 | for element in template.template.descendants() { | 405 | let mut builder = TextEditBuilder::default(); |
406 | if let Some(var) = template.placeholders.get(&element) { | 406 | for element in template.template.descendants() { |
407 | builder.replace(element.text_range(), binding[var].to_string()) | 407 | if let Some(var) = template.placeholders.get(&element) { |
408 | builder.replace(element.text_range(), binding[var].to_string()) | ||
409 | } | ||
408 | } | 410 | } |
409 | } | 411 | for comment in ignored_comments { |
410 | for comment in ignored_comments { | 412 | builder.insert(template.template.text_range().end(), comment.syntax().to_string()) |
411 | builder.insert(template.template.text_range().end(), comment.syntax().to_string()) | 413 | } |
412 | } | 414 | builder.finish() |
413 | builder.finish().apply(&template.template.text().to_string()) | 415 | }; |
416 | |||
417 | let mut text = template.template.text().to_string(); | ||
418 | edit.apply(&mut text); | ||
419 | text | ||
414 | } | 420 | } |
415 | 421 | ||
416 | #[cfg(test)] | 422 | #[cfg(test)] |
@@ -505,7 +511,9 @@ mod tests { | |||
505 | ); | 511 | ); |
506 | 512 | ||
507 | let edit = replace(&matches, &query.template); | 513 | let edit = replace(&matches, &query.template); |
508 | assert_eq!(edit.apply(input), "fn main() { bar(1+2); }"); | 514 | let mut after = input.to_string(); |
515 | edit.apply(&mut after); | ||
516 | assert_eq!(after, "fn main() { bar(1+2); }"); | ||
509 | } | 517 | } |
510 | 518 | ||
511 | fn assert_ssr_transform(query: &str, input: &str, result: &str) { | 519 | fn assert_ssr_transform(query: &str, input: &str, result: &str) { |
@@ -513,7 +521,9 @@ mod tests { | |||
513 | let code = SourceFile::parse(input).tree(); | 521 | let code = SourceFile::parse(input).tree(); |
514 | let matches = find(&query.pattern, code.syntax()); | 522 | let matches = find(&query.pattern, code.syntax()); |
515 | let edit = replace(&matches, &query.template); | 523 | let edit = replace(&matches, &query.template); |
516 | assert_eq!(edit.apply(input), result); | 524 | let mut after = input.to_string(); |
525 | edit.apply(&mut after); | ||
526 | assert_eq!(after, result); | ||
517 | } | 527 | } |
518 | 528 | ||
519 | #[test] | 529 | #[test] |
diff --git a/crates/ra_ide/src/test_utils.rs b/crates/ra_ide/src/test_utils.rs index f14533e14..48c8fd1f4 100644 --- a/crates/ra_ide/src/test_utils.rs +++ b/crates/ra_ide/src/test_utils.rs | |||
@@ -13,7 +13,11 @@ pub fn check_action<F: Fn(&SourceFile, TextSize) -> Option<TextEdit>>( | |||
13 | let (before_cursor_pos, before) = extract_offset(before); | 13 | let (before_cursor_pos, before) = extract_offset(before); |
14 | let file = SourceFile::parse(&before).ok().unwrap(); | 14 | let file = SourceFile::parse(&before).ok().unwrap(); |
15 | let result = f(&file, before_cursor_pos).expect("code action is not applicable"); | 15 | let result = f(&file, before_cursor_pos).expect("code action is not applicable"); |
16 | let actual = result.apply(&before); | 16 | let actual = { |
17 | let mut actual = before.to_string(); | ||
18 | result.apply(&mut actual); | ||
19 | actual | ||
20 | }; | ||
17 | let actual_cursor_pos = | 21 | let actual_cursor_pos = |
18 | result.apply_to_offset(before_cursor_pos).expect("cursor position is affected by the edit"); | 22 | result.apply_to_offset(before_cursor_pos).expect("cursor position is affected by the edit"); |
19 | let actual = add_cursor(&actual, actual_cursor_pos); | 23 | let actual = add_cursor(&actual, actual_cursor_pos); |
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs index 2a8b4327f..a03da4693 100644 --- a/crates/ra_ide/src/typing.rs +++ b/crates/ra_ide/src/typing.rs | |||
@@ -142,10 +142,13 @@ mod tests { | |||
142 | fn do_type_char(char_typed: char, before: &str) -> Option<(String, SingleFileChange)> { | 142 | fn do_type_char(char_typed: char, before: &str) -> Option<(String, SingleFileChange)> { |
143 | let (offset, before) = extract_offset(before); | 143 | let (offset, before) = extract_offset(before); |
144 | let edit = TextEdit::insert(offset, char_typed.to_string()); | 144 | let edit = TextEdit::insert(offset, char_typed.to_string()); |
145 | let before = edit.apply(&before); | 145 | let mut before = before.to_string(); |
146 | edit.apply(&mut before); | ||
146 | let parse = SourceFile::parse(&before); | 147 | let parse = SourceFile::parse(&before); |
147 | on_char_typed_inner(&parse.tree(), offset, char_typed) | 148 | on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { |
148 | .map(|it| (it.edit.apply(&before), it)) | 149 | it.edit.apply(&mut before); |
150 | (before.to_string(), it) | ||
151 | }) | ||
149 | } | 152 | } |
150 | 153 | ||
151 | fn type_char(char_typed: char, before: &str, after: &str) { | 154 | fn type_char(char_typed: char, before: &str, after: &str) { |
diff --git a/crates/ra_ide/src/typing/on_enter.rs b/crates/ra_ide/src/typing/on_enter.rs index 30c8c5572..78a40cc94 100644 --- a/crates/ra_ide/src/typing/on_enter.rs +++ b/crates/ra_ide/src/typing/on_enter.rs | |||
@@ -44,7 +44,7 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Sour | |||
44 | 44 | ||
45 | Some( | 45 | Some( |
46 | SourceChange::source_file_edit( | 46 | SourceChange::source_file_edit( |
47 | "on enter", | 47 | "On enter", |
48 | SourceFileEdit { edit, file_id: position.file_id }, | 48 | SourceFileEdit { edit, file_id: position.file_id }, |
49 | ) | 49 | ) |
50 | .with_cursor(FilePosition { offset: cursor_position, file_id: position.file_id }), | 50 | .with_cursor(FilePosition { offset: cursor_position, file_id: position.file_id }), |
@@ -96,7 +96,8 @@ mod tests { | |||
96 | let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?; | 96 | let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?; |
97 | 97 | ||
98 | assert_eq!(result.source_file_edits.len(), 1); | 98 | assert_eq!(result.source_file_edits.len(), 1); |
99 | let actual = result.source_file_edits[0].edit.apply(&before); | 99 | let mut actual = before.to_string(); |
100 | result.source_file_edits[0].edit.apply(&mut actual); | ||
100 | let actual = add_cursor(&actual, result.cursor_position.unwrap().offset); | 101 | let actual = add_cursor(&actual, result.cursor_position.unwrap().offset); |
101 | Some(actual) | 102 | Some(actual) |
102 | } | 103 | } |