@@ -47,7 +47,9 @@ use self::lsp::ext as lsp_ext;
47
47
#[ cfg( test) ]
48
48
mod integrated_benchmarks;
49
49
50
+ use ide:: { CompletionItem , CompletionRelevance } ;
50
51
use serde:: de:: DeserializeOwned ;
52
+ use tenthash:: TentHasher ;
51
53
52
54
pub use crate :: {
53
55
lsp:: capabilities:: server_capabilities, main_loop:: main_loop, reload:: ws_to_crate_graph,
@@ -61,3 +63,79 @@ pub fn from_json<T: DeserializeOwned>(
61
63
serde_json:: from_value ( json. clone ( ) )
62
64
. map_err ( |e| anyhow:: format_err!( "Failed to deserialize {what}: {e}; {json}" ) )
63
65
}
66
+
67
+ fn completion_item_hash ( item : & CompletionItem , is_ref_completion : bool ) -> [ u8 ; 20 ] {
68
+ fn hash_completion_relevance ( hasher : & mut TentHasher , relevance : & CompletionRelevance ) {
69
+ use ide_completion:: {
70
+ CompletionRelevancePostfixMatch , CompletionRelevanceReturnType ,
71
+ CompletionRelevanceTypeMatch ,
72
+ } ;
73
+
74
+ hasher. update ( [
75
+ u8:: from ( relevance. exact_name_match ) ,
76
+ u8:: from ( relevance. is_local ) ,
77
+ u8:: from ( relevance. is_name_already_imported ) ,
78
+ u8:: from ( relevance. requires_import ) ,
79
+ u8:: from ( relevance. is_private_editable ) ,
80
+ ] ) ;
81
+ if let Some ( type_match) = & relevance. type_match {
82
+ let label = match type_match {
83
+ CompletionRelevanceTypeMatch :: CouldUnify => "could_unify" ,
84
+ CompletionRelevanceTypeMatch :: Exact => "exact" ,
85
+ } ;
86
+ hasher. update ( label) ;
87
+ }
88
+ if let Some ( trait_) = & relevance. trait_ {
89
+ hasher. update ( [ u8:: from ( trait_. is_op_method ) , u8:: from ( trait_. notable_trait ) ] ) ;
90
+ }
91
+ if let Some ( postfix_match) = & relevance. postfix_match {
92
+ let label = match postfix_match {
93
+ CompletionRelevancePostfixMatch :: NonExact => "non_exact" ,
94
+ CompletionRelevancePostfixMatch :: Exact => "exact" ,
95
+ } ;
96
+ hasher. update ( label) ;
97
+ }
98
+ if let Some ( function) = & relevance. function {
99
+ hasher. update ( [ u8:: from ( function. has_params ) , u8:: from ( function. has_self_param ) ] ) ;
100
+ let label = match function. return_type {
101
+ CompletionRelevanceReturnType :: Other => "other" ,
102
+ CompletionRelevanceReturnType :: DirectConstructor => "direct_constructor" ,
103
+ CompletionRelevanceReturnType :: Constructor => "constructor" ,
104
+ CompletionRelevanceReturnType :: Builder => "builder" ,
105
+ } ;
106
+ hasher. update ( label) ;
107
+ }
108
+ }
109
+
110
+ let mut hasher = TentHasher :: new ( ) ;
111
+ hasher. update ( [
112
+ u8:: from ( is_ref_completion) ,
113
+ u8:: from ( item. is_snippet ) ,
114
+ u8:: from ( item. deprecated ) ,
115
+ u8:: from ( item. trigger_call_info ) ,
116
+ ] ) ;
117
+ hasher. update ( & item. label ) ;
118
+ if let Some ( label_detail) = & item. label_detail {
119
+ hasher. update ( label_detail) ;
120
+ }
121
+ // NB: do not hash edits or source range, as those may change between the time the client sends the resolve request
122
+ // and the time it receives it: some editors do allow changing the buffer between that, leading to ranges being different.
123
+ //
124
+ // Documentation hashing is skipped too, as it's a large blob to process,
125
+ // while not really making completion properties more unique as they are already.
126
+ hasher. update ( item. kind . tag ( ) ) ;
127
+ hasher. update ( & item. lookup ) ;
128
+ if let Some ( detail) = & item. detail {
129
+ hasher. update ( detail) ;
130
+ }
131
+ hash_completion_relevance ( & mut hasher, & item. relevance ) ;
132
+ if let Some ( ( mutability, text_size) ) = & item. ref_match {
133
+ hasher. update ( mutability. as_keyword_for_ref ( ) ) ;
134
+ hasher. update ( u32:: from ( * text_size) . to_le_bytes ( ) ) ;
135
+ }
136
+ for ( import_path, import_name) in & item. import_to_add {
137
+ hasher. update ( import_path) ;
138
+ hasher. update ( import_name) ;
139
+ }
140
+ hasher. finalize ( )
141
+ }
0 commit comments