fork of https://github.com/sourcegraph/zoekt
0

Configure Feed

Select the types of activity you want to include in your feed.

query: case has effect in nested expressions (#1022)

Previously only the "top-level" "case:" affected the query. So if you
had a "case" in any sub expression it would have no effect. This adjusts
our implementation to track nested cases in our query parser and allow
them to affect the final case sensitivity on query.Qs.

Test Plan: added more test cases to demonstrate the problem. These
failed before this commit.

+74
+54
query/parse.go
··· 68 68 return "orOp" 69 69 } 70 70 71 + // caseScopeQ is a parse-time wrapper used to prevent case directives from an 72 + // outer expression list from overriding an explicitly scoped inner `case:`. 73 + type caseScopeQ struct { 74 + Child Q 75 + } 76 + 77 + func (c *caseScopeQ) String() string { 78 + return c.Child.String() 79 + } 80 + 81 + func stripCaseScopesList(qs []Q) []Q { 82 + stripped := make([]Q, len(qs)) 83 + for i, q := range qs { 84 + stripped[i] = stripCaseScopes(q) 85 + } 86 + return stripped 87 + } 88 + 89 + func stripCaseScopes(q Q) Q { 90 + switch s := q.(type) { 91 + case *And: 92 + return &And{Children: stripCaseScopesList(s.Children)} 93 + case *Or: 94 + return &Or{Children: stripCaseScopesList(s.Children)} 95 + case *Not: 96 + return &Not{Child: stripCaseScopes(s.Child)} 97 + case *Type: 98 + return &Type{Type: s.Type, Child: stripCaseScopes(s.Child)} 99 + case *Boost: 100 + return &Boost{Boost: s.Boost, Child: stripCaseScopes(s.Child)} 101 + case *caseScopeQ: 102 + return stripCaseScopes(s.Child) 103 + default: 104 + return q 105 + } 106 + } 107 + 71 108 func isSpace(c byte) bool { 72 109 return c == ' ' || c == '\t' 73 110 } ··· 89 126 if err != nil { 90 127 return nil, err 91 128 } 129 + 130 + q = stripCaseScopes(q) 92 131 93 132 return Simplify(q), nil 94 133 } ··· 354 393 } 355 394 356 395 setCase := "auto" 396 + hasCaseScope := false 357 397 newQS := qs[:0] 358 398 typeT := uint8(100) 359 399 for _, q := range qs { 360 400 switch s := q.(type) { 361 401 case *caseQ: 362 402 setCase = s.Flavor 403 + hasCaseScope = true 363 404 case *Type: 364 405 if s.Type < typeT { 365 406 typeT = s.Type ··· 377 418 if typeT != 100 { 378 419 qs = []Q{&Type{Type: typeT, Child: NewAnd(qs...)}} 379 420 } 421 + 422 + if hasCaseScope { 423 + scoped := make([]Q, 0, len(qs)) 424 + for _, q := range qs { 425 + if _, isOrOperator := q.(*orOperator); isOrOperator { 426 + scoped = append(scoped, q) 427 + continue 428 + } 429 + scoped = append(scoped, &caseScopeQ{Child: q}) 430 + } 431 + qs = scoped 432 + } 433 + 380 434 return qs, len(in) - len(b), nil 381 435 } 382 436
+20
query/parse_test.go
··· 108 108 &Substring{Pattern: "abc", CaseSensitive: true}, 109 109 &Not{Child: &Substring{Pattern: "def", FileName: true, CaseSensitive: true}}, 110 110 )}, 111 + {"(foo case:yes) bar", NewAnd( 112 + &Substring{Pattern: "foo", CaseSensitive: true}, 113 + &Substring{Pattern: "bar"}, 114 + )}, 115 + {"(case:yes foo) bar", NewAnd( 116 + &Substring{Pattern: "foo", CaseSensitive: true}, 117 + &Substring{Pattern: "bar"}, 118 + )}, 119 + {"(case:yes foo (bar))", NewAnd( 120 + &Substring{Pattern: "foo", CaseSensitive: true}, 121 + &Substring{Pattern: "bar", CaseSensitive: true}, 122 + )}, 123 + {"case:auto (foo case:yes) bar", NewAnd( 124 + &Substring{Pattern: "foo", CaseSensitive: true}, 125 + &Substring{Pattern: "bar"}, 126 + )}, 127 + {"case:yes (foo case:no) bar", NewAnd( 128 + &Substring{Pattern: "foo"}, 129 + &Substring{Pattern: "bar", CaseSensitive: true}, 130 + )}, 111 131 112 132 // type 113 133 {"type:repo abc", &Type{Type: TypeRepo, Child: &Substring{Pattern: "abc"}}},