1use core::fmt;
2
3use wit::*;
4
5// WIT re-exports.
6//
7// We explicitly enumerate the symbols we want to re-export, as there are some
8// that we may want to shadow to provide a cleaner Rust API.
9pub use wit::{
10 current_platform, download_file, latest_github_release, make_file_executable, node_binary_path,
11 npm_install_package, npm_package_installed_version, npm_package_latest_version,
12 zed::extension::lsp, Architecture, CodeLabel, CodeLabelSpan, CodeLabelSpanLiteral, Command,
13 DownloadedFileType, EnvVars, GithubRelease, GithubReleaseAsset, GithubReleaseOptions,
14 LanguageServerInstallationStatus, Os, Range, Worktree,
15};
16
17// Undocumented WIT re-exports.
18//
19// These are symbols that need to be public for the purposes of implementing
20// the extension host, but aren't relevant to extension authors.
21#[doc(hidden)]
22pub use wit::Guest;
23
24/// A result returned from a Zed extension.
25pub type Result<T, E = String> = core::result::Result<T, E>;
26
27/// Updates the installation status for the given language server.
28pub fn set_language_server_installation_status(
29 language_server_id: &LanguageServerId,
30 status: &LanguageServerInstallationStatus,
31) {
32 wit::set_language_server_installation_status(&language_server_id.0, status)
33}
34
35/// A Zed extension.
36pub trait Extension: Send + Sync {
37 /// Returns a new instance of the extension.
38 fn new() -> Self
39 where
40 Self: Sized;
41
42 /// Returns the command used to start the language server for the specified
43 /// language.
44 fn language_server_command(
45 &mut self,
46 language_server_id: &LanguageServerId,
47 worktree: &Worktree,
48 ) -> Result<Command>;
49
50 /// Returns the initialization options to pass to the specified language server.
51 fn language_server_initialization_options(
52 &mut self,
53 _language_server_id: &LanguageServerId,
54 _worktree: &Worktree,
55 ) -> Result<Option<String>> {
56 Ok(None)
57 }
58
59 /// Returns the label for the given completion.
60 fn label_for_completion(
61 &self,
62 _language_server_id: &LanguageServerId,
63 _completion: Completion,
64 ) -> Option<CodeLabel> {
65 None
66 }
67}
68
69#[macro_export]
70macro_rules! register_extension {
71 ($extension_type:ty) => {
72 #[export_name = "init-extension"]
73 pub extern "C" fn __init_extension() {
74 std::env::set_current_dir(std::env::var("PWD").unwrap()).unwrap();
75 zed_extension_api::register_extension(|| {
76 Box::new(<$extension_type as zed_extension_api::Extension>::new())
77 });
78 }
79 };
80}
81
82#[doc(hidden)]
83pub fn register_extension(build_extension: fn() -> Box<dyn Extension>) {
84 unsafe { EXTENSION = Some((build_extension)()) }
85}
86
87fn extension() -> &'static mut dyn Extension {
88 unsafe { EXTENSION.as_deref_mut().unwrap() }
89}
90
91static mut EXTENSION: Option<Box<dyn Extension>> = None;
92
93#[cfg(target_arch = "wasm32")]
94#[link_section = "zed:api-version"]
95#[doc(hidden)]
96pub static ZED_API_VERSION: [u8; 6] = *include_bytes!(concat!(env!("OUT_DIR"), "/version_bytes"));
97
98mod wit {
99 wit_bindgen::generate!({
100 skip: ["init-extension"],
101 path: "./wit/since_v0.0.6",
102 });
103}
104
105wit::export!(Component);
106
107struct Component;
108
109impl wit::Guest for Component {
110 fn language_server_command(
111 language_server_id: String,
112 worktree: &wit::Worktree,
113 ) -> Result<wit::Command> {
114 let language_server_id = LanguageServerId(language_server_id);
115 extension().language_server_command(&language_server_id, worktree)
116 }
117
118 fn language_server_initialization_options(
119 language_server_id: String,
120 worktree: &Worktree,
121 ) -> Result<Option<String>, String> {
122 let language_server_id = LanguageServerId(language_server_id);
123 extension().language_server_initialization_options(&language_server_id, worktree)
124 }
125
126 fn labels_for_completions(
127 language_server_id: String,
128 completions: Vec<Completion>,
129 ) -> Result<Vec<Option<CodeLabel>>, String> {
130 let language_server_id = LanguageServerId(language_server_id);
131 let mut labels = Vec::new();
132 for (ix, completion) in completions.into_iter().enumerate() {
133 let label = extension().label_for_completion(&language_server_id, completion);
134 if let Some(label) = label {
135 labels.resize(ix + 1, None);
136 *labels.last_mut().unwrap() = Some(label);
137 }
138 }
139 Ok(labels)
140 }
141}
142
143#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
144pub struct LanguageServerId(String);
145
146impl fmt::Display for LanguageServerId {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 write!(f, "{}", self.0)
149 }
150}
151
152impl CodeLabelSpan {
153 /// Returns a [`CodeLabelSpan::CodeRange`].
154 pub fn code_range(range: impl Into<wit::Range>) -> Self {
155 Self::CodeRange(range.into())
156 }
157
158 /// Returns a [`CodeLabelSpan::Literal`].
159 pub fn literal(text: impl Into<String>, highlight_name: Option<String>) -> Self {
160 Self::Literal(CodeLabelSpanLiteral {
161 text: text.into(),
162 highlight_name,
163 })
164 }
165}
166
167impl From<std::ops::Range<u32>> for wit::Range {
168 fn from(value: std::ops::Range<u32>) -> Self {
169 Self {
170 start: value.start,
171 end: value.end,
172 }
173 }
174}
175
176impl From<std::ops::Range<usize>> for wit::Range {
177 fn from(value: std::ops::Range<usize>) -> Self {
178 Self {
179 start: value.start as u32,
180 end: value.end as u32,
181 }
182 }
183}