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", },