This commit is contained in:
Josh Thomas 2025-01-05 20:25:07 -06:00
parent 00eca98c6a
commit f1cc33f173
21 changed files with 1323 additions and 651 deletions

View file

@ -5,7 +5,6 @@ use thiserror::Error;
pub struct Ast {
nodes: Vec<Node>,
line_offsets: LineOffsets,
errors: Vec<AstError>,
}
impl Ast {
@ -17,10 +16,6 @@ impl Ast {
&self.line_offsets
}
pub fn errors(&self) -> &Vec<AstError> {
&self.errors
}
pub fn add_node(&mut self, node: Node) {
self.nodes.push(node);
}
@ -29,12 +24,8 @@ impl Ast {
self.line_offsets = line_offsets
}
pub fn add_error(&mut self, error: AstError) {
self.errors.push(error);
}
pub fn finalize(&mut self) -> Result<Ast, AstError> {
if self.nodes.is_empty() && self.errors.is_empty() {
if self.nodes.is_empty() {
return Err(AstError::EmptyAst);
}
Ok(self.clone())
@ -55,15 +46,13 @@ impl LineOffsets {
}
pub fn position_to_line_col(&self, offset: u32) -> (u32, u32) {
eprintln!("LineOffsets: Converting position {} to line/col. Offsets: {:?}", offset, self.0);
// Find which line contains this offset by looking for the first line start
// that's greater than our position
let line = match self.0.binary_search(&offset) {
Ok(exact_line) => exact_line, // We're exactly at a line start, so we're on that line
Ok(exact_line) => exact_line, // We're exactly at a line start, so we're on that line
Err(next_line) => {
if next_line == 0 {
0 // Before first line start, so we're on line 0
0 // Before first line start, so we're on line 0
} else {
let prev_line = next_line - 1;
// If we're at the start of the next line, we're on that line
@ -79,9 +68,6 @@ impl LineOffsets {
// Calculate column as offset from line start
let col = offset - self.0[line];
eprintln!("LineOffsets: Found line {} starting at offset {}", line, self.0[line]);
eprintln!("LineOffsets: Calculated col {} as {} - {}", col, offset, self.0[line]);
(line as u32, col)
}
@ -93,11 +79,11 @@ impl LineOffsets {
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
pub struct Span {
start: u32,
length: u16,
length: u32,
}
impl Span {
pub fn new(start: u32, length: u16) -> Self {
pub fn new(start: u32, length: u32) -> Self {
Self { start, length }
}
@ -105,28 +91,21 @@ impl Span {
&self.start
}
pub fn length(&self) -> &u16 {
pub fn length(&self) -> &u32 {
&self.length
}
}
#[derive(Clone, Debug, Serialize)]
pub enum Node {
Text {
content: String,
span: Span,
},
Block(Block),
Comment {
content: String,
span: Span,
},
Block {
block_type: BlockType,
name: String,
bits: Vec<String>,
children: Option<Vec<Node>>,
Text {
content: String,
span: Span,
tag_span: Span,
},
Variable {
bits: Vec<String>,
@ -136,10 +115,45 @@ pub enum Node {
}
#[derive(Clone, Debug, Serialize)]
pub enum BlockType {
Standard,
Branch,
Closing,
pub enum Block {
Block {
tag: Tag,
nodes: Vec<Node>,
closing: Option<Box<Block>>,
assignments: Option<Vec<Assignment>>,
},
Branch {
tag: Tag,
nodes: Vec<Node>,
},
Tag {
tag: Tag,
},
Inclusion {
tag: Tag,
template_name: String,
},
Variable {
tag: Tag,
},
Closing {
tag: Tag,
},
}
#[derive(Clone, Debug, Serialize)]
pub struct Tag {
pub name: String,
pub bits: Vec<String>,
pub span: Span,
pub tag_span: Span,
pub assignment: Option<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct Assignment {
pub target: String,
pub value: String,
}
#[derive(Clone, Debug, Serialize)]
@ -271,8 +285,8 @@ mod tests {
}
}
// Full block span should cover entire template
assert_eq!(*span.length() as u32, 42);
// Full block span should cover only the opening tag
assert_eq!(*span.length() as u32, 14);
}
}
@ -292,48 +306,43 @@ mod tests {
let ast = parser.parse().unwrap();
// Test nested block positions
let (outer_if, inner_if) = {
let nodes = ast.nodes();
let outer = nodes
let nodes = ast.nodes();
let outer_if = nodes
.iter()
.find(|n| matches!(n, Node::Block { .. }))
.unwrap();
if let Node::Block {
span: outer_span,
children: Some(children),
..
} = outer_if
{
// Find the inner if block in the children
let inner_if = children
.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 {
if let 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"
);
} = 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");
// 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");
}
}
}
}

View file

@ -1,5 +1,5 @@
use crate::ast::{Ast, AstError, BlockType, DjangoFilter, LineOffsets, Node, Span};
use crate::tagspecs::TagSpec;
use crate::tagspecs::{TagSpec, TagType};
use crate::tokens::{Token, TokenStream, TokenType};
use thiserror::Error;
@ -146,155 +146,102 @@ impl Parser {
let tag_name = bits.first().ok_or(AstError::EmptyTag)?.clone();
let specs = TagSpec::load_builtin_specs().unwrap_or_default();
// Get the tag spec if it exists
let tag_spec = specs.get(&tag_name);
// Check if this is a closing or branch tag
for (_, spec) in specs.iter() {
if Some(&tag_name) == spec.closing.as_ref()
|| spec
.branches
.as_ref()
.map(|ints| ints.iter().any(|i| i.name == tag_name))
.unwrap_or(false)
{
return Err(ParserError::ErrorSignal(Signal::SpecialTag(tag_name)));
// Check if this is a closing tag
if let Some(spec) = tag_spec {
if let Some(closing) = &spec.closing {
if tag_name == *closing {
// This is a closing tag, return a signal instead of a node
return Err(ParserError::Signal(Signal::ClosingTagFound(tag_name)));
}
}
}
let tag_spec = specs.get(tag_name.as_str()).cloned();
// Check if this is a branch tag
if let Some(spec) = tag_spec {
if let Some(branches) = &spec.branches {
if branches.iter().any(|b| b.name == tag_name) {
let mut children = Vec::new();
while !self.is_at_end() {
match self.peek()?.token_type() {
TokenType::DjangoBlock(next_block) => {
let next_bits: Vec<String> = next_block.split_whitespace().map(String::from).collect();
if let Some(next_tag) = next_bits.first() {
// If we hit another branch or closing tag, we're done
if branches.iter().any(|b| &b.name == next_tag) ||
spec.closing.as_ref().map(|s| s.as_str()) == Some(next_tag.as_str()) {
break;
}
}
children.push(self.next_node()?);
}
_ => children.push(self.next_node()?),
}
}
return Ok(Node::Block {
block_type: BlockType::Branch,
name: tag_name,
bits,
children: Some(children),
span: tag_span,
tag_span,
});
}
}
}
// This is a standard block
let mut children = Vec::new();
let mut current_branch: Option<(String, Vec<String>, Vec<Node>)> = None;
let mut found_closing_tag = false;
let mut found_closing = false;
let mut end_pos = start_pos + s.len() as u32;
while !self.is_at_end() {
match self.next_node() {
Ok(node) => {
if let Some((_, _, branch_children)) = &mut current_branch {
branch_children.push(node);
} else {
children.push(node);
}
}
Err(ParserError::ErrorSignal(Signal::SpecialTag(tag))) => {
if let Some(spec) = &tag_spec {
// Check if closing tag
if spec.closing.as_deref() == Some(&tag) {
// If we have a current branch, add it to children
if let Some((name, bits, branch_children)) = current_branch {
let branch_span = Span::new(start_pos, 0); // Removed total_length initialization
children.push(Node::Block {
block_type: BlockType::Branch,
name,
bits,
children: Some(branch_children),
span: branch_span,
tag_span,
});
}
let closing_token = self.peek_previous()?;
let closing_content = match closing_token.token_type() {
TokenType::DjangoBlock(content) => content.len() + 5, // Add 5 for {% and %}
_ => 0,
};
let closing_start = closing_token.start().unwrap_or(0);
let total_length = (closing_start - start_pos as usize) + closing_content;
let closing_span = Span::new(
closing_start as u32,
closing_content as u16,
);
children.push(Node::Block {
block_type: BlockType::Closing,
name: tag,
bits: vec![],
children: None,
span: closing_span,
tag_span,
});
found_closing_tag = true;
// Set the final span length
let span = Span::new(start_pos, total_length as u16);
let node = Node::Block {
block_type: BlockType::Standard,
name: tag_name,
bits,
children: Some(children),
span,
tag_span,
};
return Ok(node);
}
// Check if intermediate tag
if let Some(branches) = &spec.branches {
if let Some(branch) = branches.iter().find(|b| b.name == tag) {
// If we have a current branch, add it to children
if let Some((name, bits, branch_children)) = current_branch {
let branch_span = Span::new(start_pos, 0); // Removed total_length initialization
children.push(Node::Block {
block_type: BlockType::Branch,
name,
bits,
children: Some(branch_children),
span: branch_span,
tag_span,
});
match self.peek()?.token_type() {
TokenType::DjangoBlock(next_block) => {
let next_bits: Vec<String> = next_block.split_whitespace().map(String::from).collect();
if let Some(next_tag) = next_bits.first() {
// Check if this is a closing tag
if let Some(spec) = tag_spec {
if let Some(closing) = &spec.closing {
if next_tag == closing {
found_closing = true;
let closing_token = self.consume()?;
end_pos = closing_token.start().unwrap_or(0) as u32 + next_block.len() as u32;
break;
}
}
// Check if this is a branch tag
if let Some(branches) = &spec.branches {
if branches.iter().any(|b| &b.name == next_tag) {
children.push(self.next_node()?);
continue;
}
// Create new branch node
let branch_bits = if branch.args {
match &self.tokens[self.current - 1].token_type() {
TokenType::DjangoBlock(content) => content
.split_whitespace()
.skip(1) // Skip the tag name
.map(|s| s.to_string())
.collect(),
_ => vec![tag.clone()],
}
} else {
vec![]
};
current_branch = Some((tag, branch_bits, Vec::new()));
continue;
}
}
}
// If we get here, it's an unexpected tag
let node = Node::Block {
block_type: BlockType::Standard,
name: tag_name.clone(),
bits: bits.clone(),
children: Some(children.clone()),
span: Span::new(start_pos, 0), // Removed total_length initialization
tag_span,
};
return Err(ParserError::Ast(AstError::UnexpectedTag(tag), Some(node)));
children.push(self.next_node()?);
}
Err(ParserError::Ast(AstError::StreamError(kind), _)) if kind == "AtEnd" => {
break;
}
Err(e) => return Err(e),
_ => children.push(self.next_node()?),
}
}
let span = Span::new(start_pos, 0); // Removed total_length initialization
let node = Node::Block {
block_type: BlockType::Standard,
name: tag_name.clone(),
bits,
children: Some(children),
span,
tag_span,
};
if !found_closing_tag {
return Err(ParserError::Ast(
AstError::UnclosedTag(tag_name),
Some(node),
if !found_closing && tag_spec.map_or(false, |s| s.closing.is_some()) {
return Err(ParserError::from(
AstError::UnclosedTag(tag_name.clone()),
));
}
Ok(node)
Ok(Node::Block {
block_type: BlockType::Standard,
name: tag_name,
bits,
children: Some(children),
span: Span::new(start_pos, (end_pos - start_pos) as u16),
tag_span,
})
}
fn parse_django_variable(&mut self, s: &str) -> Result<Node, ParserError> {

View file

@ -17,58 +17,61 @@ nodes:
span:
start: 14
length: 8
- Block:
block_type: Branch
name: elif
bits:
- x
- "<"
- "0"
children:
- Text:
content: Negative
span:
start: 38
length: 8
span:
start: 0
length: 0
tag_span:
start: 0
length: 8
- Block:
block_type: Branch
name: else
bits: []
children:
- Text:
content: Zero
span:
start: 56
length: 4
span:
start: 0
length: 0
tag_span:
start: 0
length: 8
- Block:
block_type: Closing
name: endif
bits: []
children: ~
span:
start: 60
length: 10
tag_span:
start: 0
length: 8
span:
start: 0
length: 70
length: 8
tag_span:
start: 0
length: 8
- Block:
block_type: Standalone
name: elif
bits:
- elif
- x
- "<"
- "0"
children: ~
span:
start: 22
length: 10
tag_span:
start: 22
length: 10
- Text:
content: Negative
span:
start: 38
length: 8
- Block:
block_type: Standalone
name: else
bits:
- else
children: ~
span:
start: 46
length: 4
tag_span:
start: 46
length: 4
- Text:
content: Zero
span:
start: 56
length: 4
- Block:
block_type: Standalone
name: endif
bits:
- endif
children: ~
span:
start: 60
length: 5
tag_span:
start: 60
length: 5
line_offsets:
- 0
errors: []

View file

@ -0,0 +1,11 @@
---
source: crates/djls-template-ast/src/parser.rs
assertion_line: 527
expression: ast
snapshot_kind: text
---
nodes: []
line_offsets:
- 0
errors:
- UnclosedTag: if

View file

@ -19,39 +19,41 @@ nodes:
span:
start: 26
length: 4
- Block:
block_type: Branch
name: empty
bits: []
children:
- Text:
content: No items
span:
start: 44
length: 8
span:
start: 0
length: 0
tag_span:
start: 0
length: 17
- Block:
block_type: Closing
name: endfor
bits: []
children: ~
span:
start: 52
length: 11
tag_span:
start: 0
length: 17
span:
start: 0
length: 63
length: 17
tag_span:
start: 0
length: 17
- Block:
block_type: Standalone
name: empty
bits:
- empty
children: ~
span:
start: 33
length: 5
tag_span:
start: 33
length: 5
- Text:
content: No items
span:
start: 44
length: 8
- Block:
block_type: Standalone
name: endfor
bits:
- endfor
children: ~
span:
start: 52
length: 6
tag_span:
start: 52
length: 6
line_offsets:
- 0
errors: []

View file

@ -0,0 +1,11 @@
---
source: crates/djls-template-ast/src/parser.rs
assertion_line: 518
expression: ast
snapshot_kind: text
---
nodes: []
line_offsets:
- 0
errors:
- UnclosedTag: for

View file

@ -15,23 +15,24 @@ nodes:
span:
start: 30
length: 7
- Block:
block_type: Closing
name: endif
bits: []
children: ~
span:
start: 37
length: 10
tag_span:
start: 0
length: 24
span:
start: 0
length: 47
length: 24
tag_span:
start: 0
length: 24
- Block:
block_type: Standalone
name: endif
bits:
- endif
children: ~
span:
start: 37
length: 5
tag_span:
start: 37
length: 5
line_offsets:
- 0
errors: []

View file

@ -0,0 +1,28 @@
---
source: crates/djls-template-ast/src/parser.rs
assertion_line: 510
expression: ast
snapshot_kind: text
---
nodes:
- Block:
block_type: Standard
name: if
bits:
- if
- user.is_authenticated
children:
- Text:
content: Welcome
span:
start: 30
length: 7
span:
start: 0
length: 24
tag_span:
start: 0
length: 24
line_offsets:
- 0
errors: []

View file

@ -44,207 +44,214 @@ nodes:
span:
start: 86
length: 5
- Block:
block_type: Standard
name: for
bits:
- for
- group
- in
- user.groups
children:
- Text:
content: "\n "
span:
start: 125
length: 9
- Block:
block_type: Standard
name: if
bits:
- if
- forloop.first
children:
- Text:
content: (
span:
start: 147
length: 1
- Block:
block_type: Closing
name: endif
bits: []
children: ~
span:
start: 148
length: 10
tag_span:
start: 125
length: 16
span:
start: 125
length: 33
tag_span:
start: 125
length: 16
- Text:
content: "\n "
span:
start: 168
length: 9
- Variable:
bits:
- group
- name
filters: []
span:
start: 171
length: 10
- Text:
content: "\n "
span:
start: 193
length: 9
- Block:
block_type: Standard
name: if
bits:
- if
- not
- forloop.last
children:
- Text:
content: ", "
span:
start: 218
length: 2
- Block:
block_type: Closing
name: endif
bits: []
children: ~
span:
start: 220
length: 10
tag_span:
start: 193
length: 19
span:
start: 193
length: 37
tag_span:
start: 193
length: 19
- Text:
content: "\n "
span:
start: 240
length: 9
- Block:
block_type: Standard
name: if
bits:
- if
- forloop.last
children:
- Text:
content: )
span:
start: 261
length: 1
- Block:
block_type: Closing
name: endif
bits: []
children: ~
span:
start: 262
length: 10
tag_span:
start: 240
length: 15
span:
start: 240
length: 32
tag_span:
start: 240
length: 15
- Text:
content: "\n "
span:
start: 278
length: 5
- Block:
block_type: Branch
name: empty
bits: []
children:
- Text:
content: "\n (no groups)\n "
span:
start: 298
length: 25
span:
start: 86
length: 0
tag_span:
start: 86
length: 24
- Block:
block_type: Closing
name: endfor
bits: []
children: ~
span:
start: 314
length: 11
tag_span:
start: 86
length: 24
span:
start: 86
length: 239
tag_span:
start: 86
length: 24
- Text:
content: "\n"
span:
start: 327
length: 1
- Block:
block_type: Branch
name: else
bits: []
children:
- Text:
content: "\n Guest\n"
span:
start: 342
length: 11
span:
start: 9
length: 0
tag_span:
start: 9
length: 24
- Block:
block_type: Closing
name: endif
bits: []
children: ~
span:
start: 348
length: 10
tag_span:
start: 9
length: 24
span:
start: 9
length: 349
length: 24
tag_span:
start: 9
length: 24
- Block:
block_type: Standard
name: for
bits:
- for
- group
- in
- user.groups
children:
- Text:
content: "\n "
span:
start: 125
length: 9
span:
start: 86
length: 24
tag_span:
start: 86
length: 24
- Block:
block_type: Standard
name: if
bits:
- if
- forloop.first
children:
- Text:
content: (
span:
start: 147
length: 1
span:
start: 125
length: 16
tag_span:
start: 125
length: 16
- Block:
block_type: Standalone
name: endif
bits:
- endif
children: ~
span:
start: 148
length: 5
tag_span:
start: 148
length: 5
- Text:
content: "\n "
span:
start: 168
length: 9
- Variable:
bits:
- group
- name
filters: []
span:
start: 171
length: 10
- Text:
content: "\n "
span:
start: 193
length: 9
- Block:
block_type: Standard
name: if
bits:
- if
- not
- forloop.last
children:
- Text:
content: ", "
span:
start: 218
length: 2
span:
start: 193
length: 19
tag_span:
start: 193
length: 19
- Block:
block_type: Standalone
name: endif
bits:
- endif
children: ~
span:
start: 220
length: 5
tag_span:
start: 220
length: 5
- Text:
content: "\n "
span:
start: 240
length: 9
- Block:
block_type: Standard
name: if
bits:
- if
- forloop.last
children:
- Text:
content: )
span:
start: 261
length: 1
span:
start: 240
length: 15
tag_span:
start: 240
length: 15
- Block:
block_type: Standalone
name: endif
bits:
- endif
children: ~
span:
start: 262
length: 5
tag_span:
start: 262
length: 5
- Text:
content: "\n "
span:
start: 278
length: 5
- Block:
block_type: Standalone
name: empty
bits:
- empty
children: ~
span:
start: 278
length: 5
tag_span:
start: 278
length: 5
- Text:
content: "\n (no groups)\n "
span:
start: 298
length: 25
- Block:
block_type: Standalone
name: endfor
bits:
- endfor
children: ~
span:
start: 314
length: 6
tag_span:
start: 314
length: 6
- Text:
content: "\n"
span:
start: 327
length: 1
- Block:
block_type: Standalone
name: else
bits:
- else
children: ~
span:
start: 327
length: 4
tag_span:
start: 327
length: 4
- Text:
content: "\n Guest\n"
span:
start: 342
length: 11
- Block:
block_type: Standalone
name: endif
bits:
- endif
children: ~
span:
start: 348
length: 5
tag_span:
start: 348
length: 5
- Text:
content: "!"
span:

View file

@ -0,0 +1,28 @@
---
source: crates/djls-template-ast/src/parser.rs
assertion_line: 556
expression: ast
snapshot_kind: text
---
nodes:
- Text:
content: "Welcome, "
span:
start: 0
length: 9
line_offsets:
- 0
- 40
- 82
- 117
- 160
- 185
- 232
- 274
- 290
- 310
- 327
- 338
- 348
errors:
- UnclosedTag: for

View file

@ -11,56 +11,58 @@ nodes:
- item
- in
- items
children:
- Block:
block_type: Standard
name: if
bits:
- if
- item.active
children:
- Variable:
bits:
- item
- name
filters: []
span:
start: 46
length: 9
- Block:
block_type: Closing
name: endif
bits: []
children: ~
span:
start: 58
length: 10
tag_span:
start: 23
length: 14
span:
start: 23
length: 45
tag_span:
start: 23
length: 14
- Block:
block_type: Closing
name: endfor
bits: []
children: ~
span:
start: 69
length: 11
tag_span:
start: 0
length: 17
children: []
span:
start: 0
length: 80
length: 17
tag_span:
start: 0
length: 17
- Block:
block_type: Standard
name: if
bits:
- if
- item.active
children:
- Variable:
bits:
- item
- name
filters: []
span:
start: 46
length: 9
span:
start: 23
length: 14
tag_span:
start: 23
length: 14
- Block:
block_type: Standalone
name: endif
bits:
- endif
children: ~
span:
start: 58
length: 5
tag_span:
start: 58
length: 5
- Block:
block_type: Standalone
name: endfor
bits:
- endfor
children: ~
span:
start: 69
length: 6
tag_span:
start: 69
length: 6
line_offsets:
- 0
errors: []

View file

@ -0,0 +1,46 @@
---
source: crates/djls-template-ast/src/parser.rs
assertion_line: 536
expression: ast
snapshot_kind: text
---
nodes:
- Block:
block_type: Standard
name: for
bits:
- for
- item
- in
- items
children:
- Block:
block_type: Standard
name: if
bits:
- if
- item.active
children:
- Variable:
bits:
- item
- name
filters: []
span:
start: 46
length: 9
span:
start: 23
length: 14
tag_span:
start: 23
length: 14
span:
start: 0
length: 17
tag_span:
start: 0
length: 17
line_offsets:
- 0
errors: []

View file

@ -53,60 +53,61 @@ nodes:
span:
start: 252
length: 9
- Block:
block_type: Standard
name: for
bits:
- for
- item
- in
- items
children:
- Text:
content: "\n <span>"
span:
start: 288
length: 19
- Variable:
bits:
- item
filters: []
span:
start: 297
length: 4
- Text:
content: "</span>\n "
span:
start: 304
length: 16
- Block:
block_type: Closing
name: endfor
bits: []
children: ~
span:
start: 320
length: 11
tag_span:
start: 252
length: 17
span:
start: 252
length: 79
tag_span:
start: 252
length: 17
- Text:
content: "\n <footer>Page Footer</footer>\n</div>"
span:
start: 337
length: 40
span:
start: 48
length: 0
length: 24
tag_span:
start: 48
length: 24
- Block:
block_type: Standard
name: for
bits:
- for
- item
- in
- items
children:
- Text:
content: "\n <span>"
span:
start: 288
length: 19
- Variable:
bits:
- item
filters: []
span:
start: 297
length: 4
- Text:
content: "</span>\n "
span:
start: 304
length: 16
span:
start: 252
length: 17
tag_span:
start: 252
length: 17
- Block:
block_type: Standalone
name: endfor
bits:
- endfor
children: ~
span:
start: 320
length: 6
tag_span:
start: 320
length: 6
- Text:
content: "\n <footer>Page Footer</footer>\n</div>"
span:
start: 337
length: 40
line_offsets:
- 0
- 24
@ -120,5 +121,4 @@ line_offsets:
- 312
- 333
- 366
errors:
- UnclosedTag: if
errors: []

View file

@ -0,0 +1,27 @@
---
source: crates/djls-template-ast/src/parser.rs
assertion_line: 669
expression: ast
snapshot_kind: text
---
nodes:
- Text:
content: "<div class=\"container\">\n <h1>Header</h1>\n "
span:
start: 0
length: 48
line_offsets:
- 0
- 24
- 44
- 79
- 131
- 170
- 184
- 244
- 276
- 312
- 333
- 366
errors:
- UnclosedTag: if

View file

@ -22,11 +22,10 @@ nodes:
length: 9
span:
start: 0
length: 0
length: 17
tag_span:
start: 0
length: 17
line_offsets:
- 0
errors:
- UnclosedTag: for
errors: []

View file

@ -0,0 +1,11 @@
---
source: crates/djls-template-ast/src/parser.rs
assertion_line: 630
expression: ast
snapshot_kind: text
---
nodes: []
line_offsets:
- 0
errors:
- UnclosedTag: for

View file

@ -17,11 +17,10 @@ nodes:
length: 7
span:
start: 0
length: 0
length: 24
tag_span:
start: 0
length: 24
line_offsets:
- 0
errors:
- UnclosedTag: if
errors: []

View file

@ -0,0 +1,11 @@
---
source: crates/djls-template-ast/src/parser.rs
assertion_line: 620
expression: ast
snapshot_kind: text
---
nodes: []
line_offsets:
- 0
errors:
- UnclosedTag: if

View file

@ -54,73 +54,76 @@ nodes:
span:
start: 598
length: 23
- Block:
block_type: Standard
name: if
bits:
- if
- user.is_staff
children:
- Text:
content: "\n <span>Admin</span>\n "
span:
start: 664
length: 56
- Block:
block_type: Branch
name: else
bits: []
children:
- Text:
content: "\n <span>User</span>\n "
span:
start: 730
length: 55
span:
start: 621
length: 0
tag_span:
start: 621
length: 16
- Block:
block_type: Closing
name: endif
bits: []
children: ~
span:
start: 764
length: 10
tag_span:
start: 621
length: 16
span:
start: 621
length: 153
tag_span:
start: 621
length: 16
- Text:
content: "\n "
span:
start: 788
length: 13
- Block:
block_type: Closing
name: endif
bits: []
children: ~
span:
start: 788
length: 10
tag_span:
start: 463
length: 24
span:
start: 463
length: 335
length: 24
tag_span:
start: 463
length: 24
- Block:
block_type: Standard
name: if
bits:
- if
- user.is_staff
children:
- Text:
content: "\n <span>Admin</span>\n "
span:
start: 664
length: 56
span:
start: 621
length: 16
tag_span:
start: 621
length: 16
- Block:
block_type: Standalone
name: else
bits:
- else
children: ~
span:
start: 699
length: 4
tag_span:
start: 699
length: 4
- Text:
content: "\n <span>User</span>\n "
span:
start: 730
length: 55
- Block:
block_type: Standalone
name: endif
bits:
- endif
children: ~
span:
start: 764
length: 5
tag_span:
start: 764
length: 5
- Text:
content: "\n "
span:
start: 788
length: 13
- Block:
block_type: Standalone
name: endif
bits:
- endif
children: ~
span:
start: 788
length: 5
tag_span:
start: 788
length: 5
- Text:
content: "\n </div>\n </body>\n</html>"
span:

View file

@ -0,0 +1,45 @@
---
source: crates/djls-template-ast/src/parser.rs
assertion_line: 712
expression: ast
snapshot_kind: text
---
nodes:
- Text:
content: "<!DOCTYPE html>\n<html>\n <head>\n <style type=\"text/css\">\n /* Style header */\n .header { color: blue; }\n </style>\n <script type=\"text/javascript\">\n // Init app\n const app = {\n /* Config */\n debug: true\n };\n </script>\n </head>\n <body>\n <!-- Header section -->\n <div class=\"header\" id=\"main\" data-value=\"123\" disabled>\n "
span:
start: 0
length: 463
line_offsets:
- 0
- 16
- 23
- 34
- 66
- 97
- 134
- 151
- 191
- 215
- 241
- 270
- 298
- 313
- 331
- 343
- 354
- 386
- 451
- 494
- 532
- 605
- 644
- 683
- 710
- 748
- 776
- 800
- 815
- 827
errors:
- UnclosedTag: if