diff options
Diffstat (limited to 'crates/ide/src')
-rw-r--r-- | crates/ide/src/doc_links.rs | 58 |
1 files changed, 41 insertions, 17 deletions
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index c5dc14a23..cb5a8e19a 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs | |||
@@ -108,13 +108,13 @@ pub(crate) fn external_docs( | |||
108 | let node = token.parent()?; | 108 | let node = token.parent()?; |
109 | let definition = match_ast! { | 109 | let definition = match_ast! { |
110 | match node { | 110 | match node { |
111 | ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)), | 111 | ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db))?, |
112 | ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db)), | 112 | ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db))?, |
113 | _ => None, | 113 | _ => return None, |
114 | } | 114 | } |
115 | }; | 115 | }; |
116 | 116 | ||
117 | get_doc_link(db, definition?) | 117 | get_doc_link(db, definition) |
118 | } | 118 | } |
119 | 119 | ||
120 | /// Extracts all links from a given markdown text. | 120 | /// Extracts all links from a given markdown text. |
@@ -214,20 +214,20 @@ fn broken_link_clone_cb<'a, 'b>(link: BrokenLink<'a>) -> Option<(CowStr<'b>, Cow | |||
214 | // This should cease to be a problem if RFC2988 (Stable Rustdoc URLs) is implemented | 214 | // This should cease to be a problem if RFC2988 (Stable Rustdoc URLs) is implemented |
215 | // https://github.com/rust-lang/rfcs/pull/2988 | 215 | // https://github.com/rust-lang/rfcs/pull/2988 |
216 | fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> { | 216 | fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> { |
217 | // Get the outermost definition for the moduledef. This is used to resolve the public path to the type, | 217 | // Get the outermost definition for the module def. This is used to resolve the public path to the type, |
218 | // then we can join the method, field, etc onto it if required. | 218 | // then we can join the method, field, etc onto it if required. |
219 | let target_def: ModuleDef = match definition { | 219 | let target_def: ModuleDef = match definition { |
220 | Definition::ModuleDef(moddef) => match moddef { | 220 | Definition::ModuleDef(def) => match def { |
221 | ModuleDef::Function(f) => f | 221 | ModuleDef::Function(f) => f |
222 | .as_assoc_item(db) | 222 | .as_assoc_item(db) |
223 | .and_then(|assoc| match assoc.container(db) { | 223 | .and_then(|assoc| match assoc.container(db) { |
224 | AssocItemContainer::Trait(t) => Some(t.into()), | 224 | AssocItemContainer::Trait(t) => Some(t.into()), |
225 | AssocItemContainer::Impl(impld) => { | 225 | AssocItemContainer::Impl(impl_) => { |
226 | impld.self_ty(db).as_adt().map(|adt| adt.into()) | 226 | impl_.self_ty(db).as_adt().map(|adt| adt.into()) |
227 | } | 227 | } |
228 | }) | 228 | }) |
229 | .unwrap_or_else(|| f.clone().into()), | 229 | .unwrap_or_else(|| def), |
230 | moddef => moddef, | 230 | def => def, |
231 | }, | 231 | }, |
232 | Definition::Field(f) => f.parent_def(db).into(), | 232 | Definition::Field(f) => f.parent_def(db).into(), |
233 | // FIXME: Handle macros | 233 | // FIXME: Handle macros |
@@ -236,17 +236,28 @@ fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> { | |||
236 | 236 | ||
237 | let ns = ItemInNs::from(target_def); | 237 | let ns = ItemInNs::from(target_def); |
238 | 238 | ||
239 | let module = definition.module(db)?; | 239 | let krate = match definition { |
240 | let krate = module.krate(); | 240 | // Definition::module gives back the parent module, we don't want that as it fails for root modules |
241 | Definition::ModuleDef(ModuleDef::Module(module)) => module.krate(), | ||
242 | _ => definition.module(db)?.krate(), | ||
243 | }; | ||
241 | let import_map = db.import_map(krate.into()); | 244 | let import_map = db.import_map(krate.into()); |
242 | let base = once(krate.display_name(db)?.to_string()) | 245 | |
243 | .chain(import_map.path_of(ns)?.segments.iter().map(|name| name.to_string())) | 246 | let mut base = krate.display_name(db)?.to_string(); |
244 | .join("/") | 247 | let is_root_module = matches!( |
245 | + "/"; | 248 | definition, |
249 | Definition::ModuleDef(ModuleDef::Module(module)) if krate.root_module(db) == module | ||
250 | ); | ||
251 | if !is_root_module { | ||
252 | base = once(base) | ||
253 | .chain(import_map.path_of(ns)?.segments.iter().map(|name| name.to_string())) | ||
254 | .join("/"); | ||
255 | } | ||
256 | base += "/"; | ||
246 | 257 | ||
247 | let filename = get_symbol_filename(db, &target_def); | 258 | let filename = get_symbol_filename(db, &target_def); |
248 | let fragment = match definition { | 259 | let fragment = match definition { |
249 | Definition::ModuleDef(moddef) => match moddef { | 260 | Definition::ModuleDef(def) => match def { |
250 | ModuleDef::Function(f) => { | 261 | ModuleDef::Function(f) => { |
251 | get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Function(f))) | 262 | get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Function(f))) |
252 | } | 263 | } |
@@ -533,6 +544,19 @@ mod tests { | |||
533 | } | 544 | } |
534 | 545 | ||
535 | #[test] | 546 | #[test] |
547 | fn test_doc_url_crate() { | ||
548 | check( | ||
549 | r#" | ||
550 | //- /main.rs crate:main deps:test | ||
551 | use test$0::Foo; | ||
552 | //- /lib.rs crate:test | ||
553 | pub struct Foo; | ||
554 | "#, | ||
555 | expect![[r#"https://docs.rs/test/*/test/index.html"#]], | ||
556 | ); | ||
557 | } | ||
558 | |||
559 | #[test] | ||
536 | fn test_doc_url_struct() { | 560 | fn test_doc_url_struct() { |
537 | check( | 561 | check( |
538 | r#" | 562 | r#" |