From fb99fd2b3987db14e0a9f37ba55a38bcbd64616a Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 29 Oct 2023 10:32:05 -0400 Subject: [PATCH] Get the source code lines. --- src/intermediate/src_block.rs | 11 +-- src/intermediate/util.rs | 126 ++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 4 deletions(-) diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index 9fa4a0d..9577179 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -1,6 +1,7 @@ use crate::error::CustomError; use super::registry::Registry; +use super::util::GetFullLines; #[derive(Debug)] pub(crate) struct ISrcBlock { @@ -12,9 +13,11 @@ impl ISrcBlock { registry: &mut Registry<'parse>, original: &organic::types::SrcBlock<'parse>, ) -> Result { - // let contents = original.contents. - Ok(ISrcBlock { - lines: Vec::new(), // TODO - }) + 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()); + } + } +}