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,93 @@ 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
+ fn_params : ast:: AstChildren < ast:: Param > ,
81
+ ) {
82
+ fn_params. for_each ( |param| {
83
+ let whole_param = param. syntax ( ) . text ( ) . to_string ( ) ;
84
+ file_params. remove ( & whole_param) ;
85
+
86
+ if let Some ( pattern) = param. pat ( ) {
87
+ let binding = pattern. syntax ( ) . text ( ) . to_string ( ) ;
88
+ file_params. retain ( |_, v| v != & binding) ;
89
+ }
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 ) -> String {
101
+ match fallible_surround_with_commas ( ctx, & param) {
102
+ Some ( surrounded) => surrounded,
103
+ // fallback to the original parameter
104
+ None => param,
105
+ }
106
+ }
107
+
108
+ fn fallible_surround_with_commas ( ctx : & CompletionContext , param : & str ) -> Option < String > {
109
+ let next_token = {
110
+ let t = ctx. token . next_token ( ) ?;
111
+ match t. kind ( ) {
112
+ SyntaxKind :: WHITESPACE => t. next_token ( ) ?,
113
+ _ => t,
114
+ }
115
+ } ;
116
+
117
+ let trailing_comma_missing = matches ! ( next_token. kind( ) , SyntaxKind :: IDENT ) ;
118
+ let trailing = if trailing_comma_missing { "," } else { "" } ;
119
+
120
+ let previous_token = if matches ! ( ctx. token. kind( ) , SyntaxKind :: IDENT | SyntaxKind :: WHITESPACE ) {
121
+ ctx. previous_token . as_ref ( ) ?
122
+ } else {
123
+ & ctx. token
124
+ } ;
125
+
126
+ let needs_leading = !matches ! ( previous_token. kind( ) , SyntaxKind :: L_PAREN | SyntaxKind :: COMMA ) ;
127
+ let leading = if needs_leading { ", " } else { "" } ;
128
+
129
+ Some ( format ! ( "{}{}{}" , leading, param, trailing) )
130
+ }
131
+
73
132
fn add_new_item_to_acc (
74
133
ctx : & CompletionContext ,
75
134
acc : & mut Completions ,
0 commit comments