From 070a66f1612fe15ad809beec9edd797dbfd4550c Mon Sep 17 00:00:00 2001 From: Josh Date: Sun, 5 Jan 2025 00:15:43 -0600 Subject: [PATCH] add testing --- crates/djls-template-ast/src/ast.rs | 44 +++++++++- crates/djls-template-ast/src/parser.rs | 111 +++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 4 deletions(-) diff --git a/crates/djls-template-ast/src/ast.rs b/crates/djls-template-ast/src/ast.rs index 6a56b83..5b4ae89 100644 --- a/crates/djls-template-ast/src/ast.rs +++ b/crates/djls-template-ast/src/ast.rs @@ -13,6 +13,10 @@ impl Ast { &self.nodes } + pub fn line_offsets(&self) -> &LineOffsets { + &self.line_offsets + } + pub fn errors(&self) -> &Vec { &self.errors } @@ -42,8 +46,7 @@ pub struct LineOffsets(Vec); impl LineOffsets { pub fn new() -> Self { - let mut offsets = Vec::new(); - offsets.push(0); // First line always starts at 0 + let offsets = vec![0]; Self(offsets) } @@ -51,7 +54,7 @@ impl LineOffsets { self.0.push(offset); } - fn position_to_line_col(&self, offset: u32) -> (u32, u32) { + pub fn position_to_line_col(&self, offset: u32) -> (u32, u32) { let line = match self.0.binary_search(&offset) { Ok(line) => line, Err(line) => line - 1, @@ -60,11 +63,44 @@ impl LineOffsets { (line as u32, col) } - fn line_col_to_position(&self, line: u32, col: u32) -> u32 { + pub fn line_col_to_position(&self, line: u32, col: u32) -> u32 { self.0[line as usize] + col } } +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_line_offsets() { + let mut offsets = LineOffsets::new(); + offsets.add_line(10); // Line 1 starts at offset 10 + offsets.add_line(25); // Line 2 starts at offset 25 + offsets.add_line(40); // Line 3 starts at offset 40 + + // Test position_to_line_col + assert_eq!(offsets.position_to_line_col(0), (0, 0)); // Start of first line + assert_eq!(offsets.position_to_line_col(5), (0, 5)); // Middle of first line + assert_eq!(offsets.position_to_line_col(10), (1, 0)); // Start of second line + assert_eq!(offsets.position_to_line_col(15), (1, 5)); // Middle of second line + assert_eq!(offsets.position_to_line_col(25), (2, 0)); // Start of third line + assert_eq!(offsets.position_to_line_col(35), (2, 10)); // Middle of third line + assert_eq!(offsets.position_to_line_col(40), (3, 0)); // Start of fourth line + assert_eq!(offsets.position_to_line_col(45), (3, 5)); // Middle of fourth line + + // Test line_col_to_position + assert_eq!(offsets.line_col_to_position(0, 0), 0); // Start of first line + assert_eq!(offsets.line_col_to_position(0, 5), 5); // Middle of first line + assert_eq!(offsets.line_col_to_position(1, 0), 10); // Start of second line + assert_eq!(offsets.line_col_to_position(1, 5), 15); // Middle of second line + assert_eq!(offsets.line_col_to_position(2, 0), 25); // Start of third line + assert_eq!(offsets.line_col_to_position(2, 10), 35); // Middle of third line + assert_eq!(offsets.line_col_to_position(3, 0), 40); // Start of fourth line + assert_eq!(offsets.line_col_to_position(3, 5), 45); // Middle of fourth line + } +} + #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)] pub struct Span { start: u32, diff --git a/crates/djls-template-ast/src/parser.rs b/crates/djls-template-ast/src/parser.rs index ae82676..e33b6d4 100644 --- a/crates/djls-template-ast/src/parser.rs +++ b/crates/djls-template-ast/src/parser.rs @@ -741,4 +741,115 @@ mod tests { insta::assert_yaml_snapshot!(ast); } } + + mod span_tracking { + use super::*; + + #[test] + fn test_span_tracking() { + let mut tokens = TokenStream::default(); + // First line: "Hello\n" + tokens.add_token(Token::new(TokenType::Text("Hello".to_string()), 0, Some(0))); + tokens.add_token(Token::new(TokenType::Newline, 0, Some(5))); + // Second line: "{{ name }}\n" + tokens.add_token(Token::new( + TokenType::DjangoVariable("name".to_string()), + 1, + Some(6), + )); + tokens.add_token(Token::new(TokenType::Newline, 1, Some(16))); + // Third line: "{% if condition %}\n" + tokens.add_token(Token::new( + TokenType::DjangoBlock("if condition".to_string()), + 2, + Some(17), + )); + tokens.add_token(Token::new(TokenType::Newline, 2, Some(34))); + // Fourth line: " Content\n" + tokens.add_token(Token::new(TokenType::Whitespace(2), 3, Some(35))); + tokens.add_token(Token::new( + TokenType::Text("Content".to_string()), + 3, + Some(37), + )); + tokens.add_token(Token::new(TokenType::Newline, 3, Some(44))); + // Fifth line: "{% endif %}" + tokens.add_token(Token::new( + TokenType::DjangoBlock("endif".to_string()), + 4, + Some(45), + )); + tokens.finalize(4); + + let mut parser = Parser::new(tokens); + let ast = parser.parse().unwrap(); + + // Verify line offsets + let offsets = ast.line_offsets(); + assert_eq!(offsets.position_to_line_col(0), (0, 0)); // Start of first line + assert_eq!(offsets.position_to_line_col(6), (1, 0)); // Start of second line + assert_eq!(offsets.position_to_line_col(17), (2, 0)); // Start of third line + assert_eq!(offsets.position_to_line_col(35), (3, 0)); // Start of fourth line + assert_eq!(offsets.position_to_line_col(45), (4, 0)); // Start of fifth line + + // Verify node spans + let nodes = ast.nodes(); + + // First node: Text "Hello" + if let Node::Text { content, span } = &nodes[0] { + assert_eq!(content, "Hello"); + assert_eq!(*span.start(), 0); + assert_eq!(*span.length(), 5); + } else { + panic!("Expected Text node"); + } + + // Second node: Variable "name" + if let Node::Variable { + bits, + filters, + span, + } = &nodes[1] + { + assert_eq!(bits[0], "name"); + assert!(filters.is_empty()); + assert_eq!(*span.start(), 6); + assert_eq!(*span.length(), 4); + } else { + panic!("Expected Variable node"); + } + + // Third node: Block "if condition" + if let Node::Block { + name, + bits, + children, + span, + tag_span, + .. + } = &nodes[2] + { + assert_eq!(name, "if"); + assert_eq!(bits[1], "condition"); + assert_eq!(*span.start(), 17); + assert_eq!(*tag_span.start(), 17); + assert_eq!(*tag_span.length(), 11); + + // Check content node + if let Some(child_nodes) = children { + if let Node::Text { content, span } = &child_nodes[0] { + assert_eq!(content.trim(), "Content"); + assert_eq!(*span.start(), 37); + assert_eq!(*span.length(), 7); + } else { + panic!("Expected Text node as child"); + } + } else { + panic!("Expected children in if block"); + } + } else { + panic!("Expected Block node"); + } + } + } }