fx/search.go
2022-04-16 22:16:19 +02:00

120 lines
2.6 KiB
Go

package main
import (
"encoding/json"
"fmt"
"regexp"
)
type searchResult struct {
key, value [][]int
}
func (m *model) doSearch(s string) {
re, err := regexp.Compile("(?i)" + s)
if err != nil {
m.searchRegexCompileError = err.Error()
m.searchInput.Blur()
return
}
m.searchRegexCompileError = ""
results := newDict()
addSearchResult := func(path string, indexes [][]int) {
if indexes != nil {
sr := searchResult{}
prev, ok := results.get(path)
if ok {
sr = prev.(searchResult)
}
sr.value = indexes
results.set(path, sr)
}
}
dfs(m.json, func(it iterator) {
switch it.object.(type) {
case nil:
line := "null"
found := re.FindAllStringIndex(line, -1)
addSearchResult(it.path, found)
case bool:
line := stringify(it.object)
found := re.FindAllStringIndex(line, -1)
addSearchResult(it.path, found)
case json.Number:
line := it.object.(json.Number).String()
found := re.FindAllStringIndex(line, -1)
addSearchResult(it.path, found)
case string:
line := fmt.Sprintf("%q", it.object)
found := re.FindAllStringIndex(line, -1)
addSearchResult(it.path, found)
case *dict:
keys := it.object.(*dict).keys
for _, key := range keys {
line := fmt.Sprintf("%q", key)
subpath := it.path + "." + key
indexes := re.FindAllStringIndex(line, -1)
if indexes != nil {
sr := searchResult{}
prev, ok := results.get(subpath)
if ok {
sr = prev.(searchResult)
}
sr.key = indexes
results.set(subpath, sr)
}
}
}
})
m.searchResults = results
m.searchInput.Blur()
m.showSearchResults = true
m.jumpToSearchResult(0)
}
func (m *model) jumpToSearchResult(at int) {
if m.searchResults == nil || len(m.searchResults.keys) == 0 {
return
}
m.showCursor = false
m.resultsCursor = at % len(m.searchResults.keys)
desiredPath := m.searchResults.keys[m.resultsCursor]
lineNumber, ok := m.pathToLineNumber.get(desiredPath)
if ok {
m.cursor = m.pathToLineNumber.indexes[desiredPath]
m.SetOffset(lineNumber.(int))
m.render()
} else {
m.expandToPath(desiredPath)
m.render()
m.jumpToSearchResult(at)
}
}
func (m *model) expandToPath(path string) {
m.expandedPaths[path] = true
if path != "" {
m.expandToPath(m.parents[path])
}
}
func (m *model) nextSearchResult() {
m.jumpToSearchResult((m.resultsCursor + 1) % len(m.searchResults.keys))
}
func (m *model) prevSearchResult() {
i := m.resultsCursor - 1
if i < 0 {
i = len(m.searchResults.keys) - 1
}
m.jumpToSearchResult(i)
}
func (m *model) resultsCursorPath() string {
if m.searchResults == nil || len(m.searchResults.keys) == 0 {
return "?"
}
return m.searchResults.keys[m.resultsCursor]
}