Merge branch 'master' into stable

This commit is contained in:
Kogia-sima 2020-08-04 09:18:49 +09:00
commit c50a87cf79
30 changed files with 305 additions and 103 deletions

15
CHANGELOG.md Normal file
View File

@ -0,0 +1,15 @@
<a name="v0.2.0"></a>
## [v0.2.0](https://github.com/Kogia-sima/sailfish/compare/v0.1.3...v0.2.0) (2020-07-17)
### Breaking Changes
* Remove Buffer::set_len method
* Syntactically disallow invalid filter expression
* remove runtime::Context API
* Remove register_escape_fn API
### Features
* Implement Compiler::compile_str() function
* Implement filters

26
Cargo.lock generated
View File

@ -27,14 +27,22 @@ name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "home"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "integration-tests"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"sailfish 0.2.0",
"sailfish-compiler 0.2.0",
"sailfish-macros 0.2.0",
"sailfish 0.2.1",
"sailfish-compiler 0.2.1",
"sailfish-macros 0.2.1",
"trybuild 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -105,7 +113,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "sailfish"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"itoap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -114,8 +122,9 @@ dependencies = [
[[package]]
name = "sailfish-compiler"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"home 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
@ -126,10 +135,10 @@ dependencies = [
[[package]]
name = "sailfish-macros"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
"sailfish-compiler 0.2.0",
"sailfish-compiler 0.2.1",
]
[[package]]
@ -249,6 +258,7 @@ dependencies = [
"checksum ctor 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cf6b25ee9ac1995c54d7adb2eff8cfffb7260bc774fb63c601ec65467f43cd9d"
"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
"checksum home 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654"
"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
"checksum itoap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8e804a5759b475f44377998918a7e3be9da3767056f5e77751ef7803893db0e9"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"

View File

@ -10,24 +10,3 @@ exclude = [
"benches",
"examples"
]
[profile.dev.package.syn]
opt-level = 0
debug = false
debug-assertions = false
overflow-checks = false
incremental = true
[profile.test.package.syn]
opt-level = 0
debug = false
debug-assertions = false
overflow-checks = false
incremental = true
[profile.release.package.syn]
opt-level = 0
debug = false
debug-assertions = false
overflow-checks = false
incremental = true

View File

@ -30,8 +30,8 @@ Dependencies:
```toml
[dependencies]
sailfish = "0.2.0"
sailfish-macros = "0.2.0"
sailfish = "0.2.1"
sailfish-macros = "0.2.1"
```
Template file (templates/hello.stpl):

18
benches/Cargo.lock generated
View File

@ -188,7 +188,7 @@ checksum = "474a626a67200bd107d44179bb3d4fc61891172d11696609264589be6a0e6a43"
[[package]]
name = "benches"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"askama",
"bytes",
@ -845,6 +845,15 @@ dependencies = [
"libc",
]
[[package]]
name = "home"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654"
dependencies = [
"winapi",
]
[[package]]
name = "horrorshow"
version = "0.8.3"
@ -1810,7 +1819,7 @@ checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
[[package]]
name = "sailfish"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"itoap",
"ryu",
@ -1819,8 +1828,9 @@ dependencies = [
[[package]]
name = "sailfish-compiler"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"home",
"memchr",
"proc-macro2 1.0.18",
"quote 1.0.7",
@ -1830,7 +1840,7 @@ dependencies = [
[[package]]
name = "sailfish-macros"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"proc-macro2 1.0.18",
"sailfish-compiler",

View File

@ -1,6 +1,6 @@
[package]
name = "benches"
version = "0.2.0"
version = "0.2.1"
authors = ["Dirkjan Ochtman <dirkjan@ochtman.nl>", "Ryohei Machida <orcinus4627@gmail.com>"]
build = "src/build.rs"
edition = "2018"

View File

@ -51,8 +51,8 @@ And render the data with `render_once()` method.
```rust
fn main() {
let ctx = HelloTemplate {
messages: vec![String::from("foo"), String::from("bar")];
}
messages: vec![String::from("foo"), String::from("bar")],
};
// Now render templates with given data
println!("{}", ctx.render_once().unwrap());

View File

@ -4,8 +4,8 @@ In order to use sailfish templates, you have add two dependencies in your `Cargo
```toml
[dependencies]
sailfish = "0.2.0"
sailfish-macros = "0.2.0"
sailfish = "0.2.1"
sailfish-macros = "0.2.1"
```
`sailfish` crate contains runtime for rendering contents, and `sailfish-macros` serves you derive macros to compile and import the template files.

View File

@ -0,0 +1,37 @@
# Filters
Filters are used to format the rendered contents.
Example:
```ejs
message: <%= "foo\nbar" | dbg %>
```
Output:
```html
message: &quot;foo\nbar&quot;
```
!!! Note
Since `dbg` filter accepts '<T: std::fmt::Debug>' types, that type isn't required to implement [`Render`](https://docs.rs/sailfish/latest/sailfish/runtime/trait.Render.html) trait. That means you can pass the type which doen't implement `Render` trait.
## Syntax
- Apply filter and HTML escaping
```ejs
<%= expression | filter %>
```
- Apply filter only
```ejs
<%- expression | filter %>
```
## Built-In Filters
Built-In filters can be found in [`sailfish::runtime::filter`](https://docs.rs/sailfish/latest/sailfish/runtime/filter/index.html) module.

View File

@ -32,6 +32,15 @@
Unlike EJS, you cannot omit the file extension.
## Helpers
## Filters
(Work in progress)
```ejs
<%= message | upper %>
```
```ejs
{
"id": <%= id %>
"comment": <%- comment | dbg %>
}
```

View File

@ -50,3 +50,4 @@ nav:
- 'Overview': 'syntax/overview.md'
- 'Tags': 'syntax/tags.md'
- 'Includes': 'syntax/includes.md'
- 'Filters': 'syntax/filters.md'

18
examples/Cargo.lock generated
View File

@ -651,6 +651,15 @@ dependencies = [
"libc",
]
[[package]]
name = "home"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "hostname"
version = "0.3.1"
@ -1121,7 +1130,7 @@ checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "sailfish"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"itoap",
"ryu",
@ -1130,8 +1139,9 @@ dependencies = [
[[package]]
name = "sailfish-compiler"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"home",
"memchr",
"proc-macro2",
"quote",
@ -1141,7 +1151,7 @@ dependencies = [
[[package]]
name = "sailfish-examples"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"actix-rt",
"actix-web",
@ -1151,7 +1161,7 @@ dependencies = [
[[package]]
name = "sailfish-macros"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"proc-macro2",
"sailfish-compiler",

View File

@ -1,6 +1,6 @@
[package]
name = "sailfish-examples"
version = "0.2.0"
version = "0.2.1"
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
edition = "2018"
publish = false

View File

@ -1,6 +1,6 @@
[package]
name = "sailfish-compiler"
version = "0.2.0"
version = "0.2.1"
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
description = "Really fast, intuitive template engine for Rust"
homepage = "https://github.com/Kogia-sima/sailfish"
@ -25,6 +25,7 @@ config = ["yaml-rust"]
memchr = "2.3.3"
quote = { version = "1.0.6", default-features = false }
yaml-rust = { version = "0.4.4", optional = true }
home = "0.5.3"
[dependencies.syn]
version = "1.0.21"
@ -32,7 +33,7 @@ default-features = false
features = ["parsing", "full", "visit-mut", "printing", "clone-impls", "extra-traits"]
[dependencies.proc-macro2]
version = ">=1.0.11, <=1.0.18"
version = ">=1.0.11, <=1.0.19"
default-features = false
features = ["span-locations"]

View File

@ -274,13 +274,15 @@ fn derive_template_impl(tokens: TokenStream) -> Result<TokenStream, syn::Error>
static SIZE_HINT: __sf_rt::SizeHint = __sf_rt::SizeHint::new();
let mut __sf_buf = __sf_rt::Buffer::from(std::mem::take(buf));
let mut __sf_buf = __sf_rt::Buffer::from(buf.as_str());
__sf_buf.reserve(SIZE_HINT.get());
let __sf_old_len = __sf_buf.len();
let #name { #field_names } = self;
include!(#output_file_string);
SIZE_HINT.update(__sf_buf.len());
SIZE_HINT.update(__sf_buf.len() - __sf_old_len);
*buf = __sf_buf.into_string();
Ok(())
}

View File

@ -1,5 +1,6 @@
use std::fs;
use std::io::{self, Write};
use std::path::Path;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
pub fn read_to_string(path: &Path) -> io::Result<String> {
@ -16,13 +17,38 @@ pub fn read_to_string(path: &Path) -> io::Result<String> {
Ok(content)
}
fn find_rustfmt() -> io::Result<Option<PathBuf>> {
let mut toolchain_dir = home::rustup_home()?;
toolchain_dir.push("toolchains");
for e in fs::read_dir(toolchain_dir)? {
let mut path = e?.path();
path.push("bin");
path.push("rustfmt");
if path.exists() {
return Ok(Some(path));
}
}
Ok(None)
}
/// Format block expression using `rustfmt` command
pub fn rustfmt_block(source: &str) -> io::Result<String> {
let rustfmt = match find_rustfmt()? {
Some(p) => p,
None => {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"rustfmt command not found",
))
}
};
let mut new_source = String::with_capacity(source.len() + 11);
new_source.push_str("fn render()");
new_source.push_str(source);
let mut child = Command::new("rustfmt")
let mut child = Command::new(rustfmt)
.args(&["--emit", "stdout", "--color", "never", "--quiet"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())

View File

@ -1,6 +1,6 @@
[package]
name = "sailfish-macros"
version = "0.2.0"
version = "0.2.1"
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
description = "Really fast, intuitive template engine for Rust"
homepage = "https://github.com/Kogia-sima/sailfish"
@ -29,6 +29,6 @@ proc-macro2 = "1.0.11"
[dependencies.sailfish-compiler]
path = "../sailfish-compiler"
version = "0.2.0"
version = "0.2.1"
default-features = false
features = ["procmacro"]

View File

@ -62,7 +62,7 @@ dependencies = [
[[package]]
name = "fuzzing-tests"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"afl",
"sailfish",
@ -78,6 +78,15 @@ dependencies = [
"libc",
]
[[package]]
name = "home"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654"
dependencies = [
"winapi",
]
[[package]]
name = "itoap"
version = "0.1.0"
@ -137,7 +146,7 @@ checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "sailfish"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"itoap",
"ryu",
@ -146,8 +155,9 @@ dependencies = [
[[package]]
name = "sailfish-compiler"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"home",
"memchr",
"proc-macro2",
"quote",

View File

@ -1,6 +1,6 @@
[package]
name = "fuzzing-tests"
version = "0.2.0"
version = "0.2.1"
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
edition = "2018"
publish = false

View File

@ -1,6 +1,6 @@
[package]
name = "integration-tests"
version = "0.2.0"
version = "0.2.1"
authors = ["Kogia-sima <orcinus4627@gmail.com>"]
edition = "2018"
publish = false

View File

@ -1,3 +1,3 @@
<table>
<tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr>
<tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr>
</table>

View File

@ -1,11 +1,11 @@
<html>
<head>
<title>2015</title>
</head>
<body>
<h1>CSL 2015</h1>
<ul>
<li class="champion"><b>Jiangsu</b>: 43</li><li class=""><b>Beijing</b>: 27</li><li class=""><b>Guangzhou</b>: 22</li><li class=""><b>Shandong</b>: 12</li>
</ul>
</body>
<head>
<title>2015</title>
</head>
<body>
<h1>CSL 2015</h1>
<ul>
<li class="champion"><b>Jiangsu</b>: 43</li><li class=""><b>Beijing</b>: 27</li><li class=""><b>Guangzhou</b>: 22</li><li class=""><b>Shandong</b>: 12</li>
</ul>
</body>
</html>

View File

@ -104,7 +104,7 @@ fn test_include() {
}
#[derive(TemplateOnce)]
#[template(path = "big-table.stpl")]
#[template(path = "big-table.stpl", rm_whitespace = true)]
struct BigTable {
table: Vec<Vec<usize>>,
}
@ -116,7 +116,7 @@ fn test_big_table() {
}
#[derive(TemplateOnce)]
#[template(path = "teams.stpl")]
#[template(path = "teams.stpl", rm_whitespace = true)]
struct Teams {
year: u16,
teams: Vec<Team>,

View File

@ -1,6 +1,6 @@
[package]
name = "sailfish"
version = "0.2.0"
version = "0.2.1"
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
description = "Really fast, intuitive template engine for Rust"
homepage = "https://github.com/Kogia-sima/sailfish"

View File

@ -47,6 +47,7 @@ pub trait TemplateOnce: Sized {
/// This method never returns `Err`, unless you explicitly return RenderError
/// inside templates
#[inline]
#[allow(deprecated)]
fn render_once(self) -> runtime::RenderResult {
let mut buf = String::new();
self.render_once_to_string(&mut buf)?;
@ -57,6 +58,10 @@ pub trait TemplateOnce: Sized {
///
/// This method never returns `Err`, unless you explicitly return RenderError
/// inside templates
#[deprecated(
since = "0.2.1",
note = "This function may be removed in the future due to performance issue"
)]
fn render_once_to_string(self, buf: &mut String) -> Result<(), RenderError>;
}

View File

@ -30,14 +30,8 @@ impl Buffer {
if unlikely!(n == 0) {
Self::new()
} else {
let layout = Layout::from_size_align_unchecked(n, 1);
let data = alloc(layout);
if data.is_null() {
handle_alloc_error(layout);
}
Self {
data,
data: safe_alloc(n),
len: 0,
capacity: n,
}
@ -148,6 +142,17 @@ impl Buffer {
}
}
unsafe fn safe_alloc(capacity: usize) -> *mut u8 {
assert!(capacity <= std::usize::MAX / 2, "capacity is too large");
let layout = Layout::from_size_align_unchecked(capacity, 1);
let data = alloc(layout);
if data.is_null() {
handle_alloc_error(layout);
}
data
}
#[cold]
unsafe fn safe_realloc(
ptr: *mut u8,
@ -178,14 +183,8 @@ impl Clone for Buffer {
if self.capacity == 0 {
Self::new()
} else {
let layout = Layout::from_size_align_unchecked(self.len, 1);
let data = alloc(layout);
if data.is_null() {
handle_alloc_error(layout);
}
let buf = Self {
data,
data: safe_alloc(self.len),
len: self.len,
capacity: self.len,
};
@ -223,13 +222,17 @@ impl fmt::Write for Buffer {
}
impl From<String> for Buffer {
/// Shrink the data and pass raw pointer directory to buffer
///
/// This operation is `O(1)`
#[inline]
fn from(other: String) -> Buffer {
let mut other = ManuallyDrop::new(other);
let bs = other.into_boxed_str();
let data = unsafe { &mut *Box::into_raw(bs) };
Buffer {
data: other.as_mut_ptr(),
len: other.len(),
capacity: other.capacity(),
data: data.as_mut_ptr(),
len: data.len(),
capacity: data.len(),
}
}
}
@ -237,7 +240,12 @@ impl From<String> for Buffer {
impl From<&str> for Buffer {
#[inline]
fn from(other: &str) -> Buffer {
Buffer::from(other.to_owned())
let mut buf = Buffer::with_capacity(other.len());
unsafe {
ptr::copy_nonoverlapping(other.as_ptr(), buf.as_mut_ptr(), other.len());
buf.advance(other.len());
}
buf
}
}

View File

@ -60,11 +60,10 @@ pub(super) unsafe fn escape_small(feed: &str, mut buf: *mut u8) -> usize {
if unlikely!(idx < ESCAPED_LEN) {
let escaped = ESCAPED.get_unchecked(idx);
if ptr > start_ptr {
let slc =
slice::from_raw_parts(start_ptr, ptr as usize - start_ptr as usize);
let len = ptr as usize - start_ptr as usize;
memcpy_16(slc.as_ptr(), buf, slc.len());
buf = buf.add(slc.len());
memcpy_16(start_ptr, buf, len);
buf = buf.add(len);
}
memcpy_16(escaped.as_ptr(), buf, escaped.len());
buf = buf.add(escaped.len());
@ -77,9 +76,9 @@ pub(super) unsafe fn escape_small(feed: &str, mut buf: *mut u8) -> usize {
debug_assert!(start_ptr <= ptr);
if likely!(end_ptr > start_ptr) {
let slc = slice::from_raw_parts(start_ptr, end_ptr as usize - start_ptr as usize);
memcpy_16(slc.as_ptr(), buf, slc.len());
buf = buf.add(slc.len());
let len = end_ptr as usize - start_ptr as usize;
memcpy_16(start_ptr, buf, len);
buf = buf.add(len);
}
buf as usize - buf_begin as usize

View File

@ -1,6 +1,9 @@
//! Build-in filters
// TODO: performance improvement
use std::fmt;
use std::ptr;
use super::{Buffer, Render, RenderError};
@ -86,6 +89,60 @@ pub fn lower<T: Render>(expr: &T) -> Lower<T> {
Lower(expr)
}
pub struct Trim<'a, T>(&'a T);
impl<'a, T: Render> Render for Trim<'a, T> {
#[inline]
fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
let old_len = b.len();
self.0.render(b)?;
trim_impl(b, old_len);
Ok(())
}
#[inline]
fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
let old_len = b.len();
self.0.render_escaped(b)?;
trim_impl(b, old_len);
Ok(())
}
}
fn trim_impl(b: &mut Buffer, old_len: usize) {
debug_assert!(b.len() >= old_len);
let new_contents = &b.as_str()[old_len..];
let trimmed = new_contents.trim();
let trimmed_len = trimmed.len();
if new_contents.len() != trimmed_len {
// performs inplace trimming
if new_contents.as_ptr() != trimmed.as_ptr() {
debug_assert!(new_contents.as_ptr() < trimmed.as_ptr());
let offset = trimmed.as_ptr() as usize - new_contents.as_ptr() as usize;
unsafe {
ptr::copy(
b.as_mut_ptr().add(old_len + offset),
b.as_mut_ptr().add(old_len),
trimmed_len,
);
}
}
debug_assert!(b.capacity() >= old_len + trimmed_len);
unsafe {
b._set_len(old_len + trimmed_len);
}
}
}
/// convert the rendered contents to lowercase
#[inline]
pub fn trim<T: Render>(expr: &T) -> Trim<T> {
Trim(expr)
}
#[cfg(test)]
mod tests {
use super::*;
@ -104,4 +161,23 @@ mod tests {
lower(&"<h1>TITLE</h1>").render_escaped(&mut buf).unwrap();
assert_eq!(buf.as_str(), "&lt;h1&gt;title&lt;/h1&gt;");
}
#[test]
fn trim_test() {
let mut buf = Buffer::new();
trim(&" hello ").render(&mut buf).unwrap();
trim(&"hello ").render(&mut buf).unwrap();
trim(&" hello").render(&mut buf).unwrap();
assert_eq!(buf.as_str(), "hellohellohello");
let mut buf = Buffer::new();
trim(&"hello ").render(&mut buf).unwrap();
trim(&" hello").render(&mut buf).unwrap();
trim(&"hello").render(&mut buf).unwrap();
assert_eq!(buf.as_str(), "hellohellohello");
let mut buf = Buffer::new();
trim(&" hello").render(&mut buf).unwrap();
assert_eq!(buf.as_str(), "hello");
}
}

View File

@ -25,7 +25,7 @@ impl SizeHint {
/// as the value passed on update()
#[inline]
pub fn update(&self, mut value: usize) {
value = value + value / 4;
value = value + value / 8 + 75;
if unlikely!(self.get() < value) {
self.value.store(value, Ordering::Release);
}

View File

@ -3,15 +3,19 @@ unlet b:current_syntax
syn include @rustSyntax syntax/rust.vim
syn region sailfishCodeBlock matchgroup=sailfishTag start=/<%/ keepend end=/%>/ contains=@rustSyntax
syn region sailfishBufferBlock matchgroup=sailfishTag start=/<%=/ keepend end=/%>/ contains=@rustSyntax
syn region sailfishCodeBlock contained matchgroup=sailfishTag start=/<%/ keepend end=/%>/ contains=@rustSyntax
syn region sailfishBufferBlock contained matchgroup=sailfishTag start=/<%=/ keepend end=/%>/ contains=@rustSyntax
syn region sailfishCommentBlock start=/<%#/ end=/%>/
" Redefine htmlTag so that it can contain jspExpr
syn clear htmlString
syn region htmlString contained start=+"+ end=+"+ contains=htmlSpecialChar,javaScriptExpression,@htmlPreproc,sailfishCodeBlock,sailfishBufferBlock
syn region htmlString contained start=+'+ end=+'+ contains=htmlSpecialChar,javaScriptExpression,@htmlPreproc,sailfishCodeBlock,sailfishBufferBlock
syn clear htmlTag
syn region htmlTag start=+<[^/%]+ end=+>+ fold contains=htmlTagN,htmlString,htmlArg,htmlValue,htmlTagError,htmlEvent,htmlCssDefinition,@htmlPreproc,@htmlArgCluster,sailfishBufferBlock
hi default link sailfishTag htmlTag
hi default link sailfishTag htmlPreProc
hi default link sailfishCommentBlock htmlComment
let b:current_syntax = "sailfish"