Skip to content

Commit

Permalink
Add Span::lines_span() (#682)
Browse files Browse the repository at this point in the history
* Span::lines_span() and refactor span::Lines
  • Loading branch information
Alextopher committed Aug 4, 2022
1 parent 7b900c9 commit 9a93978
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 13 deletions.
2 changes: 1 addition & 1 deletion pest/src/lib.rs
Expand Up @@ -77,7 +77,7 @@ extern crate serde_json;
pub use crate::parser::Parser;
pub use crate::parser_state::{state, Atomicity, Lookahead, MatchDir, ParseResult, ParserState};
pub use crate::position::Position;
pub use crate::span::{Lines, Span};
pub use crate::span::{Lines, LinesSpan, Span};
pub use crate::token::Token;
use core::fmt::Debug;
use core::hash::Hash;
Expand Down
89 changes: 77 additions & 12 deletions pest/src/span.rs
Expand Up @@ -212,7 +212,7 @@ impl<'i> Span<'i> {
&self.input[self.start..self.end]
}

/// Iterates over all lines (partially) covered by this span.
/// Iterates over all lines (partially) covered by this span. Yielding a `&str` for each line.
///
/// # Examples
///
Expand All @@ -232,6 +232,30 @@ impl<'i> Span<'i> {
#[inline]
pub fn lines(&self) -> Lines {
Lines {
inner: self.lines_span(),
}
}

/// Iterates over all lines (partially) covered by this span. Yielding a `Span` for each line.
///
/// # Examples
///
/// ```
/// # use pest;
/// # use pest::Span;
/// # #[allow(non_camel_case_types)]
/// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
/// enum Rule {}
///
/// let input = "a\nb\nc";
/// let mut state: Box<pest::ParserState<Rule>> = pest::ParserState::new(input).skip(2).unwrap();
/// let start_pos = state.position().clone();
/// state = state.match_string("b\nc").unwrap();
/// let span = start_pos.span(&state.position().clone());
/// assert_eq!(span.lines_span().collect::<Vec<_>>(), vec![Span::new(input, 2, 4).unwrap(), Span::new(input, 4, 5).unwrap()]);
/// ```
pub fn lines_span(&self) -> LinesSpan {
LinesSpan {
span: self,
pos: self.start,
}
Expand Down Expand Up @@ -264,29 +288,47 @@ impl<'i> Hash for Span<'i> {
}
}

/// Line iterator for Spans, created by [`Span::lines()`].
/// Line iterator for Spans, created by [`Span::lines_span()`].
///
/// Iterates all lines that are at least partially covered by the span.
/// Iterates all lines that are at least _partially_ covered by the span. Yielding a `Span` for each.
///
/// [`Span::lines()`]: struct.Span.html#method.lines
pub struct Lines<'i> {
/// [`Span::lines_span()`]: struct.Span.html#method.lines_span
pub struct LinesSpan<'i> {
span: &'i Span<'i>,
pos: usize,
}

impl<'i> Iterator for Lines<'i> {
type Item = &'i str;
fn next(&mut self) -> Option<&'i str> {
impl<'i> Iterator for LinesSpan<'i> {
type Item = Span<'i>;
fn next(&mut self) -> Option<Self::Item> {
if self.pos > self.span.end {
return None;
}
let pos = position::Position::new(self.span.input, self.pos)?;
if pos.at_end() {
return None;
}
let line = pos.line_of();

let line_start = pos.find_line_start();
self.pos = pos.find_line_end();
Some(line)

Span::new(self.span.input, line_start, self.pos)
}
}

/// Line iterator for Spans, created by [`Span::lines()`].
///
/// Iterates all lines that are at least _partially_ covered by the span. Yielding a `&str` for each.
///
/// [`Span::lines()`]: struct.Span.html#method.lines
pub struct Lines<'i> {
inner: LinesSpan<'i>,
}

impl<'i> Iterator for Lines<'i> {
type Item = &'i str;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|span| span.as_str())
}
}

Expand Down Expand Up @@ -364,10 +406,12 @@ mod tests {
let input = "abc\ndef\nghi";
let span = Span::new(input, 1, 7).unwrap();
let lines: Vec<_> = span.lines().collect();
//println!("{:?}", lines);
let lines_span: Vec<_> = span.lines_span().map(|span| span.as_str()).collect();

assert_eq!(lines.len(), 2);
assert_eq!(lines[0], "abc\n".to_owned());
assert_eq!(lines[1], "def\n".to_owned());
assert_eq!(lines, lines_span) // Verify parity with lines_span()
}

#[test]
Expand All @@ -377,9 +421,30 @@ mod tests {
assert!(span.end_pos().at_end());
assert_eq!(span.end(), 11);
let lines: Vec<_> = span.lines().collect();
//println!("{:?}", lines);
let lines_span: Vec<_> = span.lines_span().map(|span| span.as_str()).collect();

assert_eq!(lines.len(), 2);
assert_eq!(lines[0], "def\n".to_owned());
assert_eq!(lines[1], "ghi".to_owned());
assert_eq!(lines, lines_span) // Verify parity with lines_span()
}

#[test]
fn lines_span() {
let input = "abc\ndef\nghi";
let span = Span::new(input, 1, 7).unwrap();
let lines_span: Vec<_> = span.lines_span().collect();
let lines: Vec<_> = span.lines().collect();

assert_eq!(lines_span.len(), 2);
assert_eq!(lines_span[0], Span::new(input, 0, 4).unwrap());
assert_eq!(lines_span[1], Span::new(input, 4, 8).unwrap());
assert_eq!(
lines_span
.iter()
.map(|span| span.as_str())
.collect::<Vec<_>>(),
lines
);
}
}

0 comments on commit 9a93978

Please sign in to comment.