From 24c9fee204e976cbd217955af8ee9eb2e52c7457 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sat, 31 Jul 2021 13:44:23 +0200 Subject: [PATCH] Fully implement string interpolation with ${} syntax --- Cargo.lock | 1 + crates/eww/src/error_handling_ctx.rs | 4 +- crates/eww_shared_util/Cargo.toml | 4 + crates/eww_shared_util/src/lib.rs | 33 ++++ crates/simplexpr/src/eval.rs | 20 ++- crates/simplexpr/src/lib.rs | 1 + crates/simplexpr/src/parser/lexer.rs | 93 ++++++----- ...simplexpr__parser__lexer__test__basic.snap | 7 + ...plexpr__parser__lexer__test__comments.snap | 6 + ...plexpr__parser__lexer__test__escaping.snap | 6 + ..._parser__lexer__test__interpolation_1.snap | 6 + ...er__lexer__test__interpolation_nested.snap | 6 + ...xpr__parser__lexer__test__symbol_spam.snap | 17 ++ ...r__lexer__test__weird_char_boundaries.snap | 8 + .../snapshots/yuck__config__test__config.snap | 6 +- crates/yuck/src/error.rs | 4 + crates/yuck/src/format_diagnostic.rs | 9 +- crates/yuck/src/parser/ast_iterator.rs | 19 ++- crates/yuck/src/parser/lexer.rs | 148 +++++++++++------- crates/yuck/src/parser/parser.lalrpop | 18 +-- .../yuck__parser__lexer__test__basic.snap | 6 + ...k__parser__lexer__test__char_boundary.snap | 6 + ...k__parser__lexer__test__escaped_quote.snap | 6 + ..._parser__lexer__test__escaped_strings.snap | 6 + ...parser__lexer__test__quotes_in_quotes.snap | 6 + .../yuck__parser__lexer__yuck_lexer-2.snap | 42 ++++- .../yuck__parser__lexer__yuck_lexer-3.snap | 20 ++- .../yuck__parser__lexer__yuck_lexer-4.snap | 32 +++- .../yuck__parser__lexer__yuck_lexer-5.snap | 16 -- .../yuck__parser__lexer__yuck_lexer.snap | 20 ++- .../snapshots/yuck__parser__test-11.snap | 2 +- .../snapshots/yuck__parser__test-12.snap | 2 +- .../snapshots/yuck__parser__test-13.snap | 2 +- .../snapshots/yuck__parser__test-17.snap | 2 +- examples/eww-bar/eww.yuck | 17 +- examples/eww-bar/scripts/getvol | 2 +- 36 files changed, 440 insertions(+), 163 deletions(-) create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__basic.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__comments.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__escaping.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__interpolation_1.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__interpolation_nested.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__symbol_spam.snap create mode 100644 crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__weird_char_boundaries.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__basic.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__char_boundary.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__escaped_quote.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__escaped_strings.snap create mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__quotes_in_quotes.snap delete mode 100644 crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-5.snap diff --git a/Cargo.lock b/Cargo.lock index d1597d0..cca7641 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -482,6 +482,7 @@ name = "eww_shared_util" version = "0.1.0" dependencies = [ "derive_more", + "insta", "serde", ] diff --git a/crates/eww/src/error_handling_ctx.rs b/crates/eww/src/error_handling_ctx.rs index 0d6681d..0611e51 100644 --- a/crates/eww/src/error_handling_ctx.rs +++ b/crates/eww/src/error_handling_ctx.rs @@ -6,7 +6,7 @@ use codespan_reporting::{ }; use eww_shared_util::Span; use once_cell::sync::Lazy; -use simplexpr::eval::EvalError; +use simplexpr::{dynval::ConversionError, eval::EvalError}; use yuck::{config::file_provider::YuckFiles, error::AstError, format_diagnostic::ToDiagnostic, gen_diagnostic}; use crate::error::DiagError; @@ -22,6 +22,8 @@ pub fn anyhow_err_to_diagnostic(err: &anyhow::Error) -> Diagnostic { err.diag.clone() } else if let Some(err) = err.downcast_ref::() { err.to_diagnostic() + } else if let Some(err) = err.downcast_ref::() { + err.to_diagnostic() } else if let Some(err) = err.downcast_ref::() { err.to_diagnostic() } else { diff --git a/crates/eww_shared_util/Cargo.toml b/crates/eww_shared_util/Cargo.toml index 3293833..fdcb937 100644 --- a/crates/eww_shared_util/Cargo.toml +++ b/crates/eww_shared_util/Cargo.toml @@ -6,3 +6,7 @@ edition = "2018" [dependencies] serde = {version = "1.0", features = ["derive"]} derive_more = "0.99" + + +[dev-dependencies] +insta = "1.7" diff --git a/crates/eww_shared_util/src/lib.rs b/crates/eww_shared_util/src/lib.rs index de590ff..299f994 100644 --- a/crates/eww_shared_util/src/lib.rs +++ b/crates/eww_shared_util/src/lib.rs @@ -3,3 +3,36 @@ pub mod wrappers; pub use span::*; pub use wrappers::*; + +#[macro_export] +macro_rules! snapshot_debug { + ( $($name:ident => $test:expr),* $(,)?) => { + $( + #[test] + fn $name() { ::insta::assert_debug_snapshot!($test); } + )* + }; +} +#[macro_export] +macro_rules! snapshot_string { + ( $($name:ident => $test:expr),* $(,)?) => { + $( + #[test] + fn $name() { ::insta::assert_snapshot!($test); } + )* + }; +} + +#[macro_export] +macro_rules! snapshot_ron { + ( $($name:ident => $test:expr),* $(,)?) => { + $( + #[test] + fn $name() { + ::insta::with_settings!({sort_maps => true}, { + ::insta::assert_ron_snapshot!($test); + }); + } + )* + }; +} diff --git a/crates/simplexpr/src/eval.rs b/crates/simplexpr/src/eval.rs index 227fa6f..e703797 100644 --- a/crates/simplexpr/src/eval.rs +++ b/crates/simplexpr/src/eval.rs @@ -15,9 +15,13 @@ pub enum EvalError { #[error("Invalid regex: {0}")] InvalidRegex(#[from] regex::Error), + // TODO unresolved and unknown are the same for the user,.... #[error("got unresolved variable `{0}`")] UnresolvedVariable(VarName), + #[error("Unknown variable {0}")] + UnknownVariable(VarName), + #[error(transparent)] ConversionError(#[from] ConversionError), @@ -27,9 +31,6 @@ pub enum EvalError { #[error("Unknown function {0}")] UnknownFunction(String), - #[error("Unknown variable {0}")] - UnknownVariable(VarName), - #[error("Unable to index into value {0}")] CannotIndex(String), @@ -41,6 +42,13 @@ impl EvalError { pub fn at(self, span: Span) -> Self { Self::Spanned(span, Box::new(self)) } + + pub fn map_in_span(self, f: impl FnOnce(Self) -> Self) -> Self { + match self { + EvalError::Spanned(span, err) => EvalError::Spanned(span, Box::new(err.map_in_span(f))), + other => f(other), + } + } } impl Spanned for EvalError { @@ -145,8 +153,10 @@ impl SimplExpr { pub fn eval_no_vars(&self) -> Result { match self.eval(&HashMap::new()) { Ok(x) => Ok(x), - Err(EvalError::UnknownVariable(name)) => Err(EvalError::NoVariablesAllowed(name)), - Err(x) => Err(x), + Err(x) => Err(x.map_in_span(|err| match err { + EvalError::UnknownVariable(name) | EvalError::UnresolvedVariable(name) => EvalError::NoVariablesAllowed(name), + other => other, + })), } } diff --git a/crates/simplexpr/src/lib.rs b/crates/simplexpr/src/lib.rs index b4eefc1..23de86b 100644 --- a/crates/simplexpr/src/lib.rs +++ b/crates/simplexpr/src/lib.rs @@ -1,4 +1,5 @@ #![feature(box_patterns)] +#![feature(pattern)] #![feature(box_syntax)] #![feature(try_blocks)] #![feature(unwrap_infallible)] diff --git a/crates/simplexpr/src/parser/lexer.rs b/crates/simplexpr/src/parser/lexer.rs index f382b65..01b890f 100644 --- a/crates/simplexpr/src/parser/lexer.rs +++ b/crates/simplexpr/src/parser/lexer.rs @@ -1,3 +1,5 @@ +use std::str::pattern::Pattern; + use eww_shared_util::{Span, Spanned}; use once_cell::sync::Lazy; use regex::{escape, Regex, RegexSet}; @@ -63,9 +65,10 @@ macro_rules! regex_rules { } static ESCAPE_REPLACE_REGEX: Lazy = Lazy::new(|| Regex::new(r"\\(.)").unwrap()); +pub static STR_INTERPOLATION_START: &str = "${"; +pub static STR_INTERPOLATION_END: &str = "}"; regex_rules! { - escape(r"+") => |_| Token::Plus, escape(r"-") => |_| Token::Minus, escape(r"*") => |_| Token::Times, @@ -100,6 +103,7 @@ regex_rules! { r"[+-]?(?:[0-9]+[.])?[0-9]+" => |x| Token::NumLit(x.to_string()) } +#[derive(Debug)] pub struct Lexer<'s> { file_id: usize, source: &'s str, @@ -117,6 +121,10 @@ impl<'s> Lexer<'s> { &self.source[self.pos..] } + pub fn continues_with(&self, pat: impl Pattern<'s>) -> bool { + self.remaining().starts_with(pat) + } + pub fn next_token(&mut self) -> Option, LexicalError>> { loop { if self.failed || self.pos >= self.source.len() { @@ -125,9 +133,7 @@ impl<'s> Lexer<'s> { let remaining = self.remaining(); if remaining.starts_with(&['"', '\'', '`'][..]) { - return self - .string_lit() - .map(|x| x.map(|(lo, segs, hi)| (lo + self.offset, Token::StringLit(segs), hi + self.offset))); + return self.string_lit().map(|x| x.map(|(lo, segs, hi)| (lo, Token::StringLit(segs), hi))); } else { let match_set = LEXER_REGEX_SET.matches(remaining); let matched_token = match_set @@ -148,7 +154,7 @@ impl<'s> Lexer<'s> { let tok_str = &self.source[self.pos..self.pos + len]; let old_pos = self.pos; - self.pos += len; + self.advance_by(len); match LEXER_FNS[i](tok_str.to_string()) { Token::Skip | Token::Comment => {} token => { @@ -159,54 +165,56 @@ impl<'s> Lexer<'s> { } } - fn advance_until_char_boundary(&mut self) { + fn advance_by(&mut self, n: usize) { + self.pos += n; while self.pos < self.source.len() && !self.source.is_char_boundary(self.pos) { self.pos += 1; } } - fn advance_until_one_of(&mut self, pat: &[char]) -> Option { - for (idx, cur) in self.remaining().char_indices() { - if let Some(matched) = pat.iter().find(|p| **p == cur) { - self.pos += idx + 1; - return Some(*matched); + fn advance_until_one_of<'a>(&mut self, pat: &[&'a str]) -> Option<&'a str> { + loop { + let remaining = self.remaining(); + if remaining.is_empty() { + return None; + } else if let Some(matched) = pat.iter().find(|&&p| remaining.starts_with(p)) { + self.advance_by(matched.len()); + return Some(matched); + } else { + self.advance_by(1); } } - self.pos = self.source.len(); - return None; } - fn advance_until_unescaped_one_of(&mut self, pat: &[char]) -> Option { + fn advance_until_unescaped_one_of<'a>(&mut self, pat: &[&'a str]) -> Option<&'a str> { let mut pattern = pat.to_vec(); - pattern.push('\\'); + pattern.push("\\"); match self.advance_until_one_of(pattern.as_slice()) { - Some('\\') => { - self.pos += 1; - self.advance_until_char_boundary(); + Some("\\") => { + self.advance_by(1); self.advance_until_unescaped_one_of(pat) } result => result, } } - fn string_lit(&mut self) -> Option>>, LexicalError>> { - let quote = self.remaining().chars().next().unwrap(); + pub fn string_lit(&mut self) -> Option>>, LexicalError>> { + let quote = self.remaining().chars().next()?.to_string(); let str_lit_start = self.pos; - self.pos += 1; - self.advance_until_char_boundary(); + self.advance_by(quote.len()); let mut elements = Vec::new(); let mut in_string_lit = true; loop { if in_string_lit { - let segment_start = self.pos - 1; + let segment_start = self.pos - quote.len(); - let segment_ender = self.advance_until_unescaped_one_of(&['{', quote][..])?; - let lit_content = &self.source[segment_start + 1..self.pos - 1]; + let segment_ender = self.advance_until_unescaped_one_of(&[STR_INTERPOLATION_START, "e])?; + let lit_content = &self.source[segment_start + quote.len()..self.pos - segment_ender.len()]; let lit_content = ESCAPE_REPLACE_REGEX.replace_all(lit_content, "$1").to_string(); elements.push((segment_start + self.offset, StrLitSegment::Literal(lit_content), self.pos + self.offset)); - if segment_ender == '{' { + if segment_ender == STR_INTERPOLATION_START { in_string_lit = false; } else if segment_ender == quote { return Some(Ok((str_lit_start + self.offset, elements, self.pos + self.offset))); @@ -214,14 +222,14 @@ impl<'s> Lexer<'s> { } else { let segment_start = self.pos; let mut toks = Vec::new(); - while self.pos < self.source.len() && !self.remaining().starts_with('}') { + while self.pos < self.source.len() && !self.remaining().starts_with(STR_INTERPOLATION_END) { match self.next_token()? { Ok(tok) => toks.push(tok), Err(err) => return Some(Err(err)), } } elements.push((segment_start + self.offset, StrLitSegment::Interp(toks), self.pos + self.offset)); - self.pos += 1; + self.advance_by(STR_INTERPOLATION_END.len()); in_string_lit = true; } } @@ -254,20 +262,27 @@ impl std::fmt::Display for LexicalError { #[cfg(test)] mod test { use super::*; + use eww_shared_util::snapshot_string; use itertools::Itertools; - #[test] - fn test_simplexpr_lexer_basic() { - insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"bar "foo""#).collect_vec()); + macro_rules! v { + ($x:literal) => { + Lexer::new(0, 0, $x) + .map(|x| match x { + Ok((l, x, r)) => format!("({}, {:?}, {})", l, x, r), + Err(err) => format!("{}", err), + }) + .join("\n") + }; } - #[test] - fn test_simplexpr_lexer_str_interpolate() { - insta::assert_debug_snapshot!(Lexer::new(0, 0, r#" "foo {2 * 2} bar" "#).collect_vec()); - insta::assert_debug_snapshot!(Lexer::new(0, 0, r#" "foo {(2 * 2) + "{5 + 5}"} bar" "#).collect_vec()); - insta::assert_debug_snapshot!(Lexer::new(0, 0, r#" "a\"b\{}" "#).collect_vec()); + snapshot_string! { + basic => v!(r#"bar "foo""#), + interpolation_1 => v!(r#" "foo ${2 * 2} bar" "#), + interpolation_nested => v!(r#" "foo ${(2 * 2) + "${5 + 5}"} bar" "#), + escaping => v!(r#" "a\"b\{}" "#), + comments => v!("foo ; bar"), + weird_char_boundaries => v!(r#"" " + music"#), + symbol_spam => v!(r#"(foo + - "()" "a\"b" true false [] 12.2)"#), } - // insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"(foo + - "()" "a\"b" true false [] 12.2)"#).collect_vec()); - // insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"" " + music"#).collect_vec()); - // insta::assert_debug_snapshot!(Lexer::new(0, 0, r#"foo ; bar"#).collect_vec()); } diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__basic.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__basic.snap new file mode 100644 index 0000000..a7382f1 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__basic.snap @@ -0,0 +1,7 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "v!(r#\"bar \"foo\"\"#)" + +--- +(0, Ident("bar"), 3) +(4, StringLit([(4, Literal("foo"), 9)]), 9) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__comments.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__comments.snap new file mode 100644 index 0000000..281e375 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__comments.snap @@ -0,0 +1,6 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "v!(\"foo ; bar\")" + +--- +(0, Ident("foo"), 3) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__escaping.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__escaping.snap new file mode 100644 index 0000000..e0d89c0 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__escaping.snap @@ -0,0 +1,6 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "v!(r#\" \"a\\\"b\\{}\" \"#)" + +--- +(1, StringLit([(1, Literal("a\"b{}"), 10)]), 10) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__interpolation_1.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__interpolation_1.snap new file mode 100644 index 0000000..8c5b37b --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__interpolation_1.snap @@ -0,0 +1,6 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "v!(r#\" \"foo ${2 * 2} bar\" \"#)" + +--- +(1, StringLit([(1, Literal("foo "), 8), (8, Interp([(8, NumLit("2"), 9), (10, Times, 11), (12, NumLit("2"), 13)]), 13), (13, Literal(" bar"), 19)]), 19) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__interpolation_nested.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__interpolation_nested.snap new file mode 100644 index 0000000..dd53372 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__interpolation_nested.snap @@ -0,0 +1,6 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "v!(r#\" \"foo ${(2 * 2) + \"${5 + 5}\"} bar\" \"#)" + +--- +(1, StringLit([(1, Literal("foo "), 8), (8, Interp([(8, LPren, 9), (9, NumLit("2"), 10), (11, Times, 12), (13, NumLit("2"), 14), (14, RPren, 15), (16, Plus, 17), (18, StringLit([(18, Literal(""), 21), (21, Interp([(21, NumLit("5"), 22), (23, Plus, 24), (25, NumLit("5"), 26)]), 26), (26, Literal(""), 28)]), 28)]), 28), (28, Literal(" bar"), 34)]), 34) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__symbol_spam.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__symbol_spam.snap new file mode 100644 index 0000000..f44221e --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__symbol_spam.snap @@ -0,0 +1,17 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "v!(r#\"(foo + - \"()\" \"a\\\"b\" true false [] 12.2)\"#)" + +--- +(0, LPren, 1) +(1, Ident("foo"), 4) +(5, Plus, 6) +(7, Minus, 8) +(9, StringLit([(9, Literal("()"), 13)]), 13) +(14, StringLit([(14, Literal("a\"b"), 20)]), 20) +(21, True, 25) +(26, False, 31) +(32, LBrack, 33) +(33, RBrack, 34) +(35, NumLit("12.2"), 39) +(39, RPren, 40) diff --git a/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__weird_char_boundaries.snap b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__weird_char_boundaries.snap new file mode 100644 index 0000000..3ced076 --- /dev/null +++ b/crates/simplexpr/src/parser/snapshots/simplexpr__parser__lexer__test__weird_char_boundaries.snap @@ -0,0 +1,8 @@ +--- +source: crates/simplexpr/src/parser/lexer.rs +expression: "v!(r#\"\" \" + music\"#)" + +--- +(0, StringLit([(0, Literal("\u{f001} "), 8)]), 8) +(9, Plus, 10) +(11, Ident("music"), 16) diff --git a/crates/yuck/src/config/snapshots/yuck__config__test__config.snap b/crates/yuck/src/config/snapshots/yuck__config__test__config.snap index 059337b..ef3554d 100644 --- a/crates/yuck/src/config/snapshots/yuck__config__test__config.snap +++ b/crates/yuck/src/config/snapshots/yuck__config__test__config.snap @@ -18,7 +18,7 @@ Config( attrs: { AttrName("arg"): AttrEntry( key_span: Span(52, 56, 0), - value: Literal(Span(57, 61, 0), DynVal("hi", Span(18446744073709551615, 18446744073709551615, 18446744073709551615))), + value: SimplExpr(Span(57, 61, 0), Literal(Span(57, 61, 0), DynVal("hi", Span(57, 61, 0)))), ), }, ), @@ -56,7 +56,7 @@ Config( attrs: { AttrName("arg"): AttrEntry( key_span: Span(468, 472, 0), - value: Literal(Span(473, 478, 0), DynVal("bla", Span(18446744073709551615, 18446744073709551615, 18446744073709551615))), + value: SimplExpr(Span(473, 478, 0), Literal(Span(473, 478, 0), DynVal("bla", Span(473, 478, 0)))), ), }, ), @@ -79,7 +79,7 @@ Config( var_definitions: { VarName("some_var"): VarDefinition( name: VarName("some_var"), - initial_value: DynVal("bla", Span(18446744073709551615, 18446744073709551615, 18446744073709551615)), + initial_value: DynVal("bla", Span(89, 94, 0)), span: Span(72, 95, 0), ), }, diff --git a/crates/yuck/src/error.rs b/crates/yuck/src/error.rs index 22ea68e..9c22e2a 100644 --- a/crates/yuck/src/error.rs +++ b/crates/yuck/src/error.rs @@ -39,6 +39,9 @@ pub enum AstError { #[error("{1}")] ErrorNote(String, #[source] Box), + #[error(transparent)] + SimplExpr(#[from] simplexpr::error::Error), + #[error(transparent)] ConversionError(#[from] dynval::ConversionError), @@ -94,6 +97,7 @@ impl Spanned for AstError { AstError::ParseError { file_id, source } => get_parse_error_span(*file_id, source), AstError::ErrorNote(_, err) => err.span(), AstError::NoMoreElementsExpected(span) => *span, + AstError::SimplExpr(err) => err.span(), } } } diff --git a/crates/yuck/src/format_diagnostic.rs b/crates/yuck/src/format_diagnostic.rs index 1f7237f..3653082 100644 --- a/crates/yuck/src/format_diagnostic.rs +++ b/crates/yuck/src/format_diagnostic.rs @@ -118,6 +118,7 @@ impl ToDiagnostic for AstError { AstError::ErrorNote(note, source) => source.to_diagnostic().with_notes(vec![note.to_string()]), AstError::ValidationError(source) => source.to_diagnostic(), AstError::NoMoreElementsExpected(span) => gen_diagnostic!(self, span), + AstError::SimplExpr(source) => source.to_diagnostic(), } } } @@ -185,12 +186,13 @@ fn lalrpop_error_to_diagnostic( impl ToDiagnostic for simplexpr::error::Error { fn to_diagnostic(&self) -> Diagnostic { use simplexpr::error::Error::*; + dbg!(&self); match self { ParseError { source, file_id } => lalrpop_error_to_diagnostic(source, *file_id), ConversionError(error) => error.to_diagnostic(), Eval(error) => error.to_diagnostic(), Other(error) => gen_diagnostic!(error), - Spanned(span, error) => gen_diagnostic!(error, span), + Spanned(span, error) => error.to_diagnostic().with_label(span_to_primary_label(*span)), } } } @@ -205,7 +207,10 @@ impl ToDiagnostic for simplexpr::eval::EvalError { fn to_diagnostic(&self) -> Diagnostic { use simplexpr::eval::EvalError::*; match self { - UnresolvedVariable(name) | UnknownVariable(name) | NoVariablesAllowed(name) => gen_diagnostic! { + NoVariablesAllowed(name) => gen_diagnostic!(self), + // TODO the note here is confusing when it's an unknown variable being used _within_ a string literal / simplexpr + // it only really makes sense on top-level symbols + UnresolvedVariable(name) | UnknownVariable(name) => gen_diagnostic! { msg = self, note = format!("If you meant to use the literal value \"{}\", surround the value in quotes", name) }, diff --git a/crates/yuck/src/parser/ast_iterator.rs b/crates/yuck/src/parser/ast_iterator.rs index aebe312..63c61de 100644 --- a/crates/yuck/src/parser/ast_iterator.rs +++ b/crates/yuck/src/parser/ast_iterator.rs @@ -39,12 +39,29 @@ macro_rules! return_or_put_back { impl> AstIterator { return_or_put_back!(expect_symbol, AstType::Symbol, (Span, String) = Ast::Symbol(span, x) => (span, x)); - return_or_put_back!(expect_literal, AstType::Literal, (Span, DynVal) = Ast::Literal(span, x) => (span, x)); + // return_or_put_back!(expect_literal, AstType::Literal, (Span, DynVal) = Ast::Literal(span, x) => (span, x)); return_or_put_back!(expect_list, AstType::List, (Span, Vec) = Ast::List(span, x) => (span, x)); return_or_put_back!(expect_array, AstType::Array, (Span, Vec) = Ast::Array(span, x) => (span, x)); + pub fn expect_literal(&mut self) -> AstResult<(Span, DynVal)> { + match self.expect_any()? { + // Ast::List(_, _) => todo!(), + // Ast::Array(_, _) => todo!(), + // Ast::Keyword(_, _) => todo!(), + // Ast::Symbol(_, _) => todo!(), + // Ast::Literal(_, _) => todo!(), + Ast::SimplExpr(span, expr) => Ok((span, dbg!(expr.eval_no_vars()).map_err(|e| AstError::SimplExpr(e.into()))?)), + other => { + let span = other.span(); + let actual_type = other.expr_type(); + self.put_back(other); + Err(AstError::WrongExprType(span, AstType::Literal, actual_type)) + } + } + } + pub fn new(span: Span, iter: I) -> Self { AstIterator { remaining_span: span, iter: itertools::put_back(iter) } } diff --git a/crates/yuck/src/parser/lexer.rs b/crates/yuck/src/parser/lexer.rs index 3d5bf64..edfe6e5 100644 --- a/crates/yuck/src/parser/lexer.rs +++ b/crates/yuck/src/parser/lexer.rs @@ -1,8 +1,9 @@ use once_cell::sync::Lazy; -use regex::{Regex, RegexSet}; +use regex::{escape, Regex, RegexSet}; +use simplexpr::parser::lexer::{STR_INTERPOLATION_END, STR_INTERPOLATION_START}; use super::parse_error; -use eww_shared_util::{AttrName, Span, VarName}; +use eww_shared_util::{AttrName, Span, Spanned, VarName}; #[derive(Debug, PartialEq, Eq, Clone)] pub enum Token { @@ -12,11 +13,10 @@ pub enum Token { RBrack, True, False, - StrLit(String), NumLit(String), Symbol(String), Keyword(String), - SimplExpr(String), + SimplExpr(Vec<(usize, simplexpr::parser::lexer::Token, usize)>), Comment, Skip, } @@ -30,11 +30,10 @@ impl std::fmt::Display for Token { Token::RBrack => write!(f, "']'"), Token::True => write!(f, "true"), Token::False => write!(f, "false"), - Token::StrLit(x) => write!(f, "\"{}\"", x), Token::NumLit(x) => write!(f, "{}", x), Token::Symbol(x) => write!(f, "{}", x), Token::Keyword(x) => write!(f, "{}", x), - Token::SimplExpr(x) => write!(f, "{{{}}}", x), + Token::SimplExpr(x) => write!(f, "{{{:?}}}", x.iter().map(|x| &x.1)), Token::Comment => write!(f, ""), Token::Skip => write!(f, ""), } @@ -42,7 +41,7 @@ impl std::fmt::Display for Token { } macro_rules! regex_rules { - ($( $regex:literal => $token:expr),*) => { + ($( $regex:expr => $token:expr),*) => { static LEXER_REGEX_SET: Lazy = Lazy::new(|| { RegexSet::new(&[ $(format!("^{}", $regex)),* ]).unwrap()}); @@ -58,15 +57,12 @@ macro_rules! regex_rules { static ESCAPE_REPLACE_REGEX: Lazy = Lazy::new(|| Regex::new(r"\\(.)").unwrap()); regex_rules! { - r"\(" => |_| Token::LPren, - r"\)" => |_| Token::RPren, - r"\[" => |_| Token::LBrack, - r"\]" => |_| Token::RBrack, - r"true" => |_| Token::True, - r"false" => |_| Token::False, - r#""(?:[^"\\]|\\.)*""# => |x| Token::StrLit(ESCAPE_REPLACE_REGEX.replace_all(&x, "$1").to_string()), - r#"`(?:[^`\\]|\\.)*`"# => |x| Token::StrLit(ESCAPE_REPLACE_REGEX.replace_all(&x, "$1").to_string()), - r#"'(?:[^'\\]|\\.)*'"# => |x| Token::StrLit(ESCAPE_REPLACE_REGEX.replace_all(&x, "$1").to_string()), + escape("(") => |_| Token::LPren, + escape(")") => |_| Token::RPren, + escape("[") => |_| Token::LBrack, + escape("]") => |_| Token::RBrack, + escape("true") => |_| Token::True, + escape("false") => |_| Token::False, r#"[+-]?(?:[0-9]+[.])?[0-9]+"# => |x| Token::NumLit(x), r#":[^\s\)\]}]+"# => |x| Token::Keyword(x), r#"[a-zA-Z_!\?<>/\.\*-\+\-][^\s{}\(\)\[\](){}]*"# => |x| Token::Symbol(x), @@ -85,9 +81,53 @@ impl Lexer { pub fn new(file_id: usize, source: String) -> Self { Lexer { source, file_id, failed: false, pos: 0 } } -} -// TODO string literal interpolation stuff by looking for indexes of {{ and }}? + fn string_lit(&mut self) -> Option> { + let mut simplexpr_lexer = simplexpr::parser::lexer::Lexer::new(self.file_id, self.pos, &self.source[self.pos..]); + match simplexpr_lexer.string_lit() { + Some(Ok((lo, segments, hi))) => { + self.pos = hi; + self.advance_until_char_boundary(); + Some(Ok((lo, Token::SimplExpr(vec![(lo, simplexpr::parser::lexer::Token::StringLit(segments), hi)]), hi))) + } + Some(Err(e)) => Some(Err(parse_error::ParseError::LexicalError(e.0))), + None => None, + } + } + + fn simplexpr(&mut self) -> Option> { + self.pos += 1; + let mut simplexpr_lexer = simplexpr::parser::lexer::Lexer::new(self.file_id, self.pos, &self.source[self.pos..]); + let mut toks = Vec::new(); + let mut end = self.pos; + loop { + match simplexpr_lexer.next_token() { + Some(Ok((lo, tok, hi))) => { + end = hi; + toks.push((lo, tok, hi)); + } + Some(Err(err)) => { + dbg!(&simplexpr_lexer); + if simplexpr_lexer.continues_with('}') { + let start = toks.first().map(|x| x.0).unwrap_or(end); + self.pos = end + 1; + self.advance_until_char_boundary(); + return Some(Ok((start, Token::SimplExpr(toks), end))); + } else { + return Some(Err(parse_error::ParseError::LexicalError(err.span()))); + } + } + None => return None, + } + } + } + + fn advance_until_char_boundary(&mut self) { + while self.pos < self.source.len() && !self.source.is_char_boundary(self.pos) { + self.pos += 1; + } + } +} impl Iterator for Lexer { type Item = Result<(usize, Token, usize), parse_error::ParseError>; @@ -97,45 +137,17 @@ impl Iterator for Lexer { if self.failed || self.pos >= self.source.len() { return None; } - let string = &self.source[self.pos..]; - - if string.starts_with('{') { - let expr_start = self.pos; - let mut in_string = None; - loop { - if self.pos >= self.source.len() { - return None; - } - while !self.source.is_char_boundary(self.pos) { - self.pos += 1; - } - let string = &self.source[self.pos..]; - - if string.starts_with('}') && in_string.is_none() { - self.pos += 1; - let tok_str = &self.source[expr_start..self.pos]; - return Some(Ok((expr_start, Token::SimplExpr(tok_str.to_string()), self.pos - 1))); - } else if string.starts_with('"') || string.starts_with('\'') || string.starts_with('`') { - if let Some(quote) = in_string { - if string.starts_with(quote) { - in_string = None; - } - } else { - in_string = Some(string.chars().next().unwrap()); - } - self.pos += 1; - } else if string.starts_with("\\\"") { - self.pos += 2; - } else { - self.pos += 1; - } - } + let remaining = &self.source[self.pos..]; + if remaining.starts_with(&['"', '\'', '`'][..]) { + return self.string_lit(); + } else if remaining.starts_with('{') { + return self.simplexpr(); } else { - let match_set = LEXER_REGEX_SET.matches(string); + let match_set = LEXER_REGEX_SET.matches(remaining); let matched_token = match_set .into_iter() .map(|i: usize| { - let m = LEXER_REGEXES[i].find(string).unwrap(); + let m = LEXER_REGEXES[i].find(remaining).unwrap(); (m.end(), i) }) .min_by_key(|(_, x)| *x); @@ -163,12 +175,28 @@ impl Iterator for Lexer { } #[cfg(test)] -#[test] -fn test_yuck_lexer() { +mod test { + + use super::*; + use eww_shared_util::snapshot_string; use itertools::Itertools; - insta::assert_debug_snapshot!(Lexer::new(0, r#"(foo + - "text" )"#.to_string()).collect_vec()); - insta::assert_debug_snapshot!(Lexer::new(0, r#"{ bla "} \" }" " \" "}"#.to_string()).collect_vec()); - insta::assert_debug_snapshot!(Lexer::new(0, r#""< \" >""#.to_string()).collect_vec()); - insta::assert_debug_snapshot!(Lexer::new(0, r#"{ " " + music}"#.to_string()).collect_vec()); - insta::assert_debug_snapshot!(Lexer::new(0, r#"{ " } ' }" }"#.to_string()).collect_vec()); + + macro_rules! v { + ($x:literal) => { + Lexer::new(0, 0, $x) + .map(|x| match x { + Ok((l, x, r)) => format!("({}, {:?}, {})", l, x, r), + Err(err) => format!("{}", err), + }) + .join("\n") + }; + } + + snapshot_string! { + basic => r#"(foo + - "text" )"#, + escaped_strings => r#"{ bla "} \" }" " \" "}"#, + escaped_quote => r#""< \" >""#, + char_boundary => r#"{ " " + music}"#, + quotes_in_quotes => r#"{ " } ' }" }"#, + } } diff --git a/crates/yuck/src/parser/parser.lalrpop b/crates/yuck/src/parser/parser.lalrpop index a1d30d5..9f65b8f 100644 --- a/crates/yuck/src/parser/parser.lalrpop +++ b/crates/yuck/src/parser/parser.lalrpop @@ -18,11 +18,10 @@ extern { "]" => Token::RBrack, "true" => Token::True, "false" => Token::False, - "string" => Token::StrLit(), "number" => Token::NumLit(), "symbol" => Token::Symbol(), "keyword" => Token::Keyword(), - "simplexpr" => Token::SimplExpr(), + "simplexpr" => Token::SimplExpr(>), "comment" => Token::Comment, } } @@ -45,22 +44,17 @@ Keyword: Ast = => Ast::Keyword(Span(l, r, file_id), Symbol: Ast = => Ast::Symbol(Span(l, r, file_id), x.to_string()); Literal: String = { - => <>, => <>, => <>, }; -StrLit: String = { - => { - x[1..x.len() - 1].to_owned() - }, -}; - SimplExpr: SimplExpr = { =>? { - let expr = x[1..x.len() - 1].to_string(); - simplexpr::parse_string(l + 1, file_id, &expr).map_err(|e| { - ParseError::User { error: parse_error::ParseError::SimplExpr(e) }}) + let parser = simplexpr::simplexpr_parser::ExprParser::new(); + parser.parse(file_id, x.into_iter().map(Ok)) + .map_err(|e| ParseError::User { + error: parse_error::ParseError::SimplExpr(simplexpr::error::Error::from_parse_error(file_id, e)) + }) } } diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__basic.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__basic.snap new file mode 100644 index 0000000..e8d564a --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__basic.snap @@ -0,0 +1,6 @@ +--- +source: crates/yuck/src/parser/lexer.rs +expression: "r#\"(foo + - \"text\" )\"#" + +--- +(foo + - "text" ) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__char_boundary.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__char_boundary.snap new file mode 100644 index 0000000..708318b --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__char_boundary.snap @@ -0,0 +1,6 @@ +--- +source: crates/yuck/src/parser/lexer.rs +expression: "r#\"{ \" \" + music}\"#" + +--- +{ " " + music} diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__escaped_quote.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__escaped_quote.snap new file mode 100644 index 0000000..9ab4524 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__escaped_quote.snap @@ -0,0 +1,6 @@ +--- +source: crates/yuck/src/parser/lexer.rs +expression: "r#\"\"< \\\" >\"\"#" + +--- +"< \" >" diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__escaped_strings.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__escaped_strings.snap new file mode 100644 index 0000000..eb6ab16 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__escaped_strings.snap @@ -0,0 +1,6 @@ +--- +source: crates/yuck/src/parser/lexer.rs +expression: "r#\"{ bla \"} \\\" }\" \" \\\" \"}\"#" + +--- +{ bla "} \" }" " \" "} diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__quotes_in_quotes.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__quotes_in_quotes.snap new file mode 100644 index 0000000..26ead15 --- /dev/null +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__test__quotes_in_quotes.snap @@ -0,0 +1,6 @@ +--- +source: crates/yuck/src/parser/lexer.rs +expression: "r#\"{ \" } ' }\" }\"#" + +--- +{ " } ' }" } diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-2.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-2.snap index 52688dc..846e42a 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-2.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-2.snap @@ -6,9 +6,47 @@ expression: "Lexer::new(0, r#\"{ bla \"} \\\" }\" \" \\\" \"}\"#.to_string()).co [ Ok( ( - 0, + 2, SimplExpr( - "{ bla \"} \\\" }\" \" \\\" \"}", + [ + ( + 2, + Ident( + "bla", + ), + 5, + ), + ( + 6, + StringLit( + [ + ( + 6, + Literal( + "} \" }", + ), + 14, + ), + ], + ), + 14, + ), + ( + 15, + StringLit( + [ + ( + 15, + Literal( + " \" ", + ), + 21, + ), + ], + ), + 21, + ), + ], ), 21, ), diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-3.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-3.snap index 18607c4..cda3a96 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-3.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-3.snap @@ -7,8 +7,24 @@ expression: "Lexer::new(0, r#\"\"< \\\" >\"\"#.to_string()).collect_vec()" Ok( ( 0, - StrLit( - "\"< \" >\"", + SimplExpr( + [ + ( + 0, + StringLit( + [ + ( + 0, + Literal( + "< \" >", + ), + 8, + ), + ], + ), + 8, + ), + ], ), 8, ), diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-4.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-4.snap index 558fd17..462cc2a 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-4.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-4.snap @@ -6,9 +6,37 @@ expression: "Lexer::new(0, r#\"{ \" \" + music}\"#.to_string()).collect_vec [ Ok( ( - 0, + 2, SimplExpr( - "{ \"\u{f001} \" + music}", + [ + ( + 2, + StringLit( + [ + ( + 2, + Literal( + "\u{f001} ", + ), + 10, + ), + ], + ), + 10, + ), + ( + 11, + Plus, + 12, + ), + ( + 13, + Ident( + "music", + ), + 18, + ), + ], ), 18, ), diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-5.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-5.snap deleted file mode 100644 index 0e4ea1b..0000000 --- a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer-5.snap +++ /dev/null @@ -1,16 +0,0 @@ ---- -source: crates/yuck/src/parser/lexer.rs -expression: "Lexer::new(0, r#\"{ \" } ' }\" }\"#.to_string()).collect_vec()" - ---- -[ - Ok( - ( - 0, - SimplExpr( - "{ \" } ' }\" }", - ), - 11, - ), - ), -] diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer.snap b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer.snap index 695431e..813a740 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__lexer__yuck_lexer.snap @@ -41,8 +41,24 @@ expression: "Lexer::new(0, r#\"(foo + - \"text\" )\"#.to_string()).collect_vec() Ok( ( 9, - StrLit( - "\"text\"", + SimplExpr( + [ + ( + 9, + StringLit( + [ + ( + 9, + Literal( + "text", + ), + 15, + ), + ], + ), + 15, + ), + ], ), 15, ), diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-11.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-11.snap index 4f80a03..f260cbc 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__test-11.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-11.snap @@ -4,5 +4,5 @@ expression: "p.parse(0, Lexer::new(0, r#\"(test \"hi\")\"#.to_string()))" --- Ok( - (test "hi"), + (test {"hi"}), ) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-12.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-12.snap index 3fcc8ea..b2bddd7 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__test-12.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-12.snap @@ -4,5 +4,5 @@ expression: "p.parse(0, Lexer::new(0, r#\"(test \"h\\\"i\")\"#.to_string()))" --- Ok( - (test "h"i"), + (test {"h"i"}), ) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-13.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-13.snap index 8d4d533..c776d11 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__test-13.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-13.snap @@ -4,5 +4,5 @@ expression: "p.parse(0, Lexer::new(0, r#\"(test \" hi \")\"#.to_string()))" --- Ok( - (test " hi "), + (test {" hi "}), ) diff --git a/crates/yuck/src/parser/snapshots/yuck__parser__test-17.snap b/crates/yuck/src/parser/snapshots/yuck__parser__test-17.snap index a2e848a..098049f 100644 --- a/crates/yuck/src/parser/snapshots/yuck__parser__test-17.snap +++ b/crates/yuck/src/parser/snapshots/yuck__parser__test-17.snap @@ -4,5 +4,5 @@ expression: "p.parse(0, Lexer::new(0, \"\\\"h\\\\\\\"i\\\"\".to_string()))" --- Ok( - "h"i", + {"h"i"}, ) diff --git a/examples/eww-bar/eww.yuck b/examples/eww-bar/eww.yuck index 888d8ba..11a3831 100644 --- a/examples/eww-bar/eww.yuck +++ b/examples/eww-bar/eww.yuck @@ -3,6 +3,7 @@ (workspaces) (music) (sidestuff))) + (defwidget sidestuff [] (box :class "sidestuff" :orientation "h" :space-evenly false :halign "end" (slider-vol) @@ -23,12 +24,12 @@ (defwidget music [] (box :class "music" :orientation "h" :space-evenly false :halign "center" - { ' ' + music})) + ' ${music}')) (defwidget slider-vol [] (box :class "slider-vol" :orientation "h" :space-evenly "false" (box :class "label-vol" "" - (scale :min 0 :max 101 :value volume :onchange "amixer -D pulse sset Master {}%")))) + (scale :min 0 :max 101 :value volume :onchange "amixer -D pulse sset Master \{}%")))) (defwidget slider-ram [] (box :orientation "h" :class "slider-ram" :space-evenly false @@ -38,14 +39,10 @@ (defwidget time [] (box :class "time" - {"{hour}:{min} {month + "the month" * 2} {number_day}, {year_full}"})) - ;{hour + ":" + min + " " + month + " " + number_day + ", " + year_full})) + "${hour}:${min} ${month} ${number_day}, ${year_full}")) -(defvar music "bruh") - -;(defpoll music :interval "5s" "playerctl metadata --format '{{ artist }} - {{ title }}' || true") -(defvar volume "20") -;(defpoll volume :interval "16s" "scripts/getvol") +(defpoll music :interval "5s" "playerctl metadata --format '{{ artist }} - {{ title }}' || true") +(defpoll volume :interval "1s" "scripts/getvol") (defpoll number_day :interval "5h" "date '+%d'") (defpoll month :interval "10h" "date '+%b'") @@ -61,5 +58,3 @@ :geometry (geometry :x "0%" :y "0%" :width "100%" :height "4%") :reserve (struts :side "top" :distance "4%") (bar)) - -; asdf diff --git a/examples/eww-bar/scripts/getvol b/examples/eww-bar/scripts/getvol index 3c31607..6a95077 100755 --- a/examples/eww-bar/scripts/getvol +++ b/examples/eww-bar/scripts/getvol @@ -1,2 +1,2 @@ #!/bin/sh -amixer -D pulse sget Master | grep 'Left:' | awk -F'[][]' '{ print $2 }' | tr -d '%' +amixer -D pulse sget Master | grep 'Left:' | awk -F'[][]' '{ print $2 }' | tr -d '%' | head -1