Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/djls-ide/src/completions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ fn generate_argument_completions(
}
}
}
TagArg::Var { name, .. } => {
TagArg::Variable { name, .. } => {
// For variables, we could offer variable completions from context
// For now, just provide a hint
if partial.is_empty() {
Expand Down
47 changes: 10 additions & 37 deletions crates/djls-ide/src/snippets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
// At this point, we know it's required (optional literals were skipped above)
lit.to_string()
}
TagArg::Var { name, .. } | TagArg::Expr { name, .. } => {
TagArg::Variable { name, .. } | TagArg::Any { name, .. } => {
// Variables and expressions become placeholders
let result = format!("${{{}:{}}}", placeholder_index, name.as_ref());
placeholder_index += 1;
Expand Down Expand Up @@ -125,22 +125,10 @@
#[test]
fn test_snippet_for_for_tag() {
let args = vec![
TagArg::Var {
name: "item".into(),
required: true,
},
TagArg::Literal {
lit: "in".into(),
required: true,
},
TagArg::Var {
name: "items".into(),
required: true,
},
TagArg::Literal {
lit: "reversed".into(),
required: false,
},
TagArg::var("item", true),
TagArg::syntax("in", true),
TagArg::var("items", true),
TagArg::modifier("reversed", false),
];

let snippet = generate_snippet_from_args(&args);
Expand All @@ -149,10 +137,7 @@

#[test]
fn test_snippet_for_if_tag() {
let args = vec![TagArg::Expr {
name: "condition".into(),
required: true,
}];
let args = vec![TagArg::expr("condition", true)];

let snippet = generate_snippet_from_args(&args);
assert_eq!(snippet, "${1:condition}");
Expand Down Expand Up @@ -195,20 +180,14 @@

let spec = TagSpec {
module: "django.template.loader_tags".into(),
end_tag: Some(EndTag {

Check warning on line 183 in crates/djls-ide/src/snippets.rs

View workflow job for this annotation

GitHub Actions / rustfmt

Diff in /home/runner/work/django-language-server/django-language-server/crates/djls-ide/src/snippets.rs
name: "endblock".into(),
required: true,
args: vec![TagArg::Var {
name: "name".into(),
required: false,
}]
args: vec![TagArg::var("name", false)]
.into(),
}),
intermediate_tags: Cow::Borrowed(&[]),
args: vec![TagArg::Var {
name: "name".into(),
required: true,
}]
args: vec![TagArg::var("name", true)]
.into(),
};
Comment on lines +186 to 192
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix the cargo fmt regression.

CI’s cargo fmt check is failing here because the newline before .into() doesn’t match rustfmt output. Please run cargo fmt (or collapse .into() onto the same line) so the lint job passes.

-                args: vec![TagArg::var("name", false)]
-                .into(),
+                args: vec![TagArg::var("name", false)].into(),
             }),
             intermediate_tags: Cow::Borrowed(&[]),
-            args: vec![TagArg::var("name", true)]
-            .into(),
+            args: vec![TagArg::var("name", true)].into(),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
args: vec![TagArg::var("name", false)]
.into(),
}),
intermediate_tags: Cow::Borrowed(&[]),
args: vec![TagArg::Var {
name: "name".into(),
required: true,
}]
args: vec![TagArg::var("name", true)]
.into(),
};
args: vec![TagArg::var("name", false)].into(),
}),
intermediate_tags: Cow::Borrowed(&[]),
args: vec![TagArg::var("name", true)].into(),
};
🤖 Prompt for AI Agents
In crates/djls-ide/src/snippets.rs around lines 186 to 192, the formatting
mismatch is causing the CI cargo fmt failure: the newline before `.into()`
doesn't match rustfmt output; fix by running `cargo fmt` or collapsing the
`.into()` call onto the previous line so the code conforms to rustfmt (e.g.,
place `.into()` immediately after the vec construction or reformat the block)
and commit the formatted changes.


