diff options
Diffstat (limited to 'crates/ra_ide_api/src')
-rw-r--r-- | crates/ra_ide_api/src/change.rs | 5 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_dot.rs | 4 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_struct_literal.rs | 4 | ||||
-rw-r--r-- | crates/ra_ide_api/src/goto_definition.rs | 17 | ||||
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 43 | ||||
-rw-r--r-- | crates/ra_ide_api/src/lib.rs | 7 | ||||
-rw-r--r-- | crates/ra_ide_api/src/syntax_tree.rs | 87 |
7 files changed, 136 insertions, 31 deletions
diff --git a/crates/ra_ide_api/src/change.rs b/crates/ra_ide_api/src/change.rs index 3f041f9c3..0c90ed5b5 100644 --- a/crates/ra_ide_api/src/change.rs +++ b/crates/ra_ide_api/src/change.rs | |||
@@ -223,8 +223,7 @@ impl RootDatabase { | |||
223 | self.query(hir::db::FileItemsQuery).sweep(sweep); | 223 | self.query(hir::db::FileItemsQuery).sweep(sweep); |
224 | self.query(hir::db::FileItemQuery).sweep(sweep); | 224 | self.query(hir::db::FileItemQuery).sweep(sweep); |
225 | 225 | ||
226 | self.query(hir::db::LowerModuleQuery).sweep(sweep); | 226 | self.query(hir::db::LowerModuleWithSourceMapQuery).sweep(sweep); |
227 | self.query(hir::db::LowerModuleSourceMapQuery).sweep(sweep); | 227 | self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep); |
228 | self.query(hir::db::BodySyntaxMappingQuery).sweep(sweep); | ||
229 | } | 228 | } |
230 | } | 229 | } |
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs index d5ad2e79f..94c66be31 100644 --- a/crates/ra_ide_api/src/completion/complete_dot.rs +++ b/crates/ra_ide_api/src/completion/complete_dot.rs | |||
@@ -9,8 +9,8 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | |||
9 | _ => return, | 9 | _ => return, |
10 | }; | 10 | }; |
11 | let infer_result = function.infer(ctx.db); | 11 | let infer_result = function.infer(ctx.db); |
12 | let syntax_mapping = function.body_syntax_mapping(ctx.db); | 12 | let source_map = function.body_source_map(ctx.db); |
13 | let expr = match syntax_mapping.node_expr(receiver) { | 13 | let expr = match source_map.node_expr(receiver) { |
14 | Some(expr) => expr, | 14 | Some(expr) => expr, |
15 | None => return, | 15 | None => return, |
16 | }; | 16 | }; |
diff --git a/crates/ra_ide_api/src/completion/complete_struct_literal.rs b/crates/ra_ide_api/src/completion/complete_struct_literal.rs index afb092f59..6bef9624e 100644 --- a/crates/ra_ide_api/src/completion/complete_struct_literal.rs +++ b/crates/ra_ide_api/src/completion/complete_struct_literal.rs | |||
@@ -9,8 +9,8 @@ pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionCon | |||
9 | _ => return, | 9 | _ => return, |
10 | }; | 10 | }; |
11 | let infer_result = function.infer(ctx.db); | 11 | let infer_result = function.infer(ctx.db); |
12 | let syntax_mapping = function.body_syntax_mapping(ctx.db); | 12 | let source_map = function.body_source_map(ctx.db); |
13 | let expr = match syntax_mapping.node_expr(struct_lit.into()) { | 13 | let expr = match source_map.node_expr(struct_lit.into()) { |
14 | Some(expr) => expr, | 14 | Some(expr) => expr, |
15 | None => return, | 15 | None => return, |
16 | }; | 16 | }; |
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 6fa430754..9ec179593 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs | |||
@@ -54,10 +54,10 @@ pub(crate) fn reference_definition( | |||
54 | if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) { | 54 | if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) { |
55 | tested_by!(goto_definition_works_for_methods); | 55 | tested_by!(goto_definition_works_for_methods); |
56 | let infer_result = function.infer(db); | 56 | let infer_result = function.infer(db); |
57 | let syntax_mapping = function.body_syntax_mapping(db); | 57 | let source_map = function.body_source_map(db); |
58 | let expr = ast::Expr::cast(method_call.syntax()).unwrap(); | 58 | let expr = ast::Expr::cast(method_call.syntax()).unwrap(); |
59 | if let Some(func) = | 59 | if let Some(func) = |
60 | syntax_mapping.node_expr(expr).and_then(|it| infer_result.method_resolution(it)) | 60 | source_map.node_expr(expr).and_then(|it| infer_result.method_resolution(it)) |
61 | { | 61 | { |
62 | return Exact(NavigationTarget::from_function(db, func)); | 62 | return Exact(NavigationTarget::from_function(db, func)); |
63 | }; | 63 | }; |
@@ -66,10 +66,10 @@ pub(crate) fn reference_definition( | |||
66 | if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) { | 66 | if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) { |
67 | tested_by!(goto_definition_works_for_fields); | 67 | tested_by!(goto_definition_works_for_fields); |
68 | let infer_result = function.infer(db); | 68 | let infer_result = function.infer(db); |
69 | let syntax_mapping = function.body_syntax_mapping(db); | 69 | let source_map = function.body_source_map(db); |
70 | let expr = ast::Expr::cast(field_expr.syntax()).unwrap(); | 70 | let expr = ast::Expr::cast(field_expr.syntax()).unwrap(); |
71 | if let Some(field) = | 71 | if let Some(field) = |
72 | syntax_mapping.node_expr(expr).and_then(|it| infer_result.field_resolution(it)) | 72 | source_map.node_expr(expr).and_then(|it| infer_result.field_resolution(it)) |
73 | { | 73 | { |
74 | return Exact(NavigationTarget::from_field(db, field)); | 74 | return Exact(NavigationTarget::from_field(db, field)); |
75 | }; | 75 | }; |
@@ -80,11 +80,11 @@ pub(crate) fn reference_definition( | |||
80 | tested_by!(goto_definition_works_for_named_fields); | 80 | tested_by!(goto_definition_works_for_named_fields); |
81 | 81 | ||
82 | let infer_result = function.infer(db); | 82 | let infer_result = function.infer(db); |
83 | let syntax_mapping = function.body_syntax_mapping(db); | 83 | let source_map = function.body_source_map(db); |
84 | 84 | ||
85 | let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast); | 85 | let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast); |
86 | 86 | ||
87 | if let Some(expr) = struct_lit.and_then(|lit| syntax_mapping.node_expr(lit.into())) { | 87 | if let Some(expr) = struct_lit.and_then(|lit| source_map.node_expr(lit.into())) { |
88 | let ty = infer_result[expr].clone(); | 88 | let ty = infer_result[expr].clone(); |
89 | if let hir::Ty::Adt { def_id, .. } = ty { | 89 | if let hir::Ty::Adt { def_id, .. } = ty { |
90 | if let hir::AdtDef::Struct(s) = def_id { | 90 | if let hir::AdtDef::Struct(s) = def_id { |
@@ -109,9 +109,8 @@ pub(crate) fn reference_definition( | |||
109 | Some(Resolution::Def(def)) => return Exact(NavigationTarget::from_def(db, def)), | 109 | Some(Resolution::Def(def)) => return Exact(NavigationTarget::from_def(db, def)), |
110 | Some(Resolution::LocalBinding(pat)) => { | 110 | Some(Resolution::LocalBinding(pat)) => { |
111 | let body = resolver.body().expect("no body for local binding"); | 111 | let body = resolver.body().expect("no body for local binding"); |
112 | let syntax_mapping = body.syntax_mapping(db); | 112 | let source_map = body.owner().body_source_map(db); |
113 | let ptr = | 113 | let ptr = source_map.pat_syntax(pat).expect("pattern not found in syntax mapping"); |
114 | syntax_mapping.pat_syntax(pat).expect("pattern not found in syntax mapping"); | ||
115 | let name = | 114 | let name = |
116 | path.as_ident().cloned().expect("local binding from a multi-segment path"); | 115 | path.as_ident().cloned().expect("local binding from a multi-segment path"); |
117 | let nav = NavigationTarget::from_scope_entry(file_id, name, ptr); | 116 | let nav = NavigationTarget::from_scope_entry(file_id, name, ptr); |
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index 1a1853df3..8ec60090d 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -107,7 +107,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
107 | leaf.ancestors().find(|n| ast::Expr::cast(*n).is_some() || ast::Pat::cast(*n).is_some()) | 107 | leaf.ancestors().find(|n| ast::Expr::cast(*n).is_some() || ast::Pat::cast(*n).is_some()) |
108 | })?; | 108 | })?; |
109 | let frange = FileRange { file_id: position.file_id, range: node.range() }; | 109 | let frange = FileRange { file_id: position.file_id, range: node.range() }; |
110 | res.extend(type_of(db, frange).map(Into::into)); | 110 | res.extend(type_of(db, frange).map(rust_code_markup)); |
111 | range = Some(node.range()); | 111 | range = Some(node.range()); |
112 | } | 112 | } |
113 | 113 | ||
@@ -132,22 +132,37 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { | |||
132 | let parent_fn = node.ancestors().find_map(ast::FnDef::cast)?; | 132 | let parent_fn = node.ancestors().find_map(ast::FnDef::cast)?; |
133 | let function = hir::source_binder::function_from_source(db, frange.file_id, parent_fn)?; | 133 | let function = hir::source_binder::function_from_source(db, frange.file_id, parent_fn)?; |
134 | let infer = function.infer(db); | 134 | let infer = function.infer(db); |
135 | let syntax_mapping = function.body_syntax_mapping(db); | 135 | let source_map = function.body_source_map(db); |
136 | if let Some(expr) = ast::Expr::cast(node).and_then(|e| syntax_mapping.node_expr(e)) { | 136 | if let Some(expr) = ast::Expr::cast(node).and_then(|e| source_map.node_expr(e)) { |
137 | Some(infer[expr].to_string()) | 137 | Some(infer[expr].to_string()) |
138 | } else if let Some(pat) = ast::Pat::cast(node).and_then(|p| syntax_mapping.node_pat(p)) { | 138 | } else if let Some(pat) = ast::Pat::cast(node).and_then(|p| source_map.node_pat(p)) { |
139 | Some(infer[pat].to_string()) | 139 | Some(infer[pat].to_string()) |
140 | } else { | 140 | } else { |
141 | None | 141 | None |
142 | } | 142 | } |
143 | } | 143 | } |
144 | 144 | ||
145 | fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String { | ||
146 | rust_code_markup_with_doc::<_, &str>(val, None) | ||
147 | } | ||
148 | |||
149 | fn rust_code_markup_with_doc<CODE, DOC>(val: CODE, doc: Option<DOC>) -> String | ||
150 | where | ||
151 | CODE: AsRef<str>, | ||
152 | DOC: AsRef<str>, | ||
153 | { | ||
154 | if let Some(doc) = doc { | ||
155 | format!("```rust\n{}\n```\n\n{}", val.as_ref(), doc.as_ref()) | ||
156 | } else { | ||
157 | format!("```rust\n{}\n```", val.as_ref()) | ||
158 | } | ||
159 | } | ||
160 | |||
145 | // FIXME: this should not really use navigation target. Rather, approximately | 161 | // FIXME: this should not really use navigation target. Rather, approximately |
146 | // resolved symbol should return a `DefId`. | 162 | // resolved symbol should return a `DefId`. |
147 | fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Option<String> { | 163 | fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Option<String> { |
148 | match (nav.description(db), nav.docs(db)) { | 164 | match (nav.description(db), nav.docs(db)) { |
149 | (Some(desc), Some(docs)) => Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs), | 165 | (Some(desc), docs) => Some(rust_code_markup_with_doc(desc, docs)), |
150 | (Some(desc), None) => Some("```rust\n".to_string() + &*desc + "\n```"), | ||
151 | (None, Some(docs)) => Some(docs), | 166 | (None, Some(docs)) => Some(docs), |
152 | _ => None, | 167 | _ => None, |
153 | } | 168 | } |
@@ -238,6 +253,10 @@ mod tests { | |||
238 | s.trim_start_matches("```rust\n").trim_end_matches("\n```") | 253 | s.trim_start_matches("```rust\n").trim_end_matches("\n```") |
239 | } | 254 | } |
240 | 255 | ||
256 | fn trim_markup_opt(s: Option<&str>) -> Option<&str> { | ||
257 | s.map(trim_markup) | ||
258 | } | ||
259 | |||
241 | fn check_hover_result(fixture: &str, expected: &[&str]) { | 260 | fn check_hover_result(fixture: &str, expected: &[&str]) { |
242 | let (analysis, position) = analysis_and_position(fixture); | 261 | let (analysis, position) = analysis_and_position(fixture); |
243 | let hover = analysis.hover(position).unwrap().unwrap(); | 262 | let hover = analysis.hover(position).unwrap().unwrap(); |
@@ -264,7 +283,7 @@ mod tests { | |||
264 | ); | 283 | ); |
265 | let hover = analysis.hover(position).unwrap().unwrap(); | 284 | let hover = analysis.hover(position).unwrap().unwrap(); |
266 | assert_eq!(hover.range, TextRange::from_to(95.into(), 100.into())); | 285 | assert_eq!(hover.range, TextRange::from_to(95.into(), 100.into())); |
267 | assert_eq!(hover.info.first(), Some("u32")); | 286 | assert_eq!(trim_markup_opt(hover.info.first()), Some("u32")); |
268 | } | 287 | } |
269 | 288 | ||
270 | #[test] | 289 | #[test] |
@@ -410,21 +429,21 @@ mod tests { | |||
410 | ); | 429 | ); |
411 | let hover = analysis.hover(position).unwrap().unwrap(); | 430 | let hover = analysis.hover(position).unwrap().unwrap(); |
412 | // not the nicest way to show it currently | 431 | // not the nicest way to show it currently |
413 | assert_eq!(hover.info.first(), Some("Some<i32>(T) -> Option<T>")); | 432 | assert_eq!(trim_markup_opt(hover.info.first()), Some("Some<i32>(T) -> Option<T>")); |
414 | } | 433 | } |
415 | 434 | ||
416 | #[test] | 435 | #[test] |
417 | fn hover_for_local_variable() { | 436 | fn hover_for_local_variable() { |
418 | let (analysis, position) = single_file_with_position("fn func(foo: i32) { fo<|>o; }"); | 437 | let (analysis, position) = single_file_with_position("fn func(foo: i32) { fo<|>o; }"); |
419 | let hover = analysis.hover(position).unwrap().unwrap(); | 438 | let hover = analysis.hover(position).unwrap().unwrap(); |
420 | assert_eq!(hover.info.first(), Some("i32")); | 439 | assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); |
421 | } | 440 | } |
422 | 441 | ||
423 | #[test] | 442 | #[test] |
424 | fn hover_for_local_variable_pat() { | 443 | fn hover_for_local_variable_pat() { |
425 | let (analysis, position) = single_file_with_position("fn func(fo<|>o: i32) {}"); | 444 | let (analysis, position) = single_file_with_position("fn func(fo<|>o: i32) {}"); |
426 | let hover = analysis.hover(position).unwrap().unwrap(); | 445 | let hover = analysis.hover(position).unwrap().unwrap(); |
427 | assert_eq!(hover.info.first(), Some("i32")); | 446 | assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); |
428 | } | 447 | } |
429 | 448 | ||
430 | #[test] | 449 | #[test] |
@@ -455,7 +474,7 @@ mod tests { | |||
455 | ); | 474 | ); |
456 | 475 | ||
457 | let type_name = analysis.type_of(range).unwrap().unwrap(); | 476 | let type_name = analysis.type_of(range).unwrap().unwrap(); |
458 | assert_eq!("[unknown]", &type_name); | 477 | assert_eq!("{unknown}", &type_name); |
459 | } | 478 | } |
460 | 479 | ||
461 | #[test] | 480 | #[test] |
@@ -491,6 +510,6 @@ mod tests { | |||
491 | ", | 510 | ", |
492 | ); | 511 | ); |
493 | let hover = analysis.hover(position).unwrap().unwrap(); | 512 | let hover = analysis.hover(position).unwrap().unwrap(); |
494 | assert_eq!(hover.info.first(), Some("Thing")); | 513 | assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing")); |
495 | } | 514 | } |
496 | } | 515 | } |
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 6546d0644..b8a4adbce 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -32,13 +32,14 @@ mod references; | |||
32 | mod impls; | 32 | mod impls; |
33 | mod assists; | 33 | mod assists; |
34 | mod diagnostics; | 34 | mod diagnostics; |
35 | mod syntax_tree; | ||
35 | 36 | ||
36 | #[cfg(test)] | 37 | #[cfg(test)] |
37 | mod marks; | 38 | mod marks; |
38 | 39 | ||
39 | use std::sync::Arc; | 40 | use std::sync::Arc; |
40 | 41 | ||
41 | use ra_syntax::{SourceFile, TreeArc, TextRange, TextUnit, AstNode}; | 42 | use ra_syntax::{SourceFile, TreeArc, TextRange, TextUnit}; |
42 | use ra_text_edit::TextEdit; | 43 | use ra_text_edit::TextEdit; |
43 | use ra_db::{ | 44 | use ra_db::{ |
44 | SourceDatabase, CheckCanceled, | 45 | SourceDatabase, CheckCanceled, |
@@ -245,8 +246,8 @@ impl Analysis { | |||
245 | 246 | ||
246 | /// Returns a syntax tree represented as `String`, for debug purposes. | 247 | /// Returns a syntax tree represented as `String`, for debug purposes. |
247 | // FIXME: use a better name here. | 248 | // FIXME: use a better name here. |
248 | pub fn syntax_tree(&self, file_id: FileId) -> String { | 249 | pub fn syntax_tree(&self, file_id: FileId, text_range: Option<TextRange>) -> String { |
249 | self.db.parse(file_id).syntax().debug_dump() | 250 | syntax_tree::syntax_tree(&self.db, file_id, text_range) |
250 | } | 251 | } |
251 | 252 | ||
252 | /// Returns an edit to remove all newlines in the range, cleaning up minor | 253 | /// Returns an edit to remove all newlines in the range, cleaning up minor |
diff --git a/crates/ra_ide_api/src/syntax_tree.rs b/crates/ra_ide_api/src/syntax_tree.rs new file mode 100644 index 000000000..bbe9222b4 --- /dev/null +++ b/crates/ra_ide_api/src/syntax_tree.rs | |||
@@ -0,0 +1,87 @@ | |||
1 | use ra_db::SourceDatabase; | ||
2 | use crate::db::RootDatabase; | ||
3 | use ra_syntax::{ | ||
4 | SourceFile, SyntaxNode, TextRange, AstNode, | ||
5 | algo::{self, visit::{visitor, Visitor}}, ast::{self, AstToken} | ||
6 | }; | ||
7 | |||
8 | pub use ra_db::FileId; | ||
9 | |||
10 | pub(crate) fn syntax_tree( | ||
11 | db: &RootDatabase, | ||
12 | file_id: FileId, | ||
13 | text_range: Option<TextRange>, | ||
14 | ) -> String { | ||
15 | if let Some(text_range) = text_range { | ||
16 | let file = db.parse(file_id); | ||
17 | let node = algo::find_covering_node(file.syntax(), text_range); | ||
18 | |||
19 | if let Some(tree) = syntax_tree_for_string(node, text_range) { | ||
20 | return tree; | ||
21 | } | ||
22 | |||
23 | node.debug_dump() | ||
24 | } else { | ||
25 | db.parse(file_id).syntax().debug_dump() | ||
26 | } | ||
27 | } | ||
28 | |||
29 | /// Attempts parsing the selected contents of a string literal | ||
30 | /// as rust syntax and returns its syntax tree | ||
31 | fn syntax_tree_for_string(node: &SyntaxNode, text_range: TextRange) -> Option<String> { | ||
32 | // When the range is inside a string | ||
33 | // we'll attempt parsing it as rust syntax | ||
34 | // to provide the syntax tree of the contents of the string | ||
35 | visitor() | ||
36 | .visit(|node: &ast::String| syntax_tree_for_token(node, text_range)) | ||
37 | .visit(|node: &ast::RawString| syntax_tree_for_token(node, text_range)) | ||
38 | .accept(node)? | ||
39 | } | ||
40 | |||
41 | fn syntax_tree_for_token<T: AstToken>(node: &T, text_range: TextRange) -> Option<String> { | ||
42 | // Range of the full node | ||
43 | let node_range = node.syntax().range(); | ||
44 | let text = node.text().to_string(); | ||
45 | |||
46 | // We start at some point inside the node | ||
47 | // Either we have selected the whole string | ||
48 | // or our selection is inside it | ||
49 | let start = text_range.start() - node_range.start(); | ||
50 | |||
51 | // how many characters we have selected | ||
52 | let len = text_range.len().to_usize(); | ||
53 | |||
54 | let node_len = node_range.len().to_usize(); | ||
55 | |||
56 | let start = start.to_usize(); | ||
57 | |||
58 | // We want to cap our length | ||
59 | let len = len.min(node_len); | ||
60 | |||
61 | // Ensure our slice is inside the actual string | ||
62 | let end = if start + len < text.len() { start + len } else { text.len() - start }; | ||
63 | |||
64 | let text = &text[start..end]; | ||
65 | |||
66 | // Remove possible extra string quotes from the start | ||
67 | // and the end of the string | ||
68 | let text = text | ||
69 | .trim_start_matches('r') | ||
70 | .trim_start_matches('#') | ||
71 | .trim_start_matches('"') | ||
72 | .trim_end_matches('#') | ||
73 | .trim_end_matches('"') | ||
74 | .trim() | ||
75 | // Remove custom markers | ||
76 | .replace("<|>", ""); | ||
77 | |||
78 | let parsed = SourceFile::parse(&text); | ||
79 | |||
80 | // If the "file" parsed without errors, | ||
81 | // return its syntax | ||
82 | if parsed.errors().is_empty() { | ||
83 | return Some(parsed.syntax().debug_dump()); | ||
84 | } | ||
85 | |||
86 | None | ||
87 | } | ||