diff --git a/crates/djls-template-ast/src/ast.rs b/crates/djls-template-ast/src/ast.rs index c0f95a2..c4e13b4 100644 --- a/crates/djls-template-ast/src/ast.rs +++ b/crates/djls-template-ast/src/ast.rs @@ -47,14 +47,19 @@ impl LineOffsets { pub fn position_to_line_col(&self, position: usize) -> (usize, usize) { let position = position as u32; let line = match self.0.binary_search(&position) { - Ok(_) => self.0.partition_point(|&x| x <= position), - Err(i) => i, + Ok(exact_line) => exact_line, // Position is at start of this line + Err(0) => 0, // Before first line start + Err(next_line) => next_line - 1, // We're on the previous line }; + + // Calculate column as offset from line start let col = if line == 0 { position as usize } else { - (position - self.0[line - 1]) as usize + (position - self.0[line]) as usize }; + + // Convert to 1-based line number (line + 1, col) } @@ -348,7 +353,7 @@ mod tests { eprintln!("Line offsets: {:?}", ast.line_offsets()); eprintln!("Span: {:?}", span); let (line, col) = ast.line_offsets().position_to_line_col(span.start as usize); - assert_eq!((line, col), (1, 0), "Content should be on line 1, col 0"); + assert_eq!((line, col), (2, 0), "Content should be on line 2, col 0"); // Check closing tag if let Block::Closing { tag } = diff --git a/crates/djls-template-ast/src/parser.rs b/crates/djls-template-ast/src/parser.rs index c228a98..731ac83 100644 --- a/crates/djls-template-ast/src/parser.rs +++ b/crates/djls-template-ast/src/parser.rs @@ -23,10 +23,13 @@ impl Parser { let mut line_offsets = LineOffsets::new(); // First pass: collect line offsets + let mut current_line_start = 0; for token in self.tokens.tokens() { if let TokenType::Newline = token.token_type() { if let Some(start) = token.start() { - line_offsets.add_line(start + 1); + // Add offset for next line + current_line_start = start + 1; + line_offsets.add_line(current_line_start); } } } @@ -157,9 +160,11 @@ impl Parser { if !found_closing { // Push the last branch if we didn't find a closing tag nodes.push(Node::Block(Block::Branch { - tag: branch_tag, - nodes: branch_nodes, + tag: branch_tag.clone(), + nodes: branch_nodes.clone(), })); + // Add error for unclosed tag + self.errors.push(ParserError::Ast(AstError::UnclosedTag(tag_name.clone()))); } if found_closing { break; @@ -192,6 +197,13 @@ impl Parser { } }; + // Add error if we didn't find a closing tag for a block + if let Block::Block { closing: None, tag: tag_ref, .. } = &block { + if let Some(expected_closing) = &spec.closing { + self.errors.push(ParserError::Ast(AstError::UnclosedTag(tag_ref.name.clone()))); + } + } + Ok(Node::Block(block)) }