Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions extension.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,9 @@ language = "CSharp"
[grammars.c_sharp]
repository = "https://github.com/tree-sitter/tree-sitter-c-sharp"
commit = "dd5e59721a5f8dae34604060833902b882023aaf"

# Used to run `csharp-language-server --download` after an update
[[capabilities]]
kind = "process:exec"
command = "*"
args = ["--download"]
1 change: 1 addition & 0 deletions src/language_servers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod omnisharp;
pub mod roslyn;
pub mod util;

pub use omnisharp::*;
pub use roslyn::*;
13 changes: 4 additions & 9 deletions src/language_servers/omnisharp.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::fs;
use zed_extension_api::{self as zed, settings::LspSettings, LanguageServerId, Result};

use crate::language_servers::util;

pub struct Omnisharp {
cached_binary_path: Option<String>,
}
Expand Down Expand Up @@ -91,7 +93,7 @@ impl Omnisharp {
.find(|asset| asset.name == asset_name)
.ok_or_else(|| format!("no asset found matching {:?}", asset_name))?;

let version_dir = format!("omnisharp-{}", release.version);
let version_dir = format!("{}-{}", Self::LANGUAGE_SERVER_ID, release.version);
let binary_path = match platform {
zed::Os::Windows => format!("{version_dir}/OmniSharp.exe"),
_ => format!("{version_dir}/OmniSharp"),
Expand All @@ -113,14 +115,7 @@ impl Omnisharp {
)
.map_err(|e| format!("failed to download file: {e}"))?;

let entries =
fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
for entry in entries {
let entry = entry.map_err(|e| format!("failed to load directory entry {e}"))?;
if entry.file_name().to_str() != Some(&version_dir) {
fs::remove_dir_all(entry.path()).ok();
}
}
util::remove_outdated_versions(Self::LANGUAGE_SERVER_ID, &version_dir)?;
}

self.cached_binary_path = Some(binary_path.clone());
Expand Down
42 changes: 26 additions & 16 deletions src/language_servers/roslyn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use std::fs;

use zed_extension_api::{self as zed, settings::LspSettings, LanguageServerId, Result};

use crate::language_servers::util;

const REPO: &str = "SofusA/csharp-language-server";

pub struct Roslyn {
Expand Down Expand Up @@ -87,8 +89,11 @@ impl Roslyn {
.find(|asset| asset.name == asset_name)
.ok_or_else(|| format!("no asset found matching {:?}", asset_name))?;

let version_dir = format!("roslyn-{}", release.version);
let binary_path = format!("{version_dir}/csharp-language-server");
let version_dir = format!("{}-{}", Self::LANGUAGE_SERVER_ID, release.version);
let binary_path = match platform {
zed::Os::Windows => format!("{version_dir}/csharp-language-server.exe"),
_ => format!("{version_dir}/csharp-language-server"),
};

if !fs::metadata(&binary_path).is_ok_and(|stat| stat.is_file()) {
zed::set_language_server_installation_status(
Expand All @@ -108,13 +113,17 @@ impl Roslyn {

zed::make_file_executable(&binary_path)?;

let entries =
fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
for entry in entries {
let entry = entry.map_err(|e| format!("failed to load directory entry {e}"))?;
if entry.file_name().to_str() != Some(&version_dir) {
fs::remove_dir_all(entry.path()).ok();
}
util::remove_outdated_versions(Self::LANGUAGE_SERVER_ID, &version_dir)?;

if let Ok(full_binary_path) = std::env::current_dir().map(|dir| dir.join(&binary_path))
{
// The `csharp-language-server` wrapper automatically downloads Roslyn on first launch,
// but we trigger `--download` here while the "Downloading roslyn" status is still visible.
// If this fails (e.g., if the exec capability is denied), ignore the error--Roslyn will
// be downloaded when the language server starts anyway.
_ = zed::Command::new(full_binary_path.to_string_lossy().as_ref())
.arg("--download")
.output();
}
}

Expand All @@ -138,20 +147,21 @@ impl Roslyn {

fn transform_settings_for_roslyn(settings: zed::serde_json::Value) -> zed::serde_json::Value {
let mut roslyn_config = zed::serde_json::json!({
// These code lenses rely show up as "Unknown Command" in Zed and don't do anything when clicked. Disable them by default.
// These code lenses show up as "Unknown Command" in Zed and don't do anything when clicked. Disable them by default.
"csharp|code_lens.dotnet_enable_references_code_lens": false,
"csharp|code_lens.dotnet_enable_tests_code_lens": false,
// Enable inlay hints in the language server by default.
// This way, enabling inlay hints in Zed will cause inlay hints to show up in C# without extra configuration.
"csharp|inlay_hints.csharp_enable_inlay_hints_for_implicit_object_creation": true,
"csharp|inlay_hints.csharp_enable_inlay_hints_for_implicit_variable_types": true,
"csharp|inlay_hints.csharp_enable_inlay_hints_for_lambda_parameter_types": true,
"csharp|inlay_hints.csharp_enable_inlay_hints_for_types": true,
"csharp|inlay_hints.dotnet_enable_inlay_hints_for_indexer_parameters": true,
"csharp|inlay_hints.dotnet_enable_inlay_hints_for_parameters": true,
"csharp|inlay_hints.dotnet_enable_inlay_hints_for_literal_parameters": true,
"csharp|inlay_hints.dotnet_enable_inlay_hints_for_indexer_parameters": true,
"csharp|inlay_hints.dotnet_enable_inlay_hints_for_object_creation_parameters": true,
"csharp|inlay_hints.dotnet_enable_inlay_hints_for_other_parameters": true,
"csharp|inlay_hints.dotnet_enable_inlay_hints_for_parameters": true
"csharp|inlay_hints.csharp_enable_inlay_hints_for_types": true,
"csharp|inlay_hints.csharp_enable_inlay_hints_for_implicit_variable_types": true,
"csharp|inlay_hints.csharp_enable_inlay_hints_for_lambda_parameter_types": true,
"csharp|inlay_hints.csharp_enable_inlay_hints_for_implicit_object_creation": true,
"csharp|inlay_hints.csharp_enable_inlay_hints_for_collection_expressions": true,
});

let config_map = roslyn_config.as_object_mut().unwrap();
Expand Down
19 changes: 19 additions & 0 deletions src/language_servers/util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use std::fs;

use zed_extension_api::Result;

pub(super) fn remove_outdated_versions(
language_server_id: &'static str,
version_dir: &str,
) -> Result<()> {
let entries = fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
for entry in entries {
let entry = entry.map_err(|e| format!("failed to load directory entry {e}"))?;
if entry.file_name().to_str().is_none_or(|file_name| {
file_name.starts_with(language_server_id) && file_name != version_dir
}) {
fs::remove_dir_all(entry.path()).ok();
}
}
Ok(())
}