mirror of
https://github.com/joshuadavidthomas/django-language-server.git
synced 2025-09-13 13:56:25 +00:00
wip
This commit is contained in:
parent
00eca98c6a
commit
f1cc33f173
21 changed files with 1323 additions and 651 deletions
482
crates/djls-template-ast/SPEC.md
Normal file
482
crates/djls-template-ast/SPEC.md
Normal file
|
@ -0,0 +1,482 @@
|
||||||
|
# Django Template AST Specification
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document specifies the Abstract Syntax Tree (AST) design for parsing Django templates. The AST represents the structure and semantics of Django templates, enabling accurate parsing, analysis, and tooling support.
|
||||||
|
|
||||||
|
## Types
|
||||||
|
|
||||||
|
### `Ast`
|
||||||
|
|
||||||
|
The root of the AST, representing the entire parsed template.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub struct Ast {
|
||||||
|
pub nodes: Vec<Node>, // Top-level nodes in the template
|
||||||
|
pub line_offsets: Vec<u32>, // Positions of line breaks for mapping offsets to line/column
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `Span`
|
||||||
|
|
||||||
|
Represents the position of a node within the source template.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub struct Span {
|
||||||
|
pub start: u32, // Byte offset from the start of the template
|
||||||
|
pub length: u32, // Length in bytes
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `Node`
|
||||||
|
|
||||||
|
Enumeration of all possible node types in the AST.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub enum Node {
|
||||||
|
Text {
|
||||||
|
content: String, // The raw text content
|
||||||
|
span: Span, // The position of the text in the template
|
||||||
|
},
|
||||||
|
Comment {
|
||||||
|
content: String, // The comment content
|
||||||
|
span: Span, // The position of the comment in the template
|
||||||
|
},
|
||||||
|
Variable {
|
||||||
|
bits: Vec<String>, // Components of the variable path
|
||||||
|
filters: Vec<DjangoFilter>, // Filters applied to the variable
|
||||||
|
span: Span, // The position of the variable in the template
|
||||||
|
},
|
||||||
|
Block(Block),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `Node::Text`
|
||||||
|
|
||||||
|
Represents raw text and HTML content outside of Django template tags.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
Node::Text {
|
||||||
|
content: String, // The raw text content
|
||||||
|
span: Span, // The position of the text in the template
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `Node::Comment`
|
||||||
|
|
||||||
|
Represents Django template comments (`{# ... #}`).
|
||||||
|
|
||||||
|
```rust
|
||||||
|
Node::Comment {
|
||||||
|
content: String, // The comment content
|
||||||
|
span: Span, // The position of the comment in the template
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `Node::Variable`
|
||||||
|
|
||||||
|
Represents variable interpolation (`{{ variable|filter }}`).
|
||||||
|
|
||||||
|
```rust
|
||||||
|
Node::Variable {
|
||||||
|
bits: Vec<String>, // Components of the variable path
|
||||||
|
filters: Vec<DjangoFilter>, // Filters applied to the variable
|
||||||
|
span: Span, // The position of the variable in the template
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### `DjangoFilter`
|
||||||
|
|
||||||
|
Represents a filter applied to a variable.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub struct DjangoFilter {
|
||||||
|
pub name: String, // Name of the filter
|
||||||
|
pub args: Vec<String>, // Arguments passed to the filter
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `Node::Block`
|
||||||
|
|
||||||
|
Represents Django template tags that may have nested content, assignments, and control flow structures.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
Node::Block(Block)
|
||||||
|
```
|
||||||
|
|
||||||
|
### `Block`
|
||||||
|
|
||||||
|
Represents Django template tags that may have nested content, assignments, and control flow structures.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub enum Block {
|
||||||
|
Block {
|
||||||
|
tag: Tag,
|
||||||
|
nodes: Vec<Node>,
|
||||||
|
closing: Option<Box<Block>>, // Contains Block::Closing if present
|
||||||
|
assignments: Option<Vec<Assignment>>, // Assignments declared within the tag (e.g., `{% with var=value %}`)
|
||||||
|
},
|
||||||
|
Branch {
|
||||||
|
tag: Tag,
|
||||||
|
nodes: Vec<Node>,
|
||||||
|
},
|
||||||
|
Tag {
|
||||||
|
tag: Tag,
|
||||||
|
},
|
||||||
|
Inclusion {
|
||||||
|
tag: Tag,
|
||||||
|
template_name: String,
|
||||||
|
},
|
||||||
|
Variable {
|
||||||
|
tag: Tag,
|
||||||
|
},
|
||||||
|
Closing {
|
||||||
|
tag: Tag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `Tag`
|
||||||
|
|
||||||
|
Shared structure for all tag-related nodes in `Block`.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub struct Tag {
|
||||||
|
pub name: String, // Name of the tag (e.g., "if", "for", "include")
|
||||||
|
pub bits: Vec<String>, // Arguments or components of the tag
|
||||||
|
pub span: Span, // Span covering the entire tag
|
||||||
|
pub tag_span: Span, // Span covering just the tag declaration (`{% tag ... %}`)
|
||||||
|
pub assignment: Option<String>, // Optional assignment target variable name
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `Assignment`
|
||||||
|
|
||||||
|
Represents an assignment within a tag (e.g., `{% with var=value %}` or `{% url 'some-view' as assigned_url %}`).
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub struct Assignment {
|
||||||
|
pub target: String, // Variable name to assign to
|
||||||
|
pub value: String, // Value assigned to the variable
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Variants
|
||||||
|
|
||||||
|
##### `Block::Block`
|
||||||
|
|
||||||
|
Represents standard block tags that may contain child nodes and require a closing tag.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
Block::Block {
|
||||||
|
tag: Tag, // The opening Tag of the block
|
||||||
|
nodes: Vec<Node>, // Nodes contained within the block
|
||||||
|
closing: Option<Box<Block>>, // Contains Block::Closing if present
|
||||||
|
assignments: Option<Vec<Assignment>>, // Assignments declared within the tag
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
- `{% if %}...{% endif %}`
|
||||||
|
- `{% for %}...{% endfor %}`
|
||||||
|
- `{% with %}...{% endwith %}`
|
||||||
|
|
||||||
|
##### `Block::Branch`
|
||||||
|
|
||||||
|
Represents branch tags that are part of control flow structures and contain child nodes.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
Block::Branch {
|
||||||
|
tag: Tag, // The Tag of the branch
|
||||||
|
nodes: Vec<Node>, // Nodes contained within the branch
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
- `{% elif %}`
|
||||||
|
- `{% else %}`
|
||||||
|
- `{% empty %}`
|
||||||
|
|
||||||
|
##### `Block::Tag`
|
||||||
|
|
||||||
|
Represents standalone tags that do not contain child nodes or require a closing tag.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
Block::Tag {
|
||||||
|
tag: Tag, // The Tag of the standalone tag
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
- `{% csrf_token %}`
|
||||||
|
- `{% load %}`
|
||||||
|
- `{% now "Y-m-d" %}`
|
||||||
|
|
||||||
|
##### `Block::Inclusion`
|
||||||
|
|
||||||
|
Represents tags that include or extend templates.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
Block::Inclusion {
|
||||||
|
tag: Tag, // The Tag of the inclusion tag
|
||||||
|
template_name: String, // Name of the template being included/extended
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
- `{% include "template.html" %}`
|
||||||
|
- `{% extends "base.html" %}`
|
||||||
|
|
||||||
|
##### `Block::Variable`
|
||||||
|
|
||||||
|
Represents tags that output a value directly.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
Block::Variable {
|
||||||
|
tag: Tag, // The Tag of the variable tag
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
- `{% cycle %}`
|
||||||
|
- `{% firstof %}`
|
||||||
|
|
||||||
|
##### `Block::Closing`
|
||||||
|
|
||||||
|
Represents closing tags corresponding to opening block tags.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
Block::Closing {
|
||||||
|
tag: Tag, // The Tag of the closing tag
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
- `{% endif %}`
|
||||||
|
- `{% endfor %}`
|
||||||
|
- `{% endwith %}`
|
||||||
|
|
||||||
|
## TagSpecs
|
||||||
|
|
||||||
|
### Schema
|
||||||
|
|
||||||
|
Tag Specifications (TagSpecs) define how tags are parsed and understood. They allow the parser to handle custom tags without hard-coding them.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[tag_name]
|
||||||
|
type = "block" | "branch" | "tag" | "inclusion" | "variable"
|
||||||
|
closing = "closing_tag_name" # For block tags that require a closing tag
|
||||||
|
supports_assignment = true | false # Whether the tag supports 'as' assignment
|
||||||
|
branches = ["branch_tag_name", ...] # For block tags that support branches
|
||||||
|
|
||||||
|
[[tag_name.args]]
|
||||||
|
name = "argument_name"
|
||||||
|
required = true | false
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
- **Built-in TagSpecs**: The parser includes TagSpecs for Django's built-in tags and popular third-party tags.
|
||||||
|
- **User-defined TagSpecs**: Users can expand or override TagSpecs via `pyproject.toml` or `djls.toml` files in their project, allowing custom tags and configurations to be seamlessly integrated.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
#### If Tag
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[if]
|
||||||
|
type = "block"
|
||||||
|
closing = "endif"
|
||||||
|
supports_assignment = false
|
||||||
|
branches = ["elif", "else"]
|
||||||
|
|
||||||
|
[[if.args]]
|
||||||
|
name = "condition"
|
||||||
|
required = true
|
||||||
|
position = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Include Tag
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[include]
|
||||||
|
type = "inclusion"
|
||||||
|
supports_assignment = true
|
||||||
|
|
||||||
|
[[include.args]]
|
||||||
|
name = "template_name"
|
||||||
|
required = true
|
||||||
|
position = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Custom Tag Example
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[my_custom_tag]
|
||||||
|
type = "tag"
|
||||||
|
supports_assignment = true
|
||||||
|
|
||||||
|
[[my_custom_tag.args]]
|
||||||
|
name = "arg1"
|
||||||
|
required = false
|
||||||
|
position = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
### AST Examples
|
||||||
|
|
||||||
|
#### Standard Block with Branches
|
||||||
|
|
||||||
|
Template:
|
||||||
|
|
||||||
|
```django
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
Hello, {{ user.name }}!
|
||||||
|
{% elif user.is_guest %}
|
||||||
|
Welcome, guest!
|
||||||
|
{% else %}
|
||||||
|
Please log in.
|
||||||
|
{% endif %}
|
||||||
|
```
|
||||||
|
|
||||||
|
AST Representation:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
Node::Block(Block::Block {
|
||||||
|
tag: Tag {
|
||||||
|
name: "if".to_string(),
|
||||||
|
bits: vec!["user.is_authenticated".to_string()],
|
||||||
|
span: Span { start: 0, length: 35 },
|
||||||
|
tag_span: Span { start: 0, length: 28 },
|
||||||
|
assignment: None,
|
||||||
|
},
|
||||||
|
nodes: vec![
|
||||||
|
Node::Text {
|
||||||
|
content: " Hello, ".to_string(),
|
||||||
|
span: Span { start: 35, length: 12 },
|
||||||
|
},
|
||||||
|
Node::Variable {
|
||||||
|
bits: vec!["user".to_string(), "name".to_string()],
|
||||||
|
filters: vec![],
|
||||||
|
span: Span { start: 47, length: 13 },
|
||||||
|
},
|
||||||
|
Node::Text {
|
||||||
|
content: "!\n".to_string(),
|
||||||
|
span: Span { start: 60, length: 2 },
|
||||||
|
},
|
||||||
|
Node::Block(Block::Branch {
|
||||||
|
tag: Tag {
|
||||||
|
name: "elif".to_string(),
|
||||||
|
bits: vec!["user.is_guest".to_string()],
|
||||||
|
span: Span { start: 62, length: 32 },
|
||||||
|
tag_span: Span { start: 62, length: 26 },
|
||||||
|
assignment: None,
|
||||||
|
},
|
||||||
|
nodes: vec![
|
||||||
|
Node::Text {
|
||||||
|
content: " Welcome, guest!\n".to_string(),
|
||||||
|
span: Span { start: 94, length: 22 },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
Node::Block(Block::Branch {
|
||||||
|
tag: Tag {
|
||||||
|
name: "else".to_string(),
|
||||||
|
bits: vec![],
|
||||||
|
span: Span { start: 116, length: 22 },
|
||||||
|
tag_span: Span { start: 116, length: 16 },
|
||||||
|
assignment: None,
|
||||||
|
},
|
||||||
|
nodes: vec![
|
||||||
|
Node::Text {
|
||||||
|
content: " Please log in.\n".to_string(),
|
||||||
|
span: Span { start: 138, length: 21 },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
closing: Some(Box::new(Block::Closing {
|
||||||
|
tag: Tag {
|
||||||
|
name: "endif".to_string(),
|
||||||
|
bits: vec![],
|
||||||
|
span: Span { start: 159, length: 9 },
|
||||||
|
tag_span: Span { start: 159, length: 9 },
|
||||||
|
assignment: None,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
assignments: None,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Inclusion Tag with Assignment
|
||||||
|
|
||||||
|
Template:
|
||||||
|
|
||||||
|
```django
|
||||||
|
{% include "header.html" as header_content %}
|
||||||
|
```
|
||||||
|
|
||||||
|
AST Representation:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
Node::Block(Block::Inclusion {
|
||||||
|
tag: Tag {
|
||||||
|
name: "include".to_string(),
|
||||||
|
bits: vec!["\"header.html\"".to_string()],
|
||||||
|
span: Span { start: 0, length: 45 },
|
||||||
|
tag_span: Span { start: 0, length: 45 },
|
||||||
|
assignment: Some("header_content".to_string()),
|
||||||
|
},
|
||||||
|
template_name: "header.html".to_string(),
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Variable Tag
|
||||||
|
|
||||||
|
Template:
|
||||||
|
|
||||||
|
```django
|
||||||
|
{% cycle 'odd' 'even' %}
|
||||||
|
```
|
||||||
|
|
||||||
|
AST Representation:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
Node::Block(Block::Variable {
|
||||||
|
tag: Tag {
|
||||||
|
name: "cycle".to_string(),
|
||||||
|
bits: vec!["'odd'".to_string(), "'even'".to_string()],
|
||||||
|
span: Span { start: 0, length: 24 },
|
||||||
|
tag_span: Span { start: 0, length: 24 },
|
||||||
|
assignment: None,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## LSP Support
|
||||||
|
|
||||||
|
The AST design supports integration with Language Server Protocol (LSP) features:
|
||||||
|
|
||||||
|
- **Diagnostics**:
|
||||||
|
- Detect unclosed or mismatched tags.
|
||||||
|
- Identify invalid arguments or unknown tags/filters.
|
||||||
|
- Highlight syntax errors with precise location information.
|
||||||
|
- **Code Navigation**:
|
||||||
|
- Go to definitions of variables, tags, and included templates.
|
||||||
|
- Find references and usages of variables and blocks.
|
||||||
|
- Provide an outline of the template structure.
|
||||||
|
- **Code Completion**:
|
||||||
|
- Suggest tags, filters, and variables in context.
|
||||||
|
- Auto-complete tag names and attributes based on TagSpecs.
|
||||||
|
- **Hover Information**:
|
||||||
|
- Display documentation and usage information for tags and filters.
|
||||||
|
- Show variable types and values in context.
|
||||||
|
- **Refactoring Tools**:
|
||||||
|
- Support renaming of variables and blocks.
|
||||||
|
- Assist in extracting or inlining templates.
|
||||||
|
- Provide code actions for common refactoring tasks.
|
|
@ -5,7 +5,6 @@ use thiserror::Error;
|
||||||
pub struct Ast {
|
pub struct Ast {
|
||||||
nodes: Vec<Node>,
|
nodes: Vec<Node>,
|
||||||
line_offsets: LineOffsets,
|
line_offsets: LineOffsets,
|
||||||
errors: Vec<AstError>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ast {
|
impl Ast {
|
||||||
|
@ -17,10 +16,6 @@ impl Ast {
|
||||||
&self.line_offsets
|
&self.line_offsets
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn errors(&self) -> &Vec<AstError> {
|
|
||||||
&self.errors
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_node(&mut self, node: Node) {
|
pub fn add_node(&mut self, node: Node) {
|
||||||
self.nodes.push(node);
|
self.nodes.push(node);
|
||||||
}
|
}
|
||||||
|
@ -29,12 +24,8 @@ impl Ast {
|
||||||
self.line_offsets = line_offsets
|
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> {
|
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);
|
return Err(AstError::EmptyAst);
|
||||||
}
|
}
|
||||||
Ok(self.clone())
|
Ok(self.clone())
|
||||||
|
@ -55,15 +46,13 @@ impl LineOffsets {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn position_to_line_col(&self, offset: u32) -> (u32, u32) {
|
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
|
// Find which line contains this offset by looking for the first line start
|
||||||
// that's greater than our position
|
// that's greater than our position
|
||||||
let line = match self.0.binary_search(&offset) {
|
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) => {
|
Err(next_line) => {
|
||||||
if next_line == 0 {
|
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 {
|
} else {
|
||||||
let prev_line = next_line - 1;
|
let prev_line = next_line - 1;
|
||||||
// If we're at the start of the next line, we're on that line
|
// 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
|
// Calculate column as offset from line start
|
||||||
let col = offset - self.0[line];
|
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)
|
(line as u32, col)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,11 +79,11 @@ impl LineOffsets {
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
|
||||||
pub struct Span {
|
pub struct Span {
|
||||||
start: u32,
|
start: u32,
|
||||||
length: u16,
|
length: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Span {
|
impl Span {
|
||||||
pub fn new(start: u32, length: u16) -> Self {
|
pub fn new(start: u32, length: u32) -> Self {
|
||||||
Self { start, length }
|
Self { start, length }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,28 +91,21 @@ impl Span {
|
||||||
&self.start
|
&self.start
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn length(&self) -> &u16 {
|
pub fn length(&self) -> &u32 {
|
||||||
&self.length
|
&self.length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
pub enum Node {
|
pub enum Node {
|
||||||
Text {
|
Block(Block),
|
||||||
content: String,
|
|
||||||
span: Span,
|
|
||||||
},
|
|
||||||
Comment {
|
Comment {
|
||||||
content: String,
|
content: String,
|
||||||
span: Span,
|
span: Span,
|
||||||
},
|
},
|
||||||
Block {
|
Text {
|
||||||
block_type: BlockType,
|
content: String,
|
||||||
name: String,
|
|
||||||
bits: Vec<String>,
|
|
||||||
children: Option<Vec<Node>>,
|
|
||||||
span: Span,
|
span: Span,
|
||||||
tag_span: Span,
|
|
||||||
},
|
},
|
||||||
Variable {
|
Variable {
|
||||||
bits: Vec<String>,
|
bits: Vec<String>,
|
||||||
|
@ -136,10 +115,45 @@ pub enum Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
pub enum BlockType {
|
pub enum Block {
|
||||||
Standard,
|
Block {
|
||||||
Branch,
|
tag: Tag,
|
||||||
Closing,
|
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)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
|
@ -271,8 +285,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Full block span should cover entire template
|
// Full block span should cover only the opening tag
|
||||||
assert_eq!(*span.length() as u32, 42);
|
assert_eq!(*span.length() as u32, 14);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,48 +306,43 @@ mod tests {
|
||||||
let ast = parser.parse().unwrap();
|
let ast = parser.parse().unwrap();
|
||||||
|
|
||||||
// Test nested block positions
|
// Test nested block positions
|
||||||
let (outer_if, inner_if) = {
|
let nodes = ast.nodes();
|
||||||
let nodes = ast.nodes();
|
let outer_if = nodes
|
||||||
let outer = 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()
|
.iter()
|
||||||
.find(|n| matches!(n, Node::Block { .. }))
|
.find(|n| matches!(n, Node::Block { .. }))
|
||||||
.unwrap();
|
.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 (
|
if let Node::Block {
|
||||||
Node::Block {
|
|
||||||
span: outer_span, ..
|
|
||||||
},
|
|
||||||
Node::Block {
|
|
||||||
span: inner_span, ..
|
span: inner_span, ..
|
||||||
},
|
} = inner_if
|
||||||
) = (outer_if, inner_if)
|
{
|
||||||
{
|
// Verify outer if starts at the right line/column
|
||||||
// Verify outer if starts at the right line/column
|
let (outer_line, outer_col) =
|
||||||
let (outer_line, outer_col) =
|
ast.line_offsets.position_to_line_col(*outer_span.start());
|
||||||
ast.line_offsets.position_to_line_col(*outer_span.start());
|
assert_eq!(
|
||||||
assert_eq!(
|
(outer_line, outer_col),
|
||||||
(outer_line, outer_col),
|
(1, 4),
|
||||||
(1, 4),
|
"Outer if should be indented"
|
||||||
"Outer if should be indented"
|
);
|
||||||
);
|
|
||||||
|
|
||||||
// Verify inner if is more indented than outer if
|
// Verify inner if is more indented than outer if
|
||||||
let (inner_line, inner_col) =
|
let (inner_line, inner_col) =
|
||||||
ast.line_offsets.position_to_line_col(*inner_span.start());
|
ast.line_offsets.position_to_line_col(*inner_span.start());
|
||||||
assert!(inner_col > outer_col, "Inner if should be more indented");
|
assert!(inner_col > outer_col, "Inner if should be more indented");
|
||||||
assert!(inner_line > outer_line, "Inner if should be on later line");
|
assert!(inner_line > outer_line, "Inner if should be on later line");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ast::{Ast, AstError, BlockType, DjangoFilter, LineOffsets, Node, Span};
|
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 crate::tokens::{Token, TokenStream, TokenType};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
@ -146,155 +146,102 @@ impl Parser {
|
||||||
let tag_name = bits.first().ok_or(AstError::EmptyTag)?.clone();
|
let tag_name = bits.first().ok_or(AstError::EmptyTag)?.clone();
|
||||||
|
|
||||||
let specs = TagSpec::load_builtin_specs().unwrap_or_default();
|
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
|
// Check if this is a closing tag
|
||||||
for (_, spec) in specs.iter() {
|
if let Some(spec) = tag_spec {
|
||||||
if Some(&tag_name) == spec.closing.as_ref()
|
if let Some(closing) = &spec.closing {
|
||||||
|| spec
|
if tag_name == *closing {
|
||||||
.branches
|
// This is a closing tag, return a signal instead of a node
|
||||||
.as_ref()
|
return Err(ParserError::Signal(Signal::ClosingTagFound(tag_name)));
|
||||||
.map(|ints| ints.iter().any(|i| i.name == tag_name))
|
}
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
return Err(ParserError::ErrorSignal(Signal::SpecialTag(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 children = Vec::new();
|
||||||
let mut current_branch: Option<(String, Vec<String>, Vec<Node>)> = None;
|
let mut found_closing = false;
|
||||||
let mut found_closing_tag = false;
|
let mut end_pos = start_pos + s.len() as u32;
|
||||||
|
|
||||||
while !self.is_at_end() {
|
while !self.is_at_end() {
|
||||||
match self.next_node() {
|
match self.peek()?.token_type() {
|
||||||
Ok(node) => {
|
TokenType::DjangoBlock(next_block) => {
|
||||||
if let Some((_, _, branch_children)) = &mut current_branch {
|
let next_bits: Vec<String> = next_block.split_whitespace().map(String::from).collect();
|
||||||
branch_children.push(node);
|
if let Some(next_tag) = next_bits.first() {
|
||||||
} else {
|
// Check if this is a closing tag
|
||||||
children.push(node);
|
if let Some(spec) = tag_spec {
|
||||||
}
|
if let Some(closing) = &spec.closing {
|
||||||
}
|
if next_tag == closing {
|
||||||
Err(ParserError::ErrorSignal(Signal::SpecialTag(tag))) => {
|
found_closing = true;
|
||||||
if let Some(spec) = &tag_spec {
|
let closing_token = self.consume()?;
|
||||||
// Check if closing tag
|
end_pos = closing_token.start().unwrap_or(0) as u32 + next_block.len() as u32;
|
||||||
if spec.closing.as_deref() == Some(&tag) {
|
break;
|
||||||
// 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
|
// Check if this is a branch tag
|
||||||
children.push(Node::Block {
|
if let Some(branches) = &spec.branches {
|
||||||
block_type: BlockType::Branch,
|
if branches.iter().any(|b| &b.name == next_tag) {
|
||||||
name,
|
children.push(self.next_node()?);
|
||||||
bits,
|
continue;
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
// 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
|
children.push(self.next_node()?);
|
||||||
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)));
|
|
||||||
}
|
}
|
||||||
Err(ParserError::Ast(AstError::StreamError(kind), _)) if kind == "AtEnd" => {
|
_ => children.push(self.next_node()?),
|
||||||
break;
|
|
||||||
}
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let span = Span::new(start_pos, 0); // Removed total_length initialization
|
if !found_closing && tag_spec.map_or(false, |s| s.closing.is_some()) {
|
||||||
|
return Err(ParserError::from(
|
||||||
let node = Node::Block {
|
AstError::UnclosedTag(tag_name.clone()),
|
||||||
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),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
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> {
|
fn parse_django_variable(&mut self, s: &str) -> Result<Node, ParserError> {
|
||||||
|
|
|
@ -17,58 +17,61 @@ nodes:
|
||||||
span:
|
span:
|
||||||
start: 14
|
start: 14
|
||||||
length: 8
|
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:
|
span:
|
||||||
start: 0
|
start: 0
|
||||||
length: 70
|
length: 8
|
||||||
tag_span:
|
tag_span:
|
||||||
start: 0
|
start: 0
|
||||||
length: 8
|
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:
|
line_offsets:
|
||||||
- 0
|
- 0
|
||||||
errors: []
|
errors: []
|
||||||
|
|
|
@ -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
|
|
@ -19,39 +19,41 @@ nodes:
|
||||||
span:
|
span:
|
||||||
start: 26
|
start: 26
|
||||||
length: 4
|
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:
|
span:
|
||||||
start: 0
|
start: 0
|
||||||
length: 63
|
length: 17
|
||||||
tag_span:
|
tag_span:
|
||||||
start: 0
|
start: 0
|
||||||
length: 17
|
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:
|
line_offsets:
|
||||||
- 0
|
- 0
|
||||||
errors: []
|
errors: []
|
||||||
|
|
|
@ -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
|
|
@ -15,23 +15,24 @@ nodes:
|
||||||
span:
|
span:
|
||||||
start: 30
|
start: 30
|
||||||
length: 7
|
length: 7
|
||||||
- Block:
|
|
||||||
block_type: Closing
|
|
||||||
name: endif
|
|
||||||
bits: []
|
|
||||||
children: ~
|
|
||||||
span:
|
|
||||||
start: 37
|
|
||||||
length: 10
|
|
||||||
tag_span:
|
|
||||||
start: 0
|
|
||||||
length: 24
|
|
||||||
span:
|
span:
|
||||||
start: 0
|
start: 0
|
||||||
length: 47
|
length: 24
|
||||||
tag_span:
|
tag_span:
|
||||||
start: 0
|
start: 0
|
||||||
length: 24
|
length: 24
|
||||||
|
- Block:
|
||||||
|
block_type: Standalone
|
||||||
|
name: endif
|
||||||
|
bits:
|
||||||
|
- endif
|
||||||
|
children: ~
|
||||||
|
span:
|
||||||
|
start: 37
|
||||||
|
length: 5
|
||||||
|
tag_span:
|
||||||
|
start: 37
|
||||||
|
length: 5
|
||||||
line_offsets:
|
line_offsets:
|
||||||
- 0
|
- 0
|
||||||
errors: []
|
errors: []
|
||||||
|
|
|
@ -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: []
|
|
@ -44,207 +44,214 @@ nodes:
|
||||||
span:
|
span:
|
||||||
start: 86
|
start: 86
|
||||||
length: 5
|
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:
|
span:
|
||||||
start: 9
|
start: 9
|
||||||
length: 349
|
length: 24
|
||||||
tag_span:
|
tag_span:
|
||||||
start: 9
|
start: 9
|
||||||
length: 24
|
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:
|
- Text:
|
||||||
content: "!"
|
content: "!"
|
||||||
span:
|
span:
|
||||||
|
|
|
@ -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
|
|
@ -11,56 +11,58 @@ nodes:
|
||||||
- item
|
- item
|
||||||
- in
|
- in
|
||||||
- items
|
- items
|
||||||
children:
|
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
|
|
||||||
span:
|
span:
|
||||||
start: 0
|
start: 0
|
||||||
length: 80
|
length: 17
|
||||||
tag_span:
|
tag_span:
|
||||||
start: 0
|
start: 0
|
||||||
length: 17
|
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:
|
line_offsets:
|
||||||
- 0
|
- 0
|
||||||
errors: []
|
errors: []
|
||||||
|
|
|
@ -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: []
|
|
@ -53,60 +53,61 @@ nodes:
|
||||||
span:
|
span:
|
||||||
start: 252
|
start: 252
|
||||||
length: 9
|
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:
|
span:
|
||||||
start: 48
|
start: 48
|
||||||
length: 0
|
length: 24
|
||||||
tag_span:
|
tag_span:
|
||||||
start: 48
|
start: 48
|
||||||
length: 24
|
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:
|
line_offsets:
|
||||||
- 0
|
- 0
|
||||||
- 24
|
- 24
|
||||||
|
@ -120,5 +121,4 @@ line_offsets:
|
||||||
- 312
|
- 312
|
||||||
- 333
|
- 333
|
||||||
- 366
|
- 366
|
||||||
errors:
|
errors: []
|
||||||
- UnclosedTag: if
|
|
||||||
|
|
|
@ -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
|
|
@ -22,11 +22,10 @@ nodes:
|
||||||
length: 9
|
length: 9
|
||||||
span:
|
span:
|
||||||
start: 0
|
start: 0
|
||||||
length: 0
|
length: 17
|
||||||
tag_span:
|
tag_span:
|
||||||
start: 0
|
start: 0
|
||||||
length: 17
|
length: 17
|
||||||
line_offsets:
|
line_offsets:
|
||||||
- 0
|
- 0
|
||||||
errors:
|
errors: []
|
||||||
- UnclosedTag: for
|
|
||||||
|
|
|
@ -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
|
|
@ -17,11 +17,10 @@ nodes:
|
||||||
length: 7
|
length: 7
|
||||||
span:
|
span:
|
||||||
start: 0
|
start: 0
|
||||||
length: 0
|
length: 24
|
||||||
tag_span:
|
tag_span:
|
||||||
start: 0
|
start: 0
|
||||||
length: 24
|
length: 24
|
||||||
line_offsets:
|
line_offsets:
|
||||||
- 0
|
- 0
|
||||||
errors:
|
errors: []
|
||||||
- UnclosedTag: if
|
|
||||||
|
|
|
@ -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
|
|
@ -54,73 +54,76 @@ nodes:
|
||||||
span:
|
span:
|
||||||
start: 598
|
start: 598
|
||||||
length: 23
|
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:
|
span:
|
||||||
start: 463
|
start: 463
|
||||||
length: 335
|
length: 24
|
||||||
tag_span:
|
tag_span:
|
||||||
start: 463
|
start: 463
|
||||||
length: 24
|
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:
|
- Text:
|
||||||
content: "\n </div>\n </body>\n</html>"
|
content: "\n </div>\n </body>\n</html>"
|
||||||
span:
|
span:
|
||||||
|
|
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue