mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-22 17:48:11 +08:00
Move implicit attribute defaults into the attrs! key declarations to skip per-item boxing
This commit is contained in:
@@ -5,7 +5,8 @@
|
|||||||
//!
|
//!
|
||||||
//! Keys are declared with the [`node_macro::attrs!`] macro: `Name: Type` entries, where
|
//! Keys are declared with the [`node_macro::attrs!`] macro: `Name: Type` entries, where
|
||||||
//! `namespace { ... }` blocks contribute a `namespace:` name prefix. The key name is
|
//! `namespace { ... }` blocks contribute a `namespace:` name prefix. The key name is
|
||||||
//! derived mechanically from the ident (UpperCamel -> snake_case).
|
//! derived mechanically from the ident (UpperCamel -> snake_case). An optional `= value`
|
||||||
|
//! after the type declares the key's implicit default (see [`Attr::implicit_default`]).
|
||||||
|
|
||||||
use crate::Color;
|
use crate::Color;
|
||||||
use crate::list::NodeIdPath;
|
use crate::list::NodeIdPath;
|
||||||
@@ -16,6 +17,12 @@ use std::fmt::Debug;
|
|||||||
pub trait Attr {
|
pub trait Attr {
|
||||||
type Value: Clone + Send + Sync + Default + Debug + PartialEq + CacheHash + 'static;
|
type Value: Clone + Send + Sync + Default + Debug + PartialEq + CacheHash + 'static;
|
||||||
fn name() -> &'static str;
|
fn name() -> &'static str;
|
||||||
|
|
||||||
|
/// The value an item without this attribute is considered to have: the value type's `Default`,
|
||||||
|
/// unless the key's `attrs!` declaration overrides it with `= value`.
|
||||||
|
fn implicit_default() -> Self::Value {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node_macro::attrs! {
|
node_macro::attrs! {
|
||||||
@@ -24,9 +31,9 @@ node_macro::attrs! {
|
|||||||
/// Item's `BlendMode`, controlling how it composites with content beneath it.
|
/// Item's `BlendMode`, controlling how it composites with content beneath it.
|
||||||
BlendMode: crate::blending::BlendMode,
|
BlendMode: crate::blending::BlendMode,
|
||||||
/// Item's opacity multiplier, composed multiplicatively through nested groups. Affects content clipped to the item.
|
/// Item's opacity multiplier, composed multiplicatively through nested groups. Affects content clipped to the item.
|
||||||
Opacity: f64,
|
Opacity: f64 = 1.,
|
||||||
/// Item's fill opacity multiplier. Like opacity but does not affect content clipped to the item.
|
/// Item's fill opacity multiplier. Like opacity but does not affect content clipped to the item.
|
||||||
OpacityFill: f64,
|
OpacityFill: f64 = 1.,
|
||||||
/// Whether an item inherits the alpha of the content beneath it (clipping mask).
|
/// Whether an item inherits the alpha of the content beneath it (clipping mask).
|
||||||
ClippingMask: bool,
|
ClippingMask: bool,
|
||||||
/// Byte offset where a regex match begins ('Regex Find All', 'Regex Capture' text nodes).
|
/// Byte offset where a regex match begins ('Regex Find All', 'Regex Capture' text nodes).
|
||||||
|
|||||||
@@ -60,27 +60,9 @@ unsafe impl<T: StaticTypeSized> StaticType for Bundle<T> {
|
|||||||
// Implicit attribute defaults
|
// Implicit attribute defaults
|
||||||
// ===========================
|
// ===========================
|
||||||
|
|
||||||
// TODO: Remove this is not maintainable
|
/// Appends `count` copies of `key`'s implicit default to `attribute` (see [`attr::implicit_default_value`]).
|
||||||
/// Overrides the type's default value for certain attributes.
|
|
||||||
fn implicit_default_value(key: &str) -> Option<Box<dyn AnyAttributeValue>> {
|
|
||||||
if key == attr::Opacity::name() || key == attr::OpacityFill::name() {
|
|
||||||
Some(Box::new(1_f64))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The value an item without attribute `A` is considered to have: the key's implicit default if it
|
|
||||||
/// has one, otherwise the value type's `Default`.
|
|
||||||
fn implicit_default<A: Attr>() -> A::Value {
|
|
||||||
implicit_default_value(A::name())
|
|
||||||
.and_then(|value| value.into_any().downcast::<A::Value>().ok())
|
|
||||||
.map_or_else(Default::default, |value| *value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Appends `count` copies of `key`'s implicit default to `attribute` (see [`implicit_default_value`]).
|
|
||||||
fn pad_with_implicit_default(key: &str, attribute: &mut Box<dyn AnyAttribute>, count: usize) {
|
fn pad_with_implicit_default(key: &str, attribute: &mut Box<dyn AnyAttribute>, count: usize) {
|
||||||
match implicit_default_value(key) {
|
match attr::implicit_default_value(key) {
|
||||||
Some(default) => attribute.push_repeated(&*default, count),
|
Some(default) => attribute.push_repeated(&*default, count),
|
||||||
None => {
|
None => {
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
@@ -661,7 +643,7 @@ impl ItemAttributeValues {
|
|||||||
|
|
||||||
/// Gets a mutable reference to the value of the typed attribute, inserting the key's default value if absent.
|
/// Gets a mutable reference to the value of the typed attribute, inserting the key's default value if absent.
|
||||||
pub fn attr_mut_or_insert_default<A: Attr>(&mut self) -> &mut A::Value {
|
pub fn attr_mut_or_insert_default<A: Attr>(&mut self) -> &mut A::Value {
|
||||||
self.get_or_insert_with_mut(A::name(), implicit_default::<A>)
|
self.get_or_insert_with_mut(A::name(), A::implicit_default)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts the typed attribute's value, replacing any existing entry.
|
/// Inserts the typed attribute's value, replacing any existing entry.
|
||||||
@@ -1049,7 +1031,7 @@ impl<T> List<T> {
|
|||||||
|
|
||||||
/// Returns a clone of the value of the typed attribute at the given item index, or the key's default value if absent.
|
/// Returns a clone of the value of the typed attribute at the given item index, or the key's default value if absent.
|
||||||
pub fn attr_cloned_or_default<A: Attr>(&self, index: usize) -> A::Value {
|
pub fn attr_cloned_or_default<A: Attr>(&self, index: usize) -> A::Value {
|
||||||
self.attr::<A>(index).cloned().unwrap_or_else(implicit_default::<A>)
|
self.attr::<A>(index).cloned().unwrap_or_else(A::implicit_default)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a clone of the value of the typed attribute at the given item index, or the provided default if absent.
|
/// Returns a clone of the value of the typed attribute at the given item index, or the provided default if absent.
|
||||||
@@ -1087,7 +1069,7 @@ impl<T> List<T> {
|
|||||||
pub fn iter_attr_values_or_default<A: Attr>(&self) -> impl Iterator<Item = A::Value> + '_ {
|
pub fn iter_attr_values_or_default<A: Attr>(&self) -> impl Iterator<Item = A::Value> + '_ {
|
||||||
let slice = self.attributes.get_attribute_slice::<A::Value>(A::name());
|
let slice = self.attributes.get_attribute_slice::<A::Value>(A::name());
|
||||||
let len = self.element.len();
|
let len = self.element.len();
|
||||||
(0..len).map(move |i| slice.map_or_else(implicit_default::<A>, |s| s[i].clone()))
|
(0..len).map(move |i| slice.map_or_else(A::implicit_default, |s| s[i].clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable iterator over the typed attribute, creating the attribute with defaults if it doesn't exist.
|
/// Returns a mutable iterator over the typed attribute, creating the attribute with defaults if it doesn't exist.
|
||||||
@@ -1349,7 +1331,7 @@ impl<T> Item<T> {
|
|||||||
|
|
||||||
/// Returns a clone of the value of the typed attribute, or the key's default value if absent.
|
/// Returns a clone of the value of the typed attribute, or the key's default value if absent.
|
||||||
pub fn attr_cloned_or_default<A: Attr>(&self) -> A::Value {
|
pub fn attr_cloned_or_default<A: Attr>(&self) -> A::Value {
|
||||||
self.attr::<A>().cloned().unwrap_or_else(implicit_default::<A>)
|
self.attr::<A>().cloned().unwrap_or_else(A::implicit_default)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable reference to the value of the typed attribute, if present.
|
/// Returns a mutable reference to the value of the typed attribute, if present.
|
||||||
|
|||||||
@@ -2,30 +2,42 @@ use crate::crate_ident::CrateIdent;
|
|||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::parse::ParseStream;
|
use syn::parse::ParseStream;
|
||||||
use syn::{Attribute, Ident, Token, Type, braced, token};
|
use syn::{Attribute, Expr, Ident, Token, Type, braced, token};
|
||||||
|
|
||||||
/// Implementation of the `attrs!` macro declaring typed attribute keys.
|
/// Implementation of the `attrs!` macro declaring typed attribute keys.
|
||||||
///
|
///
|
||||||
/// Grammar: `Name: Type`, comma-separated; `namespace { ... }` blocks nest and contribute a
|
/// Grammar: `Name: Type`, comma-separated; `namespace { ... }` blocks nest and contribute a
|
||||||
/// `namespace:` prefix to the key name, which is otherwise derived mechanically from the key
|
/// `namespace:` prefix to the key name, which is otherwise derived mechanically from the key
|
||||||
/// ident (UpperCamel → snake_case).
|
/// ident (UpperCamel → snake_case). An optional `= value` after the type declares the key's
|
||||||
|
/// implicit default, overriding the value type's `Default` for items lacking the attribute.
|
||||||
pub fn attrs_impl(input: TokenStream) -> syn::Result<TokenStream> {
|
pub fn attrs_impl(input: TokenStream) -> syn::Result<TokenStream> {
|
||||||
let entries: Entries = syn::parse2(input)?;
|
let entries: Entries = syn::parse2(input)?;
|
||||||
let crate_ident = CrateIdent::default();
|
let crate_ident = CrateIdent::default();
|
||||||
let core = crate_ident.gcore()?;
|
let core = crate_ident.gcore()?;
|
||||||
|
|
||||||
let items = entries.0.iter().map(|entry| generate_entry(entry, core, "")).collect::<syn::Result<Vec<_>>>()?;
|
let items = entries.0.iter().map(|entry| generate_entry(entry, core, "")).collect::<syn::Result<Vec<_>>>()?;
|
||||||
|
let lookup = generate_implicit_default_lookup(&entries.0, core);
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#(#items)*
|
#(#items)*
|
||||||
|
#lookup
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Entries(Vec<Entry>);
|
struct Entries(Vec<Entry>);
|
||||||
|
|
||||||
enum Entry {
|
enum Entry {
|
||||||
Key { docs: Vec<Attribute>, ident: Ident, ty: Type },
|
Key {
|
||||||
Namespace { docs: Vec<Attribute>, ident: Ident, entries: Vec<Entry> },
|
docs: Vec<Attribute>,
|
||||||
|
ident: Ident,
|
||||||
|
ty: Box<Type>,
|
||||||
|
default: Option<Box<Expr>>,
|
||||||
|
},
|
||||||
|
Namespace {
|
||||||
|
docs: Vec<Attribute>,
|
||||||
|
ident: Ident,
|
||||||
|
entries: Vec<Entry>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl syn::parse::Parse for Entries {
|
impl syn::parse::Parse for Entries {
|
||||||
@@ -49,8 +61,14 @@ fn parse_entries(input: ParseStream) -> syn::Result<Vec<Entry>> {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
input.parse::<Token![:]>()?;
|
input.parse::<Token![:]>()?;
|
||||||
let ty: Type = input.parse()?;
|
let ty = Box::new(input.parse::<Type>()?);
|
||||||
entries.push(Entry::Key { docs, ident, ty });
|
let default = if input.peek(Token![=]) {
|
||||||
|
input.parse::<Token![=]>()?;
|
||||||
|
Some(Box::new(input.parse::<Expr>()?))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
entries.push(Entry::Key { docs, ident, ty, default });
|
||||||
}
|
}
|
||||||
if !input.is_empty() {
|
if !input.is_empty() {
|
||||||
input.parse::<Token![,]>()?;
|
input.parse::<Token![,]>()?;
|
||||||
@@ -61,8 +79,15 @@ fn parse_entries(input: ParseStream) -> syn::Result<Vec<Entry>> {
|
|||||||
|
|
||||||
fn generate_entry(entry: &Entry, core: &TokenStream, prefix: &str) -> syn::Result<TokenStream> {
|
fn generate_entry(entry: &Entry, core: &TokenStream, prefix: &str) -> syn::Result<TokenStream> {
|
||||||
match entry {
|
match entry {
|
||||||
Entry::Key { docs, ident, ty } => {
|
Entry::Key { docs, ident, ty, default } => {
|
||||||
let name = key_name(ident, prefix);
|
let name = key_name(ident, prefix);
|
||||||
|
let implicit_default = default.as_ref().map(|value| {
|
||||||
|
quote! {
|
||||||
|
fn implicit_default() -> Self::Value {
|
||||||
|
#value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#(#docs)*
|
#(#docs)*
|
||||||
pub struct #ident;
|
pub struct #ident;
|
||||||
@@ -71,6 +96,7 @@ fn generate_entry(entry: &Entry, core: &TokenStream, prefix: &str) -> syn::Resul
|
|||||||
fn name() -> &'static str {
|
fn name() -> &'static str {
|
||||||
#name
|
#name
|
||||||
}
|
}
|
||||||
|
#implicit_default
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -88,6 +114,40 @@ fn generate_entry(entry: &Entry, core: &TokenStream, prefix: &str) -> syn::Resul
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates a string-keyed lookup of the boxed implicit defaults for erased attribute code paths,
|
||||||
|
/// or nothing if no key in this invocation declares a `= value` default.
|
||||||
|
fn generate_implicit_default_lookup(entries: &[Entry], core: &TokenStream) -> Option<TokenStream> {
|
||||||
|
let mut defaulted_keys = Vec::new();
|
||||||
|
collect_defaulted_key_paths(entries, &TokenStream::new(), &mut defaulted_keys);
|
||||||
|
|
||||||
|
if defaulted_keys.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(quote! {
|
||||||
|
/// The boxed implicit default for the key named `key`, if that key declares one with `= value` in `attrs!`.
|
||||||
|
pub fn implicit_default_value(key: &str) -> ::std::option::Option<::std::boxed::Box<dyn #core::list::AnyAttributeValue>> {
|
||||||
|
#(
|
||||||
|
if key == <#defaulted_keys as #core::attr::Attr>::name() {
|
||||||
|
return ::std::option::Option::Some(::std::boxed::Box::new(<#defaulted_keys as #core::attr::Attr>::implicit_default()));
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
::std::option::Option::None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Walks the entry tree collecting module-qualified paths (like `namespace::Key`) of keys that declare a default.
|
||||||
|
fn collect_defaulted_key_paths(entries: &[Entry], module_path: &TokenStream, paths: &mut Vec<TokenStream>) {
|
||||||
|
for entry in entries {
|
||||||
|
match entry {
|
||||||
|
Entry::Key { ident, default: Some(_), .. } => paths.push(quote!(#module_path #ident)),
|
||||||
|
Entry::Key { .. } => {}
|
||||||
|
Entry::Namespace { ident, entries, .. } => collect_defaulted_key_paths(entries, "e!(#module_path #ident::), paths),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn key_name(ident: &Ident, prefix: &str) -> String {
|
fn key_name(ident: &Ident, prefix: &str) -> String {
|
||||||
let snake = snake_case(&ident.to_string());
|
let snake = snake_case(&ident.to_string());
|
||||||
if prefix.is_empty() { snake } else { format!("{prefix}:{snake}") }
|
if prefix.is_empty() { snake } else { format!("{prefix}:{snake}") }
|
||||||
|
|||||||
Reference in New Issue
Block a user