mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-01 00:33:05 +08:00
Rename the query DSL to the filter DSL.
This commit is contained in:
parent
3e512a6dd6
commit
b43e5b8793
|
@ -198,7 +198,7 @@ func completionStart(app cli.App, bindings tk.Bindings, cfg complete.Config, sma
|
|||
}
|
||||
w, err := mode.NewCompletion(app, mode.CompletionSpec{
|
||||
Name: result.Name, Replace: result.Replace, Items: result.Items,
|
||||
Filter: queryFilterSpec, Bindings: bindings,
|
||||
Filter: filterSpec, Bindings: bindings,
|
||||
})
|
||||
if w != nil {
|
||||
app.SetAddon(w, false)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// Package query implements the Elvish query language.
|
||||
// Package filter implements the Elvish filter DSL.
|
||||
//
|
||||
// The Elvish query language is a DSL for filtering data, such as in history
|
||||
// listing mode and location mode of the interactive editor. It is a subset of
|
||||
// Elvish's expression syntax, currently a very small one.
|
||||
package query
|
||||
// The filter DSL is a subset of Elvish's expression syntax, and is useful for
|
||||
// filtering a list of items. It is currently used in the listing modes of the
|
||||
// interactive editor.
|
||||
package filter
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -15,20 +15,20 @@ import (
|
|||
"src.elv.sh/pkg/parse/cmpd"
|
||||
)
|
||||
|
||||
// Compile parses and compiles a query.
|
||||
func Compile(q string) (Query, error) {
|
||||
qn, errParse := parseQuery(q)
|
||||
query, errCompile := compileQuery(qn)
|
||||
return query, diag.Errors(errParse, errCompile)
|
||||
// Compile parses and compiles a filter.
|
||||
func Compile(q string) (Filter, error) {
|
||||
qn, errParse := parseFilter(q)
|
||||
filter, errCompile := compileFilter(qn)
|
||||
return filter, diag.Errors(errParse, errCompile)
|
||||
}
|
||||
|
||||
func parseQuery(q string) (*parse.Query, error) {
|
||||
qn := &parse.Query{}
|
||||
err := parse.ParseAs(parse.Source{Name: "query", Code: q}, qn, parse.Config{})
|
||||
func parseFilter(q string) (*parse.Filter, error) {
|
||||
qn := &parse.Filter{}
|
||||
err := parse.ParseAs(parse.Source{Name: "filter", Code: q}, qn, parse.Config{})
|
||||
return qn, err
|
||||
}
|
||||
|
||||
func compileQuery(qn *parse.Query) (Query, error) {
|
||||
func compileFilter(qn *parse.Filter) (Filter, error) {
|
||||
if len(qn.Opts) > 0 {
|
||||
return nil, notSupportedError{"option"}
|
||||
}
|
||||
|
@ -36,11 +36,11 @@ func compileQuery(qn *parse.Query) (Query, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return andQuery{qs}, nil
|
||||
return andFilter{qs}, nil
|
||||
}
|
||||
|
||||
func compileCompounds(ns []*parse.Compound) ([]Query, error) {
|
||||
qs := make([]Query, len(ns))
|
||||
func compileCompounds(ns []*parse.Compound) ([]Filter, error) {
|
||||
qs := make([]Filter, len(ns))
|
||||
for i, n := range ns {
|
||||
q, err := compileCompound(n)
|
||||
if err != nil {
|
||||
|
@ -51,13 +51,13 @@ func compileCompounds(ns []*parse.Compound) ([]Query, error) {
|
|||
return qs, nil
|
||||
}
|
||||
|
||||
func compileCompound(n *parse.Compound) (Query, error) {
|
||||
func compileCompound(n *parse.Compound) (Filter, error) {
|
||||
if pn, ok := cmpd.Primary(n); ok {
|
||||
switch pn.Type {
|
||||
case parse.Bareword, parse.SingleQuoted, parse.DoubleQuoted:
|
||||
s := pn.Value
|
||||
ignoreCase := s == strings.ToLower(s)
|
||||
return substringQuery{s, ignoreCase}, nil
|
||||
return substringFilter{s, ignoreCase}, nil
|
||||
case parse.List:
|
||||
return compileList(pn.Elements)
|
||||
}
|
||||
|
@ -65,46 +65,46 @@ func compileCompound(n *parse.Compound) (Query, error) {
|
|||
return nil, notSupportedError{cmpd.Shape(n)}
|
||||
}
|
||||
|
||||
var errEmptySubquery = errors.New("empty subquery")
|
||||
var errEmptySubfilter = errors.New("empty subfilter")
|
||||
|
||||
func compileList(elems []*parse.Compound) (Query, error) {
|
||||
func compileList(elems []*parse.Compound) (Filter, error) {
|
||||
if len(elems) == 0 {
|
||||
return nil, errEmptySubquery
|
||||
return nil, errEmptySubfilter
|
||||
}
|
||||
head, ok := cmpd.StringLiteral(elems[0])
|
||||
if !ok {
|
||||
return nil, notSupportedError{"non-literal subquery head"}
|
||||
return nil, notSupportedError{"non-literal subfilter head"}
|
||||
}
|
||||
switch head {
|
||||
case "re":
|
||||
if len(elems) == 1 {
|
||||
return nil, notSupportedError{"re subquery with no argument"}
|
||||
return nil, notSupportedError{"re subfilter with no argument"}
|
||||
}
|
||||
if len(elems) > 2 {
|
||||
return nil, notSupportedError{"re subquery with two or more arguments"}
|
||||
return nil, notSupportedError{"re subfilter with two or more arguments"}
|
||||
}
|
||||
arg := elems[1]
|
||||
s, ok := cmpd.StringLiteral(arg)
|
||||
if !ok {
|
||||
return nil, notSupportedError{"re subquery with " + cmpd.Shape(arg)}
|
||||
return nil, notSupportedError{"re subfilter with " + cmpd.Shape(arg)}
|
||||
}
|
||||
p, err := regexp.Compile(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return regexpQuery{p}, nil
|
||||
return regexpFilter{p}, nil
|
||||
case "and":
|
||||
qs, err := compileCompounds(elems[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return andQuery{qs}, nil
|
||||
return andFilter{qs}, nil
|
||||
case "or":
|
||||
qs, err := compileCompounds(elems[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return orQuery{qs}, nil
|
||||
return orFilter{qs}, nil
|
||||
default:
|
||||
return nil, notSupportedError{"head " + parse.SourceText(elems[0])}
|
||||
}
|
|
@ -1,86 +1,86 @@
|
|||
package query_test
|
||||
package filter_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"src.elv.sh/pkg/edit/query"
|
||||
"src.elv.sh/pkg/edit/filter"
|
||||
"src.elv.sh/pkg/parse"
|
||||
)
|
||||
|
||||
func TestCompile(t *testing.T) {
|
||||
test(t,
|
||||
That("empty query matches anything").
|
||||
Query("").Matches("foo", "bar", " ", ""),
|
||||
That("empty filter matches anything").
|
||||
Filter("").Matches("foo", "bar", " ", ""),
|
||||
|
||||
That("bareword matches any string containing it").
|
||||
Query("foo").Matches("foobar", "afoo").DoesNotMatch("", "faoo"),
|
||||
That("bareword is case-insensitive is query is all lower case").
|
||||
Query("foo").Matches("FOO", "Foo", "FOObar").DoesNotMatch("", "faoo"),
|
||||
That("bareword is case-sensitive is query is not all lower case").
|
||||
Query("Foo").Matches("Foobar").DoesNotMatch("foo", "FOO"),
|
||||
Filter("foo").Matches("foobar", "afoo").DoesNotMatch("", "faoo"),
|
||||
That("bareword is case-insensitive is filter is all lower case").
|
||||
Filter("foo").Matches("FOO", "Foo", "FOObar").DoesNotMatch("", "faoo"),
|
||||
That("bareword is case-sensitive is filter is not all lower case").
|
||||
Filter("Foo").Matches("Foobar").DoesNotMatch("foo", "FOO"),
|
||||
|
||||
That("double quoted string works like bareword").
|
||||
Query(`"foo"`).Matches("FOO", "Foo", "FOObar").DoesNotMatch("", "faoo"),
|
||||
Filter(`"foo"`).Matches("FOO", "Foo", "FOObar").DoesNotMatch("", "faoo"),
|
||||
|
||||
That("single quoted string works like bareword").
|
||||
Query(`'foo'`).Matches("FOO", "Foo", "FOObar").DoesNotMatch("", "faoo"),
|
||||
Filter(`'foo'`).Matches("FOO", "Foo", "FOObar").DoesNotMatch("", "faoo"),
|
||||
|
||||
That("space-separated words work like an AND query").
|
||||
Query("foo bar").
|
||||
That("space-separated words work like an AND filter").
|
||||
Filter("foo bar").
|
||||
Matches("foobar", "bar foo", "foo lorem ipsum bar").
|
||||
DoesNotMatch("foo", "bar", ""),
|
||||
|
||||
That("quoted string can be used when string contains spaces").
|
||||
Query(`"foo bar"`).
|
||||
Filter(`"foo bar"`).
|
||||
Matches("__foo bar xyz").
|
||||
DoesNotMatch("foobar"),
|
||||
|
||||
That("AND query matches if all components match").
|
||||
Query("[and foo bar]").Matches("foobar", "bar foo").DoesNotMatch("foo"),
|
||||
That("OR query matches if any component matches").
|
||||
Query("[or foo bar]").Matches("foo", "bar", "foobar").DoesNotMatch(""),
|
||||
That("RE query uses component as regular expression to match").
|
||||
Query("[re f..]").Matches("foo", "f..").DoesNotMatch("fo", ""),
|
||||
That("AND filter matches if all components match").
|
||||
Filter("[and foo bar]").Matches("foobar", "bar foo").DoesNotMatch("foo"),
|
||||
That("OR filter matches if any component matches").
|
||||
Filter("[or foo bar]").Matches("foo", "bar", "foobar").DoesNotMatch(""),
|
||||
That("RE filter uses component as regular expression to match").
|
||||
Filter("[re f..]").Matches("foo", "f..").DoesNotMatch("fo", ""),
|
||||
|
||||
// Invalid queries
|
||||
That("empty list is invalid").
|
||||
Query("[]").DoesNotCompile("empty subquery"),
|
||||
Filter("[]").DoesNotCompile("empty subfilter"),
|
||||
That("starting list with non-literal is invalid").
|
||||
Query("[[foo] bar]").
|
||||
DoesNotCompile("non-literal subquery head not supported"),
|
||||
That("RE query with no argument is invalid").
|
||||
Query("[re]").
|
||||
DoesNotCompile("re subquery with no argument not supported"),
|
||||
That("RE query with two or more arguments is invalid").
|
||||
Query("[re foo bar]").
|
||||
DoesNotCompile("re subquery with two or more arguments not supported"),
|
||||
That("RE query with invalid regular expression is invalid").
|
||||
Query("[re '[']").
|
||||
Filter("[[foo] bar]").
|
||||
DoesNotCompile("non-literal subfilter head not supported"),
|
||||
That("RE filter with no argument is invalid").
|
||||
Filter("[re]").
|
||||
DoesNotCompile("re subfilter with no argument not supported"),
|
||||
That("RE filter with two or more arguments is invalid").
|
||||
Filter("[re foo bar]").
|
||||
DoesNotCompile("re subfilter with two or more arguments not supported"),
|
||||
That("RE filter with invalid regular expression is invalid").
|
||||
Filter("[re '[']").
|
||||
DoesNotCompile("error parsing regexp: missing closing ]: `[`"),
|
||||
That("invalid syntax results in parse error").
|
||||
Query("[and").DoesNotParse("parse error: 4-4 in query: should be ']'"),
|
||||
Filter("[and").DoesNotParse("parse error: 4-4 in filter: should be ']'"),
|
||||
|
||||
// Unsupported for now, but may be in future
|
||||
That("options are not supported yet").
|
||||
Query("foo &k=v").DoesNotCompile("option not supported"),
|
||||
Filter("foo &k=v").DoesNotCompile("option not supported"),
|
||||
That("compound expressions are not supported yet").
|
||||
Query(`a"foo"`).DoesNotCompile("compound expression not supported"),
|
||||
Filter(`a"foo"`).DoesNotCompile("compound expression not supported"),
|
||||
That("indexing expressions are not supported yet").
|
||||
Query("foo[0]").DoesNotCompile("indexing expression not supported"),
|
||||
Filter("foo[0]").DoesNotCompile("indexing expression not supported"),
|
||||
That("variable references are not supported yet").
|
||||
Query("$a").
|
||||
Filter("$a").
|
||||
DoesNotCompile("primary expression of type Variable not supported"),
|
||||
That("variable references in RE subquery are not supported yet").
|
||||
Query("[re $a]").
|
||||
DoesNotCompile("re subquery with primary expression of type Variable not supported"),
|
||||
That("variable references in AND subquery are not supported yet").
|
||||
Query("[and $a]").
|
||||
That("variable references in RE subfilter are not supported yet").
|
||||
Filter("[re $a]").
|
||||
DoesNotCompile("re subfilter with primary expression of type Variable not supported"),
|
||||
That("variable references in AND subfilter are not supported yet").
|
||||
Filter("[and $a]").
|
||||
DoesNotCompile("primary expression of type Variable not supported"),
|
||||
That("variable references in OR subquery are not supported yet").
|
||||
Query("[or $a]").
|
||||
That("variable references in OR subfilter are not supported yet").
|
||||
Filter("[or $a]").
|
||||
DoesNotCompile("primary expression of type Variable not supported"),
|
||||
That("other subqueries are not supported yet").
|
||||
Query("[other foo bar]").
|
||||
Filter("[other foo bar]").
|
||||
DoesNotCompile("head other not supported"),
|
||||
)
|
||||
}
|
||||
|
@ -88,28 +88,28 @@ func TestCompile(t *testing.T) {
|
|||
func test(t *testing.T, tests ...testCase) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
q, err := query.Compile(test.query)
|
||||
q, err := filter.Compile(test.filter)
|
||||
if errType := getErrorType(err); errType != test.errorType {
|
||||
t.Errorf("%q should have %s, but has %s",
|
||||
test.query, test.errorType, errType)
|
||||
test.filter, test.errorType, errType)
|
||||
}
|
||||
if err != nil {
|
||||
if err.Error() != test.errorMessage {
|
||||
t.Errorf("%q should have error message %q, but is %q",
|
||||
test.query, test.errorMessage, err)
|
||||
test.filter, test.errorMessage, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
for _, s := range test.matches {
|
||||
ok := q.Match(s)
|
||||
if !ok {
|
||||
t.Errorf("%q should match %q, but doesn't", test.query, s)
|
||||
t.Errorf("%q should match %q, but doesn't", test.filter, s)
|
||||
}
|
||||
}
|
||||
for _, s := range test.doesntMatch {
|
||||
ok := q.Match(s)
|
||||
if ok {
|
||||
t.Errorf("%q shouldn't match %q, but does", test.query, s)
|
||||
t.Errorf("%q shouldn't match %q, but does", test.filter, s)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -118,7 +118,7 @@ func test(t *testing.T, tests ...testCase) {
|
|||
|
||||
type testCase struct {
|
||||
name string
|
||||
query string
|
||||
filter string
|
||||
matches []string
|
||||
doesntMatch []string
|
||||
errorType errorType
|
||||
|
@ -129,8 +129,8 @@ func That(name string) testCase {
|
|||
return testCase{name: name}
|
||||
}
|
||||
|
||||
func (t testCase) Query(q string) testCase {
|
||||
t.query = q
|
||||
func (t testCase) Filter(q string) testCase {
|
||||
t.filter = q
|
||||
return t
|
||||
}
|
||||
|
|
@ -1,20 +1,20 @@
|
|||
package query
|
||||
package filter
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Query represents a compiled query, which can be used to match text.
|
||||
type Query interface {
|
||||
// Filter represents a compiled filter, which can be used to match text.
|
||||
type Filter interface {
|
||||
Match(s string) bool
|
||||
}
|
||||
|
||||
type andQuery struct {
|
||||
queries []Query
|
||||
type andFilter struct {
|
||||
queries []Filter
|
||||
}
|
||||
|
||||
func (aq andQuery) Match(s string) bool {
|
||||
func (aq andFilter) Match(s string) bool {
|
||||
for _, q := range aq.queries {
|
||||
if !q.Match(s) {
|
||||
return false
|
||||
|
@ -23,11 +23,11 @@ func (aq andQuery) Match(s string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
type orQuery struct {
|
||||
queries []Query
|
||||
type orFilter struct {
|
||||
queries []Filter
|
||||
}
|
||||
|
||||
func (oq orQuery) Match(s string) bool {
|
||||
func (oq orFilter) Match(s string) bool {
|
||||
for _, q := range oq.queries {
|
||||
if q.Match(s) {
|
||||
return true
|
||||
|
@ -36,22 +36,22 @@ func (oq orQuery) Match(s string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
type substringQuery struct {
|
||||
type substringFilter struct {
|
||||
pattern string
|
||||
ignoreCase bool
|
||||
}
|
||||
|
||||
func (sq substringQuery) Match(s string) bool {
|
||||
func (sq substringFilter) Match(s string) bool {
|
||||
if sq.ignoreCase {
|
||||
s = strings.ToLower(s)
|
||||
}
|
||||
return strings.Contains(s, sq.pattern)
|
||||
}
|
||||
|
||||
type regexpQuery struct {
|
||||
type regexpFilter struct {
|
||||
pattern *regexp.Regexp
|
||||
}
|
||||
|
||||
func (rq regexpQuery) Match(s string) bool {
|
||||
func (rq regexpFilter) Match(s string) bool {
|
||||
return rq.pattern.MatchString(s)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package query
|
||||
package filter
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
func Highlight(q string) (ui.Text, []error) {
|
||||
n, _ := parseQuery(q)
|
||||
n, _ := parseFilter(q)
|
||||
w := walker{}
|
||||
w.walk(n)
|
||||
text := ui.StyleRegions(q, w.regions)
|
|
@ -1,9 +1,10 @@
|
|||
package query
|
||||
package filter_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"src.elv.sh/pkg/edit/filter"
|
||||
"src.elv.sh/pkg/ui"
|
||||
)
|
||||
|
||||
|
@ -51,7 +52,7 @@ var highlightTests = []struct {
|
|||
func TestHighlight(t *testing.T) {
|
||||
for _, test := range highlightTests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got, _ := Highlight(test.q)
|
||||
got, _ := filter.Highlight(test.q)
|
||||
if !reflect.DeepEqual(got, test.want) {
|
||||
t.Errorf("got %s, want %s", got, test.want)
|
||||
}
|
|
@ -8,7 +8,7 @@ import (
|
|||
"src.elv.sh/pkg/cli/histutil"
|
||||
"src.elv.sh/pkg/cli/mode"
|
||||
"src.elv.sh/pkg/cli/tk"
|
||||
"src.elv.sh/pkg/edit/query"
|
||||
"src.elv.sh/pkg/edit/filter"
|
||||
"src.elv.sh/pkg/eval"
|
||||
"src.elv.sh/pkg/eval/vals"
|
||||
"src.elv.sh/pkg/eval/vars"
|
||||
|
@ -43,15 +43,15 @@ func initListings(ed *Editor, ev *eval.Evaler, st store.Store, histStore histuti
|
|||
initLocation(ed, ev, st, bindingVar, nb)
|
||||
}
|
||||
|
||||
var queryFilterSpec = mode.FilterSpec{
|
||||
var filterSpec = mode.FilterSpec{
|
||||
Maker: func(f string) func(string) bool {
|
||||
q, _ := query.Compile(f)
|
||||
q, _ := filter.Compile(f)
|
||||
if q == nil {
|
||||
return func(string) bool { return true }
|
||||
}
|
||||
return q.Match
|
||||
},
|
||||
Highlighter: query.Highlight,
|
||||
Highlighter: filter.Highlight,
|
||||
}
|
||||
|
||||
func initHistlist(ed *Editor, ev *eval.Evaler, histStore histutil.Store, commonBindingVar vars.PtrVar, nb eval.NsBuilder) {
|
||||
|
@ -69,7 +69,7 @@ func initHistlist(ed *Editor, ev *eval.Evaler, histStore histutil.Store, commonB
|
|||
Dedup: func() bool {
|
||||
return dedup.Get().(bool)
|
||||
},
|
||||
Filter: queryFilterSpec,
|
||||
Filter: filterSpec,
|
||||
})
|
||||
startMode(ed.app, w, err)
|
||||
},
|
||||
|
@ -117,7 +117,7 @@ func initLocation(ed *Editor, ev *eval.Evaler, st store.Store, commonBindingVar
|
|||
IteratePinned: adaptToIterateString(pinnedVar),
|
||||
IterateHidden: adaptToIterateString(hiddenVar),
|
||||
IterateWorkspaces: workspaceIterator,
|
||||
Filter: queryFilterSpec,
|
||||
Filter: filterSpec,
|
||||
})
|
||||
startMode(ed.app, w, err)
|
||||
}).Ns())
|
||||
|
|
|
@ -124,7 +124,7 @@ func initNavigation(ed *Editor, ev *eval.Evaler, nb eval.NsBuilder) {
|
|||
WidthRatio: func() [3]int {
|
||||
return convertNavWidthRatio(widthRatioVar.Get())
|
||||
},
|
||||
Filter: queryFilterSpec,
|
||||
Filter: filterSpec,
|
||||
})
|
||||
startMode(app, w, nil)
|
||||
},
|
||||
|
|
|
@ -343,15 +343,15 @@ const (
|
|||
Append
|
||||
)
|
||||
|
||||
// Query represents an Elvish query. It uses the same syntax as arguments and
|
||||
// Filter is the Elvish filter DSL. It uses the same syntax as arguments and
|
||||
// options to a command.
|
||||
type Query struct {
|
||||
type Filter struct {
|
||||
node
|
||||
Args []*Compound
|
||||
Opts []*MapPair
|
||||
}
|
||||
|
||||
func (qn *Query) parse(ps *parser) {
|
||||
func (qn *Filter) parse(ps *parser) {
|
||||
parseSpaces(qn, ps)
|
||||
for {
|
||||
r := ps.peek()
|
||||
|
|
|
@ -146,38 +146,38 @@ var testCases = []struct {
|
|||
wantErrMsg: "should be a composite term representing fd",
|
||||
},
|
||||
|
||||
// Query
|
||||
// Filter
|
||||
{
|
||||
name: "empty query",
|
||||
name: "empty filter",
|
||||
code: "",
|
||||
node: &Query{},
|
||||
want: ast{"Query", fs{}},
|
||||
node: &Filter{},
|
||||
want: ast{"Filter", fs{}},
|
||||
},
|
||||
{
|
||||
name: "query with arguments",
|
||||
name: "filter with arguments",
|
||||
code: "foo bar",
|
||||
node: &Query{},
|
||||
want: ast{"Query", fs{"Args": []string{"foo", "bar"}}},
|
||||
node: &Filter{},
|
||||
want: ast{"Filter", fs{"Args": []string{"foo", "bar"}}},
|
||||
},
|
||||
{
|
||||
name: "query with options",
|
||||
name: "filter with options",
|
||||
code: "&foo=bar &lorem=ipsum",
|
||||
node: &Query{},
|
||||
want: ast{"Query", fs{"Opts": []string{"&foo=bar", "&lorem=ipsum"}}},
|
||||
node: &Filter{},
|
||||
want: ast{"Filter", fs{"Opts": []string{"&foo=bar", "&lorem=ipsum"}}},
|
||||
},
|
||||
{
|
||||
name: "query mixing arguments and options",
|
||||
name: "filter mixing arguments and options",
|
||||
code: "foo &a=b bar &x=y",
|
||||
node: &Query{},
|
||||
want: ast{"Query", fs{
|
||||
node: &Filter{},
|
||||
want: ast{"Filter", fs{
|
||||
"Args": []string{"foo", "bar"},
|
||||
"Opts": []string{"&a=b", "&x=y"}}},
|
||||
},
|
||||
{
|
||||
name: "query with leading and trailing whitespaces",
|
||||
name: "filter with leading and trailing whitespaces",
|
||||
code: " foo ",
|
||||
node: &Query{},
|
||||
want: ast{"Query", fs{"Args": []string{"foo"}}},
|
||||
node: &Filter{},
|
||||
want: ast{"Filter", fs{"Args": []string{"foo"}}},
|
||||
},
|
||||
|
||||
// Compound
|
||||
|
|
Loading…
Reference in New Issue
Block a user