aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/hir/function/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src/hir/function/mod.rs')
-rw-r--r--crates/ra_analysis/src/hir/function/mod.rs137
1 files changed, 137 insertions, 0 deletions
diff --git a/crates/ra_analysis/src/hir/function/mod.rs b/crates/ra_analysis/src/hir/function/mod.rs
new file mode 100644
index 000000000..86eee5e93
--- /dev/null
+++ b/crates/ra_analysis/src/hir/function/mod.rs
@@ -0,0 +1,137 @@
1pub(super) mod imp;
2mod scope;
3
4use std::cmp::{max, min};
5
6use ra_syntax::{
7 ast::{self, AstNode, DocCommentsOwner, NameOwner},
8 TextRange, TextUnit,
9};
10
11use crate::{
12 syntax_ptr::SyntaxPtr, FileId,
13 loc2id::IdDatabase,
14};
15
16pub(crate) use self::scope::{resolve_local_name, FnScopes};
17pub(crate) use crate::loc2id::FnId;
18
19impl FnId {
20 pub(crate) fn get(db: &impl IdDatabase, file_id: FileId, fn_def: ast::FnDef) -> FnId {
21 let ptr = SyntaxPtr::new(file_id, fn_def.syntax());
22 db.id_maps().fn_id(ptr)
23 }
24}
25
26#[derive(Debug, Clone)]
27pub struct FnDescriptor {
28 pub name: String,
29 pub label: String,
30 pub ret_type: Option<String>,
31 pub params: Vec<String>,
32 pub doc: Option<String>,
33}
34
35impl FnDescriptor {
36 pub fn new(node: ast::FnDef) -> Option<Self> {
37 let name = node.name()?.text().to_string();
38
39 let mut doc = None;
40
41 // Strip the body out for the label.
42 let mut label: String = if let Some(body) = node.body() {
43 let body_range = body.syntax().range();
44 let label: String = node
45 .syntax()
46 .children()
47 .filter(|child| !child.range().is_subrange(&body_range))
48 .map(|node| node.text().to_string())
49 .collect();
50 label
51 } else {
52 node.syntax().text().to_string()
53 };
54
55 if let Some((comment_range, docs)) = FnDescriptor::extract_doc_comments(node) {
56 let comment_range = comment_range
57 .checked_sub(node.syntax().range().start())
58 .unwrap();
59 let start = comment_range.start().to_usize();
60 let end = comment_range.end().to_usize();
61
62 // Remove the comment from the label
63 label.replace_range(start..end, "");
64
65 // Massage markdown
66 let mut processed_lines = Vec::new();
67 let mut in_code_block = false;
68 for line in docs.lines() {
69 if line.starts_with("```") {
70 in_code_block = !in_code_block;
71 }
72
73 let line = if in_code_block && line.starts_with("```") && !line.contains("rust") {
74 "```rust".into()
75 } else {
76 line.to_string()
77 };
78
79 processed_lines.push(line);
80 }
81
82 if !processed_lines.is_empty() {
83 doc = Some(processed_lines.join("\n"));
84 }
85 }
86
87 let params = FnDescriptor::param_list(node);
88 let ret_type = node.ret_type().map(|r| r.syntax().text().to_string());
89
90 Some(FnDescriptor {
91 name,
92 ret_type,
93 params,
94 label: label.trim().to_owned(),
95 doc,
96 })
97 }
98
99 fn extract_doc_comments(node: ast::FnDef) -> Option<(TextRange, String)> {
100 if node.doc_comments().count() == 0 {
101 return None;
102 }
103
104 let comment_text = node.doc_comment_text();
105
106 let (begin, end) = node
107 .doc_comments()
108 .map(|comment| comment.syntax().range())
109 .map(|range| (range.start().to_usize(), range.end().to_usize()))
110 .fold((std::usize::MAX, std::usize::MIN), |acc, range| {
111 (min(acc.0, range.0), max(acc.1, range.1))
112 });
113
114 let range = TextRange::from_to(TextUnit::from_usize(begin), TextUnit::from_usize(end));
115
116 Some((range, comment_text))
117 }
118
119 fn param_list(node: ast::FnDef) -> Vec<String> {
120 let mut res = vec![];
121 if let Some(param_list) = node.param_list() {
122 if let Some(self_param) = param_list.self_param() {
123 res.push(self_param.syntax().text().to_string())
124 }
125
126 // Maybe use param.pat here? See if we can just extract the name?
127 //res.extend(param_list.params().map(|p| p.syntax().text().to_string()));
128 res.extend(
129 param_list
130 .params()
131 .filter_map(|p| p.pat())
132 .map(|pat| pat.syntax().text().to_string()),
133 );
134 }
135 res
136 }
137}