aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_analysis/src/completion.rs61
-rw-r--r--crates/ra_analysis/src/imp.rs22
-rw-r--r--crates/ra_analysis/src/lib.rs4
-rw-r--r--crates/ra_analysis/src/roots.rs3
-rw-r--r--crates/ra_analysis/tests/tests.rs14
-rw-r--r--crates/ra_editor/src/completion.rs30
-rw-r--r--crates/ra_editor/src/lib.rs2
-rw-r--r--crates/ra_syntax/src/ast/generated.rs4
-rw-r--r--crates/ra_syntax/src/ast/mod.rs30
-rw-r--r--crates/ra_syntax/src/grammar.ron3
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 @@
1use ra_editor::{CompletionItem, find_node_at_offset, complete_module_items};
2use ra_syntax::{
3 AtomEdit, File, TextUnit, AstNode,
4 ast::{self, ModuleItemOwner},
5};
6
7use crate::{
8 FileId, Cancelable,
9 db::{self, SyntaxDatabase},
10 descriptors::module::{ModulesDatabase, ModuleTree, ModuleId},
11};
12
13pub(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
32pub(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
44fn 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
8use ra_editor::{self, find_node_at_offset, resolve_local_name, FileSymbol, LineIndex, LocalEdit}; 8use ra_editor::{self, find_node_at_offset, resolve_local_name, FileSymbol, LineIndex, LocalEdit, CompletionItem};
9use ra_syntax::{ 9use 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;
11mod imp; 11mod imp;
12mod roots; 12mod roots;
13mod symbol_index; 13mod symbol_index;
14mod completion;
14 15
15use std::{fmt::Debug, sync::Arc}; 16use 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 @@
1use std::{sync::Arc}; 1use std::{sync::Arc};
2 2
3use ra_editor::LineIndex;
4use ra_syntax::File;
5use rustc_hash::FxHashSet; 3use rustc_hash::FxHashSet;
6use rayon::prelude::*; 4use rayon::prelude::*;
7use salsa::Database; 5use 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]
269fn 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
3use ra_syntax::{ 3use 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
68pub 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
68fn complete_name_ref(file: &File, name_ref: ast::NameRef, acc: &mut Vec<CompletionItem>) { 83fn 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
22pub use self::{ 22pub 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)]
237pub enum PathSegmentKind<'a> {
238 Name(NameRef<'a>),
239 SelfKw,
240 SuperKw,
241 CrateKw,
242}
243
244impl<'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
235fn child_opt<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> Option<C> { 265fn 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": (