Skip to content

Commit 70b9958

Browse files
update specs
1 parent c8c547a commit 70b9958

File tree

2 files changed

+40
-251
lines changed

2 files changed

+40
-251
lines changed

crates/djls-template-ast/SPEC.md

Lines changed: 40 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,17 @@ Enumeration of all possible node types in the AST.
3535
```rust
3636
pub enum Node {
3737
Text {
38-
content: String, // The raw text content
39-
span: Span, // The position of the text in the template
38+
content: String,
39+
span: Span,
4040
},
4141
Comment {
42-
content: String, // The comment content
43-
span: Span, // The position of the comment in the template
42+
content: String,
43+
span: Span,
4444
},
4545
Variable {
46-
bits: Vec<String>, // Components of the variable path
47-
filters: Vec<DjangoFilter>, // Filters applied to the variable
48-
span: Span, // The position of the variable in the template
46+
bits: Vec<String>,
47+
filters: Vec<DjangoFilter>,
48+
span: Span,
4949
},
5050
Block(Block),
5151
}
@@ -113,8 +113,8 @@ pub enum Block {
113113
Block {
114114
tag: Tag,
115115
nodes: Vec<Node>,
116-
closing: Option<Box<Block>>, // Contains Block::Closing if present
117-
assignments: Option<Vec<Assignment>>, // Assignments declared within the tag (e.g., `{% with var=value %}`)
116+
closing: Option<Box<Block>>,
117+
assignments: Option<Vec<Assignment>>,
118118
},
119119
Branch {
120120
tag: Tag,
@@ -270,16 +270,44 @@ Tag Specifications (TagSpecs) define how tags are parsed and understood. They al
270270

271271
```toml
272272
[package.module.path.tag_name] # Path where tag is registered, e.g., django.template.defaulttags
273-
type = "block" | "tag" | "inclusion" | "variable"
273+
type = "block" | "inclusion" | "tag" | "variable"
274274
closing = "closing_tag_name" # For block tags that require a closing tag
275-
supports_assignment = true | false # Whether the tag supports 'as' assignment
276275
branches = ["branch_tag_name", ...] # For block tags that support branches
277276

278277
[[package.module.path.tag_name.args]]
279278
name = "argument_name"
280279
required = true | false
281280
```
282281

282+
### Tag Types
283+
284+
- `block`: Tags that wrap content and require a closing tag
285+
286+
```django
287+
{% if condition %}content{% endif %}
288+
{% for item in items %}content{% endfor %}
289+
```
290+
291+
- `inclusion`: Tags that include or extend templates.
292+
293+
```django
294+
{% extends "base.html" %}
295+
{% include "partial.html" %}
296+
```
297+
298+
- `tag`: Single tags that don't wrap content
299+
300+
```django
301+
{% csrf_token %}
302+
```
303+
304+
- `variable`: Tags that output a value directly
305+
306+
```django
307+
{% cycle 'odd' 'even' %}
308+
{% firstof var1 var2 var3 %}
309+
```
310+
283311
### Configuration
284312

285313
- **Built-in TagSpecs**: The parser includes TagSpecs for Django's built-in tags and popular third-party tags.
@@ -293,7 +321,6 @@ required = true | false
293321
[django.template.defaulttags.if]
294322
type = "block"
295323
closing = "endif"
296-
supports_assignment = false
297324
branches = ["elif", "else"]
298325

299326
[[django.template.defaulttags.if.args]]
@@ -306,174 +333,19 @@ required = true
306333
```toml
307334
[django.template.defaulttags.includes]
308335
type = "inclusion"
309-
supports_assignment = true
310336

311337
[[django.template.defaulttags.includes.args]]
312338
name = "template_name"
313339
required = true
314340
```
315341

316-
#### Custom Tag Example
342+
#### Custom Tag
317343

318344
```toml
319345
[my_module.templatetags.my_tags.my_custom_tag]
320346
type = "tag"
321-
supports_assignment = true
322347

