diff --git a/src/algo/algo.go b/src/algo/algo.go index 3d5edb0..c93563a 100644 --- a/src/algo/algo.go +++ b/src/algo/algo.go @@ -1,6 +1,7 @@ package algo import ( + "strings" "unicode" "github.com/junegunn/fzf/src/util" @@ -159,3 +160,17 @@ func SuffixMatch(caseSensitive bool, input *[]rune, pattern []rune) (int, int) { } return trimmedLen - len(pattern), trimmedLen } + +func EqualMatch(caseSensitive bool, runes *[]rune, pattern []rune) (int, int) { + if len(*runes) != len(pattern) { + return -1, -1 + } + runesStr := string(*runes) + if !caseSensitive { + runesStr = strings.ToLower(runesStr) + } + if runesStr == string(pattern) { + return 0, len(pattern) + } + return -1, -1 +} diff --git a/src/pattern.go b/src/pattern.go index 64e0c6e..ffdf6d8 100644 --- a/src/pattern.go +++ b/src/pattern.go @@ -24,6 +24,7 @@ const ( termExact termPrefix termSuffix + termEqual ) type term struct { @@ -116,6 +117,7 @@ func BuildPattern(mode Mode, caseMode Case, procFun: make(map[termType]func(bool, *[]rune, []rune) (int, int))} ptr.procFun[termFuzzy] = algo.FuzzyMatch + ptr.procFun[termEqual] = algo.EqualMatch ptr.procFun[termExact] = algo.ExactMatchNaive ptr.procFun[termPrefix] = algo.PrefixMatch ptr.procFun[termSuffix] = algo.SuffixMatch @@ -151,8 +153,13 @@ func parseTerms(mode Mode, caseMode Case, str string) []term { text = text[1:] } } else if strings.HasPrefix(text, "^") { - typ = termPrefix - text = text[1:] + if strings.HasSuffix(text, "$") { + typ = termEqual + text = text[1 : len(text)-1] + } else { + typ = termPrefix + text = text[1:] + } } else if strings.HasSuffix(text, "$") { typ = termSuffix text = text[:len(text)-1] diff --git a/src/pattern_test.go b/src/pattern_test.go index 7f00272..fe6561c 100644 --- a/src/pattern_test.go +++ b/src/pattern_test.go @@ -8,8 +8,8 @@ import ( func TestParseTermsExtended(t *testing.T) { terms := parseTerms(ModeExtended, CaseSmart, - "aaa 'bbb ^ccc ddd$ !eee !'fff !^ggg !hhh$") - if len(terms) != 8 || + "aaa 'bbb ^ccc ddd$ !eee !'fff !^ggg !hhh$ ^iii$") + if len(terms) != 9 || terms[0].typ != termFuzzy || terms[0].inv || terms[1].typ != termExact || terms[1].inv || terms[2].typ != termPrefix || terms[2].inv || @@ -17,7 +17,8 @@ func TestParseTermsExtended(t *testing.T) { terms[4].typ != termFuzzy || !terms[4].inv || terms[5].typ != termExact || !terms[5].inv || terms[6].typ != termPrefix || !terms[6].inv || - terms[7].typ != termSuffix || !terms[7].inv { + terms[7].typ != termSuffix || !terms[7].inv || + terms[8].typ != termEqual || terms[8].inv { t.Errorf("%s", terms) } for idx, term := range terms { @@ -65,6 +66,22 @@ func TestExact(t *testing.T) { } } +func TestEqual(t *testing.T) { + defer clearPatternCache() + clearPatternCache() + pattern := BuildPattern(ModeExtended, CaseSmart, []Range{}, nil, []rune("^AbC$")) + + match := func(str string, sidxExpected int, eidxExpected int) { + runes := []rune(str) + sidx, eidx := algo.EqualMatch(pattern.caseSensitive, &runes, pattern.terms[0].text) + if sidx != sidxExpected || eidx != eidxExpected { + t.Errorf("%s / %d / %d", pattern.terms, sidx, eidx) + } + } + match("ABC", -1, -1) + match("AbC", 0, 3) +} + func TestCaseSensitivity(t *testing.T) { defer clearPatternCache() clearPatternCache()