Handle hyphens in attribute and element names

Closes 
This commit is contained in:
Chris Wong 2015-09-15 17:47:32 +12:00
parent 059b82e091
commit b50a3be6f6
2 changed files with 78 additions and 37 deletions
maud_macros

View file

@ -2,6 +2,7 @@ use std::mem;
use syntax::ast::{Expr, ExprParen, Lit, Stmt, TokenTree, TtDelimited, TtToken};
use syntax::ext::quote::rt::ToTokens;
use syntax::codemap::Span;
use syntax::diagnostic::FatalError;
use syntax::ext::base::ExtCtxt;
use syntax::parse::{self, PResult};
use syntax::parse::parser::Parser as RustParser;
@ -163,9 +164,9 @@ impl<'cx, 'i> Parser<'cx, 'i> {
self.render.splice(expr, Escape::Escape);
},
// Element
[ident!(sp, name), ..] => {
self.shift(1);
try!(self.element(sp, &name.name.as_str()));
[ident!(sp, _), ..] => {
let name = try!(self.name());
try!(self.element(sp, &name));
},
// Block
[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.
fn attrs(&mut self) -> PResult<()> {
loop { match self.input {
[ident!(name), eq!(), ..] => {
// Non-empty attribute
self.shift(2);
self.render.attribute_start(&name.name.as_str());
{
// 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
loop {
let old_input = self.input;
let maybe_name = self.name();
match (maybe_name, self.input) {
(Ok(name), [eq!(), ..]) => {
// Non-empty attribute
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.name.as_str());
r.into_stmts()
};
self.render.emit_if(cond, body, None);
} else {
// Write the attribute unconditionally
self.render.attribute_empty(&name.name.as_str());
}
},
_ => return Ok(()),
self.render.attribute_start(&name);
{
// 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();
},
(Ok(name), [question!(), ..]) => {
// Empty attribute
self.shift(1);
if let [ref tt @ eq!(), ..] = self.input {
// 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.

View file

@ -255,3 +255,19 @@ fn html_utf8() {
html_utf8!(buf, p "hello").unwrap();
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>"#);
}
}