diff --git a/README.md b/README.md
index 1d01b45..8f51628 100644
--- a/README.md
+++ b/README.md
@@ -32,19 +32,24 @@ In brief, these aren't implemented:
- The `cycle` and `tablerow` tags
- `{% case %}…{% else %}` and `{% when a or b %}`
-- The `escape`, `sort_natural`, `truncatewords`, `url_decode`, and `url_encode` filters
+- The `sort_natural`, `url_decode`, and `url_encode` filters
- Loop ranges `{% for a in 1...10 %}`
- Error modes
- Whitespace control
+These are opinionated differences that unlikely to change:
+
+- The expression parser accepts parentheses in more locations
+- The `truncatewords` filter leaves whitespace prior to the truncation point unchanged.
+
## Stability Guarantees
This library is at an early stage of development.
It has been mostly used by its author.
-Until it reaches 1.0, breaking changes will accompanied by a bump in the minor version, not the major version. For example, use `go get gopkg.in/osteele/liquid.v0.2` to stay at versions that are compatible with the v0.2 API. The v0.3 release will not in general be compatible with version 0.2.
+Until it reaches 1.0, breaking changes will accompanied by a bump in the minor version, not the major version. For example, tag `v0.2` is incompatible with `v0.1` and (hypothetical) `v0.1.1`. ([gopkg.in](http://gopkg.in) doesn't work this way, so you won't can't use `gopkg.in/osteele/liquid.v0.1` to specify version 0.1.)
-Even within these parameters, only the liquid package itself, and the sub-package APIs that it documents, are guaranteed stable. For example, `render.Context` is documented as the parameter type for tag definitions; it therefore has the same stability guarantees as `liquid.Engine` and `liquid.Template`. Other "public" definitions in `render` and other sub-packages are public only to the implementation of packages in the repo; they are not generally stable.
+Even within these parameters, only the liquid package itself, and the sub-package APIs that it documents, are guaranteed stable. For example, `render.Context` is documented as the parameter type for tag definitions; it therefore has the same stability guarantees as `liquid.Engine` and `liquid.Template`. Other "public" definitions in `render` and in other sub-packages are intended only for use in other packages in this repo; they are not generally stable between even sub-minor releases.
## Install
diff --git a/filters/filters.go b/filters/filters.go
index ba780ad..4d4ade6 100644
--- a/filters/filters.go
+++ b/filters/filters.go
@@ -126,7 +126,6 @@ func AddStandardFilters(fd FilterDictionary) { // nolint: gocyclo
fd.AddFilter("escape_once", func(s, suffix string) string {
return html.EscapeString(html.UnescapeString(s))
})
- // TODO test case for this
fd.AddFilter("newline_to_br", func(s string) string {
return strings.Replace(s, "\n", "
", -1)
})
@@ -170,11 +169,23 @@ func AddStandardFilters(fd FilterDictionary) { // nolint: gocyclo
fd.AddFilter("rstrip", func(s string) string {
return strings.TrimRightFunc(s, unicode.IsSpace)
})
- fd.AddFilter("truncate", func(s string, n int, ellipsis func(string) string) string {
+ fd.AddFilter("truncate", func(s string, length func(int) int, ellipsis func(string) string) string {
+ n := length(50)
el := ellipsis("...")
// runes aren't bytes; don't use slice
- p := regexp.MustCompile(fmt.Sprintf(`^(.{%d})..{%d,}`, n-len(el), len(el)))
- return p.ReplaceAllString(s, `$1`+el)
+ re := regexp.MustCompile(fmt.Sprintf(`^(.{%d})..{%d,}`, n-len(el), len(el)))
+ return re.ReplaceAllString(s, `$1`+el)
+ })
+ fd.AddFilter("truncatewords", func(s string, length func(int) int, ellipsis func(string) string) string {
+ el := ellipsis("...")
+ n := length(15)
+ // re := regexp.MustCompile(fmt.Sprintf(`^\s*(?:\S+\s+){%d}`, n))
+ re := regexp.MustCompile(fmt.Sprintf(`^(?:\s*\S+){%d}`, n))
+ m := re.FindString(s)
+ if m == "" {
+ return s
+ }
+ return m + el
})
fd.AddFilter("upcase", func(s, suffix string) string {
return strings.ToUpper(s)
diff --git a/filters/filters_test.go b/filters/filters_test.go
index 376f7d4..49b206c 100644
--- a/filters/filters_test.go
+++ b/filters/filters_test.go
@@ -52,7 +52,6 @@ var filterTests = []struct {
{`"apples, oranges, peaches, plums" | split: ", " | size`, 4},
// string filters
- // TODO escape, truncatewords, url_decode, url_encode
{`"Take my protein pills and put my helmet on" | replace: "my", "your"`, "Take your protein pills and put your helmet on"},
{`"Take my protein pills and put my helmet on" | replace_first: "my", "your"`, "Take your protein pills and put my helmet on"},
{`"/my/fancy/url" | append: ".html"`, "/my/fancy/url.html"},
@@ -62,6 +61,7 @@ var filterTests = []struct {
{`"Parker Moore" | downcase`, "parker moore"},
{`"Have you read 'James & the Giant Peach'?" | escape`, "Have you read 'James & the Giant Peach'?"},
{`"1 < 2 & 3" | escape_once`, "1 < 2 & 3"},
+ {`string_with_newlines | newline_to_br`, "
Hello
there
"},
{`"1 < 2 & 3" | escape_once`, "1 < 2 & 3"},
{`"apples, oranges, and bananas" | prepend: "Some fruit: "`, "Some fruit: apples, oranges, and bananas"},
{`"I strained to see the train through the rain" | remove: "rain"`, "I sted to see the t through the "},
@@ -75,6 +75,15 @@ var filterTests = []struct {
{`"Ground control to Major Tom." | truncate: 25, ", and so on"`, "Ground control, and so on"},
{`"Ground control to Major Tom." | truncate: 20, ""`, "Ground control to Ma"},
{`"Ground" | truncate: 20`, "Ground"},
+ {`"Ground control to Major Tom." | truncatewords: 3`, "Ground control to..."},
+ {`"Ground control to Major Tom." | truncatewords: 3, "--"`, "Ground control to--"},
+ {`"Ground control to Major Tom." | truncatewords: 3, ""`, "Ground control to"},
+ {`"Ground control" | truncatewords: 3, ""`, "Ground control"},
+ {`"Ground" | truncatewords: 3, ""`, "Ground"},
+ {`" Ground" | truncatewords: 3, ""`, " Ground"},
+ {`"" | truncatewords: 3, ""`, ""},
+ {`" " | truncatewords: 3, ""`, " "},
+
{`"Parker Moore" | upcase`, "PARKER MOORE"},
{`" So much room for activities! " | strip`, "So much room for activities!"},
{`" So much room for activities! " | lstrip`, "So much room for activities! "},
@@ -157,6 +166,7 @@ var filterTestBindings = map[string]interface{}{
{"weight": 3},
{"weight": nil},
},
+ "string_with_newlines": "\nHello\nthere\n",
"page": map[string]interface{}{
"title": "Introduction",
},