parent
059b82e091
commit
b50a3be6f6
2 changed files with 78 additions and 37 deletions
|
@ -2,6 +2,7 @@ use std::mem;
|
||||||
use syntax::ast::{Expr, ExprParen, Lit, Stmt, TokenTree, TtDelimited, TtToken};
|
use syntax::ast::{Expr, ExprParen, Lit, Stmt, TokenTree, TtDelimited, TtToken};
|
||||||
use syntax::ext::quote::rt::ToTokens;
|
use syntax::ext::quote::rt::ToTokens;
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
|
use syntax::diagnostic::FatalError;
|
||||||
use syntax::ext::base::ExtCtxt;
|
use syntax::ext::base::ExtCtxt;
|
||||||
use syntax::parse::{self, PResult};
|
use syntax::parse::{self, PResult};
|
||||||
use syntax::parse::parser::Parser as RustParser;
|
use syntax::parse::parser::Parser as RustParser;
|
||||||
|
@ -163,9 +164,9 @@ impl<'cx, 'i> Parser<'cx, 'i> {
|
||||||
self.render.splice(expr, Escape::Escape);
|
self.render.splice(expr, Escape::Escape);
|
||||||
},
|
},
|
||||||
// Element
|
// Element
|
||||||
[ident!(sp, name), ..] => {
|
[ident!(sp, _), ..] => {
|
||||||
self.shift(1);
|
let name = try!(self.name());
|
||||||
try!(self.element(sp, &name.name.as_str()));
|
try!(self.element(sp, &name));
|
||||||
},
|
},
|
||||||
// Block
|
// Block
|
||||||
[TtDelimited(_, ref d), ..] if d.delim == DelimToken::Brace => {
|
[TtDelimited(_, ref d), ..] if d.delim == DelimToken::Brace => {
|
||||||
|
@ -335,42 +336,66 @@ impl<'cx, 'i> Parser<'cx, 'i> {
|
||||||
|
|
||||||
/// Parses and renders the attributes of an element.
|
/// Parses and renders the attributes of an element.
|
||||||
fn attrs(&mut self) -> PResult<()> {
|
fn attrs(&mut self) -> PResult<()> {
|
||||||
loop { match self.input {
|
loop {
|
||||||
[ident!(name), eq!(), ..] => {
|
let old_input = self.input;
|
||||||
// Non-empty attribute
|
let maybe_name = self.name();
|
||||||
self.shift(2);
|
match (maybe_name, self.input) {
|
||||||
self.render.attribute_start(&name.name.as_str());
|
(Ok(name), [eq!(), ..]) => {
|
||||||
{
|
// Non-empty attribute
|
||||||
// Parse a value under an attribute context
|
|
||||||
let mut in_attr = true;
|
|
||||||
mem::swap(&mut self.in_attr, &mut in_attr);
|
|
||||||
try!(self.markup());
|
|
||||||
mem::swap(&mut self.in_attr, &mut 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);
|
self.shift(1);
|
||||||
let cond = try!(self.splice(tt.get_span()));
|
self.render.attribute_start(&name);
|
||||||
// Silence "unnecessary parentheses" warnings
|
{
|
||||||
let cond = strip_outer_parens(cond).to_tokens(self.render.cx);
|
// Parse a value under an attribute context
|
||||||
let body = {
|
let mut in_attr = true;
|
||||||
let mut r = self.render.fork();
|
mem::swap(&mut self.in_attr, &mut in_attr);
|
||||||
r.attribute_empty(&name.name.as_str());
|
try!(self.markup());
|
||||||
r.into_stmts()
|
mem::swap(&mut self.in_attr, &mut in_attr);
|
||||||
};
|
}
|
||||||
self.render.emit_if(cond, body, None);
|
self.render.attribute_end();
|
||||||
} else {
|
},
|
||||||
// Write the attribute unconditionally
|
(Ok(name), [question!(), ..]) => {
|
||||||
self.render.attribute_empty(&name.name.as_str());
|
// Empty attribute
|
||||||
}
|
self.shift(1);
|
||||||
},
|
if let [ref tt @ eq!(), ..] = self.input {
|
||||||
_ => return Ok(()),
|
// Toggle the attribute based on a boolean expression
|
||||||
|
self.shift(1);
|
||||||
|
let cond = try!(self.splice(tt.get_span()));
|
||||||
|
// Silence "unnecessary parentheses" warnings
|
||||||
|
let cond = strip_outer_parens(cond).to_tokens(self.render.cx);
|
||||||
|
let body = {
|
||||||
|
let mut r = self.render.fork();
|
||||||
|
r.attribute_empty(&name);
|
||||||
|
r.into_stmts()
|
||||||
|
};
|
||||||
|
self.render.emit_if(cond, body, None);
|
||||||
|
} else {
|
||||||
|
// Write the attribute unconditionally
|
||||||
|
self.render.attribute_empty(&name);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
self.input = old_input;
|
||||||
|
break;
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a HTML element or attribute name.
|
||||||
|
fn name(&mut self) -> PResult<String> {
|
||||||
|
let mut s = match self.input {
|
||||||
|
[ident!(name), ..] => {
|
||||||
|
self.shift(1);
|
||||||
|
String::from(&name.name.as_str() as &str)
|
||||||
|
},
|
||||||
|
_ => return Err(FatalError),
|
||||||
|
};
|
||||||
|
while let [minus!(), ident!(name), ..] = self.input {
|
||||||
|
self.shift(2);
|
||||||
|
s.push('-');
|
||||||
|
s.push_str(&name.name.as_str());
|
||||||
|
}
|
||||||
|
Ok(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the given token tree, returning a vector of statements.
|
/// Parses the given token tree, returning a vector of statements.
|
||||||
|
|
|
@ -255,3 +255,19 @@ fn html_utf8() {
|
||||||
html_utf8!(buf, p "hello").unwrap();
|
html_utf8!(buf, p "hello").unwrap();
|
||||||
assert_eq!(buf, b"<p>hello</p>");
|
assert_eq!(buf, b"<p>hello</p>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod issue_10 {
|
||||||
|
#[test]
|
||||||
|
fn hyphens_in_element_names() {
|
||||||
|
let mut s = String::new();
|
||||||
|
html!(s, custom-element {}).unwrap();
|
||||||
|
assert_eq!(s, "<custom-element></custom-element>");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hyphens_in_attribute_names() {
|
||||||
|
let mut s = String::new();
|
||||||
|
html!(s, this sentence-is="false" of-course? {}).unwrap();
|
||||||
|
assert_eq!(s, r#"<this sentence-is="false" of-course></this>"#);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue