bug fixes + cleanup

mine
derailed 2019-10-02 14:52:15 -06:00
parent 8778305bf2
commit ffd1c61c8c
29 changed files with 253 additions and 140 deletions

View File

@ -8,7 +8,7 @@ import (
"gopkg.in/yaml.v2"
)
// K9sAlias stores K9s command aliases.
// K9sAlias manages K9s aliases.
var K9sAlias = filepath.Join(K9sHome, "alias.yml")
type Alias map[string]string
@ -33,7 +33,7 @@ func (a Aliases) loadDefaults() {
a.Alias["crb"] = "rbac.authorization.k8s.io/v1/clusterrolebindings"
a.Alias["ro"] = "rbac.authorization.k8s.io/v1/roles"
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["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 {
f, err := ioutil.ReadFile(path)
if err != nil {

View File

@ -7,20 +7,42 @@ import (
"github.com/stretchr/testify/assert"
)
func TestAliasesLoad(t *testing.T) {
aa := config.NewAliases()
assert.Nil(t, aa.LoadAliases("test_assets/alias.yml"))
func TestAliasDefine(t *testing.T) {
uu := map[string]struct {
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) {
aa := config.NewAliases()
a := config.NewAliases()
aa.Alias["test"] = "fred"
aa.Alias["blee"] = "duh"
aa.SaveAliases("/tmp/a.yml")
a.Alias["test"] = "fred"
a.Alias["blee"] = "duh"
a.SaveAliases("/tmp/a.yml")
assert.Nil(t, aa.LoadAliases("/tmp/a.yml"))
assert.Equal(t, 28, len(aa.Alias))
assert.Nil(t, a.LoadAliases("/tmp/a.yml"))
assert.Equal(t, 28, len(a.Alias))
}

View File

@ -100,7 +100,6 @@ func TestConfigLoad(t *testing.T) {
assert.Equal(t, "minikube", cfg.K9s.CurrentCluster)
assert.NotNil(t, cfg.K9s.Clusters)
assert.Equal(t, 2, len(cfg.K9s.Clusters))
assert.Equal(t, 1, len(cfg.K9s.Plugins))
nn := []string{
"default",
@ -294,19 +293,6 @@ var expectedConfig = `k9s:
- kube-system
view:
active: ctx
plugins:
blah:
shortCut: shift-s
scopes:
- po
- dp
description: blee
command: duh
background: false
args:
- -n
- $NAMESPACE
- -boolean
`
var resetConfig = `k9s:
@ -324,17 +310,4 @@ var resetConfig = `k9s:
- default
view:
active: po
plugins:
blah:
shortCut: shift-s
scopes:
- po
- dp
description: blee
command: duh
background: false
args:
- -n
- $NAMESPACE
- -boolean
`

View File

@ -19,7 +19,6 @@ type K9s struct {
CurrentContext string `yaml:"currentContext"`
CurrentCluster string `yaml:"currentCluster"`
Clusters map[string]*Cluster `yaml:"clusters,omitempty"`
Plugins map[string]*Plugin `yaml:"plugins,omitempty"`
manualRefreshRate int
manualHeadless *bool
manualCommand *string
@ -32,7 +31,6 @@ func NewK9s() *K9s {
LogBufferSize: defaultLogBufferSize,
LogRequestSize: defaultLogRequestSize,
Clusters: make(map[string]*Cluster),
Plugins: make(map[string]*Plugin),
}
}

View File

@ -1,5 +1,20 @@
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
type Plugin struct {
ShortCut string `yaml:"shortCut"`
@ -9,3 +24,33 @@ type Plugin struct {
Background bool `yaml:"background"`
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
}

View File

@ -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))
}

View File

@ -197,7 +197,7 @@ func newTitle() Title {
BgColor: "black",
HighlightColor: "fuchsia",
CounterColor: "papayawhip",
FilterColor: "orange",
FilterColor: "seagreen",
}
}

View File

@ -27,15 +27,3 @@ k9s:
- kube-system
view:
active: po
plugins:
blah:
shortCut: shift-s
description: blee
scopes:
- po
- dp
command: duh
args:
- -n
- $NAMESPACE
- -boolean

View File

@ -0,0 +1,12 @@
plugin:
blah:
shortCut: shift-s
description: blee
scopes:
- po
- dp
command: duh
args:
- -n
- $NAMESPACE
- -boolean

View File

@ -16,7 +16,6 @@ import (
var (
// RestMapping holds k8s resource mapping
// BOZO!! Has to be a better way...
RestMapping = &RestMapper{}
toFileName = regexp.MustCompile(`[^(\w/\.)]`)
)

View File

@ -54,7 +54,7 @@ func NewApp() *App {
actions: make(KeyActions),
pages: tview.NewPages(),
content: tview.NewPages(),
cmdBuff: NewCmdBuff(':'),
cmdBuff: NewCmdBuff(':', CommandBuff),
}
s.RefreshStyles()
@ -62,7 +62,7 @@ func NewApp() *App {
s.views = map[string]tview.Primitive{
"menu": NewMenuView(s.Styles),
"logo": NewLogoView(s.Styles),
"cmd": NewCmdView(s.Styles, '🐶'),
"cmd": NewCmdView(s.Styles),
"crumbs": NewCrumbsView(s.Styles),
}
@ -186,7 +186,6 @@ func (a *App) activateCmd(evt *tcell.EventKey) *tcell.EventKey {
if a.InCmdMode() {
return evt
}
a.Flash().Info("Command mode activated.")
a.cmdBuff.SetActive(true)
a.cmdBuff.Clear()

View File

@ -5,6 +5,7 @@ import (
"github.com/derailed/k9s/internal/config"
"github.com/derailed/tview"
"github.com/gdamore/tcell"
)
const defaultPrompt = "%c> %s"
@ -20,8 +21,8 @@ type CmdView struct {
}
// NewCmdView returns a new command view.
func NewCmdView(styles *config.Styles, ic rune) *CmdView {
v := CmdView{styles: styles, icon: ic, TextView: tview.NewTextView()}
func NewCmdView(styles *config.Styles) *CmdView {
v := CmdView{styles: styles, TextView: tview.NewTextView()}
{
v.SetWordWrap(true)
v.SetWrap(true)
@ -29,7 +30,7 @@ func NewCmdView(styles *config.Styles, ic rune) *CmdView {
v.SetBorder(true)
v.SetBorderPadding(0, 0, 1, 1)
v.SetBackgroundColor(styles.BgColor())
v.SetBorderColor(config.AsColor(styles.Frame().Border.FocusColor))
// v.SetBorderColor(config.AsColor(styles.Frame().Border.FocusColor))
v.SetTextColor(styles.FgColor())
}
return &v
@ -67,11 +68,13 @@ func (v *CmdView) BufferChanged(s string) {
}
// BufferActive indicates the buff activity changed.
func (v *CmdView) BufferActive(f bool) {
func (v *CmdView) BufferActive(f bool, k BufferKind) {
v.activated = f
if f {
v.SetBorder(true)
v.icon = iconFor(k)
v.SetTextColor(v.styles.FgColor())
v.SetBorderColor(colorFor(k))
v.activate()
} else {
v.SetBorder(false)
@ -79,3 +82,20 @@ func (v *CmdView) BufferActive(f bool) {
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 '🤓'
}
}

View File

@ -2,19 +2,30 @@ package ui
const maxBuff = 10
const (
// CommandBuff indicates a command buffer.
CommandBuff BufferKind = 1 << iota
// FilterBuff indicates a search buffer.
FilterBuff
)
type (
// BufferKind indicates a buffer type
BufferKind int8
// BuffWatcher represents a command buffer listener.
BuffWatcher interface {
// Changed indicates the buffer was changed.
BufferChanged(s string)
// Active indicates the buff activity changed.
BufferActive(state bool)
BufferActive(state bool, kind BufferKind)
}
// CmdBuff represents user command input.
CmdBuff struct {
buff []rune
kind BufferKind
hotKey rune
active bool
listeners []BuffWatcher
@ -22,9 +33,10 @@ type (
)
// NewCmdBuff returns a new command buffer.
func NewCmdBuff(key rune) *CmdBuff {
func NewCmdBuff(key rune, kind BufferKind) *CmdBuff {
return &CmdBuff{
hotKey: key,
kind: kind,
buff: make([]rune, 0, maxBuff),
listeners: []BuffWatcher{},
}
@ -47,8 +59,9 @@ func (c *CmdBuff) String() string {
}
// Set initializes the buffer with a command.
func (c *CmdBuff) Set(rr []rune) {
c.buff = rr
func (c *CmdBuff) Set(cmd string) {
c.buff = []rune(cmd)
c.fireChanged()
}
// Add adds a new charater to the buffer.
@ -104,6 +117,6 @@ func (c *CmdBuff) fireChanged() {
func (c *CmdBuff) fireActive(b bool) {
for _, l := range c.listeners {
l.BufferActive(b)
l.BufferActive(b, c.kind)
}
}

View File

@ -16,7 +16,7 @@ func (l *testListener) BufferChanged(s string) {
l.text = s
}
func (l *testListener) BufferActive(s bool) {
func (l *testListener) BufferActive(s bool, _ BufferKind) {
if s {
l.act++
return
@ -25,7 +25,7 @@ func (l *testListener) BufferActive(s bool) {
}
func TestCmdBuffActivate(t *testing.T) {
b, l := NewCmdBuff('>'), testListener{}
b, l := NewCmdBuff('>', CommandBuff), testListener{}
b.AddListener(&l)
b.SetActive(true)
@ -35,7 +35,7 @@ func TestCmdBuffActivate(t *testing.T) {
}
func TestCmdBuffDeactivate(t *testing.T) {
b, l := NewCmdBuff('>'), testListener{}
b, l := NewCmdBuff('>', CommandBuff), testListener{}
b.AddListener(&l)
b.SetActive(false)
@ -45,7 +45,7 @@ func TestCmdBuffDeactivate(t *testing.T) {
}
func TestCmdBuffChanged(t *testing.T) {
b, l := NewCmdBuff('>'), testListener{}
b, l := NewCmdBuff('>', CommandBuff), testListener{}
b.AddListener(&l)
b.Add('b')
@ -77,7 +77,7 @@ func TestCmdBuffChanged(t *testing.T) {
}
func TestCmdBuffAdd(t *testing.T) {
b := NewCmdBuff('>')
b := NewCmdBuff('>', CommandBuff)
uu := []struct {
runes []rune
@ -98,7 +98,7 @@ func TestCmdBuffAdd(t *testing.T) {
}
func TestCmdBuffDel(t *testing.T) {
b := NewCmdBuff('>')
b := NewCmdBuff('>', CommandBuff)
uu := []struct {
runes []rune
@ -120,7 +120,7 @@ func TestCmdBuffDel(t *testing.T) {
}
func TestCmdBuffEmpty(t *testing.T) {
b := NewCmdBuff('>')
b := NewCmdBuff('>', CommandBuff)
uu := []struct {
runes []rune

View File

@ -9,20 +9,20 @@ import (
func TestNewCmdUpdate(t *testing.T) {
defaults, _ := config.NewStyles("")
v := NewCmdView(defaults, 'T')
v := NewCmdView(defaults)
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) {
defaults, _ := config.NewStyles("")
v := NewCmdView(defaults, 'T')
v := NewCmdView(defaults)
v.update("blee")
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())
v.BufferActive(true)
v.BufferActive(true, CommandBuff)
assert.True(t, v.InCmdMode())
}

View File

@ -47,7 +47,7 @@ func (v *MenuView) HydrateMenu(hh Hints) {
t := v.buildMenuTable(hh)
for row := 0; row < len(t); row++ {
for col := 0; col < len(t[row]); col++ {
if len(t[row][col]) == 0 {
if t[row][col] == "" {
continue
}
c := tview.NewTableCell(t[row][col])
@ -57,33 +57,42 @@ func (v *MenuView) HydrateMenu(hh Hints) {
}
}
func isDigit(s string) bool {
return menuRX.MatchString(s)
}
func (v *MenuView) buildMenuTable(hh Hints) [][]string {
table := make([][]Hint, maxRows+1)
colCount := (len(hh) / maxRows) + 1
for row := 0; row < maxRows; row++ {
table[row] = make([]Hint, colCount+1)
table := make([][]Hint, maxRows)
colCount := len(hh) / maxRows
if colCount == 0 {
colCount = 1
}
var row, col int
if isDigit(hh[0].Mnemonic) {
colCount++
}
for row := 0; row < maxRows; row++ {
table[row] = make([]Hint, colCount)
}
var row, col, added int
firstCmd := true
maxKeys := make([]int, colCount+1)
for _, h := range hh {
if !h.Visible {
continue
}
isDigit := menuRX.MatchString(h.Mnemonic)
if !isDigit && firstCmd {
if !isDigit(h.Mnemonic) && firstCmd {
row, col, firstCmd = 0, col+1, false
if added == 0 {
col = 0
}
}
if maxKeys[col] < len(h.Mnemonic) {
maxKeys[col] = len(h.Mnemonic)
}
table[row][col] = h
row++
added, row = added+1, row+1
if row >= maxRows {
col++
row = 0
row, col = 0, col+1
}
}
@ -248,6 +257,7 @@ func initKeys() {
initNumbKeys()
initStdKeys()
initShiftKeys()
initCtrlKeys()
}
func initNumbKeys() {
@ -292,6 +302,12 @@ func initStdKeys() {
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() {
tcell.KeyNames[tcell.Key(KeyShiftA)] = "Shift-A"
tcell.KeyNames[tcell.Key(KeyShiftB)] = "Shift-B"

View File

@ -12,9 +12,9 @@ func TestNewMenuView(t *testing.T) {
defaults, _ := config.NewStyles("")
v := NewMenuView(defaults)
v.HydrateMenu(Hints{
{"0", "zero", true},
{"a", "bleeA", true},
{"b", "bleeB", true},
{"0", "zero", true},
})
assert.Equal(t, " [fuchsia:black:b]<0> [white:black:d]zero ", v.GetCell(0, 0).Text)

View File

@ -12,8 +12,6 @@ import (
"github.com/derailed/k9s/internal/resource"
"github.com/derailed/tview"
"github.com/gdamore/tcell"
// "github.com/ktr0731/go-fuzzyfinder/matching"
"github.com/rs/zerolog/log"
"github.com/sahilm/fuzzy"
"k8s.io/apimachinery/pkg/util/duration"
@ -52,7 +50,7 @@ func NewTable(title string, styles *config.Styles) *Table {
Table: tview.NewTable(),
styles: styles,
actions: make(KeyActions),
cmdBuff: NewCmdBuff('/'),
cmdBuff: NewCmdBuff('/', FilterBuff),
baseTitle: title,
sortCol: SortColumn{0, 0, true},
}
@ -178,6 +176,7 @@ func (v *Table) keyboard(evt *tcell.EventKey) *tcell.EventKey {
v.SearchBuff().Add(evt.Rune())
v.ClearSelection()
v.doUpdate(v.filtered())
v.UpdateTitle()
v.SelectFirstRow()
return nil
}
@ -383,7 +382,7 @@ func (v *Table) filtered() resource.TableData {
q := v.cmdBuff.String()
if isFuzzySelector(q) {
return v.fuzzFilter(q[2:])
return v.fuzzyFilter(q[2:])
}
return v.rxFilter(q)
@ -412,7 +411,7 @@ func (v *Table) rxFilter(q string) resource.TableData {
return filtered
}
func (v *Table) fuzzFilter(q string) resource.TableData {
func (v *Table) fuzzyFilter(q string) resource.TableData {
var ss, kk []string
for k, row := range v.data.Rows {
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())
}
if !v.cmdBuff.IsActive() && !v.cmdBuff.Empty() {
if !v.cmdBuff.Empty() {
cmd := v.cmdBuff.String()
if isLabelSelector(cmd) {
cmd = trimLabelSelector(cmd)

View File

@ -49,6 +49,7 @@ func (v *aliasView) Init(context.Context, string) {
func (v *aliasView) registerActions() {
v.RmAction(ui.KeyShiftA)
v.RmAction(tcell.KeyCtrlS)
v.SetActions(ui.KeyActions{
tcell.KeyEnter: ui.NewKeyAction("Goto", v.gotoCmd, true),

View File

@ -20,6 +20,7 @@ const (
splashTime = 1
devMode = "dev"
clusterRefresh = time.Duration(5 * time.Second)
indicatorFmt = "[orange::b]K9s [aqua::]%s [white::]%s:%s:%s [lawngreen::]%s%%[white::]::[darkturquoise::]%s%%"
)
type (
@ -55,6 +56,7 @@ type (
forwarders map[string]forwarder
version string
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)
main.AddItem(a.indicator(), 1, 1, false)
// main.AddItem(a.Cmd(), 3, 1, false)
main.AddItem(a.Frame(), 0, 10, true)
main.AddItem(a.Crumbs(), 2, 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) {}
// 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)
if !ok {
return
@ -144,6 +145,7 @@ func (a *appView) toggleHeader(flag bool) {
func (a *appView) buildHeader() tview.Primitive {
header := tview.NewFlex()
header.SetBorderPadding(0, 0, 1, 1)
header.SetDirection(tview.FlexColumn)
if !a.showHeader {
return header
@ -192,7 +194,7 @@ func (a *appView) refreshIndicator() {
}
info := fmt.Sprintf(
"[orange::b]K9s [aqua::]%s [white::]%s:%s:%s [lawngreen::]%s%%[white::]::[darkturquoise::]%s%%",
indicatorFmt,
a.version,
cluster.ClusterName(),
cluster.UserName(),

View File

@ -54,7 +54,7 @@ func (c *command) defaultCmd() {
// 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 {
cmds := strings.Split(cmd, " ")
@ -69,10 +69,10 @@ func (c *command) isK9sCmd(cmd string) bool {
c.app.aliasCmd(nil)
return true
default:
if !policyMatcher.MatchString(cmd) {
if !authRX.MatchString(cmd) {
return false
}
tokens := policyMatcher.FindAllStringSubmatch(cmd, -1)
tokens := authRX.FindAllStringSubmatch(cmd, -1)
if len(tokens) == 1 && len(tokens[0]) == 3 {
c.app.inject(newPolicyView(c.app, tokens[0][1], tokens[0][2]))
return true
@ -100,8 +100,8 @@ func (c *command) viewMetaFor(cmd string) (string, *viewer) {
}
v, ok := vv[gvr]
if !ok {
log.Error().Err(fmt.Errorf("Huh? `%s` viewer not found", cmd)).Msg("Viewer Failed")
c.app.Flash().Warnf("Huh? `%s` viewer not found", gvr)
log.Error().Err(fmt.Errorf("Huh? `%s` viewer not found", gvr)).Msg("Viewer Failed")
c.app.Flash().Warnf("Huh? viewer for %s not found", cmd)
return "", nil
}

View File

@ -60,7 +60,6 @@ func (v *containerView) k9sEnv() K9sEnv {
ns, n := namespaced(*v.path)
env["POD"] = n
env["NAMESPACE"] = ns
log.Debug().Msgf("OVER ENV %#v", env)
return env
}

View File

@ -55,7 +55,7 @@ func newDetailsView(app *appView, backFn ui.ActionHandler) *detailsView {
v.SetTitleColor(tcell.ColorAqua)
v.SetInputCapture(v.keyboard)
v.cmdBuff = ui.NewCmdBuff('/')
v.cmdBuff = ui.NewCmdBuff('/', ui.FilterBuff)
v.cmdBuff.AddListener(app.Cmd())
v.cmdBuff.Reset()

View File

@ -10,7 +10,7 @@ import (
type K9sEnv map[string]string
// 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) {
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 env, nil
return envRX.ReplaceAllString(n, env), nil
}

View File

@ -14,16 +14,17 @@ func TestK9sEnv(t *testing.T) {
err error
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: ""},
"lower": {q: "$b", err: nil, e: "blee"},
"dash": {q: "$col-0", err: nil, e: "fred"},
"lower": {q: "$b", e: "blee"},
"dash": {q: "$col0", e: "fred"},
"mix": {q: "$col0-blee", e: "fred-blee"},
}
e := K9sEnv{
"A": "10",
"B": "blee",
"COL-0": "fred",
"COL0": "fred",
}
for k, u := range uu {

View File

@ -130,16 +130,16 @@ func (v *logView) flush(index int, buff []string) {
return
}
v.log(strings.Join(buff[:index], "\n"))
if atomic.LoadInt32(&v.autoScroll) == 1 {
v.log(strings.Join(buff[:index], "\n"))
v.app.QueueUpdateDraw(func() {
v.update()
v.updateIndicator()
v.logs.ScrollToEnd()
})
}
}
func (v *logView) update() {
func (v *logView) updateIndicator() {
status := "Off"
if v.autoScroll == 1 {
status = "On"
@ -205,7 +205,7 @@ func (v *logView) toggleScrollCmd(evt *tcell.EventKey) *tcell.EventKey {
v.logs.LineUp()
v.app.Flash().Info("Autoscroll is off.")
}
v.update()
v.updateIndicator()
return nil
}

View File

@ -334,7 +334,6 @@ func (v *resourceView) refresh() {
if v.list.Namespaced() {
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 {
v.app.Flash().Err(err)
}
@ -347,7 +346,6 @@ func (v *resourceView) refresh() {
func (v *resourceView) namespaceActions(aa ui.KeyActions) {
ns, err := v.app.Conn().Config().CurrentNamespaceName()
log.Debug().Msgf("NAMESPACE %q -- %v", ns, err)
if err == nil && ns != resource.AllNamespace {
return
}
@ -401,7 +399,13 @@ func (v *resourceView) refreshActions() {
}
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()) {
continue
}
@ -428,10 +432,12 @@ func (v *resourceView) execCmd(bin string, bg bool, args ...string) ui.ActionHan
return evt
}
env := v.envFn()
aa := make([]string, len(args))
var (
env = v.envFn()
aa = make([]string, len(args))
err error
)
for i, a := range args {
var err error
aa[i], err = env.envFor(a)
if err != nil {
log.Error().Err(err).Msg("Args match failed")
@ -454,12 +460,10 @@ func (v *resourceView) defaultK9sEnv() K9sEnv {
"NAMESPACE": ns,
"NAME": n,
}
row := v.masterPage().GetRow()
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
}

View File

@ -19,8 +19,7 @@ func newTableView(app *appView, title string) *tableView {
}
v.SearchBuff().AddListener(app.Cmd())
v.SearchBuff().AddListener(&v)
v.SearchBuff().Reset()
v.SearchBuff().Set(app.filter)
v.bindKeys()
return &v
@ -30,8 +29,8 @@ func newTableView(app *appView, title string) *tableView {
func (v *tableView) BufferChanged(s string) {}
// BufferActive indicates the buff activity changed.
func (v *tableView) BufferActive(state bool) {
v.app.BufferActive(state)
func (v *tableView) BufferActive(state bool, k ui.BufferKind) {
v.app.BufferActive(state, k)
}
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)) {
v.filterFn = fn
cmd := v.SearchBuff().String()
if isLabelSelector(cmd) && v.filterFn != nil {
v.filterFn(trimLabelSelector(cmd))
}
}
func (v *tableView) bindKeys() {
@ -70,6 +74,7 @@ func (v *tableView) filterCmd(evt *tcell.EventKey) *tcell.EventKey {
v.SearchBuff().SetActive(false)
cmd := v.SearchBuff().String()
v.app.filter = cmd
if isLabelSelector(cmd) && v.filterFn != nil {
v.filterFn(trimLabelSelector(cmd))
return nil
@ -91,6 +96,7 @@ func (v *tableView) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
if !v.SearchBuff().Empty() {
v.app.Flash().Info("Clearing filter...")
}
v.app.filter = ""
if isLabelSelector(v.SearchBuff().String()) {
v.filterFn("")
}
@ -106,11 +112,12 @@ func (v *tableView) activateCmd(evt *tcell.EventKey) *tcell.EventKey {
}
v.app.Flash().Info("Filter mode activated.")
if isLabelSelector(v.SearchBuff().String()) {
return nil
}
v.SearchBuff().Reset()
// if isLabelSelector(v.SearchBuff().String()) {
// return nil
// }
// v.SearchBuff().Reset()
v.SearchBuff().SetActive(true)
v.SearchBuff().Set(v.app.filter)
return nil
}

View File

@ -73,7 +73,7 @@ func TestTableViewFilter(t *testing.T) {
}
v.Update(data)
v.SearchBuff().SetActive(true)
v.SearchBuff().Set([]rune("blee"))
v.SearchBuff().Set("blee")
v.filterCmd(nil)
assert.Equal(t, 2, v.GetRowCount())
v.resetCmd(nil)