extension_api.rs

  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}