aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/call_info.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src/call_info.rs')
-rw-r--r--crates/ra_analysis/src/call_info.rs122
1 files changed, 112 insertions, 10 deletions
diff --git a/crates/ra_analysis/src/call_info.rs b/crates/ra_analysis/src/call_info.rs
index 2fcd03c9b..911ac3955 100644
--- a/crates/ra_analysis/src/call_info.rs
+++ b/crates/ra_analysis/src/call_info.rs
@@ -1,16 +1,17 @@
1use std::cmp::{max, min};
2
1use ra_db::{SyntaxDatabase, Cancelable}; 3use ra_db::{SyntaxDatabase, Cancelable};
2use ra_syntax::{ 4use ra_syntax::{
3 AstNode, SyntaxNode, TextUnit, TextRange, 5 AstNode, SyntaxNode, TextUnit, TextRange,
4 SyntaxKind::FN_DEF, 6 SyntaxKind::FN_DEF,
5 ast::{self, ArgListOwner}, 7 ast::{self, ArgListOwner, DocCommentsOwner},
6}; 8};
7use ra_editor::find_node_at_offset; 9use ra_editor::find_node_at_offset;
8use hir::FnSignatureInfo;
9 10
10use crate::{FilePosition, CallInfo, db::RootDatabase}; 11use crate::{FilePosition, CallInfo, db::RootDatabase};
11 12
12pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Cancelable<Option<CallInfo>> { 13pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Cancelable<Option<CallInfo>> {
13 let (sig_info, active_parameter) = ctry!(call_info_(db, position)?); 14 let (sig_info, active_parameter) = ctry!(signature_and_active_param(db, position)?);
14 let res = CallInfo { 15 let res = CallInfo {
15 label: sig_info.label, 16 label: sig_info.label,
16 doc: sig_info.doc, 17 doc: sig_info.doc,
@@ -21,7 +22,7 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Cancelable
21} 22}
22 23
23/// Computes parameter information for the given call expression. 24/// Computes parameter information for the given call expression.
24fn call_info_( 25fn signature_and_active_param(
25 db: &RootDatabase, 26 db: &RootDatabase,
26 position: FilePosition, 27 position: FilePosition,
27) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { 28) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> {
@@ -39,12 +40,7 @@ fn call_info_(
39 let fn_file = db.source_file(symbol.file_id); 40 let fn_file = db.source_file(symbol.file_id);
40 let fn_def = symbol.ptr.resolve(&fn_file); 41 let fn_def = symbol.ptr.resolve(&fn_file);
41 let fn_def = ast::FnDef::cast(&fn_def).unwrap(); 42 let fn_def = ast::FnDef::cast(&fn_def).unwrap();
42 let descr = ctry!(hir::source_binder::function_from_source( 43 if let Some(descriptor) = FnSignatureInfo::new(fn_def) {
43 db,
44 symbol.file_id,
45 fn_def
46 )?);
47 if let Some(descriptor) = descr.signature_info(db) {
48 // If we have a calling expression let's find which argument we are on 44 // If we have a calling expression let's find which argument we are on
49 let mut current_parameter = None; 45 let mut current_parameter = None;
50 46
@@ -129,6 +125,112 @@ impl<'a> FnCallNode<'a> {
129 } 125 }
130} 126}
131 127
128#[derive(Debug, Clone)]
129struct FnSignatureInfo {
130 label: String,
131 params: Vec<String>,
132 doc: Option<String>,
133}
134
135impl FnSignatureInfo {
136 fn new(node: &ast::FnDef) -> Option<Self> {
137 let mut doc = None;
138
139 // Strip the body out for the label.
140 let mut label: String = if let Some(body) = node.body() {
141 let body_range = body.syntax().range();
142 let label: String = node
143 .syntax()
144 .children()
145 .filter(|child| !child.range().is_subrange(&body_range))
146 .map(|node| node.text().to_string())
147 .collect();
148 label
149 } else {
150 node.syntax().text().to_string()
151 };
152
153 if let Some((comment_range, docs)) = FnSignatureInfo::extract_doc_comments(node) {
154 let comment_range = comment_range
155 .checked_sub(node.syntax().range().start())
156 .unwrap();
157 let start = comment_range.start().to_usize();
158 let end = comment_range.end().to_usize();
159
160 // Remove the comment from the label
161 label.replace_range(start..end, "");
162
163 // Massage markdown
164 let mut processed_lines = Vec::new();
165 let mut in_code_block = false;
166 for line in docs.lines() {
167 if line.starts_with("```") {
168 in_code_block = !in_code_block;
169 }
170
171 let line = if in_code_block && line.starts_with("```") && !line.contains("rust") {
172 "```rust".into()
173 } else {
174 line.to_string()
175 };
176
177 processed_lines.push(line);
178 }
179
180 if !processed_lines.is_empty() {
181 doc = Some(processed_lines.join("\n"));
182 }
183 }
184
185 let params = FnSignatureInfo::param_list(node);
186
187 Some(FnSignatureInfo {
188 params,
189 label: label.trim().to_owned(),
190 doc,
191 })
192 }
193
194 fn extract_doc_comments(node: &ast::FnDef) -> Option<(TextRange, String)> {
195 if node.doc_comments().count() == 0 {
196 return None;
197 }
198
199 let comment_text = node.doc_comment_text();
200
201 let (begin, end) = node
202 .doc_comments()
203 .map(|comment| comment.syntax().range())
204 .map(|range| (range.start().to_usize(), range.end().to_usize()))
205 .fold((std::usize::MAX, std::usize::MIN), |acc, range| {
206 (min(acc.0, range.0), max(acc.1, range.1))
207 });
208
209 let range = TextRange::from_to(TextUnit::from_usize(begin), TextUnit::from_usize(end));
210
211 Some((range, comment_text))
212 }
213
214 fn param_list(node: &ast::FnDef) -> Vec<String> {
215 let mut res = vec![];
216 if let Some(param_list) = node.param_list() {
217 if let Some(self_param) = param_list.self_param() {
218 res.push(self_param.syntax().text().to_string())
219 }
220
221 // Maybe use param.pat here? See if we can just extract the name?
222 //res.extend(param_list.params().map(|p| p.syntax().text().to_string()));
223 res.extend(
224 param_list
225 .params()
226 .filter_map(|p| p.pat())
227 .map(|pat| pat.syntax().text().to_string()),
228 );
229 }
230 res
231 }
232}
233
132#[cfg(test)] 234#[cfg(test)]
133mod tests { 235mod tests {
134 use super::*; 236 use super::*;