44//! and generating appropriate completion items for Django templates.
55
66use djls_project:: TemplateTags ;
7- use djls_semantic:: generate_partial_snippet;
8- use djls_semantic:: generate_snippet_for_tag_with_end;
97use djls_semantic:: ArgType ;
108use djls_semantic:: SimpleArgType ;
119use djls_semantic:: TagSpecs ;
1210use djls_workspace:: FileKind ;
1311use djls_workspace:: PositionEncoding ;
1412use djls_workspace:: TextDocument ;
15- use tower_lsp_server:: lsp_types:: CompletionItem ;
16- use tower_lsp_server:: lsp_types:: CompletionItemKind ;
17- use tower_lsp_server:: lsp_types:: Documentation ;
18- use tower_lsp_server:: lsp_types:: InsertTextFormat ;
19- use tower_lsp_server:: lsp_types:: Position ;
20- use tower_lsp_server:: lsp_types:: Range ;
21- use tower_lsp_server:: lsp_types:: TextEdit ;
13+ use tower_lsp_server:: lsp_types;
14+
15+ use crate :: snippets:: generate_partial_snippet;
16+ use crate :: snippets:: generate_snippet_for_tag_with_end;
2217
2318/// Tracks what closing characters are needed to complete a template tag.
2419///
@@ -96,15 +91,16 @@ pub struct LineInfo {
9691}
9792
9893/// Main entry point for handling completion requests
94+ #[ must_use]
9995pub fn handle_completion (
10096 document : & TextDocument ,
101- position : Position ,
97+ position : lsp_types :: Position ,
10298 encoding : PositionEncoding ,
10399 file_kind : FileKind ,
104100 template_tags : Option < & TemplateTags > ,
105101 tag_specs : Option < & TagSpecs > ,
106102 supports_snippets : bool ,
107- ) -> Vec < CompletionItem > {
103+ ) -> Vec < lsp_types :: CompletionItem > {
108104 // Only handle template files
109105 if file_kind != FileKind :: Template {
110106 return Vec :: new ( ) ;
@@ -135,7 +131,7 @@ pub fn handle_completion(
135131/// Extract line information from document at given position
136132fn get_line_info (
137133 document : & TextDocument ,
138- position : Position ,
134+ position : lsp_types :: Position ,
139135 encoding : PositionEncoding ,
140136) -> Option < LineInfo > {
141137 let content = document. content ( ) ;
@@ -290,10 +286,10 @@ fn generate_template_completions(
290286 template_tags : Option < & TemplateTags > ,
291287 tag_specs : Option < & TagSpecs > ,
292288 supports_snippets : bool ,
293- position : Position ,
289+ position : lsp_types :: Position ,
294290 line_text : & str ,
295291 cursor_offset : usize ,
296- ) -> Vec < CompletionItem > {
292+ ) -> Vec < lsp_types :: CompletionItem > {
297293 match context {
298294 TemplateCompletionContext :: TagName {
299295 partial,
@@ -340,17 +336,17 @@ fn generate_template_completions(
340336
341337/// Calculate the range to replace for a completion
342338fn calculate_replacement_range (
343- position : Position ,
339+ position : lsp_types :: Position ,
344340 line_text : & str ,
345341 cursor_offset : usize ,
346342 partial_len : usize ,
347343 closing : & ClosingBrace ,
348- ) -> Range {
344+ ) -> lsp_types :: Range {
349345 // Start position: move back by the length of the partial text
350346 let start_col = position
351347 . character
352348 . saturating_sub ( u32:: try_from ( partial_len) . unwrap_or ( 0 ) ) ;
353- let start = Position :: new ( position. line , start_col) ;
349+ let start = lsp_types :: Position :: new ( position. line , start_col) ;
354350
355351 // End position: include auto-paired } if present
356352 let mut end_col = position. character ;
@@ -361,9 +357,9 @@ fn calculate_replacement_range(
361357 end_col += 1 ;
362358 }
363359 }
364- let end = Position :: new ( position. line , end_col) ;
360+ let end = lsp_types :: Position :: new ( position. line , end_col) ;
365361
366- Range :: new ( start, end)
362+ lsp_types :: Range :: new ( start, end)
367363}
368364
369365/// Generate completions for tag names
@@ -375,10 +371,10 @@ fn generate_tag_name_completions(
375371 template_tags : Option < & TemplateTags > ,
376372 tag_specs : Option < & TagSpecs > ,
377373 supports_snippets : bool ,
378- position : Position ,
374+ position : lsp_types :: Position ,
379375 line_text : & str ,
380376 cursor_offset : usize ,
381- ) -> Vec < CompletionItem > {
377+ ) -> Vec < lsp_types :: CompletionItem > {
382378 let Some ( tags) = template_tags else {
383379 return Vec :: new ( ) ;
384380 } ;
@@ -413,14 +409,14 @@ fn generate_tag_name_completions(
413409 ClosingBrace :: FullClose => { } // No closing needed
414410 }
415411
416- completions. push ( CompletionItem {
412+ completions. push ( lsp_types :: CompletionItem {
417413 label : end_tag. name . clone ( ) ,
418- kind : Some ( CompletionItemKind :: KEYWORD ) ,
414+ kind : Some ( lsp_types :: CompletionItemKind :: KEYWORD ) ,
419415 detail : Some ( format ! ( "End tag for {opener_name}" ) ) ,
420416 text_edit : Some ( tower_lsp_server:: lsp_types:: CompletionTextEdit :: Edit (
421- TextEdit :: new ( replacement_range, insert_text. clone ( ) ) ,
417+ lsp_types :: TextEdit :: new ( replacement_range, insert_text. clone ( ) ) ,
422418 ) ) ,
423- insert_text_format : Some ( InsertTextFormat :: PLAIN_TEXT ) ,
419+ insert_text_format : Some ( lsp_types :: InsertTextFormat :: PLAIN_TEXT ) ,
424420 filter_text : Some ( end_tag. name . clone ( ) ) ,
425421 sort_text : Some ( format ! ( "0_{}" , end_tag. name) ) , // Priority sort
426422 ..Default :: default ( )
@@ -464,7 +460,7 @@ fn generate_tag_name_completions(
464460 }
465461 }
466462
467- ( text, InsertTextFormat :: SNIPPET )
463+ ( text, lsp_types :: InsertTextFormat :: SNIPPET )
468464 }
469465 } else {
470466 // No spec found, use plain text
@@ -481,19 +477,21 @@ fn generate_tag_name_completions(
481477
482478 // Create completion item
483479 // Use SNIPPET kind when we're inserting a snippet, KEYWORD otherwise
484- let kind = if matches ! ( insert_format, InsertTextFormat :: SNIPPET ) {
485- CompletionItemKind :: SNIPPET
480+ let kind = if matches ! ( insert_format, lsp_types :: InsertTextFormat :: SNIPPET ) {
481+ lsp_types :: CompletionItemKind :: SNIPPET
486482 } else {
487- CompletionItemKind :: KEYWORD
483+ lsp_types :: CompletionItemKind :: KEYWORD
488484 } ;
489485
490- let completion_item = CompletionItem {
486+ let completion_item = lsp_types :: CompletionItem {
491487 label : tag. name ( ) . clone ( ) ,
492488 kind : Some ( kind) ,
493489 detail : Some ( format ! ( "from {}" , tag. library( ) ) ) ,
494- documentation : tag. doc ( ) . map ( |doc| Documentation :: String ( doc. clone ( ) ) ) ,
490+ documentation : tag
491+ . doc ( )
492+ . map ( |doc| lsp_types:: Documentation :: String ( doc. clone ( ) ) ) ,
495493 text_edit : Some ( tower_lsp_server:: lsp_types:: CompletionTextEdit :: Edit (
496- TextEdit :: new ( replacement_range, insert_text. clone ( ) ) ,
494+ lsp_types :: TextEdit :: new ( replacement_range, insert_text. clone ( ) ) ,
497495 ) ) ,
498496 insert_text_format : Some ( insert_format) ,
499497 filter_text : Some ( tag. name ( ) . clone ( ) ) ,
@@ -519,7 +517,7 @@ fn generate_argument_completions(
519517 _template_tags : Option < & TemplateTags > ,
520518 tag_specs : Option < & TagSpecs > ,
521519 supports_snippets : bool ,
522- ) -> Vec < CompletionItem > {
520+ ) -> Vec < lsp_types :: CompletionItem > {
523521 let Some ( specs) = tag_specs else {
524522 return Vec :: new ( ) ;
525523 } ;
@@ -548,12 +546,12 @@ fn generate_argument_completions(
548546 ClosingBrace :: FullClose => { } // No closing needed
549547 }
550548
551- completions. push ( CompletionItem {
549+ completions. push ( lsp_types :: CompletionItem {
552550 label : arg. name . clone ( ) ,
553- kind : Some ( CompletionItemKind :: KEYWORD ) ,
551+ kind : Some ( lsp_types :: CompletionItemKind :: KEYWORD ) ,
554552 detail : Some ( "literal argument" . to_string ( ) ) ,
555553 insert_text : Some ( insert_text) ,
556- insert_text_format : Some ( InsertTextFormat :: PLAIN_TEXT ) ,
554+ insert_text_format : Some ( lsp_types :: InsertTextFormat :: PLAIN_TEXT ) ,
557555 ..Default :: default ( )
558556 } ) ;
559557 }
@@ -571,12 +569,12 @@ fn generate_argument_completions(
571569 ClosingBrace :: FullClose => { } // No closing needed
572570 }
573571
574- completions. push ( CompletionItem {
572+ completions. push ( lsp_types :: CompletionItem {
575573 label : option. clone ( ) ,
576- kind : Some ( CompletionItemKind :: ENUM_MEMBER ) ,
574+ kind : Some ( lsp_types :: CompletionItemKind :: ENUM_MEMBER ) ,
577575 detail : Some ( format ! ( "choice for {}" , arg. name) ) ,
578576 insert_text : Some ( insert_text) ,
579- insert_text_format : Some ( InsertTextFormat :: PLAIN_TEXT ) ,
577+ insert_text_format : Some ( lsp_types :: InsertTextFormat :: PLAIN_TEXT ) ,
580578 ..Default :: default ( )
581579 } ) ;
582580 }
@@ -586,12 +584,12 @@ fn generate_argument_completions(
586584 // For variables, we could offer variable completions from context
587585 // For now, just provide a hint
588586 if partial. is_empty ( ) {
589- completions. push ( CompletionItem {
587+ completions. push ( lsp_types :: CompletionItem {
590588 label : format ! ( "<{}>" , arg. name) ,
591- kind : Some ( CompletionItemKind :: VARIABLE ) ,
589+ kind : Some ( lsp_types :: CompletionItemKind :: VARIABLE ) ,
592590 detail : Some ( "variable argument" . to_string ( ) ) ,
593591 insert_text : None , // Don't insert placeholder
594- insert_text_format : Some ( InsertTextFormat :: PLAIN_TEXT ) ,
592+ insert_text_format : Some ( lsp_types :: InsertTextFormat :: PLAIN_TEXT ) ,
595593 ..Default :: default ( )
596594 } ) ;
597595 }
@@ -600,12 +598,12 @@ fn generate_argument_completions(
600598 // For strings, could offer template name completions
601599 // For now, just provide a hint
602600 if partial. is_empty ( ) {
603- completions. push ( CompletionItem {
601+ completions. push ( lsp_types :: CompletionItem {
604602 label : format ! ( "\" {}\" " , arg. name) ,
605- kind : Some ( CompletionItemKind :: TEXT ) ,
603+ kind : Some ( lsp_types :: CompletionItemKind :: TEXT ) ,
606604 detail : Some ( "string argument" . to_string ( ) ) ,
607605 insert_text : None , // Don't insert placeholder
608- insert_text_format : Some ( InsertTextFormat :: PLAIN_TEXT ) ,
606+ insert_text_format : Some ( lsp_types :: InsertTextFormat :: PLAIN_TEXT ) ,
609607 ..Default :: default ( )
610608 } ) ;
611609 }
@@ -636,12 +634,12 @@ fn generate_argument_completions(
636634 "remaining arguments" . to_string ( )
637635 } ;
638636
639- completions. push ( CompletionItem {
637+ completions. push ( lsp_types :: CompletionItem {
640638 label,
641- kind : Some ( CompletionItemKind :: SNIPPET ) ,
639+ kind : Some ( lsp_types :: CompletionItemKind :: SNIPPET ) ,
642640 detail : Some ( "Complete remaining arguments" . to_string ( ) ) ,
643641 insert_text : Some ( insert_text) ,
644- insert_text_format : Some ( InsertTextFormat :: SNIPPET ) ,
642+ insert_text_format : Some ( lsp_types :: InsertTextFormat :: SNIPPET ) ,
645643 sort_text : Some ( "zzz" . to_string ( ) ) , // Sort at the end
646644 ..Default :: default ( )
647645 } ) ;
@@ -656,7 +654,7 @@ fn generate_library_completions(
656654 partial : & str ,
657655 closing : & ClosingBrace ,
658656 template_tags : Option < & TemplateTags > ,
659- ) -> Vec < CompletionItem > {
657+ ) -> Vec < lsp_types :: CompletionItem > {
660658 let Some ( tags) = template_tags else {
661659 return Vec :: new ( ) ;
662660 } ;
@@ -680,12 +678,12 @@ fn generate_library_completions(
680678 ClosingBrace :: FullClose => { } // No closing needed
681679 }
682680
683- completions. push ( CompletionItem {
681+ completions. push ( lsp_types :: CompletionItem {
684682 label : library. clone ( ) ,
685- kind : Some ( CompletionItemKind :: MODULE ) ,
683+ kind : Some ( lsp_types :: CompletionItemKind :: MODULE ) ,
686684 detail : Some ( "Django template library" . to_string ( ) ) ,
687685 insert_text : Some ( insert_text) ,
688- insert_text_format : Some ( InsertTextFormat :: PLAIN_TEXT ) ,
686+ insert_text_format : Some ( lsp_types :: InsertTextFormat :: PLAIN_TEXT ) ,
689687 filter_text : Some ( library. clone ( ) ) ,
690688 ..Default :: default ( )
691689 } ) ;
@@ -700,7 +698,7 @@ fn build_plain_insert_for_tag(
700698 tag_name : & str ,
701699 needs_space : bool ,
702700 closing : & ClosingBrace ,
703- ) -> ( String , InsertTextFormat ) {
701+ ) -> ( String , lsp_types :: InsertTextFormat ) {
704702 let mut insert_text = String :: new ( ) ;
705703
706704 // Add leading space if needed (cursor right after {%)
@@ -717,7 +715,7 @@ fn build_plain_insert_for_tag(
717715 ClosingBrace :: FullClose => { } // No closing needed
718716 }
719717
720- ( insert_text, InsertTextFormat :: PLAIN_TEXT )
718+ ( insert_text, lsp_types :: InsertTextFormat :: PLAIN_TEXT )
721719}
722720
723721#[ cfg( test) ]
@@ -847,8 +845,15 @@ mod tests {
847845 closing : ClosingBrace :: None ,
848846 } ;
849847
850- let completions =
851- generate_template_completions ( & context, None , None , false , Position :: new ( 0 , 0 ) , "" , 0 ) ;
848+ let completions = generate_template_completions (
849+ & context,
850+ None ,
851+ None ,
852+ false ,
853+ lsp_types:: Position :: new ( 0 , 0 ) ,
854+ "" ,
855+ 0 ,
856+ ) ;
852857
853858 assert ! ( completions. is_empty( ) ) ;
854859 }
0 commit comments