323348
{[my_module.templatetags.my_tags.my_custom_tag.args]]
324349
name = "arg1"
325350
required = false
326351
```
327-
328-
### AST Examples
329-
330-
#### Standard Block with Branches
331-
332-
Template:
333-
334-
```django
335-
{% if user.is_authenticated %}
336-
Hello, {{ user.name }}!
337-
{% elif user.is_guest %}
338-
Welcome, guest!
339-
{% else %}
340-
Please log in.
341-
{% endif %}
342-
```
343-
344-
AST Representation:
345-
346-
```rust
347-
Node::Block(Block::Block {
348-
tag: Tag {
349-
name: "if".to_string(),
350-
bits: vec!["user.is_authenticated".to_string()],
351-
span: Span { start: 0, length: 35 },
352-
tag_span: Span { start: 0, length: 28 },
353-
assignment: None,
354-
},
355-
nodes: vec![
356-
Node::Text {
357-
content: " Hello, ".to_string(),
358-
span: Span { start: 35, length: 12 },
359-
},
360-
Node::Variable {
361-
bits: vec!["user".to_string(), "name".to_string()],
362-
filters: vec![],
363-
span: Span { start: 47, length: 13 },
364-
},
365-
Node::Text {
366-
content: "!\n".to_string(),
367-
span: Span { start: 60, length: 2 },
368-
},
369-
Node::Block(Block::Branch {
370-
tag: Tag {
371-
name: "elif".to_string(),
372-
bits: vec!["user.is_guest".to_string()],
373-
span: Span { start: 62, length: 32 },
374-
tag_span: Span { start: 62, length: 26 },
375-
assignment: None,
376-
},
377-
nodes: vec![
378-
Node::Text {
379-
content: " Welcome, guest!\n".to_string(),
380-
span: Span { start: 94, length: 22 },
381-
},
382-
],
383-
}),
384-
Node::Block(Block::Branch {
385-
tag: Tag {
386-
name: "else".to_string(),
387-
bits: vec![],
388-
span: Span { start: 116, length: 22 },
389-
tag_span: Span { start: 116, length: 16 },
390-
assignment: None,
391-
},
392-
nodes: vec![
393-
Node::Text {
394-
content: " Please log in.\n".to_string(),
395-
span: Span { start: 138, length: 21 },
396-
},
397-
],
398-
}),
399-
],
400-
closing: Some(Box::new(Block::Closing {
401-
tag: Tag {
402-
name: "endif".to_string(),
403-
bits: vec![],
404-
span: Span { start: 159, length: 9 },
405-
tag_span: Span { start: 159, length: 9 },
406-
assignment: None,
407-
},
408-
})),
409-
assignments: None,
410-
})
411-
```
412-
413-
#### Inclusion Tag with Assignment
414-
415-
Template:
416-
417-
```django
418-
{% include "header.html" as header_content %}
419-
```
420-
421-
AST Representation:
422-
423-
```rust
424-
Node::Block(Block::Inclusion {
425-
tag: Tag {
426-
name: "include".to_string(),
427-
bits: vec!["\"header.html\"".to_string()],
428-
span: Span { start: 0, length: 45 },
429-
tag_span: Span { start: 0, length: 45 },
430-
assignment: Some("header_content".to_string()),
431-
},
432-
template_name: "header.html".to_string(),
433-
})
434-
```
435-
436-
#### Variable Tag
437-
438-
Template:
439-
440-
```django
441-
{% cycle 'odd' 'even' %}
442-
```
443-
444-
AST Representation:
445-
446-
```rust
447-
Node::Block(Block::Variable {
448-
tag: Tag {
449-
name: "cycle".to_string(),
450-
bits: vec!["'odd'".to_string(), "'even'".to_string()],
451-
span: Span { start: 0, length: 24 },
452-
tag_span: Span { start: 0, length: 24 },
453-
assignment: None,
454-
},
455-
})
456-
```
457-
458-
## LSP Support
459-
460-
The AST design supports integration with Language Server Protocol (LSP) features:
461-
462-
- **Diagnostics**:
463-
- Detect unclosed or mismatched tags.
464-
- Identify invalid arguments or unknown tags/filters.
465-
- Highlight syntax errors with precise location information.
466-
- **Code Navigation**:
467-
- Go to definitions of variables, tags, and included templates.
468-
- Find references and usages of variables and blocks.
469-
- Provide an outline of the template structure.
470-
- **Code Completion**:
471-
- Suggest tags, filters, and variables in context.
472-
- Auto-complete tag names and attributes based on TagSpecs.
473-
- **Hover Information**:
474-
- Display documentation and usage information for tags and filters.
475-
- Show variable types and values in context.
476-
- **Refactoring Tools**:
477-
- Support renaming of variables and blocks.
478-
- Assist in extracting or inlining templates.
479-
- Provide code actions for common refactoring tasks.

crates/djls-template-ast/tagspecs/README.md

Lines changed: 0 additions & 83 deletions
This file was deleted.

0 commit comments

Comments
 (0)