mirror of
https://github.com/ddworken/hishtory.git
synced 2025-06-19 03:26:46 +02:00
Add support for single quotes in search queries, and add a heuristic to avoid consuming unclosed quotes
This commit is contained in:
parent
60f5a222c0
commit
8082bd5a2d
@ -984,9 +984,10 @@ func splitEscaped(query string, separator rune, maxSplit int) []string {
|
|||||||
var tokens []string
|
var tokens []string
|
||||||
splits := 1
|
splits := 1
|
||||||
runeQuery := []rune(query)
|
runeQuery := []rune(query)
|
||||||
isInQuotedString := false
|
isInDoubleQuotedString := false
|
||||||
|
isInSingleQuotedString := false
|
||||||
for i := 0; i < len(runeQuery); i++ {
|
for i := 0; i < len(runeQuery); i++ {
|
||||||
if (maxSplit < 0 || splits < maxSplit) && runeQuery[i] == separator && !isInQuotedString {
|
if (maxSplit < 0 || splits < maxSplit) && runeQuery[i] == separator && !isInSingleQuotedString && !isInDoubleQuotedString {
|
||||||
tokens = append(tokens, string(token))
|
tokens = append(tokens, string(token))
|
||||||
token = token[:0]
|
token = token[:0]
|
||||||
splits++
|
splits++
|
||||||
@ -999,8 +1000,10 @@ func splitEscaped(query string, separator rune, maxSplit int) []string {
|
|||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
token = append(token, runeQuery[i])
|
token = append(token, runeQuery[i])
|
||||||
} else if runeQuery[i] == '"' {
|
} else if runeQuery[i] == '"' && !isInSingleQuotedString && !heuristicIgnoreUnclosedQuote(isInDoubleQuotedString, '"', runeQuery, i) {
|
||||||
isInQuotedString = !isInQuotedString
|
isInDoubleQuotedString = !isInDoubleQuotedString
|
||||||
|
} else if runeQuery[i] == '\'' && !isInDoubleQuotedString && !heuristicIgnoreUnclosedQuote(isInSingleQuotedString, '\'', runeQuery, i) {
|
||||||
|
isInSingleQuotedString = !isInSingleQuotedString
|
||||||
} else {
|
} else {
|
||||||
token = append(token, runeQuery[i])
|
token = append(token, runeQuery[i])
|
||||||
}
|
}
|
||||||
@ -1009,6 +1012,23 @@ func splitEscaped(query string, separator rune, maxSplit int) []string {
|
|||||||
return tokens
|
return tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func heuristicIgnoreUnclosedQuote(isCurrentlyInQuotedString bool, quoteType rune, query []rune, idx int) bool {
|
||||||
|
if isCurrentlyInQuotedString {
|
||||||
|
// We're already in a quoted string, so the heuristic doesn't apply
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
idx++
|
||||||
|
for idx < len(query) {
|
||||||
|
if query[idx] == quoteType {
|
||||||
|
// There is a close quote, so the heuristic doesn't apply
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
// There is no unclosed quote, so we apply the heuristic and ignore the single quote
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func containsUnescaped(query string, token string) bool {
|
func containsUnescaped(query string, token string) bool {
|
||||||
runeQuery := []rune(query)
|
runeQuery := []rune(query)
|
||||||
for i := 0; i < len(runeQuery); i++ {
|
for i := 0; i < len(runeQuery); i++ {
|
||||||
|
@ -269,24 +269,49 @@ func TestSplitEscaped(t *testing.T) {
|
|||||||
limit int
|
limit int
|
||||||
expected []string
|
expected []string
|
||||||
}{
|
}{
|
||||||
|
// Basic tests
|
||||||
{"foo bar", ' ', 2, []string{"foo", "bar"}},
|
{"foo bar", ' ', 2, []string{"foo", "bar"}},
|
||||||
{"foo bar baz", ' ', 2, []string{"foo", "bar baz"}},
|
{"foo bar baz", ' ', 2, []string{"foo", "bar baz"}},
|
||||||
{"foo bar baz", ' ', 3, []string{"foo", "bar", "baz"}},
|
{"foo bar baz", ' ', 3, []string{"foo", "bar", "baz"}},
|
||||||
{"foo bar baz", ' ', 1, []string{"foo bar baz"}},
|
{"foo bar baz", ' ', 1, []string{"foo bar baz"}},
|
||||||
{"foo bar baz", ' ', -1, []string{"foo", "bar", "baz"}},
|
{"foo bar baz", ' ', -1, []string{"foo", "bar", "baz"}},
|
||||||
|
// Tests for escaping
|
||||||
{"foo\\ bar baz", ' ', -1, []string{"foo bar", "baz"}},
|
{"foo\\ bar baz", ' ', -1, []string{"foo bar", "baz"}},
|
||||||
{"foo\\bar baz", ' ', -1, []string{"foobar", "baz"}},
|
{"foo\\bar baz", ' ', -1, []string{"foobar", "baz"}},
|
||||||
{"foo\\bar baz foob", ' ', 2, []string{"foobar", "baz foob"}},
|
{"foo\\bar baz foob", ' ', 2, []string{"foobar", "baz foob"}},
|
||||||
{"foo\\ bar\\ baz", ' ', -1, []string{"foo bar baz"}},
|
{"foo\\ bar\\ baz", ' ', -1, []string{"foo bar baz"}},
|
||||||
{"foo\\ bar\\ baz", ' ', -1, []string{"foo bar ", "baz"}},
|
{"foo\\ bar\\ baz", ' ', -1, []string{"foo bar ", "baz"}},
|
||||||
|
// Tests for single quotes
|
||||||
|
{"'foo bar'", ' ', -1, []string{"foo bar"}},
|
||||||
|
{"'foo bar' ' '", ' ', -1, []string{"foo bar", " "}},
|
||||||
|
{"'foo bar baz' and", ' ', -1, []string{"foo bar baz", "and"}},
|
||||||
|
{"'foo bar baz", ' ', -1, []string{"'foo", "bar", "baz"}},
|
||||||
|
{"'foo bar baz\\''", ' ', -1, []string{"foo bar baz'"}},
|
||||||
|
{"cwd:'foo bar :baz\\''", ':', -1, []string{"cwd", "foo bar :baz'"}},
|
||||||
|
{"cwd:'foo bar :baz\\''", ' ', -1, []string{"cwd:foo bar :baz'"}},
|
||||||
|
// Tests for double quotes
|
||||||
{"\"foo bar\"", ' ', -1, []string{"foo bar"}},
|
{"\"foo bar\"", ' ', -1, []string{"foo bar"}},
|
||||||
{"\"foo bar\" \" \"", ' ', -1, []string{"foo bar", " "}},
|
{"\"foo bar\" \" \"", ' ', -1, []string{"foo bar", " "}},
|
||||||
{"\"foo bar baz\" and", ' ', -1, []string{"foo bar baz", "and"}},
|
{"\"foo bar baz\" and", ' ', -1, []string{"foo bar baz", "and"}},
|
||||||
{"\"foo bar baz\" and", ' ', -1, []string{"foo bar baz", "and"}},
|
{"\"foo bar baz", ' ', -1, []string{"\"foo", "bar", "baz"}},
|
||||||
{"\"foo bar baz", ' ', -1, []string{"foo bar baz"}},
|
|
||||||
{"\"foo bar baz\\\"\"", ' ', -1, []string{"foo bar baz\""}},
|
{"\"foo bar baz\\\"\"", ' ', -1, []string{"foo bar baz\""}},
|
||||||
{"cwd:\"foo bar :baz\\\"\"", ':', -1, []string{"cwd", "foo bar :baz\""}},
|
{"cwd:\"foo bar :baz\\\"\"", ':', -1, []string{"cwd", "foo bar :baz\""}},
|
||||||
{"cwd:\"foo bar :baz\\\"\"", ' ', -1, []string{"cwd:foo bar :baz\""}},
|
{"cwd:\"foo bar :baz\\\"\"", ' ', -1, []string{"cwd:foo bar :baz\""}},
|
||||||
|
// Tests for complex quotes
|
||||||
|
{"\"foo'bar\"", ' ', -1, []string{"foo'bar"}},
|
||||||
|
{"'foo\"bar'", ' ', -1, []string{"foo\"bar"}},
|
||||||
|
{"\"foo'bar", ' ', -1, []string{"\"foo'bar"}},
|
||||||
|
{"'foo\"bar", ' ', -1, []string{"'foo\"bar"}},
|
||||||
|
{"\"foo'\\\"bar\"", ' ', -1, []string{"foo'\"bar"}},
|
||||||
|
{"'foo\"\\'bar'", ' ', -1, []string{"foo\"'bar"}},
|
||||||
|
{"''", ' ', -1, []string{""}},
|
||||||
|
{"\"\"", ' ', -1, []string{""}},
|
||||||
|
{"\\\"", ' ', -1, []string{"\""}},
|
||||||
|
{"\\'", ' ', -1, []string{"'"}},
|
||||||
|
// Tests the behavior of quotes with
|
||||||
|
{"it's", ' ', -1, []string{"it's"}},
|
||||||
|
{"'foo bar", ' ', -1, []string{"'foo", "bar"}},
|
||||||
|
// Tests for various complex/interesting escaping
|
||||||
{"ls \\-foo", ' ', -1, []string{"ls", "\\-foo"}},
|
{"ls \\-foo", ' ', -1, []string{"ls", "\\-foo"}},
|
||||||
{"ls \\-foo \\a \\\\", ' ', -1, []string{"ls", "\\-foo", "a", "\\\\"}},
|
{"ls \\-foo \\a \\\\", ' ', -1, []string{"ls", "\\-foo", "a", "\\\\"}},
|
||||||
{"foo:bar", ':', -1, []string{"foo", "bar"}},
|
{"foo:bar", ':', -1, []string{"foo", "bar"}},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user