k9s/internal/model/text.go

122 lines
2.5 KiB
Go

package model
import (
"regexp"
"strings"
"github.com/derailed/k9s/internal/dao"
"github.com/sahilm/fuzzy"
)
// TextListener represents a text model listener.
type TextListener interface {
// TextChanged notifies the model changed.
TextChanged([]string)
// TextFiltered notifies when the filter changed.
TextFiltered([]string, fuzzy.Matches)
}
// Text represents a text model.
type Text struct {
lines []string
listeners []TextListener
query string
}
// NewText returns a new model.
func NewText() *Text {
return &Text{}
}
// Peek returns the current model state.
func (t *Text) Peek() []string {
return t.lines
}
// ClearFilter clear out filter.
func (t *Text) ClearFilter() {
t.query = ""
t.filterChanged(t.lines)
}
// Filter filters out the text.
func (t *Text) Filter(q string) {
t.query = q
t.filterChanged(t.lines)
}
// SetText sets the current model content.
func (t *Text) SetText(buff string) {
t.lines = strings.Split(buff, "\n")
t.fireTextChanged(t.lines)
}
// AddListener adds a new model listener.
func (t *Text) AddListener(listener TextListener) {
t.listeners = append(t.listeners, listener)
}
// RemoveListener delete a listener from the list.
func (t *Text) RemoveListener(listener TextListener) {
victim := -1
for i, lis := range t.listeners {
if lis == listener {
victim = i
break
}
}
if victim >= 0 {
t.listeners = append(t.listeners[:victim], t.listeners[victim+1:]...)
}
}
func (t *Text) filterChanged(lines []string) {
t.fireTextFiltered(lines, t.filter(t.query, lines))
}
func (t *Text) fireTextChanged(lines []string) {
for _, lis := range t.listeners {
lis.TextChanged(lines)
}
}
func (t *Text) fireTextFiltered(lines []string, matches fuzzy.Matches) {
for _, lis := range t.listeners {
lis.TextFiltered(lines, matches)
}
}
// ----------------------------------------------------------------------------
// Helpers...
func (t *Text) filter(q string, lines []string) fuzzy.Matches {
if q == "" {
return nil
}
if dao.IsFuzzySelector(q) {
return t.fuzzyFilter(strings.TrimSpace(q[2:]), lines)
}
return t.rxFilter(q, lines)
}
func (*Text) fuzzyFilter(q string, lines []string) fuzzy.Matches {
return fuzzy.Find(q, lines)
}
func (*Text) rxFilter(q string, lines []string) fuzzy.Matches {
rx, err := regexp.Compile(`(?i)` + q)
if err != nil {
return nil
}
matches := make(fuzzy.Matches, 0, len(lines))
for i, l := range lines {
if loc := rx.FindStringIndex(l); len(loc) == 2 {
matches = append(matches, fuzzy.Match{Str: q, Index: i, MatchedIndexes: loc})
}
}
return matches
}