639 lines · 22.1 KB · Rust
RawDownload
//! The Zed Rust Extension API allows you write extensions for [Zed](https://zed.dev/) in Rust. pub mod http_client;pub mod process;pub mod settings; use core::fmt; use wit::*; pub use serde_json; // WIT re-exports.//// We explicitly enumerate the symbols we want to re-export, as there are some// that we may want to shadow to provide a cleaner Rust API.pub use wit::{    CodeLabel, CodeLabelSpan, CodeLabelSpanLiteral, Command, DownloadedFileType, EnvVars,    KeyValueStore, LanguageServerInstallationStatus, Project, Range, Worktree, download_file,    make_file_executable,    zed::extension::context_server::ContextServerConfiguration,    zed::extension::dap::{        AttachRequest, BuildTaskDefinition, BuildTaskDefinitionTemplatePayload, BuildTaskTemplate,        DebugAdapterBinary, DebugConfig, DebugRequest, DebugScenario, DebugTaskDefinition,        LaunchRequest, StartDebuggingRequestArguments, StartDebuggingRequestArgumentsRequest,        TaskTemplate, TcpArguments, TcpArgumentsTemplate, resolve_tcp_template,    },    zed::extension::github::{        GithubRelease, GithubReleaseAsset, GithubReleaseOptions, github_release_by_tag_name,        latest_github_release,    },    zed::extension::nodejs::{        node_binary_path, npm_install_package, npm_package_installed_version,        npm_package_latest_version,    },    zed::extension::platform::{Architecture, Os, current_platform},    zed::extension::slash_command::{        SlashCommand, SlashCommandArgumentCompletion, SlashCommandOutput, SlashCommandOutputSection,    },}; // Undocumented WIT re-exports.//// These are symbols that need to be public for the purposes of implementing// the extension host, but aren't relevant to extension authors.#[doc(hidden)]pub use wit::Guest; /// Constructs for interacting with language servers over the/// Language Server Protocol (LSP).pub mod lsp {    pub use crate::wit::zed::extension::lsp::{        Completion, CompletionKind, InsertTextFormat, Symbol, SymbolKind,    };} /// A result returned from a Zed extension.pub type Result<T, E = String> = core::result::Result<T, E>; /// Updates the installation status for the given language server.pub fn set_language_server_installation_status(    language_server_id: &LanguageServerId,    status: &LanguageServerInstallationStatus,) {    wit::set_language_server_installation_status(&language_server_id.0, status)} /// A Zed extension.pub trait Extension: Send + Sync {    /// Returns a new instance of the extension.    fn new() -> Self    where        Self: Sized;     /// Returns the command used to start the language server for the specified    /// language.    fn language_server_command(        &mut self,        _language_server_id: &LanguageServerId,        _worktree: &Worktree,    ) -> Result<Command> {        Err("`language_server_command` not implemented".to_string())    }     /// Returns the initialization options to pass to the specified language server.    fn language_server_initialization_options(        &mut self,        _language_server_id: &LanguageServerId,        _worktree: &Worktree,    ) -> Result<Option<serde_json::Value>> {        Ok(None)    }     /// Returns the workspace configuration options to pass to the language server.    fn language_server_workspace_configuration(        &mut self,        _language_server_id: &LanguageServerId,        _worktree: &Worktree,    ) -> Result<Option<serde_json::Value>> {        Ok(None)    }     /// Returns the JSON schema for the initialization options.    ///    /// The schema must conform to the JSON Schema speification.    fn language_server_initialization_options_schema(        &mut self,        _language_server_id: &LanguageServerId,        _worktree: &Worktree,    ) -> Option<serde_json::Value> {        None    }     /// Returns the JSON schema for the workspace configuration.    ///    /// The schema must conform to the JSON Schema specification.    fn language_server_workspace_configuration_schema(        &mut self,        _language_server_id: &LanguageServerId,        _worktree: &Worktree,    ) -> Option<serde_json::Value> {        None    }     /// Returns the initialization options to pass to the other language server.    fn language_server_additional_initialization_options(        &mut self,        _language_server_id: &LanguageServerId,        _target_language_server_id: &LanguageServerId,        _worktree: &Worktree,    ) -> Result<Option<serde_json::Value>> {        Ok(None)    }     /// Returns the workspace configuration options to pass to the other language server.    fn language_server_additional_workspace_configuration(        &mut self,        _language_server_id: &LanguageServerId,        _target_language_server_id: &LanguageServerId,        _worktree: &Worktree,    ) -> Result<Option<serde_json::Value>> {        Ok(None)    }     /// Returns the label for the given completion.    fn label_for_completion(        &self,        _language_server_id: &LanguageServerId,        _completion: Completion,    ) -> Option<CodeLabel> {        None    }     /// Returns the label for the given symbol.    fn label_for_symbol(        &self,        _language_server_id: &LanguageServerId,        _symbol: Symbol,    ) -> Option<CodeLabel> {        None    }     /// Returns the completions that should be shown when completing the provided slash command with the given query.    fn complete_slash_command_argument(        &self,        _command: SlashCommand,        _args: Vec<String>,    ) -> Result<Vec<SlashCommandArgumentCompletion>, String> {        Ok(Vec::new())    }     /// Returns the output from running the provided slash command.    fn run_slash_command(        &self,        _command: SlashCommand,        _args: Vec<String>,        _worktree: Option<&Worktree>,    ) -> Result<SlashCommandOutput, String> {        Err("`run_slash_command` not implemented".to_string())    }     /// Returns the command used to start a context server.    fn context_server_command(        &mut self,        _context_server_id: &ContextServerId,        _project: &Project,    ) -> Result<Command> {        Err("`context_server_command` not implemented".to_string())    }     /// Returns the configuration options for the specified context server.    fn context_server_configuration(        &mut self,        _context_server_id: &ContextServerId,        _project: &Project,    ) -> Result<Option<ContextServerConfiguration>> {        Ok(None)    }     /// Returns a list of package names as suggestions to be included in the    /// search results of the `/docs` slash command.    ///    /// This can be used to provide completions for known packages (e.g., from the    /// local project or a registry) before a package has been indexed.    fn suggest_docs_packages(&self, _provider: String) -> Result<Vec<String>, String> {        Ok(Vec::new())    }     /// Indexes the docs for the specified package.    fn index_docs(        &self,        _provider: String,        _package: String,        _database: &KeyValueStore,    ) -> Result<(), String> {        Err("`index_docs` not implemented".to_string())    }     /// Returns the debug adapter binary for the specified adapter name and configuration.    fn get_dap_binary(        &mut self,        _adapter_name: String,        _config: DebugTaskDefinition,        _user_provided_debug_adapter_path: Option<String>,        _worktree: &Worktree,    ) -> Result<DebugAdapterBinary, String> {        Err("`get_dap_binary` not implemented".to_string())    }     /// Determines whether the specified adapter configuration should *launch* a new debuggee process    /// or *attach* to an existing one. This function should not perform any further validation (outside of determining the kind of a request).    /// This function should return an error when the kind cannot be determined (rather than fall back to a known default).    fn dap_request_kind(        &mut self,        _adapter_name: String,        _config: serde_json::Value,    ) -> Result<StartDebuggingRequestArgumentsRequest, String> {        Err("`dap_request_kind` not implemented".to_string())    }    /// Converts a high-level definition of a debug scenario (originating in a new session UI) to a "low-level" configuration suitable for a particular adapter.    ///    /// In layman's terms: given a program, list of arguments, current working directory and environment variables,    /// create a configuration that can be used to start a debug session.    fn dap_config_to_scenario(&mut self, _config: DebugConfig) -> Result<DebugScenario, String> {        Err("`dap_config_to_scenario` not implemented".to_string())    }     /// Locators are entities that convert a Zed task into a debug scenario.    ///    /// They can be provided even by extensions that don't provide a debug adapter.    /// For all tasks applicable to a given buffer, Zed will query all locators to find one that can turn the task into a debug scenario.    /// A converted debug scenario can include a build task (it shouldn't contain any configuration in such case); a build task result will later    /// be resolved with [`Extension::run_dap_locator`].    ///    /// To work through a real-world example, take a `cargo run` task and a hypothetical `cargo` locator:    /// 1. We may need to modify the task; in this case, it is problematic that `cargo run` spawns a binary. We should turn `cargo run` into a debug scenario with    ///    `cargo build` task. This is the decision we make at `dap_locator_create_scenario` scope.    /// 2. Then, after the build task finishes, we will run `run_dap_locator` of the locator that produced the build task to find the program to be debugged. This function    ///    should give us a debugger-agnostic configuration for launching a debug target (that we end up resolving with [`Extension::dap_config_to_scenario`]). It's almost as if the user    ///    found the artifact path by themselves.    ///    /// Note that you're not obliged to use build tasks with locators. Specifically, it is sufficient to provide a debug configuration directly in the return value of    /// `dap_locator_create_scenario` if you're able to do that. Make sure to not fill out `build` field in that case, as that will prevent Zed from running second phase of resolution in such case.    /// This might be of particular relevance to interpreted languages.    fn dap_locator_create_scenario(        &mut self,        _locator_name: String,        _build_task: TaskTemplate,        _resolved_label: String,        _debug_adapter_name: String,    ) -> Option<DebugScenario> {        None    }     /// Runs the second phase of locator resolution.    /// See [`Extension::dap_locator_create_scenario`] for a hefty comment on locators.    fn run_dap_locator(        &mut self,        _locator_name: String,        _build_task: TaskTemplate,    ) -> Result<DebugRequest, String> {        Err("`run_dap_locator` not implemented".to_string())    }} /// Registers the provided type as a Zed extension.////// The type must implement the [`Extension`] trait.#[macro_export]macro_rules! register_extension {    ($extension_type:ty) => {        #[cfg(target_os = "wasi")]        mod wasi_ext {            unsafe extern "C" {                static mut errno: i32;                pub static mut __wasilibc_cwd: *mut std::ffi::c_char;            }             pub fn init_cwd() {                unsafe {                    // Ensure that our chdir function is linked, instead of the                    // one from wasi-libc in the chdir.o translation unit. Otherwise                    // we risk linking in `__wasilibc_find_relpath_alloc` which                    // is a weak symbol and is being used by                    // `__wasilibc_find_relpath`, which we do not want on                    // Windows.                    chdir(std::ptr::null());                     __wasilibc_cwd = std::ffi::CString::new(std::env::var("PWD").unwrap())                        .unwrap()                        .into_raw()                        .cast();                }            }             #[unsafe(no_mangle)]            pub unsafe extern "C" fn chdir(raw_path: *const std::ffi::c_char) -> i32 {                // Forbid extensions from changing CWD and so return an appropriate error code.                errno = 58; // NOTSUP                return -1;            }        }         #[unsafe(export_name = "init-extension")]        pub extern "C" fn __init_extension() {            #[cfg(target_os = "wasi")]            wasi_ext::init_cwd();             zed_extension_api::register_extension(|| {                Box::new(<$extension_type as zed_extension_api::Extension>::new())            });        }    };} #[doc(hidden)]pub fn register_extension(build_extension: fn() -> Box<dyn Extension>) {    unsafe { EXTENSION = Some((build_extension)()) }} fn extension() -> &'static mut dyn Extension {    #[expect(static_mut_refs)]    unsafe {        EXTENSION.as_deref_mut().unwrap()    }} static mut EXTENSION: Option<Box<dyn Extension>> = None; #[cfg(target_arch = "wasm32")]#[unsafe(link_section = "zed:api-version")]#[doc(hidden)]pub static ZED_API_VERSION: [u8; 6] = *include_bytes!(concat!(env!("OUT_DIR"), "/version_bytes")); mod wit {    wit_bindgen::generate!({        skip: ["init-extension"],        path: "./wit/since_v0.8.0",    });} wit::export!(Component); struct Component; impl wit::Guest for Component {    fn language_server_command(        language_server_id: String,        worktree: &wit::Worktree,    ) -> Result<wit::Command> {        let language_server_id = LanguageServerId(language_server_id);        extension().language_server_command(&language_server_id, worktree)    }     fn language_server_initialization_options(        language_server_id: String,        worktree: &Worktree,    ) -> Result<Option<String>, String> {        let language_server_id = LanguageServerId(language_server_id);        Ok(extension()            .language_server_initialization_options(&language_server_id, worktree)?            .and_then(|value| serde_json::to_string(&value).ok()))    }     fn language_server_workspace_configuration(        language_server_id: String,        worktree: &Worktree,    ) -> Result<Option<String>, String> {        let language_server_id = LanguageServerId(language_server_id);        Ok(extension()            .language_server_workspace_configuration(&language_server_id, worktree)?            .and_then(|value| serde_json::to_string(&value).ok()))    }     fn language_server_initialization_options_schema(        language_server_id: String,        worktree: &Worktree,    ) -> Option<String> {        let language_server_id = LanguageServerId(language_server_id);        extension()            .language_server_initialization_options_schema(&language_server_id, worktree)            .and_then(|value| serde_json::to_string(&value).ok())    }     fn language_server_workspace_configuration_schema(        language_server_id: String,        worktree: &Worktree,    ) -> Option<String> {        let language_server_id = LanguageServerId(language_server_id);        extension()            .language_server_workspace_configuration_schema(&language_server_id, worktree)            .and_then(|value| serde_json::to_string(&value).ok())    }     fn language_server_additional_initialization_options(        language_server_id: String,        target_language_server_id: String,        worktree: &Worktree,    ) -> Result<Option<String>, String> {        let language_server_id = LanguageServerId(language_server_id);        let target_language_server_id = LanguageServerId(target_language_server_id);        Ok(extension()            .language_server_additional_initialization_options(                &language_server_id,                &target_language_server_id,                worktree,            )?            .and_then(|value| serde_json::to_string(&value).ok()))    }     fn language_server_additional_workspace_configuration(        language_server_id: String,        target_language_server_id: String,        worktree: &Worktree,    ) -> Result<Option<String>, String> {        let language_server_id = LanguageServerId(language_server_id);        let target_language_server_id = LanguageServerId(target_language_server_id);        Ok(extension()            .language_server_additional_workspace_configuration(                &language_server_id,                &target_language_server_id,                worktree,            )?            .and_then(|value| serde_json::to_string(&value).ok()))    }     fn labels_for_completions(        language_server_id: String,        completions: Vec<Completion>,    ) -> Result<Vec<Option<CodeLabel>>, String> {        let language_server_id = LanguageServerId(language_server_id);        let mut labels = Vec::new();        for (ix, completion) in completions.into_iter().enumerate() {            let label = extension().label_for_completion(&language_server_id, completion);            if let Some(label) = label {                labels.resize(ix + 1, None);                *labels.last_mut().unwrap() = Some(label);            }        }        Ok(labels)    }     fn labels_for_symbols(        language_server_id: String,        symbols: Vec<Symbol>,    ) -> Result<Vec<Option<CodeLabel>>, String> {        let language_server_id = LanguageServerId(language_server_id);        let mut labels = Vec::new();        for (ix, symbol) in symbols.into_iter().enumerate() {            let label = extension().label_for_symbol(&language_server_id, symbol);            if let Some(label) = label {                labels.resize(ix + 1, None);                *labels.last_mut().unwrap() = Some(label);            }        }        Ok(labels)    }     fn complete_slash_command_argument(        command: SlashCommand,        args: Vec<String>,    ) -> Result<Vec<SlashCommandArgumentCompletion>, String> {        extension().complete_slash_command_argument(command, args)    }     fn run_slash_command(        command: SlashCommand,        args: Vec<String>,        worktree: Option<&Worktree>,    ) -> Result<SlashCommandOutput, String> {        extension().run_slash_command(command, args, worktree)    }     fn context_server_command(        context_server_id: String,        project: &Project,    ) -> Result<wit::Command> {        let context_server_id = ContextServerId(context_server_id);        extension().context_server_command(&context_server_id, project)    }

Lines 1–500 of 639.