logiguard fork: GPUI xdg-activation keyboard-focus serial fix
Some checks failed
Update All Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
Triage Project Sync (#84) / Sync triage project (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_linux_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
Bump collab-staging Tag / update-collab-staging-tag (push) Has been cancelled
compliance_check / scheduled_compliance_check (push) Has been cancelled
Some checks failed
Update All Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
Triage Project Sync (#84) / Sync triage project (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_linux_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
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:
18
crates/feature_flags_macros/Cargo.toml
Normal file
18
crates/feature_flags_macros/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "feature_flags_macros"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lib]
|
||||
path = "src/feature_flags_macros.rs"
|
||||
proc-macro = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2.workspace = true
|
||||
quote.workspace = true
|
||||
syn.workspace = true
|
||||
1
crates/feature_flags_macros/LICENSE-GPL
Symbolic link
1
crates/feature_flags_macros/LICENSE-GPL
Symbolic link
@@ -0,0 +1 @@
|
||||
../../LICENSE-GPL
|
||||
190
crates/feature_flags_macros/src/feature_flags_macros.rs
Normal file
190
crates/feature_flags_macros/src/feature_flags_macros.rs
Normal file
@@ -0,0 +1,190 @@
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::quote;
|
||||
use syn::{Data, DeriveInput, Fields, Ident, LitStr, parse_macro_input};
|
||||
|
||||
/// Derives [`feature_flags::FeatureFlagValue`] for a unit-only enum.
|
||||
///
|
||||
/// Exactly one variant must be marked with `#[default]`. The default variant
|
||||
/// is the one returned when the feature flag is announced by the server,
|
||||
/// enabled for all users, or enabled by the staff rule — it's the "on"
|
||||
/// value, and also the fallback for `from_wire`.
|
||||
///
|
||||
/// The generated impl derives:
|
||||
///
|
||||
/// * `all_variants` — every variant, in source order.
|
||||
/// * `override_key` — the variant name, lower-cased with dashes between
|
||||
/// PascalCase word boundaries (e.g. `NewWorktree` → `"new-worktree"`).
|
||||
/// * `label` — the variant name with PascalCase boundaries expanded to
|
||||
/// spaces (e.g. `NewWorktree` → `"New Worktree"`).
|
||||
/// * `from_wire` — always returns the default variant, since today the
|
||||
/// server wire format is just presence and does not carry a variant.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[derive(Clone, Copy, PartialEq, Eq, Debug, EnumFeatureFlag)]
|
||||
/// enum Intensity {
|
||||
/// #[default]
|
||||
/// Low,
|
||||
/// High,
|
||||
/// }
|
||||
/// ```
|
||||
// `attributes(default)` lets users write `#[default]` on a variant even when
|
||||
// they're not also deriving `Default`. If `#[derive(Default)]` is present in
|
||||
// the same list, it reuses the same attribute — there's no conflict, because
|
||||
// helper attributes aren't consumed.
|
||||
#[proc_macro_derive(EnumFeatureFlag, attributes(default))]
|
||||
pub fn derive_enum_feature_flag(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
match expand(&input) {
|
||||
Ok(tokens) => tokens.into(),
|
||||
Err(e) => e.to_compile_error().into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn expand(input: &DeriveInput) -> syn::Result<TokenStream2> {
|
||||
let Data::Enum(data) = &input.data else {
|
||||
return Err(syn::Error::new_spanned(
|
||||
input,
|
||||
"EnumFeatureFlag can only be derived for enums",
|
||||
));
|
||||
};
|
||||
|
||||
if data.variants.is_empty() {
|
||||
return Err(syn::Error::new_spanned(
|
||||
input,
|
||||
"EnumFeatureFlag requires at least one variant",
|
||||
));
|
||||
}
|
||||
|
||||
let mut default_ident: Option<&Ident> = None;
|
||||
let mut variant_idents: Vec<&Ident> = Vec::new();
|
||||
|
||||
for variant in &data.variants {
|
||||
if !matches!(variant.fields, Fields::Unit) {
|
||||
return Err(syn::Error::new_spanned(
|
||||
variant,
|
||||
"EnumFeatureFlag only supports unit variants (no fields)",
|
||||
));
|
||||
}
|
||||
if has_default_attr(variant) {
|
||||
if default_ident.is_some() {
|
||||
return Err(syn::Error::new_spanned(
|
||||
variant,
|
||||
"only one variant may be marked with #[default]",
|
||||
));
|
||||
}
|
||||
default_ident = Some(&variant.ident);
|
||||
}
|
||||
variant_idents.push(&variant.ident);
|
||||
}
|
||||
|
||||
let Some(default_ident) = default_ident else {
|
||||
return Err(syn::Error::new_spanned(
|
||||
input,
|
||||
"EnumFeatureFlag requires exactly one variant to be marked with #[default]",
|
||||
));
|
||||
};
|
||||
|
||||
let name = &input.ident;
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
|
||||
let override_key_arms = variant_idents.iter().map(|variant| {
|
||||
let key = LitStr::new(&to_kebab_case(&variant.to_string()), Span::call_site());
|
||||
quote! { #name::#variant => #key }
|
||||
});
|
||||
|
||||
let label_arms = variant_idents.iter().map(|variant| {
|
||||
let label = LitStr::new(&to_space_separated(&variant.to_string()), Span::call_site());
|
||||
quote! { #name::#variant => #label }
|
||||
});
|
||||
|
||||
let all_variants = variant_idents.iter().map(|v| quote! { #name::#v });
|
||||
|
||||
Ok(quote! {
|
||||
impl #impl_generics ::std::default::Default for #name #ty_generics #where_clause {
|
||||
fn default() -> Self {
|
||||
#name::#default_ident
|
||||
}
|
||||
}
|
||||
|
||||
impl #impl_generics ::feature_flags::FeatureFlagValue for #name #ty_generics #where_clause {
|
||||
fn all_variants() -> &'static [Self] {
|
||||
&[ #( #all_variants ),* ]
|
||||
}
|
||||
|
||||
fn override_key(&self) -> &'static str {
|
||||
match self {
|
||||
#( #override_key_arms ),*
|
||||
}
|
||||
}
|
||||
|
||||
fn label(&self) -> &'static str {
|
||||
match self {
|
||||
#( #label_arms ),*
|
||||
}
|
||||
}
|
||||
|
||||
fn from_wire(_: &str) -> ::std::option::Option<Self> {
|
||||
::std::option::Option::Some(#name::#default_ident)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn has_default_attr(variant: &syn::Variant) -> bool {
|
||||
variant.attrs.iter().any(|a| a.path().is_ident("default"))
|
||||
}
|
||||
|
||||
/// Converts a PascalCase identifier to lowercase kebab-case.
|
||||
///
|
||||
/// `"NewWorktree"` → `"new-worktree"`, `"Low"` → `"low"`,
|
||||
/// `"HTTPServer"` → `"httpserver"` (acronyms are not split — keep variant
|
||||
/// names descriptive to avoid this).
|
||||
fn to_kebab_case(ident: &str) -> String {
|
||||
let mut out = String::with_capacity(ident.len() + 4);
|
||||
for (i, ch) in ident.chars().enumerate() {
|
||||
if ch.is_ascii_uppercase() {
|
||||
if i != 0 {
|
||||
out.push('-');
|
||||
}
|
||||
out.push(ch.to_ascii_lowercase());
|
||||
} else {
|
||||
out.push(ch);
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Converts a PascalCase identifier to space-separated word form for display.
|
||||
///
|
||||
/// `"NewWorktree"` → `"New Worktree"`, `"Low"` → `"Low"`.
|
||||
fn to_space_separated(ident: &str) -> String {
|
||||
let mut out = String::with_capacity(ident.len() + 4);
|
||||
for (i, ch) in ident.chars().enumerate() {
|
||||
if ch.is_ascii_uppercase() && i != 0 {
|
||||
out.push(' ');
|
||||
}
|
||||
out.push(ch);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn kebab_case() {
|
||||
assert_eq!(to_kebab_case("Low"), "low");
|
||||
assert_eq!(to_kebab_case("NewWorktree"), "new-worktree");
|
||||
assert_eq!(to_kebab_case("A"), "a");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn space_separated() {
|
||||
assert_eq!(to_space_separated("Low"), "Low");
|
||||
assert_eq!(to_space_separated("NewWorktree"), "New Worktree");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user