about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-01-23 02:53:50 +0000
committerbors <bors@rust-lang.org>2015-01-23 02:53:50 +0000
commitd8d5e4d2178097fbe92b26e57d0e18dc1eedbe5e (patch)
tree21595913f46325a665699acac95b7e56a488e64c
parent4874ca36f61d1975398c2ddb9e3d43443a8c373b (diff)
parent2b11a80a60dbae980418b229ab89d2a28dbbc15f (diff)
downloadrust-d8d5e4d2178097fbe92b26e57d0e18dc1eedbe5e.tar.gz
rust-d8d5e4d2178097fbe92b26e57d0e18dc1eedbe5e.zip
Auto merge of #20221 - liigo:rustdoc-sidebar-tooltips-v3, r=alexcrichton
This pull request add tooltips to most links of sidebar.
The tooltips display "summary line" of items' document.

Some lengthy/annoying raw markdown code are eliminated, such as links and headers.
- `[Rust](http://rust-lang.org)` displays as `Rust` (no URLs)
- `# header` displays as `header` (no `#`s)

Some inline spans, e.g. ``` `code` ``` and ```*emphasis*```, are kept as they are, for better readable.

I've make sure `&` `'` `"` `<` and `>` are properly displayed in tooltips, for example, `&'a Option<T>`.

Online preview: http://liigo.com/tmp/tooltips/std/index.html

@alexcrichton @steveklabnik since you have reviewed my previous ([v1](https://github.com/rust-lang/rust/pull/13014),[v2](https://github.com/rust-lang/rust/pull/16448)) PRs of this serise, which have been closed for technical reasons. Thank you.
-rw-r--r--src/librustdoc/html/markdown.rs101
-rw-r--r--src/librustdoc/html/render.rs31
-rw-r--r--src/librustdoc/html/static/main.js13
3 files changed, 130 insertions, 15 deletions
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 6f19519ee7c..00182a80ab3 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -72,16 +72,40 @@ type blockcodefn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
 type headerfn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
                               libc::c_int, *mut libc::c_void);
 
+type linkfn = extern "C" fn (*mut hoedown_buffer, *const hoedown_buffer,
+                             *const hoedown_buffer, *const hoedown_buffer,
+                             *mut libc::c_void) -> libc::c_int;
+
+type normaltextfn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
+                                  *mut libc::c_void);
+
 #[repr(C)]
 struct hoedown_renderer {
-    opaque: *mut hoedown_html_renderer_state,
+    opaque: *mut libc::c_void,
+
     blockcode: Option<blockcodefn>,
     blockquote: Option<extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
                                      *mut libc::c_void)>,
     blockhtml: Option<extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
                                     *mut libc::c_void)>,
     header: Option<headerfn>,
-    other: [libc::size_t; 28],
+
+    other_block_level_callbacks: [libc::size_t; 9],
+
+    /* span level callbacks - NULL or return 0 prints the span verbatim */
+    other_span_level_callbacks_1: [libc::size_t; 9],
+    link: Option<linkfn>,
+    other_span_level_callbacks_2: [libc::size_t; 5],
+    // hoedown will add `math` callback here, but we use an old version of it.
+
+    /* low level callbacks - NULL copies input directly into the output */
+    entity: Option<extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
+                                 *mut libc::c_void)>,
+    normal_text: Option<normaltextfn>,
+
+    /* header and footer */
+    doc_header: Option<extern "C" fn(*mut hoedown_buffer, *mut libc::c_void)>,
+    doc_footer: Option<extern "C" fn(*mut hoedown_buffer, *mut libc::c_void)>,
 }
 
 #[repr(C)]
