diff options
Diffstat (limited to 'crates/ra_ide_api/src')
14 files changed, 364 insertions, 213 deletions
diff --git a/crates/ra_ide_api/src/call_info.rs b/crates/ra_ide_api/src/call_info.rs index 29fa7d30b..dbb3853d0 100644 --- a/crates/ra_ide_api/src/call_info.rs +++ b/crates/ra_ide_api/src/call_info.rs | |||
@@ -6,9 +6,8 @@ use ra_syntax::{ | |||
6 | ast::{self, ArgListOwner}, | 6 | ast::{self, ArgListOwner}, |
7 | algo::find_node_at_offset, | 7 | algo::find_node_at_offset, |
8 | }; | 8 | }; |
9 | use hir::Docs; | ||
10 | 9 | ||
11 | use crate::{FilePosition, CallInfo, db::RootDatabase}; | 10 | use crate::{FilePosition, CallInfo, FunctionSignature, db::RootDatabase}; |
12 | 11 | ||
13 | /// Computes parameter information for the given call expression. | 12 | /// Computes parameter information for the given call expression. |
14 | pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> { | 13 | pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> { |
@@ -27,10 +26,10 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal | |||
27 | let fn_def = ast::FnDef::cast(fn_def).unwrap(); | 26 | let fn_def = ast::FnDef::cast(fn_def).unwrap(); |
28 | let function = hir::source_binder::function_from_source(db, symbol.file_id, fn_def)?; | 27 | let function = hir::source_binder::function_from_source(db, symbol.file_id, fn_def)?; |
29 | 28 | ||
30 | let mut call_info = CallInfo::new(db, function, fn_def)?; | 29 | let mut call_info = CallInfo::new(db, function); |
31 | 30 | ||
32 | // If we have a calling expression let's find which argument we are on | 31 | // If we have a calling expression let's find which argument we are on |
33 | let num_params = call_info.parameters.len(); | 32 | let num_params = call_info.parameters().len(); |
34 | let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); | 33 | let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); |
35 | 34 | ||
36 | if num_params == 1 { | 35 | if num_params == 1 { |
@@ -107,28 +106,15 @@ impl<'a> FnCallNode<'a> { | |||
107 | } | 106 | } |
108 | 107 | ||
109 | impl CallInfo { | 108 | impl CallInfo { |
110 | fn new(db: &RootDatabase, function: hir::Function, node: &ast::FnDef) -> Option<Self> { | 109 | fn new(db: &RootDatabase, function: hir::Function) -> Self { |
111 | let label = crate::completion::function_label(node)?; | 110 | let signature = FunctionSignature::from_hir(db, function); |
112 | let doc = function.docs(db); | ||
113 | 111 | ||
114 | Some(CallInfo { parameters: param_list(node), label, doc, active_parameter: None }) | 112 | CallInfo { signature, active_parameter: None } |
115 | } | 113 | } |
116 | } | ||
117 | |||
118 | fn param_list(node: &ast::FnDef) -> Vec<String> { | ||
119 | let mut res = vec![]; | ||
120 | if let Some(param_list) = node.param_list() { | ||
121 | if let Some(self_param) = param_list.self_param() { | ||
122 | res.push(self_param.syntax().text().to_string()) | ||
123 | } | ||
124 | 114 | ||
125 | // Maybe use param.pat here? See if we can just extract the name? | 115 | fn parameters(&self) -> &[String] { |
126 | //res.extend(param_list.params().map(|p| p.syntax().text().to_string())); | 116 | &self.signature.parameters |
127 | res.extend( | ||
128 | param_list.params().filter_map(|p| p.pat()).map(|pat| pat.syntax().text().to_string()), | ||
129 | ); | ||
130 | } | 117 | } |
131 | res | ||
132 | } | 118 | } |
133 | 119 | ||
134 | #[cfg(test)] | 120 | #[cfg(test)] |
@@ -139,6 +125,17 @@ mod tests { | |||
139 | 125 | ||
140 | use super::*; | 126 | use super::*; |
141 | 127 | ||
128 | // These are only used when testing | ||
129 | impl CallInfo { | ||
130 | fn doc(&self) -> Option<hir::Documentation> { | ||
131 | self.signature.doc.clone() | ||
132 | } | ||
133 | |||
134 | fn label(&self) -> String { | ||
135 | self.signature.to_string() | ||
136 | } | ||
137 | } | ||
138 | |||
142 | fn call_info(text: &str) -> CallInfo { | 139 | fn call_info(text: &str) -> CallInfo { |
143 | let (analysis, position) = single_file_with_position(text); | 140 | let (analysis, position) = single_file_with_position(text); |
144 | analysis.call_info(position).unwrap().unwrap() | 141 | analysis.call_info(position).unwrap().unwrap() |
@@ -151,7 +148,7 @@ mod tests { | |||
151 | fn bar() { foo(<|>3, ); }"#, | 148 | fn bar() { foo(<|>3, ); }"#, |
152 | ); | 149 | ); |
153 | 150 | ||
154 | assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string())); | 151 | assert_eq!(info.parameters(), ["x: u32", "y: u32"]); |
155 | assert_eq!(info.active_parameter, Some(0)); | 152 | assert_eq!(info.active_parameter, Some(0)); |
156 | } | 153 | } |
157 | 154 | ||
@@ -162,7 +159,7 @@ fn bar() { foo(<|>3, ); }"#, | |||
162 | fn bar() { foo(3, <|>); }"#, | 159 | fn bar() { foo(3, <|>); }"#, |
163 | ); | 160 | ); |
164 | 161 | ||
165 | assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string())); | 162 | assert_eq!(info.parameters(), ["x: u32", "y: u32"]); |
166 | assert_eq!(info.active_parameter, Some(1)); | 163 | assert_eq!(info.active_parameter, Some(1)); |
167 | } | 164 | } |
168 | 165 | ||
@@ -173,18 +170,57 @@ fn bar() { foo(3, <|>); }"#, | |||
173 | fn bar() { foo(<|>); }"#, | 170 | fn bar() { foo(<|>); }"#, |
174 | ); | 171 | ); |
175 | 172 | ||
176 | assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string())); | 173 | assert_eq!(info.parameters(), ["x: u32", "y: u32"]); |
174 | assert_eq!(info.active_parameter, Some(0)); | ||
175 | } | ||
176 | |||
177 | #[test] | ||
178 | fn test_fn_signature_two_args_first_generics() { | ||
179 | let info = call_info( | ||
180 | r#"fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 where T: Copy + Display, U: Debug {x + y} | ||
181 | fn bar() { foo(<|>3, ); }"#, | ||
182 | ); | ||
183 | |||
184 | assert_eq!(info.parameters(), ["x: T", "y: U"]); | ||
185 | assert_eq!( | ||
186 | info.label(), | ||
187 | r#" | ||
188 | fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 | ||
189 | where T: Copy + Display, | ||
190 | U: Debug | ||
191 | "# | ||
192 | .trim() | ||
193 | ); | ||
177 | assert_eq!(info.active_parameter, Some(0)); | 194 | assert_eq!(info.active_parameter, Some(0)); |
178 | } | 195 | } |
179 | 196 | ||
180 | #[test] | 197 | #[test] |
198 | fn test_fn_signature_no_params() { | ||
199 | let info = call_info( | ||
200 | r#"fn foo<T>() -> T where T: Copy + Display {} | ||
201 | fn bar() { foo(<|>); }"#, | ||
202 | ); | ||
203 | |||
204 | assert!(info.parameters().is_empty()); | ||
205 | assert_eq!( | ||
206 | info.label(), | ||
207 | r#" | ||
208 | fn foo<T>() -> T | ||
209 | where T: Copy + Display | ||
210 | "# | ||
211 | .trim() | ||
212 | ); | ||
213 | assert!(info.active_parameter.is_none()); | ||
214 | } | ||
215 | |||
216 | #[test] | ||
181 | fn test_fn_signature_for_impl() { | 217 | fn test_fn_signature_for_impl() { |
182 | let info = call_info( | 218 | let info = call_info( |
183 | r#"struct F; impl F { pub fn new() { F{}} } | 219 | r#"struct F; impl F { pub fn new() { F{}} } |
184 | fn bar() {let _ : F = F::new(<|>);}"#, | 220 | fn bar() {let _ : F = F::new(<|>);}"#, |
185 | ); | 221 | ); |
186 | 222 | ||
187 | assert_eq!(info.parameters, Vec::<String>::new()); | 223 | assert!(info.parameters().is_empty()); |
188 | assert_eq!(info.active_parameter, None); | 224 | assert_eq!(info.active_parameter, None); |
189 | } | 225 | } |
190 | 226 | ||
@@ -206,7 +242,7 @@ fn bar() { | |||
206 | }"#, | 242 | }"#, |
207 | ); | 243 | ); |
208 | 244 | ||
209 | assert_eq!(info.parameters, vec!["&self".to_string()]); | 245 | assert_eq!(info.parameters(), ["&self"]); |
210 | assert_eq!(info.active_parameter, None); | 246 | assert_eq!(info.active_parameter, None); |
211 | } | 247 | } |
212 | 248 | ||
@@ -228,7 +264,7 @@ fn bar() { | |||
228 | }"#, | 264 | }"#, |
229 | ); | 265 | ); |
230 | 266 | ||
231 | assert_eq!(info.parameters, vec!["&self".to_string(), "x".to_string()]); | 267 | assert_eq!(info.parameters(), ["&self", "x: i32"]); |
232 | assert_eq!(info.active_parameter, Some(1)); | 268 | assert_eq!(info.active_parameter, Some(1)); |
233 | } | 269 | } |
234 | 270 | ||
@@ -248,10 +284,10 @@ fn bar() { | |||
248 | "#, | 284 | "#, |
249 | ); | 285 | ); |
250 | 286 | ||
251 | assert_eq!(info.parameters, vec!["j".to_string()]); | 287 | assert_eq!(info.parameters(), ["j: u32"]); |
252 | assert_eq!(info.active_parameter, Some(0)); | 288 | assert_eq!(info.active_parameter, Some(0)); |
253 | assert_eq!(info.label, "fn foo(j: u32) -> u32".to_string()); | 289 | assert_eq!(info.label(), "fn foo(j: u32) -> u32"); |
254 | assert_eq!(info.doc.map(|it| it.into()), Some("test".to_string())); | 290 | assert_eq!(info.doc().map(|it| it.into()), Some("test".to_string())); |
255 | } | 291 | } |
256 | 292 | ||
257 | #[test] | 293 | #[test] |
@@ -276,11 +312,11 @@ pub fn do() { | |||
276 | }"#, | 312 | }"#, |
277 | ); | 313 | ); |
278 | 314 | ||
279 | assert_eq!(info.parameters, vec!["x".to_string()]); | 315 | assert_eq!(info.parameters(), ["x: i32"]); |
280 | assert_eq!(info.active_parameter, Some(0)); | 316 | assert_eq!(info.active_parameter, Some(0)); |
281 | assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string()); | 317 | assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32"); |
282 | assert_eq!( | 318 | assert_eq!( |
283 | info.doc.map(|it| it.into()), | 319 | info.doc().map(|it| it.into()), |
284 | Some( | 320 | Some( |
285 | r#"Adds one to the number given. | 321 | r#"Adds one to the number given. |
286 | 322 | ||
@@ -322,11 +358,11 @@ pub fn do_it() { | |||
322 | }"#, | 358 | }"#, |
323 | ); | 359 | ); |
324 | 360 | ||
325 | assert_eq!(info.parameters, vec!["x".to_string()]); | 361 | assert_eq!(info.parameters(), ["x: i32"]); |
326 | assert_eq!(info.active_parameter, Some(0)); | 362 | assert_eq!(info.active_parameter, Some(0)); |
327 | assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string()); | 363 | assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32"); |
328 | assert_eq!( | 364 | assert_eq!( |
329 | info.doc.map(|it| it.into()), | 365 | info.doc().map(|it| it.into()), |
330 | Some( | 366 | Some( |
331 | r#"Adds one to the number given. | 367 | r#"Adds one to the number given. |
332 | 368 | ||
@@ -375,10 +411,10 @@ pub fn foo() { | |||
375 | "#, | 411 | "#, |
376 | ); | 412 | ); |
377 | 413 | ||
378 | assert_eq!(info.parameters, vec!["&mut self".to_string(), "ctx".to_string()]); | 414 | assert_eq!(info.parameters(), ["&mut self", "ctx: &mut Self::Context"]); |
379 | assert_eq!(info.active_parameter, Some(1)); | 415 | assert_eq!(info.active_parameter, Some(1)); |
380 | assert_eq!( | 416 | assert_eq!( |
381 | info.doc.map(|it| it.into()), | 417 | info.doc().map(|it| it.into()), |
382 | Some( | 418 | Some( |
383 | r#"Method is called when writer finishes. | 419 | r#"Method is called when writer finishes. |
384 | 420 | ||
diff --git a/crates/ra_ide_api/src/completion.rs b/crates/ra_ide_api/src/completion.rs index a846a7a3c..deff59cd3 100644 --- a/crates/ra_ide_api/src/completion.rs +++ b/crates/ra_ide_api/src/completion.rs | |||
@@ -13,7 +13,6 @@ mod complete_scope; | |||
13 | mod complete_postfix; | 13 | mod complete_postfix; |
14 | 14 | ||
15 | use ra_db::SourceDatabase; | 15 | use ra_db::SourceDatabase; |
16 | use ra_syntax::{ast::{self, AstNode}, SyntaxKind::{ATTR, COMMENT}}; | ||
17 | 16 | ||
18 | use crate::{ | 17 | use crate::{ |
19 | db, | 18 | db, |
@@ -70,43 +69,3 @@ pub(crate) fn completions(db: &db::RootDatabase, position: FilePosition) -> Opti | |||
70 | complete_postfix::complete_postfix(&mut acc, &ctx); | 69 | complete_postfix::complete_postfix(&mut acc, &ctx); |
71 | Some(acc) | 70 | Some(acc) |
72 | } | 71 | } |
73 | |||
74 | pub fn function_label(node: &ast::FnDef) -> Option<String> { | ||
75 | let label: String = if let Some(body) = node.body() { | ||
76 | let body_range = body.syntax().range(); | ||
77 | let label: String = node | ||
78 | .syntax() | ||
79 | .children_with_tokens() | ||
80 | .filter(|child| !child.range().is_subrange(&body_range)) // Filter out body | ||
81 | .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR)) // Filter out comments and attrs | ||
82 | .map(|node| node.to_string()) | ||
83 | .collect(); | ||
84 | label | ||
85 | } else { | ||
86 | node.syntax().text().to_string() | ||
87 | }; | ||
88 | |||
89 | Some(label.trim().to_owned()) | ||
90 | } | ||
91 | |||
92 | pub fn const_label(node: &ast::ConstDef) -> String { | ||
93 | let label: String = node | ||
94 | .syntax() | ||
95 | .children_with_tokens() | ||
96 | .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR)) | ||
97 | .map(|node| node.to_string()) | ||
98 | .collect(); | ||
99 | |||
100 | label.trim().to_owned() | ||
101 | } | ||
102 | |||
103 | pub fn type_label(node: &ast::TypeAliasDef) -> String { | ||
104 | let label: String = node | ||
105 | .syntax() | ||
106 | .children_with_tokens() | ||
107 | .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR)) | ||
108 | .map(|node| node.to_string()) | ||
109 | .collect(); | ||
110 | |||
111 | label.trim().to_owned() | ||
112 | } | ||
diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs index 6146b7bb6..9d82f2270 100644 --- a/crates/ra_ide_api/src/completion/complete_scope.rs +++ b/crates/ra_ide_api/src/completion/complete_scope.rs | |||
@@ -145,7 +145,7 @@ mod tests { | |||
145 | check_reference_completion( | 145 | check_reference_completion( |
146 | "dont_show_both_completions_for_shadowing", | 146 | "dont_show_both_completions_for_shadowing", |
147 | r" | 147 | r" |
148 | fn foo() -> { | 148 | fn foo() { |
149 | let bar = 92; | 149 | let bar = 92; |
150 | { | 150 | { |
151 | let bar = 62; | 151 | let bar = 62; |
diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs index 28c8f83ab..9aa346688 100644 --- a/crates/ra_ide_api/src/completion/presentation.rs +++ b/crates/ra_ide_api/src/completion/presentation.rs | |||
@@ -6,6 +6,9 @@ use ra_syntax::ast::NameOwner; | |||
6 | 6 | ||
7 | use crate::completion::{ | 7 | use crate::completion::{ |
8 | Completions, CompletionKind, CompletionItemKind, CompletionContext, CompletionItem, | 8 | Completions, CompletionKind, CompletionItemKind, CompletionContext, CompletionItem, |
9 | }; | ||
10 | |||
11 | use crate::display::{ | ||
9 | function_label, const_label, type_label, | 12 | function_label, const_label, type_label, |
10 | }; | 13 | }; |
11 | 14 | ||
@@ -101,7 +104,7 @@ impl Completions { | |||
101 | CompletionItemKind::Function | 104 | CompletionItemKind::Function |
102 | }) | 105 | }) |
103 | .set_documentation(func.docs(ctx.db)) | 106 | .set_documentation(func.docs(ctx.db)) |
104 | .set_detail(detail); | 107 | .detail(detail); |
105 | // If not an import, add parenthesis automatically. | 108 | // If not an import, add parenthesis automatically. |
106 | if ctx.use_item_syntax.is_none() && !ctx.is_call { | 109 | if ctx.use_item_syntax.is_none() && !ctx.is_call { |
107 | tested_by!(inserts_parens_for_function_calls); | 110 | tested_by!(inserts_parens_for_function_calls); |
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap index 87691b304..34adcda6c 100644 --- a/crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap +++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__dont_show_both_completions_for_shadowing.snap | |||
@@ -1,23 +1,23 @@ | |||
1 | --- | 1 | --- |
2 | created: "2019-02-18T09:22:24.188564584Z" | 2 | created: "2019-04-04T14:52:24.531844100Z" |
3 | creator: insta@0.6.2 | 3 | creator: insta@0.7.4 |
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | 4 | source: crates/ra_ide_api/src/completion/completion_item.rs |
5 | expression: kind_completions | 5 | expression: kind_completions |
6 | --- | 6 | --- |
7 | [ | 7 | [ |
8 | CompletionItem { | 8 | CompletionItem { |
9 | label: "bar", | 9 | label: "bar", |
10 | source_range: [129; 129), | 10 | source_range: [126; 126), |
11 | delete: [129; 129), | 11 | delete: [126; 126), |
12 | insert: "bar", | 12 | insert: "bar", |
13 | kind: Binding | 13 | kind: Binding |
14 | }, | 14 | }, |
15 | CompletionItem { | 15 | CompletionItem { |
16 | label: "foo", | 16 | label: "foo", |
17 | source_range: [129; 129), | 17 | source_range: [126; 126), |
18 | delete: [129; 129), | 18 | delete: [126; 126), |
19 | insert: "foo()$0", | 19 | insert: "foo()$0", |
20 | kind: Function, | 20 | kind: Function, |
21 | detail: "fn foo() ->" | 21 | detail: "fn foo()" |
22 | } | 22 | } |
23 | ] | 23 | ] |
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__return_type.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__return_type.snap index 0738cf466..ff36df707 100644 --- a/crates/ra_ide_api/src/completion/snapshots/completion_item__return_type.snap +++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__return_type.snap | |||
@@ -1,6 +1,6 @@ | |||
1 | --- | 1 | --- |
2 | created: "2019-02-18T09:22:24.182964414Z" | 2 | created: "2019-04-04T14:52:24.525395600Z" |
3 | creator: insta@0.6.2 | 3 | creator: insta@0.7.4 |
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | 4 | source: crates/ra_ide_api/src/completion/completion_item.rs |
5 | expression: kind_completions | 5 | expression: kind_completions |
6 | --- | 6 | --- |
@@ -18,6 +18,6 @@ expression: kind_completions | |||
18 | delete: [47; 47), | 18 | delete: [47; 47), |
19 | insert: "x()$0", | 19 | insert: "x()$0", |
20 | kind: Function, | 20 | kind: Function, |
21 | detail: "fn x() ->" | 21 | detail: "fn x()" |
22 | } | 22 | } |
23 | ] | 23 | ] |
diff --git a/crates/ra_ide_api/src/display.rs b/crates/ra_ide_api/src/display.rs new file mode 100644 index 000000000..1b06abf94 --- /dev/null +++ b/crates/ra_ide_api/src/display.rs | |||
@@ -0,0 +1,82 @@ | |||
1 | //! This module contains utilities for turning SyntaxNodes and HIR types | ||
2 | //! into types that may be used to render in a UI. | ||
3 | |||
4 | mod function_signature; | ||
5 | mod navigation_target; | ||
6 | mod structure; | ||
7 | |||
8 | use crate::db::RootDatabase; | ||
9 | use ra_syntax::{ast::{self, AstNode, TypeParamsOwner}, SyntaxKind::{ATTR, COMMENT}}; | ||
10 | |||
11 | pub use navigation_target::NavigationTarget; | ||
12 | pub use structure::{StructureNode, file_structure}; | ||
13 | pub use function_signature::FunctionSignature; | ||
14 | |||
15 | pub(crate) fn function_label(node: &ast::FnDef) -> String { | ||
16 | FunctionSignature::from(node).to_string() | ||
17 | } | ||
18 | |||
19 | pub(crate) fn const_label(node: &ast::ConstDef) -> String { | ||
20 | let label: String = node | ||
21 | .syntax() | ||
22 | .children_with_tokens() | ||
23 | .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR)) | ||
24 | .map(|node| node.to_string()) | ||
25 | .collect(); | ||
26 | |||
27 | label.trim().to_owned() | ||
28 | } | ||
29 | |||
30 | pub(crate) fn type_label(node: &ast::TypeAliasDef) -> String { | ||
31 | let label: String = node | ||
32 | .syntax() | ||
33 | .children_with_tokens() | ||
34 | .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR)) | ||
35 | .map(|node| node.to_string()) | ||
36 | .collect(); | ||
37 | |||
38 | label.trim().to_owned() | ||
39 | } | ||
40 | |||
41 | pub(crate) fn generic_parameters<N: TypeParamsOwner>(node: &N) -> Vec<String> { | ||
42 | let mut res = vec![]; | ||
43 | if let Some(type_params) = node.type_param_list() { | ||
44 | res.extend(type_params.lifetime_params().map(|p| p.syntax().text().to_string())); | ||
45 | res.extend(type_params.type_params().map(|p| p.syntax().text().to_string())); | ||
46 | } | ||
47 | res | ||
48 | } | ||
49 | |||
50 | pub(crate) fn where_predicates<N: TypeParamsOwner>(node: &N) -> Vec<String> { | ||
51 | let mut res = vec![]; | ||
52 | if let Some(clause) = node.where_clause() { | ||
53 | res.extend(clause.predicates().map(|p| p.syntax().text().to_string())); | ||
54 | } | ||
55 | res | ||
56 | } | ||
57 | |||
58 | pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String { | ||
59 | rust_code_markup_with_doc::<_, &str>(val, None) | ||
60 | } | ||
61 | |||
62 | pub(crate) fn rust_code_markup_with_doc<CODE, DOC>(val: CODE, doc: Option<DOC>) -> String | ||
63 | where | ||
64 | CODE: AsRef<str>, | ||
65 | DOC: AsRef<str>, | ||
66 | { | ||
67 | if let Some(doc) = doc { | ||
68 | format!("```rust\n{}\n```\n\n{}", val.as_ref(), doc.as_ref()) | ||
69 | } else { | ||
70 | format!("```rust\n{}\n```", val.as_ref()) | ||
71 | } | ||
72 | } | ||
73 | |||
74 | // FIXME: this should not really use navigation target. Rather, approximately | ||
75 | // resolved symbol should return a `DefId`. | ||
76 | pub(crate) fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Option<String> { | ||
77 | match (nav.description(db), nav.docs(db)) { | ||
78 | (Some(desc), docs) => Some(rust_code_markup_with_doc(desc, docs)), | ||
79 | (None, Some(docs)) => Some(docs), | ||
80 | _ => None, | ||
81 | } | ||
82 | } | ||
diff --git a/crates/ra_ide_api/src/display/function_signature.rs b/crates/ra_ide_api/src/display/function_signature.rs new file mode 100644 index 000000000..d09950bce --- /dev/null +++ b/crates/ra_ide_api/src/display/function_signature.rs | |||
@@ -0,0 +1,101 @@ | |||
1 | use super::{where_predicates, generic_parameters}; | ||
2 | use crate::db; | ||
3 | use std::fmt::{self, Display}; | ||
4 | use join_to_string::join; | ||
5 | use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; | ||
6 | use std::convert::From; | ||
7 | use hir::{Docs, Documentation}; | ||
8 | |||
9 | /// Contains information about a function signature | ||
10 | #[derive(Debug)] | ||
11 | pub struct FunctionSignature { | ||
12 | /// Optional visibility | ||
13 | pub visibility: Option<String>, | ||
14 | /// Name of the function | ||
15 | pub name: Option<String>, | ||
16 | /// Documentation for the function | ||
17 | pub doc: Option<Documentation>, | ||
18 | /// Generic parameters | ||
19 | pub generic_parameters: Vec<String>, | ||
20 | /// Parameters of the function | ||
21 | pub parameters: Vec<String>, | ||
22 | /// Optional return type | ||
23 | pub ret_type: Option<String>, | ||
24 | /// Where predicates | ||
25 | pub where_predicates: Vec<String>, | ||
26 | } | ||
27 | |||
28 | impl FunctionSignature { | ||
29 | pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self { | ||
30 | self.doc = doc; | ||
31 | self | ||
32 | } | ||
33 | |||
34 | pub(crate) fn from_hir(db: &db::RootDatabase, function: hir::Function) -> Self { | ||
35 | let doc = function.docs(db); | ||
36 | let (_, ast_node) = function.source(db); | ||
37 | FunctionSignature::from(&*ast_node).with_doc_opt(doc) | ||
38 | } | ||
39 | } | ||
40 | |||
41 | impl From<&'_ ast::FnDef> for FunctionSignature { | ||
42 | fn from(node: &ast::FnDef) -> FunctionSignature { | ||
43 | fn param_list(node: &ast::FnDef) -> Vec<String> { | ||
44 | let mut res = vec![]; | ||
45 | if let Some(param_list) = node.param_list() { | ||
46 | if let Some(self_param) = param_list.self_param() { | ||
47 | res.push(self_param.syntax().text().to_string()) | ||
48 | } | ||
49 | |||
50 | res.extend(param_list.params().map(|param| param.syntax().text().to_string())); | ||
51 | } | ||
52 | res | ||
53 | } | ||
54 | |||
55 | FunctionSignature { | ||
56 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), | ||
57 | name: node.name().map(|n| n.text().to_string()), | ||
58 | ret_type: node | ||
59 | .ret_type() | ||
60 | .and_then(|r| r.type_ref()) | ||
61 | .map(|n| n.syntax().text().to_string()), | ||
62 | parameters: param_list(node), | ||
63 | generic_parameters: generic_parameters(node), | ||
64 | where_predicates: where_predicates(node), | ||
65 | // docs are processed separately | ||
66 | doc: None, | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | |||
71 | impl Display for FunctionSignature { | ||
72 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
73 | if let Some(t) = &self.visibility { | ||
74 | write!(f, "{} ", t)?; | ||
75 | } | ||
76 | |||
77 | if let Some(name) = &self.name { | ||
78 | write!(f, "fn {}", name)?; | ||
79 | } | ||
80 | |||
81 | if !self.generic_parameters.is_empty() { | ||
82 | join(self.generic_parameters.iter()) | ||
83 | .separator(", ") | ||
84 | .surround_with("<", ">") | ||
85 | .to_fmt(f)?; | ||
86 | } | ||
87 | |||
88 | join(self.parameters.iter()).separator(", ").surround_with("(", ")").to_fmt(f)?; | ||
89 | |||
90 | if let Some(t) = &self.ret_type { | ||
91 | write!(f, " -> {}", t)?; | ||
92 | } | ||
93 | |||
94 | if !self.where_predicates.is_empty() { | ||
95 | write!(f, "\nwhere ")?; | ||
96 | join(self.where_predicates.iter()).separator(",\n ").to_fmt(f)?; | ||
97 | } | ||
98 | |||
99 | Ok(()) | ||
100 | } | ||
101 | } | ||
diff --git a/crates/ra_ide_api/src/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs index f6d7f3192..3c518faf5 100644 --- a/crates/ra_ide_api/src/navigation_target.rs +++ b/crates/ra_ide_api/src/display/navigation_target.rs | |||
@@ -1,7 +1,9 @@ | |||
1 | use ra_db::FileId; | 1 | use ra_db::{FileId, SourceDatabase}; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | SyntaxNode, SyntaxNodePtr, AstNode, SmolStr, TextRange, ast, | 3 | SyntaxNode, SyntaxNodePtr, AstNode, SmolStr, TextRange, TreeArc, |
4 | SyntaxKind::{self, NAME}, | 4 | SyntaxKind::{self, NAME}, |
5 | ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner}, | ||
6 | algo::visit::{visitor, Visitor}, | ||
5 | }; | 7 | }; |
6 | use hir::{ModuleSource, FieldSource, Name, ImplItem}; | 8 | use hir::{ModuleSource, FieldSource, Name, ImplItem}; |
7 | 9 | ||
@@ -248,4 +250,80 @@ impl NavigationTarget { | |||
248 | container_name: None, | 250 | container_name: None, |
249 | } | 251 | } |
250 | } | 252 | } |
253 | |||
254 | pub(crate) fn node(&self, db: &RootDatabase) -> Option<TreeArc<SyntaxNode>> { | ||
255 | let source_file = db.parse(self.file_id()); | ||
256 | let source_file = source_file.syntax(); | ||
257 | let node = source_file | ||
258 | .descendants() | ||
259 | .find(|node| node.kind() == self.kind() && node.range() == self.full_range())? | ||
260 | .to_owned(); | ||
261 | Some(node) | ||
262 | } | ||
263 | |||
264 | pub(crate) fn docs(&self, db: &RootDatabase) -> Option<String> { | ||
265 | let node = self.node(db)?; | ||
266 | fn doc_comments<N: ast::DocCommentsOwner>(node: &N) -> Option<String> { | ||
267 | node.doc_comment_text() | ||
268 | } | ||
269 | |||
270 | visitor() | ||
271 | .visit(doc_comments::<ast::FnDef>) | ||
272 | .visit(doc_comments::<ast::StructDef>) | ||
273 | .visit(doc_comments::<ast::EnumDef>) | ||
274 | .visit(doc_comments::<ast::TraitDef>) | ||
275 | .visit(doc_comments::<ast::Module>) | ||
276 | .visit(doc_comments::<ast::TypeAliasDef>) | ||
277 | .visit(doc_comments::<ast::ConstDef>) | ||
278 | .visit(doc_comments::<ast::StaticDef>) | ||
279 | .visit(doc_comments::<ast::NamedFieldDef>) | ||
280 | .visit(doc_comments::<ast::EnumVariant>) | ||
281 | .accept(&node)? | ||
282 | } | ||
283 | |||
284 | /// Get a description of this node. | ||
285 | /// | ||
286 | /// e.g. `struct Name`, `enum Name`, `fn Name` | ||
287 | pub(crate) fn description(&self, db: &RootDatabase) -> Option<String> { | ||
288 | // FIXME: After type inference is done, add type information to improve the output | ||
289 | let node = self.node(db)?; | ||
290 | |||
291 | fn visit_ascribed_node<T>(node: &T, prefix: &str) -> Option<String> | ||
292 | where | ||
293 | T: NameOwner + VisibilityOwner + TypeAscriptionOwner, | ||
294 | { | ||
295 | let mut string = visit_node(node, prefix)?; | ||
296 | |||
297 | if let Some(type_ref) = node.ascribed_type() { | ||
298 | string.push_str(": "); | ||
299 | type_ref.syntax().text().push_to(&mut string); | ||
300 | } | ||
301 | |||
302 | Some(string) | ||
303 | } | ||
304 | |||
305 | fn visit_node<T>(node: &T, label: &str) -> Option<String> | ||
306 | where | ||
307 | T: NameOwner + VisibilityOwner, | ||
308 | { | ||
309 | let mut string = | ||
310 | node.visibility().map(|v| format!("{} ", v.syntax().text())).unwrap_or_default(); | ||
311 | string.push_str(label); | ||
312 | string.push_str(node.name()?.text().as_str()); | ||
313 | Some(string) | ||
314 | } | ||
315 | |||
316 | visitor() | ||
317 | .visit(|node: &ast::FnDef| Some(crate::display::function_label(node))) | ||
318 | .visit(|node: &ast::StructDef| visit_node(node, "struct ")) | ||
319 | .visit(|node: &ast::EnumDef| visit_node(node, "enum ")) | ||
320 | .visit(|node: &ast::TraitDef| visit_node(node, "trait ")) | ||
321 | .visit(|node: &ast::Module| visit_node(node, "mod ")) | ||
322 | .visit(|node: &ast::TypeAliasDef| visit_node(node, "type ")) | ||
323 | .visit(|node: &ast::ConstDef| visit_ascribed_node(node, "const ")) | ||
324 | .visit(|node: &ast::StaticDef| visit_ascribed_node(node, "static ")) | ||
325 | .visit(|node: &ast::NamedFieldDef| visit_ascribed_node(node, "")) | ||
326 | .visit(|node: &ast::EnumVariant| Some(node.name()?.text().to_string())) | ||
327 | .accept(&node)? | ||
328 | } | ||
251 | } | 329 | } |
diff --git a/crates/ra_ide_api/src/snapshots/tests__file_structure.snap b/crates/ra_ide_api/src/display/snapshots/tests__file_structure.snap index 2efa8e22c..32dd99484 100644 --- a/crates/ra_ide_api/src/snapshots/tests__file_structure.snap +++ b/crates/ra_ide_api/src/display/snapshots/tests__file_structure.snap | |||
@@ -1,7 +1,7 @@ | |||
1 | --- | 1 | --- |
2 | created: "2019-02-05T22:03:50.763530100Z" | 2 | created: "2019-04-08T09:44:50.196004400Z" |
3 | creator: insta@0.6.1 | 3 | creator: insta@0.7.4 |
4 | source: crates/ra_ide_api/src/structure.rs | 4 | source: crates/ra_ide_api/src/display/structure.rs |
5 | expression: structure | 5 | expression: structure |
6 | --- | 6 | --- |
7 | [ | 7 | [ |
diff --git a/crates/ra_ide_api/src/structure.rs b/crates/ra_ide_api/src/display/structure.rs index ec2c9bbc6..ec2c9bbc6 100644 --- a/crates/ra_ide_api/src/structure.rs +++ b/crates/ra_ide_api/src/display/structure.rs | |||
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index bfa7cd67a..3a8c93b99 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -1,11 +1,11 @@ | |||
1 | use ra_db::SourceDatabase; | 1 | use ra_db::SourceDatabase; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | AstNode, SyntaxNode, TreeArc, ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner}, | 3 | AstNode, ast, |
4 | algo::{find_covering_element, find_node_at_offset, find_token_at_offset, visit::{visitor, Visitor}}, | 4 | algo::{find_covering_element, find_node_at_offset, find_token_at_offset}, |
5 | }; | 5 | }; |
6 | use hir::HirDisplay; | 6 | use hir::HirDisplay; |
7 | 7 | ||
8 | use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget}; | 8 | use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, display::{rust_code_markup, doc_text_for}}; |
9 | 9 | ||
10 | /// Contains the results when hovering over an item | 10 | /// Contains the results when hovering over an item |
11 | #[derive(Debug, Clone)] | 11 | #[derive(Debug, Clone)] |
@@ -145,110 +145,6 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { | |||
145 | } | 145 | } |
146 | } | 146 | } |
147 | 147 | ||
148 | fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String { | ||
149 | rust_code_markup_with_doc::<_, &str>(val, None) | ||
150 | } | ||
151 | |||
152 | fn rust_code_markup_with_doc<CODE, DOC>(val: CODE, doc: Option<DOC>) -> String | ||
153 | where | ||
154 | CODE: AsRef<str>, | ||
155 | DOC: AsRef<str>, | ||
156 | { | ||
157 | if let Some(doc) = doc { | ||
158 | format!("```rust\n{}\n```\n\n{}", val.as_ref(), doc.as_ref()) | ||
159 | } else { | ||
160 | format!("```rust\n{}\n```", val.as_ref()) | ||
161 | } | ||
162 | } | ||
163 | |||
164 | // FIXME: this should not really use navigation target. Rather, approximately | ||
165 | // resolved symbol should return a `DefId`. | ||
166 | fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Option<String> { | ||
167 | match (nav.description(db), nav.docs(db)) { | ||
168 | (Some(desc), docs) => Some(rust_code_markup_with_doc(desc, docs)), | ||
169 | (None, Some(docs)) => Some(docs), | ||
170 | _ => None, | ||
171 | } | ||
172 | } | ||
173 | |||
174 | impl NavigationTarget { | ||
175 | fn node(&self, db: &RootDatabase) -> Option<TreeArc<SyntaxNode>> { | ||
176 | let source_file = db.parse(self.file_id()); | ||
177 | let source_file = source_file.syntax(); | ||
178 | let node = source_file | ||
179 | .descendants() | ||
180 | .find(|node| node.kind() == self.kind() && node.range() == self.full_range())? | ||
181 | .to_owned(); | ||
182 | Some(node) | ||
183 | } | ||
184 | |||
185 | fn docs(&self, db: &RootDatabase) -> Option<String> { | ||
186 | let node = self.node(db)?; | ||
187 | fn doc_comments<N: ast::DocCommentsOwner>(node: &N) -> Option<String> { | ||
188 | node.doc_comment_text() | ||
189 | } | ||
190 | |||
191 | visitor() | ||
192 | .visit(doc_comments::<ast::FnDef>) | ||
193 | .visit(doc_comments::<ast::StructDef>) | ||
194 | .visit(doc_comments::<ast::EnumDef>) | ||
195 | .visit(doc_comments::<ast::TraitDef>) | ||
196 | .visit(doc_comments::<ast::Module>) | ||
197 | .visit(doc_comments::<ast::TypeAliasDef>) | ||
198 | .visit(doc_comments::<ast::ConstDef>) | ||
199 | .visit(doc_comments::<ast::StaticDef>) | ||
200 | .visit(doc_comments::<ast::NamedFieldDef>) | ||
201 | .visit(doc_comments::<ast::EnumVariant>) | ||
202 | .accept(&node)? | ||
203 | } | ||
204 | |||
205 | /// Get a description of this node. | ||
206 | /// | ||
207 | /// e.g. `struct Name`, `enum Name`, `fn Name` | ||
208 | fn description(&self, db: &RootDatabase) -> Option<String> { | ||
209 | // FIXME: After type inference is done, add type information to improve the output | ||
210 | let node = self.node(db)?; | ||
211 | |||
212 | fn visit_ascribed_node<T>(node: &T, prefix: &str) -> Option<String> | ||
213 | where | ||
214 | T: NameOwner + VisibilityOwner + TypeAscriptionOwner, | ||
215 | { | ||
216 | let mut string = visit_node(node, prefix)?; | ||
217 | |||
218 | if let Some(type_ref) = node.ascribed_type() { | ||
219 | string.push_str(": "); | ||
220 | type_ref.syntax().text().push_to(&mut string); | ||
221 | } | ||
222 | |||
223 | Some(string) | ||
224 | } | ||
225 | |||
226 | fn visit_node<T>(node: &T, label: &str) -> Option<String> | ||
227 | where | ||
228 | T: NameOwner + VisibilityOwner, | ||
229 | { | ||
230 | let mut string = | ||
231 | node.visibility().map(|v| format!("{} ", v.syntax().text())).unwrap_or_default(); | ||
232 | string.push_str(label); | ||
233 | string.push_str(node.name()?.text().as_str()); | ||
234 | Some(string) | ||
235 | } | ||
236 | |||
237 | visitor() | ||
238 | .visit(crate::completion::function_label) | ||
239 | .visit(|node: &ast::StructDef| visit_node(node, "struct ")) | ||
240 | .visit(|node: &ast::EnumDef| visit_node(node, "enum ")) | ||
241 | .visit(|node: &ast::TraitDef| visit_node(node, "trait ")) | ||
242 | .visit(|node: &ast::Module| visit_node(node, "mod ")) | ||
243 | .visit(|node: &ast::TypeAliasDef| visit_node(node, "type ")) | ||
244 | .visit(|node: &ast::ConstDef| visit_ascribed_node(node, "const ")) | ||
245 | .visit(|node: &ast::StaticDef| visit_ascribed_node(node, "static ")) | ||
246 | .visit(|node: &ast::NamedFieldDef| visit_ascribed_node(node, "")) | ||
247 | .visit(|node: &ast::EnumVariant| Some(node.name()?.text().to_string())) | ||
248 | .accept(&node)? | ||
249 | } | ||
250 | } | ||
251 | |||
252 | #[cfg(test)] | 148 | #[cfg(test)] |
253 | mod tests { | 149 | mod tests { |
254 | use ra_syntax::TextRange; | 150 | use ra_syntax::TextRange; |
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 9063f78a9..d25795adc 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -13,7 +13,6 @@ | |||
13 | mod db; | 13 | mod db; |
14 | pub mod mock_analysis; | 14 | pub mod mock_analysis; |
15 | mod symbol_index; | 15 | mod symbol_index; |
16 | mod navigation_target; | ||
17 | mod change; | 16 | mod change; |
18 | 17 | ||
19 | mod status; | 18 | mod status; |
@@ -34,9 +33,9 @@ mod folding_ranges; | |||
34 | mod line_index; | 33 | mod line_index; |
35 | mod line_index_utils; | 34 | mod line_index_utils; |
36 | mod join_lines; | 35 | mod join_lines; |
37 | mod structure; | ||
38 | mod typing; | 36 | mod typing; |
39 | mod matching_brace; | 37 | mod matching_brace; |
38 | mod display; | ||
40 | 39 | ||
41 | #[cfg(test)] | 40 | #[cfg(test)] |
42 | mod marks; | 41 | mod marks; |
@@ -62,7 +61,6 @@ pub use crate::{ | |||
62 | change::{AnalysisChange, LibraryData}, | 61 | change::{AnalysisChange, LibraryData}, |
63 | completion::{CompletionItem, CompletionItemKind, InsertTextFormat}, | 62 | completion::{CompletionItem, CompletionItemKind, InsertTextFormat}, |
64 | runnables::{Runnable, RunnableKind}, | 63 | runnables::{Runnable, RunnableKind}, |
65 | navigation_target::NavigationTarget, | ||
66 | references::ReferenceSearchResult, | 64 | references::ReferenceSearchResult, |
67 | assists::{Assist, AssistId}, | 65 | assists::{Assist, AssistId}, |
68 | hover::{HoverResult}, | 66 | hover::{HoverResult}, |
@@ -70,8 +68,8 @@ pub use crate::{ | |||
70 | line_index_utils::translate_offset_with_edit, | 68 | line_index_utils::translate_offset_with_edit, |
71 | folding_ranges::{Fold, FoldKind}, | 69 | folding_ranges::{Fold, FoldKind}, |
72 | syntax_highlighting::HighlightedRange, | 70 | syntax_highlighting::HighlightedRange, |
73 | structure::{StructureNode, file_structure}, | ||
74 | diagnostics::Severity, | 71 | diagnostics::Severity, |
72 | display::{FunctionSignature, NavigationTarget, StructureNode, file_structure}, | ||
75 | }; | 73 | }; |
76 | 74 | ||
77 | pub use ra_db::{ | 75 | pub use ra_db::{ |
@@ -243,9 +241,7 @@ impl<T> RangeInfo<T> { | |||
243 | 241 | ||
244 | #[derive(Debug)] | 242 | #[derive(Debug)] |
245 | pub struct CallInfo { | 243 | pub struct CallInfo { |
246 | pub label: String, | 244 | pub signature: FunctionSignature, |
247 | pub doc: Option<Documentation>, | ||
248 | pub parameters: Vec<String>, | ||
249 | pub active_parameter: Option<usize>, | 245 | pub active_parameter: Option<usize>, |
250 | } | 246 | } |
251 | 247 | ||
@@ -387,7 +383,7 @@ impl Analysis { | |||
387 | /// file outline. | 383 | /// file outline. |
388 | pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { | 384 | pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { |
389 | let file = self.db.parse(file_id); | 385 | let file = self.db.parse(file_id); |
390 | structure::file_structure(&file) | 386 | file_structure(&file) |
391 | } | 387 | } |
392 | 388 | ||
393 | /// Returns the set of folding ranges. | 389 | /// Returns the set of folding ranges. |
diff --git a/crates/ra_ide_api/src/symbol_index.rs b/crates/ra_ide_api/src/symbol_index.rs index 0eadc4e71..914d3fc71 100644 --- a/crates/ra_ide_api/src/symbol_index.rs +++ b/crates/ra_ide_api/src/symbol_index.rs | |||
@@ -275,7 +275,7 @@ fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> { | |||
275 | mod tests { | 275 | mod tests { |
276 | use ra_syntax::SmolStr; | 276 | use ra_syntax::SmolStr; |
277 | use crate::{ | 277 | use crate::{ |
278 | navigation_target::NavigationTarget, | 278 | display::NavigationTarget, |
279 | mock_analysis::single_file, | 279 | mock_analysis::single_file, |
280 | Query, | 280 | Query, |
281 | }; | 281 | }; |