1use depyler_hir::error::ErrorKind;
10use depyler_hir::hir::{HirClass, HirFunction, HirMethod, HirModule};
11use rustpython_parser::text_size::{TextRange, TextSize};
12use std::collections::HashMap;
13
14#[derive(Debug, Clone)]
16pub struct Symbol {
17 pub name: String,
18 pub kind: SymbolKind,
19 pub range: TextRange,
20 pub detail: Option<String>,
21 pub documentation: Option<String>,
22}
23
24#[derive(Debug, Clone, PartialEq)]
25pub enum SymbolKind {
26 Function,
27 Class,
28 Method,
29 Variable,
30 Parameter,
31 Field,
32 Module,
33}
34
35#[derive(Default)]
37pub struct IdeIntegration {
38 symbols: HashMap<String, Vec<Symbol>>,
39 diagnostics: Vec<Diagnostic>,
40}
41
42#[derive(Debug, Clone)]
43pub struct Diagnostic {
44 pub range: TextRange,
45 pub severity: DiagnosticSeverity,
46 pub message: String,
47 pub code: Option<String>,
48 pub source: String,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq)]
52pub enum DiagnosticSeverity {
53 Error,
54 Warning,
55 Information,
56 Hint,
57}
58
59impl IdeIntegration {
60 pub fn new() -> Self {
61 Self::default()
62 }
63
64 pub fn index_symbols(&mut self, module: &HirModule, source: &str) {
66 for func in &module.functions {
68 self.index_function(func, source);
69 }
70
71 for class in &module.classes {
73 self.index_class(class, source);
74 }
75 }
76
77 fn index_function(&mut self, func: &HirFunction, _source: &str) {
78 let range = TextRange::new(TextSize::from(0), TextSize::from(100));
80
81 let mut params = Vec::new();
82 for param in &func.params {
83 params.push(format!("{}: {:?}", param.name, param.ty));
84 }
85 let detail = format!(
86 "fn {}({}) -> {:?}",
87 func.name,
88 params.join(", "),
89 func.ret_type
90 );
91
92 let symbol = Symbol {
93 name: func.name.clone(),
94 kind: SymbolKind::Function,
95 range,
96 detail: Some(detail),
97 documentation: None, };
99
100 self.symbols
101 .entry(func.name.clone())
102 .or_default()
103 .push(symbol);
104 }
105
106 fn index_class(&mut self, class: &HirClass, _source: &str) {
107 let range = TextRange::new(TextSize::from(0), TextSize::from(100));
109
110 let symbol = Symbol {
111 name: class.name.clone(),
112 kind: SymbolKind::Class,
113 range,
114 detail: Some(format!("class {}", class.name)),
115 documentation: None,
116 };
117
118 self.symbols
119 .entry(class.name.clone())
120 .or_default()
121 .push(symbol);
122
123 for method in &class.methods {
125 self.index_method(method, &class.name, _source);
126 }
127
128 for field in &class.fields {
130 let field_symbol = Symbol {
131 name: field.name.clone(),
132 kind: SymbolKind::Field,
133 range, detail: Some(format!("{}: {:?}", field.name, field.field_type)),
135 documentation: None,
136 };
137 self.symbols
138 .entry(field.name.clone())
139 .or_default()
140 .push(field_symbol);
141 }
142 }
143
144 fn index_method(&mut self, method: &HirMethod, class_name: &str, _source: &str) {
145 let range = TextRange::new(TextSize::from(0), TextSize::from(100));
147
148 let mut params = Vec::new();
149 for param in &method.params {
150 params.push(format!("{}: {:?}", param.name, param.ty));
151 }
152 let detail = format!(
153 "{}::{}({}) -> {:?}",
154 class_name,
155 method.name,
156 params.join(", "),
157 method.ret_type
158 );
159
160 let symbol = Symbol {
161 name: format!("{}::{}", class_name, method.name),
162 kind: SymbolKind::Method,
163 range,
164 detail: Some(detail),
165 documentation: None,
166 };
167
168 self.symbols
169 .entry(method.name.clone())
170 .or_default()
171 .push(symbol);
172 }
173
174 pub fn symbol_at_position(&self, position: TextSize) -> Option<&Symbol> {
176 for symbols in self.symbols.values() {
177 for symbol in symbols {
178 if symbol.range.contains(position) {
179 return Some(symbol);
180 }
181 }
182 }
183 None
184 }
185
186 pub fn find_references(&self, symbol_name: &str) -> Vec<&Symbol> {
188 self.symbols
189 .get(symbol_name)
190 .map(|symbols| symbols.iter().collect())
191 .unwrap_or_default()
192 }
193
194 pub fn completions_at_position(
196 &self,
197 _position: TextSize,
198 prefix: &str,
199 ) -> Vec<CompletionItem> {
200 let mut completions = Vec::new();
201
202 for (name, symbols) in &self.symbols {
203 if name.starts_with(prefix) {
204 for symbol in symbols {
205 completions.push(CompletionItem {
206 label: name.clone(),
207 kind: match symbol.kind {
208 SymbolKind::Function => CompletionKind::Function,
209 SymbolKind::Class => CompletionKind::Class,
210 SymbolKind::Method => CompletionKind::Method,
211 SymbolKind::Variable => CompletionKind::Variable,
212 SymbolKind::Parameter => CompletionKind::Variable,
213 SymbolKind::Field => CompletionKind::Field,
214 SymbolKind::Module => CompletionKind::Module,
215 },
216 detail: symbol.detail.clone(),
217 documentation: symbol.documentation.clone(),
218 });
219 }
220 }
221 }
222
223 completions
224 }
225
226 pub fn add_diagnostic(&mut self, diagnostic: Diagnostic) {
228 self.diagnostics.push(diagnostic);
229 }
230
231 pub fn diagnostics(&self) -> &[Diagnostic] {
233 &self.diagnostics
234 }
235
236 pub fn add_error(&mut self, error: &ErrorKind, range: TextRange) {
238 let diagnostic = Diagnostic {
239 range,
240 severity: DiagnosticSeverity::Error,
241 message: error.to_string(),
242 code: None,
243 source: "depyler".to_string(),
244 };
245 self.add_diagnostic(diagnostic);
246 }
247
248 pub fn add_warning(&mut self, message: String, range: TextRange) {
250 let diagnostic = Diagnostic {
251 range,
252 severity: DiagnosticSeverity::Warning,
253 message,
254 code: None,
255 source: "depyler".to_string(),
256 };
257 self.add_diagnostic(diagnostic);
258 }
259}
260
261#[derive(Debug, Clone)]
262pub struct CompletionItem {
263 pub label: String,
264 pub kind: CompletionKind,
265 pub detail: Option<String>,
266 pub documentation: Option<String>,
267}
268
269#[derive(Debug, Clone, Copy, PartialEq)]
270pub enum CompletionKind {
271 Function,
272 Class,
273 Method,
274 Variable,
275 Field,
276 Module,
277}
278
279pub fn generate_hover_info(symbol: &Symbol) -> String {
281 let mut hover = String::new();
282
283 if let Some(detail) = &symbol.detail {
285 hover.push_str(&format!("```rust\n{}\n```\n\n", detail));
286 }
287
288 if let Some(doc) = &symbol.documentation {
290 hover.push_str(doc);
291 }
292
293 hover
294}
295
296pub trait IdeContext {
298 fn get_symbol_at(&self, position: TextSize) -> Option<&Symbol>;
299 fn get_completions(&self, position: TextSize, prefix: &str) -> Vec<CompletionItem>;
300 fn get_diagnostics(&self) -> &[Diagnostic];
301}
302
303pub fn create_ide_integration(module: &HirModule, source: &str) -> IdeIntegration {
305 let mut ide = IdeIntegration::new();
306 ide.index_symbols(module, source);
307 ide
308}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313 use depyler_hir::hir::{FunctionProperties, HirClass, HirField, HirMethod, HirParam, Type};
314 use smallvec::smallvec;
315
316 #[test]
319 fn test_symbol_new() {
320 let symbol = Symbol {
321 name: "my_symbol".to_string(),
322 kind: SymbolKind::Function,
323 range: TextRange::new(TextSize::from(0), TextSize::from(50)),
324 detail: Some("fn my_symbol()".to_string()),
325 documentation: Some("A test symbol".to_string()),
326 };
327 assert_eq!(symbol.name, "my_symbol");
328 assert_eq!(symbol.kind, SymbolKind::Function);
329 assert!(symbol.detail.is_some());
330 assert!(symbol.documentation.is_some());
331 }
332
333 #[test]
334 fn test_symbol_clone() {
335 let symbol = Symbol {
336 name: "test".to_string(),
337 kind: SymbolKind::Class,
338 range: TextRange::new(TextSize::from(10), TextSize::from(20)),
339 detail: None,
340 documentation: None,
341 };
342 let cloned = symbol.clone();
343 assert_eq!(cloned.name, symbol.name);
344 assert_eq!(cloned.kind, symbol.kind);
345 }
346
347 #[test]
348 fn test_symbol_debug() {
349 let symbol = Symbol {
350 name: "debug_test".to_string(),
351 kind: SymbolKind::Variable,
352 range: TextRange::new(TextSize::from(0), TextSize::from(10)),
353 detail: None,
354 documentation: None,
355 };
356 let debug = format!("{:?}", symbol);
357 assert!(debug.contains("debug_test"));
358 assert!(debug.contains("Variable"));
359 }
360
361 #[test]
364 fn test_symbol_kind_function() {
365 assert_eq!(SymbolKind::Function, SymbolKind::Function);
366 assert_ne!(SymbolKind::Function, SymbolKind::Class);
367 }
368
369 #[test]
370 fn test_symbol_kind_class() {
371 let kind = SymbolKind::Class;
372 assert_eq!(kind.clone(), SymbolKind::Class);
373 }
374
375 #[test]
376 fn test_symbol_kind_method() {
377 let kind = SymbolKind::Method;
378 let debug = format!("{:?}", kind);
379 assert!(debug.contains("Method"));
380 }
381
382 #[test]
383 fn test_symbol_kind_variable() {
384 assert_eq!(SymbolKind::Variable, SymbolKind::Variable);
385 }
386
387 #[test]
388 fn test_symbol_kind_parameter() {
389 assert_eq!(SymbolKind::Parameter, SymbolKind::Parameter);
390 }
391
392 #[test]
393 fn test_symbol_kind_field() {
394 assert_eq!(SymbolKind::Field, SymbolKind::Field);
395 }
396
397 #[test]
398 fn test_symbol_kind_module() {
399 assert_eq!(SymbolKind::Module, SymbolKind::Module);
400 }
401
402 #[test]
405 fn test_diagnostic_severity_error() {
406 assert_eq!(DiagnosticSeverity::Error, DiagnosticSeverity::Error);
407 assert_ne!(DiagnosticSeverity::Error, DiagnosticSeverity::Warning);
408 }
409
410 #[test]
411 fn test_diagnostic_severity_warning() {
412 assert_eq!(DiagnosticSeverity::Warning, DiagnosticSeverity::Warning);
413 }
414
415 #[test]
416 fn test_diagnostic_severity_information() {
417 assert_eq!(
418 DiagnosticSeverity::Information,
419 DiagnosticSeverity::Information
420 );
421 }
422
423 #[test]
424 fn test_diagnostic_severity_hint() {
425 let severity = DiagnosticSeverity::Hint;
426 let cloned = severity;
427 assert_eq!(cloned, DiagnosticSeverity::Hint);
428 }
429
430 #[test]
433 fn test_diagnostic_new() {
434 let diag = Diagnostic {
435 range: TextRange::new(TextSize::from(0), TextSize::from(10)),
436 severity: DiagnosticSeverity::Error,
437 message: "Test error".to_string(),
438 code: Some("E0001".to_string()),
439 source: "depyler".to_string(),
440 };
441 assert_eq!(diag.message, "Test error");
442 assert_eq!(diag.severity, DiagnosticSeverity::Error);
443 assert_eq!(diag.code, Some("E0001".to_string()));
444 }
445
446 #[test]
447 fn test_diagnostic_clone() {
448 let diag = Diagnostic {
449 range: TextRange::new(TextSize::from(0), TextSize::from(5)),
450 severity: DiagnosticSeverity::Warning,
451 message: "Warning".to_string(),
452 code: None,
453 source: "test".to_string(),
454 };
455 let cloned = diag.clone();
456 assert_eq!(cloned.message, diag.message);
457 assert_eq!(cloned.severity, diag.severity);
458 }
459
460 #[test]
461 fn test_diagnostic_debug() {
462 let diag = Diagnostic {
463 range: TextRange::new(TextSize::from(0), TextSize::from(1)),
464 severity: DiagnosticSeverity::Information,
465 message: "Info".to_string(),
466 code: None,
467 source: "test".to_string(),
468 };
469 let debug = format!("{:?}", diag);
470 assert!(debug.contains("Information"));
471 assert!(debug.contains("Info"));
472 }
473
474 #[test]
477 fn test_completion_kind_function() {
478 assert_eq!(CompletionKind::Function, CompletionKind::Function);
479 }
480
481 #[test]
482 fn test_completion_kind_class() {
483 assert_eq!(CompletionKind::Class, CompletionKind::Class);
484 }
485
486 #[test]
487 fn test_completion_kind_method() {
488 assert_eq!(CompletionKind::Method, CompletionKind::Method);
489 }
490
491 #[test]
492 fn test_completion_kind_variable() {
493 assert_eq!(CompletionKind::Variable, CompletionKind::Variable);
494 }
495
496 #[test]
497 fn test_completion_kind_field() {
498 assert_eq!(CompletionKind::Field, CompletionKind::Field);
499 }
500
501 #[test]
502 fn test_completion_kind_module() {
503 let kind = CompletionKind::Module;
504 let debug = format!("{:?}", kind);
505 assert!(debug.contains("Module"));
506 }
507
508 #[test]
511 fn test_completion_item_new() {
512 let item = CompletionItem {
513 label: "my_func".to_string(),
514 kind: CompletionKind::Function,
515 detail: Some("fn my_func()".to_string()),
516 documentation: Some("Does something".to_string()),
517 };
518 assert_eq!(item.label, "my_func");
519 assert_eq!(item.kind, CompletionKind::Function);
520 }
521
522 #[test]
523 fn test_completion_item_clone() {
524 let item = CompletionItem {
525 label: "test".to_string(),
526 kind: CompletionKind::Variable,
527 detail: None,
528 documentation: None,
529 };
530 let cloned = item.clone();
531 assert_eq!(cloned.label, item.label);
532 }
533
534 #[test]
537 fn test_ide_integration_new() {
538 let ide = IdeIntegration::new();
539 assert!(ide.diagnostics.is_empty());
540 assert!(ide.symbols.is_empty());
541 }
542
543 #[test]
544 fn test_ide_integration_default() {
545 let ide = IdeIntegration::default();
546 assert!(ide.diagnostics.is_empty());
547 }
548
549 #[test]
550 fn test_add_diagnostic() {
551 let mut ide = IdeIntegration::new();
552 let diag = Diagnostic {
553 range: TextRange::new(TextSize::from(0), TextSize::from(10)),
554 severity: DiagnosticSeverity::Error,
555 message: "Test".to_string(),
556 code: None,
557 source: "test".to_string(),
558 };
559 ide.add_diagnostic(diag);
560 assert_eq!(ide.diagnostics().len(), 1);
561 }
562
563 #[test]
564 fn test_diagnostics_getter() {
565 let mut ide = IdeIntegration::new();
566 assert!(ide.diagnostics().is_empty());
567
568 ide.add_diagnostic(Diagnostic {
569 range: TextRange::new(TextSize::from(0), TextSize::from(1)),
570 severity: DiagnosticSeverity::Warning,
571 message: "Warn".to_string(),
572 code: None,
573 source: "test".to_string(),
574 });
575 assert_eq!(ide.diagnostics().len(), 1);
576 assert_eq!(ide.diagnostics()[0].message, "Warn");
577 }
578
579 #[test]
580 fn test_add_warning() {
581 let mut ide = IdeIntegration::new();
582 let range = TextRange::new(TextSize::from(5), TextSize::from(15));
583 ide.add_warning("Unused variable".to_string(), range);
584
585 assert_eq!(ide.diagnostics().len(), 1);
586 assert_eq!(ide.diagnostics()[0].severity, DiagnosticSeverity::Warning);
587 assert_eq!(ide.diagnostics()[0].message, "Unused variable");
588 assert_eq!(ide.diagnostics()[0].source, "depyler");
589 }
590
591 #[test]
592 fn test_add_error() {
593 let mut ide = IdeIntegration::new();
594 let range = TextRange::new(TextSize::from(0), TextSize::from(10));
595 let error = depyler_hir::error::ErrorKind::ParseError;
596 ide.add_error(&error, range);
597
598 assert_eq!(ide.diagnostics().len(), 1);
599 assert_eq!(ide.diagnostics()[0].severity, DiagnosticSeverity::Error);
600 }
601
602 #[test]
603 fn test_symbol_at_position_found() {
604 let mut ide = IdeIntegration::new();
605 ide.symbols.insert(
606 "test".to_string(),
607 vec![Symbol {
608 name: "test".to_string(),
609 kind: SymbolKind::Function,
610 range: TextRange::new(TextSize::from(10), TextSize::from(50)),
611 detail: None,
612 documentation: None,
613 }],
614 );
615
616 let symbol = ide.symbol_at_position(TextSize::from(25));
617 assert!(symbol.is_some());
618 assert_eq!(symbol.unwrap().name, "test");
619 }
620
621 #[test]
622 fn test_symbol_at_position_not_found() {
623 let mut ide = IdeIntegration::new();
624 ide.symbols.insert(
625 "test".to_string(),
626 vec![Symbol {
627 name: "test".to_string(),
628 kind: SymbolKind::Function,
629 range: TextRange::new(TextSize::from(10), TextSize::from(20)),
630 detail: None,
631 documentation: None,
632 }],
633 );
634
635 let symbol = ide.symbol_at_position(TextSize::from(100));
636 assert!(symbol.is_none());
637 }
638
639 #[test]
640 fn test_find_references_found() {
641 let mut ide = IdeIntegration::new();
642 ide.symbols.insert(
643 "my_func".to_string(),
644 vec![
645 Symbol {
646 name: "my_func".to_string(),
647 kind: SymbolKind::Function,
648 range: TextRange::new(TextSize::from(0), TextSize::from(10)),
649 detail: None,
650 documentation: None,
651 },
652 Symbol {
653 name: "my_func".to_string(),
654 kind: SymbolKind::Function,
655 range: TextRange::new(TextSize::from(50), TextSize::from(60)),
656 detail: None,
657 documentation: None,
658 },
659 ],
660 );
661
662 let refs = ide.find_references("my_func");
663 assert_eq!(refs.len(), 2);
664 }
665
666 #[test]
667 fn test_find_references_not_found() {
668 let ide = IdeIntegration::new();
669 let refs = ide.find_references("nonexistent");
670 assert!(refs.is_empty());
671 }
672
673 #[test]
674 fn test_completions_empty_prefix() {
675 let mut ide = IdeIntegration::new();
676 ide.symbols.insert(
677 "abc".to_string(),
678 vec![Symbol {
679 name: "abc".to_string(),
680 kind: SymbolKind::Variable,
681 range: TextRange::new(TextSize::from(0), TextSize::from(5)),
682 detail: None,
683 documentation: None,
684 }],
685 );
686
687 let completions = ide.completions_at_position(TextSize::from(0), "");
688 assert!(!completions.is_empty());
690 }
691
692 #[test]
693 fn test_completions_no_match() {
694 let mut ide = IdeIntegration::new();
695 ide.symbols.insert(
696 "foo".to_string(),
697 vec![Symbol {
698 name: "foo".to_string(),
699 kind: SymbolKind::Function,
700 range: TextRange::new(TextSize::from(0), TextSize::from(5)),
701 detail: None,
702 documentation: None,
703 }],
704 );
705
706 let completions = ide.completions_at_position(TextSize::from(0), "bar");
707 assert!(completions.is_empty());
708 }
709
710 #[test]
711 fn test_index_class_with_methods() {
712 let mut ide = IdeIntegration::new();
713
714 let class = HirClass {
715 name: "MyClass".to_string(),
716 fields: vec![HirField {
717 name: "value".to_string(),
718 field_type: Type::Int,
719 default_value: None,
720 is_class_var: false,
721 }],
722 methods: vec![HirMethod {
723 name: "get_value".to_string(),
724 params: smallvec![],
725 ret_type: Type::Int,
726 body: vec![],
727 docstring: None,
728 is_static: false,
729 is_classmethod: false,
730 is_property: false,
731 is_async: false,
732 }],
733 base_classes: vec![],
734 is_dataclass: false,
735 docstring: None,
736 type_params: vec![],
737 };
738
739 let module = HirModule {
740 functions: vec![],
741 classes: vec![class],
742 imports: vec![],
743 type_aliases: vec![],
744 protocols: vec![],
745 constants: vec![],
746 top_level_stmts: vec![],
747 };
748
749 ide.index_symbols(&module, "");
750
751 assert!(ide.symbols.contains_key("MyClass"));
753 assert!(ide.symbols.contains_key("get_value"));
754 assert!(ide.symbols.contains_key("value"));
755 }
756
757 #[test]
758 fn test_create_ide_integration() {
759 let module = HirModule {
760 functions: vec![HirFunction {
761 name: "main".to_string(),
762 params: smallvec![],
763 ret_type: Type::None,
764 body: vec![],
765 annotations: depyler_annotations::TranspilationAnnotations::default(),
766 docstring: None,
767 properties: FunctionProperties::default(),
768 }],
769 classes: vec![],
770 imports: vec![],
771 type_aliases: vec![],
772 protocols: vec![],
773 constants: vec![],
774 top_level_stmts: vec![],
775 };
776
777 let ide = create_ide_integration(&module, "");
778 assert!(ide.symbols.contains_key("main"));
779 }
780
781 #[test]
784 fn test_generate_hover_info_with_detail_only() {
785 let symbol = Symbol {
786 name: "func".to_string(),
787 kind: SymbolKind::Function,
788 range: TextRange::new(TextSize::from(0), TextSize::from(10)),
789 detail: Some("fn func() -> i32".to_string()),
790 documentation: None,
791 };
792
793 let hover = generate_hover_info(&symbol);
794 assert!(hover.contains("```rust"));
795 assert!(hover.contains("fn func() -> i32"));
796 assert!(hover.contains("```"));
797 }
798
799 #[test]
800 fn test_generate_hover_info_with_doc_only() {
801 let symbol = Symbol {
802 name: "var".to_string(),
803 kind: SymbolKind::Variable,
804 range: TextRange::new(TextSize::from(0), TextSize::from(5)),
805 detail: None,
806 documentation: Some("A counter variable".to_string()),
807 };
808
809 let hover = generate_hover_info(&symbol);
810 assert!(hover.contains("A counter variable"));
811 }
812
813 #[test]
814 fn test_generate_hover_info_empty() {
815 let symbol = Symbol {
816 name: "empty".to_string(),
817 kind: SymbolKind::Variable,
818 range: TextRange::new(TextSize::from(0), TextSize::from(5)),
819 detail: None,
820 documentation: None,
821 };
822
823 let hover = generate_hover_info(&symbol);
824 assert!(hover.is_empty());
825 }
826
827 #[test]
830 fn test_symbol_indexing() {
831 let mut ide = IdeIntegration::new();
832
833 let func = HirFunction {
834 name: "test_func".to_string(),
835 params: smallvec![HirParam::new("x".to_string(), Type::Int)],
836 ret_type: Type::Int,
837 body: vec![],
838 annotations: depyler_annotations::TranspilationAnnotations::default(),
839 docstring: None,
840 properties: FunctionProperties::default(),
841 };
842
843 let module = HirModule {
844 functions: vec![func],
845 classes: vec![],
846 imports: vec![],
847 type_aliases: vec![],
848 protocols: vec![],
849 constants: vec![],
850 top_level_stmts: vec![],
851 };
852
853 ide.index_symbols(&module, "def test_func(x: int) -> int:\n pass");
854
855 assert_eq!(ide.symbols.len(), 1);
856 assert!(ide.symbols.contains_key("test_func"));
857
858 let symbols = &ide.symbols["test_func"];
859 assert_eq!(symbols.len(), 1);
860 assert_eq!(symbols[0].kind, SymbolKind::Function);
861 }
862
863 #[test]
864 fn test_hover_generation() {
865 let symbol = Symbol {
866 name: "my_func".to_string(),
867 kind: SymbolKind::Function,
868 range: TextRange::new(TextSize::from(0), TextSize::from(10)),
869 detail: Some("fn my_func(x: int) -> int".to_string()),
870 documentation: Some("Calculates something".to_string()),
871 };
872
873 let hover = generate_hover_info(&symbol);
874 assert!(hover.contains("```rust"));
875 assert!(hover.contains("fn my_func(x: int) -> int"));
876 assert!(hover.contains("Calculates something"));
877 }
878
879 #[test]
880 fn test_completions() {
881 let mut ide = IdeIntegration::new();
882
883 ide.symbols.insert(
885 "test_func".to_string(),
886 vec![Symbol {
887 name: "test_func".to_string(),
888 kind: SymbolKind::Function,
889 range: TextRange::new(TextSize::from(0), TextSize::from(10)),
890 detail: Some("fn test_func()".to_string()),
891 documentation: None,
892 }],
893 );
894
895 ide.symbols.insert(
896 "test_class".to_string(),
897 vec![Symbol {
898 name: "test_class".to_string(),
899 kind: SymbolKind::Class,
900 range: TextRange::new(TextSize::from(20), TextSize::from(30)),
901 detail: Some("class test_class".to_string()),
902 documentation: None,
903 }],
904 );
905
906 let completions = ide.completions_at_position(TextSize::from(0), "test");
907 assert_eq!(completions.len(), 2);
908
909 let completions = ide.completions_at_position(TextSize::from(0), "test_f");
910 assert_eq!(completions.len(), 1);
911 assert_eq!(completions[0].label, "test_func");
912 }
913}