1#![expect(clippy::result_large_err)]
2use std::{path::Path, sync::Arc};
3
4use dap::{Scope, StackFrame, Variable, requests::Variables};
5use editor::{Editor, EditorMode, MultiBuffer};
6use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
7use language::{
8 Language, LanguageConfig, LanguageMatcher, rust_lang, tree_sitter_python,
9 tree_sitter_typescript,
10};
11use project::{FakeFs, Project};
12use serde_json::json;
13use unindent::Unindent as _;
14use util::{path, rel_path::rel_path};
15
16use crate::{
17 debugger_panel::DebugPanel,
18 tests::{active_debug_session_panel, init_test, init_test_workspace, start_debug_session},
19};
20
21#[gpui::test]
22async fn test_rust_inline_values(executor: BackgroundExecutor, cx: &mut TestAppContext) {
23 init_test(cx);
24
25 fn stack_frame_for_line(line: u64) -> dap::StackFrame {
26 StackFrame {
27 id: 1,
28 name: "Stack Frame 1".into(),
29 source: Some(dap::Source {
30 name: Some("main.rs".into()),
31 path: Some(path!("/project/main.rs").into()),
32 source_reference: None,
33 presentation_hint: None,
34 origin: None,
35 sources: None,
36 adapter_data: None,
37 checksums: None,
38 }),
39 line,
40 column: 1,
41 end_line: None,
42 end_column: None,
43 can_restart: None,
44 instruction_pointer_reference: None,
45 module_id: None,
46 presentation_hint: None,
47 }
48 }
49
50 let fs = FakeFs::new(executor.clone());
51 let source_code = r#"
52static mut GLOBAL: usize = 1;
53
54fn main() {
55 let x = 10;
56 let value = 42;
57 let y = 4;
58 let tester = {
59 let y = 10;
60 let y = 5;
61 let b = 3;
62 vec![y, 20, 30]
63 };
64
65 let caller = || {
66 let x = 3;
67 println!("x={}", x);
68 };
69
70 caller();
71
72 unsafe {
73 GLOBAL = 2;
74 }
75
76 let result = value * 2 * x;
77 println!("Simple test executed: value={}, result={}", value, result);
78 assert!(true);
79}
80"#
81 .unindent();
82 fs.insert_tree(path!("/project"), json!({ "main.rs": source_code }))
83 .await;
84
85 let project = Project::test(fs.clone(), [path!("/project").as_ref()], cx).await;
86 let workspace = init_test_workspace(&project, cx).await;
87 workspace
88 .update(cx, |workspace, window, cx| {
89 workspace.focus_panel::<DebugPanel>(window, cx);
90 })
91 .unwrap();
92 let cx = &mut VisualTestContext::from_window(*workspace, cx);
93
94 let session = start_debug_session(&workspace, cx, |_| {}).unwrap();
95 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
96
97 client.on_request::<dap::requests::Threads, _>(move |_, _| {
98 Ok(dap::ThreadsResponse {
99 threads: vec![dap::Thread {
100 id: 1,
101 name: "Thread 1".into(),
102 }],
103 })
104 });
105
106 client.on_request::<dap::requests::StackTrace, _>(move |_, _| {
107 Ok(dap::StackTraceResponse {
108 stack_frames: vec![stack_frame_for_line(4)],
109 total_frames: None,
110 })
111 });
112
113 client.on_request::<dap::requests::Evaluate, _>(move |_, args| {
114 assert_eq!("GLOBAL", args.expression);
115 Ok(dap::EvaluateResponse {
116 result: "1".into(),
117 type_: None,
118 presentation_hint: None,
119 variables_reference: 0,
120 named_variables: None,
121 indexed_variables: None,
122 memory_reference: None,
123 value_location_reference: None,
124 })
125 });
126
127 let local_variables = vec![
128 Variable {
129 name: "x".into(),
130 value: "10".into(),
131 type_: None,
132 presentation_hint: None,
133 evaluate_name: None,
134 variables_reference: 0,
135 named_variables: None,
136 indexed_variables: None,
137 memory_reference: None,
138 declaration_location_reference: None,
139 value_location_reference: None,
140 },
141 Variable {
142 name: "y".into(),
143 value: "4".into(),
144 type_: None,
145 presentation_hint: None,
146 evaluate_name: None,
147 variables_reference: 0,
148 named_variables: None,
149 indexed_variables: None,
150 memory_reference: None,
151 declaration_location_reference: None,
152 value_location_reference: None,
153 },
154 Variable {
155 name: "value".into(),
156 value: "42".into(),
157 type_: None,
158 presentation_hint: None,
159 evaluate_name: None,
160 variables_reference: 0,
161 named_variables: None,
162 indexed_variables: None,
163 memory_reference: None,
164 declaration_location_reference: None,
165 value_location_reference: None,
166 },
167 ];
168
169 client.on_request::<Variables, _>({
170 let local_variables = Arc::new(local_variables.clone());
171 move |_, _| {
172 Ok(dap::VariablesResponse {
173 variables: (*local_variables).clone(),
174 })
175 }
176 });
177
178 client.on_request::<dap::requests::Scopes, _>(move |_, _| {
179 Ok(dap::ScopesResponse {
180 scopes: vec![Scope {
181 name: "Locale".into(),
182 presentation_hint: None,
183 variables_reference: 2,
184 named_variables: None,
185 indexed_variables: None,
186 expensive: false,
187 source: None,
188 line: None,
189 column: None,
190 end_line: None,
191 end_column: None,
192 }],
193 })
194 });
195
196 client
197 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
198 reason: dap::StoppedEventReason::Pause,
199 description: None,
200 thread_id: Some(1),
201 preserve_focus_hint: None,
202 text: None,
203 all_threads_stopped: None,
204 hit_breakpoint_ids: None,
205 }))
206 .await;
207
208 cx.run_until_parked();
209
210 let project_path = Path::new(path!("/project"));
211 let worktree = project
212 .update(cx, |project, cx| project.find_worktree(project_path, cx))
213 .expect("This worktree should exist in project")
214 .0;
215
216 let worktree_id = workspace
217 .update(cx, |_, _, cx| worktree.read(cx).id())
218 .unwrap();
219
220 let buffer = project
221 .update(cx, |project, cx| {
222 project.open_buffer((worktree_id, rel_path("main.rs")), cx)
223 })
224 .await
225 .unwrap();
226
227 buffer.update(cx, |buffer, cx| {
228 buffer.set_language(Some(rust_lang()), cx);
229 });
230
231 let (editor, cx) = cx.add_window_view(|window, cx| {
232 Editor::new(
233 EditorMode::full(),
234 MultiBuffer::build_from_buffer(buffer, cx),
235 Some(project),
236 window,
237 cx,
238 )
239 });
240
241 active_debug_session_panel(workspace, cx).update_in(cx, |_, window, cx| {
242 cx.focus_self(window);
243 });
244 cx.run_until_parked();
245
246 editor.update(cx, |editor, cx| editor.refresh_inline_values(cx));
247
248 cx.run_until_parked();
249
250 editor.update_in(cx, |editor, window, cx| {
251 pretty_assertions::assert_eq!(
252 r#"
253 static mut GLOBAL: usize = 1;
254
255 fn main() {
256 let x: 10 = 10;
257 let value = 42;
258 let y = 4;
259 let tester = {
260 let y = 10;
261 let y = 5;
262 let b = 3;
263 vec![y, 20, 30]
264 };
265
266 let caller = || {
267 let x = 3;
268 println!("x={}", x);
269 };
270
271 caller();
272
273 unsafe {
274 GLOBAL = 2;
275 }
276
277 let result = value * 2 * x;
278 println!("Simple test executed: value={}, result={}", value, result);
279 assert!(true);
280 }
281 "#
282 .unindent(),
283 editor.snapshot(window, cx).text()
284 );
285 });
286
287 client.on_request::<dap::requests::StackTrace, _>(move |_, _| {
288 Ok(dap::StackTraceResponse {
289 stack_frames: vec![stack_frame_for_line(5)],
290 total_frames: None,
291 })
292 });
293 client
294 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
295 reason: dap::StoppedEventReason::Pause,
296 description: None,
297 thread_id: Some(1),
298 preserve_focus_hint: None,
299 text: None,
300 all_threads_stopped: None,
301 hit_breakpoint_ids: None,
302 }))
303 .await;
304
305 cx.run_until_parked();
306
307 editor.update_in(cx, |editor, window, cx| {
308 pretty_assertions::assert_eq!(
309 r#"
310 static mut GLOBAL: usize = 1;
311
312 fn main() {
313 let x: 10 = 10;
314 let value: 42 = 42;
315 let y = 4;
316 let tester = {
317 let y = 10;
318 let y = 5;
319 let b = 3;
320 vec![y, 20, 30]
321 };
322
323 let caller = || {
324 let x = 3;
325 println!("x={}", x);
326 };
327
328 caller();
329
330 unsafe {
331 GLOBAL = 2;
332 }
333
334 let result = value * 2 * x;
335 println!("Simple test executed: value={}, result={}", value, result);
336 assert!(true);
337 }
338 "#
339 .unindent(),
340 editor.snapshot(window, cx).text()
341 );
342 });
343
344 client.on_request::<dap::requests::StackTrace, _>(move |_, _| {
345 Ok(dap::StackTraceResponse {
346 stack_frames: vec![stack_frame_for_line(6)],
347 total_frames: None,
348 })
349 });
350 client
351 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
352 reason: dap::StoppedEventReason::Pause,
353 description: None,
354 thread_id: Some(1),
355 preserve_focus_hint: None,
356 text: None,
357 all_threads_stopped: None,
358 hit_breakpoint_ids: None,
359 }))
360 .await;
361
362 cx.run_until_parked();
363
364 editor.update_in(cx, |editor, window, cx| {
365 pretty_assertions::assert_eq!(
366 r#"
367 static mut GLOBAL: usize = 1;
368
369 fn main() {
370 let x: 10 = 10;
371 let value: 42 = 42;
372 let y: 4 = 4;
373 let tester = {
374 let y = 10;
375 let y = 5;
376 let b = 3;
377 vec![y, 20, 30]
378 };
379
380 let caller = || {
381 let x = 3;
382 println!("x={}", x);
383 };
384
385 caller();
386
387 unsafe {
388 GLOBAL = 2;
389 }
390
391 let result = value * 2 * x;
392 println!("Simple test executed: value={}, result={}", value, result);
393 assert!(true);
394 }
395 "#
396 .unindent(),
397 editor.snapshot(window, cx).text()
398 );
399 });
400
401 client.on_request::<dap::requests::StackTrace, _>(move |_, _| {
402 Ok(dap::StackTraceResponse {
403 stack_frames: vec![stack_frame_for_line(7)],
404 total_frames: None,
405 })
406 });
407 client
408 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
409 reason: dap::StoppedEventReason::Pause,
410 description: None,
411 thread_id: Some(1),
412 preserve_focus_hint: None,
413 text: None,
414 all_threads_stopped: None,
415 hit_breakpoint_ids: None,
416 }))
417 .await;
418
419 cx.run_until_parked();
420
421 editor.update_in(cx, |editor, window, cx| {
422 pretty_assertions::assert_eq!(
423 r#"
424 static mut GLOBAL: usize = 1;
425
426 fn main() {
427 let x: 10 = 10;
428 let value: 42 = 42;
429 let y: 4 = 4;
430 let tester = {
431 let y = 10;
432 let y = 5;
433 let b = 3;
434 vec![y, 20, 30]
435 };
436
437 let caller = || {
438 let x = 3;
439 println!("x={}", x);
440 };
441
442 caller();
443
444 unsafe {
445 GLOBAL = 2;
446 }
447
448 let result = value * 2 * x;
449 println!("Simple test executed: value={}, result={}", value, result);
450 assert!(true);
451 }
452 "#
453 .unindent(),
454 editor.snapshot(window, cx).text()
455 );
456 });
457
458 client.on_request::<dap::requests::StackTrace, _>(move |_, _| {
459 Ok(dap::StackTraceResponse {
460 stack_frames: vec![stack_frame_for_line(8)],
461 total_frames: None,
462 })
463 });
464 client
465 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
466 reason: dap::StoppedEventReason::Pause,
467 description: None,
468 thread_id: Some(1),
469 preserve_focus_hint: None,
470 text: None,
471 all_threads_stopped: None,
472 hit_breakpoint_ids: None,
473 }))
474 .await;
475
476 cx.run_until_parked();
477
478 editor.update_in(cx, |editor, window, cx| {
479 pretty_assertions::assert_eq!(
480 r#"
481 static mut GLOBAL: usize = 1;
482
483 fn main() {
484 let x: 10 = 10;
485 let value: 42 = 42;
486 let y: 4 = 4;
487 let tester = {
488 let y: 4 = 10;
489 let y = 5;
490 let b = 3;
491 vec![y, 20, 30]
492 };
493
494 let caller = || {
495 let x = 3;
496 println!("x={}", x);
497 };
498
499 caller();
500
501 unsafe {
502 GLOBAL = 2;
503 }
504
505 let result = value * 2 * x;
506 println!("Simple test executed: value={}, result={}", value, result);
507 assert!(true);
508 }
509 "#
510 .unindent(),
511 editor.snapshot(window, cx).text()
512 );
513 });
514
515 let local_variables = vec![
516 Variable {
517 name: "x".into(),
518 value: "10".into(),
519 type_: None,
520 presentation_hint: None,
521 evaluate_name: None,
522 variables_reference: 0,
523 named_variables: None,
524 indexed_variables: None,
525 memory_reference: None,
526 declaration_location_reference: None,
527 value_location_reference: None,
528 },
529 Variable {
530 name: "y".into(),
531 value: "10".into(),
532 type_: None,
533 presentation_hint: None,
534 evaluate_name: None,
535 variables_reference: 0,
536 named_variables: None,
537 indexed_variables: None,
538 memory_reference: None,
539 declaration_location_reference: None,
540 value_location_reference: None,
541 },
542 Variable {
543 name: "value".into(),
544 value: "42".into(),
545 type_: None,
546 presentation_hint: None,
547 evaluate_name: None,
548 variables_reference: 0,
549 named_variables: None,
550 indexed_variables: None,
551 memory_reference: None,
552 declaration_location_reference: None,
553 value_location_reference: None,
554 },
555 ];
556
557 client.on_request::<Variables, _>({
558 let local_variables = Arc::new(local_variables.clone());
559 move |_, _| {
560 Ok(dap::VariablesResponse {
561 variables: (*local_variables).clone(),
562 })
563 }
564 });
565 client.on_request::<dap::requests::StackTrace, _>(move |_, _| {
566 Ok(dap::StackTraceResponse {
567 stack_frames: vec![stack_frame_for_line(9)],
568 total_frames: None,
569 })
570 });
571 client
572 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
573 reason: dap::StoppedEventReason::Pause,
574 description: None,
575 thread_id: Some(1),
576 preserve_focus_hint: None,
577 text: None,
578 all_threads_stopped: None,
579 hit_breakpoint_ids: None,
580 }))
581 .await;
582
583 cx.run_until_parked();
584
585 editor.update_in(cx, |editor, window, cx| {
586 pretty_assertions::assert_eq!(
587 r#"
588 static mut GLOBAL: usize = 1;
589
590 fn main() {
591 let x: 10 = 10;
592 let value: 42 = 42;
593 let y: 10 = 4;
594 let tester = {
595 let y: 10 = 10;
596 let y: 10 = 5;
597 let b = 3;
598 vec![y, 20, 30]
599 };
600
601 let caller = || {
602 let x = 3;
603 println!("x={}", x);
604 };
605
606 caller();
607
608 unsafe {
609 GLOBAL = 2;
610 }
611
612 let result = value * 2 * x;
613 println!("Simple test executed: value={}, result={}", value, result);
614 assert!(true);
615 }
616 "#
617 .unindent(),
618 editor.snapshot(window, cx).text()
619 );
620 });
621
622 let local_variables = vec![
623 Variable {
624 name: "x".into(),
625 value: "10".into(),
626 type_: None,
627 presentation_hint: None,
628 evaluate_name: None,
629 variables_reference: 0,
630 named_variables: None,
631 indexed_variables: None,
632 memory_reference: None,
633 declaration_location_reference: None,
634 value_location_reference: None,
635 },
636 Variable {
637 name: "y".into(),
638 value: "5".into(),
639 type_: None,
640 presentation_hint: None,
641 evaluate_name: None,
642 variables_reference: 0,
643 named_variables: None,
644 indexed_variables: None,
645 memory_reference: None,
646 declaration_location_reference: None,
647 value_location_reference: None,
648 },
649 Variable {
650 name: "value".into(),
651 value: "42".into(),
652 type_: None,
653 presentation_hint: None,
654 evaluate_name: None,
655 variables_reference: 0,
656 named_variables: None,
657 indexed_variables: None,
658 memory_reference: None,
659 declaration_location_reference: None,
660 value_location_reference: None,
661 },
662 ];
663
664 client.on_request::<Variables, _>({
665 let local_variables = Arc::new(local_variables.clone());
666 move |_, _| {
667 Ok(dap::VariablesResponse {
668 variables: (*local_variables).clone(),
669 })
670 }
671 });
672 client.on_request::<dap::requests::StackTrace, _>(move |_, _| {
673 Ok(dap::StackTraceResponse {
674 stack_frames: vec![stack_frame_for_line(10)],
675 total_frames: None,
676 })
677 });
678 client
679 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
680 reason: dap::StoppedEventReason::Pause,
681 description: None,
682 thread_id: Some(1),
683 preserve_focus_hint: None,
684 text: None,
685 all_threads_stopped: None,
686 hit_breakpoint_ids: None,
687 }))
688 .await;
689
690 cx.run_until_parked();
691
692 editor.update_in(cx, |editor, window, cx| {
693 pretty_assertions::assert_eq!(
694 r#"
695 static mut GLOBAL: usize = 1;
696
697 fn main() {
698 let x: 10 = 10;
699 let value: 42 = 42;
700 let y: 5 = 4;
701 let tester = {
702 let y: 5 = 10;
703 let y: 5 = 5;
704 let b = 3;
705 vec![y, 20, 30]
706 };
707
708 let caller = || {
709 let x = 3;
710 println!("x={}", x);
711 };
712
713 caller();
714
715 unsafe {
716 GLOBAL = 2;
717 }
718
719 let result = value * 2 * x;
720 println!("Simple test executed: value={}, result={}", value, result);
721 assert!(true);
722 }
723 "#
724 .unindent(),
725 editor.snapshot(window, cx).text()
726 );
727 });
728
729 let local_variables = vec![
730 Variable {
731 name: "x".into(),
732 value: "10".into(),
733 type_: None,
734 presentation_hint: None,
735 evaluate_name: None,
736 variables_reference: 0,
737 named_variables: None,
738 indexed_variables: None,
739 memory_reference: None,
740 declaration_location_reference: None,
741 value_location_reference: None,
742 },
743 Variable {
744 name: "y".into(),
745 value: "5".into(),
746 type_: None,
747 presentation_hint: None,
748 evaluate_name: None,
749 variables_reference: 0,
750 named_variables: None,
751 indexed_variables: None,
752 memory_reference: None,
753 declaration_location_reference: None,
754 value_location_reference: None,
755 },
756 Variable {
757 name: "value".into(),
758 value: "42".into(),
759 type_: None,
760 presentation_hint: None,
761 evaluate_name: None,
762 variables_reference: 0,
763 named_variables: None,
764 indexed_variables: None,
765 memory_reference: None,
766 declaration_location_reference: None,
767 value_location_reference: None,
768 },
769 Variable {
770 name: "b".into(),
771 value: "3".into(),
772 type_: None,
773 presentation_hint: None,
774 evaluate_name: None,
775 variables_reference: 0,
776 named_variables: None,
777 indexed_variables: None,
778 memory_reference: None,
779 declaration_location_reference: None,
780 value_location_reference: None,
781 },
782 ];
783 client.on_request::<Variables, _>({
784 let local_variables = Arc::new(local_variables.clone());
785 move |_, _| {
786 Ok(dap::VariablesResponse {
787 variables: (*local_variables).clone(),
788 })
789 }
790 });
791 client.on_request::<dap::requests::StackTrace, _>(move |_, _| {
792 Ok(dap::StackTraceResponse {
793 stack_frames: vec![stack_frame_for_line(11)],
794 total_frames: None,
795 })
796 });
797 client
798 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
799 reason: dap::StoppedEventReason::Pause,
800 description: None,
801 thread_id: Some(1),
802 preserve_focus_hint: None,
803 text: None,
804 all_threads_stopped: None,
805 hit_breakpoint_ids: None,
806 }))
807 .await;
808
809 cx.run_until_parked();
810
811 editor.update_in(cx, |editor, window, cx| {
812 pretty_assertions::assert_eq!(
813 r#"
814 static mut GLOBAL: usize = 1;
815
816 fn main() {
817 let x: 10 = 10;
818 let value: 42 = 42;
819 let y: 5 = 4;
820 let tester = {
821 let y: 5 = 10;
822 let y: 5 = 5;
823 let b: 3 = 3;
824 vec![y: 5, 20, 30]
825 };
826
827 let caller = || {
828 let x = 3;
829 println!("x={}", x);
830 };
831
832 caller();
833
834 unsafe {
835 GLOBAL = 2;
836 }
837
838 let result = value * 2 * x;
839 println!("Simple test executed: value={}, result={}", value, result);
840 assert!(true);
841 }
842 "#
843 .unindent(),
844 editor.snapshot(window, cx).text()
845 );
846 });
847
848 let local_variables = vec![
849 Variable {
850 name: "x".into(),
851 value: "10".into(),
852 type_: None,
853 presentation_hint: None,
854 evaluate_name: None,
855 variables_reference: 0,
856 named_variables: None,
857 indexed_variables: None,
858 memory_reference: None,
859 declaration_location_reference: None,
860 value_location_reference: None,
861 },
862 Variable {
863 name: "y".into(),
864 value: "4".into(),
865 type_: None,
866 presentation_hint: None,
867 evaluate_name: None,
868 variables_reference: 0,
869 named_variables: None,
870 indexed_variables: None,
871 memory_reference: None,
872 declaration_location_reference: None,
873 value_location_reference: None,
874 },
875 Variable {
876 name: "value".into(),
877 value: "42".into(),
878 type_: None,
879 presentation_hint: None,
880 evaluate_name: None,
881 variables_reference: 0,
882 named_variables: None,
883 indexed_variables: None,
884 memory_reference: None,
885 declaration_location_reference: None,
886 value_location_reference: None,
887 },
888 Variable {
889 name: "tester".into(),
890 value: "size=3".into(),
891 type_: None,
892 presentation_hint: None,
893 evaluate_name: None,
894 variables_reference: 0,
895 named_variables: None,
896 indexed_variables: None,
897 memory_reference: None,
898 declaration_location_reference: None,
899 value_location_reference: None,
900 },
901 ];
902 client.on_request::<Variables, _>({
903 let local_variables = Arc::new(local_variables.clone());
904 move |_, _| {
905 Ok(dap::VariablesResponse {
906 variables: (*local_variables).clone(),
907 })
908 }
909 });
910 client.on_request::<dap::requests::StackTrace, _>(move |_, _| {
911 Ok(dap::StackTraceResponse {
912 stack_frames: vec![stack_frame_for_line(14)],
913 total_frames: None,
914 })
915 });
916 client
917 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
918 reason: dap::StoppedEventReason::Pause,
919 description: None,
920 thread_id: Some(1),
921 preserve_focus_hint: None,
922 text: None,
923 all_threads_stopped: None,
924 hit_breakpoint_ids: None,
925 }))
926 .await;
927
928 cx.run_until_parked();
929
930 editor.update_in(cx, |editor, window, cx| {
931 pretty_assertions::assert_eq!(
932 r#"
933 static mut GLOBAL: usize = 1;
934
935 fn main() {
936 let x: 10 = 10;
937 let value: 42 = 42;
938 let y: 4 = 4;
939 let tester: size=3 = {
940 let y = 10;
941 let y = 5;
942 let b = 3;
943 vec![y, 20, 30]
944 };
945
946 let caller = || {
947 let x = 3;
948 println!("x={}", x);
949 };
950
951 caller();
952
953 unsafe {
954 GLOBAL = 2;
955 }
956
957 let result = value * 2 * x;
958 println!("Simple test executed: value={}, result={}", value, result);
959 assert!(true);
960 }
961 "#
962 .unindent(),
963 editor.snapshot(window, cx).text()
964 );
965 });
966
967 let local_variables = vec![
968 Variable {
969 name: "x".into(),
970 value: "10".into(),
971 type_: None,
972 presentation_hint: None,
973 evaluate_name: None,
974 variables_reference: 0,
975 named_variables: None,
976 indexed_variables: None,
977 memory_reference: None,
978 declaration_location_reference: None,
979 value_location_reference: None,
980 },
981 Variable {
982 name: "y".into(),
983 value: "4".into(),
984 type_: None,
985 presentation_hint: None,
986 evaluate_name: None,
987 variables_reference: 0,
988 named_variables: None,
989 indexed_variables: None,
990 memory_reference: None,
991 declaration_location_reference: None,
992 value_location_reference: None,
993 },
994 Variable {
995 name: "value".into(),
996 value: "42".into(),
997 type_: None,
998 presentation_hint: None,
999 evaluate_name: None,
1000 variables_reference: 0,
1001 named_variables: None,
1002 indexed_variables: None,
1003 memory_reference: None,
1004 declaration_location_reference: None,
1005 value_location_reference: None,
1006 },
1007 Variable {
1008 name: "tester".into(),
1009 value: "size=3".into(),
1010 type_: None,
1011 presentation_hint: None,
1012 evaluate_name: None,
1013 variables_reference: 0,
1014 named_variables: None,
1015 indexed_variables: None,
1016 memory_reference: None,
1017 declaration_location_reference: None,
1018 value_location_reference: None,
1019 },
1020 Variable {
1021 name: "caller".into(),
1022 value: "<not available>".into(),
1023 type_: None,
1024 presentation_hint: None,
1025 evaluate_name: None,
1026 variables_reference: 0,
1027 named_variables: None,
1028 indexed_variables: None,
1029 memory_reference: None,
1030 declaration_location_reference: None,
1031 value_location_reference: None,
1032 },
1033 ];
1034 client.on_request::<Variables, _>({
1035 let local_variables = Arc::new(local_variables.clone());
1036 move |_, _| {
1037 Ok(dap::VariablesResponse {
1038 variables: (*local_variables).clone(),
1039 })
1040 }
1041 });
1042 client.on_request::<dap::requests::StackTrace, _>(move |_, _| {
1043 Ok(dap::StackTraceResponse {
1044 stack_frames: vec![stack_frame_for_line(19)],
1045 total_frames: None,
1046 })
1047 });
1048 client
1049 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
1050 reason: dap::StoppedEventReason::Pause,
1051 description: None,
1052 thread_id: Some(1),
1053 preserve_focus_hint: None,
1054 text: None,
1055 all_threads_stopped: None,
1056 hit_breakpoint_ids: None,
1057 }))
1058 .await;
1059
1060 cx.run_until_parked();
1061
1062 editor.update_in(cx, |editor, window, cx| {
1063 pretty_assertions::assert_eq!(
1064 r#"
1065 static mut GLOBAL: usize = 1;
1066
1067 fn main() {
1068 let x: 10 = 10;
1069 let value: 42 = 42;
1070 let y: 4 = 4;
1071 let tester: size=3 = {
1072 let y = 10;
1073 let y = 5;
1074 let b = 3;
1075 vec![y, 20, 30]
1076 };
1077
1078 let caller: <not available> = || {
1079 let x = 3;
1080 println!("x={}", x);
1081 };
1082
1083 caller();
1084
1085 unsafe {
1086 GLOBAL = 2;
1087 }
1088
1089 let result = value * 2 * x;
1090 println!("Simple test executed: value={}, result={}", value, result);
1091 assert!(true);
1092 }
1093 "#
1094 .unindent(),
1095 editor.snapshot(window, cx).text()
1096 );
1097 });
1098
1099 client.on_request::<dap::requests::StackTrace, _>(move |_, _| {
1100 Ok(dap::StackTraceResponse {
1101 stack_frames: vec![stack_frame_for_line(15)],
1102 total_frames: None,
1103 })
1104 });
1105 client
1106 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
1107 reason: dap::StoppedEventReason::Pause,
1108 description: None,
1109 thread_id: Some(1),
1110 preserve_focus_hint: None,
1111 text: None,
1112 all_threads_stopped: None,
1113 hit_breakpoint_ids: None,
1114 }))
1115 .await;
1116
1117 cx.run_until_parked();
1118
1119 editor.update_in(cx, |editor, window, cx| {
1120 pretty_assertions::assert_eq!(
1121 r#"
1122 static mut GLOBAL: usize = 1;
1123
1124 fn main() {
1125 let x: 10 = 10;
1126 let value: 42 = 42;
1127 let y: 4 = 4;
1128 let tester: size=3 = {
1129 let y = 10;
1130 let y = 5;
1131 let b = 3;
1132 vec![y, 20, 30]
1133 };
1134
1135 let caller: <not available> = || {
1136 let x: 10 = 3;
1137 println!("x={}", x);
1138 };
1139
1140 caller();
1141
1142 unsafe {
1143 GLOBAL = 2;
1144 }
1145
1146 let result = value * 2 * x;
1147 println!("Simple test executed: value={}, result={}", value, result);
1148 assert!(true);
1149 }
1150 "#
1151 .unindent(),
1152 editor.snapshot(window, cx).text()
1153 );
1154 });
1155
1156 let local_variables = vec![Variable {
1157 name: "x".into(),
1158 value: "3".into(),
1159 type_: None,
1160 presentation_hint: None,
1161 evaluate_name: None,
1162 variables_reference: 0,
1163 named_variables: None,
1164 indexed_variables: None,
1165 memory_reference: None,
1166 declaration_location_reference: None,
1167 value_location_reference: None,
1168 }];
1169 client.on_request::<Variables, _>({
1170 let local_variables = Arc::new(local_variables.clone());
1171 move |_, _| {
1172 Ok(dap::VariablesResponse {
1173 variables: (*local_variables).clone(),
1174 })
1175 }
1176 });
1177 client.on_request::<dap::requests::StackTrace, _>(move |_, _| {
1178 Ok(dap::StackTraceResponse {
1179 stack_frames: vec![stack_frame_for_line(16)],
1180 total_frames: None,
1181 })
1182 });
1183 client
1184 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
1185 reason: dap::StoppedEventReason::Pause,
1186 description: None,
1187 thread_id: Some(1),
1188 preserve_focus_hint: None,
1189 text: None,
1190 all_threads_stopped: None,
1191 hit_breakpoint_ids: None,
1192 }))
1193 .await;
1194
1195 cx.run_until_parked();
1196
1197 editor.update_in(cx, |editor, window, cx| {
1198 pretty_assertions::assert_eq!(
1199 r#"
1200 static mut GLOBAL: usize = 1;
1201
1202 fn main() {
1203 let x: 3 = 10;
1204 let value = 42;
1205 let y = 4;
1206 let tester = {
1207 let y = 10;
1208 let y = 5;
1209 let b = 3;
1210 vec![y, 20, 30]
1211 };
1212
1213 let caller = || {
1214 let x: 3 = 3;
1215 println!("x={}", x: 3);
1216 };
1217
1218 caller();
1219
1220 unsafe {
1221 GLOBAL = 2;
1222 }
1223
1224 let result = value * 2 * x;
1225 println!("Simple test executed: value={}, result={}", value, result);
1226 assert!(true);
1227 }
1228 "#
1229 .unindent(),
1230 editor.snapshot(window, cx).text()
1231 );
1232 });
1233
1234 let local_variables = vec![
1235 Variable {
1236 name: "x".into(),
1237 value: "10".into(),
1238 type_: None,
1239 presentation_hint: None,
1240 evaluate_name: None,
1241 variables_reference: 0,
1242 named_variables: None,
1243 indexed_variables: None,
1244 memory_reference: None,
1245 declaration_location_reference: None,
1246 value_location_reference: None,
1247 },
1248 Variable {
1249 name: "y".into(),
1250 value: "4".into(),
1251 type_: None,
1252 presentation_hint: None,
1253 evaluate_name: None,
1254 variables_reference: 0,
1255 named_variables: None,
1256 indexed_variables: None,
1257 memory_reference: None,
1258 declaration_location_reference: None,
1259 value_location_reference: None,
1260 },
1261 Variable {
1262 name: "value".into(),
1263 value: "42".into(),
1264 type_: None,
1265 presentation_hint: None,
1266 evaluate_name: None,
1267 variables_reference: 0,
1268 named_variables: None,
1269 indexed_variables: None,
1270 memory_reference: None,
1271 declaration_location_reference: None,
1272 value_location_reference: None,
1273 },
1274 Variable {
1275 name: "tester".into(),
1276 value: "size=3".into(),
1277 type_: None,
1278 presentation_hint: None,
1279 evaluate_name: None,
1280 variables_reference: 0,
1281 named_variables: None,
1282 indexed_variables: None,
1283 memory_reference: None,
1284 declaration_location_reference: None,
1285 value_location_reference: None,
1286 },
1287 Variable {
1288 name: "caller".into(),
1289 value: "<not available>".into(),
1290 type_: None,
1291 presentation_hint: None,
1292 evaluate_name: None,
1293 variables_reference: 0,
1294 named_variables: None,
1295 indexed_variables: None,
1296 memory_reference: None,
1297 declaration_location_reference: None,
1298 value_location_reference: None,
1299 },
1300 ];
1301 client.on_request::<Variables, _>({
1302 let local_variables = Arc::new(local_variables.clone());
1303 move |_, _| {
1304 Ok(dap::VariablesResponse {
1305 variables: (*local_variables).clone(),
1306 })
1307 }
1308 });
1309 client.on_request::<dap::requests::Evaluate, _>(move |_, args| {
1310 assert_eq!("GLOBAL", args.expression);
1311 Ok(dap::EvaluateResponse {
1312 result: "2".into(),
1313 type_: None,
1314 presentation_hint: None,
1315 variables_reference: 0,
1316 named_variables: None,
1317 indexed_variables: None,
1318 memory_reference: None,
1319 value_location_reference: None,
1320 })
1321 });
1322 client.on_request::<dap::requests::StackTrace, _>(move |_, _| {
1323 Ok(dap::StackTraceResponse {
1324 stack_frames: vec![stack_frame_for_line(25)],
1325 total_frames: None,
1326 })
1327 });
1328 client
1329 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
1330 reason: dap::StoppedEventReason::Pause,
1331 description: None,
1332 thread_id: Some(1),
1333 preserve_focus_hint: None,
1334 text: None,
1335 all_threads_stopped: None,
1336 hit_breakpoint_ids: None,
1337 }))
1338 .await;
1339
1340 cx.run_until_parked();
1341
1342 editor.update_in(cx, |editor, window, cx| {
1343 pretty_assertions::assert_eq!(
1344 r#"
1345 static mut GLOBAL: usize = 1;
1346
1347 fn main() {
1348 let x: 10 = 10;
1349 let value: 42 = 42;
1350 let y: 4 = 4;
1351 let tester: size=3 = {
1352 let y = 10;
1353 let y = 5;
1354 let b = 3;
1355 vec![y, 20, 30]
1356 };
1357
1358 let caller: <not available> = || {
1359 let x = 3;
1360 println!("x={}", x);
1361 };
1362
1363 caller();
1364
1365 unsafe {
1366 GLOBAL = 2;
1367 }
1368
1369 let result = value: 42 * 2 * x: 10;
1370 println!("Simple test executed: value={}, result={}", value, result);
1371 assert!(true);
1372 }
1373 "#
1374 .unindent(),
1375 editor.snapshot(window, cx).text()
1376 );
1377 });
1378
1379 let local_variables = vec![
1380 Variable {
1381 name: "x".into(),
1382 value: "10".into(),
1383 type_: None,
1384 presentation_hint: None,
1385 evaluate_name: None,
1386 variables_reference: 0,
1387 named_variables: None,
1388 indexed_variables: None,
1389 memory_reference: None,
1390 declaration_location_reference: None,
1391 value_location_reference: None,
1392 },
1393 Variable {
1394 name: "y".into(),
1395 value: "4".into(),
1396 type_: None,
1397 presentation_hint: None,
1398 evaluate_name: None,
1399 variables_reference: 0,
1400 named_variables: None,
1401 indexed_variables: None,
1402 memory_reference: None,
1403 declaration_location_reference: None,
1404 value_location_reference: None,
1405 },
1406 Variable {
1407 name: "value".into(),
1408 value: "42".into(),
1409 type_: None,
1410 presentation_hint: None,
1411 evaluate_name: None,
1412 variables_reference: 0,
1413 named_variables: None,
1414 indexed_variables: None,
1415 memory_reference: None,
1416 declaration_location_reference: None,
1417 value_location_reference: None,
1418 },
1419 Variable {
1420 name: "tester".into(),
1421 value: "size=3".into(),
1422 type_: None,
1423 presentation_hint: None,
1424 evaluate_name: None,
1425 variables_reference: 0,
1426 named_variables: None,
1427 indexed_variables: None,
1428 memory_reference: None,
1429 declaration_location_reference: None,
1430 value_location_reference: None,
1431 },
1432 Variable {
1433 name: "caller".into(),
1434 value: "<not available>".into(),
1435 type_: None,
1436 presentation_hint: None,
1437 evaluate_name: None,
1438 variables_reference: 0,
1439 named_variables: None,
1440 indexed_variables: None,
1441 memory_reference: None,
1442 declaration_location_reference: None,
1443 value_location_reference: None,
1444 },
1445 Variable {
1446 name: "result".into(),
1447 value: "840".into(),
1448 type_: None,
1449 presentation_hint: None,
1450 evaluate_name: None,
1451 variables_reference: 0,
1452 named_variables: None,
1453 indexed_variables: None,
1454 memory_reference: None,
1455 declaration_location_reference: None,
1456 value_location_reference: None,
1457 },
1458 ];
1459 client.on_request::<Variables, _>({
1460 let local_variables = Arc::new(local_variables.clone());
1461 move |_, _| {
1462 Ok(dap::VariablesResponse {
1463 variables: (*local_variables).clone(),
1464 })
1465 }
1466 });
1467 client.on_request::<dap::requests::StackTrace, _>(move |_, _| {
1468 Ok(dap::StackTraceResponse {
1469 stack_frames: vec![stack_frame_for_line(26)],
1470 total_frames: None,
1471 })
1472 });
1473 client
1474 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
1475 reason: dap::StoppedEventReason::Pause,
1476 description: None,
1477 thread_id: Some(1),
1478 preserve_focus_hint: None,
1479 text: None,
1480 all_threads_stopped: None,
1481 hit_breakpoint_ids: None,
1482 }))
1483 .await;
1484
1485 cx.run_until_parked();
1486
1487 editor.update_in(cx, |editor, window, cx| {
1488 pretty_assertions::assert_eq!(
1489 r#"
1490 static mut GLOBAL: usize = 1;
1491
1492 fn main() {
1493 let x: 10 = 10;
1494 let value: 42 = 42;
1495 let y: 4 = 4;
1496 let tester: size=3 = {
1497 let y = 10;
1498 let y = 5;
1499 let b = 3;
1500 vec![y, 20, 30]
1501 };
1502
1503 let caller: <not available> = || {
1504 let x = 3;
1505 println!("x={}", x);
1506 };
1507
1508 caller();
1509
1510 unsafe {
1511 GLOBAL = 2;
1512 }
1513
1514 let result: 840 = value: 42 * 2 * x: 10;
1515 println!("Simple test executed: value={}, result={}", value: 42, result: 840);
1516 assert!(true);
1517 }
1518 "#
1519 .unindent(),
1520 editor.snapshot(window, cx).text()
1521 );
1522 });
1523}
1524
1525#[gpui::test]
1526async fn test_python_inline_values(executor: BackgroundExecutor, cx: &mut TestAppContext) {
1527 init_test(cx);
1528
1529 let fs = FakeFs::new(executor.clone());
1530 let source_code = r#"
1531def process_data(untyped_param, typed_param: int, another_typed: str):
1532 # Local variables
1533 x = 10
1534 result = typed_param * 2
1535 text = "Hello, " + another_typed
1536
1537 # For loop with range
1538 sum_value = 0
1539 for i in range(5):
1540 sum_value += i
1541
1542 # Final result
1543 final_result = x + result + sum_value
1544 return final_result
1545"#
1546 .unindent();
1547 fs.insert_tree(path!("/project"), json!({ "main.py": source_code }))
1548 .await;
1549
1550 let project = Project::test(fs.clone(), [path!("/project").as_ref()], cx).await;
1551 let workspace = init_test_workspace(&project, cx).await;
1552 workspace
1553 .update(cx, |workspace, window, cx| {
1554 workspace.focus_panel::<DebugPanel>(window, cx);
1555 })
1556 .unwrap();
1557 let cx = &mut VisualTestContext::from_window(*workspace, cx);
1558
1559 let session = start_debug_session(&workspace, cx, |_| {}).unwrap();
1560 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
1561
1562 let project_path = Path::new(path!("/project"));
1563 let worktree = project
1564 .update(cx, |project, cx| project.find_worktree(project_path, cx))
1565 .expect("This worktree should exist in project")
1566 .0;
1567
1568 let worktree_id = workspace
1569 .update(cx, |_, _, cx| worktree.read(cx).id())
1570 .unwrap();
1571
1572 let buffer = project
1573 .update(cx, |project, cx| {
1574 project.open_buffer((worktree_id, rel_path("main.py")), cx)
1575 })
1576 .await
1577 .unwrap();
1578
1579 buffer.update(cx, |buffer, cx| {
1580 buffer.set_language(Some(Arc::new(python_lang())), cx);
1581 });
1582
1583 let (editor, cx) = cx.add_window_view(|window, cx| {
1584 Editor::new(
1585 EditorMode::full(),
1586 MultiBuffer::build_from_buffer(buffer, cx),
1587 Some(project),
1588 window,
1589 cx,
1590 )
1591 });
1592
1593 editor.update(cx, |editor, cx| editor.refresh_inline_values(cx));
1594
1595 client.on_request::<dap::requests::Threads, _>(move |_, _| {
1596 Ok(dap::ThreadsResponse {
1597 threads: vec![dap::Thread {
1598 id: 1,
1599 name: "Thread 1".into(),
1600 }],
1601 })
1602 });
1603
1604 client.on_request::<dap::requests::StackTrace, _>(move |_, args| {
1605 assert_eq!(args.thread_id, 1);
1606 Ok(dap::StackTraceResponse {
1607 stack_frames: vec![StackFrame {
1608 id: 1,
1609 name: "Stack Frame 1".into(),
1610 source: Some(dap::Source {
1611 name: Some("main.py".into()),
1612 path: Some(path!("/project/main.py").into()),
1613 source_reference: None,
1614 presentation_hint: None,
1615 origin: None,
1616 sources: None,
1617 adapter_data: None,
1618 checksums: None,
1619 }),
1620 line: 12,
1621 column: 1,
1622 end_line: None,
1623 end_column: None,
1624 can_restart: None,
1625 instruction_pointer_reference: None,
1626 module_id: None,
1627 presentation_hint: None,
1628 }],
1629 total_frames: None,
1630 })
1631 });
1632
1633 client.on_request::<dap::requests::Scopes, _>(move |_, _| {
1634 Ok(dap::ScopesResponse {
1635 scopes: vec![
1636 Scope {
1637 name: "Local".into(),
1638 presentation_hint: None,
1639 variables_reference: 1,
1640 named_variables: None,
1641 indexed_variables: None,
1642 expensive: false,
1643 source: None,
1644 line: None,
1645 column: None,
1646 end_line: None,
1647 end_column: None,
1648 },
1649 Scope {
1650 name: "Global".into(),
1651 presentation_hint: None,
1652 variables_reference: 2,
1653 named_variables: None,
1654 indexed_variables: None,
1655 expensive: false,
1656 source: None,
1657 line: None,
1658 column: None,
1659 end_line: None,
1660 end_column: None,
1661 },
1662 ],
1663 })
1664 });
1665
1666 client.on_request::<Variables, _>(move |_, args| match args.variables_reference {
1667 1 => Ok(dap::VariablesResponse {
1668 variables: vec![
1669 Variable {
1670 name: "untyped_param".into(),
1671 value: "test_value".into(),
1672 type_: Some("str".into()),
1673 presentation_hint: None,
1674 evaluate_name: None,
1675 variables_reference: 0,
1676 named_variables: None,
1677 indexed_variables: None,
1678 memory_reference: None,
1679 declaration_location_reference: None,
1680 value_location_reference: None,
1681 },
1682 Variable {
1683 name: "typed_param".into(),
1684 value: "42".into(),
1685 type_: Some("int".into()),
1686 presentation_hint: None,
1687 evaluate_name: None,
1688 variables_reference: 0,
1689 named_variables: None,
1690 indexed_variables: None,
1691 memory_reference: None,
1692 declaration_location_reference: None,
1693 value_location_reference: None,
1694 },
1695 Variable {
1696 name: "another_typed".into(),
1697 value: "world".into(),
1698 type_: Some("str".into()),
1699 presentation_hint: None,
1700 evaluate_name: None,
1701 variables_reference: 0,
1702 named_variables: None,
1703 indexed_variables: None,
1704 memory_reference: None,
1705 declaration_location_reference: None,
1706 value_location_reference: None,
1707 },
1708 Variable {
1709 name: "x".into(),
1710 value: "10".into(),
1711 type_: Some("int".into()),
1712 presentation_hint: None,
1713 evaluate_name: None,
1714 variables_reference: 0,
1715 named_variables: None,
1716 indexed_variables: None,
1717 memory_reference: None,
1718 declaration_location_reference: None,
1719 value_location_reference: None,
1720 },
1721 Variable {
1722 name: "result".into(),
1723 value: "84".into(),
1724 type_: Some("int".into()),
1725 presentation_hint: None,
1726 evaluate_name: None,
1727 variables_reference: 0,
1728 named_variables: None,
1729 indexed_variables: None,
1730 memory_reference: None,
1731 declaration_location_reference: None,
1732 value_location_reference: None,
1733 },
1734 Variable {
1735 name: "text".into(),
1736 value: "Hello, world".into(),
1737 type_: Some("str".into()),
1738 presentation_hint: None,
1739 evaluate_name: None,
1740 variables_reference: 0,
1741 named_variables: None,
1742 indexed_variables: None,
1743 memory_reference: None,
1744 declaration_location_reference: None,
1745 value_location_reference: None,
1746 },
1747 Variable {
1748 name: "sum_value".into(),
1749 value: "10".into(),
1750 type_: Some("int".into()),
1751 presentation_hint: None,
1752 evaluate_name: None,
1753 variables_reference: 0,
1754 named_variables: None,
1755 indexed_variables: None,
1756 memory_reference: None,
1757 declaration_location_reference: None,
1758 value_location_reference: None,
1759 },
1760 Variable {
1761 name: "i".into(),
1762 value: "4".into(),
1763 type_: Some("int".into()),
1764 presentation_hint: None,
1765 evaluate_name: None,
1766 variables_reference: 0,
1767 named_variables: None,
1768 indexed_variables: None,
1769 memory_reference: None,
1770 declaration_location_reference: None,
1771 value_location_reference: None,
1772 },
1773 Variable {
1774 name: "final_result".into(),
1775 value: "104".into(),
1776 type_: Some("int".into()),
1777 presentation_hint: None,
1778 evaluate_name: None,
1779 variables_reference: 0,
1780 named_variables: None,
1781 indexed_variables: None,
1782 memory_reference: None,
1783 declaration_location_reference: None,
1784 value_location_reference: None,
1785 },
1786 ],
1787 }),
1788 _ => Ok(dap::VariablesResponse { variables: vec![] }),
1789 });
1790
1791 client
1792 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
1793 reason: dap::StoppedEventReason::Pause,
1794 description: None,
1795 thread_id: Some(1),
1796 preserve_focus_hint: None,
1797 text: None,
1798 all_threads_stopped: None,
1799 hit_breakpoint_ids: None,
1800 }))
1801 .await;
1802
1803 cx.run_until_parked();
1804
1805 editor.update_in(cx, |editor, window, cx| {
1806 pretty_assertions::assert_eq!(
1807 r#"
1808 def process_data(untyped_param: test_value, typed_param: 42: int, another_typed: world: str):
1809 # Local variables
1810 x: 10 = 10
1811 result: 84 = typed_param: 42 * 2
1812 text: Hello, world = "Hello, " + another_typed: world
1813
1814 # For loop with range
1815 sum_value: 10 = 0
1816 for i: 4 in range(5):
1817 sum_value += i
1818
1819 # Final result
1820 final_result = x + result + sum_value
1821 return final_result
1822 "#
1823 .unindent(),
1824 editor.snapshot(window, cx).text()
1825 );
1826 });
1827}
1828
1829fn python_lang() -> Language {
1830 let debug_variables_query = include_str!("../../../grammars/src/python/debugger.scm");
1831 Language::new(
1832 LanguageConfig {
1833 name: "Python".into(),
1834 matcher: LanguageMatcher {
1835 path_suffixes: vec!["py".to_string()],
1836 ..Default::default()
1837 },
1838 ..Default::default()
1839 },
1840 Some(tree_sitter_python::LANGUAGE.into()),
1841 )
1842 .with_debug_variables_query(debug_variables_query)
1843 .unwrap()
1844}
1845
1846fn go_lang() -> Arc<Language> {
1847 let debug_variables_query = include_str!("../../../grammars/src/go/debugger.scm");
1848 Arc::new(
1849 Language::new(
1850 LanguageConfig {
1851 name: "Go".into(),
1852 matcher: LanguageMatcher {
1853 path_suffixes: vec!["go".to_string()],
1854 ..Default::default()
1855 },
1856 ..Default::default()
1857 },
1858 Some(tree_sitter_go::LANGUAGE.into()),
1859 )
1860 .with_debug_variables_query(debug_variables_query)
1861 .unwrap(),
1862 )
1863}
1864
1865/// Test utility function for inline values testing
1866///
1867/// # Arguments
1868/// * `variables` - List of tuples containing (variable_name, variable_value)
1869/// * `before` - Source code before inline values are applied
1870/// * `after` - Expected source code after inline values are applied
1871/// * `language` - Language configuration to use for parsing
1872/// * `executor` - Background executor for async operations
1873/// * `cx` - Test app context
1874async fn test_inline_values_util(
1875 local_variables: &[(&str, &str)],
1876 global_variables: &[(&str, &str)],
1877 before: &str,
1878 after: &str,
1879 active_debug_line: Option<usize>,
1880 language: Arc<Language>,
1881 executor: BackgroundExecutor,
1882 cx: &mut TestAppContext,
1883) {
1884 init_test(cx);
1885
1886 let lines_count = before.lines().count();
1887 let stop_line =
1888 active_debug_line.unwrap_or_else(|| if lines_count > 6 { 6 } else { lines_count - 1 });
1889
1890 let fs = FakeFs::new(executor.clone());
1891 fs.insert_tree(path!("/project"), json!({ "main.rs": before.to_string() }))
1892 .await;
1893
1894 let project = Project::test(fs.clone(), [path!("/project").as_ref()], cx).await;
1895 let workspace = init_test_workspace(&project, cx).await;
1896 workspace
1897 .update(cx, |workspace, window, cx| {
1898 workspace.focus_panel::<DebugPanel>(window, cx);
1899 })
1900 .unwrap();
1901 let cx = &mut VisualTestContext::from_window(*workspace, cx);
1902
1903 let session = start_debug_session(&workspace, cx, |_| {}).unwrap();
1904 let client = session.update(cx, |session, _| session.adapter_client().unwrap());
1905
1906 client.on_request::<dap::requests::Threads, _>(|_, _| {
1907 Ok(dap::ThreadsResponse {
1908 threads: vec![dap::Thread {
1909 id: 1,
1910 name: "main".into(),
1911 }],
1912 })
1913 });
1914
1915 client.on_request::<dap::requests::StackTrace, _>(move |_, _| {
1916 Ok(dap::StackTraceResponse {
1917 stack_frames: vec![dap::StackFrame {
1918 id: 1,
1919 name: "main".into(),
1920 source: Some(dap::Source {
1921 name: Some("main.rs".into()),
1922 path: Some(path!("/project/main.rs").into()),
1923 source_reference: None,
1924 presentation_hint: None,
1925 origin: None,
1926 sources: None,
1927 adapter_data: None,
1928 checksums: None,
1929 }),
1930 line: stop_line as u64,
1931 column: 1,
1932 end_line: None,
1933 end_column: None,
1934 can_restart: None,
1935 instruction_pointer_reference: None,
1936 module_id: None,
1937 presentation_hint: None,
1938 }],
1939 total_frames: None,
1940 })
1941 });
1942
1943 let local_vars: Vec<Variable> = local_variables
1944 .iter()
1945 .map(|(name, value)| Variable {
1946 name: (*name).into(),
1947 value: (*value).into(),
1948 type_: None,
1949 presentation_hint: None,
1950 evaluate_name: None,
1951 variables_reference: 0,
1952 named_variables: None,
1953 indexed_variables: None,
1954 memory_reference: None,
1955 declaration_location_reference: None,
1956 value_location_reference: None,
1957 })
1958 .collect();
1959
1960 let global_vars: Vec<Variable> = global_variables
1961 .iter()
1962 .map(|(name, value)| Variable {
1963 name: (*name).into(),
1964 value: (*value).into(),
1965 type_: None,
1966 presentation_hint: None,
1967 evaluate_name: None,
1968 variables_reference: 0,
1969 named_variables: None,
1970 indexed_variables: None,
1971 memory_reference: None,
1972 declaration_location_reference: None,
1973 value_location_reference: None,
1974 })
1975 .collect();
1976
1977 client.on_request::<Variables, _>({
1978 let local_vars = Arc::new(local_vars.clone());
1979 let global_vars = Arc::new(global_vars.clone());
1980 move |_, args| {
1981 let variables = match args.variables_reference {
1982 2 => (*local_vars).clone(),
1983 3 => (*global_vars).clone(),
1984 _ => vec![],
1985 };
1986 Ok(dap::VariablesResponse { variables })
1987 }
1988 });
1989
1990 client.on_request::<dap::requests::Scopes, _>(move |_, _| {
1991 Ok(dap::ScopesResponse {
1992 scopes: vec![
1993 Scope {
1994 name: "Local".into(),
1995 presentation_hint: None,
1996 variables_reference: 2,
1997 named_variables: None,
1998 indexed_variables: None,
1999 expensive: false,
2000 source: None,
2001 line: None,
2002 column: None,
2003 end_line: None,
2004 end_column: None,
2005 },
2006 Scope {
2007 name: "Global".into(),
2008 presentation_hint: None,
2009 variables_reference: 3,
2010 named_variables: None,
2011 indexed_variables: None,
2012 expensive: false,
2013 source: None,
2014 line: None,
2015 column: None,
2016 end_line: None,
2017 end_column: None,
2018 },
2019 ],
2020 })
2021 });
2022
2023 if !global_variables.is_empty() {
2024 let global_evaluate_map: std::collections::HashMap<String, String> = global_variables
2025 .iter()
2026 .map(|(name, value)| (name.to_string(), value.to_string()))
2027 .collect();
2028
2029 client.on_request::<dap::requests::Evaluate, _>(move |_, args| {
2030 let value = global_evaluate_map
2031 .get(&args.expression)
2032 .unwrap_or(&"undefined".to_string())
2033 .clone();
2034
2035 Ok(dap::EvaluateResponse {
2036 result: value,
2037 type_: None,
2038 presentation_hint: None,
2039 variables_reference: 0,
2040 named_variables: None,
2041 indexed_variables: None,
2042 memory_reference: None,
2043 value_location_reference: None,
2044 })
2045 });
2046 }
2047
2048 client
2049 .fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
2050 reason: dap::StoppedEventReason::Pause,
2051 description: None,
2052 thread_id: Some(1),
2053 preserve_focus_hint: None,
2054 text: None,
2055 all_threads_stopped: None,
2056 hit_breakpoint_ids: None,
2057 }))
2058 .await;
2059
2060 cx.run_until_parked();
2061
2062 let project_path = Path::new(path!("/project"));
2063 let worktree = project
2064 .update(cx, |project, cx| project.find_worktree(project_path, cx))
2065 .expect("This worktree should exist in project")
2066 .0;
2067
2068 let worktree_id = workspace
2069 .update(cx, |_, _, cx| worktree.read(cx).id())
2070 .unwrap();
2071
2072 let buffer = project
2073 .update(cx, |project, cx| {
2074 project.open_buffer((worktree_id, rel_path("main.rs")), cx)
2075 })
2076 .await
2077 .unwrap();
2078
2079 buffer.update(cx, |buffer, cx| {
2080 buffer.set_language(Some(language), cx);
2081 });
2082
2083 let (editor, cx) = cx.add_window_view(|window, cx| {
2084 Editor::new(
2085 EditorMode::full(),
2086 MultiBuffer::build_from_buffer(buffer, cx),
2087 Some(project),
2088 window,
2089 cx,
2090 )
2091 });
2092
2093 active_debug_session_panel(workspace, cx).update_in(cx, |_, window, cx| {
2094 cx.focus_self(window);
2095 });
2096 cx.run_until_parked();
2097
2098 editor.update(cx, |editor, cx| editor.refresh_inline_values(cx));
2099
2100 cx.run_until_parked();
2101
2102 editor.update_in(cx, |editor, window, cx| {
2103 pretty_assertions::assert_eq!(after, editor.snapshot(window, cx).text());
2104 });
2105}
2106
2107#[gpui::test]
2108async fn test_inline_values_example(executor: BackgroundExecutor, cx: &mut TestAppContext) {
2109 let variables = [("x", "10"), ("y", "20"), ("result", "30")];
2110
2111 let before = r#"
2112fn main() {
2113 let x = 10;
2114 let y = 20;
2115 let result = x + y;
2116 println!("Result: {}", result);
2117}
2118"#
2119 .unindent();
2120
2121 let after = r#"
2122fn main() {
2123 let x: 10 = 10;
2124 let y: 20 = 20;
2125 let result: 30 = x: 10 + y: 20;
2126 println!("Result: {}", result: 30);
2127}
2128"#
2129 .unindent();
2130
2131 test_inline_values_util(
2132 &variables,
2133 &[],
2134 &before,
2135 &after,
2136 None,
2137 rust_lang(),
2138 executor,
2139 cx,
2140 )
2141 .await;
2142}
2143
2144#[gpui::test]
2145async fn test_inline_values_with_globals(executor: BackgroundExecutor, cx: &mut TestAppContext) {
2146 let variables = [("x", "5"), ("y", "10")];
2147
2148 let before = r#"
2149static mut GLOBAL_COUNTER: usize = 42;
2150
2151fn main() {
2152 let x = 5;
2153 let y = 10;
2154 unsafe {
2155 GLOBAL_COUNTER += 1;
2156 }
2157 println!("x={}, y={}, global={}", x, y, unsafe { GLOBAL_COUNTER });
2158}
2159"#
2160 .unindent();
2161
2162 let after = r#"
2163static mut GLOBAL_COUNTER: 42: usize = 42;
2164
2165fn main() {
2166 let x: 5 = 5;
2167 let y: 10 = 10;
2168 unsafe {
2169 GLOBAL_COUNTER += 1;
2170 }
2171 println!("x={}, y={}, global={}", x, y, unsafe { GLOBAL_COUNTER });
2172}
2173"#
2174 .unindent();
2175
2176 test_inline_values_util(
2177 &variables,
2178 &[("GLOBAL_COUNTER", "42")],
2179 &before,
2180 &after,
2181 None,
2182 rust_lang(),
2183 executor,
2184 cx,
2185 )
2186 .await;
2187}
2188
2189#[gpui::test]
2190async fn test_go_inline_values(executor: BackgroundExecutor, cx: &mut TestAppContext) {
2191 let variables = [("x", "42"), ("y", "hello")];
2192
2193 let before = r#"
2194package main
2195
2196var globalCounter int = 100
2197
2198func main() {
2199 x := 42
2200 y := "hello"
2201 z := x + 10
2202 println(x, y, z)
2203}
2204"#
2205 .unindent();
2206
2207 let after = r#"
2208package main
2209
2210var globalCounter: 100 int = 100
2211
2212func main() {
2213 x: 42 := 42
2214 y := "hello"
2215 z := x + 10
2216 println(x, y, z)
2217}
2218"#
2219 .unindent();
2220
2221 test_inline_values_util(
2222 &variables,
2223 &[("globalCounter", "100")],
2224 &before,
2225 &after,
2226 None,
2227 go_lang(),
2228 executor,
2229 cx,
2230 )
2231 .await;
2232}
2233
2234#[gpui::test]
2235async fn test_trim_multi_line_inline_value(executor: BackgroundExecutor, cx: &mut TestAppContext) {
2236 let variables = [("y", "hello\n world")];
2237
2238 let before = r#"
2239fn main() {
2240 let y = "hello\n world";
2241}
2242"#
2243 .unindent();
2244
2245 let after = r#"
2246fn main() {
2247 let y: hello… = "hello\n world";
2248}
2249"#
2250 .unindent();
2251
2252 test_inline_values_util(
2253 &variables,
2254 &[],
2255 &before,
2256 &after,
2257 None,
2258 rust_lang(),
2259 executor,
2260 cx,
2261 )
2262 .await;
2263}
2264
2265fn javascript_lang() -> Arc<Language> {
2266 let debug_variables_query = include_str!("../../../grammars/src/javascript/debugger.scm");
2267 Arc::new(
2268 Language::new(
2269 LanguageConfig {
2270 name: "JavaScript".into(),
2271 matcher: LanguageMatcher {
2272 path_suffixes: vec!["js".to_string()],
2273 ..Default::default()
2274 },
2275 ..Default::default()
2276 },
2277 Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
2278 )
2279 .with_debug_variables_query(debug_variables_query)
2280 .unwrap(),
2281 )
2282}
2283
2284fn typescript_lang() -> Arc<Language> {
2285 let debug_variables_query = include_str!("../../../grammars/src/typescript/debugger.scm");
2286 Arc::new(
2287 Language::new(
2288 LanguageConfig {
2289 name: "TypeScript".into(),
2290 matcher: LanguageMatcher {
2291 path_suffixes: vec!["ts".to_string()],
2292 ..Default::default()
2293 },
2294 ..Default::default()
2295 },
2296 Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
2297 )
2298 .with_debug_variables_query(debug_variables_query)
2299 .unwrap(),
2300 )
2301}
2302
2303fn tsx_lang() -> Arc<Language> {
2304 let debug_variables_query = include_str!("../../../grammars/src/tsx/debugger.scm");
2305 Arc::new(
2306 Language::new(
2307 LanguageConfig {
2308 name: "TSX".into(),
2309 matcher: LanguageMatcher {
2310 path_suffixes: vec!["tsx".to_string()],
2311 ..Default::default()
2312 },
2313 ..Default::default()
2314 },
2315 Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
2316 )
2317 .with_debug_variables_query(debug_variables_query)
2318 .unwrap(),
2319 )
2320}
2321
2322#[gpui::test]
2323async fn test_javascript_inline_values(executor: BackgroundExecutor, cx: &mut TestAppContext) {
2324 let variables = [
2325 ("x", "10"),
2326 ("y", "20"),
2327 ("sum", "30"),
2328 ("message", "Hello"),
2329 ];
2330
2331 let before = r#"
2332function calculate() {
2333 const x = 10;
2334 const y = 20;
2335 const sum = x + y;
2336 const message = "Hello";
2337 console.log(message, "Sum:", sum);
2338}
2339"#
2340 .unindent();
2341
2342 let after = r#"
2343function calculate() {
2344 const x: 10 = 10;
2345 const y: 20 = 20;
2346 const sum: 30 = x: 10 + y: 20;
2347 const message: Hello = "Hello";
2348 console.log(message, "Sum:", sum);
2349}
2350"#
2351 .unindent();
2352
2353 test_inline_values_util(
2354 &variables,
2355 &[],
2356 &before,
2357 &after,
2358 None,
2359 javascript_lang(),
2360 executor,
2361 cx,
2362 )
2363 .await;
2364}
2365
2366#[gpui::test]
2367async fn test_typescript_inline_values(executor: BackgroundExecutor, cx: &mut TestAppContext) {
2368 let variables = [
2369 ("count", "42"),
2370 ("name", "Alice"),
2371 ("result", "84"),
2372 ("i", "3"),
2373 ];
2374
2375 let before = r#"
2376function processData(count: number, name: string): number {
2377 let result = count * 2;
2378 for (let i = 0; i < 5; i++) {
2379 console.log(i);
2380 }
2381 return result;
2382}
2383"#
2384 .unindent();
2385
2386 let after = r#"
2387function processData(count: number, name: string): number {
2388 let result: 84 = count: 42 * 2;
2389 for (let i: 3 = 0; i: 3 < 5; i: 3++) {
2390 console.log(i);
2391 }
2392 return result: 84;
2393}
2394"#
2395 .unindent();
2396
2397 test_inline_values_util(
2398 &variables,
2399 &[],
2400 &before,
2401 &after,
2402 None,
2403 typescript_lang(),
2404 executor,
2405 cx,
2406 )
2407 .await;
2408}
2409
2410#[gpui::test]
2411async fn test_tsx_inline_values(executor: BackgroundExecutor, cx: &mut TestAppContext) {
2412 let variables = [("count", "5"), ("message", "Hello React")];
2413
2414 let before = r#"
2415const Counter = () => {
2416 const count = 5;
2417 const message = "Hello React";
2418 return (
2419 <div>
2420 <p>{message}</p>
2421 <span>{count}</span>
2422 </div>
2423 );
2424};
2425"#
2426 .unindent();
2427
2428 let after = r#"
2429const Counter = () => {
2430 const count: 5 = 5;
2431 const message: Hello React = "Hello React";
2432 return (
2433 <div>
2434 <p>{message: Hello React}</p>
2435 <span>{count}</span>
2436 </div>
2437 );
2438};
2439"#
2440 .unindent();
2441
2442 test_inline_values_util(
2443 &variables,
2444 &[],
2445 &before,
2446 &after,
2447 None,
2448 tsx_lang(),
2449 executor,
2450 cx,
2451 )
2452 .await;
2453}
2454
2455#[gpui::test]
2456async fn test_javascript_arrow_functions(executor: BackgroundExecutor, cx: &mut TestAppContext) {
2457 let variables = [("x", "42"), ("result", "84")];
2458
2459 let before = r#"
2460const double = (x) => {
2461 const result = x * 2;
2462 return result;
2463};
2464"#
2465 .unindent();
2466
2467 let after = r#"
2468const double = (x) => {
2469 const result: 84 = x: 42 * 2;
2470 return result: 84;
2471};
2472"#
2473 .unindent();
2474
2475 test_inline_values_util(
2476 &variables,
2477 &[],
2478 &before,
2479 &after,
2480 None,
2481 javascript_lang(),
2482 executor,
2483 cx,
2484 )
2485 .await;
2486}
2487
2488#[gpui::test]
2489async fn test_typescript_for_in_loop(executor: BackgroundExecutor, cx: &mut TestAppContext) {
2490 let variables = [("key", "name"), ("obj", "{name: 'test'}")];
2491
2492 let before = r#"
2493function iterate() {
2494 const obj = {name: 'test'};
2495 for (const key in obj) {
2496 console.log(key);
2497 }
2498}
2499"#
2500 .unindent();
2501
2502 let after = r#"
2503function iterate() {
2504 const obj: {name: 'test'} = {name: 'test'};
2505 for (const key: name in obj) {
2506 console.log(key);
2507 }
2508}
2509"#
2510 .unindent();
2511
2512 test_inline_values_util(
2513 &variables,
2514 &[],
2515 &before,
2516 &after,
2517 None,
2518 typescript_lang(),
2519 executor,
2520 cx,
2521 )
2522 .await;
2523}