checkpoint
parent
ffc31f856a
commit
81820b6459
|
|
@ -276,8 +276,8 @@ K9s uses aliases to navigate most K8s resources.
|
||||||
tail: 200
|
tail: 200
|
||||||
# Defines the total number of log lines to allow in the view. Default 1000
|
# Defines the total number of log lines to allow in the view. Default 1000
|
||||||
buffer: 500
|
buffer: 500
|
||||||
# Represents how far to go back in the log timeline in seconds. Default is 1min
|
# Represents how far to go back in the log timeline in seconds. Setting to -1 will show all available logs. Default is 5min.
|
||||||
sinceSeconds: 60
|
sinceSeconds: 300
|
||||||
# Go full screen while displaying logs. Default false
|
# Go full screen while displaying logs. Default false
|
||||||
fullScreenLogs: false
|
fullScreenLogs: false
|
||||||
# Toggles log line wrap. Default false
|
# Toggles log line wrap. Default false
|
||||||
|
|
|
||||||
|
|
@ -380,7 +380,7 @@ func (t *Table) filtered(data render.TableData) render.TableData {
|
||||||
return fuzzyFilter(q[2:], filtered)
|
return fuzzyFilter(q[2:], filtered)
|
||||||
}
|
}
|
||||||
|
|
||||||
filtered, err := rxFilter(t.cmdBuff.GetText(), filtered)
|
filtered, err := rxFilter(q, filtered)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(errors.New("Invalid filter expression")).Msg("Regexp")
|
log.Error().Err(errors.New("Invalid filter expression")).Msg("Regexp")
|
||||||
t.cmdBuff.ClearText(true)
|
t.cmdBuff.ClearText(true)
|
||||||
|
|
|
||||||
|
|
@ -113,8 +113,6 @@ func (a *App) Init(version string, rate int) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a.CmdBuff().SetSuggestionFn(a.suggestCommand())
|
a.CmdBuff().SetSuggestionFn(a.suggestCommand())
|
||||||
// BOZO!!
|
|
||||||
// a.CmdBuff().AddListener(a)
|
|
||||||
|
|
||||||
a.layout(ctx, version)
|
a.layout(ctx, version)
|
||||||
a.initSignals()
|
a.initSignals()
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,9 @@ const detailsTitleFmt = "[fg:bg:b] %s([hilite:bg:b]%s[fg:bg:-])[fg:bg:-] "
|
||||||
|
|
||||||
// Details represents a generic text viewer.
|
// Details represents a generic text viewer.
|
||||||
type Details struct {
|
type Details struct {
|
||||||
*tview.TextView
|
*tview.Flex
|
||||||
|
|
||||||
|
text *tview.TextView
|
||||||
actions ui.KeyActions
|
actions ui.KeyActions
|
||||||
app *App
|
app *App
|
||||||
title, subject string
|
title, subject string
|
||||||
|
|
@ -27,12 +28,14 @@ type Details struct {
|
||||||
model *model.Text
|
model *model.Text
|
||||||
currentRegion, maxRegions int
|
currentRegion, maxRegions int
|
||||||
searchable bool
|
searchable bool
|
||||||
|
fullScreen bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDetails returns a details viewer.
|
// NewDetails returns a details viewer.
|
||||||
func NewDetails(app *App, title, subject string, searchable bool) *Details {
|
func NewDetails(app *App, title, subject string, searchable bool) *Details {
|
||||||
d := Details{
|
d := Details{
|
||||||
TextView: tview.NewTextView(),
|
Flex: tview.NewFlex(),
|
||||||
|
text: tview.NewTextView(),
|
||||||
app: app,
|
app: app,
|
||||||
title: title,
|
title: title,
|
||||||
subject: subject,
|
subject: subject,
|
||||||
|
|
@ -41,6 +44,7 @@ func NewDetails(app *App, title, subject string, searchable bool) *Details {
|
||||||
model: model.NewText(),
|
model: model.NewText(),
|
||||||
searchable: searchable,
|
searchable: searchable,
|
||||||
}
|
}
|
||||||
|
d.AddItem(d.text, 0, 1, true)
|
||||||
|
|
||||||
return &d
|
return &d
|
||||||
}
|
}
|
||||||
|
|
@ -50,9 +54,9 @@ func (d *Details) Init(_ context.Context) error {
|
||||||
if d.title != "" {
|
if d.title != "" {
|
||||||
d.SetBorder(true)
|
d.SetBorder(true)
|
||||||
}
|
}
|
||||||
d.SetScrollable(true).SetWrap(true).SetRegions(true)
|
d.text.SetScrollable(true).SetWrap(true).SetRegions(true)
|
||||||
d.SetDynamicColors(true)
|
d.text.SetDynamicColors(true)
|
||||||
d.SetHighlightColor(tcell.ColorOrange)
|
d.text.SetHighlightColor(tcell.ColorOrange)
|
||||||
d.SetTitleColor(tcell.ColorAqua)
|
d.SetTitleColor(tcell.ColorAqua)
|
||||||
d.SetInputCapture(d.keyboard)
|
d.SetInputCapture(d.keyboard)
|
||||||
d.SetBorderPadding(0, 0, 1, 1)
|
d.SetBorderPadding(0, 0, 1, 1)
|
||||||
|
|
@ -73,8 +77,8 @@ func (d *Details) Init(_ context.Context) error {
|
||||||
|
|
||||||
// TextChanged notifies the model changed.
|
// TextChanged notifies the model changed.
|
||||||
func (d *Details) TextChanged(lines []string) {
|
func (d *Details) TextChanged(lines []string) {
|
||||||
d.SetText(colorizeYAML(d.app.Styles.Views().Yaml, strings.Join(lines, "\n")))
|
d.text.SetText(colorizeYAML(d.app.Styles.Views().Yaml, strings.Join(lines, "\n")))
|
||||||
d.ScrollToBeginning()
|
d.text.ScrollToBeginning()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TextFiltered notifies when the filter changed.
|
// TextFiltered notifies when the filter changed.
|
||||||
|
|
@ -89,11 +93,11 @@ func (d *Details) TextFiltered(lines []string, matches fuzzy.Matches) {
|
||||||
d.maxRegions++
|
d.maxRegions++
|
||||||
}
|
}
|
||||||
|
|
||||||
d.SetText(colorizeYAML(d.app.Styles.Views().Yaml, strings.Join(ll, "\n")))
|
d.text.SetText(colorizeYAML(d.app.Styles.Views().Yaml, strings.Join(ll, "\n")))
|
||||||
d.Highlight()
|
d.text.Highlight()
|
||||||
if d.maxRegions > 0 {
|
if d.maxRegions > 0 {
|
||||||
d.Highlight("search_0")
|
d.text.Highlight("search_0")
|
||||||
d.ScrollToHighlight()
|
d.text.ScrollToHighlight()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,6 +121,7 @@ func (d *Details) bindKeys() {
|
||||||
tcell.KeyEscape: ui.NewKeyAction("Back", d.resetCmd, false),
|
tcell.KeyEscape: ui.NewKeyAction("Back", d.resetCmd, false),
|
||||||
tcell.KeyCtrlS: ui.NewKeyAction("Save", d.saveCmd, false),
|
tcell.KeyCtrlS: ui.NewKeyAction("Save", d.saveCmd, false),
|
||||||
ui.KeyC: ui.NewKeyAction("Copy", d.cpCmd, true),
|
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.KeyN: ui.NewKeyAction("Next Match", d.nextCmd, true),
|
||||||
ui.KeyShiftN: ui.NewKeyAction("Prev Match", d.prevCmd, true),
|
ui.KeyShiftN: ui.NewKeyAction("Prev Match", d.prevCmd, true),
|
||||||
ui.KeySlash: ui.NewSharedKeyAction("Filter Mode", d.activateCmd, false),
|
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.
|
// StylesChanged notifies the skin changed.
|
||||||
func (d *Details) StylesChanged(s *config.Styles) {
|
func (d *Details) StylesChanged(s *config.Styles) {
|
||||||
d.SetBackgroundColor(d.app.Styles.BgColor())
|
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.SetBorderFocusColor(d.app.Styles.Frame().Border.FocusColor.Color())
|
||||||
d.TextChanged(d.model.Peek())
|
d.TextChanged(d.model.Peek())
|
||||||
}
|
}
|
||||||
|
|
@ -190,13 +195,25 @@ func (d *Details) nextCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if d.currentRegion >= d.maxRegions {
|
if d.currentRegion >= d.maxRegions {
|
||||||
d.currentRegion = 0
|
d.currentRegion = 0
|
||||||
}
|
}
|
||||||
d.Highlight(fmt.Sprintf("search_%d", d.currentRegion))
|
d.text.Highlight(fmt.Sprintf("search_%d", d.currentRegion))
|
||||||
d.ScrollToHighlight()
|
d.text.ScrollToHighlight()
|
||||||
d.updateTitle()
|
d.updateTitle()
|
||||||
|
|
||||||
return nil
|
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 {
|
func (d *Details) prevCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if d.cmdBuff.Empty() {
|
if d.cmdBuff.Empty() {
|
||||||
return evt
|
return evt
|
||||||
|
|
@ -206,8 +223,8 @@ func (d *Details) prevCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if d.currentRegion < 0 {
|
if d.currentRegion < 0 {
|
||||||
d.currentRegion = d.maxRegions - 1
|
d.currentRegion = d.maxRegions - 1
|
||||||
}
|
}
|
||||||
d.Highlight(fmt.Sprintf("search_%d", d.currentRegion))
|
d.text.Highlight(fmt.Sprintf("search_%d", d.currentRegion))
|
||||||
d.ScrollToHighlight()
|
d.text.ScrollToHighlight()
|
||||||
d.updateTitle()
|
d.updateTitle()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -256,7 +273,7 @@ func (d *Details) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Details) saveCmd(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)
|
d.app.Flash().Err(err)
|
||||||
} else {
|
} else {
|
||||||
d.app.Flash().Infof("Log %s saved successfully!", path)
|
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 {
|
func (d *Details) cpCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
d.app.Flash().Info("Content copied to clipboard...")
|
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)
|
d.app.Flash().Err(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ type Log struct {
|
||||||
*tview.Flex
|
*tview.Flex
|
||||||
|
|
||||||
app *App
|
app *App
|
||||||
logs *Details
|
logs *Logger
|
||||||
indicator *LogIndicator
|
indicator *LogIndicator
|
||||||
ansiWriter io.Writer
|
ansiWriter io.Writer
|
||||||
model *model.Log
|
model *model.Log
|
||||||
|
|
@ -76,7 +76,7 @@ func (l *Log) Init(ctx context.Context) (err error) {
|
||||||
l.AddItem(l.indicator, 1, 1, false)
|
l.AddItem(l.indicator, 1, 1, false)
|
||||||
l.indicator.Refresh()
|
l.indicator.Refresh()
|
||||||
|
|
||||||
l.logs = NewDetails(l.app, "", "", false)
|
l.logs = NewLogger(l.app)
|
||||||
if err = l.logs.Init(ctx); err != nil {
|
if err = l.logs.Init(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +105,6 @@ func (l *Log) Init(ctx context.Context) (err error) {
|
||||||
func (l *Log) LogCleared() {
|
func (l *Log) LogCleared() {
|
||||||
l.app.QueueUpdateDraw(func() {
|
l.app.QueueUpdateDraw(func() {
|
||||||
l.logs.Clear()
|
l.logs.Clear()
|
||||||
// l.logs.ScrollTo(0, 0)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,7 +258,7 @@ func (l *Log) updateTitle() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logs returns the log viewer.
|
// Logs returns the log viewer.
|
||||||
func (l *Log) Logs() *Details {
|
func (l *Log) Logs() *Logger {
|
||||||
return l.logs
|
return l.logs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue