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