diff --git a/cliedit/completion.go b/cliedit/completion.go index 009b1eee..5e4be5c1 100644 --- a/cliedit/completion.go +++ b/cliedit/completion.go @@ -316,7 +316,7 @@ func commonPrefix(s1, s2 string) string { // The type for a native Go matcher. This is not equivalent to the Elvish // counterpart, which streams input and output. This is because we can actually // afford calling a Go function for each item, so omitting the streaming -// behavior makes the implementation easier. +// behavior makes the implementation simpler. // // Native Go matchers are wrapped into Elvish matchers, but never the other way // around. @@ -325,14 +325,30 @@ func commonPrefix(s1, s2 string) string { // wrapped into match-substr and match-prefix respectively. type matcher func(text, seed string) bool -type wrappedMatcher func(fm *eval.Frame, seed string, inputs eval.Inputs) +type matcherOpts struct { + IgnoreCase bool + SmartCase bool +} + +func (*matcherOpts) SetDefaultOptions() {} + +type wrappedMatcher func(fm *eval.Frame, opts matcherOpts, seed string, inputs eval.Inputs) func wrapMatcher(m matcher) wrappedMatcher { - return func(fm *eval.Frame, seed string, input eval.Inputs) { + return func(fm *eval.Frame, opts matcherOpts, seed string, inputs eval.Inputs) { out := fm.OutputChan() - input(func(v interface{}) { - out <- m(vals.ToString(v), seed) - }) + if opts.IgnoreCase || (opts.SmartCase && seed == strings.ToLower(seed)) { + if opts.IgnoreCase { + seed = strings.ToLower(seed) + } + inputs(func(v interface{}) { + out <- m(strings.ToLower(vals.ToString(v)), seed) + }) + } else { + inputs(func(v interface{}) { + out <- m(vals.ToString(v), seed) + }) + } } } diff --git a/cliedit/completion_test.go b/cliedit/completion_test.go index 23f52988..17883a0f 100644 --- a/cliedit/completion_test.go +++ b/cliedit/completion_test.go @@ -217,3 +217,23 @@ func TestBuiltinMatchers(t *testing.T) { "subseq": vals.MakeList(true, true, true, true, false, true, true, false), }) } + +func TestBuiltinMatchers_Options(t *testing.T) { + f := setup() + defer f.Cleanup() + + // The two options work identically on all the builtin matchers, so we only + // test for match-prefix for simplicity. + evals(f.Evaler, + `@a = (edit:match-prefix &ignore-case ab [abc aBc AbC])`, + `@b = (edit:match-prefix &ignore-case aB [abc aBc AbC])`, + `@c = (edit:match-prefix &smart-case ab [abc aBc Abc])`, + `@d = (edit:match-prefix &smart-case aB [abc aBc AbC])`, + ) + testGlobals(t, f.Evaler, map[string]interface{}{ + "a": vals.MakeList(true, true, true), + "b": vals.MakeList(true, true, true), + "c": vals.MakeList(true, true, true), + "d": vals.MakeList(false, true, false), + }) +}