extension_api.rs

  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}