feat(config): Add optional widget arguments prefixed with ?

This commit is contained in:
elkowar 2021-08-21 14:24:13 +02:00
parent 7aa8d8a404
commit c380313ba7
No known key found for this signature in database
GPG key ID: E321AD71B1D1F27F
4 changed files with 39 additions and 9 deletions

View file

@ -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::<AstResult<HashMap<VarName, _>>>()?;
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 {

View file

@ -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,
});

View file

@ -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<Self> {
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<AttrName>,
pub expected_args: Vec<AttrSpec>,
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::<AstResult<_>>()?;
let expected_args = expected_args.into_iter().map(AttrSpec::from_ast).collect::<AstResult<_>>()?;
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 })

View file

@ -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.