Skip to content

Commit e92f2ff

Browse files
bors[bot]matklad
andcommitted
Merge #410
410: Detangle symbols r=matklad a=matklad Previously, we used `FileSymbol` both to represent bytes which are stored in the index and as an API of `ra_analysis`. Mixing internal storage format and an API is not a really bright idea, so we introduce `NavigationTarget` to handle API part. Co-authored-by: Aleksey Kladov <[email protected]>
2 parents f673529 + 8d61853 commit e92f2ff

File tree

6 files changed

+221
-171
lines changed

6 files changed

+221
-171
lines changed

crates/ra_analysis/src/imp.rs

+150-61
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ use hir::{
88
use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase};
99
use ra_editor::{self, find_node_at_offset, LocalEdit, Severity};
1010
use ra_syntax::{
11-
algo::find_covering_node,
11+
algo::{find_covering_node, visit::{visitor, Visitor}},
1212
ast::{self, ArgListOwner, Expr, FnDef, NameOwner},
1313
AstNode, SourceFileNode,
1414
SyntaxKind::*,
15-
SyntaxNodeRef, TextRange, TextUnit,
15+
SyntaxNode, SyntaxNodeRef, TextRange, TextUnit,
1616
};
1717

1818
use crate::{
@@ -116,12 +116,13 @@ impl db::RootDatabase {
116116
};
117117
let decl = decl.borrowed();
118118
let decl_name = decl.name().unwrap();
119-
let symbol = FileSymbol {
119+
Ok(vec![NavigationTarget {
120+
file_id,
120121
name: decl_name.text(),
121-
node_range: decl_name.syntax().range(),
122+
range: decl_name.syntax().range(),
122123
kind: MODULE,
123-
};
124-
Ok(vec![NavigationTarget { file_id, symbol }])
124+
ptr: None,
125+
}])
125126
}
126127
/// Returns `Vec` for the same reason as `parent_module`
127128
pub(crate) fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
@@ -153,14 +154,13 @@ impl db::RootDatabase {
153154
let scope = fn_descr.scopes(self);
154155
// First try to resolve the symbol locally
155156
if let Some(entry) = scope.resolve_local_name(name_ref) {
156-
rr.add_resolution(
157-
position.file_id,
158-
FileSymbol {
159-
name: entry.name().to_string().into(),
160-
node_range: entry.ptr().range(),
161-
kind: NAME,
162-
},
163-
);
157+
rr.resolves_to.push(NavigationTarget {
158+
file_id: position.file_id,
159+
name: entry.name().to_string().into(),
160+
range: entry.ptr().range(),
161+
kind: NAME,
162+
ptr: None,
163+
});
164164
return Ok(Some(rr));
165165
};
166166
}
@@ -182,12 +182,14 @@ impl db::RootDatabase {
182182
Some(name) => name.to_string().into(),
183183
None => "".into(),
184184
};
185-
let symbol = FileSymbol {
185+
let symbol = NavigationTarget {
186+
file_id,
186187
name,
187-
node_range: TextRange::offset_len(0.into(), 0.into()),
188+
range: TextRange::offset_len(0.into(), 0.into()),
188189
kind: MODULE,
190+
ptr: None,
189191
};
190-
rr.add_resolution(file_id, symbol);
192+
rr.resolves_to.push(symbol);
191193
return Ok(Some(rr));
192194
}
193195
}
@@ -253,8 +255,7 @@ impl db::RootDatabase {
253255
}
254256
}
255257
pub(crate) fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> {
256-
let file = self.source_file(nav.file_id);
257-
let result = match (nav.symbol.description(&file), nav.symbol.docs(&file)) {
258+
let result = match (nav.description(self), nav.docs(self)) {
258259
(Some(desc), Some(docs)) => {
259260
Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs)
260261
}
@@ -362,52 +363,52 @@ impl db::RootDatabase {
362363
// Resolve the function's NameRef (NOTE: this isn't entirely accurate).
363364
let file_symbols = self.index_resolve(name_ref)?;
364365
for (fn_file_id, fs) in file_symbols {
365-
if fs.kind == FN_DEF {
366+
if fs.ptr.kind() == FN_DEF {
366367
let fn_file = self.source_file(fn_file_id);
367-
if let Some(fn_def) = find_node_at_offset(fn_file.syntax(), fs.node_range.start()) {
368-
let descr = ctry!(source_binder::function_from_source(
369-
self, fn_file_id, fn_def
370-
)?);
371-
if let Some(descriptor) = descr.signature_info(self) {
372-
// If we have a calling expression let's find which argument we are on
373-
let mut current_parameter = None;
374-
375-
let num_params = descriptor.params.len();
376-
let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some();
377-
378-
if num_params == 1 {
379-
if !has_self {
380-
current_parameter = Some(0);
381-
}
382-
} else if num_params > 1 {
383-
// Count how many parameters into the call we are.
384-
// TODO: This is best effort for now and should be fixed at some point.
385-
// It may be better to see where we are in the arg_list and then check
386-
// where offset is in that list (or beyond).
387-
// Revisit this after we get documentation comments in.
388-
if let Some(ref arg_list) = calling_node.arg_list() {
389-
let start = arg_list.syntax().range().start();
390-
391-
let range_search = TextRange::from_to(start, position.offset);
392-
let mut commas: usize = arg_list
393-
.syntax()
394-
.text()
395-
.slice(range_search)
396-
.to_string()
397-
.matches(',')
398-
.count();
399-
400-
// If we have a method call eat the first param since it's just self.
401-
if has_self {
402-
commas += 1;
403-
}
404-
405-
current_parameter = Some(commas);
406-
}
368+
let fn_def = fs.ptr.resolve(&fn_file);
369+
let fn_def = ast::FnDef::cast(fn_def.borrowed()).unwrap();
370+
let descr = ctry!(source_binder::function_from_source(
371+
self, fn_file_id, fn_def
372+
)?);
373+
if let Some(descriptor) = descr.signature_info(self) {
374+
// If we have a calling expression let's find which argument we are on
375+
let mut current_parameter = None;
376+
377+
let num_params = descriptor.params.len();
378+
let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some();
379+
380+
if num_params == 1 {
381+
if !has_self {
382+
current_parameter = Some(0);
407383
}
384+
} else if num_params > 1 {
385+
// Count how many parameters into the call we are.
386+
// TODO: This is best effort for now and should be fixed at some point.
387+
// It may be better to see where we are in the arg_list and then check
388+
// where offset is in that list (or beyond).
389+
// Revisit this after we get documentation comments in.
390+
if let Some(ref arg_list) = calling_node.arg_list() {
391+
let start = arg_list.syntax().range().start();
392+
393+
let range_search = TextRange::from_to(start, position.offset);
394+
let mut commas: usize = arg_list
395+
.syntax()
396+
.text()
397+
.slice(range_search)
398+
.to_string()
399+
.matches(',')
400+
.count();
401+
402+
// If we have a method call eat the first param since it's just self.
403+
if has_self {
404+
commas += 1;
405+
}
408406

409-
return Ok(Some((descriptor, current_parameter)));
407+
current_parameter = Some(commas);
408+
}
410409
}
410+
411+
return Ok(Some((descriptor, current_parameter)));
411412
}
412413
}
413414
}
@@ -511,3 +512,91 @@ impl<'a> FnCallNode<'a> {
511512
}
512513
}
513514
}
515+
516+
impl NavigationTarget {
517+
fn node(&self, db: &db::RootDatabase) -> Option<SyntaxNode> {
518+
let source_file = db.source_file(self.file_id);
519+
let source_file = source_file.syntax();
520+
let node = source_file
521+
.descendants()
522+
.find(|node| node.kind() == self.kind && node.range() == self.range)?
523+
.owned();
524+
Some(node)
525+
}
526+
527+
fn docs(&self, db: &db::RootDatabase) -> Option<String> {
528+
let node = self.node(db)?;
529+
let node = node.borrowed();
530+
fn doc_comments<'a, N: ast::DocCommentsOwner<'a>>(node: N) -> Option<String> {
531+
let comments = node.doc_comment_text();
532+
if comments.is_empty() {
533+
None
534+
} else {
535+
Some(comments)
536+
}
537+
}
538+
539+
visitor()
540+
.visit(doc_comments::<ast::FnDef>)
541+
.visit(doc_comments::<ast::StructDef>)
542+
.visit(doc_comments::<ast::EnumDef>)
543+
.visit(doc_comments::<ast::TraitDef>)
544+
.visit(doc_comments::<ast::Module>)
545+
.visit(doc_comments::<ast::TypeDef>)
546+
.visit(doc_comments::<ast::ConstDef>)
547+
.visit(doc_comments::<ast::StaticDef>)
548+
.accept(node)?
549+
}
550+
551+
/// Get a description of this node.
552+
///
553+
/// e.g. `struct Name`, `enum Name`, `fn Name`
554+
fn description(&self, db: &db::RootDatabase) -> Option<String> {
555+
// TODO: After type inference is done, add type information to improve the output
556+
let node = self.node(db)?;
557+
let node = node.borrowed();
558+
// TODO: Refactor to be have less repetition
559+
visitor()
560+
.visit(|node: ast::FnDef| {
561+
let mut string = "fn ".to_string();
562+
node.name()?.syntax().text().push_to(&mut string);
563+
Some(string)
564+
})
565+
.visit(|node: ast::StructDef| {
566+
let mut string = "struct ".to_string();
567+
node.name()?.syntax().text().push_to(&mut string);
568+
Some(string)
569+
})
570+
.visit(|node: ast::EnumDef| {
571+
let mut string = "enum ".to_string();
572+
node.name()?.syntax().text().push_to(&mut string);
573+
Some(string)
574+
})
575+
.visit(|node: ast::TraitDef| {
576+
let mut string = "trait ".to_string();
577+
node.name()?.syntax().text().push_to(&mut string);
578+
Some(string)
579+
})
580+
.visit(|node: ast::Module| {
581+
let mut string = "mod ".to_string();
582+
node.name()?.syntax().text().push_to(&mut string);
583+
Some(string)
584+
})
585+
.visit(|node: ast::TypeDef| {
586+
let mut string = "type ".to_string();
587+
node.name()?.syntax().text().push_to(&mut string);
588+
Some(string)
589+
})
590+
.visit(|node: ast::ConstDef| {
591+
let mut string = "const ".to_string();
592+
node.name()?.syntax().text().push_to(&mut string);
593+
Some(string)
594+
})
595+
.visit(|node: ast::StaticDef| {
596+
let mut string = "static ".to_string();
597+
node.name()?.syntax().text().push_to(&mut string);
598+
Some(string)
599+
})
600+
.accept(node)?
601+
}
602+
}

crates/ra_analysis/src/lib.rs

+32-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
//! ra_analyzer crate is the brain of Rust analyzer. It relies on the `salsa`
2-
//! crate, which provides and incremental on-demand database of facts.
3-
1+
//! ra_analyzer crate provides "ide-centric" APIs for the rust-analyzer. What
2+
//! powers this API are the `RootDatabase` struct, which defines a `salsa`
3+
//! database, and the `ra_hir` crate, where majority of the analysis happens.
4+
//! However, IDE specific bits of the analysis (most notably completion) happen
5+
//! in this crate.
46
macro_rules! ctry {
57
($expr:expr) => {
68
match $expr {
@@ -41,7 +43,7 @@ pub use ra_editor::{
4143
pub use hir::FnSignatureInfo;
4244

4345
pub use ra_db::{
44-
Canceled, Cancelable, FilePosition, FileRange,
46+
Canceled, Cancelable, FilePosition, FileRange, LocalSyntaxPtr,
4547
CrateGraph, CrateId, SourceRootId, FileId, SyntaxDatabase, FilesDatabase
4648
};
4749

@@ -219,24 +221,42 @@ impl Query {
219221
}
220222
}
221223

224+
/// `NavigationTarget` represents and element in the editor's UI whihc you can
225+
/// click on to navigate to a particular piece of code.
226+
///
227+
/// Typically, a `NavigationTarget` corresponds to some element in the source
228+
/// code, like a function or a struct, but this is not strictly required.
222229
#[derive(Debug)]
223230
pub struct NavigationTarget {
224231
file_id: FileId,
225-
symbol: FileSymbol,
232+
name: SmolStr,
233+
kind: SyntaxKind,
234+
range: TextRange,
235+
// Should be DefId ideally
236+
ptr: Option<LocalSyntaxPtr>,
226237
}
227238

228239
impl NavigationTarget {
229-
pub fn name(&self) -> SmolStr {
230-
self.symbol.name.clone()
240+
fn from_symbol(file_id: FileId, symbol: FileSymbol) -> NavigationTarget {
241+
NavigationTarget {
242+
name: symbol.name.clone(),
243+
kind: symbol.ptr.kind(),
244+
file_id,
245+
range: symbol.ptr.range(),
246+
ptr: Some(symbol.ptr.clone()),
247+
}
248+
}
249+
pub fn name(&self) -> &SmolStr {
250+
&self.name
231251
}
232252
pub fn kind(&self) -> SyntaxKind {
233-
self.symbol.kind
253+
self.kind
234254
}
235255
pub fn file_id(&self) -> FileId {
236256
self.file_id
237257
}
238258
pub fn range(&self) -> TextRange {
239-
self.symbol.node_range
259+
self.range
240260
}
241261
}
242262

@@ -260,7 +280,8 @@ impl ReferenceResolution {
260280
}
261281

262282
fn add_resolution(&mut self, file_id: FileId, symbol: FileSymbol) {
263-
self.resolves_to.push(NavigationTarget { file_id, symbol })
283+
self.resolves_to
284+
.push(NavigationTarget::from_symbol(file_id, symbol))
264285
}
265286
}
266287

@@ -359,7 +380,7 @@ impl Analysis {
359380
pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<NavigationTarget>> {
360381
let res = symbol_index::world_symbols(&*self.db, query)?
361382
.into_iter()
362-
.map(|(file_id, symbol)| NavigationTarget { file_id, symbol })
383+
.map(|(file_id, symbol)| NavigationTarget::from_symbol(file_id, symbol))
363384
.collect();
364385
Ok(res)
365386
}

0 commit comments

Comments
 (0)