github.com/aretext/aretext@v1.3.0/menu/search.go (about) 1 package menu 2 3 import ( 4 "strings" 5 6 "github.com/aretext/aretext/menu/fuzzy" 7 ) 8 9 const ( 10 maxSearchItemNameLen = 1024 11 maxSearchQueryLen = 1024 12 ) 13 14 // Search performs approximate text searches for menu items matching a query string. 15 type Search struct { 16 emptyQueryShowAll bool 17 fuzzyIndex *fuzzy.Index 18 aliasIndex map[string]int 19 items []Item 20 results []Item 21 } 22 23 func NewSearch(items []Item, emptyQueryShowAll bool) *Search { 24 itemNames := make([]string, len(items)) 25 aliasIndex := make(map[string]int, 0) 26 for itemId, item := range items { 27 // Truncate long names to avoid perf issues when fuzzy searching. 28 itemNames[itemId] = truncateString(item.Name, maxSearchItemNameLen) 29 for _, alias := range item.Aliases { 30 aliasIndex[alias] = itemId 31 } 32 } 33 34 var results []Item 35 if emptyQueryShowAll { 36 results = append(results, items...) 37 } 38 39 return &Search{ 40 emptyQueryShowAll: emptyQueryShowAll, 41 fuzzyIndex: fuzzy.NewIndex(itemNames), 42 aliasIndex: aliasIndex, 43 items: items, 44 results: results, 45 } 46 } 47 48 // Execute searches for the given query. 49 func (s *Search) Execute(q string) { 50 if len(q) == 0 { 51 if s.emptyQueryShowAll { 52 s.results = make([]Item, 0, len(s.items)) 53 s.results = append(s.results, s.items...) 54 } else { 55 s.results = nil 56 } 57 return 58 } 59 60 // Truncate long queries to avoid perf issues when fuzzy searching. 61 truncatedQuery := truncateString(q, maxSearchQueryLen) 62 resultItemIds := s.fuzzyIndex.Search(truncatedQuery) 63 results := make([]Item, 0, len(resultItemIds)+1) 64 itemIdMatchingAlias := -1 65 if itemId, ok := s.aliasIndex[strings.ToLower(truncatedQuery)]; ok { 66 itemIdMatchingAlias = itemId 67 results = append(results, s.items[itemId]) 68 } 69 for _, itemId := range resultItemIds { 70 if itemId != itemIdMatchingAlias { 71 results = append(results, s.items[itemId]) 72 } 73 } 74 s.results = results 75 } 76 77 // Results returns the menu items matching the current query. 78 // Items are sorted descending by relevance to the query, 79 // with ties broken by lexicographic ordering. 80 func (s *Search) Results() []Item { 81 return s.results 82 } 83 84 func truncateString(s string, maxLen int) string { 85 if len(s) > maxLen { 86 return s[0:maxLen] 87 } else { 88 return s 89 } 90 }