aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/hover.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-02-18 17:35:10 +0000
committerAleksey Kladov <[email protected]>2020-02-26 11:55:50 +0000
commitc3a4c4429de83450654795534e64e878a774a088 (patch)
tree12d89798f61b276f8bd640db07276a7d4e92b1c2 /crates/ra_ide/src/hover.rs
parent04deae3dba7c9b7054f7a1d64e4b93a05aecc132 (diff)
Refactor primary IDE API
This introduces the new type -- Semantics. Semantics maps SyntaxNodes to various semantic info, such as type, name resolution or macro expansions. To do so, Semantics maintains a HashMap which maps every node it saw to the file from which the node originated. This is enough to get all the necessary hir bits just from syntax.
Diffstat (limited to 'crates/ra_ide/src/hover.rs')
-rw-r--r--crates/ra_ide/src/hover.rs45
1 files changed, 22 insertions, 23 deletions
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 1c6ca36df..ace33c079 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -1,8 +1,10 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{db::AstDatabase, Adt, HasSource, HirDisplay, SourceBinder}; 3use hir::{Adt, HasSource, HirDisplay, Semantics};
4use ra_db::SourceDatabase; 4use ra_ide_db::{
5use ra_ide_db::{defs::NameDefinition, RootDatabase}; 5 defs::{classify_name, NameDefinition},
6 RootDatabase,
7};
6use ra_syntax::{ 8use ra_syntax::{
7 algo::find_covering_element, 9 algo::find_covering_element,
8 ast::{self, DocCommentsOwner}, 10 ast::{self, DocCommentsOwner},
@@ -13,8 +15,7 @@ use ra_syntax::{
13 15
14use crate::{ 16use crate::{
15 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, 17 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel},
16 expand::{descend_into_macros, original_range}, 18 references::classify_name_ref,
17 references::{classify_name, classify_name_ref},
18 FilePosition, FileRange, RangeInfo, 19 FilePosition, FileRange, RangeInfo,
19}; 20};
20 21
@@ -143,25 +144,25 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: NameDefinition) -> Option<S
143} 144}
144 145
145pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { 146pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> {
146 let file = db.parse_or_expand(position.file_id.into())?; 147 let sema = Semantics::new(db);
148 let file = sema.parse(position.file_id).syntax().clone();
147 let token = pick_best(file.token_at_offset(position.offset))?; 149 let token = pick_best(file.token_at_offset(position.offset))?;
148 let token = descend_into_macros(db, position.file_id, token); 150 let token = sema.descend_into_macros(token);
149 151
150 let mut res = HoverResult::new(); 152 let mut res = HoverResult::new();
151 153
152 let mut sb = SourceBinder::new(db);
153 if let Some((node, name_kind)) = match_ast! { 154 if let Some((node, name_kind)) = match_ast! {
154 match (token.value.parent()) { 155 match (token.parent()) {
155 ast::NameRef(name_ref) => { 156 ast::NameRef(name_ref) => {
156 classify_name_ref(&mut sb, token.with_value(&name_ref)).map(|d| (name_ref.syntax().clone(), d)) 157 classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d))
157 }, 158 },
158 ast::Name(name) => { 159 ast::Name(name) => {
159 classify_name(&mut sb, token.with_value(&name)).map(|d| (name.syntax().clone(), d)) 160 classify_name(&sema, &name).map(|d| (name.syntax().clone(), d))
160 }, 161 },
161 _ => None, 162 _ => None,
162 } 163 }
163 } { 164 } {
164 let range = original_range(db, token.with_value(&node)).range; 165 let range = sema.original_range(&node).range;
165 res.extend(hover_text_from_name_kind(db, name_kind)); 166 res.extend(hover_text_from_name_kind(db, name_kind));
166 167
167 if !res.is_empty() { 168 if !res.is_empty() {
@@ -170,11 +171,10 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
170 } 171 }
171 172
172 let node = token 173 let node = token
173 .value
174 .ancestors() 174 .ancestors()
175 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; 175 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
176 176
177 let frange = original_range(db, token.with_value(&node)); 177 let frange = sema.original_range(&node);
178 res.extend(type_of(db, frange).map(rust_code_markup)); 178 res.extend(type_of(db, frange).map(rust_code_markup));
179 if res.is_empty() { 179 if res.is_empty() {
180 return None; 180 return None;
@@ -197,19 +197,17 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
197} 197}
198 198
199pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { 199pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {
200 let parse = db.parse(frange.file_id); 200 let sema = Semantics::new(db);
201 let leaf_node = find_covering_element(parse.tree().syntax(), frange.range); 201 let source_file = sema.parse(frange.file_id);
202 let leaf_node = find_covering_element(source_file.syntax(), frange.range);
202 // if we picked identifier, expand to pattern/expression 203 // if we picked identifier, expand to pattern/expression
203 let node = leaf_node 204 let node = leaf_node
204 .ancestors() 205 .ancestors()
205 .take_while(|it| it.text_range() == leaf_node.text_range()) 206 .take_while(|it| it.text_range() == leaf_node.text_range())
206 .find(|it| ast::Expr::cast(it.clone()).is_some() || ast::Pat::cast(it.clone()).is_some())?; 207 .find(|it| ast::Expr::cast(it.clone()).is_some() || ast::Pat::cast(it.clone()).is_some())?;
207 let analyzer = 208 let ty = if let Some(ty) = ast::Expr::cast(node.clone()).and_then(|e| sema.type_of_expr(&e)) {
208 hir::SourceAnalyzer::new(db, hir::InFile::new(frange.file_id.into(), &node), None);
209 let ty = if let Some(ty) = ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e))
210 {
211 ty 209 ty
212 } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| analyzer.type_of_pat(db, &p)) { 210 } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| sema.type_of_pat(&p)) {
213 ty 211 ty
214 } else { 212 } else {
215 return None; 213 return None;
@@ -219,11 +217,12 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {
219 217
220#[cfg(test)] 218#[cfg(test)]
221mod tests { 219mod tests {
220 use ra_db::FileLoader;
221 use ra_syntax::TextRange;
222
222 use crate::mock_analysis::{ 223 use crate::mock_analysis::{
223 analysis_and_position, single_file_with_position, single_file_with_range, 224 analysis_and_position, single_file_with_position, single_file_with_range,
224 }; 225 };
225 use ra_db::FileLoader;
226 use ra_syntax::TextRange;
227 226
228 fn trim_markup(s: &str) -> &str { 227 fn trim_markup(s: &str) -> &str {
229 s.trim_start_matches("```rust\n").trim_end_matches("\n```") 228 s.trim_start_matches("```rust\n").trim_end_matches("\n```")