mirror of
https://github.com/rust-lang/mdBook.git
synced 2025-12-27 10:16:09 -05:00
Merge pull request #2927 from ehuss/fix-unbalanced-html-in-header
Handle unclosed HTML tags inside a markdown element
This commit is contained in:
@@ -306,25 +306,7 @@ where
|
||||
trace!("event={event:?}");
|
||||
match event {
|
||||
Event::Start(tag) => self.start_tag(tag),
|
||||
Event::End(tag) => {
|
||||
// TODO: This should validate that the event stack is
|
||||
// properly synchronized with the tag stack.
|
||||
self.pop();
|
||||
match tag {
|
||||
TagEnd::TableHead => {
|
||||
self.table_state = TableState::Body;
|
||||
self.push(Node::Element(Element::new("tbody")));
|
||||
}
|
||||
TagEnd::TableCell => {
|
||||
self.table_cell_index += 1;
|
||||
}
|
||||
TagEnd::Table => {
|
||||
// Pop tbody or thead
|
||||
self.pop();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Event::End(tag) => self.end_tag(tag),
|
||||
Event::Text(text) => {
|
||||
self.append_text(text.into_tendril());
|
||||
}
|
||||
@@ -600,6 +582,46 @@ where
|
||||
self.push(Node::Element(element));
|
||||
}
|
||||
|
||||
fn end_tag(&mut self, tag: TagEnd) {
|
||||
// TODO: This should validate that the event stack is properly
|
||||
// synchronized with the tag stack. That, would likely require keeping
|
||||
// a parallel "expected end tag" with the tag stack, since mapping a
|
||||
// pulldown-cmark event tag to an HTML tag isn't always clear.
|
||||
//
|
||||
// Check for unclosed HTML tags when exiting a markdown event.
|
||||
while let Some(node_id) = self.tag_stack.last() {
|
||||
let node = self.tree.get(*node_id).unwrap().value();
|
||||
let Node::Element(el) = node else {
|
||||
break;
|
||||
};
|
||||
if !el.was_raw {
|
||||
break;
|
||||
}
|
||||
warn!(
|
||||
"unclosed HTML tag `<{}>` found in `{}` while exiting {tag:?}\n\
|
||||
HTML tags must be closed before exiting a markdown element.",
|
||||
el.name.local,
|
||||
self.options.path.display(),
|
||||
);
|
||||
self.pop();
|
||||
}
|
||||
self.pop();
|
||||
match tag {
|
||||
TagEnd::TableHead => {
|
||||
self.table_state = TableState::Body;
|
||||
self.push(Node::Element(Element::new("tbody")));
|
||||
}
|
||||
TagEnd::TableCell => {
|
||||
self.table_cell_index += 1;
|
||||
}
|
||||
TagEnd::Table => {
|
||||
// Pop tbody or thead
|
||||
self.pop();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given some HTML, parse it into [`Node`] elements and append them to
|
||||
/// the current node.
|
||||
fn append_html(&mut self, html: &str) {
|
||||
|
||||
@@ -283,3 +283,24 @@ Check that the HTML tags are properly balanced.
|
||||
})
|
||||
.check_main_file("book/chapter_1.html", str!["<div>x<span>foo</span></div>"]);
|
||||
}
|
||||
|
||||
// Test for bug with unbalanced HTML handling in the heading.
|
||||
#[test]
|
||||
fn heading_with_unbalanced_html() {
|
||||
BookTest::init(|_| {})
|
||||
.change_file("src/chapter_1.md", "### Option<T>")
|
||||
.run("build", |cmd| {
|
||||
cmd.expect_stderr(str![[r#"
|
||||
INFO Book building has started
|
||||
INFO Running the html backend
|
||||
WARN unclosed HTML tag `<t>` found in `chapter_1.md` while exiting Heading(H3)
|
||||
HTML tags must be closed before exiting a markdown element.
|
||||
INFO HTML book written to `[ROOT]/book`
|
||||
|
||||
"#]]);
|
||||
})
|
||||
.check_main_file(
|
||||
"book/chapter_1.html",
|
||||
str![[r##"<h3 id="option"><a class="header" href="#option">Option<t></t></a></h3>"##]],
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user