parent
67cdba1b30
commit
3a02001695
|
|
@ -136,6 +136,7 @@ func loadConfiguration() (*config.Config, error) {
|
|||
return nil, err
|
||||
}
|
||||
conn, err := client.InitConnection(k8sCfg)
|
||||
k9sCfg.SetConnection(conn)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("failed to connect to cluster")
|
||||
} else {
|
||||
|
|
@ -143,12 +144,14 @@ func loadConfiguration() (*config.Config, error) {
|
|||
if !k9sCfg.GetConnection().CheckConnectivity() {
|
||||
log.Panic().Msgf("K9s can't connect to cluster")
|
||||
}
|
||||
if !k9sCfg.GetConnection().ConnectionOK() {
|
||||
panic("No connectivity")
|
||||
}
|
||||
log.Info().Msg("✅ Kubernetes connectivity")
|
||||
if err := k9sCfg.Save(); err != nil {
|
||||
log.Error().Err(err).Msg("Config save")
|
||||
}
|
||||
}
|
||||
k9sCfg.SetConnection(conn)
|
||||
|
||||
return k9sCfg, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ const (
|
|||
cacheExpiry = 5 * time.Minute
|
||||
cacheMXKey = "metrics"
|
||||
cacheMXAPIKey = "metricsAPI"
|
||||
checkConnTimeout = 5 * time.Second
|
||||
checkConnTimeout = 3 * time.Second
|
||||
|
||||
// CallTimeout represents default api call timeout.
|
||||
CallTimeout = 5 * time.Second
|
||||
|
|
@ -64,9 +64,10 @@ func InitConnection(config *Config) (*APIClient, error) {
|
|||
config: config,
|
||||
cache: cache.NewLRUExpireCache(cacheSize),
|
||||
}
|
||||
a.connOK = true
|
||||
_, err := a.supportsMetricsResources()
|
||||
if err == nil {
|
||||
a.connOK = true
|
||||
if err != nil {
|
||||
a.connOK = false
|
||||
}
|
||||
|
||||
return &a, err
|
||||
|
|
@ -136,7 +137,7 @@ func (a *APIClient) clearCache() {
|
|||
func (a *APIClient) CanI(ns, gvr string, verbs []string) (auth bool, err error) {
|
||||
log.Debug().Msgf("Check Access %q::%q", ns, gvr)
|
||||
if !a.connOK {
|
||||
return false, errors.New("no API server connection")
|
||||
return false, errors.New("ACCESS -- No API server connection")
|
||||
}
|
||||
if IsClusterWide(ns) {
|
||||
ns = AllNamespaces
|
||||
|
|
@ -148,6 +149,7 @@ func (a *APIClient) CanI(ns, gvr string, verbs []string) (auth bool, err error)
|
|||
}
|
||||
}
|
||||
|
||||
log.Debug().Msgf("----> Calling API")
|
||||
dial, err := a.Dial()
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
@ -229,12 +231,13 @@ func (a *APIClient) CheckConnectivity() (status bool) {
|
|||
if err != nil {
|
||||
return false
|
||||
}
|
||||
cfg.Timeout = checkConnTimeout
|
||||
client, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("Unable to connect to api server")
|
||||
return
|
||||
}
|
||||
log.Debug().Msgf("Checking APIServer on %#v", cfg.Host)
|
||||
log.Debug().Msgf("CONN-CHECK on %#v", cfg.Host)
|
||||
|
||||
// Check connection
|
||||
if _, err := client.ServerVersion(); err == nil {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
|
|
@ -299,8 +298,6 @@ func (c *Config) RESTConfig() (*restclient.Config, error) {
|
|||
}
|
||||
c.restConfig.QPS = defaultQPS
|
||||
c.restConfig.Burst = defaultBurst
|
||||
c.restConfig.Timeout = checkConnTimeout
|
||||
log.Debug().Msgf("Connecting to API Server %s", c.restConfig.Host)
|
||||
|
||||
return c.restConfig, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@ import (
|
|||
"fmt"
|
||||
)
|
||||
|
||||
// ColorFmt colorize a string with ansi colors.
|
||||
const ColorFmt = "\x1b[%dm%s\x1b[0m"
|
||||
const (
|
||||
colorFmt = "\x1b[%dm%s\x1b[0m"
|
||||
ansiColorFmt = "\033[38;5;%dm%s\033[0m"
|
||||
)
|
||||
|
||||
// Paint describes a terminal color.
|
||||
type Paint int
|
||||
|
|
@ -30,5 +32,29 @@ func Colorize(s string, c Paint) string {
|
|||
if c == 0 {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf(ColorFmt, c, s)
|
||||
return fmt.Sprintf(colorFmt, c, s)
|
||||
}
|
||||
|
||||
// AnsiColorize colors a string.
|
||||
func AnsiColorize(s string, c int) string {
|
||||
return fmt.Sprintf(ansiColorFmt, c, s)
|
||||
}
|
||||
|
||||
// Highlight colorize bytes at given indices.
|
||||
func Highlight(bb []byte, ii []int, c int) []byte {
|
||||
b := make([]byte, 0, len(bb))
|
||||
for i, j := 0, 0; i < len(bb); i++ {
|
||||
if j < len(ii) && ii[j] == i {
|
||||
b = append(b, colorizeByte(bb[i], 209)...)
|
||||
j++
|
||||
} else {
|
||||
b = append(b, bb[i])
|
||||
}
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func colorizeByte(b byte, color int) []byte {
|
||||
return []byte(AnsiColorize(string(b), color))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,3 +25,26 @@ func TestColorize(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHighlight(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
text []byte
|
||||
indices []int
|
||||
color int
|
||||
e string
|
||||
}{
|
||||
"white": {
|
||||
text: []byte("the brown fox"),
|
||||
color: 209,
|
||||
indices: []int{4, 5, 6, 7, 8},
|
||||
e: "the \x1b[38;5;209mb\x1b[0m\x1b[38;5;209mr\x1b[0m\x1b[38;5;209mo\x1b[0m\x1b[38;5;209mw\x1b[0m\x1b[38;5;209mn\x1b[0m fox",
|
||||
},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, string(color.Highlight([]byte(u.text), u.indices, u.color)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal/color"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sahilm/fuzzy"
|
||||
|
|
@ -51,6 +52,18 @@ func (l *LogItem) ID() string {
|
|||
return l.Container
|
||||
}
|
||||
|
||||
func (l *LogItem) Clone() *LogItem {
|
||||
bytes := make([]byte, len(l.Bytes))
|
||||
copy(bytes, l.Bytes)
|
||||
return &LogItem{
|
||||
Container: l.Container,
|
||||
Pod: l.Pod,
|
||||
Timestamp: l.Timestamp,
|
||||
SingleContainer: l.SingleContainer,
|
||||
Bytes: bytes,
|
||||
}
|
||||
}
|
||||
|
||||
// Info returns pod and container information.
|
||||
func (l *LogItem) Info() string {
|
||||
return fmt.Sprintf("%q::%q", l.Pod, l.Container)
|
||||
|
|
@ -61,26 +74,19 @@ func (l *LogItem) IsEmpty() bool {
|
|||
return len(l.Bytes) == 0
|
||||
}
|
||||
|
||||
const colorFmt = "\033[38;5;%dm%s\033[0m"
|
||||
|
||||
// colorize me
|
||||
func colorize(s string, c int) string {
|
||||
return fmt.Sprintf(colorFmt, c, s)
|
||||
}
|
||||
|
||||
// Render returns a log line as string.
|
||||
func (l *LogItem) Render(c int, showTime bool) []byte {
|
||||
bb := make([]byte, 0, 30+len(l.Bytes)+len(l.Info()))
|
||||
if showTime {
|
||||
bb = append(bb, colorize(fmt.Sprintf("%-30s ", l.Timestamp), 106)...)
|
||||
bb = append(bb, color.AnsiColorize(fmt.Sprintf("%-30s ", l.Timestamp), 106)...)
|
||||
}
|
||||
|
||||
if l.Pod != "" {
|
||||
bb = append(bb, []byte(colorize(l.Pod, c))...)
|
||||
bb = append(bb, []byte(color.AnsiColorize(l.Pod, c))...)
|
||||
bb = append(bb, ':')
|
||||
}
|
||||
if !l.SingleContainer && l.Container != "" {
|
||||
bb = append(bb, []byte(colorize(l.Container, c))...)
|
||||
bb = append(bb, []byte(color.AnsiColorize(l.Container, c))...)
|
||||
bb = append(bb, ' ')
|
||||
}
|
||||
bb = append(bb, []byte(tview.Escape(string(l.Bytes)))...)
|
||||
|
|
@ -139,45 +145,54 @@ func (l LogItems) DumpDebug(m string) {
|
|||
}
|
||||
|
||||
// Filter filters out log items based on given filter.
|
||||
func (l LogItems) Filter(q string) ([]int, error) {
|
||||
func (l LogItems) Filter(q string) ([]int, [][]int, error) {
|
||||
if q == "" {
|
||||
return nil, nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
if IsFuzzySelector(q) {
|
||||
return l.fuzzyFilter(strings.TrimSpace(q[2:])), nil
|
||||
mm, ii := l.fuzzyFilter(strings.TrimSpace(q[2:]))
|
||||
return mm, ii, nil
|
||||
}
|
||||
indexes, err := l.filterLogs(q)
|
||||
matches, indices, err := l.filterLogs(q)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("Logs filter failed")
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
return indexes, nil
|
||||
return matches, indices, nil
|
||||
}
|
||||
|
||||
var fuzzyRx = regexp.MustCompile(`\A\-f`)
|
||||
|
||||
func (l LogItems) fuzzyFilter(q string) []int {
|
||||
func (l LogItems) fuzzyFilter(q string) ([]int, [][]int) {
|
||||
q = strings.TrimSpace(q)
|
||||
matches := make([]int, 0, len(l))
|
||||
matches, indices := make([]int, 0, len(l)), make([][]int, 0, 10)
|
||||
mm := fuzzy.Find(q, l.Lines())
|
||||
for _, m := range mm {
|
||||
matches = append(matches, m.Index)
|
||||
indices = append(indices, m.MatchedIndexes)
|
||||
}
|
||||
|
||||
return matches
|
||||
return matches, indices
|
||||
}
|
||||
|
||||
func (l LogItems) filterLogs(q string) ([]int, error) {
|
||||
func (l LogItems) filterLogs(q string) ([]int, [][]int, error) {
|
||||
rx, err := regexp.Compile(`(?i)` + q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
matches := make([]int, 0, len(l))
|
||||
matches, indices := make([]int, 0, len(l)), make([][]int, 0, 10)
|
||||
for i, line := range l.Lines() {
|
||||
if rx.MatchString(line) {
|
||||
if locs := rx.FindStringIndex(line); locs != nil {
|
||||
matches = append(matches, i)
|
||||
ii := make([]int, 0, 10)
|
||||
for i := 0; i < len(locs); i += 2 {
|
||||
for j := locs[i]; j < locs[i+1]; j++ {
|
||||
ii = append(ii, j)
|
||||
}
|
||||
}
|
||||
indices = append(indices, ii)
|
||||
}
|
||||
}
|
||||
|
||||
return matches, nil
|
||||
return matches, indices, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ func TestLogItemsFilter(t *testing.T) {
|
|||
for _, i := range ii {
|
||||
i.Pod, i.Container = n, u.opts.Container
|
||||
}
|
||||
res, err := ii.Filter(u.q)
|
||||
res, _, err := ii.Filter(u.q)
|
||||
assert.Equal(t, u.err, err)
|
||||
if err == nil {
|
||||
assert.Equal(t, u.e, res)
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ func loadRBAC(m ResourceMetas) {
|
|||
|
||||
func loadPreferred(f Factory, m ResourceMetas) error {
|
||||
if !f.Client().ConnectionOK() {
|
||||
log.Error().Msgf("no API server connection")
|
||||
log.Error().Msgf("PreferredRES - No API server connection")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/color"
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/dao"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
|
@ -277,21 +278,24 @@ func applyFilter(q string, lines dao.LogItems) (dao.LogItems, error) {
|
|||
if q == "" {
|
||||
return lines, nil
|
||||
}
|
||||
indexes, err := lines.Filter(q)
|
||||
matches, indices, err := lines.Filter(q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// No filter!
|
||||
if indexes == nil {
|
||||
if matches == nil {
|
||||
return lines, nil
|
||||
}
|
||||
// Blank filter
|
||||
if len(indexes) == 0 {
|
||||
if len(matches) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
filtered := make(dao.LogItems, 0, len(indexes))
|
||||
for _, idx := range indexes {
|
||||
filtered = append(filtered, lines[idx])
|
||||
filtered := make(dao.LogItems, 0, len(matches))
|
||||
for i, idx := range matches {
|
||||
item := lines[idx].Clone()
|
||||
item.Bytes = color.Highlight(item.Bytes, indices[i], 209)
|
||||
filtered = append(filtered, item)
|
||||
}
|
||||
|
||||
return filtered, nil
|
||||
|
|
|
|||
|
|
@ -195,7 +195,8 @@ func TestLogTimedout(t *testing.T) {
|
|||
assert.Equal(t, 1, v.dataCalled)
|
||||
assert.Equal(t, 2, v.clearCalled)
|
||||
assert.Equal(t, 0, v.errCalled)
|
||||
assert.Equal(t, dao.LogItems{data[0]}, v.data)
|
||||
const e = "\x1b[38;5;209ml\x1b[0m\x1b[38;5;209mi\x1b[0m\x1b[38;5;209mn\x1b[0m\x1b[38;5;209me\x1b[0m\x1b[38;5;209m1\x1b[0m"
|
||||
assert.Equal(t, e, string(v.data[0].Bytes))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package ui
|
|||
import (
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// SelectTable represents a table with selections.
|
||||
|
|
@ -51,6 +52,17 @@ func (s *SelectTable) GetSelectedItems() []string {
|
|||
return items
|
||||
}
|
||||
|
||||
// GetRowID returns the row id at at given location.
|
||||
func (s *SelectTable) GetRowID(index int) (string, bool) {
|
||||
cell := s.GetCell(index, 0)
|
||||
if cell == nil {
|
||||
return "", false
|
||||
}
|
||||
id, ok := cell.GetReference().(string)
|
||||
|
||||
return id, ok
|
||||
}
|
||||
|
||||
// GetSelectedItem returns the currently selected item name.
|
||||
func (s *SelectTable) GetSelectedItem() string {
|
||||
if s.GetSelectedRowIndex() == 0 || s.model.Empty() {
|
||||
|
|
@ -138,6 +150,68 @@ func (s *SelectTable) ToggleMark() {
|
|||
)
|
||||
}
|
||||
|
||||
// ToggleSpanMark toggles marked row
|
||||
func (s *SelectTable) SpanMark() {
|
||||
selIndex, prev := s.GetSelectedRowIndex(), -1
|
||||
if selIndex <= 0 {
|
||||
return
|
||||
}
|
||||
// Look back to find previous mark
|
||||
for i := selIndex - 1; i > 0; i-- {
|
||||
id, ok := s.GetRowID(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if _, ok := s.marks[id]; ok {
|
||||
prev = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if prev != -1 {
|
||||
s.markRange(prev, selIndex)
|
||||
return
|
||||
}
|
||||
|
||||
// Look forward to see if we have a mark
|
||||
for i := selIndex; i < s.GetRowCount(); i++ {
|
||||
id, ok := s.GetRowID(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if _, ok := s.marks[id]; ok {
|
||||
prev = i
|
||||
break
|
||||
}
|
||||
}
|
||||
s.markRange(prev, selIndex)
|
||||
}
|
||||
|
||||
func (s *SelectTable) markRange(prev, curr int) {
|
||||
if prev < 0 {
|
||||
return
|
||||
}
|
||||
if prev > curr {
|
||||
prev, curr = curr, prev
|
||||
}
|
||||
log.Debug().Msgf("Span Range %d::%d", prev, curr)
|
||||
for i := prev + 1; i <= curr; i++ {
|
||||
id, ok := s.GetRowID(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
s.marks[id] = struct{}{}
|
||||
cell := s.GetCell(s.GetSelectedRowIndex(), 0)
|
||||
if cell == nil {
|
||||
break
|
||||
}
|
||||
s.SetSelectedStyle(
|
||||
tcell.ColorBlack,
|
||||
cell.Color,
|
||||
tcell.AttrBold,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// IsMarked returns true if this item was marked.
|
||||
func (s *Table) IsMarked(item string) bool {
|
||||
_, ok := s.marks[item]
|
||||
|
|
|
|||
|
|
@ -399,13 +399,9 @@ func (b *Browser) defaultContext() context.Context {
|
|||
if b.Path != "" {
|
||||
ctx = context.WithValue(ctx, internal.KeyPath, b.Path)
|
||||
}
|
||||
// BOZO!!
|
||||
// ctx = context.WithValue(ctx, internal.KeyLabels, "")
|
||||
if ui.IsLabelSelector(b.CmdBuff().GetText()) {
|
||||
ctx = context.WithValue(ctx, internal.KeyLabels, ui.TrimLabelSelector(b.CmdBuff().GetText()))
|
||||
}
|
||||
// BOZO!!
|
||||
// ctx = context.WithValue(ctx, internal.KeyFields, "")
|
||||
ctx = context.WithValue(ctx, internal.KeyNamespace, client.CleanseNamespace(b.App().Config.ActiveNamespace()))
|
||||
|
||||
return ctx
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ func (c *Command) Init() error {
|
|||
c.alias = dao.NewAlias(c.app.factory)
|
||||
if _, err := c.alias.Ensure(); err != nil {
|
||||
log.Error().Err(err).Msgf("command init failed!")
|
||||
return err
|
||||
}
|
||||
customViewers = loadCustomViewers()
|
||||
|
||||
|
|
@ -106,21 +107,6 @@ func (c *Command) xrayCmd(cmd string) error {
|
|||
return c.exec(cmd, "xrays", x, true)
|
||||
}
|
||||
|
||||
// BOZO!!
|
||||
// func (c *Command) checkAccess(gvr string) error {
|
||||
// m, err := dao.MetaAccess.MetaFor(client.NewGVR(gvr))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// ns := client.CleanseNamespace(c.app.Config.ActiveNamespace())
|
||||
// if dao.IsK8sMeta(m) && c.app.ConOK() {
|
||||
// if _, e := c.app.factory.CanForResource(ns, gvr, client.MonitorAccess); e != nil {
|
||||
// return e
|
||||
// }
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// Exec the Command by showing associated display.
|
||||
func (c *Command) run(cmd, path string, clearStack bool) error {
|
||||
if c.specialCmd(cmd, path) {
|
||||
|
|
@ -131,9 +117,6 @@ func (c *Command) run(cmd, path string, clearStack bool) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//if err := c.checkAccess(gvr); err != nil {
|
||||
// return err
|
||||
//}
|
||||
|
||||
switch cmds[0] {
|
||||
case "ctx", "context", "contexts":
|
||||
|
|
@ -159,6 +142,7 @@ func (c *Command) run(cmd, path string, clearStack bool) error {
|
|||
|
||||
func (c *Command) defaultCmd() error {
|
||||
if !c.app.Conn().ConnectionOK() {
|
||||
log.Debug().Msgf("YO!!")
|
||||
return c.run("ctx", "", true)
|
||||
}
|
||||
view := c.app.Config.ActiveView()
|
||||
|
|
|
|||
|
|
@ -220,11 +220,11 @@ func (h *Help) showGeneral() model.MenuHints {
|
|||
},
|
||||
{
|
||||
Mnemonic: "tab",
|
||||
Description: "Next Field",
|
||||
Description: "Field Next",
|
||||
},
|
||||
{
|
||||
Mnemonic: "backtab",
|
||||
Description: "Previous Field",
|
||||
Description: "Field Previous",
|
||||
},
|
||||
{
|
||||
Mnemonic: "Ctrl-r",
|
||||
|
|
@ -232,7 +232,7 @@ func (h *Help) showGeneral() model.MenuHints {
|
|||
},
|
||||
{
|
||||
Mnemonic: "Ctrl-u",
|
||||
Description: "Clear command",
|
||||
Description: "Command Clear",
|
||||
},
|
||||
{
|
||||
Mnemonic: "Ctrl-e",
|
||||
|
|
@ -248,7 +248,11 @@ func (h *Help) showGeneral() model.MenuHints {
|
|||
},
|
||||
{
|
||||
Mnemonic: "Ctrl-space",
|
||||
Description: "Clear Marks",
|
||||
Description: "Mark Range",
|
||||
},
|
||||
{
|
||||
Mnemonic: "Ctrl-\\",
|
||||
Description: "Mark Clear",
|
||||
},
|
||||
{
|
||||
Mnemonic: "Ctrl-s",
|
||||
|
|
|
|||
|
|
@ -183,6 +183,7 @@ func (l *Log) bindKeys() {
|
|||
ui.Key5: ui.NewKeyAction("1h", l.sinceCmd(60*60), true),
|
||||
tcell.KeyEnter: ui.NewSharedKeyAction("Filter", l.filterCmd, false),
|
||||
ui.KeyC: ui.NewKeyAction("Clear", l.clearCmd, true),
|
||||
ui.KeyM: ui.NewKeyAction("Mark", l.markCmd, true),
|
||||
ui.KeyS: ui.NewKeyAction("Toggle AutoScroll", l.toggleAutoScrollCmd, true),
|
||||
ui.KeyF: ui.NewKeyAction("Toggle FullScreen", l.toggleFullScreenCmd, true),
|
||||
ui.KeyT: ui.NewKeyAction("Toggle Timestamp", l.toggleTimestampCmd, true),
|
||||
|
|
@ -324,6 +325,11 @@ func (l *Log) clearCmd(*tcell.EventKey) *tcell.EventKey {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (l *Log) markCmd(*tcell.EventKey) *tcell.EventKey {
|
||||
fmt.Fprintln(l.ansiWriter, fmt.Sprintf("[white::b]%s[::]", strings.Repeat("-", 80)))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Log) toggleTimestampCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if l.app.InCmdMode() {
|
||||
return evt
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ func TestLogAutoScroll(t *testing.T) {
|
|||
v.GetModel().Set(dao.LogItems{dao.NewLogItemFromString("blee"), dao.NewLogItemFromString("bozo")})
|
||||
v.GetModel().Notify(true)
|
||||
|
||||
assert.Equal(t, 13, len(v.Hints()))
|
||||
assert.Equal(t, 14, len(v.Hints()))
|
||||
|
||||
v.toggleAutoScrollCmd(nil)
|
||||
assert.Equal(t, "Autoscroll: Off FullScreen: Off Timestamps: Off Wrap: Off", v.Indicator().GetText(true))
|
||||
|
|
@ -85,7 +85,7 @@ func TestLogFilter(t *testing.T) {
|
|||
l.SendKeys(ui.KeySlash)
|
||||
l.SendStrokes("zorg")
|
||||
|
||||
assert.Equal(t, "zorg", list.lines)
|
||||
assert.Equal(t, "\x1b[38;5;209mz\x1b[0m\x1b[38;5;209mo\x1b[0m\x1b[38;5;209mr\x1b[0m\x1b[38;5;209mg\x1b[0m", list.lines)
|
||||
assert.Equal(t, 5, list.change)
|
||||
assert.Equal(t, 5, list.clear)
|
||||
assert.Equal(t, 0, list.fail)
|
||||
|
|
|
|||
|
|
@ -150,14 +150,15 @@ func (t *Table) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
|
||||
func (t *Table) bindKeys() {
|
||||
t.Actions().Add(ui.KeyActions{
|
||||
ui.KeySpace: ui.NewSharedKeyAction("Mark", t.markCmd, false),
|
||||
tcell.KeyCtrlSpace: ui.NewSharedKeyAction("Marks Clear", t.clearMarksCmd, false),
|
||||
tcell.KeyCtrlS: ui.NewSharedKeyAction("Save", t.saveCmd, false),
|
||||
ui.KeySlash: ui.NewSharedKeyAction("Filter Mode", t.activateCmd, false),
|
||||
tcell.KeyCtrlZ: ui.NewKeyAction("Toggle Faults", t.toggleFaultCmd, false),
|
||||
tcell.KeyCtrlW: ui.NewKeyAction("Toggle Wide", t.toggleWideCmd, false),
|
||||
ui.KeyShiftN: ui.NewKeyAction("Sort Name", t.SortColCmd(nameCol, true), false),
|
||||
ui.KeyShiftA: ui.NewKeyAction("Sort Age", t.SortColCmd(ageCol, true), false),
|
||||
ui.KeySpace: ui.NewSharedKeyAction("Mark", t.markCmd, false),
|
||||
tcell.KeyCtrlSpace: ui.NewSharedKeyAction("Mark Range", t.markSpanCmd, false),
|
||||
tcell.KeyCtrlBackslash: ui.NewSharedKeyAction("Marks Clear", t.clearMarksCmd, false),
|
||||
tcell.KeyCtrlS: ui.NewSharedKeyAction("Save", t.saveCmd, false),
|
||||
ui.KeySlash: ui.NewSharedKeyAction("Filter Mode", t.activateCmd, false),
|
||||
tcell.KeyCtrlZ: ui.NewKeyAction("Toggle Faults", t.toggleFaultCmd, false),
|
||||
tcell.KeyCtrlW: ui.NewKeyAction("Toggle Wide", t.toggleWideCmd, false),
|
||||
ui.KeyShiftN: ui.NewKeyAction("Sort Name", t.SortColCmd(nameCol, true), false),
|
||||
ui.KeyShiftA: ui.NewKeyAction("Sort Age", t.SortColCmd(ageCol, true), false),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -188,22 +189,22 @@ func (t *Table) cpCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
}
|
||||
|
||||
func (t *Table) markCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
path := t.GetSelectedItem()
|
||||
if path == "" {
|
||||
return evt
|
||||
}
|
||||
t.ToggleMark()
|
||||
t.Refresh()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Table) markSpanCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
t.SpanMark()
|
||||
t.Refresh()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Table) clearMarksCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
path := t.GetSelectedItem()
|
||||
if path == "" {
|
||||
return evt
|
||||
}
|
||||
t.ClearMarks()
|
||||
t.Refresh()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue