From 7a90453b8ad08f8e64107d5f1b8c26aecd81d72a Mon Sep 17 00:00:00 2001 From: elfgzp Date: Wed, 18 Nov 2020 15:10:58 +0800 Subject: [PATCH] feat: add resource filter dialog --- go.mod | 13 +- go.sum | 94 ++++------- pkg/app/action.go | 363 ++++------------------------------------- pkg/app/app.go | 38 ++--- pkg/app/dialog.go | 159 +++++++++++++----- pkg/app/format.go | 4 +- pkg/app/handler.go | 262 +++++++++++++++++++++++++++++ pkg/app/panel.go | 164 ++++++++++++++----- pkg/app/render.go | 170 +++++++++---------- pkg/app/render_plot.go | 38 +++-- pkg/app/style.go | 20 +-- pkg/gui/action.go | 21 ++- pkg/gui/gui.go | 44 ++++- pkg/gui/view.go | 21 +++ 14 files changed, 776 insertions(+), 635 deletions(-) create mode 100644 pkg/app/handler.go diff --git a/go.mod b/go.mod index 6ef1032..7697e18 100644 --- a/go.mod +++ b/go.mod @@ -2,16 +2,16 @@ module github.com/TNK-Studio/lazykube go 1.14 -replace github.com/jroimartin/gocui v0.4.0 => github.com/elfgzp/gocui v0.4.1-0.20201117132342-726b87d9ad85 +replace github.com/jroimartin/gocui v0.4.0 => github.com/elfgzp/gocui v0.4.1-0.20201118030412-21fac610f2e0 require ( - github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129 - github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 + github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect github.com/gookit/color v1.3.2 - github.com/guptarohit/asciigraph v0.5.1 + github.com/imdario/mergo v0.3.8 // indirect + github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee - github.com/jesseduffield/lazydocker v0.9.1 // indirect github.com/jroimartin/gocui v0.4.0 + github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 // indirect github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 // indirect @@ -19,12 +19,11 @@ require ( github.com/sirupsen/logrus v1.7.0 github.com/spf13/cobra v1.0.0 github.com/spkg/bom v1.0.0 - google.golang.org/appengine v1.6.5 + github.com/tebeka/strftime v0.1.5 // indirect k8s.io/api v0.19.3 k8s.io/apimachinery v0.19.3 k8s.io/cli-runtime v0.19.3 k8s.io/client-go v0.19.3 - k8s.io/klog v1.0.0 // indirect k8s.io/klog/v2 v2.2.0 k8s.io/kubectl v0.19.3 k8s.io/metrics v0.19.3 diff --git a/go.sum b/go.sum index eb4cc01..d9dba2c 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,7 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7 cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= @@ -27,19 +28,14 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c h1:YDsGA6tou+tAxVe0Dre29iSbQ8TrWdWfwOisKArJT5E= -github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c/go.mod h1:tMoSueLQlMf0TCldjrJLNIjAc5qAOIcHt5REi88/Ygo= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= -github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -49,8 +45,6 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129 h1:gfAMKE626QEuKG3si0pdTRcr/YEbBoxY+3GOH3gWvl4= -github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129/go.mod h1:u9UyCz2eTrSGy6fbupqJ54eY5c4IC8gREQ1053dK12U= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -60,8 +54,6 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do= -github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -76,18 +68,17 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v0.7.3-0.20190307005417-54dddadc7d5d h1:nIS6IF5oAIkXtSTHeRgFDtNKHpTjvZe1q3RCA0cm1rQ= -github.com/docker/docker v0.7.3-0.20190307005417-54dddadc7d5d/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elfgzp/gocui v0.4.1-0.20201117132342-726b87d9ad85 h1:xqURWew9MdwptjjqRnJqT8JD/FdnHziYj7/w9gsrPVE= github.com/elfgzp/gocui v0.4.1-0.20201117132342-726b87d9ad85/go.mod h1:1T9sKFyEfxmqNFj5KQhbIVFmcdDa8cMMUSO0kI++Y1s= +github.com/elfgzp/gocui v0.4.1-0.20201118021059-ac108d4f06d5 h1:7ff/VDGXt1k6j7RvGGbTZnnCEue258vrUQl1rfnT6cs= +github.com/elfgzp/gocui v0.4.1-0.20201118021059-ac108d4f06d5/go.mod h1:1T9sKFyEfxmqNFj5KQhbIVFmcdDa8cMMUSO0kI++Y1s= +github.com/elfgzp/gocui v0.4.1-0.20201118030412-21fac610f2e0 h1:EftbBb8ZVdoT+yW+xU/lDqBZu41blKJlU4+HFbaNdc0= +github.com/elfgzp/gocui v0.4.1-0.20201118030412-21fac610f2e0/go.mod h1:1T9sKFyEfxmqNFj5KQhbIVFmcdDa8cMMUSO0kI++Y1s= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -97,17 +88,16 @@ github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQo github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 h1:Ghm4eQYC0nEPnSJdVkTrXpu9KtoVCSo1hg7mtI7G9KU= +github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw= 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/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -136,11 +126,10 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4= -github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -176,6 +165,7 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -183,43 +173,29 @@ github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyyc github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/gookit/color v1.3.2 h1:WO8+16ZZtx+HlOb6cueziUAF8VtALZKRr/jOvuDk0X0= github.com/gookit/color v1.3.2/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 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/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/guptarohit/asciigraph v0.5.1 h1:rzRUdibSt3ff75gVGtcUXQ0dEkNgG0A20fXkA8cOMsA= -github.com/guptarohit/asciigraph v0.5.1/go.mod h1:9fYEfE5IGJGxlP1B+w8wHFy7sNZMhPtn59f0RLtpRFM= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/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/integrii/flaggy v1.4.0/go.mod h1:tnTxHeTJbah0gQ6/K0RW0J7fMUBk9MCF5blhm43LNpI= +github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4= +github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee h1:7Zi/OQlGbMz4MT2V1+prN/gv1C64NDyVb/MbJnS0ZfA= github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee/go.mod h1:Z9UKHveKXXgyo8ME7R8yxh/BUTFOK+FgfWKlhy8oOAg= -github.com/jesseduffield/gocui v0.3.0 h1:l7wH8MKR2p+ozuZdtdhQiX7szILbv50vkMk1tg2+xow= -github.com/jesseduffield/gocui v0.3.0/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw= -github.com/jesseduffield/gocui v0.3.1-0.20200201013258-57fdcf23edc5 h1:tE0w3tuL/bj1o5VMhjjE0ep6i7Fva+RYjKcMFcniJEY= -github.com/jesseduffield/gocui v0.3.1-0.20200201013258-57fdcf23edc5/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw= -github.com/jesseduffield/lazydocker v0.9.1 h1:KpykgtMzg1U0QBtG9Z3Wjt72MjyUzqal3gXI0M3R/bY= -github.com/jesseduffield/lazydocker v0.9.1/go.mod h1:2ozd6Kslvf+JqWq61YxkdSv+cEQ66IwTo/8xNJr32XE= -github.com/jesseduffield/roll v0.0.0-20190629104057-695be2e62b00/go.mod h1:cWNQljQAWYBp4wchyGfql4q2jRNZXxiE1KhVQgz+JaM= -github.com/jesseduffield/rollrus v0.0.0-20190701125922-dd028cb0bfd7/go.mod h1:VspA3aTkEo0Q7TPCLmX1uHNP+Wb4iSDX09hmTRo1QYc= -github.com/jesseduffield/termbox-go v0.0.0-20200130214842-1d31d1faa3c9 h1:iBBk1lhFwjwJw//J2m1yyz9S368GeXQTpMVACTyQMh0= -github.com/jesseduffield/termbox-go v0.0.0-20200130214842-1d31d1faa3c9/go.mod h1:anMibpZtqNxjDbxrcDEAwSdaJ37vyUeM1f/M4uekib4= -github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56 h1:33wSxJWU/f2TAozHYtJ8zqBxEnEVYM+22moLoiAkxvg= -github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56/go.mod h1:FZJBwOhE+RXz8EVZfY+xnbCw2cVOwxlK3/aIi581z/s= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jroimartin/gocui v0.4.0 h1:52jnalstgmc25FmtGcWqa0tcbMEWS6RpFLsOIO+I+E8= -github.com/jroimartin/gocui v0.4.0/go.mod h1:7i7bbj99OgFHzo7kB2zPb8pXLqMBSQegY7azfqXMkyY= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -230,14 +206,17 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +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/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 h1:0iQektZGS248WXmGIYOwRXSQhD4qn3icjMpuxwO7qlo= +github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570/go.mod h1:BLt8L9ld7wVsvEWQbuLrUZnCMnUmLZ+CGDzKtclrTlE= github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 h1:Bvq8AziQ5jFF4BHGAEDSqwPW1NJS3XshxbRCxtjFAZc= github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042/go.mod h1:TPpsiPUEh0zFL1Snz4crhMlBe60PYxRHr5oFF3rRYg0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= @@ -249,20 +228,10 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767 h1:BrhJNdEFWGuiJk/3/SwsG5Rex3zjFxYsDi2bpd7382Y= -github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767/go.mod h1:ct+byCpkFokm4J0tiuAvB8cf2ttm6GcCe89Yr25nGKg= -github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw= -github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= @@ -275,7 +244,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 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/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= @@ -284,15 +252,13 @@ github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1/go.mod h1:IuKpRQcYE github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= @@ -328,8 +294,6 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= @@ -347,7 +311,6 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/spkg/bom v1.0.0 h1:S939THe0ukL5WcTGiGqkgtaW5JW+O6ITaIlpJXTYY64= github.com/spkg/bom v1.0.0/go.mod h1:lAz2VbTuYNcvs7iaFF8WW0ufXrHShJ7ck1fYFFbVXJs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -358,9 +321,12 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tebeka/strftime v0.1.5 h1:1NQKN1NiQgkqd/2moD6ySP/5CoZQsKa1d3ZhJ44Jpmg= +github.com/tebeka/strftime v0.1.5/go.mod h1:29/OidkoWHdEKZqzyDLUyC+LmgDgdHo4WAFCDT7D/Ig= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1 h1:j2hhcujLRHAg872RWAV5yaUrEjHEObwDv3aImCaNLek= github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -439,7 +405,6 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -452,8 +417,6 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 h1:gSbV7h1NRL2G1xTg/owz62CST1oJBmxy4QpMMregXVQ= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -523,7 +486,6 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -541,12 +503,15 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +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/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +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.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -557,6 +522,7 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -572,15 +538,11 @@ k8s.io/cli-runtime v0.19.3 h1:vZUTphJIvlh7+867cXiLmyzoCAuQdukbPLIad6eEajQ= k8s.io/cli-runtime v0.19.3/go.mod h1:q+l845i5/uWzcUpCrl+L4f3XLaJi8ZeLVQ/decwty0A= k8s.io/client-go v0.19.3 h1:ctqR1nQ52NUs6LpI0w+a5U+xjYwflFwA13OJKcicMxg= k8s.io/client-go v0.19.3/go.mod h1:+eEMktZM+MG0KO+PTkci8xnbCZHvj9TqR6Q1XDUIJOM= -k8s.io/client-go v1.5.1 h1:XaX/lo2/u3/pmFau8HN+sB5C/b4dc4Dmm2eXjBH4p1E= -k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o= k8s.io/code-generator v0.19.3/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= k8s.io/component-base v0.19.3 h1:c+DzDNAQFlaoyX+yv8YuWi8xmlQvvY5DnJGbaz5U74o= k8s.io/component-base v0.19.3/go.mod h1:WhLWSIefQn8W8jxSLl5WNiR6z8oyMe/8Zywg7alOkRc= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= diff --git a/pkg/app/action.go b/pkg/app/action.go index a9ca5ad..d0a90bb 100644 --- a/pkg/app/action.go +++ b/pkg/app/action.go @@ -1,12 +1,9 @@ package app import ( - "fmt" guilib "github.com/TNK-Studio/lazykube/pkg/gui" "github.com/TNK-Studio/lazykube/pkg/kubecli" - "github.com/TNK-Studio/lazykube/pkg/log" "github.com/jroimartin/gocui" - "math" ) const ( @@ -48,134 +45,75 @@ var ( } previousLine = &guilib.Action{ - Name: "previousLine", - Key: gocui.KeyArrowUp, - Handler: previousLineHandler, - Mod: gocui.ModNone, - ReRender: true, + Name: "previousLine", + Key: gocui.KeyArrowUp, + Handler: previousLineHandler, + Mod: gocui.ModNone, } nextLine = &guilib.Action{ - Name: "nextLine", - Key: gocui.KeyArrowDown, - Handler: nextLineHandler, - Mod: gocui.ModNone, - ReRender: true, + Name: "nextLine", + Key: gocui.KeyArrowDown, + Handler: nextLineHandler, + Mod: gocui.ModNone, } actions = []*guilib.Action{ backToPreviousView, - &guilib.Action{ - Name: "scrollUp", + { + Name: "previousPage", Keys: []interface{}{ gocui.KeyPgup, + }, + Handler: previousPageHandler, + Mod: gocui.ModNone, + }, + { + Name: "nextPage", + Keys: []interface{}{ + gocui.KeyPgdn, + }, + Handler: nextPageHandler, + Mod: gocui.ModNone, + }, + { + Name: "scrollUp", + Keys: []interface{}{ gocui.MouseWheelUp, }, Handler: scrollUpHandler, Mod: gocui.ModNone, }, - &guilib.Action{ + { Name: "scrollDown", Keys: []interface{}{ - gocui.KeyPgdn, gocui.MouseWheelDown, }, Handler: scrollDownHandler, Mod: gocui.ModNone, }, - &guilib.Action{ + { Name: "scrollTop", Key: gocui.KeyHome, Handler: scrollTopHandler, Mod: gocui.ModNone, }, - &guilib.Action{ + { Name: "scrollBottom", Key: gocui.KeyEnd, Handler: scrollBottomHandler, Mod: gocui.ModNone, }, } +) - viewActionsMap = map[string][]*guilib.Action{ - navigationViewName: { - &guilib.Action{ - Name: "navigationArrowLeft", - Key: gocui.KeyArrowLeft, - Handler: navigationArrowLeftHandler, - Mod: gocui.ModNone, - ReRender: true, - }, - &guilib.Action{ - Name: "navigationArrowRight", - Key: gocui.KeyArrowRight, - Handler: navigationArrowRightHandler, - Mod: gocui.ModNone, - ReRender: true, - }, - &guilib.Action{ - Name: "navigationDown", - Key: gocui.KeyArrowDown, - Handler: func(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { - return func(*gocui.Gui, *gocui.View) error { - gui.FocusView(detailViewName, false) - return nil - } - }, - Mod: gocui.ModNone, - ReRender: true, - }, - }, - detailViewName: { - &guilib.Action{ - Name: "detailArrowUp", - Key: gocui.KeyArrowUp, - Handler: func(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { - return func(*gocui.Gui, *gocui.View) error { - gui.FocusView(navigationViewName, false) - return nil - } - }, - Mod: gocui.ModNone, - ReRender: true, - }, - }, - clusterInfoViewName: { - toNavigation, - nextCyclicView, - //previousCyclicView, - }, - namespaceViewName: { - toNavigation, - nextCyclicView, - //previousCyclicView, - previousLine, - nextLine, - newFilterAction(namespaceViewName, "namespaces"), - }, - serviceViewName: { - toNavigation, - nextCyclicView, - //previousCyclicView, - previousLine, - nextLine, - }, - deploymentViewName: { - toNavigation, - nextCyclicView, - //previousCyclicView, - previousLine, - nextLine, - }, - podViewName: { - toNavigation, - nextCyclicView, - //previousCyclicView, - previousLine, - nextLine, - }, +func setViewSelectedLine(gui *guilib.Gui, view *guilib.View, selectedLine string) error { + formatted := formatResourceName(selectedLine, 0) + if notResourceSelected(formatted) { + formatted = "" } -) + return nil +} func switchNamespace(gui *guilib.Gui, selectedNamespaceLine string) { kubecli.Cli.SetNamespace(selectedNamespaceLine) @@ -193,234 +131,5 @@ func switchNamespace(gui *guilib.Gui, selectedNamespaceLine string) { } detailView.Autoscroll = false detailView.SetOrigin(0, 0) -} - -func nextCyclicViewHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { - return func(g *gocui.Gui, view *gocui.View) error { - - currentView := gui.CurrentView() - if currentView == nil { - return nil - } - - for index, viewName := range cyclicViews { - if currentView.Name == viewName { - nextIndex := index + 1 - if nextIndex >= len(cyclicViews) { - nextIndex = 0 - } - nextViewName := cyclicViews[nextIndex] - log.Logger.Debugf("nextCyclicViewHandler - nextViewName: %s", nextViewName) - return gui.FocusView(nextViewName, true) - } - } - return nil - } -} - -func previousCyclicViewHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { - return func(g *gocui.Gui, view *gocui.View) error { - - currentView := gui.CurrentView() - if currentView == nil { - return nil - } - - for index, viewName := range cyclicViews { - if currentView.Name == viewName { - nextIndex := index - 1 - if nextIndex < 0 { - nextIndex = len(cyclicViews) - 1 - } - previousViewName := cyclicViews[nextIndex] - log.Logger.Debugf("previousCyclicViewHandler - previousViewName: %s", previousViewName) - return gui.FocusView(cyclicViews[nextIndex], true) - } - } - return nil - } -} - -func backToPreviousViewHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { - return func(g *gocui.Gui, view *gocui.View) error { - if gui.HasPreviousView() { - return gui.ReturnPreviousView() - } - - return gui.FocusView(clusterInfoViewName, false) - } -} - -func toNavigationHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { - return func(g *gocui.Gui, view *gocui.View) error { - return gui.FocusView(navigationViewName, true) - } -} - -func navigationArrowRightHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { - return func(g *gocui.Gui, view *gocui.View) error { - options := viewNavigationMap[activeView.Name] - if navigationIndex+1 >= len(options) { - return nil - } - switchNavigation(navigationIndex + 1) - return nil - } -} - -func navigationArrowLeftHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { - return func(g *gocui.Gui, view *gocui.View) error { - if navigationIndex-1 < 0 { - return gui.ReturnPreviousView() - } - switchNavigation(navigationIndex - 1) - return nil - } -} - -func scrollUpHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { - return func(g *gocui.Gui, v *gocui.View) error { - v.Autoscroll = false - ox, oy := v.Origin() - newOy := int(math.Max(0, float64(oy-2))) - return v.SetOrigin(ox, newOy) - } -} - -func scrollDownHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { - return func(g *gocui.Gui, v *gocui.View) error { - v.Autoscroll = false - ox, oy := v.Origin() - - reservedLines := 0 - _, sizeY := v.Size() - reservedLines = sizeY - - totalLines := len(v.ViewBufferLines()) - if oy+reservedLines >= totalLines { - v.Autoscroll = true - return nil - } - - return v.SetOrigin(ox, oy+2) - } -} - -func scrollTopHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { - return func(g *gocui.Gui, v *gocui.View) error { - v.Autoscroll = false - ox, _ := v.Origin() - return v.SetOrigin(ox, 0) - } -} - -func scrollBottomHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { - return func(g *gocui.Gui, v *gocui.View) error { - totalLines := len(v.ViewBufferLines()) - if totalLines == 0 { - return nil - } - _, vy := v.Size() - if totalLines <= vy { - return nil - } - - ox, _ := v.Origin() - v.Autoscroll = true - return v.SetOrigin(ox, totalLines-1) - } -} - -func viewLineClickHandler(gui *guilib.Gui, view *guilib.View, cy int, lineString string) error { - detailView, _ := gui.GetView(detailViewName) - if detailView != nil { - detailView.SetOrigin(0, 0) - } - - if cy == 0 { - selected := formatSelectedName(lineString, 0) - if notResourceSelected(selected) { - log.Logger.Debugf("viewLineClickHandler - view: '%s' cy == 0, setViewSelectedLine(gui, view, \"\")", view.Name) - return setViewSelectedLine(gui, view, "") - } - } - - log.Logger.Debugf("viewLineClickHandler - view: '%s' setViewSelectedLine(gui, %s, \"%s\")", view.Name, lineString) - return setViewSelectedLine(gui, view, lineString) -} - -func previousLineHandler(gui *guilib.Gui) func(gui *gocui.Gui, view *gocui.View) error { - return func(g *gocui.Gui, v *gocui.View) error { - currentView := gui.CurrentView() - if currentView == nil { - return nil - } - - _, cy := v.Cursor() - ox, oy := v.Origin() - var lineStr string - var err error - if cy-1 < 0 { - if err := v.SetOrigin(ox, int(math.Max(0, float64(oy-1)))); err != nil { - return err - } - v.MoveCursor(0, -1, false) - _, newCy := v.Cursor() - lineStr, err = v.Line(newCy) - if err != nil { - log.Logger.Warningf("previousLineHandler - v.Line(cy - 1)", cy) - } - } else { - lineStr, err = v.Line(cy - 1) - if err != nil { - log.Logger.Warningf("previousLineHandler - v.Line(cy - 1)", cy) - } - v.MoveCursor(0, -1, false) - } - - return setViewSelectedLine(gui, currentView, lineStr) - } -} - -func nextLineHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { - return func(g *gocui.Gui, v *gocui.View) error { - currentView := gui.CurrentView() - if currentView == nil { - return nil - } - - _, cy := v.Cursor() - lineStr, err := v.Line(cy + 1) - if err != nil { - log.Logger.Warningf("nextLineHandler - v.Line(%d + 1)", cy) - } - v.MoveCursor(0, 1, false) - if currentView.Name == namespaceViewName { - namespace := formatSelectedNamespace(lineStr) - log.Logger.Debugf("nextLineHandler - switch namespace to %s", namespace) - switchNamespace(gui, namespace) - } - - return setViewSelectedLine(gui, currentView, lineStr) - } -} - -func newFilterAction(viewName string, resourceName string) *guilib.Action { - return &guilib.Action{ - Name: fmt.Sprintf("%sFilterAction", viewName), - Key: gocui.KeyF4, - Handler: func(gui *guilib.Gui) func(g *gocui.Gui, v *gocui.View) error { - return func(g *gocui.Gui, v *gocui.View) error { - view, err := gui.GetView(viewName) - if err != nil { - return err - } - if err := newFilterDialog(fmt.Sprintf("Input to filter %s", resourceName), gui, view); err != nil { - return err - } - return nil - } - }, - Mod: gocui.ModNone, - } + gui.ReRenderViews(namespaceViewName, serviceViewName, deploymentViewName, podViewName, navigationViewName, detailViewName) } diff --git a/pkg/app/app.go b/pkg/app/app.go index e5895aa..c741437 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -2,21 +2,21 @@ package app import ( "github.com/TNK-Studio/lazykube/pkg/config" - "github.com/TNK-Studio/lazykube/pkg/gui" + guilib "github.com/TNK-Studio/lazykube/pkg/gui" "github.com/TNK-Studio/lazykube/pkg/utils" "github.com/jroimartin/gocui" ) type App struct { - ClusterInfo *gui.View - Namespace *gui.View - Service *gui.View - Deployment *gui.View - Pod *gui.View - Navigation *gui.View - Detail *gui.View - Option *gui.View - Gui *gui.Gui + ClusterInfo *guilib.View + Namespace *guilib.View + Service *guilib.View + Deployment *guilib.View + Pod *guilib.View + Navigation *guilib.View + Detail *guilib.View + Option *guilib.View + Gui *guilib.Gui } func NewApp() *App { @@ -39,7 +39,7 @@ func NewApp() *App { Mouse: true, InputEsc: true, } - app.Gui = gui.NewGui( + app.Gui = guilib.NewGui( conf, app.ClusterInfo, app.Namespace, @@ -52,19 +52,11 @@ func NewApp() *App { ) app.Gui.OnRender = app.OnRender app.Gui.OnRenderOptions = app.OnRenderOptions + app.Gui.Actions = actions return app } func (app *App) Run() { - for _, act := range actions { - app.Gui.BindAction("", act) - } - - for viewName, actArr := range viewActionsMap { - for _, act := range actArr { - app.Gui.BindAction(viewName, act) - } - } app.Gui.Run() } @@ -72,7 +64,7 @@ func (app *App) Stop() { app.Gui.Close() } -func (app *App) OnRender(gui *gui.Gui) error { +func (app *App) OnRender(gui *guilib.Gui) error { if gui.MaxHeight() < 28 { for _, viewName := range functionViews { if _, err := gui.SetViewOnTop(viewName); err != nil { @@ -89,13 +81,13 @@ func (app *App) OnRender(gui *gui.Gui) error { return nil } -func (app *App) OnRenderOptions(gui *gui.Gui) error { +func (app *App) OnRenderOptions(gui *guilib.Gui) error { return gui.RenderString( app.Option.Name, utils.OptionsMapToString( map[string]string{ "← → ↑ ↓": "navigate", - "Ctrl+c": "close", + "Ctrl+c": "exit", "Esc": "back", "PgUp/PgDn": "scroll", "Home/End": "top/bottom", diff --git a/pkg/app/dialog.go b/pkg/app/dialog.go index 1a9abfc..924991b 100644 --- a/pkg/app/dialog.go +++ b/pkg/app/dialog.go @@ -4,14 +4,16 @@ import ( "fmt" guilib "github.com/TNK-Studio/lazykube/pkg/gui" "github.com/TNK-Studio/lazykube/pkg/log" + "github.com/TNK-Studio/lazykube/pkg/utils" "github.com/jroimartin/gocui" "strings" ) const ( - filterInputViewName = "filterInput" - filteredViewName = "filtered" - filteredNoResource = "No Resource." + filterInputViewName = "filterInput" + filterInputValueStateKey = "value" + filteredViewName = "filtered" + filteredNoResource = "No Resource." ) var ( @@ -41,21 +43,42 @@ var ( }, Mod: gocui.ModNone, } -) -func newFilterDialog(title string, gui *guilib.Gui, resourceView *guilib.View) error { - resourceList := resourceView.ViewBufferLines() - if len(resourceList) == 0 { - return nil + filteredNextLine = &guilib.Action{ + Name: "filteredNextLine", + Keys: []interface{}{ + gocui.KeyArrowDown, + }, + Handler: nextLineHandler, + Mod: gocui.ModNone, + } + + filteredPreviousLine = &guilib.Action{ + Name: "filteredPreviousLine", + Keys: []interface{}{ + gocui.KeyArrowUp, + }, + Handler: func(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { + return func(g *gocui.Gui, v *gocui.View) error { + _, oy := v.Origin() + _, cy := v.Cursor() + if oy == 0 && cy-1 < 0 { + return gui.FocusView(filterInputViewName, false) + } + return previousLineHandler(gui)(g, v) + } + }, + Mod: gocui.ModNone, } +) +func NewConfirmFilterInput(resourceViewName string) *guilib.Action { confirmFilterInput := &guilib.Action{ Name: "confirmFilterInput", Key: gocui.KeyEnter, Handler: func(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { return func(*gocui.Gui, *gocui.View) error { filteredView, err := gui.GetView(filteredViewName) - if err != nil { return err } @@ -66,16 +89,21 @@ func newFilterDialog(title string, gui *guilib.Gui, resourceView *guilib.View) e return nil } + resourceView, err := gui.GetView(resourceViewName) + if err != nil { + return err + } + y := resourceView.WhichLine(filtered) if y < 0 { if err := resourceView.ResetCursorOrigin(); err != nil { return err } } else { - if err := setViewSelectedLine(gui, resourceView, filtered); err != nil { + if err := resourceView.SetOrigin(0, y); err != nil { return err } - if err := resourceView.SetOrigin(0, y); err != nil { + if err := resourceView.SetCursor(0, 0); err != nil { return err } } @@ -85,12 +113,17 @@ func newFilterDialog(title string, gui *guilib.Gui, resourceView *guilib.View) e if err := gui.ReturnPreviousView(); err != nil { return err } - gui.ReRender() return nil } }, Mod: gocui.ModNone, } + return confirmFilterInput +} + +func newFilterDialog(title string, gui *guilib.Gui, resourceViewName string) error { + + confirmFilterInput := NewConfirmFilterInput(resourceViewName) filterInput := &guilib.View{ Name: filterInputViewName, @@ -113,6 +146,12 @@ func newFilterDialog(title string, gui *guilib.Gui, resourceView *guilib.View) e toFilteredView, confirmFilterInput, }, + OnRender: func(gui *guilib.Gui, view *guilib.View) error { + gui.Config.Cursor = true + gui.Configure() + return nil + }, + OnRenderOptions: filterDialogRenderOption, OnFocusLost: func(gui *guilib.Gui, view *guilib.View) error { gui.Config.Cursor = false gui.Configure() @@ -129,22 +168,60 @@ func newFilterDialog(title string, gui *guilib.Gui, resourceView *guilib.View) e if len(bufferLines) > 0 { value = view.ViewBufferLines()[0] } - filteredView, err := gui.GetView(filteredViewName) - if err != nil { - log.Logger.Warningf("OnEditedChange - gui.GetView(%s) error %s", filteredViewName, err) + if err := view.State.Set(filterInputValueStateKey, value); err != nil { + log.Logger.Warningf("OnEditedChange - view.State.Set(filterInputValueStateKey,%s) error %s", value, err) return } - filteredView.Clear() - if err := filteredView.ResetCursorOrigin(); err != nil { - log.Logger.Warningf("OnEditedChange filteredView.ResetCursorOrigin() error %s", err) + filteredView, err := gui.GetView(filteredViewName) + if err != nil { + log.Logger.Warningf("filteredView - gui.GetView(filteredViewName) error %s", err) return } + filteredView.ReRender() + }, + } + filtered := &guilib.View{ + Name: filteredViewName, + Clickable: true, + CanNotReturn: true, + AlwaysOnTop: true, + Highlight: true, + SelFgColor: gocui.ColorBlack, + SelBgColor: gocui.ColorWhite, + Actions: []*guilib.Action{ + toFilterInputView, + confirmFilterInput, + filteredNextLine, + filteredPreviousLine, + }, + OnRender: func(gui *guilib.Gui, view *guilib.View) error { + value := "" + val, _ := filterInput.State.Get(filterInputValueStateKey) + if val != nil { + value = val.(string) + } + + view.Clear() + if err := view.ResetCursorOrigin(); err != nil { + log.Logger.Warningf("OnRender - view %s view.ResetCursorOrigin() error %s", filterInputViewName, err) + return err + } + + resourceView, err := gui.GetView(resourceViewName) + if err != nil { + return err + } + + resourceList := resourceView.ViewBufferLines() + if len(resourceList) == 0 { + return nil + } if value == "" { - fmt.Fprint(filteredView, strings.Join(resourceList[1:], "\n")) - return + fmt.Fprint(view, strings.Join(resourceList[1:], "\n")) + return nil } filtered := make([]string, 0) @@ -156,30 +233,18 @@ func newFilterDialog(title string, gui *guilib.Gui, resourceView *guilib.View) e } if len(filtered) == 0 { - fmt.Fprint(filteredView, filteredNoResource) - return + fmt.Fprint(view, filteredNoResource) + return nil } - fmt.Fprint(filteredView, strings.Join(filtered, "\n")) - }, - } - filtered := &guilib.View{ - Name: filteredViewName, - Clickable: true, - CanNotReturn: true, - AlwaysOnTop: true, - Highlight: true, - SelFgColor: gocui.ColorBlack, - SelBgColor: gocui.ColorWhite, - Actions: []*guilib.Action{ - toFilterInputView, - confirmFilterInput, + fmt.Fprint(view, strings.Join(filtered, "\n")) + return nil }, - OnRender: func(gui *guilib.Gui, view *guilib.View) error { - fmt.Fprint(view, strings.Join(resourceList[1:], "\n")) + OnRenderOptions: filterDialogRenderOption, + OnFocusLost: filterDialogFocusLost, + OnLineClick: func(gui *guilib.Gui, view *guilib.View, cy int, lineString string) error { return nil }, - OnFocusLost: filterDialogFocusLost, DimensionFunc: func(gui *guilib.Gui, view *guilib.View) (int, int, int, int) { maxWidth, maxHeight := gui.Size() quarterWidth, quarterHeight := maxWidth/4, maxHeight/4 @@ -210,6 +275,22 @@ func newFilterDialog(title string, gui *guilib.Gui, resourceView *guilib.View) e return nil } +func filterDialogRenderOption(gui *guilib.Gui, view *guilib.View) error { + return gui.RenderString( + optionViewName, + utils.OptionsMapToString( + map[string]string{ + "← → ↑ ↓": "navigate", + "Ctrl+c": "exit", + "Esc": "close dialog", + "PgUp/PgDn": "scroll", + "Home/End": "top/bottom", + "Tab": "next panel", + "Enter": "confirm", + }), + ) +} + func filterDialogFocusLost(gui *guilib.Gui, view *guilib.View) error { currentView := gui.CurrentView() diff --git a/pkg/app/format.go b/pkg/app/format.go index 271ad46..cb36009 100644 --- a/pkg/app/format.go +++ b/pkg/app/format.go @@ -6,10 +6,10 @@ import ( ) func formatSelectedNamespace(selected string) string { - return formatSelectedName(selected, 0) + return formatResourceName(selected, 0) } -func formatSelectedName(selected string, index int) string { +func formatResourceName(selected string, index int) string { if selected == "" { return "" } diff --git a/pkg/app/handler.go b/pkg/app/handler.go new file mode 100644 index 0000000..57de88a --- /dev/null +++ b/pkg/app/handler.go @@ -0,0 +1,262 @@ +package app + +import ( + "fmt" + guilib "github.com/TNK-Studio/lazykube/pkg/gui" + "github.com/TNK-Studio/lazykube/pkg/log" + "github.com/jroimartin/gocui" + "math" +) + +func nextCyclicViewHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { + return func(g *gocui.Gui, view *gocui.View) error { + + currentView := gui.CurrentView() + if currentView == nil { + return nil + } + + for index, viewName := range cyclicViews { + if currentView.Name == viewName { + nextIndex := index + 1 + if nextIndex >= len(cyclicViews) { + nextIndex = 0 + } + nextViewName := cyclicViews[nextIndex] + log.Logger.Debugf("nextCyclicViewHandler - nextViewName: %s", nextViewName) + return gui.FocusView(nextViewName, true) + } + } + return nil + } +} + +func previousCyclicViewHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { + return func(g *gocui.Gui, view *gocui.View) error { + + currentView := gui.CurrentView() + if currentView == nil { + return nil + } + + for index, viewName := range cyclicViews { + if currentView.Name == viewName { + nextIndex := index - 1 + if nextIndex < 0 { + nextIndex = len(cyclicViews) - 1 + } + previousViewName := cyclicViews[nextIndex] + log.Logger.Debugf("previousCyclicViewHandler - previousViewName: %s", previousViewName) + return gui.FocusView(cyclicViews[nextIndex], true) + } + } + return nil + } +} + +func backToPreviousViewHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { + return func(g *gocui.Gui, view *gocui.View) error { + if gui.HasPreviousView() { + return gui.ReturnPreviousView() + } + + return gui.FocusView(clusterInfoViewName, false) + } +} + +func toNavigationHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { + return func(g *gocui.Gui, view *gocui.View) error { + return gui.FocusView(navigationViewName, true) + } +} + +func navigationArrowRightHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { + return func(g *gocui.Gui, view *gocui.View) error { + options := viewNavigationMap[activeView.Name] + if navigationIndex+1 >= len(options) { + return nil + } + switchNavigation(navigationIndex + 1) + gui.ReRenderViews(navigationViewName, detailViewName) + return nil + } +} + +func navigationArrowLeftHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { + return func(g *gocui.Gui, view *gocui.View) error { + if navigationIndex-1 < 0 { + return gui.ReturnPreviousView() + } + switchNavigation(navigationIndex - 1) + gui.ReRenderViews(navigationViewName, detailViewName) + return nil + } +} + +func nextPageHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { + return func(g *gocui.Gui, v *gocui.View) error { + v.Autoscroll = false + ox, oy := v.Origin() + _, height := v.Size() + newOy := int(math.Min(float64(len(v.ViewBufferLines())), float64(oy+height))) + return v.SetOrigin(ox, newOy) + } +} + +func previousPageHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { + return func(g *gocui.Gui, v *gocui.View) error { + v.Autoscroll = false + ox, oy := v.Origin() + _, height := v.Size() + newOy := int(math.Max(0, float64(oy-height))) + return v.SetOrigin(ox, newOy) + } +} + +func scrollUpHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { + return func(g *gocui.Gui, v *gocui.View) error { + v.Autoscroll = false + ox, oy := v.Origin() + newOy := int(math.Max(0, float64(oy-2))) + return v.SetOrigin(ox, newOy) + } +} + +func scrollDownHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { + return func(g *gocui.Gui, v *gocui.View) error { + v.Autoscroll = false + ox, oy := v.Origin() + + reservedLines := 0 + _, sizeY := v.Size() + reservedLines = sizeY + + totalLines := len(v.ViewBufferLines()) + if oy+reservedLines >= totalLines { + v.Autoscroll = true + return nil + } + + return v.SetOrigin(ox, oy+2) + } +} + +func scrollTopHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { + return func(g *gocui.Gui, v *gocui.View) error { + v.Autoscroll = false + ox, _ := v.Origin() + return v.SetOrigin(ox, 0) + } +} + +func scrollBottomHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { + return func(g *gocui.Gui, v *gocui.View) error { + totalLines := len(v.ViewBufferLines()) + if totalLines == 0 { + return nil + } + _, vy := v.Size() + if totalLines <= vy { + return nil + } + + ox, _ := v.Origin() + v.Autoscroll = true + return v.SetOrigin(ox, totalLines-1) + } +} + +func viewLineClickHandler(gui *guilib.Gui, view *guilib.View, cy int, lineString string) error { + detailView, _ := gui.GetView(detailViewName) + if detailView != nil { + detailView.SetOrigin(0, 0) + } + + if cy == 0 { + selected := formatResourceName(lineString, 0) + if notResourceSelected(selected) { + log.Logger.Debugf("viewLineClickHandler - view: '%s' cy == 0, setViewSelectedLine(gui, view, \"\")", view.Name) + return setViewSelectedLine(gui, view, "") + } + } + + log.Logger.Debugf("viewLineClickHandler - view: '%s' setViewSelectedLine(gui, %s, \"%s\")", view.Name, lineString) + return setViewSelectedLine(gui, view, lineString) +} + +func previousLineHandler(gui *guilib.Gui) func(gui *gocui.Gui, view *gocui.View) error { + return func(g *gocui.Gui, v *gocui.View) error { + currentView := gui.CurrentView() + if currentView == nil { + return nil + } + + _, height := v.Size() + cx, cy := v.Cursor() + ox, oy := v.Origin() + + if cy-1 <= 0 && oy-1 > 0 { + v.SetOrigin(ox, int(math.Max(0, float64(oy-height+1)))) + v.SetCursor(cx, height-1) + return nil + } + + v.MoveCursor(0, -1, false) + return nil + } +} + +func nextLineHandler(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { + return func(g *gocui.Gui, v *gocui.View) error { + currentView := gui.CurrentView() + if currentView == nil { + return nil + } + + _, height := v.Size() + cx, cy := v.Cursor() + + if cy+1 >= height-1 { + ox, oy := v.Origin() + v.SetOrigin(ox, oy+height-1) + v.SetCursor(cx, 0) + return nil + } + + v.MoveCursor(0, 1, false) + return nil + } +} + +func newFilterAction(viewName string, resourceName string) *guilib.Action { + return &guilib.Action{ + Name: fmt.Sprintf("%sFilterAction", viewName), + Key: gocui.KeyF4, + Handler: func(gui *guilib.Gui) func(g *gocui.Gui, v *gocui.View) error { + return func(g *gocui.Gui, v *gocui.View) error { + if err := newFilterDialog(fmt.Sprintf("Input to filter %s", resourceName), gui, viewName); err != nil { + return err + } + return nil + } + }, + Mod: gocui.ModNone, + } +} + +func viewCursorChangeHandler(gui *guilib.Gui, view *guilib.View, x, y int) error { + selectedLine, _ := view.Line(y) + if err := setViewSelectedLine(gui, view, selectedLine); err != nil { + return err + } + gui.ReRenderViews(view.Name, navigationViewName, detailViewName) + return nil +} + +func viewSelectedLineChangeHandler(gui *guilib.Gui, view *guilib.View, selectedLine string) error { + if err := setViewSelectedLine(gui, view, selectedLine); err != nil { + return err + } + gui.ReRenderViews(view.Name, navigationViewName, detailViewName) + return nil +} diff --git a/pkg/app/panel.go b/pkg/app/panel.go index cf9a37b..23dcd05 100644 --- a/pkg/app/panel.go +++ b/pkg/app/panel.go @@ -26,15 +26,22 @@ var ( }, LowerRightPointYFunc: reactiveHeight, OnRender: renderClusterInfo, + Actions: []*guilib.Action{ + toNavigation, + nextCyclicView, + //previousCyclicView, + }, } Deployment = &guilib.View{ - Name: deploymentViewName, - Title: "Deployments", - FgColor: gocui.ColorDefault, - Clickable: true, - OnRender: deploymentRender, - OnLineClick: viewLineClickHandler, + Name: deploymentViewName, + Title: "Deployments", + FgColor: gocui.ColorDefault, + Clickable: true, + Highlight: true, + SelFgColor: gocui.ColorGreen, + OnRender: deploymentRender, + OnSelectedLineChange: viewSelectedLineChangeHandler, OnFocus: func(gui *guilib.Gui, view *guilib.View) error { if err := onFocusClearSelected(gui, view); err != nil { return err @@ -46,6 +53,14 @@ var ( reactiveHeight, migrateTopFunc, ), + Actions: []*guilib.Action{ + toNavigation, + nextCyclicView, + //previousCyclicView, + previousLine, + nextLine, + newFilterAction(deploymentViewName, "deployments"), + }, } Navigation = &guilib.View{ @@ -59,6 +74,33 @@ var ( return leftSideWidth(gui.MaxWidth()) + 1, 0, gui.MaxWidth() - 1, 2 }, OnRender: navigationRender, + Actions: []*guilib.Action{ + { + Name: "navigationArrowLeft", + Key: gocui.KeyArrowLeft, + Handler: navigationArrowLeftHandler, + Mod: gocui.ModNone, + }, + { + Name: "navigationArrowRight", + Key: gocui.KeyArrowRight, + Handler: navigationArrowRightHandler, + Mod: gocui.ModNone, + }, + { + Name: "navigationDown", + Key: gocui.KeyArrowDown, + Handler: func(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { + return func(*gocui.Gui, *gocui.View) error { + if err := gui.FocusView(detailViewName, false); err != nil { + return err + } + return nil + } + }, + Mod: gocui.ModNone, + }, + }, } Detail = &guilib.View{ @@ -70,14 +112,43 @@ var ( DimensionFunc: func(gui *guilib.Gui, view *guilib.View) (int, int, int, int) { return leftSideWidth(gui.MaxWidth()) + 1, 2, gui.MaxWidth() - 1, gui.MaxHeight() - 2 }, + Actions: []*guilib.Action{ + { + Name: "detailArrowUp", + Key: gocui.KeyArrowUp, + Handler: func(gui *guilib.Gui) func(*gocui.Gui, *gocui.View) error { + return func(*gocui.Gui, *gocui.View) error { + gui.FocusView(navigationViewName, false) + return nil + } + }, + Mod: gocui.ModNone, + }, + }, } Namespace = &guilib.View{ - Name: namespaceViewName, - Title: "Namespaces", - Clickable: true, - OnRender: namespaceRender, - OnLineClick: viewLineClickHandler, + Name: namespaceViewName, + Title: "Namespaces", + Clickable: true, + OnRender: namespaceRender, + //OnLineClick: viewLineClickHandler, + OnSelectedLineChange: func(gui *guilib.Gui, view *guilib.View, selectedLine string) error { + formatted := formatResourceName(selectedLine, 0) + if notResourceSelected(formatted) { + formatted = "" + } + + if formatted == "" { + switchNamespace(gui, "") + return nil + } else { + switchNamespace(gui, formatSelectedNamespace(selectedLine)) + } + return nil + }, + Highlight: true, + SelFgColor: gocui.ColorGreen, OnFocus: func(gui *guilib.Gui, view *guilib.View) error { if err := onFocusClearSelected(gui, view); err != nil { return err @@ -90,6 +161,14 @@ var ( reactiveHeight, migrateTopFunc, ), + Actions: []*guilib.Action{ + toNavigation, + nextCyclicView, + //previousCyclicView, + previousLine, + nextLine, + newFilterAction(namespaceViewName, "namespaces"), + }, } Option = &guilib.View{ @@ -103,11 +182,13 @@ var ( } Pod = &guilib.View{ - Name: podViewName, - Title: "Pods", - Clickable: true, - OnRender: podRender, - OnLineClick: viewLineClickHandler, + Name: podViewName, + Title: "Pods", + Clickable: true, + OnRender: podRender, + OnSelectedLineChange: viewSelectedLineChangeHandler, + Highlight: true, + SelFgColor: gocui.ColorGreen, OnFocus: func(gui *guilib.Gui, view *guilib.View) error { if err := onFocusClearSelected(gui, view); err != nil { return err @@ -120,14 +201,24 @@ var ( reactiveHeight, migrateTopFunc, ), + Actions: []*guilib.Action{ + toNavigation, + nextCyclicView, + //previousCyclicView, + previousLine, + nextLine, + newFilterAction(podViewName, "pods"), + }, } Service = &guilib.View{ - Name: serviceViewName, - Title: "Services", - Clickable: true, - OnRender: serviceRender, - OnLineClick: viewLineClickHandler, + Name: serviceViewName, + Title: "Services", + Clickable: true, + OnRender: serviceRender, + OnSelectedLineChange: viewSelectedLineChangeHandler, + Highlight: true, + SelFgColor: gocui.ColorGreen, OnFocus: func(gui *guilib.Gui, view *guilib.View) error { if err := onFocusClearSelected(gui, view); err != nil { return err @@ -139,28 +230,13 @@ var ( reactiveHeight, migrateTopFunc, ), + Actions: []*guilib.Action{ + toNavigation, + nextCyclicView, + //previousCyclicView, + previousLine, + nextLine, + newFilterAction(serviceViewName, "services"), + }, } ) - -func setViewSelectedLine(gui *guilib.Gui, view *guilib.View, selectedLine string) error { - formatted := formatSelectedName(selectedLine, 0) - if formatted == "NAME" || formatted == "NAMESPACE" { - formatted = "" - } - - if formatted == "" { - if err := view.State.Set(selectedViewLine, nil); err != nil { - return err - } - return nil - } - - if view.Name == namespaceViewName { - switchNamespace(gui, formatSelectedNamespace(selectedLine)) - } - - if err := view.State.Set(selectedViewLine, selectedLine); err != nil { - return err - } - return nil -} diff --git a/pkg/app/render.go b/pkg/app/render.go index 2ac495b..fc92301 100644 --- a/pkg/app/render.go +++ b/pkg/app/render.go @@ -17,7 +17,7 @@ import ( const ( OptSeparator = " " navigationPathJoin = " + " - logsTail = "200" + logsTail = "500" ) var ( @@ -59,7 +59,7 @@ var ( ) func notResourceSelected(selectedName string) bool { - if selectedName == "" || selectedName == "NAME" || selectedName == "NAMESPACE" { + if selectedName == "" || selectedName == "NAME" || selectedName == "NAMESPACE" || selectedName == "No" { return true } return false @@ -78,6 +78,7 @@ func navigationPath(args ...string) string { func switchNavigation(index int) string { Detail.SetOrigin(0, 0) + Detail.Clear() if index < 0 { return "" } @@ -172,6 +173,8 @@ func navigationOnClick(gui *guilib.Gui, view *guilib.View) error { optionIndex := i / 2 log.Logger.Debugf("navigationOnClick - cx %d in selection(%d)[%d, %d]", cx, optionIndex, left, right) selected = switchNavigation(optionIndex) + view.ReRender() + Detail.ReRender() break } } @@ -226,48 +229,37 @@ func topNodesRender(gui *guilib.Gui, view *guilib.View) error { func namespaceRender(gui *guilib.Gui, view *guilib.View) error { view.Clear() - streams := newStream() - kubecli.Cli.Get(streams, "namespaces").Run() - renderHighlightSelected(view, streamToString(streams)) + kubecli.Cli.Get(viewStreams(view), "namespaces").Run() return nil } func serviceRender(gui *guilib.Gui, view *guilib.View) error { view.Clear() - streams := newStream() if kubecli.Cli.Namespace() == "" { - kubecli.Cli.Get(streams, "services").SetFlag("all-namespaces", "true").Run() - renderHighlightSelected(view, streamToString(streams)) + kubecli.Cli.Get(viewStreams(view), "services").SetFlag("all-namespaces", "true").Run() return nil } - kubecli.Cli.Get(streams, "services").Run() - renderHighlightSelected(view, streamToString(streams)) + kubecli.Cli.Get(viewStreams(view), "services").Run() return nil } func deploymentRender(gui *guilib.Gui, view *guilib.View) error { view.Clear() - streams := newStream() if kubecli.Cli.Namespace() == "" { - kubecli.Cli.Get(streams, "deployments").SetFlag("all-namespaces", "true").Run() - renderHighlightSelected(view, streamToString(streams)) + kubecli.Cli.Get(viewStreams(view), "deployments").SetFlag("all-namespaces", "true").Run() return nil } kubecli.Cli.Get(viewStreams(view), "deployments").Run() - renderHighlightSelected(view, streamToString(streams)) return nil } func podRender(gui *guilib.Gui, view *guilib.View) error { view.Clear() - streams := newStream() if kubecli.Cli.Namespace() == "" { - kubecli.Cli.Get(streams, "pods").SetFlag("all-namespaces", "true").SetFlag("output", "wide").Run() - renderHighlightSelected(view, streamToString(streams)) + kubecli.Cli.Get(viewStreams(view), "pods").SetFlag("all-namespaces", "true").SetFlag("output", "wide").Run() return nil } - kubecli.Cli.Get(streams, "pods").SetFlag("output", "wide").Run() - renderHighlightSelected(view, streamToString(streams)) + kubecli.Cli.Get(viewStreams(view), "pods").SetFlag("output", "wide").Run() return nil } @@ -295,17 +287,6 @@ func streamToString(streams genericclioptions.IOStreams) string { return buf.String() } -func renderHighlightSelected(view *guilib.View, content string) { - selected, _ := view.State.Get(selectedViewLine) - if selected != nil { - highlightString := selected.(string) - content = strings.Replace(content, highlightString, color.Green.Sprint(highlightString), 1) - fmt.Fprint(view, content) - return - } - fmt.Fprint(view, content) -} - func showPleaseSelected(view *guilib.View, name string) { fmt.Fprintf(view, "Please select a %s. ", name) } @@ -316,19 +297,13 @@ func namespaceConfigRender(gui *guilib.Gui, view *guilib.View) error { if err != nil { return nil } - selected, _ := namespaceView.State.Get(selectedViewLine) - if selected != nil { - namespace := formatSelectedNamespace(selected.(string)) - if namespace == "" { - showPleaseSelected(view, namespaceViewName) - return nil - } - - kubecli.Cli.Get(viewStreams(view), "namespaces", namespace).SetFlag("output", "yaml").Run() + namespace := formatSelectedNamespace(namespaceView.SelectedLine) + if notResourceSelected(namespace) { + showPleaseSelected(view, namespaceViewName) return nil } - showPleaseSelected(view, namespaceViewName) + kubecli.Cli.Get(viewStreams(view), "namespaces", namespace).SetFlag("output", "yaml").Run() return nil } @@ -337,16 +312,18 @@ func configRender(gui *guilib.Gui, view *guilib.View) error { if activeView == nil { return nil } - if activeView == Namespace { - return namespaceConfigRender(gui, view) - } + namespaceView, err := gui.GetView(namespaceViewName) if err != nil { return nil } - selectedNamespace, _ := namespaceView.State.Get(selectedViewLine) - selected, _ := activeView.State.Get(selectedViewLine) + if activeView == namespaceView { + return namespaceConfigRender(gui, view) + } + + namespace := formatSelectedNamespace(namespaceView.SelectedLine) + selected := activeView.SelectedLine resource := "" switch activeView.Name { case serviceViewName: @@ -364,29 +341,29 @@ func configRender(gui *guilib.Gui, view *guilib.View) error { return nil } - if selected == nil { + if selected == "" { showPleaseSelected(view, resource) return nil } - if selectedNamespace != nil { - selectedName := formatSelectedName(selected.(string), 0) - if notResourceSelected(selectedName) { + if !notResourceSelected(namespace) { + resourceName := formatResourceName(selected, 0) + if notResourceSelected(resourceName) { showPleaseSelected(view, resource) return nil } - kubecli.Cli.Get(viewStreams(view), resource, selectedName).SetFlag("output", "yaml").Run() + kubecli.Cli.Get(viewStreams(view), resource, resourceName).SetFlag("output", "yaml").Run() return nil } - namespace := formatSelectedName(selected.(string), 0) - selectedName := formatSelectedName(selected.(string), 1) - if notResourceSelected(selectedName) { + namespace = formatResourceName(selected, 0) + resourceName := formatResourceName(selected, 1) + if notResourceSelected(resourceName) { showPleaseSelected(view, resource) return nil } - kubecli.Cli.WithNamespace(namespace).Get(viewStreams(view), resource, selectedName).SetFlag("output", "yaml").Run() + kubecli.Cli.WithNamespace(namespace).Get(viewStreams(view), resource, resourceName).SetFlag("output", "yaml").Run() return nil } @@ -403,8 +380,8 @@ func describeRender(gui *guilib.Gui, view *guilib.View) error { return nil } - selectedNamespace, _ := namespaceView.State.Get(selectedViewLine) - selected, _ := activeView.State.Get(selectedViewLine) + namespace := formatSelectedNamespace(namespaceView.SelectedLine) + selected := activeView.SelectedLine resource := "" switch activeView.Name { case deploymentViewName: @@ -419,29 +396,29 @@ func describeRender(gui *guilib.Gui, view *guilib.View) error { return nil } - if selected == nil { + if notResourceSelected(selected) { showPleaseSelected(view, resource) return nil } - if selectedNamespace != nil { - selectedName := formatSelectedName(selected.(string), 0) - if notResourceSelected(selectedName) { + if !notResourceSelected(namespace) { + resourceName := formatResourceName(selected, 0) + if notResourceSelected(resourceName) { showPleaseSelected(view, resource) return nil } - kubecli.Cli.Describe(viewStreams(view), resource, selectedName).Run() + kubecli.Cli.Describe(viewStreams(view), resource, resourceName).Run() return nil } - namespace := formatSelectedName(selected.(string), 0) - selectedName := formatSelectedName(selected.(string), 1) - if notResourceSelected(selectedName) { + namespace = formatResourceName(selected, 0) + resourceName := formatResourceName(selected, 1) + if notResourceSelected(resourceName) { showPleaseSelected(view, resource) return nil } - kubecli.Cli.WithNamespace(namespace).Describe(viewStreams(view), resource, selectedName).Run() + kubecli.Cli.WithNamespace(namespace).Describe(viewStreams(view), resource, resourceName).Run() view.ReRender() return nil } @@ -456,43 +433,59 @@ func onFocusClearSelected(gui *guilib.Gui, view *guilib.View) error { log.Logger.Warningf("onFocusClearSelected - view name %s gui.GetView(\"%s\") error %s", view.Name, functionView, err) continue } - functionView.State.Set(selectedViewLine, nil) + if err := functionView.SetOrigin(0, 0); err != nil { + return err + } + if err := functionView.SetCursor(0, 0); err != nil { + return err + } } return nil } func podLogsRender(gui *guilib.Gui, view *guilib.View) error { - selectedNamespace, _ := Namespace.State.Get(selectedViewLine) - selected, _ := Pod.State.Get(selectedViewLine) + namespaceView, err := gui.GetView(namespaceViewName) + if err != nil { + return err + } + + namespace := formatSelectedNamespace(namespaceView.SelectedLine) + + podView, err := gui.GetView(podViewName) + if err != nil { + return err + } + + selected := podView.SelectedLine resource := "pod" - if selected == nil { + if notResourceSelected(selected) { showPleaseSelected(view, resource) return nil } - if selectedNamespace != nil { - selectedName := formatSelectedName(selected.(string), 0) - if notResourceSelected(selectedName) { + if !notResourceSelected(namespace) { + resourceName := formatResourceName(selected, 0) + if notResourceSelected(resourceName) { showPleaseSelected(view, resource) return nil } streams := newStream() - kubecli.Cli.Logs(streams, selectedName).SetFlag("all-containers", "true").SetFlag("tail", logsTail).SetFlag("prefix", "true").Run() + kubecli.Cli.Logs(streams, resourceName).SetFlag("all-containers", "true").SetFlag("tail", logsTail).SetFlag("prefix", "true").Run() view.Clear() streamCopyTo(streams, view) view.ReRender() return nil } - namespace := formatSelectedName(selected.(string), 0) - selectedName := formatSelectedName(selected.(string), 1) - if notResourceSelected(selectedName) { + namespace = formatResourceName(selected, 0) + resourceName := formatResourceName(selected, 1) + if notResourceSelected(resourceName) { showPleaseSelected(view, resource) return nil } streams := newStream() - kubecli.Cli.WithNamespace(namespace).Logs(streams, selectedName).SetFlag("all-containers", "true").SetFlag("tail", logsTail).SetFlag("prefix", "true").Run() + kubecli.Cli.WithNamespace(namespace).Logs(streams, resourceName).SetFlag("all-containers", "true").SetFlag("tail", logsTail).SetFlag("prefix", "true").Run() streamCopyTo(streams, view) view.ReRender() return nil @@ -556,8 +549,8 @@ func podsSelectorRenderHelper(cmdFunc func(namespace string, labelsArr []string) return nil } - selectedNamespace, _ := namespaceView.State.Get(selectedViewLine) - selected, _ := activeView.State.Get(selectedViewLine) + namespace := formatSelectedNamespace(namespaceView.SelectedLine) + selected := activeView.SelectedLine var resource string var jsonPath string switch activeView.Name { @@ -575,28 +568,27 @@ func podsSelectorRenderHelper(cmdFunc func(namespace string, labelsArr []string) return nil } - if selected == nil { + if notResourceSelected(selected) { showPleaseSelected(view, resource) return nil } output := newStream() - var namespace string - if selectedNamespace != nil { - selectedName := formatSelectedName(selected.(string), 0) - if notResourceSelected(selectedName) { + if !notResourceSelected(namespace) { + resourceName := formatResourceName(selected, 0) + if notResourceSelected(resourceName) { showPleaseSelected(view, resource) return nil } - kubecli.Cli.Get(output, resource, selectedName).SetFlag("output", jsonPath).Run() + kubecli.Cli.Get(output, resource, resourceName).SetFlag("output", jsonPath).Run() } else { - namespace = formatSelectedName(selected.(string), 0) - selectedName := formatSelectedName(selected.(string), 1) - if notResourceSelected(selectedName) { + namespace = formatResourceName(selected, 0) + resourceName := formatResourceName(selected, 1) + if notResourceSelected(resourceName) { showPleaseSelected(view, resource) return nil } - kubecli.Cli.WithNamespace(namespace).Get(output, resource, selectedName).SetFlag("output", jsonPath).Run() + kubecli.Cli.WithNamespace(namespace).Get(output, resource, resourceName).SetFlag("output", jsonPath).Run() } labelJson := streamToString(output) diff --git a/pkg/app/render_plot.go b/pkg/app/render_plot.go index cad805f..77f3d3b 100644 --- a/pkg/app/render_plot.go +++ b/pkg/app/render_plot.go @@ -25,34 +25,44 @@ func podMetricsPlotRender(gui *guilib.Gui, view *guilib.View) error { } view.Clear() - selectedNamespace, _ := Namespace.State.Get(selectedViewLine) - selected, _ := Pod.State.Get(selectedViewLine) + namespaceView, err := gui.GetView(namespaceViewName) + if err != nil { + return err + } + namespace := formatSelectedNamespace(namespaceView.SelectedLine) + + podView, err := gui.GetView(podViewName) + if err != nil { + return err + } + + selected := podView.SelectedLine resource := "pod" - if selected == nil { + if notResourceSelected(selected) { showPleaseSelected(view, resource) return nil } - var namespace, selectedName string - if selectedNamespace != nil { - selectedName = formatSelectedName(selected.(string), 0) - if selectedName == "" { + var resourceName string + if !notResourceSelected(namespace) { + resourceName = formatResourceName(selected, 0) + if notResourceSelected(resourceName) { showPleaseSelected(view, resource) return nil } namespace = kubecli.Cli.Namespace() } else { - namespace = formatSelectedName(selected.(string), 0) - selectedName = formatSelectedName(selected.(string), 1) + namespace = formatResourceName(selected, 0) + resourceName = formatResourceName(selected, 1) } - if selectedName == "" { + if notResourceSelected(resourceName) { showPleaseSelected(view, resource) return nil } - metrics, err := kubecli.Cli.GetPodMetrics(namespace, selectedName, false, nil) + metrics, err := kubecli.Cli.GetPodMetrics(namespace, resourceName, false, nil) if err != nil { - log.Logger.Warningf("podMetricsDataGetter - kubecli.Cli.GetPodMetrics('%s', '%s', false, nil) error %s", namespace, selectedName, err) + log.Logger.Warningf("podMetricsDataGetter - kubecli.Cli.GetPodMetrics('%s', '%s', false, nil) error %s", namespace, resourceName, err) } fmt.Fprintln(view) cpuPlot := getPlot( @@ -61,7 +71,7 @@ func podMetricsPlotRender(gui *guilib.Gui, view *guilib.View) error { cpuPlotStateKey, "CPU: %0.0fm (%v)", namespace, - selectedName, + resourceName, func() []float64 { data := make([]float64, 0) if metrics == nil { @@ -83,7 +93,7 @@ func podMetricsPlotRender(gui *guilib.Gui, view *guilib.View) error { memoryPlotStateKey, "Memory: %0.0fMi (%v)", namespace, - selectedName, + resourceName, func() []float64 { data := make([]float64, 0) if metrics == nil { diff --git a/pkg/app/style.go b/pkg/app/style.go index 84efbb3..9a990cc 100644 --- a/pkg/app/style.go +++ b/pkg/app/style.go @@ -5,9 +5,7 @@ import ( ) var ( - viewHeights = map[string]int{} - resizeView = "" - resizeableViews = []string{namespaceViewName, serviceViewName, deploymentViewName, podViewName} + viewHeights = map[string]int{} ) func leftSideWidth(maxWidth int) int { @@ -38,18 +36,16 @@ func reactiveHeight(gui *gui.Gui, view *gui.View) int { viewHeights[podViewName] = space / tallPanels viewHeights[optionViewName] = 1 + resizeView := namespaceViewName currentView := gui.CurrentView() - if currentView != nil { - for _, viewName := range resizeableViews { - if currentView.Name == viewName { - resizeView = viewName - break - } - } - - viewHeights[resizeView] += space % tallPanels + if currentView != nil && currentView.Name != clusterInfoViewName && currentView.Name != navigationViewName && currentView.Name != detailViewName { + resizeView = currentView.Name + } else if gui.PeekPreviousView() != "" && gui.PeekPreviousView() != clusterInfoViewName { + resizeView = gui.PeekPreviousView() } + viewHeights[resizeView] += space % tallPanels + height := viewHeights[view.Name] return height } diff --git a/pkg/gui/action.go b/pkg/gui/action.go index 2ac17fc..26641b6 100644 --- a/pkg/gui/action.go +++ b/pkg/gui/action.go @@ -18,21 +18,20 @@ var ( } ClickView = &Action{ - Name: "clickView", - Key: gocui.MouseLeft, - Handler: ViewClickHandler, - Mod: gocui.ModNone, - ReRender: true, + Name: "clickView", + Key: gocui.MouseLeft, + Handler: ViewClickHandler, + Mod: gocui.ModNone, } ) type Action struct { - Name string - Key interface{} - Keys []interface{} - ReRender bool - Handler func(gui *Gui) func(*gocui.Gui, *gocui.View) error - Mod gocui.Modifier + Name string + Key interface{} + Keys []interface{} + ReRenderAllView bool + Handler func(gui *Gui) func(*gocui.Gui, *gocui.View) error + Mod gocui.Modifier } type ActionHandler func(gui *Gui) func(*gocui.Gui, *gocui.View) error diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index cb00685..44999df 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -22,6 +22,8 @@ type Gui struct { preHeight int preWidth int + + Actions []*Action } func NewGui(config config.GuiConfig, views ...*View) *Gui { @@ -73,6 +75,10 @@ func (gui *Gui) layout(*gocui.Gui) error { return err } for _, view := range gui.views { + if err := gui.updateSelectedViewLine(view); err != nil { + return err + } + err := gui.RenderView(view) if err == nil { continue @@ -106,6 +112,24 @@ func (gui *Gui) layout(*gocui.Gui) error { return nil } +func (gui *Gui) updateSelectedViewLine(view *View) error { + if !view.Rendered() { + return nil + } + + _, cy := view.Cursor() + selectedLine, _ := view.Line(cy) + if selectedLine != view.SelectedLine { + view.SelectedLine = selectedLine + if view.OnSelectedLineChange != nil { + if err := view.OnSelectedLineChange(gui, view, selectedLine); err != nil { + return err + } + } + } + return nil +} + func (gui *Gui) setTopViews() error { for _, view := range gui.views { if view.AlwaysOnTop { @@ -159,7 +183,7 @@ func (gui *Gui) SetKeybinding(viewName string, key interface{}, mod gocui.Modifi func (gui *Gui) BindAction(viewName string, action *Action) { var handler func(g *gocui.Gui, v *gocui.View) error - if action.ReRender { + if action.ReRenderAllView { handler = func(g *gocui.Gui, v *gocui.View) error { if err := action.Handler(gui)(g, v); err != nil { return err @@ -199,6 +223,12 @@ func (gui *Gui) ViewDimensionValidated(x0, y0, x1, y1 int) bool { } func (gui *Gui) Run() { + if gui.Actions != nil { + for _, act := range gui.Actions { + gui.BindAction("", act) + } + } + for _, view := range gui.views { if view.Clickable { gui.BindAction(view.Name, ClickView) @@ -531,3 +561,15 @@ func (gui *Gui) renderOptions() error { func (gui *Gui) SetRune(x, y int, ch rune, fgColor, bgColor gocui.Attribute) error { return gui.g.SetRune(x, y, ch, fgColor, bgColor) } + +func (gui *Gui) ReRenderViews(viewNames ...string) { + for _, name := range viewNames { + view, err := gui.GetView(name) + if err != nil { + log.Logger.Warningf("ReRenderViews - view '%s' error %s", name, err) + continue + } + + view.ReRender() + } +} diff --git a/pkg/gui/view.go b/pkg/gui/view.go index ea2bb78..1d11346 100644 --- a/pkg/gui/view.go +++ b/pkg/gui/view.go @@ -19,6 +19,12 @@ func init() { maxWidth, maxHeight := gui.Size() return 0, 0, maxWidth - 1, maxHeight - 1 }, + OnRender: func(gui *Gui, view *View) error { + gui.Config.Cursor = false + gui.Configure() + view.ReRender() + return nil + }, } } @@ -58,8 +64,13 @@ type View struct { OnFocus func(gui *Gui, view *View) error OnFocusLost func(gui *Gui, view *View) error + OnCursorChange func(gui *Gui, view *View, x, y int) error + OnEditedChange func(gui *Gui, view *View, key gocui.Key, ch rune, mod gocui.Modifier) + SelectedLine string + OnSelectedLineChange func(gui *Gui, view *View, selectedLine string) error + DimensionFunc DimensionFunc UpperLeftPointXFunc ViewPointFunc @@ -92,6 +103,7 @@ func (view *View) InitView() { } view.v.Editor = NewViewEditor(view.gui, view) + view.v.OnCursorChange = view.onCursorChange } func (view *View) BindGui(gui *Gui) { @@ -286,5 +298,14 @@ func (view *View) ResetCursorOrigin() error { return nil } +func (view *View) onCursorChange(v *gocui.View, x, y int) error { + if view.OnCursorChange != nil { + if err := view.OnCursorChange(view.gui, view, x, y); err != nil { + return err + } + } + return nil +} + type DimensionFunc func(gui *Gui, view *View) (int, int, int, int) type ViewPointFunc func(gui *Gui, view *View) int