bug fixes + cleanup
parent
8778305bf2
commit
ffd1c61c8c
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// K9sAlias stores K9s command aliases.
|
// K9sAlias manages K9s aliases.
|
||||||
var K9sAlias = filepath.Join(K9sHome, "alias.yml")
|
var K9sAlias = filepath.Join(K9sHome, "alias.yml")
|
||||||
|
|
||||||
type Alias map[string]string
|
type Alias map[string]string
|
||||||
|
|
@ -33,7 +33,7 @@ func (a Aliases) loadDefaults() {
|
||||||
a.Alias["crb"] = "rbac.authorization.k8s.io/v1/clusterrolebindings"
|
a.Alias["crb"] = "rbac.authorization.k8s.io/v1/clusterrolebindings"
|
||||||
a.Alias["ro"] = "rbac.authorization.k8s.io/v1/roles"
|
a.Alias["ro"] = "rbac.authorization.k8s.io/v1/roles"
|
||||||
a.Alias["rob"] = "rbac.authorization.k8s.io/v1/rolebindings"
|
a.Alias["rob"] = "rbac.authorization.k8s.io/v1/rolebindings"
|
||||||
a.Alias["np"] = "networking.k8s.io/v1beta1/rolebindings"
|
a.Alias["np"] = "networking.k8s.io/v1/networkpolicies"
|
||||||
{
|
{
|
||||||
a.Alias["ctx"] = "contexts"
|
a.Alias["ctx"] = "contexts"
|
||||||
a.Alias["contexts"] = "contexts"
|
a.Alias["contexts"] = "contexts"
|
||||||
|
|
@ -87,7 +87,7 @@ func (a Aliases) Define(args ...string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadAliases K9s alias from a given file.
|
// LoadAliases loads alias from a given file.
|
||||||
func (a Aliases) LoadAliases(path string) error {
|
func (a Aliases) LoadAliases(path string) error {
|
||||||
f, err := ioutil.ReadFile(path)
|
f, err := ioutil.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -7,20 +7,42 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAliasesLoad(t *testing.T) {
|
func TestAliasDefine(t *testing.T) {
|
||||||
aa := config.NewAliases()
|
uu := map[string]struct {
|
||||||
assert.Nil(t, aa.LoadAliases("test_assets/alias.yml"))
|
aa []string
|
||||||
|
}{
|
||||||
|
"one": {[]string{"blee", "duh"}},
|
||||||
|
"multi": {[]string{"blee", "duh", "fred", "zorg"}},
|
||||||
|
}
|
||||||
|
|
||||||
assert.Equal(t, 27, len(aa.Alias))
|
for k, u := range uu {
|
||||||
|
t.Run(k, func(t *testing.T) {
|
||||||
|
a := config.NewAliases()
|
||||||
|
a.Define(u.aa...)
|
||||||
|
for i := 0; i < len(u.aa); i += 2 {
|
||||||
|
v, ok := a.Get(u.aa[i])
|
||||||
|
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, u.aa[i+1], v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAliasesLoad(t *testing.T) {
|
||||||
|
a := config.NewAliases()
|
||||||
|
assert.Nil(t, a.LoadAliases("test_assets/alias.yml"))
|
||||||
|
|
||||||
|
assert.Equal(t, 27, len(a.Alias))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAliasesSave(t *testing.T) {
|
func TestAliasesSave(t *testing.T) {
|
||||||
aa := config.NewAliases()
|
a := config.NewAliases()
|
||||||
|
|
||||||
aa.Alias["test"] = "fred"
|
a.Alias["test"] = "fred"
|
||||||
aa.Alias["blee"] = "duh"
|
a.Alias["blee"] = "duh"
|
||||||
aa.SaveAliases("/tmp/a.yml")
|
a.SaveAliases("/tmp/a.yml")
|
||||||
|
|
||||||
assert.Nil(t, aa.LoadAliases("/tmp/a.yml"))
|
assert.Nil(t, a.LoadAliases("/tmp/a.yml"))
|
||||||
assert.Equal(t, 28, len(aa.Alias))
|
assert.Equal(t, 28, len(a.Alias))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,6 @@ func TestConfigLoad(t *testing.T) {
|
||||||
assert.Equal(t, "minikube", cfg.K9s.CurrentCluster)
|
assert.Equal(t, "minikube", cfg.K9s.CurrentCluster)
|
||||||
assert.NotNil(t, cfg.K9s.Clusters)
|
assert.NotNil(t, cfg.K9s.Clusters)
|
||||||
assert.Equal(t, 2, len(cfg.K9s.Clusters))
|
assert.Equal(t, 2, len(cfg.K9s.Clusters))
|
||||||
assert.Equal(t, 1, len(cfg.K9s.Plugins))
|
|
||||||
|
|
||||||
nn := []string{
|
nn := []string{
|
||||||
"default",
|
"default",
|
||||||
|
|
@ -294,19 +293,6 @@ var expectedConfig = `k9s:
|
||||||
- kube-system
|
- kube-system
|
||||||
view:
|
view:
|
||||||
active: ctx
|
active: ctx
|
||||||
plugins:
|
|
||||||
blah:
|
|
||||||
shortCut: shift-s
|
|
||||||
scopes:
|
|
||||||
- po
|
|
||||||
- dp
|
|
||||||
description: blee
|
|
||||||
command: duh
|
|
||||||
background: false
|
|
||||||
args:
|
|
||||||
- -n
|
|
||||||
- $NAMESPACE
|
|
||||||
- -boolean
|
|
||||||
`
|
`
|
||||||
|
|
||||||
var resetConfig = `k9s:
|
var resetConfig = `k9s:
|
||||||
|
|
@ -324,17 +310,4 @@ var resetConfig = `k9s:
|
||||||
- default
|
- default
|
||||||
view:
|
view:
|
||||||
active: po
|
active: po
|
||||||
plugins:
|
|
||||||
blah:
|
|
||||||
shortCut: shift-s
|
|
||||||
scopes:
|
|
||||||
- po
|
|
||||||
- dp
|
|
||||||
description: blee
|
|
||||||
command: duh
|
|
||||||
background: false
|
|
||||||
args:
|
|
||||||
- -n
|
|
||||||
- $NAMESPACE
|
|
||||||
- -boolean
|
|
||||||
`
|
`
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ type K9s struct {
|
||||||
CurrentContext string `yaml:"currentContext"`
|
CurrentContext string `yaml:"currentContext"`
|
||||||
CurrentCluster string `yaml:"currentCluster"`
|
CurrentCluster string `yaml:"currentCluster"`
|
||||||
Clusters map[string]*Cluster `yaml:"clusters,omitempty"`
|
Clusters map[string]*Cluster `yaml:"clusters,omitempty"`
|
||||||
Plugins map[string]*Plugin `yaml:"plugins,omitempty"`
|
|
||||||
manualRefreshRate int
|
manualRefreshRate int
|
||||||
manualHeadless *bool
|
manualHeadless *bool
|
||||||
manualCommand *string
|
manualCommand *string
|
||||||
|
|
@ -32,7 +31,6 @@ func NewK9s() *K9s {
|
||||||
LogBufferSize: defaultLogBufferSize,
|
LogBufferSize: defaultLogBufferSize,
|
||||||
LogRequestSize: defaultLogRequestSize,
|
LogRequestSize: defaultLogRequestSize,
|
||||||
Clusters: make(map[string]*Cluster),
|
Clusters: make(map[string]*Cluster),
|
||||||
Plugins: make(map[string]*Plugin),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,20 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// K9sPlugins manages K9s plugins.
|
||||||
|
var K9sPlugins = filepath.Join(K9sHome, "plugin.yml")
|
||||||
|
|
||||||
|
// Plugins represents a collection of plugins.
|
||||||
|
type Plugins struct {
|
||||||
|
Plugin map[string]Plugin `yaml:"plugin"`
|
||||||
|
}
|
||||||
|
|
||||||
// Plugin describes a K9s plugin
|
// Plugin describes a K9s plugin
|
||||||
type Plugin struct {
|
type Plugin struct {
|
||||||
ShortCut string `yaml:"shortCut"`
|
ShortCut string `yaml:"shortCut"`
|
||||||
|
|
@ -9,3 +24,33 @@ type Plugin struct {
|
||||||
Background bool `yaml:"background"`
|
Background bool `yaml:"background"`
|
||||||
Args []string `yaml:"args"`
|
Args []string `yaml:"args"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewPlugins returns a new plugin.
|
||||||
|
func NewPlugins() Plugins {
|
||||||
|
return Plugins{
|
||||||
|
Plugin: make(map[string]Plugin),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load K9s plugins.
|
||||||
|
func (p Plugins) Load() error {
|
||||||
|
return p.LoadPlugins(K9sPlugins)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadPlugins loads plugins from a given file.
|
||||||
|
func (p Plugins) LoadPlugins(path string) error {
|
||||||
|
f, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pp Plugins
|
||||||
|
if err := yaml.Unmarshal(f, &pp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for k, v := range pp.Plugin {
|
||||||
|
p.Plugin[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package config_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/derailed/k9s/internal/config"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPluginLoad(t *testing.T) {
|
||||||
|
p := config.NewPlugins()
|
||||||
|
assert.Nil(t, p.LoadPlugins("test_assets/plugin.yml"))
|
||||||
|
|
||||||
|
assert.Equal(t, 1, len(p.Plugin))
|
||||||
|
}
|
||||||
|
|
@ -197,7 +197,7 @@ func newTitle() Title {
|
||||||
BgColor: "black",
|
BgColor: "black",
|
||||||
HighlightColor: "fuchsia",
|
HighlightColor: "fuchsia",
|
||||||
CounterColor: "papayawhip",
|
CounterColor: "papayawhip",
|
||||||
FilterColor: "orange",
|
FilterColor: "seagreen",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,15 +27,3 @@ k9s:
|
||||||
- kube-system
|
- kube-system
|
||||||
view:
|
view:
|
||||||
active: po
|
active: po
|
||||||
plugins:
|
|
||||||
blah:
|
|
||||||
shortCut: shift-s
|
|
||||||
description: blee
|
|
||||||
scopes:
|
|
||||||
- po
|
|
||||||
- dp
|
|
||||||
command: duh
|
|
||||||
args:
|
|
||||||
- -n
|
|
||||||
- $NAMESPACE
|
|
||||||
- -boolean
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
plugin:
|
||||||
|
blah:
|
||||||
|
shortCut: shift-s
|
||||||
|
description: blee
|
||||||
|
scopes:
|
||||||
|
- po
|
||||||
|
- dp
|
||||||
|
command: duh
|
||||||
|
args:
|
||||||
|
- -n
|
||||||
|
- $NAMESPACE
|
||||||
|
- -boolean
|
||||||
|
|
@ -16,7 +16,6 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// RestMapping holds k8s resource mapping
|
// RestMapping holds k8s resource mapping
|
||||||
// BOZO!! Has to be a better way...
|
|
||||||
RestMapping = &RestMapper{}
|
RestMapping = &RestMapper{}
|
||||||
toFileName = regexp.MustCompile(`[^(\w/\.)]`)
|
toFileName = regexp.MustCompile(`[^(\w/\.)]`)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ func NewApp() *App {
|
||||||
actions: make(KeyActions),
|
actions: make(KeyActions),
|
||||||
pages: tview.NewPages(),
|
pages: tview.NewPages(),
|
||||||
content: tview.NewPages(),
|
content: tview.NewPages(),
|
||||||
cmdBuff: NewCmdBuff(':'),
|
cmdBuff: NewCmdBuff(':', CommandBuff),
|
||||||
}
|
}
|
||||||
|
|
||||||
s.RefreshStyles()
|
s.RefreshStyles()
|
||||||
|
|
@ -62,7 +62,7 @@ func NewApp() *App {
|
||||||
s.views = map[string]tview.Primitive{
|
s.views = map[string]tview.Primitive{
|
||||||
"menu": NewMenuView(s.Styles),
|
"menu": NewMenuView(s.Styles),
|
||||||
"logo": NewLogoView(s.Styles),
|
"logo": NewLogoView(s.Styles),
|
||||||
"cmd": NewCmdView(s.Styles, '🐶'),
|
"cmd": NewCmdView(s.Styles),
|
||||||
"crumbs": NewCrumbsView(s.Styles),
|
"crumbs": NewCrumbsView(s.Styles),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,7 +186,6 @@ func (a *App) activateCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if a.InCmdMode() {
|
if a.InCmdMode() {
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
a.Flash().Info("Command mode activated.")
|
|
||||||
a.cmdBuff.SetActive(true)
|
a.cmdBuff.SetActive(true)
|
||||||
a.cmdBuff.Clear()
|
a.cmdBuff.Clear()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultPrompt = "%c> %s"
|
const defaultPrompt = "%c> %s"
|
||||||
|
|
@ -20,8 +21,8 @@ type CmdView struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCmdView returns a new command view.
|
// NewCmdView returns a new command view.
|
||||||
func NewCmdView(styles *config.Styles, ic rune) *CmdView {
|
func NewCmdView(styles *config.Styles) *CmdView {
|
||||||
v := CmdView{styles: styles, icon: ic, TextView: tview.NewTextView()}
|
v := CmdView{styles: styles, TextView: tview.NewTextView()}
|
||||||
{
|
{
|
||||||
v.SetWordWrap(true)
|
v.SetWordWrap(true)
|
||||||
v.SetWrap(true)
|
v.SetWrap(true)
|
||||||
|
|
@ -29,7 +30,7 @@ func NewCmdView(styles *config.Styles, ic rune) *CmdView {
|
||||||
v.SetBorder(true)
|
v.SetBorder(true)
|
||||||
v.SetBorderPadding(0, 0, 1, 1)
|
v.SetBorderPadding(0, 0, 1, 1)
|
||||||
v.SetBackgroundColor(styles.BgColor())
|
v.SetBackgroundColor(styles.BgColor())
|
||||||
v.SetBorderColor(config.AsColor(styles.Frame().Border.FocusColor))
|
// v.SetBorderColor(config.AsColor(styles.Frame().Border.FocusColor))
|
||||||
v.SetTextColor(styles.FgColor())
|
v.SetTextColor(styles.FgColor())
|
||||||
}
|
}
|
||||||
return &v
|
return &v
|
||||||
|
|
@ -67,11 +68,13 @@ func (v *CmdView) BufferChanged(s string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// BufferActive indicates the buff activity changed.
|
// BufferActive indicates the buff activity changed.
|
||||||
func (v *CmdView) BufferActive(f bool) {
|
func (v *CmdView) BufferActive(f bool, k BufferKind) {
|
||||||
v.activated = f
|
v.activated = f
|
||||||
if f {
|
if f {
|
||||||
v.SetBorder(true)
|
v.SetBorder(true)
|
||||||
|
v.icon = iconFor(k)
|
||||||
v.SetTextColor(v.styles.FgColor())
|
v.SetTextColor(v.styles.FgColor())
|
||||||
|
v.SetBorderColor(colorFor(k))
|
||||||
v.activate()
|
v.activate()
|
||||||
} else {
|
} else {
|
||||||
v.SetBorder(false)
|
v.SetBorder(false)
|
||||||
|
|
@ -79,3 +82,20 @@ func (v *CmdView) BufferActive(f bool) {
|
||||||
v.Clear()
|
v.Clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func colorFor(k BufferKind) tcell.Color {
|
||||||
|
switch k {
|
||||||
|
case CommandBuff:
|
||||||
|
return tcell.ColorAqua
|
||||||
|
default:
|
||||||
|
return tcell.ColorSeaGreen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func iconFor(k BufferKind) rune {
|
||||||
|
switch k {
|
||||||
|
case CommandBuff:
|
||||||
|
return '🐶'
|
||||||
|
default:
|
||||||
|
return '🤓'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,19 +2,30 @@ package ui
|
||||||
|
|
||||||
const maxBuff = 10
|
const maxBuff = 10
|
||||||
|
|
||||||
|
const (
|
||||||
|
// CommandBuff indicates a command buffer.
|
||||||
|
CommandBuff BufferKind = 1 << iota
|
||||||
|
// FilterBuff indicates a search buffer.
|
||||||
|
FilterBuff
|
||||||
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
// BufferKind indicates a buffer type
|
||||||
|
BufferKind int8
|
||||||
|
|
||||||
// BuffWatcher represents a command buffer listener.
|
// BuffWatcher represents a command buffer listener.
|
||||||
BuffWatcher interface {
|
BuffWatcher interface {
|
||||||
// Changed indicates the buffer was changed.
|
// Changed indicates the buffer was changed.
|
||||||
BufferChanged(s string)
|
BufferChanged(s string)
|
||||||
|
|
||||||
// Active indicates the buff activity changed.
|
// Active indicates the buff activity changed.
|
||||||
BufferActive(state bool)
|
BufferActive(state bool, kind BufferKind)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CmdBuff represents user command input.
|
// CmdBuff represents user command input.
|
||||||
CmdBuff struct {
|
CmdBuff struct {
|
||||||
buff []rune
|
buff []rune
|
||||||
|
kind BufferKind
|
||||||
hotKey rune
|
hotKey rune
|
||||||
active bool
|
active bool
|
||||||
listeners []BuffWatcher
|
listeners []BuffWatcher
|
||||||
|
|
@ -22,9 +33,10 @@ type (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewCmdBuff returns a new command buffer.
|
// NewCmdBuff returns a new command buffer.
|
||||||
func NewCmdBuff(key rune) *CmdBuff {
|
func NewCmdBuff(key rune, kind BufferKind) *CmdBuff {
|
||||||
return &CmdBuff{
|
return &CmdBuff{
|
||||||
hotKey: key,
|
hotKey: key,
|
||||||
|
kind: kind,
|
||||||
buff: make([]rune, 0, maxBuff),
|
buff: make([]rune, 0, maxBuff),
|
||||||
listeners: []BuffWatcher{},
|
listeners: []BuffWatcher{},
|
||||||
}
|
}
|
||||||
|
|
@ -47,8 +59,9 @@ func (c *CmdBuff) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set initializes the buffer with a command.
|
// Set initializes the buffer with a command.
|
||||||
func (c *CmdBuff) Set(rr []rune) {
|
func (c *CmdBuff) Set(cmd string) {
|
||||||
c.buff = rr
|
c.buff = []rune(cmd)
|
||||||
|
c.fireChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add adds a new charater to the buffer.
|
// Add adds a new charater to the buffer.
|
||||||
|
|
@ -104,6 +117,6 @@ func (c *CmdBuff) fireChanged() {
|
||||||
|
|
||||||
func (c *CmdBuff) fireActive(b bool) {
|
func (c *CmdBuff) fireActive(b bool) {
|
||||||
for _, l := range c.listeners {
|
for _, l := range c.listeners {
|
||||||
l.BufferActive(b)
|
l.BufferActive(b, c.kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ func (l *testListener) BufferChanged(s string) {
|
||||||
l.text = s
|
l.text = s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *testListener) BufferActive(s bool) {
|
func (l *testListener) BufferActive(s bool, _ BufferKind) {
|
||||||
if s {
|
if s {
|
||||||
l.act++
|
l.act++
|
||||||
return
|
return
|
||||||
|
|
@ -25,7 +25,7 @@ func (l *testListener) BufferActive(s bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCmdBuffActivate(t *testing.T) {
|
func TestCmdBuffActivate(t *testing.T) {
|
||||||
b, l := NewCmdBuff('>'), testListener{}
|
b, l := NewCmdBuff('>', CommandBuff), testListener{}
|
||||||
b.AddListener(&l)
|
b.AddListener(&l)
|
||||||
|
|
||||||
b.SetActive(true)
|
b.SetActive(true)
|
||||||
|
|
@ -35,7 +35,7 @@ func TestCmdBuffActivate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCmdBuffDeactivate(t *testing.T) {
|
func TestCmdBuffDeactivate(t *testing.T) {
|
||||||
b, l := NewCmdBuff('>'), testListener{}
|
b, l := NewCmdBuff('>', CommandBuff), testListener{}
|
||||||
b.AddListener(&l)
|
b.AddListener(&l)
|
||||||
|
|
||||||
b.SetActive(false)
|
b.SetActive(false)
|
||||||
|
|
@ -45,7 +45,7 @@ func TestCmdBuffDeactivate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCmdBuffChanged(t *testing.T) {
|
func TestCmdBuffChanged(t *testing.T) {
|
||||||
b, l := NewCmdBuff('>'), testListener{}
|
b, l := NewCmdBuff('>', CommandBuff), testListener{}
|
||||||
b.AddListener(&l)
|
b.AddListener(&l)
|
||||||
|
|
||||||
b.Add('b')
|
b.Add('b')
|
||||||
|
|
@ -77,7 +77,7 @@ func TestCmdBuffChanged(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCmdBuffAdd(t *testing.T) {
|
func TestCmdBuffAdd(t *testing.T) {
|
||||||
b := NewCmdBuff('>')
|
b := NewCmdBuff('>', CommandBuff)
|
||||||
|
|
||||||
uu := []struct {
|
uu := []struct {
|
||||||
runes []rune
|
runes []rune
|
||||||
|
|
@ -98,7 +98,7 @@ func TestCmdBuffAdd(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCmdBuffDel(t *testing.T) {
|
func TestCmdBuffDel(t *testing.T) {
|
||||||
b := NewCmdBuff('>')
|
b := NewCmdBuff('>', CommandBuff)
|
||||||
|
|
||||||
uu := []struct {
|
uu := []struct {
|
||||||
runes []rune
|
runes []rune
|
||||||
|
|
@ -120,7 +120,7 @@ func TestCmdBuffDel(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCmdBuffEmpty(t *testing.T) {
|
func TestCmdBuffEmpty(t *testing.T) {
|
||||||
b := NewCmdBuff('>')
|
b := NewCmdBuff('>', CommandBuff)
|
||||||
|
|
||||||
uu := []struct {
|
uu := []struct {
|
||||||
runes []rune
|
runes []rune
|
||||||
|
|
|
||||||
|
|
@ -9,20 +9,20 @@ import (
|
||||||
|
|
||||||
func TestNewCmdUpdate(t *testing.T) {
|
func TestNewCmdUpdate(t *testing.T) {
|
||||||
defaults, _ := config.NewStyles("")
|
defaults, _ := config.NewStyles("")
|
||||||
v := NewCmdView(defaults, 'T')
|
v := NewCmdView(defaults)
|
||||||
v.update("blee")
|
v.update("blee")
|
||||||
|
|
||||||
assert.Equal(t, "T> blee\n", v.GetText(false))
|
assert.Equal(t, "\x00> blee\n", v.GetText(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCmdInCmdMode(t *testing.T) {
|
func TestCmdInCmdMode(t *testing.T) {
|
||||||
defaults, _ := config.NewStyles("")
|
defaults, _ := config.NewStyles("")
|
||||||
v := NewCmdView(defaults, 'T')
|
v := NewCmdView(defaults)
|
||||||
v.update("blee")
|
v.update("blee")
|
||||||
v.append('!')
|
v.append('!')
|
||||||
|
|
||||||
assert.Equal(t, "T> blee!\n", v.GetText(false))
|
assert.Equal(t, "\x00> blee!\n", v.GetText(false))
|
||||||
assert.False(t, v.InCmdMode())
|
assert.False(t, v.InCmdMode())
|
||||||
v.BufferActive(true)
|
v.BufferActive(true, CommandBuff)
|
||||||
assert.True(t, v.InCmdMode())
|
assert.True(t, v.InCmdMode())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ func (v *MenuView) HydrateMenu(hh Hints) {
|
||||||
t := v.buildMenuTable(hh)
|
t := v.buildMenuTable(hh)
|
||||||
for row := 0; row < len(t); row++ {
|
for row := 0; row < len(t); row++ {
|
||||||
for col := 0; col < len(t[row]); col++ {
|
for col := 0; col < len(t[row]); col++ {
|
||||||
if len(t[row][col]) == 0 {
|
if t[row][col] == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c := tview.NewTableCell(t[row][col])
|
c := tview.NewTableCell(t[row][col])
|
||||||
|
|
@ -57,33 +57,42 @@ func (v *MenuView) HydrateMenu(hh Hints) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *MenuView) buildMenuTable(hh Hints) [][]string {
|
func isDigit(s string) bool {
|
||||||
table := make([][]Hint, maxRows+1)
|
return menuRX.MatchString(s)
|
||||||
|
|
||||||
colCount := (len(hh) / maxRows) + 1
|
|
||||||
for row := 0; row < maxRows; row++ {
|
|
||||||
table[row] = make([]Hint, colCount+1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var row, col int
|
func (v *MenuView) buildMenuTable(hh Hints) [][]string {
|
||||||
|
table := make([][]Hint, maxRows)
|
||||||
|
colCount := len(hh) / maxRows
|
||||||
|
if colCount == 0 {
|
||||||
|
colCount = 1
|
||||||
|
}
|
||||||
|
if isDigit(hh[0].Mnemonic) {
|
||||||
|
colCount++
|
||||||
|
}
|
||||||
|
for row := 0; row < maxRows; row++ {
|
||||||
|
table[row] = make([]Hint, colCount)
|
||||||
|
}
|
||||||
|
var row, col, added int
|
||||||
firstCmd := true
|
firstCmd := true
|
||||||
maxKeys := make([]int, colCount+1)
|
maxKeys := make([]int, colCount+1)
|
||||||
for _, h := range hh {
|
for _, h := range hh {
|
||||||
if !h.Visible {
|
if !h.Visible {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
isDigit := menuRX.MatchString(h.Mnemonic)
|
if !isDigit(h.Mnemonic) && firstCmd {
|
||||||
if !isDigit && firstCmd {
|
|
||||||
row, col, firstCmd = 0, col+1, false
|
row, col, firstCmd = 0, col+1, false
|
||||||
|
if added == 0 {
|
||||||
|
col = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if maxKeys[col] < len(h.Mnemonic) {
|
if maxKeys[col] < len(h.Mnemonic) {
|
||||||
maxKeys[col] = len(h.Mnemonic)
|
maxKeys[col] = len(h.Mnemonic)
|
||||||
}
|
}
|
||||||
table[row][col] = h
|
table[row][col] = h
|
||||||
row++
|
added, row = added+1, row+1
|
||||||
if row >= maxRows {
|
if row >= maxRows {
|
||||||
col++
|
row, col = 0, col+1
|
||||||
row = 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,6 +257,7 @@ func initKeys() {
|
||||||
initNumbKeys()
|
initNumbKeys()
|
||||||
initStdKeys()
|
initStdKeys()
|
||||||
initShiftKeys()
|
initShiftKeys()
|
||||||
|
initCtrlKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
func initNumbKeys() {
|
func initNumbKeys() {
|
||||||
|
|
@ -292,6 +302,12 @@ func initStdKeys() {
|
||||||
tcell.KeyNames[tcell.Key(KeyZ)] = "z"
|
tcell.KeyNames[tcell.Key(KeyZ)] = "z"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BOZO!! No sure why these aren't mapped??
|
||||||
|
func initCtrlKeys() {
|
||||||
|
tcell.KeyNames[tcell.KeyCtrlI] = "Ctrl-I"
|
||||||
|
tcell.KeyNames[tcell.KeyCtrlM] = "Ctrl-M"
|
||||||
|
}
|
||||||
|
|
||||||
func initShiftKeys() {
|
func initShiftKeys() {
|
||||||
tcell.KeyNames[tcell.Key(KeyShiftA)] = "Shift-A"
|
tcell.KeyNames[tcell.Key(KeyShiftA)] = "Shift-A"
|
||||||
tcell.KeyNames[tcell.Key(KeyShiftB)] = "Shift-B"
|
tcell.KeyNames[tcell.Key(KeyShiftB)] = "Shift-B"
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@ func TestNewMenuView(t *testing.T) {
|
||||||
defaults, _ := config.NewStyles("")
|
defaults, _ := config.NewStyles("")
|
||||||
v := NewMenuView(defaults)
|
v := NewMenuView(defaults)
|
||||||
v.HydrateMenu(Hints{
|
v.HydrateMenu(Hints{
|
||||||
|
{"0", "zero", true},
|
||||||
{"a", "bleeA", true},
|
{"a", "bleeA", true},
|
||||||
{"b", "bleeB", true},
|
{"b", "bleeB", true},
|
||||||
{"0", "zero", true},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.Equal(t, " [fuchsia:black:b]<0> [white:black:d]zero ", v.GetCell(0, 0).Text)
|
assert.Equal(t, " [fuchsia:black:b]<0> [white:black:d]zero ", v.GetCell(0, 0).Text)
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,6 @@ import (
|
||||||
"github.com/derailed/k9s/internal/resource"
|
"github.com/derailed/k9s/internal/resource"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
|
|
||||||
// "github.com/ktr0731/go-fuzzyfinder/matching"
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/sahilm/fuzzy"
|
"github.com/sahilm/fuzzy"
|
||||||
"k8s.io/apimachinery/pkg/util/duration"
|
"k8s.io/apimachinery/pkg/util/duration"
|
||||||
|
|
@ -52,7 +50,7 @@ func NewTable(title string, styles *config.Styles) *Table {
|
||||||
Table: tview.NewTable(),
|
Table: tview.NewTable(),
|
||||||
styles: styles,
|
styles: styles,
|
||||||
actions: make(KeyActions),
|
actions: make(KeyActions),
|
||||||
cmdBuff: NewCmdBuff('/'),
|
cmdBuff: NewCmdBuff('/', FilterBuff),
|
||||||
baseTitle: title,
|
baseTitle: title,
|
||||||
sortCol: SortColumn{0, 0, true},
|
sortCol: SortColumn{0, 0, true},
|
||||||
}
|
}
|
||||||
|
|
@ -178,6 +176,7 @@ func (v *Table) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
v.SearchBuff().Add(evt.Rune())
|
v.SearchBuff().Add(evt.Rune())
|
||||||
v.ClearSelection()
|
v.ClearSelection()
|
||||||
v.doUpdate(v.filtered())
|
v.doUpdate(v.filtered())
|
||||||
|
v.UpdateTitle()
|
||||||
v.SelectFirstRow()
|
v.SelectFirstRow()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -383,7 +382,7 @@ func (v *Table) filtered() resource.TableData {
|
||||||
|
|
||||||
q := v.cmdBuff.String()
|
q := v.cmdBuff.String()
|
||||||
if isFuzzySelector(q) {
|
if isFuzzySelector(q) {
|
||||||
return v.fuzzFilter(q[2:])
|
return v.fuzzyFilter(q[2:])
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.rxFilter(q)
|
return v.rxFilter(q)
|
||||||
|
|
@ -412,7 +411,7 @@ func (v *Table) rxFilter(q string) resource.TableData {
|
||||||
return filtered
|
return filtered
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Table) fuzzFilter(q string) resource.TableData {
|
func (v *Table) fuzzyFilter(q string) resource.TableData {
|
||||||
var ss, kk []string
|
var ss, kk []string
|
||||||
for k, row := range v.data.Rows {
|
for k, row := range v.data.Rows {
|
||||||
ss = append(ss, row.Fields[v.NameColIndex()])
|
ss = append(ss, row.Fields[v.NameColIndex()])
|
||||||
|
|
@ -506,7 +505,7 @@ func (v *Table) UpdateTitle() {
|
||||||
title = skinTitle(fmt.Sprintf(nsTitleFmt, v.baseTitle, ns, rc), v.styles.Frame())
|
title = skinTitle(fmt.Sprintf(nsTitleFmt, v.baseTitle, ns, rc), v.styles.Frame())
|
||||||
}
|
}
|
||||||
|
|
||||||
if !v.cmdBuff.IsActive() && !v.cmdBuff.Empty() {
|
if !v.cmdBuff.Empty() {
|
||||||
cmd := v.cmdBuff.String()
|
cmd := v.cmdBuff.String()
|
||||||
if isLabelSelector(cmd) {
|
if isLabelSelector(cmd) {
|
||||||
cmd = trimLabelSelector(cmd)
|
cmd = trimLabelSelector(cmd)
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ func (v *aliasView) Init(context.Context, string) {
|
||||||
|
|
||||||
func (v *aliasView) registerActions() {
|
func (v *aliasView) registerActions() {
|
||||||
v.RmAction(ui.KeyShiftA)
|
v.RmAction(ui.KeyShiftA)
|
||||||
|
v.RmAction(tcell.KeyCtrlS)
|
||||||
|
|
||||||
v.SetActions(ui.KeyActions{
|
v.SetActions(ui.KeyActions{
|
||||||
tcell.KeyEnter: ui.NewKeyAction("Goto", v.gotoCmd, true),
|
tcell.KeyEnter: ui.NewKeyAction("Goto", v.gotoCmd, true),
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ const (
|
||||||
splashTime = 1
|
splashTime = 1
|
||||||
devMode = "dev"
|
devMode = "dev"
|
||||||
clusterRefresh = time.Duration(5 * time.Second)
|
clusterRefresh = time.Duration(5 * time.Second)
|
||||||
|
indicatorFmt = "[orange::b]K9s [aqua::]%s [white::]%s:%s:%s [lawngreen::]%s%%[white::]::[darkturquoise::]%s%%"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|
@ -55,6 +56,7 @@ type (
|
||||||
forwarders map[string]forwarder
|
forwarders map[string]forwarder
|
||||||
version string
|
version string
|
||||||
showHeader bool
|
showHeader bool
|
||||||
|
filter string
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -105,7 +107,6 @@ func (a *appView) Init(version string, rate int) {
|
||||||
a.Main().AddPage("splash", ui.NewSplash(a.Styles, version), true, true)
|
a.Main().AddPage("splash", ui.NewSplash(a.Styles, version), true, true)
|
||||||
|
|
||||||
main.AddItem(a.indicator(), 1, 1, false)
|
main.AddItem(a.indicator(), 1, 1, false)
|
||||||
// main.AddItem(a.Cmd(), 3, 1, false)
|
|
||||||
main.AddItem(a.Frame(), 0, 10, true)
|
main.AddItem(a.Frame(), 0, 10, true)
|
||||||
main.AddItem(a.Crumbs(), 2, 1, false)
|
main.AddItem(a.Crumbs(), 2, 1, false)
|
||||||
main.AddItem(a.Flash(), 1, 1, false)
|
main.AddItem(a.Flash(), 1, 1, false)
|
||||||
|
|
@ -116,7 +117,7 @@ func (a *appView) Init(version string, rate int) {
|
||||||
func (a *appView) BufferChanged(s string) {}
|
func (a *appView) BufferChanged(s string) {}
|
||||||
|
|
||||||
// Active indicates the buff activity changed.
|
// Active indicates the buff activity changed.
|
||||||
func (a *appView) BufferActive(state bool) {
|
func (a *appView) BufferActive(state bool, _ ui.BufferKind) {
|
||||||
flex, ok := a.Main().GetPrimitive("main").(*tview.Flex)
|
flex, ok := a.Main().GetPrimitive("main").(*tview.Flex)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
|
@ -144,6 +145,7 @@ func (a *appView) toggleHeader(flag bool) {
|
||||||
|
|
||||||
func (a *appView) buildHeader() tview.Primitive {
|
func (a *appView) buildHeader() tview.Primitive {
|
||||||
header := tview.NewFlex()
|
header := tview.NewFlex()
|
||||||
|
header.SetBorderPadding(0, 0, 1, 1)
|
||||||
header.SetDirection(tview.FlexColumn)
|
header.SetDirection(tview.FlexColumn)
|
||||||
if !a.showHeader {
|
if !a.showHeader {
|
||||||
return header
|
return header
|
||||||
|
|
@ -192,7 +194,7 @@ func (a *appView) refreshIndicator() {
|
||||||
}
|
}
|
||||||
|
|
||||||
info := fmt.Sprintf(
|
info := fmt.Sprintf(
|
||||||
"[orange::b]K9s [aqua::]%s [white::]%s:%s:%s [lawngreen::]%s%%[white::]::[darkturquoise::]%s%%",
|
indicatorFmt,
|
||||||
a.version,
|
a.version,
|
||||||
cluster.ClusterName(),
|
cluster.ClusterName(),
|
||||||
cluster.UserName(),
|
cluster.UserName(),
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ func (c *command) defaultCmd() {
|
||||||
|
|
||||||
// Helpers...
|
// Helpers...
|
||||||
|
|
||||||
var policyMatcher = regexp.MustCompile(`\Apol\s([u|g|s]):([\w-:]+)\b`)
|
var authRX = regexp.MustCompile(`\Apol\s([u|g|s]):([\w-:]+)\b`)
|
||||||
|
|
||||||
func (c *command) isK9sCmd(cmd string) bool {
|
func (c *command) isK9sCmd(cmd string) bool {
|
||||||
cmds := strings.Split(cmd, " ")
|
cmds := strings.Split(cmd, " ")
|
||||||
|
|
@ -69,10 +69,10 @@ func (c *command) isK9sCmd(cmd string) bool {
|
||||||
c.app.aliasCmd(nil)
|
c.app.aliasCmd(nil)
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
if !policyMatcher.MatchString(cmd) {
|
if !authRX.MatchString(cmd) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
tokens := policyMatcher.FindAllStringSubmatch(cmd, -1)
|
tokens := authRX.FindAllStringSubmatch(cmd, -1)
|
||||||
if len(tokens) == 1 && len(tokens[0]) == 3 {
|
if len(tokens) == 1 && len(tokens[0]) == 3 {
|
||||||
c.app.inject(newPolicyView(c.app, tokens[0][1], tokens[0][2]))
|
c.app.inject(newPolicyView(c.app, tokens[0][1], tokens[0][2]))
|
||||||
return true
|
return true
|
||||||
|
|
@ -100,8 +100,8 @@ func (c *command) viewMetaFor(cmd string) (string, *viewer) {
|
||||||
}
|
}
|
||||||
v, ok := vv[gvr]
|
v, ok := vv[gvr]
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Error().Err(fmt.Errorf("Huh? `%s` viewer not found", cmd)).Msg("Viewer Failed")
|
log.Error().Err(fmt.Errorf("Huh? `%s` viewer not found", gvr)).Msg("Viewer Failed")
|
||||||
c.app.Flash().Warnf("Huh? `%s` viewer not found", gvr)
|
c.app.Flash().Warnf("Huh? viewer for %s not found", cmd)
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ func (v *containerView) k9sEnv() K9sEnv {
|
||||||
ns, n := namespaced(*v.path)
|
ns, n := namespaced(*v.path)
|
||||||
env["POD"] = n
|
env["POD"] = n
|
||||||
env["NAMESPACE"] = ns
|
env["NAMESPACE"] = ns
|
||||||
log.Debug().Msgf("OVER ENV %#v", env)
|
|
||||||
|
|
||||||
return env
|
return env
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ func newDetailsView(app *appView, backFn ui.ActionHandler) *detailsView {
|
||||||
v.SetTitleColor(tcell.ColorAqua)
|
v.SetTitleColor(tcell.ColorAqua)
|
||||||
v.SetInputCapture(v.keyboard)
|
v.SetInputCapture(v.keyboard)
|
||||||
|
|
||||||
v.cmdBuff = ui.NewCmdBuff('/')
|
v.cmdBuff = ui.NewCmdBuff('/', ui.FilterBuff)
|
||||||
v.cmdBuff.AddListener(app.Cmd())
|
v.cmdBuff.AddListener(app.Cmd())
|
||||||
v.cmdBuff.Reset()
|
v.cmdBuff.Reset()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
type K9sEnv map[string]string
|
type K9sEnv map[string]string
|
||||||
|
|
||||||
// EnvRX match $XXX custom arg.
|
// EnvRX match $XXX custom arg.
|
||||||
var envRX = regexp.MustCompile(`\A\$([\w|-]+)`)
|
var envRX = regexp.MustCompile(`\A\$([\w]+)`)
|
||||||
|
|
||||||
func (e K9sEnv) envFor(n string) (string, error) {
|
func (e K9sEnv) envFor(n string) (string, error) {
|
||||||
envs := envRX.FindStringSubmatch(n)
|
envs := envRX.FindStringSubmatch(n)
|
||||||
|
|
@ -22,5 +22,5 @@ func (e K9sEnv) envFor(n string) (string, error) {
|
||||||
return "", fmt.Errorf("No matching for %s", n)
|
return "", fmt.Errorf("No matching for %s", n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return env, nil
|
return envRX.ReplaceAllString(n, env), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,16 +14,17 @@ func TestK9sEnv(t *testing.T) {
|
||||||
err error
|
err error
|
||||||
e string
|
e string
|
||||||
}{
|
}{
|
||||||
"match": {q: "$A", err: nil, e: "10"},
|
"match": {q: "$A", e: "10"},
|
||||||
"noMatch": {q: "$BLEE", err: errors.New("No matching for $BLEE"), e: ""},
|
"noMatch": {q: "$BLEE", err: errors.New("No matching for $BLEE"), e: ""},
|
||||||
"lower": {q: "$b", err: nil, e: "blee"},
|
"lower": {q: "$b", e: "blee"},
|
||||||
"dash": {q: "$col-0", err: nil, e: "fred"},
|
"dash": {q: "$col0", e: "fred"},
|
||||||
|
"mix": {q: "$col0-blee", e: "fred-blee"},
|
||||||
}
|
}
|
||||||
|
|
||||||
e := K9sEnv{
|
e := K9sEnv{
|
||||||
"A": "10",
|
"A": "10",
|
||||||
"B": "blee",
|
"B": "blee",
|
||||||
"COL-0": "fred",
|
"COL0": "fred",
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, u := range uu {
|
for k, u := range uu {
|
||||||
|
|
|
||||||
|
|
@ -130,16 +130,16 @@ func (v *logView) flush(index int, buff []string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v.log(strings.Join(buff[:index], "\n"))
|
|
||||||
if atomic.LoadInt32(&v.autoScroll) == 1 {
|
if atomic.LoadInt32(&v.autoScroll) == 1 {
|
||||||
|
v.log(strings.Join(buff[:index], "\n"))
|
||||||
v.app.QueueUpdateDraw(func() {
|
v.app.QueueUpdateDraw(func() {
|
||||||
v.update()
|
v.updateIndicator()
|
||||||
v.logs.ScrollToEnd()
|
v.logs.ScrollToEnd()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *logView) update() {
|
func (v *logView) updateIndicator() {
|
||||||
status := "Off"
|
status := "Off"
|
||||||
if v.autoScroll == 1 {
|
if v.autoScroll == 1 {
|
||||||
status = "On"
|
status = "On"
|
||||||
|
|
@ -205,7 +205,7 @@ func (v *logView) toggleScrollCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
v.logs.LineUp()
|
v.logs.LineUp()
|
||||||
v.app.Flash().Info("Autoscroll is off.")
|
v.app.Flash().Info("Autoscroll is off.")
|
||||||
}
|
}
|
||||||
v.update()
|
v.updateIndicator()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -334,7 +334,6 @@ func (v *resourceView) refresh() {
|
||||||
if v.list.Namespaced() {
|
if v.list.Namespaced() {
|
||||||
v.list.SetNamespace(v.currentNS)
|
v.list.SetNamespace(v.currentNS)
|
||||||
}
|
}
|
||||||
log.Debug().Msgf("Reconcile with NS %q", v.currentNS)
|
|
||||||
if err := v.list.Reconcile(v.app.informer, v.path); err != nil {
|
if err := v.list.Reconcile(v.app.informer, v.path); err != nil {
|
||||||
v.app.Flash().Err(err)
|
v.app.Flash().Err(err)
|
||||||
}
|
}
|
||||||
|
|
@ -347,7 +346,6 @@ func (v *resourceView) refresh() {
|
||||||
|
|
||||||
func (v *resourceView) namespaceActions(aa ui.KeyActions) {
|
func (v *resourceView) namespaceActions(aa ui.KeyActions) {
|
||||||
ns, err := v.app.Conn().Config().CurrentNamespaceName()
|
ns, err := v.app.Conn().Config().CurrentNamespaceName()
|
||||||
log.Debug().Msgf("NAMESPACE %q -- %v", ns, err)
|
|
||||||
if err == nil && ns != resource.AllNamespace {
|
if err == nil && ns != resource.AllNamespace {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -401,7 +399,13 @@ func (v *resourceView) refreshActions() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *resourceView) customActions(aa ui.KeyActions) {
|
func (v *resourceView) customActions(aa ui.KeyActions) {
|
||||||
for k, plugin := range v.app.Config.K9s.Plugins {
|
pp := config.NewPlugins()
|
||||||
|
if err := pp.Load(); err != nil {
|
||||||
|
log.Warn().Msgf("No plugin configuration found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, plugin := range pp.Plugin {
|
||||||
if !in(plugin.Scopes, v.list.GetName()) {
|
if !in(plugin.Scopes, v.list.GetName()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -428,10 +432,12 @@ func (v *resourceView) execCmd(bin string, bg bool, args ...string) ui.ActionHan
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
|
|
||||||
env := v.envFn()
|
var (
|
||||||
aa := make([]string, len(args))
|
env = v.envFn()
|
||||||
|
aa = make([]string, len(args))
|
||||||
|
err error
|
||||||
|
)
|
||||||
for i, a := range args {
|
for i, a := range args {
|
||||||
var err error
|
|
||||||
aa[i], err = env.envFor(a)
|
aa[i], err = env.envFor(a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Args match failed")
|
log.Error().Err(err).Msg("Args match failed")
|
||||||
|
|
@ -454,12 +460,10 @@ func (v *resourceView) defaultK9sEnv() K9sEnv {
|
||||||
"NAMESPACE": ns,
|
"NAMESPACE": ns,
|
||||||
"NAME": n,
|
"NAME": n,
|
||||||
}
|
}
|
||||||
|
|
||||||
row := v.masterPage().GetRow()
|
row := v.masterPage().GetRow()
|
||||||
for i, r := range row {
|
for i, r := range row {
|
||||||
env["COL-"+strconv.Itoa(i)] = r
|
env["COL"+strconv.Itoa(i)] = r
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug().Msgf("ENVs %#v", env)
|
|
||||||
return env
|
return env
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,7 @@ func newTableView(app *appView, title string) *tableView {
|
||||||
}
|
}
|
||||||
v.SearchBuff().AddListener(app.Cmd())
|
v.SearchBuff().AddListener(app.Cmd())
|
||||||
v.SearchBuff().AddListener(&v)
|
v.SearchBuff().AddListener(&v)
|
||||||
v.SearchBuff().Reset()
|
v.SearchBuff().Set(app.filter)
|
||||||
|
|
||||||
v.bindKeys()
|
v.bindKeys()
|
||||||
|
|
||||||
return &v
|
return &v
|
||||||
|
|
@ -30,8 +29,8 @@ func newTableView(app *appView, title string) *tableView {
|
||||||
func (v *tableView) BufferChanged(s string) {}
|
func (v *tableView) BufferChanged(s string) {}
|
||||||
|
|
||||||
// BufferActive indicates the buff activity changed.
|
// BufferActive indicates the buff activity changed.
|
||||||
func (v *tableView) BufferActive(state bool) {
|
func (v *tableView) BufferActive(state bool, k ui.BufferKind) {
|
||||||
v.app.BufferActive(state)
|
v.app.BufferActive(state, k)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *tableView) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *tableView) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
@ -46,6 +45,11 @@ func (v *tableView) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
||||||
func (v *tableView) setFilterFn(fn func(string)) {
|
func (v *tableView) setFilterFn(fn func(string)) {
|
||||||
v.filterFn = fn
|
v.filterFn = fn
|
||||||
|
|
||||||
|
cmd := v.SearchBuff().String()
|
||||||
|
if isLabelSelector(cmd) && v.filterFn != nil {
|
||||||
|
v.filterFn(trimLabelSelector(cmd))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *tableView) bindKeys() {
|
func (v *tableView) bindKeys() {
|
||||||
|
|
@ -70,6 +74,7 @@ func (v *tableView) filterCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
||||||
v.SearchBuff().SetActive(false)
|
v.SearchBuff().SetActive(false)
|
||||||
cmd := v.SearchBuff().String()
|
cmd := v.SearchBuff().String()
|
||||||
|
v.app.filter = cmd
|
||||||
if isLabelSelector(cmd) && v.filterFn != nil {
|
if isLabelSelector(cmd) && v.filterFn != nil {
|
||||||
v.filterFn(trimLabelSelector(cmd))
|
v.filterFn(trimLabelSelector(cmd))
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -91,6 +96,7 @@ func (v *tableView) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if !v.SearchBuff().Empty() {
|
if !v.SearchBuff().Empty() {
|
||||||
v.app.Flash().Info("Clearing filter...")
|
v.app.Flash().Info("Clearing filter...")
|
||||||
}
|
}
|
||||||
|
v.app.filter = ""
|
||||||
if isLabelSelector(v.SearchBuff().String()) {
|
if isLabelSelector(v.SearchBuff().String()) {
|
||||||
v.filterFn("")
|
v.filterFn("")
|
||||||
}
|
}
|
||||||
|
|
@ -106,11 +112,12 @@ func (v *tableView) activateCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
v.app.Flash().Info("Filter mode activated.")
|
v.app.Flash().Info("Filter mode activated.")
|
||||||
if isLabelSelector(v.SearchBuff().String()) {
|
// if isLabelSelector(v.SearchBuff().String()) {
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
v.SearchBuff().Reset()
|
// v.SearchBuff().Reset()
|
||||||
v.SearchBuff().SetActive(true)
|
v.SearchBuff().SetActive(true)
|
||||||
|
v.SearchBuff().Set(v.app.filter)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ func TestTableViewFilter(t *testing.T) {
|
||||||
}
|
}
|
||||||
v.Update(data)
|
v.Update(data)
|
||||||
v.SearchBuff().SetActive(true)
|
v.SearchBuff().SetActive(true)
|
||||||
v.SearchBuff().Set([]rune("blee"))
|
v.SearchBuff().Set("blee")
|
||||||
v.filterCmd(nil)
|
v.filterCmd(nil)
|
||||||
assert.Equal(t, 2, v.GetRowCount())
|
assert.Equal(t, 2, v.GetRowCount())
|
||||||
v.resetCmd(nil)
|
v.resetCmd(nil)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue