Skip to content

Commit 69e3f07

Browse files
committed
Emit header id and class attributes
1 parent 1b25850 commit 69e3f07

File tree

2 files changed

+59
-5
lines changed

2 files changed

+59
-5
lines changed

src/html/markdown.rs

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
//! ```
2626
2727
use rustc::session::config::get_unstable_features_setting;
28+
use std::ascii::AsciiExt;
2829
use std::borrow::Cow;
2930
use std::cell::RefCell;
3031
use std::fmt::{self, Write};
@@ -34,7 +35,7 @@ use cmark::{Parser, Event as CmEvent, Tag};
3435
use hamlet::Token as HmToken;
3536
use cmark_hamlet;
3637

37-
//use html::render::derive_id;
38+
use html::render::derive_id;
3839
//use html::toc::TocBuilder;
3940
use html::highlight;
4041
use html::escape::Escape;
@@ -76,13 +77,64 @@ thread_local!(pub static PLAYGROUND_KRATE: RefCell<Option<Option<String>>> = {
7677
RefCell::new(None)
7778
});
7879

80+
fn is_header(tag_name: &str) -> bool {
81+
if tag_name.len() == 2 {
82+
tag_name.char_indices().all(|(i, c)| {
83+
(i == 0 && c == 'h') || (i == 1 && c >= '1' && c <= '6')
84+
})
85+
} else {
86+
false
87+
}
88+
}
89+
90+
fn id_from_text(text: &str) -> String {
91+
let id = text.chars().filter_map(|c| {
92+
if c.is_alphanumeric() || c == '-' || c == '_' {
93+
if c.is_ascii() {
94+
Some(c.to_ascii_lowercase())
95+
} else {
96+
Some(c)
97+
}
98+
} else if c.is_whitespace() && c.is_ascii() {
99+
Some('-')
100+
} else {
101+
None
102+
}
103+
}).collect::<String>();
104+
derive_id(id)
105+
}
106+
79107
pub fn render(w: &mut fmt::Formatter, md: &str, _: bool) -> fmt::Result {
80108
let mut rust_block = false;
109+
let mut header = false;
110+
let mut header_inner_buf = String::from("");
111+
let mut header_id_buf = String::from("");
81112
for hm_tok in cmark_hamlet::Adapter::new(Parser::new(md), true) {
82113
match hm_tok {
83-
HmToken::StartTag { ref name, .. } |
84-
HmToken::EndTag { ref name } if rust_block && name.as_ref() == "code" => (),
85-
HmToken::StartTag { name: Cow::Borrowed("pre"), ref attrs, .. } => {
114+
HmToken::StartTag { ref name, .. } if is_header(name.as_ref()) => {
115+
header = true;
116+
}
117+
HmToken::EndTag { ref name } if is_header(name.as_ref()) => {
118+
let id = id_from_text(&*header_id_buf);
119+
try!(write!(w,
120+
"{start}<a href=\"#{id}\">{inner}</a>{end}",
121+
start = HmToken::start_tag(name.as_ref(),
122+
attrs!(id = &*id,
123+
class = "section-header")),
124+
id = &*id,
125+
inner = header_inner_buf,
126+
end = hm_tok));
127+
header = false;
128+
header_id_buf.truncate(0);
129+
header_inner_buf.truncate(0);
130+
}
131+
_ if header => {
132+
if let HmToken::Text(ref text) = hm_tok {
133+
try!(write!(header_id_buf, "{}", text));
134+
}
135+
try!(write!(header_inner_buf, "{}", hm_tok));
136+
}
137+
HmToken::StartTag { ref name, ref attrs, .. } if name.as_ref() == "pre" => {
86138
let is_rust = attrs.get("data-lang")
87139
.map(|lang| LangString::parse(lang).rust);
88140
if let Some(true) = is_rust {
@@ -91,6 +143,8 @@ pub fn render(w: &mut fmt::Formatter, md: &str, _: bool) -> fmt::Result {
91143
try!(write!(w, "{}", hm_tok));
92144
}
93145
}
146+
HmToken::StartTag { ref name, .. } |
147+
HmToken::EndTag { ref name } if rust_block && name.as_ref() == "code" => (),
94148
HmToken::Text(ref text) if rust_block => {
95149
let code = text.as_ref();
96150
// insert newline to clearly separate it from the

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ extern crate rustc_unicode;
4444
#[macro_use] extern crate log;
4545

4646
extern crate pulldown_cmark as cmark;
47-
extern crate hamlet;
47+
#[macro_use] extern crate hamlet;
4848
extern crate cmark_hamlet;
4949

5050
extern crate serialize as rustc_serialize; // used by deriving

0 commit comments

Comments
 (0)