From 8f56e7c3b1220ed0b065e97a9061e59284ac1df1 Mon Sep 17 00:00:00 2001 From: Zac Pullar-Strecker Date: Wed, 10 Jun 2020 14:21:00 +1200 Subject: Generate correct symbol filename for relative links --- Cargo.lock | 2 ++ crates/ra_ide/Cargo.toml | 1 + crates/ra_ide/src/hover.rs | 49 ++++++++++++++++++++++++++++++++++++---------- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc36f0fab..112c4210d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1276,12 +1276,14 @@ dependencies = [ "ra_fmt", "ra_hir", "ra_hir_def", + "ra_hir_expand", "ra_ide_db", "ra_prof", "ra_project_model", "ra_ssr", "ra_syntax", "ra_text_edit", + "ra_tt", "rand", "rustc-hash", "stdx", diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml index fa925fc5a..219ad33e6 100644 --- a/crates/ra_ide/Cargo.toml +++ b/crates/ra_ide/Cargo.toml @@ -35,6 +35,7 @@ ra_ssr = { path = "../ra_ssr" } ra_project_model = { path = "../ra_project_model" } ra_hir_def = { path = "../ra_hir_def" } ra_tt = { path = "../ra_tt" } +ra_hir_expand = { path = "../ra_hir_expand" } # ra_ide should depend only on the top-level `hir` package. if you need # something from some `hir_xxx` subpackage, reexport the API via `hir`. diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index f703af434..760d7fe14 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -16,6 +16,7 @@ use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffs use ra_project_model::ProjectWorkspace; use ra_hir_def::{item_scope::ItemInNs, db::DefDatabase}; use ra_tt::{Literal, Ident, Punct, TokenTree, Leaf}; +use ra_hir_expand::name::AsName; use comrak::{parse_document,format_commonmark, ComrakOptions, Arena}; use comrak::nodes::NodeValue; @@ -406,9 +407,9 @@ fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition, wor match Url::parse(&String::from_utf8(link.url.clone()).unwrap()) { // If this is a valid absolute URL don't touch it Ok(_) => (), - // If contains .html file-based link to new page - // If starts with #fragment file-based link to fragment on current page - // If contains :: module-based link + // Otherwise there are two main possibilities + // path-based links: `../../module/struct.MyStruct.html` + // module-based links (AKA intra-doc links): `super::super::module::MyStruct` Err(_) => { let link_str = String::from_utf8(link.url.clone()).unwrap(); let resolved = try_resolve_path(db, &mut doc_target_dirs.clone(), definition, &link_str, UrlMode::Url) @@ -429,7 +430,9 @@ fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition, wor Some(String::from_utf8(out).unwrap()) } -/// Try to resolve path to local documentation via intra-doc-links (i.e. `super::gateway::Shard`) +/// Try to resolve path to local documentation via intra-doc-links (i.e. `super::gateway::Shard`). +/// +/// See [RFC1946](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md). fn try_resolve_intra(db: &RootDatabase, doc_target_dirs: impl Iterator, definition: &Definition, link: &str) -> Option { None } @@ -439,7 +442,7 @@ enum UrlMode { File } -/// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`) +/// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`). fn try_resolve_path(db: &RootDatabase, doc_target_dirs: impl Iterator, definition: &Definition, link: &str, mode: UrlMode) -> Option { let ns = if let Definition::ModuleDef(moddef) = definition { ItemInNs::Types(moddef.clone().into()) @@ -456,12 +459,12 @@ fn try_resolve_path(db: &RootDatabase, doc_target_dirs: impl Iterator { - let root = get_doc_url(db, &krate); let mut base = base.join("/"); - if link.starts_with("#") { - base = base + "/" - }; - root.and_then(|url| url.join(&base).ok()).and_then(|url| url.join(link).ok()).map(|url| url.into_string()) + get_doc_url(db, &krate) + .and_then(|url| url.join(&base).ok()) + .and_then(|url| get_symbol_filename(db, definition).as_deref().map(|f| url.join(f).ok()).flatten()) + .and_then(|url| url.join(link).ok()) + .map(|url| url.into_string()) }, UrlMode::File => { let base = base.collect::(); @@ -502,6 +505,32 @@ fn get_doc_url(db: &RootDatabase, krate: &Crate) -> Option { doc_url.map(|s| s.trim_matches('"').to_owned() + "/").and_then(|s| Url::parse(&s).ok()) } +/// Get the filename and extension generated for a symbol by rustdoc. +/// +/// Example: `struct.Shard.html` +fn get_symbol_filename(db: &RootDatabase, definition: &Definition) -> Option { + Some(match definition { + Definition::ModuleDef(def) => match def { + 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(_) => "index.html".to_string(), + 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.as_name()), + ModuleDef::Function(f) => format!("fn.{}.html", f.name(db)), + ModuleDef::EnumVariant(ev) => format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db)), + ModuleDef::Const(c) => format!("const.{}.html", c.name(db)?), + // TODO: Check this is the right prefix + ModuleDef::Static(s) => format!("static.{}.html", s.name(db)?) + }, + Definition::Macro(m) => format!("macro.{}.html", m.name(db)?), + _ => None? + }) +} + fn iter_nodes<'a, F>(node: &'a comrak::nodes::AstNode<'a>, f: &F) where F : Fn(&'a comrak::nodes::AstNode<'a>) { f(node); -- cgit v1.2.3