github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/utils/readline/find.go (about) 1 package readline 2 3 import ( 4 "path" 5 "regexp" 6 "strings" 7 ) 8 9 var ( 10 rFindSearchPart = []rune("partial word match: ") 11 rFindCancelPart = []rune("Cancelled partial word match") 12 13 rFindSearchGlob = []rune("globbing match: ") 14 rFindCancelGlob = []rune("Cancelled globbing match") 15 16 rFindSearchRegex = []rune("regexp match: ") 17 rFindCancelRegex = []rune("Cancelled regexp match") 18 ) 19 20 type findT interface { 21 MatchString(string) bool 22 } 23 24 type fuzzyFindT struct { 25 mode int 26 tokens []string 27 } 28 29 const ( 30 ffMatchAll = 0 31 ffMatchSome = iota + 1 32 ffMatchNone 33 ffMatchRegexp 34 ffMatchGlob 35 ) 36 37 func (ff *fuzzyFindT) MatchString(item string) bool { 38 switch ff.mode { 39 40 case ffMatchSome: 41 return ff.matchSome(item) 42 43 case ffMatchNone: 44 return ff.matchNone(item) 45 46 default: 47 return ff.matchAll(item) 48 } 49 } 50 51 func (ff *fuzzyFindT) matchAll(item string) bool { 52 if len(ff.tokens) == 0 { 53 return true 54 } 55 56 for i := range ff.tokens { 57 if !strings.Contains(strings.ToLower(item), ff.tokens[i]) { 58 return false 59 } 60 } 61 62 return true 63 } 64 65 func (ff *fuzzyFindT) matchSome(item string) bool { 66 if len(ff.tokens) == 0 { 67 return true 68 } 69 70 for i := range ff.tokens { 71 if strings.Contains(strings.ToLower(item), ff.tokens[i]) { 72 return true 73 } 74 } 75 76 return false 77 } 78 79 func (ff *fuzzyFindT) matchNone(item string) bool { 80 if len(ff.tokens) == 0 { 81 return false 82 } 83 84 for i := range ff.tokens { 85 if strings.Contains(strings.ToLower(item), ff.tokens[i]) { 86 return false 87 } 88 } 89 90 return true 91 } 92 93 type globFindT struct{ pattern string } 94 95 func (gf *globFindT) MatchString(item string) bool { 96 found, _ := path.Match(gf.pattern, item) 97 return found 98 } 99 100 func newGlobFind(pattern string) (*globFindT, error) { 101 gf := new(globFindT) 102 gf.pattern = pattern 103 return gf, nil 104 } 105 106 func newFuzzyFind(pattern string) (findT, []rune, []rune, error) { 107 pattern = strings.ToLower(pattern) 108 ff := new(fuzzyFindT) 109 110 ff.tokens = strings.Split(pattern, " ") 111 112 for { 113 if len(ff.tokens) == 0 { 114 return ff, rFindSearchPart, rFindCancelPart, nil 115 } 116 117 if ff.tokens[len(ff.tokens)-1] == "" { 118 ff.tokens = ff.tokens[:len(ff.tokens)-1] 119 } else { 120 break 121 } 122 } 123 124 switch ff.tokens[0] { 125 case "or": 126 ff.mode = ffMatchSome 127 ff.tokens = ff.tokens[1:] 128 129 case "!": 130 ff.mode = ffMatchNone 131 ff.tokens = ff.tokens[1:] 132 133 case "rx": 134 ff.mode = ffMatchRegexp 135 pattern = strings.Join(ff.tokens[1:], " ") 136 find, err := regexp.Compile("(?i)" + pattern) 137 return find, rFindSearchRegex, rFindCancelRegex, err 138 139 case "g": 140 ff.mode = ffMatchGlob 141 pattern = strings.Join(ff.tokens[1:], " ") 142 find, err := newGlobFind(pattern) 143 return find, rFindSearchGlob, rFindCancelGlob, err 144 145 default: 146 if strings.Contains(pattern, "*") { 147 ff.mode = ffMatchGlob 148 find, err := newGlobFind(pattern) 149 return find, rFindSearchGlob, rFindCancelGlob, err 150 } 151 } 152 153 return ff, rFindSearchPart, rFindCancelPart, nil 154 }