aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api')
-rw-r--r--crates/ra_ide_api/Cargo.toml5
-rw-r--r--crates/ra_ide_api/src/call_info.rs18
-rw-r--r--crates/ra_ide_api/src/completion.rs19
-rw-r--r--crates/ra_ide_api/src/completion/complete_dot.rs36
-rw-r--r--crates/ra_ide_api/src/completion/complete_path.rs18
-rw-r--r--crates/ra_ide_api/src/completion/complete_scope.rs18
-rw-r--r--crates/ra_ide_api/src/completion/completion_context.rs11
-rw-r--r--crates/ra_ide_api/src/completion/completion_item.rs2
-rw-r--r--crates/ra_ide_api/src/extend_selection.rs5
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs56
-rw-r--r--crates/ra_ide_api/src/hover.rs122
-rw-r--r--crates/ra_ide_api/src/imp.rs76
-rw-r--r--crates/ra_ide_api/src/lib.rs44
-rw-r--r--crates/ra_ide_api/src/navigation_target.rs56
-rw-r--r--crates/ra_ide_api/src/parent_module.rs15
-rw-r--r--crates/ra_ide_api/src/runnables.rs113
-rw-r--r--crates/ra_ide_api/src/snapshots/tests__highlight_query_group_macro.snap26
-rw-r--r--crates/ra_ide_api/src/snapshots/tests__highlights_code_inside_macros.snap70
-rw-r--r--crates/ra_ide_api/src/snapshots/tests__runnables.snap22
-rw-r--r--crates/ra_ide_api/src/snapshots/tests__runnables_module.snap18
-rw-r--r--crates/ra_ide_api/src/snapshots/tests__runnables_multiple_depth_module.snap18
-rw-r--r--crates/ra_ide_api/src/snapshots/tests__runnables_one_depth_layer_module.snap18
-rw-r--r--crates/ra_ide_api/src/symbol_index.rs17
-rw-r--r--crates/ra_ide_api/src/syntax_highlighting.rs38
-rw-r--r--crates/ra_ide_api/tests/test/main.rs20
-rw-r--r--crates/ra_ide_api/tests/test/runnables.rs109
-rw-r--r--crates/ra_ide_api/tests/test/snapshots/test__unresolved_module_diagnostic.snap26
27 files changed, 528 insertions, 468 deletions
diff --git a/crates/ra_ide_api/Cargo.toml b/crates/ra_ide_api/Cargo.toml
index d42a664b6..022cbd5a3 100644
--- a/crates/ra_ide_api/Cargo.toml
+++ b/crates/ra_ide_api/Cargo.toml
@@ -10,7 +10,7 @@ log = "0.4.5"
10relative-path = "0.4.0" 10relative-path = "0.4.0"
11rayon = "1.0.2" 11rayon = "1.0.2"
12fst = "0.3.1" 12fst = "0.3.1"
13salsa = "0.9.1" 13salsa = "0.9.2"
14rustc-hash = "1.0" 14rustc-hash = "1.0"
15parking_lot = "0.7.0" 15parking_lot = "0.7.0"
16unicase = "2.2.0" 16unicase = "2.2.0"
@@ -21,3 +21,6 @@ ra_text_edit = { path = "../ra_text_edit" }
21ra_db = { path = "../ra_db" } 21ra_db = { path = "../ra_db" }
22hir = { path = "../ra_hir", package = "ra_hir" } 22hir = { path = "../ra_hir", package = "ra_hir" }
23test_utils = { path = "../test_utils" } 23test_utils = { path = "../test_utils" }
24
25[dev-dependencies]
26insta = "0.1.4"
diff --git a/crates/ra_ide_api/src/call_info.rs b/crates/ra_ide_api/src/call_info.rs
index 27b760780..18b9508ef 100644
--- a/crates/ra_ide_api/src/call_info.rs
+++ b/crates/ra_ide_api/src/call_info.rs
@@ -1,6 +1,6 @@
1use std::cmp::{max, min}; 1use std::cmp::{max, min};
2 2
3use ra_db::{SyntaxDatabase, Cancelable}; 3use ra_db::SyntaxDatabase;
4use ra_syntax::{ 4use ra_syntax::{
5 AstNode, SyntaxNode, TextUnit, TextRange, 5 AstNode, SyntaxNode, TextUnit, TextRange,
6 SyntaxKind::FN_DEF, 6 SyntaxKind::FN_DEF,
@@ -11,21 +11,23 @@ use ra_syntax::{
11use crate::{FilePosition, CallInfo, db::RootDatabase}; 11use crate::{FilePosition, CallInfo, db::RootDatabase};
12 12
13/// Computes parameter information for the given call expression. 13/// Computes parameter information for the given call expression.
14pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Cancelable<Option<CallInfo>> { 14pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
15 let file = db.source_file(position.file_id); 15 let file = db.source_file(position.file_id);
16 let syntax = file.syntax(); 16 let syntax = file.syntax();
17 17
18 // Find the calling expression and it's NameRef 18 // Find the calling expression and it's NameRef
19 let calling_node = ctry!(FnCallNode::with_node(syntax, position.offset)); 19 let calling_node = FnCallNode::with_node(syntax, position.offset)?;
20 let name_ref = ctry!(calling_node.name_ref()); 20 let name_ref = calling_node.name_ref()?;
21 21
22 // Resolve the function's NameRef (NOTE: this isn't entirely accurate). 22 // Resolve the function's NameRef (NOTE: this isn't entirely accurate).
23 let file_symbols = db.index_resolve(name_ref)?; 23 let file_symbols = db.index_resolve(name_ref);
24 let symbol = ctry!(file_symbols.into_iter().find(|it| it.ptr.kind() == FN_DEF)); 24 let symbol = file_symbols
25 .into_iter()
26 .find(|it| it.ptr.kind() == FN_DEF)?;
25 let fn_file = db.source_file(symbol.file_id); 27 let fn_file = db.source_file(symbol.file_id);
26 let fn_def = symbol.ptr.resolve(&fn_file); 28 let fn_def = symbol.ptr.resolve(&fn_file);
27 let fn_def = ast::FnDef::cast(&fn_def).unwrap(); 29 let fn_def = ast::FnDef::cast(&fn_def).unwrap();
28 let mut call_info = ctry!(CallInfo::new(fn_def)); 30 let mut call_info = CallInfo::new(fn_def)?;
29 // If we have a calling expression let's find which argument we are on 31 // If we have a calling expression let's find which argument we are on
30 let num_params = call_info.parameters.len(); 32 let num_params = call_info.parameters.len();
31 let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); 33 let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some();
@@ -61,7 +63,7 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Cancelable
61 } 63 }
62 } 64 }
63 65
64 Ok(Some(call_info)) 66 Some(call_info)
65} 67}
66 68
67enum FnCallNode<'a> { 69enum FnCallNode<'a> {
diff --git a/crates/ra_ide_api/src/completion.rs b/crates/ra_ide_api/src/completion.rs
index ce777a771..b03ddd74c 100644
--- a/crates/ra_ide_api/src/completion.rs
+++ b/crates/ra_ide_api/src/completion.rs
@@ -12,7 +12,7 @@ use ra_db::SyntaxDatabase;
12 12
13use crate::{ 13use crate::{
14 db, 14 db,
15 Cancelable, FilePosition, 15 FilePosition,
16 completion::{ 16 completion::{
17 completion_item::{Completions, CompletionKind}, 17 completion_item::{Completions, CompletionKind},
18 completion_context::CompletionContext, 18 completion_context::CompletionContext,
@@ -43,12 +43,9 @@ pub use crate::completion::completion_item::{CompletionItem, InsertText, Complet
43/// `foo` *should* be present among the completion variants. Filtering by 43/// `foo` *should* be present among the completion variants. Filtering by
44/// identifier prefix/fuzzy match should be done higher in the stack, together 44/// identifier prefix/fuzzy match should be done higher in the stack, together
45/// with ordering of completions (currently this is done by the client). 45/// with ordering of completions (currently this is done by the client).
46pub(crate) fn completions( 46pub(crate) fn completions(db: &db::RootDatabase, position: FilePosition) -> Option<Completions> {
47 db: &db::RootDatabase,
48 position: FilePosition,
49) -> Cancelable<Option<Completions>> {
50 let original_file = db.source_file(position.file_id); 47 let original_file = db.source_file(position.file_id);
51 let ctx = ctry!(CompletionContext::new(db, &original_file, position)?); 48 let ctx = CompletionContext::new(db, &original_file, position)?;
52 49
53 let mut acc = Completions::default(); 50 let mut acc = Completions::default();
54 51
@@ -57,11 +54,11 @@ pub(crate) fn completions(
57 complete_keyword::complete_use_tree_keyword(&mut acc, &ctx); 54 complete_keyword::complete_use_tree_keyword(&mut acc, &ctx);
58 complete_snippet::complete_expr_snippet(&mut acc, &ctx); 55 complete_snippet::complete_expr_snippet(&mut acc, &ctx);
59 complete_snippet::complete_item_snippet(&mut acc, &ctx); 56 complete_snippet::complete_item_snippet(&mut acc, &ctx);
60 complete_path::complete_path(&mut acc, &ctx)?; 57 complete_path::complete_path(&mut acc, &ctx);
61 complete_scope::complete_scope(&mut acc, &ctx)?; 58 complete_scope::complete_scope(&mut acc, &ctx);
62 complete_dot::complete_dot(&mut acc, &ctx)?; 59 complete_dot::complete_dot(&mut acc, &ctx);
63 60
64 Ok(Some(acc)) 61 Some(acc)
65} 62}
66 63
67#[cfg(test)] 64#[cfg(test)]
@@ -72,6 +69,6 @@ fn check_completion(code: &str, expected_completions: &str, kind: CompletionKind
72 } else { 69 } else {
73 single_file_with_position(code) 70 single_file_with_position(code)
74 }; 71 };
75 let completions = completions(&analysis.db, position).unwrap().unwrap(); 72 let completions = completions(&analysis.db, position).unwrap();
76 completions.assert_match(expected_completions, kind); 73 completions.assert_match(expected_completions, kind);
77} 74}
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs
index 37985b398..473edc50e 100644
--- a/crates/ra_ide_api/src/completion/complete_dot.rs
+++ b/crates/ra_ide_api/src/completion/complete_dot.rs
@@ -1,41 +1,39 @@
1use hir::{Ty, Def}; 1use hir::{Ty, Def};
2 2
3use crate::Cancelable;
4use crate::completion::{CompletionContext, Completions, CompletionKind, CompletionItem, CompletionItemKind}; 3use crate::completion::{CompletionContext, Completions, CompletionKind, CompletionItem, CompletionItemKind};
5 4
6/// Complete dot accesses, i.e. fields or methods (currently only fields). 5/// Complete dot accesses, i.e. fields or methods (currently only fields).
7pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> { 6pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
8 let (function, receiver) = match (&ctx.function, ctx.dot_receiver) { 7 let (function, receiver) = match (&ctx.function, ctx.dot_receiver) {
9 (Some(function), Some(receiver)) => (function, receiver), 8 (Some(function), Some(receiver)) => (function, receiver),
10 _ => return Ok(()), 9 _ => return,
11 }; 10 };
12 let infer_result = function.infer(ctx.db)?; 11 let infer_result = function.infer(ctx.db);
13 let syntax_mapping = function.body_syntax_mapping(ctx.db)?; 12 let syntax_mapping = function.body_syntax_mapping(ctx.db);
14 let expr = match syntax_mapping.node_expr(receiver) { 13 let expr = match syntax_mapping.node_expr(receiver) {
15 Some(expr) => expr, 14 Some(expr) => expr,
16 None => return Ok(()), 15 None => return,
17 }; 16 };
18 let receiver_ty = infer_result[expr].clone(); 17 let receiver_ty = infer_result[expr].clone();
19 if !ctx.is_call { 18 if !ctx.is_call {
20 complete_fields(acc, ctx, receiver_ty.clone())?; 19 complete_fields(acc, ctx, receiver_ty.clone());
21 } 20 }
22 complete_methods(acc, ctx, receiver_ty)?; 21 complete_methods(acc, ctx, receiver_ty);
23 Ok(())
24} 22}
25 23
26fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) -> Cancelable<()> { 24fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
27 for receiver in receiver.autoderef(ctx.db) { 25 for receiver in receiver.autoderef(ctx.db) {
28 match receiver { 26 match receiver {
29 Ty::Adt { def_id, .. } => { 27 Ty::Adt { def_id, .. } => {
30 match def_id.resolve(ctx.db)? { 28 match def_id.resolve(ctx.db) {
31 Def::Struct(s) => { 29 Def::Struct(s) => {
32 for field in s.fields(ctx.db)? { 30 for field in s.fields(ctx.db) {
33 CompletionItem::new( 31 CompletionItem::new(
34 CompletionKind::Reference, 32 CompletionKind::Reference,
35 field.name().to_string(), 33 field.name().to_string(),
36 ) 34 )
37 .kind(CompletionItemKind::Field) 35 .kind(CompletionItemKind::Field)
38 .set_detail(field.ty(ctx.db)?.map(|ty| ty.to_string())) 36 .set_detail(field.ty(ctx.db).map(|ty| ty.to_string()))
39 .add_to(acc); 37 .add_to(acc);
40 } 38 }
41 } 39 }
@@ -53,14 +51,9 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty)
53 _ => {} 51 _ => {}
54 }; 52 };
55 } 53 }
56 Ok(())
57} 54}
58 55
59fn complete_methods( 56fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
60 acc: &mut Completions,
61 ctx: &CompletionContext,
62 receiver: Ty,
63) -> Cancelable<()> {
64 receiver.iterate_methods(ctx.db, |func| { 57 receiver.iterate_methods(ctx.db, |func| {
65 let sig = func.signature(ctx.db); 58 let sig = func.signature(ctx.db);
66 if sig.has_self_param() { 59 if sig.has_self_param() {
@@ -69,9 +62,8 @@ fn complete_methods(
69 .kind(CompletionItemKind::Method) 62 .kind(CompletionItemKind::Method)
70 .add_to(acc); 63 .add_to(acc);
71 } 64 }
72 Ok(None::<()>) 65 None::<()>
73 })?; 66 });
74 Ok(())
75} 67}
76 68
77#[cfg(test)] 69#[cfg(test)]
diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs
index a25ad3f13..1eded7658 100644
--- a/crates/ra_ide_api/src/completion/complete_path.rs
+++ b/crates/ra_ide_api/src/completion/complete_path.rs
@@ -1,20 +1,19 @@
1use crate::{ 1use crate::{
2 Cancelable,
3 completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext}, 2 completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext},
4}; 3};
5 4
6pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> { 5pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
7 let (path, module) = match (&ctx.path_prefix, &ctx.module) { 6 let (path, module) = match (&ctx.path_prefix, &ctx.module) {
8 (Some(path), Some(module)) => (path.clone(), module), 7 (Some(path), Some(module)) => (path.clone(), module),
9 _ => return Ok(()), 8 _ => return,
10 }; 9 };
11 let def_id = match module.resolve_path(ctx.db, &path)?.take_types() { 10 let def_id = match module.resolve_path(ctx.db, &path).take_types() {
12 Some(it) => it, 11 Some(it) => it,
13 None => return Ok(()), 12 None => return,
14 }; 13 };
15 match def_id.resolve(ctx.db)? { 14 match def_id.resolve(ctx.db) {
16 hir::Def::Module(module) => { 15 hir::Def::Module(module) => {
17 let module_scope = module.scope(ctx.db)?; 16 let module_scope = module.scope(ctx.db);
18 for (name, res) in module_scope.entries() { 17 for (name, res) in module_scope.entries() {
19 CompletionItem::new(CompletionKind::Reference, name.to_string()) 18 CompletionItem::new(CompletionKind::Reference, name.to_string())
20 .from_resolution(ctx, res) 19 .from_resolution(ctx, res)
@@ -22,7 +21,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> C
22 } 21 }
23 } 22 }
24 hir::Def::Enum(e) => { 23 hir::Def::Enum(e) => {
25 e.variants(ctx.db)? 24 e.variants(ctx.db)
26 .into_iter() 25 .into_iter()
27 .for_each(|(variant_name, _variant)| { 26 .for_each(|(variant_name, _variant)| {
28 CompletionItem::new(CompletionKind::Reference, variant_name.to_string()) 27 CompletionItem::new(CompletionKind::Reference, variant_name.to_string())
@@ -30,9 +29,8 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> C
30 .add_to(acc) 29 .add_to(acc)
31 }); 30 });
32 } 31 }
33 _ => return Ok(()), 32 _ => return,
34 }; 33 };
35 Ok(())
36} 34}
37 35
38#[cfg(test)] 36#[cfg(test)]
diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs
index 770a0fdf2..699680748 100644
--- a/crates/ra_ide_api/src/completion/complete_scope.rs
+++ b/crates/ra_ide_api/src/completion/complete_scope.rs
@@ -1,26 +1,23 @@
1use rustc_hash::FxHashSet; 1use rustc_hash::FxHashSet;
2use ra_syntax::TextUnit; 2use ra_syntax::TextUnit;
3 3
4use crate::{ 4use crate::completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext};
5 Cancelable,
6 completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext},
7};
8 5
9pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> { 6pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
10 if !ctx.is_trivial_path { 7 if !ctx.is_trivial_path {
11 return Ok(()); 8 return;
12 } 9 }
13 let module = match &ctx.module { 10 let module = match &ctx.module {
14 Some(it) => it, 11 Some(it) => it,
15 None => return Ok(()), 12 None => return,
16 }; 13 };
17 if let Some(function) = &ctx.function { 14 if let Some(function) = &ctx.function {
18 let scopes = function.scopes(ctx.db)?; 15 let scopes = function.scopes(ctx.db);
19 complete_fn(acc, &scopes, ctx.offset); 16 complete_fn(acc, &scopes, ctx.offset);
20 } 17 }
21 18
22 let module_scope = module.scope(ctx.db)?; 19 let module_scope = module.scope(ctx.db);
23 let (file_id, _) = module.definition_source(ctx.db)?; 20 let (file_id, _) = module.definition_source(ctx.db);
24 module_scope 21 module_scope
25 .entries() 22 .entries()
26 .filter(|(_name, res)| { 23 .filter(|(_name, res)| {
@@ -40,7 +37,6 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) ->
40 .from_resolution(ctx, res) 37 .from_resolution(ctx, res)
41 .add_to(acc) 38 .add_to(acc)
42 }); 39 });
43 Ok(())
44} 40}
45 41
46fn complete_fn(acc: &mut Completions, scopes: &hir::ScopesWithSyntaxMapping, offset: TextUnit) { 42fn complete_fn(acc: &mut Completions, scopes: &hir::ScopesWithSyntaxMapping, offset: TextUnit) {
diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs
index 113f6c070..e537e0082 100644
--- a/crates/ra_ide_api/src/completion/completion_context.rs
+++ b/crates/ra_ide_api/src/completion/completion_context.rs
@@ -7,7 +7,7 @@ use ra_syntax::{
7}; 7};
8use hir::source_binder; 8use hir::source_binder;
9 9
10use crate::{db, FilePosition, Cancelable}; 10use crate::{db, FilePosition};
11 11
12/// `CompletionContext` is created early during completion to figure out, where 12/// `CompletionContext` is created early during completion to figure out, where
13/// exactly is the cursor, syntax-wise. 13/// exactly is the cursor, syntax-wise.
@@ -41,10 +41,9 @@ impl<'a> CompletionContext<'a> {
41 db: &'a db::RootDatabase, 41 db: &'a db::RootDatabase,
42 original_file: &'a SourceFile, 42 original_file: &'a SourceFile,
43 position: FilePosition, 43 position: FilePosition,
44 ) -> Cancelable<Option<CompletionContext<'a>>> { 44 ) -> Option<CompletionContext<'a>> {
45 let module = source_binder::module_from_position(db, position)?; 45 let module = source_binder::module_from_position(db, position);
46 let leaf = 46 let leaf = find_leaf_at_offset(original_file.syntax(), position.offset).left_biased()?;
47 ctry!(find_leaf_at_offset(original_file.syntax(), position.offset).left_biased());
48 let mut ctx = CompletionContext { 47 let mut ctx = CompletionContext {
49 db, 48 db,
50 leaf, 49 leaf,
@@ -63,7 +62,7 @@ impl<'a> CompletionContext<'a> {
63 is_call: false, 62 is_call: false,
64 }; 63 };
65 ctx.fill(original_file, position.offset); 64 ctx.fill(original_file, position.offset);
66 Ok(Some(ctx)) 65 Some(ctx)
67 } 66 }
68 67
69 fn fill(&mut self, original_file: &'a SourceFile, offset: TextUnit) { 68 fn fill(&mut self, original_file: &'a SourceFile, offset: TextUnit) {
diff --git a/crates/ra_ide_api/src/completion/completion_item.rs b/crates/ra_ide_api/src/completion/completion_item.rs
index b75d65de3..11d00f78c 100644
--- a/crates/ra_ide_api/src/completion/completion_item.rs
+++ b/crates/ra_ide_api/src/completion/completion_item.rs
@@ -144,7 +144,7 @@ impl Builder {
144 ctx: &CompletionContext, 144 ctx: &CompletionContext,
145 resolution: &hir::Resolution, 145 resolution: &hir::Resolution,
146 ) -> Builder { 146 ) -> Builder {
147 let resolved = resolution.def_id.and_then(|d| d.resolve(ctx.db).ok()); 147 let resolved = resolution.def_id.map(|d| d.resolve(ctx.db));
148 let kind = match resolved { 148 let kind = match resolved {
149 PerNs { 149 PerNs {
150 types: Some(hir::Def::Module(..)), 150 types: Some(hir::Def::Module(..)),
diff --git a/crates/ra_ide_api/src/extend_selection.rs b/crates/ra_ide_api/src/extend_selection.rs
index c3c809c9f..9f0ab2f1c 100644
--- a/crates/ra_ide_api/src/extend_selection.rs
+++ b/crates/ra_ide_api/src/extend_selection.rs
@@ -38,8 +38,9 @@ fn find_macro_call(node: &SyntaxNode, range: TextRange) -> Option<&ast::MacroCal
38 38
39#[cfg(test)] 39#[cfg(test)]
40mod tests { 40mod tests {
41 use ra_syntax::TextRange;
42
41 use crate::mock_analysis::single_file_with_range; 43 use crate::mock_analysis::single_file_with_range;
42 use test_utils::assert_eq_dbg;
43 44
44 #[test] 45 #[test]
45 fn extend_selection_inside_macros() { 46 fn extend_selection_inside_macros() {
@@ -51,6 +52,6 @@ mod tests {
51 ", 52 ",
52 ); 53 );
53 let r = analysis.extend_selection(frange); 54 let r = analysis.extend_selection(frange);
54 assert_eq_dbg("[51; 56)", &r); 55 assert_eq!(r, TextRange::from_to(51.into(), 56.into()));
55 } 56 }
56} 57}
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index 332a2fb8d..b1becca03 100644
--- a/crates/ra_ide_api/src/goto_definition.rs
+++ b/crates/ra_ide_api/src/goto_definition.rs
@@ -1,4 +1,4 @@
1use ra_db::{FileId, Cancelable, SyntaxDatabase}; 1use ra_db::{FileId, SyntaxDatabase};
2use ra_syntax::{ 2use ra_syntax::{
3 AstNode, ast, 3 AstNode, ast,
4 algo::find_node_at_offset, 4 algo::find_node_at_offset,
@@ -9,21 +9,18 @@ use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo};
9pub(crate) fn goto_definition( 9pub(crate) fn goto_definition(
10 db: &RootDatabase, 10 db: &RootDatabase,
11 position: FilePosition, 11 position: FilePosition,
12) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { 12) -> Option<RangeInfo<Vec<NavigationTarget>>> {
13 let file = db.source_file(position.file_id); 13 let file = db.source_file(position.file_id);
14 let syntax = file.syntax(); 14 let syntax = file.syntax();
15 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { 15 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
16 let navs = reference_definition(db, position.file_id, name_ref)?.to_vec(); 16 let navs = reference_definition(db, position.file_id, name_ref).to_vec();
17 return Ok(Some(RangeInfo::new( 17 return Some(RangeInfo::new(name_ref.syntax().range(), navs.to_vec()));
18 name_ref.syntax().range(),
19 navs.to_vec(),
20 )));
21 } 18 }
22 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) { 19 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) {
23 let navs = ctry!(name_definition(db, position.file_id, name)?); 20 let navs = name_definition(db, position.file_id, name)?;
24 return Ok(Some(RangeInfo::new(name.syntax().range(), navs))); 21 return Some(RangeInfo::new(name.syntax().range(), navs));
25 } 22 }
26 Ok(None) 23 None
27} 24}
28 25
29pub(crate) enum ReferenceResult { 26pub(crate) enum ReferenceResult {
@@ -45,16 +42,16 @@ pub(crate) fn reference_definition(
45 db: &RootDatabase, 42 db: &RootDatabase,
46 file_id: FileId, 43 file_id: FileId,
47 name_ref: &ast::NameRef, 44 name_ref: &ast::NameRef,
48) -> Cancelable<ReferenceResult> { 45) -> ReferenceResult {
49 use self::ReferenceResult::*; 46 use self::ReferenceResult::*;
50 if let Some(function) = 47 if let Some(function) =
51 hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())? 48 hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())
52 { 49 {
53 let scope = function.scopes(db)?; 50 let scope = function.scopes(db);
54 // First try to resolve the symbol locally 51 // First try to resolve the symbol locally
55 if let Some(entry) = scope.resolve_local_name(name_ref) { 52 if let Some(entry) = scope.resolve_local_name(name_ref) {
56 let nav = NavigationTarget::from_scope_entry(file_id, &entry); 53 let nav = NavigationTarget::from_scope_entry(file_id, &entry);
57 return Ok(Exact(nav)); 54 return Exact(nav);
58 }; 55 };
59 56
60 // Next check if it is a method 57 // Next check if it is a method
@@ -63,22 +60,21 @@ pub(crate) fn reference_definition(
63 .parent() 60 .parent()
64 .and_then(ast::MethodCallExpr::cast) 61 .and_then(ast::MethodCallExpr::cast)
65 { 62 {
66 let infer_result = function.infer(db)?; 63 let infer_result = function.infer(db);
67 let syntax_mapping = function.body_syntax_mapping(db)?; 64 let syntax_mapping = function.body_syntax_mapping(db);
68 let expr = ast::Expr::cast(method_call.syntax()).unwrap(); 65 let expr = ast::Expr::cast(method_call.syntax()).unwrap();
69 if let Some(def_id) = syntax_mapping 66 if let Some(def_id) = syntax_mapping
70 .node_expr(expr) 67 .node_expr(expr)
71 .and_then(|it| infer_result.method_resolution(it)) 68 .and_then(|it| infer_result.method_resolution(it))
72 { 69 {
73 if let Some(target) = NavigationTarget::from_def(db, def_id.resolve(db)?)? { 70 if let Some(target) = NavigationTarget::from_def(db, def_id.resolve(db)) {
74 return Ok(Exact(target)); 71 return Exact(target);
75 } 72 }
76 }; 73 };
77 } 74 }
78 } 75 }
79 // Then try module name resolution 76 // Then try module name resolution
80 if let Some(module) = 77 if let Some(module) = hir::source_binder::module_from_child_node(db, file_id, name_ref.syntax())
81 hir::source_binder::module_from_child_node(db, file_id, name_ref.syntax())?
82 { 78 {
83 if let Some(path) = name_ref 79 if let Some(path) = name_ref
84 .syntax() 80 .syntax()
@@ -86,39 +82,39 @@ pub(crate) fn reference_definition(
86 .find_map(ast::Path::cast) 82 .find_map(ast::Path::cast)
87 .and_then(hir::Path::from_ast) 83 .and_then(hir::Path::from_ast)
88 { 84 {
89 let resolved = module.resolve_path(db, &path)?; 85 let resolved = module.resolve_path(db, &path);
90 if let Some(def_id) = resolved.take_types().or(resolved.take_values()) { 86 if let Some(def_id) = resolved.take_types().or(resolved.take_values()) {
91 if let Some(target) = NavigationTarget::from_def(db, def_id.resolve(db)?)? { 87 if let Some(target) = NavigationTarget::from_def(db, def_id.resolve(db)) {
92 return Ok(Exact(target)); 88 return Exact(target);
93 } 89 }
94 } 90 }
95 } 91 }
96 } 92 }
97 // If that fails try the index based approach. 93 // If that fails try the index based approach.
98 let navs = db 94 let navs = db
99 .index_resolve(name_ref)? 95 .index_resolve(name_ref)
100 .into_iter() 96 .into_iter()
101 .map(NavigationTarget::from_symbol) 97 .map(NavigationTarget::from_symbol)
102 .collect(); 98 .collect();
103 Ok(Approximate(navs)) 99 Approximate(navs)
104} 100}
105 101
106fn name_definition( 102fn name_definition(
107 db: &RootDatabase, 103 db: &RootDatabase,
108 file_id: FileId, 104 file_id: FileId,
109 name: &ast::Name, 105 name: &ast::Name,
110) -> Cancelable<Option<Vec<NavigationTarget>>> { 106) -> Option<Vec<NavigationTarget>> {
111 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { 107 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
112 if module.has_semi() { 108 if module.has_semi() {
113 if let Some(child_module) = 109 if let Some(child_module) =
114 hir::source_binder::module_from_declaration(db, file_id, module)? 110 hir::source_binder::module_from_declaration(db, file_id, module)
115 { 111 {
116 let nav = NavigationTarget::from_module(db, child_module)?; 112 let nav = NavigationTarget::from_module(db, child_module);
117 return Ok(Some(vec![nav])); 113 return Some(vec![nav]);
118 } 114 }
119 } 115 }
120 } 116 }
121 Ok(None) 117 None
122} 118}
123 119
124#[cfg(test)] 120#[cfg(test)]
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs
index c445d4fa1..d91151c15 100644
--- a/crates/ra_ide_api/src/hover.rs
+++ b/crates/ra_ide_api/src/hover.rs
@@ -1,25 +1,22 @@
1use ra_db::{Cancelable, SyntaxDatabase}; 1use ra_db::{SyntaxDatabase};
2use ra_syntax::{ 2use ra_syntax::{
3 AstNode, SyntaxNode, TreeArc, 3 AstNode, SyntaxNode, TreeArc,
4 ast::{self, NameOwner}, 4 ast::self,
5 algo::{find_covering_node, find_node_at_offset, find_leaf_at_offset, visit::{visitor, Visitor}}, 5 algo::{find_covering_node, find_node_at_offset, find_leaf_at_offset, visit::{visitor, Visitor}},
6}; 6};
7 7
8use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget}; 8use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget};
9 9
10pub(crate) fn hover( 10pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<String>> {
11 db: &RootDatabase,
12 position: FilePosition,
13) -> Cancelable<Option<RangeInfo<String>>> {
14 let file = db.source_file(position.file_id); 11 let file = db.source_file(position.file_id);
15 let mut res = Vec::new(); 12 let mut res = Vec::new();
16 13
17 let mut range = None; 14 let mut range = None;
18 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) { 15 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) {
19 use crate::goto_definition::{ReferenceResult::*, reference_definition}; 16 use crate::goto_definition::{ReferenceResult::*, reference_definition};
20 let ref_result = reference_definition(db, position.file_id, name_ref)?; 17 let ref_result = reference_definition(db, position.file_id, name_ref);
21 match ref_result { 18 match ref_result {
22 Exact(nav) => res.extend(doc_text_for(db, nav)?), 19 Exact(nav) => res.extend(doc_text_for(db, nav)),
23 Approximate(navs) => { 20 Approximate(navs) => {
24 let mut msg = String::from("Failed to exactly resolve the symbol. This is probably because rust_analyzer does not yet support glob imports or traits."); 21 let mut msg = String::from("Failed to exactly resolve the symbol. This is probably because rust_analyzer does not yet support glob imports or traits.");
25 if !navs.is_empty() { 22 if !navs.is_empty() {
@@ -27,7 +24,7 @@ pub(crate) fn hover(
27 } 24 }
28 res.push(msg); 25 res.push(msg);
29 for nav in navs { 26 for nav in navs {
30 res.extend(doc_text_for(db, nav)?) 27 res.extend(doc_text_for(db, nav))
31 } 28 }
32 } 29 }
33 } 30 }
@@ -39,25 +36,24 @@ pub(crate) fn hover(
39 let node = find_leaf_at_offset(file.syntax(), position.offset).find_map(|leaf| { 36 let node = find_leaf_at_offset(file.syntax(), position.offset).find_map(|leaf| {
40 leaf.ancestors() 37 leaf.ancestors()
41 .find(|n| ast::Expr::cast(*n).is_some() || ast::Pat::cast(*n).is_some()) 38 .find(|n| ast::Expr::cast(*n).is_some() || ast::Pat::cast(*n).is_some())
42 }); 39 })?;
43 let node = ctry!(node);
44 let frange = FileRange { 40 let frange = FileRange {
45 file_id: position.file_id, 41 file_id: position.file_id,
46 range: node.range(), 42 range: node.range(),
47 }; 43 };
48 res.extend(type_of(db, frange)?.map(Into::into)); 44 res.extend(type_of(db, frange).map(Into::into));
49 range = Some(node.range()); 45 range = Some(node.range());
50 }; 46 };
51 47
52 let range = ctry!(range); 48 let range = range?;
53 if res.is_empty() { 49 if res.is_empty() {
54 return Ok(None); 50 return None;
55 } 51 }
56 let res = RangeInfo::new(range, res.join("\n\n---\n")); 52 let res = RangeInfo::new(range, res.join("\n\n---\n"));
57 Ok(Some(res)) 53 Some(res)
58} 54}
59 55
60pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Cancelable<Option<String>> { 56pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {
61 let file = db.source_file(frange.file_id); 57 let file = db.source_file(frange.file_id);
62 let syntax = file.syntax(); 58 let syntax = file.syntax();
63 let leaf_node = find_covering_node(syntax, frange.range); 59 let leaf_node = find_covering_node(syntax, frange.range);
@@ -67,34 +63,28 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Cancelable<Option
67 .take_while(|it| it.range() == leaf_node.range()) 63 .take_while(|it| it.range() == leaf_node.range())
68 .find(|&it| ast::Expr::cast(it).is_some() || ast::Pat::cast(it).is_some()) 64 .find(|&it| ast::Expr::cast(it).is_some() || ast::Pat::cast(it).is_some())
69 .unwrap_or(leaf_node); 65 .unwrap_or(leaf_node);
70 let parent_fn = ctry!(node.ancestors().find_map(ast::FnDef::cast)); 66 let parent_fn = node.ancestors().find_map(ast::FnDef::cast)?;
71 let function = ctry!(hir::source_binder::function_from_source( 67 let function = hir::source_binder::function_from_source(db, frange.file_id, parent_fn)?;
72 db, 68 let infer = function.infer(db);
73 frange.file_id, 69 let syntax_mapping = function.body_syntax_mapping(db);
74 parent_fn
75 )?);
76 let infer = function.infer(db)?;
77 let syntax_mapping = function.body_syntax_mapping(db)?;
78 if let Some(expr) = ast::Expr::cast(node).and_then(|e| syntax_mapping.node_expr(e)) { 70 if let Some(expr) = ast::Expr::cast(node).and_then(|e| syntax_mapping.node_expr(e)) {
79 Ok(Some(infer[expr].to_string())) 71 Some(infer[expr].to_string())
80 } else if let Some(pat) = ast::Pat::cast(node).and_then(|p| syntax_mapping.node_pat(p)) { 72 } else if let Some(pat) = ast::Pat::cast(node).and_then(|p| syntax_mapping.node_pat(p)) {
81 Ok(Some(infer[pat].to_string())) 73 Some(infer[pat].to_string())
82 } else { 74 } else {
83 Ok(None) 75 None
84 } 76 }
85} 77}
86 78
87// FIXME: this should not really use navigation target. Rather, approximatelly 79// FIXME: this should not really use navigation target. Rather, approximatelly
88// resovled symbol should return a `DefId`. 80// resovled symbol should return a `DefId`.
89fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Cancelable<Option<String>> { 81fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Option<String> {
90 let result = match (nav.description(db), nav.docs(db)) { 82 match (nav.description(db), nav.docs(db)) {
91 (Some(desc), Some(docs)) => Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs), 83 (Some(desc), Some(docs)) => Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs),
92 (Some(desc), None) => Some("```rust\n".to_string() + &*desc + "\n```"), 84 (Some(desc), None) => Some("```rust\n".to_string() + &*desc + "\n```"),
93 (None, Some(docs)) => Some(docs), 85 (None, Some(docs)) => Some(docs),
94 _ => None, 86 _ => None,
95 }; 87 }
96
97 Ok(result)
98} 88}
99 89
100impl NavigationTarget { 90impl NavigationTarget {
@@ -137,48 +127,29 @@ impl NavigationTarget {
137 fn description(&self, db: &RootDatabase) -> Option<String> { 127 fn description(&self, db: &RootDatabase) -> Option<String> {
138 // TODO: After type inference is done, add type information to improve the output 128 // TODO: After type inference is done, add type information to improve the output
139 let node = self.node(db)?; 129 let node = self.node(db)?;
140 // TODO: Refactor to be have less repetition 130
131 fn visit_node<T>(node: &T, label: &str) -> Option<String>
132 where
133 T: ast::NameOwner + ast::VisibilityOwner,
134 {
135 let mut string = node
136 .visibility()
137 .map(|v| format!("{} ", v.syntax().text()))
138 .unwrap_or_default();
139 string.push_str(label);
140 node.name()?.syntax().text().push_to(&mut string);
141 Some(string)
142 }
143
141 visitor() 144 visitor()
142 .visit(|node: &ast::FnDef| { 145 .visit(|node: &ast::FnDef| visit_node(node, "fn "))
143 let mut string = "fn ".to_string(); 146 .visit(|node: &ast::StructDef| visit_node(node, "struct "))
144 node.name()?.syntax().text().push_to(&mut string); 147 .visit(|node: &ast::EnumDef| visit_node(node, "enum "))
145 Some(string) 148 .visit(|node: &ast::TraitDef| visit_node(node, "trait "))
146 }) 149 .visit(|node: &ast::Module| visit_node(node, "mod "))
147 .visit(|node: &ast::StructDef| { 150 .visit(|node: &ast::TypeDef| visit_node(node, "type "))
148 let mut string = "struct ".to_string(); 151 .visit(|node: &ast::ConstDef| visit_node(node, "const "))
149 node.name()?.syntax().text().push_to(&mut string); 152 .visit(|node: &ast::StaticDef| visit_node(node, "static "))
150 Some(string)
151 })
152 .visit(|node: &ast::EnumDef| {
153 let mut string = "enum ".to_string();
154 node.name()?.syntax().text().push_to(&mut string);
155 Some(string)
156 })
157 .visit(|node: &ast::TraitDef| {
158 let mut string = "trait ".to_string();
159 node.name()?.syntax().text().push_to(&mut string);
160 Some(string)
161 })
162 .visit(|node: &ast::Module| {
163 let mut string = "mod ".to_string();
164 node.name()?.syntax().text().push_to(&mut string);
165 Some(string)
166 })
167 .visit(|node: &ast::TypeDef| {
168 let mut string = "type ".to_string();
169 node.name()?.syntax().text().push_to(&mut string);
170 Some(string)
171 })
172 .visit(|node: &ast::ConstDef| {
173 let mut string = "const ".to_string();
174 node.name()?.syntax().text().push_to(&mut string);
175 Some(string)
176 })
177 .visit(|node: &ast::StaticDef| {
178 let mut string = "static ".to_string();
179 node.name()?.syntax().text().push_to(&mut string);
180 Some(string)
181 })
182 .accept(&node)? 153 .accept(&node)?
183 } 154 }
184} 155}
@@ -249,20 +220,19 @@ mod tests {
249 assert_eq!("[unknown]", &type_name); 220 assert_eq!("[unknown]", &type_name);
250 } 221 }
251 222
252 // FIXME: improve type_of to make this work
253 #[test] 223 #[test]
254 fn test_type_of_for_expr_2() { 224 fn test_type_of_for_expr_2() {
255 let (analysis, range) = single_file_with_range( 225 let (analysis, range) = single_file_with_range(
256 " 226 "
257 fn main() { 227 fn main() {
258 let foo: usize = 1; 228 let foo: usize = 1;
259 let bar = <|>1 + foo_test<|>; 229 let bar = <|>1 + foo<|>;
260 } 230 }
261 ", 231 ",
262 ); 232 );
263 233
264 let type_name = analysis.type_of(range).unwrap().unwrap(); 234 let type_name = analysis.type_of(range).unwrap().unwrap();
265 assert_eq!("[unknown]", &type_name); 235 assert_eq!("usize", &type_name);
266 } 236 }
267 237
268} 238}
diff --git a/crates/ra_ide_api/src/imp.rs b/crates/ra_ide_api/src/imp.rs
index ba4aa0fd5..61771ed40 100644
--- a/crates/ra_ide_api/src/imp.rs
+++ b/crates/ra_ide_api/src/imp.rs
@@ -15,7 +15,6 @@ use ra_syntax::{
15 15
16use crate::{ 16use crate::{
17 AnalysisChange, 17 AnalysisChange,
18 Cancelable,
19 CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit, 18 CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit,
20 Query, RootChange, SourceChange, SourceFileEdit, 19 Query, RootChange, SourceChange, SourceFileEdit,
21 symbol_index::{LibrarySymbolsQuery, FileSymbol}, 20 symbol_index::{LibrarySymbolsQuery, FileSymbol},
@@ -99,25 +98,22 @@ impl db::RootDatabase {
99 98
100impl db::RootDatabase { 99impl db::RootDatabase {
101 /// Returns `Vec` for the same reason as `parent_module` 100 /// Returns `Vec` for the same reason as `parent_module`
102 pub(crate) fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { 101 pub(crate) fn crate_for(&self, file_id: FileId) -> Vec<CrateId> {
103 let module = match source_binder::module_from_file_id(self, file_id)? { 102 let module = match source_binder::module_from_file_id(self, file_id) {
104 Some(it) => it, 103 Some(it) => it,
105 None => return Ok(Vec::new()), 104 None => return Vec::new(),
106 }; 105 };
107 let krate = match module.krate(self)? { 106 let krate = match module.krate(self) {
108 Some(it) => it, 107 Some(it) => it,
109 None => return Ok(Vec::new()), 108 None => return Vec::new(),
110 }; 109 };
111 Ok(vec![krate.crate_id()]) 110 vec![krate.crate_id()]
112 } 111 }
113 pub(crate) fn find_all_refs( 112 pub(crate) fn find_all_refs(&self, position: FilePosition) -> Vec<(FileId, TextRange)> {
114 &self,
115 position: FilePosition,
116 ) -> Cancelable<Vec<(FileId, TextRange)>> {
117 let file = self.source_file(position.file_id); 113 let file = self.source_file(position.file_id);
118 // Find the binding associated with the offset 114 // Find the binding associated with the offset
119 let (binding, descr) = match find_binding(self, &file, position)? { 115 let (binding, descr) = match find_binding(self, &file, position) {
120 None => return Ok(Vec::new()), 116 None => return Vec::new(),
121 Some(it) => it, 117 Some(it) => it,
122 }; 118 };
123 119
@@ -128,46 +124,40 @@ impl db::RootDatabase {
128 .collect::<Vec<_>>(); 124 .collect::<Vec<_>>();
129 ret.extend( 125 ret.extend(
130 descr 126 descr
131 .scopes(self)? 127 .scopes(self)
132 .find_all_refs(binding) 128 .find_all_refs(binding)
133 .into_iter() 129 .into_iter()
134 .map(|ref_desc| (position.file_id, ref_desc.range)), 130 .map(|ref_desc| (position.file_id, ref_desc.range)),
135 ); 131 );
136 132
137 return Ok(ret); 133 return ret;
138 134
139 fn find_binding<'a>( 135 fn find_binding<'a>(
140 db: &db::RootDatabase, 136 db: &db::RootDatabase,
141 source_file: &'a SourceFile, 137 source_file: &'a SourceFile,
142 position: FilePosition, 138 position: FilePosition,
143 ) -> Cancelable<Option<(&'a ast::BindPat, hir::Function)>> { 139 ) -> Option<(&'a ast::BindPat, hir::Function)> {
144 let syntax = source_file.syntax(); 140 let syntax = source_file.syntax();
145 if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) { 141 if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) {
146 let descr = ctry!(source_binder::function_from_child_node( 142 let descr = source_binder::function_from_child_node(
147 db, 143 db,
148 position.file_id, 144 position.file_id,
149 binding.syntax(), 145 binding.syntax(),
150 )?); 146 )?;
151 return Ok(Some((binding, descr))); 147 return Some((binding, descr));
152 }; 148 };
153 let name_ref = ctry!(find_node_at_offset::<ast::NameRef>(syntax, position.offset)); 149 let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?;
154 let descr = ctry!(source_binder::function_from_child_node( 150 let descr =
155 db, 151 source_binder::function_from_child_node(db, position.file_id, name_ref.syntax())?;
156 position.file_id, 152 let scope = descr.scopes(db);
157 name_ref.syntax(), 153 let resolved = scope.resolve_local_name(name_ref)?;
158 )?);
159 let scope = descr.scopes(db)?;
160 let resolved = ctry!(scope.resolve_local_name(name_ref));
161 let resolved = resolved.ptr().resolve(source_file); 154 let resolved = resolved.ptr().resolve(source_file);
162 let binding = ctry!(find_node_at_offset::<ast::BindPat>( 155 let binding = find_node_at_offset::<ast::BindPat>(syntax, resolved.range().end())?;
163 syntax, 156 Some((binding, descr))
164 resolved.range().end()
165 ));
166 Ok(Some((binding, descr)))
167 } 157 }
168 } 158 }
169 159
170 pub(crate) fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { 160 pub(crate) fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> {
171 let syntax = self.source_file(file_id); 161 let syntax = self.source_file(file_id);
172 162
173 let mut res = ra_ide_api_light::diagnostics(&syntax) 163 let mut res = ra_ide_api_light::diagnostics(&syntax)
@@ -179,8 +169,8 @@ impl db::RootDatabase {
179 fix: d.fix.map(|fix| SourceChange::from_local_edit(file_id, fix)), 169 fix: d.fix.map(|fix| SourceChange::from_local_edit(file_id, fix)),
180 }) 170 })
181 .collect::<Vec<_>>(); 171 .collect::<Vec<_>>();
182 if let Some(m) = source_binder::module_from_file_id(self, file_id)? { 172 if let Some(m) = source_binder::module_from_file_id(self, file_id) {
183 for (name_node, problem) in m.problems(self)? { 173 for (name_node, problem) in m.problems(self) {
184 let source_root = self.file_source_root(file_id); 174 let source_root = self.file_source_root(file_id);
185 let diag = match problem { 175 let diag = match problem {
186 Problem::UnresolvedModule { candidate } => { 176 Problem::UnresolvedModule { candidate } => {
@@ -228,7 +218,7 @@ impl db::RootDatabase {
228 res.push(diag) 218 res.push(diag)
229 } 219 }
230 }; 220 };
231 Ok(res) 221 res
232 } 222 }
233 223
234 pub(crate) fn assists(&self, frange: FileRange) -> Vec<SourceChange> { 224 pub(crate) fn assists(&self, frange: FileRange) -> Vec<SourceChange> {
@@ -239,13 +229,8 @@ impl db::RootDatabase {
239 .collect() 229 .collect()
240 } 230 }
241 231
242 pub(crate) fn rename( 232 pub(crate) fn rename(&self, position: FilePosition, new_name: &str) -> Vec<SourceFileEdit> {
243 &self, 233 self.find_all_refs(position)
244 position: FilePosition,
245 new_name: &str,
246 ) -> Cancelable<Vec<SourceFileEdit>> {
247 let res = self
248 .find_all_refs(position)?
249 .iter() 234 .iter()
250 .map(|(file_id, text_range)| SourceFileEdit { 235 .map(|(file_id, text_range)| SourceFileEdit {
251 file_id: *file_id, 236 file_id: *file_id,
@@ -255,10 +240,9 @@ impl db::RootDatabase {
255 builder.finish() 240 builder.finish()
256 }, 241 },
257 }) 242 })
258 .collect::<Vec<_>>(); 243 .collect::<Vec<_>>()
259 Ok(res)
260 } 244 }
261 pub(crate) fn index_resolve(&self, name_ref: &ast::NameRef) -> Cancelable<Vec<FileSymbol>> { 245 pub(crate) fn index_resolve(&self, name_ref: &ast::NameRef) -> Vec<FileSymbol> {
262 let name = name_ref.text(); 246 let name = name_ref.text();
263 let mut query = Query::new(name.to_string()); 247 let mut query = Query::new(name.to_string());
264 query.exact(); 248 query.exact();
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index abb50ff95..3a0d2dbbe 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -9,15 +9,6 @@
9//! 9//!
10//! The sibling `ra_ide_api_light` handles thouse bits of IDE functionality 10//! The sibling `ra_ide_api_light` handles thouse bits of IDE functionality
11//! which are restricted to a single file and need only syntax. 11//! which are restricted to a single file and need only syntax.
12macro_rules! ctry {
13 ($expr:expr) => {
14 match $expr {
15 None => return Ok(None),
16 Some(it) => it,
17 }
18 };
19}
20
21mod db; 12mod db;
22mod imp; 13mod imp;
23pub mod mock_analysis; 14pub mod mock_analysis;
@@ -58,9 +49,11 @@ pub use ra_ide_api_light::{
58 LineIndex, LineCol, translate_offset_with_edit, 49 LineIndex, LineCol, translate_offset_with_edit,
59}; 50};
60pub use ra_db::{ 51pub use ra_db::{
61 Cancelable, Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId 52 Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId
62}; 53};
63 54
55pub type Cancelable<T> = Result<T, Canceled>;
56
64#[derive(Default)] 57#[derive(Default)]
65pub struct AnalysisChange { 58pub struct AnalysisChange {
66 new_roots: Vec<(SourceRootId, bool)>, 59 new_roots: Vec<(SourceRootId, bool)>,
@@ -381,12 +374,11 @@ impl Analysis {
381 /// Fuzzy searches for a symbol. 374 /// Fuzzy searches for a symbol.
382 pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<NavigationTarget>> { 375 pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<NavigationTarget>> {
383 self.with_db(|db| { 376 self.with_db(|db| {
384 let res = symbol_index::world_symbols(db, query)? 377 symbol_index::world_symbols(db, query)
385 .into_iter() 378 .into_iter()
386 .map(NavigationTarget::from_symbol) 379 .map(NavigationTarget::from_symbol)
387 .collect::<Vec<_>>(); 380 .collect::<Vec<_>>()
388 Ok(res) 381 })
389 })?
390 } 382 }
391 383
392 pub fn goto_definition( 384 pub fn goto_definition(
@@ -394,33 +386,33 @@ impl Analysis {
394 position: FilePosition, 386 position: FilePosition,
395 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { 387 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> {
396 self.db 388 self.db
397 .catch_canceled(|db| goto_definition::goto_definition(db, position))? 389 .catch_canceled(|db| goto_definition::goto_definition(db, position))
398 } 390 }
399 391
400 /// Finds all usages of the reference at point. 392 /// Finds all usages of the reference at point.
401 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { 393 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
402 self.with_db(|db| db.find_all_refs(position))? 394 self.with_db(|db| db.find_all_refs(position))
403 } 395 }
404 396
405 /// Returns a short text descrbing element at position. 397 /// Returns a short text descrbing element at position.
406 pub fn hover(&self, position: FilePosition) -> Cancelable<Option<RangeInfo<String>>> { 398 pub fn hover(&self, position: FilePosition) -> Cancelable<Option<RangeInfo<String>>> {
407 self.with_db(|db| hover::hover(db, position))? 399 self.with_db(|db| hover::hover(db, position))
408 } 400 }
409 401
410 /// Computes parameter information for the given call expression. 402 /// Computes parameter information for the given call expression.
411 pub fn call_info(&self, position: FilePosition) -> Cancelable<Option<CallInfo>> { 403 pub fn call_info(&self, position: FilePosition) -> Cancelable<Option<CallInfo>> {
412 self.db 404 self.db
413 .catch_canceled(|db| call_info::call_info(db, position))? 405 .catch_canceled(|db| call_info::call_info(db, position))
414 } 406 }
415 407
416 /// Returns a `mod name;` declaration which created the current module. 408 /// Returns a `mod name;` declaration which created the current module.
417 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> { 409 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> {
418 self.with_db(|db| parent_module::parent_module(db, position))? 410 self.with_db(|db| parent_module::parent_module(db, position))
419 } 411 }
420 412
421 /// Returns crates this file belongs too. 413 /// Returns crates this file belongs too.
422 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { 414 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
423 self.with_db(|db| db.crate_for(file_id))? 415 self.with_db(|db| db.crate_for(file_id))
424 } 416 }
425 417
426 /// Returns the root file of the given crate. 418 /// Returns the root file of the given crate.
@@ -431,20 +423,20 @@ impl Analysis {
431 /// Returns the set of possible targets to run for the current file. 423 /// Returns the set of possible targets to run for the current file.
432 pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> { 424 pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> {
433 self.db 425 self.db
434 .catch_canceled(|db| runnables::runnables(db, file_id))? 426 .catch_canceled(|db| runnables::runnables(db, file_id))
435 } 427 }
436 428
437 /// Computes syntax highlighting for the given file. 429 /// Computes syntax highlighting for the given file.
438 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 430 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
439 self.db 431 self.db
440 .catch_canceled(|db| syntax_highlighting::highlight(db, file_id))? 432 .catch_canceled(|db| syntax_highlighting::highlight(db, file_id))
441 } 433 }
442 434
443 /// Computes completions at the given position. 435 /// Computes completions at the given position.
444 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { 436 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
445 let completions = self 437 let completions = self
446 .db 438 .db
447 .catch_canceled(|db| completion::completions(db, position))??; 439 .catch_canceled(|db| completion::completions(db, position))?;
448 Ok(completions.map(|it| it.into())) 440 Ok(completions.map(|it| it.into()))
449 } 441 }
450 442
@@ -456,12 +448,12 @@ impl Analysis {
456 448
457 /// Computes the set of diagnostics for the given file. 449 /// Computes the set of diagnostics for the given file.
458 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { 450 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
459 self.with_db(|db| db.diagnostics(file_id))? 451 self.with_db(|db| db.diagnostics(file_id))
460 } 452 }
461 453
462 /// Computes the type of the expression at the given position. 454 /// Computes the type of the expression at the given position.
463 pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> { 455 pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> {
464 self.with_db(|db| hover::type_of(db, frange))? 456 self.with_db(|db| hover::type_of(db, frange))
465 } 457 }
466 458
467 /// Returns the edit required to rename reference at the position to the new 459 /// Returns the edit required to rename reference at the position to the new
@@ -471,7 +463,7 @@ impl Analysis {
471 position: FilePosition, 463 position: FilePosition,
472 new_name: &str, 464 new_name: &str,
473 ) -> Cancelable<Vec<SourceFileEdit>> { 465 ) -> Cancelable<Vec<SourceFileEdit>> {
474 self.with_db(|db| db.rename(position, new_name))? 466 self.with_db(|db| db.rename(position, new_name))
475 } 467 }
476 468
477 fn with_db<F: FnOnce(&db::RootDatabase) -> T + std::panic::UnwindSafe, T>( 469 fn with_db<F: FnOnce(&db::RootDatabase) -> T + std::panic::UnwindSafe, T>(
diff --git a/crates/ra_ide_api/src/navigation_target.rs b/crates/ra_ide_api/src/navigation_target.rs
index 230d0f67a..21c15c0c0 100644
--- a/crates/ra_ide_api/src/navigation_target.rs
+++ b/crates/ra_ide_api/src/navigation_target.rs
@@ -1,4 +1,4 @@
1use ra_db::{FileId, Cancelable}; 1use ra_db::FileId;
2use ra_syntax::{ 2use ra_syntax::{
3 SyntaxNode, AstNode, SmolStr, TextRange, ast, 3 SyntaxNode, AstNode, SmolStr, TextRange, ast,
4 SyntaxKind::{self, NAME}, 4 SyntaxKind::{self, NAME},
@@ -69,84 +69,72 @@ impl NavigationTarget {
69 } 69 }
70 } 70 }
71 71
72 pub(crate) fn from_module( 72 pub(crate) fn from_module(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
73 db: &RootDatabase, 73 let (file_id, source) = module.definition_source(db);
74 module: hir::Module,
75 ) -> Cancelable<NavigationTarget> {
76 let (file_id, source) = module.definition_source(db)?;
77 let name = module 74 let name = module
78 .name(db)? 75 .name(db)
79 .map(|it| it.to_string().into()) 76 .map(|it| it.to_string().into())
80 .unwrap_or_default(); 77 .unwrap_or_default();
81 let res = match source { 78 match source {
82 ModuleSource::SourceFile(node) => { 79 ModuleSource::SourceFile(node) => {
83 NavigationTarget::from_syntax(file_id, name, None, node.syntax()) 80 NavigationTarget::from_syntax(file_id, name, None, node.syntax())
84 } 81 }
85 ModuleSource::Module(node) => { 82 ModuleSource::Module(node) => {
86 NavigationTarget::from_syntax(file_id, name, None, node.syntax()) 83 NavigationTarget::from_syntax(file_id, name, None, node.syntax())
87 } 84 }
88 }; 85 }
89 Ok(res)
90 } 86 }
91 87
92 pub(crate) fn from_module_to_decl( 88 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
93 db: &RootDatabase,
94 module: hir::Module,
95 ) -> Cancelable<NavigationTarget> {
96 let name = module 89 let name = module
97 .name(db)? 90 .name(db)
98 .map(|it| it.to_string().into()) 91 .map(|it| it.to_string().into())
99 .unwrap_or_default(); 92 .unwrap_or_default();
100 if let Some((file_id, source)) = module.declaration_source(db)? { 93 if let Some((file_id, source)) = module.declaration_source(db) {
101 return Ok(NavigationTarget::from_syntax( 94 return NavigationTarget::from_syntax(file_id, name, None, source.syntax());
102 file_id,
103 name,
104 None,
105 source.syntax(),
106 ));
107 } 95 }
108 NavigationTarget::from_module(db, module) 96 NavigationTarget::from_module(db, module)
109 } 97 }
110 98
111 // TODO once Def::Item is gone, this should be able to always return a NavigationTarget 99 // TODO once Def::Item is gone, this should be able to always return a NavigationTarget
112 pub(crate) fn from_def(db: &RootDatabase, def: Def) -> Cancelable<Option<NavigationTarget>> { 100 pub(crate) fn from_def(db: &RootDatabase, def: Def) -> Option<NavigationTarget> {
113 let res = match def { 101 let res = match def {
114 Def::Struct(s) => { 102 Def::Struct(s) => {
115 let (file_id, node) = s.source(db)?; 103 let (file_id, node) = s.source(db);
116 NavigationTarget::from_named(file_id.original_file(db), &*node) 104 NavigationTarget::from_named(file_id.original_file(db), &*node)
117 } 105 }
118 Def::Enum(e) => { 106 Def::Enum(e) => {
119 let (file_id, node) = e.source(db)?; 107 let (file_id, node) = e.source(db);
120 NavigationTarget::from_named(file_id.original_file(db), &*node) 108 NavigationTarget::from_named(file_id.original_file(db), &*node)
121 } 109 }
122 Def::EnumVariant(ev) => { 110 Def::EnumVariant(ev) => {
123 let (file_id, node) = ev.source(db)?; 111 let (file_id, node) = ev.source(db);
124 NavigationTarget::from_named(file_id.original_file(db), &*node) 112 NavigationTarget::from_named(file_id.original_file(db), &*node)
125 } 113 }
126 Def::Function(f) => { 114 Def::Function(f) => {
127 let (file_id, node) = f.source(db)?; 115 let (file_id, node) = f.source(db);
128 NavigationTarget::from_named(file_id.original_file(db), &*node) 116 NavigationTarget::from_named(file_id.original_file(db), &*node)
129 } 117 }
130 Def::Trait(f) => { 118 Def::Trait(f) => {
131 let (file_id, node) = f.source(db)?; 119 let (file_id, node) = f.source(db);
132 NavigationTarget::from_named(file_id.original_file(db), &*node) 120 NavigationTarget::from_named(file_id.original_file(db), &*node)
133 } 121 }
134 Def::Type(f) => { 122 Def::Type(f) => {
135 let (file_id, node) = f.source(db)?; 123 let (file_id, node) = f.source(db);
136 NavigationTarget::from_named(file_id.original_file(db), &*node) 124 NavigationTarget::from_named(file_id.original_file(db), &*node)
137 } 125 }
138 Def::Static(f) => { 126 Def::Static(f) => {
139 let (file_id, node) = f.source(db)?; 127 let (file_id, node) = f.source(db);
140 NavigationTarget::from_named(file_id.original_file(db), &*node) 128 NavigationTarget::from_named(file_id.original_file(db), &*node)
141 } 129 }
142 Def::Const(f) => { 130 Def::Const(f) => {
143 let (file_id, node) = f.source(db)?; 131 let (file_id, node) = f.source(db);
144 NavigationTarget::from_named(file_id.original_file(db), &*node) 132 NavigationTarget::from_named(file_id.original_file(db), &*node)
145 } 133 }
146 Def::Module(m) => NavigationTarget::from_module(db, m)?, 134 Def::Module(m) => NavigationTarget::from_module(db, m),
147 Def::Item => return Ok(None), 135 Def::Item => return None,
148 }; 136 };
149 Ok(Some(res)) 137 Some(res)
150 } 138 }
151 139
152 #[cfg(test)] 140 #[cfg(test)]
diff --git a/crates/ra_ide_api/src/parent_module.rs b/crates/ra_ide_api/src/parent_module.rs
index 675042a6c..e94297fe3 100644
--- a/crates/ra_ide_api/src/parent_module.rs
+++ b/crates/ra_ide_api/src/parent_module.rs
@@ -1,19 +1,16 @@
1use ra_db::{Cancelable, FilePosition}; 1use ra_db::FilePosition;
2 2
3use crate::{NavigationTarget, db::RootDatabase}; 3use crate::{NavigationTarget, db::RootDatabase};
4 4
5/// This returns `Vec` because a module may be included from several places. We 5/// This returns `Vec` because a module may be included from several places. We
6/// don't handle this case yet though, so the Vec has length at most one. 6/// don't handle this case yet though, so the Vec has length at most one.
7pub(crate) fn parent_module( 7pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> {
8 db: &RootDatabase, 8 let module = match hir::source_binder::module_from_position(db, position) {
9 position: FilePosition, 9 None => return Vec::new(),
10) -> Cancelable<Vec<NavigationTarget>> {
11 let module = match hir::source_binder::module_from_position(db, position)? {
12 None => return Ok(Vec::new()),
13 Some(it) => it, 10 Some(it) => it,
14 }; 11 };
15 let nav = NavigationTarget::from_module_to_decl(db, module)?; 12 let nav = NavigationTarget::from_module_to_decl(db, module);
16 Ok(vec![nav]) 13 vec![nav]
17} 14}
18 15
19#[cfg(test)] 16#[cfg(test)]
diff --git a/crates/ra_ide_api/src/runnables.rs b/crates/ra_ide_api/src/runnables.rs
index 53e49da5b..0f9f8deb3 100644
--- a/crates/ra_ide_api/src/runnables.rs
+++ b/crates/ra_ide_api/src/runnables.rs
@@ -3,7 +3,7 @@ use ra_syntax::{
3 TextRange, SyntaxNode, 3 TextRange, SyntaxNode,
4 ast::{self, AstNode, NameOwner, ModuleItemOwner}, 4 ast::{self, AstNode, NameOwner, ModuleItemOwner},
5}; 5};
6use ra_db::{Cancelable, SyntaxDatabase}; 6use ra_db::SyntaxDatabase;
7 7
8use crate::{db::RootDatabase, FileId}; 8use crate::{db::RootDatabase, FileId};
9 9
@@ -21,14 +21,13 @@ pub enum RunnableKind {
21 Bin, 21 Bin,
22} 22}
23 23
24pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<Runnable>> { 24pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
25 let source_file = db.source_file(file_id); 25 let source_file = db.source_file(file_id);
26 let res = source_file 26 source_file
27 .syntax() 27 .syntax()
28 .descendants() 28 .descendants()
29 .filter_map(|i| runnable(db, file_id, i)) 29 .filter_map(|i| runnable(db, file_id, i))
30 .collect(); 30 .collect()
31 Ok(res)
32} 31}
33 32
34fn runnable(db: &RootDatabase, file_id: FileId, item: &SyntaxNode) -> Option<Runnable> { 33fn runnable(db: &RootDatabase, file_id: FileId, item: &SyntaxNode) -> Option<Runnable> {
@@ -75,20 +74,114 @@ fn runnable_mod(db: &RootDatabase, file_id: FileId, module: &ast::Module) -> Opt
75 return None; 74 return None;
76 } 75 }
77 let range = module.syntax().range(); 76 let range = module.syntax().range();
78 let module = 77 let module = hir::source_binder::module_from_child_node(db, file_id, module.syntax())?;
79 hir::source_binder::module_from_child_node(db, file_id, module.syntax()).ok()??;
80 78
81 // FIXME: thread cancellation instead of `.ok`ing 79 // FIXME: thread cancellation instead of `.ok`ing
82 let path = module 80 let path = module
83 .path_to_root(db) 81 .path_to_root(db)
84 .ok()?
85 .into_iter() 82 .into_iter()
86 .rev() 83 .rev()
87 .filter_map(|it| it.name(db).ok()) 84 .filter_map(|it| it.name(db))
88 .filter_map(|it| it)
89 .join("::"); 85 .join("::");
90 Some(Runnable { 86 Some(Runnable {
91 range, 87 range,
92 kind: RunnableKind::TestMod { path }, 88 kind: RunnableKind::TestMod { path },
93 }) 89 })
94} 90}
91
92#[cfg(test)]
93mod tests {
94 use insta::assert_debug_snapshot_matches;
95
96 use crate::mock_analysis::analysis_and_position;
97
98 #[test]
99 fn test_runnables() {
100 let (analysis, pos) = analysis_and_position(
101 r#"
102 //- /lib.rs
103 <|> //empty
104 fn main() {}
105
106 #[test]
107 fn test_foo() {}
108
109 #[test]
110 #[ignore]
111 fn test_foo() {}
112 "#,
113 );
114 let runnables = analysis.runnables(pos.file_id).unwrap();
115 assert_debug_snapshot_matches!("runnables", &runnables)
116 }
117
118 #[test]
119 fn test_runnables_module() {
120 let (analysis, pos) = analysis_and_position(
121 r#"
122 //- /lib.rs
123 <|> //empty
124 mod test_mod {
125 #[test]
126 fn test_foo1() {}
127 }
128 "#,
129 );
130 let runnables = analysis.runnables(pos.file_id).unwrap();
131 assert_debug_snapshot_matches!("runnables_module", &runnables)
132 }
133
134 #[test]
135 fn test_runnables_one_depth_layer_module() {
136 let (analysis, pos) = analysis_and_position(
137 r#"
138 //- /lib.rs
139 <|> //empty
140 mod foo {
141 mod test_mod {
142 #[test]
143 fn test_foo1() {}
144 }
145 }
146 "#,
147 );
148 let runnables = analysis.runnables(pos.file_id).unwrap();
149 assert_debug_snapshot_matches!("runnables_one_depth_layer_module", &runnables)
150 }
151
152 #[test]
153 fn test_runnables_multiple_depth_module() {
154 let (analysis, pos) = analysis_and_position(
155 r#"
156 //- /lib.rs
157 <|> //empty
158 mod foo {
159 mod bar {
160 mod test_mod {
161 #[test]
162 fn test_foo1() {}
163 }
164 }
165 }
166 "#,
167 );
168 let runnables = analysis.runnables(pos.file_id).unwrap();
169 assert_debug_snapshot_matches!("runnables_multiple_depth_module", &runnables)
170 }
171
172 #[test]
173 fn test_runnables_no_test_function_in_module() {
174 let (analysis, pos) = analysis_and_position(
175 r#"
176 //- /lib.rs
177 <|> //empty
178 mod test_mod {
179 fn foo1() {}
180 }
181 "#,
182 );
183 let runnables = analysis.runnables(pos.file_id).unwrap();
184 assert!(runnables.is_empty())
185 }
186
187}
diff --git a/crates/ra_ide_api/src/snapshots/tests__highlight_query_group_macro.snap b/crates/ra_ide_api/src/snapshots/tests__highlight_query_group_macro.snap
new file mode 100644
index 000000000..b84aa9c78
--- /dev/null
+++ b/crates/ra_ide_api/src/snapshots/tests__highlight_query_group_macro.snap
@@ -0,0 +1,26 @@
1Created: 2019-01-15T11:15:20.732493641+00:00
2Creator: [email protected]
3Source: crates/ra_ide_api/src/syntax_highlighting.rs
4
5[
6 HighlightedRange {
7 range: [20; 32),
8 tag: "macro"
9 },
10 HighlightedRange {
11 range: [13; 18),
12 tag: "text"
13 },
14 HighlightedRange {
15 range: [51; 54),
16 tag: "keyword"
17 },
18 HighlightedRange {
19 range: [55; 60),
20 tag: "keyword"
21 },
22 HighlightedRange {
23 range: [61; 72),
24 tag: "function"
25 }
26]
diff --git a/crates/ra_ide_api/src/snapshots/tests__highlights_code_inside_macros.snap b/crates/ra_ide_api/src/snapshots/tests__highlights_code_inside_macros.snap
new file mode 100644
index 000000000..14c6e5a4e
--- /dev/null
+++ b/crates/ra_ide_api/src/snapshots/tests__highlights_code_inside_macros.snap
@@ -0,0 +1,70 @@
1Created: 2019-01-15T11:15:20.732523231+00:00
2Creator: [email protected]
3Source: crates/ra_ide_api/src/syntax_highlighting.rs
4
5[
6 HighlightedRange {
7 range: [13; 15),
8 tag: "keyword"
9 },
10 HighlightedRange {
11 range: [16; 20),
12 tag: "function"
13 },
14 HighlightedRange {
15 range: [41; 46),
16 tag: "macro"
17 },
18 HighlightedRange {
19 range: [49; 52),
20 tag: "keyword"
21 },
22 HighlightedRange {
23 range: [57; 59),
24 tag: "literal"
25 },
26 HighlightedRange {
27 range: [82; 86),
28 tag: "macro"
29 },
30 HighlightedRange {
31 range: [89; 92),
32 tag: "keyword"
33 },
34 HighlightedRange {
35 range: [97; 99),
36 tag: "literal"
37 },
38 HighlightedRange {
39 range: [49; 52),
40 tag: "keyword"
41 },
42 HighlightedRange {
43 range: [53; 54),
44 tag: "function"
45 },
46 HighlightedRange {
47 range: [57; 59),
48 tag: "literal"
49 },
50 HighlightedRange {
51 range: [61; 62),
52 tag: "text"
53 },
54 HighlightedRange {
55 range: [89; 92),
56 tag: "keyword"
57 },
58 HighlightedRange {
59 range: [93; 94),
60 tag: "function"
61 },
62 HighlightedRange {
63 range: [97; 99),
64 tag: "literal"
65 },
66 HighlightedRange {
67 range: [101; 102),
68 tag: "text"
69 }
70]
diff --git a/crates/ra_ide_api/src/snapshots/tests__runnables.snap b/crates/ra_ide_api/src/snapshots/tests__runnables.snap
new file mode 100644
index 000000000..ba6cba0ab
--- /dev/null
+++ b/crates/ra_ide_api/src/snapshots/tests__runnables.snap
@@ -0,0 +1,22 @@
1Created: 2019-01-15T11:15:20.732460119+00:00
2Creator: [email protected]
3Source: crates/ra_ide_api/src/runnables.rs
4
5[
6 Runnable {
7 range: [1; 21),
8 kind: Bin
9 },
10 Runnable {
11 range: [22; 46),
12 kind: Test {
13 name: "test_foo"
14 }
15 },
16 Runnable {
17 range: [47; 81),
18 kind: Test {
19 name: "test_foo"
20 }
21 }
22]
diff --git a/crates/ra_ide_api/src/snapshots/tests__runnables_module.snap b/crates/ra_ide_api/src/snapshots/tests__runnables_module.snap
new file mode 100644
index 000000000..b3f2d4d6e
--- /dev/null
+++ b/crates/ra_ide_api/src/snapshots/tests__runnables_module.snap
@@ -0,0 +1,18 @@
1Created: 2019-01-15T11:15:20.732460109+00:00
2Creator: [email protected]
3Source: crates/ra_ide_api/src/runnables.rs
4
5[
6 Runnable {
7 range: [1; 59),
8 kind: TestMod {
9 path: "test_mod"
10 }
11 },
12 Runnable {
13 range: [28; 57),
14 kind: Test {
15 name: "test_foo1"
16 }
17 }
18]
diff --git a/crates/ra_ide_api/src/snapshots/tests__runnables_multiple_depth_module.snap b/crates/ra_ide_api/src/snapshots/tests__runnables_multiple_depth_module.snap
new file mode 100644
index 000000000..6eba482e7
--- /dev/null
+++ b/crates/ra_ide_api/src/snapshots/tests__runnables_multiple_depth_module.snap
@@ -0,0 +1,18 @@
1Created: 2019-01-15T11:15:20.732522773+00:00
2Creator: [email protected]
3Source: crates/ra_ide_api/src/runnables.rs
4
5[
6 Runnable {
7 range: [41; 115),
8 kind: TestMod {
9 path: "foo::bar::test_mod"
10 }
11 },
12 Runnable {
13 range: [68; 105),
14 kind: Test {
15 name: "test_foo1"
16 }
17 }
18]
diff --git a/crates/ra_ide_api/src/snapshots/tests__runnables_one_depth_layer_module.snap b/crates/ra_ide_api/src/snapshots/tests__runnables_one_depth_layer_module.snap
new file mode 100644
index 000000000..f40c762f3
--- /dev/null
+++ b/crates/ra_ide_api/src/snapshots/tests__runnables_one_depth_layer_module.snap
@@ -0,0 +1,18 @@
1Created: 2019-01-15T11:15:20.732480089+00:00
2Creator: [email protected]
3Source: crates/ra_ide_api/src/runnables.rs
4
5[
6 Runnable {
7 range: [23; 85),
8 kind: TestMod {
9 path: "foo::test_mod"
10 }
11 },
12 Runnable {
13 range: [46; 79),
14 kind: Test {
15 name: "test_foo1"
16 }
17 }
18]
diff --git a/crates/ra_ide_api/src/symbol_index.rs b/crates/ra_ide_api/src/symbol_index.rs
index fdda57022..74165d68f 100644
--- a/crates/ra_ide_api/src/symbol_index.rs
+++ b/crates/ra_ide_api/src/symbol_index.rs
@@ -37,13 +37,13 @@ use salsa::ParallelDatabase;
37use rayon::prelude::*; 37use rayon::prelude::*;
38 38
39use crate::{ 39use crate::{
40 Cancelable, FileId, Query, 40 FileId, Query,
41 db::RootDatabase, 41 db::RootDatabase,
42}; 42};
43 43
44salsa::query_group! { 44salsa::query_group! {
45 pub(crate) trait SymbolsDatabase: hir::db::HirDatabase { 45 pub(crate) trait SymbolsDatabase: hir::db::HirDatabase {
46 fn file_symbols(file_id: FileId) -> Cancelable<Arc<SymbolIndex>> { 46 fn file_symbols(file_id: FileId) -> Arc<SymbolIndex> {
47 type FileSymbolsQuery; 47 type FileSymbolsQuery;
48 } 48 }
49 fn library_symbols(id: SourceRootId) -> Arc<SymbolIndex> { 49 fn library_symbols(id: SourceRootId) -> Arc<SymbolIndex> {
@@ -53,8 +53,8 @@ salsa::query_group! {
53 } 53 }
54} 54}
55 55
56fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Cancelable<Arc<SymbolIndex>> { 56fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> {
57 db.check_canceled()?; 57 db.check_canceled();
58 let source_file = db.source_file(file_id); 58 let source_file = db.source_file(file_id);
59 let mut symbols = source_file 59 let mut symbols = source_file
60 .syntax() 60 .syntax()
@@ -63,16 +63,16 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Cancelable<Arc<Sy
63 .map(move |(name, ptr)| FileSymbol { name, ptr, file_id }) 63 .map(move |(name, ptr)| FileSymbol { name, ptr, file_id })
64 .collect::<Vec<_>>(); 64 .collect::<Vec<_>>();
65 65
66 for (name, text_range) in hir::source_binder::macro_symbols(db, file_id)? { 66 for (name, text_range) in hir::source_binder::macro_symbols(db, file_id) {
67 let node = find_covering_node(source_file.syntax(), text_range); 67 let node = find_covering_node(source_file.syntax(), text_range);
68 let ptr = LocalSyntaxPtr::new(node); 68 let ptr = LocalSyntaxPtr::new(node);
69 symbols.push(FileSymbol { file_id, name, ptr }) 69 symbols.push(FileSymbol { file_id, name, ptr })
70 } 70 }
71 71
72 Ok(Arc::new(SymbolIndex::new(symbols))) 72 Arc::new(SymbolIndex::new(symbols))
73} 73}
74 74
75pub(crate) fn world_symbols(db: &RootDatabase, query: Query) -> Cancelable<Vec<FileSymbol>> { 75pub(crate) fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
76 /// Need to wrap Snapshot to provide `Clone` impl for `map_with` 76 /// Need to wrap Snapshot to provide `Clone` impl for `map_with`
77 struct Snap(salsa::Snapshot<RootDatabase>); 77 struct Snap(salsa::Snapshot<RootDatabase>);
78 impl Clone for Snap { 78 impl Clone for Snap {
@@ -98,10 +98,9 @@ pub(crate) fn world_symbols(db: &RootDatabase, query: Query) -> Cancelable<Vec<F
98 files 98 files
99 .par_iter() 99 .par_iter()
100 .map_with(snap, |db, &file_id| db.0.file_symbols(file_id)) 100 .map_with(snap, |db, &file_id| db.0.file_symbols(file_id))
101 .filter_map(|it| it.ok())
102 .collect() 101 .collect()
103 }; 102 };
104 Ok(query.search(&buf)) 103 query.search(&buf)
105} 104}
106 105
107#[derive(Default, Debug)] 106#[derive(Default, Debug)]
diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs
index cb19e9515..a4d3ad005 100644
--- a/crates/ra_ide_api/src/syntax_highlighting.rs
+++ b/crates/ra_ide_api/src/syntax_highlighting.rs
@@ -2,11 +2,11 @@ use ra_syntax::{ast, AstNode,};
2use ra_db::SyntaxDatabase; 2use ra_db::SyntaxDatabase;
3 3
4use crate::{ 4use crate::{
5 FileId, Cancelable, HighlightedRange, 5 FileId, HighlightedRange,
6 db::RootDatabase, 6 db::RootDatabase,
7}; 7};
8 8
9pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 9pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> {
10 let source_file = db.source_file(file_id); 10 let source_file = db.source_file(file_id);
11 let mut res = ra_ide_api_light::highlight(source_file.syntax()); 11 let mut res = ra_ide_api_light::highlight(source_file.syntax());
12 for macro_call in source_file 12 for macro_call in source_file
@@ -28,13 +28,14 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<Hi
28 res.extend(mapped_ranges); 28 res.extend(mapped_ranges);
29 } 29 }
30 } 30 }
31 Ok(res) 31 res
32} 32}
33 33
34#[cfg(test)] 34#[cfg(test)]
35mod tests { 35mod tests {
36 use crate::mock_analysis::single_file; 36 use crate::mock_analysis::single_file;
37 use test_utils::assert_eq_dbg; 37
38 use insta::assert_debug_snapshot_matches;
38 39
39 #[test] 40 #[test]
40 fn highlights_code_inside_macros() { 41 fn highlights_code_inside_macros() {
@@ -47,25 +48,7 @@ mod tests {
47 ", 48 ",
48 ); 49 );
49 let highlights = analysis.highlight(file_id).unwrap(); 50 let highlights = analysis.highlight(file_id).unwrap();
50 assert_eq_dbg( 51 assert_debug_snapshot_matches!("highlights_code_inside_macros", &highlights);
51 r#"[HighlightedRange { range: [13; 15), tag: "keyword" },
52 HighlightedRange { range: [16; 20), tag: "function" },
53 HighlightedRange { range: [41; 46), tag: "macro" },
54 HighlightedRange { range: [49; 52), tag: "keyword" },
55 HighlightedRange { range: [57; 59), tag: "literal" },
56 HighlightedRange { range: [82; 86), tag: "macro" },
57 HighlightedRange { range: [89; 92), tag: "keyword" },
58 HighlightedRange { range: [97; 99), tag: "literal" },
59 HighlightedRange { range: [49; 52), tag: "keyword" },
60 HighlightedRange { range: [53; 54), tag: "function" },
61 HighlightedRange { range: [57; 59), tag: "literal" },
62 HighlightedRange { range: [61; 62), tag: "text" },
63 HighlightedRange { range: [89; 92), tag: "keyword" },
64 HighlightedRange { range: [93; 94), tag: "function" },
65 HighlightedRange { range: [97; 99), tag: "literal" },
66 HighlightedRange { range: [101; 102), tag: "text" }]"#,
67 &highlights,
68 )
69 } 52 }
70 53
71 // FIXME: this test is not really necessary: artifact of the inital hacky 54 // FIXME: this test is not really necessary: artifact of the inital hacky
@@ -80,13 +63,6 @@ mod tests {
80 ", 63 ",
81 ); 64 );
82 let highlights = analysis.highlight(file_id).unwrap(); 65 let highlights = analysis.highlight(file_id).unwrap();
83 assert_eq_dbg( 66 assert_debug_snapshot_matches!("highlight_query_group_macro", &highlights);
84 r#"[HighlightedRange { range: [20; 32), tag: "macro" },
85 HighlightedRange { range: [13; 18), tag: "text" },
86 HighlightedRange { range: [51; 54), tag: "keyword" },
87 HighlightedRange { range: [55; 60), tag: "keyword" },
88 HighlightedRange { range: [61; 72), tag: "function" }]"#,
89 &highlights,
90 )
91 } 67 }
92} 68}
diff --git a/crates/ra_ide_api/tests/test/main.rs b/crates/ra_ide_api/tests/test/main.rs
index 7dc1dba73..2b863aedf 100644
--- a/crates/ra_ide_api/tests/test/main.rs
+++ b/crates/ra_ide_api/tests/test/main.rs
@@ -1,7 +1,6 @@
1mod runnables;
2
3use ra_syntax::TextRange; 1use ra_syntax::TextRange;
4use test_utils::{assert_eq_dbg, assert_eq_text}; 2use test_utils::assert_eq_text;
3use insta::assert_debug_snapshot_matches;
5 4
6use ra_ide_api::{ 5use ra_ide_api::{
7 mock_analysis::{single_file, single_file_with_position, MockAnalysis}, 6 mock_analysis::{single_file, single_file_with_position, MockAnalysis},
@@ -12,18 +11,7 @@ use ra_ide_api::{
12fn test_unresolved_module_diagnostic() { 11fn test_unresolved_module_diagnostic() {
13 let (analysis, file_id) = single_file("mod foo;"); 12 let (analysis, file_id) = single_file("mod foo;");
14 let diagnostics = analysis.diagnostics(file_id).unwrap(); 13 let diagnostics = analysis.diagnostics(file_id).unwrap();
15 assert_eq_dbg( 14 assert_debug_snapshot_matches!("unresolved_module_diagnostic", &diagnostics);
16 r#"[Diagnostic {
17 message: "unresolved module",
18 range: [4; 7),
19 fix: Some(SourceChange {
20 label: "create module",
21 source_file_edits: [],
22 file_system_edits: [CreateFile { source_root: SourceRootId(0), path: "foo.rs" }],
23 cursor_position: None }),
24 severity: Error }]"#,
25 &diagnostics,
26 );
27} 15}
28 16
29// FIXME: move this test to hir 17// FIXME: move this test to hir
@@ -31,7 +19,7 @@ fn test_unresolved_module_diagnostic() {
31fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() { 19fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() {
32 let (analysis, file_id) = single_file("mod foo {}"); 20 let (analysis, file_id) = single_file("mod foo {}");
33 let diagnostics = analysis.diagnostics(file_id).unwrap(); 21 let diagnostics = analysis.diagnostics(file_id).unwrap();
34 assert_eq_dbg(r#"[]"#, &diagnostics); 22 assert!(diagnostics.is_empty());
35} 23}
36 24
37#[test] 25#[test]
diff --git a/crates/ra_ide_api/tests/test/runnables.rs b/crates/ra_ide_api/tests/test/runnables.rs
deleted file mode 100644
index da8d5e0d5..000000000
--- a/crates/ra_ide_api/tests/test/runnables.rs
+++ /dev/null
@@ -1,109 +0,0 @@
1use test_utils::assert_eq_dbg;
2
3use ra_ide_api::mock_analysis::analysis_and_position;
4
5#[test]
6fn test_runnables() {
7 let (analysis, pos) = analysis_and_position(
8 r#"
9 //- /lib.rs
10 <|> //empty
11 fn main() {}
12
13 #[test]
14 fn test_foo() {}
15
16 #[test]
17 #[ignore]
18 fn test_foo() {}
19 "#,
20 );
21 let runnables = analysis.runnables(pos.file_id).unwrap();
22 assert_eq_dbg(
23 r#"[Runnable { range: [1; 21), kind: Bin },
24 Runnable { range: [22; 46), kind: Test { name: "test_foo" } },
25 Runnable { range: [47; 81), kind: Test { name: "test_foo" } }]"#,
26 &runnables,
27 )
28}
29
30#[test]
31fn test_runnables_module() {
32 let (analysis, pos) = analysis_and_position(
33 r#"
34 //- /lib.rs
35 <|> //empty
36 mod test_mod {
37 #[test]
38 fn test_foo1() {}
39 }
40 "#,
41 );
42 let runnables = analysis.runnables(pos.file_id).unwrap();
43 assert_eq_dbg(
44 r#"[Runnable { range: [1; 59), kind: TestMod { path: "test_mod" } },
45 Runnable { range: [28; 57), kind: Test { name: "test_foo1" } }]"#,
46 &runnables,
47 )
48}
49
50#[test]
51fn test_runnables_one_depth_layer_module() {
52 let (analysis, pos) = analysis_and_position(
53 r#"
54 //- /lib.rs
55 <|> //empty
56 mod foo {
57 mod test_mod {
58 #[test]
59 fn test_foo1() {}
60 }
61 }
62 "#,
63 );
64 let runnables = analysis.runnables(pos.file_id).unwrap();
65 assert_eq_dbg(
66 r#"[Runnable { range: [23; 85), kind: TestMod { path: "foo::test_mod" } },
67 Runnable { range: [46; 79), kind: Test { name: "test_foo1" } }]"#,
68 &runnables,
69 )
70}
71
72#[test]
73fn test_runnables_multiple_depth_module() {
74 let (analysis, pos) = analysis_and_position(
75 r#"
76 //- /lib.rs
77 <|> //empty
78 mod foo {
79 mod bar {
80 mod test_mod {
81 #[test]
82 fn test_foo1() {}
83 }
84 }
85 }
86 "#,
87 );
88 let runnables = analysis.runnables(pos.file_id).unwrap();
89 assert_eq_dbg(
90 r#"[Runnable { range: [41; 115), kind: TestMod { path: "foo::bar::test_mod" } },
91 Runnable { range: [68; 105), kind: Test { name: "test_foo1" } }]"#,
92 &runnables,
93 )
94}
95
96#[test]
97fn test_runnables_no_test_function_in_module() {
98 let (analysis, pos) = analysis_and_position(
99 r#"
100 //- /lib.rs
101 <|> //empty
102 mod test_mod {
103 fn foo1() {}
104 }
105 "#,
106 );
107 let runnables = analysis.runnables(pos.file_id).unwrap();
108 assert_eq_dbg(r#"[]"#, &runnables)
109}
diff --git a/crates/ra_ide_api/tests/test/snapshots/test__unresolved_module_diagnostic.snap b/crates/ra_ide_api/tests/test/snapshots/test__unresolved_module_diagnostic.snap
new file mode 100644
index 000000000..1b41e2b00
--- /dev/null
+++ b/crates/ra_ide_api/tests/test/snapshots/test__unresolved_module_diagnostic.snap
@@ -0,0 +1,26 @@
1Created: 2019-01-15T11:15:20.891129945+00:00
2Creator: [email protected]
3Source: crates/ra_ide_api/tests/test/main.rs
4
5[
6 Diagnostic {
7 message: "unresolved module",
8 range: [4; 7),
9 fix: Some(
10 SourceChange {
11 label: "create module",
12 source_file_edits: [],
13 file_system_edits: [
14 CreateFile {
15 source_root: SourceRootId(
16 0
17 ),
18 path: "foo.rs"
19 }
20 ],
21 cursor_position: None
22 }
23 ),
24 severity: Error
25 }
26]