3
3
use rustc_hash:: FxHashMap ;
4
4
use syntax:: {
5
5
ast:: { self , HasModuleItem } ,
6
- match_ast, AstNode ,
6
+ match_ast, AstNode , SyntaxKind ,
7
7
} ;
8
8
9
9
use crate :: {
@@ -16,24 +16,22 @@ use crate::{
16
16
/// `spam: &mut Spam` insert text/label and `spam` lookup string will be
17
17
/// suggested.
18
18
pub ( crate ) fn complete_fn_param ( acc : & mut Completions , ctx : & CompletionContext ) -> Option < ( ) > {
19
- if !matches ! ( ctx. pattern_ctx, Some ( PatternContext { is_param: Some ( ParamKind :: Function ) , .. } ) )
20
- {
19
+ let param_of_fn =
20
+ matches ! ( ctx. pattern_ctx, Some ( PatternContext { is_param: Some ( ParamKind :: Function ) , .. } ) ) ;
21
+
22
+ if !param_of_fn {
21
23
return None ;
22
24
}
23
25
24
- let mut params = FxHashMap :: default ( ) ;
26
+ let mut file_params = FxHashMap :: default ( ) ;
25
27
26
- let me = ctx. token . ancestors ( ) . find_map ( ast:: Fn :: cast) ;
27
- let mut process_fn = |func : ast:: Fn | {
28
- if Some ( & func) == me. as_ref ( ) {
29
- return ;
30
- }
31
- func. param_list ( ) . into_iter ( ) . flat_map ( |it| it. params ( ) ) . for_each ( |param| {
28
+ let mut extract_params = |f : ast:: Fn | {
29
+ f. param_list ( ) . into_iter ( ) . flat_map ( |it| it. params ( ) ) . for_each ( |param| {
32
30
if let Some ( pat) = param. pat ( ) {
33
31
// FIXME: We should be able to turn these into SmolStr without having to allocate a String
34
- let text = param. syntax ( ) . text ( ) . to_string ( ) ;
35
- let lookup = pat. syntax ( ) . text ( ) . to_string ( ) ;
36
- params . entry ( text ) . or_insert ( lookup ) ;
32
+ let whole_param = param. syntax ( ) . text ( ) . to_string ( ) ;
33
+ let binding = pat. syntax ( ) . text ( ) . to_string ( ) ;
34
+ file_params . entry ( whole_param ) . or_insert ( binding ) ;
37
35
}
38
36
} ) ;
39
37
} ;
@@ -44,32 +42,77 @@ pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
44
42
ast:: SourceFile ( it) => it. items( ) . filter_map( |item| match item {
45
43
ast:: Item :: Fn ( it) => Some ( it) ,
46
44
_ => None ,
47
- } ) . for_each( & mut process_fn ) ,
45
+ } ) . for_each( & mut extract_params ) ,
48
46
ast:: ItemList ( it) => it. items( ) . filter_map( |item| match item {
49
47
ast:: Item :: Fn ( it) => Some ( it) ,
50
48
_ => None ,
51
- } ) . for_each( & mut process_fn ) ,
49
+ } ) . for_each( & mut extract_params ) ,
52
50
ast:: AssocItemList ( it) => it. assoc_items( ) . filter_map( |item| match item {
53
51
ast:: AssocItem :: Fn ( it) => Some ( it) ,
54
52
_ => None ,
55
- } ) . for_each( & mut process_fn ) ,
53
+ } ) . for_each( & mut extract_params ) ,
56
54
_ => continue ,
57
55
}
58
56
} ;
59
57
}
60
58
59
+ let function = ctx. token . ancestors ( ) . find_map ( ast:: Fn :: cast) ?;
60
+ let param_list = function. param_list ( ) ?;
61
+
62
+ remove_duplicated ( & mut file_params, param_list. params ( ) ) ?;
63
+
61
64
let self_completion_items = [ "self" , "&self" , "mut self" , "&mut self" ] ;
62
- if ctx . impl_def . is_some ( ) && me? . param_list ( ) ? . params ( ) . next ( ) . is_none ( ) {
65
+ if should_add_self_completions ( ctx , param_list) {
63
66
self_completion_items. into_iter ( ) . for_each ( |self_item| {
64
67
add_new_item_to_acc ( ctx, acc, self_item. to_string ( ) , self_item. to_string ( ) )
65
68
} ) ;
66
69
}
67
70
68
- params. into_iter ( ) . for_each ( |( label, lookup) | add_new_item_to_acc ( ctx, acc, label, lookup) ) ;
71
+ file_params. into_iter ( ) . try_for_each ( |( whole_param, binding) | {
72
+ Some ( add_new_item_to_acc ( ctx, acc, surround_with_commas ( ctx, whole_param) ?, binding) )
73
+ } ) ?;
69
74
70
75
Some ( ( ) )
71
76
}
72
77
78
+ fn remove_duplicated (
79
+ file_params : & mut FxHashMap < String , String > ,
80
+ mut fn_params : ast:: AstChildren < ast:: Param > ,
81
+ ) -> Option < ( ) > {
82
+ fn_params. try_for_each ( |param| {
83
+ let whole_param = param. syntax ( ) . text ( ) . to_string ( ) ;
84
+ file_params. remove ( & whole_param) ;
85
+
86
+ let binding = param. pat ( ) ?. syntax ( ) . text ( ) . to_string ( ) ;
87
+
88
+ file_params. retain ( |_, v| v != & binding) ;
89
+ Some ( ( ) )
90
+ } )
91
+ }
92
+
93
+ fn should_add_self_completions ( ctx : & CompletionContext , param_list : ast:: ParamList ) -> bool {
94
+ let inside_impl = ctx. impl_def . is_some ( ) ;
95
+ let no_params = param_list. params ( ) . next ( ) . is_none ( ) && param_list. self_param ( ) . is_none ( ) ;
96
+
97
+ inside_impl && no_params
98
+ }
99
+
100
+ fn surround_with_commas ( ctx : & CompletionContext , param : String ) -> Option < String > {
101
+ let end_of_param_list = matches ! ( ctx. token. next_token( ) ?. kind( ) , SyntaxKind :: R_PAREN ) ;
102
+ let trailing = if end_of_param_list { "" } else { "," } ;
103
+
104
+ let previous_token = if matches ! ( ctx. token. kind( ) , SyntaxKind :: IDENT | SyntaxKind :: WHITESPACE ) {
105
+ ctx. previous_token . as_ref ( ) ?
106
+ } else {
107
+ & ctx. token
108
+ } ;
109
+
110
+ let needs_leading = !matches ! ( previous_token. kind( ) , SyntaxKind :: L_PAREN | SyntaxKind :: COMMA ) ;
111
+ let leading = if needs_leading { ", " } else { "" } ;
112
+
113
+ Some ( format ! ( "{}{}{}" , leading, param, trailing) )
114
+ }
115
+
73
116
fn add_new_item_to_acc (
74
117
ctx : & CompletionContext ,
75
118
acc : & mut Completions ,
0 commit comments