Expand Down Expand Up @@ -254,14 +233,8 @@
name: "args".into(),
required: false,
},
TagArg::Literal {
lit: "as".into(),
required: false,
},
TagArg::Var {
name: "varname".into(),
required: false,
},
TagArg::syntax("as", false),
TagArg::var("varname", false),
];

let snippet = generate_snippet_from_args(&args);
Expand Down
2 changes: 2 additions & 0 deletions crates/djls-semantic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ use semantic::validate_block_tags;
use semantic::validate_non_block_tags;
pub use templatetags::django_builtin_specs;
pub use templatetags::EndTag;
pub use templatetags::LiteralKind;
pub use templatetags::TagArg;
pub use templatetags::TagSpec;
pub use templatetags::TagSpecs;
pub use templatetags::TokenCount;

/// Validate a Django template node list and return validation errors.
///
Expand Down
70 changes: 20 additions & 50 deletions crates/djls-semantic/src/semantic/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,8 @@
args_consumed = arg_index + 1;

match arg {
TagArg::Literal { lit, required } => {
TagArg::Literal { lit, required, .. } => {
// kind field is ignored for validation - it's only for semantic hints
let matches_literal = bits[bit_index] == lit.as_ref();
if *required {
if matches_literal {
Expand Down Expand Up @@ -257,6 +258,11 @@
if token.contains('=') {
break;
}
crate::templatetags::TokenCount::Greedy => {
// Assignment arguments can appear as:
// 1. Single token: var=value
// 2. Multi-token: expr as varname
// Consume until we find = or "as", or hit next literal

Comment on lines 258 to 266
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove the stray TokenCount::Greedy match fragment.

The inserted crate::templatetags::TokenCount::Greedy => { ... } is hanging inside the loop, which leaves an unclosed match arm and breaks compilation (matching the formatter error). Drop it or move the intended logic into a proper match count above.

-                    if token.contains('=') {
-                        break;
-                    }
-                crate::templatetags::TokenCount::Greedy => {
-                        // Assignment arguments can appear as:
-                        // 1. Single token: var=value
-                        // 2. Multi-token: expr as varname
-                        // Consume until we find = or "as", or hit next literal
-
-                // If we hit "as", consume one more token (the variable name)
+                    if token.contains('=') {
+                        break;
+                    }
+
+                    // If we hit "as", consume one more token (the variable name)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if token.contains('=') {
break;
}
crate::templatetags::TokenCount::Greedy => {
// Assignment arguments can appear as:
// 1. Single token: var=value
// 2. Multi-token: expr as varname
// Consume until we find = or "as", or hit next literal
if token.contains('=') {
break;
}
// If we hit "as", consume one more token (the variable name)
🤖 Prompt for AI Agents
In crates/djls-semantic/src/semantic/args.rs around lines 258 to 266, there is a
stray match arm fragment `crate::templatetags::TokenCount::Greedy => { ... }`
left inside a loop which leaves an unclosed match and breaks compilation; remove
this dangling fragment or relocate its logic into the existing `match count {
... }` block above so all `TokenCount` arms are handled in one place, ensuring
the match is properly closed and the loop only contains valid control flow.

// If we hit "as", consume one more token (the variable name)
if token == "as" {
Expand Down Expand Up @@ -449,16 +455,13 @@
fn test_if_tag_with_comparison_operator() {
// Issue #1: {% if message.input_tokens > 0 %}
// Parser tokenizes as: ["message.input_tokens", ">", "0"]
// Spec expects: [Expr{name="condition"}]
// Spec expects: [Any{name="condition", count=Greedy}]
let bits = vec![
"message.input_tokens".to_string(),
">".to_string(),
"0".to_string(),
];
let args = vec![TagArg::Expr {
name: "condition".into(),
required: true,
}];
let args = vec![TagArg::expr("condition", true)];

let errors = check_validation_errors("if", &bits, &args);
assert!(
Expand Down Expand Up @@ -494,22 +497,10 @@
"reversed".to_string(),
];
let args = vec![
TagArg::Var {
name: "item".into(),
required: true,
},
TagArg::Literal {
lit: "in".into(),
required: true,
},
TagArg::Var {
name: "items".into(),
required: true,
},
TagArg::Literal {
lit: "reversed".into(),
required: false,
},
TagArg::var("item", true),
TagArg::syntax("in", true),
TagArg::var("items", true),
TagArg::modifier("reversed", false),
];

let errors = check_validation_errors("for", &bits, &args);
Expand All @@ -527,10 +518,7 @@
"and".to_string(),
"user.is_staff".to_string(),
];
let args = vec![TagArg::Expr {
name: "condition".into(),
required: true,
}];
let args = vec![TagArg::expr("condition", true)];

let errors = check_validation_errors("if", &bits, &args);
assert!(
Expand Down Expand Up @@ -567,10 +555,7 @@
fn test_with_assignment() {
// {% with total=items|length %}
let bits = vec!["total=items|length".to_string()];
let args = vec![TagArg::Assignment {
name: "bindings".into(),
required: true,
}];
let args = vec![TagArg::assignment("bindings", true)];

let errors = check_validation_errors("with", &bits, &args);
assert!(
Expand Down Expand Up @@ -602,14 +587,8 @@
"reversed".to_string(),
];
let args = vec![
TagArg::Expr {
name: "condition".into(),
required: true,
},
TagArg::Literal {
lit: "reversed".into(),
required: false,
},
TagArg::expr("condition", true),
TagArg::modifier("reversed", false),
];

let errors = check_validation_errors("if", &bits, &args);
Expand Down Expand Up @@ -769,18 +748,9 @@
"library".to_string(),
];
let args = vec![
TagArg::VarArgs {
name: "tags".into(),
required: false,
},
TagArg::Literal {
lit: "from".into(),
required: false,
},
TagArg::Var {
name: "library".into(),
required: false,
},
TagArg::varargs("tags", false),
TagArg::syntax("from", false),
TagArg::var("library", false),
];

let errors = check_validation_errors("load", &bits, &args);
Expand Down Expand Up @@ -810,4 +780,4 @@
"Should error on extra argument after complete regroup args"
);
}
}

Check failure on line 783 in crates/djls-semantic/src/semantic/args.rs

View workflow job for this annotation

GitHub Actions / benchmarks

this file contains an unclosed delimiter

Check failure on line 783 in crates/djls-semantic/src/semantic/args.rs

View workflow job for this annotation

GitHub Actions / rustfmt

this file contains an unclosed delimiter

Check failure on line 783 in crates/djls-semantic/src/semantic/args.rs

View workflow job for this annotation

GitHub Actions / cargo-check

this file contains an unclosed delimiter

Check failure on line 783 in crates/djls-semantic/src/semantic/args.rs

View workflow job for this annotation

GitHub Actions / clippy

this file contains an unclosed delimiter
1 change: 1 addition & 0 deletions crates/djls-semantic/src/templatetags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ pub(crate) use specs::IntermediateTag;
pub use specs::TagArg;
pub use specs::TagSpec;
pub use specs::TagSpecs;
pub use specs::TokenCount;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Expose LiteralKind to fix compile error.

lib.rs now does pub use templatetags::LiteralKind;, but this module never re-exports that symbol, so the crate won’t compile. Please export LiteralKind here alongside TokenCount.

 pub use specs::TagArg;
 pub use specs::TagSpec;
 pub use specs::TagSpecs;
+pub use specs::LiteralKind;
 pub use specs::TokenCount;

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In crates/djls-semantic/src/templatetags.rs around line 10, the module currently
only re-exports TokenCount but lib.rs expects LiteralKind to be re-exported as
well; add a pub use for LiteralKind from the appropriate source (the specs crate
or the module where LiteralKind is defined) alongside TokenCount so that lib.rs
can publicly expose templatetags::LiteralKind and the crate will compile.

Loading
Loading