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