From b28c54a2c239acd73f2eea80fda9ee3960d2c046 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 13 Aug 2020 16:28:27 +0200 Subject: Rename ra_hir_def -> hir_def --- crates/hir_def/src/docs.rs | 121 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 crates/hir_def/src/docs.rs (limited to 'crates/hir_def/src/docs.rs') diff --git a/crates/hir_def/src/docs.rs b/crates/hir_def/src/docs.rs new file mode 100644 index 000000000..e9a02b11b --- /dev/null +++ b/crates/hir_def/src/docs.rs @@ -0,0 +1,121 @@ +//! Defines hir documentation. +//! +//! This really shouldn't exist, instead, we should deshugar doc comments into attributes, see +//! https://github.com/rust-analyzer/rust-analyzer/issues/2148#issuecomment-550519102 + +use std::sync::Arc; + +use either::Either; +use syntax::ast; + +use crate::{ + db::DefDatabase, + src::{HasChildSource, HasSource}, + AdtId, AttrDefId, Lookup, +}; + +/// Holds documentation +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Documentation(Arc); + +impl Into for Documentation { + fn into(self) -> String { + self.as_str().to_owned() + } +} + +impl Documentation { + fn new(s: &str) -> Documentation { + Documentation(s.into()) + } + + pub fn from_ast(node: &N) -> Option + where + N: ast::DocCommentsOwner + ast::AttrsOwner, + { + docs_from_ast(node) + } + + pub fn as_str(&self) -> &str { + &*self.0 + } + + pub(crate) fn documentation_query( + db: &dyn DefDatabase, + def: AttrDefId, + ) -> Option { + match def { + AttrDefId::ModuleId(module) => { + let def_map = db.crate_def_map(module.krate); + let src = def_map[module.local_id].declaration_source(db)?; + docs_from_ast(&src.value) + } + AttrDefId::FieldId(it) => { + let src = it.parent.child_source(db); + match &src.value[it.local_id] { + Either::Left(_tuple) => None, + Either::Right(record) => docs_from_ast(record), + } + } + AttrDefId::AdtId(it) => match it { + AdtId::StructId(it) => docs_from_ast(&it.lookup(db).source(db).value), + AdtId::EnumId(it) => docs_from_ast(&it.lookup(db).source(db).value), + AdtId::UnionId(it) => docs_from_ast(&it.lookup(db).source(db).value), + }, + AttrDefId::EnumVariantId(it) => { + let src = it.parent.child_source(db); + docs_from_ast(&src.value[it.local_id]) + } + AttrDefId::TraitId(it) => docs_from_ast(&it.lookup(db).source(db).value), + AttrDefId::MacroDefId(it) => docs_from_ast(&it.ast_id?.to_node(db.upcast())), + AttrDefId::ConstId(it) => docs_from_ast(&it.lookup(db).source(db).value), + AttrDefId::StaticId(it) => docs_from_ast(&it.lookup(db).source(db).value), + AttrDefId::FunctionId(it) => docs_from_ast(&it.lookup(db).source(db).value), + AttrDefId::TypeAliasId(it) => docs_from_ast(&it.lookup(db).source(db).value), + AttrDefId::ImplId(_) => None, + } + } +} + +pub(crate) fn docs_from_ast(node: &N) -> Option +where + N: ast::DocCommentsOwner + ast::AttrsOwner, +{ + let doc_comment_text = node.doc_comment_text(); + let doc_attr_text = expand_doc_attrs(node); + let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); + docs.map(|it| Documentation::new(&it)) +} + +fn merge_doc_comments_and_attrs( + doc_comment_text: Option, + doc_attr_text: Option, +) -> Option { + match (doc_comment_text, doc_attr_text) { + (Some(mut comment_text), Some(attr_text)) => { + comment_text.push_str("\n\n"); + comment_text.push_str(&attr_text); + Some(comment_text) + } + (Some(comment_text), None) => Some(comment_text), + (None, Some(attr_text)) => Some(attr_text), + (None, None) => None, + } +} + +fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option { + let mut docs = String::new(); + for attr in owner.attrs() { + if let Some(("doc", value)) = + attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str())) + { + docs.push_str(value); + docs.push_str("\n\n"); + } + } + if docs.is_empty() { + None + } else { + Some(docs.trim_end_matches("\n\n").to_owned()) + } +} -- cgit v1.2.3