mirror of
https://github.com/joshuadavidthomas/django-language-server.git
synced 2025-09-10 20:36:21 +00:00
fix
This commit is contained in:
parent
c17ddab1cc
commit
6075d13c9c
2 changed files with 161 additions and 150 deletions
|
@ -154,32 +154,162 @@ pub enum AstError {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::lexer::Lexer;
|
||||||
|
use crate::parser::Parser;
|
||||||
|
|
||||||
#[test]
|
mod line_offsets {
|
||||||
fn test_line_offsets() {
|
use super::*;
|
||||||
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
|
#[test]
|
||||||
assert_eq!(offsets.position_to_line_col(0), (0, 0)); // Start of first line
|
fn test_new_starts_at_zero() {
|
||||||
assert_eq!(offsets.position_to_line_col(5), (0, 5)); // Middle of first line
|
let offsets = LineOffsets::new();
|
||||||
assert_eq!(offsets.position_to_line_col(10), (1, 0)); // Start of second line
|
assert_eq!(offsets.position_to_line_col(0), (0, 0));
|
||||||
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
|
#[test]
|
||||||
assert_eq!(offsets.line_col_to_position(0, 0), 0); // Start of first line
|
fn test_start_of_lines() {
|
||||||
assert_eq!(offsets.line_col_to_position(0, 5), 5); // Middle of first line
|
let mut offsets = LineOffsets::new();
|
||||||
assert_eq!(offsets.line_col_to_position(1, 0), 10); // Start of second line
|
offsets.add_line(10); // Line 1
|
||||||
assert_eq!(offsets.line_col_to_position(1, 5), 15); // Middle of second line
|
offsets.add_line(25); // Line 2
|
||||||
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.position_to_line_col(0), (0, 0)); // Line 0
|
||||||
assert_eq!(offsets.line_col_to_position(3, 0), 40); // Start of fourth line
|
assert_eq!(offsets.position_to_line_col(10), (1, 0)); // Line 1
|
||||||
assert_eq!(offsets.line_col_to_position(3, 5), 45); // Middle of fourth line
|
assert_eq!(offsets.position_to_line_col(25), (2, 0)); // Line 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod spans_and_positions {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_variable_spans() {
|
||||||
|
let template = "Hello\n{{ user.name }}\nWorld";
|
||||||
|
let tokens = Lexer::new(template).tokenize().unwrap();
|
||||||
|
let mut parser = Parser::new(tokens);
|
||||||
|
let ast = parser.parse().unwrap();
|
||||||
|
|
||||||
|
// Find the variable node
|
||||||
|
let nodes = ast.nodes();
|
||||||
|
let var_node = nodes
|
||||||
|
.iter()
|
||||||
|
.find(|n| matches!(n, Node::Variable { .. }))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if let Node::Variable { span, .. } = var_node {
|
||||||
|
// Variable starts after newline + "{{"
|
||||||
|
let (line, col) = ast.line_offsets.position_to_line_col(span.start());
|
||||||
|
assert_eq!(
|
||||||
|
(line, col),
|
||||||
|
(1, 3),
|
||||||
|
"Variable should start at line 1, col 3"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Span should be exactly "user.name"
|
||||||
|
assert_eq!(span.length(), 9, "Variable span should cover 'user.name'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_block_spans() {
|
||||||
|
let template = "{% if user.active %}\n Welcome!\n{% endif %}";
|
||||||
|
let tokens = Lexer::new(template).tokenize().unwrap();
|
||||||
|
let mut parser = Parser::new(tokens);
|
||||||
|
let ast = parser.parse().unwrap();
|
||||||
|
|
||||||
|
// Find the block node
|
||||||
|
let nodes = ast.nodes();
|
||||||
|
if let Node::Block {
|
||||||
|
span,
|
||||||
|
tag_span,
|
||||||
|
children,
|
||||||
|
..
|
||||||
|
} = &nodes[0]
|
||||||
|
{
|
||||||
|
// Check opening tag span
|
||||||
|
let (tag_line, tag_col) = ast.line_offsets.position_to_line_col(tag_span.start());
|
||||||
|
assert_eq!(
|
||||||
|
(tag_line, tag_col),
|
||||||
|
(0, 0),
|
||||||
|
"Opening tag should start at beginning"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check content span
|
||||||
|
if let Some(content) = children {
|
||||||
|
if let Node::Text { span, .. } = &content[0] {
|
||||||
|
let (content_line, content_col) =
|
||||||
|
ast.line_offsets.position_to_line_col(span.start());
|
||||||
|
assert_eq!(
|
||||||
|
(content_line, content_col),
|
||||||
|
(1, 2),
|
||||||
|
"Content should be indented"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Full block span should cover entire template
|
||||||
|
assert_eq!(span.length(), template.len() as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_multiline_template() {
|
||||||
|
let template = "\
|
||||||
|
<div>
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
{{ user.name }}
|
||||||
|
{% if user.is_staff %}
|
||||||
|
(Staff)
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</div>";
|
||||||
|
let tokens = Lexer::new(template).tokenize().unwrap();
|
||||||
|
let mut parser = Parser::new(tokens);
|
||||||
|
let ast = parser.parse().unwrap();
|
||||||
|
|
||||||
|
// Test nested block positions
|
||||||
|
let (outer_if, inner_if) = {
|
||||||
|
let nodes = ast.nodes();
|
||||||
|
let outer = nodes
|
||||||
|
.iter()
|
||||||
|
.find(|n| matches!(n, Node::Block { .. }))
|
||||||
|
.unwrap();
|
||||||
|
let inner = if let Node::Block { children, .. } = outer {
|
||||||
|
children
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.find(|n| matches!(n, Node::Block { .. }))
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
panic!("Expected block with children");
|
||||||
|
};
|
||||||
|
(outer, inner)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let (
|
||||||
|
Node::Block {
|
||||||
|
span: outer_span, ..
|
||||||
|
},
|
||||||
|
Node::Block {
|
||||||
|
span: inner_span, ..
|
||||||
|
},
|
||||||
|
) = (outer_if, inner_if)
|
||||||
|
{
|
||||||
|
// Verify outer if starts at the right line/column
|
||||||
|
let (outer_line, outer_col) =
|
||||||
|
ast.line_offsets.position_to_line_col(outer_span.start());
|
||||||
|
assert_eq!(
|
||||||
|
(outer_line, outer_col),
|
||||||
|
(1, 4),
|
||||||
|
"Outer if should be indented"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify inner if is more indented than outer if
|
||||||
|
let (inner_line, inner_col) =
|
||||||
|
ast.line_offsets.position_to_line_col(inner_span.start());
|
||||||
|
assert!(inner_col > outer_col, "Inner if should be more indented");
|
||||||
|
assert!(inner_line > outer_line, "Inner if should be on later line");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -460,10 +460,10 @@ impl ParserError {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::lexer::Lexer;
|
use crate::lexer::Lexer;
|
||||||
|
use crate::tokens::Token;
|
||||||
|
|
||||||
mod html {
|
mod html {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_html_doctype() {
|
fn test_parse_html_doctype() {
|
||||||
let source = "<!DOCTYPE html>";
|
let source = "<!DOCTYPE html>";
|
||||||
|
@ -472,7 +472,6 @@ mod tests {
|
||||||
let ast = parser.parse().unwrap();
|
let ast = parser.parse().unwrap();
|
||||||
insta::assert_yaml_snapshot!(ast);
|
insta::assert_yaml_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_html_tag() {
|
fn test_parse_html_tag() {
|
||||||
let source = "<div class=\"container\">Hello</div>";
|
let source = "<div class=\"container\">Hello</div>";
|
||||||
|
@ -481,7 +480,6 @@ mod tests {
|
||||||
let ast = parser.parse().unwrap();
|
let ast = parser.parse().unwrap();
|
||||||
insta::assert_yaml_snapshot!(ast);
|
insta::assert_yaml_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_html_void() {
|
fn test_parse_html_void() {
|
||||||
let source = "<input type=\"text\" />";
|
let source = "<input type=\"text\" />";
|
||||||
|
@ -491,10 +489,8 @@ mod tests {
|
||||||
insta::assert_yaml_snapshot!(ast);
|
insta::assert_yaml_snapshot!(ast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod django {
|
mod django {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_django_variable() {
|
fn test_parse_django_variable() {
|
||||||
let source = "{{ user.name|title }}";
|
let source = "{{ user.name|title }}";
|
||||||
|
@ -503,7 +499,6 @@ mod tests {
|
||||||
let ast = parser.parse().unwrap();
|
let ast = parser.parse().unwrap();
|
||||||
insta::assert_yaml_snapshot!(ast);
|
insta::assert_yaml_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_filter_chains() {
|
fn test_parse_filter_chains() {
|
||||||
let source = "{{ value|default:'nothing'|title|upper }}";
|
let source = "{{ value|default:'nothing'|title|upper }}";
|
||||||
|
@ -512,7 +507,6 @@ mod tests {
|
||||||
let ast = parser.parse().unwrap();
|
let ast = parser.parse().unwrap();
|
||||||
insta::assert_yaml_snapshot!(ast);
|
insta::assert_yaml_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_django_if_block() {
|
fn test_parse_django_if_block() {
|
||||||
let source = "{% if user.is_authenticated %}Welcome{% endif %}";
|
let source = "{% if user.is_authenticated %}Welcome{% endif %}";
|
||||||
|
@ -521,7 +515,6 @@ mod tests {
|
||||||
let ast = parser.parse().unwrap();
|
let ast = parser.parse().unwrap();
|
||||||
insta::assert_yaml_snapshot!(ast);
|
insta::assert_yaml_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_django_for_block() {
|
fn test_parse_django_for_block() {
|
||||||
let source = "{% for item in items %}{{ item }}{% empty %}No items{% endfor %}";
|
let source = "{% for item in items %}{{ item }}{% empty %}No items{% endfor %}";
|
||||||
|
@ -530,7 +523,6 @@ mod tests {
|
||||||
let ast = parser.parse().unwrap();
|
let ast = parser.parse().unwrap();
|
||||||
insta::assert_yaml_snapshot!(ast);
|
insta::assert_yaml_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_complex_if_elif() {
|
fn test_parse_complex_if_elif() {
|
||||||
let source = "{% if x > 0 %}Positive{% elif x < 0 %}Negative{% else %}Zero{% endif %}";
|
let source = "{% if x > 0 %}Positive{% elif x < 0 %}Negative{% else %}Zero{% endif %}";
|
||||||
|
@ -539,7 +531,6 @@ mod tests {
|
||||||
let ast = parser.parse().unwrap();
|
let ast = parser.parse().unwrap();
|
||||||
insta::assert_yaml_snapshot!(ast);
|
insta::assert_yaml_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_nested_for_if() {
|
fn test_parse_nested_for_if() {
|
||||||
let source =
|
let source =
|
||||||
|
@ -549,7 +540,6 @@ mod tests {
|
||||||
let ast = parser.parse().unwrap();
|
let ast = parser.parse().unwrap();
|
||||||
insta::assert_yaml_snapshot!(ast);
|
insta::assert_yaml_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_mixed_content() {
|
fn test_parse_mixed_content() {
|
||||||
let source = "Welcome, {% if user.is_authenticated %}
|
let source = "Welcome, {% if user.is_authenticated %}
|
||||||
|
@ -571,10 +561,8 @@ mod tests {
|
||||||
insta::assert_yaml_snapshot!(ast);
|
insta::assert_yaml_snapshot!(ast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod script {
|
mod script {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_script() {
|
fn test_parse_script() {
|
||||||
let source = r#"<script type="text/javascript">
|
let source = r#"<script type="text/javascript">
|
||||||
|
@ -590,10 +578,8 @@ mod tests {
|
||||||
insta::assert_yaml_snapshot!(ast);
|
insta::assert_yaml_snapshot!(ast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod style {
|
mod style {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_style() {
|
fn test_parse_style() {
|
||||||
let source = r#"<style type="text/css">
|
let source = r#"<style type="text/css">
|
||||||
|
@ -608,10 +594,8 @@ mod tests {
|
||||||
insta::assert_yaml_snapshot!(ast);
|
insta::assert_yaml_snapshot!(ast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod comments {
|
mod comments {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_comments() {
|
fn test_parse_comments() {
|
||||||
let source = "<!-- HTML comment -->{# Django comment #}";
|
let source = "<!-- HTML comment -->{# Django comment #}";
|
||||||
|
@ -621,10 +605,8 @@ mod tests {
|
||||||
insta::assert_yaml_snapshot!(ast);
|
insta::assert_yaml_snapshot!(ast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod errors {
|
mod errors {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_unclosed_html_tag() {
|
fn test_parse_unclosed_html_tag() {
|
||||||
let source = "<div>";
|
let source = "<div>";
|
||||||
|
@ -634,7 +616,6 @@ mod tests {
|
||||||
insta::assert_yaml_snapshot!(ast);
|
insta::assert_yaml_snapshot!(ast);
|
||||||
assert_eq!(ast.errors().len(), 0);
|
assert_eq!(ast.errors().len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_unclosed_django_if() {
|
fn test_parse_unclosed_django_if() {
|
||||||
let source = "{% if user.is_authenticated %}Welcome";
|
let source = "{% if user.is_authenticated %}Welcome";
|
||||||
|
@ -645,7 +626,6 @@ mod tests {
|
||||||
assert_eq!(ast.errors().len(), 1);
|
assert_eq!(ast.errors().len(), 1);
|
||||||
assert!(matches!(&ast.errors()[0], AstError::UnclosedTag(tag) if tag == "if"));
|
assert!(matches!(&ast.errors()[0], AstError::UnclosedTag(tag) if tag == "if"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_unclosed_django_for() {
|
fn test_parse_unclosed_django_for() {
|
||||||
let source = "{% for item in items %}{{ item.name }}";
|
let source = "{% for item in items %}{{ item.name }}";
|
||||||
|
@ -656,7 +636,6 @@ mod tests {
|
||||||
assert_eq!(ast.errors().len(), 1);
|
assert_eq!(ast.errors().len(), 1);
|
||||||
assert!(matches!(&ast.errors()[0], AstError::UnclosedTag(tag) if tag == "for"));
|
assert!(matches!(&ast.errors()[0], AstError::UnclosedTag(tag) if tag == "for"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_unclosed_script() {
|
fn test_parse_unclosed_script() {
|
||||||
let source = "<script>console.log('test');";
|
let source = "<script>console.log('test');";
|
||||||
|
@ -666,7 +645,6 @@ mod tests {
|
||||||
insta::assert_yaml_snapshot!(ast);
|
insta::assert_yaml_snapshot!(ast);
|
||||||
assert_eq!(ast.errors().len(), 0);
|
assert_eq!(ast.errors().len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_unclosed_style() {
|
fn test_parse_unclosed_style() {
|
||||||
let source = "<style>body { color: blue; ";
|
let source = "<style>body { color: blue; ";
|
||||||
|
@ -676,7 +654,6 @@ mod tests {
|
||||||
insta::assert_yaml_snapshot!(ast);
|
insta::assert_yaml_snapshot!(ast);
|
||||||
assert_eq!(ast.errors().len(), 0);
|
assert_eq!(ast.errors().len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_error_recovery() {
|
fn test_parse_error_recovery() {
|
||||||
let source = r#"<div class="container">
|
let source = r#"<div class="container">
|
||||||
|
@ -702,7 +679,6 @@ mod tests {
|
||||||
|
|
||||||
mod full_templates {
|
mod full_templates {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_full() {
|
fn test_parse_full() {
|
||||||
let source = r#"<!DOCTYPE html>
|
let source = r#"<!DOCTYPE html>
|
||||||
|
@ -742,114 +718,19 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod span_tracking {
|
mod line_tracking {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_span_tracking() {
|
fn test_parser_tracks_line_offsets() {
|
||||||
let mut tokens = TokenStream::default();
|
let source = "line1\nline2";
|
||||||
// First line: "Hello\n"
|
let tokens = Lexer::new(source).tokenize().unwrap();
|
||||||
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 mut parser = Parser::new(tokens);
|
||||||
let ast = parser.parse().unwrap();
|
let ast = parser.parse().unwrap();
|
||||||
|
|
||||||
// Verify line offsets
|
|
||||||
let offsets = ast.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(0), (0, 0)); // Start of line 1
|
||||||
assert_eq!(offsets.position_to_line_col(6), (1, 0)); // Start of second line
|
assert_eq!(offsets.position_to_line_col(6), (1, 0)); // Start of line 2
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue