diff options
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/lib.rs | 2 | ||||
| -rw-r--r-- | src/spans.rs | 21 | ||||
| -rw-r--r-- | tests/span-tests/demo.html | 1 | ||||
| -rw-r--r-- | tests/span-tests/demo.out | 7 | ||||
| -rw-r--r-- | tests/test_spans.rs | 63 | 
6 files changed, 88 insertions, 7 deletions
| @@ -11,6 +11,7 @@ version = "0.2.1"  include = ["src/**/*", "LICENSE", "README.md"]  [dev-dependencies] +codespan-reporting = "0.11.1"  pretty_assertions = "1.0.0"  serde = { version = "1.0.130", features = ["derive"] }  serde_json = "1.0.71" @@ -9,7 +9,7 @@ mod error;  mod machine;  mod never;  mod reader; -mod spans; +pub mod spans;  mod tokenizer;  mod utils; diff --git a/src/spans.rs b/src/spans.rs index 9d02940..72b30c0 100644 --- a/src/spans.rs +++ b/src/spans.rs @@ -1,3 +1,4 @@ +//! Source code spans.  use std::{      collections::{BTreeSet, VecDeque},      marker::PhantomData, @@ -8,13 +9,18 @@ use crate::{Doctype, Emitter, EndTag, Error, Reader, StartTag, Token};  type Span = std::ops::Range<usize>; +/// A trait to be implemented by readers that track their own position.  pub trait GetPos { +    /// Returns the byte index of the current position.      fn get_pos(&self) -> usize;  } -struct PosTracker<R> { -    reader: R, -    position: usize, +/// Wraps a [`Reader`] so that it implements [`GetPos`]. +pub struct PosTracker<R> { +    /// The wrapped reader. +    pub reader: R, +    /// The current position. +    pub position: usize,  }  impl<R> GetPos for PosTracker<R> { @@ -145,12 +151,15 @@ impl<R: GetPos> Emitter<R> for SpanEmitter<R> {      fn init_start_tag(&mut self, reader: &R) {          self.current_token = Some(Token::StartTag(StartTag { -            name_span: reader.get_pos()..reader.get_pos(), +            name_span: reader.get_pos() - 1..reader.get_pos() - 1,              ..Default::default()          }));      } -    fn init_end_tag(&mut self, _reader: &R) { -        self.current_token = Some(Token::EndTag(Default::default())); +    fn init_end_tag(&mut self, reader: &R) { +        self.current_token = Some(Token::EndTag(EndTag { +            name_span: reader.get_pos() - 1..reader.get_pos() - 1, +            ..Default::default() +        }));          self.seen_attributes.clear();      } diff --git a/tests/span-tests/demo.html b/tests/span-tests/demo.html new file mode 100644 index 0000000..900f74c --- /dev/null +++ b/tests/span-tests/demo.html @@ -0,0 +1 @@ +this is a tag: <h1>test</h1> diff --git a/tests/span-tests/demo.out b/tests/span-tests/demo.out new file mode 100644 index 0000000..98d6766 --- /dev/null +++ b/tests/span-tests/demo.out @@ -0,0 +1,7 @@ +note: +  ┌─ test.html:1:17 +  │ +1 │ this is a tag: <h1>test</h1> +  │                 ^^       ^^ end tag +  │                 │ +  │                 start tag diff --git a/tests/test_spans.rs b/tests/test_spans.rs new file mode 100644 index 0000000..a3d1c96 --- /dev/null +++ b/tests/test_spans.rs @@ -0,0 +1,63 @@ +use std::include_str; + +use codespan_reporting::{ +    self, +    diagnostic::{Diagnostic, Label}, +    files::SimpleFiles, +    term::{self, termcolor::Buffer}, +}; +use html5gum::{ +    spans::{PosTracker, SpanEmitter}, +    Readable, Token, Tokenizer, +}; + +#[test] +fn test() { +    let html = include_str!("span-tests/demo.html"); + +    let mut files = SimpleFiles::new(); +    let file_id = files.add("test.html", html); +    let mut labels = Vec::new(); + +    for token in Tokenizer::new_with_emitter( +        PosTracker { +            reader: html.to_reader(), +            position: 0, +        }, +        SpanEmitter::default(), +    ) +    .infallible() +    { +        if let Token::StartTag(tag) = token { +            labels.push(Label::primary(file_id, tag.name_span).with_message("start tag")); +        } else if let Token::EndTag(tag) = token { +            labels.push(Label::primary(file_id, tag.name_span).with_message("end tag")); +        } +    } + +    let diagnostic = Diagnostic::note().with_labels(labels); + +    let mut writer = Buffer::no_color(); +    let config = codespan_reporting::term::Config::default(); +    term::emit(&mut writer, &config, &files, &diagnostic).unwrap(); + +    let actual = remove_trailing_spaces(std::str::from_utf8(writer.as_slice()).unwrap()); +    let expected = include_str!("span-tests/demo.out"); + +    if actual != expected { +        println!( +            "EXPECTED:\n{banner}\n{expected}{banner}\n\nACTUAL OUTPUT:\n{banner}\n{actual}{banner}", +            banner = "-".repeat(30), +            expected = expected, +            actual = actual +        ); +        panic!("failed"); +    } +} + +fn remove_trailing_spaces(text: &str) -> String { +    text.lines() +        .map(|l| l.trim_end()) +        .collect::<Vec<_>>() +        .join("\n") +} | 
