Simplify debug launcher UI (#31928)
This PR updates the name of the `NewSessionModal` to `NewProcessModal` (to reflect it's new purpose), changes the tabs in the modal to read `Run | Debug | Attach | Launch` and changes the associated types in code to match the tabs. In addition, this PR adds a few labels to the text fields in the `Launch` tab, and adds a link to open the associated settings file. In both debug.json files, added links to the zed.dev debugger docs. Release Notes: - Debugger Beta: Improve the new process modal
This commit is contained in:
parent
f1aab1120d
commit
b7ec437b13
12 changed files with 497 additions and 490 deletions
349
crates/debugger_ui/src/tests/new_process_modal.rs
Normal file
349
crates/debugger_ui/src/tests/new_process_modal.rs
Normal file
|
@ -0,0 +1,349 @@
|
|||
use dap::DapRegistry;
|
||||
use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
|
||||
use project::{FakeFs, Project};
|
||||
use serde_json::json;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use task::{DebugRequest, DebugScenario, LaunchRequest, TaskContext, VariableName, ZedDebugConfig};
|
||||
use util::path;
|
||||
|
||||
// use crate::new_process_modal::NewProcessMode;
|
||||
use crate::tests::{init_test, init_test_workspace};
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_debug_session_substitutes_variables_and_relativizes_paths(
|
||||
executor: BackgroundExecutor,
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(executor.clone());
|
||||
fs.insert_tree(
|
||||
path!("/project"),
|
||||
json!({
|
||||
"main.rs": "fn main() {}"
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
|
||||
let workspace = init_test_workspace(&project, cx).await;
|
||||
let cx = &mut VisualTestContext::from_window(*workspace, cx);
|
||||
|
||||
let test_variables = vec![(
|
||||
VariableName::WorktreeRoot,
|
||||
path!("/test/worktree/path").to_string(),
|
||||
)]
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let task_context = TaskContext {
|
||||
cwd: None,
|
||||
task_variables: test_variables,
|
||||
project_env: Default::default(),
|
||||
};
|
||||
|
||||
let home_dir = paths::home_dir();
|
||||
|
||||
let test_cases: Vec<(&'static str, &'static str)> = vec![
|
||||
// Absolute path - should not be relativized
|
||||
(
|
||||
path!("/absolute/path/to/program"),
|
||||
path!("/absolute/path/to/program"),
|
||||
),
|
||||
// Relative path - should be prefixed with worktree root
|
||||
(
|
||||
format!(".{0}src{0}program", std::path::MAIN_SEPARATOR).leak(),
|
||||
path!("/test/worktree/path/src/program"),
|
||||
),
|
||||
// Home directory path - should be expanded to full home directory path
|
||||
(
|
||||
format!("~{0}src{0}program", std::path::MAIN_SEPARATOR).leak(),
|
||||
home_dir
|
||||
.join("src")
|
||||
.join("program")
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
.leak(),
|
||||
),
|
||||
// Path with $ZED_WORKTREE_ROOT - should be substituted without double appending
|
||||
(
|
||||
format!(
|
||||
"$ZED_WORKTREE_ROOT{0}src{0}program",
|
||||
std::path::MAIN_SEPARATOR
|
||||
)
|
||||
.leak(),
|
||||
path!("/test/worktree/path/src/program"),
|
||||
),
|
||||
];
|
||||
|
||||
let called_launch = Arc::new(AtomicBool::new(false));
|
||||
|
||||
for (input_path, expected_path) in test_cases {
|
||||
let _subscription = project::debugger::test::intercept_debug_sessions(cx, {
|
||||
let called_launch = called_launch.clone();
|
||||
move |client| {
|
||||
client.on_request::<dap::requests::Launch, _>({
|
||||
let called_launch = called_launch.clone();
|
||||
|
||||
move |_, args| {
|
||||
let config = args.raw.as_object().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
config["program"].as_str().unwrap(),
|
||||
expected_path,
|
||||
"Program path was not correctly substituted for input: {}",
|
||||
input_path
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
config["cwd"].as_str().unwrap(),
|
||||
expected_path,
|
||||
"CWD path was not correctly substituted for input: {}",
|
||||
input_path
|
||||
);
|
||||
|
||||
let expected_other_field = if input_path.contains("$ZED_WORKTREE_ROOT") {
|
||||
input_path
|
||||
.replace("$ZED_WORKTREE_ROOT", &path!("/test/worktree/path"))
|
||||
.to_owned()
|
||||
} else {
|
||||
input_path.to_string()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
config["otherField"].as_str().unwrap(),
|
||||
&expected_other_field,
|
||||
"Other field was incorrectly modified for input: {}",
|
||||
input_path
|
||||
);
|
||||
|
||||
called_launch.store(true, Ordering::SeqCst);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let scenario = DebugScenario {
|
||||
adapter: "fake-adapter".into(),
|
||||
label: "test-debug-session".into(),
|
||||
build: None,
|
||||
config: json!({
|
||||
"request": "launch",
|
||||
"program": input_path,
|
||||
"cwd": input_path,
|
||||
"otherField": input_path
|
||||
}),
|
||||
tcp_connection: None,
|
||||
};
|
||||
|
||||
workspace
|
||||
.update(cx, |workspace, window, cx| {
|
||||
workspace.start_debug_session(scenario, task_context.clone(), None, window, cx)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
cx.run_until_parked();
|
||||
|
||||
assert!(called_launch.load(Ordering::SeqCst));
|
||||
called_launch.store(false, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
// #[gpui::test]
|
||||
// async fn test_save_debug_scenario_to_file(executor: BackgroundExecutor, cx: &mut TestAppContext) {
|
||||
// init_test(cx);
|
||||
|
||||
// let fs = FakeFs::new(executor.clone());
|
||||
// fs.insert_tree(
|
||||
// path!("/project"),
|
||||
// json!({
|
||||
// "main.rs": "fn main() {}"
|
||||
// }),
|
||||
// )
|
||||
// .await;
|
||||
|
||||
// let project = Project::test(fs.clone(), [path!("/project").as_ref()], cx).await;
|
||||
// let workspace = init_test_workspace(&project, cx).await;
|
||||
// let cx = &mut VisualTestContext::from_window(*workspace, cx);
|
||||
|
||||
// workspace
|
||||
// .update(cx, |workspace, window, cx| {
|
||||
// crate::new_process_modal::NewProcessModal::show(
|
||||
// workspace,
|
||||
// window,
|
||||
// NewProcessMode::Debug,
|
||||
// None,
|
||||
// cx,
|
||||
// );
|
||||
// })
|
||||
// .unwrap();
|
||||
|
||||
// cx.run_until_parked();
|
||||
|
||||
// let modal = workspace
|
||||
// .update(cx, |workspace, _, cx| {
|
||||
// workspace.active_modal::<crate::new_process_modal::NewProcessModal>(cx)
|
||||
// })
|
||||
// .unwrap()
|
||||
// .expect("Modal should be active");
|
||||
|
||||
// modal.update_in(cx, |modal, window, cx| {
|
||||
// modal.set_configure("/project/main", "/project", false, window, cx);
|
||||
// modal.save_scenario(window, cx);
|
||||
// });
|
||||
|
||||
// cx.executor().run_until_parked();
|
||||
|
||||
// let debug_json_content = fs
|
||||
// .load(path!("/project/.zed/debug.json").as_ref())
|
||||
// .await
|
||||
// .expect("debug.json should exist");
|
||||
|
||||
// let expected_content = vec![
|
||||
// "[",
|
||||
// " {",
|
||||
// r#" "adapter": "fake-adapter","#,
|
||||
// r#" "label": "main (fake-adapter)","#,
|
||||
// r#" "request": "launch","#,
|
||||
// r#" "program": "/project/main","#,
|
||||
// r#" "cwd": "/project","#,
|
||||
// r#" "args": [],"#,
|
||||
// r#" "env": {}"#,
|
||||
// " }",
|
||||
// "]",
|
||||
// ];
|
||||
|
||||
// let actual_lines: Vec<&str> = debug_json_content.lines().collect();
|
||||
// pretty_assertions::assert_eq!(expected_content, actual_lines);
|
||||
|
||||
// modal.update_in(cx, |modal, window, cx| {
|
||||
// modal.set_configure("/project/other", "/project", true, window, cx);
|
||||
// modal.save_scenario(window, cx);
|
||||
// });
|
||||
|
||||
// cx.executor().run_until_parked();
|
||||
|
||||
// let debug_json_content = fs
|
||||
// .load(path!("/project/.zed/debug.json").as_ref())
|
||||
// .await
|
||||
// .expect("debug.json should exist after second save");
|
||||
|
||||
// let expected_content = vec![
|
||||
// "[",
|
||||
// " {",
|
||||
// r#" "adapter": "fake-adapter","#,
|
||||
// r#" "label": "main (fake-adapter)","#,
|
||||
// r#" "request": "launch","#,
|
||||
// r#" "program": "/project/main","#,
|
||||
// r#" "cwd": "/project","#,
|
||||
// r#" "args": [],"#,
|
||||
// r#" "env": {}"#,
|
||||
// " },",
|
||||
// " {",
|
||||
// r#" "adapter": "fake-adapter","#,
|
||||
// r#" "label": "other (fake-adapter)","#,
|
||||
// r#" "request": "launch","#,
|
||||
// r#" "program": "/project/other","#,
|
||||
// r#" "cwd": "/project","#,
|
||||
// r#" "args": [],"#,
|
||||
// r#" "env": {}"#,
|
||||
// " }",
|
||||
// "]",
|
||||
// ];
|
||||
|
||||
// let actual_lines: Vec<&str> = debug_json_content.lines().collect();
|
||||
// pretty_assertions::assert_eq!(expected_content, actual_lines);
|
||||
// }
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_dap_adapter_config_conversion_and_validation(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut expected_adapters = vec![
|
||||
"CodeLLDB",
|
||||
"Debugpy",
|
||||
"PHP",
|
||||
"JavaScript",
|
||||
"Ruby",
|
||||
"Delve",
|
||||
"GDB",
|
||||
"fake-adapter",
|
||||
];
|
||||
|
||||
let adapter_names = cx.update(|cx| {
|
||||
let registry = DapRegistry::global(cx);
|
||||
registry.enumerate_adapters()
|
||||
});
|
||||
|
||||
let zed_config = ZedDebugConfig {
|
||||
label: "test_debug_session".into(),
|
||||
adapter: "test_adapter".into(),
|
||||
request: DebugRequest::Launch(LaunchRequest {
|
||||
program: "test_program".into(),
|
||||
cwd: None,
|
||||
args: vec![],
|
||||
env: Default::default(),
|
||||
}),
|
||||
stop_on_entry: Some(true),
|
||||
};
|
||||
|
||||
for adapter_name in adapter_names {
|
||||
let adapter_str = adapter_name.to_string();
|
||||
if let Some(pos) = expected_adapters.iter().position(|&x| x == adapter_str) {
|
||||
expected_adapters.remove(pos);
|
||||
}
|
||||
|
||||
let adapter = cx
|
||||
.update(|cx| {
|
||||
let registry = DapRegistry::global(cx);
|
||||
registry.adapter(adapter_name.as_ref())
|
||||
})
|
||||
.unwrap_or_else(|| panic!("Adapter {} should exist", adapter_name));
|
||||
|
||||
let mut adapter_specific_config = zed_config.clone();
|
||||
adapter_specific_config.adapter = adapter_name.to_string().into();
|
||||
|
||||
let debug_scenario = adapter
|
||||
.config_from_zed_format(adapter_specific_config)
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"Adapter {} should successfully convert from Zed format",
|
||||
adapter_name
|
||||
)
|
||||
});
|
||||
|
||||
assert!(
|
||||
debug_scenario.config.is_object(),
|
||||
"Adapter {} should produce a JSON object for config",
|
||||
adapter_name
|
||||
);
|
||||
|
||||
let request_type = adapter
|
||||
.request_kind(&debug_scenario.config)
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"Adapter {} should validate the config successfully",
|
||||
adapter_name
|
||||
)
|
||||
});
|
||||
|
||||
match request_type {
|
||||
dap::StartDebuggingRequestArgumentsRequest::Launch => {}
|
||||
dap::StartDebuggingRequestArgumentsRequest::Attach => {
|
||||
panic!(
|
||||
"Expected Launch request but got Attach for adapter {}",
|
||||
adapter_name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert!(
|
||||
expected_adapters.is_empty(),
|
||||
"The following expected adapters were not found in the registry: {:?}",
|
||||
expected_adapters
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue