diff --git a/default_environment/templates/html/inline_source_block.dust b/default_environment/templates/html/inline_source_block.dust index 7ab64e2..85ced05 100644 --- a/default_environment/templates/html/inline_source_block.dust +++ b/default_environment/templates/html/inline_source_block.dust @@ -1 +1 @@ -!!!!!!!! inline_source_block +{.value} diff --git a/default_environment/templates/html/src_block.dust b/default_environment/templates/html/src_block.dust index 02185e2..22e7f33 100644 --- a/default_environment/templates/html/src_block.dust +++ b/default_environment/templates/html/src_block.dust @@ -1 +1,9 @@ -!!!!!!!! src_block + + + {#.lines} + + + + {/.lines} + +
{.}
diff --git a/src/context/inline_source_block.rs b/src/context/inline_source_block.rs index 9972c95..8aa3ced 100644 --- a/src/context/inline_source_block.rs +++ b/src/context/inline_source_block.rs @@ -9,15 +9,19 @@ use crate::intermediate::IInlineSourceBlock; #[derive(Debug, Serialize)] #[serde(tag = "type")] #[serde(rename = "inline_source_block")] -pub(crate) struct RenderInlineSourceBlock {} +pub(crate) struct RenderInlineSourceBlock { + value: String, +} impl RenderInlineSourceBlock { pub(crate) fn new( config: &Config, output_directory: &Path, output_file: &Path, - comment: &IInlineSourceBlock, + original: &IInlineSourceBlock, ) -> Result { - Ok(RenderInlineSourceBlock {}) + Ok(RenderInlineSourceBlock { + value: original.value.clone(), + }) } } diff --git a/src/context/src_block.rs b/src/context/src_block.rs index 0c19570..6c06124 100644 --- a/src/context/src_block.rs +++ b/src/context/src_block.rs @@ -9,7 +9,9 @@ use crate::intermediate::ISrcBlock; #[derive(Debug, Serialize)] #[serde(tag = "type")] #[serde(rename = "src_block")] -pub(crate) struct RenderSrcBlock {} +pub(crate) struct RenderSrcBlock { + lines: Vec, +} impl RenderSrcBlock { pub(crate) fn new( @@ -18,6 +20,8 @@ impl RenderSrcBlock { output_file: &Path, original: &ISrcBlock, ) -> Result { - Ok(RenderSrcBlock {}) + Ok(RenderSrcBlock { + lines: original.lines.clone(), + }) } } diff --git a/src/intermediate/inline_source_block.rs b/src/intermediate/inline_source_block.rs index 5a90e29..29071b3 100644 --- a/src/intermediate/inline_source_block.rs +++ b/src/intermediate/inline_source_block.rs @@ -3,13 +3,17 @@ use crate::error::CustomError; use super::registry::Registry; #[derive(Debug)] -pub(crate) struct IInlineSourceBlock {} +pub(crate) struct IInlineSourceBlock { + pub(crate) value: String, +} impl IInlineSourceBlock { pub(crate) async fn new<'parse>( registry: &mut Registry<'parse>, original: &organic::types::InlineSourceBlock<'parse>, ) -> Result { - Ok(IInlineSourceBlock {}) + Ok(IInlineSourceBlock { + value: original.value.to_owned(), + }) } } diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index ed90cd0..9577179 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -1,15 +1,23 @@ use crate::error::CustomError; use super::registry::Registry; +use super::util::GetFullLines; #[derive(Debug)] -pub(crate) struct ISrcBlock {} +pub(crate) struct ISrcBlock { + pub(crate) lines: Vec, +} impl ISrcBlock { pub(crate) async fn new<'parse>( registry: &mut Registry<'parse>, original: &organic::types::SrcBlock<'parse>, ) -> Result { - Ok(ISrcBlock {}) + let lines = original + .contents + .full_lines() + .map(|s| s.to_owned()) + .collect(); + Ok(ISrcBlock { lines }) } } diff --git a/src/intermediate/util.rs b/src/intermediate/util.rs index b482b98..f2718dc 100644 --- a/src/intermediate/util.rs +++ b/src/intermediate/util.rs @@ -46,3 +46,129 @@ enum CoalesceWhitespace { Normal, HasWhitespace { in_whitespace: bool, ret: String }, } + +pub(crate) struct FullLinesIterator<'i> { + inner: Option<&'i str>, +} + +impl<'i> Iterator for FullLinesIterator<'i> { + type Item = &'i str; + + fn next(&mut self) -> Option { + if let Some(inner) = self.inner { + for (i, c) in inner.char_indices() { + if c == '\n' { + let (current_line, remainder) = inner.split_at(i + 1); + self.inner = Some(remainder); + return Some(current_line); + } + } + if inner.is_empty() { + None + } else { + let final_unterminated_line = Some(inner); + self.inner = None; + final_unterminated_line + } + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + if let Some(inner) = self.inner { + let line_count = { + inner.bytes().filter(|b| *b == b'\n').count() + + (if inner.ends_with('\n') { 0 } else { 1 }) + }; + (line_count, Some(line_count)) + } else { + (0, Some(0)) + } + } +} + +impl ExactSizeIterator for FullLinesIterator<'_> {} + +pub(crate) trait GetFullLines { + fn full_lines(&self) -> FullLinesIterator<'_>; +} + +impl<'s, S: AsRef> GetFullLines for S { + fn full_lines(&self) -> FullLinesIterator<'_> { + let inner = self.as_ref(); + FullLinesIterator { + inner: if inner.is_empty() { None } else { Some(inner) }, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_full_lines_iterator() { + { + let input = "foo\nbar\nbaz"; + assert_eq!(input.lines().count(), input.full_lines().count()); + assert_eq!(input.lines().count(), input.full_lines().len()); + assert_eq!(input.lines().collect::>(), vec!["foo", "bar", "baz"]); + assert_eq!( + input.full_lines().collect::>(), + vec!["foo\n", "bar\n", "baz"] + ); + } + + { + // Trailing newline + let input = "foo\nbar\nbaz\n"; + assert_eq!(input.lines().count(), input.full_lines().count()); + assert_eq!(input.lines().count(), input.full_lines().len()); + assert_eq!(input.lines().collect::>(), vec!["foo", "bar", "baz"]); + assert_eq!( + input.full_lines().collect::>(), + vec!["foo\n", "bar\n", "baz\n"] + ); + } + + { + // Leading newline + let input = "\nfoo\nbar\nbaz"; + assert_eq!(input.lines().count(), input.full_lines().count()); + assert_eq!(input.lines().count(), input.full_lines().len()); + assert_eq!( + input.lines().collect::>(), + vec!["", "foo", "bar", "baz"] + ); + assert_eq!( + input.full_lines().collect::>(), + vec!["\n", "foo\n", "bar\n", "baz"] + ); + } + + { + // Double newline + let input = "foo\nbar\n\nbaz"; + assert_eq!(input.lines().count(), input.full_lines().count()); + assert_eq!(input.lines().count(), input.full_lines().len()); + assert_eq!( + input.lines().collect::>(), + vec!["foo", "bar", "", "baz"] + ); + assert_eq!( + input.full_lines().collect::>(), + vec!["foo\n", "bar\n", "\n", "baz"] + ); + } + + { + // Empty + let input = ""; + assert_eq!(input.lines().count(), input.full_lines().count()); + assert_eq!(input.lines().count(), input.full_lines().len()); + assert_eq!(input.lines().collect::>(), Vec::<&str>::new()); + assert_eq!(input.full_lines().collect::>(), Vec::<&str>::new()); + } + } +}