5
5
use std:: {
6
6
io:: Write as _,
7
7
process:: { self , Stdio } ,
8
+ sync:: Arc ,
8
9
} ;
9
10
10
11
use ide:: {
11
- FileId , FilePosition , FileRange , HoverAction , HoverGotoTypeData , NavigationTarget , Query ,
12
- RangeInfo , Runnable , RunnableKind , SearchScope , TextEdit ,
12
+ FileId , FilePosition , FileRange , HoverAction , HoverGotoTypeData , ImportToAdd , LineIndex ,
13
+ NavigationTarget , Query , RangeInfo , Runnable , RunnableKind , SearchScope , TextEdit ,
13
14
} ;
14
15
use ide_db:: helpers:: { insert_use, mod_path_to_ast} ;
15
16
use itertools:: Itertools ;
@@ -36,6 +37,7 @@ use crate::{
36
37
config:: RustfmtConfig ,
37
38
from_json, from_proto,
38
39
global_state:: { GlobalState , GlobalStateSnapshot } ,
40
+ line_endings:: LineEndings ,
39
41
lsp_ext:: { self , InlayHint , InlayHintsParams } ,
40
42
to_proto, LspError , Result ,
41
43
} ;
@@ -536,6 +538,12 @@ pub(crate) fn handle_runnables(
536
538
Ok ( res)
537
539
}
538
540
541
+ #[ derive( Debug , Copy , Clone , Serialize , Deserialize ) ]
542
+ pub ( crate ) struct ResolveCompletionData {
543
+ completion_id : usize ,
544
+ completion_file_id : u32 ,
545
+ }
546
+
539
547
pub ( crate ) fn handle_completion (
540
548
global_state : & mut GlobalState ,
541
549
params : lsp_types:: CompletionParams ,
@@ -575,20 +583,31 @@ pub(crate) fn handle_completion(
575
583
576
584
let items: Vec < CompletionItem > = items
577
585
. into_iter ( )
578
- . flat_map ( |item| {
586
+ . enumerate ( )
587
+ . flat_map ( |( item_index, item) | {
588
+ let resolve_completion_data = ResolveCompletionData {
589
+ completion_id : item_index,
590
+ completion_file_id : position. file_id . 0 ,
591
+ } ;
579
592
let import_to_add = item. import_to_add ( ) . cloned ( ) ;
580
- let new_completion_items = to_proto:: completion_item ( & line_index, line_endings, item) ;
593
+ let mut new_completion_items =
594
+ to_proto:: completion_item ( & line_index, line_endings, item) ;
595
+
581
596
if let Some ( import_to_add) = import_to_add {
582
- for new_item in & new_completion_items {
583
- additional_imports. insert ( new_item. label . clone ( ) , import_to_add. clone ( ) ) ;
597
+ for new_item in & mut new_completion_items {
598
+ match serde_json:: to_value ( & resolve_completion_data) {
599
+ Ok ( resolve_value) => {
600
+ new_item. data = Some ( resolve_value) ;
601
+ additional_imports. insert ( item_index, import_to_add. clone ( ) ) ;
602
+ }
603
+ Err ( e) => {
604
+ log:: error!( "Failed to serialize completion resolve metadata: {}" , e)
605
+ }
606
+ }
584
607
}
585
608
}
586
609
new_completion_items
587
610
} )
588
- . map ( |mut item| {
589
- item. data = Some ( position. file_id . 0 . into ( ) ) ;
590
- item
591
- } )
592
611
. collect ( ) ;
593
612
594
613
global_state. additional_imports = additional_imports;
@@ -601,41 +620,75 @@ pub(crate) fn handle_resolve_completion(
601
620
global_state : & mut GlobalState ,
602
621
mut original_completion : lsp_types:: CompletionItem ,
603
622
) -> Result < lsp_types:: CompletionItem > {
604
- // TODO kb slow, takes over 130ms
605
623
let _p = profile:: span ( "handle_resolve_completion" ) ;
606
624
607
- if let Some ( import_data) =
608
- global_state. additional_imports . get ( dbg ! ( original_completion. label. as_str( ) ) )
609
- {
610
- let rewriter = insert_use:: insert_use (
611
- & import_data. import_scope ,
612
- mod_path_to_ast ( & import_data. import_path ) ,
613
- import_data. merge_behaviour ,
614
- ) ;
615
- if let Some ( ( old_ast, file_id) ) =
616
- // TODO kb for file_id, better use &str and then cast to u32?
617
- rewriter
618
- . rewrite_root ( )
619
- . zip ( original_completion. data . as_ref ( ) . and_then ( |value| Some ( value. as_u64 ( ) ? as u32 ) ) )
620
- {
621
- let snap = global_state. snapshot ( ) ;
622
- let mut import_insert = TextEdit :: builder ( ) ;
623
- algo:: diff ( & old_ast, & rewriter. rewrite ( & old_ast) ) . into_text_edit ( & mut import_insert) ;
624
- let line_index = snap. analysis . file_line_index ( FileId ( file_id) ) ?;
625
- let line_endings = snap. file_line_endings ( FileId ( file_id) ) ;
626
- let text_edit = import_insert. finish ( ) ;
627
-
628
- let mut new_edits = original_completion. additional_text_edits . unwrap_or_default ( ) ;
629
- for indel in text_edit {
630
- new_edits. push ( to_proto:: text_edit ( & line_index, line_endings, indel) ) ;
625
+ match original_completion. data . as_ref ( ) {
626
+ Some ( completion_data) => {
627
+ match serde_json:: from_value :: < ResolveCompletionData > ( completion_data. clone ( ) ) {
628
+ Ok ( resolve_completion_data) => {
629
+ if let Some ( import_to_add) =
630
+ global_state. additional_imports . get ( & resolve_completion_data. completion_id )
631
+ {
632
+ let snap = global_state. snapshot ( ) ;
633
+ let file_id = FileId ( resolve_completion_data. completion_file_id ) ;
634
+ let line_index = snap. analysis . file_line_index ( file_id) ?;
635
+ let line_endings = snap. file_line_endings ( file_id) ;
636
+
637
+ let resolved_edits =
638
+ resolve_additional_edits ( import_to_add, line_index, line_endings) ;
639
+
640
+ original_completion. additional_text_edits =
641
+ match original_completion. additional_text_edits {
642
+ Some ( mut original_additional_edits) => {
643
+ if let Some ( mut new_edits) = resolved_edits {
644
+ original_additional_edits. extend ( new_edits. drain ( ..) )
645
+ }
646
+ Some ( original_additional_edits)
647
+ }
648
+ None => resolved_edits,
649
+ } ;
650
+ } else {
651
+ log:: error!(
652
+ "Got no import data for completion with label {}, id {}" ,
653
+ original_completion. label,
654
+ resolve_completion_data. completion_id
655
+ )
656
+ }
657
+ }
658
+ Err ( e) => log:: error!( "Failed to deserialize completion resolve metadata: {}" , e) ,
631
659
}
632
- original_completion. additional_text_edits = Some ( new_edits) ;
633
660
}
661
+ None => ( ) ,
634
662
}
635
-
636
663
Ok ( original_completion)
637
664
}
638
665
666
+ // TODO kb what to do when no resolve is available on the client?
667
+ fn resolve_additional_edits (
668
+ import_to_add : & ImportToAdd ,
669
+ line_index : Arc < LineIndex > ,
670
+ line_endings : LineEndings ,
671
+ ) -> Option < Vec < lsp_types:: TextEdit > > {
672
+ let _p = profile:: span ( "resolve_additional_edits" ) ;
673
+
674
+ let rewriter = insert_use:: insert_use (
675
+ & import_to_add. import_scope ,
676
+ mod_path_to_ast ( & import_to_add. import_path ) ,
677
+ import_to_add. merge_behaviour ,
678
+ ) ;
679
+ let old_ast = rewriter. rewrite_root ( ) ?;
680
+ let mut import_insert = TextEdit :: builder ( ) ;
681
+ algo:: diff ( & old_ast, & rewriter. rewrite ( & old_ast) ) . into_text_edit ( & mut import_insert) ;
682
+ let text_edit = import_insert. finish ( ) ;
683
+
684
+ Some (
685
+ text_edit
686
+ . into_iter ( )
687
+ . map ( |indel| to_proto:: text_edit ( & line_index, line_endings, indel) )
688
+ . collect_vec ( ) ,
689
+ )
690
+ }
691
+
639
692
pub ( crate ) fn handle_folding_range (
640
693
snap : GlobalStateSnapshot ,
641
694
params : FoldingRangeParams ,
0 commit comments