checkpoint

mine
derailed 2020-10-26 21:05:25 -06:00
parent ffc31f856a
commit 81820b6459
6 changed files with 214 additions and 27 deletions

View File

@ -276,8 +276,8 @@ K9s uses aliases to navigate most K8s resources.
tail: 200
# Defines the total number of log lines to allow in the view. Default 1000
buffer: 500
# Represents how far to go back in the log timeline in seconds. Default is 1min
sinceSeconds: 60
# Represents how far to go back in the log timeline in seconds. Setting to -1 will show all available logs. Default is 5min.
sinceSeconds: 300
# Go full screen while displaying logs. Default false
fullScreenLogs: false
# Toggles log line wrap. Default false

View File

@ -380,7 +380,7 @@ func (t *Table) filtered(data render.TableData) render.TableData {
return fuzzyFilter(q[2:], filtered)
}
filtered, err := rxFilter(t.cmdBuff.GetText(), filtered)
filtered, err := rxFilter(q, filtered)
if err != nil {
log.Error().Err(errors.New("Invalid filter expression")).Msg("Regexp")
t.cmdBuff.ClearText(true)

View File

@ -113,8 +113,6 @@ func (a *App) Init(version string, rate int) error {
return err
}
a.CmdBuff().SetSuggestionFn(a.suggestCommand())
// BOZO!!
// a.CmdBuff().AddListener(a)
a.layout(ctx, version)
a.initSignals()

View File

@ -18,8 +18,9 @@ const detailsTitleFmt = "[fg:bg:b] %s([hilite:bg:b]%s[fg:bg:-])[fg:bg:-] "
// Details represents a generic text viewer.
type Details struct {
*tview.TextView
*tview.Flex
text *tview.TextView
actions ui.KeyActions
app *App
title, subject string
@ -27,12 +28,14 @@ type Details struct {
model *model.Text
currentRegion, maxRegions int
searchable bool
fullScreen bool
}
// NewDetails returns a details viewer.
func NewDetails(app *App, title, subject string, searchable bool) *Details {
d := Details{
TextView: tview.NewTextView(),
Flex: tview.NewFlex(),
text: tview.NewTextView(),
app: app,
title: title,
subject: subject,
@ -41,6 +44,7 @@ func NewDetails(app *App, title, subject string, searchable bool) *Details {
model: model.NewText(),
searchable: searchable,
}
d.AddItem(d.text, 0, 1, true)
return &d
}
@ -50,9 +54,9 @@ func (d *Details) Init(_ context.Context) error {
if d.title != "" {
d.SetBorder(true)
}
d.SetScrollable(true).SetWrap(true).SetRegions(true)
d.SetDynamicColors(true)
d.SetHighlightColor(tcell.ColorOrange)
d.text.SetScrollable(true).SetWrap(true).SetRegions(true)
d.text.SetDynamicColors(true)
d.text.SetHighlightColor(tcell.ColorOrange)
d.SetTitleColor(tcell.ColorAqua)
d.SetInputCapture(d.keyboard)
d.SetBorderPadding(0, 0, 1, 1)
@ -73,8 +77,8 @@ func (d *Details) Init(_ context.Context) error {
// TextChanged notifies the model changed.
func (d *Details) TextChanged(lines []string) {
d.SetText(colorizeYAML(d.app.Styles.Views().Yaml, strings.Join(lines, "\n")))
d.ScrollToBeginning()
d.text.SetText(colorizeYAML(d.app.Styles.Views().Yaml, strings.Join(lines, "\n")))
d.text.ScrollToBeginning()
}
// TextFiltered notifies when the filter changed.
@ -89,11 +93,11 @@ func (d *Details) TextFiltered(lines []string, matches fuzzy.Matches) {
d.maxRegions++
}
d.SetText(colorizeYAML(d.app.Styles.Views().Yaml, strings.Join(ll, "\n")))
d.Highlight()
d.text.SetText(colorizeYAML(d.app.Styles.Views().Yaml, strings.Join(ll, "\n")))
d.text.Highlight()
if d.maxRegions > 0 {
d.Highlight("search_0")
d.ScrollToHighlight()
d.text.Highlight("search_0")
d.text.ScrollToHighlight()
}
}
@ -117,6 +121,7 @@ func (d *Details) bindKeys() {
tcell.KeyEscape: ui.NewKeyAction("Back", d.resetCmd, false),
tcell.KeyCtrlS: ui.NewKeyAction("Save", d.saveCmd, false),
ui.KeyC: ui.NewKeyAction("Copy", d.cpCmd, true),
ui.KeyF: ui.NewKeyAction("Toggle FullScreen", d.toggleFullScreenCmd, true),
ui.KeyN: ui.NewKeyAction("Next Match", d.nextCmd, true),
ui.KeyShiftN: ui.NewKeyAction("Prev Match", d.prevCmd, true),
ui.KeySlash: ui.NewSharedKeyAction("Filter Mode", d.activateCmd, false),
@ -139,7 +144,7 @@ func (d *Details) keyboard(evt *tcell.EventKey) *tcell.EventKey {
// StylesChanged notifies the skin changed.
func (d *Details) StylesChanged(s *config.Styles) {
d.SetBackgroundColor(d.app.Styles.BgColor())
d.SetTextColor(d.app.Styles.FgColor())
d.text.SetTextColor(d.app.Styles.FgColor())
d.SetBorderFocusColor(d.app.Styles.Frame().Border.FocusColor.Color())
d.TextChanged(d.model.Peek())
}
@ -190,13 +195,25 @@ func (d *Details) nextCmd(evt *tcell.EventKey) *tcell.EventKey {
if d.currentRegion >= d.maxRegions {
d.currentRegion = 0
}
d.Highlight(fmt.Sprintf("search_%d", d.currentRegion))
d.ScrollToHighlight()
d.text.Highlight(fmt.Sprintf("search_%d", d.currentRegion))
d.text.ScrollToHighlight()
d.updateTitle()
return nil
}
func (d *Details) toggleFullScreenCmd(evt *tcell.EventKey) *tcell.EventKey {
if d.app.InCmdMode() {
return evt
}
d.fullScreen = !d.fullScreen
d.SetFullScreen(d.fullScreen)
d.Box.SetBorder(!d.fullScreen)
return nil
}
func (d *Details) prevCmd(evt *tcell.EventKey) *tcell.EventKey {
if d.cmdBuff.Empty() {
return evt
@ -206,8 +223,8 @@ func (d *Details) prevCmd(evt *tcell.EventKey) *tcell.EventKey {
if d.currentRegion < 0 {
d.currentRegion = d.maxRegions - 1
}
d.Highlight(fmt.Sprintf("search_%d", d.currentRegion))
d.ScrollToHighlight()
d.text.Highlight(fmt.Sprintf("search_%d", d.currentRegion))
d.text.ScrollToHighlight()
d.updateTitle()
return nil
@ -256,7 +273,7 @@ func (d *Details) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
}
func (d *Details) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
if path, err := saveYAML(d.app.Config.K9s.CurrentCluster, d.title, d.GetText(true)); err != nil {
if path, err := saveYAML(d.app.Config.K9s.CurrentCluster, d.title, d.text.GetText(true)); err != nil {
d.app.Flash().Err(err)
} else {
d.app.Flash().Infof("Log %s saved successfully!", path)
@ -267,7 +284,7 @@ func (d *Details) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
func (d *Details) cpCmd(evt *tcell.EventKey) *tcell.EventKey {
d.app.Flash().Info("Content copied to clipboard...")
if err := clipboard.WriteAll(d.GetText(true)); err != nil {
if err := clipboard.WriteAll(d.text.GetText(true)); err != nil {
d.app.Flash().Err(err)
}

View File

@ -40,7 +40,7 @@ type Log struct {
*tview.Flex
app *App
logs *Details
logs *Logger
indicator *LogIndicator
ansiWriter io.Writer
model *model.Log
@ -76,7 +76,7 @@ func (l *Log) Init(ctx context.Context) (err error) {
l.AddItem(l.indicator, 1, 1, false)
l.indicator.Refresh()
l.logs = NewDetails(l.app, "", "", false)
l.logs = NewLogger(l.app)
if err = l.logs.Init(ctx); err != nil {
return err
}
@ -105,7 +105,6 @@ func (l *Log) Init(ctx context.Context) (err error) {
func (l *Log) LogCleared() {
l.app.QueueUpdateDraw(func() {
l.logs.Clear()
// l.logs.ScrollTo(0, 0)
})
}
@ -259,7 +258,7 @@ func (l *Log) updateTitle() {
}
// Logs returns the log viewer.
func (l *Log) Logs() *Details {
func (l *Log) Logs() *Logger {
return l.logs
}

173
internal/view/logger.go Normal file
View File

@ -0,0 +1,173 @@
package view
import (
"context"
"github.com/atotto/clipboard"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/model"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/tview"
"github.com/gdamore/tcell"
)
const loggerTitleFmt = "[fg:bg:b] %s([hilite:bg:b]%s[fg:bg:-])[fg:bg:-] "
// Logger represents a generic log viewer.
type Logger struct {
*tview.TextView
actions ui.KeyActions
app *App
title, subject string
cmdBuff *model.FishBuff
}
// NewLogger returns a logger viewer.
func NewLogger(app *App) *Logger {
return &Logger{
TextView: tview.NewTextView(),
app: app,
actions: make(ui.KeyActions),
cmdBuff: model.NewFishBuff('/', model.FilterBuffer),
}
}
// Init initializes the viewer.
func (l *Logger) Init(_ context.Context) error {
if l.title != "" {
l.SetBorder(true)
}
l.SetScrollable(true).SetWrap(true).SetRegions(true)
l.SetDynamicColors(true)
l.SetHighlightColor(tcell.ColorOrange)
l.SetTitleColor(tcell.ColorAqua)
l.SetInputCapture(l.keyboard)
l.SetBorderPadding(0, 0, 1, 1)
l.app.Styles.AddListener(l)
l.StylesChanged(l.app.Styles)
l.app.Prompt().SetModel(l.cmdBuff)
l.cmdBuff.AddListener(l)
l.bindKeys()
l.SetInputCapture(l.keyboard)
return nil
}
// BufferChanged indicates the buffer was changel.
func (l *Logger) BufferChanged(s string) {}
// BufferCompleted indicates input was acceptel.
func (l *Logger) BufferCompleted(s string) {
}
// BufferActive indicates the buff activity changel.
func (l *Logger) BufferActive(state bool, k model.BufferKind) {
l.app.BufferActive(state, k)
}
func (l *Logger) bindKeys() {
l.actions.Set(ui.KeyActions{
tcell.KeyEscape: ui.NewKeyAction("Back", l.resetCmd, false),
tcell.KeyCtrlS: ui.NewKeyAction("Save", l.saveCmd, false),
ui.KeyC: ui.NewKeyAction("Copy", l.cpCmd, true),
ui.KeySlash: ui.NewSharedKeyAction("Filter Mode", l.activateCmd, false),
tcell.KeyDelete: ui.NewSharedKeyAction("Erase", l.eraseCmd, false),
})
}
func (l *Logger) keyboard(evt *tcell.EventKey) *tcell.EventKey {
if a, ok := l.actions[ui.AsKey(evt)]; ok {
return a.Action(evt)
}
return evt
}
// StylesChanged notifies the skin changel.
func (l *Logger) StylesChanged(s *config.Styles) {
l.SetBackgroundColor(l.app.Styles.BgColor())
l.SetTextColor(l.app.Styles.FgColor())
l.SetBorderFocusColor(l.app.Styles.Frame().Border.FocusColor.Color())
}
// SetSubject updates the subject.
func (l *Logger) SetSubject(s string) {
l.subject = s
}
// Actions returns menu actions
func (l *Logger) Actions() ui.KeyActions {
return l.actions
}
// Name returns the component name.
func (l *Logger) Name() string { return l.title }
// Start starts the view updater.
func (l *Logger) Start() {}
// Stop terminates the updater.
func (l *Logger) Stop() {
l.app.Styles.RemoveListener(l)
}
// Hints returns menu hints.
func (l *Logger) Hints() model.MenuHints {
return l.actions.Hints()
}
// ExtraHints returns additional hints.
func (l *Logger) ExtraHints() map[string]string {
return nil
}
func (l *Logger) activateCmd(evt *tcell.EventKey) *tcell.EventKey {
if l.app.InCmdMode() {
return evt
}
l.app.ResetPrompt(l.cmdBuff)
return nil
}
func (l *Logger) eraseCmd(evt *tcell.EventKey) *tcell.EventKey {
if !l.cmdBuff.IsActive() {
return nil
}
l.cmdBuff.Delete()
return nil
}
func (l *Logger) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
if !l.cmdBuff.InCmdMode() {
l.cmdBuff.Reset()
return l.app.PrevCmd(evt)
}
l.cmdBuff.SetActive(false)
l.cmdBuff.Reset()
return nil
}
func (l *Logger) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
if path, err := saveYAML(l.app.Config.K9s.CurrentCluster, l.title, l.GetText(true)); err != nil {
l.app.Flash().Err(err)
} else {
l.app.Flash().Infof("Log %s saved successfully!", path)
}
return nil
}
func (l *Logger) cpCmd(evt *tcell.EventKey) *tcell.EventKey {
l.app.Flash().Info("Content copied to clipboard...")
if err := clipboard.WriteAll(l.GetText(true)); err != nil {
l.app.Flash().Err(err)
}
return nil
}