diff --git a/crates/djls-semantic/src/db.rs b/crates/djls-semantic/src/db.rs index b49d985..2e9eaa9 100644 --- a/crates/djls-semantic/src/db.rs +++ b/crates/djls-semantic/src/db.rs @@ -2,12 +2,27 @@ use std::sync::Arc; use djls_templates::Db as TemplateDb; use djls_workspace::Db as WorkspaceDb; +use tower_lsp_server::lsp_types; use crate::specs::TagSpecs; -/// Semantic database trait extending the template and workspace databases #[salsa::db] pub trait SemanticDb: TemplateDb + WorkspaceDb { /// Get the Django tag specifications for semantic analysis fn tag_specs(&self) -> Arc; } + +#[salsa::accumulator] +pub struct SemanticDiagnostic(pub lsp_types::Diagnostic); + +impl From for lsp_types::Diagnostic { + fn from(diagnostic: SemanticDiagnostic) -> Self { + diagnostic.0 + } +} + +impl From<&SemanticDiagnostic> for lsp_types::Diagnostic { + fn from(diagnostic: &SemanticDiagnostic) -> Self { + diagnostic.0.clone() + } +} diff --git a/crates/djls-semantic/src/lib.rs b/crates/djls-semantic/src/lib.rs index e378c03..e37e415 100644 --- a/crates/djls-semantic/src/lib.rs +++ b/crates/djls-semantic/src/lib.rs @@ -6,6 +6,8 @@ pub mod validation; pub use builtins::django_builtin_specs; pub use db::SemanticDb; +pub use db::SemanticDiagnostic; +use salsa::Accumulator; pub use snippets::generate_partial_snippet; pub use snippets::generate_snippet_for_tag; pub use snippets::generate_snippet_for_tag_with_end; @@ -17,6 +19,7 @@ pub use specs::SimpleArgType; pub use specs::TagArg; pub use specs::TagSpec; pub use specs::TagSpecs; +use tower_lsp_server::lsp_types; pub use validation::TagValidator; pub enum TagType { @@ -40,3 +43,46 @@ impl TagType { } } } + +/// Validate a Django template node list and return validation errors. +/// +/// This function runs the TagValidator on the parsed node list to check for: +/// - Unclosed block tags +/// - Mismatched tag pairs +/// - Orphaned intermediate tags +/// - Invalid argument counts +/// - Unmatched block names +#[salsa::tracked] +pub fn validate_nodelist(db: &dyn SemanticDb, nodelist: djls_templates::NodeList<'_>) { + if nodelist.nodelist(db).is_empty() { + return; + } + + let validation_errors = TagValidator::new(db, nodelist).validate(); + + let line_offsets = nodelist.line_offsets(db); + for error in validation_errors { + let code = error.diagnostic_code(); + let range = error + .span() + .map(|(start, length)| { + let span = djls_templates::nodelist::Span::new(start, length); + span.to_lsp_range(line_offsets) + }) + .unwrap_or_default(); + + let diagnostic = lsp_types::Diagnostic { + range, + severity: Some(lsp_types::DiagnosticSeverity::ERROR), + code: Some(lsp_types::NumberOrString::String(code.to_string())), + code_description: None, + source: Some("Django Language Server".to_string()), + message: error.to_string(), + related_information: None, + tags: None, + data: None, + }; + + SemanticDiagnostic(diagnostic).accumulate(db); + } +} diff --git a/crates/djls-semantic/src/validation.rs b/crates/djls-semantic/src/validation.rs index 003e567..10b323c 100644 --- a/crates/djls-semantic/src/validation.rs +++ b/crates/djls-semantic/src/validation.rs @@ -15,13 +15,13 @@ //! ## Architecture //! //! The `TagValidator` follows the same pattern as the Parser and Lexer, -//! maintaining minimal state and walking through the AST to accumulate errors. +//! maintaining minimal state and walking through the node list to accumulate errors. -use djls_templates::ast::Node; -use djls_templates::ast::NodeListError; -use djls_templates::ast::Span; -use djls_templates::ast::TagBit; -use djls_templates::ast::TagName; +use djls_templates::nodelist::Node; +use djls_templates::nodelist::NodeListError; +use djls_templates::nodelist::Span; +use djls_templates::nodelist::TagBit; +use djls_templates::nodelist::TagName; use djls_templates::NodeList; use crate::db::SemanticDb; diff --git a/crates/djls-server/src/server.rs b/crates/djls-server/src/server.rs index 7ce742a..f6247f8 100644 --- a/crates/djls-server/src/server.rs +++ b/crates/djls-server/src/server.rs @@ -2,7 +2,9 @@ use std::future::Future; use std::sync::Arc; use djls_project::Db as ProjectDb; -use djls_templates::analyze_template; +use djls_semantic::validate_nodelist; +use djls_semantic::SemanticDiagnostic; +use djls_templates::parse_template; use djls_templates::TemplateDiagnostic; use djls_workspace::paths; use djls_workspace::FileKind; @@ -96,14 +98,26 @@ impl DjangoLanguageServer { let file = session.get_or_create_file(&path); session.with_db(|db| { - // Parse and validate the template (triggers accumulation) - // This should be a cheap call since salsa should cache the function - // call, but we may need to revisit if that assumption is incorrect - let _ast = analyze_template(db, file); + let Some(nodelist) = parse_template(db, file) else { + // If parsing failed completely, just return syntax errors + return parse_template::accumulated::(db, file) + .into_iter() + .map(Into::into) + .collect(); + }; - let diagnostics = analyze_template::accumulated::(db, file); + validate_nodelist(db, nodelist); - diagnostics.into_iter().map(Into::into).collect() + let syntax_diagnostics = + parse_template::accumulated::(db, file); + let semantic_diagnostics = + validate_nodelist::accumulated::(db, nodelist); + + syntax_diagnostics + .into_iter() + .map(Into::into) + .chain(semantic_diagnostics.into_iter().map(Into::into)) + .collect() }) }) .await; @@ -240,7 +254,6 @@ impl LanguageServer for DjangoLanguageServer { }) .await; - // Publish diagnostics for template files if let Some((url, version)) = url_version { self.publish_diagnostics(&url, Some(version)).await; } @@ -262,7 +275,6 @@ impl LanguageServer for DjangoLanguageServer { }) .await; - // Publish diagnostics for template files if let Some((url, version)) = url_version { self.publish_diagnostics(&url, version).await; } @@ -426,14 +438,25 @@ impl LanguageServer for DjangoLanguageServer { return vec![]; }; - // Parse and validate the template (triggers accumulation) - let _ast = analyze_template(db, file); + let Some(nodelist) = parse_template(db, file) else { + return parse_template::accumulated::(db, file) + .into_iter() + .map(Into::into) + .collect(); + }; - // Get accumulated diagnostics directly - they're already LSP diagnostics! - let diagnostics = analyze_template::accumulated::(db, file); + validate_nodelist(db, nodelist); - // Convert from TemplateDiagnostic wrapper to lsp_types::Diagnostic - diagnostics.into_iter().map(Into::into).collect() + let syntax_diagnostics = + parse_template::accumulated::(db, file); + let semantic_diagnostics = + validate_nodelist::accumulated::(db, nodelist); + + syntax_diagnostics + .into_iter() + .map(Into::into) + .chain(semantic_diagnostics.into_iter().map(Into::into)) + .collect() }) }) .await; diff --git a/crates/djls-templates/src/db.rs b/crates/djls-templates/src/db.rs index cfbc481..89db3c7 100644 --- a/crates/djls-templates/src/db.rs +++ b/crates/djls-templates/src/db.rs @@ -14,30 +14,30 @@ //! ## Key Components //! //! - [`Db`]: Database trait extending the workspace database -//! - [`analyze_template`]: Main entry point for template analysis +//! - [`parse_template`]: Main entry point for template parsing //! - [`TemplateDiagnostic`]: Accumulator for collecting LSP diagnostics //! //! ## Incremental Computation //! //! When a template file changes: //! 1. Salsa invalidates the cached AST for that file -//! 2. Next access to `analyze_template` triggers reparse +//! 2. Next access to `parse_template` triggers reparse //! 3. Diagnostics are accumulated during parse/validation //! 4. Other files remain cached unless they also changed //! //! ## Example //! //! ```ignore -//! // Analyze a template and get its AST -//! let ast = analyze_template(db, file); +//! // Parse a template and get its AST +//! let nodelist = parse_template(db, file); //! //! // Retrieve accumulated diagnostics -//! let diagnostics = analyze_template::accumulated::(db, file); +//! let diagnostics = parse_template::accumulated::(db, file); //! //! // Get diagnostics for all workspace files //! for file in workspace.files() { -//! let _ = analyze_template(db, file); // Trigger analysis -//! let diags = analyze_template::accumulated::(db, file); +//! let _ = parse_template(db, file); // Trigger parsing +//! let diags = parse_template::accumulated::(db, file); //! // Process diagnostics... //! } //! ``` diff --git a/crates/djls-templates/src/error.rs b/crates/djls-templates/src/error.rs index aaed385..ef4a20b 100644 --- a/crates/djls-templates/src/error.rs +++ b/crates/djls-templates/src/error.rs @@ -1,7 +1,7 @@ use serde::Serialize; use thiserror::Error; -use crate::ast::NodeListError; +use crate::nodelist::NodeListError; use crate::parser::ParserError; #[derive(Clone, Debug, Error, PartialEq, Eq, Serialize)] diff --git a/crates/djls-templates/src/lexer.rs b/crates/djls-templates/src/lexer.rs index fe36abb..98ce7cc 100644 --- a/crates/djls-templates/src/lexer.rs +++ b/crates/djls-templates/src/lexer.rs @@ -1,5 +1,5 @@ -use crate::ast::LineOffsets; use crate::db::Db as TemplateDb; +use crate::nodelist::LineOffsets; use crate::tokens::Token; use crate::tokens::TokenContent; diff --git a/crates/djls-templates/src/lib.rs b/crates/djls-templates/src/lib.rs index 8908069..6823f5f 100644 --- a/crates/djls-templates/src/lib.rs +++ b/crates/djls-templates/src/lib.rs @@ -2,7 +2,7 @@ //! //! This crate provides comprehensive support for Django template files including: //! - Lexical analysis and tokenization -//! - Parsing into an Abstract Syntax Tree (AST) +//! - Parsing into a flat node list //! - Validation using configurable tag specifications //! - LSP diagnostic generation with Salsa integration //! @@ -11,13 +11,13 @@ //! The system uses a multi-stage pipeline: //! //! 1. **Lexing**: Template text is tokenized into Django constructs (tags, variables, text) -//! 2. **Parsing**: Tokens are parsed into a structured AST -//! 3. **Validation**: The AST is validated using the visitor pattern +//! 2. **Parsing**: Tokens are parsed into a flat node list +//! 3. **Validation**: The node list is validated using the visitor pattern //! 4. **Diagnostics**: Errors are converted to LSP diagnostics via Salsa accumulators //! //! ## Key Components //! -//! - [`ast`]: AST node definitions and visitor pattern implementation +//! - [`nodelist`]: Node list definitions and structure //! - [`db`]: Salsa database integration for incremental computation //! - [`validation`]: Validation rules using the visitor pattern //! - [`tagspecs`]: Django tag specifications for validation @@ -32,43 +32,40 @@ //! //! ```ignore //! // For LSP integration with Salsa (primary usage): -//! use djls_templates::db::{analyze_template, TemplateDiagnostic}; +//! use djls_templates::{parse_template, TemplateDiagnostic}; //! -//! let ast = analyze_template(db, file); -//! let diagnostics = analyze_template::accumulated::(db, file); +//! let nodelist = parse_template(db, file); +//! let diagnostics = parse_template::accumulated::(db, file); //! //! // For direct parsing (testing/debugging): //! use djls_templates::{Lexer, Parser}; //! //! let tokens = Lexer::new(source).tokenize()?; //! let mut parser = Parser::new(tokens); -//! let (ast, errors) = parser.parse()?; +//! let (nodelist, errors) = parser.parse()?; //! ``` -pub mod ast; pub mod db; mod error; mod lexer; +pub mod nodelist; mod parser; mod tokens; -use ast::LineOffsets; -pub use ast::NodeList; pub use db::Db; pub use db::TemplateDiagnostic; use djls_workspace::db::SourceFile; use djls_workspace::FileKind; pub use error::TemplateError; pub use lexer::Lexer; +use nodelist::LineOffsets; +pub use nodelist::NodeList; pub use parser::Parser; pub use parser::ParserError; use salsa::Accumulator; use tokens::TokenStream; /// Lex a template file into tokens. -/// -/// This is the first phase of template processing. It tokenizes the source text -/// into Django-specific tokens (tags, variables, text, etc.). #[salsa::tracked] fn lex_template(db: &dyn Db, file: SourceFile) -> TokenStream<'_> { if file.kind(db) != FileKind::Template { @@ -82,53 +79,55 @@ fn lex_template(db: &dyn Db, file: SourceFile) -> TokenStream<'_> { TokenStream::new(db, tokens, line_offsets) } -/// Parse tokens into an AST. +/// Parse a Django template file and accumulate diagnostics. /// -/// This is the second phase of template processing. It takes the token stream -/// from lexing and builds an Abstract Syntax Tree. +/// Diagnostics can be retrieved using: +/// ```ignore +/// let diagnostics = +/// parse_template::accumulated::(db, file); +/// ``` #[salsa::tracked] -fn parse_template(db: &dyn Db, file: SourceFile) -> NodeList<'_> { - let token_stream = lex_template(db, file); - - // Check if lexing produced no tokens (likely due to an error) - if token_stream.stream(db).is_empty() { - // Return empty AST for error recovery - let empty_nodelist = Vec::new(); - let empty_offsets = LineOffsets::default(); - return NodeList::new(db, empty_nodelist, empty_offsets); +pub fn parse_template(db: &dyn Db, file: SourceFile) -> Option> { + if file.kind(db) != FileKind::Template { + return None; } - // Parser needs the TokenStream<'db> - match Parser::new(db, token_stream).parse() { - Ok((ast, errors)) => { - // Accumulate parser errors + let token_stream = lex_template(db, file); + + if token_stream.stream(db).is_empty() { + let empty_nodelist = Vec::new(); + let empty_offsets = LineOffsets::default(); + return Some(NodeList::new(db, empty_nodelist, empty_offsets)); + } + + let nodelist = match Parser::new(db, token_stream).parse() { + Ok((nodelist, errors)) => { for error in errors { let template_error = TemplateError::Parser(error.to_string()); - accumulate_error(db, &template_error, ast.line_offsets(db)); + accumulate_error(db, &template_error, nodelist.line_offsets(db)); } - ast + nodelist } Err(err) => { - // Critical parser error let template_error = TemplateError::Parser(err.to_string()); let empty_offsets = LineOffsets::default(); accumulate_error(db, &template_error, &empty_offsets); - // Return empty AST let empty_nodelist = Vec::new(); let empty_offsets = LineOffsets::default(); NodeList::new(db, empty_nodelist, empty_offsets) } - } + }; + + Some(nodelist) } -/// Helper function to convert errors to LSP diagnostics and accumulate fn accumulate_error(db: &dyn Db, error: &TemplateError, line_offsets: &LineOffsets) { let code = error.diagnostic_code(); let range = error .span() .map(|(start, length)| { - let span = crate::ast::Span::new(start, length); + let span = crate::nodelist::Span::new(start, length); span.to_lsp_range(line_offsets) }) .unwrap_or_default(); @@ -152,30 +151,3 @@ fn accumulate_error(db: &dyn Db, error: &TemplateError, line_offsets: &LineOffse TemplateDiagnostic(diagnostic).accumulate(db); } - -/// Analyze a Django template file - parse and accumulate diagnostics. -/// -/// This is the PRIMARY function for template processing. It's a Salsa tracked function -/// that orchestrates the parsing phases of template processing: -/// 1. Lexing (tokenization) -/// 2. Parsing (AST construction) -/// -/// Validation has been moved to the djls-semantic crate for semantic analysis. -/// -/// Each phase is independently cached by Salsa, allowing for fine-grained -/// incremental computation. -/// -/// The function returns the parsed AST (or None for non-template files). -/// -/// Diagnostics can be retrieved using: -/// ```ignore -/// let diagnostics = -/// analyze_template::accumulated::(db, file); -/// ``` -#[salsa::tracked] -pub fn analyze_template(db: &dyn Db, file: SourceFile) -> Option> { - if file.kind(db) != FileKind::Template { - return None; - } - Some(parse_template(db, file)) -} diff --git a/crates/djls-templates/src/ast.rs b/crates/djls-templates/src/nodelist.rs similarity index 100% rename from crates/djls-templates/src/ast.rs rename to crates/djls-templates/src/nodelist.rs diff --git a/crates/djls-templates/src/parser.rs b/crates/djls-templates/src/parser.rs index a0026dd..3b56dac 100644 --- a/crates/djls-templates/src/parser.rs +++ b/crates/djls-templates/src/parser.rs @@ -1,16 +1,16 @@ use serde::Serialize; use thiserror::Error; -use crate::ast::FilterName; -use crate::ast::LineOffsets; -use crate::ast::Node; -use crate::ast::NodeList; -use crate::ast::NodeListError; -use crate::ast::Span; -use crate::ast::TagBit; -use crate::ast::TagName; -use crate::ast::VariableName; use crate::db::Db as TemplateDb; +use crate::nodelist::FilterName; +use crate::nodelist::LineOffsets; +use crate::nodelist::Node; +use crate::nodelist::NodeList; +use crate::nodelist::NodeListError; +use crate::nodelist::Span; +use crate::nodelist::TagBit; +use crate::nodelist::TagName; +use crate::nodelist::VariableName; use crate::tokens::Token; use crate::tokens::TokenStream; @@ -51,9 +51,9 @@ impl<'db> Parser<'db> { } } - let ast = NodeList::new(self.db, nodelist, self.line_offsets.clone()); + let nodelist = NodeList::new(self.db, nodelist, self.line_offsets.clone()); - Ok((ast, std::mem::take(&mut self.errors))) + Ok((nodelist, std::mem::take(&mut self.errors))) } fn next_node(&mut self) -> Result, ParserError> { @@ -287,7 +287,7 @@ pub enum ParseError { #[error("Stream error: {kind:?}")] StreamError { kind: StreamError }, - #[error("AST error: {0}")] + #[error("Node list error: {0}")] NodeList(#[from] NodeListError), } @@ -357,12 +357,12 @@ mod tests { let (tokens, line_offsets) = Lexer::new(db, source).tokenize(); let token_stream = TokenStream::new(db, tokens, line_offsets); let mut parser = Parser::new(db, token_stream); - let (ast, _) = parser.parse().unwrap(); - ast + let (nodelist, _) = parser.parse().unwrap(); + nodelist } #[derive(Debug, Clone, PartialEq, Serialize)] - struct TestAst { + struct TestNodeList { nodelist: Vec, line_offsets: Vec, } @@ -413,10 +413,13 @@ mod tests { } } - fn convert_ast_for_testing(ast: NodeList<'_>, db: &dyn crate::db::Db) -> TestAst { - TestAst { - nodelist: convert_nodelist_for_testing(ast.nodelist(db), db), - line_offsets: ast.line_offsets(db).0.clone(), + fn convert_nodelist_for_testing_wrapper( + nodelist: NodeList<'_>, + db: &dyn crate::db::Db, + ) -> TestNodeList { + TestNodeList { + nodelist: convert_nodelist_for_testing(nodelist.nodelist(db), db), + line_offsets: nodelist.line_offsets(db).0.clone(), } } @@ -432,9 +435,9 @@ mod tests { let db = TestDatabase::new(); let source = "".to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } #[test] @@ -442,9 +445,9 @@ mod tests { let db = TestDatabase::new(); let source = "
Hello
".to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } #[test] @@ -452,9 +455,9 @@ mod tests { let db = TestDatabase::new(); let source = "".to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } } @@ -466,9 +469,9 @@ mod tests { let db = TestDatabase::new(); let source = "{{ user.name }}".to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } #[test] @@ -476,9 +479,9 @@ mod tests { let db = TestDatabase::new(); let source = "{{ user.name|title }}".to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } #[test] @@ -486,9 +489,9 @@ mod tests { let db = TestDatabase::new(); let source = "{{ value|default:'nothing'|title|upper }}".to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } #[test] @@ -496,9 +499,9 @@ mod tests { let db = TestDatabase::new(); let source = "{% if user.is_authenticated %}Welcome{% endif %}".to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } #[test] @@ -507,9 +510,9 @@ mod tests { let source = "{% for item in items %}{{ item }}{% empty %}No items{% endfor %}".to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } #[test] @@ -518,9 +521,9 @@ mod tests { let source = "{% if x > 0 %}Positive{% elif x < 0 %}Negative{% else %}Zero{% endif %}" .to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } #[test] @@ -528,9 +531,9 @@ mod tests { let db = TestDatabase::new(); let source = "{% url 'view-name' as view %}".to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } #[test] @@ -540,9 +543,9 @@ mod tests { "{% for item in items %}{% if item.active %}{{ item.name }}{% endif %}{% endfor %}" .to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } #[test] @@ -563,9 +566,9 @@ mod tests { {% endif %}!" .to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } } @@ -584,9 +587,9 @@ mod tests { "# .to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } } @@ -604,9 +607,9 @@ mod tests { "# .to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } } @@ -618,9 +621,9 @@ mod tests { let db = TestDatabase::new(); let source = "{# Django comment #}".to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } } @@ -632,9 +635,9 @@ mod tests { let db = TestDatabase::new(); let source = " hello".to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } #[test] @@ -642,9 +645,9 @@ mod tests { let db = TestDatabase::new(); let source = "\n hello".to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } #[test] @@ -652,9 +655,9 @@ mod tests { let db = TestDatabase::new(); let source = "hello ".to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } #[test] @@ -662,9 +665,9 @@ mod tests { let db = TestDatabase::new(); let source = "hello \n".to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } } @@ -676,9 +679,9 @@ mod tests { let db = TestDatabase::new(); let source = "
".to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } #[test] @@ -686,9 +689,9 @@ mod tests { let db = TestDatabase::new(); let source = "{% if user.is_authenticated %}Welcome".to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } #[test] @@ -696,9 +699,9 @@ mod tests { let db = TestDatabase::new(); let source = "{% for item in items %}{{ item.name }}".to_string(); let template = TestTemplate::new(&db, source); - let ast = parse_test_template(&db, template); - let test_ast = convert_ast_for_testing(ast, &db); - insta::assert_yaml_snapshot!(test_ast); + let nodelist = parse_test_template(&db, template); + let test_nodelist = convert_nodelist_for_testing_wrapper(nodelist, &db); + insta::assert_yaml_snapshot!(test_nodelist); } #[test] @@ -706,9 +709,9 @@ mod tests { let db = TestDatabase::new(); let source = "