diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_analysis/src/completion.rs | 43 | ||||
-rw-r--r-- | crates/ra_analysis/src/completion/complete_path.rs | 95 | ||||
-rw-r--r-- | crates/ra_analysis/src/completion/reference_completion.rs | 33 |
3 files changed, 127 insertions, 44 deletions
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs index 883b3e851..d91304bc2 100644 --- a/crates/ra_analysis/src/completion.rs +++ b/crates/ra_analysis/src/completion.rs | |||
@@ -4,13 +4,12 @@ mod reference_completion; | |||
4 | mod complete_fn_param; | 4 | mod complete_fn_param; |
5 | mod complete_keyword; | 5 | mod complete_keyword; |
6 | mod complete_snippet; | 6 | mod complete_snippet; |
7 | mod complete_path; | ||
7 | 8 | ||
8 | use ra_editor::find_node_at_offset; | 9 | use ra_editor::find_node_at_offset; |
9 | use ra_text_edit::AtomTextEdit; | 10 | use ra_text_edit::AtomTextEdit; |
10 | use ra_syntax::{ | 11 | use ra_syntax::{ |
11 | algo::{ | 12 | algo::find_leaf_at_offset, |
12 | find_leaf_at_offset, | ||
13 | }, | ||
14 | ast, | 13 | ast, |
15 | AstNode, | 14 | AstNode, |
16 | SyntaxNodeRef, | 15 | SyntaxNodeRef, |
@@ -48,11 +47,12 @@ pub(crate) fn completions( | |||
48 | reference_completion::completions(&mut acc, db, &module, &file, name_ref)?; | 47 | reference_completion::completions(&mut acc, db, &module, &file, name_ref)?; |
49 | } | 48 | } |
50 | 49 | ||
51 | let ctx = ctry!(SyntaxContext::new(&original_file, position.offset)); | 50 | let ctx = ctry!(SyntaxContext::new(db, &original_file, position)?); |
52 | complete_fn_param::complete_fn_param(&mut acc, &ctx); | 51 | complete_fn_param::complete_fn_param(&mut acc, &ctx); |
53 | complete_keyword::complete_expr_keyword(&mut acc, &ctx); | 52 | complete_keyword::complete_expr_keyword(&mut acc, &ctx); |
54 | complete_snippet::complete_expr_snippet(&mut acc, &ctx); | 53 | complete_snippet::complete_expr_snippet(&mut acc, &ctx); |
55 | complete_snippet::complete_item_snippet(&mut acc, &ctx); | 54 | complete_snippet::complete_item_snippet(&mut acc, &ctx); |
55 | complete_path::complete_path(&mut acc, &ctx)?; | ||
56 | 56 | ||
57 | Ok(Some(acc)) | 57 | Ok(Some(acc)) |
58 | } | 58 | } |
@@ -61,31 +61,44 @@ pub(crate) fn completions( | |||
61 | /// exactly is the cursor, syntax-wise. | 61 | /// exactly is the cursor, syntax-wise. |
62 | #[derive(Debug)] | 62 | #[derive(Debug)] |
63 | pub(super) struct SyntaxContext<'a> { | 63 | pub(super) struct SyntaxContext<'a> { |
64 | db: &'a db::RootDatabase, | ||
64 | leaf: SyntaxNodeRef<'a>, | 65 | leaf: SyntaxNodeRef<'a>, |
66 | module: Option<hir::Module>, | ||
65 | enclosing_fn: Option<ast::FnDef<'a>>, | 67 | enclosing_fn: Option<ast::FnDef<'a>>, |
66 | is_param: bool, | 68 | is_param: bool, |
67 | /// A single-indent path, like `foo`. | 69 | /// A single-indent path, like `foo`. |
68 | is_trivial_path: bool, | 70 | is_trivial_path: bool, |
71 | /// If not a trivial, path, the prefix (qualifier). | ||
72 | path_prefix: Option<hir::Path>, | ||
69 | after_if: bool, | 73 | after_if: bool, |
70 | is_stmt: bool, | 74 | is_stmt: bool, |
71 | /// Something is typed at the "top" level, in module or impl/trait. | 75 | /// Something is typed at the "top" level, in module or impl/trait. |
72 | is_new_item: bool, | 76 | is_new_item: bool, |
73 | } | 77 | } |
74 | 78 | ||
75 | impl SyntaxContext<'_> { | 79 | impl<'a> SyntaxContext<'a> { |
76 | pub(super) fn new(original_file: &SourceFileNode, offset: TextUnit) -> Option<SyntaxContext> { | 80 | pub(super) fn new( |
77 | let leaf = find_leaf_at_offset(original_file.syntax(), offset).left_biased()?; | 81 | db: &'a db::RootDatabase, |
82 | original_file: &'a SourceFileNode, | ||
83 | position: FilePosition, | ||
84 | ) -> Cancelable<Option<SyntaxContext<'a>>> { | ||
85 | let module = source_binder::module_from_position(db, position)?; | ||
86 | let leaf = | ||
87 | ctry!(find_leaf_at_offset(original_file.syntax(), position.offset).left_biased()); | ||
78 | let mut ctx = SyntaxContext { | 88 | let mut ctx = SyntaxContext { |
89 | db, | ||
79 | leaf, | 90 | leaf, |
91 | module, | ||
80 | enclosing_fn: None, | 92 | enclosing_fn: None, |
81 | is_param: false, | 93 | is_param: false, |
82 | is_trivial_path: false, | 94 | is_trivial_path: false, |
95 | path_prefix: None, | ||
83 | after_if: false, | 96 | after_if: false, |
84 | is_stmt: false, | 97 | is_stmt: false, |
85 | is_new_item: false, | 98 | is_new_item: false, |
86 | }; | 99 | }; |
87 | ctx.fill(original_file, offset); | 100 | ctx.fill(original_file, position.offset); |
88 | Some(ctx) | 101 | Ok(Some(ctx)) |
89 | } | 102 | } |
90 | 103 | ||
91 | fn fill(&mut self, original_file: &SourceFileNode, offset: TextUnit) { | 104 | fn fill(&mut self, original_file: &SourceFileNode, offset: TextUnit) { |
@@ -140,11 +153,13 @@ impl SyntaxContext<'_> { | |||
140 | }; | 153 | }; |
141 | if let Some(segment) = ast::PathSegment::cast(parent) { | 154 | if let Some(segment) = ast::PathSegment::cast(parent) { |
142 | let path = segment.parent_path(); | 155 | let path = segment.parent_path(); |
143 | // if let Some(path) = Path::from_ast(path) { | 156 | if let Some(mut path) = hir::Path::from_ast(path) { |
144 | // if !path.is_ident() { | 157 | if !path.is_ident() { |
145 | // return Some(NameRefKind::Path(path)); | 158 | path.segments.pop().unwrap(); |
146 | // } | 159 | self.path_prefix = Some(path); |
147 | // } | 160 | return; |
161 | } | ||
162 | } | ||
148 | if path.qualifier().is_none() { | 163 | if path.qualifier().is_none() { |
149 | self.is_trivial_path = true; | 164 | self.is_trivial_path = true; |
150 | self.enclosing_fn = self | 165 | self.enclosing_fn = self |
diff --git a/crates/ra_analysis/src/completion/complete_path.rs b/crates/ra_analysis/src/completion/complete_path.rs new file mode 100644 index 000000000..d04503e46 --- /dev/null +++ b/crates/ra_analysis/src/completion/complete_path.rs | |||
@@ -0,0 +1,95 @@ | |||
1 | use crate::{ | ||
2 | completion::{CompletionItem, Completions, CompletionKind::*, SyntaxContext}, | ||
3 | Cancelable, | ||
4 | }; | ||
5 | |||
6 | pub(super) fn complete_path(acc: &mut Completions, ctx: &SyntaxContext) -> Cancelable<()> { | ||
7 | let (path, module) = match (&ctx.path_prefix, &ctx.module) { | ||
8 | (Some(path), Some(module)) => (path.clone(), module), | ||
9 | _ => return Ok(()), | ||
10 | }; | ||
11 | let def_id = match module.resolve_path(ctx.db, path)? { | ||
12 | None => return Ok(()), | ||
13 | Some(it) => it, | ||
14 | }; | ||
15 | let target_module = match def_id.resolve(ctx.db)? { | ||
16 | hir::Def::Module(it) => it, | ||
17 | _ => return Ok(()), | ||
18 | }; | ||
19 | let module_scope = target_module.scope(ctx.db)?; | ||
20 | module_scope.entries().for_each(|(name, _res)| { | ||
21 | CompletionItem::new(name.to_string()) | ||
22 | .kind(Reference) | ||
23 | .add_to(acc) | ||
24 | }); | ||
25 | Ok(()) | ||
26 | } | ||
27 | |||
28 | #[cfg(test)] | ||
29 | mod tests { | ||
30 | use crate::completion::{CompletionKind, check_completion}; | ||
31 | |||
32 | fn check_reference_completion(code: &str, expected_completions: &str) { | ||
33 | check_completion(code, expected_completions, CompletionKind::Reference); | ||
34 | } | ||
35 | |||
36 | #[test] | ||
37 | fn completes_use_item_starting_with_self() { | ||
38 | check_reference_completion( | ||
39 | r" | ||
40 | use self::m::<|>; | ||
41 | |||
42 | mod m { | ||
43 | struct Bar; | ||
44 | } | ||
45 | ", | ||
46 | "Bar", | ||
47 | ); | ||
48 | } | ||
49 | |||
50 | #[test] | ||
51 | fn completes_use_item_starting_with_crate() { | ||
52 | check_reference_completion( | ||
53 | " | ||
54 | //- /lib.rs | ||
55 | mod foo; | ||
56 | struct Spam; | ||
57 | //- /foo.rs | ||
58 | use crate::Sp<|> | ||
59 | ", | ||
60 | "Spam;foo", | ||
61 | ); | ||
62 | } | ||
63 | |||
64 | #[test] | ||
65 | fn completes_nested_use_tree() { | ||
66 | check_reference_completion( | ||
67 | " | ||
68 | //- /lib.rs | ||
69 | mod foo; | ||
70 | struct Spam; | ||
71 | //- /foo.rs | ||
72 | use crate::{Sp<|>}; | ||
73 | ", | ||
74 | "Spam;foo", | ||
75 | ); | ||
76 | } | ||
77 | |||
78 | #[test] | ||
79 | fn completes_deeply_nested_use_tree() { | ||
80 | check_reference_completion( | ||
81 | " | ||
82 | //- /lib.rs | ||
83 | mod foo; | ||
84 | pub mod bar { | ||
85 | pub mod baz { | ||
86 | pub struct Spam; | ||
87 | } | ||
88 | } | ||
89 | //- /foo.rs | ||
90 | use crate::{bar::{baz::Sp<|>}}; | ||
91 | ", | ||
92 | "Spam", | ||
93 | ); | ||
94 | } | ||
95 | } | ||
diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs index 46d381927..459ed8f6f 100644 --- a/crates/ra_analysis/src/completion/reference_completion.rs +++ b/crates/ra_analysis/src/completion/reference_completion.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use rustc_hash::{FxHashSet}; | 1 | use rustc_hash::FxHashSet; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | SourceFileNode, AstNode, | 3 | SourceFileNode, AstNode, |
4 | ast, | 4 | ast, |
@@ -6,7 +6,7 @@ use ra_syntax::{ | |||
6 | }; | 6 | }; |
7 | use hir::{ | 7 | use hir::{ |
8 | self, | 8 | self, |
9 | FnScopes, Def, Path | 9 | FnScopes, Path |
10 | }; | 10 | }; |
11 | 11 | ||
12 | use crate::{ | 12 | use crate::{ |
@@ -53,7 +53,7 @@ pub(super) fn completions( | |||
53 | .add_to(acc) | 53 | .add_to(acc) |
54 | }); | 54 | }); |
55 | } | 55 | } |
56 | NameRefKind::Path(path) => complete_path(acc, db, module, path)?, | 56 | NameRefKind::Path(_) => (), |
57 | NameRefKind::BareIdentInMod => (), | 57 | NameRefKind::BareIdentInMod => (), |
58 | } | 58 | } |
59 | Ok(()) | 59 | Ok(()) |
@@ -121,33 +121,6 @@ fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Completions) | |||
121 | } | 121 | } |
122 | } | 122 | } |
123 | 123 | ||
124 | fn complete_path( | ||
125 | acc: &mut Completions, | ||
126 | db: &RootDatabase, | ||
127 | module: &hir::Module, | ||
128 | mut path: Path, | ||
129 | ) -> Cancelable<()> { | ||
130 | if path.segments.is_empty() { | ||
131 | return Ok(()); | ||
132 | } | ||
133 | path.segments.pop(); | ||
134 | let def_id = match module.resolve_path(db, path)? { | ||
135 | None => return Ok(()), | ||
136 | Some(it) => it, | ||
137 | }; | ||
138 | let target_module = match def_id.resolve(db)? { | ||
139 | Def::Module(it) => it, | ||
140 | _ => return Ok(()), | ||
141 | }; | ||
142 | let module_scope = target_module.scope(db)?; | ||
143 | module_scope.entries().for_each(|(name, _res)| { | ||
144 | CompletionItem::new(name.to_string()) | ||
145 | .kind(Reference) | ||
146 | .add_to(acc) | ||
147 | }); | ||
148 | Ok(()) | ||
149 | } | ||
150 | |||
151 | #[cfg(test)] | 124 | #[cfg(test)] |
152 | mod tests { | 125 | mod tests { |
153 | use crate::completion::{CompletionKind, check_completion}; | 126 | use crate::completion::{CompletionKind, check_completion}; |