Merge #621
621: Completion docs for code model r=kjeremy a=kjeremy Adds a way to access documentation through the code model and exposes it to completions. Also allows us to document enum variants. Co-authored-by: Jeremy A. Kolb <jkolb@ara.com> Co-authored-by: Jeremy Kolb <kjeremy@gmail.com>
This commit is contained in:
commit
6a0a4a564a
9 changed files with 138 additions and 43 deletions
|
@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||
|
||||
use relative_path::RelativePathBuf;
|
||||
use ra_db::{CrateId, FileId};
|
||||
use ra_syntax::{ast::{self, AstNode, DocCommentsOwner}, TreeArc, SyntaxNode};
|
||||
use ra_syntax::{ast::self, TreeArc, SyntaxNode};
|
||||
|
||||
use crate::{
|
||||
Name, DefId, Path, PerNs, ScopesWithSyntaxMapping, Ty, HirFileId,
|
||||
|
@ -14,6 +14,7 @@ use crate::{
|
|||
adt::VariantData,
|
||||
generics::GenericParams,
|
||||
code_model_impl::def_id_to_ast,
|
||||
docs::{Documentation, Docs, docs_from_ast}
|
||||
};
|
||||
|
||||
/// hir::Crate describes a single crate. It's the main interface with which
|
||||
|
@ -208,6 +209,12 @@ impl Struct {
|
|||
}
|
||||
}
|
||||
|
||||
impl Docs for Struct {
|
||||
fn docs(&self, db: &impl HirDatabase) -> Option<Documentation> {
|
||||
docs_from_ast(&*self.source(db).1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Enum {
|
||||
pub(crate) def_id: DefId,
|
||||
|
@ -239,6 +246,12 @@ impl Enum {
|
|||
}
|
||||
}
|
||||
|
||||
impl Docs for Enum {
|
||||
fn docs(&self, db: &impl HirDatabase) -> Option<Documentation> {
|
||||
docs_from_ast(&*self.source(db).1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct EnumVariant {
|
||||
pub(crate) def_id: DefId,
|
||||
|
@ -281,6 +294,12 @@ impl EnumVariant {
|
|||
}
|
||||
}
|
||||
|
||||
impl Docs for EnumVariant {
|
||||
fn docs(&self, db: &impl HirDatabase) -> Option<Documentation> {
|
||||
docs_from_ast(&*self.source(db).1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Function {
|
||||
pub(crate) def_id: DefId,
|
||||
|
@ -352,19 +371,11 @@ impl Function {
|
|||
pub fn generic_params(&self, db: &impl HirDatabase) -> Arc<GenericParams> {
|
||||
db.generic_params(self.def_id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn docs(&self, db: &impl HirDatabase) -> Option<String> {
|
||||
let def_loc = self.def_id.loc(db);
|
||||
let syntax = db.file_item(def_loc.source_item_id);
|
||||
let fn_def = ast::FnDef::cast(&syntax).expect("fn def should point to FnDef node");
|
||||
|
||||
// doc_comment_text unconditionally returns a String
|
||||
let comments = fn_def.doc_comment_text();
|
||||
if comments.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(comments)
|
||||
}
|
||||
impl Docs for Function {
|
||||
fn docs(&self, db: &impl HirDatabase) -> Option<Documentation> {
|
||||
docs_from_ast(&*self.source(db).1)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -383,6 +394,12 @@ impl Const {
|
|||
}
|
||||
}
|
||||
|
||||
impl Docs for Const {
|
||||
fn docs(&self, db: &impl HirDatabase) -> Option<Documentation> {
|
||||
docs_from_ast(&*self.source(db).1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Static {
|
||||
pub(crate) def_id: DefId,
|
||||
|
@ -398,6 +415,12 @@ impl Static {
|
|||
}
|
||||
}
|
||||
|
||||
impl Docs for Static {
|
||||
fn docs(&self, db: &impl HirDatabase) -> Option<Documentation> {
|
||||
docs_from_ast(&*self.source(db).1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Trait {
|
||||
pub(crate) def_id: DefId,
|
||||
|
@ -417,6 +440,12 @@ impl Trait {
|
|||
}
|
||||
}
|
||||
|
||||
impl Docs for Trait {
|
||||
fn docs(&self, db: &impl HirDatabase) -> Option<Documentation> {
|
||||
docs_from_ast(&*self.source(db).1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Type {
|
||||
pub(crate) def_id: DefId,
|
||||
|
@ -435,3 +464,9 @@ impl Type {
|
|||
db.generic_params(self.def_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl Docs for Type {
|
||||
fn docs(&self, db: &impl HirDatabase) -> Option<Documentation> {
|
||||
docs_from_ast(&*self.source(db).1)
|
||||
}
|
||||
}
|
||||
|
|
36
crates/ra_hir/src/docs.rs
Normal file
36
crates/ra_hir/src/docs.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use ra_syntax::ast;
|
||||
|
||||
use crate::HirDatabase;
|
||||
|
||||
/// Holds documentation
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Documentation(String);
|
||||
|
||||
impl Documentation {
|
||||
pub fn new(s: &str) -> Self {
|
||||
Self(s.into())
|
||||
}
|
||||
|
||||
pub fn contents(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<String> for Documentation {
|
||||
fn into(self) -> String {
|
||||
self.contents().into()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Docs {
|
||||
fn docs(&self, db: &impl HirDatabase) -> Option<Documentation>;
|
||||
}
|
||||
|
||||
pub(crate) fn docs_from_ast(node: &impl ast::DocCommentsOwner) -> Option<Documentation> {
|
||||
let comments = node.doc_comment_text();
|
||||
if comments.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Documentation::new(&comments))
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ mod ty;
|
|||
mod impl_block;
|
||||
mod expr;
|
||||
mod generics;
|
||||
mod docs;
|
||||
|
||||
mod code_model_api;
|
||||
mod code_model_impl;
|
||||
|
@ -45,6 +46,7 @@ pub use self::{
|
|||
ty::Ty,
|
||||
impl_block::{ImplBlock, ImplItem},
|
||||
code_model_impl::function::{FnScopes, ScopesWithSyntaxMapping},
|
||||
docs::{Docs, Documentation}
|
||||
};
|
||||
|
||||
pub use self::code_model_api::{
|
||||
|
|
|
@ -2,6 +2,8 @@ use crate::{
|
|||
completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext},
|
||||
};
|
||||
|
||||
use hir::Docs;
|
||||
|
||||
pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
let (path, module) = match (&ctx.path_prefix, &ctx.module) {
|
||||
(Some(path), Some(module)) => (path.clone(), module),
|
||||
|
@ -27,13 +29,14 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
|
|||
hir::Def::Enum(e) => {
|
||||
e.variants(ctx.db)
|
||||
.into_iter()
|
||||
.for_each(|(variant_name, _variant)| {
|
||||
.for_each(|(variant_name, variant)| {
|
||||
CompletionItem::new(
|
||||
CompletionKind::Reference,
|
||||
ctx.source_range(),
|
||||
variant_name.to_string(),
|
||||
)
|
||||
.kind(CompletionItemKind::EnumVariant)
|
||||
.set_documentation(variant.docs(ctx.db))
|
||||
.add_to(acc)
|
||||
});
|
||||
}
|
||||
|
@ -116,7 +119,13 @@ mod tests {
|
|||
"reference_completion",
|
||||
"
|
||||
//- /lib.rs
|
||||
enum E { Foo, Bar(i32) }
|
||||
/// An enum
|
||||
enum E {
|
||||
/// Foo Variant
|
||||
Foo,
|
||||
/// Bar Variant with i32
|
||||
Bar(i32)
|
||||
}
|
||||
fn foo() { let _ = E::<|> }
|
||||
",
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use hir::PerNs;
|
||||
use hir::{Docs, Documentation, PerNs};
|
||||
|
||||
use crate::completion::completion_context::CompletionContext;
|
||||
use ra_syntax::{
|
||||
|
@ -19,7 +19,7 @@ pub struct CompletionItem {
|
|||
label: String,
|
||||
kind: Option<CompletionItemKind>,
|
||||
detail: Option<String>,
|
||||
documentation: Option<String>,
|
||||
documentation: Option<Documentation>,
|
||||
lookup: Option<String>,
|
||||
insert_text: Option<String>,
|
||||
insert_text_format: InsertTextFormat,
|
||||
|
@ -98,7 +98,7 @@ impl CompletionItem {
|
|||
}
|
||||
/// A doc-comment
|
||||
pub fn documentation(&self) -> Option<&str> {
|
||||
self.documentation.as_ref().map(|it| it.as_str())
|
||||
self.documentation.as_ref().map(|it| it.contents())
|
||||
}
|
||||
/// What string is used for filtering.
|
||||
pub fn lookup(&self) -> &str {
|
||||
|
@ -137,7 +137,7 @@ pub(crate) struct Builder {
|
|||
insert_text: Option<String>,
|
||||
insert_text_format: InsertTextFormat,
|
||||
detail: Option<String>,
|
||||
documentation: Option<String>,
|
||||
documentation: Option<Documentation>,
|
||||
lookup: Option<String>,
|
||||
kind: Option<CompletionItemKind>,
|
||||
text_edit: Option<TextEdit>,
|
||||
|
@ -197,10 +197,10 @@ impl Builder {
|
|||
self
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub(crate) fn documentation(self, docs: impl Into<String>) -> Builder {
|
||||
pub(crate) fn documentation(self, docs: Documentation) -> Builder {
|
||||
self.set_documentation(Some(docs))
|
||||
}
|
||||
pub(crate) fn set_documentation(mut self, docs: Option<impl Into<String>>) -> Builder {
|
||||
pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder {
|
||||
self.documentation = docs.map(Into::into);
|
||||
self
|
||||
}
|
||||
|
@ -210,35 +210,35 @@ impl Builder {
|
|||
resolution: &hir::Resolution,
|
||||
) -> Builder {
|
||||
let resolved = resolution.def_id.map(|d| d.resolve(ctx.db));
|
||||
let kind = match resolved {
|
||||
let (kind, docs) = match resolved {
|
||||
PerNs {
|
||||
types: Some(hir::Def::Module(..)),
|
||||
..
|
||||
} => CompletionItemKind::Module,
|
||||
} => (CompletionItemKind::Module, None),
|
||||
PerNs {
|
||||
types: Some(hir::Def::Struct(..)),
|
||||
types: Some(hir::Def::Struct(s)),
|
||||
..
|
||||
} => CompletionItemKind::Struct,
|
||||
} => (CompletionItemKind::Struct, s.docs(ctx.db)),
|
||||
PerNs {
|
||||
types: Some(hir::Def::Enum(..)),
|
||||
types: Some(hir::Def::Enum(e)),
|
||||
..
|
||||
} => CompletionItemKind::Enum,
|
||||
} => (CompletionItemKind::Enum, e.docs(ctx.db)),
|
||||
PerNs {
|
||||
types: Some(hir::Def::Trait(..)),
|
||||
types: Some(hir::Def::Trait(t)),
|
||||
..
|
||||
} => CompletionItemKind::Trait,
|
||||
} => (CompletionItemKind::Trait, t.docs(ctx.db)),
|
||||
PerNs {
|
||||
types: Some(hir::Def::Type(..)),
|
||||
types: Some(hir::Def::Type(t)),
|
||||
..
|
||||
} => CompletionItemKind::TypeAlias,
|
||||
} => (CompletionItemKind::TypeAlias, t.docs(ctx.db)),
|
||||
PerNs {
|
||||
values: Some(hir::Def::Const(..)),
|
||||
values: Some(hir::Def::Const(c)),
|
||||
..
|
||||
} => CompletionItemKind::Const,
|
||||
} => (CompletionItemKind::Const, c.docs(ctx.db)),
|
||||
PerNs {
|
||||
values: Some(hir::Def::Static(..)),
|
||||
values: Some(hir::Def::Static(s)),
|
||||
..
|
||||
} => CompletionItemKind::Static,
|
||||
} => (CompletionItemKind::Static, s.docs(ctx.db)),
|
||||
PerNs {
|
||||
values: Some(hir::Def::Function(function)),
|
||||
..
|
||||
|
@ -246,6 +246,8 @@ impl Builder {
|
|||
_ => return self,
|
||||
};
|
||||
self.kind = Some(kind);
|
||||
self.documentation = docs;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -265,6 +267,7 @@ impl Builder {
|
|||
}
|
||||
self.insert_text_format = InsertTextFormat::Snippet;
|
||||
}
|
||||
|
||||
if let Some(docs) = function.docs(ctx.db) {
|
||||
self.documentation = Some(docs);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
created: "2019-01-22T14:45:00.717917+00:00"
|
||||
creator: insta@0.4.0
|
||||
created: "2019-01-23T23:49:43.278245900+00:00"
|
||||
creator: insta@0.5.1
|
||||
expression: kind_completions
|
||||
source: "crates\\ra_ide_api\\src\\completion\\completion_item.rs"
|
||||
---
|
||||
|
@ -12,11 +12,15 @@ source: "crates\\ra_ide_api\\src\\completion\\completion_item.rs"
|
|||
EnumVariant
|
||||
),
|
||||
detail: None,
|
||||
documentation: None,
|
||||
documentation: Some(
|
||||
Documentation(
|
||||
"Foo Variant"
|
||||
)
|
||||
),
|
||||
lookup: None,
|
||||
insert_text: None,
|
||||
insert_text_format: PlainText,
|
||||
source_range: [47; 47),
|
||||
source_range: [116; 116),
|
||||
text_edit: None
|
||||
},
|
||||
CompletionItem {
|
||||
|
@ -26,11 +30,15 @@ source: "crates\\ra_ide_api\\src\\completion\\completion_item.rs"
|
|||
EnumVariant
|
||||
),
|
||||
detail: None,
|
||||
documentation: None,
|
||||
documentation: Some(
|
||||
Documentation(
|
||||
"Bar Variant with i32"
|
||||
)
|
||||
),
|
||||
lookup: None,
|
||||
insert_text: None,
|
||||
insert_text_format: PlainText,
|
||||
source_range: [47; 47),
|
||||
source_range: [116; 116),
|
||||
text_edit: None
|
||||
}
|
||||
]
|
||||
|
|
|
@ -632,6 +632,7 @@ impl AstNode for EnumVariant {
|
|||
|
||||
|
||||
impl ast::NameOwner for EnumVariant {}
|
||||
impl ast::DocCommentsOwner for EnumVariant {}
|
||||
impl EnumVariant {
|
||||
pub fn expr(&self) -> Option<&Expr> {
|
||||
super::child_opt(self)
|
||||
|
|
|
@ -279,7 +279,7 @@ Grammar(
|
|||
"DocCommentsOwner"
|
||||
], options: [["variant_list", "EnumVariantList"]] ),
|
||||
"EnumVariantList": ( collections: [["variants", "EnumVariant"]] ),
|
||||
"EnumVariant": ( traits: ["NameOwner"], options: ["Expr"] ),
|
||||
"EnumVariant": ( traits: ["NameOwner", "DocCommentsOwner"], options: ["Expr"] ),
|
||||
"TraitDef": ( traits: ["VisibilityOwner", "NameOwner", "AttrsOwner", "DocCommentsOwner"] ),
|
||||
"Module": (
|
||||
traits: ["VisibilityOwner", "NameOwner", "AttrsOwner", "DocCommentsOwner" ],
|
||||
|
|
|
@ -249,7 +249,8 @@ fn n_attached_trivias<'a>(
|
|||
trivias: impl Iterator<Item = (SyntaxKind, &'a str)>,
|
||||
) -> usize {
|
||||
match kind {
|
||||
CONST_DEF | TYPE_DEF | STRUCT_DEF | ENUM_DEF | FN_DEF | TRAIT_DEF | MODULE => {
|
||||
CONST_DEF | TYPE_DEF | STRUCT_DEF | ENUM_DEF | ENUM_VARIANT | FN_DEF | TRAIT_DEF
|
||||
| MODULE => {
|
||||
let mut res = 0;
|
||||
for (i, (kind, text)) in trivias.enumerate() {
|
||||
match kind {
|
||||
|
|
Loading…
Reference in a new issue