diff --git a/examples/ed2018/src/main.rs b/examples/ed2018/src/main.rs
index 136d2fa..43756e5 100644
--- a/examples/ed2018/src/main.rs
+++ b/examples/ed2018/src/main.rs
@@ -16,14 +16,14 @@ fn main() {
fn test_page_w_static() {
assert_eq!(
r2s(|o| page(o)),
- "\n \
-
\n \
- Example with stylesheet\n \
+ "\n\
+ \n\
+ Example with stylesheet\n\
\n \
- \n \
- \n \
- Hello world!\n \
+ type=\"text/css\"/>\n\
+ \n\
+ \n\
+ Hello world!\n\
\n\
\n"
);
diff --git a/examples/simple/src/main.rs b/examples/simple/src/main.rs
index e2d1028..08e4b3b 100644
--- a/examples/simple/src/main.rs
+++ b/examples/simple/src/main.rs
@@ -72,7 +72,7 @@ fn test_if_let_destructure() {
fn test_list() {
assert_eq!(
r2s(|o| list(o, &["foo", "bar"])),
- "\n\n\n"
+ "\n\n\n"
);
}
@@ -85,8 +85,8 @@ fn test_list_empty() {
fn test_list_destructure() {
assert_eq!(
r2s(|o| list_destructure(o, &["foo", "bar"])),
- "\n \n - 0: foo
\n \n \
- - 1: bar
\n
\n"
+ "\n\n- 0: foo
\n\n\
+ - 1: bar
\n
\n"
);
}
@@ -94,7 +94,7 @@ fn test_list_destructure() {
fn test_list_destructure_2() {
assert_eq!(
r2s(|o| list_destructure_2(o)),
- "\n Rasmus is 44 years old.
\n\n \
+ "\nRasmus is 44 years old.
\n\n\
Mike is 36 years old.
\n"
);
}
@@ -104,8 +104,8 @@ fn test_uselist() {
assert_eq!(
r2s(|o| uselist(o)),
"Two items
\n\n\
- \n\n\n\
+ \n\n\n\
No items
\n\n\
No items
\n\n\n"
);
@@ -190,8 +190,7 @@ fn test_hello_code() {
fn test_for_loop() {
assert_eq!(
r2s(|o| for_loop(o, &vec!["Hello", "World"])),
- "Looped paragraphs
\n\n \
- Hello
\n\n World
\n"
+ "Looped paragraphs
\n\nHello
\n\nWorld
\n"
);
}
@@ -238,15 +237,15 @@ fn test_page_with_base() {
r2s(|o| page::page(o, "World")),
"\
\n\
- \n Hello World!\
- \n \
+ \nHello World!\
+ \n\
\n\
- \n \
- \n Hello World!
\
- \n \
- \n This is page content for World
\
+ \n\
+ \nHello World!
\
\n\
- \n \
+ \nThis is page content for World
\
+ \n\
+ \n\
\n\n\n"
);
}
diff --git a/examples/static-sass/src/main.rs b/examples/static-sass/src/main.rs
index 41fe3e5..96fc1c0 100644
--- a/examples/static-sass/src/main.rs
+++ b/examples/static-sass/src/main.rs
@@ -25,15 +25,15 @@ mod test {
fn page_w_static() {
assert_eq!(
r2s(|o| page(o)),
- "\n \
- \n \
- Example with stylesheet\n \
+ "\n\
+ \n\
+ Example with stylesheet\n\
\n \
- \n \
- \n \
- Hello world!\n \
+ type=\"text/css\"/>\n\
+ \n\
+ \n\
+ Hello world!\n\
\n\
\n"
);
diff --git a/examples/statics/src/main.rs b/examples/statics/src/main.rs
index 45f9ce3..724f02a 100644
--- a/examples/statics/src/main.rs
+++ b/examples/statics/src/main.rs
@@ -16,14 +16,14 @@ fn main() {
fn test_page_w_static() {
assert_eq!(
r2s(|o| page(o)),
- "\n \
- \n \
- Example with stylesheet\n \
+ "\n\
+ \n\
+ Example with stylesheet\n\
\n \
- \n \
- \n \
- Hello world!\n \
+ type=\"text/css\"/>\n\
+ \n\
+ \n\
+ Hello world!\n\
\n\
\n"
);
diff --git a/src/template.rs b/src/template.rs
index 819144f..367e6af 100644
--- a/src/template.rs
+++ b/src/template.rs
@@ -3,7 +3,9 @@ use itertools::Itertools;
use nom::types::CompleteByteSlice as Input;
use spacelike::spacelike;
use std::io::{self, Write};
-use templateexpression::{template_expression, TemplateExpression};
+use templateexpression::{
+ apply_spacemode, template_expression, SpaceMode, TemplateExpression,
+};
#[derive(Debug, PartialEq, Eq)]
pub struct Template {
@@ -70,7 +72,7 @@ named!(
template_expression),
call!(end_of_file))
),
- |((), preamble, args, body)| Template { preamble, args, body: body.0 }
+ |((), preamble, args, body)| Template { preamble, args, body: apply_spacemode(body.0, SpaceMode::Compact) }
)
);
diff --git a/src/templateexpression.rs b/src/templateexpression.rs
index 18e1c95..f90d89c 100644
--- a/src/templateexpression.rs
+++ b/src/templateexpression.rs
@@ -29,6 +29,16 @@ pub enum TemplateExpression {
name: String,
args: Vec,
},
+ /// The actual mode is allreay applied, this variant is just to ensure
+ /// that outer space mode changes don't descend into this group.
+ SpaceMode(Vec),
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum SpaceMode {
+ AsIs,
+ Compact,
+ Removed,
}
#[derive(Debug, PartialEq, Eq)]
@@ -37,6 +47,17 @@ pub enum TemplateArgument {
Body(Vec),
}
+impl TemplateArgument {
+ pub fn apply_spacemode(self, mode: SpaceMode) -> Self {
+ match self {
+ TemplateArgument::Rust(s) => TemplateArgument::Rust(s),
+ TemplateArgument::Body(v) => {
+ TemplateArgument::Body(apply_spacemode(v, mode))
+ }
+ }
+ }
+}
+
impl Display for TemplateArgument {
fn fmt(&self, out: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
@@ -53,12 +74,64 @@ impl Display for TemplateArgument {
}
}
+pub fn apply_spacemode(
+ v: Vec,
+ mode: SpaceMode,
+) -> Vec {
+ v.into_iter().map(|b| b.apply_spacemode(mode)).collect()
+}
+
impl TemplateExpression {
pub fn text(text: &str) -> Self {
TemplateExpression::Text {
text: text.to_string(),
}
}
+ pub fn apply_spacemode(self, mode: SpaceMode) -> Self {
+ match self {
+ TemplateExpression::Comment => TemplateExpression::Comment,
+ TemplateExpression::Text { text } => TemplateExpression::Text {
+ text: match mode {
+ SpaceMode::AsIs => text,
+ SpaceMode::Compact => compactify(&text),
+ SpaceMode::Removed => {
+ compactify(&text).trim().to_string()
+ }
+ },
+ },
+ TemplateExpression::Expression { expr } => {
+ TemplateExpression::Expression { expr }
+ }
+ TemplateExpression::ForLoop { name, expr, body } => {
+ TemplateExpression::ForLoop {
+ name,
+ expr,
+ body: apply_spacemode(body, mode),
+ }
+ }
+ TemplateExpression::IfBlock {
+ expr,
+ body,
+ else_body,
+ } => TemplateExpression::IfBlock {
+ expr,
+ body: apply_spacemode(body, mode),
+ else_body: else_body.map(|eb| apply_spacemode(eb, mode)),
+ },
+ TemplateExpression::CallTemplate { name, args } => {
+ TemplateExpression::CallTemplate {
+ name,
+ args: args
+ .into_iter()
+ .map(|b| b.apply_spacemode(mode))
+ .collect(),
+ }
+ }
+ TemplateExpression::SpaceMode(body) => {
+ TemplateExpression::SpaceMode(body)
+ }
+ }
+ }
pub fn code(&self) -> String {
match *self {
TemplateExpression::Comment => String::new(),
@@ -104,10 +177,56 @@ impl TemplateExpression {
))),
)
}
+ TemplateExpression::SpaceMode(ref body) => {
+ body.iter().map(|b| b.code()).format("").to_string()
+ }
}
}
}
+fn compactify(s: &str) -> String {
+ let mut space = false;
+ let mut newline = false;
+ let mut result = String::new();
+ for c in s.chars() {
+ match c {
+ '\n' | '\r' => newline = true,
+ ' ' | '\t' => space = true,
+ c => {
+ if newline {
+ result.push('\n');
+ } else if space {
+ result.push(' ');
+ }
+ result.push(c);
+ newline = false;
+ space = false;
+ }
+ }
+ }
+ if newline {
+ result.push('\n');
+ } else if space {
+ result.push(' ');
+ }
+ result
+}
+
+#[test]
+fn t_compactify() {
+ assert_eq!(
+ compactify(" hello world \n \n This is nice.\n\n\n"),
+ " hello world\nThis is nice.\n"
+ )
+}
+#[test]
+fn t_compactify_tabbed() {
+ assert_eq!(
+ compactify("\n\thello world\n\t\n\t This \t is nice.\n\t\n \n"),
+ "\nhello world\nThis is nice.\n"
+ )
+}
+
named!(
pub template_expression,
add_return_error!(
@@ -117,7 +236,8 @@ named!(
alt!(tag!("*") | tag!(":") | tag!("@") |
tag!("{") | tag!("}") |
terminated!(
- alt!(tag!("if") | tag!("for")),
+ alt!(tag!("if") | tag!("for") |
+ tag!("whitespace") | tag!("ws")),
tag!(" ")) |
value!(Input(&b""[..]))))),
Some(Input(b":")) => map!(
@@ -168,6 +288,25 @@ named!(
expr: expr.to_string(),
body,
}) |
+ Some(Input(b"whitespace"))
+ | Some(Input(b"ws")) => map!(
+ tuple!(
+ delimited!(delimited!(spacelike, tag!("("), spacelike),
+ alt!(
+ value!(SpaceMode::AsIs, tag!("as-is")) |
+ value!(SpaceMode::Compact, tag!("compact")) |
+ value!(SpaceMode::Removed, tag!("removed"))
+ ),
+ delimited!(spacelike, tag!(")"), spacelike)),
+ template_block
+ ),
+ |(mode, body)| {
+ eprintln!("TODO: Apply {:?} to {:?}", mode, body);
+ TemplateExpression::SpaceMode(
+ body.into_iter().map(|b| b.apply_spacemode(mode)).collect()
+ )
+ }
+ ) |
Some(Input(b"")) => map!(
expression,
|expr| TemplateExpression::Expression{ expr: expr.to_string() }
@@ -489,6 +628,35 @@ mod test {
)
}
+ #[test]
+ fn skip_ws() {
+ assert_eq!(
+ template_expression(Input(
+ b"@whitespace (removed) {\
+ \n @if something {\
+ \n construct with\
+ \n inner space\
+ \n }\
+ \n}\
+ ",
+ )),
+ Ok((
+ Input(&b""[..]),
+ TemplateExpression::SpaceMode(vec![
+ TemplateExpression::text(""),
+ TemplateExpression::IfBlock {
+ expr: "something".to_string(),
+ body: vec![TemplateExpression::text(
+ "construct with\ninner space"
+ )],
+ else_body: None,
+ },
+ TemplateExpression::text(""),
+ ]),
+ ))
+ )
+ }
+
#[test]
fn for_missing_in() {
// TODO The second part of this message isn't really helpful.