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