Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/doc_links.rs')
| -rw-r--r-- | crates/ide/src/doc_links.rs | 254 |
1 files changed, 113 insertions, 141 deletions
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 703449fe40..075c917225 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -8,10 +8,7 @@ use pulldown_cmark_to_cmark::{cmark_with_options, Options as CMarkOptions}; use stdx::format_to; use url::Url; -use hir::{ - db::HirDatabase, Adt, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasAttrs, MacroDef, - ModuleDef, -}; +use hir::{db::HirDatabase, Adt, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasAttrs}; use ide_db::{ defs::{Definition, NameClass, NameRefClass}, helpers::pick_best_token, @@ -53,10 +50,8 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: Defin if let Some(rewritten) = rewrite_intra_doc_link(db, definition, target, title) { return rewritten; } - if let Definition::ModuleDef(def) = definition { - if let Some(target) = rewrite_url_link(db, Either::Left(def), target) { - return (target, title.to_string()); - } + if let Some(target) = rewrite_url_link(db, definition, target) { + return (target, title.to_string()); } (target.to_string(), title.to_string()) @@ -174,25 +169,27 @@ pub(crate) fn resolve_doc_path_for_def( def: Definition, link: &str, ns: Option<hir::Namespace>, -) -> Option<Either<ModuleDef, MacroDef>> { - match def { - Definition::ModuleDef(def) => match def { - hir::ModuleDef::Module(it) => it.resolve_doc_path(db, link, ns), - hir::ModuleDef::Function(it) => it.resolve_doc_path(db, link, ns), - hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, link, ns), - hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, link, ns), - hir::ModuleDef::Const(it) => it.resolve_doc_path(db, link, ns), - hir::ModuleDef::Static(it) => it.resolve_doc_path(db, link, ns), - hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, link, ns), - hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, link, ns), - hir::ModuleDef::BuiltinType(_) => None, - }, +) -> Option<Definition> { + let def = match def { + Definition::Module(it) => it.resolve_doc_path(db, link, ns), + Definition::Function(it) => it.resolve_doc_path(db, link, ns), + Definition::Adt(it) => it.resolve_doc_path(db, link, ns), + Definition::Variant(it) => it.resolve_doc_path(db, link, ns), + Definition::Const(it) => it.resolve_doc_path(db, link, ns), + Definition::Static(it) => it.resolve_doc_path(db, link, ns), + Definition::Trait(it) => it.resolve_doc_path(db, link, ns), + Definition::TypeAlias(it) => it.resolve_doc_path(db, link, ns), Definition::Macro(it) => it.resolve_doc_path(db, link, ns), Definition::Field(it) => it.resolve_doc_path(db, link, ns), - Definition::SelfType(_) + Definition::BuiltinType(_) + | Definition::SelfType(_) | Definition::Local(_) | Definition::GenericParam(_) | Definition::Label(_) => None, + }?; + match def { + Either::Left(def) => Some(Definition::from(def)), + Either::Right(def) => Some(Definition::Macro(def)), } } @@ -202,17 +199,17 @@ pub(crate) fn doc_attributes( ) -> Option<(hir::AttrsWithOwner, Definition)> { match_ast! { match node { - ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), - ast::Module(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), - ast::Fn(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Function(def)))), - ast::Struct(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(def))))), - ast::Union(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Union(def))))), - ast::Enum(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(def))))), - ast::Variant(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Variant(def)))), - ast::Trait(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Trait(def)))), - ast::Static(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Static(def)))), - ast::Const(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Const(def)))), - ast::TypeAlias(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::TypeAlias(def)))), + ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Module(def))), + ast::Module(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Module(def))), + ast::Fn(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Function(def))), + ast::Struct(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Adt(hir::Adt::Struct(def)))), + ast::Union(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Adt(hir::Adt::Union(def)))), + ast::Enum(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Adt(hir::Adt::Enum(def)))), + ast::Variant(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Variant(def))), + ast::Trait(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Trait(def))), + ast::Static(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Static(def))), + ast::Const(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Const(def))), + ast::TypeAlias(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::TypeAlias(def))), ast::Impl(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::SelfType(def))), ast::RecordField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), ast::TupleField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), @@ -274,10 +271,7 @@ impl DocCommentToken { let in_expansion_relative_range = in_expansion_range - descended_prefix_len - token_start; // Apply relative range to the original input comment let absolute_range = in_expansion_relative_range + original_start + prefix_len; - let def = match resolve_doc_path_for_def(sema.db, def, &link, ns)? { - Either::Left(it) => Definition::ModuleDef(it), - Either::Right(it) => Definition::Macro(it), - }; + let def = resolve_doc_path_for_def(sema.db, def, &link, ns)?; cb(def, node, absolute_range) }) } @@ -299,33 +293,8 @@ fn broken_link_clone_cb<'a, 'b>(link: BrokenLink<'a>) -> Option<(CowStr<'b>, Cow // // This should cease to be a problem if RFC2988 (Stable Rustdoc URLs) is implemented // https://github.com/rust-lang/rfcs/pull/2988 -fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> { - let (target, frag) = match definition { - Definition::ModuleDef(def) => { - if let Some(assoc_item) = def.as_assoc_item(db) { - let def = match assoc_item.container(db) { - AssocItemContainer::Trait(t) => t.into(), - AssocItemContainer::Impl(i) => i.self_ty(db).as_adt()?.into(), - }; - let frag = get_assoc_item_fragment(db, assoc_item)?; - (Either::Left(def), Some(frag)) - } else { - (Either::Left(def), None) - } - } - Definition::Field(field) => { - let def = match field.parent_def(db) { - hir::VariantDef::Struct(it) => it.into(), - hir::VariantDef::Union(it) => it.into(), - hir::VariantDef::Variant(it) => it.into(), - }; - (Either::Left(def), Some(format!("structfield.{}", field.name(db)))) - } - Definition::Macro(makro) => (Either::Right(makro), None), - // FIXME impls - Definition::SelfType(_) => return None, - Definition::Local(_) | Definition::GenericParam(_) | Definition::Label(_) => return None, - }; +fn get_doc_link(db: &RootDatabase, def: Definition) -> Option<String> { + let (target, file, frag) = filename_and_frag_for_def(db, def)?; let krate = crate_of_def(db, target)?; let mut url = get_doc_base_url(db, &krate)?; @@ -334,7 +303,7 @@ fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> { url = url.join(&path).ok()?; } - url = url.join(&get_symbol_filename(db, target)?).ok()?; + url = url.join(&file).ok()?; url.set_fragment(frag.as_deref()); Some(url.into()) @@ -352,73 +321,51 @@ fn rewrite_intra_doc_link( let krate = crate_of_def(db, resolved)?; let mut url = get_doc_base_url(db, &krate)?; + let (_, file, frag) = filename_and_frag_for_def(db, resolved)?; if let Some(path) = mod_path_of_def(db, resolved) { url = url.join(&path).ok()?; } - let (resolved, frag) = - if let Some(assoc_item) = resolved.left().and_then(|it| it.as_assoc_item(db)) { - let resolved = match assoc_item.container(db) { - AssocItemContainer::Trait(t) => t.into(), - AssocItemContainer::Impl(i) => i.self_ty(db).as_adt()?.into(), - }; - let frag = get_assoc_item_fragment(db, assoc_item)?; - (Either::Left(resolved), Some(frag)) - } else { - (resolved, None) - }; - url = url.join(&get_symbol_filename(db, resolved)?).ok()?; + url = url.join(&file).ok()?; url.set_fragment(frag.as_deref()); Some((url.into(), strip_prefixes_suffixes(title).to_string())) } /// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`). -fn rewrite_url_link( - db: &RootDatabase, - def: Either<ModuleDef, MacroDef>, - target: &str, -) -> Option<String> { +fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option<String> { if !(target.contains('#') || target.contains(".html")) { return None; } let krate = crate_of_def(db, def)?; let mut url = get_doc_base_url(db, &krate)?; + let (def, file, frag) = filename_and_frag_for_def(db, def)?; if let Some(path) = mod_path_of_def(db, def) { url = url.join(&path).ok()?; } - url = url.join(&get_symbol_filename(db, def)?).ok()?; + url = url.join(&file).ok()?; + url.set_fragment(frag.as_deref()); url.join(target).ok().map(Into::into) } -fn crate_of_def(db: &RootDatabase, def: Either<ModuleDef, MacroDef>) -> Option<Crate> { +fn crate_of_def(db: &RootDatabase, def: Definition) -> Option<Crate> { let krate = match def { // Definition::module gives back the parent module, we don't want that as it fails for root modules - Either::Left(ModuleDef::Module(module)) => module.krate(), - Either::Left(def) => def.module(db)?.krate(), - Either::Right(def) => def.module(db)?.krate(), + Definition::Module(module) => module.krate(), + def => def.module(db)?.krate(), }; Some(krate) } -fn mod_path_of_def(db: &RootDatabase, def: Either<ModuleDef, MacroDef>) -> Option<String> { - match def { - Either::Left(def) => def.canonical_module_path(db).map(|it| { - let mut path = String::new(); - it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name)); - path - }), - Either::Right(def) => { - def.module(db).map(|it| it.path_to_root(db).into_iter().rev()).map(|it| { - let mut path = String::new(); - it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name)); - path - }) - } - } +fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option<String> { + def.canonical_module_path(db).map(|it| { + let mut path = String::new(); + it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name)); + path + }) } /// Rewrites a markdown document, applying 'callback' to each link. @@ -496,34 +443,61 @@ fn get_doc_base_url(db: &RootDatabase, krate: &Crate) -> Option<Url> { /// https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next /// ^^^^^^^^^^^^^^^^^^^ /// ``` -fn get_symbol_filename( +fn filename_and_frag_for_def( db: &dyn HirDatabase, - definition: Either<ModuleDef, MacroDef>, -) -> Option<String> { - let res = match definition { - Either::Left(definition) => match definition { - ModuleDef::Adt(adt) => match adt { - Adt::Struct(s) => format!("struct.{}.html", s.name(db)), - Adt::Enum(e) => format!("enum.{}.html", e.name(db)), - Adt::Union(u) => format!("union.{}.html", u.name(db)), - }, - ModuleDef::Module(m) => match m.name(db) { - Some(name) => format!("{}/index.html", name), - None => String::from("index.html"), - }, - ModuleDef::Trait(t) => format!("trait.{}.html", t.name(db)), - ModuleDef::TypeAlias(t) => format!("type.{}.html", t.name(db)), - ModuleDef::BuiltinType(t) => format!("primitive.{}.html", t.name()), - ModuleDef::Function(f) => format!("fn.{}.html", f.name(db)), - ModuleDef::Variant(ev) => { - format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db)) - } - ModuleDef::Const(c) => format!("const.{}.html", c.name(db)?), - ModuleDef::Static(s) => format!("static.{}.html", s.name(db)?), + def: Definition, +) -> Option<(Definition, String, Option<String>)> { + if let Some(assoc_item) = def.as_assoc_item(db) { + let def = match assoc_item.container(db) { + AssocItemContainer::Trait(t) => t.into(), + AssocItemContainer::Impl(i) => i.self_ty(db).as_adt()?.into(), + }; + let (_, file, _) = filename_and_frag_for_def(db, def)?; + let frag = get_assoc_item_fragment(db, assoc_item)?; + return Some((def, file, Some(frag))); + } + + let res = match def { + Definition::Adt(adt) => match adt { + Adt::Struct(s) => format!("struct.{}.html", s.name(db)), + Adt::Enum(e) => format!("enum.{}.html", e.name(db)), + Adt::Union(u) => format!("union.{}.html", u.name(db)), + }, + Definition::Module(m) => match m.name(db) { + Some(name) => format!("{}/index.html", name), + None => String::from("index.html"), }, - Either::Right(mac) => format!("macro.{}.html", mac.name(db)?), + Definition::Trait(t) => format!("trait.{}.html", t.name(db)), + Definition::TypeAlias(t) => format!("type.{}.html", t.name(db)), + Definition::BuiltinType(t) => format!("primitive.{}.html", t.name()), + Definition::Function(f) => format!("fn.{}.html", f.name(db)), + Definition::Variant(ev) => { + format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db)) + } + Definition::Const(c) => format!("const.{}.html", c.name(db)?), + Definition::Static(s) => format!("static.{}.html", s.name(db)), + Definition::Macro(mac) => format!("macro.{}.html", mac.name(db)?), + Definition::Field(field) => { + let def = match field.parent_def(db) { + hir::VariantDef::Struct(it) => Definition::Adt(it.into()), + hir::VariantDef::Union(it) => Definition::Adt(it.into()), + hir::VariantDef::Variant(it) => Definition::Variant(it), + }; + let (_, file, _) = filename_and_frag_for_def(db, def)?; + return Some((def, file, Some(format!("structfield.{}", field.name(db))))); + } + Definition::SelfType(impl_) => { + let adt = impl_.self_ty(db).as_adt()?.into(); + let (_, file, _) = filename_and_frag_for_def(db, adt)?; + // FIXME fragment numbering + return Some((adt, file, Some(String::from("impl")))); + } + Definition::Local(_) => return None, + Definition::GenericParam(_) => return None, + Definition::Label(_) => return None, }; - Some(res) + + Some((def, res, None)) } /// Get the fragment required to link to a specific field, method, associated type, or associated constant. @@ -803,8 +777,6 @@ pub struct B$0ar #[test] fn rewrite_on_field() { - // FIXME: Should be - // [Foo](https://docs.rs/test/*/test/struct.Foo.html) check_rewrite( r#" //- /main.rs crate:foo @@ -813,7 +785,7 @@ pub struct Foo { fie$0ld: () } "#, - expect"#]], + expect"#]], ); } @@ -927,17 +899,17 @@ pub struct $0Foo; ) -> Option<Option<(Option<hir::Documentation>, Definition)>> { Some(match_ast! { match node { - ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), - ast::Module(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), - ast::Fn(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::ModuleDef(hir::ModuleDef::Function(def)))), - ast::Struct(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(def))))), - ast::Union(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Union(def))))), - ast::Enum(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(def))))), - ast::Variant(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::ModuleDef(hir::ModuleDef::Variant(def)))), - ast::Trait(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::ModuleDef(hir::ModuleDef::Trait(def)))), - ast::Static(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::ModuleDef(hir::ModuleDef::Static(def)))), - ast::Const(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::ModuleDef(hir::ModuleDef::Const(def)))), - ast::TypeAlias(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::ModuleDef(hir::ModuleDef::TypeAlias(def)))), + ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Module(def))), + ast::Module(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Module(def))), + ast::Fn(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Function(def))), + ast::Struct(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Adt(hir::Adt::Struct(def)))), + ast::Union(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Adt(hir::Adt::Union(def)))), + ast::Enum(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Adt(hir::Adt::Enum(def)))), + ast::Variant(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Variant(def))), + ast::Trait(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Trait(def))), + ast::Static(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Static(def))), + ast::Const(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Const(def))), + ast::TypeAlias(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::TypeAlias(def))), ast::Impl(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::SelfType(def))), ast::RecordField(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Field(def))), ast::TupleField(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Field(def))), |