diff options
author | Titus Wormer <tituswormer@gmail.com> | 2022-07-29 18:22:59 +0200 |
---|---|---|
committer | Titus Wormer <tituswormer@gmail.com> | 2022-07-29 18:22:59 +0200 |
commit | 0eeff9148e327183e532752f46421a75506dd7a6 (patch) | |
tree | 4f0aed04f90aa759ce96a2e87aa719e7fa95c450 /src/compiler.rs | |
parent | 148ede7f0f42f0ccb1620b13d91f35d0c7d04c2f (diff) | |
download | markdown-rs-0eeff9148e327183e532752f46421a75506dd7a6.tar.gz markdown-rs-0eeff9148e327183e532752f46421a75506dd7a6.tar.bz2 markdown-rs-0eeff9148e327183e532752f46421a75506dd7a6.zip |
Refactor to improve states
* Remove custom kind wrappers, use plain bytes instead
* Remove `Into`s, use the explicit expected types instead
* Refactor to use `slice.as_str` in most places
* Remove unneeded unique check before adding a definition
* Use a shared CDATA prefix in constants
* Inline byte checks into matches
* Pass bytes back from parser instead of whole parse state
* Refactor to work more often on bytes
* Rename custom `size` to `len`
Diffstat (limited to 'src/compiler.rs')
-rw-r--r-- | src/compiler.rs | 540 |
1 files changed, 304 insertions, 236 deletions
diff --git a/src/compiler.rs b/src/compiler.rs index de76142..e0ab1e9 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,6 +1,5 @@ //! Turn events into a string of HTML. use crate::constant::{SAFE_PROTOCOL_HREF, SAFE_PROTOCOL_SRC}; -use crate::construct::character_reference::Kind as CharacterReferenceKind; use crate::token::Token; use crate::tokenizer::{Event, EventType}; use crate::util::normalize_identifier::normalize_identifier; @@ -68,14 +67,14 @@ struct CompileContext<'a> { pub code_flow_seen_data: Option<bool>, pub code_fenced_fences_count: Option<usize>, pub code_text_inside: bool, - pub character_reference_kind: Option<CharacterReferenceKind>, + pub character_reference_marker: Option<u8>, pub expect_first_item: Option<bool>, pub media_stack: Vec<Media>, pub definitions: Vec<(String, Definition)>, pub tight_stack: Vec<bool>, /// Fields used to influance the current compilation. pub slurp_one_line_ending: bool, - pub tags: bool, + pub in_image_alt: bool, pub encode_html: bool, pub last_was_tag: bool, /// Configuration @@ -104,13 +103,13 @@ impl<'a> CompileContext<'a> { code_flow_seen_data: None, code_fenced_fences_count: None, code_text_inside: false, - character_reference_kind: None, + character_reference_marker: None, expect_first_item: None, media_stack: vec![], definitions: vec![], tight_stack: vec![], slurp_one_line_ending: false, - tags: true, + in_image_alt: false, encode_html: true, last_was_tag: false, protocol_href: if options.allow_dangerous_protocol { @@ -140,8 +139,7 @@ impl<'a> CompileContext<'a> { self.buffers.pop().expect("Cannot resume w/o buffer") } - pub fn push<'x, S: Into<&'x str>>(&mut self, value: S) { - let value = value.into(); + pub fn push(&mut self, value: &str) { self.buffers .last_mut() .expect("Cannot push w/o buffer") @@ -149,17 +147,8 @@ impl<'a> CompileContext<'a> { self.last_was_tag = false; } - pub fn push_raw<'x, S: Into<&'x str>>(&mut self, value: S) { - let value = value.into(); - self.push(&*encode(value, self.encode_html)); - } - - pub fn tag<'x, S: Into<&'x str>>(&mut self, value: S) { - if self.tags { - let value = value.into(); - self.push(&*encode(value, false)); - self.last_was_tag = true; - } + pub fn push_raw(&mut self, value: &str) { + self.push(&encode(value, self.encode_html)); } /// Get the current buffer. @@ -172,7 +161,7 @@ impl<'a> CompileContext<'a> { /// Add a line ending. pub fn line_ending(&mut self) { let eol = self.line_ending_default.as_str().to_string(); - self.push(&*eol); + self.push(&eol); } /// Add a line ending if needed (as in, there’s no eol/eof already). @@ -210,7 +199,7 @@ pub fn compile(events: &[Event], bytes: &[u8], options: &Options) -> String { && (event.token_type == Token::BlankLineEnding || event.token_type == Token::LineEnding) { line_ending_inferred = Some(LineEnding::from_str( - &Slice::from_position(bytes, &Position::from_exit_event(events, index)).serialize(), + Slice::from_position(bytes, &Position::from_exit_event(events, index)).as_str(), )); break; } @@ -398,14 +387,16 @@ fn on_enter_buffer(context: &mut CompileContext) { fn on_enter_block_quote(context: &mut CompileContext) { context.tight_stack.push(false); context.line_ending_if_needed(); - context.tag("<blockquote>"); + context.push("<blockquote>"); + context.last_was_tag = true; } /// Handle [`Enter`][EventType::Enter]:[`CodeIndented`][Token::CodeIndented]. fn on_enter_code_indented(context: &mut CompileContext) { context.code_flow_seen_data = Some(false); context.line_ending_if_needed(); - context.tag("<pre><code>"); + context.push("<pre><code>"); + context.last_was_tag = true; } /// Handle [`Enter`][EventType::Enter]:[`CodeFenced`][Token::CodeFenced]. @@ -413,14 +404,18 @@ fn on_enter_code_fenced(context: &mut CompileContext) { context.code_flow_seen_data = Some(false); context.line_ending_if_needed(); // Note that no `>` is used, which is added later. - context.tag("<pre><code"); + context.push("<pre><code"); + context.last_was_tag = true; context.code_fenced_fences_count = Some(0); } /// Handle [`Enter`][EventType::Enter]:[`CodeText`][Token::CodeText]. fn on_enter_code_text(context: &mut CompileContext) { context.code_text_inside = true; - context.tag("<code>"); + if !context.in_image_alt { + context.push("<code>"); + context.last_was_tag = true; + } context.buffer(); } @@ -445,7 +440,10 @@ fn on_enter_definition_destination_string(context: &mut CompileContext) { /// Handle [`Enter`][EventType::Enter]:[`Emphasis`][Token::Emphasis]. fn on_enter_emphasis(context: &mut CompileContext) { - context.tag("<em>"); + if !context.in_image_alt { + context.push("<em>"); + context.last_was_tag = true; + } } /// Handle [`Enter`][EventType::Enter]:[`HtmlFlow`][Token::HtmlFlow]. @@ -473,7 +471,7 @@ fn on_enter_image(context: &mut CompileContext) { destination: None, title: None, }); - context.tags = false; // Disallow tags. + context.in_image_alt = true; // Disallow tags. } /// Handle [`Enter`][EventType::Enter]:[`Link`][Token::Link]. @@ -546,14 +544,12 @@ fn on_enter_list(context: &mut CompileContext) { context.tight_stack.push(!loose); context.line_ending_if_needed(); // Note: no `>`. - context.tag(&*format!( - "<{}", - if *token_type == Token::ListOrdered { - "ol" - } else { - "ul" - } - )); + context.push(if *token_type == Token::ListOrdered { + "<ol" + } else { + "<ul" + }); + context.last_was_tag = true; context.expect_first_item = Some(true); } @@ -562,11 +558,14 @@ fn on_enter_list_item_marker(context: &mut CompileContext) { let expect_first_item = context.expect_first_item.take().unwrap(); if expect_first_item { - context.tag(">"); + context.push(">"); + context.last_was_tag = true; } context.line_ending_if_needed(); - context.tag("<li>"); + + context.push("<li>"); + context.last_was_tag = true; context.expect_first_item = Some(false); // “Hack” to prevent a line ending from showing up if the item is empty. context.last_was_tag = false; @@ -578,15 +577,15 @@ fn on_enter_paragraph(context: &mut CompileContext) { if !tight { context.line_ending_if_needed(); - context.tag("<p>"); + context.push("<p>"); + context.last_was_tag = true; } } /// Handle [`Enter`][EventType::Enter]:[`Resource`][Token::Resource]. fn on_enter_resource(context: &mut CompileContext) { context.buffer(); // We can have line endings in the resource, ignore them. - let media = context.media_stack.last_mut().unwrap(); - media.destination = Some("".to_string()); + context.media_stack.last_mut().unwrap().destination = Some("".to_string()); } /// Handle [`Enter`][EventType::Enter]:[`ResourceDestinationString`][Token::ResourceDestinationString]. @@ -599,47 +598,67 @@ fn on_enter_resource_destination_string(context: &mut CompileContext) { /// Handle [`Enter`][EventType::Enter]:[`Strong`][Token::Strong]. fn on_enter_strong(context: &mut CompileContext) { - context.tag("<strong>"); + if !context.in_image_alt { + context.push("<strong>"); + context.last_was_tag = true; + } } /// Handle [`Exit`][EventType::Exit]:[`AutolinkEmail`][Token::AutolinkEmail]. fn on_exit_autolink_email(context: &mut CompileContext) { - let value = Slice::from_position( + let slice = Slice::from_position( context.bytes, &Position::from_exit_event(context.events, context.index), - ) - .serialize(); + ); + let value = slice.as_str(); - context.tag(&*format!( - "<a href=\"{}\">", - sanitize_uri( - format!("mailto:{}", value.as_str()).as_str(), - &context.protocol_href - ) - )); - context.push_raw(&*value); - context.tag("</a>"); + if !context.in_image_alt { + context.push("<a href=\""); + context.push(&sanitize_uri( + &format!("mailto:{}", value), + &context.protocol_href, + )); + context.push("\">"); + context.last_was_tag = true; + } + + context.push_raw(value); + + if !context.in_image_alt { + context.push("</a>"); + context.last_was_tag = true; + } } /// Handle [`Exit`][EventType::Exit]:[`AutolinkProtocol`][Token::AutolinkProtocol]. fn on_exit_autolink_protocol(context: &mut CompileContext) { - let value = Slice::from_position( + let slice = Slice::from_position( context.bytes, &Position::from_exit_event(context.events, context.index), - ) - .serialize(); + ); + let value = slice.as_str(); - context.tag(&*format!( - "<a href=\"{}\">", - sanitize_uri(value.as_str(), &context.protocol_href) - )); - context.push_raw(&*value); - context.tag("</a>"); + if !context.in_image_alt { + context.push("<a href=\""); + context.push(&sanitize_uri(value, &context.protocol_href)); + context.push("\">"); + context.last_was_tag = true; + } + + context.push_raw(value); + + if !context.in_image_alt { + context.push("</a>"); + context.last_was_tag = true; + } } /// Handle [`Exit`][EventType::Exit]:{[`HardBreakEscape`][Token::HardBreakEscape],[`HardBreakTrailing`][Token::HardBreakTrailing]}. fn on_exit_break(context: &mut CompileContext) { - context.tag("<br />"); + if !context.in_image_alt { + context.push("<br />"); + context.last_was_tag = true; + } } /// Handle [`Exit`][EventType::Exit]:[`BlankLineEnding`][Token::BlankLineEnding]. @@ -654,56 +673,58 @@ fn on_exit_block_quote(context: &mut CompileContext) { context.tight_stack.pop(); context.line_ending_if_needed(); context.slurp_one_line_ending = false; - context.tag("</blockquote>"); + context.push("</blockquote>"); + context.last_was_tag = true; } /// Handle [`Exit`][EventType::Exit]:[`CharacterReferenceMarker`][Token::CharacterReferenceMarker]. fn on_exit_character_reference_marker(context: &mut CompileContext) { - context.character_reference_kind = Some(CharacterReferenceKind::Named); + context.character_reference_marker = Some(b'&'); } /// Handle [`Exit`][EventType::Exit]:[`CharacterReferenceMarkerHexadecimal`][Token::CharacterReferenceMarkerHexadecimal]. fn on_exit_character_reference_marker_hexadecimal(context: &mut CompileContext) { - context.character_reference_kind = Some(CharacterReferenceKind::Hexadecimal); + context.character_reference_marker = Some(b'x'); } /// Handle [`Exit`][EventType::Exit]:[`CharacterReferenceMarkerNumeric`][Token::CharacterReferenceMarkerNumeric]. fn on_exit_character_reference_marker_numeric(context: &mut CompileContext) { - context.character_reference_kind = Some(CharacterReferenceKind::Decimal); + context.character_reference_marker = Some(b'#'); } /// Handle [`Exit`][EventType::Exit]:[`CharacterReferenceValue`][Token::CharacterReferenceValue]. fn on_exit_character_reference_value(context: &mut CompileContext) { - let kind = context - .character_reference_kind + let marker = context + .character_reference_marker .take() .expect("expected `character_reference_kind` to be set"); - let reference = Slice::from_position( + let slice = Slice::from_position( context.bytes, &Position::from_exit_event(context.events, context.index), - ) - .serialize(); + ); + let value = slice.as_str(); - let ref_string = reference.as_str(); - let value = match kind { - CharacterReferenceKind::Decimal => decode_numeric(ref_string, 10).to_string(), - CharacterReferenceKind::Hexadecimal => decode_numeric(ref_string, 16).to_string(), - CharacterReferenceKind::Named => decode_named(ref_string), + let value = match marker { + b'#' => decode_numeric(value, 10), + b'x' => decode_numeric(value, 16), + b'&' => decode_named(value), + _ => panic!("impossible"), }; - context.push_raw(&*value); + context.push_raw(&value); } /// Handle [`Exit`][EventType::Exit]:[`CodeFlowChunk`][Token::CodeFlowChunk]. fn on_exit_code_flow_chunk(context: &mut CompileContext) { - let value = Slice::from_position( - context.bytes, - &Position::from_exit_event(context.events, context.index), - ) - .serialize(); - context.code_flow_seen_data = Some(true); - context.push_raw(&*value); + context.push_raw( + &Slice::from_position( + context.bytes, + &Position::from_exit_event(context.events, context.index), + ) + // Must serialize to get virtual spaces. + .serialize(), + ); } /// Handle [`Exit`][EventType::Exit]:[`CodeFencedFence`][Token::CodeFencedFence]. @@ -715,7 +736,8 @@ fn on_exit_code_fenced_fence(context: &mut CompileContext) { }; if count == 0 { - context.tag(">"); + context.push(">"); + context.last_was_tag = true; context.slurp_one_line_ending = true; } @@ -725,7 +747,10 @@ fn on_exit_code_fenced_fence(context: &mut CompileContext) { /// Handle [`Exit`][EventType::Exit]:[`CodeFencedFenceInfo`][Token::CodeFencedFenceInfo]. fn on_exit_code_fenced_fence_info(context: &mut CompileContext) { let value = context.resume(); - context.tag(&*format!(" class=\"language-{}\"", value)); + context.push(" class=\"language-"); + context.push(&value); + context.push("\""); + context.last_was_tag = true; } /// Handle [`Exit`][EventType::Exit]:{[`CodeFenced`][Token::CodeFenced],[`CodeIndented`][Token::CodeIndented]}. @@ -752,7 +777,8 @@ fn on_exit_code_flow(context: &mut CompileContext) { context.line_ending_if_needed(); } - context.tag("</code></pre>"); + context.push("</code></pre>"); + context.last_was_tag = true; if let Some(count) = context.code_fenced_fences_count.take() { if count < 2 { @@ -781,12 +807,16 @@ fn on_exit_code_text(context: &mut CompileContext) { } context.code_text_inside = false; - context.push(&*if trim { + context.push(&if trim { result[1..(result.len() - 1)].to_string() } else { result }); - context.tag("</code>"); + + if !context.in_image_alt { + context.push("</code>"); + context.last_was_tag = true; + } } /// Handle [`Exit`][EventType::Exit]:*. @@ -798,72 +828,63 @@ fn on_exit_drop(context: &mut CompileContext) { /// Handle [`Exit`][EventType::Exit]:{[`CodeTextData`][Token::CodeTextData],[`Data`][Token::Data],[`CharacterEscapeValue`][Token::CharacterEscapeValue]}. fn on_exit_data(context: &mut CompileContext) { - let value = Slice::from_position( - context.bytes, - &Position::from_exit_event(context.events, context.index), - ) - .serialize(); - - // Just output it. - context.push_raw(&*value); + context.push_raw( + Slice::from_position( + context.bytes, + &Position::from_exit_event(context.events, context.index), + ) + .as_str(), + ); } /// Handle [`Exit`][EventType::Exit]:[`Definition`][Token::Definition]. fn on_exit_definition(context: &mut CompileContext) { - let definition = context.media_stack.pop().unwrap(); - let reference_id = normalize_identifier(&definition.reference_id.unwrap()); - let destination = definition.destination; - let title = definition.title; - context.resume(); - - let mut index = 0; - - while index < context.definitions.len() { - if context.definitions[index].0 == reference_id { - return; - } - - index += 1; - } - - context - .definitions - .push((reference_id, Definition { destination, title })); + let media = context.media_stack.pop().unwrap(); + let id = normalize_identifier(&media.reference_id.unwrap()); + + context.definitions.push(( + id, + Definition { + destination: media.destination, + title: media.title, + }, + )); } /// Handle [`Exit`][EventType::Exit]:[`DefinitionDestinationString`][Token::DefinitionDestinationString]. fn on_exit_definition_destination_string(context: &mut CompileContext) { let buf = context.resume(); - let definition = context.media_stack.last_mut().unwrap(); - definition.destination = Some(buf); + context.media_stack.last_mut().unwrap().destination = Some(buf); context.encode_html = true; } /// Handle [`Exit`][EventType::Exit]:[`DefinitionLabelString`][Token::DefinitionLabelString]. fn on_exit_definition_label_string(context: &mut CompileContext) { - let value = Slice::from_position( - context.bytes, - &Position::from_exit_event(context.events, context.index), - ) - .serialize(); - // Discard label, use the source content instead. context.resume(); - let definition = context.media_stack.last_mut().unwrap(); - definition.reference_id = Some(value); + context.media_stack.last_mut().unwrap().reference_id = Some( + // To do: lifetimes, reference bytes? + Slice::from_position( + context.bytes, + &Position::from_exit_event(context.events, context.index), + ) + .serialize(), + ); } /// Handle [`Exit`][EventType::Exit]:[`DefinitionTitleString`][Token::DefinitionTitleString]. fn on_exit_definition_title_string(context: &mut CompileContext) { let buf = context.resume(); - let definition = context.media_stack.last_mut().unwrap(); - definition.title = Some(buf); + context.media_stack.last_mut().unwrap().title = Some(buf); } /// Handle [`Exit`][EventType::Exit]:[`Strong`][Token::Emphasis]. fn on_exit_emphasis(context: &mut CompileContext) { - context.tag("</em>"); + if !context.in_image_alt { + context.push("</em>"); + context.last_was_tag = true; + } } /// Handle [`Exit`][EventType::Exit]:[`HeadingAtx`][Token::HeadingAtx]. @@ -873,7 +894,10 @@ fn on_exit_heading_atx(context: &mut CompileContext) { .take() .expect("`atx_opening_sequence_size` must be set in headings"); - context.tag(&*format!("</h{}>", rank)); + context.push("</h"); + context.push(&rank.to_string()); + context.push(">"); + context.last_was_tag = true; } /// Handle [`Exit`][EventType::Exit]:[`HeadingAtxSequence`][Token::HeadingAtxSequence]. @@ -884,17 +908,20 @@ fn on_exit_heading_atx_sequence(context: &mut CompileContext) { context.bytes, &Position::from_exit_event(context.events, context.index), ) - .size(); + .len(); context.line_ending_if_needed(); context.atx_opening_sequence_size = Some(rank); - context.tag(&*format!("<h{}>", rank)); + context.push("<h"); + context.push(&rank.to_string()); + context.push(">"); + context.last_was_tag = true; } } /// Handle [`Exit`][EventType::Exit]:[`HeadingAtxText`][Token::HeadingAtxText]. fn on_exit_heading_atx_text(context: &mut CompileContext) { let value = context.resume(); - context.push(&*value); + context.push(&value); } /// Handle [`Exit`][EventType::Exit]:[`HeadingSetextText`][Token::HeadingSetextText]. @@ -915,12 +942,18 @@ fn on_exit_heading_setext_underline(context: &mut CompileContext) { &Position::from_exit_event(context.events, context.index), ) .head(); - let level = if head == Some(b'-') { 2 } else { 1 }; + let rank = if head == Some(b'-') { "2" } else { "1" }; context.line_ending_if_needed(); - context.tag(&*format!("<h{}>", level)); - context.push(&*text); - context.tag(&*format!("</h{}>", level)); + context.push("<h"); + context.push(rank); + context.push(">"); + context.last_was_tag = true; + context.push(&text); + context.push("</h"); + context.push(rank); + context.push(">"); + context.last_was_tag = true; } /// Handle [`Exit`][EventType::Exit]:{[`HtmlFlow`][Token::HtmlFlow],[`HtmlText`][Token::HtmlText]}. @@ -930,32 +963,31 @@ fn on_exit_html(context: &mut CompileContext) { /// Handle [`Exit`][EventType::Exit]:{[`HtmlFlowData`][Token::HtmlFlowData],[`HtmlTextData`][Token::HtmlTextData]}. fn on_exit_html_data(context: &mut CompileContext) { - let value = Slice::from_position( - context.bytes, - &Position::from_exit_event(context.events, context.index), - ) - .serialize(); - - context.push_raw(&*value); + context.push_raw( + Slice::from_position( + context.bytes, + &Position::from_exit_event(context.events, context.index), + ) + .as_str(), + ); } /// Handle [`Exit`][EventType::Exit]:[`Label`][Token::Label]. fn on_exit_label(context: &mut CompileContext) { let buf = context.resume(); - let media = context.media_stack.last_mut().unwrap(); - media.label = Some(buf); + context.media_stack.last_mut().unwrap().label = Some(buf); } /// Handle [`Exit`][EventType::Exit]:[`LabelText`][Token::LabelText]. fn on_exit_label_text(context: &mut CompileContext) { - let value = Slice::from_position( - context.bytes, - &Position::from_exit_event(context.events, context.index), - ) - .serialize(); - - let media = context.media_stack.last_mut().unwrap(); - media.label_id = Some(value); + context.media_stack.last_mut().unwrap().label_id = Some( + // To do: lifetimes, reference bytes? + Slice::from_position( + context.bytes, + &Position::from_exit_event(context.events, context.index), + ) + .serialize(), + ); } /// Handle [`Exit`][EventType::Exit]:[`LineEnding`][Token::LineEnding]. @@ -965,26 +997,28 @@ fn on_exit_line_ending(context: &mut CompileContext) { } else if context.slurp_one_line_ending { context.slurp_one_line_ending = false; } else { - let value = Slice::from_position( - context.bytes, - &Position::from_exit_event(context.events, context.index), - ) - .serialize(); - - context.push_raw(&*value); + context.push_raw( + Slice::from_position( + context.bytes, + &Position::from_exit_event(context.events, context.index), + ) + .as_str(), + ); } } /// Handle [`Exit`][EventType::Exit]:{[`ListOrdered`][Token::ListOrdered],[`ListUnordered`][Token::ListUnordered]}. fn on_exit_list(context: &mut CompileContext) { - let tag_name = if context.events[context.index].token_type == Token::ListOrdered { - "ol" - } else { - "ul" - }; context.tight_stack.pop(); context.line_ending(); - context.tag(&*format!("</{}>", tag_name)); + context.push( + if context.events[context.index].token_type == Token::ListOrdered { + "</ol>" + } else { + "</ul>" + }, + ); + context.last_was_tag = true; } /// Handle [`Exit`][EventType::Exit]:[`ListItem`][Token::ListItem]. @@ -1010,7 +1044,8 @@ fn on_exit_list_item(context: &mut CompileContext) { context.line_ending_if_needed(); } - context.tag("</li>"); + context.push("</li>"); + context.last_was_tag = true; } /// Handle [`Exit`][EventType::Exit]:[`ListItemValue`][Token::ListItemValue]. @@ -1018,17 +1053,17 @@ fn on_exit_list_item_value(context: &mut CompileContext) { let expect_first_item = context.expect_first_item.unwrap(); if expect_first_item { - let value = Slice::from_position( + let slice = Slice::from_position( context.bytes, &Position::from_exit_event(context.events, context.index), - ) - .serialize(); - let value = value.parse::<u32>().ok().unwrap(); + ); + let value = slice.as_str().parse::<u32>().ok().unwrap(); if value != 1 { - context.tag(" start=\""); - context.tag(&*value.to_string()); - context.tag("\""); + context.push(" start=\""); + context.push(&value.to_string()); + context.push("\""); + context.last_was_tag = true; } } } @@ -1048,68 +1083,98 @@ fn on_exit_media(context: &mut CompileContext) { index += 1; } - context.tags = !is_in_image; + context.in_image_alt = is_in_image; let media = context.media_stack.pop().unwrap(); + let label = media.label.unwrap(); + let in_image_alt = context.in_image_alt; let id = media .reference_id .or(media.label_id) .map(|id| normalize_identifier(&id)); - let label = media.label.unwrap(); - let mut definition = None; - if let Some(id) = id { - let mut index = 0; + let definition_index = if media.destination.is_none() { + id.and_then(|id| { + let mut index = 0; - while index < context.definitions.len() { - if context.definitions[index].0 == id { - definition = Some(&context.definitions[index].1); - break; - } + while index < context.definitions.len() { + if context.definitions[index].0 == id { + return Some(index); + } - index += 1; - } - } + index += 1; + } - let destination = if media.destination.is_some() { - &media.destination + None + }) } else { - &definition.unwrap().destination - }; - let title = if media.destination.is_some() { - &media.title - } else { - &definition.unwrap().title + None }; - let destination = if let Some(destination) = destination { - destination - } else { - "" - }; + if !in_image_alt { + if media.image { + context.push("<img src=\""); + } else { + context.push("<a href=\""); + }; - let title = if let Some(title) = title { - format!(" title=\"{}\"", title) - } else { - "".to_string() - }; + let destination = if let Some(index) = definition_index { + context.definitions[index].1.destination.as_ref() + } else { + media.destination.as_ref() + }; + + if let Some(destination) = destination { + context.push(&sanitize_uri( + destination, + if media.image { + &context.protocol_src + } else { + &context.protocol_href + }, + )); + } + + if media.image { + context.push("\" alt=\""); + }; + } if media.image { - context.tag(&*format!( - "<img src=\"{}\" alt=\"", - sanitize_uri(destination, &context.protocol_src), - )); - context.push(&*label); - context.tag(&*format!("\"{} />", title)); - } else { - context.tag(&*format!( - "<a href=\"{}\"{}>", - sanitize_uri(destination, &context.protocol_href), - title, - )); - context.push(&*label); - context.tag("</a>"); - }; + context.push(&label); + } + + if !in_image_alt { + context.push("\""); + + let title = if let Some(index) = definition_index { + context.definitions[index].1.title.clone() + } else { + media.title + }; + + if let Some(title) = title { + context.push(" title=\""); + context.push(&title); + context.push("\""); + }; + + if media.image { + context.push(" /"); + } + + context.push(">"); + context.last_was_tag = true; + } + + if !media.image { + context.push(&label); + + if !in_image_alt { + context.push("</a>"); + context.last_was_tag = true; + } + } } /// Handle [`Exit`][EventType::Exit]:[`Paragraph`][Token::Paragraph]. @@ -1119,46 +1184,49 @@ fn on_exit_paragraph(context: &mut CompileContext) { if *tight { context.slurp_one_line_ending = true; } else { - context.tag("</p>"); + context.push("</p>"); + context.last_was_tag = true; } } /// Handle [`Exit`][EventType::Exit]:[`ReferenceString`][Token::ReferenceString]. fn on_exit_reference_string(context: &mut CompileContext) { - let value = Slice::from_position( - context.bytes, - &Position::from_exit_event(context.events, context.index), - ) - .serialize(); - // Drop stuff. context.resume(); - let media = context.media_stack.last_mut().unwrap(); - media.reference_id = Some(value); + // To do: lifetimes, reference bytes. + context.media_stack.last_mut().unwrap().reference_id = Some( + Slice::from_position( + context.bytes, + &Position::from_exit_event(context.events, context.index), + ) + .serialize(), + ); } /// Handle [`Exit`][EventType::Exit]:[`ResourceDestinationString`][Token::ResourceDestinationString]. fn on_exit_resource_destination_string(context: &mut CompileContext) { let buf = context.resume(); - let media = context.media_stack.last_mut().unwrap(); - media.destination = Some(buf); + context.media_stack.last_mut().unwrap().destination = Some(buf); context.encode_html = true; } /// Handle [`Exit`][EventType::Exit]:[`ResourceTitleString`][Token::ResourceTitleString]. fn on_exit_resource_title_string(context: &mut CompileContext) { let buf = context.resume(); - let media = context.media_stack.last_mut().unwrap(); - media.title = Some(buf); + context.media_stack.last_mut().unwrap().title = Some(buf); } /// Handle [`Exit`][EventType::Exit]:[`Strong`][Token::Strong]. fn on_exit_strong(context: &mut CompileContext) { - context.tag("</strong>"); + if !context.in_image_alt { + context.push("</strong>"); + context.last_was_tag = true; + } } /// Handle [`Exit`][EventType::Exit]:[`ThematicBreak`][Token::ThematicBreak]. fn on_exit_thematic_break(context: &mut CompileContext) { context.line_ending_if_needed(); - context.tag("<hr />"); + context.push("<hr />"); + context.last_was_tag = true; } |