@@ -134,6 +158,8 @@ extern {
     fn hoedown_document_free(md: *mut hoedown_document);
 
     fn hoedown_buffer_new(unit: libc::size_t) -> *mut hoedown_buffer;
+    fn hoedown_buffer_put(b: *mut hoedown_buffer, c: *const libc::c_char,
+                          n: libc::size_t);
     fn hoedown_buffer_puts(b: *mut hoedown_buffer, c: *const libc::c_char);
     fn hoedown_buffer_free(b: *mut hoedown_buffer);
 
@@ -279,7 +305,8 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result {
             dfltblk: (*renderer).blockcode.unwrap(),
             toc_builder: if print_toc {Some(TocBuilder::new())} else {None}
         };
-        (*(*renderer).opaque).opaque = &mut opaque as *mut _ as *mut libc::c_void;
+        (*((*renderer).opaque as *mut hoedown_html_renderer_state)).opaque
+                = &mut opaque as *mut _ as *mut libc::c_void;
         (*renderer).blockcode = Some(block as blockcodefn);
         (*renderer).header = Some(header as headerfn);
 
@@ -355,7 +382,8 @@ pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) {
         let renderer = hoedown_html_renderer_new(0, 0);
         (*renderer).blockcode = Some(block as blockcodefn);
         (*renderer).header = Some(header as headerfn);
-        (*(*renderer).opaque).opaque = tests as *mut _ as *mut libc::c_void;
+        (*((*renderer).opaque as *mut hoedown_html_renderer_state)).opaque
+                = tests as *mut _ as *mut libc::c_void;
 
         let document = hoedown_document_new(renderer, HOEDOWN_EXTENSIONS, 16);
         hoedown_document_render(document, ob, doc.as_ptr(),
@@ -442,9 +470,60 @@ impl<'a> fmt::Display for MarkdownWithToc<'a> {
     }
 }
 
+pub fn plain_summary_line(md: &str) -> String {
+    extern fn link(_ob: *mut hoedown_buffer,
+                       _link: *const hoedown_buffer,
+                       _title: *const hoedown_buffer,
+                       content: *const hoedown_buffer,
+                       opaque: *mut libc::c_void) -> libc::c_int
+    {
+        unsafe {
+            if !content.is_null() && (*content).size > 0 {
+                let ob = opaque as *mut hoedown_buffer;
+                hoedown_buffer_put(ob, (*content).data as *const libc::c_char,
+                                   (*content).size);
+            }
+        }
+        1
+    }
+
+    extern fn normal_text(_ob: *mut hoedown_buffer,
+                              text: *const hoedown_buffer,
+                              opaque: *mut libc::c_void)
+    {
+        unsafe {
+            let ob = opaque as *mut hoedown_buffer;
+            hoedown_buffer_put(ob, (*text).data as *const libc::c_char,
+                               (*text).size);
+        }
+    }
+
+    unsafe {
+        let ob = hoedown_buffer_new(DEF_OUNIT);
+        let mut plain_renderer: hoedown_renderer = ::std::mem::zeroed();
+        let renderer = &mut plain_renderer as *mut hoedown_renderer;
+        (*renderer).opaque = ob as *mut libc::c_void;
+        (*renderer).link = Some(link as linkfn);
+        (*renderer).normal_text = Some(normal_text as normaltextfn);
+
+        let document = hoedown_document_new(renderer, HOEDOWN_EXTENSIONS, 16);
+        hoedown_document_render(document, ob, md.as_ptr(),
+                                md.len() as libc::size_t);
+        hoedown_document_free(document);
+        let plain_slice = slice::from_raw_buf(&(*ob).data, (*ob).size as uint);
+        let plain = match str::from_utf8(plain_slice) {
+            Ok(s) => s.to_string(),
+            Err(_) => "".to_string(),
+        };
+        hoedown_buffer_free(ob);
+        plain
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::{LangString, Markdown};
+    use super::plain_summary_line;
 
     #[test]
     fn test_lang_string_parse() {
@@ -478,4 +557,18 @@ mod tests {
         let markdown = "# title";
         format!("{}", Markdown(markdown.as_slice()));
     }
+
+    #[test]
+    fn test_plain_summary_line() {
+        fn t(input: &str, expect: &str) {
+            let output = plain_summary_line(input);
+            assert_eq!(output, expect);
+        }
+
+        t("hello [Rust](http://rust-lang.org) :)", "hello Rust :)");
+        t("code `let x = i32;` ...", "code `let x = i32;` ...");
+        t("type `Type<'static>` ...", "type `Type<'static>` ...");
+        t("# top header", "top header");
+        t("## header", "header");
+    }
 }
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index cd2ed5f0a97..9a25993d28a 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -64,8 +64,13 @@ use html::item_type::ItemType;
 use html::layout;
 use html::markdown::Markdown;
 use html::markdown;
+use html::escape::Escape;
 use stability_summary;
 
+/// A pair of name and its optional document.
+#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
+pub struct NameDoc(String, Option<String>);
+
 /// Major driving force in all rustdoc rendering. This contains information
 /// about where in the tree-like hierarchy rendering is occurring and controls
 /// how the current page is being rendered.
@@ -95,7 +100,7 @@ pub struct Context {
     /// functions), and the value is the list of containers belonging to this
     /// header. This map will change depending on the surrounding context of the
     /// page.
-    pub sidebar: HashMap<String, Vec<String>>,
+    pub sidebar: HashMap<String, Vec<NameDoc>>,
     /// This flag indicates whether [src] links should be generated or not. If
     /// the source files are present in the html rendering, then this will be
     /// `true`.
@@ -1245,7 +1250,7 @@ impl Context {
         }
     }
 
-    fn build_sidebar(&self, m: &clean::Module) -> HashMap<String, Vec<String>> {
+    fn build_sidebar(&self, m: &clean::Module) -> HashMap<String, Vec<NameDoc>> {
         let mut map = HashMap::new();
         for item in m.items.iter() {
             if self.ignore_private_item(item) { continue }
@@ -1262,7 +1267,7 @@ impl Context {
             let short = short.to_string();
             let v = map.entry(short).get().unwrap_or_else(
                 |vacant_entry| vacant_entry.insert(Vec::with_capacity(1)));
-            v.push(myname);
+            v.push(NameDoc(myname, Some(shorter_line(item.doc_value()))));
         }
 
         for (_, items) in map.iter_mut() {
@@ -1476,6 +1481,11 @@ fn shorter<'a>(s: Option<&'a str>) -> &'a str {
     }
 }
 
+#[inline]
+fn shorter_line(s: Option<&str>) -> String {
+    shorter(s).replace("\n", " ")
+}
+
 fn document(w: &mut fmt::Formatter, item: &clean::Item) -> fmt::Result {
     match item.doc_value() {
         Some(s) => {
@@ -2201,21 +2211,22 @@ impl<'a> fmt::Display for Sidebar<'a> {
                 None => return Ok(())
             };
             try!(write!(w, "<div class='block {}'><h2>{}</h2>", short, longty));
-            for item in items.iter() {
+            for &NameDoc(ref name, ref doc) in items.iter() {
                 let curty = shortty(cur).to_static_str();
-                let class = if cur.name.as_ref().unwrap() == item &&
+                let class = if cur.name.as_ref().unwrap() == name &&
                                short == curty { "current" } else { "" };
-                try!(write!(w, "<a class='{ty} {class}' href='{href}{path}'>\
-                                {name}</a>",
+                try!(write!(w, "<a class='{ty} {class}' href='{href}{path}' \
+                                title='{title}'>{name}</a>",
                        ty = short,
                        class = class,
                        href = if curty == "mod" {"../"} else {""},
                        path = if short == "mod" {
-                           format!("{}/index.html", item.as_slice())
+                           format!("{}/index.html", name.as_slice())
                        } else {
-                           format!("{}.{}.html", short, item.as_slice())
+                           format!("{}.{}.html", short, name.as_slice())
                        },
-                       name = item.as_slice()));
+                       title = Escape(doc.as_ref().unwrap().as_slice()),
+                       name = name.as_slice()));
             }
             try!(write!(w, "</div>"));
             Ok(())
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index 1b0c3b00640..aac3985f0cc 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -668,6 +668,15 @@
             search();
         }
 
+        function plainSummaryLine(markdown) {
+            var str = markdown.replace(/\n/g, ' ')
+            str = str.replace(/'/g, "\'")
+            str = str.replace(/^#+? (.+?)/, "$1")
+            str = str.replace(/\[(.*?)\]\(.*?\)/g, "$1")
+            str = str.replace(/\[(.*?)\]\[.*?\]/g, "$1")
+            return str;
+        }
+
         index = buildIndex(rawSearchIndex);
         startSearch();
 
@@ -688,8 +697,10 @@
                 if (crates[i] == window.currentCrate) {
                     klass += ' current';
                 }
+                var desc = rawSearchIndex[crates[i]].items[0][3];
                 div.append($('<a>', {'href': '../' + crates[i] + '/index.html',
-                                    'class': klass}).text(crates[i]));
+                                     'title': plainSummaryLine(desc),
+                                     'class': klass}).text(crates[i]));
             }
             sidebar.append(div);
         }