rustdoc: show code spans as <code>
in TOC
This commit is contained in:
parent
68773c789a
commit
7091fa5880
@ -50,7 +50,7 @@
|
|||||||
use crate::clean::RenderedLink;
|
use crate::clean::RenderedLink;
|
||||||
use crate::doctest;
|
use crate::doctest;
|
||||||
use crate::doctest::GlobalTestOptions;
|
use crate::doctest::GlobalTestOptions;
|
||||||
use crate::html::escape::Escape;
|
use crate::html::escape::{Escape, EscapeBodyText};
|
||||||
use crate::html::format::Buffer;
|
use crate::html::format::Buffer;
|
||||||
use crate::html::highlight;
|
use crate::html::highlight;
|
||||||
use crate::html::length_limit::HtmlWithLimit;
|
use crate::html::length_limit::HtmlWithLimit;
|
||||||
@ -535,7 +535,9 @@ fn next(&mut self) -> Option<Self::Item> {
|
|||||||
if let Some(ref mut builder) = self.toc {
|
if let Some(ref mut builder) = self.toc {
|
||||||
let mut text_header = String::new();
|
let mut text_header = String::new();
|
||||||
plain_text_from_events(self.buf.iter().map(|(ev, _)| ev.clone()), &mut text_header);
|
plain_text_from_events(self.buf.iter().map(|(ev, _)| ev.clone()), &mut text_header);
|
||||||
let sec = builder.push(level as u32, text_header, id.clone());
|
let mut html_header = String::new();
|
||||||
|
html_text_from_events(self.buf.iter().map(|(ev, _)| ev.clone()), &mut html_header);
|
||||||
|
let sec = builder.push(level as u32, text_header, html_header, id.clone());
|
||||||
self.buf.push_front((Event::Html(format!("{sec} ").into()), 0..0));
|
self.buf.push_front((Event::Html(format!("{sec} ").into()), 0..0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1655,6 +1657,29 @@ pub(crate) fn plain_text_from_events<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn html_text_from_events<'a>(
|
||||||
|
events: impl Iterator<Item = pulldown_cmark::Event<'a>>,
|
||||||
|
s: &mut String,
|
||||||
|
) {
|
||||||
|
for event in events {
|
||||||
|
match &event {
|
||||||
|
Event::Text(text) => {
|
||||||
|
write!(s, "{}", EscapeBodyText(text)).expect("string alloc infallible")
|
||||||
|
}
|
||||||
|
Event::Code(code) => {
|
||||||
|
s.push_str("<code>");
|
||||||
|
write!(s, "{}", EscapeBodyText(code)).expect("string alloc infallible");
|
||||||
|
s.push_str("</code>");
|
||||||
|
}
|
||||||
|
Event::HardBreak | Event::SoftBreak => s.push(' '),
|
||||||
|
Event::Start(Tag::CodeBlock(..)) => break,
|
||||||
|
Event::End(TagEnd::Paragraph) => break,
|
||||||
|
Event::End(TagEnd::Heading(..)) => break,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct MarkdownLink {
|
pub(crate) struct MarkdownLink {
|
||||||
pub kind: LinkType,
|
pub kind: LinkType,
|
||||||
|
@ -81,8 +81,10 @@ pub fn should_render(&self) -> bool {
|
|||||||
/// A link to an item. Content should not be escaped.
|
/// A link to an item. Content should not be escaped.
|
||||||
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone)]
|
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone)]
|
||||||
pub(crate) struct Link<'a> {
|
pub(crate) struct Link<'a> {
|
||||||
/// The content for the anchor tag
|
/// The content for the anchor tag and title attr
|
||||||
name: Cow<'a, str>,
|
name: Cow<'a, str>,
|
||||||
|
/// The content for the anchor tag (if different from name)
|
||||||
|
name_html: Option<Cow<'a, str>>,
|
||||||
/// The id of an anchor within the page (without a `#` prefix)
|
/// The id of an anchor within the page (without a `#` prefix)
|
||||||
href: Cow<'a, str>,
|
href: Cow<'a, str>,
|
||||||
/// Nested list of links (used only in top-toc)
|
/// Nested list of links (used only in top-toc)
|
||||||
@ -91,7 +93,7 @@ pub(crate) struct Link<'a> {
|
|||||||
|
|
||||||
impl<'a> Link<'a> {
|
impl<'a> Link<'a> {
|
||||||
pub fn new(href: impl Into<Cow<'a, str>>, name: impl Into<Cow<'a, str>>) -> Self {
|
pub fn new(href: impl Into<Cow<'a, str>>, name: impl Into<Cow<'a, str>>) -> Self {
|
||||||
Self { href: href.into(), name: name.into(), children: vec![] }
|
Self { href: href.into(), name: name.into(), children: vec![], name_html: None }
|
||||||
}
|
}
|
||||||
pub fn empty() -> Link<'static> {
|
pub fn empty() -> Link<'static> {
|
||||||
Link::new("", "")
|
Link::new("", "")
|
||||||
@ -207,6 +209,7 @@ fn docblock_toc<'a>(
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|entry| {
|
.map(|entry| {
|
||||||
Link {
|
Link {
|
||||||
|
name_html: if entry.html == entry.name { None } else { Some(entry.html.into()) },
|
||||||
name: entry.name.into(),
|
name: entry.name.into(),
|
||||||
href: entry.id.into(),
|
href: entry.id.into(),
|
||||||
children: entry
|
children: entry
|
||||||
@ -214,6 +217,11 @@ fn docblock_toc<'a>(
|
|||||||
.entries
|
.entries
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|entry| Link {
|
.map(|entry| Link {
|
||||||
|
name_html: if entry.html == entry.name {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(entry.html.into())
|
||||||
|
},
|
||||||
name: entry.name.into(),
|
name: entry.name.into(),
|
||||||
href: entry.id.into(),
|
href: entry.id.into(),
|
||||||
// Only a single level of nesting is shown here.
|
// Only a single level of nesting is shown here.
|
||||||
|
@ -23,11 +23,25 @@
|
|||||||
<ul class="block{% if !block.class.is_empty() +%} {{+block.class}}{% endif %}">
|
<ul class="block{% if !block.class.is_empty() +%} {{+block.class}}{% endif %}">
|
||||||
{% for link in block.links %}
|
{% for link in block.links %}
|
||||||
<li> {# #}
|
<li> {# #}
|
||||||
<a href="#{{link.href|safe}}">{{link.name}}</a> {# #}
|
<a href="#{{link.href|safe}}" title="{{link.name}}">
|
||||||
|
{% match link.name_html %}
|
||||||
|
{% when Some with (html) %}
|
||||||
|
{{html|safe}}
|
||||||
|
{% else %}
|
||||||
|
{{link.name}}
|
||||||
|
{% endmatch %}
|
||||||
|
</a> {# #}
|
||||||
{% if !link.children.is_empty() %}
|
{% if !link.children.is_empty() %}
|
||||||
<ul>
|
<ul>
|
||||||
{% for child in link.children %}
|
{% for child in link.children %}
|
||||||
<li><a href="#{{child.href|safe}}">{{child.name}}</a></li>
|
<li><a href="#{{child.href|safe}}" title="{{child.name}}">
|
||||||
|
{% match child.name_html %}
|
||||||
|
{% when Some with (html) %}
|
||||||
|
{{html|safe}}
|
||||||
|
{% else %}
|
||||||
|
{{child.name}}
|
||||||
|
{% endmatch %}
|
||||||
|
</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
//! Table-of-contents creation.
|
//! Table-of-contents creation.
|
||||||
use crate::html::escape::EscapeBodyText;
|
use crate::html::escape::Escape;
|
||||||
|
|
||||||
/// A (recursive) table of contents
|
/// A (recursive) table of contents
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@ -30,7 +30,12 @@ fn count_entries_with_level(&self, level: u32) -> usize {
|
|||||||
pub(crate) struct TocEntry {
|
pub(crate) struct TocEntry {
|
||||||
pub(crate) level: u32,
|
pub(crate) level: u32,
|
||||||
pub(crate) sec_number: String,
|
pub(crate) sec_number: String,
|
||||||
|
// name is a plain text header that works in a `title` tag
|
||||||
|
// html includes `<code>` tags
|
||||||
|
// the tooltip is used so that, when a toc is truncated,
|
||||||
|
// you can mouse over it to see the whole thing
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
|
pub(crate) html: String,
|
||||||
pub(crate) id: String,
|
pub(crate) id: String,
|
||||||
pub(crate) children: Toc,
|
pub(crate) children: Toc,
|
||||||
}
|
}
|
||||||
@ -116,7 +121,7 @@ fn fold_until(&mut self, level: u32) {
|
|||||||
/// Push a level `level` heading into the appropriate place in the
|
/// Push a level `level` heading into the appropriate place in the
|
||||||
/// hierarchy, returning a string containing the section number in
|
/// hierarchy, returning a string containing the section number in
|
||||||
/// `<num>.<num>.<num>` format.
|
/// `<num>.<num>.<num>` format.
|
||||||
pub(crate) fn push(&mut self, level: u32, name: String, id: String) -> &str {
|
pub(crate) fn push(&mut self, level: u32, name: String, html: String, id: String) -> &str {
|
||||||
assert!(level >= 1);
|
assert!(level >= 1);
|
||||||
|
|
||||||
// collapse all previous sections into their parents until we
|
// collapse all previous sections into their parents until we
|
||||||
@ -150,6 +155,7 @@ pub(crate) fn push(&mut self, level: u32, name: String, id: String) -> &str {
|
|||||||
self.chain.push(TocEntry {
|
self.chain.push(TocEntry {
|
||||||
level,
|
level,
|
||||||
name,
|
name,
|
||||||
|
html,
|
||||||
sec_number,
|
sec_number,
|
||||||
id,
|
id,
|
||||||
children: Toc { entries: Vec::new() },
|
children: Toc { entries: Vec::new() },
|
||||||
@ -171,10 +177,11 @@ fn print_inner(&self, v: &mut String) {
|
|||||||
// recursively format this table of contents
|
// recursively format this table of contents
|
||||||
let _ = write!(
|
let _ = write!(
|
||||||
v,
|
v,
|
||||||
"\n<li><a href=\"#{id}\">{num} {name}</a>",
|
"\n<li><a href=\"#{id}\" title=\"{name}\">{num} {html}</a>",
|
||||||
id = entry.id,
|
id = entry.id,
|
||||||
num = entry.sec_number,
|
num = entry.sec_number,
|
||||||
name = EscapeBodyText(&entry.name)
|
name = Escape(&entry.name),
|
||||||
|
html = &entry.html,
|
||||||
);
|
);
|
||||||
entry.children.print_inner(&mut *v);
|
entry.children.print_inner(&mut *v);
|
||||||
v.push_str("</li>");
|
v.push_str("</li>");
|
||||||
|
@ -9,7 +9,10 @@ fn builder_smoke() {
|
|||||||
// there's been no macro mistake.
|
// there's been no macro mistake.
|
||||||
macro_rules! push {
|
macro_rules! push {
|
||||||
($level: expr, $name: expr) => {
|
($level: expr, $name: expr) => {
|
||||||
assert_eq!(builder.push($level, $name.to_string(), "".to_string()), $name);
|
assert_eq!(
|
||||||
|
builder.push($level, $name.to_string(), $name.to_string(), "".to_string()),
|
||||||
|
$name
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
push!(2, "0.1");
|
push!(2, "0.1");
|
||||||
@ -48,6 +51,7 @@ macro_rules! toc {
|
|||||||
TocEntry {
|
TocEntry {
|
||||||
level: $level,
|
level: $level,
|
||||||
name: $name.to_string(),
|
name: $name.to_string(),
|
||||||
|
html: $name.to_string(),
|
||||||
sec_number: $name.to_string(),
|
sec_number: $name.to_string(),
|
||||||
id: "".to_string(),
|
id: "".to_string(),
|
||||||
children: toc!($($sub),*)
|
children: toc!($($sub),*)
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#![feature(lazy_type_alias)]
|
#![feature(lazy_type_alias)]
|
||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
//! # Basic [link](https://example.com) and *emphasis*
|
//! # Basic [link](https://example.com) and *emphasis* and `code`
|
||||||
//!
|
//!
|
||||||
//! This test case covers TOC entries with rich text inside.
|
//! This test case covers TOC entries with rich text inside.
|
||||||
//! Rustdoc normally supports headers with links, but for the
|
//! Rustdoc normally supports headers with links, but for the
|
||||||
@ -12,9 +12,12 @@
|
|||||||
//!
|
//!
|
||||||
//! For consistency, emphasis is also filtered out.
|
//! For consistency, emphasis is also filtered out.
|
||||||
|
|
||||||
// @has foo/index.html
|
//@ has foo/index.html
|
||||||
// User header
|
// User header
|
||||||
// @has - '//section[@id="TOC"]/h3' 'Sections'
|
//@ has - '//section[@id="TOC"]/h3' 'Sections'
|
||||||
// @has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis"]' 'Basic link and emphasis'
|
//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]/@title' 'Basic link and emphasis and `code`'
|
||||||
// @count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis"]/em' 0
|
//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]' 'Basic link and emphasis and code'
|
||||||
// @count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis"]/a' 0
|
//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]/em' 0
|
||||||
|
//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]/a' 0
|
||||||
|
//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]/code' 1
|
||||||
|
//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#basic-link-and-emphasis-and-code"]/code' 'code'
|
||||||
|
@ -14,13 +14,13 @@
|
|||||||
//! in the `top-doc`, and the one that's not in the `top-doc`
|
//! in the `top-doc`, and the one that's not in the `top-doc`
|
||||||
//! needs to match the one that isn't in the `top-toc`.
|
//! needs to match the one that isn't in the `top-toc`.
|
||||||
|
|
||||||
// @has foo/index.html
|
//@ has foo/index.html
|
||||||
// User header
|
// User header
|
||||||
// @has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#structs"]' 'Structs'
|
//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#structs"]' 'Structs'
|
||||||
// @has - '//details[@class="toggle top-doc"]/div[@class="docblock"]/h2[@id="structs"]' 'Structs'
|
//@ has - '//details[@class="toggle top-doc"]/div[@class="docblock"]/h2[@id="structs"]' 'Structs'
|
||||||
// Built-in header
|
// Built-in header
|
||||||
// @has - '//section[@id="TOC"]/ul[@class="block"]/li/a[@href="#structs-1"]' 'Structs'
|
//@ has - '//section[@id="TOC"]/ul[@class="block"]/li/a[@href="#structs-1"]' 'Structs'
|
||||||
// @has - '//section[@id="main-content"]/h2[@id="structs-1"]' 'Structs'
|
//@ has - '//section[@id="main-content"]/h2[@id="structs-1"]' 'Structs'
|
||||||
|
|
||||||
/// # Fields
|
/// # Fields
|
||||||
/// ## Fields
|
/// ## Fields
|
||||||
@ -29,15 +29,15 @@
|
|||||||
/// The difference between struct-like headers and module-like headers
|
/// The difference between struct-like headers and module-like headers
|
||||||
/// is strange, but not actually a problem as long as we're consistent.
|
/// is strange, but not actually a problem as long as we're consistent.
|
||||||
|
|
||||||
// @has foo/struct.MyStruct.html
|
//@ has foo/struct.MyStruct.html
|
||||||
// User header
|
// User header
|
||||||
// @has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#fields-1"]' 'Fields'
|
//@ has - '//section[@id="TOC"]/ul[@class="block top-toc"]/li/a[@href="#fields-1"]' 'Fields'
|
||||||
// @has - '//details[@class="toggle top-doc"]/div[@class="docblock"]/h2[@id="fields-1"]' 'Fields'
|
//@ has - '//details[@class="toggle top-doc"]/div[@class="docblock"]/h2[@id="fields-1"]' 'Fields'
|
||||||
// Only one level of nesting
|
// Only one level of nesting
|
||||||
// @count - '//section[@id="TOC"]/ul[@class="block top-toc"]//a' 2
|
//@ count - '//section[@id="TOC"]/ul[@class="block top-toc"]//a' 2
|
||||||
// Built-in header
|
// Built-in header
|
||||||
// @has - '//section[@id="TOC"]/h3/a[@href="#fields"]' 'Fields'
|
//@ has - '//section[@id="TOC"]/h3/a[@href="#fields"]' 'Fields'
|
||||||
// @has - '//section[@id="main-content"]/h2[@id="fields"]' 'Fields'
|
//@ has - '//section[@id="main-content"]/h2[@id="fields"]' 'Fields'
|
||||||
|
|
||||||
pub struct MyStruct {
|
pub struct MyStruct {
|
||||||
pub fields: i32,
|
pub fields: i32,
|
||||||
|
@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
//! This test case covers missing top TOC entries.
|
//! This test case covers missing top TOC entries.
|
||||||
|
|
||||||
// @has foo/index.html
|
//@ has foo/index.html
|
||||||
// User header
|
// User header
|
||||||
// @!has - '//section[@id="TOC"]/ul[@class="block top-toc"]' 'Basic link and emphasis'
|
//@ !has - '//section[@id="TOC"]/ul[@class="block top-toc"]' 'Basic link and emphasis'
|
||||||
|
@ -1 +1 @@
|
|||||||
<ul class="block variant"><li><a href="#variant.Shown">Shown</a></li></ul>
|
<ul class="block variant"><li><a href="#variant.Shown" title="Shown">Shown</a></li></ul>
|
Loading…
Reference in New Issue
Block a user