parent
d8ee991da2
commit
dca0400692
4 changed files with 68 additions and 20 deletions
|
@ -99,7 +99,7 @@
|
|||
//! html! {
|
||||
//! form method="POST" {
|
||||
//! label for="waffles" "Do you like waffles?"
|
||||
//! input name="waffles" type="checkbox" checked=! /
|
||||
//! input name="waffles" type="checkbox" checked? /
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
|
@ -114,7 +114,7 @@
|
|||
//! Add attributes using the syntax `attr="value"`. Attributes must be
|
||||
//! quoted: they are parsed as string literals.
|
||||
//!
|
||||
//! To declare an empty attribute, use `!` for the value: `checked=!`.
|
||||
//! To declare an empty attribute, use a `?` suffix: `checked?`.
|
||||
//!
|
||||
//! ## Splices
|
||||
//!
|
||||
|
|
|
@ -20,6 +20,9 @@ macro_rules! eq {
|
|||
macro_rules! not {
|
||||
() => (TtToken(_, token::Not))
|
||||
}
|
||||
macro_rules! question {
|
||||
() => (TtToken(_, token::Question))
|
||||
}
|
||||
macro_rules! semi {
|
||||
() => (TtToken(_, token::Semi))
|
||||
}
|
||||
|
@ -90,11 +93,13 @@ impl<'cx, 's, 'i, 'r, 'o> Parser<'cx, 's, 'i, 'r, 'o> {
|
|||
// Splice
|
||||
[ref tt @ dollar!(), dollar!(), ..] => {
|
||||
self.shift(2);
|
||||
self.splice(Escape::PassThru, tt.get_span())
|
||||
let expr = self.splice(tt.get_span());
|
||||
self.render.splice(expr, Escape::PassThru);
|
||||
},
|
||||
[ref tt @ dollar!(), ..] => {
|
||||
self.shift(1);
|
||||
self.splice(Escape::Escape, tt.get_span())
|
||||
let expr = self.splice(tt.get_span());
|
||||
self.render.splice(expr, Escape::Escape);
|
||||
},
|
||||
// Element
|
||||
[ident!(sp, name), ..] => {
|
||||
|
@ -127,7 +132,7 @@ impl<'cx, 's, 'i, 'r, 'o> Parser<'cx, 's, 'i, 'r, 'o> {
|
|||
}
|
||||
}
|
||||
|
||||
fn splice(&mut self, escape: Escape, sp: Span) {
|
||||
fn splice(&mut self, sp: Span) -> P<Expr> {
|
||||
let mut tts = vec![];
|
||||
// First, munch a single token tree
|
||||
if let [ref tt, ..] = self.input {
|
||||
|
@ -151,10 +156,9 @@ impl<'cx, 's, 'i, 'r, 'o> Parser<'cx, 's, 'i, 'r, 'o> {
|
|||
}
|
||||
}
|
||||
if tts.is_empty() {
|
||||
self.render.cx.span_err(sp, "expected expression for this splice");
|
||||
self.render.cx.span_fatal(sp, "expected expression for this splice");
|
||||
} else {
|
||||
let expr = self.new_rust_parser(tts).parse_expr();
|
||||
self.render.splice(expr, escape);
|
||||
self.new_rust_parser(tts).parse_expr()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,14 +179,10 @@ impl<'cx, 's, 'i, 'r, 'o> Parser<'cx, 's, 'i, 'r, 'o> {
|
|||
}
|
||||
|
||||
fn attrs(&mut self) {
|
||||
while let [ident!(name), eq!(), ..] = self.input {
|
||||
self.shift(2);
|
||||
if let [not!(), ..] = self.input {
|
||||
// Empty attribute
|
||||
self.shift(1);
|
||||
self.render.attribute_empty(name.as_str());
|
||||
} else {
|
||||
loop { match self.input {
|
||||
[ident!(name), eq!(), ..] => {
|
||||
// Non-empty attribute
|
||||
self.shift(2);
|
||||
self.render.attribute_start(name.as_str());
|
||||
{
|
||||
// Parse a value under an attribute context
|
||||
|
@ -192,8 +192,22 @@ impl<'cx, 's, 'i, 'r, 'o> Parser<'cx, 's, 'i, 'r, 'o> {
|
|||
self.in_attr = old_in_attr;
|
||||
}
|
||||
self.render.attribute_end();
|
||||
}
|
||||
}
|
||||
},
|
||||
[ident!(name), question!(), ..] => {
|
||||
// Empty attribute
|
||||
self.shift(2);
|
||||
if let [ref tt @ eq!(), ..] = self.input {
|
||||
// Toggle the attribute based on a boolean expression
|
||||
self.shift(1);
|
||||
let expr = self.splice(tt.get_span());
|
||||
self.render.attribute_empty_if(name.as_str(), expr);
|
||||
} else {
|
||||
// Write the attribute unconditionally
|
||||
self.render.attribute_empty(name.as_str());
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
}}
|
||||
}
|
||||
|
||||
fn block(&mut self, tts: &[TokenTree]) {
|
||||
|
|
|
@ -43,8 +43,13 @@ impl<'cx, 's, 'o> Renderer<'cx, 's, 'o> {
|
|||
|
||||
/// Push an expression statement, also wrapping it with `try!`.
|
||||
fn push(&mut self, expr: P<Expr>) {
|
||||
let expr = self.cx.stmt_expr(self.cx.expr_try(expr.span, expr));
|
||||
self.stmts.push(expr);
|
||||
let stmt = self.make_stmt(expr);
|
||||
self.stmts.push(stmt);
|
||||
}
|
||||
|
||||
/// Create an expression statement, also wrapping it with `try!`.
|
||||
fn make_stmt(&mut self, expr: P<Expr>) -> P<Stmt> {
|
||||
self.cx.stmt_expr(self.cx.expr_try(expr.span, expr))
|
||||
}
|
||||
|
||||
/// Append a literal pre-escaped string.
|
||||
|
@ -94,6 +99,19 @@ impl<'cx, 's, 'o> Renderer<'cx, 's, 'o> {
|
|||
self.write(name);
|
||||
}
|
||||
|
||||
pub fn attribute_empty_if(&mut self, name: &str, expr: P<Expr>) {
|
||||
let s: String = [" ", name].concat();
|
||||
let s = &s[];
|
||||
let w = self.w;
|
||||
let expr = quote_expr!(self.cx,
|
||||
if $expr {
|
||||
$w.write_str($s)
|
||||
} else {
|
||||
Ok(())
|
||||
});
|
||||
self.push(expr);
|
||||
}
|
||||
|
||||
pub fn attribute_end(&mut self) {
|
||||
self.write("\"");
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ mod elements {
|
|||
|
||||
#[test]
|
||||
fn empty_attributes() {
|
||||
let s = html! { div readonly=! input type="checkbox" checked=! / }.to_string();
|
||||
let s = html! { div readonly? input type="checkbox" checked? / }.to_string();
|
||||
assert_eq!(s, r#"<div readonly><input type="checkbox" checked></div>"#);
|
||||
}
|
||||
}
|
||||
|
@ -108,6 +108,22 @@ mod splices {
|
|||
assert_eq!(s, "3628800");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attributes() {
|
||||
let rocks = true;
|
||||
let s = html! {
|
||||
input checked?=true /
|
||||
input checked?=false /
|
||||
input checked?=rocks /
|
||||
input checked?=(!rocks) /
|
||||
}.to_string();
|
||||
assert_eq!(s, concat!(
|
||||
r#"<input checked>"#,
|
||||
r#"<input>"#,
|
||||
r#"<input checked>"#,
|
||||
r#"<input>"#));
|
||||
}
|
||||
|
||||
static BEST_PONY: &'static str = "Pinkie Pie";
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Add table
Reference in a new issue