1//! The Zed Rust Extension API allows you write extensions for [Zed](https://zed.dev/) in Rust.
2
3pub mod settings;
4
5use core::fmt;
6
7use wit::*;
8
9pub use serde_json;
10
11// WIT re-exports.
12//
13// We explicitly enumerate the symbols we want to re-export, as there are some
14// that we may want to shadow to provide a cleaner Rust API.
15pub use wit::{
16 current_platform, download_file, latest_github_release, make_file_executable, node_binary_path,
17 npm_install_package, npm_package_installed_version, npm_package_latest_version, Architecture,
18 CodeLabel, CodeLabelSpan, CodeLabelSpanLiteral, Command, DownloadedFileType, EnvVars,
19 GithubRelease, GithubReleaseAsset, GithubReleaseOptions, LanguageServerInstallationStatus, Os,
20 Range, Worktree,
21};
22
23// Undocumented WIT re-exports.
24//
25// These are symbols that need to be public for the purposes of implementing
26// the extension host, but aren't relevant to extension authors.
27#[doc(hidden)]
28pub use wit::Guest;
29
30/// Constructs for interacting with language servers over the
31/// Language Server Protocol (LSP).
32pub mod lsp {
33 pub use crate::wit::zed::extension::lsp::{
34 Completion, CompletionKind, InsertTextFormat, Symbol, SymbolKind,
35 };
36}
37
38/// A result returned from a Zed extension.
39pub type Result<T, E = String> = core::result::Result<T, E>;
40
41/// Updates the installation status for the given language server.
42pub fn set_language_server_installation_status(
43 language_server_id: &LanguageServerId,
44 status: &LanguageServerInstallationStatus,
45) {
46 wit::set_language_server_installation_status(&language_server_id.0, status)
47}
48
49/// A Zed extension.
50pub trait Extension: Send + Sync {
51 /// Returns a new instance of the extension.
52 fn new() -> Self
53 where
54 Self: Sized;
55
56 /// Returns the command used to start the language server for the specified
57 /// language.
58 fn language_server_command(
59 &mut self,
60 language_server_id: &LanguageServerId,
61 worktree: &Worktree,
62 ) -> Result<Command>;
63
64 /// Returns the initialization options to pass to the specified language server.
65 fn language_server_initialization_options(
66 &mut self,
67 _language_server_id: &LanguageServerId,
68 _worktree: &Worktree,
69 ) -> Result<Option<serde_json::Value>> {
70 Ok(None)
71 }
72
73 /// Returns the workspace configuration options to pass to the language server.
74 fn language_server_workspace_configuration(
75 &mut self,
76 _language_server_id: &LanguageServerId,
77 _worktree: &Worktree,
78 ) -> Result<Option<serde_json::Value>> {
79 Ok(None)
80 }
81
82 /// Returns the label for the given completion.
83 fn label_for_completion(
84 &self,
85 _language_server_id: &LanguageServerId,
86 _completion: Completion,
87 ) -> Option<CodeLabel> {
88 None
89 }
90
91 /// Returns the label for the given symbol.
92 fn label_for_symbol(
93 &self,
94 _language_server_id: &LanguageServerId,
95 _symbol: Symbol,
96 ) -> Option<CodeLabel> {
97 None
98 }
99}
100
101/// Registers the provided type as a Zed extension.
102///
103/// The type must implement the [`Extension`] trait.
104#[macro_export]
105macro_rules! register_extension {
106 ($extension_type:ty) => {
107 #[export_name = "init-extension"]
108 pub extern "C" fn __init_extension() {
109 std::env::set_current_dir(std::env::var("PWD").unwrap()).unwrap();
110 zed_extension_api::register_extension(|| {
111 Box::new(<$extension_type as zed_extension_api::Extension>::new())
112 });
113 }
114 };
115}
116
117#[doc(hidden)]
118pub fn register_extension(build_extension: fn() -> Box<dyn Extension>) {
119 unsafe { EXTENSION = Some((build_extension)()) }
120}
121
122fn extension() -> &'static mut dyn Extension {
123 unsafe { EXTENSION.as_deref_mut().unwrap() }
124}
125
126static mut EXTENSION: Option<Box<dyn Extension>> = None;
127
128#[cfg(target_arch = "wasm32")]
129#[link_section = "zed:api-version"]
130#[doc(hidden)]
131pub static ZED_API_VERSION: [u8; 6] = *include_bytes!(concat!(env!("OUT_DIR"), "/version_bytes"));
132
133mod wit {
134 wit_bindgen::generate!({
135 skip: ["init-extension"],
136 path: "./wit/since_v0.0.6",
137 });
138}
139
140wit::export!(Component);
141
142struct Component;
143
144impl wit::Guest for Component {
145 fn language_server_command(
146 language_server_id: String,
147 worktree: &wit::Worktree,
148 ) -> Result<wit::Command> {
149 let language_server_id = LanguageServerId(language_server_id);
150 extension().language_server_command(&language_server_id, worktree)
151 }
152
153 fn language_server_initialization_options(
154 language_server_id: String,
155 worktree: &Worktree,
156 ) -> Result<Option<String>, String> {
157 let language_server_id = LanguageServerId(language_server_id);
158 Ok(extension()
159 .language_server_initialization_options(&language_server_id, worktree)?
160 .and_then(|value| serde_json::to_string(&value).ok()))
161 }
162
163 fn language_server_workspace_configuration(
164 language_server_id: String,
165 worktree: &Worktree,
166 ) -> Result<Option<String>, String> {
167 let language_server_id = LanguageServerId(language_server_id);
168 Ok(extension()
169 .language_server_workspace_configuration(&language_server_id, worktree)?
170 .and_then(|value| serde_json::to_string(&value).ok()))
171 }
172
173 fn labels_for_completions(
174 language_server_id: String,
175 completions: Vec<Completion>,
176 ) -> Result<Vec<Option<CodeLabel>>, String> {
177 let language_server_id = LanguageServerId(language_server_id);
178 let mut labels = Vec::new();
179 for (ix, completion) in completions.into_iter().enumerate() {
180 let label = extension().label_for_completion(&language_server_id, completion);
181 if let Some(label) = label {
182 labels.resize(ix + 1, None);
183 *labels.last_mut().unwrap() = Some(label);
184 }
185 }
186 Ok(labels)
187 }
188
189 fn labels_for_symbols(
190 language_server_id: String,
191 symbols: Vec<Symbol>,
192 ) -> Result<Vec<Option<CodeLabel>>, String> {
193 let language_server_id = LanguageServerId(language_server_id);
194 let mut labels = Vec::new();
195 for (ix, symbol) in symbols.into_iter().enumerate() {
196 let label = extension().label_for_symbol(&language_server_id, symbol);
197 if let Some(label) = label {
198 labels.resize(ix + 1, None);
199 *labels.last_mut().unwrap() = Some(label);
200 }
201 }
202 Ok(labels)
203 }
204}
205
206/// The ID of a language server.
207#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
208pub struct LanguageServerId(String);
209
210impl AsRef<str> for LanguageServerId {
211 fn as_ref(&self) -> &str {
212 &self.0
213 }
214}
215
216impl fmt::Display for LanguageServerId {
217 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218 write!(f, "{}", self.0)
219 }
220}
221
222impl CodeLabelSpan {
223 /// Returns a [`CodeLabelSpan::CodeRange`].
224 pub fn code_range(range: impl Into<wit::Range>) -> Self {
225 Self::CodeRange(range.into())
226 }
227
228 /// Returns a [`CodeLabelSpan::Literal`].
229 pub fn literal(text: impl Into<String>, highlight_name: Option<String>) -> Self {
230 Self::Literal(CodeLabelSpanLiteral {
231 text: text.into(),
232 highlight_name,
233 })
234 }
235}
236
237impl From<std::ops::Range<u32>> for wit::Range {
238 fn from(value: std::ops::Range<u32>) -> Self {
239 Self {
240 start: value.start,
241 end: value.end,
242 }
243 }
244}
245
246impl From<std::ops::Range<usize>> for wit::Range {
247 fn from(value: std::ops::Range<usize>) -> Self {
248 Self {
249 start: value.start as u32,
250 end: value.end as u32,
251 }
252 }
253}