81 lines
2.3 KiB
Go
81 lines
2.3 KiB
Go
package code
|
|
|
|
import (
|
|
"log/slog"
|
|
"strings"
|
|
|
|
"github.com/egoist/mygo/ui"
|
|
|
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
|
)
|
|
|
|
// TerminalSearchState is a search through a terminal's scrollback and screen.
|
|
type TerminalSearchState struct {
|
|
Query string
|
|
// Current is the chosen match, from 0.
|
|
Current int
|
|
query string
|
|
version int
|
|
ran bool
|
|
}
|
|
|
|
// TerminalSearchOptions configure a TerminalSearch.
|
|
type TerminalSearchOptions struct {
|
|
Disabled bool
|
|
}
|
|
|
|
// TerminalSearch finds Query in term and shows the current match.
|
|
func TerminalSearch(c *ui.Context, term *TerminalState, s *TerminalSearchState, opts TerminalSearchOptions) core.ChoiceResult {
|
|
if term == nil || s == nil {
|
|
panic("mujica: TerminalSearch needs a terminal and a state")
|
|
}
|
|
term.init()
|
|
fresh := terminalSearchRun(term, s)
|
|
r := input.SearchInput(c, &s.Query, input.SearchInputOptions{Find: true, Matches: len(term.matches), Current: &s.Current,
|
|
Disabled: opts.Disabled, Label: core.Msg(c, "terminal_search.label")})
|
|
var res core.ChoiceResult
|
|
res.Element = r.Element
|
|
if r.Changed() {
|
|
s.Current = 0
|
|
terminalSearchRun(term, s)
|
|
res = core.NewChoiceResult(res.Element, true)
|
|
}
|
|
if r.Moved() {
|
|
res = core.NewChoiceResult(res.Element, true)
|
|
}
|
|
if res.Changed() || fresh {
|
|
term.current = s.Current
|
|
if s.Current < len(term.matches) {
|
|
term.reveal(term.matches[s.Current].pos.Line)
|
|
}
|
|
slog.Debug("mujica: terminal_search move", "matches", len(term.matches), "current", s.Current)
|
|
}
|
|
return res
|
|
}
|
|
|
|
// terminalSearchRun refinds matches; it reports a new query.
|
|
func terminalSearchRun(term *TerminalState, s *TerminalSearchState) bool {
|
|
if s.ran && s.query == s.Query && s.version == term.version {
|
|
return false
|
|
}
|
|
fresh := !s.ran || s.query != s.Query
|
|
s.ran, s.query, s.version = true, s.Query, term.version
|
|
term.matches = term.matches[:0]
|
|
q := []rune(strings.ToLower(s.Query))
|
|
if len(q) > 0 {
|
|
for i := range term.Lines() {
|
|
line := []rune(strings.ToLower(term.LineText(i)))
|
|
for col := 0; col+len(q) <= len(line); col++ {
|
|
if string(line[col:col+len(q)]) == string(q) {
|
|
term.matches = append(term.matches, terminalMatch{TerminalPos{i, col}, len(q)})
|
|
col += len(q) - 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
s.Current = max(0, min(s.Current, len(term.matches)-1))
|
|
term.current = s.Current
|
|
return fresh
|
|
}
|