mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 13:25:09 +00:00
fold doc_comment into hover
This commit is contained in:
parent
3ad0037f90
commit
9f44d4c56d
3 changed files with 110 additions and 109 deletions
|
@ -1,7 +1,11 @@
|
||||||
use ra_db::{Cancelable, SyntaxDatabase};
|
use ra_db::{Cancelable, SyntaxDatabase};
|
||||||
use ra_syntax::{ast, AstNode};
|
use ra_syntax::{
|
||||||
|
AstNode, SyntaxNode,
|
||||||
|
ast::{self, NameOwner},
|
||||||
|
algo::visit::{visitor, Visitor},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange};
|
use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget};
|
||||||
|
|
||||||
pub(crate) fn hover(
|
pub(crate) fn hover(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
|
@ -10,7 +14,7 @@ pub(crate) fn hover(
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
let range = if let Some(rr) = db.approximately_resolve_symbol(position)? {
|
let range = if let Some(rr) = db.approximately_resolve_symbol(position)? {
|
||||||
for nav in rr.resolves_to {
|
for nav in rr.resolves_to {
|
||||||
res.extend(db.doc_text_for(nav)?)
|
res.extend(doc_text_for(db, nav)?)
|
||||||
}
|
}
|
||||||
rr.reference_range
|
rr.reference_range
|
||||||
} else {
|
} else {
|
||||||
|
@ -33,6 +37,107 @@ pub(crate) fn hover(
|
||||||
Ok(Some(res))
|
Ok(Some(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: this should not really use navigation target. Rather, approximatelly
|
||||||
|
// resovled symbol should return a `DefId`.
|
||||||
|
fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Cancelable<Option<String>> {
|
||||||
|
let result = match (nav.description(db), nav.docs(db)) {
|
||||||
|
(Some(desc), Some(docs)) => Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs),
|
||||||
|
(Some(desc), None) => Some("```rust\n".to_string() + &*desc + "\n```"),
|
||||||
|
(None, Some(docs)) => Some(docs),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NavigationTarget {
|
||||||
|
fn node(&self, db: &RootDatabase) -> Option<SyntaxNode> {
|
||||||
|
let source_file = db.source_file(self.file_id);
|
||||||
|
let source_file = source_file.syntax();
|
||||||
|
let node = source_file
|
||||||
|
.descendants()
|
||||||
|
.find(|node| node.kind() == self.kind && node.range() == self.range)?
|
||||||
|
.owned();
|
||||||
|
Some(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn docs(&self, db: &RootDatabase) -> Option<String> {
|
||||||
|
let node = self.node(db)?;
|
||||||
|
let node = node.borrowed();
|
||||||
|
fn doc_comments<'a, N: ast::DocCommentsOwner<'a>>(node: N) -> Option<String> {
|
||||||
|
let comments = node.doc_comment_text();
|
||||||
|
if comments.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(comments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visitor()
|
||||||
|
.visit(doc_comments::<ast::FnDef>)
|
||||||
|
.visit(doc_comments::<ast::StructDef>)
|
||||||
|
.visit(doc_comments::<ast::EnumDef>)
|
||||||
|
.visit(doc_comments::<ast::TraitDef>)
|
||||||
|
.visit(doc_comments::<ast::Module>)
|
||||||
|
.visit(doc_comments::<ast::TypeDef>)
|
||||||
|
.visit(doc_comments::<ast::ConstDef>)
|
||||||
|
.visit(doc_comments::<ast::StaticDef>)
|
||||||
|
.accept(node)?
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a description of this node.
|
||||||
|
///
|
||||||
|
/// e.g. `struct Name`, `enum Name`, `fn Name`
|
||||||
|
fn description(&self, db: &RootDatabase) -> Option<String> {
|
||||||
|
// TODO: After type inference is done, add type information to improve the output
|
||||||
|
let node = self.node(db)?;
|
||||||
|
let node = node.borrowed();
|
||||||
|
// TODO: Refactor to be have less repetition
|
||||||
|
visitor()
|
||||||
|
.visit(|node: ast::FnDef| {
|
||||||
|
let mut string = "fn ".to_string();
|
||||||
|
node.name()?.syntax().text().push_to(&mut string);
|
||||||
|
Some(string)
|
||||||
|
})
|
||||||
|
.visit(|node: ast::StructDef| {
|
||||||
|
let mut string = "struct ".to_string();
|
||||||
|
node.name()?.syntax().text().push_to(&mut string);
|
||||||
|
Some(string)
|
||||||
|
})
|
||||||
|
.visit(|node: ast::EnumDef| {
|
||||||
|
let mut string = "enum ".to_string();
|
||||||
|
node.name()?.syntax().text().push_to(&mut string);
|
||||||
|
Some(string)
|
||||||
|
})
|
||||||
|
.visit(|node: ast::TraitDef| {
|
||||||
|
let mut string = "trait ".to_string();
|
||||||
|
node.name()?.syntax().text().push_to(&mut string);
|
||||||
|
Some(string)
|
||||||
|
})
|
||||||
|
.visit(|node: ast::Module| {
|
||||||
|
let mut string = "mod ".to_string();
|
||||||
|
node.name()?.syntax().text().push_to(&mut string);
|
||||||
|
Some(string)
|
||||||
|
})
|
||||||
|
.visit(|node: ast::TypeDef| {
|
||||||
|
let mut string = "type ".to_string();
|
||||||
|
node.name()?.syntax().text().push_to(&mut string);
|
||||||
|
Some(string)
|
||||||
|
})
|
||||||
|
.visit(|node: ast::ConstDef| {
|
||||||
|
let mut string = "const ".to_string();
|
||||||
|
node.name()?.syntax().text().push_to(&mut string);
|
||||||
|
Some(string)
|
||||||
|
})
|
||||||
|
.visit(|node: ast::StaticDef| {
|
||||||
|
let mut string = "static ".to_string();
|
||||||
|
node.name()?.syntax().text().push_to(&mut string);
|
||||||
|
Some(string)
|
||||||
|
})
|
||||||
|
.accept(node)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ra_syntax::TextRange;
|
use ra_syntax::TextRange;
|
||||||
|
|
|
@ -8,11 +8,11 @@ use hir::{
|
||||||
use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase};
|
use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase};
|
||||||
use ra_editor::{self, find_node_at_offset, assists, LocalEdit, Severity};
|
use ra_editor::{self, find_node_at_offset, assists, LocalEdit, Severity};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
algo::{find_covering_node, visit::{visitor, Visitor}},
|
algo::find_covering_node,
|
||||||
ast::{self, ArgListOwner, Expr, FnDef, NameOwner},
|
ast::{self, ArgListOwner, Expr, FnDef, NameOwner},
|
||||||
AstNode, SourceFileNode,
|
AstNode, SourceFileNode,
|
||||||
SyntaxKind::*,
|
SyntaxKind::*,
|
||||||
SyntaxNode, SyntaxNodeRef, TextRange, TextUnit,
|
SyntaxNodeRef, TextRange, TextUnit,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -256,18 +256,6 @@ impl db::RootDatabase {
|
||||||
Ok(Some((binding, descr)))
|
Ok(Some((binding, descr)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub(crate) fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> {
|
|
||||||
let result = match (nav.description(self), nav.docs(self)) {
|
|
||||||
(Some(desc), Some(docs)) => {
|
|
||||||
Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs)
|
|
||||||
}
|
|
||||||
(Some(desc), None) => Some("```rust\n".to_string() + &*desc + "\n```"),
|
|
||||||
(None, Some(docs)) => Some(docs),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
|
pub(crate) fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
|
||||||
let syntax = self.source_file(file_id);
|
let syntax = self.source_file(file_id);
|
||||||
|
@ -506,91 +494,3 @@ impl<'a> FnCallNode<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavigationTarget {
|
|
||||||
fn node(&self, db: &db::RootDatabase) -> Option<SyntaxNode> {
|
|
||||||
let source_file = db.source_file(self.file_id);
|
|
||||||
let source_file = source_file.syntax();
|
|
||||||
let node = source_file
|
|
||||||
.descendants()
|
|
||||||
.find(|node| node.kind() == self.kind && node.range() == self.range)?
|
|
||||||
.owned();
|
|
||||||
Some(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn docs(&self, db: &db::RootDatabase) -> Option<String> {
|
|
||||||
let node = self.node(db)?;
|
|
||||||
let node = node.borrowed();
|
|
||||||
fn doc_comments<'a, N: ast::DocCommentsOwner<'a>>(node: N) -> Option<String> {
|
|
||||||
let comments = node.doc_comment_text();
|
|
||||||
if comments.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(comments)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visitor()
|
|
||||||
.visit(doc_comments::<ast::FnDef>)
|
|
||||||
.visit(doc_comments::<ast::StructDef>)
|
|
||||||
.visit(doc_comments::<ast::EnumDef>)
|
|
||||||
.visit(doc_comments::<ast::TraitDef>)
|
|
||||||
.visit(doc_comments::<ast::Module>)
|
|
||||||
.visit(doc_comments::<ast::TypeDef>)
|
|
||||||
.visit(doc_comments::<ast::ConstDef>)
|
|
||||||
.visit(doc_comments::<ast::StaticDef>)
|
|
||||||
.accept(node)?
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a description of this node.
|
|
||||||
///
|
|
||||||
/// e.g. `struct Name`, `enum Name`, `fn Name`
|
|
||||||
fn description(&self, db: &db::RootDatabase) -> Option<String> {
|
|
||||||
// TODO: After type inference is done, add type information to improve the output
|
|
||||||
let node = self.node(db)?;
|
|
||||||
let node = node.borrowed();
|
|
||||||
// TODO: Refactor to be have less repetition
|
|
||||||
visitor()
|
|
||||||
.visit(|node: ast::FnDef| {
|
|
||||||
let mut string = "fn ".to_string();
|
|
||||||
node.name()?.syntax().text().push_to(&mut string);
|
|
||||||
Some(string)
|
|
||||||
})
|
|
||||||
.visit(|node: ast::StructDef| {
|
|
||||||
let mut string = "struct ".to_string();
|
|
||||||
node.name()?.syntax().text().push_to(&mut string);
|
|
||||||
Some(string)
|
|
||||||
})
|
|
||||||
.visit(|node: ast::EnumDef| {
|
|
||||||
let mut string = "enum ".to_string();
|
|
||||||
node.name()?.syntax().text().push_to(&mut string);
|
|
||||||
Some(string)
|
|
||||||
})
|
|
||||||
.visit(|node: ast::TraitDef| {
|
|
||||||
let mut string = "trait ".to_string();
|
|
||||||
node.name()?.syntax().text().push_to(&mut string);
|
|
||||||
Some(string)
|
|
||||||
})
|
|
||||||
.visit(|node: ast::Module| {
|
|
||||||
let mut string = "mod ".to_string();
|
|
||||||
node.name()?.syntax().text().push_to(&mut string);
|
|
||||||
Some(string)
|
|
||||||
})
|
|
||||||
.visit(|node: ast::TypeDef| {
|
|
||||||
let mut string = "type ".to_string();
|
|
||||||
node.name()?.syntax().text().push_to(&mut string);
|
|
||||||
Some(string)
|
|
||||||
})
|
|
||||||
.visit(|node: ast::ConstDef| {
|
|
||||||
let mut string = "const ".to_string();
|
|
||||||
node.name()?.syntax().text().push_to(&mut string);
|
|
||||||
Some(string)
|
|
||||||
})
|
|
||||||
.visit(|node: ast::StaticDef| {
|
|
||||||
let mut string = "static ".to_string();
|
|
||||||
node.name()?.syntax().text().push_to(&mut string);
|
|
||||||
Some(string)
|
|
||||||
})
|
|
||||||
.accept(node)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -403,10 +403,6 @@ impl Analysis {
|
||||||
pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
|
pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
|
||||||
self.db.find_all_refs(position)
|
self.db.find_all_refs(position)
|
||||||
}
|
}
|
||||||
/// Returns documentation string for a given target.
|
|
||||||
pub fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> {
|
|
||||||
self.db.doc_text_for(nav)
|
|
||||||
}
|
|
||||||
/// Returns a short text descrbing element at position.
|
/// Returns a short text descrbing element at position.
|
||||||
pub fn hover(&self, position: FilePosition) -> Cancelable<Option<RangeInfo<String>>> {
|
pub fn hover(&self, position: FilePosition) -> Cancelable<Option<RangeInfo<String>>> {
|
||||||
hover::hover(&*self.db, position)
|
hover::hover(&*self.db, position)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue