1pub mod http_client;
4pub mod process;
5pub mod settings;
6
7use core::fmt;
8
9use wit::*;
10
11pub use serde_json;
12
13pub 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 AttachRequest, BuildTaskDefinition, BuildTaskDefinitionTemplatePayload, BuildTaskTemplate,
24 DebugAdapterBinary, DebugConfig, DebugRequest, DebugScenario, DebugTaskDefinition,
25 LaunchRequest, StartDebuggingRequestArguments, StartDebuggingRequestArgumentsRequest,
26 TaskTemplate, TcpArguments, TcpArgumentsTemplate, resolve_tcp_template,
27 },
28 zed::extension::github::{
29 GithubRelease, GithubReleaseAsset, GithubReleaseOptions, github_release_by_tag_name,
30 latest_github_release,
31 },
32 zed::extension::nodejs::{
33 node_binary_path, npm_install_package, npm_package_installed_version,
34 npm_package_latest_version,
35 },
36 zed::extension::platform::{Architecture, Os, current_platform},
37 zed::extension::slash_command::{
38 SlashCommand, SlashCommandArgumentCompletion, SlashCommandOutput, SlashCommandOutputSection,
39 },
40};
41
42#[doc(hidden)]
47pub use wit::Guest;
48
49pub mod lsp {
52 pub use crate::wit::zed::extension::lsp::{
53 Completion, CompletionKind, InsertTextFormat, Symbol, SymbolKind,
54 };
55}
56
57pub type Result<T, E = String> = core::result::Result<T, E>;
59
60pub fn set_language_server_installation_status(
62 language_server_id: &LanguageServerId,
63 status: &LanguageServerInstallationStatus,
64) {
65 wit::set_language_server_installation_status(&language_server_id.0, status)
66}
67
68pub trait Extension: Send + Sync {
70 fn new() -> Self
72 where
73 Self: Sized;
74
75 fn language_server_command(
78 &mut self,
79 _language_server_id: &LanguageServerId,
80 _worktree: &Worktree,
81 ) -> Result<Command> {
82 Err("`language_server_command` not implemented".to_string())
83 }
84
85 fn language_server_initialization_options(
87 &mut self,
88 _language_server_id: &LanguageServerId,
89 _worktree: &Worktree,
90 ) -> Result<Option<serde_json::Value>> {
91 Ok(None)
92 }
93
94 fn language_server_workspace_configuration(
96 &mut self,
97 _language_server_id: &LanguageServerId,
98 _worktree: &Worktree,
99 ) -> Result<Option<serde_json::Value>> {
100 Ok(None)
101 }
102
103 fn language_server_additional_initialization_options(
105 &mut self,
106 _language_server_id: &LanguageServerId,
107 _target_language_server_id: &LanguageServerId,
108 _worktree: &Worktree,
109 ) -> Result<Option<serde_json::Value>> {
110 Ok(None)
111 }
112
113 fn language_server_additional_workspace_configuration(
115 &mut self,
116 _language_server_id: &LanguageServerId,
117 _target_language_server_id: &LanguageServerId,
118 _worktree: &Worktree,
119 ) -> Result<Option<serde_json::Value>> {
120 Ok(None)
121 }
122
123 fn label_for_completion(
125 &self,
126 _language_server_id: &LanguageServerId,
127 _completion: Completion,
128 ) -> Option<CodeLabel> {
129 None
130 }
131
132 fn label_for_symbol(
134 &self,
135 _language_server_id: &LanguageServerId,
136 _symbol: Symbol,
137 ) -> Option<CodeLabel> {
138 None
139 }
140
141 fn complete_slash_command_argument(
143 &self,
144 _command: SlashCommand,
145 _args: Vec<String>,
146 ) -> Result<Vec<SlashCommandArgumentCompletion>, String> {
147 Ok(Vec::new())
148 }
149
150 fn run_slash_command(
152 &self,
153 _command: SlashCommand,
154 _args: Vec<String>,
155 _worktree: Option<&Worktree>,
156 ) -> Result<SlashCommandOutput, String> {
157 Err("`run_slash_command` not implemented".to_string())
158 }
159
160 fn context_server_command(
162 &mut self,
163 _context_server_id: &ContextServerId,
164 _project: &Project,
165 ) -> Result<Command> {
166 Err("`context_server_command` not implemented".to_string())
167 }
168
169 fn context_server_configuration(
171 &mut self,
172 _context_server_id: &ContextServerId,
173 _project: &Project,
174 ) -> Result<Option<ContextServerConfiguration>> {
175 Ok(None)
176 }
177
178 fn suggest_docs_packages(&self, _provider: String) -> Result<Vec<String>, String> {
184 Ok(Vec::new())
185 }
186
187 fn index_docs(
189 &self,
190 _provider: String,
191 _package: String,
192 _database: &KeyValueStore,
193 ) -> Result<(), String> {
194 Err("`index_docs` not implemented".to_string())
195 }
196
197 fn get_dap_binary(
199 &mut self,
200 _adapter_name: String,
201 _config: DebugTaskDefinition,
202 _user_provided_debug_adapter_path: Option<String>,
203 _worktree: &Worktree,
204 ) -> Result<DebugAdapterBinary, String> {
205 Err("`get_dap_binary` not implemented".to_string())
206 }
207
208 fn dap_request_kind(
212 &mut self,
213 _adapter_name: String,
214 _config: serde_json::Value,
215 ) -> Result<StartDebuggingRequestArgumentsRequest, String> {
216 Err("`dap_request_kind` not implemented".to_string())
217 }
218 fn dap_config_to_scenario(&mut self, _config: DebugConfig) -> Result<DebugScenario, String> {
223 Err("`dap_config_to_scenario` not implemented".to_string())
224 }
225
226 fn dap_locator_create_scenario(
244 &mut self,
245 _locator_name: String,
246 _build_task: TaskTemplate,
247 _resolved_label: String,
248 _debug_adapter_name: String,
249 ) -> Option<DebugScenario> {
250 None
251 }
252
253 fn run_dap_locator(
256 &mut self,
257 _locator_name: String,
258 _build_task: TaskTemplate,
259 ) -> Result<DebugRequest, String> {
260 Err("`run_dap_locator` not implemented".to_string())
261 }
262}
263
264#[macro_export]
268macro_rules! register_extension {
269 ($extension_type:ty) => {
270 #[cfg(target_os = "wasi")]
271 mod wasi_ext {
272 unsafe extern "C" {
273 static mut errno: i32;
274 pub static mut __wasilibc_cwd: *mut std::ffi::c_char;
275 }
276
277 pub fn init_cwd() {
278 unsafe {
279 chdir(std::ptr::null());
286
287 __wasilibc_cwd = std::ffi::CString::new(std::env::var("PWD").unwrap())
288 .unwrap()
289 .into_raw()
290 .cast();
291 }
292 }
293
294 #[unsafe(no_mangle)]
295 pub unsafe extern "C" fn chdir(raw_path: *const std::ffi::c_char) -> i32 {
296 errno = 58; return -1;
299 }
300 }
301
302 #[unsafe(export_name = "init-extension")]
303 pub extern "C" fn __init_extension() {
304 #[cfg(target_os = "wasi")]
305 wasi_ext::init_cwd();
306
307 zed_extension_api::register_extension(|| {
308 Box::new(<$extension_type as zed_extension_api::Extension>::new())
309 });
310 }
311 };
312}
313
314#[doc(hidden)]
315pub fn register_extension(build_extension: fn() -> Box<dyn Extension>) {
316 unsafe { EXTENSION = Some((build_extension)()) }
317}
318
319fn extension() -> &'static mut dyn Extension {
320 #[expect(static_mut_refs)]
321 unsafe {
322 EXTENSION.as_deref_mut().unwrap()
323 }
324}
325
326static mut EXTENSION: Option<Box<dyn Extension>> = None;
327
328#[cfg(target_arch = "wasm32")]
329#[unsafe(link_section = "zed:api-version")]
330#[doc(hidden)]
331pub static ZED_API_VERSION: [u8; 6] = *include_bytes!(concat!(env!("OUT_DIR"), "/version_bytes"));
332
333mod wit {
334
335 wit_bindgen::generate!({
336 skip: ["init-extension"],
337 path: "./wit/since_v0.6.0",
338 });
339}
340
341wit::export!(Component);
342
343struct Component;
344
345impl wit::Guest for Component {
346 fn language_server_command(
347 language_server_id: String,
348 worktree: &wit::Worktree,
349 ) -> Result<wit::Command> {
350 let language_server_id = LanguageServerId(language_server_id);
351 extension().language_server_command(&language_server_id, worktree)
352 }
353
354 fn language_server_initialization_options(
355 language_server_id: String,
356 worktree: &Worktree,
357 ) -> Result<Option<String>, String> {
358 let language_server_id = LanguageServerId(language_server_id);
359 Ok(extension()
360 .language_server_initialization_options(&language_server_id, worktree)?
361 .and_then(|value| serde_json::to_string(&value).ok()))
362 }
363
364 fn language_server_workspace_configuration(
365 language_server_id: String,
366 worktree: &Worktree,
367 ) -> Result<Option<String>, String> {
368 let language_server_id = LanguageServerId(language_server_id);
369 Ok(extension()
370 .language_server_workspace_configuration(&language_server_id, worktree)?
371 .and_then(|value| serde_json::to_string(&value).ok()))
372 }
373
374 fn language_server_additional_initialization_options(
375 language_server_id: String,
376 target_language_server_id: String,
377 worktree: &Worktree,
378 ) -> Result<Option<String>, String> {
379 let language_server_id = LanguageServerId(language_server_id);
380 let target_language_server_id = LanguageServerId(target_language_server_id);
381 Ok(extension()
382 .language_server_additional_initialization_options(
383 &language_server_id,
384 &target_language_server_id,
385 worktree,
386 )?
387 .and_then(|value| serde_json::to_string(&value).ok()))
388 }
389
390 fn language_server_additional_workspace_configuration(
391 language_server_id: String,
392 target_language_server_id: String,
393 worktree: &Worktree,
394 ) -> Result<Option<String>, String> {
395 let language_server_id = LanguageServerId(language_server_id);
396 let target_language_server_id = LanguageServerId(target_language_server_id);
397 Ok(extension()
398 .language_server_additional_workspace_configuration(
399 &language_server_id,
400 &target_language_server_id,
401 worktree,
402 )?
403 .and_then(|value| serde_json::to_string(&value).ok()))
404 }
405
406 fn labels_for_completions(
407 language_server_id: String,
408 completions: Vec<Completion>,
409 ) -> Result<Vec<Option<CodeLabel>>, String> {
410 let language_server_id = LanguageServerId(language_server_id);
411 let mut labels = Vec::new();
412 for (ix, completion) in completions.into_iter().enumerate() {
413 let label = extension().label_for_completion(&language_server_id, completion);
414 if let Some(label) = label {
415 labels.resize(ix + 1, None);
416 *labels.last_mut().unwrap() = Some(label);
417 }
418 }
419 Ok(labels)
420 }
421
422 fn labels_for_symbols(
423 language_server_id: String,
424 symbols: Vec<Symbol>,
425 ) -> Result<Vec<Option<CodeLabel>>, String> {
426 let language_server_id = LanguageServerId(language_server_id);
427 let mut labels = Vec::new();
428 for (ix, symbol) in symbols.into_iter().enumerate() {
429 let label = extension().label_for_symbol(&language_server_id, symbol);
430 if let Some(label) = label {
431 labels.resize(ix + 1, None);
432 *labels.last_mut().unwrap() = Some(label);
433 }
434 }
435 Ok(labels)
436 }
437
438 fn complete_slash_command_argument(
439 command: SlashCommand,
440 args: Vec<String>,
441 ) -> Result<Vec<SlashCommandArgumentCompletion>, String> {
442 extension().complete_slash_command_argument(command, args)
443 }
444
445 fn run_slash_command(
446 command: SlashCommand,
447 args: Vec<String>,
448 worktree: Option<&Worktree>,
449 ) -> Result<SlashCommandOutput, String> {
450 extension().run_slash_command(command, args, worktree)
451 }
452
453 fn context_server_command(
454 context_server_id: String,
455 project: &Project,
456 ) -> Result<wit::Command> {
457 let context_server_id = ContextServerId(context_server_id);
458 extension().context_server_command(&context_server_id, project)
459 }
460
461 fn context_server_configuration(
462 context_server_id: String,
463 project: &Project,
464 ) -> Result<Option<ContextServerConfiguration>, String> {
465 let context_server_id = ContextServerId(context_server_id);
466 extension().context_server_configuration(&context_server_id, project)
467 }
468
469 fn suggest_docs_packages(provider: String) -> Result<Vec<String>, String> {
470 extension().suggest_docs_packages(provider)
471 }
472
473 fn index_docs(
474 provider: String,
475 package: String,
476 database: &KeyValueStore,
477 ) -> Result<(), String> {
478 extension().index_docs(provider, package, database)
479 }
480
481 fn get_dap_binary(
482 adapter_name: String,
483 config: DebugTaskDefinition,
484 user_installed_path: Option<String>,
485 worktree: &Worktree,
486 ) -> Result<wit::DebugAdapterBinary, String> {
487 extension().get_dap_binary(adapter_name, config, user_installed_path, worktree)
488 }
489
490 fn dap_request_kind(
491 adapter_name: String,
492 config: String,
493 ) -> Result<StartDebuggingRequestArgumentsRequest, String> {
494 extension().dap_request_kind(
495 adapter_name,
496 serde_json::from_str(&config).map_err(|e| format!("Failed to parse config: {e}"))?,
497 )
498 }
499 fn dap_config_to_scenario(config: DebugConfig) -> Result<DebugScenario, String> {
500 extension().dap_config_to_scenario(config)
501 }
502 fn dap_locator_create_scenario(
503 locator_name: String,
504 build_task: TaskTemplate,
505 resolved_label: String,
506 debug_adapter_name: String,
507 ) -> Option<DebugScenario> {
508 extension().dap_locator_create_scenario(
509 locator_name,
510 build_task,
511 resolved_label,
512 debug_adapter_name,
513 )
514 }
515 fn run_dap_locator(
516 locator_name: String,
517 build_task: TaskTemplate,
518 ) -> Result<DebugRequest, String> {
519 extension().run_dap_locator(locator_name, build_task)
520 }
521}
522
523#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
525pub struct LanguageServerId(String);
526
527impl AsRef<str> for LanguageServerId {
528 fn as_ref(&self) -> &str {
529 &self.0
530 }
531}
532
533impl fmt::Display for LanguageServerId {
534 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
535 write!(f, "{}", self.0)
536 }
537}
538
539#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
541pub struct ContextServerId(String);
542
543impl AsRef<str> for ContextServerId {
544 fn as_ref(&self) -> &str {
545 &self.0
546 }
547}
548
549impl fmt::Display for ContextServerId {
550 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
551 write!(f, "{}", self.0)
552 }
553}
554
555impl CodeLabelSpan {
556 pub fn code_range(range: impl Into<wit::Range>) -> Self {
558 Self::CodeRange(range.into())
559 }
560
561 pub fn literal(text: impl Into<String>, highlight_name: Option<String>) -> Self {
563 Self::Literal(CodeLabelSpanLiteral {
564 text: text.into(),
565 highlight_name,
566 })
567 }
568}
569
570impl From<std::ops::Range<u32>> for wit::Range {
571 fn from(value: std::ops::Range<u32>) -> Self {
572 Self {
573 start: value.start,
574 end: value.end,
575 }
576 }
577}
578
579impl From<std::ops::Range<usize>> for wit::Range {
580 fn from(value: std::ops::Range<usize>) -> Self {
581 Self {
582 start: value.start as u32,
583 end: value.end as u32,
584 }
585 }
586}