1use std::fs;
2use zed_extension_api::{self as zed, settings::LspSettings, LanguageServerId, Result};
3
4struct OmnisharpBinary {
5 path: String,
6 args: Option<Vec<String>>,
7}
8
9struct CsharpExtension {
10 cached_binary_path: Option<String>,
11}
12
13impl CsharpExtension {
14 fn language_server_binary(
15 &mut self,
16 language_server_id: &LanguageServerId,
17 worktree: &zed::Worktree,
18 ) -> Result<OmnisharpBinary> {
19 let binary_settings = LspSettings::for_worktree("omnisharp", worktree)
20 .ok()
21 .and_then(|lsp_settings| lsp_settings.binary);
22 let binary_args = binary_settings
23 .as_ref()
24 .and_then(|binary_settings| binary_settings.arguments.clone());
25
26 if let Some(path) = binary_settings.and_then(|binary_settings| binary_settings.path) {
27 return Ok(OmnisharpBinary {
28 path,
29 args: binary_args,
30 });
31 }
32
33 if let Some(path) = worktree.which("OmniSharp") {
34 return Ok(OmnisharpBinary {
35 path,
36 args: binary_args,
37 });
38 }
39
40 if let Some(path) = &self.cached_binary_path {
41 if fs::metadata(path).map_or(false, |stat| stat.is_file()) {
42 return Ok(OmnisharpBinary {
43 path: path.clone(),
44 args: binary_args,
45 });
46 }
47 }
48
49 zed::set_language_server_installation_status(
50 language_server_id,
51 &zed::LanguageServerInstallationStatus::CheckingForUpdate,
52 );
53 let release = zed::latest_github_release(
54 "OmniSharp/omnisharp-roslyn",
55 zed::GithubReleaseOptions {
56 require_assets: true,
57 pre_release: false,
58 },
59 )?;
60
61 let (platform, arch) = zed::current_platform();
62 let asset_name = format!(
63 "omnisharp-{os}-{arch}-net6.0.{extension}",
64 os = match platform {
65 zed::Os::Mac => "osx",
66 zed::Os::Linux => "linux",
67 zed::Os::Windows => "win",
68 },
69 arch = match arch {
70 zed::Architecture::Aarch64 => "arm64",
71 zed::Architecture::X86 => "x86",
72 zed::Architecture::X8664 => "x64",
73 },
74 extension = match platform {
75 zed::Os::Mac | zed::Os::Linux => "tar.gz",
76 zed::Os::Windows => "zip",
77 }
78 );
79
80 let asset = release
81 .assets
82 .iter()
83 .find(|asset| asset.name == asset_name)
84 .ok_or_else(|| format!("no asset found matching {:?}", asset_name))?;
85
86 let version_dir = format!("omnisharp-{}", release.version);
87 let binary_path = format!("{version_dir}/OmniSharp");
88
89 if !fs::metadata(&binary_path).map_or(false, |stat| stat.is_file()) {
90 zed::set_language_server_installation_status(
91 language_server_id,
92 &zed::LanguageServerInstallationStatus::Downloading,
93 );
94
95 zed::download_file(
96 &asset.download_url,
97 &version_dir,
98 match platform {
99 zed::Os::Mac | zed::Os::Linux => zed::DownloadedFileType::GzipTar,
100 zed::Os::Windows => zed::DownloadedFileType::Zip,
101 },
102 )
103 .map_err(|e| format!("failed to download file: {e}"))?;
104
105 let entries =
106 fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
107 for entry in entries {
108 let entry = entry.map_err(|e| format!("failed to load directory entry {e}"))?;
109 if entry.file_name().to_str() != Some(&version_dir) {
110 fs::remove_dir_all(entry.path()).ok();
111 }
112 }
113 }
114
115 self.cached_binary_path = Some(binary_path.clone());
116 Ok(OmnisharpBinary {
117 path: binary_path,
118 args: binary_args,
119 })
120 }
121}
122
123impl zed::Extension for CsharpExtension {
124 fn new() -> Self {
125 Self {
126 cached_binary_path: None,
127 }
128 }
129
130 fn language_server_command(
131 &mut self,
132 language_server_id: &zed::LanguageServerId,
133 worktree: &zed::Worktree,
134 ) -> Result<zed::Command> {
135 let omnisharp_binary = self.language_server_binary(language_server_id, worktree)?;
136 Ok(zed::Command {
137 command: omnisharp_binary.path,
138 args: omnisharp_binary.args.unwrap_or_else(|| vec!["-lsp".into()]),
139 env: Default::default(),
140 })
141 }
142}
143
144zed::register_extension!(CsharpExtension);