@@ -35,17 +35,17 @@ Enumeration of all possible node types in the AST.
3535``` rust
3636pub 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"
274274closing = " closing_tag_name" # For block tags that require a closing tag
275- supports_assignment = true | false # Whether the tag supports 'as' assignment
276275branches = [" branch_tag_name" , ...] # For block tags that support branches
277276
278277[[package.module.path.tag_name.args]]
279278name = " argument_name"
280279required = 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]
294322type = "block"
295323closing = "endif"
296- supports_assignment = false
297324branches = ["elif", "else"]
298325
299326[[django.template.defaulttags.if.args]]
@@ -306,174 +333,19 @@ required = true
306333```toml
307334[django.template.defaulttags.includes]
308335type = "inclusion"
309- supports_assignment = true
310336
311337[[django.template.defaulttags.includes.args]]
312338name = "template_name"
313339required = true
314340```
315341
316- # ### Custom Tag Example
342+ # ### Custom Tag
317343
318344```toml
319345[my_module.templatetags.my_tags.my_custom_tag]
320346type = "tag"
321- supports_assignment = true
322347
323348{[my_module.templatetags.my_tags.my_custom_tag.args]]
324349name = "arg1"
325350required = 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.
0 commit comments