added search/filter feature + support init container
parent
2cf5ec8877
commit
efb95866b1
81
go.mod
81
go.mod
|
|
@ -1,71 +1,48 @@
|
|||
module github.com/derailed/k9s
|
||||
|
||||
// replace github.com/k8sland/tview => /Users/fernand/go_wk/k8sland/src/github.com/k8sland/tview
|
||||
// replace github.com/derailed/tview => /Users/fernand/go_wk/derailed/src/github.com/derailed/tview
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.34.0
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.4.3
|
||||
github.com/Azure/go-autorest v11.4.0+incompatible
|
||||
github.com/census-instrumentation/opencensus-proto v0.1.0-0.20181214143942-ba49f56771b8
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/evanphx/json-patch v4.1.0+incompatible
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.4.3 // indirect
|
||||
github.com/Azure/go-autorest v11.4.0+incompatible // indirect
|
||||
github.com/derailed/tview v0.1.1
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||
github.com/evanphx/json-patch v4.1.0+incompatible // indirect
|
||||
github.com/fatih/camelcase v1.0.0 // indirect
|
||||
github.com/gdamore/encoding v1.0.0
|
||||
github.com/gdamore/tcell v1.1.0
|
||||
github.com/gogo/protobuf v1.1.1
|
||||
github.com/golang/protobuf v1.2.0
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf
|
||||
github.com/googleapis/gnostic v0.2.0
|
||||
github.com/gophercloud/gophercloud v0.0.0-20190206201033-83b528acebb4
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.6.2
|
||||
github.com/imdario/mergo v0.3.6
|
||||
github.com/inconshreveable/mousetrap v1.0.0
|
||||
github.com/json-iterator/go v1.1.5
|
||||
github.com/jtolds/gls v4.2.1+incompatible // indirect
|
||||
github.com/k8sland/tview v0.1.1
|
||||
github.com/gdamore/tcell v1.1.1
|
||||
github.com/gogo/protobuf v1.1.1 // indirect
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c // indirect
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect
|
||||
github.com/googleapis/gnostic v0.2.0 // indirect
|
||||
github.com/gophercloud/gophercloud v0.0.0-20190206201033-83b528acebb4 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
|
||||
github.com/imdario/mergo v0.3.6 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.5 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08
|
||||
github.com/mattn/go-runewidth v0.0.4
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
|
||||
github.com/modern-go/reflect2 v1.0.1
|
||||
github.com/onsi/gomega v1.4.3
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/onsi/gomega v1.4.3 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/petergtz/pegomock v0.0.0-20181206220228-b113d17a7e81
|
||||
github.com/pmezard/go-difflib v1.0.0
|
||||
github.com/sirupsen/logrus v1.2.0
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 // indirect
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c // indirect
|
||||
github.com/sirupsen/logrus v1.3.0
|
||||
github.com/spf13/cobra v0.0.3
|
||||
github.com/spf13/pflag v1.0.3
|
||||
github.com/spf13/pflag v1.0.3 // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
go.opencensus.io v0.19.0
|
||||
golang.org/x/crypto v0.0.0-20181106171534-e4dc69e5b2fd
|
||||
golang.org/x/net v0.0.0-20181217023233-e147a9138326
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f
|
||||
golang.org/x/sys v0.0.0-20181218192612-074acd46bca6
|
||||
golang.org/x/text v0.3.0
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2
|
||||
google.golang.org/appengine v1.3.0
|
||||
google.golang.org/genproto v0.0.0-20181219182458-5a97ab628bfb
|
||||
google.golang.org/grpc v1.17.0
|
||||
gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1
|
||||
go.opencensus.io v0.19.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20181106171534-e4dc69e5b2fd // indirect
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
k8s.io/api v0.0.0-20190202010724-74b699b93c15
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20190219174952-73ef971883ab
|
||||
k8s.io/apimachinery v0.0.0-20190207091153-095b9d203467
|
||||
k8s.io/cli-runtime v0.0.0-20190207094101-a32b78e5dd0a
|
||||
k8s.io/client-go v10.0.0+incompatible
|
||||
k8s.io/klog v0.1.0
|
||||
k8s.io/klog v0.1.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20190215190454-ea82251f3668 // indirect
|
||||
k8s.io/kubernetes v1.13.3
|
||||
k8s.io/metrics v0.0.0-20181121073115-d8618695b08f
|
||||
k8s.io/utils v0.0.0-20190212002617-cdba02414f76 // indirect
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190130003954-e5e029740eb8
|
||||
sigs.k8s.io/yaml v1.1.0
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190130003954-e5e029740eb8 // indirect
|
||||
sigs.k8s.io/yaml v1.1.0 // indirect
|
||||
)
|
||||
|
|
|
|||
36
go.sum
36
go.sum
|
|
@ -13,20 +13,24 @@ github.com/census-instrumentation/opencensus-proto v0.1.0-0.20181214143942-ba49f
|
|||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/derailed/tview v0.1.1 h1:s9rGoufUkZYsIFCfV3M23yHxTKa7Rt6n+QFaOgnirpk=
|
||||
github.com/derailed/tview v0.1.1/go.mod h1:WRYVfgb2PBMLZ/muaSpOc/4H4fYsOPnHOaGnBoJ+hGE=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/evanphx/json-patch v4.1.0+incompatible h1:K1MDoo4AZ4wU0GIU/fPmtZg7VpzLjCxu+UwBD1FvwOc=
|
||||
github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell v1.1.0 h1:RbQgl7jukmdqROeNcKps7R2YfDCQbWkOd1BwdXrxfr4=
|
||||
github.com/gdamore/tcell v1.1.0/go.mod h1:tqyG50u7+Ctv1w5VX67kLzKcj9YXR/JSBZQq/+mLl1A=
|
||||
github.com/gdamore/tcell v1.1.1 h1:U73YL+jMem2XfhvaIUfPO6MpJawaG92B2funXVb9qLs=
|
||||
github.com/gdamore/tcell v1.1.1/go.mod h1:K1udHkiR3cOtlpKG5tZPD5XxrF7v2y7lDq7Whcj+xkQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
|
|
@ -35,6 +39,7 @@ github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM
|
|||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
|
|
@ -42,29 +47,29 @@ github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhp
|
|||
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/gophercloud/gophercloud v0.0.0-20190206201033-83b528acebb4 h1:wAcVWwS69gs5c6cFkCa/ns/eaL2gC761nF8Ugvd1dGw=
|
||||
github.com/gophercloud/gophercloud v0.0.0-20190206201033-83b528acebb4/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.6.2 h1:8KyC64BiO8ndiGHY5DlFWWdangUPC9QHPakFRre/Ud0=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.6.2/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
|
||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/k8sland/tview v0.1.1 h1:732F8kcz5EjUAsFTZJ5BkJx3n34+EwiQRuDaegeS2yU=
|
||||
github.com/k8sland/tview v0.1.1/go.mod h1:PwEtOCvGYNgUA2FQuciQBKB6igksu4GHtq3GY6vOkQo=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08 h1:5MnxBC15uMxFv5FY/J/8vzyaBiArCOkMdFT9Jsw78iY=
|
||||
github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4=
|
||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
|
|
@ -72,6 +77,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
|
|
@ -91,11 +97,9 @@ github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7q
|
|||
github.com/prometheus/common v0.0.0-20181218105931-67670fe90761/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/rivo/tview v0.0.0-20190124120153-84fdb36408f3/go.mod h1:J4W+hErFfITUbyFAEXizpmkuxX7ZN56dopxHB4XQhMw=
|
||||
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/rivo/tview v0.0.0-20190213202703-b373355e9db4/go.mod h1:J4W+hErFfITUbyFAEXizpmkuxX7ZN56dopxHB4XQhMw=
|
||||
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
|
||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
|
|
@ -140,6 +144,7 @@ google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+
|
|||
google.golang.org/api v0.0.0-20181220000619-583d854617af h1:iQMS7JKv/0w/iiWf1M49Cg3dmOkBoBZT5KheqPDpaac=
|
||||
google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
|
|
@ -150,12 +155,16 @@ google.golang.org/grpc v1.15.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9M
|
|||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0 h1:TRJYBgMclJvGYn2rIMjj+h9KtMt5r1Ij7ODVRIZkwhk=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 h1:FVCohIoYO7IJoDDVpV2pdq7SgrMH6wHnuTyrdrxJNoY=
|
||||
gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
|
|
@ -164,7 +173,6 @@ honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWh
|
|||
honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.0.0-20190202010724-74b699b93c15 h1:AoUGjnJ3PJMFz+Rkp4lx3X+6mPUnY1MESJhbUSGX+pc=
|
||||
k8s.io/api v0.0.0-20190202010724-74b699b93c15/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
|
||||
k8s.io/apiextensions-apiserver v0.0.0-20190219174952-73ef971883ab/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
|
||||
k8s.io/apimachinery v0.0.0-20190207091153-095b9d203467 h1:zmz9UYvvXrK/B8EDqFuqreJEaXbIWdzEkNgWrN/Cd3o=
|
||||
k8s.io/apimachinery v0.0.0-20190207091153-095b9d203467/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
|
||||
k8s.io/cli-runtime v0.0.0-20190207094101-a32b78e5dd0a h1:MrGQxLLZ09Bl5hYYU9VlKnhY60bpPlYd9yXOPnxkdc0=
|
||||
|
|
@ -173,11 +181,13 @@ k8s.io/client-go v10.0.0+incompatible h1:F1IqCqw7oMBzDkqlcBymRq1450wD0eNqLE9jzUr
|
|||
k8s.io/client-go v10.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
|
||||
k8s.io/klog v0.1.0 h1:I5HMfc/DtuVaGR1KPwUrTc476K8NCqNBldC7H4dYEzk=
|
||||
k8s.io/klog v0.1.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/kube-openapi v0.0.0-20190215190454-ea82251f3668 h1:M80qeWaBNOX2Uc4plRHcb6k+3YE5VWMaJXKZo+tX9aU=
|
||||
k8s.io/kube-openapi v0.0.0-20190215190454-ea82251f3668/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
|
||||
k8s.io/kubernetes v1.13.3 h1:46t44D87wKtdKFgr/lXM60K8xPrW0wO67Woof3Vsv6E=
|
||||
k8s.io/kubernetes v1.13.3/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/metrics v0.0.0-20181121073115-d8618695b08f h1:HyUoIBzks9xTaSnMJ6kv/SSmwaQQccokuiriu2cV0aA=
|
||||
k8s.io/metrics v0.0.0-20181121073115-d8618695b08f/go.mod h1:a25VAbm3QT3xiVl1jtoF1ueAKQM149UdZ+L93ePfV3M=
|
||||
k8s.io/utils v0.0.0-20190212002617-cdba02414f76 h1:xj+3UbmNFpFTZmF/snXlV1oYutGHU1VmjfkfH1ugr6A=
|
||||
k8s.io/utils v0.0.0-20190212002617-cdba02414f76/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190130003954-e5e029740eb8 h1:UBkrbecoQliUCGP3Izc0NRKu877BV6VLT3lUykRJURM=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190130003954-e5e029740eb8/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ import (
|
|||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/k8s"
|
||||
"github.com/derailed/k9s/internal/views"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/k8sland/tview"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
|
|
@ -82,7 +80,7 @@ func initK9sConfig() {
|
|||
}
|
||||
|
||||
ctx := cfg.CurrentContext
|
||||
switch{
|
||||
switch {
|
||||
case isSet(k8sFlags.Context):
|
||||
ctx = *k8sFlags.Context
|
||||
config.Root.K9s.CurrentContext = ctx
|
||||
|
|
@ -131,9 +129,6 @@ func run(cmd *cobra.Command, args []string) {
|
|||
log.SetFormatter(&log.TextFormatter{FullTimestamp: true, ForceColors: true})
|
||||
|
||||
initK9s()
|
||||
initStyles()
|
||||
initKeys()
|
||||
|
||||
app := views.NewApp()
|
||||
{
|
||||
app.Init(version, refreshRate, k8sFlags)
|
||||
|
|
@ -141,52 +136,6 @@ func run(cmd *cobra.Command, args []string) {
|
|||
}
|
||||
}
|
||||
|
||||
func initKeys() {
|
||||
tcell.KeyNames[tcell.Key(views.Key0)] = "0"
|
||||
tcell.KeyNames[tcell.Key(views.Key1)] = "1"
|
||||
tcell.KeyNames[tcell.Key(views.Key2)] = "2"
|
||||
tcell.KeyNames[tcell.Key(views.Key3)] = "3"
|
||||
tcell.KeyNames[tcell.Key(views.Key4)] = "4"
|
||||
tcell.KeyNames[tcell.Key(views.Key5)] = "5"
|
||||
tcell.KeyNames[tcell.Key(views.Key6)] = "6"
|
||||
tcell.KeyNames[tcell.Key(views.Key7)] = "7"
|
||||
tcell.KeyNames[tcell.Key(views.Key8)] = "8"
|
||||
tcell.KeyNames[tcell.Key(views.Key9)] = "9"
|
||||
tcell.KeyNames[tcell.Key(views.KeyA)] = "a"
|
||||
tcell.KeyNames[tcell.Key(views.KeyB)] = "b"
|
||||
tcell.KeyNames[tcell.Key(views.KeyC)] = "c"
|
||||
tcell.KeyNames[tcell.Key(views.KeyD)] = "d"
|
||||
tcell.KeyNames[tcell.Key(views.KeyE)] = "e"
|
||||
tcell.KeyNames[tcell.Key(views.KeyF)] = "f"
|
||||
tcell.KeyNames[tcell.Key(views.KeyG)] = "g"
|
||||
tcell.KeyNames[tcell.Key(views.KeyH)] = "h"
|
||||
tcell.KeyNames[tcell.Key(views.KeyI)] = "i"
|
||||
tcell.KeyNames[tcell.Key(views.KeyJ)] = "j"
|
||||
tcell.KeyNames[tcell.Key(views.KeyK)] = "k"
|
||||
tcell.KeyNames[tcell.Key(views.KeyL)] = "l"
|
||||
tcell.KeyNames[tcell.Key(views.KeyM)] = "m"
|
||||
tcell.KeyNames[tcell.Key(views.KeyN)] = "n"
|
||||
tcell.KeyNames[tcell.Key(views.KeyO)] = "o"
|
||||
tcell.KeyNames[tcell.Key(views.KeyP)] = "p"
|
||||
tcell.KeyNames[tcell.Key(views.KeyQ)] = "q"
|
||||
tcell.KeyNames[tcell.Key(views.KeyR)] = "r"
|
||||
tcell.KeyNames[tcell.Key(views.KeyS)] = "s"
|
||||
tcell.KeyNames[tcell.Key(views.KeyT)] = "t"
|
||||
tcell.KeyNames[tcell.Key(views.KeyU)] = "u"
|
||||
tcell.KeyNames[tcell.Key(views.KeyV)] = "v"
|
||||
tcell.KeyNames[tcell.Key(views.KeyX)] = "x"
|
||||
tcell.KeyNames[tcell.Key(views.KeyY)] = "y"
|
||||
tcell.KeyNames[tcell.Key(views.KeyZ)] = "z"
|
||||
tcell.KeyNames[tcell.Key(views.KeyHelp)] = "?"
|
||||
}
|
||||
|
||||
func initStyles() {
|
||||
tview.Styles.PrimitiveBackgroundColor = tcell.ColorBlack
|
||||
tview.Styles.ContrastBackgroundColor = tcell.ColorBlack
|
||||
tview.Styles.FocusColor = tcell.ColorLightSkyBlue
|
||||
tview.Styles.BorderColor = tcell.ColorDodgerBlue
|
||||
}
|
||||
|
||||
func initK8sFlags() {
|
||||
k8sFlags = genericclioptions.NewConfigFlags(false)
|
||||
rootCmd.Flags().StringVar(
|
||||
|
|
@ -266,4 +215,4 @@ func initK8sFlags() {
|
|||
"",
|
||||
"If present, the namespace scope for this CLI request",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ func (c *Config) FavNamespaces() []string {
|
|||
func (c *Config) SetActiveNamespace(ns string) {
|
||||
if c.K9s.ActiveCluster() != nil {
|
||||
c.K9s.ActiveCluster().Namespace.SetActive(ns)
|
||||
} else {
|
||||
log.Debug("Doh! no active cluster. unable to set active namespace")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ func (k *K9s) Validate(ks KubeSettings) {
|
|||
k.Clusters[k.CurrentCluster] = NewCluster()
|
||||
}
|
||||
|
||||
if ns, err := ks.CurrentNamespaceName(); err == nil {
|
||||
if ns, err := ks.CurrentNamespaceName(); err == nil && len(ns) != 0 {
|
||||
k.Clusters[k.CurrentCluster].Namespace.Active = ns
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,12 +203,12 @@ func (c *Config) CurrentNamespaceName() (string, error) {
|
|||
return "", err
|
||||
}
|
||||
|
||||
if ctx, ok := cfg.Contexts[ctx]; ok {
|
||||
if ctx, ok := cfg.Contexts[ctx]; ok {
|
||||
if isSet(&ctx.Namespace) {
|
||||
return ctx.Namespace, nil
|
||||
}
|
||||
}
|
||||
return defaultNamespace, nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// NamespaceNames fetch all available namespaces on current cluster.
|
||||
|
|
|
|||
|
|
@ -48,13 +48,13 @@ func (*Job) Delete(ns, n string) error {
|
|||
}
|
||||
|
||||
// Containers returns all container names on pod
|
||||
func (j *Job) Containers(ns, n string) ([]string, error) {
|
||||
func (j *Job) Containers(ns, n string, includeInit bool) ([]string, error) {
|
||||
pod, err := j.assocPod(ns, n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debug("Containers found assoc pod", pod)
|
||||
return NewPod().(Loggable).Containers(ns, pod)
|
||||
return NewPod().(Loggable).Containers(ns, pod, includeInit)
|
||||
}
|
||||
|
||||
// Logs fetch container logs for a given pod and container.
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ type (
|
|||
// Loggable represents a K8s resource that has containers and can be logged.
|
||||
Loggable interface {
|
||||
Res
|
||||
Containers(ns, n string) ([]string, error)
|
||||
Containers(ns, n string, includeInit bool) ([]string, error)
|
||||
Logs(ns, n, co string, lines int64, previous bool) *restclient.Request
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ func (*Pod) Delete(ns, n string) error {
|
|||
}
|
||||
|
||||
// Containers returns all container names on pod
|
||||
func (*Pod) Containers(ns, n string) ([]string, error) {
|
||||
func (*Pod) Containers(ns, n string, includeInit bool) ([]string, error) {
|
||||
opts := metav1.GetOptions{}
|
||||
cc := []string{}
|
||||
po, err := conn.dialOrDie().CoreV1().Pods(ns).Get(n, opts)
|
||||
|
|
@ -66,6 +66,11 @@ func (*Pod) Containers(ns, n string) ([]string, error) {
|
|||
return cc, err
|
||||
}
|
||||
|
||||
if includeInit {
|
||||
for _, c := range po.Spec.InitContainers {
|
||||
cc = append(cc, c.Name)
|
||||
}
|
||||
}
|
||||
for _, c := range po.Spec.Containers {
|
||||
cc = append(cc, c.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/k8sland/tview"
|
||||
"github.com/derailed/tview"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/duration"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
|
|
|
|||
|
|
@ -74,11 +74,13 @@ func (r *Job) Marshal(path string) (string, error) {
|
|||
return r.marshalObject(jo)
|
||||
}
|
||||
|
||||
func (r *Job) Containers(path string) ([]string, error) {
|
||||
// Containers fetch all the containers on this job, may include init containers.
|
||||
func (r *Job) Containers(path string, includeInit bool) ([]string, error) {
|
||||
ns, n := namespaced(path)
|
||||
return r.caller.(k8s.Loggable).Containers(ns, n)
|
||||
return r.caller.(k8s.Loggable).Containers(ns, n, includeInit)
|
||||
}
|
||||
|
||||
// Logs retrieves logs for a given container.
|
||||
func (r *Job) Logs(c chan<- string, ns, n, co string, lines int64, prev bool) (context.CancelFunc, error) {
|
||||
req := r.caller.(k8s.Loggable).Logs(ns, n, co, lines, prev)
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import (
|
|||
"sort"
|
||||
|
||||
"github.com/derailed/k9s/internal/k8s"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
)
|
||||
|
||||
|
|
@ -219,7 +218,6 @@ func (l *list) Reconcile() error {
|
|||
err error
|
||||
)
|
||||
|
||||
log.Debugf("Fetching list for resource `%s` in ns `%s`", l.name, l.namespace)
|
||||
if items, err = l.api.List(l.namespace); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const defaultTimeout = 1 * time.Second
|
|||
type (
|
||||
// Container represents a resource that encompass multiple containers.
|
||||
Container interface {
|
||||
Containers(path string) ([]string, error)
|
||||
Containers(path string, includeInit bool) ([]string, error)
|
||||
}
|
||||
|
||||
// Tailable represents a resource with tailable logs.
|
||||
|
|
@ -114,9 +114,9 @@ func (r *Pod) Marshal(path string) (string, error) {
|
|||
}
|
||||
|
||||
// Containers lists out all the docker contrainers name contained in a pod.
|
||||
func (r *Pod) Containers(path string) ([]string, error) {
|
||||
func (r *Pod) Containers(path string, includeInit bool) ([]string, error) {
|
||||
ns, po := namespaced(path)
|
||||
return r.caller.(k8s.Loggable).Containers(ns, po)
|
||||
return r.caller.(k8s.Loggable).Containers(ns, po, includeInit)
|
||||
}
|
||||
|
||||
// Logs tails a given container logs
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
package views
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
const (
|
||||
aliasTitle = "Aliases"
|
||||
aliasTitleFmt = " [aqua::b]%s[[aqua::b]%d[aqua::-]][aqua::-] "
|
||||
)
|
||||
|
||||
type aliasView struct {
|
||||
*tableView
|
||||
|
||||
current igniter
|
||||
}
|
||||
|
||||
func newAliasView(app *appView) *aliasView {
|
||||
v := aliasView{tableView: newTableView(app, aliasTitle, nil)}
|
||||
{
|
||||
v.SetSelectedStyle(tcell.ColorWhite, tcell.ColorFuchsia, tcell.AttrNone)
|
||||
v.colorerFn = aliasColorer
|
||||
v.current = app.content.GetPrimitive("main").(igniter)
|
||||
}
|
||||
v.actions[tcell.KeyEnter] = newKeyAction("Search", v.aliasCmd)
|
||||
v.actions[KeySlash] = newKeyAction("Filter", v.activateCmd)
|
||||
return &v
|
||||
}
|
||||
|
||||
// Init the view.
|
||||
func (v *aliasView) init(context.Context, string) {
|
||||
v.update(v.hydrate())
|
||||
v.app.SetFocus(v)
|
||||
v.resetTitle()
|
||||
}
|
||||
|
||||
func (v *aliasView) getTitle() string {
|
||||
return aliasTitle
|
||||
}
|
||||
|
||||
func (v *aliasView) aliasCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if v.cmdBuff.isActive() {
|
||||
return v.filterCmd(evt)
|
||||
}
|
||||
return v.runCmd(evt)
|
||||
}
|
||||
|
||||
func (v *aliasView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
v.app.inject(v.current)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *aliasView) runCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
r, _ := v.GetSelection()
|
||||
if r > 0 {
|
||||
v.app.command.run(strings.TrimSpace(v.GetCell(r, 0).Text))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *aliasView) hints() hints {
|
||||
return v.actions.toHints()
|
||||
}
|
||||
|
||||
func (v *aliasView) hydrate() resource.TableData {
|
||||
cmds := helpCmds()
|
||||
|
||||
data := resource.TableData{
|
||||
Header: resource.Row{"ALIAS", "RESOURCE", "APIGROUP"},
|
||||
Rows: make(resource.RowEvents, len(cmds)),
|
||||
Namespace: "",
|
||||
}
|
||||
|
||||
for k := range cmds {
|
||||
fields := resource.Row{
|
||||
resource.Pad(k, 30),
|
||||
resource.Pad(cmds[k].title, 30),
|
||||
resource.Pad(cmds[k].api, 30),
|
||||
}
|
||||
data.Rows[k] = &resource.RowEvent{
|
||||
Action: resource.New,
|
||||
Fields: fields,
|
||||
Deltas: fields,
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (v *aliasView) resetTitle() {
|
||||
v.SetTitle(fmt.Sprintf(aliasTitleFmt, aliasTitle, v.GetRowCount()-1))
|
||||
}
|
||||
|
|
@ -3,12 +3,11 @@ package views
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/k8sland/tview"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
)
|
||||
|
|
@ -24,6 +23,10 @@ type (
|
|||
init(ctx context.Context, ns string)
|
||||
}
|
||||
|
||||
keyHandler interface {
|
||||
keyboard(evt *tcell.EventKey) *tcell.EventKey
|
||||
}
|
||||
|
||||
resourceViewer interface {
|
||||
igniter
|
||||
}
|
||||
|
|
@ -44,35 +47,47 @@ type (
|
|||
cancel context.CancelFunc
|
||||
cmdBuff *cmdBuff
|
||||
cmdView *cmdView
|
||||
actions keyActions
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
initKeys()
|
||||
initStyles()
|
||||
}
|
||||
|
||||
// NewApp returns a K9s app instance.
|
||||
func NewApp() *appView {
|
||||
var app appView
|
||||
v := appView{Application: tview.NewApplication()}
|
||||
{
|
||||
app = appView{
|
||||
Application: tview.NewApplication(),
|
||||
pages: tview.NewPages(),
|
||||
menuView: newMenuView(),
|
||||
content: tview.NewPages(),
|
||||
cmdBuff: newCmdBuff(':'),
|
||||
cmdView: newCmdView('🐶'),
|
||||
}
|
||||
app.command = newCommand(&app)
|
||||
app.focusChanged = app.changedFocus
|
||||
app.SetInputCapture(app.keyboard)
|
||||
v.pages = tview.NewPages()
|
||||
v.actions = make(keyActions)
|
||||
v.menuView = newMenuView()
|
||||
v.content = tview.NewPages()
|
||||
v.cmdBuff = newCmdBuff(':')
|
||||
v.cmdView = newCmdView('🐶')
|
||||
v.command = newCommand(&v)
|
||||
v.flashView = newFlashView(v.Application, "Initializing...")
|
||||
v.infoView = newInfoView(&v)
|
||||
v.focusChanged = v.changedFocus
|
||||
v.SetInputCapture(v.keyboard)
|
||||
}
|
||||
return &app
|
||||
|
||||
v.actions[KeyColon] = newKeyAction("Cmd", v.activateCmd)
|
||||
v.actions[tcell.KeyCtrlR] = newKeyAction("Redraw", v.redrawCmd)
|
||||
v.actions[KeyQ] = newKeyAction("Quit", v.quitCmd)
|
||||
v.actions[KeyHelp] = newKeyAction("Help", v.helpCmd)
|
||||
v.actions[tcell.KeyEscape] = newKeyAction("Exit Cmd", v.deactivateCmd)
|
||||
v.actions[tcell.KeyEnter] = newKeyAction("Goto", v.gotoCmd)
|
||||
v.actions[tcell.KeyBackspace2] = newKeyAction("Goto", v.eraseCmd)
|
||||
v.actions[tcell.KeyTab] = newKeyAction("Focus", v.focusCmd)
|
||||
|
||||
return &v
|
||||
}
|
||||
|
||||
func (a *appView) Init(v string, rate int, flags *genericclioptions.ConfigFlags) {
|
||||
a.version = v
|
||||
a.infoView = newInfoView(a)
|
||||
a.infoView.init()
|
||||
|
||||
a.flashView = newFlashView(a.Application, "Initializing...")
|
||||
|
||||
a.cmdBuff.addListener(a.cmdView)
|
||||
|
||||
header := tview.NewFlex()
|
||||
|
|
@ -114,51 +129,81 @@ func (a *appView) Run() {
|
|||
func (a *appView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||
key := evt.Key()
|
||||
if key == tcell.KeyRune {
|
||||
switch evt.Rune() {
|
||||
case 'q':
|
||||
if !a.cmdMode() {
|
||||
a.quit(evt)
|
||||
}
|
||||
case '?':
|
||||
if !a.cmdView.inCmdMode() {
|
||||
a.help(evt)
|
||||
return evt
|
||||
}
|
||||
case a.cmdBuff.hotKey:
|
||||
a.flash(flashInfo, "Entering command mode...")
|
||||
log.Debug("K9s entering command mode...")
|
||||
a.cmdBuff.setActive(true)
|
||||
a.cmdBuff.clear()
|
||||
return evt
|
||||
}
|
||||
|
||||
if a.cmdBuff.isActive() {
|
||||
a.cmdBuff.add(evt.Rune())
|
||||
}
|
||||
return evt
|
||||
key = tcell.Key(evt.Rune())
|
||||
}
|
||||
|
||||
switch evt.Key() {
|
||||
case tcell.KeyCtrlR:
|
||||
log.Debug("Refreshing screen...")
|
||||
a.Draw()
|
||||
case tcell.KeyEsc:
|
||||
a.cmdBuff.reset()
|
||||
case tcell.KeyEnter:
|
||||
if a.cmdBuff.isActive() && !a.cmdBuff.empty() {
|
||||
a.command.run(a.cmdBuff.String())
|
||||
}
|
||||
a.cmdBuff.setActive(false)
|
||||
case tcell.KeyBackspace2:
|
||||
if a.cmdBuff.isActive() {
|
||||
a.cmdBuff.del()
|
||||
}
|
||||
case tcell.KeyTab:
|
||||
a.nextFocus()
|
||||
if a, ok := a.actions[key]; ok {
|
||||
log.Debug(">> AppView handled key: ", tcell.KeyNames[key])
|
||||
return a.action(evt)
|
||||
}
|
||||
return evt
|
||||
}
|
||||
|
||||
func (a *appView) redrawCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
a.Draw()
|
||||
return evt
|
||||
}
|
||||
|
||||
func (a *appView) focusCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
a.nextFocus()
|
||||
return evt
|
||||
}
|
||||
|
||||
func (a *appView) eraseCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if a.cmdBuff.isActive() {
|
||||
a.cmdBuff.del()
|
||||
return nil
|
||||
}
|
||||
return evt
|
||||
}
|
||||
|
||||
func (a *appView) deactivateCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if a.cmdBuff.isActive() {
|
||||
a.cmdBuff.reset()
|
||||
}
|
||||
return evt
|
||||
}
|
||||
|
||||
func (a *appView) gotoCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if a.cmdBuff.isActive() && !a.cmdBuff.empty() {
|
||||
a.command.run(a.cmdBuff.String())
|
||||
a.cmdBuff.reset()
|
||||
return nil
|
||||
}
|
||||
a.cmdBuff.setActive(false)
|
||||
return evt
|
||||
}
|
||||
|
||||
func (a *appView) activateCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
a.flash(flashInfo, "Entering command mode...")
|
||||
log.Debug("Entering app command mode...")
|
||||
a.cmdBuff.setActive(true)
|
||||
a.cmdBuff.clear()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *appView) quitCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if a.cmdMode() {
|
||||
return evt
|
||||
}
|
||||
a.Stop()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *appView) helpCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if a.cmdView.inCmdMode() {
|
||||
return evt
|
||||
}
|
||||
a.inject(newAliasView(a))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *appView) noopCmd(*tcell.EventKey) *tcell.EventKey {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *appView) showPage(p string) {
|
||||
a.pages.SwitchToPage(p)
|
||||
}
|
||||
|
|
@ -195,18 +240,6 @@ func (a *appView) refresh() {
|
|||
a.infoView.refresh()
|
||||
}
|
||||
|
||||
func (a *appView) help(*tcell.EventKey) {
|
||||
a.inject(newHelpView(a))
|
||||
}
|
||||
|
||||
func (a *appView) noop(*tcell.EventKey) {
|
||||
}
|
||||
|
||||
func (a *appView) quit(*tcell.EventKey) {
|
||||
a.Stop()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func (a *appView) flash(level flashLevel, m ...string) {
|
||||
a.flashView.setMessage(level, m...)
|
||||
}
|
||||
|
|
@ -260,3 +293,10 @@ func (a *appView) nextFocus() {
|
|||
}
|
||||
return
|
||||
}
|
||||
|
||||
func initStyles() {
|
||||
tview.Styles.PrimitiveBackgroundColor = tcell.ColorBlack
|
||||
tview.Styles.ContrastBackgroundColor = tcell.ColorBlack
|
||||
tview.Styles.FocusColor = tcell.ColorLightSkyBlue
|
||||
tview.Styles.BorderColor = tcell.ColorDodgerBlue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/k8sland/tview"
|
||||
"github.com/derailed/tview"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,10 @@ func (c *cmdBuff) del() {
|
|||
c.fireChanged()
|
||||
}
|
||||
|
||||
func (c *cmdBuff) wipe() {
|
||||
c.buff = make([]rune, 0, maxBuff)
|
||||
}
|
||||
|
||||
func (c *cmdBuff) clear() {
|
||||
c.buff = make([]rune, 0, maxBuff)
|
||||
c.fireChanged()
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ func defaultColorer(ns string, r *resource.RowEvent) tcell.Color {
|
|||
return c
|
||||
}
|
||||
|
||||
func aliasColorer(string, *resource.RowEvent) tcell.Color {
|
||||
return tcell.ColorFuchsia
|
||||
}
|
||||
|
||||
func podColorer(ns string, r *resource.RowEvent) tcell.Color {
|
||||
c := defaultColorer(ns, r)
|
||||
|
||||
|
|
@ -37,7 +41,7 @@ func podColorer(ns string, r *resource.RowEvent) tcell.Color {
|
|||
statusCol = 2
|
||||
}
|
||||
switch strings.TrimSpace(r.Fields[statusCol]) {
|
||||
case "ContainerCreating":
|
||||
case "ContainerCreating", "PodInitializing":
|
||||
return addColor
|
||||
case "Terminating", "Initialized":
|
||||
return highlightColor
|
||||
|
|
|
|||
|
|
@ -27,9 +27,11 @@ func (c *command) run(cmd string) {
|
|||
var v igniter
|
||||
switch cmd {
|
||||
case "q", "quit":
|
||||
c.app.quit(nil)
|
||||
c.app.Stop()
|
||||
return
|
||||
case "?", "help", "alias":
|
||||
c.app.help(nil)
|
||||
c.app.inject(newAliasView(c.app))
|
||||
return
|
||||
default:
|
||||
if res, ok := cmdMap[cmd]; ok {
|
||||
v = res.viewFn(res.title, c.app, res.listFn(resource.DefaultNamespace), res.colorerFn)
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ func newContextView(t string, app *appView, list resource.List, c colorerFn) res
|
|||
return &v
|
||||
}
|
||||
|
||||
func (v *contextView) useContext(*tcell.EventKey) {
|
||||
func (v *contextView) useContext(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !v.rowSelected() {
|
||||
return
|
||||
return evt
|
||||
}
|
||||
|
||||
ctx := strings.TrimSpace(v.selectedItem)
|
||||
|
|
@ -37,7 +37,7 @@ func (v *contextView) useContext(*tcell.EventKey) {
|
|||
err := v.list.Resource().(*resource.Context).Switch(ctx)
|
||||
if err != nil {
|
||||
v.app.flash(flashWarn, err.Error())
|
||||
return
|
||||
return evt
|
||||
}
|
||||
|
||||
config.Root.Reset()
|
||||
|
|
@ -46,10 +46,11 @@ func (v *contextView) useContext(*tcell.EventKey) {
|
|||
v.app.flash(flashInfo, "Switching context to", ctx)
|
||||
v.refresh()
|
||||
if tv, ok := v.GetPrimitive("ctx").(*tableView); ok {
|
||||
tv.table.Select(0, 0)
|
||||
tv.Select(0, 0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *contextView) extraActions(aa keyActions) {
|
||||
aa[KeyU] = keyAction{description: "Use", action: v.useContext}
|
||||
aa[KeyU] = newKeyAction("Use", v.useContext)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,17 +20,19 @@ func newCronJobView(t string, app *appView, list resource.List, c colorerFn) res
|
|||
return &v
|
||||
}
|
||||
|
||||
func (v *cronJobView) trigger(*tcell.EventKey) {
|
||||
func (v *cronJobView) trigger(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !v.rowSelected() {
|
||||
return
|
||||
return evt
|
||||
}
|
||||
|
||||
v.app.flash(flashInfo, fmt.Sprintf("Triggering %s %s", v.list.GetName(), v.selectedItem))
|
||||
if err := v.list.Resource().(resource.Runner).Run(v.selectedItem); err != nil {
|
||||
v.app.flash(flashErr, "Boom!", err.Error())
|
||||
return evt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *cronJobView) extraActions(aa keyActions) {
|
||||
aa[tcell.KeyCtrlT] = keyAction{description: "Trigger", action: v.trigger}
|
||||
aa[tcell.KeyCtrlT] = newKeyAction("Trigger", v.trigger)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,27 +2,59 @@ package views
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/k8sland/tview"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const detailFmt = " [aqua::-]%s [fuchsia::b]%s "
|
||||
const detailsTitleFmt = " [aqua::b]%s([fuchsia::b]%s[aqua::-])[aqua::-] "
|
||||
|
||||
// detailsView display yaml output
|
||||
// detailsView displays text output.
|
||||
type detailsView struct {
|
||||
*tview.TextView
|
||||
|
||||
actions keyActions
|
||||
category string
|
||||
app *appView
|
||||
actions keyActions
|
||||
title string
|
||||
category string
|
||||
cmdBuff *cmdBuff
|
||||
backFn actionHandler
|
||||
numSelections int
|
||||
}
|
||||
|
||||
func newDetailsView() *detailsView {
|
||||
v := detailsView{TextView: tview.NewTextView()}
|
||||
v.TextView.SetDynamicColors(true)
|
||||
v.TextView.SetBorder(true)
|
||||
v.SetTitleColor(tcell.ColorAqua)
|
||||
v.SetInputCapture(v.keyboard)
|
||||
func newDetailsView(app *appView, backFn actionHandler) *detailsView {
|
||||
v := detailsView{TextView: tview.NewTextView(), app: app, actions: make(keyActions)}
|
||||
{
|
||||
v.backFn = backFn
|
||||
v.SetScrollable(true)
|
||||
v.SetWrap(false)
|
||||
v.SetDynamicColors(true)
|
||||
v.SetRegions(true)
|
||||
v.SetBorder(true)
|
||||
v.SetHighlightColor(tcell.ColorOrange)
|
||||
v.SetTitleColor(tcell.ColorAqua)
|
||||
v.SetInputCapture(v.keyboard)
|
||||
v.cmdBuff = newCmdBuff('/')
|
||||
{
|
||||
v.cmdBuff.addListener(app.cmdView)
|
||||
v.cmdBuff.reset()
|
||||
}
|
||||
v.SetChangedFunc(func() {
|
||||
app.Draw()
|
||||
})
|
||||
}
|
||||
|
||||
v.actions[KeySlash] = newKeyAction("Search", v.activateCmd)
|
||||
v.actions[tcell.KeyEnter] = newKeyAction("Search", v.searchCmd)
|
||||
v.actions[tcell.KeyBackspace2] = newKeyAction("Erase", v.eraseCmd)
|
||||
v.actions[tcell.KeyEscape] = newKeyAction("Reset", v.backCmd)
|
||||
v.actions[tcell.KeyTab] = newKeyAction("Next", v.nextCmd)
|
||||
v.actions[tcell.KeyBacktab] = newKeyAction("Previous", v.prevCmd)
|
||||
|
||||
return &v
|
||||
}
|
||||
|
||||
|
|
@ -31,23 +63,120 @@ func (v *detailsView) setCategory(n string) {
|
|||
}
|
||||
|
||||
func (v *detailsView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if evt.Key() == tcell.KeyRune {
|
||||
if a, ok := v.actions[evt.Key()]; ok {
|
||||
a.action(evt)
|
||||
evt = nil
|
||||
}
|
||||
} else {
|
||||
if a, ok := v.actions[evt.Key()]; ok {
|
||||
a.action(evt)
|
||||
evt = nil
|
||||
key := evt.Key()
|
||||
if key == tcell.KeyRune {
|
||||
if v.cmdBuff.isActive() {
|
||||
v.cmdBuff.add(evt.Rune())
|
||||
v.refreshTitle()
|
||||
return nil
|
||||
}
|
||||
key = tcell.Key(evt.Rune())
|
||||
}
|
||||
|
||||
if a, ok := v.actions[key]; ok {
|
||||
log.Debug(">> DetailsView handled ", tcell.KeyNames[key])
|
||||
return a.action(evt)
|
||||
}
|
||||
log.Debug("Doh! DetailsView got no registered action for key ", tcell.KeyNames[key])
|
||||
return evt
|
||||
}
|
||||
|
||||
func (v *detailsView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !v.cmdBuff.empty() {
|
||||
v.cmdBuff.reset()
|
||||
v.search(evt)
|
||||
return nil
|
||||
}
|
||||
v.cmdBuff.reset()
|
||||
if v.backFn != nil {
|
||||
return v.backFn(evt)
|
||||
}
|
||||
return evt
|
||||
}
|
||||
|
||||
func (v *detailsView) eraseCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !v.cmdBuff.isActive() {
|
||||
return evt
|
||||
}
|
||||
v.cmdBuff.del()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *detailsView) activateCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !v.app.cmdView.inCmdMode() {
|
||||
v.cmdBuff.setActive(true)
|
||||
v.cmdBuff.clear()
|
||||
return nil
|
||||
}
|
||||
return evt
|
||||
}
|
||||
|
||||
func (v *detailsView) searchCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if v.cmdBuff.isActive() && !v.cmdBuff.empty() {
|
||||
v.app.flash(flashInfo, fmt.Sprintf("Searching for %s...", v.cmdBuff))
|
||||
v.search(evt)
|
||||
highlights := v.GetHighlights()
|
||||
if len(highlights) > 0 {
|
||||
v.Highlight()
|
||||
} else {
|
||||
v.Highlight("0").ScrollToHighlight()
|
||||
}
|
||||
}
|
||||
v.cmdBuff.setActive(false)
|
||||
return evt
|
||||
}
|
||||
|
||||
func (v *detailsView) search(evt *tcell.EventKey) {
|
||||
v.numSelections = 0
|
||||
v.Highlight()
|
||||
log.Debug("Searching...", v.cmdBuff, v.numSelections)
|
||||
v.SetText(v.decorateLines(v.GetText(true), v.cmdBuff.String()))
|
||||
|
||||
if v.cmdBuff.empty() {
|
||||
v.app.flash(flashWarn, "Clearing out search query...")
|
||||
v.refreshTitle()
|
||||
return
|
||||
}
|
||||
if v.numSelections == 0 {
|
||||
v.app.flash(flashWarn, "No matches found!")
|
||||
return
|
||||
}
|
||||
v.app.flash(flashWarn, fmt.Sprintf("Found <%d> matches! <tab>/<TAB> for next/previous", v.numSelections))
|
||||
}
|
||||
|
||||
func (v *detailsView) nextCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
highlights := v.GetHighlights()
|
||||
if len(highlights) == 0 || v.numSelections == 0 {
|
||||
return evt
|
||||
}
|
||||
index, _ := strconv.Atoi(highlights[0])
|
||||
index = (index + 1) % v.numSelections
|
||||
if index+1 == v.numSelections {
|
||||
v.app.flash(flashInfo, "Search hit BOTTOM, continuing at TOP")
|
||||
}
|
||||
v.Highlight(strconv.Itoa(index)).ScrollToHighlight()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *detailsView) prevCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
highlights := v.GetHighlights()
|
||||
if len(highlights) == 0 || v.numSelections == 0 {
|
||||
return evt
|
||||
}
|
||||
index, _ := strconv.Atoi(highlights[0])
|
||||
index = (index - 1 + v.numSelections) % v.numSelections
|
||||
if index == 0 {
|
||||
v.app.flash(flashInfo, "Search hit TOP, continuing at BOTTOM")
|
||||
}
|
||||
v.Highlight(strconv.Itoa(index)).ScrollToHighlight()
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetActions to handle keyboard inputs
|
||||
func (v *detailsView) setActions(aa keyActions) {
|
||||
v.actions = aa
|
||||
for k, a := range aa {
|
||||
v.actions[k] = a
|
||||
}
|
||||
}
|
||||
|
||||
// Hints fetch mmemonic and hints
|
||||
|
|
@ -58,6 +187,27 @@ func (v *detailsView) hints() hints {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (v *detailsView) setTitle(t string) {
|
||||
v.SetTitle(fmt.Sprintf(detailFmt, t, v.category))
|
||||
func (v *detailsView) refreshTitle() {
|
||||
v.setTitle(v.title)
|
||||
}
|
||||
|
||||
func (v *detailsView) setTitle(t string) {
|
||||
v.title = t
|
||||
title := fmt.Sprintf(detailsTitleFmt, v.category, t)
|
||||
if !v.cmdBuff.empty() {
|
||||
title += fmt.Sprintf(searchFmt, v.cmdBuff.String())
|
||||
}
|
||||
v.SetTitle(title)
|
||||
}
|
||||
|
||||
func (v *detailsView) decorateLines(buff, q string) string {
|
||||
rx := regexp.MustCompile(`(?i)` + q)
|
||||
lines := strings.Split(buff, "\n")
|
||||
for i, l := range lines {
|
||||
if m := rx.FindString(l); len(m) > 0 {
|
||||
lines[i] = rx.ReplaceAllString(l, fmt.Sprintf(`["%d"]%s[""]`, v.numSelections, m))
|
||||
v.numSelections++
|
||||
}
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
package views
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDetailsDecorateLines(t *testing.T) {
|
||||
buff := `
|
||||
I love blee
|
||||
blee is much [blue::]cooler [green::]than foo!
|
||||
`
|
||||
exp := `
|
||||
I love ["0"]blee[""]
|
||||
["1"]blee[""] is much [blue::]cooler [green::]than foo!
|
||||
`
|
||||
v := detailsView{}
|
||||
assert.Equal(t, exp, v.decorateLines(buff, "blee"))
|
||||
}
|
||||
|
|
@ -7,12 +7,13 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func run(app *appView, args ...string) {
|
||||
app.Suspend(func() {
|
||||
func run(app *appView, args ...string) bool {
|
||||
return app.Suspend(func() {
|
||||
if err := execute(args...); err != nil {
|
||||
log.Error("Command failed:", err, args)
|
||||
app.flash(flashErr, "Doh! command failed", err.Error())
|
||||
}
|
||||
log.Debug("Command Done Running..")
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/k8sland/tview"
|
||||
"github.com/derailed/tview"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
|||
|
|
@ -1,110 +0,0 @@
|
|||
package views
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/k8sland/tview"
|
||||
)
|
||||
|
||||
type helpView struct {
|
||||
*tview.Pages
|
||||
|
||||
app *appView
|
||||
title string
|
||||
current igniter
|
||||
keys keyActions
|
||||
}
|
||||
|
||||
func newHelpView(app *appView) *helpView {
|
||||
return &helpView{
|
||||
app: app,
|
||||
title: "Help",
|
||||
current: app.content.GetPrimitive("main").(igniter),
|
||||
Pages: tview.NewPages(),
|
||||
}
|
||||
}
|
||||
|
||||
// Init the view.
|
||||
func (v *helpView) init(context.Context, string) {
|
||||
v.keys = keyActions{
|
||||
tcell.KeyEscape: keyAction{description: "Back", action: v.back},
|
||||
}
|
||||
|
||||
t := tview.NewTable()
|
||||
{
|
||||
t.SetBorder(true)
|
||||
t.SetTitle(" [::b]Commands Aliases ")
|
||||
t.SetTitleColor(tcell.ColorAqua)
|
||||
t.SetBorderColor(tcell.ColorDodgerBlue)
|
||||
t.SetSelectable(true, false)
|
||||
t.SetSelectedStyle(tcell.ColorWhite, tcell.ColorFuchsia, tcell.AttrNone)
|
||||
t.SetInputCapture(v.keyboard)
|
||||
}
|
||||
|
||||
var row int
|
||||
for c, h := range []string{"ALIAS", "RESOURCE", "APIGROUP"} {
|
||||
th := tview.NewTableCell(h)
|
||||
th.SetExpansion(3)
|
||||
t.SetCell(row, c, th)
|
||||
}
|
||||
row++
|
||||
|
||||
cmds := helpCmds()
|
||||
kk := make([]string, 0, len(cmds))
|
||||
for k := range cmds {
|
||||
kk = append(kk, k)
|
||||
}
|
||||
sort.Strings(kk)
|
||||
var col int
|
||||
for _, k := range kk {
|
||||
tc := tview.NewTableCell(resource.Pad(k, 30))
|
||||
tc.SetExpansion(2)
|
||||
t.SetCell(row, col, tc)
|
||||
col++
|
||||
tc = tview.NewTableCell(resource.Pad(cmds[k].title, 30))
|
||||
tc.SetExpansion(2)
|
||||
t.SetCell(row, col, tc)
|
||||
col++
|
||||
tc = tview.NewTableCell(resource.Pad(cmds[k].api, 30))
|
||||
tc.SetExpansion(2)
|
||||
t.SetCell(row, col, tc)
|
||||
col = 0
|
||||
row++
|
||||
}
|
||||
|
||||
v.AddPage("main", t, true, true)
|
||||
v.SwitchToPage("main")
|
||||
v.app.SetFocus(v.CurrentPage().Item)
|
||||
}
|
||||
|
||||
func (v *helpView) getTitle() string {
|
||||
return v.title
|
||||
}
|
||||
|
||||
func (v *helpView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||
switch evt.Key() {
|
||||
case tcell.KeyEscape:
|
||||
v.back(evt)
|
||||
return nil
|
||||
case tcell.KeyEnter:
|
||||
t := v.GetPrimitive("main").(*tview.Table)
|
||||
r, _ := t.GetSelection()
|
||||
if r > 0 {
|
||||
v.app.command.run(strings.TrimSpace(t.GetCell(r, 0).Text))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return evt
|
||||
}
|
||||
|
||||
func (v *helpView) back(evt *tcell.EventKey) {
|
||||
v.app.inject(v.current)
|
||||
}
|
||||
|
||||
func (v *helpView) hints() hints {
|
||||
return v.keys.toHints()
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ package views
|
|||
import (
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/k8sland/tview"
|
||||
"github.com/derailed/tview"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ type jobView struct {
|
|||
|
||||
func newJobView(t string, app *appView, list resource.List, c colorerFn) resourceViewer {
|
||||
v := jobView{newResourceView(t, app, list, c).(*resourceView)}
|
||||
v.extraActionsFn = v.extraActions
|
||||
{
|
||||
v.extraActionsFn = v.extraActions
|
||||
}
|
||||
v.AddPage("logs", newLogsView(&v), true, false)
|
||||
v.switchPage("job")
|
||||
return &v
|
||||
|
|
@ -23,23 +25,31 @@ func newJobView(t string, app *appView, list resource.List, c colorerFn) resourc
|
|||
func (v *jobView) appView() *appView {
|
||||
return v.app
|
||||
}
|
||||
|
||||
func (v *jobView) backFn() actionHandler {
|
||||
return v.backCmd
|
||||
}
|
||||
|
||||
func (v *jobView) getList() resource.List {
|
||||
return v.list
|
||||
}
|
||||
|
||||
func (v *jobView) getSelection() string {
|
||||
return v.selectedItem
|
||||
}
|
||||
|
||||
// Handlers...
|
||||
|
||||
func (v *jobView) logs(*tcell.EventKey) {
|
||||
func (v *jobView) logs(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !v.rowSelected() {
|
||||
return
|
||||
return evt
|
||||
}
|
||||
|
||||
cc, err := fetchContainers(v.list, v.selectedItem)
|
||||
cc, err := fetchContainers(v.list, v.selectedItem, true)
|
||||
if err != nil {
|
||||
v.app.flash(flashErr, err.Error())
|
||||
log.Error(err)
|
||||
return evt
|
||||
}
|
||||
|
||||
l := v.GetPrimitive("logs").(*logsView)
|
||||
|
|
@ -50,8 +60,9 @@ func (v *jobView) logs(*tcell.EventKey) {
|
|||
|
||||
v.switchPage("logs")
|
||||
l.init()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *jobView) extraActions(aa keyActions) {
|
||||
aa[KeyL] = newKeyHandler("Logs", v.logs)
|
||||
aa[KeyL] = newKeyAction("Logs", v.logs)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,29 +3,20 @@ package views
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/k8sland/tview"
|
||||
)
|
||||
|
||||
const (
|
||||
logTitleFmt = " [aqua::b]Logs %s ([fuchsia::-]container=[fuchsia::b]%s[aqua::b]) "
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type logView struct {
|
||||
*tview.TextView
|
||||
*detailsView
|
||||
}
|
||||
|
||||
func newLogView(title string, parent loggable) *logView {
|
||||
v := logView{TextView: tview.NewTextView()}
|
||||
log.Debug("LogsView init...")
|
||||
v := logView{detailsView: newDetailsView(parent.appView(), parent.backFn())}
|
||||
{
|
||||
v.SetScrollable(true)
|
||||
v.SetDynamicColors(true)
|
||||
v.SetBorder(true)
|
||||
v.SetBorderPadding(0, 0, 1, 1)
|
||||
v.SetTitle(fmt.Sprintf(logTitleFmt, parent.getSelection(), title))
|
||||
v.SetWrap(false)
|
||||
v.SetChangedFunc(func() {
|
||||
parent.appView().Draw()
|
||||
})
|
||||
v.setCategory("Logs")
|
||||
v.setTitle(parent.getSelection())
|
||||
}
|
||||
return &v
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import (
|
|||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/k8sland/tview"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
|
@ -42,8 +42,8 @@ func newLogsView(parent loggable) *logsView {
|
|||
KeyC: {description: "Clear", action: v.clearLogs},
|
||||
KeyU: {description: "Top", action: v.top},
|
||||
KeyD: {description: "Bottom", action: v.bottom},
|
||||
KeyF: {description: "PageUp", action: v.pageUp},
|
||||
KeyB: {description: "PageDown", action: v.pageDown},
|
||||
KeyF: {description: "Up", action: v.pageUp},
|
||||
KeyB: {description: "Down", action: v.pageDown},
|
||||
})
|
||||
v.SetInputCapture(v.keyboard)
|
||||
|
||||
|
|
@ -57,26 +57,26 @@ func (v *logsView) init() {
|
|||
}
|
||||
|
||||
func (v *logsView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if m, ok := v.actions[evt.Key()]; ok {
|
||||
m.action(evt)
|
||||
return nil
|
||||
}
|
||||
|
||||
if evt.Key() != tcell.KeyRune {
|
||||
return evt
|
||||
}
|
||||
|
||||
if m, ok := v.actions[tcell.Key(evt.Rune())]; ok {
|
||||
if m.action != nil {
|
||||
m.action(evt)
|
||||
if kv, ok := v.CurrentPage().Item.(keyHandler); ok {
|
||||
if kv.keyboard(evt) == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if i, err := strconv.Atoi(string(evt.Rune())); err == nil {
|
||||
if _, ok := numKeys[i]; ok {
|
||||
v.load(i - 1)
|
||||
key := evt.Key()
|
||||
if key == tcell.KeyRune {
|
||||
if i, err := strconv.Atoi(string(evt.Rune())); err == nil {
|
||||
if _, ok := numKeys[i]; ok {
|
||||
v.load(i - 1)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
key = tcell.Key(evt.Rune())
|
||||
}
|
||||
|
||||
if m, ok := v.actions[key]; ok {
|
||||
log.Debug(">> LogsView handled ", tcell.KeyNames[key])
|
||||
return m.action(evt)
|
||||
}
|
||||
return evt
|
||||
}
|
||||
|
|
@ -90,7 +90,7 @@ func (v *logsView) setActions(aa keyActions) {
|
|||
func (v *logsView) hints() hints {
|
||||
if len(v.containers) > 1 {
|
||||
for i, c := range v.containers {
|
||||
v.actions[tcell.Key(numKeys[i+1])] = keyAction{description: c}
|
||||
v.actions[tcell.Key(numKeys[i+1])] = newKeyAction(c, nil)
|
||||
}
|
||||
}
|
||||
return v.actions.toHints()
|
||||
|
|
@ -99,7 +99,9 @@ func (v *logsView) hints() hints {
|
|||
func (v *logsView) addContainer(n string) {
|
||||
v.containers = append(v.containers, n)
|
||||
l := newLogView(n, v.parent)
|
||||
l.SetInputCapture(v.keyboard)
|
||||
{
|
||||
l.SetInputCapture(v.keyboard)
|
||||
}
|
||||
v.AddPage(n, l, true, false)
|
||||
}
|
||||
|
||||
|
|
@ -145,6 +147,7 @@ func (v *logsView) doLoad(path, co string) error {
|
|||
c := make(chan string)
|
||||
go func() {
|
||||
l, count, first := v.CurrentPage().Item.(*logView), 0, true
|
||||
l.setTitle(path + ":" + co)
|
||||
for {
|
||||
select {
|
||||
case line, ok := <-c:
|
||||
|
|
@ -189,50 +192,55 @@ func (v *logsView) doLoad(path, co string) error {
|
|||
return err
|
||||
}
|
||||
v.cancelFunc = cancelFn
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Actions...
|
||||
|
||||
func (v *logsView) back(*tcell.EventKey) {
|
||||
func (v *logsView) back(evt *tcell.EventKey) *tcell.EventKey {
|
||||
v.stop()
|
||||
v.parent.switchPage(v.parent.getList().GetName())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *logsView) top(*tcell.EventKey) {
|
||||
func (v *logsView) top(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if p := v.CurrentPage(); p != nil {
|
||||
v.parent.appView().flash(flashInfo, "Top logs...")
|
||||
p.Item.(*logView).ScrollToBeginning()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *logsView) bottom(*tcell.EventKey) {
|
||||
func (v *logsView) bottom(*tcell.EventKey) *tcell.EventKey {
|
||||
if p := v.CurrentPage(); p != nil {
|
||||
v.parent.appView().flash(flashInfo, "Bottom logs...")
|
||||
p.Item.(*logView).ScrollToEnd()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *logsView) pageUp(*tcell.EventKey) {
|
||||
func (v *logsView) pageUp(*tcell.EventKey) *tcell.EventKey {
|
||||
if p := v.CurrentPage(); p != nil {
|
||||
v.parent.appView().flash(flashInfo, "Page Up logs...")
|
||||
p.Item.(*logView).PageUp()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *logsView) pageDown(*tcell.EventKey) {
|
||||
func (v *logsView) pageDown(*tcell.EventKey) *tcell.EventKey {
|
||||
if p := v.CurrentPage(); p != nil {
|
||||
v.parent.appView().flash(flashInfo, "Page Down logs...")
|
||||
p.Item.(*logView).PageDown()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *logsView) clearLogs(*tcell.EventKey) {
|
||||
func (v *logsView) clearLogs(*tcell.EventKey) *tcell.EventKey {
|
||||
if p := v.CurrentPage(); p != nil {
|
||||
v.parent.appView().flash(flashInfo, "Clearing logs...")
|
||||
v.buffer.clear()
|
||||
p.Item.(*logView).Clear()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,25 +2,27 @@ package views
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/k8sland/tview"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
menuSepFmt = " [dodgerblue::b]%-8s [white::d]%s "
|
||||
menuSepFmt = " [dodgerblue::b]%-9s [white::d]%s "
|
||||
menuIndexFmt = " [fuchsia::b]<%d> [white::d]%s "
|
||||
maxRows = 6
|
||||
colLen = 20
|
||||
)
|
||||
|
||||
type (
|
||||
keyboardHandler func(*tcell.EventKey)
|
||||
var menuRX = regexp.MustCompile(`\d`)
|
||||
|
||||
type (
|
||||
hint struct {
|
||||
mnemonic, display string
|
||||
}
|
||||
|
|
@ -29,37 +31,43 @@ type (
|
|||
hinter interface {
|
||||
hints() hints
|
||||
}
|
||||
|
||||
keyAction struct {
|
||||
description string
|
||||
action keyboardHandler
|
||||
}
|
||||
keyActions map[tcell.Key]keyAction
|
||||
|
||||
menuView struct {
|
||||
*tview.Table
|
||||
}
|
||||
)
|
||||
|
||||
func (h hints) Len() int {
|
||||
return len(h)
|
||||
}
|
||||
|
||||
func (h hints) Swap(i, j int) {
|
||||
h[i], h[j] = h[j], h[i]
|
||||
}
|
||||
|
||||
func (h hints) Less(i, j int) bool {
|
||||
n, err1 := strconv.Atoi(h[i].mnemonic)
|
||||
m, err2 := strconv.Atoi(h[j].mnemonic)
|
||||
|
||||
if err1 == nil && err2 == nil {
|
||||
return n < m
|
||||
}
|
||||
|
||||
d := strings.Compare(h[i].mnemonic, h[j].mnemonic)
|
||||
return d < 0
|
||||
if err1 == nil && err2 != nil {
|
||||
return true
|
||||
}
|
||||
if err1 != nil && err2 == nil {
|
||||
return false
|
||||
}
|
||||
return strings.Compare(h[i].mnemonic, h[j].mnemonic) < 0
|
||||
}
|
||||
|
||||
func newKeyHandler(d string, a keyboardHandler) keyAction {
|
||||
// -----------------------------------------------------------------------------
|
||||
type (
|
||||
actionHandler func(*tcell.EventKey) *tcell.EventKey
|
||||
|
||||
keyAction struct {
|
||||
description string
|
||||
action actionHandler
|
||||
}
|
||||
keyActions map[tcell.Key]keyAction
|
||||
)
|
||||
|
||||
func newKeyAction(d string, a actionHandler) keyAction {
|
||||
return keyAction{description: d, action: a}
|
||||
}
|
||||
|
||||
|
|
@ -68,6 +76,11 @@ func newMenuView() *menuView {
|
|||
return &v
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
type menuView struct {
|
||||
*tview.Table
|
||||
}
|
||||
|
||||
func (v *menuView) setMenu(hh hints) {
|
||||
v.Clear()
|
||||
sort.Sort(hh)
|
||||
|
|
@ -75,17 +88,12 @@ func (v *menuView) setMenu(hh hints) {
|
|||
var row, col int
|
||||
firstNS, firstCmd := true, true
|
||||
for _, h := range hh {
|
||||
_, err := strconv.Atoi(h.mnemonic)
|
||||
if err == nil && firstNS {
|
||||
row = 0
|
||||
col = 2
|
||||
firstNS = false
|
||||
isDigit := menuRX.MatchString(h.mnemonic)
|
||||
if isDigit && firstNS {
|
||||
row, col, firstNS = 0, 2, false
|
||||
}
|
||||
|
||||
if err != nil && firstCmd {
|
||||
row = 0
|
||||
col = 0
|
||||
firstCmd = false
|
||||
if !isDigit && firstCmd {
|
||||
row, col, firstCmd = 0, 0, false
|
||||
}
|
||||
c := tview.NewTableCell(v.item(h))
|
||||
v.SetCell(row, col, c)
|
||||
|
|
@ -106,14 +114,25 @@ func (v *menuView) item(h hint) string {
|
|||
if err == nil {
|
||||
return fmt.Sprintf(menuIndexFmt, i, resource.Truncate(h.display, 14))
|
||||
}
|
||||
|
||||
return fmt.Sprintf(menuSepFmt, v.toMnemonic(h.mnemonic), h.display)
|
||||
}
|
||||
|
||||
func (a keyActions) skipKey(k tcell.Key) bool {
|
||||
switch k {
|
||||
case tcell.KeyBackspace2:
|
||||
fallthrough
|
||||
case tcell.KeyEnter:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a keyActions) toHints() hints {
|
||||
kk := make([]int, 0, len(a))
|
||||
for k := range a {
|
||||
kk = append(kk, int(k))
|
||||
if !a.skipKey(k) {
|
||||
kk = append(kk, int(k))
|
||||
}
|
||||
}
|
||||
sort.Ints(kk)
|
||||
|
||||
|
|
@ -123,6 +142,8 @@ func (a keyActions) toHints() hints {
|
|||
hh = append(hh, hint{
|
||||
mnemonic: name,
|
||||
display: a[tcell.Key(k)].description})
|
||||
} else {
|
||||
log.Errorf("Unable to local KeyName for %#v", k)
|
||||
}
|
||||
}
|
||||
return hh
|
||||
|
|
@ -173,7 +194,39 @@ const (
|
|||
KeyX
|
||||
KeyY
|
||||
KeyZ
|
||||
KeyHelp = 63
|
||||
KeyHelp = 63
|
||||
KeySlash = 47
|
||||
KeyColon = 58
|
||||
)
|
||||
|
||||
// Define Shift Keys
|
||||
const (
|
||||
KeyShiftA tcell.Key = iota + 65
|
||||
KeyShiftB
|
||||
KeyShiftC
|
||||
KeyShiftD
|
||||
KeyShiftE
|
||||
KeyShiftF
|
||||
KeyShiftG
|
||||
KeyShiftH
|
||||
KeyShiftI
|
||||
KeyShiftJ
|
||||
KeyShiftK
|
||||
KeyShiftL
|
||||
KeyShiftM
|
||||
KeyShiftN
|
||||
KeyShiftO
|
||||
KeyShiftP
|
||||
KeyShiftQ
|
||||
KeyShiftR
|
||||
KeyShiftS
|
||||
KeyShiftT
|
||||
KeyShiftU
|
||||
KeyShiftV
|
||||
KeyShiftW
|
||||
KeyShiftX
|
||||
KeyShiftY
|
||||
KeyShiftZ
|
||||
)
|
||||
|
||||
var numKeys = map[int]int32{
|
||||
|
|
@ -188,3 +241,71 @@ var numKeys = map[int]int32{
|
|||
8: Key8,
|
||||
9: Key9,
|
||||
}
|
||||
|
||||
func initKeys() {
|
||||
tcell.KeyNames[tcell.Key(Key0)] = "0"
|
||||
tcell.KeyNames[tcell.Key(Key1)] = "1"
|
||||
tcell.KeyNames[tcell.Key(Key2)] = "2"
|
||||
tcell.KeyNames[tcell.Key(Key3)] = "3"
|
||||
tcell.KeyNames[tcell.Key(Key4)] = "4"
|
||||
tcell.KeyNames[tcell.Key(Key5)] = "5"
|
||||
tcell.KeyNames[tcell.Key(Key6)] = "6"
|
||||
tcell.KeyNames[tcell.Key(Key7)] = "7"
|
||||
tcell.KeyNames[tcell.Key(Key8)] = "8"
|
||||
tcell.KeyNames[tcell.Key(Key9)] = "9"
|
||||
|
||||
tcell.KeyNames[tcell.Key(KeyA)] = "a"
|
||||
tcell.KeyNames[tcell.Key(KeyB)] = "b"
|
||||
tcell.KeyNames[tcell.Key(KeyC)] = "c"
|
||||
tcell.KeyNames[tcell.Key(KeyD)] = "d"
|
||||
tcell.KeyNames[tcell.Key(KeyE)] = "e"
|
||||
tcell.KeyNames[tcell.Key(KeyF)] = "f"
|
||||
tcell.KeyNames[tcell.Key(KeyG)] = "g"
|
||||
tcell.KeyNames[tcell.Key(KeyH)] = "h"
|
||||
tcell.KeyNames[tcell.Key(KeyI)] = "i"
|
||||
tcell.KeyNames[tcell.Key(KeyJ)] = "j"
|
||||
tcell.KeyNames[tcell.Key(KeyK)] = "k"
|
||||
tcell.KeyNames[tcell.Key(KeyL)] = "l"
|
||||
tcell.KeyNames[tcell.Key(KeyM)] = "m"
|
||||
tcell.KeyNames[tcell.Key(KeyN)] = "n"
|
||||
tcell.KeyNames[tcell.Key(KeyO)] = "o"
|
||||
tcell.KeyNames[tcell.Key(KeyP)] = "p"
|
||||
tcell.KeyNames[tcell.Key(KeyQ)] = "q"
|
||||
tcell.KeyNames[tcell.Key(KeyR)] = "r"
|
||||
tcell.KeyNames[tcell.Key(KeyS)] = "s"
|
||||
tcell.KeyNames[tcell.Key(KeyT)] = "t"
|
||||
tcell.KeyNames[tcell.Key(KeyU)] = "u"
|
||||
tcell.KeyNames[tcell.Key(KeyV)] = "v"
|
||||
tcell.KeyNames[tcell.Key(KeyX)] = "x"
|
||||
tcell.KeyNames[tcell.Key(KeyY)] = "y"
|
||||
tcell.KeyNames[tcell.Key(KeyZ)] = "z"
|
||||
|
||||
tcell.KeyNames[tcell.Key(KeyShiftA)] = "SHIFT-A"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftB)] = "SHIFT-B"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftC)] = "SHIFT-C"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftD)] = "SHIFT-D"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftE)] = "SHIFT-E"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftF)] = "SHIFT-F"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftG)] = "SHIFT-G"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftH)] = "SHIFT-H"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftI)] = "SHIFT-I"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftJ)] = "SHIFT-J"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftK)] = "SHIFT-K"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftL)] = "SHIFT-L"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftM)] = "SHIFT-M"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftN)] = "SHIFT-N"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftO)] = "SHIFT-O"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftP)] = "SHIFT-P"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftQ)] = "SHIFT-Q"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftR)] = "SHIFT-R"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftS)] = "SHIFT-S"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftT)] = "SHIFT-T"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftU)] = "SHIFT-U"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftV)] = "SHIFT-V"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftX)] = "SHIFT-X"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftY)] = "SHIFT-Y"
|
||||
tcell.KeyNames[tcell.Key(KeyShiftZ)] = "SHIFT-Z"
|
||||
|
||||
tcell.KeyNames[tcell.Key(KeyHelp)] = "?"
|
||||
tcell.KeyNames[tcell.Key(KeySlash)] = "/"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package views
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"regexp"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
|
|
@ -15,40 +15,43 @@ const (
|
|||
deltaNSIndicator = "(𝜟)"
|
||||
)
|
||||
|
||||
var nsCleanser = regexp.MustCompile(`(\w+)[+|(*)|(𝜟)]*`)
|
||||
|
||||
type namespaceView struct {
|
||||
*resourceView
|
||||
}
|
||||
|
||||
func newNamespaceView(t string, app *appView, list resource.List, c colorerFn) resourceViewer {
|
||||
v := namespaceView{
|
||||
resourceView: newResourceView(t, app, list, c).(*resourceView),
|
||||
}
|
||||
v := namespaceView{newResourceView(t, app, list, c).(*resourceView)}
|
||||
v.extraActionsFn = v.extraActions
|
||||
v.selectedFn = v.getSelectedItem
|
||||
v.decorateDataFn = v.decorate
|
||||
v.switchPage("ns")
|
||||
return &v
|
||||
}
|
||||
|
||||
func (v *namespaceView) useNamespace(*tcell.EventKey) {
|
||||
if !v.rowSelected() {
|
||||
return
|
||||
}
|
||||
|
||||
ns := v.selectedItem
|
||||
for _, i := range []string{deltaNSIndicator, favNSIndicator, defaultNSIndicator} {
|
||||
if strings.HasSuffix(ns, i) {
|
||||
ns = strings.TrimRight(ns, i)
|
||||
}
|
||||
}
|
||||
v.refresh()
|
||||
|
||||
config.Root.SetActiveNamespace(ns)
|
||||
config.Root.Save()
|
||||
v.app.flash(flashInfo, fmt.Sprintf("Setting namespace `%s as your default namespace", ns))
|
||||
func (v *namespaceView) extraActions(aa keyActions) {
|
||||
aa[KeyU] = newKeyAction("Use", v.useNamespace)
|
||||
}
|
||||
|
||||
func (v *namespaceView) extraActions(aa keyActions) {
|
||||
aa[KeyU] = keyAction{description: "Use", action: v.useNamespace}
|
||||
func (v *namespaceView) useNamespace(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !v.rowSelected() {
|
||||
return evt
|
||||
}
|
||||
ns := v.getSelectedItem()
|
||||
config.Root.SetActiveNamespace(ns)
|
||||
config.Root.Save()
|
||||
v.refresh()
|
||||
v.app.flash(flashInfo, fmt.Sprintf("Setting namespace `%s as your default namespace", ns))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *namespaceView) getSelectedItem() string {
|
||||
return v.cleanser(v.selectedItem)
|
||||
}
|
||||
|
||||
func (*namespaceView) cleanser(s string) string {
|
||||
return nsCleanser.ReplaceAllString(s, `$1`)
|
||||
}
|
||||
|
||||
func (v *namespaceView) decorate(data resource.TableData) resource.TableData {
|
||||
|
|
@ -59,18 +62,15 @@ func (v *namespaceView) decorate(data resource.TableData) resource.TableData {
|
|||
Deltas: resource.Row{"", "", ""},
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range data.Rows {
|
||||
if config.InList(config.Root.FavNamespaces(), k) {
|
||||
v.Fields[0] += "+"
|
||||
v.Action = resource.Unchanged
|
||||
}
|
||||
|
||||
if config.Root.ActiveNamespace() == k {
|
||||
v.Fields[0] += "(*)"
|
||||
v.Action = resource.Unchanged
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
package views
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNSCleanser(t *testing.T) {
|
||||
var v namespaceView
|
||||
|
||||
uu := []struct {
|
||||
s, e string
|
||||
}{
|
||||
{"fred", "fred"},
|
||||
{"fred+", "fred"},
|
||||
{"fred(*)", "fred"},
|
||||
{"fred+(*)", "fred"},
|
||||
{"fred-blee+(*)", "fred-blee"},
|
||||
{"fred1-blee2+(*)", "fred1-blee2"},
|
||||
{"fred(𝜟)", "fred"},
|
||||
}
|
||||
|
||||
for _, u := range uu {
|
||||
assert.Equal(t, u.e, v.cleanser(u.s))
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ type podView struct {
|
|||
|
||||
type loggable interface {
|
||||
appView() *appView
|
||||
backFn() actionHandler
|
||||
getSelection() string
|
||||
getList() resource.List
|
||||
switchPage(n string)
|
||||
|
|
@ -19,7 +20,9 @@ type loggable interface {
|
|||
|
||||
func newPodView(t string, app *appView, list resource.List, c colorerFn) resourceViewer {
|
||||
v := podView{newResourceView(t, app, list, c).(*resourceView)}
|
||||
v.extraActionsFn = v.extraActions
|
||||
{
|
||||
v.extraActionsFn = v.extraActions
|
||||
}
|
||||
|
||||
v.AddPage("logs", newLogsView(&v), true, false)
|
||||
|
||||
|
|
@ -29,7 +32,7 @@ func newPodView(t string, app *appView, list resource.List, c colorerFn) resourc
|
|||
v.sshInto(v.selectedItem, t)
|
||||
})
|
||||
picker.setActions(keyActions{
|
||||
tcell.KeyEscape: {description: "Back", action: v.back},
|
||||
tcell.KeyEscape: {description: "Back", action: v.backCmd},
|
||||
})
|
||||
v.AddPage("choose", picker, true, false)
|
||||
}
|
||||
|
|
@ -40,56 +43,60 @@ func newPodView(t string, app *appView, list resource.List, c colorerFn) resourc
|
|||
|
||||
// Protocol...
|
||||
|
||||
func (v *podView) backFn() actionHandler {
|
||||
return v.backCmd
|
||||
}
|
||||
|
||||
func (v *podView) appView() *appView {
|
||||
return v.app
|
||||
}
|
||||
|
||||
func (v *podView) getList() resource.List {
|
||||
return v.list
|
||||
}
|
||||
|
||||
func (v *podView) getSelection() string {
|
||||
return v.selectedItem
|
||||
}
|
||||
|
||||
// Handlers...
|
||||
|
||||
func (v *podView) logs(*tcell.EventKey) {
|
||||
func (v *podView) logsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !v.rowSelected() {
|
||||
return
|
||||
return evt
|
||||
}
|
||||
|
||||
cc, err := fetchContainers(v.list, v.selectedItem)
|
||||
cc, err := fetchContainers(v.list, v.selectedItem, true)
|
||||
if err != nil {
|
||||
v.app.flash(flashErr, err.Error())
|
||||
log.Error(err)
|
||||
return evt
|
||||
}
|
||||
|
||||
l := v.GetPrimitive("logs").(*logsView)
|
||||
l.deleteAllPages()
|
||||
for _, c := range cc {
|
||||
l.addContainer(c)
|
||||
}
|
||||
|
||||
v.switchPage("logs")
|
||||
l.init()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *podView) shell(*tcell.EventKey) {
|
||||
func (v *podView) shellCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !v.rowSelected() {
|
||||
return
|
||||
return evt
|
||||
}
|
||||
|
||||
cc, err := fetchContainers(v.list, v.selectedItem)
|
||||
cc, err := fetchContainers(v.list, v.selectedItem, false)
|
||||
if err != nil {
|
||||
v.app.flash(flashErr, err.Error())
|
||||
log.Error("Error fetching containers", err)
|
||||
return
|
||||
return evt
|
||||
}
|
||||
|
||||
if len(cc) == 1 {
|
||||
v.sshInto(v.selectedItem, "")
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
v.showPicker(cc)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *podView) showPicker(cc []string) {
|
||||
|
|
@ -109,13 +116,13 @@ func (v *podView) sshInto(path, co string) {
|
|||
}
|
||||
|
||||
func (v *podView) extraActions(aa keyActions) {
|
||||
aa[KeyL] = newKeyHandler("Logs", v.logs)
|
||||
aa[KeyS] = newKeyHandler("Shell", v.shell)
|
||||
aa[KeyL] = newKeyAction("Logs", v.logsCmd)
|
||||
aa[KeyS] = newKeyAction("Shell", v.shellCmd)
|
||||
}
|
||||
|
||||
func fetchContainers(l resource.List, po string) ([]string, error) {
|
||||
func fetchContainers(l resource.List, po string, includeInit bool) ([]string, error) {
|
||||
if len(po) == 0 {
|
||||
return []string{}, nil
|
||||
}
|
||||
return l.Resource().(resource.Container).Containers(po)
|
||||
return l.Resource().(resource.Container).Containers(po, includeInit)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
|
@ -13,8 +12,8 @@ import (
|
|||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/k8s"
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/k8sland/tview"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
|
@ -40,6 +39,7 @@ type (
|
|||
update sync.Mutex
|
||||
list resource.List
|
||||
extraActionsFn func(keyActions)
|
||||
selectedFn func() string
|
||||
decorateDataFn func(resource.TableData) resource.TableData
|
||||
}
|
||||
)
|
||||
|
|
@ -56,36 +56,17 @@ func newResourceView(title string, app *appView, list resource.List, c colorerFn
|
|||
tv := newTableView(app, v.title, list.SortFn())
|
||||
{
|
||||
tv.SetColorer(c)
|
||||
tv.table.SetSelectionChangedFunc(v.selChanged)
|
||||
tv.SetSelectionChangedFunc(v.selChanged)
|
||||
}
|
||||
v.AddPage(v.list.GetName(), tv, true, true)
|
||||
|
||||
var xray details
|
||||
if list.HasXRay() {
|
||||
xray = newXrayView(app)
|
||||
} else {
|
||||
xray = newYamlView(app)
|
||||
}
|
||||
xray.setActions(keyActions{
|
||||
tcell.KeyEscape: {description: "Back", action: v.back},
|
||||
})
|
||||
|
||||
details := newDetailsView()
|
||||
details.setActions(keyActions{
|
||||
tcell.KeyEscape: {description: "Back", action: v.back},
|
||||
})
|
||||
|
||||
details := newDetailsView(app, v.backCmd)
|
||||
v.AddPage("details", details, true, false)
|
||||
v.AddPage("xray", xray, true, false)
|
||||
|
||||
return &v
|
||||
}
|
||||
|
||||
// Init watches all running pods in given namespace
|
||||
func (v *resourceView) init(ctx context.Context, ns string) {
|
||||
details := v.GetPrimitive("xray").(details)
|
||||
details.clear()
|
||||
|
||||
v.selectedItem, v.selectedNS = noSelection, ns
|
||||
|
||||
go func(ctx context.Context) {
|
||||
|
|
@ -103,7 +84,7 @@ func (v *resourceView) init(ctx context.Context, ns string) {
|
|||
}(ctx)
|
||||
v.refreshActions()
|
||||
if tv, ok := v.CurrentPage().Item.(*tableView); ok {
|
||||
tv.table.Select(0, 0)
|
||||
tv.Select(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,6 +100,13 @@ func (v *resourceView) colorFn(f colorerFn) {
|
|||
v.getTV().SetColorer(f)
|
||||
}
|
||||
|
||||
func (v *resourceView) getSelectedItem() string {
|
||||
if v.selectedFn != nil {
|
||||
return v.selectedFn()
|
||||
}
|
||||
return v.selectedItem
|
||||
}
|
||||
|
||||
// Protocol...
|
||||
|
||||
// Hints fetch menu hints
|
||||
|
|
@ -126,89 +114,89 @@ func (v *resourceView) hints() hints {
|
|||
return v.CurrentPage().Item.(hinter).hints()
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Actions...
|
||||
|
||||
func (v *resourceView) back(*tcell.EventKey) {
|
||||
func (v *resourceView) backCmd(*tcell.EventKey) *tcell.EventKey {
|
||||
v.switchPage(v.list.GetName())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *resourceView) delete(*tcell.EventKey) {
|
||||
func (v *resourceView) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !v.rowSelected() {
|
||||
return
|
||||
return evt
|
||||
}
|
||||
|
||||
v.getTV().setDeleted()
|
||||
v.app.flash(flashInfo, fmt.Sprintf("Deleting %s %s", v.list.GetName(), v.selectedItem))
|
||||
if err := v.list.Resource().Delete(v.selectedItem); err != nil {
|
||||
sel := v.getSelectedItem()
|
||||
v.app.flash(flashInfo, fmt.Sprintf("Deleting %s %s", v.list.GetName(), sel))
|
||||
if err := v.list.Resource().Delete(sel); err != nil {
|
||||
v.app.flash(flashErr, "Boom!", err.Error())
|
||||
}
|
||||
v.selectedItem = noSelection
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *resourceView) describe(*tcell.EventKey) {
|
||||
func (v *resourceView) describeCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !v.rowSelected() {
|
||||
return
|
||||
return evt
|
||||
}
|
||||
|
||||
selected := v.selectedItem
|
||||
selected = strings.Replace(selected, "+", "", -1)
|
||||
selected = strings.Replace(selected, "(*)", "", -1)
|
||||
|
||||
raw, err := v.list.Resource().Describe(v.title, selected)
|
||||
sel := v.getSelectedItem()
|
||||
raw, err := v.list.Resource().Describe(v.title, sel)
|
||||
if err != nil {
|
||||
v.app.flash(flashErr, "Unable to describe this resource", err.Error())
|
||||
v.app.flash(flashErr, "Unable to describeCmd this resource", err.Error())
|
||||
log.Error(err)
|
||||
return
|
||||
return evt
|
||||
}
|
||||
|
||||
var re = regexp.MustCompile(`(?m:(^(.+)$))`)
|
||||
str := re.ReplaceAllString(string(raw), `[aqua]$1`)
|
||||
|
||||
details := v.GetPrimitive("details").(*detailsView)
|
||||
details.ScrollToBeginning()
|
||||
details.setCategory("DESC")
|
||||
details.SetText(str)
|
||||
details.setTitle(selected)
|
||||
{
|
||||
details.setCategory("Describe")
|
||||
details.setTitle(sel)
|
||||
details.SetTextColor(tcell.ColorAqua)
|
||||
details.SetText(string(raw))
|
||||
details.ScrollToBeginning()
|
||||
}
|
||||
v.switchPage("details")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *resourceView) view(*tcell.EventKey) {
|
||||
func (v *resourceView) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !v.rowSelected() {
|
||||
return
|
||||
return evt
|
||||
}
|
||||
|
||||
raw, err := v.list.Resource().Marshal(v.selectedItem)
|
||||
sel := v.getSelectedItem()
|
||||
raw, err := v.list.Resource().Marshal(sel)
|
||||
if err != nil {
|
||||
v.app.flash(flashErr, "Unable to marshal resource", err.Error())
|
||||
log.Error(err)
|
||||
return
|
||||
return evt
|
||||
}
|
||||
|
||||
var re = regexp.MustCompile(`(?m:([\w|\.|"|\-|\/|\@]+):(.*)$)`)
|
||||
str := re.ReplaceAllString(string(raw), `[aqua]$1: [white]$2`)
|
||||
|
||||
details := v.GetPrimitive("details").(*detailsView)
|
||||
details.ScrollToBeginning()
|
||||
details.setCategory("YAML")
|
||||
details.SetText(str)
|
||||
details.setTitle(v.selectedItem)
|
||||
{
|
||||
details.setCategory("View")
|
||||
details.setTitle(sel)
|
||||
details.SetTextColor(tcell.ColorMediumAquamarine)
|
||||
details.SetText(string(raw))
|
||||
details.ScrollToBeginning()
|
||||
}
|
||||
v.switchPage("details")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *resourceView) edit(*tcell.EventKey) {
|
||||
func (v *resourceView) editCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if !v.rowSelected() {
|
||||
return
|
||||
return evt
|
||||
}
|
||||
v.app.flash(flashInfo, fmt.Sprintf("Editing %s %s", v.title, v.selectedItem))
|
||||
ns, s := namespaced(v.selectedItem)
|
||||
run(v.app, "edit", v.list.GetName(), "-n", ns, s)
|
||||
return
|
||||
return evt
|
||||
}
|
||||
|
||||
func (v *resourceView) switchNamespace(evt *tcell.EventKey) {
|
||||
func (v *resourceView) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
i, _ := strconv.Atoi(string(evt.Rune()))
|
||||
ns := v.namespaces[i]
|
||||
v.doSwitchNamespace(ns)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *resourceView) doSwitchNamespace(ns string) {
|
||||
|
|
@ -225,7 +213,7 @@ func (v *resourceView) doSwitchNamespace(ns string) {
|
|||
v.refresh()
|
||||
v.selectItem(0, 0)
|
||||
v.getTV().resetTitle()
|
||||
v.getTV().table.Select(0, 0)
|
||||
v.getTV().Select(0, 0)
|
||||
v.app.cmdBuff.reset()
|
||||
config.Root.SetActiveNamespace(v.selectedNS)
|
||||
config.Root.Save()
|
||||
|
|
@ -275,16 +263,16 @@ func (v *resourceView) selectItem(r, c int) {
|
|||
t := v.getTV()
|
||||
switch v.list.GetNamespace() {
|
||||
case resource.NotNamespaced:
|
||||
v.selectedItem = strings.TrimSpace(t.table.GetCell(r, 0).Text)
|
||||
v.selectedItem = strings.TrimSpace(t.GetCell(r, 0).Text)
|
||||
case resource.AllNamespaces:
|
||||
v.selectedItem = path.Join(
|
||||
strings.TrimSpace(t.table.GetCell(r, 0).Text),
|
||||
strings.TrimSpace(t.table.GetCell(r, 1).Text),
|
||||
strings.TrimSpace(t.GetCell(r, 0).Text),
|
||||
strings.TrimSpace(t.GetCell(r, 1).Text),
|
||||
)
|
||||
default:
|
||||
v.selectedItem = path.Join(
|
||||
v.selectedNS,
|
||||
strings.TrimSpace(t.table.GetCell(r, 0).Text),
|
||||
strings.TrimSpace(t.GetCell(r, 0).Text),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -293,8 +281,8 @@ func (v *resourceView) switchPage(p string) {
|
|||
v.update.Lock()
|
||||
{
|
||||
v.SwitchToPage(p)
|
||||
h := v.GetPrimitive(p).(hinter)
|
||||
v.selectedNS = v.list.GetNamespace()
|
||||
h := v.GetPrimitive(p).(hinter)
|
||||
v.app.setHints(h.hints())
|
||||
v.app.SetFocus(v.CurrentPage().Item)
|
||||
}
|
||||
|
|
@ -328,30 +316,30 @@ func (v *resourceView) refreshActions() {
|
|||
}
|
||||
}
|
||||
|
||||
aa := keyActions{}
|
||||
aa := make(keyActions)
|
||||
if v.list.Access(resource.NamespaceAccess) {
|
||||
v.namespaces = make(map[int]string, config.MaxFavoritesNS)
|
||||
for i, n := range config.Root.FavNamespaces() {
|
||||
aa[tcell.Key(numKeys[i])] = newKeyHandler(n, v.switchNamespace)
|
||||
aa[tcell.Key(numKeys[i])] = newKeyAction(n, v.switchNamespaceCmd)
|
||||
v.namespaces[i] = n
|
||||
}
|
||||
}
|
||||
|
||||
if v.list.Access(resource.EditAccess) {
|
||||
aa[KeyE] = newKeyHandler("Edit", v.edit)
|
||||
aa[KeyE] = newKeyAction("Edit", v.editCmd)
|
||||
}
|
||||
|
||||
if v.list.Access(resource.DeleteAccess) {
|
||||
aa[tcell.KeyCtrlD] = newKeyHandler("Delete", v.delete)
|
||||
aa[tcell.KeyCtrlD] = newKeyAction("Delete", v.deleteCmd)
|
||||
}
|
||||
if v.list.Access(resource.ViewAccess) {
|
||||
aa[KeyV] = newKeyHandler("View", v.view)
|
||||
aa[KeyV] = newKeyAction("View", v.viewCmd)
|
||||
}
|
||||
if v.list.Access(resource.DescribeAccess) {
|
||||
aa[KeyD] = newKeyHandler("Describe", v.describe)
|
||||
aa[KeyD] = newKeyAction("Describe", v.describeCmd)
|
||||
}
|
||||
|
||||
aa[KeyHelp] = newKeyHandler("Help", v.app.noop)
|
||||
aa[KeyHelp] = newKeyAction("Help", v.app.noopCmd)
|
||||
|
||||
if v.extraActionsFn != nil {
|
||||
v.extraActionsFn(aa)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package views
|
|||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/k8sland/tview"
|
||||
)
|
||||
|
||||
type selectList struct {
|
||||
|
|
@ -15,15 +15,17 @@ type selectList struct {
|
|||
|
||||
func newSelectList() *selectList {
|
||||
v := selectList{List: tview.NewList()}
|
||||
v.SetBorder(true)
|
||||
v.SetTitle(" Please select a Container ")
|
||||
v.SetInputCapture(func(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if a, ok := v.actions[evt.Key()]; ok {
|
||||
a.action(evt)
|
||||
evt = nil
|
||||
}
|
||||
return evt
|
||||
})
|
||||
{
|
||||
v.SetBorder(true)
|
||||
v.SetTitle(" [aqua::b]Container Selector ")
|
||||
v.SetInputCapture(func(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if a, ok := v.actions[evt.Key()]; ok {
|
||||
a.action(evt)
|
||||
evt = nil
|
||||
}
|
||||
return evt
|
||||
})
|
||||
}
|
||||
return &v
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/k8sland/tview"
|
||||
"github.com/derailed/tview"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
|||
|
|
@ -7,149 +7,125 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/k8sland/tview"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
titleFmt = " [aqua::b]%s[aqua::-]([fuchsia::b]%d[aqua::-]) "
|
||||
searchFmt = "<[green::b]/%s[aqua::]> "
|
||||
nsTitleFmt = " [aqua::b]%s([fuchsia::b]%s[aqua::-])[aqua::-][[aqua::b]%d[aqua::-]][aqua::-] "
|
||||
)
|
||||
|
||||
type (
|
||||
tableView struct {
|
||||
*tview.Flex
|
||||
*tview.Table
|
||||
|
||||
app *appView
|
||||
baseTitle string
|
||||
currentNS string
|
||||
refresh sync.Mutex
|
||||
refreshMX sync.Mutex
|
||||
actions keyActions
|
||||
colorer colorerFn
|
||||
colorerFn colorerFn
|
||||
sortFn resource.SortFn
|
||||
table *tview.Table
|
||||
data resource.TableData
|
||||
cmdBuff *cmdBuff
|
||||
}
|
||||
)
|
||||
|
||||
func newTableView(app *appView, title string, sortFn resource.SortFn) *tableView {
|
||||
v := tableView{app: app, Flex: tview.NewFlex().SetDirection(tview.FlexRow)}
|
||||
v := tableView{app: app, Table: tview.NewTable()}
|
||||
{
|
||||
v.baseTitle = title
|
||||
v.sortFn = sortFn
|
||||
v.actions = make(keyActions)
|
||||
v.SetBorder(true)
|
||||
v.SetBorderColor(tcell.ColorDodgerBlue)
|
||||
v.SetBorderAttributes(tcell.AttrBold)
|
||||
v.SetBorderPadding(0, 0, 1, 1)
|
||||
v.cmdBuff = newCmdBuff('/')
|
||||
v.cmdBuff.addListener(app.cmdView)
|
||||
v.cmdBuff.reset()
|
||||
v.SetSelectable(true, false)
|
||||
v.SetSelectedStyle(tcell.ColorBlack, tcell.ColorAqua, tcell.AttrBold)
|
||||
v.SetInputCapture(v.keyboard)
|
||||
}
|
||||
|
||||
v.cmdBuff.addListener(app.cmdView)
|
||||
v.cmdBuff.reset()
|
||||
v.actions[KeySlash] = newKeyAction("Filter", v.activateCmd)
|
||||
v.actions[tcell.KeyEnter] = newKeyAction("Search", v.filterCmd)
|
||||
v.actions[tcell.KeyEscape] = newKeyAction("Reset", v.resetCmd)
|
||||
v.actions[tcell.KeyBackspace2] = newKeyAction("Erase", v.eraseCmd)
|
||||
|
||||
v.table = tview.NewTable()
|
||||
{
|
||||
v.table.SetSelectable(true, false)
|
||||
v.table.SetSelectedStyle(tcell.ColorBlack, tcell.ColorAqua, tcell.AttrBold)
|
||||
v.table.SetInputCapture(v.keyboard)
|
||||
}
|
||||
|
||||
v.AddItem(v.table, 0, 1, true)
|
||||
return &v
|
||||
}
|
||||
|
||||
func (v *tableView) setDeleted() {
|
||||
r, _ := v.table.GetSelection()
|
||||
cols := v.table.GetColumnCount()
|
||||
for x := 0; x < cols; x++ {
|
||||
v.table.GetCell(r, x).SetAttributes(tcell.AttrDim)
|
||||
}
|
||||
}
|
||||
|
||||
func (v *tableView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||
key := evt.Key()
|
||||
if evt.Key() == tcell.KeyRune {
|
||||
if key == tcell.KeyRune {
|
||||
if v.cmdBuff.isActive() {
|
||||
v.cmdBuff.add(evt.Rune())
|
||||
}
|
||||
switch evt.Rune() {
|
||||
case v.cmdBuff.hotKey:
|
||||
if !v.app.cmdView.inCmdMode() {
|
||||
v.app.flash(flashInfo, "Entering filtering mode...")
|
||||
log.Info("K9s entering filtering mode...")
|
||||
v.cmdBuff.setActive(true)
|
||||
}
|
||||
return evt
|
||||
return nil
|
||||
}
|
||||
key = tcell.Key(evt.Rune())
|
||||
}
|
||||
|
||||
if a, ok := v.actions[key]; ok {
|
||||
if !v.app.cmdView.inCmdMode() {
|
||||
a.action(evt)
|
||||
}
|
||||
}
|
||||
|
||||
switch evt.Key() {
|
||||
case tcell.KeyEnter:
|
||||
if v.cmdBuff.isActive() && !v.cmdBuff.empty() {
|
||||
v.filter()
|
||||
}
|
||||
v.cmdBuff.setActive(false)
|
||||
case tcell.KeyEsc:
|
||||
v.cmdBuff.reset()
|
||||
v.filter()
|
||||
case tcell.KeyBackspace2:
|
||||
if v.cmdBuff.isActive() {
|
||||
v.cmdBuff.del()
|
||||
}
|
||||
log.Debug(">> TableView handled ", tcell.KeyNames[key])
|
||||
return a.action(evt)
|
||||
}
|
||||
return evt
|
||||
}
|
||||
|
||||
func (v *tableView) filter() {
|
||||
v.filterData(v.cmdBuff)
|
||||
func (v *tableView) filterCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
v.cmdBuff.setActive(false)
|
||||
v.refresh()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *tableView) filterData(filter fmt.Stringer) {
|
||||
filtered := resource.TableData{
|
||||
Header: v.data.Header,
|
||||
Rows: resource.RowEvents{},
|
||||
Namespace: v.data.Namespace,
|
||||
func (v *tableView) eraseCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if v.cmdBuff.isActive() {
|
||||
v.cmdBuff.del()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *tableView) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
v.cmdBuff.reset()
|
||||
v.refresh()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *tableView) activateCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if v.app.cmdView.inCmdMode() {
|
||||
return evt
|
||||
}
|
||||
|
||||
rx, err := regexp.Compile(filter.String())
|
||||
if err != nil {
|
||||
v.app.flash(flashErr, "Invalid search expression")
|
||||
v.cmdBuff.clear()
|
||||
return
|
||||
v.app.flash(flashInfo, "Entering filtering mode...")
|
||||
log.Info("Entering filtering mode...")
|
||||
v.cmdBuff.reset()
|
||||
v.cmdBuff.setActive(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *tableView) setDeleted() {
|
||||
r, _ := v.GetSelection()
|
||||
cols := v.GetColumnCount()
|
||||
for x := 0; x < cols; x++ {
|
||||
v.GetCell(r, x).SetAttributes(tcell.AttrDim)
|
||||
}
|
||||
for k, row := range v.data.Rows {
|
||||
f := strings.Join(row.Fields, " ")
|
||||
if rx.MatchString(f) {
|
||||
filtered.Rows[k] = row
|
||||
}
|
||||
}
|
||||
v.doUpdate(filtered)
|
||||
}
|
||||
|
||||
// SetColorer sets up table row color management.
|
||||
func (v *tableView) SetColorer(f colorerFn) {
|
||||
v.colorer = f
|
||||
}
|
||||
|
||||
// AddActions sets up keyboard action listener.
|
||||
func (v *tableView) addActions(kk keyActions) {
|
||||
for k, a := range kk {
|
||||
v.actions[k] = a
|
||||
}
|
||||
v.colorerFn = f
|
||||
}
|
||||
|
||||
// SetActions sets up keyboard action listener.
|
||||
func (v *tableView) setActions(aa keyActions) {
|
||||
v.actions = aa
|
||||
for k, a := range aa {
|
||||
v.actions[k] = a
|
||||
}
|
||||
}
|
||||
|
||||
// Hints options
|
||||
|
|
@ -160,43 +136,53 @@ func (v *tableView) hints() hints {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (v *tableView) resetTitle() {
|
||||
var title string
|
||||
|
||||
switch v.currentNS {
|
||||
case resource.NotNamespaced:
|
||||
title = fmt.Sprintf(titleFmt, v.baseTitle, v.table.GetRowCount()-1)
|
||||
default:
|
||||
ns := v.currentNS
|
||||
if v.currentNS == resource.AllNamespaces {
|
||||
ns = resource.AllNamespace
|
||||
}
|
||||
title = fmt.Sprintf(nsTitleFmt, v.baseTitle, ns, v.table.GetRowCount()-1)
|
||||
}
|
||||
|
||||
if !v.cmdBuff.empty() {
|
||||
title += fmt.Sprintf("<[green::b]/%s[aqua::]> ", v.cmdBuff)
|
||||
}
|
||||
v.SetTitle(title)
|
||||
func (v *tableView) refresh() {
|
||||
v.update(v.data)
|
||||
}
|
||||
|
||||
// Update table content
|
||||
func (v *tableView) update(data resource.TableData) {
|
||||
v.refresh.Lock()
|
||||
v.refreshMX.Lock()
|
||||
{
|
||||
v.data = data
|
||||
if !v.cmdBuff.empty() {
|
||||
v.filter()
|
||||
if !v.cmdBuff.isActive() && !v.cmdBuff.empty() {
|
||||
v.doUpdate(v.filtered())
|
||||
} else {
|
||||
v.doUpdate(data)
|
||||
}
|
||||
v.resetTitle()
|
||||
}
|
||||
v.refresh.Unlock()
|
||||
v.refreshMX.Unlock()
|
||||
v.resetTitle()
|
||||
}
|
||||
|
||||
func (v *tableView) filtered() resource.TableData {
|
||||
if v.cmdBuff.empty() {
|
||||
return v.data
|
||||
}
|
||||
|
||||
rx, err := regexp.Compile(`(?i)` + v.cmdBuff.String())
|
||||
if err != nil {
|
||||
v.app.flash(flashErr, "Invalid filter expression")
|
||||
v.cmdBuff.clear()
|
||||
return v.data
|
||||
}
|
||||
|
||||
filtered := resource.TableData{
|
||||
Header: v.data.Header,
|
||||
Rows: resource.RowEvents{},
|
||||
Namespace: v.data.Namespace,
|
||||
}
|
||||
for k, row := range v.data.Rows {
|
||||
f := strings.Join(row.Fields, " ")
|
||||
if rx.MatchString(f) {
|
||||
filtered.Rows[k] = row
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func (v *tableView) doUpdate(data resource.TableData) {
|
||||
v.table.Clear()
|
||||
v.Clear()
|
||||
v.currentNS = data.Namespace
|
||||
|
||||
var row int
|
||||
|
|
@ -209,7 +195,7 @@ func (v *tableView) doUpdate(data resource.TableData) {
|
|||
}
|
||||
c.SetTextColor(tcell.ColorWhite)
|
||||
}
|
||||
v.table.SetCell(row, col, c)
|
||||
v.SetCell(row, col, c)
|
||||
}
|
||||
row++
|
||||
|
||||
|
|
@ -217,11 +203,13 @@ func (v *tableView) doUpdate(data resource.TableData) {
|
|||
for k := range data.Rows {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
v.sortFn(keys)
|
||||
if v.sortFn != nil {
|
||||
v.sortFn(keys)
|
||||
}
|
||||
for _, k := range keys {
|
||||
fgColor := tcell.ColorGray
|
||||
if v.colorer != nil {
|
||||
fgColor = v.colorer(data.Namespace, data.Rows[k])
|
||||
if v.colorerFn != nil {
|
||||
fgColor = v.colorerFn(data.Namespace, data.Rows[k])
|
||||
}
|
||||
for col, f := range data.Rows[k].Fields {
|
||||
c := tview.NewTableCell(deltas(data.Rows[k].Deltas[col], f))
|
||||
|
|
@ -232,12 +220,36 @@ func (v *tableView) doUpdate(data resource.TableData) {
|
|||
}
|
||||
c.SetTextColor(fgColor)
|
||||
}
|
||||
v.table.SetCell(row, col, c)
|
||||
v.SetCell(row, col, c)
|
||||
}
|
||||
row++
|
||||
}
|
||||
}
|
||||
|
||||
func (v *tableView) resetTitle() {
|
||||
var title string
|
||||
|
||||
rc := v.GetRowCount()
|
||||
if rc > 0 {
|
||||
rc--
|
||||
}
|
||||
switch v.currentNS {
|
||||
case resource.NotNamespaced:
|
||||
title = fmt.Sprintf(titleFmt, v.baseTitle, rc)
|
||||
default:
|
||||
ns := v.currentNS
|
||||
if v.currentNS == resource.AllNamespaces {
|
||||
ns = resource.AllNamespace
|
||||
}
|
||||
title = fmt.Sprintf(nsTitleFmt, v.baseTitle, ns, rc)
|
||||
}
|
||||
|
||||
if !v.cmdBuff.isActive() && !v.cmdBuff.empty() {
|
||||
title += fmt.Sprintf(searchFmt, v.cmdBuff)
|
||||
}
|
||||
v.SetTitle(title)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Event listeners...
|
||||
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
package views
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/k8sland/tview"
|
||||
)
|
||||
|
||||
type xrayView struct {
|
||||
*tview.Table
|
||||
|
||||
app *appView
|
||||
actions keyActions
|
||||
}
|
||||
|
||||
func newXrayView(app *appView) *xrayView {
|
||||
v := xrayView{app: app, Table: tview.NewTable()}
|
||||
v.SetBorder(true)
|
||||
v.SetTitle(" Details ")
|
||||
v.SetTitleColor(tcell.ColorAqua)
|
||||
v.SetSelectable(true, false)
|
||||
v.SetSelectedStyle(tcell.ColorBlack, tcell.ColorAqua, tcell.AttrNone)
|
||||
v.SetInputCapture(v.keyboard)
|
||||
return &v
|
||||
}
|
||||
|
||||
func (v *xrayView) setTitle(t string) {
|
||||
v.Table.SetTitle(t)
|
||||
}
|
||||
|
||||
func (v *xrayView) clear() {
|
||||
v.Table.Clear()
|
||||
}
|
||||
|
||||
func (v *xrayView) blur() {
|
||||
v.Table.Blur()
|
||||
}
|
||||
|
||||
func (v *xrayView) init(_ context.Context) {
|
||||
}
|
||||
|
||||
// SetActions to handle keyboard inputs
|
||||
func (v *xrayView) setActions(aa keyActions) {
|
||||
v.actions = aa
|
||||
}
|
||||
|
||||
// Hints fetch mmemonic and hints
|
||||
func (v *xrayView) hints() hints {
|
||||
if v.actions != nil {
|
||||
return v.actions.toHints()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *xrayView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if evt.Key() == tcell.KeyRune {
|
||||
if a, ok := v.actions[evt.Key()]; ok {
|
||||
a.action(evt)
|
||||
evt = nil
|
||||
}
|
||||
} else {
|
||||
if a, ok := v.actions[evt.Key()]; ok {
|
||||
a.action(evt)
|
||||
evt = nil
|
||||
}
|
||||
}
|
||||
return evt
|
||||
}
|
||||
|
||||
func (v *xrayView) update(pp resource.Properties) {
|
||||
v.Clear()
|
||||
|
||||
var row int
|
||||
for col, h := range pp["Headers"].(resource.Row) {
|
||||
tc := tview.NewTableCell(h)
|
||||
tc.SetExpansion(2)
|
||||
v.SetCell(0, col, tc)
|
||||
}
|
||||
row++
|
||||
|
||||
for _, r := range pp["Rows"].([]resource.Row) {
|
||||
for col, c := range r {
|
||||
tc := tview.NewTableCell(c)
|
||||
tc.SetExpansion(2)
|
||||
v.SetCell(row, col, tc)
|
||||
}
|
||||
row++
|
||||
}
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
package views
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/k8sland/tview"
|
||||
)
|
||||
|
||||
const (
|
||||
keyColor = "#00FF00"
|
||||
valColor = "#ADFF2F"
|
||||
yamlTitleFmt = " [aqua::-] YAML [orange::-]%s"
|
||||
)
|
||||
|
||||
type yamlView struct {
|
||||
*tview.TextView
|
||||
|
||||
app *appView
|
||||
actions keyActions
|
||||
}
|
||||
|
||||
func newYamlView(app *appView) *yamlView {
|
||||
v := yamlView{app: app, TextView: tview.NewTextView()}
|
||||
{
|
||||
v.SetBorder(true)
|
||||
v.SetDynamicColors(true)
|
||||
v.SetWrap(false)
|
||||
v.SetTitleColor(tcell.ColorAqua)
|
||||
v.SetInputCapture(v.keyboard)
|
||||
}
|
||||
return &v
|
||||
}
|
||||
|
||||
func (v *yamlView) setTitle(t string) {
|
||||
v.TextView.SetTitle(fmt.Sprintf(yamlTitleFmt, t))
|
||||
}
|
||||
|
||||
func (v *yamlView) clear() {
|
||||
v.TextView.Clear()
|
||||
}
|
||||
|
||||
func (v *yamlView) blur() {
|
||||
}
|
||||
|
||||
func (v *yamlView) init(_ context.Context) {
|
||||
}
|
||||
|
||||
// SetActions to handle keyboard inputs
|
||||
func (v *yamlView) setActions(aa keyActions) {
|
||||
v.actions = aa
|
||||
}
|
||||
|
||||
func (v *yamlView) hints() hints {
|
||||
if v.actions != nil {
|
||||
return v.actions.toHints()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *yamlView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||
if evt.Key() == tcell.KeyRune {
|
||||
if a, ok := v.actions[evt.Key()]; ok {
|
||||
a.action(evt)
|
||||
evt = nil
|
||||
}
|
||||
} else {
|
||||
if a, ok := v.actions[evt.Key()]; ok {
|
||||
a.action(evt)
|
||||
evt = nil
|
||||
}
|
||||
}
|
||||
return evt
|
||||
}
|
||||
|
||||
func (v *yamlView) isEmpty(c interface{}) bool {
|
||||
switch c.(type) {
|
||||
case []string:
|
||||
return len(c.([]string)) == 0
|
||||
case map[string]string:
|
||||
return len(c.(map[string]string)) == 0
|
||||
case map[string]interface{}:
|
||||
return len(c.(map[string]interface{})) == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (v *yamlView) update(pp resource.Properties) {
|
||||
v.Clear()
|
||||
|
||||
kk := make([]string, 0, len(pp))
|
||||
for k := range pp {
|
||||
kk = append(kk, k)
|
||||
}
|
||||
sort.Strings(kk)
|
||||
|
||||
var level int
|
||||
for _, k := range kk {
|
||||
if v.isEmpty(pp[k]) {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(v, "[%s::b]%-20s", keyColor, k+":")
|
||||
v.textFor(level, pp[k])
|
||||
fmt.Fprintf(v, "\n")
|
||||
}
|
||||
v.ScrollToBeginning()
|
||||
v.app.Draw()
|
||||
}
|
||||
|
||||
func (v *yamlView) textFor(level int, p interface{}) string {
|
||||
var indent string
|
||||
for i := 0; i < level; i++ {
|
||||
if level < 1 {
|
||||
indent += strings.Repeat(" ", 20)
|
||||
} else {
|
||||
indent += strings.Repeat(" ", 2)
|
||||
}
|
||||
}
|
||||
switch p.(type) {
|
||||
case string:
|
||||
fmt.Fprintf(v, "[%s::-]%s", valColor, p.(string))
|
||||
case []string:
|
||||
aa := p.([]string)
|
||||
for i, s := range aa {
|
||||
if i == 0 {
|
||||
fmt.Fprintf(v, "[%s::-]%s", valColor, s)
|
||||
} else {
|
||||
if level < 1 {
|
||||
fmt.Fprintf(v, "%s[%s::-]%-20s%s", indent, valColor, "", s)
|
||||
} else {
|
||||
fmt.Fprintf(v, "%s[%s::-]%-13s%s", indent, valColor, "", s)
|
||||
}
|
||||
}
|
||||
if i+1 < len(aa) {
|
||||
fmt.Fprintf(v, "\n")
|
||||
}
|
||||
}
|
||||
case map[string]interface{}:
|
||||
m := p.(map[string]interface{})
|
||||
indent += strings.Repeat(" ", 2)
|
||||
fmt.Fprintf(v, "\n")
|
||||
var i int
|
||||
for key, val := range m {
|
||||
fmt.Fprintf(v, "%s[%s::b]%-12s ", indent, keyColor, key+":")
|
||||
v.textFor(level+1, val)
|
||||
i++
|
||||
if i < len(m) {
|
||||
fmt.Fprintf(v, "\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
return indent
|
||||
}
|
||||
Loading…
Reference in New Issue