This commit is contained in:
Lukas Wirth 2023-09-02 17:12:57 +02:00
parent b1575528c0
commit 8eddc64f86
5 changed files with 158 additions and 157 deletions

View file

@ -6,33 +6,22 @@ use hir_def::{
path::{ModPath, Path}, path::{ModPath, Path},
per_ns::Namespace, per_ns::Namespace,
resolver::{HasResolver, Resolver, TypeNs}, resolver::{HasResolver, Resolver, TypeNs},
AssocItemId, AttrDefId, GenericParamId, ModuleDefId, AssocItemId, AttrDefId, ModuleDefId,
}; };
use hir_expand::{hygiene::Hygiene, name::Name}; use hir_expand::{hygiene::Hygiene, name::Name};
use hir_ty::db::HirDatabase; use hir_ty::db::HirDatabase;
use syntax::{ast, AstNode}; use syntax::{ast, AstNode};
use crate::{ use crate::{
Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, Enum, ExternCrateDecl, Field, Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl,
Function, GenericParam, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, Struct, Trait, Field, Function, GenericParam, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, Struct,
TraitAlias, TypeAlias, TypeParam, Union, Variant, VariantDef, Trait, TraitAlias, TypeAlias, TypeParam, Union, Variant, VariantDef,
}; };
pub trait HasAttrs { pub trait HasAttrs {
fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner; fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner;
fn resolve_doc_path( #[doc(hidden)]
self, fn attr_id(self) -> AttrDefId;
db: &dyn HirDatabase,
link: &str,
ns: Option<Namespace>,
) -> Option<DocLinkDef>;
}
/// Subset of `ide_db::Definition` that doc links can resolve to.
pub enum DocLinkDef {
ModuleDef(ModuleDef),
Field(Field),
SelfType(Trait),
} }
macro_rules! impl_has_attrs { macro_rules! impl_has_attrs {
@ -42,14 +31,8 @@ macro_rules! impl_has_attrs {
let def = AttrDefId::$def_id(self.into()); let def = AttrDefId::$def_id(self.into());
db.attrs_with_owner(def) db.attrs_with_owner(def)
} }
fn resolve_doc_path( fn attr_id(self) -> AttrDefId {
self, AttrDefId::$def_id(self.into())
db: &dyn HirDatabase,
link: &str,
ns: Option<Namespace>
) -> Option<DocLinkDef> {
let def = AttrDefId::$def_id(self.into());
resolve_doc_path(db, def, link, ns)
} }
} }
)*}; )*};
@ -69,6 +52,7 @@ impl_has_attrs![
(Module, ModuleId), (Module, ModuleId),
(GenericParam, GenericParamId), (GenericParam, GenericParamId),
(Impl, ImplId), (Impl, ImplId),
(ExternCrateDecl, ExternCrateId),
]; ];
macro_rules! impl_has_attrs_enum { macro_rules! impl_has_attrs_enum {
@ -77,13 +61,8 @@ macro_rules! impl_has_attrs_enum {
fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner { fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
$enum::$variant(self).attrs(db) $enum::$variant(self).attrs(db)
} }
fn resolve_doc_path( fn attr_id(self) -> AttrDefId {
self, $enum::$variant(self).attr_id()
db: &dyn HirDatabase,
link: &str,
ns: Option<Namespace>
) -> Option<DocLinkDef> {
$enum::$variant(self).resolve_doc_path(db, link, ns)
} }
} }
)*}; )*};
@ -100,45 +79,35 @@ impl HasAttrs for AssocItem {
AssocItem::TypeAlias(it) => it.attrs(db), AssocItem::TypeAlias(it) => it.attrs(db),
} }
} }
fn attr_id(self) -> AttrDefId {
fn resolve_doc_path(
self,
db: &dyn HirDatabase,
link: &str,
ns: Option<Namespace>,
) -> Option<DocLinkDef> {
match self { match self {
AssocItem::Function(it) => it.resolve_doc_path(db, link, ns), AssocItem::Function(it) => it.attr_id(),
AssocItem::Const(it) => it.resolve_doc_path(db, link, ns), AssocItem::Const(it) => it.attr_id(),
AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns), AssocItem::TypeAlias(it) => it.attr_id(),
} }
} }
} }
impl HasAttrs for ExternCrateDecl {
fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
let def = AttrDefId::ExternCrateId(self.into());
db.attrs_with_owner(def)
}
fn resolve_doc_path(
self,
db: &dyn HirDatabase,
link: &str,
ns: Option<Namespace>,
) -> Option<DocLinkDef> {
let def = AttrDefId::ExternCrateId(self.into());
resolve_doc_path(db, def, link, ns)
}
}
/// Resolves the item `link` points to in the scope of `def`. /// Resolves the item `link` points to in the scope of `def`.
fn resolve_doc_path( pub fn resolve_doc_path_on(
db: &dyn HirDatabase, db: &dyn HirDatabase,
def: AttrDefId, def: impl HasAttrs,
link: &str, link: &str,
ns: Option<Namespace>, ns: Option<Namespace>,
) -> Option<DocLinkDef> { ) -> Option<DocLinkDef> {
let resolver = match def { // AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()),
// AttrDefId::EnumVariantId(it) => it.parent.resolver(db.upcast()),
resolve_doc_path_on_(db, link, def.attr_id(), ns)
}
fn resolve_doc_path_on_(
db: &dyn HirDatabase,
link: &str,
attr_id: AttrDefId,
ns: Option<Namespace>,
) -> Option<DocLinkDef> {
let resolver = match attr_id {
AttrDefId::ModuleId(it) => it.resolver(db.upcast()), AttrDefId::ModuleId(it) => it.resolver(db.upcast()),
AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()), AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()),
AttrDefId::AdtId(it) => it.resolver(db.upcast()), AttrDefId::AdtId(it) => it.resolver(db.upcast()),
@ -154,12 +123,7 @@ fn resolve_doc_path(
AttrDefId::UseId(it) => it.resolver(db.upcast()), AttrDefId::UseId(it) => it.resolver(db.upcast()),
AttrDefId::MacroId(it) => it.resolver(db.upcast()), AttrDefId::MacroId(it) => it.resolver(db.upcast()),
AttrDefId::ExternCrateId(it) => it.resolver(db.upcast()), AttrDefId::ExternCrateId(it) => it.resolver(db.upcast()),
AttrDefId::GenericParamId(it) => match it { AttrDefId::GenericParamId(_) => return None,
GenericParamId::TypeParamId(it) => it.parent(),
GenericParamId::ConstParamId(it) => it.parent(),
GenericParamId::LifetimeParamId(it) => it.parent,
}
.resolver(db.upcast()),
}; };
let mut modpath = modpath_from_str(db, link)?; let mut modpath = modpath_from_str(db, link)?;

View file

@ -88,7 +88,7 @@ use triomphe::Arc;
use crate::db::{DefDatabase, HirDatabase}; use crate::db::{DefDatabase, HirDatabase};
pub use crate::{ pub use crate::{
attrs::{DocLinkDef, HasAttrs}, attrs::{resolve_doc_path_on, HasAttrs},
diagnostics::{ diagnostics::{
AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode, AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode,
IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError,
@ -4839,3 +4839,10 @@ pub enum ItemContainer {
ExternBlock(), ExternBlock(),
Crate(CrateId), Crate(CrateId),
} }
/// Subset of `ide_db::Definition` that doc links can resolve to.
pub enum DocLinkDef {
ModuleDef(ModuleDef),
Field(Field),
SelfType(Trait),
}

View file

@ -1,7 +1,7 @@
use either::Either; use either::Either;
use hir::{ use hir::{
db::{DefDatabase, HirDatabase}, db::{DefDatabase, HirDatabase},
AttrId, AttrSourceMap, AttrsWithOwner, HasAttrs, InFile, resolve_doc_path_on, AttrId, AttrSourceMap, AttrsWithOwner, HasAttrs, InFile,
}; };
use itertools::Itertools; use itertools::Itertools;
use syntax::{ use syntax::{
@ -32,77 +32,13 @@ impl From<Documentation> for String {
pub trait HasDocs: HasAttrs { pub trait HasDocs: HasAttrs {
fn docs(self, db: &dyn HirDatabase) -> Option<Documentation>; fn docs(self, db: &dyn HirDatabase) -> Option<Documentation>;
fn resolve_doc_path(
self,
db: &dyn HirDatabase,
link: &str,
ns: Option<hir::Namespace>,
) -> Option<hir::DocLinkDef>;
} }
macro_rules! impl_has_docs {
($($def:ident,)*) => {$(
impl HasDocs for hir::$def {
fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
docs_from_attrs(&self.attrs(db)).map(Documentation)
}
}
)*};
}
impl_has_docs![
Field,
Variant,
Static,
Const,
Trait,
TraitAlias,
TypeAlias,
Macro,
Function,
Adt,
Module,
GenericParam,
Impl,
];
macro_rules! impl_has_docs_enum {
($($variant:ident),* for $enum:ident) => {$(
impl HasDocs for hir::$variant {
fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
hir::$enum::$variant(self).docs(db)
}
}
)*};
}
impl_has_docs_enum![Struct, Union, Enum for Adt];
impl_has_docs_enum![TypeParam, ConstParam, LifetimeParam for GenericParam];
impl HasDocs for hir::AssocItem {
fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
match self {
hir::AssocItem::Function(it) => it.docs(db),
hir::AssocItem::Const(it) => it.docs(db),
hir::AssocItem::TypeAlias(it) => it.docs(db),
}
}
}
impl HasDocs for hir::ExternCrateDecl {
fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
let crate_docs =
docs_from_attrs(&self.resolved_crate(db)?.root_module().attrs(db)).map(String::from);
let decl_docs = docs_from_attrs(&self.attrs(db)).map(String::from);
match (decl_docs, crate_docs) {
(None, None) => None,
(Some(decl_docs), None) => Some(decl_docs),
(None, Some(crate_docs)) => Some(crate_docs),
(Some(mut decl_docs), Some(crate_docs)) => {
decl_docs.push('\n');
decl_docs.push('\n');
decl_docs += &crate_docs;
Some(decl_docs)
}
}
.map(Documentation::new)
}
}
/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree. /// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree.
#[derive(Debug)] #[derive(Debug)]
pub struct DocsRangeMap { pub struct DocsRangeMap {
@ -150,22 +86,6 @@ impl DocsRangeMap {
} }
} }
fn get_doc_string_in_attr(it: &ast::Attr) -> Option<ast::String> {
match it.expr() {
// #[doc = lit]
Some(ast::Expr::Literal(lit)) => match lit.kind() {
ast::LiteralKind::String(it) => Some(it),
_ => None,
},
// #[cfg_attr(..., doc = "", ...)]
None => {
// FIXME: See highlight injection for what to do here
None
}
_ => None,
}
}
pub fn docs_with_rangemap( pub fn docs_with_rangemap(
db: &dyn DefDatabase, db: &dyn DefDatabase,
attrs: &AttrsWithOwner, attrs: &AttrsWithOwner,
@ -237,6 +157,116 @@ pub fn docs_from_attrs(attrs: &hir::Attrs) -> Option<String> {
} }
} }
macro_rules! impl_has_docs {
($($def:ident,)*) => {$(
impl HasDocs for hir::$def {
fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
docs_from_attrs(&self.attrs(db)).map(Documentation)
}
fn resolve_doc_path(
self,
db: &dyn HirDatabase,
link: &str,
ns: Option<hir::Namespace>
) -> Option<hir::DocLinkDef> {
resolve_doc_path_on(db, self, link, ns)
}
}
)*};
}
impl_has_docs![
Variant, Field, Static, Const, Trait, TraitAlias, TypeAlias, Macro, Function, Adt, Module,
Impl,
];
macro_rules! impl_has_docs_enum {
($($variant:ident),* for $enum:ident) => {$(
impl HasDocs for hir::$variant {
fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
hir::$enum::$variant(self).docs(db)
}
fn resolve_doc_path(
self,
db: &dyn HirDatabase,
link: &str,
ns: Option<hir::Namespace>
) -> Option<hir::DocLinkDef> {
hir::$enum::$variant(self).resolve_doc_path(db, link, ns)
}
}
)*};
}
impl_has_docs_enum![Struct, Union, Enum for Adt];
impl HasDocs for hir::AssocItem {
fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
match self {
hir::AssocItem::Function(it) => it.docs(db),
hir::AssocItem::Const(it) => it.docs(db),
hir::AssocItem::TypeAlias(it) => it.docs(db),
}
}
fn resolve_doc_path(
self,
db: &dyn HirDatabase,
link: &str,
ns: Option<hir::Namespace>,
) -> Option<hir::DocLinkDef> {
match self {
hir::AssocItem::Function(it) => it.resolve_doc_path(db, link, ns),
hir::AssocItem::Const(it) => it.resolve_doc_path(db, link, ns),
hir::AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
}
}
}
impl HasDocs for hir::ExternCrateDecl {
fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
let crate_docs =
docs_from_attrs(&self.resolved_crate(db)?.root_module().attrs(db)).map(String::from);
let decl_docs = docs_from_attrs(&self.attrs(db)).map(String::from);
match (decl_docs, crate_docs) {
(None, None) => None,
(Some(decl_docs), None) => Some(decl_docs),
(None, Some(crate_docs)) => Some(crate_docs),
(Some(mut decl_docs), Some(crate_docs)) => {
decl_docs.push('\n');
decl_docs.push('\n');
decl_docs += &crate_docs;
Some(decl_docs)
}
}
.map(Documentation::new)
}
fn resolve_doc_path(
self,
db: &dyn HirDatabase,
link: &str,
ns: Option<hir::Namespace>,
) -> Option<hir::DocLinkDef> {
resolve_doc_path_on(db, self, link, ns)
}
}
fn get_doc_string_in_attr(it: &ast::Attr) -> Option<ast::String> {
match it.expr() {
// #[doc = lit]
Some(ast::Expr::Literal(lit)) => match lit.kind() {
ast::LiteralKind::String(it) => Some(it),
_ => None,
},
// #[cfg_attr(..., doc = "", ...)]
None => {
// FIXME: See highlight injection for what to do here
None
}
_ => None,
}
}
fn doc_indent(attrs: &hir::Attrs) -> usize { fn doc_indent(attrs: &hir::Attrs) -> usize {
attrs attrs
.by_key("doc") .by_key("doc")

View file

@ -16,7 +16,7 @@ use hir::{db::HirDatabase, Adt, AsAssocItem, AssocItem, AssocItemContainer, HasA
use ide_db::{ use ide_db::{
base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, SourceDatabase}, base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, SourceDatabase},
defs::{Definition, NameClass, NameRefClass}, defs::{Definition, NameClass, NameRefClass},
documentation::{docs_with_rangemap, Documentation}, documentation::{docs_with_rangemap, Documentation, HasDocs},
helpers::pick_best_token, helpers::pick_best_token,
RootDatabase, RootDatabase,
}; };

View file

@ -471,7 +471,7 @@ pub(super) fn definition(
Definition::SelfType(impl_def) => { Definition::SelfType(impl_def) => {
impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))? impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))?
} }
Definition::GenericParam(it) => label_and_docs(db, it), Definition::GenericParam(it) => (it.display(db).to_string(), None),
Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db).display(db))), Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db).display(db))),
Definition::ExternCrateDecl(it) => label_and_docs(db, it), Definition::ExternCrateDecl(it) => label_and_docs(db, it),
// FIXME: We should be able to show more info about these // FIXME: We should be able to show more info about these