@@ -11,59 +11,81 @@ use helix_view::{graphics::Rect, Document, Editor};
11
11
use crate :: commands;
12
12
use crate :: ui:: { menu, Markdown , Menu , Popup , PromptEvent } ;
13
13
14
- use helix_lsp:: { lsp, util} ;
15
- use lsp:: CompletionItem ;
14
+ use helix_lsp:: { lsp, util, OffsetEncoding } ;
15
+
16
+ #[ derive( Clone ) ]
17
+ pub enum CompletionItem {
18
+ LSP {
19
+ language_server_id : usize ,
20
+ item : lsp:: CompletionItem ,
21
+ offset_encoding : OffsetEncoding ,
22
+ } ,
23
+ }
16
24
17
25
impl menu:: Item for CompletionItem {
18
26
fn sort_text ( & self ) -> & str {
19
- self . filter_text . as_ref ( ) . unwrap_or ( & self . label ) . as_str ( )
27
+ match self {
28
+ CompletionItem :: LSP { item, .. } => {
29
+ item. filter_text . as_ref ( ) . unwrap_or ( & item. label ) . as_str ( )
30
+ }
31
+ }
20
32
}
21
33
22
34
fn filter_text ( & self ) -> & str {
23
- self . filter_text . as_ref ( ) . unwrap_or ( & self . label ) . as_str ( )
35
+ match self {
36
+ CompletionItem :: LSP { item, .. } => {
37
+ item. filter_text . as_ref ( ) . unwrap_or ( & item. label ) . as_str ( )
38
+ }
39
+ }
24
40
}
25
41
26
42
fn label ( & self ) -> & str {
27
- self . label . as_str ( )
43
+ match self {
44
+ CompletionItem :: LSP { item, .. } => item. label . as_str ( ) ,
45
+ }
28
46
}
29
47
30
48
fn row ( & self ) -> menu:: Row {
31
49
menu:: Row :: new ( vec ! [
32
- menu:: Cell :: from( self . label. as_str( ) ) ,
33
- menu:: Cell :: from( match self . kind {
34
- Some ( lsp:: CompletionItemKind :: TEXT ) => "text" ,
35
- Some ( lsp:: CompletionItemKind :: METHOD ) => "method" ,
36
- Some ( lsp:: CompletionItemKind :: FUNCTION ) => "function" ,
37
- Some ( lsp:: CompletionItemKind :: CONSTRUCTOR ) => "constructor" ,
38
- Some ( lsp:: CompletionItemKind :: FIELD ) => "field" ,
39
- Some ( lsp:: CompletionItemKind :: VARIABLE ) => "variable" ,
40
- Some ( lsp:: CompletionItemKind :: CLASS ) => "class" ,
41
- Some ( lsp:: CompletionItemKind :: INTERFACE ) => "interface" ,
42
- Some ( lsp:: CompletionItemKind :: MODULE ) => "module" ,
43
- Some ( lsp:: CompletionItemKind :: PROPERTY ) => "property" ,
44
- Some ( lsp:: CompletionItemKind :: UNIT ) => "unit" ,
45
- Some ( lsp:: CompletionItemKind :: VALUE ) => "value" ,
46
- Some ( lsp:: CompletionItemKind :: ENUM ) => "enum" ,
47
- Some ( lsp:: CompletionItemKind :: KEYWORD ) => "keyword" ,
48
- Some ( lsp:: CompletionItemKind :: SNIPPET ) => "snippet" ,
49
- Some ( lsp:: CompletionItemKind :: COLOR ) => "color" ,
50
- Some ( lsp:: CompletionItemKind :: FILE ) => "file" ,
51
- Some ( lsp:: CompletionItemKind :: REFERENCE ) => "reference" ,
52
- Some ( lsp:: CompletionItemKind :: FOLDER ) => "folder" ,
53
- Some ( lsp:: CompletionItemKind :: ENUM_MEMBER ) => "enum_member" ,
54
- Some ( lsp:: CompletionItemKind :: CONSTANT ) => "constant" ,
55
- Some ( lsp:: CompletionItemKind :: STRUCT ) => "struct" ,
56
- Some ( lsp:: CompletionItemKind :: EVENT ) => "event" ,
57
- Some ( lsp:: CompletionItemKind :: OPERATOR ) => "operator" ,
58
- Some ( lsp:: CompletionItemKind :: TYPE_PARAMETER ) => "type_param" ,
59
- Some ( kind) => unimplemented!( "{:?}" , kind) ,
60
- None => "" ,
61
- } ) ,
62
- // self.detail.as_deref().unwrap_or("")
63
- // self.label_details
64
- // .as_ref()
65
- // .or(self.detail())
66
- // .as_str(),
50
+ menu:: Cell :: from( self . label( ) ) ,
51
+ match self {
52
+ CompletionItem :: LSP { item, .. } => {
53
+ menu:: Cell :: from( match item. kind {
54
+ Some ( lsp:: CompletionItemKind :: TEXT ) => "text" ,
55
+ Some ( lsp:: CompletionItemKind :: METHOD ) => "method" ,
56
+ Some ( lsp:: CompletionItemKind :: FUNCTION ) => "function" ,
57
+ Some ( lsp:: CompletionItemKind :: CONSTRUCTOR ) => "constructor" ,
58
+ Some ( lsp:: CompletionItemKind :: FIELD ) => "field" ,
59
+ Some ( lsp:: CompletionItemKind :: VARIABLE ) => "variable" ,
60
+ Some ( lsp:: CompletionItemKind :: CLASS ) => "class" ,
61
+ Some ( lsp:: CompletionItemKind :: INTERFACE ) => "interface" ,
62
+ Some ( lsp:: CompletionItemKind :: MODULE ) => "module" ,
63
+ Some ( lsp:: CompletionItemKind :: PROPERTY ) => "property" ,
64
+ Some ( lsp:: CompletionItemKind :: UNIT ) => "unit" ,
65
+ Some ( lsp:: CompletionItemKind :: VALUE ) => "value" ,
66
+ Some ( lsp:: CompletionItemKind :: ENUM ) => "enum" ,
67
+ Some ( lsp:: CompletionItemKind :: KEYWORD ) => "keyword" ,
68
+ Some ( lsp:: CompletionItemKind :: SNIPPET ) => "snippet" ,
69
+ Some ( lsp:: CompletionItemKind :: COLOR ) => "color" ,
70
+ Some ( lsp:: CompletionItemKind :: FILE ) => "file" ,
71
+ Some ( lsp:: CompletionItemKind :: REFERENCE ) => "reference" ,
72
+ Some ( lsp:: CompletionItemKind :: FOLDER ) => "folder" ,
73
+ Some ( lsp:: CompletionItemKind :: ENUM_MEMBER ) => "enum_member" ,
74
+ Some ( lsp:: CompletionItemKind :: CONSTANT ) => "constant" ,
75
+ Some ( lsp:: CompletionItemKind :: STRUCT ) => "struct" ,
76
+ Some ( lsp:: CompletionItemKind :: EVENT ) => "event" ,
77
+ Some ( lsp:: CompletionItemKind :: OPERATOR ) => "operator" ,
78
+ Some ( lsp:: CompletionItemKind :: TYPE_PARAMETER ) => "type_param" ,
79
+ Some ( kind) => unimplemented!( "{:?}" , kind) ,
80
+ None => "" ,
81
+ } )
82
+ // self.detail.as_deref().unwrap_or("")
83
+ // self.label_details
84
+ // .as_ref()
85
+ // .or(self.detail())
86
+ // .as_str(),
87
+ }
88
+ } ,
67
89
] )
68
90
}
69
91
}
@@ -81,18 +103,24 @@ impl Completion {
81
103
pub fn new (
82
104
editor : & Editor ,
83
105
items : Vec < CompletionItem > ,
84
- offset_encoding : helix_lsp:: OffsetEncoding ,
85
106
start_offset : usize ,
86
107
trigger_offset : usize ,
87
108
) -> Self {
88
109
let menu = Menu :: new ( items, move |editor : & mut Editor , item, event| {
89
110
fn item_to_transaction (
90
111
doc : & Document ,
91
112
item : & CompletionItem ,
92
- offset_encoding : helix_lsp:: OffsetEncoding ,
93
113
start_offset : usize ,
94
114
trigger_offset : usize ,
95
115
) -> Transaction {
116
+ // for now only LSP support
117
+ let ( item, offset_encoding) = match item {
118
+ CompletionItem :: LSP {
119
+ item,
120
+ offset_encoding,
121
+ ..
122
+ } => ( item, * offset_encoding) ,
123
+ } ;
96
124
let transaction = if let Some ( edit) = & item. text_edit {
97
125
let edit = match edit {
98
126
lsp:: CompletionTextEdit :: Edit ( edit) => edit. clone ( ) ,
@@ -142,13 +170,7 @@ impl Completion {
142
170
// always present here
143
171
let item = item. unwrap ( ) ;
144
172
145
- let transaction = item_to_transaction (
146
- doc,
147
- item,
148
- offset_encoding,
149
- start_offset,
150
- trigger_offset,
151
- ) ;
173
+ let transaction = item_to_transaction ( doc, item, start_offset, trigger_offset) ;
152
174
153
175
// initialize a savepoint
154
176
doc. savepoint ( ) ;
@@ -163,13 +185,7 @@ impl Completion {
163
185
// always present here
164
186
let item = item. unwrap ( ) ;
165
187
166
- let transaction = item_to_transaction (
167
- doc,
168
- item,
169
- offset_encoding,
170
- start_offset,
171
- trigger_offset,
172
- ) ;
188
+ let transaction = item_to_transaction ( doc, item, start_offset, trigger_offset) ;
173
189
174
190
doc. apply ( & transaction, view. id ) ;
175
191
@@ -178,22 +194,34 @@ impl Completion {
178
194
changes : completion_changes ( & transaction, trigger_offset) ,
179
195
} ) ;
180
196
197
+ let ( lsp_item, offset_encoding, language_server_id) = match item {
198
+ CompletionItem :: LSP {
199
+ item,
200
+ offset_encoding,
201
+ language_server_id,
202
+ } => ( item, * offset_encoding, * language_server_id) ,
203
+ } ;
204
+
181
205
// apply additional edits, mostly used to auto import unqualified types
182
- let resolved_item = if item
206
+ let resolved_item = if lsp_item
183
207
. additional_text_edits
184
208
. as_ref ( )
185
209
. map ( |edits| !edits. is_empty ( ) )
186
210
. unwrap_or ( false )
187
211
{
188
212
None
189
213
} else {
190
- Self :: resolve_completion_item ( doc, item. clone ( ) )
214
+ let language_server = editor
215
+ . language_servers
216
+ . get_by_id ( language_server_id)
217
+ . unwrap ( ) ;
218
+ Self :: resolve_completion_item ( language_server, lsp_item. clone ( ) )
191
219
} ;
192
220
193
221
if let Some ( additional_edits) = resolved_item
194
222
. as_ref ( )
195
223
. and_then ( |item| item. additional_text_edits . as_ref ( ) )
196
- . or ( item . additional_text_edits . as_ref ( ) )
224
+ . or ( lsp_item . additional_text_edits . as_ref ( ) )
197
225
{
198
226
if !additional_edits. is_empty ( ) {
199
227
let transaction = util:: generate_transaction_from_edits (
@@ -221,11 +249,9 @@ impl Completion {
221
249
}
222
250
223
251
fn resolve_completion_item (
224
- doc : & Document ,
252
+ language_server : & helix_lsp :: Client ,
225
253
completion_item : lsp:: CompletionItem ,
226
- ) -> Option < CompletionItem > {
227
- // TODO support multiple language servers instead of taking the first language server
228
- let language_server = doc. language_servers ( ) . first ( ) . map ( |l| * l) ?;
254
+ ) -> Option < lsp:: CompletionItem > {
229
255
let completion_resolve_provider = language_server
230
256
. capabilities ( )
231
257
. completion_provider
@@ -246,6 +272,10 @@ impl Completion {
246
272
}
247
273
}
248
274
275
+ pub fn add_completion_items ( & mut self , items : Vec < CompletionItem > ) {
276
+ self . popup . contents_mut ( ) . add_options ( items) ;
277
+ }
278
+
249
279
pub fn recompute_filter ( & mut self , editor : & Editor ) {
250
280
// recompute menu based on matches
251
281
let menu = self . popup . contents_mut ( ) ;
@@ -306,7 +336,7 @@ impl Component for Completion {
306
336
self . popup . render ( area, surface, cx) ;
307
337
308
338
// if we have a selection, render a markdown popup on top/below with info
309
- if let Some ( option) = self . popup . contents ( ) . selection ( ) {
339
+ if let Some ( CompletionItem :: LSP { item : option, .. } ) = self . popup . contents ( ) . selection ( ) {
310
340
// need to render:
311
341
// option.detail
312
342
// ---
0 commit comments