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 /// Returns the label for the given symbol.
69 fn label_for_symbol(
70 &self,
71 _language_server_id: &LanguageServerId,
72 _symbol: Symbol,
73 ) -> Option<CodeLabel> {
74 None
75 }
76}
77
78#[macro_export]
79macro_rules! register_extension {
80 ($extension_type:ty) => {
81 #[export_name = "init-extension"]
82 pub extern "C" fn __init_extension() {
83 std::env::set_current_dir(std::env::var("PWD").unwrap()).unwrap();
84 zed_extension_api::register_extension(|| {
85 Box::new(<$extension_type as zed_extension_api::Extension>::new())
86 });
87 }
88 };
89}
90
91#[doc(hidden)]
92pub fn register_extension(build_extension: fn() -> Box<dyn Extension>) {
93 unsafe { EXTENSION = Some((build_extension)()) }
94}
95
96fn extension() -> &'static mut dyn Extension {
97 unsafe { EXTENSION.as_deref_mut().unwrap() }
98}
99
100static mut EXTENSION: Option<Box<dyn Extension>> = None;
101
102#[cfg(target_arch = "wasm32")]
103#[link_section = "zed:api-version"]
104#[doc(hidden)]
105pub static ZED_API_VERSION: [u8; 6] = *include_bytes!(concat!(env!("OUT_DIR"), "/version_bytes"));
106
107mod wit {
108 wit_bindgen::generate!({
109 skip: ["init-extension"],
110 path: "./wit/since_v0.0.6",
111 });
112}
113
114wit::export!(Component);
115
116struct Component;
117
118impl wit::Guest for Component {
119 fn language_server_command(
120 language_server_id: String,
121 worktree: &wit::Worktree,
122 ) -> Result<wit::Command> {
123 let language_server_id = LanguageServerId(language_server_id);
124 extension().language_server_command(&language_server_id, worktree)
125 }
126
127 fn language_server_initialization_options(
128 language_server_id: String,
129 worktree: &Worktree,
130 ) -> Result<Option<String>, String> {
131 let language_server_id = LanguageServerId(language_server_id);
132 extension().language_server_initialization_options(&language_server_id, worktree)
133 }
134
135 fn labels_for_completions(
136 language_server_id: String,
137 completions: Vec<Completion>,
138 ) -> Result<Vec<Option<CodeLabel>>, String> {
139 let language_server_id = LanguageServerId(language_server_id);
140 let mut labels = Vec::new();
141 for (ix, completion) in completions.into_iter().enumerate() {
142 let label = extension().label_for_completion(&language_server_id, completion);
143 if let Some(label) = label {
144 labels.resize(ix + 1, None);
145 *labels.last_mut().unwrap() = Some(label);
146 }
147 }
148 Ok(labels)
149 }
150
151 fn labels_for_symbols(
152 language_server_id: String,
153 symbols: Vec<Symbol>,
154 ) -> Result<Vec<Option<CodeLabel>>, String> {
155 let language_server_id = LanguageServerId(language_server_id);
156 let mut labels = Vec::new();
157 for (ix, symbol) in symbols.into_iter().enumerate() {
158 let label = extension().label_for_symbol(&language_server_id, symbol);
159 if let Some(label) = label {
160 labels.resize(ix + 1, None);
161 *labels.last_mut().unwrap() = Some(label);
162 }
163 }
164 Ok(labels)
165 }
166}
167
168#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
169pub struct LanguageServerId(String);
170
171impl AsRef<str> for LanguageServerId {
172 fn as_ref(&self) -> &str {
173 &self.0
174 }
175}
176
177impl fmt::Display for LanguageServerId {
178 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179 write!(f, "{}", self.0)
180 }
181}
182
183impl CodeLabelSpan {
184 /// Returns a [`CodeLabelSpan::CodeRange`].
185 pub fn code_range(range: impl Into<wit::Range>) -> Self {
186 Self::CodeRange(range.into())
187 }
188
189 /// Returns a [`CodeLabelSpan::Literal`].
190 pub fn literal(text: impl Into<String>, highlight_name: Option<String>) -> Self {
191 Self::Literal(CodeLabelSpanLiteral {
192 text: text.into(),
193 highlight_name,
194 })
195 }
196}
197
198impl From<std::ops::Range<u32>> for wit::Range {
199 fn from(value: std::ops::Range<u32>) -> Self {
200 Self {
201 start: value.start,
202 end: value.end,
203 }
204 }
205}
206
207impl From<std::ops::Range<usize>> for wit::Range {
208 fn from(value: std::ops::Range<usize>) -> Self {
209 Self {
210 start: value.start as u32,
211 end: value.end as u32,
212 }
213 }
214}