1//! The Zed Rust Extension API allows you write extensions for [Zed](https://zed.dev/) in Rust.
2
3pub mod http_client;
4pub mod process;
5pub mod settings;
6
7use core::fmt;
8
9use wit::*;
10
11pub use serde_json;
12
13// WIT re-exports.
14//
15// We explicitly enumerate the symbols we want to re-export, as there are some
16// that we may want to shadow to provide a cleaner Rust API.
17pub use wit::{
18 CodeLabel, CodeLabelSpan, CodeLabelSpanLiteral, Command, DownloadedFileType, EnvVars,
19 KeyValueStore, LanguageServerInstallationStatus, Project, Range, Worktree, download_file,
20 make_file_executable,
21 zed::extension::context_server::ContextServerConfiguration,
22 zed::extension::dap::{
23 DebugAdapterBinary, DebugTaskDefinition, StartDebuggingRequestArguments,
24 StartDebuggingRequestArgumentsRequest, TcpArguments, TcpArgumentsTemplate,
25 resolve_tcp_template,
26 },
27 zed::extension::github::{
28 GithubRelease, GithubReleaseAsset, GithubReleaseOptions, github_release_by_tag_name,
29 latest_github_release,
30 },
31 zed::extension::nodejs::{
32 node_binary_path, npm_install_package, npm_package_installed_version,
33 npm_package_latest_version,
34 },
35 zed::extension::platform::{Architecture, Os, current_platform},
36 zed::extension::slash_command::{
37 SlashCommand, SlashCommandArgumentCompletion, SlashCommandOutput, SlashCommandOutputSection,
38 },
39};
40
41// Undocumented WIT re-exports.
42//
43// These are symbols that need to be public for the purposes of implementing
44// the extension host, but aren't relevant to extension authors.
45#[doc(hidden)]
46pub use wit::Guest;
47
48/// Constructs for interacting with language servers over the
49/// Language Server Protocol (LSP).
50pub mod lsp {
51 pub use crate::wit::zed::extension::lsp::{
52 Completion, CompletionKind, InsertTextFormat, Symbol, SymbolKind,
53 };
54}
55
56/// A result returned from a Zed extension.
57pub type Result<T, E = String> = core::result::Result<T, E>;
58
59/// Updates the installation status for the given language server.
60pub fn set_language_server_installation_status(
61 language_server_id: &LanguageServerId,
62 status: &LanguageServerInstallationStatus,
63) {
64 wit::set_language_server_installation_status(&language_server_id.0, status)
65}
66
67/// A Zed extension.
68pub trait Extension: Send + Sync {
69 /// Returns a new instance of the extension.
70 fn new() -> Self
71 where
72 Self: Sized;
73
74 /// Returns the command used to start the language server for the specified
75 /// language.
76 fn language_server_command(
77 &mut self,
78 _language_server_id: &LanguageServerId,
79 _worktree: &Worktree,
80 ) -> Result<Command> {
81 Err("`language_server_command` not implemented".to_string())
82 }
83
84 /// Returns the initialization options to pass to the specified language server.
85 fn language_server_initialization_options(
86 &mut self,
87 _language_server_id: &LanguageServerId,
88 _worktree: &Worktree,
89 ) -> Result<Option<serde_json::Value>> {
90 Ok(None)
91 }
92
93 /// Returns the workspace configuration options to pass to the language server.
94 fn language_server_workspace_configuration(
95 &mut self,
96 _language_server_id: &LanguageServerId,
97 _worktree: &Worktree,
98 ) -> Result<Option<serde_json::Value>> {
99 Ok(None)
100 }
101
102 /// Returns the initialization options to pass to the other language server.
103 fn language_server_additional_initialization_options(
104 &mut self,
105 _language_server_id: &LanguageServerId,
106 _target_language_server_id: &LanguageServerId,
107 _worktree: &Worktree,
108 ) -> Result<Option<serde_json::Value>> {
109 Ok(None)
110 }
111
112 /// Returns the workspace configuration options to pass to the other language server.
113 fn language_server_additional_workspace_configuration(
114 &mut self,
115 _language_server_id: &LanguageServerId,
116 _target_language_server_id: &LanguageServerId,
117 _worktree: &Worktree,
118 ) -> Result<Option<serde_json::Value>> {
119 Ok(None)
120 }
121
122 /// Returns the label for the given completion.
123 fn label_for_completion(
124 &self,
125 _language_server_id: &LanguageServerId,
126 _completion: Completion,
127 ) -> Option<CodeLabel> {
128 None
129 }
130
131 /// Returns the label for the given symbol.
132 fn label_for_symbol(
133 &self,
134 _language_server_id: &LanguageServerId,
135 _symbol: Symbol,
136 ) -> Option<CodeLabel> {
137 None
138 }
139
140 /// Returns the completions that should be shown when completing the provided slash command with the given query.
141 fn complete_slash_command_argument(
142 &self,
143 _command: SlashCommand,
144 _args: Vec<String>,
145 ) -> Result<Vec<SlashCommandArgumentCompletion>, String> {
146 Ok(Vec::new())
147 }
148
149 /// Returns the output from running the provided slash command.
150 fn run_slash_command(
151 &self,
152 _command: SlashCommand,
153 _args: Vec<String>,
154 _worktree: Option<&Worktree>,
155 ) -> Result<SlashCommandOutput, String> {
156 Err("`run_slash_command` not implemented".to_string())
157 }
158
159 /// Returns the command used to start a context server.
160 fn context_server_command(
161 &mut self,
162 _context_server_id: &ContextServerId,
163 _project: &Project,
164 ) -> Result<Command> {
165 Err("`context_server_command` not implemented".to_string())
166 }
167
168 /// Returns the configuration options for the specified context server.
169 fn context_server_configuration(
170 &mut self,
171 _context_server_id: &ContextServerId,
172 _project: &Project,
173 ) -> Result<Option<ContextServerConfiguration>> {
174 Ok(None)
175 }
176
177 /// Returns a list of package names as suggestions to be included in the
178 /// search results of the `/docs` slash command.
179 ///
180 /// This can be used to provide completions for known packages (e.g., from the
181 /// local project or a registry) before a package has been indexed.
182 fn suggest_docs_packages(&self, _provider: String) -> Result<Vec<String>, String> {
183 Ok(Vec::new())
184 }
185
186 /// Indexes the docs for the specified package.
187 fn index_docs(
188 &self,
189 _provider: String,
190 _package: String,
191 _database: &KeyValueStore,
192 ) -> Result<(), String> {
193 Err("`index_docs` not implemented".to_string())
194 }
195
196 /// Returns the debug adapter binary for the specified adapter name and configuration.
197 fn get_dap_binary(
198 &mut self,
199 _adapter_name: String,
200 _config: DebugTaskDefinition,
201 _user_provided_path: Option<String>,
202 _worktree: &Worktree,
203 ) -> Result<DebugAdapterBinary, String> {
204 Err("`get_dap_binary` not implemented".to_string())
205 }
206
207 fn dap_schema(&mut self) -> Result<serde_json::Value, String> {
208 Err("`dap_schema` not implemented".to_string())
209 }
210}
211
212/// Registers the provided type as a Zed extension.
213///
214/// The type must implement the [`Extension`] trait.
215#[macro_export]
216macro_rules! register_extension {
217 ($extension_type:ty) => {
218 #[unsafe(export_name = "init-extension")]
219 pub extern "C" fn __init_extension() {
220 std::env::set_current_dir(std::env::var("PWD").unwrap()).unwrap();
221 zed_extension_api::register_extension(|| {
222 Box::new(<$extension_type as zed_extension_api::Extension>::new())
223 });
224 }
225 };
226}
227
228#[doc(hidden)]
229pub fn register_extension(build_extension: fn() -> Box<dyn Extension>) {
230 unsafe { EXTENSION = Some((build_extension)()) }
231}
232
233fn extension() -> &'static mut dyn Extension {
234 #[expect(static_mut_refs)]
235 unsafe {
236 EXTENSION.as_deref_mut().unwrap()
237 }
238}
239
240static mut EXTENSION: Option<Box<dyn Extension>> = None;
241
242#[cfg(target_arch = "wasm32")]
243#[unsafe(link_section = "zed:api-version")]
244#[doc(hidden)]
245pub static ZED_API_VERSION: [u8; 6] = *include_bytes!(concat!(env!("OUT_DIR"), "/version_bytes"));
246
247mod wit {
248
249 wit_bindgen::generate!({
250 skip: ["init-extension"],
251 path: "./wit/since_v0.6.0",
252 });
253}
254
255wit::export!(Component);
256
257struct Component;
258
259impl wit::Guest for Component {
260 fn language_server_command(
261 language_server_id: String,
262 worktree: &wit::Worktree,
263 ) -> Result<wit::Command> {
264 let language_server_id = LanguageServerId(language_server_id);
265 extension().language_server_command(&language_server_id, worktree)
266 }
267
268 fn language_server_initialization_options(
269 language_server_id: String,
270 worktree: &Worktree,
271 ) -> Result<Option<String>, String> {
272 let language_server_id = LanguageServerId(language_server_id);
273 Ok(extension()
274 .language_server_initialization_options(&language_server_id, worktree)?
275 .and_then(|value| serde_json::to_string(&value).ok()))
276 }
277
278 fn language_server_workspace_configuration(
279 language_server_id: String,
280 worktree: &Worktree,
281 ) -> Result<Option<String>, String> {
282 let language_server_id = LanguageServerId(language_server_id);
283 Ok(extension()
284 .language_server_workspace_configuration(&language_server_id, worktree)?
285 .and_then(|value| serde_json::to_string(&value).ok()))
286 }
287
288 fn language_server_additional_initialization_options(
289 language_server_id: String,
290 target_language_server_id: String,
291 worktree: &Worktree,
292 ) -> Result<Option<String>, String> {
293 let language_server_id = LanguageServerId(language_server_id);
294 let target_language_server_id = LanguageServerId(target_language_server_id);
295 Ok(extension()
296 .language_server_additional_initialization_options(
297 &language_server_id,
298 &target_language_server_id,
299 worktree,
300 )?
301 .and_then(|value| serde_json::to_string(&value).ok()))
302 }
303
304 fn language_server_additional_workspace_configuration(
305 language_server_id: String,
306 target_language_server_id: String,
307 worktree: &Worktree,
308 ) -> Result<Option<String>, String> {
309 let language_server_id = LanguageServerId(language_server_id);
310 let target_language_server_id = LanguageServerId(target_language_server_id);
311 Ok(extension()
312 .language_server_additional_workspace_configuration(
313 &language_server_id,
314 &target_language_server_id,
315 worktree,
316 )?
317 .and_then(|value| serde_json::to_string(&value).ok()))
318 }
319
320 fn labels_for_completions(
321 language_server_id: String,
322 completions: Vec<Completion>,
323 ) -> Result<Vec<Option<CodeLabel>>, String> {
324 let language_server_id = LanguageServerId(language_server_id);
325 let mut labels = Vec::new();
326 for (ix, completion) in completions.into_iter().enumerate() {
327 let label = extension().label_for_completion(&language_server_id, completion);
328 if let Some(label) = label {
329 labels.resize(ix + 1, None);
330 *labels.last_mut().unwrap() = Some(label);
331 }
332 }
333 Ok(labels)
334 }
335
336 fn labels_for_symbols(
337 language_server_id: String,
338 symbols: Vec<Symbol>,
339 ) -> Result<Vec<Option<CodeLabel>>, String> {
340 let language_server_id = LanguageServerId(language_server_id);
341 let mut labels = Vec::new();
342 for (ix, symbol) in symbols.into_iter().enumerate() {
343 let label = extension().label_for_symbol(&language_server_id, symbol);
344 if let Some(label) = label {
345 labels.resize(ix + 1, None);
346 *labels.last_mut().unwrap() = Some(label);
347 }
348 }
349 Ok(labels)
350 }
351
352 fn complete_slash_command_argument(
353 command: SlashCommand,
354 args: Vec<String>,
355 ) -> Result<Vec<SlashCommandArgumentCompletion>, String> {
356 extension().complete_slash_command_argument(command, args)
357 }
358
359 fn run_slash_command(
360 command: SlashCommand,
361 args: Vec<String>,
362 worktree: Option<&Worktree>,
363 ) -> Result<SlashCommandOutput, String> {
364 extension().run_slash_command(command, args, worktree)
365 }
366
367 fn context_server_command(
368 context_server_id: String,
369 project: &Project,
370 ) -> Result<wit::Command> {
371 let context_server_id = ContextServerId(context_server_id);
372 extension().context_server_command(&context_server_id, project)
373 }
374
375 fn context_server_configuration(
376 context_server_id: String,
377 project: &Project,
378 ) -> Result<Option<ContextServerConfiguration>, String> {
379 let context_server_id = ContextServerId(context_server_id);
380 extension().context_server_configuration(&context_server_id, project)
381 }
382
383 fn suggest_docs_packages(provider: String) -> Result<Vec<String>, String> {
384 extension().suggest_docs_packages(provider)
385 }
386
387 fn index_docs(
388 provider: String,
389 package: String,
390 database: &KeyValueStore,
391 ) -> Result<(), String> {
392 extension().index_docs(provider, package, database)
393 }
394
395 fn get_dap_binary(
396 adapter_name: String,
397 config: DebugTaskDefinition,
398 user_installed_path: Option<String>,
399 worktree: &Worktree,
400 ) -> Result<wit::DebugAdapterBinary, String> {
401 extension().get_dap_binary(adapter_name, config, user_installed_path, worktree)
402 }
403
404 fn dap_schema() -> Result<String, String> {
405 extension().dap_schema().map(|schema| schema.to_string())
406 }
407}
408
409/// The ID of a language server.
410#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
411pub struct LanguageServerId(String);
412
413impl AsRef<str> for LanguageServerId {
414 fn as_ref(&self) -> &str {
415 &self.0
416 }
417}
418
419impl fmt::Display for LanguageServerId {
420 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421 write!(f, "{}", self.0)
422 }
423}
424
425/// The ID of a context server.
426#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
427pub struct ContextServerId(String);
428
429impl AsRef<str> for ContextServerId {
430 fn as_ref(&self) -> &str {
431 &self.0
432 }
433}
434
435impl fmt::Display for ContextServerId {
436 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
437 write!(f, "{}", self.0)
438 }
439}
440
441impl CodeLabelSpan {
442 /// Returns a [`CodeLabelSpan::CodeRange`].
443 pub fn code_range(range: impl Into<wit::Range>) -> Self {
444 Self::CodeRange(range.into())
445 }
446
447 /// Returns a [`CodeLabelSpan::Literal`].
448 pub fn literal(text: impl Into<String>, highlight_name: Option<String>) -> Self {
449 Self::Literal(CodeLabelSpanLiteral {
450 text: text.into(),
451 highlight_name,
452 })
453 }
454}
455
456impl From<std::ops::Range<u32>> for wit::Range {
457 fn from(value: std::ops::Range<u32>) -> Self {
458 Self {
459 start: value.start,
460 end: value.end,
461 }
462 }
463}
464
465impl From<std::ops::Range<usize>> for wit::Range {
466 fn from(value: std::ops::Range<usize>) -> Self {
467 Self {
468 start: value.start as u32,
469 end: value.end as u32,
470 }
471 }
472}