feat(config): Add optional widget arguments prefixed with ?
This commit is contained in:
parent
7aa8d8a404
commit
c380313ba7
4 changed files with 39 additions and 9 deletions
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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 })
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue