about summary refs log tree commit diff
diff options
context:
space:
mode:
authorrainy-me <github@yue.coffee>2022-05-05 00:41:29 +0900
committerrainy-me <github@yue.coffee>2022-05-05 00:41:29 +0900
commit5f4351fbb6cf07582974e37845f4d30b81399b0a (patch)
tree059e0de62fcb946ef13ef0fb8239dfff46340e55
parent0ee4e6a22d79f58b6b459dbc874d6b90a4495d83 (diff)
downloadrust-5f4351fbb6cf07582974e37845f4d30b81399b0a.tar.gz
rust-5f4351fbb6cf07582974e37845f4d30b81399b0a.zip
fix: doc url link type
-rw-r--r--crates/hir-def/src/attr.rs17
-rw-r--r--crates/hir/src/lib.rs19
-rw-r--r--crates/ide/src/doc_links.rs45
-rw-r--r--crates/ide/src/hover/tests.rs34
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![[r##"
+            *move*
+
+            ```rust
+            move
+            ```
+
+            ---
+
+            [closure](https://doc.rust-lang.org/nightly/book/ch13-01-closures.html)
+            [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#"