Support relative paths in LSP & DAP binaries (#42135)

Closes #41214

Release Notes:

- Added support for relative paths in LSP and DAP binaries

---------

Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: Julia Ryan <juliaryan3.14@gmail.com>
This commit is contained in:
Andrew Farkas
2025-11-10 14:33:00 -05:00
committed by GitHub
parent 2b369d7532
commit 3fbfea491d
4 changed files with 77 additions and 6 deletions

View File

@@ -261,7 +261,10 @@ impl DapStore {
.get(&adapter.name());
let user_installed_path = dap_settings.and_then(|s| match &s.binary {
DapBinary::Default => None,
DapBinary::Custom(binary) => Some(PathBuf::from(binary)),
DapBinary::Custom(binary) => {
// if `binary` is absolute, `.join()` will keep it unmodified
Some(worktree.read(cx).abs_path().join(PathBuf::from(binary)))
}
});
let user_args = dap_settings.map(|s| s.args.clone());
let user_env = dap_settings.map(|s| s.env.clone());

View File

@@ -563,8 +563,8 @@ impl LocalLspStore {
allow_binary_download: bool,
cx: &mut App,
) -> Task<Result<LanguageServerBinary>> {
if let Some(settings) = settings.binary.as_ref()
&& settings.path.is_some()
if let Some(settings) = &settings.binary
&& let Some(path) = settings.path.as_ref().map(PathBuf::from)
{
let settings = settings.clone();
@@ -573,7 +573,8 @@ impl LocalLspStore {
env.extend(settings.env.unwrap_or_default());
Ok(LanguageServerBinary {
path: PathBuf::from(&settings.path.unwrap()),
// if `path` is absolute, `.join()` will keep it unmodified
path: delegate.worktree_root_path().join(path),
env: Some(env),
arguments: settings
.arguments

View File

@@ -1208,6 +1208,73 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
);
}
#[gpui::test]
async fn test_language_server_relative_path(cx: &mut gpui::TestAppContext) {
init_test(cx);
let settings_json_contents = json!({
"languages": {
"Rust": {
"language_servers": ["my_fake_lsp"]
}
},
"lsp": {
"my_fake_lsp": {
"binary": {
"path": path!("relative_path/to/my_fake_lsp_binary.exe").to_string(),
}
}
},
});
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
path!("/the-root"),
json!({
".zed": {
"settings.json": settings_json_contents.to_string(),
},
"relative_path": {
"to": {
"my_fake_lsp.exe": "",
},
},
"src": {
"main.rs": "",
}
}),
)
.await;
let project = Project::test(fs.clone(), [path!("/the-root").as_ref()], cx).await;
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
language_registry.add(rust_lang());
let mut fake_rust_servers = language_registry.register_fake_lsp(
"Rust",
FakeLspAdapter {
name: "my_fake_lsp",
..Default::default()
},
);
cx.run_until_parked();
// Start the language server by opening a buffer with a compatible file extension.
project
.update(cx, |project, cx| {
project.open_local_buffer_with_lsp(path!("/the-root/src/main.rs"), cx)
})
.await
.unwrap();
let lsp_path = fake_rust_servers.next().await.unwrap().binary.path;
assert_eq!(
lsp_path.to_string_lossy(),
path!("/the-root/relative_path/to/my_fake_lsp_binary.exe"),
);
}
#[gpui::test]
async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppContext) {
init_test(cx);

View File

@@ -2352,8 +2352,8 @@ impl Snapshot {
self.entries_by_path.first()
}
/// TODO: what's the difference between `root_dir` and `abs_path`?
/// is there any? if so, document it.
/// Returns `None` for a single file worktree, or `Some(self.abs_path())` if
/// it is a directory.
pub fn root_dir(&self) -> Option<Arc<Path>> {
self.root_entry()
.filter(|entry| entry.is_dir())