Skip to content

Commit 070a66f

Browse files
add testing
1 parent fab360b commit 070a66f

File tree

2 files changed

+151
-4
lines changed

2 files changed

+151
-4
lines changed

crates/djls-template-ast/src/ast.rs

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ impl Ast {
1313
&self.nodes
1414
}
1515

16+
pub fn line_offsets(&self) -> &LineOffsets {
17+
&self.line_offsets
18+
}
19+
1620
pub fn errors(&self) -> &Vec<AstError> {
1721
&self.errors
1822
}
@@ -42,16 +46,15 @@ pub struct LineOffsets(Vec<u32>);
4246

4347
impl LineOffsets {
4448
pub fn new() -> Self {
45-
let mut offsets = Vec::new();
46-
offsets.push(0); // First line always starts at 0
49+
let offsets = vec![0];
4750
Self(offsets)
4851
}
4952

5053
pub fn add_line(&mut self, offset: u32) {
5154
self.0.push(offset);
5255
}
5356

54-
fn position_to_line_col(&self, offset: u32) -> (u32, u32) {
57+
pub fn position_to_line_col(&self, offset: u32) -> (u32, u32) {
5558
let line = match self.0.binary_search(&offset) {
5659
Ok(line) => line,
5760
Err(line) => line - 1,
@@ -60,11 +63,44 @@ impl LineOffsets {
6063
(line as u32, col)
6164
}
6265

63-
fn line_col_to_position(&self, line: u32, col: u32) -> u32 {
66+
pub fn line_col_to_position(&self, line: u32, col: u32) -> u32 {
6467
self.0[line as usize] + col
6568
}
6669
}
6770

71+
#[cfg(test)]
72+
mod tests {
73+
use super::*;
74+
75+
#[test]
76+
fn test_line_offsets() {
77+
let mut offsets = LineOffsets::new();
78+
offsets.add_line(10); // Line 1 starts at offset 10
79+
offsets.add_line(25); // Line 2 starts at offset 25
80+
offsets.add_line(40); // Line 3 starts at offset 40
81+
82+
// Test position_to_line_col
83+
assert_eq!(offsets.position_to_line_col(0), (0, 0)); // Start of first line
84+
assert_eq!(offsets.position_to_line_col(5), (0, 5)); // Middle of first line
85+
assert_eq!(offsets.position_to_line_col(10), (1, 0)); // Start of second line
86+
assert_eq!(offsets.position_to_line_col(15), (1, 5)); // Middle of second line
87+
assert_eq!(offsets.position_to_line_col(25), (2, 0)); // Start of third line
88+
assert_eq!(offsets.position_to_line_col(35), (2, 10)); // Middle of third line
89+
assert_eq!(offsets.position_to_line_col(40), (3, 0)); // Start of fourth line
90+
assert_eq!(offsets.position_to_line_col(45), (3, 5)); // Middle of fourth line
91+
92+
// Test line_col_to_position
93+
assert_eq!(offsets.line_col_to_position(0, 0), 0); // Start of first line
94+
assert_eq!(offsets.line_col_to_position(0, 5), 5); // Middle of first line
95+
assert_eq!(offsets.line_col_to_position(1, 0), 10); // Start of second line
96+
assert_eq!(offsets.line_col_to_position(1, 5), 15); // Middle of second line
97+
assert_eq!(offsets.line_col_to_position(2, 0), 25); // Start of third line
98+
assert_eq!(offsets.line_col_to_position(2, 10), 35); // Middle of third line
99+
assert_eq!(offsets.line_col_to_position(3, 0), 40); // Start of fourth line
100+
assert_eq!(offsets.line_col_to_position(3, 5), 45); // Middle of fourth line
101+
}
102+
}
103+
68104
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
69105
pub struct Span {
70106
start: u32,

crates/djls-template-ast/src/parser.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,4 +741,115 @@ mod tests {
741741
insta::assert_yaml_snapshot!(ast);
742742
}
743743
}
744+
745+
mod span_tracking {
746+
use super::*;
747+
748+
#[test]
749+
fn test_span_tracking() {
750+
let mut tokens = TokenStream::default();
751+
// First line: "Hello\n"
752+
tokens.add_token(Token::new(TokenType::Text("Hello".to_string()), 0, Some(0)));
753+
tokens.add_token(Token::new(TokenType::Newline, 0, Some(5)));
754+
// Second line: "{{ name }}\n"
755+
tokens.add_token(Token::new(
756+
TokenType::DjangoVariable("name".to_string()),
757+
1,
758+
Some(6),
759+
));
760+
tokens.add_token(Token::new(TokenType::Newline, 1, Some(16)));
761+
// Third line: "{% if condition %}\n"
762+
tokens.add_token(Token::new(
763+
TokenType::DjangoBlock("if condition".to_string()),
764+
2,
765+
Some(17),
766+
));
767+
tokens.add_token(Token::new(TokenType::Newline, 2, Some(34)));
768+
// Fourth line: " Content\n"
769+
tokens.add_token(Token::new(TokenType::Whitespace(2), 3, Some(35)));
770+
tokens.add_token(Token::new(
771+
TokenType::Text("Content".to_string()),
772+
3,
773+
Some(37),
774+
));
775+
tokens.add_token(Token::new(TokenType::Newline, 3, Some(44)));
776+
// Fifth line: "{% endif %}"
777+
tokens.add_token(Token::new(
778+
TokenType::DjangoBlock("endif".to_string()),
779+
4,
780+
Some(45),
781+
));
782+
tokens.finalize(4);
783+
784+
let mut parser = Parser::new(tokens);
785+
let ast = parser.parse().unwrap();
786+
787+
// Verify line offsets
788+
let offsets = ast.line_offsets();
789+
assert_eq!(offsets.position_to_line_col(0), (0, 0)); // Start of first line
790+
assert_eq!(offsets.position_to_line_col(6), (1, 0)); // Start of second line
791+
assert_eq!(offsets.position_to_line_col(17), (2, 0)); // Start of third line
792+
assert_eq!(offsets.position_to_line_col(35), (3, 0)); // Start of fourth line
793+
assert_eq!(offsets.position_to_line_col(45), (4, 0)); // Start of fifth line
794+
795+
// Verify node spans
796+
let nodes = ast.nodes();
797+
798+
// First node: Text "Hello"
799+
if let Node::Text { content, span } = &nodes[0] {
800+
assert_eq!(content, "Hello");
801+
assert_eq!(*span.start(), 0);
802+
assert_eq!(*span.length(), 5);
803+
} else {
804+
panic!("Expected Text node");
805+
}
806+
807+
// Second node: Variable "name"
808+
if let Node::Variable {
809+
bits,
810+
filters,
811+
span,
812+
} = &nodes[1]
813+
{
814+
assert_eq!(bits[0], "name");
815+
assert!(filters.is_empty());
816+
assert_eq!(*span.start(), 6);
817+
assert_eq!(*span.length(), 4);
818+
} else {
819+
panic!("Expected Variable node");
820+
}
821+
822+
// Third node: Block "if condition"
823+
if let Node::Block {
824+
name,
825+
bits,
826+
children,
827+
span,
828+
tag_span,
829+
..
830+
} = &nodes[2]
831+
{
832+
assert_eq!(name, "if");
833+
assert_eq!(bits[1], "condition");
834+
assert_eq!(*span.start(), 17);
835+
assert_eq!(*tag_span.start(), 17);
836+
assert_eq!(*tag_span.length(), 11);
837+
838+
// Check content node
839+
if let Some(child_nodes) = children {
840+
if let Node::Text { content, span } = &child_nodes[0] {
841+
assert_eq!(content.trim(), "Content");
842+
assert_eq!(*span.start(), 37);
843+
assert_eq!(*span.length(), 7);
844+
} else {
845+
panic!("Expected Text node as child");
846+
}
847+
} else {
848+
panic!("Expected children in if block");
849+
}
850+
} else {
851+
panic!("Expected Block node");
852+
}
853+
}
854+
}
744855
}

0 commit comments

Comments
 (0)