diff options
Diffstat (limited to 'crates/ra_analysis')
-rw-r--r-- | crates/ra_analysis/src/completion.rs | 8 | ||||
-rw-r--r-- | crates/ra_analysis/src/completion/complete_dot.rs | 98 | ||||
-rw-r--r-- | crates/ra_analysis/src/completion/complete_path.rs | 2 | ||||
-rw-r--r-- | crates/ra_analysis/src/completion/completion_context.rs | 54 | ||||
-rw-r--r-- | crates/ra_analysis/src/completion/completion_item.rs | 36 | ||||
-rw-r--r-- | crates/ra_analysis/src/db.rs | 3 | ||||
-rw-r--r-- | crates/ra_analysis/src/imp.rs | 61 | ||||
-rw-r--r-- | crates/ra_analysis/src/lib.rs | 5 |
8 files changed, 205 insertions, 62 deletions
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs index d742d6295..fe580700f 100644 --- a/crates/ra_analysis/src/completion.rs +++ b/crates/ra_analysis/src/completion.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | mod completion_item; | 1 | mod completion_item; |
2 | mod completion_context; | 2 | mod completion_context; |
3 | 3 | ||
4 | mod complete_dot; | ||
4 | mod complete_fn_param; | 5 | mod complete_fn_param; |
5 | mod complete_keyword; | 6 | mod complete_keyword; |
6 | mod complete_snippet; | 7 | mod complete_snippet; |
@@ -20,13 +21,13 @@ use crate::{ | |||
20 | 21 | ||
21 | pub use crate::completion::completion_item::{CompletionItem, InsertText, CompletionItemKind}; | 22 | pub use crate::completion::completion_item::{CompletionItem, InsertText, CompletionItemKind}; |
22 | 23 | ||
23 | /// Main entry point for copmletion. We run comletion as a two-phase process. | 24 | /// Main entry point for completion. We run completion as a two-phase process. |
24 | /// | 25 | /// |
25 | /// First, we look at the position and collect a so-called `CompletionContext. | 26 | /// First, we look at the position and collect a so-called `CompletionContext. |
26 | /// This is a somewhat messy process, because, during completion, syntax tree is | 27 | /// This is a somewhat messy process, because, during completion, syntax tree is |
27 | /// incomplete and can look readlly weired. | 28 | /// incomplete and can look really weird. |
28 | /// | 29 | /// |
29 | /// Once the context is collected, we run a series of completion routines whihc | 30 | /// Once the context is collected, we run a series of completion routines which |
30 | /// look at the context and produce completion items. | 31 | /// look at the context and produce completion items. |
31 | pub(crate) fn completions( | 32 | pub(crate) fn completions( |
32 | db: &db::RootDatabase, | 33 | db: &db::RootDatabase, |
@@ -43,6 +44,7 @@ pub(crate) fn completions( | |||
43 | complete_snippet::complete_item_snippet(&mut acc, &ctx); | 44 | complete_snippet::complete_item_snippet(&mut acc, &ctx); |
44 | complete_path::complete_path(&mut acc, &ctx)?; | 45 | complete_path::complete_path(&mut acc, &ctx)?; |
45 | complete_scope::complete_scope(&mut acc, &ctx)?; | 46 | complete_scope::complete_scope(&mut acc, &ctx)?; |
47 | complete_dot::complete_dot(&mut acc, &ctx)?; | ||
46 | 48 | ||
47 | Ok(Some(acc)) | 49 | Ok(Some(acc)) |
48 | } | 50 | } |
diff --git a/crates/ra_analysis/src/completion/complete_dot.rs b/crates/ra_analysis/src/completion/complete_dot.rs new file mode 100644 index 000000000..93d657576 --- /dev/null +++ b/crates/ra_analysis/src/completion/complete_dot.rs | |||
@@ -0,0 +1,98 @@ | |||
1 | use ra_syntax::ast::AstNode; | ||
2 | use hir::{Ty, Def}; | ||
3 | |||
4 | use crate::Cancelable; | ||
5 | use crate::completion::{CompletionContext, Completions, CompletionKind, CompletionItem, CompletionItemKind}; | ||
6 | |||
7 | /// Complete dot accesses, i.e. fields or methods (currently only fields). | ||
8 | pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> { | ||
9 | let module = if let Some(module) = &ctx.module { | ||
10 | module | ||
11 | } else { | ||
12 | return Ok(()); | ||
13 | }; | ||
14 | let function = if let Some(fn_def) = ctx.enclosing_fn { | ||
15 | hir::source_binder::function_from_module(ctx.db, module, fn_def) | ||
16 | } else { | ||
17 | return Ok(()); | ||
18 | }; | ||
19 | let receiver = if let Some(receiver) = ctx.dot_receiver { | ||
20 | receiver | ||
21 | } else { | ||
22 | return Ok(()); | ||
23 | }; | ||
24 | let infer_result = function.infer(ctx.db)?; | ||
25 | let receiver_ty = if let Some(ty) = infer_result.type_of_node(receiver.syntax()) { | ||
26 | ty | ||
27 | } else { | ||
28 | return Ok(()); | ||
29 | }; | ||
30 | if !ctx.is_method_call { | ||
31 | complete_fields(acc, ctx, receiver_ty)?; | ||
32 | } | ||
33 | Ok(()) | ||
34 | } | ||
35 | |||
36 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) -> Cancelable<()> { | ||
37 | // TODO: autoderef etc. | ||
38 | match receiver { | ||
39 | Ty::Adt { def_id, .. } => { | ||
40 | match def_id.resolve(ctx.db)? { | ||
41 | Def::Struct(s) => { | ||
42 | let variant_data = s.variant_data(ctx.db)?; | ||
43 | for field in variant_data.fields() { | ||
44 | CompletionItem::new(CompletionKind::Reference, field.name().to_string()) | ||
45 | .kind(CompletionItemKind::Field) | ||
46 | .add_to(acc); | ||
47 | } | ||
48 | } | ||
49 | // TODO unions | ||
50 | _ => {} | ||
51 | } | ||
52 | } | ||
53 | Ty::Tuple(fields) => { | ||
54 | for (i, _ty) in fields.iter().enumerate() { | ||
55 | CompletionItem::new(CompletionKind::Reference, i.to_string()) | ||
56 | .kind(CompletionItemKind::Field) | ||
57 | .add_to(acc); | ||
58 | } | ||
59 | } | ||
60 | _ => {} | ||
61 | }; | ||
62 | Ok(()) | ||
63 | } | ||
64 | |||
65 | #[cfg(test)] | ||
66 | mod tests { | ||
67 | use crate::completion::*; | ||
68 | |||
69 | fn check_ref_completion(code: &str, expected_completions: &str) { | ||
70 | check_completion(code, expected_completions, CompletionKind::Reference); | ||
71 | } | ||
72 | |||
73 | #[test] | ||
74 | fn test_struct_field_completion() { | ||
75 | check_ref_completion( | ||
76 | r" | ||
77 | struct A { the_field: u32 } | ||
78 | fn foo(a: A) { | ||
79 | a.<|> | ||
80 | } | ||
81 | ", | ||
82 | r#"the_field"#, | ||
83 | ); | ||
84 | } | ||
85 | |||
86 | #[test] | ||
87 | fn test_no_struct_field_completion_for_method_call() { | ||
88 | check_ref_completion( | ||
89 | r" | ||
90 | struct A { the_field: u32 } | ||
91 | fn foo(a: A) { | ||
92 | a.<|>() | ||
93 | } | ||
94 | ", | ||
95 | r#""#, | ||
96 | ); | ||
97 | } | ||
98 | } | ||
diff --git a/crates/ra_analysis/src/completion/complete_path.rs b/crates/ra_analysis/src/completion/complete_path.rs index ad4d68a33..aaa2c7cee 100644 --- a/crates/ra_analysis/src/completion/complete_path.rs +++ b/crates/ra_analysis/src/completion/complete_path.rs | |||
@@ -8,7 +8,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> C | |||
8 | (Some(path), Some(module)) => (path.clone(), module), | 8 | (Some(path), Some(module)) => (path.clone(), module), |
9 | _ => return Ok(()), | 9 | _ => return Ok(()), |
10 | }; | 10 | }; |
11 | let def_id = match module.resolve_path(ctx.db, path)? { | 11 | let def_id = match module.resolve_path(ctx.db, &path)?.take_types() { |
12 | Some(it) => it, | 12 | Some(it) => it, |
13 | None => return Ok(()), | 13 | None => return Ok(()), |
14 | }; | 14 | }; |
diff --git a/crates/ra_analysis/src/completion/completion_context.rs b/crates/ra_analysis/src/completion/completion_context.rs index 064fbc6f7..978772fd4 100644 --- a/crates/ra_analysis/src/completion/completion_context.rs +++ b/crates/ra_analysis/src/completion/completion_context.rs | |||
@@ -1,12 +1,13 @@ | |||
1 | use ra_editor::find_node_at_offset; | 1 | use ra_editor::find_node_at_offset; |
2 | use ra_text_edit::AtomTextEdit; | 2 | use ra_text_edit::AtomTextEdit; |
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | algo::find_leaf_at_offset, | 4 | algo::{find_leaf_at_offset, find_covering_node}, |
5 | ast, | 5 | ast, |
6 | AstNode, | 6 | AstNode, |
7 | SyntaxNodeRef, | 7 | SyntaxNodeRef, |
8 | SourceFileNode, | 8 | SourceFileNode, |
9 | TextUnit, | 9 | TextUnit, |
10 | TextRange, | ||
10 | SyntaxKind::*, | 11 | SyntaxKind::*, |
11 | }; | 12 | }; |
12 | use hir::source_binder; | 13 | use hir::source_binder; |
@@ -31,6 +32,10 @@ pub(super) struct CompletionContext<'a> { | |||
31 | pub(super) is_stmt: bool, | 32 | pub(super) is_stmt: bool, |
32 | /// Something is typed at the "top" level, in module or impl/trait. | 33 | /// Something is typed at the "top" level, in module or impl/trait. |
33 | pub(super) is_new_item: bool, | 34 | pub(super) is_new_item: bool, |
35 | /// The receiver if this is a field or method access, i.e. writing something.<|> | ||
36 | pub(super) dot_receiver: Option<ast::Expr<'a>>, | ||
37 | /// If this is a method call in particular, i.e. the () are already there. | ||
38 | pub(super) is_method_call: bool, | ||
34 | } | 39 | } |
35 | 40 | ||
36 | impl<'a> CompletionContext<'a> { | 41 | impl<'a> CompletionContext<'a> { |
@@ -54,12 +59,14 @@ impl<'a> CompletionContext<'a> { | |||
54 | after_if: false, | 59 | after_if: false, |
55 | is_stmt: false, | 60 | is_stmt: false, |
56 | is_new_item: false, | 61 | is_new_item: false, |
62 | dot_receiver: None, | ||
63 | is_method_call: false, | ||
57 | }; | 64 | }; |
58 | ctx.fill(original_file, position.offset); | 65 | ctx.fill(original_file, position.offset); |
59 | Ok(Some(ctx)) | 66 | Ok(Some(ctx)) |
60 | } | 67 | } |
61 | 68 | ||
62 | fn fill(&mut self, original_file: &SourceFileNode, offset: TextUnit) { | 69 | fn fill(&mut self, original_file: &'a SourceFileNode, offset: TextUnit) { |
63 | // Insert a fake ident to get a valid parse tree. We will use this file | 70 | // Insert a fake ident to get a valid parse tree. We will use this file |
64 | // to determine context, though the original_file will be used for | 71 | // to determine context, though the original_file will be used for |
65 | // actual completion. | 72 | // actual completion. |
@@ -76,7 +83,7 @@ impl<'a> CompletionContext<'a> { | |||
76 | self.is_param = true; | 83 | self.is_param = true; |
77 | return; | 84 | return; |
78 | } | 85 | } |
79 | self.classify_name_ref(&file, name_ref); | 86 | self.classify_name_ref(original_file, name_ref); |
80 | } | 87 | } |
81 | 88 | ||
82 | // Otherwise, see if this is a declaration. We can use heuristics to | 89 | // Otherwise, see if this is a declaration. We can use heuristics to |
@@ -88,7 +95,7 @@ impl<'a> CompletionContext<'a> { | |||
88 | } | 95 | } |
89 | } | 96 | } |
90 | } | 97 | } |
91 | fn classify_name_ref(&mut self, file: &SourceFileNode, name_ref: ast::NameRef) { | 98 | fn classify_name_ref(&mut self, original_file: &'a SourceFileNode, name_ref: ast::NameRef) { |
92 | let name_range = name_ref.syntax().range(); | 99 | let name_range = name_ref.syntax().range(); |
93 | let top_node = name_ref | 100 | let top_node = name_ref |
94 | .syntax() | 101 | .syntax() |
@@ -105,6 +112,12 @@ impl<'a> CompletionContext<'a> { | |||
105 | _ => (), | 112 | _ => (), |
106 | } | 113 | } |
107 | 114 | ||
115 | self.enclosing_fn = self | ||
116 | .leaf | ||
117 | .ancestors() | ||
118 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | ||
119 | .find_map(ast::FnDef::cast); | ||
120 | |||
108 | let parent = match name_ref.syntax().parent() { | 121 | let parent = match name_ref.syntax().parent() { |
109 | Some(it) => it, | 122 | Some(it) => it, |
110 | None => return, | 123 | None => return, |
@@ -120,11 +133,6 @@ impl<'a> CompletionContext<'a> { | |||
120 | } | 133 | } |
121 | if path.qualifier().is_none() { | 134 | if path.qualifier().is_none() { |
122 | self.is_trivial_path = true; | 135 | self.is_trivial_path = true; |
123 | self.enclosing_fn = self | ||
124 | .leaf | ||
125 | .ancestors() | ||
126 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | ||
127 | .find_map(ast::FnDef::cast); | ||
128 | 136 | ||
129 | self.is_stmt = match name_ref | 137 | self.is_stmt = match name_ref |
130 | .syntax() | 138 | .syntax() |
@@ -137,7 +145,9 @@ impl<'a> CompletionContext<'a> { | |||
137 | }; | 145 | }; |
138 | 146 | ||
139 | if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) { | 147 | if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) { |
140 | if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) { | 148 | if let Some(if_expr) = |
149 | find_node_at_offset::<ast::IfExpr>(original_file.syntax(), off) | ||
150 | { | ||
141 | if if_expr.syntax().range().end() < name_ref.syntax().range().start() { | 151 | if if_expr.syntax().range().end() < name_ref.syntax().range().start() { |
142 | self.after_if = true; | 152 | self.after_if = true; |
143 | } | 153 | } |
@@ -145,9 +155,33 @@ impl<'a> CompletionContext<'a> { | |||
145 | } | 155 | } |
146 | } | 156 | } |
147 | } | 157 | } |
158 | if let Some(field_expr) = ast::FieldExpr::cast(parent) { | ||
159 | // The receiver comes before the point of insertion of the fake | ||
160 | // ident, so it should have the same range in the non-modified file | ||
161 | self.dot_receiver = field_expr | ||
162 | .expr() | ||
163 | .map(|e| e.syntax().range()) | ||
164 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | ||
165 | } | ||
166 | if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { | ||
167 | // As above | ||
168 | self.dot_receiver = method_call_expr | ||
169 | .expr() | ||
170 | .map(|e| e.syntax().range()) | ||
171 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | ||
172 | self.is_method_call = true; | ||
173 | } | ||
148 | } | 174 | } |
149 | } | 175 | } |
150 | 176 | ||
177 | fn find_node_with_range<'a, N: AstNode<'a>>( | ||
178 | syntax: SyntaxNodeRef<'a>, | ||
179 | range: TextRange, | ||
180 | ) -> Option<N> { | ||
181 | let node = find_covering_node(syntax, range); | ||
182 | node.ancestors().find_map(N::cast) | ||
183 | } | ||
184 | |||
151 | fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool { | 185 | fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool { |
152 | match node.ancestors().filter_map(N::cast).next() { | 186 | match node.ancestors().filter_map(N::cast).next() { |
153 | None => false, | 187 | None => false, |
diff --git a/crates/ra_analysis/src/completion/completion_item.rs b/crates/ra_analysis/src/completion/completion_item.rs index 911f08468..c9f9f495d 100644 --- a/crates/ra_analysis/src/completion/completion_item.rs +++ b/crates/ra_analysis/src/completion/completion_item.rs | |||
@@ -1,5 +1,7 @@ | |||
1 | use crate::db; | 1 | use crate::db; |
2 | 2 | ||
3 | use hir::PerNs; | ||
4 | |||
3 | /// `CompletionItem` describes a single completion variant in the editor pop-up. | 5 | /// `CompletionItem` describes a single completion variant in the editor pop-up. |
4 | /// It is basically a POD with various properties. To construct a | 6 | /// It is basically a POD with various properties. To construct a |
5 | /// `CompletionItem`, use `new` method and the `Builder` struct. | 7 | /// `CompletionItem`, use `new` method and the `Builder` struct. |
@@ -25,7 +27,10 @@ pub enum CompletionItemKind { | |||
25 | Keyword, | 27 | Keyword, |
26 | Module, | 28 | Module, |
27 | Function, | 29 | Function, |
30 | Struct, | ||
31 | Enum, | ||
28 | Binding, | 32 | Binding, |
33 | Field, | ||
29 | } | 34 | } |
30 | 35 | ||
31 | #[derive(Debug, PartialEq, Eq)] | 36 | #[derive(Debug, PartialEq, Eq)] |
@@ -117,16 +122,27 @@ impl Builder { | |||
117 | db: &db::RootDatabase, | 122 | db: &db::RootDatabase, |
118 | resolution: &hir::Resolution, | 123 | resolution: &hir::Resolution, |
119 | ) -> Builder { | 124 | ) -> Builder { |
120 | if let Some(def_id) = resolution.def_id { | 125 | let resolved = resolution.def_id.and_then(|d| d.resolve(db).ok()); |
121 | if let Ok(def) = def_id.resolve(db) { | 126 | let kind = match resolved { |
122 | let kind = match def { | 127 | PerNs { |
123 | hir::Def::Module(..) => CompletionItemKind::Module, | 128 | types: Some(hir::Def::Module(..)), |
124 | hir::Def::Function(..) => CompletionItemKind::Function, | 129 | .. |
125 | _ => return self, | 130 | } => CompletionItemKind::Module, |
126 | }; | 131 | PerNs { |
127 | self.kind = Some(kind); | 132 | types: Some(hir::Def::Struct(..)), |
128 | } | 133 | .. |
129 | } | 134 | } => CompletionItemKind::Struct, |
135 | PerNs { | ||
136 | types: Some(hir::Def::Enum(..)), | ||
137 | .. | ||
138 | } => CompletionItemKind::Enum, | ||
139 | PerNs { | ||
140 | values: Some(hir::Def::Function(..)), | ||
141 | .. | ||
142 | } => CompletionItemKind::Function, | ||
143 | _ => return self, | ||
144 | }; | ||
145 | self.kind = Some(kind); | ||
130 | self | 146 | self |
131 | } | 147 | } |
132 | } | 148 | } |
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs index 780a84291..036e284bf 100644 --- a/crates/ra_analysis/src/db.rs +++ b/crates/ra_analysis/src/db.rs | |||
@@ -95,6 +95,9 @@ salsa::database_storage! { | |||
95 | fn submodules() for hir::db::SubmodulesQuery; | 95 | fn submodules() for hir::db::SubmodulesQuery; |
96 | fn infer() for hir::db::InferQuery; | 96 | fn infer() for hir::db::InferQuery; |
97 | fn type_for_def() for hir::db::TypeForDefQuery; | 97 | fn type_for_def() for hir::db::TypeForDefQuery; |
98 | fn type_for_field() for hir::db::TypeForFieldQuery; | ||
99 | fn struct_data() for hir::db::StructDataQuery; | ||
100 | fn enum_data() for hir::db::EnumDataQuery; | ||
98 | } | 101 | } |
99 | } | 102 | } |
100 | } | 103 | } |
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index a547c5a20..38a5c1a7d 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -3,31 +3,32 @@ use std::{ | |||
3 | sync::Arc, | 3 | sync::Arc, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use ra_editor::{self, find_node_at_offset, FileSymbol, LineIndex, LocalEdit, Severity}; | ||
7 | use ra_syntax::{ | ||
8 | ast::{self, ArgListOwner, Expr, NameOwner, FnDef}, | ||
9 | algo::find_covering_node, | ||
10 | AstNode, SourceFileNode, | ||
11 | SyntaxKind::*, | ||
12 | SyntaxNodeRef, TextRange, TextUnit, | ||
13 | }; | ||
14 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; | ||
15 | use rayon::prelude::*; | 6 | use rayon::prelude::*; |
16 | use salsa::{Database, ParallelDatabase}; | 7 | use salsa::{Database, ParallelDatabase}; |
8 | |||
17 | use hir::{ | 9 | use hir::{ |
18 | self, | 10 | self, |
19 | source_binder, | ||
20 | FnSignatureInfo, | 11 | FnSignatureInfo, |
21 | Problem, | 12 | Problem, |
13 | source_binder, | ||
14 | }; | ||
15 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; | ||
16 | use ra_editor::{self, FileSymbol, find_node_at_offset, LineIndex, LocalEdit, Severity}; | ||
17 | use ra_syntax::{ | ||
18 | algo::find_covering_node, | ||
19 | ast::{self, ArgListOwner, Expr, FnDef, NameOwner}, | ||
20 | AstNode, SourceFileNode, | ||
21 | SyntaxKind::*, | ||
22 | SyntaxNodeRef, TextRange, TextUnit, | ||
22 | }; | 23 | }; |
23 | 24 | ||
24 | use crate::{ | 25 | use crate::{ |
25 | completion::{completions, CompletionItem}, | 26 | AnalysisChange, |
26 | db, | 27 | Cancelable, |
27 | symbol_index::{SymbolIndex, SymbolsDatabase, LibrarySymbolsQuery}, | 28 | completion::{CompletionItem, completions}, |
28 | AnalysisChange, RootChange, Cancelable, CrateId, Diagnostic, FileId, | 29 | CrateId, db, Diagnostic, FileId, FilePosition, FileSystemEdit, |
29 | FileSystemEdit, FilePosition, Query, SourceChange, SourceFileEdit, | 30 | Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit, |
30 | ReferenceResolution, | 31 | symbol_index::{LibrarySymbolsQuery, SymbolIndex, SymbolsDatabase}, |
31 | }; | 32 | }; |
32 | 33 | ||
33 | #[derive(Debug, Default)] | 34 | #[derive(Debug, Default)] |
@@ -366,7 +367,7 @@ impl AnalysisImpl { | |||
366 | range: d.range, | 367 | range: d.range, |
367 | message: d.msg, | 368 | message: d.msg, |
368 | severity: d.severity, | 369 | severity: d.severity, |
369 | fix: None, | 370 | fix: d.fix.map(|fix| SourceChange::from_local_edit(file_id, fix)), |
370 | }) | 371 | }) |
371 | .collect::<Vec<_>>(); | 372 | .collect::<Vec<_>>(); |
372 | if let Some(m) = source_binder::module_from_file_id(&*self.db, file_id)? { | 373 | if let Some(m) = source_binder::module_from_file_id(&*self.db, file_id)? { |
@@ -425,25 +426,15 @@ impl AnalysisImpl { | |||
425 | let file = self.file_syntax(file_id); | 426 | let file = self.file_syntax(file_id); |
426 | let offset = range.start(); | 427 | let offset = range.start(); |
427 | let actions = vec![ | 428 | let actions = vec![ |
428 | ( | 429 | ra_editor::flip_comma(&file, offset).map(|f| f()), |
429 | "flip comma", | 430 | ra_editor::add_derive(&file, offset).map(|f| f()), |
430 | ra_editor::flip_comma(&file, offset).map(|f| f()), | 431 | ra_editor::add_impl(&file, offset).map(|f| f()), |
431 | ), | 432 | ra_editor::make_pub_crate(&file, offset).map(|f| f()), |
432 | ( | 433 | ra_editor::introduce_variable(&file, range).map(|f| f()), |
433 | "add `#[derive]`", | ||
434 | ra_editor::add_derive(&file, offset).map(|f| f()), | ||
435 | ), | ||
436 | ("add impl", ra_editor::add_impl(&file, offset).map(|f| f())), | ||
437 | ( | ||
438 | "introduce variable", | ||
439 | ra_editor::introduce_variable(&file, range).map(|f| f()), | ||
440 | ), | ||
441 | ]; | 434 | ]; |
442 | actions | 435 | actions |
443 | .into_iter() | 436 | .into_iter() |
444 | .filter_map(|(name, local_edit)| { | 437 | .filter_map(|local_edit| Some(SourceChange::from_local_edit(file_id, local_edit?))) |
445 | Some(SourceChange::from_local_edit(file_id, name, local_edit?)) | ||
446 | }) | ||
447 | .collect() | 438 | .collect() |
448 | } | 439 | } |
449 | 440 | ||
@@ -541,13 +532,13 @@ impl AnalysisImpl { | |||
541 | } | 532 | } |
542 | 533 | ||
543 | impl SourceChange { | 534 | impl SourceChange { |
544 | pub(crate) fn from_local_edit(file_id: FileId, label: &str, edit: LocalEdit) -> SourceChange { | 535 | pub(crate) fn from_local_edit(file_id: FileId, edit: LocalEdit) -> SourceChange { |
545 | let file_edit = SourceFileEdit { | 536 | let file_edit = SourceFileEdit { |
546 | file_id, | 537 | file_id, |
547 | edit: edit.edit, | 538 | edit: edit.edit, |
548 | }; | 539 | }; |
549 | SourceChange { | 540 | SourceChange { |
550 | label: label.to_string(), | 541 | label: edit.label, |
551 | source_file_edits: vec![file_edit], | 542 | source_file_edits: vec![file_edit], |
552 | file_system_edits: vec![], | 543 | file_system_edits: vec![], |
553 | cursor_position: edit | 544 | cursor_position: edit |
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index a029f66b4..476d1b438 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs | |||
@@ -288,19 +288,18 @@ impl Analysis { | |||
288 | } | 288 | } |
289 | pub fn join_lines(&self, file_id: FileId, range: TextRange) -> SourceChange { | 289 | pub fn join_lines(&self, file_id: FileId, range: TextRange) -> SourceChange { |
290 | let file = self.imp.file_syntax(file_id); | 290 | let file = self.imp.file_syntax(file_id); |
291 | SourceChange::from_local_edit(file_id, "join lines", ra_editor::join_lines(&file, range)) | 291 | SourceChange::from_local_edit(file_id, ra_editor::join_lines(&file, range)) |
292 | } | 292 | } |
293 | pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> { | 293 | pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> { |
294 | let file = self.imp.file_syntax(position.file_id); | 294 | let file = self.imp.file_syntax(position.file_id); |
295 | let edit = ra_editor::on_enter(&file, position.offset)?; | 295 | let edit = ra_editor::on_enter(&file, position.offset)?; |
296 | let res = SourceChange::from_local_edit(position.file_id, "on enter", edit); | 296 | let res = SourceChange::from_local_edit(position.file_id, edit); |
297 | Some(res) | 297 | Some(res) |
298 | } | 298 | } |
299 | pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> { | 299 | pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> { |
300 | let file = self.imp.file_syntax(position.file_id); | 300 | let file = self.imp.file_syntax(position.file_id); |
301 | Some(SourceChange::from_local_edit( | 301 | Some(SourceChange::from_local_edit( |
302 | position.file_id, | 302 | position.file_id, |
303 | "add semicolon", | ||
304 | ra_editor::on_eq_typed(&file, position.offset)?, | 303 | ra_editor::on_eq_typed(&file, position.offset)?, |
305 | )) | 304 | )) |
306 | } | 305 | } |