diff options
-rw-r--r-- | crates/ra_analysis/src/completion.rs | 61 | ||||
-rw-r--r-- | crates/ra_analysis/src/imp.rs | 22 | ||||
-rw-r--r-- | crates/ra_analysis/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ra_analysis/src/roots.rs | 3 | ||||
-rw-r--r-- | crates/ra_analysis/tests/tests.rs | 14 | ||||
-rw-r--r-- | crates/ra_editor/src/completion.rs | 30 | ||||
-rw-r--r-- | crates/ra_editor/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/generated.rs | 4 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/mod.rs | 30 | ||||
-rw-r--r-- | crates/ra_syntax/src/grammar.ron | 3 |
10 files changed, 152 insertions, 21 deletions
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs new file mode 100644 index 000000000..a0fd6828d --- /dev/null +++ b/crates/ra_analysis/src/completion.rs | |||
@@ -0,0 +1,61 @@ | |||
1 | use ra_editor::{CompletionItem, find_node_at_offset, complete_module_items}; | ||
2 | use ra_syntax::{ | ||
3 | AtomEdit, File, TextUnit, AstNode, | ||
4 | ast::{self, ModuleItemOwner}, | ||
5 | }; | ||
6 | |||
7 | use crate::{ | ||
8 | FileId, Cancelable, | ||
9 | db::{self, SyntaxDatabase}, | ||
10 | descriptors::module::{ModulesDatabase, ModuleTree, ModuleId}, | ||
11 | }; | ||
12 | |||
13 | pub(crate) fn resolve_based_completion(db: &db::RootDatabase, file_id: FileId, offset: TextUnit) -> Cancelable<Option<Vec<CompletionItem>>> { | ||
14 | let file = db.file_syntax(file_id); | ||
15 | let module_tree = db.module_tree()?; | ||
16 | let file = { | ||
17 | let edit = AtomEdit::insert(offset, "intellijRulezz".to_string()); | ||
18 | file.reparse(&edit) | ||
19 | }; | ||
20 | let target_file = match find_target_module(&module_tree, file_id, &file, offset) { | ||
21 | None => return Ok(None), | ||
22 | Some(target_module) => { | ||
23 | let file_id = target_module.file_id(&module_tree); | ||
24 | db.file_syntax(file_id) | ||
25 | } | ||
26 | }; | ||
27 | let mut res = Vec::new(); | ||
28 | complete_module_items(target_file.ast().items(), None, &mut res); | ||
29 | Ok(Some(res)) | ||
30 | } | ||
31 | |||
32 | pub(crate) fn find_target_module(module_tree: &ModuleTree, file_id: FileId, file: &File, offset: TextUnit) -> Option<ModuleId> { | ||
33 | let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), offset)?; | ||
34 | let mut crate_path = crate_path(name_ref)?; | ||
35 | let module_id = module_tree.any_module_for_file(file_id)?; | ||
36 | crate_path.pop(); | ||
37 | let mut target_module = module_id.root(&module_tree); | ||
38 | for name in crate_path { | ||
39 | target_module = target_module.child(module_tree, name.text().as_str())?; | ||
40 | } | ||
41 | Some(target_module) | ||
42 | } | ||
43 | |||
44 | fn crate_path(name_ref: ast::NameRef) -> Option<Vec<ast::NameRef>> { | ||
45 | let mut path = name_ref.syntax() | ||
46 | .parent().and_then(ast::PathSegment::cast)? | ||
47 | .parent_path(); | ||
48 | let mut res = Vec::new(); | ||
49 | loop { | ||
50 | let segment = path.segment()?; | ||
51 | match segment.kind()? { | ||
52 | ast::PathSegmentKind::Name(name) => res.push(name), | ||
53 | ast::PathSegmentKind::CrateKw => break, | ||
54 | ast::PathSegmentKind::SelfKw | ast::PathSegmentKind::SuperKw => | ||
55 | return None, | ||
56 | } | ||
57 | path = path.qualifier()?; | ||
58 | } | ||
59 | res.reverse(); | ||
60 | Some(res) | ||
61 | } | ||
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index f142b6c43..f3e5b2887 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -5,7 +5,7 @@ use std::{ | |||
5 | sync::Arc, | 5 | sync::Arc, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use ra_editor::{self, find_node_at_offset, resolve_local_name, FileSymbol, LineIndex, LocalEdit}; | 8 | use ra_editor::{self, find_node_at_offset, resolve_local_name, FileSymbol, LineIndex, LocalEdit, CompletionItem}; |
9 | use ra_syntax::{ | 9 | use ra_syntax::{ |
10 | ast::{self, ArgListOwner, Expr, NameOwner}, | 10 | ast::{self, ArgListOwner, Expr, NameOwner}, |
11 | AstNode, File, SmolStr, | 11 | AstNode, File, SmolStr, |
@@ -197,6 +197,26 @@ impl AnalysisImpl { | |||
197 | pub fn crate_root(&self, crate_id: CrateId) -> FileId { | 197 | pub fn crate_root(&self, crate_id: CrateId) -> FileId { |
198 | self.data.crate_graph.crate_roots[&crate_id] | 198 | self.data.crate_graph.crate_roots[&crate_id] |
199 | } | 199 | } |
200 | pub fn completions(&self, file_id: FileId, offset: TextUnit) -> Cancelable<Option<Vec<CompletionItem>>> { | ||
201 | let mut res = Vec::new(); | ||
202 | let mut has_completions = false; | ||
203 | let file = self.file_syntax(file_id); | ||
204 | if let Some(scope_based) = ra_editor::scope_completion(&file, offset) { | ||
205 | res.extend(scope_based); | ||
206 | has_completions = true; | ||
207 | } | ||
208 | let root = self.root(file_id); | ||
209 | if let Some(scope_based) = crate::completion::resolve_based_completion(root.db(), file_id, offset)? { | ||
210 | res.extend(scope_based); | ||
211 | has_completions = true; | ||
212 | } | ||
213 | let res = if has_completions { | ||
214 | Some(res) | ||
215 | } else { | ||
216 | None | ||
217 | }; | ||
218 | Ok(res) | ||
219 | } | ||
200 | pub fn approximately_resolve_symbol( | 220 | pub fn approximately_resolve_symbol( |
201 | &self, | 221 | &self, |
202 | file_id: FileId, | 222 | file_id: FileId, |
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index 67a239a5c..7078e2d31 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs | |||
@@ -11,6 +11,7 @@ mod descriptors; | |||
11 | mod imp; | 11 | mod imp; |
12 | mod roots; | 12 | mod roots; |
13 | mod symbol_index; | 13 | mod symbol_index; |
14 | mod completion; | ||
14 | 15 | ||
15 | use std::{fmt::Debug, sync::Arc}; | 16 | use std::{fmt::Debug, sync::Arc}; |
16 | 17 | ||
@@ -246,8 +247,7 @@ impl Analysis { | |||
246 | Ok(ra_editor::highlight(&file)) | 247 | Ok(ra_editor::highlight(&file)) |
247 | } | 248 | } |
248 | pub fn completions(&self, file_id: FileId, offset: TextUnit) -> Cancelable<Option<Vec<CompletionItem>>> { | 249 | pub fn completions(&self, file_id: FileId, offset: TextUnit) -> Cancelable<Option<Vec<CompletionItem>>> { |
249 | let file = self.imp.file_syntax(file_id); | 250 | self.imp.completions(file_id, offset) |
250 | Ok(ra_editor::scope_completion(&file, offset)) | ||
251 | } | 251 | } |
252 | pub fn assists(&self, file_id: FileId, range: TextRange) -> Cancelable<Vec<SourceChange>> { | 252 | pub fn assists(&self, file_id: FileId, range: TextRange) -> Cancelable<Vec<SourceChange>> { |
253 | Ok(self.imp.assists(file_id, range)) | 253 | Ok(self.imp.assists(file_id, range)) |
diff --git a/crates/ra_analysis/src/roots.rs b/crates/ra_analysis/src/roots.rs index aa0243720..1e9e613ac 100644 --- a/crates/ra_analysis/src/roots.rs +++ b/crates/ra_analysis/src/roots.rs | |||
@@ -1,7 +1,5 @@ | |||
1 | use std::{sync::Arc}; | 1 | use std::{sync::Arc}; |
2 | 2 | ||
3 | use ra_editor::LineIndex; | ||
4 | use ra_syntax::File; | ||
5 | use rustc_hash::FxHashSet; | 3 | use rustc_hash::FxHashSet; |
6 | use rayon::prelude::*; | 4 | use rayon::prelude::*; |
7 | use salsa::Database; | 5 | use salsa::Database; |
@@ -10,7 +8,6 @@ use crate::{ | |||
10 | Cancelable, | 8 | Cancelable, |
11 | db::{self, FilesDatabase, SyntaxDatabase}, | 9 | db::{self, FilesDatabase, SyntaxDatabase}, |
12 | imp::FileResolverImp, | 10 | imp::FileResolverImp, |
13 | descriptors::module::{ModulesDatabase, ModuleTree}, | ||
14 | symbol_index::SymbolIndex, | 11 | symbol_index::SymbolIndex, |
15 | FileId, | 12 | FileId, |
16 | }; | 13 | }; |
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs index 7ae3d0eeb..52fae71ae 100644 --- a/crates/ra_analysis/tests/tests.rs +++ b/crates/ra_analysis/tests/tests.rs | |||
@@ -264,3 +264,17 @@ fn test_find_all_refs_for_param_inside() { | |||
264 | let refs = get_all_refs(code); | 264 | let refs = get_all_refs(code); |
265 | assert_eq!(refs.len(), 2); | 265 | assert_eq!(refs.len(), 2); |
266 | } | 266 | } |
267 | |||
268 | #[test] | ||
269 | fn test_complete_crate_path() { | ||
270 | let snap = analysis(&[ | ||
271 | ("/lib.rs", "mod foo; struct Spam;"), | ||
272 | ("/foo.rs", "use crate::Sp"), | ||
273 | ]); | ||
274 | let completions = snap.completions(FileId(2), 13.into()).unwrap().unwrap(); | ||
275 | assert_eq_dbg( | ||
276 | r#"[CompletionItem { label: "foo", lookup: None, snippet: None }, | ||
277 | CompletionItem { label: "Spam", lookup: None, snippet: None }]"#, | ||
278 | &completions, | ||
279 | ); | ||
280 | } | ||
diff --git a/crates/ra_editor/src/completion.rs b/crates/ra_editor/src/completion.rs index 8502b337d..0a3675255 100644 --- a/crates/ra_editor/src/completion.rs +++ b/crates/ra_editor/src/completion.rs | |||
@@ -2,7 +2,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; | |||
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | algo::visit::{visitor, visitor_ctx, Visitor, VisitorCtx}, | 4 | algo::visit::{visitor, visitor_ctx, Visitor, VisitorCtx}, |
5 | ast::{self, LoopBodyOwner, ModuleItemOwner}, | 5 | ast::{self, AstChildren, LoopBodyOwner, ModuleItemOwner}, |
6 | text_utils::is_subrange, | 6 | text_utils::is_subrange, |
7 | AstNode, File, | 7 | AstNode, File, |
8 | SyntaxKind::*, | 8 | SyntaxKind::*, |
@@ -65,6 +65,21 @@ pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionI | |||
65 | } | 65 | } |
66 | } | 66 | } |
67 | 67 | ||
68 | pub fn complete_module_items(items: AstChildren<ast::ModuleItem>, this_item: Option<ast::NameRef>, acc: &mut Vec<CompletionItem>) { | ||
69 | let scope = ModuleScope::new(items); | ||
70 | acc.extend( | ||
71 | scope | ||
72 | .entries() | ||
73 | .iter() | ||
74 | .filter(|entry| Some(entry.syntax()) != this_item.map(|it| it.syntax())) | ||
75 | .map(|entry| CompletionItem { | ||
76 | label: entry.name().to_string(), | ||
77 | lookup: None, | ||
78 | snippet: None, | ||
79 | }), | ||
80 | ); | ||
81 | } | ||
82 | |||
68 | fn complete_name_ref(file: &File, name_ref: ast::NameRef, acc: &mut Vec<CompletionItem>) { | 83 | fn complete_name_ref(file: &File, name_ref: ast::NameRef, acc: &mut Vec<CompletionItem>) { |
69 | if !is_node::<ast::Path>(name_ref.syntax()) { | 84 | if !is_node::<ast::Path>(name_ref.syntax()) { |
70 | return; | 85 | return; |
@@ -77,18 +92,7 @@ fn complete_name_ref(file: &File, name_ref: ast::NameRef, acc: &mut Vec<Completi | |||
77 | .accept(node) | 92 | .accept(node) |
78 | { | 93 | { |
79 | if let Some(items) = items { | 94 | if let Some(items) = items { |
80 | let scope = ModuleScope::new(items); | 95 | complete_module_items(items, Some(name_ref), acc); |
81 | acc.extend( | ||
82 | scope | ||
83 | .entries() | ||
84 | .iter() | ||
85 | .filter(|entry| entry.syntax() != name_ref.syntax()) | ||
86 | .map(|entry| CompletionItem { | ||
87 | label: entry.name().to_string(), | ||
88 | lookup: None, | ||
89 | snippet: None, | ||
90 | }), | ||
91 | ); | ||
92 | } | 96 | } |
93 | break; | 97 | break; |
94 | } else if !visited_fn { | 98 | } else if !visited_fn { |
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs index 94e9a18e4..b73eb4ac7 100644 --- a/crates/ra_editor/src/lib.rs +++ b/crates/ra_editor/src/lib.rs | |||
@@ -21,7 +21,7 @@ mod typing; | |||
21 | 21 | ||
22 | pub use self::{ | 22 | pub use self::{ |
23 | code_actions::{add_derive, add_impl, flip_comma, introduce_variable, LocalEdit}, | 23 | code_actions::{add_derive, add_impl, flip_comma, introduce_variable, LocalEdit}, |
24 | completion::{scope_completion, CompletionItem}, | 24 | completion::{scope_completion, complete_module_items, CompletionItem}, |
25 | edit::{Edit, EditBuilder}, | 25 | edit::{Edit, EditBuilder}, |
26 | extend_selection::extend_selection, | 26 | extend_selection::extend_selection, |
27 | folding_ranges::{folding_ranges, Fold, FoldKind}, | 27 | folding_ranges::{folding_ranges, Fold, FoldKind}, |
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 98c7de361..096405a38 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -1371,6 +1371,10 @@ impl<'a> Path<'a> { | |||
1371 | pub fn segment(self) -> Option<PathSegment<'a>> { | 1371 | pub fn segment(self) -> Option<PathSegment<'a>> { |
1372 | super::child_opt(self) | 1372 | super::child_opt(self) |
1373 | } | 1373 | } |
1374 | |||
1375 | pub fn qualifier(self) -> Option<Path<'a>> { | ||
1376 | super::child_opt(self) | ||
1377 | } | ||
1374 | } | 1378 | } |
1375 | 1379 | ||
1376 | // PathExpr | 1380 | // PathExpr |
diff --git a/crates/ra_syntax/src/ast/mod.rs b/crates/ra_syntax/src/ast/mod.rs index 900426a8a..c033263a1 100644 --- a/crates/ra_syntax/src/ast/mod.rs +++ b/crates/ra_syntax/src/ast/mod.rs | |||
@@ -232,6 +232,36 @@ impl<'a> IfExpr<'a> { | |||
232 | } | 232 | } |
233 | } | 233 | } |
234 | 234 | ||
235 | |||
236 | #[derive(Debug, Clone, Copy)] | ||
237 | pub enum PathSegmentKind<'a> { | ||
238 | Name(NameRef<'a>), | ||
239 | SelfKw, | ||
240 | SuperKw, | ||
241 | CrateKw, | ||
242 | } | ||
243 | |||
244 | impl<'a> PathSegment<'a> { | ||
245 | pub fn parent_path(self) -> Path<'a> { | ||
246 | self.syntax().parent().and_then(Path::cast) | ||
247 | .expect("segments are always nested in paths") | ||
248 | } | ||
249 | |||
250 | pub fn kind(self) -> Option<PathSegmentKind<'a>> { | ||
251 | let res = if let Some(name_ref) = self.name_ref() { | ||
252 | PathSegmentKind::Name(name_ref) | ||
253 | } else { | ||
254 | match self.syntax().first_child()?.kind() { | ||
255 | SELF_KW => PathSegmentKind::SelfKw, | ||
256 | SUPER_KW => PathSegmentKind::SuperKw, | ||
257 | CRATE_KW => PathSegmentKind::CrateKw, | ||
258 | _ => return None, | ||
259 | } | ||
260 | }; | ||
261 | Some(res) | ||
262 | } | ||
263 | } | ||
264 | |||
235 | fn child_opt<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> Option<C> { | 265 | fn child_opt<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> Option<C> { |
236 | children(parent).next() | 266 | children(parent).next() |
237 | } | 267 | } |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 0830e02f2..c1c215e0d 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -531,7 +531,8 @@ Grammar( | |||
531 | ), | 531 | ), |
532 | "Path": ( | 532 | "Path": ( |
533 | options: [ | 533 | options: [ |
534 | ["segment", "PathSegment"] | 534 | ["segment", "PathSegment"], |
535 | ["qualifier", "Path"], | ||
535 | ] | 536 | ] |
536 | ), | 537 | ), |
537 | "PathSegment": ( | 538 | "PathSegment": ( |