Skip to content

Commit 9c09ccf

Browse files
wip
1 parent b1419bb commit 9c09ccf

File tree

6 files changed

+801
-701
lines changed

6 files changed

+801
-701
lines changed

crates/djls-ide/src/completions.rs

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
//! and generating appropriate completion items for Django templates.
55
66
use djls_project::TemplateTags;
7-
use djls_semantic::ArgType;
8-
use djls_semantic::SimpleArgType;
7+
use djls_semantic::TagArg;
98
use djls_semantic::TagSpecs;
109
use djls_workspace::FileKind;
1110
use djls_workspace::PositionEncoding;
@@ -410,15 +409,15 @@ fn generate_tag_name_completions(
410409
}
411410

412411
completions.push(lsp_types::CompletionItem {
413-
label: end_tag.name.clone(),
412+
label: end_tag.name.to_string(),
414413
kind: Some(lsp_types::CompletionItemKind::KEYWORD),
415414
detail: Some(format!("End tag for {opener_name}")),
416415
text_edit: Some(tower_lsp_server::lsp_types::CompletionTextEdit::Edit(
417416
lsp_types::TextEdit::new(replacement_range, insert_text.clone()),
418417
)),
419418
insert_text_format: Some(lsp_types::InsertTextFormat::PLAIN_TEXT),
420-
filter_text: Some(end_tag.name.clone()),
421-
sort_text: Some(format!("0_{}", end_tag.name)), // Priority sort
419+
filter_text: Some(end_tag.name.to_string()),
420+
sort_text: Some(format!("0_{}", end_tag.name.as_ref())), // Priority sort
422421
..Default::default()
423422
});
424423
}
@@ -534,11 +533,11 @@ fn generate_argument_completions(
534533
let arg = &spec.args[position];
535534
let mut completions = Vec::new();
536535

537-
match &arg.arg_type {
538-
ArgType::Simple(SimpleArgType::Literal) => {
536+
match arg {
537+
TagArg::Literal { lit, .. } => {
539538
// For literals, complete the exact text
540-
if arg.name.starts_with(partial) {
541-
let mut insert_text = arg.name.clone();
539+
if lit.starts_with(partial) {
540+
let mut insert_text = lit.to_string();
542541

543542
// Add closing if needed
544543
match closing {
@@ -547,7 +546,7 @@ fn generate_argument_completions(
547546
}
548547

549548
completions.push(lsp_types::CompletionItem {
550-
label: arg.name.clone(),
549+
label: lit.to_string(),
551550
kind: Some(lsp_types::CompletionItemKind::KEYWORD),
552551
detail: Some("literal argument".to_string()),
553552
insert_text: Some(insert_text),
@@ -556,11 +555,11 @@ fn generate_argument_completions(
556555
});
557556
}
558557
}
559-
ArgType::Choice { choice } => {
558+
TagArg::Choice { name, choices, .. } => {
560559
// For choices, offer each option
561-
for option in choice {
560+
for option in choices.iter() {
562561
if option.starts_with(partial) {
563-
let mut insert_text = option.clone();
562+
let mut insert_text = option.to_string();
564563

565564
// Add closing if needed
566565
match closing {
@@ -570,22 +569,22 @@ fn generate_argument_completions(
570569
}
571570

572571
completions.push(lsp_types::CompletionItem {
573-
label: option.clone(),
572+
label: option.to_string(),
574573
kind: Some(lsp_types::CompletionItemKind::ENUM_MEMBER),
575-
detail: Some(format!("choice for {}", arg.name)),
574+
detail: Some(format!("choice for {}", name.as_ref())),
576575
insert_text: Some(insert_text),
577576
insert_text_format: Some(lsp_types::InsertTextFormat::PLAIN_TEXT),
578577
..Default::default()
579578
});
580579
}
581580
}
582581
}
583-
ArgType::Simple(SimpleArgType::Variable) => {
582+
TagArg::Var { name, .. } => {
584583
// For variables, we could offer variable completions from context
585584
// For now, just provide a hint
586585
if partial.is_empty() {
587586
completions.push(lsp_types::CompletionItem {
588-
label: format!("<{}>", arg.name),
587+
label: format!("<{}>", name.as_ref()),
589588
kind: Some(lsp_types::CompletionItemKind::VARIABLE),
590589
detail: Some("variable argument".to_string()),
591590
insert_text: None, // Don't insert placeholder
@@ -594,12 +593,12 @@ fn generate_argument_completions(
594593
});
595594
}
596595
}
597-
ArgType::Simple(SimpleArgType::String) => {
596+
TagArg::String { name, .. } => {
598597
// For strings, could offer template name completions
599598
// For now, just provide a hint
600599
if partial.is_empty() {
601600
completions.push(lsp_types::CompletionItem {
602-
label: format!("\"{}\"", arg.name),
601+
label: format!("\"{}\"", name.as_ref()),
603602
kind: Some(lsp_types::CompletionItemKind::TEXT),
604603
detail: Some("string argument".to_string()),
605604
insert_text: None, // Don't insert placeholder
@@ -608,8 +607,8 @@ fn generate_argument_completions(
608607
});
609608
}
610609
}
611-
ArgType::Simple(_) => {
612-
// Other argument types not handled yet
610+
_ => {
611+
// Other argument types (Expr, Assignment, VarArgs) not handled yet
613612
}
614613
}
615614

crates/djls-ide/src/snippets.rs

Lines changed: 73 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use djls_semantic::specs::ArgType;
2-
use djls_semantic::specs::SimpleArgType;
1+
32
use djls_semantic::specs::TagArg;
43
use djls_semantic::specs::TagSpec;
54

@@ -12,50 +11,49 @@ pub fn generate_snippet_from_args(args: &[TagArg]) -> String {
1211
for arg in args {
1312
// Skip optional literals entirely - they're usually flags like "reversed" or "only"
1413
// that the user can add manually if needed
15-
if !arg.required && matches!(&arg.arg_type, ArgType::Simple(SimpleArgType::Literal)) {
14+
if !arg.is_required() && matches!(arg, TagArg::Literal { .. }) {
1615
continue;
1716
}
1817

1918
// Skip other optional args if we haven't seen any required args yet
2019
// This prevents generating snippets like: "{% for %}" when everything is optional
21-
if !arg.required && parts.is_empty() {
20+
if !arg.is_required() && parts.is_empty() {
2221
continue;
2322
}
2423

25-
let snippet_part = match &arg.arg_type {
26-
ArgType::Simple(simple_type) => match simple_type {
27-
SimpleArgType::Literal => {
28-
// At this point, we know it's required (optional literals were skipped above)
29-
arg.name.clone()
30-
}
31-
SimpleArgType::Variable | SimpleArgType::Expression => {
32-
// Variables and expressions become placeholders
33-
let result = format!("${{{}:{}}}", placeholder_index, arg.name);
34-
placeholder_index += 1;
35-
result
36-
}
37-
SimpleArgType::String => {
38-
// Strings get quotes around them
39-
let result = format!("\"${{{}:{}}}\"", placeholder_index, arg.name);
40-
placeholder_index += 1;
41-
result
42-
}
43-
SimpleArgType::Assignment => {
44-
// Assignments use the name as-is (e.g., "var=value")
45-
let result = format!("${{{}:{}}}", placeholder_index, arg.name);
46-
placeholder_index += 1;
47-
result
48-
}
49-
SimpleArgType::VarArgs => {
50-
// Variable arguments, just use the name
51-
let result = format!("${{{}:{}}}", placeholder_index, arg.name);
52-
placeholder_index += 1;
53-
result
54-
}
55-
},
56-
ArgType::Choice { choice } => {
24+
let snippet_part = match arg {
25+
TagArg::Literal { lit, .. } => {
26+
// At this point, we know it's required (optional literals were skipped above)
27+
lit.to_string()
28+
}
29+
TagArg::Var { name, .. } | TagArg::Expr { name, .. } => {
30+
// Variables and expressions become placeholders
31+
let result = format!("${{{}:{}}}", placeholder_index, name.as_ref());
32+
placeholder_index += 1;
33+
result
34+
}
35+
TagArg::String { name, .. } => {
36+
// Strings get quotes around them
37+
let result = format!("\"${{{}:{}}}\"", placeholder_index, name.as_ref());
38+
placeholder_index += 1;
39+
result
40+
}
41+
TagArg::Assignment { name, .. } => {
42+
// Assignments use the name as-is (e.g., "var=value")
43+
let result = format!("${{{}:{}}}", placeholder_index, name.as_ref());
44+
placeholder_index += 1;
45+
result
46+
}
47+
TagArg::VarArgs { name, .. } => {
48+
// Variable arguments, just use the name
49+
let result = format!("${{{}:{}}}", placeholder_index, name.as_ref());
50+
placeholder_index += 1;
51+
result
52+
}
53+
TagArg::Choice { choices, .. } => {
5754
// Choice placeholders with options
58-
let result = format!("${{{}|{}|}}", placeholder_index, choice.join(","));
55+
let options: Vec<_> = choices.iter().map(|s| s.as_ref()).collect();
56+
let result = format!("${{{}|{}|}}", placeholder_index, options.join(","));
5957
placeholder_index += 1;
6058
result
6159
}
@@ -128,25 +126,21 @@ mod tests {
128126
#[test]
129127
fn test_snippet_for_for_tag() {
130128
let args = vec![
131-
TagArg {
132-
name: "item".to_string(),
129+
TagArg::Var {
130+
name: "item".into(),
133131
required: true,
134-
arg_type: ArgType::Simple(SimpleArgType::Variable),
135132
},
136-
TagArg {
137-
name: "in".to_string(),
133+
TagArg::Literal {
134+
lit: "in".into(),
138135
required: true,
139-
arg_type: ArgType::Simple(SimpleArgType::Literal),
140136
},
141-
TagArg {
142-
name: "items".to_string(),
137+
TagArg::Var {
138+
name: "items".into(),
143139
required: true,
144-
arg_type: ArgType::Simple(SimpleArgType::Variable),
145140
},
146-
TagArg {
147-
name: "reversed".to_string(),
141+
TagArg::Literal {
142+
lit: "reversed".into(),
148143
required: false,
149-
arg_type: ArgType::Simple(SimpleArgType::Literal),
150144
},
151145
];
152146

@@ -156,10 +150,9 @@ mod tests {
156150

157151
#[test]
158152
fn test_snippet_for_if_tag() {
159-
let args = vec![TagArg {
160-
name: "condition".to_string(),
153+
let args = vec![TagArg::Expr {
154+
name: "condition".into(),
161155
required: true,
162-
arg_type: ArgType::Simple(SimpleArgType::Expression),
163156
}];
164157

165158
let snippet = generate_snippet_from_args(&args);
@@ -168,12 +161,10 @@ mod tests {
168161

169162
#[test]
170163
fn test_snippet_for_autoescape_tag() {
171-
let args = vec![TagArg {
172-
name: "mode".to_string(),
164+
let args = vec![TagArg::Choice {
165+
name: "mode".into(),
173166
required: true,
174-
arg_type: ArgType::Choice {
175-
choice: vec!["on".to_string(), "off".to_string()],
176-
},
167+
choices: vec!["on".into(), "off".into()].into(),
177168
}];
178169

179170
let snippet = generate_snippet_from_args(&args);
@@ -182,10 +173,9 @@ mod tests {
182173

183174
#[test]
184175
fn test_snippet_for_extends_tag() {
185-
let args = vec![TagArg {
186-
name: "template".to_string(),
176+
let args = vec![TagArg::String {
177+
name: "template".into(),
187178
required: true,
188-
arg_type: ArgType::Simple(SimpleArgType::String),
189179
}];
190180

191181
let snippet = generate_snippet_from_args(&args);
@@ -203,22 +193,19 @@ mod tests {
203193
#[test]
204194
fn test_snippet_for_block_tag() {
205195
let spec = TagSpec {
206-
name: None,
207196
end_tag: Some(EndTag {
208-
name: "endblock".to_string(),
197+
name: "endblock".into(),
209198
optional: false,
210-
args: vec![TagArg {
211-
name: "name".to_string(),
199+
args: vec![TagArg::Var {
200+
name: "name".into(),
212201
required: false,
213-
arg_type: ArgType::Simple(SimpleArgType::Variable),
214-
}],
202+
}].into(),
215203
}),
216-
intermediate_tags: None,
217-
args: vec![TagArg {
218-
name: "name".to_string(),
204+
intermediate_tags: Cow::Borrowed(&[]),
205+
args: vec![TagArg::Var {
206+
name: "name".into(),
219207
required: true,
220-
arg_type: ArgType::Simple(SimpleArgType::Variable),
221-
}],
208+
}].into(),
222209
};
223210

224211
let snippet = generate_snippet_for_tag_with_end("block", &spec);
@@ -228,20 +215,17 @@ mod tests {
228215
#[test]
229216
fn test_snippet_with_end_tag() {
230217
let spec = TagSpec {
231-
name: None,
232218
end_tag: Some(EndTag {
233-
name: "endautoescape".to_string(),
219+
name: "endautoescape".into(),
234220
optional: false,
235-
args: vec![],
221+
args: Cow::Borrowed(&[]),
236222
}),
237-
intermediate_tags: None,
238-
args: vec![TagArg {
239-
name: "mode".to_string(),
223+
intermediate_tags: Cow::Borrowed(&[]),
224+
args: vec![TagArg::Choice {
225+
name: "mode".into(),
240226
required: true,
241-
arg_type: ArgType::Choice {
242-
choice: vec!["on".to_string(), "off".to_string()],
243-
},
244-
}],
227+
choices: vec!["on".into(), "off".into()].into(),
228+
}].into(),
245229
};
246230

247231
let snippet = generate_snippet_for_tag_with_end("autoescape", &spec);
@@ -254,25 +238,21 @@ mod tests {
254238
#[test]
255239
fn test_snippet_for_url_tag() {
256240
let args = vec![
257-
TagArg {
258-
name: "view_name".to_string(),
241+
TagArg::String {
242+
name: "view_name".into(),
259243
required: true,
260-
arg_type: ArgType::Simple(SimpleArgType::String),
261244
},
262-
TagArg {
263-
name: "args".to_string(),
245+
TagArg::VarArgs {
246+
name: "args".into(),
264247
required: false,
265-
arg_type: ArgType::Simple(SimpleArgType::VarArgs),
266248
},
267-
TagArg {
268-
name: "as".to_string(),
249+
TagArg::Literal {
250+
lit: "as".into(),
269251
required: false,
270-
arg_type: ArgType::Simple(SimpleArgType::Literal),
271252
},
272-
TagArg {
273-
name: "varname".to_string(),
253+
TagArg::Var {
254+
name: "varname".into(),
274255
required: false,
275-
arg_type: ArgType::Simple(SimpleArgType::Variable),
276256
},
277257
];
278258

0 commit comments

Comments
 (0)