mirror of
https://github.com/danog/gojekyll.git
synced 2024-11-30 07:18:59 +01:00
Implement smartify filter
This commit is contained in:
parent
ad59c0239d
commit
b96f177f2a
@ -48,18 +48,12 @@ func AddJekyllFilters(e liquid.Engine, c config.Config) {
|
||||
e.RegisterFilter("push", func(array []interface{}, item interface{}) interface{} {
|
||||
return append(array, evaluator.MustConvertItem(item, array))
|
||||
})
|
||||
e.RegisterFilter("pop", func(array []interface{}) interface{} {
|
||||
if len(array) == 0 {
|
||||
return nil
|
||||
}
|
||||
e.RegisterFilter("pop", requireNonEmptyArray(func(array []interface{}) interface{} {
|
||||
return array[0]
|
||||
})
|
||||
e.RegisterFilter("shift", func(array []interface{}) interface{} {
|
||||
if len(array) == 0 {
|
||||
return nil
|
||||
}
|
||||
}))
|
||||
e.RegisterFilter("shift", requireNonEmptyArray(func(array []interface{}) interface{} {
|
||||
return array[len(array)-1]
|
||||
})
|
||||
}))
|
||||
e.RegisterFilter("unshift", func(array []interface{}, item interface{}) interface{} {
|
||||
return append([]interface{}{evaluator.MustConvertItem(item, array)}, array...)
|
||||
})
|
||||
@ -132,7 +126,7 @@ func AddJekyllFilters(e liquid.Engine, c config.Config) {
|
||||
e.RegisterFilter("cgi_escape", unimplementedFilter("cgi_escape"))
|
||||
e.RegisterFilter("uri_escape", unimplementedFilter("uri_escape"))
|
||||
e.RegisterFilter("scssify", unimplementedFilter("scssify"))
|
||||
e.RegisterFilter("smartify", unimplementedFilter("smartify"))
|
||||
e.RegisterFilter("smartify", smartifyFilter)
|
||||
e.RegisterFilter("xml_escape", func(s string) string {
|
||||
// TODO can't handle maps
|
||||
// eval https://github.com/clbanning/mxj
|
||||
@ -145,6 +139,17 @@ func AddJekyllFilters(e liquid.Engine, c config.Config) {
|
||||
})
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
func requireNonEmptyArray(fn func([]interface{}) interface{}) func([]interface{}) interface{} {
|
||||
return func(array []interface{}) interface{} {
|
||||
if len(array) == 0 {
|
||||
return nil
|
||||
}
|
||||
return fn(array)
|
||||
}
|
||||
}
|
||||
|
||||
func unimplementedFilter(name string) func(value interface{}) interface{} {
|
||||
warned := false
|
||||
return func(value interface{}) interface{} {
|
||||
@ -156,6 +161,8 @@ func unimplementedFilter(name string) func(value interface{}) interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// array filters
|
||||
|
||||
func arrayToSentenceStringFilter(array []string, conjunction func(string) string) string {
|
||||
conj := conjunction("and ")
|
||||
switch len(array) {
|
||||
@ -254,3 +261,4 @@ func whereFilter(array []map[string]interface{}, key string, value interface{})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,12 @@ var filterTests = []struct{ in, expected string }{
|
||||
{`{{ "The _config.yml file" | slugify: 'default' }}`, "the-config-yml-file"},
|
||||
{`{{ "The _config.yml file" | slugify: 'pretty' }}`, "the-_config.yml-file"},
|
||||
|
||||
{`{{ "smartify single 'quotes' here" | smartify }}`, "smartify single ‘quotes’ here"},
|
||||
{`{{ 'smartify double "quotes" here' | smartify }}`, "smartify double “quotes” here"},
|
||||
{"{{ \"smartify ``backticks''\" | smartify }}", "smartify “backticks”"},
|
||||
{`{{ "smartify it's they're" | smartify }}`, "smartify it’s they’re"},
|
||||
{`{{ "smartify ... (c) (r) (tm) -- ---" | smartify }}`, "smartify … © ® ™ – —"},
|
||||
|
||||
{`{{ "1 < 2 & 3" | xml_escape }}`, "1 < 2 & 3"},
|
||||
// {`{{ "http://foo.com/?q=foo, \bar?" | uri_escape }}`, "http://foo.com/?q=foo,%20%5Cbar?"},
|
||||
}
|
||||
|
69
filters/smartify.go
Normal file
69
filters/smartify.go
Normal file
@ -0,0 +1,69 @@
|
||||
package filters
|
||||
|
||||
// This file contains a very inefficient implementation, that scans the string multiple times.
|
||||
// Replace it by a transducer if it shows up in hot spots.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var smartifyTransforms = []struct {
|
||||
match *regexp.Regexp
|
||||
repl string
|
||||
}{
|
||||
{regexp.MustCompile("(^|[^[:alnum:]])``(.+?)''"), "$1“$2”"},
|
||||
{regexp.MustCompile(`(^|[^[:alnum:]])'`), "$1‘"},
|
||||
{regexp.MustCompile(`'`), "’"},
|
||||
{regexp.MustCompile(`(^|[^[:alnum:]])"`), "$1“"},
|
||||
{regexp.MustCompile(`"($|[^[:alnum:]])`), "”$1"},
|
||||
{regexp.MustCompile(`(^|\s)--($|\s)`), "$1–$2"},
|
||||
{regexp.MustCompile(`(^|\s)---($|\s)`), "$1—$2"},
|
||||
}
|
||||
|
||||
// replace these wherever they appear
|
||||
var smartifyReplaceSpans = map[string]string{
|
||||
"...": "…",
|
||||
"(c)": "©",
|
||||
"(r)": "®",
|
||||
"(tm)": "™",
|
||||
}
|
||||
|
||||
// replace these only if bounded by space or word boundaries
|
||||
var smartifyReplaceWords = map[string]string{
|
||||
// "---": "–",
|
||||
// "--": "—",
|
||||
}
|
||||
|
||||
var smartifyReplacements map[string]string
|
||||
var smartifyReplacementPattern *regexp.Regexp
|
||||
|
||||
func init() {
|
||||
smartifyReplacements = map[string]string{}
|
||||
disjuncts := []string{}
|
||||
regexQuoter := regexp.MustCompile(`[\(\)\.]`)
|
||||
escape := func(s string) string {
|
||||
return regexQuoter.ReplaceAllString(s, `\$0`)
|
||||
}
|
||||
for k, v := range smartifyReplaceSpans {
|
||||
disjuncts = append(disjuncts, escape(k))
|
||||
smartifyReplacements[k] = v
|
||||
}
|
||||
for k, v := range smartifyReplaceWords {
|
||||
disjuncts = append(disjuncts, fmt.Sprintf(`(\b|\s|^)%s(\b|\s|$)`, escape(k)))
|
||||
smartifyReplacements[k] = fmt.Sprintf("$1%s$2", v)
|
||||
}
|
||||
p := fmt.Sprintf(`(%s)`, strings.Join(disjuncts, `|`))
|
||||
smartifyReplacementPattern = regexp.MustCompile(p)
|
||||
}
|
||||
|
||||
func smartifyFilter(s string) string {
|
||||
for _, rule := range smartifyTransforms {
|
||||
s = rule.match.ReplaceAllString(s, rule.repl)
|
||||
}
|
||||
s = smartifyReplacementPattern.ReplaceAllStringFunc(s, func(w string) string {
|
||||
return smartifyReplacements[w]
|
||||
})
|
||||
return s
|
||||
}
|
Loading…
Reference in New Issue
Block a user