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, DebugRequest, 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
208/// Registers the provided type as a Zed extension.
209///
210/// The type must implement the [`Extension`] trait.
211#[macro_export]
212macro_rules! register_extension {
213 ($extension_type:ty) => {
214 #[unsafe(export_name = "init-extension")]
215 pub extern "C" fn __init_extension() {
216 std::env::set_current_dir(std::env::var("PWD").unwrap()).unwrap();
217 zed_extension_api::register_extension(|| {
218 Box::new(<$extension_type as zed_extension_api::Extension>::new())
219 });
220 }
221 };
222}
223
224#[doc(hidden)]
225pub fn register_extension(build_extension: fn() -> Box<dyn Extension>) {
226 unsafe { EXTENSION = Some((build_extension)()) }
227}
228
229fn extension() -> &'static mut dyn Extension {
230 #[expect(static_mut_refs)]
231 unsafe {
232 EXTENSION.as_deref_mut().unwrap()
233 }
234}
235
236static mut EXTENSION: Option<Box<dyn Extension>> = None;
237
238#[cfg(target_arch = "wasm32")]
239#[unsafe(link_section = "zed:api-version")]
240#[doc(hidden)]
241pub static ZED_API_VERSION: [u8; 6] = *include_bytes!(concat!(env!("OUT_DIR"), "/version_bytes"));
242
243mod wit {
244
245 wit_bindgen::generate!({
246 skip: ["init-extension"],
247 path: "./wit/since_v0.6.0",
248 });
249}
250
251wit::export!(Component);
252
253struct Component;
254
255impl wit::Guest for Component {
256 fn language_server_command(
257 language_server_id: String,
258 worktree: &wit::Worktree,
259 ) -> Result<wit::Command> {
260 let language_server_id = LanguageServerId(language_server_id);
261 extension().language_server_command(&language_server_id, worktree)
262 }
263
264 fn language_server_initialization_options(
265 language_server_id: String,
266 worktree: &Worktree,
267 ) -> Result<Option<String>, String> {
268 let language_server_id = LanguageServerId(language_server_id);
269 Ok(extension()
270 .language_server_initialization_options(&language_server_id, worktree)?
271 .and_then(|value| serde_json::to_string(&value).ok()))
272 }
273
274 fn language_server_workspace_configuration(
275 language_server_id: String,
276 worktree: &Worktree,
277 ) -> Result<Option<String>, String> {
278 let language_server_id = LanguageServerId(language_server_id);
279 Ok(extension()
280 .language_server_workspace_configuration(&language_server_id, worktree)?
281 .and_then(|value| serde_json::to_string(&value).ok()))
282 }
283
284 fn language_server_additional_initialization_options(
285 language_server_id: String,
286 target_language_server_id: String,
287 worktree: &Worktree,
288 ) -> Result<Option<String>, String> {
289 let language_server_id = LanguageServerId(language_server_id);
290 let target_language_server_id = LanguageServerId(target_language_server_id);
291 Ok(extension()
292 .language_server_additional_initialization_options(
293 &language_server_id,
294 &target_language_server_id,
295 worktree,
296 )?
297 .and_then(|value| serde_json::to_string(&value).ok()))
298 }
299
300 fn language_server_additional_workspace_configuration(
301 language_server_id: String,
302 target_language_server_id: String,
303 worktree: &Worktree,
304 ) -> Result<Option<String>, String> {
305 let language_server_id = LanguageServerId(language_server_id);
306 let target_language_server_id = LanguageServerId(target_language_server_id);
307 Ok(extension()
308 .language_server_additional_workspace_configuration(
309 &language_server_id,
310 &target_language_server_id,
311 worktree,
312 )?
313 .and_then(|value| serde_json::to_string(&value).ok()))
314 }
315
316 fn labels_for_completions(
317 language_server_id: String,
318 completions: Vec<Completion>,
319 ) -> Result<Vec<Option<CodeLabel>>, String> {
320 let language_server_id = LanguageServerId(language_server_id);
321 let mut labels = Vec::new();
322 for (ix, completion) in completions.into_iter().enumerate() {
323 let label = extension().label_for_completion(&language_server_id, completion);
324 if let Some(label) = label {
325 labels.resize(ix + 1, None);
326 *labels.last_mut().unwrap() = Some(label);
327 }
328 }
329 Ok(labels)
330 }
331
332 fn labels_for_symbols(
333 language_server_id: String,
334 symbols: Vec<Symbol>,
335 ) -> Result<Vec<Option<CodeLabel>>, String> {
336 let language_server_id = LanguageServerId(language_server_id);
337 let mut labels = Vec::new();
338 for (ix, symbol) in symbols.into_iter().enumerate() {
339 let label = extension().label_for_symbol(&language_server_id, symbol);
340 if let Some(label) = label {
341 labels.resize(ix + 1, None);
342 *labels.last_mut().unwrap() = Some(label);
343 }
344 }
345 Ok(labels)
346 }
347
348 fn complete_slash_command_argument(
349 command: SlashCommand,
350 args: Vec<String>,
351 ) -> Result<Vec<SlashCommandArgumentCompletion>, String> {
352 extension().complete_slash_command_argument(command, args)
353 }
354
355 fn run_slash_command(
356 command: SlashCommand,
357 args: Vec<String>,
358 worktree: Option<&Worktree>,
359 ) -> Result<SlashCommandOutput, String> {
360 extension().run_slash_command(command, args, worktree)
361 }
362
363 fn context_server_command(
364 context_server_id: String,
365 project: &Project,
366 ) -> Result<wit::Command> {
367 let context_server_id = ContextServerId(context_server_id);
368 extension().context_server_command(&context_server_id, project)
369 }
370
371 fn context_server_configuration(
372 context_server_id: String,
373 project: &Project,
374 ) -> Result<Option<ContextServerConfiguration>, String> {
375 let context_server_id = ContextServerId(context_server_id);
376 extension().context_server_configuration(&context_server_id, project)
377 }
378
379 fn suggest_docs_packages(provider: String) -> Result<Vec<String>, String> {
380 extension().suggest_docs_packages(provider)
381 }
382
383 fn index_docs(
384 provider: String,
385 package: String,
386 database: &KeyValueStore,
387 ) -> Result<(), String> {
388 extension().index_docs(provider, package, database)
389 }
390
391 fn get_dap_binary(
392 adapter_name: String,
393 config: DebugTaskDefinition,
394 user_installed_path: Option<String>,
395 worktree: &Worktree,
396 ) -> Result<wit::DebugAdapterBinary, String> {
397 extension().get_dap_binary(adapter_name, config, user_installed_path, worktree)
398 }
399}
400
401/// The ID of a language server.
402#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
403pub struct LanguageServerId(String);
404
405impl AsRef<str> for LanguageServerId {
406 fn as_ref(&self) -> &str {
407 &self.0
408 }
409}
410
411impl fmt::Display for LanguageServerId {
412 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413 write!(f, "{}", self.0)
414 }
415}
416
417/// The ID of a context server.
418#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
419pub struct ContextServerId(String);
420
421impl AsRef<str> for ContextServerId {
422 fn as_ref(&self) -> &str {
423 &self.0
424 }
425}
426
427impl fmt::Display for ContextServerId {
428 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
429 write!(f, "{}", self.0)
430 }
431}
432
433impl CodeLabelSpan {
434 /// Returns a [`CodeLabelSpan::CodeRange`].
435 pub fn code_range(range: impl Into<wit::Range>) -> Self {
436 Self::CodeRange(range.into())
437 }
438
439 /// Returns a [`CodeLabelSpan::Literal`].
440 pub fn literal(text: impl Into<String>, highlight_name: Option<String>) -> Self {
441 Self::Literal(CodeLabelSpanLiteral {
442 text: text.into(),
443 highlight_name,
444 })
445 }
446}
447
448impl From<std::ops::Range<u32>> for wit::Range {
449 fn from(value: std::ops::Range<u32>) -> Self {
450 Self {
451 start: value.start,
452 end: value.end,
453 }
454 }
455}
456
457impl From<std::ops::Range<usize>> for wit::Range {
458 fn from(value: std::ops::Range<usize>) -> Self {
459 Self {
460 start: value.start as u32,
461 end: value.end as u32,
462 }
463 }
464}