Skip to content

Commit de51bbe

Browse files
committed
Auto merge of #24116 - zaeleus:rustdoc-codespan, r=alexcrichton
Because the current style for `code` in rustdoc is to prewrap whitespace, code spans that are hard wrapped in the source documentation are prematurely wrapped when rendered in HTML. [For example][2], ``` /// ... /// type can be borrowed as multiple different types. In particular, `Vec<T>: /// Borrow<Vec<T>>` and `Vec<T>: Borrow<[T]>`. ``` renders as ![screen shot 2015-04-06 at 12 11 21](https://cloud.githubusercontent.com/assets/191331/7008216/2706b3b0-dc56-11e4-941e-1b0154fcbc5c.png) because "`Vec<T>: Borrow<Vec<T>>`" wraps to the next line in the source. CommonMark 0.18 [[1]] specifies "interior spaces and line endings are collapsed into single spaces" for code spans, which would actually prevent this issue, but hoedown does not currently conform to the CommonMark spec. The added span-level callback attempts to adhere to how whitespace is handled as described by CommonMark, fixing the issue of early, unintentional wrapping of code spans in rendered HTML. [1]: http://spec.commonmark.org/0.18/ [2]: https://doc.rust-lang.org/std/borrow/trait.Borrow.html
2 parents c52d46e + 46cc6e5 commit de51bbe

File tree

1 file changed

+46
-3
lines changed

1 file changed

+46
-3
lines changed

src/librustdoc/html/markdown.rs

+46-3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ type blockcodefn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
7272
type headerfn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
7373
libc::c_int, *mut libc::c_void);
7474

75+
type codespanfn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
76+
*mut libc::c_void);
77+
7578
type linkfn = extern "C" fn (*mut hoedown_buffer, *const hoedown_buffer,
7679
*const hoedown_buffer, *const hoedown_buffer,
7780
*mut libc::c_void) -> libc::c_int;
@@ -89,11 +92,12 @@ struct hoedown_renderer {
8992
blockhtml: Option<extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
9093
*mut libc::c_void)>,
9194
header: Option<headerfn>,
92-
9395
other_block_level_callbacks: [libc::size_t; 9],
9496

9597
/* span level callbacks - NULL or return 0 prints the span verbatim */
96-
other_span_level_callbacks_1: [libc::size_t; 9],
98+
autolink: libc::size_t, // unused
99+
codespan: Option<codespanfn>,
100+
other_span_level_callbacks_1: [libc::size_t; 7],
97101
link: Option<linkfn>,
98102
other_span_level_callbacks_2: [libc::size_t; 5],
99103
// hoedown will add `math` callback here, but we use an old version of it.
@@ -185,6 +189,16 @@ fn stripped_filtered_line<'a>(s: &'a str) -> Option<&'a str> {
185189
}
186190
}
187191

192+
/// Returns a new string with all consecutive whitespace collapsed into
193+
/// single spaces.
194+
///
195+
/// Any leading or trailing whitespace will be trimmed.
196+
fn collapse_whitespace(s: &str) -> String {
197+
s.split(|c: char| c.is_whitespace()).filter(|s| {
198+
!s.is_empty()
199+
}).collect::<Vec<_>>().connect(" ")
200+
}
201+
188202
thread_local!(static USED_HEADER_MAP: RefCell<HashMap<String, usize>> = {
189203
RefCell::new(HashMap::new())
190204
});
@@ -299,6 +313,20 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result {
299313

300314
reset_headers();
301315

316+
extern fn codespan(ob: *mut hoedown_buffer, text: *const hoedown_buffer, _: *mut libc::c_void) {
317+
let content = if text.is_null() {
318+
"".to_string()
319+
} else {
320+
let bytes = unsafe { (*text).as_bytes() };
321+
let s = str::from_utf8(bytes).unwrap();
322+
collapse_whitespace(s)
323+
};
324+
325+
let content = format!("<code>{}</code>", Escape(&content));
326+
let element = CString::new(content).unwrap();
327+
unsafe { hoedown_buffer_puts(ob, element.as_ptr()); }
328+
}
329+
302330
unsafe {
303331
let ob = hoedown_buffer_new(DEF_OUNIT);
304332
let renderer = hoedown_html_renderer_new(0, 0);
@@ -310,6 +338,7 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result {
310338
= &mut opaque as *mut _ as *mut libc::c_void;
311339
(*renderer).blockcode = Some(block);
312340
(*renderer).header = Some(header);
341+
(*renderer).codespan = Some(codespan);
313342

314343
let document = hoedown_document_new(renderer, HOEDOWN_EXTENSIONS, 16);
315344
hoedown_document_render(document, ob, s.as_ptr(),
@@ -523,7 +552,7 @@ pub fn plain_summary_line(md: &str) -> String {
523552
#[cfg(test)]
524553
mod tests {
525554
use super::{LangString, Markdown};
526-
use super::plain_summary_line;
555+
use super::{collapse_whitespace, plain_summary_line};
527556

528557
#[test]
529558
fn test_lang_string_parse() {
@@ -571,4 +600,18 @@ mod tests {
571600
t("# top header", "top header");
572601
t("## header", "header");
573602
}
603+
604+
#[test]
605+
fn test_collapse_whitespace() {
606+
fn t(input: &str, expected: &str) {
607+
let actual = collapse_whitespace(input);
608+
assert_eq!(actual, expected);
609+
}
610+
611+
t("foo", "foo");
612+
t("foo bar baz", "foo bar baz");
613+
t(" foo bar", "foo bar");
614+
t("\tfoo bar\nbaz", "foo bar baz");
615+
t("foo bar \n baz\t\tqux\n", "foo bar baz qux");
616+
}
574617
}

0 commit comments

Comments
 (0)