1use super::{LanguageServer, Position};
5use crate::ast::{Item, Program, Type};
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct CompletionItem {
11 pub label: String,
13 pub kind: Option<CompletionItemKind>,
15 pub detail: Option<String>,
17 pub documentation: Option<String>,
19 pub insert_text: Option<String>,
21 pub insert_text_format: Option<InsertTextFormat>,
23 pub additional_text_edits: Option<Vec<TextEdit>>,
25}
26
27#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
29#[repr(u8)]
30pub enum CompletionItemKind {
31 Text = 1,
32 Method = 2,
33 Function = 3,
34 Constructor = 4,
35 Field = 5,
36 Variable = 6,
37 Class = 7,
38 Interface = 8,
39 Module = 9,
40 Property = 10,
41 Unit = 11,
42 Value = 12,
43 Enum = 13,
44 Keyword = 14,
45 Snippet = 15,
46 Color = 16,
47 File = 17,
48 Reference = 18,
49 Folder = 19,
50 EnumMember = 20,
51 Constant = 21,
52 Struct = 22,
53 Event = 23,
54 Operator = 24,
55 TypeParameter = 25,
56}
57
58#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
60#[repr(u8)]
61pub enum InsertTextFormat {
62 PlainText = 1,
63 Snippet = 2,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct TextEdit {
69 pub range: super::Range,
70 pub new_text: String,
71}
72
73pub struct CompletionContext {
75 pub position: Position,
77 pub trigger_character: Option<String>,
79 pub line_text: String,
81 pub word: String,
83 pub is_dot_access: bool,
85 pub is_module_access: bool,
87}
88
89impl LanguageServer {
90 pub fn get_completions(&self, uri: &str, position: Position) -> Vec<CompletionItem> {
92 let mut completions = Vec::new();
93
94 let doc = match self.documents.get(uri) {
96 Some(doc) => doc,
97 None => return completions,
98 };
99
100 let context = match self.get_completion_context(&doc.content, position) {
102 Some(ctx) => ctx,
103 None => return completions,
104 };
105
106 if context.is_dot_access {
108 completions.extend(self.get_member_completions(&context, doc.ast.as_ref()));
110 } else if context.is_module_access {
111 completions.extend(self.get_module_completions(&context));
113 } else {
114 completions.extend(self.get_general_completions(&context, doc.ast.as_ref()));
116 }
117
118 completions
119 }
120
121 fn get_completion_context(
123 &self,
124 content: &str,
125 position: Position,
126 ) -> Option<CompletionContext> {
127 let lines: Vec<&str> = content.lines().collect();
128 let line_text = lines.get(position.line as usize)?.to_string();
129
130 let char_pos = position.character as usize;
132 let line_chars: Vec<char> = line_text.chars().collect();
133
134 let mut word_start = char_pos;
136 while word_start > 0 && line_chars.get(word_start - 1)?.is_alphanumeric() {
137 word_start -= 1;
138 }
139
140 let word = line_chars[word_start..char_pos].iter().collect();
141
142 let is_dot_access = word_start > 0 && line_chars.get(word_start - 1) == Some(&'.');
144 let is_module_access = word_start > 1
145 && line_chars.get(word_start - 2) == Some(&':')
146 && line_chars.get(word_start - 1) == Some(&':');
147
148 Some(CompletionContext {
149 position,
150 trigger_character: None,
151 line_text,
152 word,
153 is_dot_access,
154 is_module_access,
155 })
156 }
157
158 fn get_general_completions(
160 &self,
161 context: &CompletionContext,
162 ast: Option<&Program>,
163 ) -> Vec<CompletionItem> {
164 let mut completions = Vec::new();
165
166 let keywords = vec![
168 ("fn", "Function declaration"),
169 ("let", "Variable declaration"),
170 ("mut", "Mutable variable"),
171 ("if", "If statement"),
172 ("else", "Else clause"),
173 ("while", "While loop"),
174 ("for", "For loop"),
175 ("return", "Return statement"),
176 ("struct", "Struct declaration"),
177 ("enum", "Enum declaration"),
178 ("trait", "Trait declaration"),
179 ("impl", "Implementation block"),
180 ("pub", "Public visibility"),
181 ("async", "Async function"),
182 ("await", "Await expression"),
183 ("match", "Match expression"),
184 ("break", "Break statement"),
185 ("continue", "Continue statement"),
186 ("const", "Constant declaration"),
187 ("type", "Type alias"),
188 ("use", "Import statement"),
189 ("mod", "Module declaration"),
190 ("Self", "Self type"),
191 ("self", "Self parameter"),
192 ("true", "Boolean true"),
193 ("false", "Boolean false"),
194 ];
195
196 for (keyword, detail) in keywords {
197 if keyword.starts_with(&context.word) {
198 completions.push(CompletionItem {
199 label: keyword.to_string(),
200 kind: Some(CompletionItemKind::Keyword),
201 detail: Some(detail.to_string()),
202 documentation: None,
203 insert_text: Some(keyword.to_string()),
204 insert_text_format: Some(InsertTextFormat::PlainText),
205 additional_text_edits: None,
206 });
207 }
208 }
209
210 let types = vec![
212 ("i32", "32-bit signed integer"),
213 ("i64", "64-bit signed integer"),
214 ("u32", "32-bit unsigned integer"),
215 ("u64", "64-bit unsigned integer"),
216 ("bool", "Boolean type"),
217 ("String", "UTF-8 string type"),
218 ("Vec", "Dynamic array type"),
219 ("HashMap", "Hash map type"),
220 ("Option", "Optional type"),
221 ("Result", "Result type"),
222 ];
223
224 for (type_name, detail) in types {
225 if type_name.starts_with(&context.word) {
226 completions.push(CompletionItem {
227 label: type_name.to_string(),
228 kind: Some(CompletionItemKind::Class),
229 detail: Some(detail.to_string()),
230 documentation: None,
231 insert_text: Some(type_name.to_string()),
232 insert_text_format: Some(InsertTextFormat::PlainText),
233 additional_text_edits: None,
234 });
235 }
236 }
237
238 if let Some(ast) = ast {
240 for item in &ast.items {
241 match item {
242 Item::Function(func) => {
243 if func.name.starts_with(&context.word) {
244 let params: Vec<String> = func
245 .params
246 .iter()
247 .map(|p| format!("{}: {}", p.name, self.type_to_string(&p.ty)))
248 .collect();
249
250 let signature = format!(
251 "fn {}({}){}",
252 func.name,
253 params.join(", "),
254 func.return_type
255 .as_ref()
256 .map(|t| format!(" -> {}", self.type_to_string(t)))
257 .unwrap_or_default()
258 );
259
260 completions.push(CompletionItem {
261 label: func.name.clone(),
262 kind: Some(CompletionItemKind::Function),
263 detail: Some(signature),
264 documentation: None,
265 insert_text: Some(format!("{}(", func.name)),
266 insert_text_format: Some(InsertTextFormat::PlainText),
267 additional_text_edits: None,
268 });
269 }
270 }
271 Item::Struct(struct_def) => {
272 if struct_def.name.starts_with(&context.word) {
273 completions.push(CompletionItem {
274 label: struct_def.name.clone(),
275 kind: Some(CompletionItemKind::Struct),
276 detail: Some(format!("struct {}", struct_def.name)),
277 documentation: None,
278 insert_text: Some(struct_def.name.clone()),
279 insert_text_format: Some(InsertTextFormat::PlainText),
280 additional_text_edits: None,
281 });
282 }
283 }
284 Item::Enum(enum_def) => {
285 if enum_def.name.starts_with(&context.word) {
286 completions.push(CompletionItem {
287 label: enum_def.name.clone(),
288 kind: Some(CompletionItemKind::Enum),
289 detail: Some(format!("enum {}", enum_def.name)),
290 documentation: None,
291 insert_text: Some(enum_def.name.clone()),
292 insert_text_format: Some(InsertTextFormat::PlainText),
293 additional_text_edits: None,
294 });
295 }
296 }
297 _ => {}
298 }
299 }
300 }
301
302 let builtins = vec![
304 ("print", "fn print(s: String)", "Print a string to stdout"),
305 (
306 "print_int",
307 "fn print_int(n: i64)",
308 "Print an integer to stdout",
309 ),
310 (
311 "string_len",
312 "fn string_len(s: String) -> i64",
313 "Get string length",
314 ),
315 (
316 "string_concat",
317 "fn string_concat(a: String, b: String) -> String",
318 "Concatenate strings",
319 ),
320 (
321 "int_to_string",
322 "fn int_to_string(n: i64) -> String",
323 "Convert integer to string",
324 ),
325 (
326 "string_to_int",
327 "fn string_to_int(s: String) -> Option<i64>",
328 "Parse integer from string",
329 ),
330 ];
331
332 for (name, signature, doc) in builtins {
333 if name.starts_with(&context.word) {
334 completions.push(CompletionItem {
335 label: name.to_string(),
336 kind: Some(CompletionItemKind::Function),
337 detail: Some(signature.to_string()),
338 documentation: Some(doc.to_string()),
339 insert_text: Some(format!("{}(", name)),
340 insert_text_format: Some(InsertTextFormat::PlainText),
341 additional_text_edits: None,
342 });
343 }
344 }
345
346 completions
347 }
348
349 fn get_member_completions(
351 &self,
352 context: &CompletionContext,
353 ast: Option<&Program>,
354 ) -> Vec<CompletionItem> {
355 let mut completions = Vec::new();
356
357 let line_before_dot = &context.line_text[..context.position.character as usize - 1];
359
360 let expr_end = line_before_dot.rfind(|c: char| !c.is_alphanumeric() && c != '_');
362 let expr_start = expr_end.map(|i| i + 1).unwrap_or(0);
363 let var_name = &line_before_dot[expr_start..];
364
365 if let Some(ast) = ast {
367 for item in &ast.items {
369 if let Item::Struct(struct_def) = item {
370 if var_name == struct_def.name || var_name.ends_with(&format!(" {}", struct_def.name)) {
372 for (field_name, field_ty) in &struct_def.fields {
374 completions.push(CompletionItem {
375 label: field_name.clone(),
376 kind: Some(CompletionItemKind::Field),
377 detail: Some(format!("{}: {}", field_name, self.type_to_string(field_ty))),
378 documentation: None,
379 insert_text: Some(field_name.clone()),
380 insert_text_format: Some(InsertTextFormat::PlainText),
381 additional_text_edits: None,
382 });
383 }
384 }
385 }
386 }
387
388 if var_name.contains("string") || var_name.contains("str") {
390 let string_methods = vec![
391 ("len", "fn len() -> i64", "Get the length of the string"),
392 ("is_empty", "fn is_empty() -> bool", "Check if string is empty"),
393 ("to_uppercase", "fn to_uppercase() -> String", "Convert to uppercase"),
394 ("to_lowercase", "fn to_lowercase() -> String", "Convert to lowercase"),
395 ("trim", "fn trim() -> String", "Remove leading and trailing whitespace"),
396 ("split", "fn split(sep: String) -> Vec<String>", "Split string by separator"),
397 ("contains", "fn contains(s: String) -> bool", "Check if string contains substring"),
398 ("starts_with", "fn starts_with(s: String) -> bool", "Check if string starts with prefix"),
399 ("ends_with", "fn ends_with(s: String) -> bool", "Check if string ends with suffix"),
400 ];
401
402 for (name, signature, doc) in string_methods {
403 if name.starts_with(&context.word) {
404 completions.push(CompletionItem {
405 label: name.to_string(),
406 kind: Some(CompletionItemKind::Method),
407 detail: Some(signature.to_string()),
408 documentation: Some(doc.to_string()),
409 insert_text: Some(format!("{}(", name)),
410 insert_text_format: Some(InsertTextFormat::PlainText),
411 additional_text_edits: None,
412 });
413 }
414 }
415 }
416
417 if var_name.contains('[') || var_name.contains("array") || var_name.contains("vec") {
419 let array_methods = vec![
420 ("len", "fn len() -> i64", "Get the length of the array"),
421 ("is_empty", "fn is_empty() -> bool", "Check if array is empty"),
422 ("push", "fn push(item: T)", "Add an element to the end"),
423 ("pop", "fn pop() -> Option<T>", "Remove and return the last element"),
424 ("get", "fn get(index: i64) -> Option<T>", "Get element at index"),
425 ("first", "fn first() -> Option<T>", "Get the first element"),
426 ("last", "fn last() -> Option<T>", "Get the last element"),
427 ("sort", "fn sort()", "Sort the array in place"),
428 ("reverse", "fn reverse()", "Reverse the array in place"),
429 ];
430
431 for (name, signature, doc) in array_methods {
432 if name.starts_with(&context.word) {
433 completions.push(CompletionItem {
434 label: name.to_string(),
435 kind: Some(CompletionItemKind::Method),
436 detail: Some(signature.to_string()),
437 documentation: Some(doc.to_string()),
438 insert_text: Some(if name == "push" || name == "get" {
439 format!("{}(", name)
440 } else {
441 format!("{}()", name)
442 }),
443 insert_text_format: Some(InsertTextFormat::PlainText),
444 additional_text_edits: None,
445 });
446 }
447 }
448 }
449 }
450
451 completions
452 }
453
454 fn get_module_completions(&self, context: &CompletionContext) -> Vec<CompletionItem> {
456 let mut completions = Vec::new();
457
458 let line_before_colons = &context.line_text[..context.position.character as usize - 2];
460
461 let expr_end = line_before_colons.rfind(|c: char| !c.is_alphanumeric() && c != '_');
463 let expr_start = expr_end.map(|i| i + 1).unwrap_or(0);
464 let module_name = &line_before_colons[expr_start..];
465
466 if module_name == "std" {
468 let std_modules = vec![
469 ("io", "Input/output functionality"),
470 ("fs", "File system operations"),
471 ("collections", "Data structures"),
472 ("string", "String utilities"),
473 ("math", "Mathematical functions"),
474 ("vec", "Vector operations"),
475 ("hashmap", "Hash map implementation"),
476 ("result", "Result type utilities"),
477 ("option", "Option type utilities"),
478 ];
479
480 for (name, doc) in std_modules {
481 if name.starts_with(&context.word) {
482 completions.push(CompletionItem {
483 label: name.to_string(),
484 kind: Some(CompletionItemKind::Module),
485 detail: Some(format!("mod {}", name)),
486 documentation: Some(doc.to_string()),
487 insert_text: Some(name.to_string()),
488 insert_text_format: Some(InsertTextFormat::PlainText),
489 additional_text_edits: None,
490 });
491 }
492 }
493 }
494
495 if module_name == "Result" {
497 let result_methods = vec![
498 ("Ok", "Result::Ok(T) -> Result<T, E>", "Construct a successful result"),
499 ("Err", "Result::Err(E) -> Result<T, E>", "Construct an error result"),
500 ];
501
502 for (name, signature, doc) in result_methods {
503 if name.starts_with(&context.word) {
504 completions.push(CompletionItem {
505 label: name.to_string(),
506 kind: Some(CompletionItemKind::Constructor),
507 detail: Some(signature.to_string()),
508 documentation: Some(doc.to_string()),
509 insert_text: Some(format!("{}(", name)),
510 insert_text_format: Some(InsertTextFormat::PlainText),
511 additional_text_edits: None,
512 });
513 }
514 }
515 }
516
517 if module_name == "Option" {
519 let option_methods = vec![
520 ("Some", "Option::Some(T) -> Option<T>", "Construct a Some value"),
521 ("None", "Option::None -> Option<T>", "Construct a None value"),
522 ];
523
524 for (name, signature, doc) in option_methods {
525 if name.starts_with(&context.word) {
526 completions.push(CompletionItem {
527 label: name.to_string(),
528 kind: Some(CompletionItemKind::Constructor),
529 detail: Some(signature.to_string()),
530 documentation: Some(doc.to_string()),
531 insert_text: Some(if name == "Some" {
532 format!("{}(", name)
533 } else {
534 name.to_string()
535 }),
536 insert_text_format: Some(InsertTextFormat::PlainText),
537 additional_text_edits: None,
538 });
539 }
540 }
541 }
542
543 if let Some(doc) = self.documents.get(&context.position.line.to_string()) {
545 if let Some(ast) = &doc.ast {
546 for item in &ast.items {
547 if let Item::Enum(enum_def) = item {
548 if enum_def.name == module_name {
549 for variant in &enum_def.variants {
550 if variant.name.starts_with(&context.word) {
551 let has_fields = match &variant.data {
552 crate::ast::EnumVariantData::Unit => false,
553 crate::ast::EnumVariantData::Tuple(_) => true,
554 crate::ast::EnumVariantData::Struct(_) => true,
555 };
556 completions.push(CompletionItem {
557 label: variant.name.clone(),
558 kind: Some(CompletionItemKind::EnumMember),
559 detail: Some(format!("{}::{}", enum_def.name, variant.name)),
560 documentation: None,
561 insert_text: Some(if has_fields {
562 format!("{}(", variant.name)
563 } else {
564 variant.name.clone()
565 }),
566 insert_text_format: Some(InsertTextFormat::PlainText),
567 additional_text_edits: None,
568 });
569 }
570 }
571 }
572 }
573 }
574 }
575 }
576
577 completions
578 }
579
580 #[allow(clippy::only_used_in_recursion)]
582 pub fn type_to_string(&self, ty: &Type) -> String {
583 match ty {
584 Type::I32 => "i32".to_string(),
585 Type::I64 => "i64".to_string(),
586 Type::U32 => "u32".to_string(),
587 Type::U64 => "u64".to_string(),
588 Type::Bool => "bool".to_string(),
589 Type::String => "String".to_string(),
590 Type::Unit => "()".to_string(),
591 Type::Custom(name) => name.clone(),
592 Type::Array(elem, size) => format!("[{}; {}]", self.type_to_string(elem), size),
593 Type::Reference { mutable, inner, .. } => {
594 if *mutable {
595 format!("&mut {}", self.type_to_string(inner))
596 } else {
597 format!("&{}", self.type_to_string(inner))
598 }
599 }
600 Type::Future { output } => format!("Future<{}>", self.type_to_string(output)),
601 Type::Generic { name, args } => {
602 if args.is_empty() {
603 name.clone()
604 } else {
605 let arg_strs: Vec<String> = args.iter().map(|arg| arg.to_string()).collect();
606 format!("{}<{}>", name, arg_strs.join(", "))
607 }
608 }
609 Type::TypeParam(name) => name.clone(),
610 }
611 }
612}