logiguard fork: GPUI xdg-activation keyboard-focus serial fix
Update All Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
release_nightly / notify_on_failure (push) Has been cancelled
release_nightly / check_style (push) Has been cancelled
release_nightly / run_tests_windows (push) Has been cancelled
release_nightly / clippy_windows (push) Has been cancelled
release_nightly / bundle_linux_aarch64 (push) Has been cancelled
release_nightly / bundle_linux_x86_64 (push) Has been cancelled
release_nightly / bundle_mac_aarch64 (push) Has been cancelled
release_nightly / bundle_mac_x86_64 (push) Has been cancelled
release_nightly / bundle_windows_aarch64 (push) Has been cancelled
release_nightly / bundle_windows_x86_64 (push) Has been cancelled
release_nightly / build_nix_mac_aarch64 (push) Has been cancelled
release_nightly / update_nightly_tag (push) Has been cancelled
Hotfix Review Monitor / check-hotfix-reviews (push) Has been cancelled
Stale PR Review Reminder / check-stale-prs (push) Has been cancelled
Update Weekly Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
Triage Project Sync (#84) / Sync triage project (push) Has been cancelled
release_nightly / build_nix_linux_x86_64 (push) Has been cancelled
Bump collab-staging Tag / update-collab-staging-tag (push) Has been cancelled
compliance_check / scheduled_compliance_check (push) Has been cancelled

Single-commit orphan branch: full zed-industries/zed @ 8c74db0 source tree
with a 3-file patch applied (no upstream history).

Patch (crates/gpui_linux/src/linux/wayland/):
  - serial.rs: add SerialKind::KeyboardEnter
  - client.rs: store wl_keyboard.enter serial; add latest_serial_of()
  - window.rs: activate() uses keyboard-enter serial (Mutter focus gate)

Mutter honors window activation only when the token carries the keyboard-
focus serial from wl_keyboard.enter; GPUI used a stale mouse-press serial.
See docs/tray-window-focus-wayland.md in logiguard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Mohamad Khani
2026-07-14 02:22:17 +03:30
co-authored by Claude Fable 5
commit b72a46db68
3984 changed files with 1583326 additions and 0 deletions
+639
View File
@@ -0,0 +1,639 @@
//! 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)
}
fn context_server_configuration(
context_server_id: String,
project: &Project,
) -> Result<Option<ContextServerConfiguration>, String> {
let context_server_id = ContextServerId(context_server_id);
extension().context_server_configuration(&context_server_id, project)
}
fn suggest_docs_packages(provider: String) -> Result<Vec<String>, String> {
extension().suggest_docs_packages(provider)
}
fn index_docs(
provider: String,
package: String,
database: &KeyValueStore,
) -> Result<(), String> {
extension().index_docs(provider, package, database)
}
fn get_dap_binary(
adapter_name: String,
config: DebugTaskDefinition,
user_installed_path: Option<String>,
worktree: &Worktree,
) -> Result<wit::DebugAdapterBinary, String> {
extension().get_dap_binary(adapter_name, config, user_installed_path, worktree)
}
fn dap_request_kind(
adapter_name: String,
config: String,
) -> Result<StartDebuggingRequestArgumentsRequest, String> {
extension().dap_request_kind(
adapter_name,
serde_json::from_str(&config).map_err(|e| format!("Failed to parse config: {e}"))?,
)
}
fn dap_config_to_scenario(config: DebugConfig) -> Result<DebugScenario, String> {
extension().dap_config_to_scenario(config)
}
fn dap_locator_create_scenario(
locator_name: String,
build_task: TaskTemplate,
resolved_label: String,
debug_adapter_name: String,
) -> Option<DebugScenario> {
extension().dap_locator_create_scenario(
locator_name,
build_task,
resolved_label,
debug_adapter_name,
)
}
fn run_dap_locator(
locator_name: String,
build_task: TaskTemplate,
) -> Result<DebugRequest, String> {
extension().run_dap_locator(locator_name, build_task)
}
}
/// The ID of a language server.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
pub struct LanguageServerId(String);
impl LanguageServerId {
pub fn new(value: String) -> Self {
Self(value)
}
}
impl AsRef<str> for LanguageServerId {
fn as_ref(&self) -> &str {
&self.0
}
}
impl fmt::Display for LanguageServerId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
/// The ID of a context server.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
pub struct ContextServerId(String);
impl ContextServerId {
pub fn new(value: String) -> Self {
Self(value)
}
}
impl AsRef<str> for ContextServerId {
fn as_ref(&self) -> &str {
&self.0
}
}
impl fmt::Display for ContextServerId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl CodeLabelSpan {
/// Returns a [`CodeLabelSpan::CodeRange`].
pub fn code_range(range: impl Into<wit::Range>) -> Self {
Self::CodeRange(range.into())
}
/// Returns a [`CodeLabelSpan::Literal`].
pub fn literal(text: impl Into<String>, highlight_name: Option<String>) -> Self {
Self::Literal(CodeLabelSpanLiteral {
text: text.into(),
highlight_name,
})
}
}
impl From<std::ops::Range<u32>> for wit::Range {
fn from(value: std::ops::Range<u32>) -> Self {
Self {
start: value.start,
end: value.end,
}
}
}
impl From<std::ops::Range<usize>> for wit::Range {
fn from(value: std::ops::Range<usize>) -> Self {
Self {
start: value.start as u32,
end: value.end as u32,
}
}
}
+101
View File
@@ -0,0 +1,101 @@
//! An HTTP client.
pub use crate::wit::zed::extension::http_client::{
HttpMethod, HttpRequest, HttpResponse, HttpResponseStream, RedirectPolicy, fetch, fetch_stream,
};
impl HttpRequest {
/// Returns a builder for an [`HttpRequest`].
pub fn builder() -> HttpRequestBuilder {
HttpRequestBuilder::new()
}
/// Executes the [`HttpRequest`] with [`fetch`].
pub fn fetch(&self) -> Result<HttpResponse, String> {
fetch(self)
}
/// Executes the [`HttpRequest`] with [`fetch_stream`].
pub fn fetch_stream(&self) -> Result<HttpResponseStream, String> {
fetch_stream(self)
}
}
/// A builder for an [`HttpRequest`].
#[derive(Clone)]
pub struct HttpRequestBuilder {
method: Option<HttpMethod>,
url: Option<String>,
headers: Vec<(String, String)>,
body: Option<Vec<u8>>,
redirect_policy: RedirectPolicy,
}
impl Default for HttpRequestBuilder {
fn default() -> Self {
Self::new()
}
}
impl HttpRequestBuilder {
/// Returns a new [`HttpRequestBuilder`].
pub fn new() -> Self {
HttpRequestBuilder {
method: None,
url: None,
headers: Vec::new(),
body: None,
redirect_policy: RedirectPolicy::NoFollow,
}
}
/// Sets the HTTP method for the request.
pub fn method(mut self, method: HttpMethod) -> Self {
self.method = Some(method);
self
}
/// Sets the URL for the request.
pub fn url(mut self, url: impl Into<String>) -> Self {
self.url = Some(url.into());
self
}
/// Adds a header to the request.
pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
self.headers.push((name.into(), value.into()));
self
}
/// Adds the specified headers to the request.
pub fn headers(mut self, headers: impl IntoIterator<Item = (String, String)>) -> Self {
self.headers.extend(headers);
self
}
/// Sets the body of the request.
pub fn body(mut self, body: impl Into<Vec<u8>>) -> Self {
self.body = Some(body.into());
self
}
/// Sets the redirect policy for the request.
pub fn redirect_policy(mut self, policy: RedirectPolicy) -> Self {
self.redirect_policy = policy;
self
}
/// Builds the [`HttpRequest`].
pub fn build(self) -> Result<HttpRequest, String> {
let method = self.method.ok_or_else(|| "Method not set".to_string())?;
let url = self.url.ok_or_else(|| "URL not set".to_string())?;
Ok(HttpRequest {
method,
url,
headers: self.headers,
body: self.body,
redirect_policy: self.redirect_policy,
})
}
}
+44
View File
@@ -0,0 +1,44 @@
//! A module for working with processes.
use crate::wit::zed::extension::process;
pub use crate::wit::zed::extension::process::{Command, Output};
impl Command {
pub fn new(program: impl Into<String>) -> Self {
Self {
command: program.into(),
args: Vec::new(),
env: Vec::new(),
}
}
pub fn arg(mut self, arg: impl Into<String>) -> Self {
self.args.push(arg.into());
self
}
pub fn args(mut self, args: impl IntoIterator<Item = impl Into<String>>) -> Self {
self.args.extend(args.into_iter().map(Into::into));
self
}
pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.env.push((key.into(), value.into()));
self
}
pub fn envs(
mut self,
envs: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
) -> Self {
self.env.extend(
envs.into_iter()
.map(|(key, value)| (key.into(), value.into())),
);
self
}
pub fn output(&mut self) -> Result<Output, String> {
process::run_command(self)
}
}
+56
View File
@@ -0,0 +1,56 @@
//! Provides access to Zed settings.
#[path = "../wit/since_v0.8.0/settings.rs"]
mod types;
use crate::{Project, Result, SettingsLocation, Worktree, wit};
use serde_json;
pub use types::*;
impl LanguageSettings {
/// Returns the [`LanguageSettings`] for the given language.
pub fn for_worktree(language: Option<&str>, worktree: &Worktree) -> Result<Self> {
get_settings("language", language, Some(worktree.id()))
}
}
impl LspSettings {
/// Returns the [`LspSettings`] for the given language server.
pub fn for_worktree(language_server_name: &str, worktree: &Worktree) -> Result<Self> {
get_settings("lsp", Some(language_server_name), Some(worktree.id()))
}
}
impl ContextServerSettings {
/// Returns the [`ContextServerSettings`] for the given context server.
pub fn for_project(context_server_id: &str, project: &Project) -> Result<Self> {
let global_setting: Self = get_settings("context_servers", Some(context_server_id), None)?;
for worktree_id in project.worktree_ids() {
let settings = get_settings(
"context_servers",
Some(context_server_id),
Some(worktree_id),
)?;
if settings != global_setting {
return Ok(settings);
}
}
Ok(global_setting)
}
}
fn get_settings<T: serde::de::DeserializeOwned>(
settings_type: &str,
settings_name: Option<&str>,
worktree_id: Option<u64>,
) -> Result<T> {
let location = worktree_id.map(|worktree_id| SettingsLocation {
worktree_id,
path: String::new(),
});
let settings_json = wit::get_settings(location.as_ref(), settings_type, settings_name)?;
let settings: T = serde_json::from_str(&settings_json).map_err(|err| err.to_string())?;
Ok(settings)
}