Implement loop widget (#350)
This commit is contained in:
parent
eec0358324
commit
6b7fa5d55c
8 changed files with 302 additions and 76 deletions
|
@ -1,16 +1,20 @@
|
|||
use anyhow::{Context, Result};
|
||||
use codespan_reporting::diagnostic::Severity;
|
||||
use eww_shared_util::AttrName;
|
||||
use eww_shared_util::{AttrName, Spanned};
|
||||
use gdk::prelude::Cast;
|
||||
use gtk::{
|
||||
prelude::{BoxExt, ContainerExt, WidgetExt, WidgetExtManual},
|
||||
Orientation,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use simplexpr::SimplExpr;
|
||||
use std::{collections::HashMap, rc::Rc};
|
||||
use maplit::hashmap;
|
||||
use simplexpr::{dynval::DynVal, SimplExpr};
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
use yuck::{
|
||||
config::{widget_definition::WidgetDefinition, widget_use::WidgetUse},
|
||||
config::{
|
||||
widget_definition::WidgetDefinition,
|
||||
widget_use::{BasicWidgetUse, ChildrenWidgetUse, LoopWidgetUse, WidgetUse},
|
||||
},
|
||||
gen_diagnostic,
|
||||
};
|
||||
|
||||
|
@ -28,7 +32,7 @@ use super::widget_definitions::{resolve_orientable_attrs, resolve_range_attrs, r
|
|||
|
||||
pub struct BuilderArgs<'a> {
|
||||
pub calling_scope: ScopeIndex,
|
||||
pub widget_use: WidgetUse,
|
||||
pub widget_use: BasicWidgetUse,
|
||||
pub scope_graph: &'a mut ScopeGraph,
|
||||
pub unhandled_attrs: Vec<AttrName>,
|
||||
pub widget_defs: Rc<HashMap<String, WidgetDefinition>>,
|
||||
|
@ -45,7 +49,26 @@ pub fn build_gtk_widget(
|
|||
graph: &mut ScopeGraph,
|
||||
widget_defs: Rc<HashMap<String, WidgetDefinition>>,
|
||||
calling_scope: ScopeIndex,
|
||||
mut widget_use: WidgetUse,
|
||||
widget_use: WidgetUse,
|
||||
custom_widget_invocation: Option<Rc<CustomWidgetInvocation>>,
|
||||
) -> Result<gtk::Widget> {
|
||||
match widget_use {
|
||||
WidgetUse::Basic(widget_use) => {
|
||||
build_basic_gtk_widget(graph, widget_defs, calling_scope, widget_use, custom_widget_invocation)
|
||||
}
|
||||
WidgetUse::Loop(_) | WidgetUse::Children(_) => Err(anyhow::anyhow!(DiagError::new(gen_diagnostic! {
|
||||
msg = "This widget can only be used as a child of some container widget such as box",
|
||||
label = widget_use.span(),
|
||||
note = "Hint: try wrapping this in a `box`"
|
||||
}))),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_basic_gtk_widget(
|
||||
graph: &mut ScopeGraph,
|
||||
widget_defs: Rc<HashMap<String, WidgetDefinition>>,
|
||||
calling_scope: ScopeIndex,
|
||||
mut widget_use: BasicWidgetUse,
|
||||
custom_widget_invocation: Option<Rc<CustomWidgetInvocation>>,
|
||||
) -> Result<gtk::Widget> {
|
||||
if let Some(custom_widget) = widget_defs.clone().get(&widget_use.name) {
|
||||
|
@ -100,7 +123,7 @@ fn build_builtin_gtk_widget(
|
|||
graph: &mut ScopeGraph,
|
||||
widget_defs: Rc<HashMap<String, WidgetDefinition>>,
|
||||
calling_scope: ScopeIndex,
|
||||
widget_use: WidgetUse,
|
||||
widget_use: BasicWidgetUse,
|
||||
custom_widget_invocation: Option<Rc<CustomWidgetInvocation>>,
|
||||
) -> Result<gtk::Widget> {
|
||||
let mut bargs = BuilderArgs {
|
||||
|
@ -160,25 +183,93 @@ fn populate_widget_children(
|
|||
custom_widget_invocation: Option<Rc<CustomWidgetInvocation>>,
|
||||
) -> Result<()> {
|
||||
for child in widget_use_children {
|
||||
if child.name == "children" {
|
||||
let custom_widget_invocation = custom_widget_invocation.clone().context("Not in a custom widget invocation")?;
|
||||
build_children_special_widget(
|
||||
tree,
|
||||
widget_defs.clone(),
|
||||
calling_scope,
|
||||
child,
|
||||
gtk_container,
|
||||
custom_widget_invocation,
|
||||
)?;
|
||||
} else {
|
||||
let child_widget =
|
||||
build_gtk_widget(tree, widget_defs.clone(), calling_scope, child, custom_widget_invocation.clone())?;
|
||||
gtk_container.add(&child_widget);
|
||||
match child {
|
||||
WidgetUse::Children(child) => {
|
||||
build_children_special_widget(
|
||||
tree,
|
||||
widget_defs.clone(),
|
||||
calling_scope,
|
||||
child,
|
||||
gtk_container,
|
||||
custom_widget_invocation.clone().context("Not in a custom widget invocation")?,
|
||||
)?;
|
||||
}
|
||||
WidgetUse::Loop(child) => {
|
||||
build_loop_special_widget(
|
||||
tree,
|
||||
widget_defs.clone(),
|
||||
calling_scope,
|
||||
child,
|
||||
gtk_container,
|
||||
custom_widget_invocation.clone(),
|
||||
)?;
|
||||
}
|
||||
_ => {
|
||||
let child_widget =
|
||||
build_gtk_widget(tree, widget_defs.clone(), calling_scope, child, custom_widget_invocation.clone())?;
|
||||
gtk_container.add(&child_widget);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_loop_special_widget(
|
||||
tree: &mut ScopeGraph,
|
||||
widget_defs: Rc<HashMap<String, WidgetDefinition>>,
|
||||
calling_scope: ScopeIndex,
|
||||
widget_use: LoopWidgetUse,
|
||||
gtk_container: >k::Container,
|
||||
custom_widget_invocation: Option<Rc<CustomWidgetInvocation>>,
|
||||
) -> Result<()> {
|
||||
tree.register_listener(
|
||||
calling_scope,
|
||||
Listener {
|
||||
needed_variables: widget_use.elements_expr.collect_var_refs(),
|
||||
f: Box::new({
|
||||
let custom_widget_invocation = custom_widget_invocation.clone();
|
||||
let widget_defs = widget_defs.clone();
|
||||
let elements_expr = widget_use.elements_expr.clone();
|
||||
let elements_expr_span = widget_use.elements_expr_span.clone();
|
||||
let element_name = widget_use.element_name.clone();
|
||||
let body: WidgetUse = widget_use.body.as_ref().clone();
|
||||
let created_children = Rc::new(RefCell::new(Vec::<gtk::Widget>::new()));
|
||||
let gtk_container = gtk_container.clone();
|
||||
move |tree, values| {
|
||||
let elements_value = elements_expr
|
||||
.eval(&values)?
|
||||
.as_json_value()?
|
||||
.as_array()
|
||||
.context("Not an array value")?
|
||||
.into_iter()
|
||||
.map(DynVal::from)
|
||||
.collect_vec();
|
||||
let mut created_children = created_children.borrow_mut();
|
||||
for old_child in created_children.drain(..) {
|
||||
unsafe { old_child.destroy() };
|
||||
}
|
||||
for element in elements_value {
|
||||
let scope = tree.register_new_scope(
|
||||
format!("for {} = {}", element_name.0, element),
|
||||
Some(calling_scope),
|
||||
calling_scope,
|
||||
hashmap! {
|
||||
element_name.clone().into() => SimplExpr::Literal(DynVal(element.0, elements_expr_span))
|
||||
},
|
||||
)?;
|
||||
let new_child_widget =
|
||||
build_gtk_widget(tree, widget_defs.clone(), scope, body.clone(), custom_widget_invocation.clone())?;
|
||||
gtk_container.add(&new_child_widget);
|
||||
created_children.push(new_child_widget);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Handle an invocation of the special `children` [`WidgetUse`].
|
||||
/// This widget expands to multiple other widgets, thus we require the `gtk_container` we should expand the widgets into.
|
||||
/// The `custom_widget_invocation` will be used here to evaluate the provided children in their
|
||||
|
@ -187,13 +278,12 @@ fn build_children_special_widget(
|
|||
tree: &mut ScopeGraph,
|
||||
widget_defs: Rc<HashMap<String, WidgetDefinition>>,
|
||||
calling_scope: ScopeIndex,
|
||||
mut widget_use: WidgetUse,
|
||||
widget_use: ChildrenWidgetUse,
|
||||
gtk_container: >k::Container,
|
||||
custom_widget_invocation: Rc<CustomWidgetInvocation>,
|
||||
) -> Result<()> {
|
||||
assert_eq!(&widget_use.name, "children");
|
||||
|
||||
if let Some(nth) = widget_use.attrs.ast_optional::<SimplExpr>("nth")? {
|
||||
if let Some(nth) = widget_use.nth_expr {
|
||||
// TODORW this might not be necessary, if I can keep a copy of the widget I can destroy it directly, no need to go through the container.
|
||||
// This should be a custom gtk::Bin subclass,..
|
||||
let child_container = gtk::Box::new(Orientation::Horizontal, 0);
|
||||
child_container.set_homogeneous(true);
|
||||
|
@ -250,7 +340,7 @@ pub struct CustomWidgetInvocation {
|
|||
}
|
||||
|
||||
/// Make sure that [`gtk::Bin`] widgets only get a single child.
|
||||
fn validate_container_children_count(container: >k::Container, widget_use: &WidgetUse) -> Result<(), DiagError> {
|
||||
fn validate_container_children_count(container: >k::Container, widget_use: &BasicWidgetUse) -> Result<(), DiagError> {
|
||||
if container.dynamic_cast_ref::<gtk::Bin>().is_some() && widget_use.children.len() > 1 {
|
||||
Err(DiagError::new(gen_diagnostic! {
|
||||
kind = Severity::Error,
|
||||
|
|
|
@ -185,6 +185,7 @@ impl DynVal {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO this should return Result<Vec<DynVal>> and use json parsing
|
||||
pub fn as_vec(&self) -> Result<Vec<String>> {
|
||||
if self.0.is_empty() {
|
||||
Ok(Vec::new())
|
||||
|
|
|
@ -7,7 +7,11 @@ use crate::{
|
|||
parser::{ast::Ast, ast_iterator::AstIterator, from_ast::FromAst},
|
||||
};
|
||||
|
||||
use super::{widget_definition::WidgetDefinition, widget_use::WidgetUse, Config};
|
||||
use super::{
|
||||
widget_definition::WidgetDefinition,
|
||||
widget_use::{BasicWidgetUse, WidgetUse},
|
||||
Config,
|
||||
};
|
||||
use eww_shared_util::{AttrName, Span, Spanned, VarName};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
@ -71,37 +75,52 @@ pub fn validate_variables_in_widget_use(
|
|||
widget: &WidgetUse,
|
||||
is_in_definition: bool,
|
||||
) -> 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| !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.name.clone(),
|
||||
arg_list_span: Some(matching_def.args_span),
|
||||
use_span: widget.attrs.span,
|
||||
});
|
||||
if let WidgetUse::Basic(widget) = widget {
|
||||
let matching_definition = defs.get(&widget.name);
|
||||
if let Some(matching_def) = matching_definition {
|
||||
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.name.clone(),
|
||||
arg_list_span: Some(matching_def.args_span),
|
||||
use_span: widget.attrs.span,
|
||||
});
|
||||
}
|
||||
}
|
||||
let values = widget.attrs.attrs.values();
|
||||
let unknown_var = values.filter_map(|value| value.value.as_simplexpr().ok()).find_map(|expr: SimplExpr| {
|
||||
let span = expr.span();
|
||||
expr.var_refs_with_span()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|(span, var_ref)| (span, var_ref.clone()))
|
||||
.find(|(_, var_ref)| !variables.contains(var_ref))
|
||||
});
|
||||
if let Some((span, var)) = unknown_var {
|
||||
return Err(ValidationError::UnknownVariable { span, name: var, in_definition: is_in_definition });
|
||||
}
|
||||
}
|
||||
|
||||
let values = widget.attrs.attrs.values();
|
||||
let unknown_var = values.filter_map(|value| value.value.as_simplexpr().ok()).find_map(|expr: SimplExpr| {
|
||||
let span = expr.span();
|
||||
expr.var_refs_with_span()
|
||||
for child in widget.children.iter() {
|
||||
let _ = validate_variables_in_widget_use(defs, variables, child, is_in_definition)?;
|
||||
}
|
||||
} else if let WidgetUse::Loop(widget) = widget {
|
||||
let unknown_var = widget
|
||||
.elements_expr
|
||||
.var_refs_with_span()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|(span, var_ref)| (span, var_ref.clone()))
|
||||
.find(|(_, var_ref)| !variables.contains(var_ref))
|
||||
});
|
||||
if let Some((span, var)) = unknown_var {
|
||||
return Err(ValidationError::UnknownVariable { span, name: var, in_definition: is_in_definition });
|
||||
}
|
||||
|
||||
for child in widget.children.iter() {
|
||||
let _ = validate_variables_in_widget_use(defs, variables, child, is_in_definition)?;
|
||||
.find(|(_, var_ref)| var_ref != &widget.element_name && !variables.contains(var_ref));
|
||||
if let Some((span, var)) = unknown_var {
|
||||
return Err(ValidationError::UnknownVariable { span, name: var, in_definition: is_in_definition });
|
||||
}
|
||||
let mut variables = variables.clone();
|
||||
variables.insert(widget.element_name.clone());
|
||||
let _ = validate_variables_in_widget_use(defs, &variables, &widget.body, is_in_definition)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -4,15 +4,41 @@ use simplexpr::SimplExpr;
|
|||
|
||||
use crate::{
|
||||
config::attributes::AttrEntry,
|
||||
error::{AstError, AstResult},
|
||||
parser::{ast::Ast, ast_iterator::AstIterator, from_ast::FromAst},
|
||||
error::{AstError, AstResult, AstResultExt, FormFormatError},
|
||||
parser::{
|
||||
ast::Ast,
|
||||
ast_iterator::AstIterator,
|
||||
from_ast::{FromAst, FromAstElementContent},
|
||||
},
|
||||
};
|
||||
use eww_shared_util::{AttrName, Span, Spanned, VarName};
|
||||
|
||||
use super::attributes::Attributes;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)]
|
||||
pub struct WidgetUse {
|
||||
pub enum WidgetUse {
|
||||
Basic(BasicWidgetUse),
|
||||
Loop(LoopWidgetUse),
|
||||
Children(ChildrenWidgetUse),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)]
|
||||
pub struct LoopWidgetUse {
|
||||
pub element_name: VarName,
|
||||
pub elements_expr: SimplExpr,
|
||||
pub elements_expr_span: Span,
|
||||
pub body: Box<WidgetUse>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)]
|
||||
pub struct ChildrenWidgetUse {
|
||||
pub span: Span,
|
||||
pub nth_expr: Option<SimplExpr>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)]
|
||||
pub struct BasicWidgetUse {
|
||||
pub name: String,
|
||||
pub attrs: Attributes,
|
||||
pub children: Vec<WidgetUse>,
|
||||
|
@ -20,39 +46,79 @@ pub struct WidgetUse {
|
|||
pub name_span: Span,
|
||||
}
|
||||
|
||||
impl WidgetUse {
|
||||
impl BasicWidgetUse {
|
||||
pub fn children_span(&self) -> Span {
|
||||
if self.children.is_empty() {
|
||||
self.span.point_span_at_end().shifted(-1)
|
||||
} else {
|
||||
self.children.first().unwrap().span.to(self.children.last().unwrap().span)
|
||||
self.children.first().unwrap().span().to(self.children.last().unwrap().span())
|
||||
}
|
||||
}
|
||||
|
||||
fn from_iter<I: Iterator<Item = Ast>>(
|
||||
span: Span,
|
||||
name: String,
|
||||
name_span: Span,
|
||||
mut iter: AstIterator<I>,
|
||||
) -> AstResult<Self> {
|
||||
let attrs = iter.expect_key_values()?;
|
||||
let children = iter.map(WidgetUse::from_ast).collect::<AstResult<Vec<_>>>()?;
|
||||
Ok(Self { name, attrs, children, span, name_span })
|
||||
}
|
||||
}
|
||||
|
||||
impl FromAstElementContent for LoopWidgetUse {
|
||||
const ELEMENT_NAME: &'static str = "for";
|
||||
|
||||
fn from_tail<I: Iterator<Item = Ast>>(span: Span, mut iter: AstIterator<I>) -> AstResult<Self> {
|
||||
let (element_name_span, element_name) = iter.expect_symbol()?;
|
||||
let (in_string_span, in_string) = iter.expect_symbol()?;
|
||||
if in_string != "in" {
|
||||
return Err(AstError::FormFormatError(FormFormatError::ExpectedInInForLoop(in_string_span, in_string)));
|
||||
}
|
||||
let (elements_span, elements_expr) = iter.expect_simplexpr()?;
|
||||
let body = iter.expect_any().note("Expected a loop body").and_then(WidgetUse::from_ast)?;
|
||||
iter.expect_done()?;
|
||||
Ok(Self {
|
||||
element_name: VarName(element_name),
|
||||
elements_expr,
|
||||
body: Box::new(body),
|
||||
span,
|
||||
elements_expr_span: elements_span,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FromAstElementContent for ChildrenWidgetUse {
|
||||
const ELEMENT_NAME: &'static str = "children";
|
||||
|
||||
fn from_tail<I: Iterator<Item = Ast>>(span: Span, mut iter: AstIterator<I>) -> AstResult<Self> {
|
||||
let mut attrs = iter.expect_key_values()?;
|
||||
let nth_expr = attrs.ast_optional("nth")?;
|
||||
iter.expect_done()?;
|
||||
Ok(Self { span, nth_expr })
|
||||
}
|
||||
}
|
||||
|
||||
impl FromAst for WidgetUse {
|
||||
fn from_ast(e: Ast) -> AstResult<Self> {
|
||||
let span = e.span();
|
||||
if let Ok(value) = e.clone().as_simplexpr() {
|
||||
Ok(label_from_simplexpr(value, span))
|
||||
Ok(WidgetUse::Basic(label_from_simplexpr(value, span)))
|
||||
} else {
|
||||
let mut iter = e.try_ast_iter()?;
|
||||
let (name_span, name) = iter.expect_symbol()?;
|
||||
let attrs = iter.expect_key_values()?;
|
||||
let children = iter.map(WidgetUse::from_ast).collect::<AstResult<Vec<_>>>()?;
|
||||
Ok(Self { name, attrs, children, span, name_span })
|
||||
match name.as_ref() {
|
||||
LoopWidgetUse::ELEMENT_NAME => Ok(WidgetUse::Loop(LoopWidgetUse::from_tail(span, iter)?)),
|
||||
ChildrenWidgetUse::ELEMENT_NAME => Ok(WidgetUse::Children(ChildrenWidgetUse::from_tail(span, iter)?)),
|
||||
_ => Ok(WidgetUse::Basic(BasicWidgetUse::from_iter(span, name, name_span, iter)?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Spanned for WidgetUse {
|
||||
fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
}
|
||||
|
||||
fn label_from_simplexpr(value: SimplExpr, span: Span) -> WidgetUse {
|
||||
WidgetUse {
|
||||
fn label_from_simplexpr(value: SimplExpr, span: Span) -> BasicWidgetUse {
|
||||
BasicWidgetUse {
|
||||
name: "label".to_string(),
|
||||
name_span: span.point_span(),
|
||||
attrs: Attributes::new(
|
||||
|
@ -68,3 +134,15 @@ fn label_from_simplexpr(value: SimplExpr, span: Span) -> WidgetUse {
|
|||
span,
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_spanned {
|
||||
($($super:ident => $name:ident),*) => {
|
||||
$(impl Spanned for $name { fn span(&self) -> Span { self.span } })*
|
||||
impl Spanned for WidgetUse {
|
||||
fn span(&self) -> Span {
|
||||
match self { $(WidgetUse::$super(widget) => widget.span),* }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl_spanned!(Basic => BasicWidgetUse, Loop => LoopWidgetUse, Children => ChildrenWidgetUse);
|
||||
|
|
|
@ -170,13 +170,17 @@ pub enum FormFormatError {
|
|||
|
||||
#[error("Widget definition has more than one child widget")]
|
||||
WidgetDefMultipleChildren(Span),
|
||||
|
||||
#[error("Expected 'in' in this position, but got '{}'", .1)]
|
||||
ExpectedInInForLoop(Span, String),
|
||||
}
|
||||
|
||||
impl Spanned for FormFormatError {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
FormFormatError::WidgetDefArglistMissing(span) => *span,
|
||||
FormFormatError::WidgetDefMultipleChildren(span) => *span,
|
||||
FormFormatError::WidgetDefArglistMissing(span)
|
||||
| FormFormatError::WidgetDefMultipleChildren(span)
|
||||
| FormFormatError::ExpectedInInForLoop(span, _) => *span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -298,6 +298,10 @@ impl ToDiagnostic for FormFormatError {
|
|||
To include multiple elements, wrap these elements in a single container widget such as `box`.\n\
|
||||
This is necessary as eww can't know how you want these elements to be layed out otherwise."
|
||||
},
|
||||
FormFormatError::ExpectedInInForLoop(span, got) => gen_diagnostic! {
|
||||
msg = self,
|
||||
label = span,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,9 +40,9 @@ macro_rules! return_or_put_back {
|
|||
|
||||
impl<I: Iterator<Item = Ast>> AstIterator<I> {
|
||||
return_or_put_back! {
|
||||
fn expect_symbol -> AstType::Symbol, (Span, String) = Ast::Symbol(span, x) => (span, x)
|
||||
fn expect_list -> AstType::List, (Span, Vec<Ast>) = Ast::List(span, x) => (span, x)
|
||||
fn expect_array -> AstType::Array, (Span, Vec<Ast>) = Ast::Array(span, x) => (span, x)
|
||||
fn expect_symbol -> AstType::Symbol, (Span, String) = Ast::Symbol(span, x) => (span, x)
|
||||
fn expect_list -> AstType::List, (Span, Vec<Ast>) = Ast::List(span, x) => (span, x)
|
||||
fn expect_array -> AstType::Array, (Span, Vec<Ast>) = Ast::Array(span, x) => (span, x)
|
||||
}
|
||||
|
||||
pub fn expect_literal(&mut self) -> AstResult<(Span, DynVal)> {
|
||||
|
@ -67,6 +67,20 @@ impl<I: Iterator<Item = Ast>> AstIterator<I> {
|
|||
self.next().or_missing(self.remaining_span.point_span())
|
||||
}
|
||||
|
||||
pub fn expect_simplexpr(&mut self) -> AstResult<(Span, SimplExpr)> {
|
||||
let expr_type = AstType::SimplExpr;
|
||||
match self.expect_any()? {
|
||||
Ast::SimplExpr(span, expr) => Ok((span, expr)),
|
||||
Ast::Symbol(span, var) => Ok((span, SimplExpr::VarRef(span, VarName(var)))),
|
||||
other => {
|
||||
let span = other.span();
|
||||
let actual_type = other.expr_type();
|
||||
self.put_back(other);
|
||||
Err(AstError::WrongExprType(span, expr_type, actual_type))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_done(&mut self) -> AstResult<()> {
|
||||
if let Some(next) = self.next() {
|
||||
self.put_back(next);
|
||||
|
|
|
@ -239,7 +239,7 @@ For this, you can make use of one of eww's most powerful features: the `literal`
|
|||
(defvar variable_containing_yuck
|
||||
"(box (button 'foo') (button 'bar'))")
|
||||
|
||||
; then, inside your widget, use:
|
||||
; Then, inside your widget, use:
|
||||
(literal :content variable_containing_yuck)
|
||||
```
|
||||
|
||||
|
@ -248,6 +248,22 @@ Eww then reads the provided value and renders the resulting widget. Whenever it
|
|||
|
||||
Note that this is not all that efficient. Make sure to only use `literal` when necessary!
|
||||
|
||||
## Generating a list of widgets from JSON using `for`
|
||||
|
||||
If you want to display a list of values, you can use the `for`-Element to fill a container with a list of elements generated from a JSON-array.
|
||||
```lisp
|
||||
(defvar my-json "[1, 2, 3]")
|
||||
|
||||
; Then, inside your widget, you can use
|
||||
(box
|
||||
(for entry in my-json
|
||||
(button :onclick "notify-send 'click' 'button ${entry}'"
|
||||
entry)))
|
||||
```
|
||||
|
||||
This can be useful in many situations, for example when generating a workspace list from a JSON representation of your workspaces.
|
||||
In many cases, this can be used instead of `literal`, and should most likely be preferred in those cases.
|
||||
|
||||
## Splitting up your configuration
|
||||
|
||||
As time passes, your configuration might grow larger and larger. Luckily, you can easily split up your configuration into multiple files!
|
||||
|
|
Loading…
Add table
Reference in a new issue