Search in keys

pull/268/head
Anton Medvedev 9 months ago
parent a574d75e41
commit 2a2db57629
No known key found for this signature in database

@ -122,13 +122,14 @@ func main() {
searchInput.Prompt = "/"
m := &model{
head: head,
top: head,
wrap: true,
fileName: fileName,
digInput: digInput,
searchInput: searchInput,
searchResultsIndexes: make(map[*node][]match),
head: head,
top: head,
showCursor: true,
wrap: true,
fileName: fileName,
digInput: digInput,
searchInput: searchInput,
search: NewSearch(),
}
p := tea.NewProgram(m, tea.WithAltScreen(), tea.WithMouseCellMotion())
@ -142,15 +143,13 @@ type model struct {
termWidth, termHeight int
head, top *node
cursor int // cursor position [0, termHeight)
showCursor bool
wrap bool
margin int
fileName string
digInput textinput.Model
searchInput textinput.Model
searchError error
searchResults []*node
searchResultsCursor int
searchResultsIndexes map[*node][]match
search *search
}
func (m *model) Init() tea.Cmd {
@ -163,6 +162,7 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.termWidth = msg.Width
m.termHeight = msg.Height
wrapAll(m.top, m.termWidth)
m.redoSearch()
case tea.MouseMsg:
switch msg.Type {
@ -174,6 +174,7 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.MouseLeft:
m.digInput.Blur()
m.showCursor = true
if msg.Y < m.viewHeight() {
if m.cursor == msg.Y {
to := m.cursorPointsTo()
@ -230,11 +231,12 @@ func (m *model) handleSearchKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
case msg.Type == tea.KeyEscape:
m.searchInput.Blur()
m.searchInput.SetValue("")
m.search("")
m.doSearch("")
m.showCursor = true
case msg.Type == tea.KeyEnter:
m.searchInput.Blur()
m.search(m.searchInput.Value())
m.doSearch(m.searchInput.Value())
default:
m.searchInput, cmd = m.searchInput.Update(msg)
@ -280,10 +282,12 @@ func (m *model) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
case key.Matches(msg, keyMap.GotoTop):
m.head = m.top
m.cursor = 0
m.showCursor = true
case key.Matches(msg, keyMap.GotoBottom):
m.head = m.findBottom()
m.cursor = 0
m.showCursor = true
m.scrollIntoView()
case key.Matches(msg, keyMap.NextSibling):
@ -327,23 +331,27 @@ func (m *model) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
case key.Matches(msg, keyMap.Expand):
m.cursorPointsTo().expand()
m.showCursor = true
case key.Matches(msg, keyMap.CollapseRecursively):
n := m.cursorPointsTo()
if n.hasChildren() {
n.collapseRecursively()
}
m.showCursor = true
case key.Matches(msg, keyMap.ExpandRecursively):
n := m.cursorPointsTo()
if n.hasChildren() {
n.expandRecursively()
}
m.showCursor = true
case key.Matches(msg, keyMap.CollapseAll):
m.top.collapseRecursively()
m.cursor = 0
m.head = m.top
m.showCursor = true
case key.Matches(msg, keyMap.ExpandAll):
at := m.cursorPointsTo()
@ -361,6 +369,7 @@ func (m *model) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
if at.chunk != nil && at.value == nil {
at = at.parent()
}
m.redoSearch()
m.selectNode(at)
case key.Matches(msg, keyMap.Dig):
@ -375,15 +384,16 @@ func (m *model) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
m.searchInput.Focus()
case key.Matches(msg, keyMap.SearchNext):
m.selectSearchResult(m.searchResultsCursor + 1)
m.selectSearchResult(m.search.cursor + 1)
case key.Matches(msg, keyMap.SearchPrev):
m.selectSearchResult(m.searchResultsCursor - 1)
m.selectSearchResult(m.search.cursor - 1)
}
return m, nil
}
func (m *model) up() {
m.showCursor = true
m.cursor--
if m.cursor < 0 {
m.cursor = 0
@ -394,6 +404,7 @@ func (m *model) up() {
}
func (m *model) down() {
m.showCursor = true
m.cursor++
n := m.cursorPointsTo()
if n == nil {
@ -443,19 +454,18 @@ func (m *model) View() string {
screen = append(screen, ' ', ' ')
}
selected := m.cursor == lineNumber
isSelected := m.cursor == lineNumber
if !m.showCursor {
isSelected = false // don't highlight the cursor while iterating search results
}
if n.key != nil {
keyColor := currentTheme.Key
if m.cursor == lineNumber {
keyColor = currentTheme.Cursor
}
screen = append(screen, keyColor(n.key)...)
screen = append(screen, m.prettyKey(n, isSelected)...)
screen = append(screen, colon...)
selected = false // don't highlight the key's value
isSelected = false // don't highlight the key's value
}
screen = append(screen, m.prettyPrint(n, selected)...)
screen = append(screen, m.prettyPrint(n, isSelected)...)
if n.isCollapsed() {
if n.value[0] == '{' {
@ -501,12 +511,12 @@ func (m *model) View() string {
if ci {
re += "i"
}
if m.searchError != nil {
screen = append(screen, flex(m.termWidth, re, m.searchError.Error())...)
} else if len(m.searchResults) == 0 {
if m.search.err != nil {
screen = append(screen, flex(m.termWidth, re, m.search.err.Error())...)
} else if len(m.search.results) == 0 {
screen = append(screen, flex(m.termWidth, re, "not found")...)
} else {
cursor := fmt.Sprintf("found: [%v/%v]", m.searchResultsCursor+1, len(m.searchResults))
cursor := fmt.Sprintf("found: [%v/%v]", m.search.cursor+1, len(m.search.results))
screen = append(screen, flex(m.termWidth, re, cursor)...)
}
}
@ -514,6 +524,31 @@ func (m *model) View() string {
return string(screen)
}
func (m *model) prettyKey(node *node, selected bool) []byte {
b := node.key
style := currentTheme.Key
if selected {
style = currentTheme.Cursor
}
if indexes, ok := m.search.keys[node]; ok {
var out []byte
for i, кусочек := range splitBytesByIndexes(b, indexes) {
if i%2 == 0 {
out = append(out, style(кусочек.b)...)
} else if кусочек.index == m.search.cursor {
out = append(out, currentTheme.Cursor(кусочек.b)...)
} else {
out = append(out, currentTheme.Search(кусочек.b)...)
}
}
return out
} else {
return style(b)
}
}
func (m *model) prettyPrint(node *node, selected bool) []byte {
var b []byte
if node.chunk != nil {
@ -526,44 +561,17 @@ func (m *model) prettyPrint(node *node, selected bool) []byte {
return b
}
var style color
if selected {
style = currentTheme.Cursor
} else if node.chunk != nil {
style = currentTheme.String
} else {
if node.chunk != nil {
style = currentTheme.String
}
switch b[0] {
case '"':
style = currentTheme.String
case 't', 'f':
style = currentTheme.Boolean
case 'n':
style = currentTheme.Null
case '{', '[', '}', ']':
style = currentTheme.Syntax
default:
if isDigit(b[0]) || b[0] == '-' {
style = currentTheme.Number
}
style = noColor
}
}
style := valueStyle(b, selected, node.chunk != nil)
if indexes, ok := m.searchResultsIndexes[node]; ok {
куски := splitBytesByIndexes(b, indexes)
if indexes, ok := m.search.values[node]; ok {
var out []byte
for i, кусочек := range куски {
for i, кусочек := range splitBytesByIndexes(b, indexes) {
if i%2 == 0 {
out = append(out, style(кусочек.b)...)
} else if кусочек.index == m.search.cursor {
out = append(out, currentTheme.Cursor(кусочек.b)...)
} else {
if кусочек.index == m.searchResultsCursor {
out = append(out, currentTheme.Cursor(кусочек.b)...)
} else {
out = append(out, currentTheme.Search(кусочек.b)...)
}
out = append(out, currentTheme.Search(кусочек.b)...)
}
}
return out
@ -638,6 +646,7 @@ func (m *model) selectNodeInView(n *node) {
}
func (m *model) selectNode(n *node) {
m.showCursor = true
if m.nodeInsideView(n) {
m.selectNodeInView(n)
m.scrollIntoView()
@ -701,11 +710,9 @@ func (m *model) dig(value string) *node {
return n
}
func (m *model) search(s string) {
m.searchError = nil
m.searchResults = nil
m.searchResultsCursor = 0
m.searchResultsIndexes = make(map[*node][]match)
func (m *model) doSearch(s string) {
m.search = NewSearch()
if s == "" {
return
}
@ -717,17 +724,27 @@ func (m *model) search(s string) {
re, err := regexp.Compile(code)
if err != nil {
m.searchError = err
m.search.err = err
return
}
n := m.top
searchIndex := 0
for n != nil {
if n.key != nil {
indexes := re.FindAllIndex(n.key, -1)
if len(indexes) > 0 {
for i, pair := range indexes {
m.search.results = append(m.search.results, n)
m.search.keys[n] = append(m.search.keys[n], match{start: pair[0], end: pair[1], index: searchIndex + i})
}
searchIndex += len(indexes)
}
}
indexes := re.FindAllIndex(n.value, -1)
if len(indexes) > 0 {
for range indexes {
m.searchResults = append(m.searchResults, n)
m.search.results = append(m.search.results, n)
}
if n.chunk != nil && n.hasChildren() {
// String can be split into chunks, so we need to map the indexes to the chunks.
@ -752,11 +769,11 @@ func (m *model) search(s string) {
chunkMatches := splitIndexesToChunks(chunks, indexes, searchIndex)
for i, matches := range chunkMatches {
m.searchResultsIndexes[chunkNodes[i]] = matches
m.search.values[chunkNodes[i]] = matches
}
} else {
for i, pair := range indexes {
m.searchResultsIndexes[n] = append(m.searchResultsIndexes[n], match{start: pair[0], end: pair[1], index: searchIndex + i})
m.search.values[n] = append(m.search.values[n], match{start: pair[0], end: pair[1], index: searchIndex + i})
}
}
searchIndex += len(indexes)
@ -773,16 +790,25 @@ func (m *model) search(s string) {
}
func (m *model) selectSearchResult(i int) {
if len(m.searchResults) == 0 {
if len(m.search.results) == 0 {
return
}
if i < 0 {
i = len(m.searchResults) - 1
i = len(m.search.results) - 1
}
if i >= len(m.searchResults) {
if i >= len(m.search.results) {
i = 0
}
m.searchResultsCursor = i
result := m.searchResults[i]
m.search.cursor = i
result := m.search.results[i]
m.selectNode(result)
m.showCursor = false
}
func (m *model) redoSearch() {
if m.searchInput.Value() != "" && len(m.search.results) > 0 {
cursor := m.search.cursor
m.doSearch(m.searchInput.Value())
m.selectSearchResult(cursor)
}
}

@ -1,5 +1,21 @@
package main
type search struct {
err error
results []*node
cursor int
values map[*node][]match
keys map[*node][]match
}
func NewSearch() *search {
return &search{
results: make([]*node, 0),
values: make(map[*node][]match),
keys: make(map[*node][]match),
}
}
type match struct {
start, end int
index int

@ -272,3 +272,27 @@ func themeTester() {
println()
}
}
func valueStyle(b []byte, selected, chunk bool) color {
if selected {
return currentTheme.Cursor
} else if chunk {
return currentTheme.String
} else {
switch b[0] {
case '"':
return currentTheme.String
case 't', 'f':
return currentTheme.Boolean
case 'n':
return currentTheme.Null
case '{', '[', '}', ']':
return currentTheme.Syntax
default:
if isDigit(b[0]) || b[0] == '-' {
return currentTheme.Number
}
return noColor
}
}
}

Loading…
Cancel
Save