From c380313ba735c324ae518fee5bad5d4a4f897fe9 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 21 Aug 2021 14:24:13 +0200 Subject: [PATCH] feat(config): Add optional widget arguments prefixed with ? --- crates/eww/src/widgets/widget_node.rs | 11 +++++++++-- crates/yuck/src/config/validate.rs | 9 ++++++--- crates/yuck/src/config/widget_definition.rs | 21 +++++++++++++++++++-- docs/src/configuration.md | 7 +++++-- 4 files changed, 39 insertions(+), 9 deletions(-) diff --git a/crates/eww/src/widgets/widget_node.rs b/crates/eww/src/widgets/widget_node.rs index 70d36a4..d76484b 100644 --- a/crates/eww/src/widgets/widget_node.rs +++ b/crates/eww/src/widgets/widget_node.rs @@ -2,7 +2,7 @@ use crate::eww_state::EwwState; use anyhow::*; use dyn_clone; use eww_shared_util::{AttrName, Span, Spanned, VarName}; -use simplexpr::SimplExpr; +use simplexpr::{dynval::DynVal, SimplExpr}; use std::collections::HashMap; use yuck::{ config::{validate::ValidationError, widget_definition::WidgetDefinition, widget_use::WidgetUse}, @@ -116,13 +116,20 @@ pub fn generate_generic_widget_node( Err(AstError::TooManyNodes(w.children_span(), 0).note("User-defined widgets cannot be given children."))? } - let new_local_env = w + let mut new_local_env = w .attrs .attrs .into_iter() .map(|(name, value)| Ok((VarName(name.0), value.value.as_simplexpr()?.resolve_one_level(local_env)))) .collect::>>()?; + for expected in def.expected_args.iter().filter(|x| x.optional) { + let var_name = VarName(expected.name.clone().0); + if !new_local_env.contains_key(&var_name) { + new_local_env.insert(var_name, SimplExpr::Literal(DynVal::from(String::new()))); + } + } + let content = generate_generic_widget_node(defs, &new_local_env, def.widget.clone())?; Ok(Box::new(UserDefined { name: w.name, span: w.span, content })) } else { diff --git a/crates/yuck/src/config/validate.rs b/crates/yuck/src/config/validate.rs index 309b806..f1bcc83 100644 --- a/crates/yuck/src/config/validate.rs +++ b/crates/yuck/src/config/validate.rs @@ -59,7 +59,7 @@ pub fn validate_widget_definition( ) -> Result<(), ValidationError> { let mut variables_in_scope = globals.clone(); for arg in def.expected_args.iter() { - variables_in_scope.insert(VarName(arg.0.to_string())); + variables_in_scope.insert(VarName(arg.name.to_string())); } validate_variables_in_widget_use(other_defs, &variables_in_scope, &def.widget, true) @@ -73,11 +73,14 @@ pub fn validate_variables_in_widget_use( ) -> Result<(), ValidationError> { let matching_definition = defs.get(&widget.name); if let Some(matching_def) = matching_definition { - let missing_arg = matching_def.expected_args.iter().find(|expected| !widget.attrs.attrs.contains_key(*expected)); + let missing_arg = matching_def + .expected_args + .iter() + .find(|expected| !expected.optional && !widget.attrs.attrs.contains_key(&expected.name)); if let Some(missing_arg) = missing_arg { return Err(ValidationError::MissingAttr { widget_name: widget.name.clone(), - arg_name: missing_arg.clone(), + arg_name: missing_arg.name.clone(), arg_list_span: Some(matching_def.args_span), use_span: widget.attrs.span, }); diff --git a/crates/yuck/src/config/widget_definition.rs b/crates/yuck/src/config/widget_definition.rs index 84ccb6e..d2cf377 100644 --- a/crates/yuck/src/config/widget_definition.rs +++ b/crates/yuck/src/config/widget_definition.rs @@ -13,10 +13,27 @@ use crate::{ use eww_shared_util::{AttrName, Span, Spanned, VarName}; use super::widget_use::WidgetUse; + +#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)] +pub struct AttrSpec { + pub name: AttrName, + pub optional: bool, + pub span: Span, +} + +impl FromAst for AttrSpec { + fn from_ast(e: Ast) -> AstResult { + let span = e.span(); + let symbol = e.as_symbol()?; + let (name, optional) = if let Some(name) = symbol.strip_prefix('?') { (name.to_string(), true) } else { (symbol, false) }; + Ok(Self { name: AttrName(name), optional, span }) + } +} + #[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)] pub struct WidgetDefinition { pub name: String, - pub expected_args: Vec, + pub expected_args: Vec, pub widget: WidgetUse, pub span: Span, pub args_span: Span, @@ -31,7 +48,7 @@ impl FromAstElementContent for WidgetDefinition { .expect_array() .wrong_expr_type_to(|_, _| Some(FormFormatError::WidgetDefArglistMissing(name_span.point_span_at_end()))) .note(EXPECTED_WIDGET_DEF_FORMAT)?; - let expected_args = expected_args.into_iter().map(|x| x.as_symbol().map(AttrName)).collect::>()?; + let expected_args = expected_args.into_iter().map(AttrSpec::from_ast).collect::>()?; let widget = iter.expect_any().note(EXPECTED_WIDGET_DEF_FORMAT).and_then(WidgetUse::from_ast)?; iter.expect_done().map_err(|e| FormFormatError::WidgetDefMultipleChildren(e.span()))?; Ok(Self { name, expected_args, widget, span, args_span }) diff --git a/docs/src/configuration.md b/docs/src/configuration.md index f9c9867..bdd2e71 100644 --- a/docs/src/configuration.md +++ b/docs/src/configuration.md @@ -89,7 +89,7 @@ Depending on if you are using X11 or wayland, some additional properties exist: While our bar is already looking great, it's a bit boring. Thus, let's add some actual content! ```lisp -(defwidget greeter [text name] +(defwidget greeter [?text name] (box :orientation "horizontal" :halign "center" text @@ -108,7 +108,10 @@ To show this, let's replace the text in our window definition with a call to thi There is a lot going on here, so let's step through this. -We are creating a widget named `greeter`. This widget takes two attributes, called `text` and `name`, which must be set when the widget is used. +We are creating a widget named `greeter`. This widget takes two attributes, called `text` and `name`. +The declaration `?text` specifies that the `text`-attribute is optional, and can thus be left out. In that case, +it's value will be the empty string `""`. +The `name` attribute _must_ be provided. Now, we declare the body of our widget. We make use of a `box`, which we set a couple attributes of.