diff options
author | Seivan Heidari <[email protected]> | 2019-12-23 14:35:31 +0000 |
---|---|---|
committer | Seivan Heidari <[email protected]> | 2019-12-23 14:35:31 +0000 |
commit | b21d9337d9200e2cfdc90b386591c72c302dc03e (patch) | |
tree | f81f5c08f821115cee26fa4d3ceaae88c7807fd5 /crates/ra_ide/src | |
parent | 18a0937585b836ec5ed054b9ae48e0156ab6d9ef (diff) | |
parent | ce07a2daa9e53aa86a769f8641b14c2878444fbc (diff) |
Merge branch 'master' into feature/themes
Diffstat (limited to 'crates/ra_ide/src')
31 files changed, 1003 insertions, 409 deletions
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs index d559dc4d0..2c2b6fa48 100644 --- a/crates/ra_ide/src/call_info.rs +++ b/crates/ra_ide/src/call_info.rs | |||
@@ -1,24 +1,26 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_db::SourceDatabase; | 3 | use hir::db::AstDatabase; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | algo::ancestors_at_offset, | ||
6 | ast::{self, ArgListOwner}, | 5 | ast::{self, ArgListOwner}, |
7 | match_ast, AstNode, SyntaxNode, TextUnit, | 6 | match_ast, AstNode, SyntaxNode, |
8 | }; | 7 | }; |
9 | use test_utils::tested_by; | 8 | use test_utils::tested_by; |
10 | 9 | ||
11 | use crate::{db::RootDatabase, CallInfo, FilePosition, FunctionSignature}; | 10 | use crate::{ |
11 | db::RootDatabase, expand::descend_into_macros, CallInfo, FilePosition, FunctionSignature, | ||
12 | }; | ||
12 | 13 | ||
13 | /// Computes parameter information for the given call expression. | 14 | /// Computes parameter information for the given call expression. |
14 | pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> { | 15 | pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> { |
15 | let parse = db.parse(position.file_id); | 16 | let file = db.parse_or_expand(position.file_id.into())?; |
16 | let syntax = parse.tree().syntax().clone(); | 17 | let token = file.token_at_offset(position.offset).next()?; |
18 | let token = descend_into_macros(db, position.file_id, token); | ||
17 | 19 | ||
18 | // Find the calling expression and it's NameRef | 20 | // Find the calling expression and it's NameRef |
19 | let calling_node = FnCallNode::with_node(&syntax, position.offset)?; | 21 | let calling_node = FnCallNode::with_node(&token.value.parent())?; |
20 | let name_ref = calling_node.name_ref()?; | 22 | let name_ref = calling_node.name_ref()?; |
21 | let name_ref = hir::Source::new(position.file_id.into(), name_ref.syntax()); | 23 | let name_ref = token.with_value(name_ref.syntax()); |
22 | 24 | ||
23 | let analyzer = hir::SourceAnalyzer::new(db, name_ref, None); | 25 | let analyzer = hir::SourceAnalyzer::new(db, name_ref, None); |
24 | let (mut call_info, has_self) = match &calling_node { | 26 | let (mut call_info, has_self) = match &calling_node { |
@@ -93,8 +95,8 @@ enum FnCallNode { | |||
93 | } | 95 | } |
94 | 96 | ||
95 | impl FnCallNode { | 97 | impl FnCallNode { |
96 | fn with_node(syntax: &SyntaxNode, offset: TextUnit) -> Option<FnCallNode> { | 98 | fn with_node(syntax: &SyntaxNode) -> Option<FnCallNode> { |
97 | ancestors_at_offset(syntax, offset).find_map(|node| { | 99 | syntax.ancestors().find_map(|node| { |
98 | match_ast! { | 100 | match_ast! { |
99 | match node { | 101 | match node { |
100 | ast::CallExpr(it) => { Some(FnCallNode::CallExpr(it)) }, | 102 | ast::CallExpr(it) => { Some(FnCallNode::CallExpr(it)) }, |
@@ -589,4 +591,25 @@ fn f() { | |||
589 | assert_eq!(info.label(), "foo!()"); | 591 | assert_eq!(info.label(), "foo!()"); |
590 | assert_eq!(info.doc().map(|it| it.into()), Some("empty macro".to_string())); | 592 | assert_eq!(info.doc().map(|it| it.into()), Some("empty macro".to_string())); |
591 | } | 593 | } |
594 | |||
595 | #[test] | ||
596 | fn fn_signature_for_call_in_macro() { | ||
597 | let info = call_info( | ||
598 | r#" | ||
599 | macro_rules! id { | ||
600 | ($($tt:tt)*) => { $($tt)* } | ||
601 | } | ||
602 | fn foo() { | ||
603 | |||
604 | } | ||
605 | id! { | ||
606 | fn bar() { | ||
607 | foo(<|>); | ||
608 | } | ||
609 | } | ||
610 | "#, | ||
611 | ); | ||
612 | |||
613 | assert_eq!(info.label(), "fn foo()"); | ||
614 | } | ||
592 | } | 615 | } |
diff --git a/crates/ra_ide/src/change.rs b/crates/ra_ide/src/change.rs index 4a76d1dd8..387a9cafb 100644 --- a/crates/ra_ide/src/change.rs +++ b/crates/ra_ide/src/change.rs | |||
@@ -270,7 +270,6 @@ impl RootDatabase { | |||
270 | 270 | ||
271 | self.query(hir::db::AstIdMapQuery).sweep(sweep); | 271 | self.query(hir::db::AstIdMapQuery).sweep(sweep); |
272 | 272 | ||
273 | self.query(hir::db::RawItemsWithSourceMapQuery).sweep(sweep); | ||
274 | self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep); | 273 | self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep); |
275 | 274 | ||
276 | self.query(hir::db::ExprScopesQuery).sweep(sweep); | 275 | self.query(hir::db::ExprScopesQuery).sweep(sweep); |
@@ -309,7 +308,6 @@ impl RootDatabase { | |||
309 | hir::db::StructDataQuery | 308 | hir::db::StructDataQuery |
310 | hir::db::EnumDataQuery | 309 | hir::db::EnumDataQuery |
311 | hir::db::TraitDataQuery | 310 | hir::db::TraitDataQuery |
312 | hir::db::RawItemsWithSourceMapQuery | ||
313 | hir::db::RawItemsQuery | 311 | hir::db::RawItemsQuery |
314 | hir::db::CrateDefMapQuery | 312 | hir::db::CrateDefMapQuery |
315 | hir::db::GenericParamsQuery | 313 | hir::db::GenericParamsQuery |
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index b6fe48627..294964887 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -27,7 +27,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | |||
27 | complete_methods(acc, ctx, &receiver_ty); | 27 | complete_methods(acc, ctx, &receiver_ty); |
28 | 28 | ||
29 | // Suggest .await syntax for types that implement Future trait | 29 | // Suggest .await syntax for types that implement Future trait |
30 | if ctx.analyzer.impls_future(ctx.db, receiver_ty.into_ty()) { | 30 | if ctx.analyzer.impls_future(ctx.db, receiver_ty) { |
31 | CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await") | 31 | CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await") |
32 | .detail("expr.await") | 32 | .detail("expr.await") |
33 | .insert_text("await") | 33 | .insert_text("await") |
@@ -217,6 +217,39 @@ mod tests { | |||
217 | } | 217 | } |
218 | 218 | ||
219 | #[test] | 219 | #[test] |
220 | fn test_method_completion_only_fitting_impls() { | ||
221 | assert_debug_snapshot!( | ||
222 | do_ref_completion( | ||
223 | r" | ||
224 | struct A<T> {} | ||
225 | impl A<u32> { | ||
226 | fn the_method(&self) {} | ||
227 | } | ||
228 | impl A<i32> { | ||
229 | fn the_other_method(&self) {} | ||
230 | } | ||
231 | fn foo(a: A<u32>) { | ||
232 | a.<|> | ||
233 | } | ||
234 | ", | ||
235 | ), | ||
236 | @r###" | ||
237 | [ | ||
238 | CompletionItem { | ||
239 | label: "the_method()", | ||
240 | source_range: [243; 243), | ||
241 | delete: [243; 243), | ||
242 | insert: "the_method()$0", | ||
243 | kind: Method, | ||
244 | lookup: "the_method", | ||
245 | detail: "fn the_method(&self)", | ||
246 | }, | ||
247 | ] | ||
248 | "### | ||
249 | ); | ||
250 | } | ||
251 | |||
252 | #[test] | ||
220 | fn test_trait_method_completion() { | 253 | fn test_trait_method_completion() { |
221 | assert_debug_snapshot!( | 254 | assert_debug_snapshot!( |
222 | do_ref_completion( | 255 | do_ref_completion( |
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs index 89e0009a1..cc1f7c830 100644 --- a/crates/ra_ide/src/completion/complete_path.rs +++ b/crates/ra_ide/src/completion/complete_path.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{Adt, Either, HasSource, PathResolution}; | 3 | use hir::{Adt, PathResolution, ScopeDef}; |
4 | use ra_syntax::AstNode; | 4 | use ra_syntax::AstNode; |
5 | use test_utils::tested_by; | 5 | use test_utils::tested_by; |
6 | 6 | ||
@@ -18,17 +18,15 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
18 | match def { | 18 | match def { |
19 | hir::ModuleDef::Module(module) => { | 19 | hir::ModuleDef::Module(module) => { |
20 | let module_scope = module.scope(ctx.db); | 20 | let module_scope = module.scope(ctx.db); |
21 | for (name, def, import) in module_scope { | 21 | for (name, def) in module_scope { |
22 | if let hir::ScopeDef::ModuleDef(hir::ModuleDef::BuiltinType(..)) = def { | 22 | if ctx.use_item_syntax.is_some() { |
23 | if ctx.use_item_syntax.is_some() { | 23 | if let hir::ScopeDef::ModuleDef(hir::ModuleDef::BuiltinType(..)) = def { |
24 | tested_by!(dont_complete_primitive_in_use); | 24 | tested_by!(dont_complete_primitive_in_use); |
25 | continue; | 25 | continue; |
26 | } | 26 | } |
27 | } | 27 | if let ScopeDef::Unknown = def { |
28 | if Some(module) == ctx.module { | 28 | if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { |
29 | if let Some(import) = import { | 29 | if &name_ref.syntax().text() == name.to_string().as_str() { |
30 | if let Either::A(use_tree) = import.source(ctx.db).value { | ||
31 | if use_tree.syntax().text_range().contains_inclusive(ctx.offset) { | ||
32 | // for `use self::foo<|>`, don't suggest `foo` as a completion | 30 | // for `use self::foo<|>`, don't suggest `foo` as a completion |
33 | tested_by!(dont_complete_current_use); | 31 | tested_by!(dont_complete_current_use); |
34 | continue; | 32 | continue; |
@@ -36,6 +34,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
36 | } | 34 | } |
37 | } | 35 | } |
38 | } | 36 | } |
37 | |||
39 | acc.add_resolution(ctx, name.to_string(), &def); | 38 | acc.add_resolution(ctx, name.to_string(), &def); |
40 | } | 39 | } |
41 | } | 40 | } |
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs index 646a30c76..5470dc291 100644 --- a/crates/ra_ide/src/completion/complete_postfix.rs +++ b/crates/ra_ide/src/completion/complete_postfix.rs | |||
@@ -12,7 +12,7 @@ use crate::{ | |||
12 | }; | 12 | }; |
13 | 13 | ||
14 | pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | 14 | pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { |
15 | if ctx.db.feature_flags.get("completion.enable-postfix") == false { | 15 | if !ctx.db.feature_flags.get("completion.enable-postfix") { |
16 | return; | 16 | return; |
17 | } | 17 | } |
18 | 18 | ||
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index d5739b58a..458d7525e 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs | |||
@@ -873,4 +873,41 @@ mod tests { | |||
873 | "### | 873 | "### |
874 | ); | 874 | ); |
875 | } | 875 | } |
876 | |||
877 | #[test] | ||
878 | fn completes_local_item() { | ||
879 | assert_debug_snapshot!( | ||
880 | do_reference_completion( | ||
881 | " | ||
882 | //- /main.rs | ||
883 | fn main() { | ||
884 | return f<|>; | ||
885 | fn frobnicate() {} | ||
886 | } | ||
887 | " | ||
888 | ), | ||
889 | @r###" | ||
890 | [ | ||
891 | CompletionItem { | ||
892 | label: "frobnicate()", | ||
893 | source_range: [23; 24), | ||
894 | delete: [23; 24), | ||
895 | insert: "frobnicate()$0", | ||
896 | kind: Function, | ||
897 | lookup: "frobnicate", | ||
898 | detail: "fn frobnicate()", | ||
899 | }, | ||
900 | CompletionItem { | ||
901 | label: "main()", | ||
902 | source_range: [23; 24), | ||
903 | delete: [23; 24), | ||
904 | insert: "main()$0", | ||
905 | kind: Function, | ||
906 | lookup: "main", | ||
907 | detail: "fn main()", | ||
908 | }, | ||
909 | ] | ||
910 | "### | ||
911 | ) | ||
912 | } | ||
876 | } | 913 | } |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index b8345c91d..48d69f7e5 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -19,6 +19,7 @@ pub(crate) struct CompletionContext<'a> { | |||
19 | pub(super) offset: TextUnit, | 19 | pub(super) offset: TextUnit, |
20 | pub(super) token: SyntaxToken, | 20 | pub(super) token: SyntaxToken, |
21 | pub(super) module: Option<hir::Module>, | 21 | pub(super) module: Option<hir::Module>, |
22 | pub(super) name_ref_syntax: Option<ast::NameRef>, | ||
22 | pub(super) function_syntax: Option<ast::FnDef>, | 23 | pub(super) function_syntax: Option<ast::FnDef>, |
23 | pub(super) use_item_syntax: Option<ast::UseItem>, | 24 | pub(super) use_item_syntax: Option<ast::UseItem>, |
24 | pub(super) record_lit_syntax: Option<ast::RecordLit>, | 25 | pub(super) record_lit_syntax: Option<ast::RecordLit>, |
@@ -54,13 +55,13 @@ impl<'a> CompletionContext<'a> { | |||
54 | let src = hir::ModuleSource::from_position(db, position); | 55 | let src = hir::ModuleSource::from_position(db, position); |
55 | let module = hir::Module::from_definition( | 56 | let module = hir::Module::from_definition( |
56 | db, | 57 | db, |
57 | hir::Source { file_id: position.file_id.into(), value: src }, | 58 | hir::InFile { file_id: position.file_id.into(), value: src }, |
58 | ); | 59 | ); |
59 | let token = | 60 | let token = |
60 | original_parse.tree().syntax().token_at_offset(position.offset).left_biased()?; | 61 | original_parse.tree().syntax().token_at_offset(position.offset).left_biased()?; |
61 | let analyzer = hir::SourceAnalyzer::new( | 62 | let analyzer = hir::SourceAnalyzer::new( |
62 | db, | 63 | db, |
63 | hir::Source::new(position.file_id.into(), &token.parent()), | 64 | hir::InFile::new(position.file_id.into(), &token.parent()), |
64 | Some(position.offset), | 65 | Some(position.offset), |
65 | ); | 66 | ); |
66 | let mut ctx = CompletionContext { | 67 | let mut ctx = CompletionContext { |
@@ -69,6 +70,7 @@ impl<'a> CompletionContext<'a> { | |||
69 | token, | 70 | token, |
70 | offset: position.offset, | 71 | offset: position.offset, |
71 | module, | 72 | module, |
73 | name_ref_syntax: None, | ||
72 | function_syntax: None, | 74 | function_syntax: None, |
73 | use_item_syntax: None, | 75 | use_item_syntax: None, |
74 | record_lit_syntax: None, | 76 | record_lit_syntax: None, |
@@ -142,6 +144,8 @@ impl<'a> CompletionContext<'a> { | |||
142 | } | 144 | } |
143 | 145 | ||
144 | fn classify_name_ref(&mut self, original_file: SourceFile, name_ref: ast::NameRef) { | 146 | fn classify_name_ref(&mut self, original_file: SourceFile, name_ref: ast::NameRef) { |
147 | self.name_ref_syntax = | ||
148 | find_node_at_offset(original_file.syntax(), name_ref.syntax().text_range().start()); | ||
145 | let name_range = name_ref.syntax().text_range(); | 149 | let name_range = name_ref.syntax().text_range(); |
146 | if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { | 150 | if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { |
147 | self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset); | 151 | self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset); |
@@ -188,10 +192,9 @@ impl<'a> CompletionContext<'a> { | |||
188 | self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); | 192 | self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); |
189 | self.has_type_args = segment.type_arg_list().is_some(); | 193 | self.has_type_args = segment.type_arg_list().is_some(); |
190 | 194 | ||
191 | if let Some(mut path) = hir::Path::from_ast(path.clone()) { | 195 | if let Some(path) = hir::Path::from_ast(path.clone()) { |
192 | if !path.is_ident() { | 196 | if let Some(path_prefix) = path.qualifier() { |
193 | path.segments.pop().unwrap(); | 197 | self.path_prefix = Some(path_prefix); |
194 | self.path_prefix = Some(path); | ||
195 | return; | 198 | return; |
196 | } | 199 | } |
197 | } | 200 | } |
@@ -240,16 +243,15 @@ impl<'a> CompletionContext<'a> { | |||
240 | .expr() | 243 | .expr() |
241 | .map(|e| e.syntax().text_range()) | 244 | .map(|e| e.syntax().text_range()) |
242 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | 245 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); |
243 | self.dot_receiver_is_ambiguous_float_literal = if let Some(ast::Expr::Literal(l)) = | 246 | self.dot_receiver_is_ambiguous_float_literal = |
244 | &self.dot_receiver | 247 | if let Some(ast::Expr::Literal(l)) = &self.dot_receiver { |
245 | { | 248 | match l.kind() { |
246 | match l.kind() { | 249 | ast::LiteralKind::FloatNumber { .. } => l.token().text().ends_with('.'), |
247 | ast::LiteralKind::FloatNumber { suffix: _ } => l.token().text().ends_with('.'), | 250 | _ => false, |
248 | _ => false, | 251 | } |
252 | } else { | ||
253 | false | ||
249 | } | 254 | } |
250 | } else { | ||
251 | false | ||
252 | } | ||
253 | } | 255 | } |
254 | if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { | 256 | if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { |
255 | // As above | 257 | // As above |
diff --git a/crates/ra_ide/src/db.rs b/crates/ra_ide/src/db.rs index f739ebecd..47d0aed6f 100644 --- a/crates/ra_ide/src/db.rs +++ b/crates/ra_ide/src/db.rs | |||
@@ -5,7 +5,7 @@ use std::sync::Arc; | |||
5 | use ra_db::{ | 5 | use ra_db::{ |
6 | salsa::{self, Database, Durability}, | 6 | salsa::{self, Database, Durability}, |
7 | Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, | 7 | Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, |
8 | SourceDatabase, SourceDatabaseExt, SourceRootId, | 8 | SourceDatabase, SourceRootId, |
9 | }; | 9 | }; |
10 | use rustc_hash::FxHashMap; | 10 | use rustc_hash::FxHashMap; |
11 | 11 | ||
@@ -49,18 +49,6 @@ impl FileLoader for RootDatabase { | |||
49 | } | 49 | } |
50 | } | 50 | } |
51 | 51 | ||
52 | impl hir::debug::HirDebugHelper for RootDatabase { | ||
53 | fn crate_name(&self, krate: CrateId) -> Option<String> { | ||
54 | self.debug_data.crate_names.get(&krate).cloned() | ||
55 | } | ||
56 | fn file_path(&self, file_id: FileId) -> Option<String> { | ||
57 | let source_root_id = self.file_source_root(file_id); | ||
58 | let source_root_path = self.debug_data.root_paths.get(&source_root_id)?; | ||
59 | let file_path = self.file_relative_path(file_id); | ||
60 | Some(format!("{}/{}", source_root_path, file_path)) | ||
61 | } | ||
62 | } | ||
63 | |||
64 | impl salsa::Database for RootDatabase { | 52 | impl salsa::Database for RootDatabase { |
65 | fn salsa_runtime(&self) -> &salsa::Runtime<RootDatabase> { | 53 | fn salsa_runtime(&self) -> &salsa::Runtime<RootDatabase> { |
66 | &self.runtime | 54 | &self.runtime |
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index cc1ccab4b..c50a70d99 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -96,7 +96,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
96 | }); | 96 | }); |
97 | let source_file = db.parse(file_id).tree(); | 97 | let source_file = db.parse(file_id).tree(); |
98 | let src = | 98 | let src = |
99 | hir::Source { file_id: file_id.into(), value: hir::ModuleSource::SourceFile(source_file) }; | 99 | hir::InFile { file_id: file_id.into(), value: hir::ModuleSource::SourceFile(source_file) }; |
100 | if let Some(m) = hir::Module::from_definition(db, src) { | 100 | if let Some(m) = hir::Module::from_definition(db, src) { |
101 | m.diagnostics(db, &mut sink); | 101 | m.diagnostics(db, &mut sink); |
102 | }; | 102 | }; |
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs index 30617412a..fbe89841b 100644 --- a/crates/ra_ide/src/display.rs +++ b/crates/ra_ide/src/display.rs | |||
@@ -15,7 +15,7 @@ pub use function_signature::FunctionSignature; | |||
15 | pub use navigation_target::NavigationTarget; | 15 | pub use navigation_target::NavigationTarget; |
16 | pub use structure::{file_structure, StructureNode}; | 16 | pub use structure::{file_structure, StructureNode}; |
17 | 17 | ||
18 | pub(crate) use navigation_target::{description_from_symbol, docs_from_symbol, ToNav}; | 18 | pub(crate) use navigation_target::ToNav; |
19 | pub(crate) use short_label::ShortLabel; | 19 | pub(crate) use short_label::ShortLabel; |
20 | 20 | ||
21 | pub(crate) fn function_label(node: &ast::FnDef) -> String { | 21 | pub(crate) fn function_label(node: &ast::FnDef) -> String { |
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs index 6ac60722b..b9ae67828 100644 --- a/crates/ra_ide/src/display/navigation_target.rs +++ b/crates/ra_ide/src/display/navigation_target.rs | |||
@@ -1,11 +1,12 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{AssocItem, Either, FieldSource, HasSource, ModuleSource, Source}; | 3 | use either::Either; |
4 | use hir::{AssocItem, FieldSource, HasSource, InFile, ModuleSource}; | ||
4 | use ra_db::{FileId, SourceDatabase}; | 5 | use ra_db::{FileId, SourceDatabase}; |
5 | use ra_syntax::{ | 6 | use ra_syntax::{ |
6 | ast::{self, DocCommentsOwner, NameOwner}, | 7 | ast::{self, DocCommentsOwner, NameOwner}, |
7 | match_ast, AstNode, SmolStr, | 8 | match_ast, AstNode, SmolStr, |
8 | SyntaxKind::{self, BIND_PAT}, | 9 | SyntaxKind::{self, BIND_PAT, TYPE_PARAM}, |
9 | TextRange, | 10 | TextRange, |
10 | }; | 11 | }; |
11 | 12 | ||
@@ -141,7 +142,7 @@ impl NavigationTarget { | |||
141 | /// Allows `NavigationTarget` to be created from a `NameOwner` | 142 | /// Allows `NavigationTarget` to be created from a `NameOwner` |
142 | pub(crate) fn from_named( | 143 | pub(crate) fn from_named( |
143 | db: &RootDatabase, | 144 | db: &RootDatabase, |
144 | node: Source<&dyn ast::NameOwner>, | 145 | node: InFile<&dyn ast::NameOwner>, |
145 | docs: Option<String>, | 146 | docs: Option<String>, |
146 | description: Option<String>, | 147 | description: Option<String>, |
147 | ) -> NavigationTarget { | 148 | ) -> NavigationTarget { |
@@ -230,34 +231,20 @@ impl ToNav for hir::Module { | |||
230 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | 231 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { |
231 | let src = self.definition_source(db); | 232 | let src = self.definition_source(db); |
232 | let name = self.name(db).map(|it| it.to_string().into()).unwrap_or_default(); | 233 | let name = self.name(db).map(|it| it.to_string().into()).unwrap_or_default(); |
233 | match &src.value { | 234 | let syntax = match &src.value { |
234 | ModuleSource::SourceFile(node) => { | 235 | ModuleSource::SourceFile(node) => node.syntax(), |
235 | let frange = original_range(db, src.with_value(node.syntax())); | 236 | ModuleSource::Module(node) => node.syntax(), |
236 | 237 | }; | |
237 | NavigationTarget::from_syntax( | 238 | let frange = original_range(db, src.with_value(syntax)); |
238 | frange.file_id, | 239 | NavigationTarget::from_syntax( |
239 | name, | 240 | frange.file_id, |
240 | None, | 241 | name, |
241 | frange.range, | 242 | None, |
242 | node.syntax().kind(), | 243 | frange.range, |
243 | None, | 244 | syntax.kind(), |
244 | None, | 245 | None, |
245 | ) | 246 | None, |
246 | } | 247 | ) |
247 | ModuleSource::Module(node) => { | ||
248 | let frange = original_range(db, src.with_value(node.syntax())); | ||
249 | |||
250 | NavigationTarget::from_syntax( | ||
251 | frange.file_id, | ||
252 | name, | ||
253 | None, | ||
254 | frange.range, | ||
255 | node.syntax().kind(), | ||
256 | node.doc_comment_text(), | ||
257 | node.short_label(), | ||
258 | ) | ||
259 | } | ||
260 | } | ||
261 | } | 248 | } |
262 | } | 249 | } |
263 | 250 | ||
@@ -341,22 +328,43 @@ impl ToNav for hir::AssocItem { | |||
341 | impl ToNav for hir::Local { | 328 | impl ToNav for hir::Local { |
342 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | 329 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { |
343 | let src = self.source(db); | 330 | let src = self.source(db); |
344 | let (full_range, focus_range) = match src.value { | 331 | let node = match &src.value { |
345 | Either::A(it) => { | 332 | Either::Left(bind_pat) => { |
346 | (it.syntax().text_range(), it.name().map(|it| it.syntax().text_range())) | 333 | bind_pat.name().map_or_else(|| bind_pat.syntax().clone(), |it| it.syntax().clone()) |
347 | } | 334 | } |
348 | Either::B(it) => (it.syntax().text_range(), Some(it.self_kw_token().text_range())), | 335 | Either::Right(it) => it.syntax().clone(), |
349 | }; | 336 | }; |
337 | let full_range = original_range(db, src.with_value(&node)); | ||
350 | let name = match self.name(db) { | 338 | let name = match self.name(db) { |
351 | Some(it) => it.to_string().into(), | 339 | Some(it) => it.to_string().into(), |
352 | None => "".into(), | 340 | None => "".into(), |
353 | }; | 341 | }; |
354 | NavigationTarget { | 342 | NavigationTarget { |
355 | file_id: src.file_id.original_file(db), | 343 | file_id: full_range.file_id, |
356 | name, | 344 | name, |
357 | kind: BIND_PAT, | 345 | kind: BIND_PAT, |
358 | full_range, | 346 | full_range: full_range.range, |
359 | focus_range, | 347 | focus_range: None, |
348 | container_name: None, | ||
349 | description: None, | ||
350 | docs: None, | ||
351 | } | ||
352 | } | ||
353 | } | ||
354 | |||
355 | impl ToNav for hir::TypeParam { | ||
356 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | ||
357 | let src = self.source(db); | ||
358 | let range = match src.value { | ||
359 | Either::Left(it) => it.syntax().text_range(), | ||
360 | Either::Right(it) => it.syntax().text_range(), | ||
361 | }; | ||
362 | NavigationTarget { | ||
363 | file_id: src.file_id.original_file(db), | ||
364 | name: self.name(db).to_string().into(), | ||
365 | kind: TYPE_PARAM, | ||
366 | full_range: range, | ||
367 | focus_range: None, | ||
360 | container_name: None, | 368 | container_name: None, |
361 | description: None, | 369 | description: None, |
362 | docs: None, | 370 | docs: None, |
diff --git a/crates/ra_ide/src/expand.rs b/crates/ra_ide/src/expand.rs index 2f1abf509..7a22bb0a4 100644 --- a/crates/ra_ide/src/expand.rs +++ b/crates/ra_ide/src/expand.rs | |||
@@ -1,42 +1,71 @@ | |||
1 | //! Utilities to work with files, produced by macros. | 1 | //! Utilities to work with files, produced by macros. |
2 | use std::iter::successors; | 2 | use std::iter::successors; |
3 | 3 | ||
4 | use hir::Source; | 4 | use hir::{InFile, Origin}; |
5 | use ra_db::FileId; | 5 | use ra_db::FileId; |
6 | use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken}; | 6 | use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken, TextRange}; |
7 | 7 | ||
8 | use crate::{db::RootDatabase, FileRange}; | 8 | use crate::{db::RootDatabase, FileRange}; |
9 | 9 | ||
10 | pub(crate) fn original_range(db: &RootDatabase, node: Source<&SyntaxNode>) -> FileRange { | 10 | pub(crate) fn original_range(db: &RootDatabase, node: InFile<&SyntaxNode>) -> FileRange { |
11 | let expansion = match node.file_id.expansion_info(db) { | 11 | if let Some((range, Origin::Call)) = original_range_and_origin(db, node) { |
12 | None => { | 12 | return range; |
13 | } | ||
14 | |||
15 | if let Some(expansion) = node.file_id.expansion_info(db) { | ||
16 | if let Some(call_node) = expansion.call_node() { | ||
13 | return FileRange { | 17 | return FileRange { |
14 | file_id: node.file_id.original_file(db), | 18 | file_id: call_node.file_id.original_file(db), |
15 | range: node.value.text_range(), | 19 | range: call_node.value.text_range(), |
16 | } | 20 | }; |
17 | } | 21 | } |
18 | Some(it) => it, | 22 | } |
19 | }; | 23 | |
20 | // FIXME: the following completely wrong. | 24 | FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() } |
21 | // | 25 | } |
22 | // *First*, we should try to map first and last tokens of node, and, if that | 26 | |
23 | // fails, return the range of the overall macro expansions. | 27 | fn original_range_and_origin( |
24 | // | 28 | db: &RootDatabase, |
25 | // *Second*, we should handle recurside macro expansions | 29 | node: InFile<&SyntaxNode>, |
26 | 30 | ) -> Option<(FileRange, Origin)> { | |
27 | let token = node | 31 | let expansion = node.file_id.expansion_info(db)?; |
28 | .value | 32 | |
29 | .descendants_with_tokens() | 33 | // the input node has only one token ? |
30 | .filter_map(|it| it.into_token()) | 34 | let single = node.value.first_token()? == node.value.last_token()?; |
31 | .find_map(|it| expansion.map_token_up(node.with_value(&it))); | 35 | |
32 | 36 | // FIXME: We should handle recurside macro expansions | |
33 | match token { | 37 | let (range, origin) = node.value.descendants().find_map(|it| { |
34 | Some(it) => { | 38 | let first = it.first_token()?; |
35 | FileRange { file_id: it.file_id.original_file(db), range: it.value.text_range() } | 39 | let last = it.last_token()?; |
40 | |||
41 | if !single && first == last { | ||
42 | return None; | ||
36 | } | 43 | } |
37 | None => { | 44 | |
38 | FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() } | 45 | // Try to map first and last tokens of node, and, if success, return the union range of mapped tokens |
46 | let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?; | ||
47 | let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?; | ||
48 | |||
49 | if first.file_id != last.file_id || first_origin != last_origin { | ||
50 | return None; | ||
39 | } | 51 | } |
52 | |||
53 | // FIXME: Add union method in TextRange | ||
54 | Some(( | ||
55 | first.with_value(union_range(first.value.text_range(), last.value.text_range())), | ||
56 | first_origin, | ||
57 | )) | ||
58 | })?; | ||
59 | |||
60 | return Some(( | ||
61 | FileRange { file_id: range.file_id.original_file(db), range: range.value }, | ||
62 | origin, | ||
63 | )); | ||
64 | |||
65 | fn union_range(a: TextRange, b: TextRange) -> TextRange { | ||
66 | let start = a.start().min(b.start()); | ||
67 | let end = a.end().max(b.end()); | ||
68 | TextRange::from_to(start, end) | ||
40 | } | 69 | } |
41 | } | 70 | } |
42 | 71 | ||
@@ -44,8 +73,8 @@ pub(crate) fn descend_into_macros( | |||
44 | db: &RootDatabase, | 73 | db: &RootDatabase, |
45 | file_id: FileId, | 74 | file_id: FileId, |
46 | token: SyntaxToken, | 75 | token: SyntaxToken, |
47 | ) -> Source<SyntaxToken> { | 76 | ) -> InFile<SyntaxToken> { |
48 | let src = Source::new(file_id.into(), token); | 77 | let src = InFile::new(file_id.into(), token); |
49 | 78 | ||
50 | successors(Some(src), |token| { | 79 | successors(Some(src), |token| { |
51 | let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; | 80 | let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; |
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs index abc602244..bdbc31704 100644 --- a/crates/ra_ide/src/expand_macro.rs +++ b/crates/ra_ide/src/expand_macro.rs | |||
@@ -22,7 +22,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< | |||
22 | let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)?; | 22 | let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)?; |
23 | let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?; | 23 | let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?; |
24 | 24 | ||
25 | let source = hir::Source::new(position.file_id.into(), mac.syntax()); | 25 | let source = hir::InFile::new(position.file_id.into(), mac.syntax()); |
26 | let expanded = expand_macro_recur(db, source, source.with_value(&mac))?; | 26 | let expanded = expand_macro_recur(db, source, source.with_value(&mac))?; |
27 | 27 | ||
28 | // FIXME: | 28 | // FIXME: |
@@ -34,8 +34,8 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< | |||
34 | 34 | ||
35 | fn expand_macro_recur( | 35 | fn expand_macro_recur( |
36 | db: &RootDatabase, | 36 | db: &RootDatabase, |
37 | source: hir::Source<&SyntaxNode>, | 37 | source: hir::InFile<&SyntaxNode>, |
38 | macro_call: hir::Source<&ast::MacroCall>, | 38 | macro_call: hir::InFile<&ast::MacroCall>, |
39 | ) -> Option<SyntaxNode> { | 39 | ) -> Option<SyntaxNode> { |
40 | let analyzer = hir::SourceAnalyzer::new(db, source, None); | 40 | let analyzer = hir::SourceAnalyzer::new(db, source, None); |
41 | let expansion = analyzer.expand(db, macro_call)?; | 41 | let expansion = analyzer.expand(db, macro_call)?; |
@@ -46,7 +46,7 @@ fn expand_macro_recur( | |||
46 | let mut replaces = FxHashMap::default(); | 46 | let mut replaces = FxHashMap::default(); |
47 | 47 | ||
48 | for child in children.into_iter() { | 48 | for child in children.into_iter() { |
49 | let node = hir::Source::new(macro_file_id, &child); | 49 | let node = hir::InFile::new(macro_file_id, &child); |
50 | if let Some(new_node) = expand_macro_recur(db, source, node) { | 50 | if let Some(new_node) = expand_macro_recur(db, source, node) { |
51 | // Replace the whole node if it is root | 51 | // Replace the whole node if it is root |
52 | // `replace_descendants` will not replace the parent node | 52 | // `replace_descendants` will not replace the parent node |
@@ -86,21 +86,18 @@ fn insert_whitespaces(syn: SyntaxNode) -> String { | |||
86 | let mut is_next = |f: fn(SyntaxKind) -> bool, default| -> bool { | 86 | let mut is_next = |f: fn(SyntaxKind) -> bool, default| -> bool { |
87 | token_iter.peek().map(|it| f(it.kind())).unwrap_or(default) | 87 | token_iter.peek().map(|it| f(it.kind())).unwrap_or(default) |
88 | }; | 88 | }; |
89 | let is_last = |f: fn(SyntaxKind) -> bool, default| -> bool { | 89 | let is_last = |
90 | last.map(|it| f(it)).unwrap_or(default) | 90 | |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) }; |
91 | }; | ||
92 | 91 | ||
93 | res += &match token.kind() { | 92 | res += &match token.kind() { |
94 | k @ _ if is_text(k) && is_next(|it| !it.is_punct(), true) => { | 93 | k if is_text(k) && is_next(|it| !it.is_punct(), true) => token.text().to_string() + " ", |
95 | token.text().to_string() + " " | ||
96 | } | ||
97 | L_CURLY if is_next(|it| it != R_CURLY, true) => { | 94 | L_CURLY if is_next(|it| it != R_CURLY, true) => { |
98 | indent += 1; | 95 | indent += 1; |
99 | let leading_space = if is_last(|it| is_text(it), false) { " " } else { "" }; | 96 | let leading_space = if is_last(is_text, false) { " " } else { "" }; |
100 | format!("{}{{\n{}", leading_space, " ".repeat(indent)) | 97 | format!("{}{{\n{}", leading_space, " ".repeat(indent)) |
101 | } | 98 | } |
102 | R_CURLY if is_last(|it| it != L_CURLY, true) => { | 99 | R_CURLY if is_last(|it| it != L_CURLY, true) => { |
103 | indent = indent.checked_sub(1).unwrap_or(0); | 100 | indent = indent.saturating_sub(1); |
104 | format!("\n{}}}", " ".repeat(indent)) | 101 | format!("\n{}}}", " ".repeat(indent)) |
105 | } | 102 | } |
106 | R_CURLY => format!("}}\n{}", " ".repeat(indent)), | 103 | R_CURLY => format!("}}\n{}", " ".repeat(indent)), |
diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs index 4b7bfc0b1..1ec41a117 100644 --- a/crates/ra_ide/src/extend_selection.rs +++ b/crates/ra_ide/src/extend_selection.rs | |||
@@ -34,6 +34,7 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange | |||
34 | ARG_LIST, | 34 | ARG_LIST, |
35 | ARRAY_EXPR, | 35 | ARRAY_EXPR, |
36 | TUPLE_EXPR, | 36 | TUPLE_EXPR, |
37 | TUPLE_TYPE, | ||
37 | WHERE_CLAUSE, | 38 | WHERE_CLAUSE, |
38 | ]; | 39 | ]; |
39 | 40 | ||
@@ -137,7 +138,7 @@ fn extend_ws(root: &SyntaxNode, ws: SyntaxToken, offset: TextUnit) -> TextRange | |||
137 | ws.text_range() | 138 | ws.text_range() |
138 | } | 139 | } |
139 | 140 | ||
140 | fn pick_best<'a>(l: SyntaxToken, r: SyntaxToken) -> SyntaxToken { | 141 | fn pick_best(l: SyntaxToken, r: SyntaxToken) -> SyntaxToken { |
141 | return if priority(&r) > priority(&l) { r } else { l }; | 142 | return if priority(&r) > priority(&l) { r } else { l }; |
142 | fn priority(n: &SyntaxToken) -> usize { | 143 | fn priority(n: &SyntaxToken) -> usize { |
143 | match n.kind() { | 144 | match n.kind() { |
@@ -174,12 +175,7 @@ fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> { | |||
174 | TYPE_BOUND => T![+], | 175 | TYPE_BOUND => T![+], |
175 | _ => T![,], | 176 | _ => T![,], |
176 | }; | 177 | }; |
177 | if let Some(delimiter_node) = nearby_delimiter(delimiter, node, Direction::Prev) { | 178 | |
178 | return Some(TextRange::from_to( | ||
179 | delimiter_node.text_range().start(), | ||
180 | node.text_range().end(), | ||
181 | )); | ||
182 | } | ||
183 | if let Some(delimiter_node) = nearby_delimiter(delimiter, node, Direction::Next) { | 179 | if let Some(delimiter_node) = nearby_delimiter(delimiter, node, Direction::Next) { |
184 | // Include any following whitespace when delimiter is after list item. | 180 | // Include any following whitespace when delimiter is after list item. |
185 | let final_node = delimiter_node | 181 | let final_node = delimiter_node |
@@ -190,6 +186,12 @@ fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> { | |||
190 | 186 | ||
191 | return Some(TextRange::from_to(node.text_range().start(), final_node.text_range().end())); | 187 | return Some(TextRange::from_to(node.text_range().start(), final_node.text_range().end())); |
192 | } | 188 | } |
189 | if let Some(delimiter_node) = nearby_delimiter(delimiter, node, Direction::Prev) { | ||
190 | return Some(TextRange::from_to( | ||
191 | delimiter_node.text_range().start(), | ||
192 | node.text_range().end(), | ||
193 | )); | ||
194 | } | ||
193 | 195 | ||
194 | None | 196 | None |
195 | } | 197 | } |
@@ -250,14 +252,14 @@ mod tests { | |||
250 | fn test_extend_selection_list() { | 252 | fn test_extend_selection_list() { |
251 | do_check(r#"fn foo(<|>x: i32) {}"#, &["x", "x: i32"]); | 253 | do_check(r#"fn foo(<|>x: i32) {}"#, &["x", "x: i32"]); |
252 | do_check(r#"fn foo(<|>x: i32, y: i32) {}"#, &["x", "x: i32", "x: i32, "]); | 254 | do_check(r#"fn foo(<|>x: i32, y: i32) {}"#, &["x", "x: i32", "x: i32, "]); |
253 | do_check(r#"fn foo(<|>x: i32,y: i32) {}"#, &["x", "x: i32", "x: i32,"]); | 255 | do_check(r#"fn foo(<|>x: i32,y: i32) {}"#, &["x", "x: i32", "x: i32,", "(x: i32,y: i32)"]); |
254 | do_check(r#"fn foo(x: i32, <|>y: i32) {}"#, &["y", "y: i32", ", y: i32"]); | 256 | do_check(r#"fn foo(x: i32, <|>y: i32) {}"#, &["y", "y: i32", ", y: i32"]); |
255 | do_check(r#"fn foo(x: i32, <|>y: i32, ) {}"#, &["y", "y: i32", ", y: i32"]); | 257 | do_check(r#"fn foo(x: i32, <|>y: i32, ) {}"#, &["y", "y: i32", "y: i32, "]); |
256 | do_check(r#"fn foo(x: i32,<|>y: i32) {}"#, &["y", "y: i32", ",y: i32"]); | 258 | do_check(r#"fn foo(x: i32,<|>y: i32) {}"#, &["y", "y: i32", ",y: i32"]); |
257 | 259 | ||
258 | do_check(r#"const FOO: [usize; 2] = [ 22<|> , 33];"#, &["22", "22 , "]); | 260 | do_check(r#"const FOO: [usize; 2] = [ 22<|> , 33];"#, &["22", "22 , "]); |
259 | do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|>];"#, &["33", ", 33"]); | 261 | do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|>];"#, &["33", ", 33"]); |
260 | do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|> ,];"#, &["33", ", 33"]); | 262 | do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|> ,];"#, &["33", "33 ,", "[ 22 , 33 ,]"]); |
261 | 263 | ||
262 | do_check(r#"fn main() { (1, 2<|>) }"#, &["2", ", 2", "(1, 2)"]); | 264 | do_check(r#"fn main() { (1, 2<|>) }"#, &["2", ", 2", "(1, 2)"]); |
263 | 265 | ||
@@ -276,7 +278,7 @@ const FOO: [usize; 2] = [ | |||
276 | 22 | 278 | 22 |
277 | , 33<|>, | 279 | , 33<|>, |
278 | ]"#, | 280 | ]"#, |
279 | &["33", ", 33"], | 281 | &["33", "33,"], |
280 | ); | 282 | ); |
281 | } | 283 | } |
282 | 284 | ||
@@ -424,7 +426,7 @@ fn foo<R>() | |||
424 | do_check(r#"fn foo<T>() where T: <|>Copy +Display"#, &["Copy", "Copy +"]); | 426 | do_check(r#"fn foo<T>() where T: <|>Copy +Display"#, &["Copy", "Copy +"]); |
425 | do_check(r#"fn foo<T>() where T: <|>Copy+Display"#, &["Copy", "Copy+"]); | 427 | do_check(r#"fn foo<T>() where T: <|>Copy+Display"#, &["Copy", "Copy+"]); |
426 | do_check(r#"fn foo<T>() where T: Copy + <|>Display"#, &["Display", "+ Display"]); | 428 | do_check(r#"fn foo<T>() where T: Copy + <|>Display"#, &["Display", "+ Display"]); |
427 | do_check(r#"fn foo<T>() where T: Copy + <|>Display + Sync"#, &["Display", "+ Display"]); | 429 | do_check(r#"fn foo<T>() where T: Copy + <|>Display + Sync"#, &["Display", "Display + "]); |
428 | do_check(r#"fn foo<T>() where T: Copy +<|>Display"#, &["Display", "+Display"]); | 430 | do_check(r#"fn foo<T>() where T: Copy +<|>Display"#, &["Display", "+Display"]); |
429 | } | 431 | } |
430 | 432 | ||
@@ -435,7 +437,7 @@ fn foo<R>() | |||
435 | do_check(r#"fn foo<T: <|>Copy +Display>() {}"#, &["Copy", "Copy +"]); | 437 | do_check(r#"fn foo<T: <|>Copy +Display>() {}"#, &["Copy", "Copy +"]); |
436 | do_check(r#"fn foo<T: <|>Copy+Display>() {}"#, &["Copy", "Copy+"]); | 438 | do_check(r#"fn foo<T: <|>Copy+Display>() {}"#, &["Copy", "Copy+"]); |
437 | do_check(r#"fn foo<T: Copy + <|>Display>() {}"#, &["Display", "+ Display"]); | 439 | do_check(r#"fn foo<T: Copy + <|>Display>() {}"#, &["Display", "+ Display"]); |
438 | do_check(r#"fn foo<T: Copy + <|>Display + Sync>() {}"#, &["Display", "+ Display"]); | 440 | do_check(r#"fn foo<T: Copy + <|>Display + Sync>() {}"#, &["Display", "Display + "]); |
439 | do_check(r#"fn foo<T: Copy +<|>Display>() {}"#, &["Display", "+Display"]); | 441 | do_check(r#"fn foo<T: Copy +<|>Display>() {}"#, &["Display", "+Display"]); |
440 | do_check( | 442 | do_check( |
441 | r#"fn foo<T: Copy<|> + Display, U: Copy>() {}"#, | 443 | r#"fn foo<T: Copy<|> + Display, U: Copy>() {}"#, |
@@ -449,4 +451,56 @@ fn foo<R>() | |||
449 | ], | 451 | ], |
450 | ); | 452 | ); |
451 | } | 453 | } |
454 | |||
455 | #[test] | ||
456 | fn test_extend_selection_on_tuple_in_type() { | ||
457 | do_check( | ||
458 | r#"fn main() { let _: (krate, <|>_crate_def_map, module_id) = (); }"#, | ||
459 | &["_crate_def_map", "_crate_def_map, ", "(krate, _crate_def_map, module_id)"], | ||
460 | ); | ||
461 | // white space variations | ||
462 | do_check( | ||
463 | r#"fn main() { let _: (krate,<|>_crate_def_map,module_id) = (); }"#, | ||
464 | &["_crate_def_map", "_crate_def_map,", "(krate,_crate_def_map,module_id)"], | ||
465 | ); | ||
466 | do_check( | ||
467 | r#" | ||
468 | fn main() { let _: ( | ||
469 | krate, | ||
470 | _crate<|>_def_map, | ||
471 | module_id | ||
472 | ) = (); }"#, | ||
473 | &[ | ||
474 | "_crate_def_map", | ||
475 | "_crate_def_map,", | ||
476 | "(\n krate,\n _crate_def_map,\n module_id\n)", | ||
477 | ], | ||
478 | ); | ||
479 | } | ||
480 | |||
481 | #[test] | ||
482 | fn test_extend_selection_on_tuple_in_rvalue() { | ||
483 | do_check( | ||
484 | r#"fn main() { let var = (krate, _crate_def_map<|>, module_id); }"#, | ||
485 | &["_crate_def_map", "_crate_def_map, ", "(krate, _crate_def_map, module_id)"], | ||
486 | ); | ||
487 | // white space variations | ||
488 | do_check( | ||
489 | r#"fn main() { let var = (krate,_crate<|>_def_map,module_id); }"#, | ||
490 | &["_crate_def_map", "_crate_def_map,", "(krate,_crate_def_map,module_id)"], | ||
491 | ); | ||
492 | do_check( | ||
493 | r#" | ||
494 | fn main() { let var = ( | ||
495 | krate, | ||
496 | _crate_def_map<|>, | ||
497 | module_id | ||
498 | ); }"#, | ||
499 | &[ | ||
500 | "_crate_def_map", | ||
501 | "_crate_def_map,", | ||
502 | "(\n krate,\n _crate_def_map,\n module_id\n)", | ||
503 | ], | ||
504 | ); | ||
505 | } | ||
452 | } | 506 | } |
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index c10a6c844..79d332e8c 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs | |||
@@ -1,9 +1,11 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{db::AstDatabase, Source}; | 3 | use hir::{db::AstDatabase, InFile}; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | ast::{self, DocCommentsOwner}, | 5 | ast::{self, DocCommentsOwner}, |
6 | match_ast, AstNode, SyntaxNode, | 6 | match_ast, AstNode, |
7 | SyntaxKind::*, | ||
8 | SyntaxNode, SyntaxToken, TokenAtOffset, | ||
7 | }; | 9 | }; |
8 | 10 | ||
9 | use crate::{ | 11 | use crate::{ |
@@ -19,25 +21,33 @@ pub(crate) fn goto_definition( | |||
19 | position: FilePosition, | 21 | position: FilePosition, |
20 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { | 22 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { |
21 | let file = db.parse_or_expand(position.file_id.into())?; | 23 | let file = db.parse_or_expand(position.file_id.into())?; |
22 | let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?; | 24 | let original_token = pick_best(file.token_at_offset(position.offset))?; |
23 | let token = descend_into_macros(db, position.file_id, token); | 25 | let token = descend_into_macros(db, position.file_id, original_token.clone()); |
24 | 26 | ||
25 | let res = match_ast! { | 27 | let nav_targets = match_ast! { |
26 | match (token.value.parent()) { | 28 | match (token.value.parent()) { |
27 | ast::NameRef(name_ref) => { | 29 | ast::NameRef(name_ref) => { |
28 | let navs = reference_definition(db, token.with_value(&name_ref)).to_vec(); | 30 | reference_definition(db, token.with_value(&name_ref)).to_vec() |
29 | RangeInfo::new(name_ref.syntax().text_range(), navs.to_vec()) | ||
30 | }, | 31 | }, |
31 | ast::Name(name) => { | 32 | ast::Name(name) => { |
32 | let navs = name_definition(db, token.with_value(&name))?; | 33 | name_definition(db, token.with_value(&name))? |
33 | RangeInfo::new(name.syntax().text_range(), navs) | ||
34 | |||
35 | }, | 34 | }, |
36 | _ => return None, | 35 | _ => return None, |
37 | } | 36 | } |
38 | }; | 37 | }; |
39 | 38 | ||
40 | Some(res) | 39 | Some(RangeInfo::new(original_token.text_range(), nav_targets)) |
40 | } | ||
41 | |||
42 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | ||
43 | return tokens.max_by_key(priority); | ||
44 | fn priority(n: &SyntaxToken) -> usize { | ||
45 | match n.kind() { | ||
46 | IDENT | INT_NUMBER => 2, | ||
47 | kind if kind.is_trivia() => 0, | ||
48 | _ => 1, | ||
49 | } | ||
50 | } | ||
41 | } | 51 | } |
42 | 52 | ||
43 | #[derive(Debug)] | 53 | #[derive(Debug)] |
@@ -58,15 +68,17 @@ impl ReferenceResult { | |||
58 | 68 | ||
59 | pub(crate) fn reference_definition( | 69 | pub(crate) fn reference_definition( |
60 | db: &RootDatabase, | 70 | db: &RootDatabase, |
61 | name_ref: Source<&ast::NameRef>, | 71 | name_ref: InFile<&ast::NameRef>, |
62 | ) -> ReferenceResult { | 72 | ) -> ReferenceResult { |
63 | use self::ReferenceResult::*; | 73 | use self::ReferenceResult::*; |
64 | 74 | ||
65 | let name_kind = classify_name_ref(db, name_ref).map(|d| d.kind); | 75 | let name_kind = classify_name_ref(db, name_ref).map(|d| d.kind); |
66 | match name_kind { | 76 | match name_kind { |
67 | Some(Macro(mac)) => return Exact(mac.to_nav(db)), | 77 | Some(Macro(it)) => return Exact(it.to_nav(db)), |
68 | Some(Field(field)) => return Exact(field.to_nav(db)), | 78 | Some(Field(it)) => return Exact(it.to_nav(db)), |
69 | Some(AssocItem(assoc)) => return Exact(assoc.to_nav(db)), | 79 | Some(TypeParam(it)) => return Exact(it.to_nav(db)), |
80 | Some(AssocItem(it)) => return Exact(it.to_nav(db)), | ||
81 | Some(Local(it)) => return Exact(it.to_nav(db)), | ||
70 | Some(Def(def)) => match NavigationTarget::from_def(db, def) { | 82 | Some(Def(def)) => match NavigationTarget::from_def(db, def) { |
71 | Some(nav) => return Exact(nav), | 83 | Some(nav) => return Exact(nav), |
72 | None => return Approximate(vec![]), | 84 | None => return Approximate(vec![]), |
@@ -77,10 +89,6 @@ pub(crate) fn reference_definition( | |||
77 | // us to the actual type | 89 | // us to the actual type |
78 | return Exact(imp.to_nav(db)); | 90 | return Exact(imp.to_nav(db)); |
79 | } | 91 | } |
80 | Some(Local(local)) => return Exact(local.to_nav(db)), | ||
81 | Some(GenericParam(_)) => { | ||
82 | // FIXME: go to the generic param def | ||
83 | } | ||
84 | None => {} | 92 | None => {} |
85 | }; | 93 | }; |
86 | 94 | ||
@@ -94,7 +102,7 @@ pub(crate) fn reference_definition( | |||
94 | 102 | ||
95 | pub(crate) fn name_definition( | 103 | pub(crate) fn name_definition( |
96 | db: &RootDatabase, | 104 | db: &RootDatabase, |
97 | name: Source<&ast::Name>, | 105 | name: InFile<&ast::Name>, |
98 | ) -> Option<Vec<NavigationTarget>> { | 106 | ) -> Option<Vec<NavigationTarget>> { |
99 | let parent = name.value.syntax().parent()?; | 107 | let parent = name.value.syntax().parent()?; |
100 | 108 | ||
@@ -115,7 +123,7 @@ pub(crate) fn name_definition( | |||
115 | None | 123 | None |
116 | } | 124 | } |
117 | 125 | ||
118 | fn named_target(db: &RootDatabase, node: Source<&SyntaxNode>) -> Option<NavigationTarget> { | 126 | fn named_target(db: &RootDatabase, node: InFile<&SyntaxNode>) -> Option<NavigationTarget> { |
119 | match_ast! { | 127 | match_ast! { |
120 | match (node.value) { | 128 | match (node.value) { |
121 | ast::StructDef(it) => { | 129 | ast::StructDef(it) => { |
@@ -213,21 +221,44 @@ fn named_target(db: &RootDatabase, node: Source<&SyntaxNode>) -> Option<Navigati | |||
213 | 221 | ||
214 | #[cfg(test)] | 222 | #[cfg(test)] |
215 | mod tests { | 223 | mod tests { |
216 | use test_utils::covers; | 224 | use test_utils::{assert_eq_text, covers}; |
217 | 225 | ||
218 | use crate::mock_analysis::analysis_and_position; | 226 | use crate::mock_analysis::analysis_and_position; |
219 | 227 | ||
220 | fn check_goto(fixture: &str, expected: &str) { | 228 | fn check_goto(fixture: &str, expected: &str, expected_range: &str) { |
221 | let (analysis, pos) = analysis_and_position(fixture); | 229 | let (analysis, pos) = analysis_and_position(fixture); |
222 | 230 | ||
223 | let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info; | 231 | let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info; |
224 | assert_eq!(navs.len(), 1); | 232 | assert_eq!(navs.len(), 1); |
233 | |||
225 | let nav = navs.pop().unwrap(); | 234 | let nav = navs.pop().unwrap(); |
235 | let file_text = analysis.file_text(nav.file_id()).unwrap(); | ||
236 | |||
237 | let mut actual = file_text[nav.full_range()].to_string(); | ||
238 | if let Some(focus) = nav.focus_range() { | ||
239 | actual += "|"; | ||
240 | actual += &file_text[focus]; | ||
241 | } | ||
242 | |||
243 | if !expected_range.contains("...") { | ||
244 | test_utils::assert_eq_text!(&actual, expected_range); | ||
245 | } else { | ||
246 | let mut parts = expected_range.split("..."); | ||
247 | let prefix = parts.next().unwrap(); | ||
248 | let suffix = parts.next().unwrap(); | ||
249 | assert!( | ||
250 | actual.starts_with(prefix) && actual.ends_with(suffix), | ||
251 | "\nExpected: {}\n Actual: {}\n", | ||
252 | expected_range, | ||
253 | actual | ||
254 | ); | ||
255 | } | ||
256 | |||
226 | nav.assert_match(expected); | 257 | nav.assert_match(expected); |
227 | } | 258 | } |
228 | 259 | ||
229 | #[test] | 260 | #[test] |
230 | fn goto_definition_works_in_items() { | 261 | fn goto_def_in_items() { |
231 | check_goto( | 262 | check_goto( |
232 | " | 263 | " |
233 | //- /lib.rs | 264 | //- /lib.rs |
@@ -235,6 +266,20 @@ mod tests { | |||
235 | enum E { X(Foo<|>) } | 266 | enum E { X(Foo<|>) } |
236 | ", | 267 | ", |
237 | "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)", | 268 | "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)", |
269 | "struct Foo;|Foo", | ||
270 | ); | ||
271 | } | ||
272 | |||
273 | #[test] | ||
274 | fn goto_def_at_start_of_item() { | ||
275 | check_goto( | ||
276 | " | ||
277 | //- /lib.rs | ||
278 | struct Foo; | ||
279 | enum E { X(<|>Foo) } | ||
280 | ", | ||
281 | "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)", | ||
282 | "struct Foo;|Foo", | ||
238 | ); | 283 | ); |
239 | } | 284 | } |
240 | 285 | ||
@@ -247,61 +292,65 @@ mod tests { | |||
247 | mod a; | 292 | mod a; |
248 | mod b; | 293 | mod b; |
249 | enum E { X(Foo<|>) } | 294 | enum E { X(Foo<|>) } |
295 | |||
250 | //- /a.rs | 296 | //- /a.rs |
251 | struct Foo; | 297 | struct Foo; |
298 | |||
252 | //- /b.rs | 299 | //- /b.rs |
253 | struct Foo; | 300 | struct Foo; |
254 | ", | 301 | ", |
255 | "Foo STRUCT_DEF FileId(2) [0; 11) [7; 10)", | 302 | "Foo STRUCT_DEF FileId(2) [0; 11) [7; 10)", |
303 | "struct Foo;|Foo", | ||
256 | ); | 304 | ); |
257 | } | 305 | } |
258 | 306 | ||
259 | #[test] | 307 | #[test] |
260 | fn goto_definition_works_for_module_declaration() { | 308 | fn goto_def_for_module_declaration() { |
261 | check_goto( | 309 | check_goto( |
262 | " | 310 | " |
263 | //- /lib.rs | 311 | //- /lib.rs |
264 | mod <|>foo; | 312 | mod <|>foo; |
313 | |||
265 | //- /foo.rs | 314 | //- /foo.rs |
266 | // empty | 315 | // empty |
267 | ", | 316 | ", |
268 | "foo SOURCE_FILE FileId(2) [0; 10)", | 317 | "foo SOURCE_FILE FileId(2) [0; 10)", |
318 | "// empty\n\n", | ||
269 | ); | 319 | ); |
270 | 320 | ||
271 | check_goto( | 321 | check_goto( |
272 | " | 322 | " |
273 | //- /lib.rs | 323 | //- /lib.rs |
274 | mod <|>foo; | 324 | mod <|>foo; |
325 | |||
275 | //- /foo/mod.rs | 326 | //- /foo/mod.rs |
276 | // empty | 327 | // empty |
277 | ", | 328 | ", |
278 | "foo SOURCE_FILE FileId(2) [0; 10)", | 329 | "foo SOURCE_FILE FileId(2) [0; 10)", |
330 | "// empty\n\n", | ||
279 | ); | 331 | ); |
280 | } | 332 | } |
281 | 333 | ||
282 | #[test] | 334 | #[test] |
283 | fn goto_definition_works_for_macros() { | 335 | fn goto_def_for_macros() { |
284 | covers!(goto_definition_works_for_macros); | 336 | covers!(goto_def_for_macros); |
285 | check_goto( | 337 | check_goto( |
286 | " | 338 | " |
287 | //- /lib.rs | 339 | //- /lib.rs |
288 | macro_rules! foo { | 340 | macro_rules! foo { () => { () } } |
289 | () => { | ||
290 | {} | ||
291 | }; | ||
292 | } | ||
293 | 341 | ||
294 | fn bar() { | 342 | fn bar() { |
295 | <|>foo!(); | 343 | <|>foo!(); |
296 | } | 344 | } |
297 | ", | 345 | ", |
298 | "foo MACRO_CALL FileId(1) [0; 50) [13; 16)", | 346 | "foo MACRO_CALL FileId(1) [0; 33) [13; 16)", |
347 | "macro_rules! foo { () => { () } }|foo", | ||
299 | ); | 348 | ); |
300 | } | 349 | } |
301 | 350 | ||
302 | #[test] | 351 | #[test] |
303 | fn goto_definition_works_for_macros_from_other_crates() { | 352 | fn goto_def_for_macros_from_other_crates() { |
304 | covers!(goto_definition_works_for_macros); | 353 | covers!(goto_def_for_macros); |
305 | check_goto( | 354 | check_goto( |
306 | " | 355 | " |
307 | //- /lib.rs | 356 | //- /lib.rs |
@@ -312,18 +361,15 @@ mod tests { | |||
312 | 361 | ||
313 | //- /foo/lib.rs | 362 | //- /foo/lib.rs |
314 | #[macro_export] | 363 | #[macro_export] |
315 | macro_rules! foo { | 364 | macro_rules! foo { () => { () } } |
316 | () => { | ||
317 | {} | ||
318 | }; | ||
319 | } | ||
320 | ", | 365 | ", |
321 | "foo MACRO_CALL FileId(2) [0; 66) [29; 32)", | 366 | "foo MACRO_CALL FileId(2) [0; 49) [29; 32)", |
367 | "#[macro_export]\nmacro_rules! foo { () => { () } }|foo", | ||
322 | ); | 368 | ); |
323 | } | 369 | } |
324 | 370 | ||
325 | #[test] | 371 | #[test] |
326 | fn goto_definition_works_for_macros_in_use_tree() { | 372 | fn goto_def_for_macros_in_use_tree() { |
327 | check_goto( | 373 | check_goto( |
328 | " | 374 | " |
329 | //- /lib.rs | 375 | //- /lib.rs |
@@ -331,18 +377,15 @@ mod tests { | |||
331 | 377 | ||
332 | //- /foo/lib.rs | 378 | //- /foo/lib.rs |
333 | #[macro_export] | 379 | #[macro_export] |
334 | macro_rules! foo { | 380 | macro_rules! foo { () => { () } } |
335 | () => { | ||
336 | {} | ||
337 | }; | ||
338 | } | ||
339 | ", | 381 | ", |
340 | "foo MACRO_CALL FileId(2) [0; 66) [29; 32)", | 382 | "foo MACRO_CALL FileId(2) [0; 49) [29; 32)", |
383 | "#[macro_export]\nmacro_rules! foo { () => { () } }|foo", | ||
341 | ); | 384 | ); |
342 | } | 385 | } |
343 | 386 | ||
344 | #[test] | 387 | #[test] |
345 | fn goto_definition_works_for_macro_defined_fn_with_arg() { | 388 | fn goto_def_for_macro_defined_fn_with_arg() { |
346 | check_goto( | 389 | check_goto( |
347 | " | 390 | " |
348 | //- /lib.rs | 391 | //- /lib.rs |
@@ -350,20 +393,19 @@ mod tests { | |||
350 | ($name:ident) => (fn $name() {}) | 393 | ($name:ident) => (fn $name() {}) |
351 | } | 394 | } |
352 | 395 | ||
353 | define_fn!( | 396 | define_fn!(foo); |
354 | foo | ||
355 | ) | ||
356 | 397 | ||
357 | fn bar() { | 398 | fn bar() { |
358 | <|>foo(); | 399 | <|>foo(); |
359 | } | 400 | } |
360 | ", | 401 | ", |
361 | "foo FN_DEF FileId(1) [80; 83) [80; 83)", | 402 | "foo FN_DEF FileId(1) [64; 80) [75; 78)", |
403 | "define_fn!(foo);|foo", | ||
362 | ); | 404 | ); |
363 | } | 405 | } |
364 | 406 | ||
365 | #[test] | 407 | #[test] |
366 | fn goto_definition_works_for_macro_defined_fn_no_arg() { | 408 | fn goto_def_for_macro_defined_fn_no_arg() { |
367 | check_goto( | 409 | check_goto( |
368 | " | 410 | " |
369 | //- /lib.rs | 411 | //- /lib.rs |
@@ -377,32 +419,70 @@ mod tests { | |||
377 | <|>foo(); | 419 | <|>foo(); |
378 | } | 420 | } |
379 | ", | 421 | ", |
380 | "foo FN_DEF FileId(1) [39; 42) [39; 42)", | 422 | "foo FN_DEF FileId(1) [51; 64) [51; 64)", |
423 | "define_fn!();|define_fn!();", | ||
381 | ); | 424 | ); |
382 | } | 425 | } |
383 | 426 | ||
384 | #[test] | 427 | #[test] |
385 | fn goto_definition_works_for_methods() { | 428 | fn goto_definition_works_for_macro_inside_pattern() { |
386 | covers!(goto_definition_works_for_methods); | 429 | check_goto( |
430 | " | ||
431 | //- /lib.rs | ||
432 | macro_rules! foo {() => {0}} | ||
433 | |||
434 | fn bar() { | ||
435 | match (0,1) { | ||
436 | (<|>foo!(), _) => {} | ||
437 | } | ||
438 | } | ||
439 | ", | ||
440 | "foo MACRO_CALL FileId(1) [0; 28) [13; 16)", | ||
441 | "macro_rules! foo {() => {0}}|foo", | ||
442 | ); | ||
443 | } | ||
444 | |||
445 | #[test] | ||
446 | fn goto_definition_works_for_macro_inside_match_arm_lhs() { | ||
447 | check_goto( | ||
448 | " | ||
449 | //- /lib.rs | ||
450 | macro_rules! foo {() => {0}} | ||
451 | |||
452 | fn bar() { | ||
453 | match 0 { | ||
454 | <|>foo!() => {} | ||
455 | } | ||
456 | } | ||
457 | ", | ||
458 | "foo MACRO_CALL FileId(1) [0; 28) [13; 16)", | ||
459 | "macro_rules! foo {() => {0}}|foo", | ||
460 | ); | ||
461 | } | ||
462 | |||
463 | #[test] | ||
464 | fn goto_def_for_methods() { | ||
465 | covers!(goto_def_for_methods); | ||
387 | check_goto( | 466 | check_goto( |
388 | " | 467 | " |
389 | //- /lib.rs | 468 | //- /lib.rs |
390 | struct Foo; | 469 | struct Foo; |
391 | impl Foo { | 470 | impl Foo { |
392 | fn frobnicate(&self) { } | 471 | fn frobnicate(&self) { } |
393 | } | 472 | } |
394 | 473 | ||
395 | fn bar(foo: &Foo) { | 474 | fn bar(foo: &Foo) { |
396 | foo.frobnicate<|>(); | 475 | foo.frobnicate<|>(); |
397 | } | 476 | } |
398 | ", | 477 | ", |
399 | "frobnicate FN_DEF FileId(1) [27; 52) [30; 40)", | 478 | "frobnicate FN_DEF FileId(1) [27; 51) [30; 40)", |
479 | "fn frobnicate(&self) { }|frobnicate", | ||
400 | ); | 480 | ); |
401 | } | 481 | } |
402 | 482 | ||
403 | #[test] | 483 | #[test] |
404 | fn goto_definition_works_for_fields() { | 484 | fn goto_def_for_fields() { |
405 | covers!(goto_definition_works_for_fields); | 485 | covers!(goto_def_for_fields); |
406 | check_goto( | 486 | check_goto( |
407 | " | 487 | " |
408 | //- /lib.rs | 488 | //- /lib.rs |
@@ -415,12 +495,13 @@ mod tests { | |||
415 | } | 495 | } |
416 | ", | 496 | ", |
417 | "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)", | 497 | "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)", |
498 | "spam: u32|spam", | ||
418 | ); | 499 | ); |
419 | } | 500 | } |
420 | 501 | ||
421 | #[test] | 502 | #[test] |
422 | fn goto_definition_works_for_record_fields() { | 503 | fn goto_def_for_record_fields() { |
423 | covers!(goto_definition_works_for_record_fields); | 504 | covers!(goto_def_for_record_fields); |
424 | check_goto( | 505 | check_goto( |
425 | " | 506 | " |
426 | //- /lib.rs | 507 | //- /lib.rs |
@@ -435,29 +516,48 @@ mod tests { | |||
435 | } | 516 | } |
436 | ", | 517 | ", |
437 | "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)", | 518 | "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)", |
519 | "spam: u32|spam", | ||
520 | ); | ||
521 | } | ||
522 | |||
523 | #[test] | ||
524 | fn goto_for_tuple_fields() { | ||
525 | check_goto( | ||
526 | " | ||
527 | //- /lib.rs | ||
528 | struct Foo(u32); | ||
529 | |||
530 | fn bar() { | ||
531 | let foo = Foo(0); | ||
532 | foo.<|>0; | ||
533 | } | ||
534 | ", | ||
535 | "TUPLE_FIELD_DEF FileId(1) [11; 14)", | ||
536 | "u32", | ||
438 | ); | 537 | ); |
439 | } | 538 | } |
440 | 539 | ||
441 | #[test] | 540 | #[test] |
442 | fn goto_definition_works_for_ufcs_inherent_methods() { | 541 | fn goto_def_for_ufcs_inherent_methods() { |
443 | check_goto( | 542 | check_goto( |
444 | " | 543 | " |
445 | //- /lib.rs | 544 | //- /lib.rs |
446 | struct Foo; | 545 | struct Foo; |
447 | impl Foo { | 546 | impl Foo { |
448 | fn frobnicate() { } | 547 | fn frobnicate() { } |
449 | } | 548 | } |
450 | 549 | ||
451 | fn bar(foo: &Foo) { | 550 | fn bar(foo: &Foo) { |
452 | Foo::frobnicate<|>(); | 551 | Foo::frobnicate<|>(); |
453 | } | 552 | } |
454 | ", | 553 | ", |
455 | "frobnicate FN_DEF FileId(1) [27; 47) [30; 40)", | 554 | "frobnicate FN_DEF FileId(1) [27; 46) [30; 40)", |
555 | "fn frobnicate() { }|frobnicate", | ||
456 | ); | 556 | ); |
457 | } | 557 | } |
458 | 558 | ||
459 | #[test] | 559 | #[test] |
460 | fn goto_definition_works_for_ufcs_trait_methods_through_traits() { | 560 | fn goto_def_for_ufcs_trait_methods_through_traits() { |
461 | check_goto( | 561 | check_goto( |
462 | " | 562 | " |
463 | //- /lib.rs | 563 | //- /lib.rs |
@@ -470,11 +570,12 @@ mod tests { | |||
470 | } | 570 | } |
471 | ", | 571 | ", |
472 | "frobnicate FN_DEF FileId(1) [16; 32) [19; 29)", | 572 | "frobnicate FN_DEF FileId(1) [16; 32) [19; 29)", |
573 | "fn frobnicate();|frobnicate", | ||
473 | ); | 574 | ); |
474 | } | 575 | } |
475 | 576 | ||
476 | #[test] | 577 | #[test] |
477 | fn goto_definition_works_for_ufcs_trait_methods_through_self() { | 578 | fn goto_def_for_ufcs_trait_methods_through_self() { |
478 | check_goto( | 579 | check_goto( |
479 | " | 580 | " |
480 | //- /lib.rs | 581 | //- /lib.rs |
@@ -489,6 +590,7 @@ mod tests { | |||
489 | } | 590 | } |
490 | ", | 591 | ", |
491 | "frobnicate FN_DEF FileId(1) [30; 46) [33; 43)", | 592 | "frobnicate FN_DEF FileId(1) [30; 46) [33; 43)", |
593 | "fn frobnicate();|frobnicate", | ||
492 | ); | 594 | ); |
493 | } | 595 | } |
494 | 596 | ||
@@ -505,6 +607,7 @@ mod tests { | |||
505 | } | 607 | } |
506 | ", | 608 | ", |
507 | "impl IMPL_BLOCK FileId(1) [12; 73)", | 609 | "impl IMPL_BLOCK FileId(1) [12; 73)", |
610 | "impl Foo {...}", | ||
508 | ); | 611 | ); |
509 | 612 | ||
510 | check_goto( | 613 | check_goto( |
@@ -518,6 +621,7 @@ mod tests { | |||
518 | } | 621 | } |
519 | ", | 622 | ", |
520 | "impl IMPL_BLOCK FileId(1) [12; 73)", | 623 | "impl IMPL_BLOCK FileId(1) [12; 73)", |
624 | "impl Foo {...}", | ||
521 | ); | 625 | ); |
522 | 626 | ||
523 | check_goto( | 627 | check_goto( |
@@ -531,6 +635,7 @@ mod tests { | |||
531 | } | 635 | } |
532 | ", | 636 | ", |
533 | "impl IMPL_BLOCK FileId(1) [15; 75)", | 637 | "impl IMPL_BLOCK FileId(1) [15; 75)", |
638 | "impl Foo {...}", | ||
534 | ); | 639 | ); |
535 | 640 | ||
536 | check_goto( | 641 | check_goto( |
@@ -543,6 +648,7 @@ mod tests { | |||
543 | } | 648 | } |
544 | ", | 649 | ", |
545 | "impl IMPL_BLOCK FileId(1) [15; 62)", | 650 | "impl IMPL_BLOCK FileId(1) [15; 62)", |
651 | "impl Foo {...}", | ||
546 | ); | 652 | ); |
547 | } | 653 | } |
548 | 654 | ||
@@ -562,6 +668,7 @@ mod tests { | |||
562 | } | 668 | } |
563 | ", | 669 | ", |
564 | "impl IMPL_BLOCK FileId(1) [49; 115)", | 670 | "impl IMPL_BLOCK FileId(1) [49; 115)", |
671 | "impl Make for Foo {...}", | ||
565 | ); | 672 | ); |
566 | 673 | ||
567 | check_goto( | 674 | check_goto( |
@@ -578,17 +685,19 @@ mod tests { | |||
578 | } | 685 | } |
579 | ", | 686 | ", |
580 | "impl IMPL_BLOCK FileId(1) [49; 115)", | 687 | "impl IMPL_BLOCK FileId(1) [49; 115)", |
688 | "impl Make for Foo {...}", | ||
581 | ); | 689 | ); |
582 | } | 690 | } |
583 | 691 | ||
584 | #[test] | 692 | #[test] |
585 | fn goto_definition_works_when_used_on_definition_name_itself() { | 693 | fn goto_def_when_used_on_definition_name_itself() { |
586 | check_goto( | 694 | check_goto( |
587 | " | 695 | " |
588 | //- /lib.rs | 696 | //- /lib.rs |
589 | struct Foo<|> { value: u32 } | 697 | struct Foo<|> { value: u32 } |
590 | ", | 698 | ", |
591 | "Foo STRUCT_DEF FileId(1) [0; 25) [7; 10)", | 699 | "Foo STRUCT_DEF FileId(1) [0; 25) [7; 10)", |
700 | "struct Foo { value: u32 }|Foo", | ||
592 | ); | 701 | ); |
593 | 702 | ||
594 | check_goto( | 703 | check_goto( |
@@ -599,15 +708,16 @@ mod tests { | |||
599 | } | 708 | } |
600 | "#, | 709 | "#, |
601 | "field RECORD_FIELD_DEF FileId(1) [17; 30) [17; 22)", | 710 | "field RECORD_FIELD_DEF FileId(1) [17; 30) [17; 22)", |
711 | "field: string|field", | ||
602 | ); | 712 | ); |
603 | 713 | ||
604 | check_goto( | 714 | check_goto( |
605 | " | 715 | " |
606 | //- /lib.rs | 716 | //- /lib.rs |
607 | fn foo_test<|>() { | 717 | fn foo_test<|>() { } |
608 | } | ||
609 | ", | 718 | ", |
610 | "foo_test FN_DEF FileId(1) [0; 17) [3; 11)", | 719 | "foo_test FN_DEF FileId(1) [0; 17) [3; 11)", |
720 | "fn foo_test() { }|foo_test", | ||
611 | ); | 721 | ); |
612 | 722 | ||
613 | check_goto( | 723 | check_goto( |
@@ -618,6 +728,7 @@ mod tests { | |||
618 | } | 728 | } |
619 | ", | 729 | ", |
620 | "Foo ENUM_DEF FileId(1) [0; 25) [5; 8)", | 730 | "Foo ENUM_DEF FileId(1) [0; 25) [5; 8)", |
731 | "enum Foo {...}|Foo", | ||
621 | ); | 732 | ); |
622 | 733 | ||
623 | check_goto( | 734 | check_goto( |
@@ -630,22 +741,25 @@ mod tests { | |||
630 | } | 741 | } |
631 | ", | 742 | ", |
632 | "Variant2 ENUM_VARIANT FileId(1) [29; 37) [29; 37)", | 743 | "Variant2 ENUM_VARIANT FileId(1) [29; 37) [29; 37)", |
744 | "Variant2|Variant2", | ||
633 | ); | 745 | ); |
634 | 746 | ||
635 | check_goto( | 747 | check_goto( |
636 | r#" | 748 | r#" |
637 | //- /lib.rs | 749 | //- /lib.rs |
638 | static inner<|>: &str = ""; | 750 | static INNER<|>: &str = ""; |
639 | "#, | 751 | "#, |
640 | "inner STATIC_DEF FileId(1) [0; 24) [7; 12)", | 752 | "INNER STATIC_DEF FileId(1) [0; 24) [7; 12)", |
753 | "static INNER: &str = \"\";|INNER", | ||
641 | ); | 754 | ); |
642 | 755 | ||
643 | check_goto( | 756 | check_goto( |
644 | r#" | 757 | r#" |
645 | //- /lib.rs | 758 | //- /lib.rs |
646 | const inner<|>: &str = ""; | 759 | const INNER<|>: &str = ""; |
647 | "#, | 760 | "#, |
648 | "inner CONST_DEF FileId(1) [0; 23) [6; 11)", | 761 | "INNER CONST_DEF FileId(1) [0; 23) [6; 11)", |
762 | "const INNER: &str = \"\";|INNER", | ||
649 | ); | 763 | ); |
650 | 764 | ||
651 | check_goto( | 765 | check_goto( |
@@ -654,24 +768,25 @@ mod tests { | |||
654 | type Thing<|> = Option<()>; | 768 | type Thing<|> = Option<()>; |
655 | "#, | 769 | "#, |
656 | "Thing TYPE_ALIAS_DEF FileId(1) [0; 24) [5; 10)", | 770 | "Thing TYPE_ALIAS_DEF FileId(1) [0; 24) [5; 10)", |
771 | "type Thing = Option<()>;|Thing", | ||
657 | ); | 772 | ); |
658 | 773 | ||
659 | check_goto( | 774 | check_goto( |
660 | r#" | 775 | r#" |
661 | //- /lib.rs | 776 | //- /lib.rs |
662 | trait Foo<|> { | 777 | trait Foo<|> { } |
663 | } | ||
664 | "#, | 778 | "#, |
665 | "Foo TRAIT_DEF FileId(1) [0; 13) [6; 9)", | 779 | "Foo TRAIT_DEF FileId(1) [0; 13) [6; 9)", |
780 | "trait Foo { }|Foo", | ||
666 | ); | 781 | ); |
667 | 782 | ||
668 | check_goto( | 783 | check_goto( |
669 | r#" | 784 | r#" |
670 | //- /lib.rs | 785 | //- /lib.rs |
671 | mod bar<|> { | 786 | mod bar<|> { } |
672 | } | ||
673 | "#, | 787 | "#, |
674 | "bar MODULE FileId(1) [0; 11) [4; 7)", | 788 | "bar MODULE FileId(1) [0; 11) [4; 7)", |
789 | "mod bar { }|bar", | ||
675 | ); | 790 | ); |
676 | } | 791 | } |
677 | 792 | ||
@@ -689,8 +804,128 @@ mod tests { | |||
689 | fo<|>o(); | 804 | fo<|>o(); |
690 | } | 805 | } |
691 | } | 806 | } |
807 | mod confuse_index { fn foo(); } | ||
692 | ", | 808 | ", |
693 | "foo FN_DEF FileId(1) [52; 63) [55; 58)", | 809 | "foo FN_DEF FileId(1) [52; 63) [55; 58)", |
810 | "fn foo() {}|foo", | ||
694 | ); | 811 | ); |
695 | } | 812 | } |
813 | |||
814 | #[test] | ||
815 | fn goto_through_format() { | ||
816 | check_goto( | ||
817 | " | ||
818 | //- /lib.rs | ||
819 | #[macro_export] | ||
820 | macro_rules! format { | ||
821 | ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*))) | ||
822 | } | ||
823 | #[rustc_builtin_macro] | ||
824 | #[macro_export] | ||
825 | macro_rules! format_args { | ||
826 | ($fmt:expr) => ({ /* compiler built-in */ }); | ||
827 | ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) | ||
828 | } | ||
829 | pub mod __export { | ||
830 | pub use crate::format_args; | ||
831 | fn foo() {} // for index confusion | ||
832 | } | ||
833 | fn foo() -> i8 {} | ||
834 | fn test() { | ||
835 | format!(\"{}\", fo<|>o()) | ||
836 | } | ||
837 | ", | ||
838 | "foo FN_DEF FileId(1) [398; 415) [401; 404)", | ||
839 | "fn foo() -> i8 {}|foo", | ||
840 | ); | ||
841 | } | ||
842 | |||
843 | #[test] | ||
844 | fn goto_for_type_param() { | ||
845 | check_goto( | ||
846 | " | ||
847 | //- /lib.rs | ||
848 | struct Foo<T> { | ||
849 | t: <|>T, | ||
850 | } | ||
851 | ", | ||
852 | "T TYPE_PARAM FileId(1) [11; 12)", | ||
853 | "T", | ||
854 | ); | ||
855 | } | ||
856 | |||
857 | #[test] | ||
858 | fn goto_within_macro() { | ||
859 | check_goto( | ||
860 | " | ||
861 | //- /lib.rs | ||
862 | macro_rules! id { | ||
863 | ($($tt:tt)*) => ($($tt)*) | ||
864 | } | ||
865 | |||
866 | fn foo() { | ||
867 | let x = 1; | ||
868 | id!({ | ||
869 | let y = <|>x; | ||
870 | let z = y; | ||
871 | }); | ||
872 | } | ||
873 | ", | ||
874 | "x BIND_PAT FileId(1) [69; 70)", | ||
875 | "x", | ||
876 | ); | ||
877 | |||
878 | check_goto( | ||
879 | " | ||
880 | //- /lib.rs | ||
881 | macro_rules! id { | ||
882 | ($($tt:tt)*) => ($($tt)*) | ||
883 | } | ||
884 | |||
885 | fn foo() { | ||
886 | let x = 1; | ||
887 | id!({ | ||
888 | let y = x; | ||
889 | let z = <|>y; | ||
890 | }); | ||
891 | } | ||
892 | ", | ||
893 | "y BIND_PAT FileId(1) [98; 99)", | ||
894 | "y", | ||
895 | ); | ||
896 | } | ||
897 | |||
898 | #[test] | ||
899 | fn goto_def_in_local_fn() { | ||
900 | check_goto( | ||
901 | " | ||
902 | //- /lib.rs | ||
903 | fn main() { | ||
904 | fn foo() { | ||
905 | let x = 92; | ||
906 | <|>x; | ||
907 | } | ||
908 | } | ||
909 | ", | ||
910 | "x BIND_PAT FileId(1) [39; 40)", | ||
911 | "x", | ||
912 | ); | ||
913 | } | ||
914 | |||
915 | #[test] | ||
916 | fn goto_def_for_field_init_shorthand() { | ||
917 | covers!(goto_def_for_field_init_shorthand); | ||
918 | check_goto( | ||
919 | " | ||
920 | //- /lib.rs | ||
921 | struct Foo { x: i32 } | ||
922 | fn main() { | ||
923 | let x = 92; | ||
924 | Foo { x<|> }; | ||
925 | } | ||
926 | ", | ||
927 | "x RECORD_FIELD_DEF FileId(1) [13; 19) [13; 14)", | ||
928 | "x: i32|x", | ||
929 | ) | ||
930 | } | ||
696 | } | 931 | } |
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs index 992a08809..ce8b6c72a 100644 --- a/crates/ra_ide/src/goto_type_definition.rs +++ b/crates/ra_ide/src/goto_type_definition.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::db::AstDatabase; | 3 | use hir::db::AstDatabase; |
4 | use ra_syntax::{ast, AstNode}; | 4 | use ra_syntax::{ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; |
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
7 | db::RootDatabase, display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget, | 7 | db::RootDatabase, display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget, |
@@ -13,7 +13,7 @@ pub(crate) fn goto_type_definition( | |||
13 | position: FilePosition, | 13 | position: FilePosition, |
14 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { | 14 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { |
15 | let file = db.parse_or_expand(position.file_id.into())?; | 15 | let file = db.parse_or_expand(position.file_id.into())?; |
16 | let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?; | 16 | let token = pick_best(file.token_at_offset(position.offset))?; |
17 | let token = descend_into_macros(db, position.file_id, token); | 17 | let token = descend_into_macros(db, position.file_id, token); |
18 | 18 | ||
19 | let node = token.value.ancestors().find_map(|token| { | 19 | let node = token.value.ancestors().find_map(|token| { |
@@ -41,6 +41,17 @@ pub(crate) fn goto_type_definition( | |||
41 | Some(RangeInfo::new(node.text_range(), vec![nav])) | 41 | Some(RangeInfo::new(node.text_range(), vec![nav])) |
42 | } | 42 | } |
43 | 43 | ||
44 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | ||
45 | return tokens.max_by_key(priority); | ||
46 | fn priority(n: &SyntaxToken) -> usize { | ||
47 | match n.kind() { | ||
48 | IDENT | INT_NUMBER => 2, | ||
49 | kind if kind.is_trivia() => 0, | ||
50 | _ => 1, | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | |||
44 | #[cfg(test)] | 55 | #[cfg(test)] |
45 | mod tests { | 56 | mod tests { |
46 | use crate::mock_analysis::analysis_and_position; | 57 | use crate::mock_analysis::analysis_and_position; |
@@ -102,4 +113,32 @@ mod tests { | |||
102 | "Foo STRUCT_DEF FileId(1) [52; 65) [59; 62)", | 113 | "Foo STRUCT_DEF FileId(1) [52; 65) [59; 62)", |
103 | ); | 114 | ); |
104 | } | 115 | } |
116 | |||
117 | #[test] | ||
118 | fn goto_type_definition_for_param() { | ||
119 | check_goto( | ||
120 | " | ||
121 | //- /lib.rs | ||
122 | struct Foo; | ||
123 | fn foo(<|>f: Foo) {} | ||
124 | ", | ||
125 | "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)", | ||
126 | ); | ||
127 | } | ||
128 | |||
129 | #[test] | ||
130 | fn goto_type_definition_for_tuple_field() { | ||
131 | check_goto( | ||
132 | " | ||
133 | //- /lib.rs | ||
134 | struct Foo; | ||
135 | struct Bar(Foo); | ||
136 | fn foo() { | ||
137 | let bar = Bar(Foo); | ||
138 | bar.<|>0; | ||
139 | } | ||
140 | ", | ||
141 | "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)", | ||
142 | ); | ||
143 | } | ||
105 | } | 144 | } |
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 260a7b869..35e39f965 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -6,14 +6,13 @@ use ra_syntax::{ | |||
6 | algo::find_covering_element, | 6 | algo::find_covering_element, |
7 | ast::{self, DocCommentsOwner}, | 7 | ast::{self, DocCommentsOwner}, |
8 | match_ast, AstNode, | 8 | match_ast, AstNode, |
9 | SyntaxKind::*, | ||
10 | SyntaxToken, TokenAtOffset, | ||
9 | }; | 11 | }; |
10 | 12 | ||
11 | use crate::{ | 13 | use crate::{ |
12 | db::RootDatabase, | 14 | db::RootDatabase, |
13 | display::{ | 15 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, |
14 | description_from_symbol, docs_from_symbol, macro_label, rust_code_markup, | ||
15 | rust_code_markup_with_doc, ShortLabel, | ||
16 | }, | ||
17 | expand::descend_into_macros, | 16 | expand::descend_into_macros, |
18 | references::{classify_name, classify_name_ref, NameKind, NameKind::*}, | 17 | references::{classify_name, classify_name_ref, NameKind, NameKind::*}, |
19 | FilePosition, FileRange, RangeInfo, | 18 | FilePosition, FileRange, RangeInfo, |
@@ -93,11 +92,7 @@ fn hover_text(docs: Option<String>, desc: Option<String>) -> Option<String> { | |||
93 | } | 92 | } |
94 | } | 93 | } |
95 | 94 | ||
96 | fn hover_text_from_name_kind( | 95 | fn hover_text_from_name_kind(db: &RootDatabase, name_kind: NameKind) -> Option<String> { |
97 | db: &RootDatabase, | ||
98 | name_kind: NameKind, | ||
99 | no_fallback: &mut bool, | ||
100 | ) -> Option<String> { | ||
101 | return match name_kind { | 96 | return match name_kind { |
102 | Macro(it) => { | 97 | Macro(it) => { |
103 | let src = it.source(db); | 98 | let src = it.source(db); |
@@ -133,12 +128,8 @@ fn hover_text_from_name_kind( | |||
133 | hir::ModuleDef::TypeAlias(it) => from_def_source(db, it), | 128 | hir::ModuleDef::TypeAlias(it) => from_def_source(db, it), |
134 | hir::ModuleDef::BuiltinType(it) => Some(it.to_string()), | 129 | hir::ModuleDef::BuiltinType(it) => Some(it.to_string()), |
135 | }, | 130 | }, |
136 | Local(_) => { | 131 | Local(_) => None, |
137 | // Hover for these shows type names | 132 | TypeParam(_) | SelfType(_) => { |
138 | *no_fallback = true; | ||
139 | None | ||
140 | } | ||
141 | GenericParam(_) | SelfType(_) => { | ||
142 | // FIXME: Hover for generic param | 133 | // FIXME: Hover for generic param |
143 | None | 134 | None |
144 | } | 135 | } |
@@ -156,68 +147,55 @@ fn hover_text_from_name_kind( | |||
156 | 147 | ||
157 | pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { | 148 | pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { |
158 | let file = db.parse_or_expand(position.file_id.into())?; | 149 | let file = db.parse_or_expand(position.file_id.into())?; |
159 | let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?; | 150 | let token = pick_best(file.token_at_offset(position.offset))?; |
160 | let token = descend_into_macros(db, position.file_id, token); | 151 | let token = descend_into_macros(db, position.file_id, token); |
161 | 152 | ||
162 | let mut res = HoverResult::new(); | 153 | let mut res = HoverResult::new(); |
163 | 154 | ||
164 | let mut range = match_ast! { | 155 | if let Some((range, name_kind)) = match_ast! { |
165 | match (token.value.parent()) { | 156 | match (token.value.parent()) { |
166 | ast::NameRef(name_ref) => { | 157 | ast::NameRef(name_ref) => { |
167 | let mut no_fallback = false; | 158 | classify_name_ref(db, token.with_value(&name_ref)).map(|d| (name_ref.syntax().text_range(), d.kind)) |
168 | if let Some(name_kind) = | ||
169 | classify_name_ref(db, token.with_value(&name_ref)).map(|d| d.kind) | ||
170 | { | ||
171 | res.extend(hover_text_from_name_kind(db, name_kind, &mut no_fallback)) | ||
172 | } | ||
173 | |||
174 | if res.is_empty() && !no_fallback { | ||
175 | // Fallback index based approach: | ||
176 | let symbols = crate::symbol_index::index_resolve(db, &name_ref); | ||
177 | for sym in symbols { | ||
178 | let docs = docs_from_symbol(db, &sym); | ||
179 | let desc = description_from_symbol(db, &sym); | ||
180 | res.extend(hover_text(docs, desc)); | ||
181 | } | ||
182 | } | ||
183 | |||
184 | if !res.is_empty() { | ||
185 | Some(name_ref.syntax().text_range()) | ||
186 | } else { | ||
187 | None | ||
188 | } | ||
189 | }, | 159 | }, |
190 | ast::Name(name) => { | 160 | ast::Name(name) => { |
191 | if let Some(name_kind) = classify_name(db, token.with_value(&name)).map(|d| d.kind) { | 161 | classify_name(db, token.with_value(&name)).map(|d| (name.syntax().text_range(), d.kind)) |
192 | res.extend(hover_text_from_name_kind(db, name_kind, &mut true)); | ||
193 | } | ||
194 | |||
195 | if !res.is_empty() { | ||
196 | Some(name.syntax().text_range()) | ||
197 | } else { | ||
198 | None | ||
199 | } | ||
200 | }, | 162 | }, |
201 | _ => None, | 163 | _ => None, |
202 | } | 164 | } |
203 | }; | 165 | } { |
166 | res.extend(hover_text_from_name_kind(db, name_kind)); | ||
204 | 167 | ||
205 | if range.is_none() { | 168 | if !res.is_empty() { |
206 | let node = token.value.ancestors().find(|n| { | 169 | return Some(RangeInfo::new(range, res)); |
207 | ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some() | 170 | } |
208 | })?; | 171 | } |
209 | let frange = FileRange { file_id: position.file_id, range: node.text_range() }; | ||
210 | res.extend(type_of(db, frange).map(rust_code_markup)); | ||
211 | range = Some(node.text_range()); | ||
212 | }; | ||
213 | 172 | ||
214 | let range = range?; | 173 | let node = token |
174 | .value | ||
175 | .ancestors() | ||
176 | .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; | ||
177 | let frange = FileRange { file_id: position.file_id, range: node.text_range() }; | ||
178 | res.extend(type_of(db, frange).map(rust_code_markup)); | ||
215 | if res.is_empty() { | 179 | if res.is_empty() { |
216 | return None; | 180 | return None; |
217 | } | 181 | } |
182 | let range = node.text_range(); | ||
183 | |||
218 | Some(RangeInfo::new(range, res)) | 184 | Some(RangeInfo::new(range, res)) |
219 | } | 185 | } |
220 | 186 | ||
187 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | ||
188 | return tokens.max_by_key(priority); | ||
189 | fn priority(n: &SyntaxToken) -> usize { | ||
190 | match n.kind() { | ||
191 | IDENT | INT_NUMBER => 3, | ||
192 | L_PAREN | R_PAREN => 2, | ||
193 | kind if kind.is_trivia() => 0, | ||
194 | _ => 1, | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | |||
221 | pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { | 199 | pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { |
222 | let parse = db.parse(frange.file_id); | 200 | let parse = db.parse(frange.file_id); |
223 | let leaf_node = find_covering_element(parse.tree().syntax(), frange.range); | 201 | let leaf_node = find_covering_element(parse.tree().syntax(), frange.range); |
@@ -227,7 +205,7 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { | |||
227 | .take_while(|it| it.text_range() == leaf_node.text_range()) | 205 | .take_while(|it| it.text_range() == leaf_node.text_range()) |
228 | .find(|it| ast::Expr::cast(it.clone()).is_some() || ast::Pat::cast(it.clone()).is_some())?; | 206 | .find(|it| ast::Expr::cast(it.clone()).is_some() || ast::Pat::cast(it.clone()).is_some())?; |
229 | let analyzer = | 207 | let analyzer = |
230 | hir::SourceAnalyzer::new(db, hir::Source::new(frange.file_id.into(), &node), None); | 208 | hir::SourceAnalyzer::new(db, hir::InFile::new(frange.file_id.into(), &node), None); |
231 | let ty = if let Some(ty) = ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e)) | 209 | let ty = if let Some(ty) = ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e)) |
232 | { | 210 | { |
233 | ty | 211 | ty |
@@ -236,7 +214,7 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { | |||
236 | } else { | 214 | } else { |
237 | return None; | 215 | return None; |
238 | }; | 216 | }; |
239 | Some(ty.display(db).to_string()) | 217 | Some(ty.display_truncated(db, None).to_string()) |
240 | } | 218 | } |
241 | 219 | ||
242 | #[cfg(test)] | 220 | #[cfg(test)] |
@@ -300,7 +278,7 @@ mod tests { | |||
300 | &["pub fn foo() -> u32"], | 278 | &["pub fn foo() -> u32"], |
301 | ); | 279 | ); |
302 | 280 | ||
303 | // Multiple results | 281 | // Multiple candidates but results are ambiguous. |
304 | check_hover_result( | 282 | check_hover_result( |
305 | r#" | 283 | r#" |
306 | //- /a.rs | 284 | //- /a.rs |
@@ -321,7 +299,7 @@ mod tests { | |||
321 | let foo_test = fo<|>o(); | 299 | let foo_test = fo<|>o(); |
322 | } | 300 | } |
323 | "#, | 301 | "#, |
324 | &["pub fn foo() -> &str", "pub fn foo() -> u32", "pub fn foo(a: u32, b: u32)"], | 302 | &["{unknown}"], |
325 | ); | 303 | ); |
326 | } | 304 | } |
327 | 305 | ||
@@ -411,6 +389,23 @@ mod tests { | |||
411 | } | 389 | } |
412 | 390 | ||
413 | #[test] | 391 | #[test] |
392 | fn hover_omits_default_generic_types() { | ||
393 | check_hover_result( | ||
394 | r#" | ||
395 | //- /main.rs | ||
396 | struct Test<K, T = u8> { | ||
397 | k: K, | ||
398 | t: T, | ||
399 | } | ||
400 | |||
401 | fn main() { | ||
402 | let zz<|> = Test { t: 23, k: 33 }; | ||
403 | }"#, | ||
404 | &["Test<i32>"], | ||
405 | ); | ||
406 | } | ||
407 | |||
408 | #[test] | ||
414 | fn hover_some() { | 409 | fn hover_some() { |
415 | let (analysis, position) = single_file_with_position( | 410 | let (analysis, position) = single_file_with_position( |
416 | " | 411 | " |
@@ -505,6 +500,13 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
505 | } | 500 | } |
506 | 501 | ||
507 | #[test] | 502 | #[test] |
503 | fn hover_for_param_edge() { | ||
504 | let (analysis, position) = single_file_with_position("fn func(<|>foo: i32) {}"); | ||
505 | let hover = analysis.hover(position).unwrap().unwrap(); | ||
506 | assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); | ||
507 | } | ||
508 | |||
509 | #[test] | ||
508 | fn test_type_of_for_function() { | 510 | fn test_type_of_for_function() { |
509 | let (analysis, range) = single_file_with_range( | 511 | let (analysis, range) = single_file_with_range( |
510 | " | 512 | " |
diff --git a/crates/ra_ide/src/impls.rs b/crates/ra_ide/src/impls.rs index aa480e399..9b165ee2a 100644 --- a/crates/ra_ide/src/impls.rs +++ b/crates/ra_ide/src/impls.rs | |||
@@ -16,7 +16,7 @@ pub(crate) fn goto_implementation( | |||
16 | let src = hir::ModuleSource::from_position(db, position); | 16 | let src = hir::ModuleSource::from_position(db, position); |
17 | let module = hir::Module::from_definition( | 17 | let module = hir::Module::from_definition( |
18 | db, | 18 | db, |
19 | hir::Source { file_id: position.file_id.into(), value: src }, | 19 | hir::InFile { file_id: position.file_id.into(), value: src }, |
20 | )?; | 20 | )?; |
21 | 21 | ||
22 | if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(&syntax, position.offset) { | 22 | if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(&syntax, position.offset) { |
@@ -42,15 +42,15 @@ fn impls_for_def( | |||
42 | ) -> Option<Vec<NavigationTarget>> { | 42 | ) -> Option<Vec<NavigationTarget>> { |
43 | let ty = match node { | 43 | let ty = match node { |
44 | ast::NominalDef::StructDef(def) => { | 44 | ast::NominalDef::StructDef(def) => { |
45 | let src = hir::Source { file_id: position.file_id.into(), value: def.clone() }; | 45 | let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; |
46 | hir::Struct::from_source(db, src)?.ty(db) | 46 | hir::Struct::from_source(db, src)?.ty(db) |
47 | } | 47 | } |
48 | ast::NominalDef::EnumDef(def) => { | 48 | ast::NominalDef::EnumDef(def) => { |
49 | let src = hir::Source { file_id: position.file_id.into(), value: def.clone() }; | 49 | let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; |
50 | hir::Enum::from_source(db, src)?.ty(db) | 50 | hir::Enum::from_source(db, src)?.ty(db) |
51 | } | 51 | } |
52 | ast::NominalDef::UnionDef(def) => { | 52 | ast::NominalDef::UnionDef(def) => { |
53 | let src = hir::Source { file_id: position.file_id.into(), value: def.clone() }; | 53 | let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; |
54 | hir::Union::from_source(db, src)?.ty(db) | 54 | hir::Union::from_source(db, src)?.ty(db) |
55 | } | 55 | } |
56 | }; | 56 | }; |
@@ -73,7 +73,7 @@ fn impls_for_trait( | |||
73 | node: &ast::TraitDef, | 73 | node: &ast::TraitDef, |
74 | module: hir::Module, | 74 | module: hir::Module, |
75 | ) -> Option<Vec<NavigationTarget>> { | 75 | ) -> Option<Vec<NavigationTarget>> { |
76 | let src = hir::Source { file_id: position.file_id.into(), value: node.clone() }; | 76 | let src = hir::InFile { file_id: position.file_id.into(), value: node.clone() }; |
77 | let tr = hir::Trait::from_source(db, src)?; | 77 | let tr = hir::Trait::from_source(db, src)?; |
78 | 78 | ||
79 | let krate = module.krate(); | 79 | let krate = module.krate(); |
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 45149bf0c..c5e406977 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -1,12 +1,15 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use crate::{db::RootDatabase, FileId}; | ||
4 | use hir::{HirDisplay, SourceAnalyzer}; | 3 | use hir::{HirDisplay, SourceAnalyzer}; |
4 | use once_cell::unsync::Lazy; | ||
5 | use ra_prof::profile; | ||
5 | use ra_syntax::{ | 6 | use ra_syntax::{ |
6 | ast::{self, AstNode, TypeAscriptionOwner}, | 7 | ast::{self, AstNode, TypeAscriptionOwner}, |
7 | match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange, | 8 | match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange, |
8 | }; | 9 | }; |
9 | 10 | ||
11 | use crate::{db::RootDatabase, FileId}; | ||
12 | |||
10 | #[derive(Debug, PartialEq, Eq)] | 13 | #[derive(Debug, PartialEq, Eq)] |
11 | pub enum InlayKind { | 14 | pub enum InlayKind { |
12 | TypeHint, | 15 | TypeHint, |
@@ -27,7 +30,7 @@ pub(crate) fn inlay_hints( | |||
27 | ) -> Vec<InlayHint> { | 30 | ) -> Vec<InlayHint> { |
28 | file.syntax() | 31 | file.syntax() |
29 | .descendants() | 32 | .descendants() |
30 | .map(|node| get_inlay_hints(db, file_id, &node, max_inlay_hint_length).unwrap_or_default()) | 33 | .flat_map(|node| get_inlay_hints(db, file_id, &node, max_inlay_hint_length)) |
31 | .flatten() | 34 | .flatten() |
32 | .collect() | 35 | .collect() |
33 | } | 36 | } |
@@ -38,7 +41,9 @@ fn get_inlay_hints( | |||
38 | node: &SyntaxNode, | 41 | node: &SyntaxNode, |
39 | max_inlay_hint_length: Option<usize>, | 42 | max_inlay_hint_length: Option<usize>, |
40 | ) -> Option<Vec<InlayHint>> { | 43 | ) -> Option<Vec<InlayHint>> { |
41 | let analyzer = SourceAnalyzer::new(db, hir::Source::new(file_id.into(), node), None); | 44 | let _p = profile("get_inlay_hints"); |
45 | let analyzer = | ||
46 | Lazy::new(|| SourceAnalyzer::new(db, hir::InFile::new(file_id.into(), node), None)); | ||
42 | match_ast! { | 47 | match_ast! { |
43 | match node { | 48 | match node { |
44 | ast::LetStmt(it) => { | 49 | ast::LetStmt(it) => { |
@@ -122,18 +127,11 @@ fn get_leaf_pats(root_pat: ast::Pat) -> Vec<ast::Pat> { | |||
122 | 127 | ||
123 | while let Some(maybe_leaf_pat) = pats_to_process.pop_front() { | 128 | while let Some(maybe_leaf_pat) = pats_to_process.pop_front() { |
124 | match &maybe_leaf_pat { | 129 | match &maybe_leaf_pat { |
125 | ast::Pat::BindPat(bind_pat) => { | 130 | ast::Pat::BindPat(bind_pat) => match bind_pat.pat() { |
126 | if let Some(pat) = bind_pat.pat() { | 131 | Some(pat) => pats_to_process.push_back(pat), |
127 | pats_to_process.push_back(pat); | 132 | _ => leaf_pats.push(maybe_leaf_pat), |
128 | } else { | 133 | }, |
129 | leaf_pats.push(maybe_leaf_pat); | 134 | ast::Pat::TuplePat(tuple_pat) => pats_to_process.extend(tuple_pat.args()), |
130 | } | ||
131 | } | ||
132 | ast::Pat::TuplePat(tuple_pat) => { | ||
133 | for arg_pat in tuple_pat.args() { | ||
134 | pats_to_process.push_back(arg_pat); | ||
135 | } | ||
136 | } | ||
137 | ast::Pat::RecordPat(record_pat) => { | 135 | ast::Pat::RecordPat(record_pat) => { |
138 | if let Some(pat_list) = record_pat.record_field_pat_list() { | 136 | if let Some(pat_list) = record_pat.record_field_pat_list() { |
139 | pats_to_process.extend( | 137 | pats_to_process.extend( |
@@ -151,10 +149,9 @@ fn get_leaf_pats(root_pat: ast::Pat) -> Vec<ast::Pat> { | |||
151 | } | 149 | } |
152 | } | 150 | } |
153 | ast::Pat::TupleStructPat(tuple_struct_pat) => { | 151 | ast::Pat::TupleStructPat(tuple_struct_pat) => { |
154 | for arg_pat in tuple_struct_pat.args() { | 152 | pats_to_process.extend(tuple_struct_pat.args()) |
155 | pats_to_process.push_back(arg_pat); | ||
156 | } | ||
157 | } | 153 | } |
154 | ast::Pat::RefPat(ref_pat) => pats_to_process.extend(ref_pat.pat()), | ||
158 | _ => (), | 155 | _ => (), |
159 | } | 156 | } |
160 | } | 157 | } |
@@ -163,9 +160,36 @@ fn get_leaf_pats(root_pat: ast::Pat) -> Vec<ast::Pat> { | |||
163 | 160 | ||
164 | #[cfg(test)] | 161 | #[cfg(test)] |
165 | mod tests { | 162 | mod tests { |
166 | use crate::mock_analysis::single_file; | ||
167 | use insta::assert_debug_snapshot; | 163 | use insta::assert_debug_snapshot; |
168 | 164 | ||
165 | use crate::mock_analysis::single_file; | ||
166 | |||
167 | #[test] | ||
168 | fn default_generic_types_should_not_be_displayed() { | ||
169 | let (analysis, file_id) = single_file( | ||
170 | r#" | ||
171 | struct Test<K, T = u8> { | ||
172 | k: K, | ||
173 | t: T, | ||
174 | } | ||
175 | |||
176 | fn main() { | ||
177 | let zz = Test { t: 23, k: 33 }; | ||
178 | }"#, | ||
179 | ); | ||
180 | |||
181 | assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" | ||
182 | [ | ||
183 | InlayHint { | ||
184 | range: [69; 71), | ||
185 | kind: TypeHint, | ||
186 | label: "Test<i32>", | ||
187 | }, | ||
188 | ] | ||
189 | "### | ||
190 | ); | ||
191 | } | ||
192 | |||
169 | #[test] | 193 | #[test] |
170 | fn let_statement() { | 194 | fn let_statement() { |
171 | let (analysis, file_id) = single_file( | 195 | let (analysis, file_id) = single_file( |
@@ -202,6 +226,7 @@ fn main() { | |||
202 | 226 | ||
203 | let test = (42, 'a'); | 227 | let test = (42, 'a'); |
204 | let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5)); | 228 | let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5)); |
229 | let &x = &92; | ||
205 | }"#, | 230 | }"#, |
206 | ); | 231 | ); |
207 | 232 | ||
@@ -257,6 +282,11 @@ fn main() { | |||
257 | kind: TypeHint, | 282 | kind: TypeHint, |
258 | label: "f64", | 283 | label: "f64", |
259 | }, | 284 | }, |
285 | InlayHint { | ||
286 | range: [627; 628), | ||
287 | kind: TypeHint, | ||
288 | label: "i32", | ||
289 | }, | ||
260 | ] | 290 | ] |
261 | "### | 291 | "### |
262 | ); | 292 | ); |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index d1bff4a76..779a81b2c 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -422,6 +422,11 @@ impl Analysis { | |||
422 | self.with_db(|db| parent_module::crate_for(db, file_id)) | 422 | self.with_db(|db| parent_module::crate_for(db, file_id)) |
423 | } | 423 | } |
424 | 424 | ||
425 | /// Returns the edition of the given crate. | ||
426 | pub fn crate_edition(&self, crate_id: CrateId) -> Cancelable<Edition> { | ||
427 | self.with_db(|db| db.crate_graph().edition(crate_id)) | ||
428 | } | ||
429 | |||
425 | /// Returns the root file of the given crate. | 430 | /// Returns the root file of the given crate. |
426 | pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { | 431 | pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { |
427 | self.with_db(|db| db.crate_graph().crate_root(crate_id)) | 432 | self.with_db(|db| db.crate_graph().crate_root(crate_id)) |
diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs index 848ae4dc7..077a44473 100644 --- a/crates/ra_ide/src/marks.rs +++ b/crates/ra_ide/src/marks.rs | |||
@@ -3,10 +3,11 @@ | |||
3 | test_utils::marks!( | 3 | test_utils::marks!( |
4 | inserts_angle_brackets_for_generics | 4 | inserts_angle_brackets_for_generics |
5 | inserts_parens_for_function_calls | 5 | inserts_parens_for_function_calls |
6 | goto_definition_works_for_macros | 6 | goto_def_for_macros |
7 | goto_definition_works_for_methods | 7 | goto_def_for_methods |
8 | goto_definition_works_for_fields | 8 | goto_def_for_fields |
9 | goto_definition_works_for_record_fields | 9 | goto_def_for_record_fields |
10 | goto_def_for_field_init_shorthand | ||
10 | call_info_bad_offset | 11 | call_info_bad_offset |
11 | dont_complete_current_use | 12 | dont_complete_current_use |
12 | dont_complete_primitive_in_use | 13 | dont_complete_primitive_in_use |
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs index 6027e7d54..f5a788c07 100644 --- a/crates/ra_ide/src/parent_module.rs +++ b/crates/ra_ide/src/parent_module.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_db::{CrateId, FileId, FilePosition}; | 3 | use ra_db::{CrateId, FileId, FilePosition, SourceDatabase}; |
4 | 4 | ||
5 | use crate::{db::RootDatabase, NavigationTarget}; | 5 | use crate::{db::RootDatabase, NavigationTarget}; |
6 | 6 | ||
@@ -10,7 +10,7 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na | |||
10 | let src = hir::ModuleSource::from_position(db, position); | 10 | let src = hir::ModuleSource::from_position(db, position); |
11 | let module = match hir::Module::from_definition( | 11 | let module = match hir::Module::from_definition( |
12 | db, | 12 | db, |
13 | hir::Source { file_id: position.file_id.into(), value: src }, | 13 | hir::InFile { file_id: position.file_id.into(), value: src }, |
14 | ) { | 14 | ) { |
15 | None => return Vec::new(), | 15 | None => return Vec::new(), |
16 | Some(it) => it, | 16 | Some(it) => it, |
@@ -21,15 +21,16 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na | |||
21 | 21 | ||
22 | /// Returns `Vec` for the same reason as `parent_module` | 22 | /// Returns `Vec` for the same reason as `parent_module` |
23 | pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { | 23 | pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { |
24 | let src = hir::ModuleSource::from_file_id(db, file_id); | 24 | let source_file = db.parse(file_id).tree(); |
25 | let src = hir::ModuleSource::SourceFile(source_file); | ||
25 | let module = | 26 | let module = |
26 | match hir::Module::from_definition(db, hir::Source { file_id: file_id.into(), value: src }) | 27 | match hir::Module::from_definition(db, hir::InFile { file_id: file_id.into(), value: src }) |
27 | { | 28 | { |
28 | Some(it) => it, | 29 | Some(it) => it, |
29 | None => return Vec::new(), | 30 | None => return Vec::new(), |
30 | }; | 31 | }; |
31 | let krate = module.krate(); | 32 | let krate = module.krate(); |
32 | vec![krate.crate_id()] | 33 | vec![krate.into()] |
33 | } | 34 | } |
34 | 35 | ||
35 | #[cfg(test)] | 36 | #[cfg(test)] |
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 21a1ea69e..e3ecde50d 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs | |||
@@ -14,7 +14,7 @@ mod name_definition; | |||
14 | mod rename; | 14 | mod rename; |
15 | mod search_scope; | 15 | mod search_scope; |
16 | 16 | ||
17 | use hir::Source; | 17 | use hir::InFile; |
18 | use once_cell::unsync::Lazy; | 18 | use once_cell::unsync::Lazy; |
19 | use ra_db::{SourceDatabase, SourceDatabaseExt}; | 19 | use ra_db::{SourceDatabase, SourceDatabaseExt}; |
20 | use ra_prof::profile; | 20 | use ra_prof::profile; |
@@ -85,7 +85,7 @@ pub(crate) fn find_all_refs( | |||
85 | NameKind::Def(def) => NavigationTarget::from_def(db, def)?, | 85 | NameKind::Def(def) => NavigationTarget::from_def(db, def)?, |
86 | NameKind::SelfType(imp) => imp.to_nav(db), | 86 | NameKind::SelfType(imp) => imp.to_nav(db), |
87 | NameKind::Local(local) => local.to_nav(db), | 87 | NameKind::Local(local) => local.to_nav(db), |
88 | NameKind::GenericParam(_) => return None, | 88 | NameKind::TypeParam(_) => return None, |
89 | }; | 89 | }; |
90 | 90 | ||
91 | let search_scope = { | 91 | let search_scope = { |
@@ -107,12 +107,12 @@ fn find_name<'a>( | |||
107 | position: FilePosition, | 107 | position: FilePosition, |
108 | ) -> Option<RangeInfo<(String, NameDefinition)>> { | 108 | ) -> Option<RangeInfo<(String, NameDefinition)>> { |
109 | if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { | 109 | if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { |
110 | let def = classify_name(db, Source::new(position.file_id.into(), &name))?; | 110 | let def = classify_name(db, InFile::new(position.file_id.into(), &name))?; |
111 | let range = name.syntax().text_range(); | 111 | let range = name.syntax().text_range(); |
112 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); | 112 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); |
113 | } | 113 | } |
114 | let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?; | 114 | let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?; |
115 | let def = classify_name_ref(db, Source::new(position.file_id.into(), &name_ref))?; | 115 | let def = classify_name_ref(db, InFile::new(position.file_id.into(), &name_ref))?; |
116 | let range = name_ref.syntax().text_range(); | 116 | let range = name_ref.syntax().text_range(); |
117 | Some(RangeInfo::new(range, (name_ref.text().to_string(), def))) | 117 | Some(RangeInfo::new(range, (name_ref.text().to_string(), def))) |
118 | } | 118 | } |
@@ -144,7 +144,7 @@ fn process_definition( | |||
144 | continue; | 144 | continue; |
145 | } | 145 | } |
146 | } | 146 | } |
147 | if let Some(d) = classify_name_ref(db, Source::new(file_id.into(), &name_ref)) { | 147 | if let Some(d) = classify_name_ref(db, InFile::new(file_id.into(), &name_ref)) { |
148 | if d == def { | 148 | if d == def { |
149 | refs.push(FileRange { file_id, range }); | 149 | refs.push(FileRange { file_id, range }); |
150 | } | 150 | } |
diff --git a/crates/ra_ide/src/references/classify.rs b/crates/ra_ide/src/references/classify.rs index 5cea805ec..3483a7176 100644 --- a/crates/ra_ide/src/references/classify.rs +++ b/crates/ra_ide/src/references/classify.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! Functions that are used to classify an element from its definition or reference. | 1 | //! Functions that are used to classify an element from its definition or reference. |
2 | 2 | ||
3 | use hir::{FromSource, Module, ModuleSource, PathResolution, Source, SourceAnalyzer}; | 3 | use hir::{FromSource, InFile, Module, ModuleSource, PathResolution, SourceAnalyzer}; |
4 | use ra_prof::profile; | 4 | use ra_prof::profile; |
5 | use ra_syntax::{ast, match_ast, AstNode}; | 5 | use ra_syntax::{ast, match_ast, AstNode}; |
6 | use test_utils::tested_by; | 6 | use test_utils::tested_by; |
@@ -11,7 +11,7 @@ use super::{ | |||
11 | }; | 11 | }; |
12 | use crate::db::RootDatabase; | 12 | use crate::db::RootDatabase; |
13 | 13 | ||
14 | pub(crate) fn classify_name(db: &RootDatabase, name: Source<&ast::Name>) -> Option<NameDefinition> { | 14 | pub(crate) fn classify_name(db: &RootDatabase, name: InFile<&ast::Name>) -> Option<NameDefinition> { |
15 | let _p = profile("classify_name"); | 15 | let _p = profile("classify_name"); |
16 | let parent = name.value.syntax().parent()?; | 16 | let parent = name.value.syntax().parent()?; |
17 | 17 | ||
@@ -110,6 +110,15 @@ pub(crate) fn classify_name(db: &RootDatabase, name: Source<&ast::Name>) -> Opti | |||
110 | kind: NameKind::Macro(def), | 110 | kind: NameKind::Macro(def), |
111 | }) | 111 | }) |
112 | }, | 112 | }, |
113 | ast::TypeParam(it) => { | ||
114 | let src = name.with_value(it); | ||
115 | let def = hir::TypeParam::from_source(db, src)?; | ||
116 | Some(NameDefinition { | ||
117 | visibility: None, | ||
118 | container: def.module(db), | ||
119 | kind: NameKind::TypeParam(def), | ||
120 | }) | ||
121 | }, | ||
113 | _ => None, | 122 | _ => None, |
114 | } | 123 | } |
115 | } | 124 | } |
@@ -117,7 +126,7 @@ pub(crate) fn classify_name(db: &RootDatabase, name: Source<&ast::Name>) -> Opti | |||
117 | 126 | ||
118 | pub(crate) fn classify_name_ref( | 127 | pub(crate) fn classify_name_ref( |
119 | db: &RootDatabase, | 128 | db: &RootDatabase, |
120 | name_ref: Source<&ast::NameRef>, | 129 | name_ref: InFile<&ast::NameRef>, |
121 | ) -> Option<NameDefinition> { | 130 | ) -> Option<NameDefinition> { |
122 | let _p = profile("classify_name_ref"); | 131 | let _p = profile("classify_name_ref"); |
123 | 132 | ||
@@ -125,21 +134,22 @@ pub(crate) fn classify_name_ref( | |||
125 | let analyzer = SourceAnalyzer::new(db, name_ref.map(|it| it.syntax()), None); | 134 | let analyzer = SourceAnalyzer::new(db, name_ref.map(|it| it.syntax()), None); |
126 | 135 | ||
127 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { | 136 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { |
128 | tested_by!(goto_definition_works_for_methods); | 137 | tested_by!(goto_def_for_methods); |
129 | if let Some(func) = analyzer.resolve_method_call(&method_call) { | 138 | if let Some(func) = analyzer.resolve_method_call(&method_call) { |
130 | return Some(from_assoc_item(db, func.into())); | 139 | return Some(from_assoc_item(db, func.into())); |
131 | } | 140 | } |
132 | } | 141 | } |
133 | 142 | ||
134 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { | 143 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { |
135 | tested_by!(goto_definition_works_for_fields); | 144 | tested_by!(goto_def_for_fields); |
136 | if let Some(field) = analyzer.resolve_field(&field_expr) { | 145 | if let Some(field) = analyzer.resolve_field(&field_expr) { |
137 | return Some(from_struct_field(db, field)); | 146 | return Some(from_struct_field(db, field)); |
138 | } | 147 | } |
139 | } | 148 | } |
140 | 149 | ||
141 | if let Some(record_field) = ast::RecordField::cast(parent.clone()) { | 150 | if let Some(record_field) = ast::RecordField::cast(parent.clone()) { |
142 | tested_by!(goto_definition_works_for_record_fields); | 151 | tested_by!(goto_def_for_record_fields); |
152 | tested_by!(goto_def_for_field_init_shorthand); | ||
143 | if let Some(field_def) = analyzer.resolve_record_field(&record_field) { | 153 | if let Some(field_def) = analyzer.resolve_record_field(&record_field) { |
144 | return Some(from_struct_field(db, field_def)); | 154 | return Some(from_struct_field(db, field_def)); |
145 | } | 155 | } |
@@ -151,7 +161,7 @@ pub(crate) fn classify_name_ref( | |||
151 | let visibility = None; | 161 | let visibility = None; |
152 | 162 | ||
153 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { | 163 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { |
154 | tested_by!(goto_definition_works_for_macros); | 164 | tested_by!(goto_def_for_macros); |
155 | if let Some(macro_def) = analyzer.resolve_macro_call(db, name_ref.with_value(¯o_call)) { | 165 | if let Some(macro_def) = analyzer.resolve_macro_call(db, name_ref.with_value(¯o_call)) { |
156 | let kind = NameKind::Macro(macro_def); | 166 | let kind = NameKind::Macro(macro_def); |
157 | return Some(NameDefinition { kind, container, visibility }); | 167 | return Some(NameDefinition { kind, container, visibility }); |
@@ -168,9 +178,8 @@ pub(crate) fn classify_name_ref( | |||
168 | let kind = NameKind::Local(local); | 178 | let kind = NameKind::Local(local); |
169 | Some(NameDefinition { kind, container, visibility: None }) | 179 | Some(NameDefinition { kind, container, visibility: None }) |
170 | } | 180 | } |
171 | PathResolution::GenericParam(par) => { | 181 | PathResolution::TypeParam(par) => { |
172 | // FIXME: get generic param def | 182 | let kind = NameKind::TypeParam(par); |
173 | let kind = NameKind::GenericParam(par); | ||
174 | Some(NameDefinition { kind, container, visibility }) | 183 | Some(NameDefinition { kind, container, visibility }) |
175 | } | 184 | } |
176 | PathResolution::Macro(def) => { | 185 | PathResolution::Macro(def) => { |
diff --git a/crates/ra_ide/src/references/name_definition.rs b/crates/ra_ide/src/references/name_definition.rs index 10d3a2364..8c67c8863 100644 --- a/crates/ra_ide/src/references/name_definition.rs +++ b/crates/ra_ide/src/references/name_definition.rs | |||
@@ -4,8 +4,8 @@ | |||
4 | //! Note that the reference search is possible for not all of the classified items. | 4 | //! Note that the reference search is possible for not all of the classified items. |
5 | 5 | ||
6 | use hir::{ | 6 | use hir::{ |
7 | Adt, AssocItem, GenericParam, HasSource, ImplBlock, Local, MacroDef, Module, ModuleDef, | 7 | Adt, AssocItem, HasSource, ImplBlock, Local, MacroDef, Module, ModuleDef, StructField, |
8 | StructField, VariantDef, | 8 | TypeParam, VariantDef, |
9 | }; | 9 | }; |
10 | use ra_syntax::{ast, ast::VisibilityOwner}; | 10 | use ra_syntax::{ast, ast::VisibilityOwner}; |
11 | 11 | ||
@@ -19,7 +19,7 @@ pub enum NameKind { | |||
19 | Def(ModuleDef), | 19 | Def(ModuleDef), |
20 | SelfType(ImplBlock), | 20 | SelfType(ImplBlock), |
21 | Local(Local), | 21 | Local(Local), |
22 | GenericParam(GenericParam), | 22 | TypeParam(TypeParam), |
23 | } | 23 | } |
24 | 24 | ||
25 | #[derive(PartialEq, Eq)] | 25 | #[derive(PartialEq, Eq)] |
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index d58496049..b804d5f6d 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use hir::ModuleSource; | 3 | use hir::ModuleSource; |
4 | use ra_db::{RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt}; | 4 | use ra_db::{RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt}; |
5 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SyntaxNode}; | 5 | use ra_syntax::{algo::find_node_at_offset, ast, tokenize, AstNode, SyntaxKind, SyntaxNode}; |
6 | use ra_text_edit::TextEdit; | 6 | use ra_text_edit::TextEdit; |
7 | 7 | ||
8 | use crate::{ | 8 | use crate::{ |
@@ -17,6 +17,13 @@ pub(crate) fn rename( | |||
17 | position: FilePosition, | 17 | position: FilePosition, |
18 | new_name: &str, | 18 | new_name: &str, |
19 | ) -> Option<RangeInfo<SourceChange>> { | 19 | ) -> Option<RangeInfo<SourceChange>> { |
20 | let tokens = tokenize(new_name); | ||
21 | if tokens.len() != 1 | ||
22 | || (tokens[0].kind != SyntaxKind::IDENT && tokens[0].kind != SyntaxKind::UNDERSCORE) | ||
23 | { | ||
24 | return None; | ||
25 | } | ||
26 | |||
20 | let parse = db.parse(position.file_id); | 27 | let parse = db.parse(position.file_id); |
21 | if let Some((ast_name, ast_module)) = | 28 | if let Some((ast_name, ast_module)) = |
22 | find_name_and_module_at_offset(parse.tree().syntax(), position) | 29 | find_name_and_module_at_offset(parse.tree().syntax(), position) |
@@ -55,7 +62,7 @@ fn rename_mod( | |||
55 | ) -> Option<SourceChange> { | 62 | ) -> Option<SourceChange> { |
56 | let mut source_file_edits = Vec::new(); | 63 | let mut source_file_edits = Vec::new(); |
57 | let mut file_system_edits = Vec::new(); | 64 | let mut file_system_edits = Vec::new(); |
58 | let module_src = hir::Source { file_id: position.file_id.into(), value: ast_module.clone() }; | 65 | let module_src = hir::InFile { file_id: position.file_id.into(), value: ast_module.clone() }; |
59 | if let Some(module) = hir::Module::from_declaration(db, module_src) { | 66 | if let Some(module) = hir::Module::from_declaration(db, module_src) { |
60 | let src = module.definition_source(db); | 67 | let src = module.definition_source(db); |
61 | let file_id = src.file_id.original_file(db); | 68 | let file_id = src.file_id.original_file(db); |
@@ -124,6 +131,49 @@ mod tests { | |||
124 | }; | 131 | }; |
125 | 132 | ||
126 | #[test] | 133 | #[test] |
134 | fn test_rename_to_underscore() { | ||
135 | test_rename( | ||
136 | r#" | ||
137 | fn main() { | ||
138 | let i<|> = 1; | ||
139 | }"#, | ||
140 | "_", | ||
141 | r#" | ||
142 | fn main() { | ||
143 | let _ = 1; | ||
144 | }"#, | ||
145 | ); | ||
146 | } | ||
147 | |||
148 | #[test] | ||
149 | fn test_rename_to_raw_identifier() { | ||
150 | test_rename( | ||
151 | r#" | ||
152 | fn main() { | ||
153 | let i<|> = 1; | ||
154 | }"#, | ||
155 | "r#fn", | ||
156 | r#" | ||
157 | fn main() { | ||
158 | let r#fn = 1; | ||
159 | }"#, | ||
160 | ); | ||
161 | } | ||
162 | |||
163 | #[test] | ||
164 | fn test_rename_to_invalid_identifier() { | ||
165 | let (analysis, position) = single_file_with_position( | ||
166 | " | ||
167 | fn main() { | ||
168 | let i<|> = 1; | ||
169 | }", | ||
170 | ); | ||
171 | let new_name = "invalid!"; | ||
172 | let source_change = analysis.rename(position, new_name).unwrap(); | ||
173 | assert!(source_change.is_none()); | ||
174 | } | ||
175 | |||
176 | #[test] | ||
127 | fn test_rename_for_local() { | 177 | fn test_rename_for_local() { |
128 | test_rename( | 178 | test_rename( |
129 | r#" | 179 | r#" |
diff --git a/crates/ra_ide/src/references/search_scope.rs b/crates/ra_ide/src/references/search_scope.rs index f5c9589f4..241dd358f 100644 --- a/crates/ra_ide/src/references/search_scope.rs +++ b/crates/ra_ide/src/references/search_scope.rs | |||
@@ -5,7 +5,7 @@ | |||
5 | use std::mem; | 5 | use std::mem; |
6 | 6 | ||
7 | use hir::{DefWithBody, HasSource, ModuleSource}; | 7 | use hir::{DefWithBody, HasSource, ModuleSource}; |
8 | use ra_db::{FileId, SourceDatabase, SourceDatabaseExt}; | 8 | use ra_db::{FileId, SourceDatabaseExt}; |
9 | use ra_prof::profile; | 9 | use ra_prof::profile; |
10 | use ra_syntax::{AstNode, TextRange}; | 10 | use ra_syntax::{AstNode, TextRange}; |
11 | use rustc_hash::FxHashMap; | 11 | use rustc_hash::FxHashMap; |
@@ -120,15 +120,11 @@ impl NameDefinition { | |||
120 | } | 120 | } |
121 | if vis.as_str() == "pub" { | 121 | if vis.as_str() == "pub" { |
122 | let krate = self.container.krate(); | 122 | let krate = self.container.krate(); |
123 | let crate_graph = db.crate_graph(); | 123 | for rev_dep in krate.reverse_dependencies(db) { |
124 | for crate_id in crate_graph.iter() { | 124 | let root_file = rev_dep.root_file(db); |
125 | let mut crate_deps = crate_graph.dependencies(crate_id); | 125 | let source_root_id = db.file_source_root(root_file); |
126 | if crate_deps.any(|dep| dep.crate_id() == krate.crate_id()) { | 126 | let source_root = db.source_root(source_root_id); |
127 | let root_file = crate_graph.crate_root(crate_id); | 127 | res.extend(source_root.walk().map(|id| (id, None))); |
128 | let source_root_id = db.file_source_root(root_file); | ||
129 | let source_root = db.source_root(source_root_id); | ||
130 | res.extend(source_root.walk().map(|id| (id, None))); | ||
131 | } | ||
132 | } | 128 | } |
133 | return SearchScope::new(res); | 129 | return SearchScope::new(res); |
134 | } | 130 | } |
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 8039a5164..e213e1a06 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::Source; | 3 | use hir::InFile; |
4 | use itertools::Itertools; | 4 | use itertools::Itertools; |
5 | use ra_db::SourceDatabase; | 5 | use ra_db::SourceDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
@@ -66,8 +66,8 @@ fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Opti | |||
66 | return None; | 66 | return None; |
67 | } | 67 | } |
68 | let range = module.syntax().text_range(); | 68 | let range = module.syntax().text_range(); |
69 | let src = hir::ModuleSource::from_child_node(db, Source::new(file_id.into(), &module.syntax())); | 69 | let src = hir::ModuleSource::from_child_node(db, InFile::new(file_id.into(), &module.syntax())); |
70 | let module = hir::Module::from_definition(db, Source::new(file_id.into(), src))?; | 70 | let module = hir::Module::from_definition(db, InFile::new(file_id.into(), src))?; |
71 | 71 | ||
72 | let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::"); | 72 | let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::"); |
73 | Some(Runnable { range, kind: RunnableKind::TestMod { path } }) | 73 | Some(Runnable { range, kind: RunnableKind::TestMod { path } }) |
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html index b39c4d371..a097cf8e8 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html | |||
@@ -5,12 +5,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
5 | 5 | ||
6 | .comment { color: #7F9F7F; } | 6 | .comment { color: #7F9F7F; } |
7 | .string { color: #CC9393; } | 7 | .string { color: #CC9393; } |
8 | .field { color: #94BFF3; } | ||
8 | .function { color: #93E0E3; } | 9 | .function { color: #93E0E3; } |
9 | .parameter { color: #94BFF3; } | 10 | .parameter { color: #94BFF3; } |
10 | .builtin { color: #DD6718; } | ||
11 | .text { color: #DCDCCC; } | 11 | .text { color: #DCDCCC; } |
12 | .type { color: #7CB8BB; } | ||
13 | .type\.builtin { color: #8CD0D3; } | ||
14 | .type\.param { color: #20999D; } | ||
12 | .attribute { color: #94BFF3; } | 15 | .attribute { color: #94BFF3; } |
13 | .literal { color: #BFEBBF; } | 16 | .literal { color: #BFEBBF; } |
17 | .literal\.numeric { color: #6A8759; } | ||
14 | .macro { color: #94BFF3; } | 18 | .macro { color: #94BFF3; } |
15 | .variable { color: #DCDCCC; } | 19 | .variable { color: #DCDCCC; } |
16 | .variable\.mut { color: #DCDCCC; text-decoration: underline; } | 20 | .variable\.mut { color: #DCDCCC; text-decoration: underline; } |
@@ -21,28 +25,37 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
21 | </style> | 25 | </style> |
22 | <pre><code><span class="attribute">#</span><span class="attribute">[</span><span class="attribute">derive</span><span class="attribute">(</span><span class="attribute">Clone</span><span class="attribute">,</span><span class="attribute"> </span><span class="attribute">Debug</span><span class="attribute">)</span><span class="attribute">]</span> | 26 | <pre><code><span class="attribute">#</span><span class="attribute">[</span><span class="attribute">derive</span><span class="attribute">(</span><span class="attribute">Clone</span><span class="attribute">,</span><span class="attribute"> </span><span class="attribute">Debug</span><span class="attribute">)</span><span class="attribute">]</span> |
23 | <span class="keyword">struct</span> <span class="type">Foo</span> { | 27 | <span class="keyword">struct</span> <span class="type">Foo</span> { |
24 | <span class="keyword">pub</span> <span class="field">x</span>: <span class="type">i32</span>, | 28 | <span class="keyword">pub</span> <span class="field">x</span>: <span class="type.builtin">i32</span>, |
25 | <span class="keyword">pub</span> <span class="field">y</span>: <span class="type">i32</span>, | 29 | <span class="keyword">pub</span> <span class="field">y</span>: <span class="type.builtin">i32</span>, |
26 | } | 30 | } |
27 | 31 | ||
28 | <span class="keyword">fn</span> <span class="function">foo</span><<span class="type">T</span>>() -> <span class="type">T</span> { | 32 | <span class="keyword">fn</span> <span class="function">foo</span><<span class="type.param">T</span>>() -> <span class="type.param">T</span> { |
29 | <span class="macro">unimplemented</span><span class="macro">!</span>(); | 33 | <span class="macro">unimplemented</span><span class="macro">!</span>(); |
30 | <span class="function">foo</span>::<<span class="type">i32</span>>(); | 34 | <span class="function">foo</span>::<<span class="type.builtin">i32</span>>(); |
31 | } | 35 | } |
32 | 36 | ||
33 | <span class="comment">// comment</span> | 37 | <span class="comment">// comment</span> |
34 | <span class="keyword">fn</span> <span class="function">main</span>() { | 38 | <span class="keyword">fn</span> <span class="function">main</span>() { |
35 | <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal">92</span>); | 39 | <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal.numeric">92</span>); |
36 | 40 | ||
37 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">vec</span> = <span class="text">Vec</span>::<span class="text">new</span>(); | 41 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">vec</span> = <span class="text">Vec</span>::<span class="text">new</span>(); |
38 | <span class="keyword.control">if</span> <span class="keyword">true</span> { | 42 | <span class="keyword.control">if</span> <span class="keyword">true</span> { |
39 | <span class="variable.mut">vec</span>.<span class="text">push</span>(<span class="type">Foo</span> { <span class="field">x</span>: <span class="literal">0</span>, <span class="field">y</span>: <span class="literal">1</span> }); | 43 | <span class="keyword">let</span> <span class="variable">x</span> = <span class="literal.numeric">92</span>; |
44 | <span class="variable.mut">vec</span>.<span class="text">push</span>(<span class="type">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="literal.numeric">1</span> }); | ||
40 | } | 45 | } |
41 | <span class="keyword.unsafe">unsafe</span> { <span class="variable.mut">vec</span>.<span class="text">set_len</span>(<span class="literal">0</span>); } | 46 | <span class="keyword.unsafe">unsafe</span> { <span class="variable.mut">vec</span>.<span class="text">set_len</span>(<span class="literal.numeric">0</span>); } |
42 | 47 | ||
43 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">x</span> = <span class="literal">42</span>; | 48 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">x</span> = <span class="literal.numeric">42</span>; |
44 | <span class="keyword">let</span> <span class="variable.mut">y</span> = &<span class="keyword">mut</span> <span class="variable.mut">x</span>; | 49 | <span class="keyword">let</span> <span class="variable.mut">y</span> = &<span class="keyword">mut</span> <span class="variable.mut">x</span>; |
45 | <span class="keyword">let</span> <span class="variable">z</span> = &<span class="variable.mut">y</span>; | 50 | <span class="keyword">let</span> <span class="variable">z</span> = &<span class="variable.mut">y</span>; |
46 | 51 | ||
47 | <span class="variable.mut">y</span>; | 52 | <span class="variable.mut">y</span>; |
53 | } | ||
54 | |||
55 | <span class="keyword">enum</span> <span class="type">E</span><<span class="type.param">X</span>> { | ||
56 | <span class="constant">V</span>(<span class="type.param">X</span>) | ||
57 | } | ||
58 | |||
59 | <span class="keyword">impl</span><<span class="type.param">X</span>> <span class="type">E</span><<span class="type.param">X</span>> { | ||
60 | <span class="keyword">fn</span> <span class="function">new</span><<span class="type.param">T</span>>() -> <span class="type">E</span><<span class="type.param">T</span>> {} | ||
48 | }</code></pre> \ No newline at end of file | 61 | }</code></pre> \ No newline at end of file |
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html index 79f11ea80..110556c09 100644 --- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html +++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html | |||
@@ -5,12 +5,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
5 | 5 | ||
6 | .comment { color: #7F9F7F; } | 6 | .comment { color: #7F9F7F; } |
7 | .string { color: #CC9393; } | 7 | .string { color: #CC9393; } |
8 | .field { color: #94BFF3; } | ||
8 | .function { color: #93E0E3; } | 9 | .function { color: #93E0E3; } |
9 | .parameter { color: #94BFF3; } | 10 | .parameter { color: #94BFF3; } |
10 | .builtin { color: #DD6718; } | ||
11 | .text { color: #DCDCCC; } | 11 | .text { color: #DCDCCC; } |
12 | .type { color: #7CB8BB; } | ||
13 | .type\.builtin { color: #8CD0D3; } | ||
14 | .type\.param { color: #20999D; } | ||
12 | .attribute { color: #94BFF3; } | 15 | .attribute { color: #94BFF3; } |
13 | .literal { color: #BFEBBF; } | 16 | .literal { color: #BFEBBF; } |
17 | .literal\.numeric { color: #6A8759; } | ||
14 | .macro { color: #94BFF3; } | 18 | .macro { color: #94BFF3; } |
15 | .variable { color: #DCDCCC; } | 19 | .variable { color: #DCDCCC; } |
16 | .variable\.mut { color: #DCDCCC; text-decoration: underline; } | 20 | .variable\.mut { color: #DCDCCC; text-decoration: underline; } |
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 9a3e4c82f..0228ee7e9 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use rustc_hash::{FxHashMap, FxHashSet}; | 3 | use rustc_hash::{FxHashMap, FxHashSet}; |
4 | 4 | ||
5 | use hir::{Name, Source}; | 5 | use hir::{InFile, Name}; |
6 | use ra_db::SourceDatabase; | 6 | use ra_db::SourceDatabase; |
7 | use ra_prof::profile; | 7 | use ra_prof::profile; |
8 | use ra_syntax::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T}; | 8 | use ra_syntax::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T}; |
@@ -16,6 +16,34 @@ use crate::{ | |||
16 | FileId, | 16 | FileId, |
17 | }; | 17 | }; |
18 | 18 | ||
19 | pub mod tags { | ||
20 | pub(crate) const FIELD: &str = "field"; | ||
21 | pub(crate) const FUNCTION: &str = "function"; | ||
22 | pub(crate) const MODULE: &str = "module"; | ||
23 | pub(crate) const TYPE: &str = "type"; | ||
24 | pub(crate) const CONSTANT: &str = "constant"; | ||
25 | pub(crate) const MACRO: &str = "macro"; | ||
26 | pub(crate) const VARIABLE: &str = "variable"; | ||
27 | pub(crate) const VARIABLE_MUT: &str = "variable.mut"; | ||
28 | pub(crate) const TEXT: &str = "text"; | ||
29 | |||
30 | pub(crate) const TYPE_BUILTIN: &str = "type.builtin"; | ||
31 | pub(crate) const TYPE_SELF: &str = "type.self"; | ||
32 | pub(crate) const TYPE_PARAM: &str = "type.param"; | ||
33 | pub(crate) const TYPE_LIFETIME: &str = "type.lifetime"; | ||
34 | |||
35 | pub(crate) const LITERAL_BYTE: &str = "literal.byte"; | ||
36 | pub(crate) const LITERAL_NUMERIC: &str = "literal.numeric"; | ||
37 | pub(crate) const LITERAL_CHAR: &str = "literal.char"; | ||
38 | pub(crate) const LITERAL_COMMENT: &str = "comment"; | ||
39 | pub(crate) const LITERAL_STRING: &str = "string"; | ||
40 | pub(crate) const LITERAL_ATTRIBUTE: &str = "attribute"; | ||
41 | |||
42 | pub(crate) const KEYWORD_UNSAFE: &str = "keyword.unsafe"; | ||
43 | pub(crate) const KEYWORD_CONTROL: &str = "keyword.control"; | ||
44 | pub(crate) const KEYWORD: &str = "keyword"; | ||
45 | } | ||
46 | |||
19 | #[derive(Debug)] | 47 | #[derive(Debug)] |
20 | pub struct HighlightedRange { | 48 | pub struct HighlightedRange { |
21 | pub range: TextRange, | 49 | pub range: TextRange, |
@@ -71,17 +99,16 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa | |||
71 | bindings_shadow_count.clear(); | 99 | bindings_shadow_count.clear(); |
72 | continue; | 100 | continue; |
73 | } | 101 | } |
74 | COMMENT => "comment", | 102 | COMMENT => tags::LITERAL_COMMENT, |
75 | STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string", | 103 | STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => tags::LITERAL_STRING, |
76 | ATTR => "attribute", | 104 | ATTR => tags::LITERAL_ATTRIBUTE, |
105 | // Special-case field init shorthand | ||
106 | NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => tags::FIELD, | ||
107 | NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => continue, | ||
77 | NAME_REF => { | 108 | NAME_REF => { |
78 | if node.ancestors().any(|it| it.kind() == ATTR) { | ||
79 | continue; | ||
80 | } | ||
81 | |||
82 | let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); | 109 | let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); |
83 | let name_kind = | 110 | let name_kind = |
84 | classify_name_ref(db, Source::new(file_id.into(), &name_ref)).map(|d| d.kind); | 111 | classify_name_ref(db, InFile::new(file_id.into(), &name_ref)).map(|d| d.kind); |
85 | 112 | ||
86 | if let Some(Local(local)) = &name_kind { | 113 | if let Some(Local(local)) = &name_kind { |
87 | if let Some(name) = local.name(db) { | 114 | if let Some(name) = local.name(db) { |
@@ -90,12 +117,12 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa | |||
90 | } | 117 | } |
91 | }; | 118 | }; |
92 | 119 | ||
93 | name_kind.map_or("text", |it| highlight_name(db, it)) | 120 | name_kind.map_or(tags::TEXT, |it| highlight_name(db, it)) |
94 | } | 121 | } |
95 | NAME => { | 122 | NAME => { |
96 | let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap(); | 123 | let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap(); |
97 | let name_kind = | 124 | let name_kind = |
98 | classify_name(db, Source::new(file_id.into(), &name)).map(|d| d.kind); | 125 | classify_name(db, InFile::new(file_id.into(), &name)).map(|d| d.kind); |
99 | 126 | ||
100 | if let Some(Local(local)) = &name_kind { | 127 | if let Some(Local(local)) = &name_kind { |
101 | if let Some(name) = local.name(db) { | 128 | if let Some(name) = local.name(db) { |
@@ -107,18 +134,21 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa | |||
107 | 134 | ||
108 | match name_kind { | 135 | match name_kind { |
109 | Some(name_kind) => highlight_name(db, name_kind), | 136 | Some(name_kind) => highlight_name(db, name_kind), |
110 | None => name.syntax().parent().map_or("function", |x| match x.kind() { | 137 | None => name.syntax().parent().map_or(tags::FUNCTION, |x| match x.kind() { |
111 | TYPE_PARAM | STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => "type", | 138 | STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => tags::TYPE, |
112 | RECORD_FIELD_DEF => "field", | 139 | TYPE_PARAM => tags::TYPE_PARAM, |
113 | _ => "function", | 140 | RECORD_FIELD_DEF => tags::FIELD, |
141 | _ => tags::FUNCTION, | ||
114 | }), | 142 | }), |
115 | } | 143 | } |
116 | } | 144 | } |
117 | INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal", | 145 | INT_NUMBER | FLOAT_NUMBER => tags::LITERAL_NUMERIC, |
118 | LIFETIME => "parameter", | 146 | BYTE => tags::LITERAL_BYTE, |
119 | T![unsafe] => "keyword.unsafe", | 147 | CHAR => tags::LITERAL_CHAR, |
120 | k if is_control_keyword(k) => "keyword.control", | 148 | LIFETIME => tags::TYPE_LIFETIME, |
121 | k if k.is_keyword() => "keyword", | 149 | T![unsafe] => tags::KEYWORD_UNSAFE, |
150 | k if is_control_keyword(k) => tags::KEYWORD_CONTROL, | ||
151 | k if k.is_keyword() => tags::KEYWORD, | ||
122 | _ => { | 152 | _ => { |
123 | if let Some(macro_call) = node.as_node().cloned().and_then(ast::MacroCall::cast) { | 153 | if let Some(macro_call) = node.as_node().cloned().and_then(ast::MacroCall::cast) { |
124 | if let Some(path) = macro_call.path() { | 154 | if let Some(path) = macro_call.path() { |
@@ -135,7 +165,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa | |||
135 | } | 165 | } |
136 | res.push(HighlightedRange { | 166 | res.push(HighlightedRange { |
137 | range: TextRange::from_to(range_start, range_end), | 167 | range: TextRange::from_to(range_start, range_end), |
138 | tag: "macro", | 168 | tag: tags::MACRO, |
139 | binding_hash: None, | 169 | binding_hash: None, |
140 | }) | 170 | }) |
141 | } | 171 | } |
@@ -211,29 +241,27 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo | |||
211 | 241 | ||
212 | fn highlight_name(db: &RootDatabase, name_kind: NameKind) -> &'static str { | 242 | fn highlight_name(db: &RootDatabase, name_kind: NameKind) -> &'static str { |
213 | match name_kind { | 243 | match name_kind { |
214 | Macro(_) => "macro", | 244 | Macro(_) => tags::MACRO, |
215 | Field(_) => "field", | 245 | Field(_) => tags::FIELD, |
216 | AssocItem(hir::AssocItem::Function(_)) => "function", | 246 | AssocItem(hir::AssocItem::Function(_)) => tags::FUNCTION, |
217 | AssocItem(hir::AssocItem::Const(_)) => "constant", | 247 | AssocItem(hir::AssocItem::Const(_)) => tags::CONSTANT, |
218 | AssocItem(hir::AssocItem::TypeAlias(_)) => "type", | 248 | AssocItem(hir::AssocItem::TypeAlias(_)) => tags::TYPE, |
219 | Def(hir::ModuleDef::Module(_)) => "module", | 249 | Def(hir::ModuleDef::Module(_)) => tags::MODULE, |
220 | Def(hir::ModuleDef::Function(_)) => "function", | 250 | Def(hir::ModuleDef::Function(_)) => tags::FUNCTION, |
221 | Def(hir::ModuleDef::Adt(_)) => "type", | 251 | Def(hir::ModuleDef::Adt(_)) => tags::TYPE, |
222 | Def(hir::ModuleDef::EnumVariant(_)) => "constant", | 252 | Def(hir::ModuleDef::EnumVariant(_)) => tags::CONSTANT, |
223 | Def(hir::ModuleDef::Const(_)) => "constant", | 253 | Def(hir::ModuleDef::Const(_)) => tags::CONSTANT, |
224 | Def(hir::ModuleDef::Static(_)) => "constant", | 254 | Def(hir::ModuleDef::Static(_)) => tags::CONSTANT, |
225 | Def(hir::ModuleDef::Trait(_)) => "type", | 255 | Def(hir::ModuleDef::Trait(_)) => tags::TYPE, |
226 | Def(hir::ModuleDef::TypeAlias(_)) => "type", | 256 | Def(hir::ModuleDef::TypeAlias(_)) => tags::TYPE, |
227 | Def(hir::ModuleDef::BuiltinType(_)) => "type", | 257 | Def(hir::ModuleDef::BuiltinType(_)) => tags::TYPE_BUILTIN, |
228 | SelfType(_) => "type", | 258 | SelfType(_) => tags::TYPE_SELF, |
229 | GenericParam(_) => "type", | 259 | TypeParam(_) => tags::TYPE_PARAM, |
230 | Local(local) => { | 260 | Local(local) => { |
231 | if local.is_mut(db) { | 261 | if local.is_mut(db) || local.ty(db).is_mutable_reference() { |
232 | "variable.mut" | 262 | tags::VARIABLE_MUT |
233 | } else if local.ty(db).is_mutable_reference() { | ||
234 | "variable.mut" | ||
235 | } else { | 263 | } else { |
236 | "variable" | 264 | tags::VARIABLE |
237 | } | 265 | } |
238 | } | 266 | } |
239 | } | 267 | } |
@@ -251,12 +279,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
251 | 279 | ||
252 | .comment { color: #7F9F7F; } | 280 | .comment { color: #7F9F7F; } |
253 | .string { color: #CC9393; } | 281 | .string { color: #CC9393; } |
282 | .field { color: #94BFF3; } | ||
254 | .function { color: #93E0E3; } | 283 | .function { color: #93E0E3; } |
255 | .parameter { color: #94BFF3; } | 284 | .parameter { color: #94BFF3; } |
256 | .builtin { color: #DD6718; } | ||
257 | .text { color: #DCDCCC; } | 285 | .text { color: #DCDCCC; } |
286 | .type { color: #7CB8BB; } | ||
287 | .type\\.builtin { color: #8CD0D3; } | ||
288 | .type\\.param { color: #20999D; } | ||
258 | .attribute { color: #94BFF3; } | 289 | .attribute { color: #94BFF3; } |
259 | .literal { color: #BFEBBF; } | 290 | .literal { color: #BFEBBF; } |
291 | .literal\\.numeric { color: #6A8759; } | ||
260 | .macro { color: #94BFF3; } | 292 | .macro { color: #94BFF3; } |
261 | .variable { color: #DCDCCC; } | 293 | .variable { color: #DCDCCC; } |
262 | .variable\\.mut { color: #DCDCCC; text-decoration: underline; } | 294 | .variable\\.mut { color: #DCDCCC; text-decoration: underline; } |
@@ -293,7 +325,8 @@ fn main() { | |||
293 | 325 | ||
294 | let mut vec = Vec::new(); | 326 | let mut vec = Vec::new(); |
295 | if true { | 327 | if true { |
296 | vec.push(Foo { x: 0, y: 1 }); | 328 | let x = 92; |
329 | vec.push(Foo { x, y: 1 }); | ||
297 | } | 330 | } |
298 | unsafe { vec.set_len(0); } | 331 | unsafe { vec.set_len(0); } |
299 | 332 | ||
@@ -303,6 +336,14 @@ fn main() { | |||
303 | 336 | ||
304 | y; | 337 | y; |
305 | } | 338 | } |
339 | |||
340 | enum E<X> { | ||
341 | V(X) | ||
342 | } | ||
343 | |||
344 | impl<X> E<X> { | ||
345 | fn new<T>() -> E<T> {} | ||
346 | } | ||
306 | "# | 347 | "# |
307 | .trim(), | 348 | .trim(), |
308 | ); | 349 | ); |