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