diff options
| author | rainy-me <github@yue.coffee> | 2022-05-05 00:41:29 +0900 |
|---|---|---|
| committer | rainy-me <github@yue.coffee> | 2022-05-05 00:41:29 +0900 |
| commit | 5f4351fbb6cf07582974e37845f4d30b81399b0a (patch) | |
| tree | 059e0de62fcb946ef13ef0fb8239dfff46340e55 | |
| parent | 0ee4e6a22d79f58b6b459dbc874d6b90a4495d83 (diff) | |
| download | rust-5f4351fbb6cf07582974e37845f4d30b81399b0a.tar.gz rust-5f4351fbb6cf07582974e37845f4d30b81399b0a.zip | |
fix: doc url link type
| -rw-r--r-- | crates/hir-def/src/attr.rs | 17 | ||||
| -rw-r--r-- | crates/hir/src/lib.rs | 19 | ||||
| -rw-r--r-- | crates/ide/src/doc_links.rs | 45 | ||||
| -rw-r--r-- | crates/ide/src/hover/tests.rs | 34 |
4 files changed, 82 insertions, 33 deletions
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs index 11d3f48b94a..f92de2d42d1 100644 --- a/crates/hir-def/src/attr.rs +++ b/crates/hir-def/src/attr.rs @@ -853,6 +853,23 @@ impl<'attr> AttrQuery<'attr> { .iter() .filter(move |attr| attr.path.as_ident().map_or(false, |s| s.to_smol_str() == key)) } + + pub fn find_string_value_in_tt(self, key: &'attr str) -> Option<&SmolStr> { + if !self.exists() { + return None; + } + + self.tt_values().find_map(|tt| { + let name = tt.token_trees.iter() + .skip_while(|tt| !matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { text, ..} )) if text == key)) + .nth(2); + + match name { + Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ref text, ..}))) => Some(text), + _ => None + } + }) + } } fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 2d731687277..54a04f0ba21 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -75,7 +75,6 @@ use syntax::{ ast::{self, HasAttrs as _, HasDocComments, HasName}, AstNode, AstPtr, SmolStr, SyntaxNodePtr, T, }; -use tt::{Ident, Leaf, Literal, TokenTree}; use crate::db::{DefDatabase, HirDatabase}; @@ -230,23 +229,7 @@ impl Crate { pub fn get_html_root_url(self: &Crate, db: &dyn HirDatabase) -> Option<String> { // Look for #![doc(html_root_url = "...")] let attrs = db.attrs(AttrDefId::ModuleId(self.root_module(db).into())); - let doc_attr_q = attrs.by_key("doc"); - - if !doc_attr_q.exists() { - return None; - } - - let doc_url = doc_attr_q.tt_values().filter_map(|tt| { - let name = tt.token_trees.iter() - .skip_while(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Ident(Ident { text, ..} )) if text == "html_root_url")) - .nth(2); - - match name { - Some(TokenTree::Leaf(Leaf::Literal(Literal{ref text, ..}))) => Some(text), - _ => None - } - }).next(); - + let doc_url = attrs.by_key("doc").find_string_value_in_tt("html_root_url"); doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/") } diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index a1e170634b7..2b788fb6cf9 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -45,19 +45,19 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: Defin // and valid URLs so we choose to be too eager to try to resolve what might be // a URL. if target.contains("://") { - (target.to_string(), title.to_string()) + (Some(LinkType::Inline), target.to_string(), title.to_string()) } else { // Two possibilities: // * path-based links: `../../module/struct.MyStruct.html` // * module-based links (AKA intra-doc links): `super::super::module::MyStruct` - if let Some(rewritten) = rewrite_intra_doc_link(db, definition, target, title) { - return rewritten; + if let Some((target, title)) = rewrite_intra_doc_link(db, definition, target, title) { + return (None, target, title); } if let Some(target) = rewrite_url_link(db, definition, target) { - return (target, title.to_string()); + return (Some(LinkType::Inline), target, title.to_string()); } - (target.to_string(), title.to_string()) + (None, target.to_string(), title.to_string()) } }); let mut out = String::new(); @@ -368,33 +368,42 @@ fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option<String> { /// Rewrites a markdown document, applying 'callback' to each link. fn map_links<'e>( events: impl Iterator<Item = Event<'e>>, - callback: impl Fn(&str, &str) -> (String, String), + callback: impl Fn(&str, &str) -> (Option<LinkType>, String, String), ) -> impl Iterator<Item = Event<'e>> { let mut in_link = false; - let mut link_target: Option<CowStr> = None; + // holds the origin link target on start event and the rewritten one on end event + let mut end_link_target: Option<CowStr> = None; + // normally link's type is determined by the type of link tag in the end event, + // however in same cases we want to change the link type. + // For example, Shortcut type doesn't make sense for url links + let mut end_link_type: Option<LinkType> = None; events.map(move |evt| match evt { Event::Start(Tag::Link(_, ref target, _)) => { in_link = true; - link_target = Some(target.clone()); + end_link_target = Some(target.clone()); evt } Event::End(Tag::Link(link_type, target, _)) => { in_link = false; Event::End(Tag::Link( - link_type, - link_target.take().unwrap_or(target), + end_link_type.unwrap_or(link_type), + end_link_target.take().unwrap_or(target), CowStr::Borrowed(""), )) } Event::Text(s) if in_link => { - let (link_target_s, link_name) = callback(&link_target.take().unwrap(), &s); - link_target = Some(CowStr::Boxed(link_target_s.into())); + let (link_type, link_target_s, link_name) = + callback(&end_link_target.take().unwrap(), &s); + end_link_target = Some(CowStr::Boxed(link_target_s.into())); + end_link_type = link_type; Event::Text(CowStr::Boxed(link_name.into())) } Event::Code(s) if in_link => { - let (link_target_s, link_name) = callback(&link_target.take().unwrap(), &s); - link_target = Some(CowStr::Boxed(link_target_s.into())); + let (link_type, link_target_s, link_name) = + callback(&end_link_target.take().unwrap(), &s); + end_link_target = Some(CowStr::Boxed(link_target_s.into())); + end_link_type = link_type; Event::Code(CowStr::Boxed(link_name.into())) } _ => evt, @@ -468,7 +477,13 @@ fn filename_and_frag_for_def( Adt::Union(u) => format!("union.{}.html", u.name(db)), }, Definition::Module(m) => match m.name(db) { - Some(name) => format!("{}/index.html", name), + // `#[doc(keyword = "...")]` is internal used only by rust compiler + Some(name) => match m.attrs(db).by_key("doc").find_string_value_in_tt("keyword") { + Some(kw) => { + format!("keyword.{}.html", kw.trim_matches('"')) + } + None => format!("{}/index.html", name), + }, None => String::from("index.html"), }, Definition::Trait(t) => format!("trait.{}.html", t.name(db)), diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index d61b5af13a1..95420f2ffe1 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -3642,6 +3642,40 @@ mod return_keyword {} } #[test] +fn hover_keyword_doc() { + check( + r#" +//- /main.rs crate:main deps:std +fn foo() { + let bar = mov$0e || {}; +} +//- /libstd.rs crate:std +#[doc(keyword = "move")] +/// [closure] +/// [closures][closure] +/// [threads] +/// +/// [closure]: ../book/ch13-01-closures.html +/// [threads]: ../book/ch16-01-threads.html#using-move-closures-with-threads +mod move_keyword {} +"#, + expect + [closures](https://doc.rust-lang.org/nightly/book/ch13-01-closures.html) + [threads](https://doc.rust-lang.org/nightly/book/ch16-01-threads.html#using-move-closures-with-threads) + "##]], + ); +} + +#[test] fn hover_keyword_as_primitive() { check( r#" |
