├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── average.go
├── average_test.go
├── data.go
├── demo
├── demo_1var.png
├── demo_multi.png
├── demo_single.png
└── demo_small.png
├── expvars.go
├── expvars.json
├── expvars_advanced.json
├── expvars_test.go
├── go.mod
├── go.sum
├── main.go
├── self.go
├── service.go
├── stack.go
├── stack_test.go
├── ui.go
├── ui_dummy.go
├── ui_multi.go
├── ui_single.go
├── utils.go
├── utils_test.go
├── var.go
├── var_test.go
└── vendor
├── github.com
├── antonholmquist
│ └── jason
│ │ ├── .gitignore
│ │ ├── .travis.yml
│ │ ├── LICENSE
│ │ ├── README.md
│ │ └── jason.go
├── bsiegert
│ └── ranges
│ │ ├── Makefile
│ │ ├── README
│ │ └── ranges.go
├── gizak
│ └── termui
│ │ ├── .gitignore
│ │ ├── .travis.yml
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── barchart.go
│ │ ├── block.go
│ │ ├── block_common.go
│ │ ├── block_windows.go
│ │ ├── buffer.go
│ │ ├── canvas.go
│ │ ├── doc.go
│ │ ├── events.go
│ │ ├── gauge.go
│ │ ├── go.mod
│ │ ├── go.sum
│ │ ├── grid.go
│ │ ├── linechart.go
│ │ ├── linechart_others.go
│ │ ├── linechart_windows.go
│ │ ├── list.go
│ │ ├── paragraph.go
│ │ ├── piechart.go
│ │ ├── position.go
│ │ ├── render.go
│ │ ├── sparkline.go
│ │ ├── stacked_barchart.go
│ │ ├── table.go
│ │ ├── tabpane.go
│ │ ├── textbuilder.go
│ │ ├── theme.go
│ │ ├── utils.go
│ │ └── widget.go
├── mattn
│ └── go-runewidth
│ │ ├── .travis.yml
│ │ ├── LICENSE
│ │ ├── README.mkd
│ │ ├── runewidth.go
│ │ ├── runewidth_appengine.go
│ │ ├── runewidth_js.go
│ │ ├── runewidth_posix.go
│ │ └── runewidth_windows.go
├── mitchellh
│ └── go-wordwrap
│ │ ├── LICENSE.md
│ │ ├── README.md
│ │ ├── go.mod
│ │ └── wordwrap.go
├── nsf
│ └── termbox-go
│ │ ├── AUTHORS
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── api.go
│ │ ├── api_common.go
│ │ ├── api_windows.go
│ │ ├── collect_terminfo.py
│ │ ├── escwait.go
│ │ ├── escwait_darwin.go
│ │ ├── syscalls.go
│ │ ├── syscalls_darwin.go
│ │ ├── syscalls_darwin_amd64.go
│ │ ├── syscalls_dragonfly.go
│ │ ├── syscalls_freebsd.go
│ │ ├── syscalls_linux.go
│ │ ├── syscalls_netbsd.go
│ │ ├── syscalls_openbsd.go
│ │ ├── syscalls_windows.go
│ │ ├── termbox.go
│ │ ├── termbox_common.go
│ │ ├── termbox_windows.go
│ │ ├── terminfo.go
│ │ └── terminfo_builtin.go
└── pyk
│ └── byten
│ ├── LICENSE
│ ├── README.md
│ └── size.go
└── modules.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | expvarmon
2 | *.swp
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 | go:
3 | - 1.8.3
4 | - 1.9
5 |
6 | script:
7 | - go test -v .
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Ivan Danyliuk
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ExpvarMon
2 |
3 | TermUI based Go apps monitor using [expvars](http://golang.org/pkg/expvar/) variables (/debug/vars). Quickest way to monitor your Go app.
4 |
5 | ## Introduction
6 |
7 | Go apps console monitoring tool. Minimal configuration efforts. Quick and easy monitoring solution for one or multiple services.
8 |
9 | ## Features
10 |
11 | * Single- and multi-apps mode
12 | * Local and remote apps support
13 | * HTTP and HTTPS endpoints, including Basic Auth support
14 | * Arbitrary number of apps and vars to monitor (from 1 to 30+, depends on size of your terminal)
15 | * Track restarted/failed apps
16 | * Show maximum value
17 | * Supports: Integer, float, duration, memory, string, bool, array variables
18 | * Sparkline charts for integer, duration and memory data
19 | * Auto-resize on font-size change or window resize
20 | * Uses amazing [TermUI](https://github.com/gizak/termui) library by [gizak](https://github.com/gizak)
21 |
22 | ## Demo
23 |
24 | ### Multiple apps mode
25 |
26 |
27 | ### Single app mode
28 |
29 |
30 | You can monitor arbitrary number of services and variables:
31 |
32 |
33 |
34 | ## Purpose
35 |
36 | This app targets debug/develop sessions when you need an instant way to monitor you app(s). It's not intended to monitor apps in production.
37 | Also it doesn't use any storage engines and doesn't send notifications.
38 |
39 | ## Install
40 |
41 | Just run go get:
42 |
43 | go install github.com/divan/expvarmon@latest
44 |
45 | ## Usage
46 |
47 | ### Prepare your app
48 |
49 | First, you have to add [expvars](http://golang.org/pkg/expvar/) support into your Go program. It's as simple as:
50 |
51 | import _ "expvar"
52 |
53 | and note the port your app is listening on. If it's not, just add two lines:
54 |
55 | import "net/http"
56 | ...
57 | http.ListenAndServe(":1234", nil)
58 |
59 | and expvar will add handler for "localhost:1234/debug/vars" to your app.
60 |
61 | By default, expvars adds two variables: *memstats* and *cmdline*. It's enough to monitor memory and garbage collector status in your app.
62 |
63 | ### Run expvarmon
64 |
65 | Just run expvarmon with -ports="1234" flag:
66 |
67 | expvarmon -ports="1234"
68 |
69 | That's it.
70 |
71 | More examples:
72 |
73 | ./expvarmon -ports="80"
74 | ./expvarmon -ports="23000-23010,http://example.com:80-81" -i=1m
75 | ./expvarmon -ports="80,remoteapp:80" -vars="mem:memstats.Alloc,duration:Response.Mean,Counter"
76 | ./expvarmon -ports="1234-1236" -vars="Goroutines" -self
77 | ./expvarmon -ports="https://user:pass@my.remote.app.com:443" -vars="Goroutines" -self
78 |
79 | ## Advanced usage
80 |
81 | If you need to monitor more (or less) vars, you can specify them with -vars command line flag.
82 |
83 | $ no ports specified. Use -ports arg to specify ports of Go apps to monitor
84 | Usage of ./expvarmon:
85 | -dummy
86 | Use dummy (console) output
87 | -endpoint string
88 | URL endpoint for expvars (default "/debug/vars")
89 | -i duration
90 | Polling interval (default 5s)
91 | -ports string
92 | Ports/URLs for accessing services expvars (start-end,port2,port3,https://host:port)
93 | -self
94 | Monitor itself
95 | -vars string
96 | Vars to monitor (comma-separated) (default "mem:memstats.Alloc,mem:memstats.Sys,mem:memstats.HeapAlloc,mem:memstats.HeapInuse,duration:memstats.PauseNs,duration:memstats.PauseTotalNs")
97 |
98 | Examples:
99 | ./expvarmon -ports="80"
100 | ./expvarmon -ports="23000-23010,http://example.com:80-81" -i=1m
101 | ./expvarmon -ports="80,remoteapp:80" -vars="mem:memstats.Alloc,duration:Response.Mean,Counter"
102 | ./expvarmon -ports="1234-1236" -vars="Goroutines" -self
103 |
104 | For more details and docs, see README: http://github.com/divan/expvarmon
105 |
106 |
107 | So, yes, you can specify multiple ports, using '-' for ranges, and specify fully-qualified URLs for remote apps. To override default URL endpoint ("/debug/vars"), use -endpoint flag.
108 |
109 | You can also monitor expvarmon itself, using -self flag.
110 |
111 | ### Basic Auth
112 |
113 | If your expvar endpoint is protected by Basic Auth, you have two options:
114 |
115 | - Set environmental variables *HTTP_USER* and *HTTP_PASSWORD* accordingly. These values will be applied to each endpoint.
116 | - Embed your credentials to URL via command line flag: `-ports="http://user:pass@myapp:1234"`
117 |
118 | ### Vars
119 |
120 | Expvarmon doesn't restrict you to monitor only memstats. You can publish your own counters and variables using [expvar.Publish()](http://golang.org/pkg/expvar/#Publish) method or using expvar wrappers libraries. Just pass your variables names as they appear in JSON to -var command line flag.
121 |
122 | Notation is dot-separated, for example: **memstats.Alloc** for .MemStats.Alloc field. Quick link to runtime.MemStats documentation: http://golang.org/pkg/runtime/#MemStats
123 |
124 | Expvar allows to export only basic types - structs, ints, floats, arrays (int or float), bools and strings. For arrays, average will be calculated. Ints are used for sparklines, and displayed as is. But you can specify modifier to make sure it will be rendered properly.
125 |
126 | Vars are specified as a comma-separated list of var identifiers with (optional) modifiers.
127 |
128 | | Modifier | Description |
129 | | --------- | ----------- |
130 | | mem: | renders int64 as memory string (KB, MB, etc) |
131 | | duration: | renders int64 as time.Duration (1s, 2ms, 12h23h) |
132 | | str: | doesn't display sparklines chart for this value, just display as string |
133 |
--------------------------------------------------------------------------------
/average.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/antonholmquist/jason"
5 | )
6 |
7 | func averageJason(array []*jason.Value) float64 {
8 | var arr []float64
9 | for _, v := range array {
10 | val, _ := v.Float64()
11 | arr = append(arr, val)
12 | }
13 | return average(arr)
14 | }
15 |
16 | // average calculates average (mean) value for int/float array
17 | // trimming zero values from the right.
18 | //
19 | // The whole array/average thing was added to support memstats.PauseNs
20 | // array, which may be filled with zeroes on very beginning.
21 | // Probably it would be better to use Weighted Moving Average and
22 | // add some advanced arrays avarages support, but it's probably wouldn't
23 | // be used much, but PauseNs will be for sure.
24 | func average(arr []float64) float64 {
25 | // find rightmost non-zero and trim
26 | right := len(arr)
27 | for i := right; i > 0; i-- {
28 | if arr[i-1] != 0.0 {
29 | right = i
30 | break
31 | }
32 | }
33 | trimmed := arr[:right]
34 |
35 | // calculate mean
36 | var sum float64
37 | for _, v := range trimmed {
38 | sum += v
39 | }
40 | return sum / float64(len(trimmed))
41 | }
42 |
--------------------------------------------------------------------------------
/average_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestAverage(t *testing.T) {
8 | avg := average(samplesPartial)
9 | want := 621090.75
10 | if avg != want {
11 | t.Fatalf("Average must be %v, but got %v", want, avg)
12 | }
13 | }
14 |
15 | var samplesPartial = []float64{507472, 433979, 610916, 931996, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
16 |
--------------------------------------------------------------------------------
/data.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "time"
4 |
5 | // UIData represents data to be passed to UI.
6 | type UIData struct {
7 | Services []*Service
8 | Vars []VarName
9 | LastTimestamp time.Time
10 | }
11 |
12 | // NewUIData inits and return new data object.
13 | func NewUIData(vars []VarName) *UIData {
14 | return &UIData{
15 | Vars: vars,
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/demo/demo_1var.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/divan/expvarmon/8e0b3d2778b3bb505fbfd14707ad52e68443e330/demo/demo_1var.png
--------------------------------------------------------------------------------
/demo/demo_multi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/divan/expvarmon/8e0b3d2778b3bb505fbfd14707ad52e68443e330/demo/demo_multi.png
--------------------------------------------------------------------------------
/demo/demo_single.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/divan/expvarmon/8e0b3d2778b3bb505fbfd14707ad52e68443e330/demo/demo_single.png
--------------------------------------------------------------------------------
/demo/demo_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/divan/expvarmon/8e0b3d2778b3bb505fbfd14707ad52e68443e330/demo/demo_small.png
--------------------------------------------------------------------------------
/expvars.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "io"
6 | "net/http"
7 | "net/url"
8 | "os"
9 | "time"
10 |
11 | "github.com/antonholmquist/jason"
12 | )
13 |
14 | // DefaultEndpoint is the default url for fetching expvar info.
15 | var DefaultEndpoint = "/debug/vars"
16 |
17 | // Expvar represents fetched expvar variable.
18 | type Expvar struct {
19 | *jason.Object
20 | }
21 |
22 | func getBasicAuthEnv() (user, password string) {
23 | return os.Getenv("HTTP_USER"), os.Getenv("HTTP_PASSWORD")
24 | }
25 |
26 | // FetchExpvar fetches expvar by http for the given addr (host:port)
27 | func FetchExpvar(u url.URL) (*Expvar, error) {
28 | e := &Expvar{&jason.Object{}}
29 | client := &http.Client{
30 | Timeout: 1 * time.Second, // TODO: make it configurable or left default?
31 | }
32 |
33 | req, _ := http.NewRequest("GET", "localhost", nil)
34 | req.URL = &u
35 | req.Host = u.Host
36 |
37 | if user, pass := getBasicAuthEnv(); user != "" && pass != "" {
38 | req.SetBasicAuth(user, pass)
39 | }
40 | resp, err := client.Do(req)
41 | if err != nil {
42 | return e, err
43 | }
44 | defer resp.Body.Close()
45 | if resp.StatusCode == http.StatusNotFound {
46 | return e, errors.New("Vars not found. Did you import expvars?")
47 | }
48 |
49 | expvar, err := ParseExpvar(resp.Body)
50 | if err != nil {
51 | return e, err
52 | }
53 | e = expvar
54 | return e, nil
55 | }
56 |
57 | // ParseExpvar parses expvar data from reader.
58 | func ParseExpvar(r io.Reader) (*Expvar, error) {
59 | object, err := jason.NewObjectFromReader(r)
60 | return &Expvar{object}, err
61 | }
62 |
--------------------------------------------------------------------------------
/expvars.json:
--------------------------------------------------------------------------------
1 | {
2 | "cmdline": ["./geo.service/geo.service","-p=:40004","-dsn=root:@tcp(localhost:3306)/geo"],
3 | "memstats": {"Alloc":306192,"TotalAlloc":18134040,"Sys":3999992,"Lookups":961,"Mallocs":219206,"Frees":218107,"HeapAlloc":306192,"HeapSys":1851392,"HeapIdle":1163264,"HeapInuse":688128,"HeapReleased":1114112,"HeapObjects":1099,"StackInuse":245760,"StackSys":245760,"MSpanInuse":6968,"MSpanSys":16384,"MCacheInuse":1200,"MCacheSys":16384,"BuckHashSys":1445672,"GCSys":137579,"OtherSys":286821,"NextGC":499776,"LastGC":1429554826339587426,"PauseTotalNs":58953963,"PauseNs":[149673,116970,116282,152960,241678,269277,2345682,2813202,395563,896996,413271,524246,411490,438143,465269,410017,407874,429678,407380,400152,462808,416069,396655,411999,2235261,483709,532587,423111,402061,410275,527945,377815,454049,398089,439974,428984,454590,448865,438237,408540,432028,476609,461090,459348,407046,447736,442569,501791,390718,451293,411698,554251,612890,454626,539950,475728,481920,436132,537898,407788,366017,528018,445895,475310,426996,510830,563059,562691,632919,605119,580397,469276,593978,815423,426771,575456,872826,929380,699880,1099353,727790,825090,815515,1143818,801543,2286288,826334,791492,817455,440831,376237,2237337,519402,694540,782088,948074,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"PauseEnd":[1429551836329548506,1429551836329992823,1429551836330364410,1429551836330972293,1429551836332086156,1429551836332787858,1429551896183637429,1429552016336417703,1429552038892146378,1429552038905749325,1429552038920018001,1429552038931843671,1429552038945437201,1429552038960316731,1429552038974053317,1429552038989797695,1429552039002185699,1429552039014183958,1429552039026587369,1429552039038164871,1429552039051065585,1429552039062402317,1429552039074574600,1429552039087577539,1429552039099006520,1429552039113868847,1429552039126842855,1429552039138210121,1429552039150669078,1429552045073734820,1429552045084533164,1429552045097820035,1429552045111401775,1429552045124221035,1429552045136039350,1429552045146856774,1429552045158451390,1429552045172432052,1429552045183862574,1429552045195450754,1429552045206890854,1429552045217811105,1429552045229223666,1429552045239924164,1429552045252202806,1429552045263999145,1429552045275883429,1429552045286550637,1429552045299642824,1429552045310732720,1429552045322523365,1429552049787455566,1429552049801233968,1429552049813748109,1429552049826657828,1429552049838428456,1429552049849704476,1429552049860731436,1429552049872770863,1429552049887348193,1429552049899354726,1429552049910091446,1429552049922791888,1429552049935592566,1429552049949048309,1429552049961817616,1429552049975874584,1429552049992176815,1429552050006202883,1429552050021524454,1429552050035892415,1429552050051383684,1429552050063614314,1429552170337189191,1429552291337252450,1429552412338543040,1429552533335058879,1429552653337824128,1429552774336205167,1429552895338586147,1429553016337759124,1429553136339470606,1429553257339468848,1429553378336184002,1429553499338033468,1429553619340990287,1429553740339511139,1429553861334820854,1429553981339514309,1429554102337221147,1429554223338016039,1429554344338826682,1429554465336311605,1429554585339334313,1429554706335732334,1429554826339586952,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"NumGC":96,"EnableGC":true,"DebugGC":false,"BySize":[{"Size":0,"Mallocs":0,"Frees":0},{"Size":8,"Mallocs":39541,"Frees":39497},{"Size":16,"Mallocs":47557,"Frees":47188},{"Size":32,"Mallocs":25990,"Frees":25864},{"Size":48,"Mallocs":36196,"Frees":36005},{"Size":64,"Mallocs":9930,"Frees":9822},{"Size":80,"Mallocs":585,"Frees":542},{"Size":96,"Mallocs":5530,"Frees":5518},{"Size":112,"Mallocs":8345,"Frees":8336},{"Size":128,"Mallocs":921,"Frees":916},{"Size":144,"Mallocs":23,"Frees":21},{"Size":160,"Mallocs":1451,"Frees":1432},{"Size":176,"Mallocs":13320,"Frees":13295},{"Size":192,"Mallocs":10,"Frees":2},{"Size":208,"Mallocs":3866,"Frees":3841},{"Size":224,"Mallocs":1,"Frees":0},{"Size":240,"Mallocs":1,"Frees":0},{"Size":256,"Mallocs":3,"Frees":0},{"Size":288,"Mallocs":62,"Frees":31},{"Size":320,"Mallocs":4,"Frees":3},{"Size":352,"Mallocs":69,"Frees":62},{"Size":384,"Mallocs":2,"Frees":0},{"Size":416,"Mallocs":932,"Frees":925},{"Size":448,"Mallocs":0,"Frees":0},{"Size":480,"Mallocs":2,"Frees":0},{"Size":512,"Mallocs":0,"Frees":0},{"Size":576,"Mallocs":30,"Frees":19},{"Size":640,"Mallocs":5,"Frees":2},{"Size":704,"Mallocs":4,"Frees":3},{"Size":768,"Mallocs":0,"Frees":0},{"Size":896,"Mallocs":13,"Frees":9},{"Size":1024,"Mallocs":1,"Frees":0},{"Size":1152,"Mallocs":21,"Frees":18},{"Size":1280,"Mallocs":2,"Frees":2},{"Size":1408,"Mallocs":3,"Frees":1},{"Size":1536,"Mallocs":0,"Frees":0},{"Size":1664,"Mallocs":8,"Frees":4},{"Size":2048,"Mallocs":70,"Frees":68},{"Size":2304,"Mallocs":20,"Frees":14},{"Size":2560,"Mallocs":2,"Frees":1},{"Size":2816,"Mallocs":1,"Frees":1},{"Size":3072,"Mallocs":0,"Frees":0},{"Size":3328,"Mallocs":4,"Frees":1},{"Size":4096,"Mallocs":1856,"Frees":1852},{"Size":4608,"Mallocs":19,"Frees":18},{"Size":5376,"Mallocs":7,"Frees":4},{"Size":6144,"Mallocs":22,"Frees":17},{"Size":6400,"Mallocs":0,"Frees":0},{"Size":6656,"Mallocs":1,"Frees":0},{"Size":6912,"Mallocs":0,"Frees":0},{"Size":8192,"Mallocs":0,"Frees":0},{"Size":8448,"Mallocs":0,"Frees":0},{"Size":8704,"Mallocs":2,"Frees":2},{"Size":9472,"Mallocs":4,"Frees":3},{"Size":10496,"Mallocs":1,"Frees":0},{"Size":12288,"Mallocs":1,"Frees":1},{"Size":13568,"Mallocs":0,"Frees":0},{"Size":14080,"Mallocs":0,"Frees":0},{"Size":16384,"Mallocs":0,"Frees":0},{"Size":16640,"Mallocs":0,"Frees":0},{"Size":17664,"Mallocs":0,"Frees":0}]}
4 | }
5 |
--------------------------------------------------------------------------------
/expvars_advanced.json:
--------------------------------------------------------------------------------
1 | {
2 | "cmdline": ["./geo.service/geo.service","-p=:40004","-dsn=root:@tcp(localhost:3306)/geo"],
3 | "memstats": {"Alloc":306192,"TotalAlloc":18134040,"Sys":3999992,"Lookups":961,"Mallocs":219206,"Frees":218107,"HeapAlloc":306192,"HeapSys":1851392,"HeapIdle":1163264,"HeapInuse":688128,"HeapReleased":1114112,"HeapObjects":1099,"StackInuse":245760,"StackSys":245760,"MSpanInuse":6968,"MSpanSys":16384,"MCacheInuse":1200,"MCacheSys":16384,"BuckHashSys":1445672,"GCSys":137579,"OtherSys":286821,"NextGC":499776,"LastGC":1429554826339587426,"PauseTotalNs":58953963,"PauseNs":[149673,116970,116282,152960,241678,269277,2345682,2813202,395563,896996,413271,524246,411490,438143,465269,410017,407874,429678,407380,400152,462808,416069,396655,411999,2235261,483709,532587,423111,402061,410275,527945,377815,454049,398089,439974,428984,454590,448865,438237,408540,432028,476609,461090,459348,407046,447736,442569,501791,390718,451293,411698,554251,612890,454626,539950,475728,481920,436132,537898,407788,366017,528018,445895,475310,426996,510830,563059,562691,632919,605119,580397,469276,593978,815423,426771,575456,872826,929380,699880,1099353,727790,825090,815515,1143818,801543,2286288,826334,791492,817455,440831,376237,2237337,519402,694540,782088,948074,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"PauseEnd":[1429551836329548506,1429551836329992823,1429551836330364410,1429551836330972293,1429551836332086156,1429551836332787858,1429551896183637429,1429552016336417703,1429552038892146378,1429552038905749325,1429552038920018001,1429552038931843671,1429552038945437201,1429552038960316731,1429552038974053317,1429552038989797695,1429552039002185699,1429552039014183958,1429552039026587369,1429552039038164871,1429552039051065585,1429552039062402317,1429552039074574600,1429552039087577539,1429552039099006520,1429552039113868847,1429552039126842855,1429552039138210121,1429552039150669078,1429552045073734820,1429552045084533164,1429552045097820035,1429552045111401775,1429552045124221035,1429552045136039350,1429552045146856774,1429552045158451390,1429552045172432052,1429552045183862574,1429552045195450754,1429552045206890854,1429552045217811105,1429552045229223666,1429552045239924164,1429552045252202806,1429552045263999145,1429552045275883429,1429552045286550637,1429552045299642824,1429552045310732720,1429552045322523365,1429552049787455566,1429552049801233968,1429552049813748109,1429552049826657828,1429552049838428456,1429552049849704476,1429552049860731436,1429552049872770863,1429552049887348193,1429552049899354726,1429552049910091446,1429552049922791888,1429552049935592566,1429552049949048309,1429552049961817616,1429552049975874584,1429552049992176815,1429552050006202883,1429552050021524454,1429552050035892415,1429552050051383684,1429552050063614314,1429552170337189191,1429552291337252450,1429552412338543040,1429552533335058879,1429552653337824128,1429552774336205167,1429552895338586147,1429553016337759124,1429553136339470606,1429553257339468848,1429553378336184002,1429553499338033468,1429553619340990287,1429553740339511139,1429553861334820854,1429553981339514309,1429554102337221147,1429554223338016039,1429554344338826682,1429554465336311605,1429554585339334313,1429554706335732334,1429554826339586952,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"NumGC":96,"EnableGC":true,"DebugGC":false,"BySize":[{"Size":0,"Mallocs":0,"Frees":0},{"Size":8,"Mallocs":39541,"Frees":39497},{"Size":16,"Mallocs":47557,"Frees":47188},{"Size":32,"Mallocs":25990,"Frees":25864},{"Size":48,"Mallocs":36196,"Frees":36005},{"Size":64,"Mallocs":9930,"Frees":9822},{"Size":80,"Mallocs":585,"Frees":542},{"Size":96,"Mallocs":5530,"Frees":5518},{"Size":112,"Mallocs":8345,"Frees":8336},{"Size":128,"Mallocs":921,"Frees":916},{"Size":144,"Mallocs":23,"Frees":21},{"Size":160,"Mallocs":1451,"Frees":1432},{"Size":176,"Mallocs":13320,"Frees":13295},{"Size":192,"Mallocs":10,"Frees":2},{"Size":208,"Mallocs":3866,"Frees":3841},{"Size":224,"Mallocs":1,"Frees":0},{"Size":240,"Mallocs":1,"Frees":0},{"Size":256,"Mallocs":3,"Frees":0},{"Size":288,"Mallocs":62,"Frees":31},{"Size":320,"Mallocs":4,"Frees":3},{"Size":352,"Mallocs":69,"Frees":62},{"Size":384,"Mallocs":2,"Frees":0},{"Size":416,"Mallocs":932,"Frees":925},{"Size":448,"Mallocs":0,"Frees":0},{"Size":480,"Mallocs":2,"Frees":0},{"Size":512,"Mallocs":0,"Frees":0},{"Size":576,"Mallocs":30,"Frees":19},{"Size":640,"Mallocs":5,"Frees":2},{"Size":704,"Mallocs":4,"Frees":3},{"Size":768,"Mallocs":0,"Frees":0},{"Size":896,"Mallocs":13,"Frees":9},{"Size":1024,"Mallocs":1,"Frees":0},{"Size":1152,"Mallocs":21,"Frees":18},{"Size":1280,"Mallocs":2,"Frees":2},{"Size":1408,"Mallocs":3,"Frees":1},{"Size":1536,"Mallocs":0,"Frees":0},{"Size":1664,"Mallocs":8,"Frees":4},{"Size":2048,"Mallocs":70,"Frees":68},{"Size":2304,"Mallocs":20,"Frees":14},{"Size":2560,"Mallocs":2,"Frees":1},{"Size":2816,"Mallocs":1,"Frees":1},{"Size":3072,"Mallocs":0,"Frees":0},{"Size":3328,"Mallocs":4,"Frees":1},{"Size":4096,"Mallocs":1856,"Frees":1852},{"Size":4608,"Mallocs":19,"Frees":18},{"Size":5376,"Mallocs":7,"Frees":4},{"Size":6144,"Mallocs":22,"Frees":17},{"Size":6400,"Mallocs":0,"Frees":0},{"Size":6656,"Mallocs":1,"Frees":0},{"Size":6912,"Mallocs":0,"Frees":0},{"Size":8192,"Mallocs":0,"Frees":0},{"Size":8448,"Mallocs":0,"Frees":0},{"Size":8704,"Mallocs":2,"Frees":2},{"Size":9472,"Mallocs":4,"Frees":3},{"Size":10496,"Mallocs":1,"Frees":0},{"Size":12288,"Mallocs":1,"Frees":1},{"Size":13568,"Mallocs":0,"Frees":0},{"Size":14080,"Mallocs":0,"Frees":0},{"Size":16384,"Mallocs":0,"Frees":0},{"Size":16640,"Mallocs":0,"Frees":0},{"Size":17664,"Mallocs":0,"Frees":0}]},
4 | "goroutines": 10,
5 | "counters": { "A": 123.12, "B": 245342 }
6 | }
7 |
--------------------------------------------------------------------------------
/expvars_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 | "testing"
6 | )
7 |
8 | const (
9 | expvarsTestFile = "./expvars.json"
10 | expvarsAdvTestFile = "./expvars_advanced.json"
11 | )
12 |
13 | func TestExpvars(t *testing.T) {
14 | file, err := os.Open(expvarsTestFile)
15 | if err != nil {
16 | t.Fatalf("cannot open test file %v", err)
17 | }
18 | defer file.Close()
19 |
20 | expvar, err := ParseExpvar(file)
21 | if err != nil {
22 | t.Fatal(err)
23 | }
24 |
25 | cmdline, err := expvar.GetStringArray("cmdline")
26 | if err != nil {
27 | t.Fatal(err)
28 | }
29 | if len(cmdline) != 3 {
30 | t.Fatalf("Cmdline should have 3 items, but has %d", len(cmdline))
31 | }
32 |
33 | alloc, err := expvar.GetInt64(VarName("memstats.Alloc").ToSlice()...)
34 | if err != nil {
35 | t.Fatal(err)
36 | }
37 | if alloc == 0 {
38 | t.Fatalf("Alloc should be greater than 0")
39 | }
40 |
41 | pauses, err := expvar.GetInt64Array(VarName("memstats.PauseNs").ToSlice()...)
42 | if err != nil {
43 | t.Fatal(err)
44 | }
45 | if len(pauses) == 0 {
46 | t.Fatalf("Pauses length should be greater than 0")
47 | }
48 | }
49 |
50 | func TestExpvarsAdvanced(t *testing.T) {
51 | file, err := os.Open(expvarsAdvTestFile)
52 | if err != nil {
53 | t.Fatalf("cannot open test file %v", err)
54 | }
55 | defer file.Close()
56 |
57 | expvar, err := ParseExpvar(file)
58 | if err != nil {
59 | t.Fatal(err)
60 | }
61 |
62 | goroutines, err := expvar.GetInt64("goroutines")
63 | if err != nil {
64 | t.Fatal(err)
65 | }
66 | if goroutines != 10 {
67 | t.Fatalf("Expecting 'goroutines' to be %d, but got %d", 10, goroutines)
68 | }
69 |
70 | counterA, err := expvar.GetFloat64(VarName("counters.A").ToSlice()...)
71 | if err != nil {
72 | t.Fatal(err)
73 | }
74 | if counterA != 123.12 {
75 | t.Fatalf("Expecting 'counters.A' to be %f, but got %f", 123.12, counterA)
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/divan/expvarmon
2 |
3 | require (
4 | github.com/antonholmquist/jason v1.0.0
5 | github.com/bsiegert/ranges v0.0.0-20111221115336-19303dc7aa63
6 | github.com/gizak/termui v0.0.0-20181228210747-b136f68f55f1
7 | github.com/mattn/go-runewidth v0.0.4 // indirect
8 | github.com/mitchellh/go-wordwrap v1.0.0 // indirect
9 | github.com/pyk/byten v0.0.0-20140925233358-f847a130bf6d
10 | )
11 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/antonholmquist/jason v1.0.0 h1:Ytg94Bcf1Bfi965K2q0s22mig/n4eGqEij/atENBhA0=
2 | github.com/antonholmquist/jason v1.0.0/go.mod h1:+GxMEKI0Va2U8h3os6oiUAetHAlGMvxjdpAH/9uvUMA=
3 | github.com/bsiegert/ranges v0.0.0-20111221115336-19303dc7aa63 h1:FxdkNGQyRwwk94rJ+IMNrTjv864XzT93/cvk7UpMM38=
4 | github.com/bsiegert/ranges v0.0.0-20111221115336-19303dc7aa63/go.mod h1:8z71/aZjDHLs4ihK/5nD5wZVQxm/W4eRDnxQZcJmVD4=
5 | github.com/cjbassi/drawille-go v0.0.0-20190126131713-27dc511fe6fd h1:XtfPmj9tQRilnrEmI1HjQhxXWRhEM+m8CACtaMJE/kM=
6 | github.com/cjbassi/drawille-go v0.0.0-20190126131713-27dc511fe6fd/go.mod h1:vjcQJUZJYD3MeVGhtZXSMnCHfUNZxsyYzJt90eCYxK4=
7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8 | github.com/gizak/termui v0.0.0-20181228210747-b136f68f55f1 h1:TCe+sQ3zOPa8KNePfjrb5Pix5FPa44WsiiCzsYv0Yts=
9 | github.com/gizak/termui v0.0.0-20181228210747-b136f68f55f1/go.mod h1:W48Llfv3G9PIK3TQjqynxzC9mP4HUcAn/6vlDWekq9k=
10 | github.com/gizak/termui v0.0.0-20190202061602-5ece2b7178ff h1:md8xjZ5dOJsdTnDXsnbu2W9WLBpZ3yh8ebRad+i2bQY=
11 | github.com/gizak/termui v0.0.0-20190202061602-5ece2b7178ff/go.mod h1:TISPFiAGKVhyNxElRxDknJ+HzcUyO7sAJCsc8dfAe+M=
12 | github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
13 | github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
14 | github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
15 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
16 | github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
17 | github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
18 | github.com/nsf/termbox-go v0.0.0-20180613055208-5c94acc5e6eb h1:YahEjAGkJtCrkqgVHhX6n8ZX+CZ3hDRL9fjLYugLfSs=
19 | github.com/nsf/termbox-go v0.0.0-20180613055208-5c94acc5e6eb/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
20 | github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840=
21 | github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
22 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
23 | github.com/pyk/byten v0.0.0-20140925233358-f847a130bf6d h1:/0nqYrqVyPTVDT2jPx9z9BZ+ItJWlWA9VITUGvNaZkI=
24 | github.com/pyk/byten v0.0.0-20140925233358-f847a130bf6d/go.mod h1:El8LdwAxb76Ih0mRmpu9uMVq+ajiqGSxmYiUe+nSr+w=
25 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
26 | golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8=
27 | golang.org/x/net v0.0.0-20180801234040-f4c29de78a2a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
28 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "log"
7 | "os"
8 | "sync"
9 | "time"
10 |
11 | "github.com/gizak/termui"
12 | )
13 |
14 | var (
15 | interval = flag.Duration("i", 5*time.Second, "Polling interval")
16 | urls = flag.String("ports", "", "Ports/URLs for accessing services expvars (start-end,port2,port3,https://host:port)")
17 | varsArg = flag.String("vars", "mem:memstats.Alloc,mem:memstats.Sys,mem:memstats.HeapAlloc,mem:memstats.HeapInuse,duration:memstats.PauseNs,duration:memstats.PauseTotalNs", "Vars to monitor (comma-separated)")
18 | dummy = flag.Bool("dummy", false, "Use dummy (console) output")
19 | self = flag.Bool("self", false, "Monitor itself")
20 | endpoint = flag.String("endpoint", DefaultEndpoint, "URL endpoint for expvars")
21 | )
22 |
23 | func main() {
24 | flag.Usage = Usage
25 | flag.Parse()
26 |
27 | DefaultEndpoint = *endpoint
28 |
29 | // Process ports/urls
30 | ports, _ := ParsePorts(*urls)
31 | if *self {
32 | port, err := StartSelfMonitor()
33 | if err == nil {
34 | ports = append(ports, port)
35 | }
36 | }
37 | if len(ports) == 0 {
38 | fmt.Fprintln(os.Stderr, "no ports specified. Use -ports arg to specify ports of Go apps to monitor")
39 | Usage()
40 | os.Exit(1)
41 | }
42 | if *interval <= 0 {
43 | fmt.Fprintln(os.Stderr, "update interval is not valid. Valid examples: 5s, 1m, 1h30m")
44 | Usage()
45 | os.Exit(1)
46 | }
47 |
48 | // Process vars
49 | vars, err := ParseVars(*varsArg)
50 | if err != nil {
51 | log.Fatal(err)
52 | }
53 |
54 | // Init UIData
55 | data := NewUIData(vars)
56 | for _, port := range ports {
57 | service := NewService(port, vars)
58 | data.Services = append(data.Services, service)
59 | }
60 |
61 | // Start proper UI
62 | var ui UI
63 | if len(data.Services) > 1 {
64 | ui = &TermUI{}
65 | } else {
66 | ui = &TermUISingle{}
67 | }
68 | if *dummy {
69 | ui = &DummyUI{}
70 | }
71 |
72 | if err := ui.Init(*data); err != nil {
73 | log.Fatal(err)
74 | }
75 | defer ui.Close()
76 |
77 | tick := time.NewTicker(*interval)
78 |
79 | UpdateAll(ui, data)
80 | for {
81 | select {
82 | case <-tick.C:
83 | UpdateAll(ui, data)
84 | case e := <-termui.PollEvents():
85 | if e.Type == termui.KeyboardEvent && e.ID == "q" {
86 | return
87 | }
88 | if e.Type == termui.ResizeEvent {
89 | ui.Update(*data)
90 | }
91 | }
92 | }
93 | }
94 |
95 | // UpdateAll collects data from expvars and refreshes UI.
96 | func UpdateAll(ui UI, data *UIData) {
97 | var wg sync.WaitGroup
98 | for _, service := range data.Services {
99 | wg.Add(1)
100 | go service.Update(&wg)
101 | }
102 | wg.Wait()
103 |
104 | data.LastTimestamp = time.Now()
105 |
106 | ui.Update(*data)
107 | }
108 |
109 | // Usage reimplements flag.Usage
110 | func Usage() {
111 | progname := os.Args[0]
112 | fmt.Fprintf(os.Stderr, "Usage of %s:\n", progname)
113 | flag.PrintDefaults()
114 | fmt.Fprintf(os.Stderr, `
115 | Examples:
116 | %s -ports="80"
117 | %s -ports="23000-23010,http://example.com:80-81" -i=1m
118 | %s -ports="80,remoteapp:80" -vars="mem:memstats.Alloc,duration:Response.Mean,Counter"
119 | %s -ports="1234-1236" -vars="Goroutines" -self
120 |
121 | For more details and docs, see README: http://github.com/divan/expvarmon
122 | `, progname, progname, progname, progname)
123 | }
124 |
--------------------------------------------------------------------------------
/self.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "expvar"
5 | "fmt"
6 | "net"
7 | "net/http"
8 | "net/url"
9 | "runtime"
10 | "time"
11 | )
12 |
13 | var startTime = time.Now().UTC()
14 |
15 | func goroutines() interface{} {
16 | return runtime.NumGoroutine()
17 | }
18 |
19 | // uptime is an expvar.Func compliant wrapper for uptime info.
20 | func uptime() interface{} {
21 | uptime := time.Since(startTime)
22 | return int64(uptime)
23 | }
24 |
25 | // startPort defines lower port for bind
26 | const startPort = 32768
27 |
28 | // StartSelfMonitor starts http server on random port and exports expvars.
29 | //
30 | // It tries 1024 ports, starting from startPort and registers some expvars if ok.
31 | func StartSelfMonitor() (url.URL, error) {
32 | for port := startPort; port < startPort+1024; port++ {
33 | bind := fmt.Sprintf("localhost:%d", port)
34 | l, err := net.Listen("tcp", bind)
35 | if err != nil {
36 | continue
37 | }
38 | if err := l.Close(); err != nil {
39 | continue
40 | }
41 |
42 | expvar.Publish("Goroutines", expvar.Func(goroutines))
43 | expvar.Publish("Uptime", expvar.Func(uptime))
44 | go http.ListenAndServe(bind, nil)
45 |
46 | return NewURL(fmt.Sprintf("%d", port)), nil
47 | }
48 |
49 | return url.URL{}, fmt.Errorf("no free ports found")
50 | }
51 |
--------------------------------------------------------------------------------
/service.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net/url"
5 | "strings"
6 | "sync"
7 |
8 | "github.com/antonholmquist/jason"
9 | )
10 |
11 | var (
12 | // uptimeCounter is a variable used for tracking uptime status.
13 | // It should be always incrementing and included into default expvar vars.
14 | // Could be replaced with something different or made configurable in
15 | // the future.
16 | uptimeCounter = VarName("memstats.PauseTotalNs").ToSlice()
17 | )
18 |
19 | // Service represents constantly updating info about single service.
20 | type Service struct {
21 | URL url.URL
22 | Name string
23 | Cmdline string
24 |
25 | stacks map[VarName]*Stack
26 |
27 | Err error
28 | Restarted bool
29 | UptimeCounter int64
30 | }
31 |
32 | // NewService returns new Service object.
33 | func NewService(url url.URL, vars []VarName) *Service {
34 | values := make(map[VarName]*Stack)
35 | for _, name := range vars {
36 | values[VarName(name)] = NewStack()
37 | }
38 |
39 | return &Service{
40 | Name: url.Host, // we have only port on start, so use it as name until resolved
41 | URL: url,
42 |
43 | stacks: values,
44 | }
45 | }
46 |
47 | // Update updates Service info from Expvar variable.
48 | func (s *Service) Update(wg *sync.WaitGroup) {
49 | defer wg.Done()
50 | expvar, err := FetchExpvar(s.URL)
51 | // check for restart
52 | if s.Err != nil && err == nil {
53 | s.Restarted = true
54 | }
55 | s.Err = err
56 |
57 | // if memstat.PauseTotalNs less than s.UptimeCounter
58 | // then service was restarted
59 | c, err := expvar.GetInt64(uptimeCounter...)
60 | if err != nil {
61 | s.Err = err
62 | } else {
63 | if s.UptimeCounter > c {
64 | s.Restarted = true
65 | }
66 | s.UptimeCounter = c
67 | }
68 |
69 | // Update Cmdline & Name only once
70 | if len(s.Cmdline) == 0 {
71 | cmdline, err := expvar.GetStringArray("cmdline")
72 | if err != nil {
73 | s.Err = err
74 | } else {
75 | s.Cmdline = strings.Join(cmdline, " ")
76 | s.Name = BaseCommand(cmdline)
77 | }
78 | }
79 |
80 | // For all vars, fetch desired value from Json and push to it's own stack.
81 | for name, stack := range s.stacks {
82 | value, err := expvar.GetValue(name.ToSlice()...)
83 | if err != nil {
84 | stack.Push(nil)
85 | continue
86 | }
87 | v := guessValue(value)
88 | if v != nil {
89 | stack.Push(v)
90 | }
91 | }
92 | }
93 |
94 | // guessValue attemtps to bruteforce all supported types.
95 | func guessValue(value *jason.Value) interface{} {
96 | if v, err := value.Int64(); err == nil {
97 | return v
98 | } else if v, err := value.Float64(); err == nil {
99 | return v
100 | } else if v, err := value.Boolean(); err == nil {
101 | return v
102 | } else if v, err := value.String(); err == nil {
103 | return v
104 | } else if v, err := value.Array(); err == nil {
105 | // if we get an array, calculate average
106 |
107 | // empty array, treat as zero
108 | if len(v) == 0 {
109 | return 0
110 | }
111 |
112 | avg := averageJason(v)
113 |
114 | // cast to int64 for Int64 values
115 | if _, err := v[0].Int64(); err == nil {
116 | return int64(avg)
117 | }
118 |
119 | return avg
120 | }
121 |
122 | return nil
123 | }
124 |
125 | // Value returns current value for the given var of this service.
126 | //
127 | // It also formats value, if kind is specified.
128 | func (s Service) Value(name VarName) string {
129 | if s.Err != nil {
130 | return "N/A"
131 | }
132 | val, ok := s.stacks[name]
133 | if !ok {
134 | return "N/A"
135 | }
136 |
137 | v := val.Front()
138 | if v == nil {
139 | return "N/A"
140 | }
141 |
142 | return Format(v, name.Kind())
143 | }
144 |
145 | // Values returns slice of ints with recent
146 | // values of the given var, to be used with sparkline.
147 | func (s Service) Values(name VarName) []int {
148 | stack, ok := s.stacks[name]
149 | if !ok {
150 | return nil
151 | }
152 |
153 | return stack.IntValues()
154 | }
155 |
156 | // Max returns maximum recorded value for given service and var.
157 | func (s Service) Max(name VarName) interface{} {
158 | val, ok := s.stacks[name]
159 | if !ok {
160 | return nil
161 | }
162 |
163 | v := val.Max
164 | if v == nil {
165 | return nil
166 | }
167 |
168 | return Format(v, name.Kind())
169 | }
170 |
--------------------------------------------------------------------------------
/stack.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | // DefaultSize specifies maximum number of items in stack.
4 | //
5 | // Values should be enough for sparklines on high-res terminals
6 | // with minimal font size.
7 | const DefaultSize = 1200
8 |
9 | // Stack is a limited FIFO for holding sparkline values.
10 | type Stack struct {
11 | Values []VarValue
12 | Len int
13 | Max VarValue
14 | }
15 |
16 | // NewStack inits new Stack with default size limit.
17 | func NewStack() *Stack {
18 | return NewStackWithSize(DefaultSize)
19 | }
20 |
21 | // NewStackWithSize inits new Stack with size limit.
22 | func NewStackWithSize(size int) *Stack {
23 | return &Stack{
24 | Values: make([]VarValue, size),
25 | Len: size,
26 | }
27 | }
28 |
29 | // Push inserts data to stack, preserving constant length.
30 | func (s *Stack) Push(val VarValue) {
31 | s.Values = append(s.Values, val)
32 | if len(s.Values) > s.Len {
33 | s.Values = s.Values[1:]
34 | }
35 |
36 | if s.Max == nil {
37 | s.Max = val
38 | return
39 | }
40 |
41 | switch val.(type) {
42 | case int64:
43 | switch s.Max.(type) {
44 | case int64:
45 | if val.(int64) > s.Max.(int64) {
46 | s.Max = val
47 | }
48 | case float64:
49 | if float64(val.(int64)) > s.Max.(float64) {
50 | s.Max = val
51 | }
52 | }
53 | case float64:
54 | switch s.Max.(type) {
55 | case int64:
56 | if val.(float64) > float64(s.Max.(int64)) {
57 | s.Max = val
58 | }
59 | case float64:
60 | if val.(float64) > s.Max.(float64) {
61 | s.Max = val
62 | }
63 | }
64 | }
65 | }
66 |
67 | // Front returns front value.
68 | func (s *Stack) Front() VarValue {
69 | if len(s.Values) == 0 {
70 | return nil
71 | }
72 | return s.Values[len(s.Values)-1]
73 | }
74 |
75 | // IntValues returns stack values explicitly casted to int.
76 | //
77 | // Main case is to use with termui.Sparklines.
78 | func (s *Stack) IntValues() []int {
79 | ret := make([]int, s.Len)
80 | for i, v := range s.Values {
81 | n, ok := v.(int64)
82 | if ok {
83 | ret[i] = int(n)
84 | continue
85 | }
86 |
87 | f, ok := v.(float64)
88 | if ok {
89 | // 12.34 (float) -> 1234 (int)
90 | ret[i] = int(f * 100)
91 | continue
92 | }
93 |
94 | b, ok := v.(bool)
95 | if ok {
96 | // false => 0, true = 1
97 | if b {
98 | ret[i] = 1
99 | } else {
100 | ret[i] = 0
101 | }
102 | }
103 | }
104 | return ret
105 | }
106 |
--------------------------------------------------------------------------------
/stack_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "testing"
4 |
5 | func TestPushWithFloatAndIntValue(t *testing.T) {
6 | s := NewStack()
7 | s.Push(VarValue(int64(0.0))) // from service.go:guessValue
8 | s.Push(VarValue(5.0))
9 | s.Push(VarValue(float64(15.0)))
10 | if _, ok := s.Max.(float64); !ok {
11 | t.Fatalf("Expected Max to be float64, but it's not")
12 | }
13 | s.Push(VarValue(int64(25.0)))
14 | if _, ok := s.Max.(int64); !ok {
15 | t.Fatalf("Expected Max to be int64, but it's not")
16 | }
17 | }
18 |
19 | func TestStack(t *testing.T) {
20 | size := 10
21 | s := NewStackWithSize(size)
22 |
23 | for i := 0; i < size+5; i++ {
24 | s.Push(i)
25 | l := len(s.Values)
26 |
27 | if l < size {
28 | if l != i+1 {
29 | t.Fatalf("len is incorrect. expecting %d, got %d", i, l)
30 | }
31 | } else {
32 | if l != size {
33 | t.Fatalf("len is incorrect. expecting %d, got %d", size, l)
34 | }
35 | }
36 | }
37 |
38 | if s.Front().(int) != 14 {
39 | t.Fatalf("Front returns wrong value: expecting %d, got %d", 14, s.Front())
40 | }
41 |
42 | s1 := NewStackWithSize(3)
43 | s1.Push(true)
44 | s1.Push(false)
45 | s1.Push(true)
46 |
47 | ints1 := s1.IntValues()
48 | if len(ints1) != 3 {
49 | t.Fatalf("expecting len of to be %d, but got %d", 3, len(ints1))
50 | }
51 | if ints1[0] != 1 || ints1[1] != 0 || ints1[2] != 1 {
52 | t.Fatalf("bool values converted to int incorrectly: %v", ints1)
53 | }
54 |
55 | s2 := NewStackWithSize(3)
56 | s2.Push(0.1)
57 | s2.Push(0.5)
58 | s2.Push(0.03)
59 |
60 | ints2 := s2.IntValues()
61 | if len(ints2) != 3 {
62 | t.Fatalf("expecting len to be %d, but got %d", 3, len(ints2))
63 | }
64 | if ints2[0] != 10 || ints2[1] != 50 || ints2[2] != 3 {
65 | t.Fatalf("float values converted to int incorrectly: %v", ints2)
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/ui.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | // UI represents UI renderer.
4 | type UI interface {
5 | Init(UIData) error
6 | Close()
7 | Update(UIData)
8 | }
9 |
--------------------------------------------------------------------------------
/ui_dummy.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "time"
6 | )
7 |
8 | // DummyUI is an simple console UI mockup, for testing purposes.
9 | type DummyUI struct{}
10 |
11 | // Init implements UI.
12 | func (*DummyUI) Init(UIData) error { return nil }
13 |
14 | // Close implements UI.
15 | func (*DummyUI) Close() {}
16 |
17 | // Update implements UI.
18 | func (*DummyUI) Update(data UIData) {
19 | if data.Services == nil {
20 | return
21 | }
22 | fmt.Println(time.Now().Format("15:04:05 02/01"))
23 | for _, service := range data.Services {
24 | fmt.Printf("%s: ", service.Name)
25 | if service.Err != nil {
26 | fmt.Printf("ERROR: %s\n", service.Err)
27 | continue
28 | }
29 |
30 | for _, name := range data.Vars {
31 | fmt.Printf("%s: %v, ", name.Short(), service.Value(name))
32 | }
33 |
34 | fmt.Printf("\n")
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ui_multi.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "time"
6 |
7 | "github.com/gizak/termui"
8 | )
9 |
10 | // TermUI is a termUI implementation of UI interface.
11 | type TermUI struct {
12 | Title *termui.Paragraph
13 | Status *termui.Paragraph
14 | Services *termui.List
15 | Lists []*termui.List
16 | Sparkline1 *termui.Sparklines
17 | Sparkline2 *termui.Sparklines
18 | }
19 |
20 | // Init creates widgets, sets sizes and labels.
21 | func (t *TermUI) Init(data UIData) error {
22 | err := termui.Init()
23 | if err != nil {
24 | return err
25 | }
26 |
27 | t.Title = func() *termui.Paragraph {
28 | p := termui.NewParagraph("")
29 | p.Height = 3
30 | p.TextFgColor = termui.ColorWhite
31 | p.Border = true
32 | p.BorderLabel = "Services Monitor"
33 | p.BorderFg = termui.ColorCyan
34 | return p
35 | }()
36 | t.Status = func() *termui.Paragraph {
37 | p := termui.NewParagraph("")
38 | p.Height = 3
39 | p.TextFgColor = termui.ColorWhite
40 | p.Border = true
41 | p.BorderLabel = "Status"
42 | p.BorderFg = termui.ColorCyan
43 | return p
44 | }()
45 | t.Services = func() *termui.List {
46 | list := termui.NewList()
47 | list.ItemFgColor = termui.ColorGreen
48 | list.Border = true
49 | list.BorderLabelFg = termui.ColorGreen | termui.AttrBold
50 | list.BorderLabel = "Services"
51 | list.Height = len(data.Services) + 2
52 | return list
53 | }()
54 |
55 | t.Lists = make([]*termui.List, len(data.Vars))
56 | for i, name := range data.Vars {
57 | list := termui.NewList()
58 | list.ItemFgColor = colorByKind(name.Kind())
59 | list.Border = true
60 | list.BorderLabel = name.Short()
61 | list.BorderLabelFg = termui.ColorGreen
62 | if i < 2 {
63 | list.BorderLabelFg = termui.ColorGreen | termui.AttrBold
64 | }
65 | list.Height = len(data.Services) + 2
66 | t.Lists[i] = list
67 | }
68 |
69 | makeSparkline := func(name VarName) *termui.Sparklines {
70 | var sparklines []termui.Sparkline
71 | for _, service := range data.Services {
72 | spl := termui.NewSparkline()
73 | spl.Height = 1
74 | spl.LineColor = termui.ColorGreen
75 | spl.Title = service.Name
76 | sparklines = append(sparklines, spl)
77 | }
78 |
79 | s := termui.NewSparklines(sparklines...)
80 | s.Height = 2*len(data.Services) + 2
81 | s.Border = true
82 | s.BorderLabel = fmt.Sprintf("Monitoring %s", name.Long())
83 | return s
84 | }
85 | t.Sparkline1 = makeSparkline(data.Vars[0])
86 | if len(data.Vars) > 1 {
87 | t.Sparkline2 = makeSparkline(data.Vars[1])
88 | }
89 |
90 | t.Relayout()
91 |
92 | return nil
93 | }
94 |
95 | // Update updates UI widgets from UIData.
96 | func (t *TermUI) Update(data UIData) {
97 | t.Title.Text = fmt.Sprintf("monitoring %d services every %v, press q to quit", len(data.Services), *interval)
98 | t.Status.Text = fmt.Sprintf("Last update: %v", data.LastTimestamp.Format(time.Stamp))
99 |
100 | // List with service names
101 | var services []string
102 | for _, service := range data.Services {
103 | services = append(services, StatusLine(service))
104 | }
105 | t.Services.Items = services
106 |
107 | // Lists with values
108 | for i, name := range data.Vars {
109 | var lines []string
110 | for _, service := range data.Services {
111 | lines = append(lines, service.Value(name))
112 | }
113 | t.Lists[i].Items = lines
114 | }
115 |
116 | // Sparklines
117 | for i, service := range data.Services {
118 | max := formatMax(service.Max(data.Vars[0]))
119 | t.Sparkline1.Lines[i].Title = fmt.Sprintf("%s%s", service.Name, max)
120 | t.Sparkline1.Lines[i].Data = service.Values(data.Vars[0])
121 |
122 | if len(data.Vars) > 1 {
123 | max = formatMax(service.Max(data.Vars[1]))
124 | t.Sparkline2.Lines[i].Title = fmt.Sprintf("%s%s", service.Name, max)
125 | t.Sparkline2.Lines[i].Data = service.Values(data.Vars[1])
126 | }
127 | }
128 |
129 | t.Relayout()
130 |
131 | var widgets []termui.Bufferer
132 | widgets = append(widgets, t.Title, t.Status, t.Services, t.Sparkline1)
133 | for _, list := range t.Lists {
134 | widgets = append(widgets, list)
135 | }
136 | if t.Sparkline2 != nil {
137 | widgets = append(widgets, t.Sparkline2)
138 | }
139 | termui.Render(widgets...)
140 | }
141 |
142 | // Relayout recalculates widgets sizes and coords.
143 | func (t *TermUI) Relayout() {
144 | tw, th := termui.TermWidth(), termui.TermHeight()
145 | h := th
146 |
147 | // First row: Title and Status pars
148 | firstRowH := 3
149 | t.Title.Height = firstRowH
150 | t.Title.Width = tw / 2
151 | if tw%2 == 1 {
152 | t.Title.Width++
153 | }
154 | t.Status.Height = firstRowH
155 | t.Status.Width = tw / 2
156 | t.Status.X = t.Title.X + t.Title.Width
157 | h -= firstRowH
158 |
159 | // Second Row: lists
160 | num := len(t.Lists) + 1
161 | listW := tw / num
162 |
163 | // Services list must have visible names
164 | minNameWidth := 20
165 | t.Services.Width = minNameWidth
166 | if listW > minNameWidth {
167 | t.Services.Width = listW
168 | }
169 |
170 | // Recalculate widths for each list
171 | listW = (tw - t.Services.Width) / (num - 1)
172 |
173 | // Finally, enlarge services list, if there is a space left
174 | if listW*(num-1)+t.Services.Width < tw {
175 | t.Services.Width = tw - (listW * (num - 1))
176 | }
177 |
178 | t.Services.Y = th - h
179 |
180 | for i, list := range t.Lists {
181 | list.Y = th - h
182 | list.Width = listW
183 | list.Height = len(t.Lists[0].Items) + 2
184 | list.X = t.Services.X + t.Services.Width + i*listW
185 | }
186 | h -= t.Lists[0].Height
187 |
188 | // Third row: sparklines for two vars
189 | t.Sparkline1.Width = tw
190 | t.Sparkline1.Height = h
191 | t.Sparkline1.Y = th - h
192 | if t.Sparkline2 != nil {
193 | t.Sparkline1.Width = tw / 2
194 |
195 | t.Sparkline2.Width = tw / 2
196 | if tw%2 == 1 {
197 | t.Sparkline2.Width++
198 | }
199 | t.Sparkline2.X = t.Sparkline1.X + t.Sparkline1.Width
200 | t.Sparkline2.Height = h
201 | t.Sparkline2.Y = th - h
202 | }
203 |
204 | }
205 |
206 | // Close shuts down UI module.
207 | func (t *TermUI) Close() {
208 | termui.Close()
209 | }
210 |
211 | // StatusLine returns status line for service with it's name and status.
212 | func StatusLine(s *Service) string {
213 | if s.Err != nil {
214 | return fmt.Sprintf("[E] ⛔ %s failed", s.Name)
215 | }
216 |
217 | if s.Restarted {
218 | return fmt.Sprintf("[R] 🔥 %s", s.Name)
219 | }
220 |
221 | return fmt.Sprintf("[R] %s", s.Name)
222 | }
223 |
224 | func colorByKind(kind VarKind) termui.Attribute {
225 | switch kind {
226 | case KindMemory:
227 | return termui.ColorRed | termui.AttrBold
228 | case KindDuration:
229 | return termui.ColorYellow | termui.AttrBold
230 | case KindString:
231 | return termui.ColorGreen | termui.AttrBold
232 | default:
233 | return termui.ColorBlue | termui.AttrBold
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/ui_single.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "time"
6 |
7 | "github.com/gizak/termui"
8 | )
9 |
10 | // TermUISingle is a termUI implementation of UI interface.
11 | type TermUISingle struct {
12 | Title *termui.Paragraph
13 | Status *termui.Paragraph
14 | Sparklines map[VarName]*termui.Sparkline
15 | Sparkline *termui.Sparklines
16 | Pars []*termui.Paragraph
17 | }
18 |
19 | // Init creates widgets, sets sizes and labels.
20 | func (t *TermUISingle) Init(data UIData) error {
21 | err := termui.Init()
22 | if err != nil {
23 | return err
24 | }
25 |
26 | t.Sparklines = make(map[VarName]*termui.Sparkline)
27 |
28 | t.Title = func() *termui.Paragraph {
29 | p := termui.NewParagraph("")
30 | p.Height = 3
31 | p.TextFgColor = termui.ColorWhite
32 | p.Border = true
33 | p.BorderLabel = "Services Monitor"
34 | p.BorderFg = termui.ColorCyan
35 | return p
36 | }()
37 | t.Status = func() *termui.Paragraph {
38 | p := termui.NewParagraph("")
39 | p.Height = 3
40 | p.TextFgColor = termui.ColorWhite
41 | p.Border = true
42 | p.BorderLabel = "Status"
43 | p.BorderFg = termui.ColorCyan
44 | return p
45 | }()
46 |
47 | t.Pars = make([]*termui.Paragraph, len(data.Vars))
48 | for i, name := range data.Vars {
49 | par := termui.NewParagraph("")
50 | par.TextFgColor = colorByKind(name.Kind())
51 | par.Border = true
52 | par.BorderLabel = name.Short()
53 | par.BorderLabelFg = termui.ColorGreen
54 | par.Height = 3
55 | t.Pars[i] = par
56 | }
57 |
58 | var sparklines []termui.Sparkline
59 | for _, name := range data.Vars {
60 | spl := termui.NewSparkline()
61 | spl.Height = 1
62 | spl.TitleColor = colorByKind(name.Kind())
63 | spl.LineColor = colorByKind(name.Kind())
64 | spl.Title = name.Long()
65 | sparklines = append(sparklines, spl)
66 | }
67 |
68 | t.Sparkline = func() *termui.Sparklines {
69 | s := termui.NewSparklines(sparklines...)
70 | s.Height = 2*len(sparklines) + 2
71 | s.Border = true
72 | s.BorderLabel = fmt.Sprintf("Monitoring")
73 | return s
74 | }()
75 |
76 | t.Relayout()
77 |
78 | return nil
79 | }
80 |
81 | // Update updates UI widgets from UIData.
82 | func (t *TermUISingle) Update(data UIData) {
83 | // single mode assumes we have one service only to monitor
84 | service := data.Services[0]
85 |
86 | t.Title.Text = fmt.Sprintf("monitoring %s every %v, press q to quit", service.Name, *interval)
87 | t.Status.Text = fmt.Sprintf("Last update: %v", data.LastTimestamp.Format(time.Stamp))
88 |
89 | // Pars
90 | for i, name := range data.Vars {
91 | t.Pars[i].Text = service.Value(name)
92 | }
93 |
94 | // Sparklines
95 | for i, name := range data.Vars {
96 | spl := &t.Sparkline.Lines[i]
97 |
98 | max := formatMax(service.Max(name))
99 | spl.Title = fmt.Sprintf("%s: %v%s", name.Long(), service.Value(name), max)
100 | spl.TitleColor = colorByKind(name.Kind())
101 | spl.LineColor = colorByKind(name.Kind())
102 |
103 | if name.Kind() == KindString {
104 | continue
105 | }
106 | spl.Data = service.Values(name)
107 | }
108 |
109 | t.Relayout()
110 |
111 | var widgets []termui.Bufferer
112 | widgets = append(widgets, t.Title, t.Status, t.Sparkline)
113 | for _, par := range t.Pars {
114 | widgets = append(widgets, par)
115 | }
116 | termui.Render(widgets...)
117 | }
118 |
119 | // Close shuts down UI module.
120 | func (t *TermUISingle) Close() {
121 | termui.Close()
122 | }
123 |
124 | // Relayout recalculates widgets sizes and coords.
125 | func (t *TermUISingle) Relayout() {
126 | tw, th := termui.TermWidth(), termui.TermHeight()
127 | h := th
128 |
129 | // First row: Title and Status pars
130 | firstRowH := 3
131 | t.Title.Height = firstRowH
132 | t.Title.Width = tw / 2
133 | if tw%2 == 1 {
134 | t.Title.Width++
135 | }
136 | t.Status.Height = firstRowH
137 | t.Status.Width = tw / 2
138 | t.Status.X = t.Title.X + t.Title.Width
139 | h -= firstRowH
140 |
141 | // Second row: lists
142 | secondRowH := 3
143 | num := len(t.Pars)
144 | parW := tw / num
145 | for i, par := range t.Pars {
146 | par.Y = th - h
147 | par.Width = parW
148 | par.Height = secondRowH
149 | par.X = i * parW
150 | }
151 | if num*parW < tw {
152 | t.Pars[num-1].Width = tw - ((num - 1) * parW)
153 | }
154 | h -= secondRowH
155 |
156 | // Third row: Sparklines
157 | t.Sparkline.Width = tw
158 | t.Sparkline.Height = h
159 | t.Sparkline.Y = th - h
160 | }
161 |
162 | func formatMax(max interface{}) string {
163 | var str string
164 | if max != nil {
165 | str = fmt.Sprintf(" (max: %v)", max)
166 | }
167 | return str
168 | }
169 |
--------------------------------------------------------------------------------
/utils.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "net/url"
7 | "path/filepath"
8 | "strings"
9 |
10 | "github.com/bsiegert/ranges"
11 | )
12 |
13 | var ErrParsePorts = fmt.Errorf("cannot parse ports argument")
14 |
15 | // ParseVars returns parsed and validated slice of strings with
16 | // variables names that will be used for monitoring.
17 | func ParseVars(vars string) ([]VarName, error) {
18 | if vars == "" {
19 | return nil, errors.New("no vars specified")
20 | }
21 |
22 | ss := strings.FieldsFunc(vars, func(r rune) bool { return r == ',' })
23 | var ret []VarName
24 | for _, s := range ss {
25 | ret = append(ret, VarName(s))
26 | }
27 | return ret, nil
28 | }
29 |
30 | // BaseCommand returns cleaned command name from Cmdline array.
31 | //
32 | // I.e. "./some.service/binary.name -arg 1 -arg" will be "binary.name".
33 | func BaseCommand(cmdline []string) string {
34 | if len(cmdline) == 0 {
35 | return ""
36 | }
37 | return filepath.Base(cmdline[0])
38 | }
39 |
40 | // flattenURLs returns URLs for the given addr and set of ports.
41 | //
42 | // Note, rawurl shouldn't contain port, as port will be appended.
43 | func flattenURLs(rawurl string, ports []string) ([]url.URL, error) {
44 | var urls []url.URL
45 |
46 | // Add http by default
47 | if !strings.HasPrefix(rawurl, "http") {
48 | rawurl = fmt.Sprintf("http://%s", rawurl)
49 | }
50 |
51 | // Make URL from rawurl
52 | baseURL, err := url.Parse(rawurl)
53 | if err != nil {
54 | return nil, err
55 | }
56 | if baseURL.Path == "" {
57 | baseURL.Path = DefaultEndpoint
58 | }
59 |
60 | // Create new URL for each port
61 | for _, port := range ports {
62 | u := *baseURL
63 | u.Host = fmt.Sprintf("%s:%s", u.Host, port)
64 | urls = append(urls, u)
65 | }
66 | return urls, nil
67 | }
68 |
69 | // ParsePorts parses and flattens comma-separated ports/urls into URLs slice
70 | func ParsePorts(s string) ([]url.URL, error) {
71 | var urls []url.URL
72 | fields := strings.FieldsFunc(s, func(r rune) bool { return r == ',' })
73 | for _, field := range fields {
74 | rawurl, portsRange := extractURLAndPorts(field)
75 |
76 | ports, err := parseRange(portsRange)
77 | if err != nil {
78 | return nil, ErrParsePorts
79 | }
80 |
81 | purls, err := flattenURLs(rawurl, ports)
82 | if err != nil {
83 | return nil, ErrParsePorts
84 | }
85 |
86 | urls = append(urls, purls...)
87 | }
88 |
89 | return urls, nil
90 | }
91 |
92 | // extractUrlAndPorts attempts to split url and extract raw url
93 | // for the single port and range of ports to parse.
94 | //
95 | // i.e. "http://name:1234-1236/_endpoint" would return "http://name/_endpoint" and
96 | // "1234-1236"
97 | func extractURLAndPorts(s string) (string, string) {
98 | var rawurl, ports string
99 | parts := strings.Split(s, ":")
100 | switch len(parts) {
101 | case 1:
102 | // "1234-234"
103 | rawurl = "http://localhost"
104 | ports = parts[0]
105 | case 2:
106 | // "localhost:1234"
107 | rawurl, ports = parts[0], parts[1]
108 | default:
109 | // "https://user:pass@remote.name:1234" or "http://name:1234-1236/_endpoint"
110 |
111 | // construct endpoint from the first part of URI, before ports appera
112 | rawurl = strings.Join(parts[:len(parts)-1], ":")
113 |
114 | // get either "1234-1235" or "1234-1235/_endpoint"
115 | lastPart := parts[len(parts)-1]
116 |
117 | // try to find endpoint and attach it to rawurl
118 | fields := strings.SplitN(lastPart, "/", 2)
119 | ports = fields[0]
120 | if len(fields) > 1 {
121 | rawurl = fmt.Sprintf("%s/%s", rawurl, fields[1])
122 | }
123 | }
124 |
125 | return rawurl, ports
126 | }
127 |
128 | // parseRange flattens port ranges, such as "1234-1240,1333"
129 | func parseRange(s string) ([]string, error) {
130 | portsInt, err := ranges.Parse(s)
131 | if err != nil {
132 | return nil, err
133 | }
134 |
135 | var ports []string
136 | for _, port := range portsInt {
137 | ports = append(ports, fmt.Sprintf("%d", port))
138 | }
139 | return ports, nil
140 | }
141 |
142 | // NewURL returns net.URL for the given port, with expvarmon defaults set.
143 | func NewURL(port string) url.URL {
144 | return url.URL{
145 | Scheme: "http",
146 | Host: fmt.Sprintf("localhost:%s", port),
147 | Path: "/debug/vars",
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/utils_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "testing"
4 |
5 | func TestUtils(t *testing.T) {
6 | str := "memstats.Alloc,memstats.Sys"
7 |
8 | vars, err := ParseVars(str)
9 | if err != nil {
10 | t.Fatalf("Err not nil: %v", err)
11 | }
12 |
13 | if len(vars) != 2 {
14 | t.Fatalf("vars should contain 2 elements, but has %d", len(vars))
15 | }
16 |
17 | str = "memstats.Alloc,memstats.Sys,goroutines,Counter.A"
18 |
19 | vars, err = ParseVars(str)
20 | if err != nil {
21 | t.Fatalf("Err not nil: %v", err)
22 | }
23 |
24 | if len(vars) != 4 {
25 | t.Fatalf("vars should contain 4 elements, but has %d", len(vars))
26 | }
27 | }
28 |
29 | func TestExtractUrlAndPorts(t *testing.T) {
30 | var rawurl, ports string
31 | rawurl, ports = extractURLAndPorts("40000-40002")
32 | if rawurl != "http://localhost" || ports != "40000-40002" {
33 | t.Fatalf("extract url and ports failed: %v, %v", rawurl, ports)
34 | }
35 |
36 | rawurl, ports = extractURLAndPorts("https://example.com:1234")
37 | if rawurl != "https://example.com" || ports != "1234" {
38 | t.Fatalf("extract url and ports failed: %v, %v", rawurl, ports)
39 | }
40 |
41 | rawurl, ports = extractURLAndPorts("http://user:passwd@example.com:1234-1256")
42 | if rawurl != "http://user:passwd@example.com" || ports != "1234-1256" {
43 | t.Fatalf("extract url and ports failed: %v, %v", rawurl, ports)
44 | }
45 |
46 | rawurl, ports = extractURLAndPorts("https://example.com:1234-1256/_endpoint")
47 | if rawurl != "https://example.com/_endpoint" || ports != "1234-1256" {
48 | t.Fatalf("extract url and ports failed: %v, %v", rawurl, ports)
49 | }
50 | }
51 |
52 | func TestPorts(t *testing.T) {
53 | arg := "1234,1235"
54 | ports, err := ParsePorts(arg)
55 | if err != nil {
56 | t.Fatal(err)
57 | }
58 | if len(ports) != 2 || ports[0].Host != "localhost:1234" {
59 | t.Fatalf("ParsePorts returns wrong data: %v", ports)
60 | }
61 |
62 | arg = "1234-1237,2000"
63 | ports, err = ParsePorts(arg)
64 | if err != nil {
65 | t.Fatal(err)
66 | }
67 | if len(ports) != 5 || ports[0].Host != "localhost:1234" || ports[4].Host != "localhost:2000" {
68 | t.Fatalf("ParsePorts returns wrong data: %v", ports)
69 | }
70 |
71 | arg = "40000-40002,localhost:2000-2002,remote:1234-1235,https://example.com:1234-1236"
72 | ports, err = ParsePorts(arg)
73 | if err != nil {
74 | t.Fatal(err)
75 | }
76 | if len(ports) != 11 ||
77 | ports[0].Host != "localhost:40000" ||
78 | ports[3].Host != "localhost:2000" ||
79 | ports[7].Host != "remote:1235" ||
80 | ports[7].Path != "/debug/vars" ||
81 | ports[10].Host != "example.com:1236" ||
82 | ports[10].Scheme != "https" {
83 | t.Fatalf("ParsePorts returns wrong data: %v", ports)
84 | }
85 |
86 | // Test Auth
87 | arg = "http://user:pass@localhost:2000-2002"
88 | ports, err = ParsePorts(arg)
89 | if err != nil {
90 | t.Fatal(err)
91 | }
92 | pass, isSet := ports[0].User.Password()
93 | if len(ports) != 3 ||
94 | ports[0].User.Username() != "user" ||
95 | pass != "pass" || !isSet ||
96 | ports[0].Scheme != "http" {
97 | t.Fatalf("ParsePorts returns wrong data: %v", ports)
98 | }
99 |
100 | arg = "localhost:2000-2002,remote:1234-1235,some:weird:1234-123input"
101 | _, err = ParsePorts(arg)
102 | if err == nil {
103 | t.Fatalf("err shouldn't be nil")
104 | }
105 |
106 | arg = "string,sdasd"
107 | _, err = ParsePorts(arg)
108 | if err == nil {
109 | t.Fatalf("err shouldn't be nil")
110 | }
111 |
112 | // Test endpoints
113 | arg = "localhost:2000,https://example.com:1234/_custom_expvars"
114 | ports, err = ParsePorts(arg)
115 | if err != nil {
116 | t.Fatal(err)
117 | }
118 | if ports[0].Path != "/debug/vars" || ports[1].Path != "/_custom_expvars" {
119 | t.Fatalf("ParsePorts returns wrong data: %v", ports)
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/var.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | "time"
7 |
8 | "github.com/pyk/byten"
9 | )
10 |
11 | // VarName represents variable name.
12 | //
13 | // It has dot-separated format, like "memstats.Alloc",
14 | // but can be used in different forms, hence it's own type.
15 | //
16 | // It also can have optional "kind:" modifier, like "mem:" or "duration:"
17 | type VarName string
18 |
19 | // VarKind specifies special kinds of values, affects formatting.
20 | type VarKind int
21 |
22 | // VarValue represents arbitrary value for variable.
23 | type VarValue interface{}
24 |
25 | const (
26 | KindDefault VarKind = iota
27 | KindMemory
28 | KindDuration
29 | KindString
30 | )
31 |
32 | // ToSlice converts "dot-separated" notation into the "slice of strings".
33 | //
34 | // "dot-separated" notation is a human-readable format, passed via args.
35 | // "slice of strings" is used by Jason library.
36 | //
37 | // Example: "memstats.Alloc" => []string{"memstats", "Alloc"}
38 | // Example: "mem:memstats.Alloc" => []string{"memstats", "Alloc"}
39 | func (v VarName) ToSlice() []string {
40 | start := strings.IndexRune(string(v), ':') + 1
41 | slice := DottedFieldsToSliceEscaped(string(v)[start:])
42 | return slice
43 | }
44 |
45 | // Short returns short name, which is typically is the last word in the long names.
46 | func (v VarName) Short() string {
47 | if v == "" {
48 | return ""
49 | }
50 |
51 | slice := v.ToSlice()
52 | return slice[len(slice)-1]
53 | }
54 |
55 | // Long returns long name, without kind: modifier.
56 | func (v VarName) Long() string {
57 | if v == "" {
58 | return ""
59 | }
60 |
61 | start := strings.IndexRune(string(v), ':') + 1
62 | return string(v)[start:]
63 | }
64 |
65 | // Kind returns kind of variable, based on it's name modifiers ("mem:")
66 | func (v VarName) Kind() VarKind {
67 | start := strings.IndexRune(string(v), ':')
68 | if start == -1 {
69 | return KindDefault
70 | }
71 |
72 | switch string(v)[:start] {
73 | case "mem":
74 | return KindMemory
75 | case "duration":
76 | return KindDuration
77 | case "str":
78 | return KindString
79 | }
80 | return KindDefault
81 | }
82 |
83 | // Format returns human-readable var value representation.
84 | func Format(v VarValue, kind VarKind) string {
85 | switch kind {
86 | case KindMemory:
87 | if _, ok := v.(int64); !ok {
88 | break
89 | }
90 | return fmt.Sprintf("%s", byten.Size(v.(int64)))
91 | case KindDuration:
92 | if _, ok := v.(int64); !ok {
93 | break
94 | }
95 | return fmt.Sprintf("%s", roundDuration(time.Duration(v.(int64))))
96 | }
97 |
98 | if f, ok := v.(float64); ok {
99 | return fmt.Sprintf("%.2f", f)
100 | }
101 |
102 | return fmt.Sprintf("%v", v)
103 | }
104 |
105 | // roundDuration removes unneeded precision from the String() output for time.Duration.
106 | func roundDuration(d time.Duration) time.Duration {
107 | r := time.Second
108 | if d < time.Second {
109 | r = time.Millisecond
110 | }
111 | if d < time.Millisecond {
112 | r = time.Microsecond
113 | }
114 | if r <= 0 {
115 | return d
116 | }
117 | neg := d < 0
118 | if neg {
119 | d = -d
120 | }
121 | if m := d % r; m+m < r {
122 | d = d - m
123 | } else {
124 | d = d + r - m
125 | }
126 | if neg {
127 | return -d
128 | }
129 | return d
130 | }
131 |
132 | func DottedFieldsToSliceEscaped(s string) []string {
133 | rv := make([]string, 0)
134 | lastSlash := false
135 | curr := ""
136 | for _, r := range s {
137 | // base case, dot not after slash
138 | if !lastSlash && r == '.' {
139 | if len(curr) > 0 {
140 | rv = append(rv, curr)
141 | curr = ""
142 | }
143 | continue
144 | } else if !lastSlash {
145 | // any character not after slash
146 | curr += string(r)
147 | if r == '\\' {
148 | lastSlash = true
149 | } else {
150 | lastSlash = false
151 | }
152 | continue
153 | } else if r == '\\' {
154 | // last was slash, and so is this
155 | lastSlash = false // 2 slashes = 0
156 | // we already appended a single slash on first
157 | continue
158 | } else if r == '.' {
159 | // we see \. but already appended \ last time
160 | // replace it with .
161 | curr = curr[:len(curr)-1] + "."
162 | lastSlash = false
163 | } else {
164 | // \ and any other character, ignore
165 | curr += string(r)
166 | lastSlash = false
167 | continue
168 | }
169 | }
170 | if len(curr) > 0 {
171 | rv = append(rv, curr)
172 | }
173 | return rv
174 | }
175 |
--------------------------------------------------------------------------------
/var_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestVarName(t *testing.T) {
8 | v := VarName("memstats.Alloc")
9 |
10 | slice := v.ToSlice()
11 | if len(slice) != 2 || slice[0] != "memstats" || slice[1] != "Alloc" {
12 | t.Fatalf("ToSlice failed: %v", slice)
13 | }
14 |
15 | short := v.Short()
16 | if short != "Alloc" {
17 | t.Fatalf("Expecting Short() to be 'Alloc', but got: %s", short)
18 | }
19 |
20 | kind := v.Kind()
21 | if kind != KindDefault {
22 | t.Fatalf("Expecting kind to be %v, but got: %v", KindDefault, kind)
23 | }
24 |
25 | v = VarName("mem:memstats.Alloc")
26 |
27 | slice = v.ToSlice()
28 | if len(slice) != 2 || slice[0] != "memstats" || slice[1] != "Alloc" {
29 | t.Fatalf("ToSlice failed: %v", slice)
30 | }
31 |
32 | short = v.Short()
33 | if short != "Alloc" {
34 | t.Fatalf("Expecting Short() to be 'Alloc', but got: %s", short)
35 | }
36 |
37 | kind = v.Kind()
38 | if kind != KindMemory {
39 | t.Fatalf("Expecting kind to be %v, but got: %v", KindMemory, kind)
40 | }
41 |
42 | v = VarName("duration:ResponseTimes.API.Users")
43 | kind = v.Kind()
44 | if kind != KindDuration {
45 | t.Fatalf("Expecting kind to be %v, but got: %v", KindDuration, kind)
46 | }
47 |
48 | // single \. escapes the dot
49 | v = VarName(`bleve.indexes.bench\.bleve.index.lookup_queue_len`)
50 |
51 | slice = v.ToSlice()
52 | if len(slice) != 5 || slice[0] != "bleve" || slice[1] != "indexes" || slice[2] != "bench.bleve" ||
53 | slice[3] != "index" || slice[4] != "lookup_queue_len" {
54 | t.Fatalf("ToSlice failed: %v", slice)
55 | }
56 |
57 | // double \\. escapes backslash, not dot
58 | v = VarName(`bleve.indexes.bench\\.bleve.index.lookup_queue_len`)
59 |
60 | slice = v.ToSlice()
61 | if len(slice) != 6 || slice[0] != "bleve" || slice[1] != "indexes" || slice[2] != "bench\\" ||
62 | slice[3] != "bleve" || slice[4] != "index" || slice[5] != "lookup_queue_len" {
63 | t.Fatalf("ToSlice failed: %v", slice)
64 | }
65 |
66 | // triple \\\. escapes backslash then dot
67 | v = VarName(`bleve.indexes.bench\\\.bleve.index.lookup_queue_len`)
68 |
69 | slice = v.ToSlice()
70 | if len(slice) != 5 || slice[0] != "bleve" || slice[1] != "indexes" || slice[2] != "bench\\.bleve" ||
71 | slice[3] != "index" || slice[4] != "lookup_queue_len" {
72 | t.Fatalf("ToSlice failed: %v", slice)
73 | }
74 |
75 | // quadruple \\\\. escapes two backslashes, not dot
76 | v = VarName(`bleve.indexes.bench\\\\.bleve.index.lookup_queue_len`)
77 |
78 | slice = v.ToSlice()
79 | if len(slice) != 6 || slice[0] != "bleve" || slice[1] != "indexes" || slice[2] != "bench\\\\" ||
80 | slice[3] != "bleve" || slice[4] != "index" || slice[5] != "lookup_queue_len" {
81 | t.Fatalf("ToSlice failed: %v", slice)
82 | }
83 |
84 | // unsupported \x passes through unaltered
85 | v = VarName(`bleve.indexes.bench\xbleve.index.lookup_queue_len`)
86 |
87 | slice = v.ToSlice()
88 | if len(slice) != 5 || slice[0] != "bleve" || slice[1] != "indexes" || slice[2] != "bench\\xbleve" ||
89 | slice[3] != "index" || slice[4] != "lookup_queue_len" {
90 | t.Fatalf("ToSlice failed: %v", slice)
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/vendor/github.com/antonholmquist/jason/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
--------------------------------------------------------------------------------
/vendor/github.com/antonholmquist/jason/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 |
3 | go:
4 | - 1.1
5 | - 1.2
6 | - 1.3
7 | - tip
8 |
--------------------------------------------------------------------------------
/vendor/github.com/antonholmquist/jason/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Anton Holmquist, anton.holmquist@gmail.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/vendor/github.com/antonholmquist/jason/README.md:
--------------------------------------------------------------------------------
1 |
2 | 
3 |
4 | Jason is an easy-to-use JSON library for Go.
5 |
6 | [](https://travis-ci.org/antonholmquist/jason) [](https://godoc.org/github.com/antonholmquist/jason) [](https://raw.githubusercontent.com/antonholmquist/jason/master/LICENSE)
7 |
8 | # About
9 |
10 | Jason is designed to be convenient for reading arbitrary JSON while still honoring the strictness of the language. Inspired by other libraries and improved to work well for common use cases. It currently focuses on reading JSON data rather than creating it. [API Documentation](http://godoc.org/github.com/antonholmquist/jason) can be found on godoc.org.
11 |
12 | ## Install
13 |
14 | ```shell
15 | go get github.com/antonholmquist/jason
16 | ```
17 |
18 | ## Import
19 |
20 | ```go
21 | import (
22 | "github.com/antonholmquist/jason"
23 | )
24 | ```
25 |
26 | ## Data types
27 |
28 | The following golang values are used to represent JSON data types. It is consistent with how `encoding/json` uses primitive types.
29 |
30 | - `bool`, for JSON booleans
31 | - `json.Number/float64/int64`, for JSON numbers
32 | - `string`, for JSON strings
33 | - `[]*Value`, for JSON arrays
34 | - `map[string]*Value`, for JSON objects
35 | - `nil` for JSON null
36 |
37 | ## Examples
38 |
39 | ### Create from bytes
40 |
41 | Create object from bytes. Returns an error if the bytes are not valid JSON.
42 |
43 | ```go
44 | v, err := jason.NewObjectFromBytes(b)
45 |
46 | ```
47 |
48 | If the root object is not an array, use this method instead. It can then be cased to the expected type with one of the As-Methods.
49 |
50 | ```go
51 | v, err := jason.NewValueFromBytes(b)
52 |
53 | ```
54 |
55 | ### Create from a reader (like a http response)
56 |
57 | Create value from a io.reader. Returns an error if the string couldn't be parsed.
58 |
59 | ```go
60 | v, err := jason.NewObjectFromReader(res.Body)
61 |
62 | ```
63 |
64 | ### Read values
65 |
66 | Reading values is easy. If the key path is invalid or type doesn't match, it will return an error and the default value.
67 |
68 | ```go
69 | name, err := v.GetString("name")
70 | age, err := v.GetInt64("age")
71 | verified, err := v.GetBoolean("verified")
72 | education, err := v.GetObject("education")
73 | friends, err := v.GetObjectArray("friends")
74 | interests, err := v.GetStringArray("interests")
75 |
76 | ```
77 |
78 | ### Read nested values
79 |
80 | Reading nested values is easy. If the path is invalid or type doesn't match, it will return the default value and an error.
81 |
82 | ```go
83 | name, err := v.GetString("person", "name")
84 | age, err := v.GetInt64("person", "age")
85 | verified, err := v.GetBoolean("person", "verified")
86 | education, err := v.GetObject("person", "education")
87 | friends, err := v.GetObjectArray("person", "friends")
88 |
89 | ```
90 |
91 | ### Loop through array
92 |
93 | Looping through an array is done with `GetValueArray()` or `GetObjectArray()`. It returns an error if the value at that keypath is null (or something else than an array).
94 |
95 | ```go
96 | friends, err := person.GetObjectArray("friends")
97 | for _, friend := range friends {
98 | name, err := friend.GetString("name")
99 | age, err := friend.GetNumber("age")
100 | }
101 | ```
102 |
103 | ### Loop through object
104 |
105 | Looping through an object is easy. `GetObject()` returns an error if the value at that keypath is null (or something else than an object).
106 |
107 | ```go
108 | person, err := person.GetObject("person")
109 | for key, value := range person.Map() {
110 | ...
111 | }
112 | ```
113 |
114 | ## Sample App
115 |
116 | Example project:
117 |
118 | ```go
119 | package main
120 |
121 | import (
122 | "github.com/antonholmquist/jason"
123 | "log"
124 | )
125 |
126 | func main() {
127 |
128 | exampleJSON := `{
129 | "name": "Walter White",
130 | "age": 51,
131 | "children": [
132 | "junior",
133 | "holly"
134 | ],
135 | "other": {
136 | "occupation": "chemist",
137 | "years": 23
138 | }
139 | }`
140 |
141 | v, _ := jason.NewObjectFromBytes([]byte(exampleJSON))
142 |
143 | name, _ := v.GetString("name")
144 | age, _ := v.GetNumber("age")
145 | occupation, _ := v.GetString("other", "occupation")
146 | years, _ := v.GetNumber("other", "years")
147 |
148 | log.Println("age:", age)
149 | log.Println("name:", name)
150 | log.Println("occupation:", occupation)
151 | log.Println("years:", years)
152 |
153 | children, _ := v.GetStringArray("children")
154 | for i, child := range children {
155 | log.Printf("child %d: %s", i, child)
156 | }
157 |
158 | others, _ := v.GetObject("other")
159 |
160 | for _, value := range others.Map() {
161 |
162 | s, sErr := value.String()
163 | n, nErr := value.Number()
164 |
165 | if sErr == nil {
166 | log.Println("string value: ", s)
167 | } else if nErr == nil {
168 | log.Println("number value: ", n)
169 | }
170 | }
171 | }
172 |
173 | ```
174 |
175 | ## Documentation
176 |
177 | Documentation can be found a godoc:
178 |
179 | https://godoc.org/github.com/antonholmquist/jason
180 |
181 | ## Test
182 | To run the project tests:
183 |
184 | ```shell
185 | go test
186 | ```
187 |
188 | ## Compatibility
189 |
190 | Go 1.1 and up.
191 |
192 | ## Where does the name come from?
193 |
194 | I remebered it from an email one of our projects managers sent a couple of years ago.
195 |
196 | > "Don't worry. We can handle both XML and Jason"
197 |
198 | ## Author
199 |
200 | Anton Holmquist, http://twitter.com/antonholmquist
201 |
--------------------------------------------------------------------------------
/vendor/github.com/bsiegert/ranges/Makefile:
--------------------------------------------------------------------------------
1 | # Copyright 2011 The Go Authors. All rights reserved.
2 | # Use of this source code is governed by a BSD-style
3 | # license that can be found in the LICENSE file.
4 |
5 | include $(GOROOT)/src/Make.inc
6 |
7 | TARG=ranges
8 | GOFILES=\
9 | ranges.go\
10 |
11 | include $(GOROOT)/src/Make.pkg
12 |
13 | README: $(GOFILES)
14 | godoc . > README
15 |
--------------------------------------------------------------------------------
/vendor/github.com/bsiegert/ranges/README:
--------------------------------------------------------------------------------
1 | PACKAGE
2 |
3 | package ranges
4 | import "."
5 |
6 | Package ranges contains tools for working with integer ranges.
7 |
8 |
9 | An "integer range" allows to give a set of numbers as a string, which
10 | can be parsed by a call to Parse. The result can be obtained as a slice
11 | of integers by calling Expand or be tested against with Contains.
12 |
13 | FUNCTIONS
14 |
15 | func Parse(r string) ([]int, error)
16 |
17 |
18 | TYPES
19 |
20 | type IntRange struct {
21 | Lo, Hi int
22 | }
23 | An IntRange is a single component of an integer range expression.
24 |
25 | func (ir *IntRange) Clean()
26 | Clean exchanges the upper and lower bound if the upper bound is smaller
27 | than the lower one.
28 |
29 | func (ir *IntRange) Contains(value int) bool
30 | Contains returns true if ir contains value.
31 |
32 | func (ir *IntRange) Expand() []int
33 | Expand returns a sorted slice of integers that contains all the numbers
34 | in ir.
35 |
36 | type IntRanges []IntRange
37 | IntRanges is a slice of multiple integer ranges, allowing the expression
38 | of non-contiguous ranges (for example "1,3-4").
39 |
40 | func (ir *IntRanges) Contains(value int) bool
41 | Contains returns true if ir contains value.
42 |
43 | func (ir *IntRanges) Expand() []int
44 | Expand returns a slice of integers that contains all the numbers in ir.
45 | If ir has been cleaned by calling Clean, the slice will be sorted.
46 |
47 | func (ir *IntRanges) Len() int
48 | Len returns the number of distinct ranges in ir.
49 |
50 | func (ir *IntRanges) Less(i, j int) bool
51 | Less returns true if the lower bound of the i-th element is smaller than
52 | the one of the j-th element.
53 |
54 | func (ir *IntRanges) Swap(i, j int)
55 | Swap swaps the i-th and the j-th element.
56 |
57 |
58 |
--------------------------------------------------------------------------------
/vendor/github.com/bsiegert/ranges/ranges.go:
--------------------------------------------------------------------------------
1 | /*-
2 | * Copyright (c) 2011
3 | * Benny Siegert
4 | *
5 | * Provided that these terms and disclaimer and all copyright notices
6 | * are retained or reproduced in an accompanying document, permission
7 | * is granted to deal in this work without restriction, including un-
8 | * limited rights to use, publicly perform, distribute, sell, modify,
9 | * merge, give away, or sublicence.
10 | *
11 | * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
12 | * the utmost extent permitted by applicable law, neither express nor
13 | * implied; without malicious intent or gross negligence. In no event
14 | * may a licensor, author or contributor be held liable for indirect,
15 | * direct, other damage, loss, or other issues arising in any way out
16 | * of dealing in the work, even if advised of the possibility of such
17 | * damage or existence of a defect, except proven that it results out
18 | * of said person's immediate fault when using the work as intended.
19 | */
20 |
21 | // Package ranges contains tools for working with integer ranges.
22 | //
23 | // An "integer range" allows to give a set of numbers as a string,
24 | // which can be parsed by a call to Parse. The result can be obtained
25 | // as a slice of integers by calling Expand or be tested against with
26 | // Contains.
27 | package ranges
28 |
29 | import (
30 | "fmt"
31 | "strconv"
32 | "strings"
33 | )
34 |
35 | // An IntRange is a single component of an integer range expression.
36 | type IntRange struct {
37 | Lo, Hi int
38 | }
39 |
40 | // Contains returns true if ir contains value.
41 | func (ir *IntRange) Contains(value int) bool {
42 | return value >= ir.Lo && value <= ir.Hi
43 | }
44 |
45 | // Expand returns a sorted slice of integers that contains all the numbers
46 | // in ir.
47 | func (ir *IntRange) Expand() []int {
48 | e := make([]int, 0, ir.Hi-ir.Lo+1)
49 | for i := ir.Lo; i <= ir.Hi; i++ {
50 | e = append(e, i)
51 | }
52 | return e
53 | }
54 |
55 | // Clean exchanges the upper and lower bound if the upper bound is
56 | // smaller than the lower one.
57 | func (ir *IntRange) Clean() {
58 | if ir.Hi < ir.Lo {
59 | ir.Hi, ir.Lo = ir.Lo, ir.Hi
60 | }
61 | }
62 |
63 | // IntRanges is a slice of multiple integer ranges, allowing the
64 | // expression of non-contiguous ranges (for example "1,3-4").
65 | type IntRanges []IntRange
66 |
67 | // Contains returns true if ir contains value.
68 | func (ir *IntRanges) Contains(value int) bool {
69 | for _, r := range *ir {
70 | if r.Contains(value) {
71 | return true
72 | }
73 | }
74 | return false
75 | }
76 |
77 | // Expand returns a slice of integers that contains all the numbers in ir.
78 | // If ir has been cleaned by calling Clean, the slice will be sorted.
79 | func (ir *IntRanges) Expand() []int {
80 | // This guess for the length is as good as any other.
81 | e := make([]int, 0, 2*len(*ir))
82 |
83 | for _, r := range *ir {
84 | e = append(e, r.Expand()...)
85 | }
86 | return e
87 | }
88 |
89 | // Len returns the number of distinct ranges in ir.
90 | func (ir *IntRanges) Len() int {
91 | return len(*ir)
92 | }
93 |
94 | // Less returns true if the lower bound of the i-th element is smaller
95 | // than the one of the j-th element.
96 | func (ir *IntRanges) Less(i, j int) bool {
97 | return (*ir)[i].Lo < (*ir)[j].Lo
98 | }
99 |
100 | // Swap swaps the i-th and the j-th element.
101 | func (ir *IntRanges) Swap(i, j int) {
102 | (*ir)[i], (*ir)[j] = (*ir)[j], (*ir)[i]
103 | }
104 |
105 | func Parse(r string) ([]int, error) {
106 | var expanded []int
107 |
108 | for _, item := range strings.Split(r, ",") {
109 | lohi := strings.Split(item, "-")
110 | switch len(lohi) {
111 | case 1:
112 | v, err := strconv.Atoi(item)
113 | if err != nil {
114 | return nil, err
115 | }
116 | expanded = append(expanded, v)
117 | case 2:
118 | lo, err := strconv.Atoi(lohi[0])
119 | if err != nil {
120 | return nil, err
121 | }
122 | hi, err := strconv.Atoi(lohi[1])
123 | if err != nil {
124 | return nil, err
125 | }
126 | for i := lo; i <= hi; i++ {
127 | expanded = append(expanded, i)
128 | }
129 | default:
130 | return nil, fmt.Errorf("invalid range: %s", item)
131 | }
132 | }
133 | return expanded, nil
134 | }
135 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 |
6 | # Folders
7 | _obj
8 | _test
9 |
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 |
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 |
20 | _testmain.go
21 |
22 | *.exe
23 | *.test
24 | *.prof
25 | .DS_Store
26 | /vendor
27 | .vscode/
28 | .mypy_cache/
29 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 |
3 | go:
4 | - tip
5 |
6 | script: go test -v ./
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 18/11/29
2 |
3 | - Move Tabpane from termui/extra to termui and rename it to TabPane
4 | - Rename PollEvent to PollEvents
5 |
6 | ## 18/11/28
7 |
8 | - Migrated from Dep to vgo
9 | - Overhauled the event system
10 | - check the wiki/examples for details
11 | - Renamed Par widget to Paragraph
12 | - Renamed MBarChart widget to StackedBarChart
13 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Zack Guo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/README.md:
--------------------------------------------------------------------------------
1 | # termui
2 |
3 | [](https://travis-ci.org/gizak/termui) [](https://godoc.org/github.com/gizak/termui)
4 |
5 |
6 |
7 | `termui` is a cross-platform, easy-to-compile, and fully-customizable terminal dashboard built on top of [termbox-go](https://github.com/nsf/termbox-go). It is inspired by [blessed-contrib](https://github.com/yaronn/blessed-contrib) and written purely in Go.
8 |
9 | **termui is currently undergoing some API changes so make sure to check the changelog when upgrading**
10 |
11 | ## Installation
12 |
13 | Installing from the master branch is recommended:
14 |
15 | ```bash
16 | go get -u github.com/gizak/termui@master
17 | ```
18 |
19 | ## Usage
20 |
21 | ### Hello World
22 |
23 | ```go
24 | package main
25 |
26 | import ui "github.com/gizak/termui"
27 |
28 | func main() {
29 | err := ui.Init()
30 | if err != nil {
31 | panic(err)
32 | }
33 | defer ui.Close()
34 |
35 | p := ui.NewParagraph("Hello World!")
36 | p.Width = 25
37 | p.Height = 5
38 | ui.Render(p)
39 |
40 | for e := range ui.PollEvents() {
41 | if e.Type == ui.KeyboardEvent {
42 | break
43 | }
44 | }
45 | }
46 | ```
47 |
48 | ### Widgets
49 |
50 | Click image to see the corresponding demo codes.
51 |
52 | [
](https://github.com/gizak/termui/blob/master/_examples/barchart.go)
53 | [
](https://github.com/gizak/termui/blob/master/_examples/gauge.go)
54 | [
](https://github.com/gizak/termui/blob/master/_examples/linechart.go)
55 | [
](https://github.com/gizak/termui/blob/master/_examples/list.go)
56 | [
](https://github.com/gizak/termui/blob/master/_examples/par.go)
57 | [
](https://github.com/gizak/termui/blob/master/_examples/sparklines.go)
58 | [
](https://github.com/gizak/termui/blob/master/_examples/mbarchart.go)
59 | [
](https://github.com/gizak/termui/blob/master/_examples/table.go)
60 |
61 | ### Examples
62 |
63 | Examples can be found in [\_examples](./_examples). Run with `go run _examples/...` or run all of them consecutively with `./scripts/run_examples.py`.
64 |
65 | ## Documentation
66 |
67 | - [godoc](https://godoc.org/github.com/gizak/termui) for code documentation
68 | - [wiki](https://github.com/gizak/termui/wiki) for general information
69 |
70 | ## Uses
71 |
72 | - [go-ethereum/monitorcmd](https://github.com/ethereum/go-ethereum/blob/96116758d22ddbff4dbef2050d6b63a7b74502d8/cmd/geth/monitorcmd.go)
73 |
74 | ## Related Works
75 |
76 | - [blessed-contrib](https://github.com/yaronn/blessed-contrib)
77 | - [tui-rs](https://github.com/fdehau/tui-rs)
78 | - [gocui](https://github.com/jroimartin/gocui)
79 |
80 | ## License
81 |
82 | This library is under the [MIT License](http://opensource.org/licenses/MIT)
83 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/barchart.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | package termui
6 |
7 | import "fmt"
8 |
9 | // BarChart creates multiple bars in a widget:
10 | /*
11 | bc := termui.NewBarChart()
12 | data := []int{3, 2, 5, 3, 9, 5}
13 | bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
14 | bc.BorderLabel = "Bar Chart"
15 | bc.Data = data
16 | bc.Width = 26
17 | bc.Height = 10
18 | bc.DataLabels = bclabels
19 | bc.TextColor = termui.ColorGreen
20 | bc.BarColor = termui.ColorRed
21 | bc.NumColor = termui.ColorYellow
22 | */
23 | type BarChart struct {
24 | Block
25 | BarColor Attribute
26 | TextColor Attribute
27 | NumColor Attribute
28 | NumFmt func(int) string
29 | Data []int
30 | DataLabels []string
31 | BarWidth int
32 | BarGap int
33 | CellChar rune
34 | labels [][]rune
35 | dataNum [][]rune
36 | numBar int
37 | scale float64
38 | max int
39 | }
40 |
41 | // NewBarChart returns a new *BarChart with current theme.
42 | func NewBarChart() *BarChart {
43 | return &BarChart{
44 | Block: *NewBlock(),
45 | BarColor: ThemeAttr("barchart.bar.bg"),
46 | NumColor: ThemeAttr("barchart.num.fg"),
47 | TextColor: ThemeAttr("barchart.text.fg"),
48 | NumFmt: func(n int) string { return fmt.Sprint(n) },
49 | BarGap: 1,
50 | BarWidth: 3,
51 | CellChar: ' ',
52 | }
53 | }
54 |
55 | func (bc *BarChart) layout() {
56 | bc.numBar = bc.innerArea.Dx() / (bc.BarGap + bc.BarWidth)
57 | bc.labels = make([][]rune, bc.numBar)
58 | bc.dataNum = make([][]rune, len(bc.Data))
59 |
60 | for i := 0; i < bc.numBar && i < len(bc.DataLabels) && i < len(bc.Data); i++ {
61 | bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth)
62 | n := bc.Data[i]
63 | s := bc.NumFmt(n)
64 | bc.dataNum[i] = trimStr2Runes(s, bc.BarWidth)
65 | }
66 |
67 | //bc.max = bc.Data[0] // what if Data is nil? Sometimes when bar graph is nill it produces panic with panic: runtime error: index out of range
68 | // Assign a negative value to get maxvalue auto-populates
69 | if bc.max == 0 {
70 | bc.max = -1
71 | }
72 | for i := 0; i < len(bc.Data); i++ {
73 | if bc.max < bc.Data[i] {
74 | bc.max = bc.Data[i]
75 | }
76 | }
77 | bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-1)
78 | }
79 |
80 | func (bc *BarChart) SetMax(max int) {
81 | if max > 0 {
82 | bc.max = max
83 | }
84 | }
85 |
86 | // Buffer implements Bufferer interface.
87 | func (bc *BarChart) Buffer() Buffer {
88 | buf := bc.Block.Buffer()
89 | bc.layout()
90 |
91 | for i := 0; i < bc.numBar && i < len(bc.Data) && i < len(bc.DataLabels); i++ {
92 | h := int(float64(bc.Data[i]) / bc.scale)
93 | oftX := i * (bc.BarWidth + bc.BarGap)
94 |
95 | barBg := bc.Bg
96 | barFg := bc.BarColor
97 |
98 | if bc.CellChar == ' ' {
99 | barBg = bc.BarColor
100 | barFg = ColorDefault
101 | if bc.BarColor == ColorDefault { // the same as above
102 | barBg |= AttrReverse
103 | }
104 | }
105 |
106 | // plot bar
107 | for j := 0; j < bc.BarWidth; j++ {
108 | for k := 0; k < h; k++ {
109 | c := Cell{
110 | Ch: bc.CellChar,
111 | Bg: barBg,
112 | Fg: barFg,
113 | }
114 |
115 | x := bc.innerArea.Min.X + i*(bc.BarWidth+bc.BarGap) + j
116 | y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - k
117 | buf.Set(x, y, c)
118 | }
119 | }
120 | // plot text
121 | for j, k := 0, 0; j < len(bc.labels[i]); j++ {
122 | w := charWidth(bc.labels[i][j])
123 | c := Cell{
124 | Ch: bc.labels[i][j],
125 | Bg: bc.Bg,
126 | Fg: bc.TextColor,
127 | }
128 | y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 1
129 | x := bc.innerArea.Min.X + oftX + k
130 | buf.Set(x, y, c)
131 | k += w
132 | }
133 | // plot num
134 | for j := 0; j < len(bc.dataNum[i]); j++ {
135 | c := Cell{
136 | Ch: bc.dataNum[i][j],
137 | Fg: bc.NumColor,
138 | Bg: barBg,
139 | }
140 |
141 | if h == 0 {
142 | c.Bg = bc.Bg
143 | }
144 | x := bc.innerArea.Min.X + oftX + (bc.BarWidth-len(bc.dataNum[i]))/2 + j
145 | y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2
146 | buf.Set(x, y, c)
147 | }
148 | }
149 |
150 | return buf
151 | }
152 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/block.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | package termui
6 |
7 | import "image"
8 |
9 | // Hline is a horizontal line.
10 | type Hline struct {
11 | X int
12 | Y int
13 | Len int
14 | Fg Attribute
15 | Bg Attribute
16 | }
17 |
18 | // Vline is a vertical line.
19 | type Vline struct {
20 | X int
21 | Y int
22 | Len int
23 | Fg Attribute
24 | Bg Attribute
25 | }
26 |
27 | // Buffer draws a horizontal line.
28 | func (l Hline) Buffer() Buffer {
29 | if l.Len <= 0 {
30 | return NewBuffer()
31 | }
32 | return NewFilledBuffer(l.X, l.Y, l.X+l.Len, l.Y+1, HORIZONTAL_LINE, l.Fg, l.Bg)
33 | }
34 |
35 | // Buffer draws a vertical line.
36 | func (l Vline) Buffer() Buffer {
37 | if l.Len <= 0 {
38 | return NewBuffer()
39 | }
40 | return NewFilledBuffer(l.X, l.Y, l.X+1, l.Y+l.Len, VERTICAL_LINE, l.Fg, l.Bg)
41 | }
42 |
43 | // Buffer draws a box border.
44 | func (b Block) drawBorder(buf Buffer) {
45 | if !b.Border {
46 | return
47 | }
48 |
49 | min := b.area.Min
50 | max := b.area.Max
51 |
52 | x0 := min.X
53 | y0 := min.Y
54 | x1 := max.X - 1
55 | y1 := max.Y - 1
56 |
57 | // draw lines
58 | if b.BorderTop {
59 | buf.Merge(Hline{x0, y0, x1 - x0, b.BorderFg, b.BorderBg}.Buffer())
60 | }
61 | if b.BorderBottom {
62 | buf.Merge(Hline{x0, y1, x1 - x0, b.BorderFg, b.BorderBg}.Buffer())
63 | }
64 | if b.BorderLeft {
65 | buf.Merge(Vline{x0, y0, y1 - y0, b.BorderFg, b.BorderBg}.Buffer())
66 | }
67 | if b.BorderRight {
68 | buf.Merge(Vline{x1, y0, y1 - y0, b.BorderFg, b.BorderBg}.Buffer())
69 | }
70 |
71 | // draw corners
72 | if b.BorderTop && b.BorderLeft && b.area.Dx() > 0 && b.area.Dy() > 0 {
73 | buf.Set(x0, y0, Cell{TOP_LEFT, b.BorderFg, b.BorderBg})
74 | }
75 | if b.BorderTop && b.BorderRight && b.area.Dx() > 1 && b.area.Dy() > 0 {
76 | buf.Set(x1, y0, Cell{TOP_RIGHT, b.BorderFg, b.BorderBg})
77 | }
78 | if b.BorderBottom && b.BorderLeft && b.area.Dx() > 0 && b.area.Dy() > 1 {
79 | buf.Set(x0, y1, Cell{BOTTOM_LEFT, b.BorderFg, b.BorderBg})
80 | }
81 | if b.BorderBottom && b.BorderRight && b.area.Dx() > 1 && b.area.Dy() > 1 {
82 | buf.Set(x1, y1, Cell{BOTTOM_RIGHT, b.BorderFg, b.BorderBg})
83 | }
84 | }
85 |
86 | func (b Block) drawBorderLabel(buf Buffer) {
87 | maxTxtW := b.area.Dx() - 2
88 | tx := DTrimTxCls(DefaultTxBuilder.Build(b.BorderLabel, b.BorderLabelFg, b.BorderLabelBg), maxTxtW)
89 |
90 | for i, w := 0, 0; i < len(tx); i++ {
91 | buf.Set(b.area.Min.X+1+w, b.area.Min.Y, tx[i])
92 | w += tx[i].Width()
93 | }
94 | }
95 |
96 | // Block is a base struct for all other upper level widgets,
97 | // consider it as css: display:block.
98 | // Normally you do not need to create it manually.
99 | type Block struct {
100 | area image.Rectangle
101 | innerArea image.Rectangle
102 | X int
103 | Y int
104 | Border bool
105 | BorderFg Attribute
106 | BorderBg Attribute
107 | BorderLeft bool
108 | BorderRight bool
109 | BorderTop bool
110 | BorderBottom bool
111 | BorderLabel string
112 | BorderLabelFg Attribute
113 | BorderLabelBg Attribute
114 | Display bool
115 | Bg Attribute
116 | Width int
117 | Height int
118 | PaddingTop int
119 | PaddingBottom int
120 | PaddingLeft int
121 | PaddingRight int
122 | id string
123 | Float Align
124 | }
125 |
126 | // NewBlock returns a *Block which inherits styles from current theme.
127 | func NewBlock() *Block {
128 | return &Block{
129 | Display: true,
130 | Border: true,
131 | BorderLeft: true,
132 | BorderRight: true,
133 | BorderTop: true,
134 | BorderBottom: true,
135 | BorderBg: ThemeAttr("border.bg"),
136 | BorderFg: ThemeAttr("border.fg"),
137 | BorderLabelBg: ThemeAttr("label.bg"),
138 | BorderLabelFg: ThemeAttr("label.fg"),
139 | Bg: ThemeAttr("block.bg"),
140 | Width: 2,
141 | Height: 2,
142 | id: GenId(),
143 | Float: AlignNone,
144 | }
145 | }
146 |
147 | func (b Block) Id() string {
148 | return b.id
149 | }
150 |
151 | // Align computes box model
152 | func (b *Block) Align() {
153 | // outer
154 | b.area.Min.X = 0
155 | b.area.Min.Y = 0
156 | b.area.Max.X = b.Width
157 | b.area.Max.Y = b.Height
158 |
159 | // float
160 | b.area = AlignArea(TermRect(), b.area, b.Float)
161 | b.area = MoveArea(b.area, b.X, b.Y)
162 |
163 | // inner
164 | b.innerArea.Min.X = b.area.Min.X + b.PaddingLeft
165 | b.innerArea.Min.Y = b.area.Min.Y + b.PaddingTop
166 | b.innerArea.Max.X = b.area.Max.X - b.PaddingRight
167 | b.innerArea.Max.Y = b.area.Max.Y - b.PaddingBottom
168 |
169 | if b.Border {
170 | if b.BorderLeft {
171 | b.innerArea.Min.X++
172 | }
173 | if b.BorderRight {
174 | b.innerArea.Max.X--
175 | }
176 | if b.BorderTop {
177 | b.innerArea.Min.Y++
178 | }
179 | if b.BorderBottom {
180 | b.innerArea.Max.Y--
181 | }
182 | }
183 | }
184 |
185 | // InnerBounds returns the internal bounds of the block after aligning and
186 | // calculating the padding and border, if any.
187 | func (b *Block) InnerBounds() image.Rectangle {
188 | b.Align()
189 | return b.innerArea
190 | }
191 |
192 | // Buffer implements Bufferer interface.
193 | // Draw background and border (if any).
194 | func (b *Block) Buffer() Buffer {
195 | b.Align()
196 |
197 | buf := NewBuffer()
198 | buf.SetArea(b.area)
199 | buf.Fill(' ', ColorDefault, b.Bg)
200 |
201 | b.drawBorder(buf)
202 | b.drawBorderLabel(buf)
203 |
204 | return buf
205 | }
206 |
207 | // GetHeight implements GridBufferer.
208 | // It returns current height of the block.
209 | func (b Block) GetHeight() int {
210 | return b.Height
211 | }
212 |
213 | // SetX implements GridBufferer interface, which sets block's x position.
214 | func (b *Block) SetX(x int) {
215 | b.X = x
216 | }
217 |
218 | // SetY implements GridBufferer interface, it sets y position for block.
219 | func (b *Block) SetY(y int) {
220 | b.Y = y
221 | }
222 |
223 | // SetWidth implements GridBuffer interface, it sets block's width.
224 | func (b *Block) SetWidth(w int) {
225 | b.Width = w
226 | }
227 |
228 | func (b Block) InnerWidth() int {
229 | return b.innerArea.Dx()
230 | }
231 |
232 | func (b Block) InnerHeight() int {
233 | return b.innerArea.Dy()
234 | }
235 |
236 | func (b Block) InnerX() int {
237 | return b.innerArea.Min.X
238 | }
239 |
240 | func (b Block) InnerY() int { return b.innerArea.Min.Y }
241 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/block_common.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | // +build !windows
6 |
7 | package termui
8 |
9 | const TOP_RIGHT = '┐'
10 | const VERTICAL_LINE = '│'
11 | const HORIZONTAL_LINE = '─'
12 | const TOP_LEFT = '┌'
13 | const BOTTOM_RIGHT = '┘'
14 | const BOTTOM_LEFT = '└'
15 | const VERTICAL_LEFT = '┤'
16 | const VERTICAL_RIGHT = '├'
17 | const HORIZONTAL_DOWN = '┬'
18 | const HORIZONTAL_UP = '┴'
19 | const QUOTA_LEFT = '«'
20 | const QUOTA_RIGHT = '»'
21 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/block_windows.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package termui
8 |
9 | const TOP_RIGHT = '+'
10 | const VERTICAL_LINE = '|'
11 | const HORIZONTAL_LINE = '-'
12 | const TOP_LEFT = '+'
13 | const BOTTOM_RIGHT = '+'
14 | const BOTTOM_LEFT = '+'
15 | const VERTICAL_LEFT = '+'
16 | const VERTICAL_RIGHT = '+'
17 | const HORIZONTAL_DOWN = '+'
18 | const HORIZONTAL_UP = '+'
19 | const QUOTA_LEFT = '<'
20 | const QUOTA_RIGHT = '>'
21 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/buffer.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | package termui
6 |
7 | import "image"
8 |
9 | // Cell is a rune with assigned Fg and Bg
10 | type Cell struct {
11 | Ch rune
12 | Fg Attribute
13 | Bg Attribute
14 | }
15 |
16 | // Buffer is a renderable rectangle cell data container.
17 | type Buffer struct {
18 | Area image.Rectangle // selected drawing area
19 | CellMap map[image.Point]Cell
20 | }
21 |
22 | // At returns the cell at (x,y).
23 | func (b Buffer) At(x, y int) Cell {
24 | return b.CellMap[image.Pt(x, y)]
25 | }
26 |
27 | // Set assigns a char to (x,y)
28 | func (b Buffer) Set(x, y int, c Cell) {
29 | b.CellMap[image.Pt(x, y)] = c
30 | }
31 |
32 | // Bounds returns the domain for which At can return non-zero color.
33 | func (b Buffer) Bounds() image.Rectangle {
34 | x0, y0, x1, y1 := 0, 0, 0, 0
35 | for p := range b.CellMap {
36 | if p.X > x1 {
37 | x1 = p.X
38 | }
39 | if p.X < x0 {
40 | x0 = p.X
41 | }
42 | if p.Y > y1 {
43 | y1 = p.Y
44 | }
45 | if p.Y < y0 {
46 | y0 = p.Y
47 | }
48 | }
49 | return image.Rect(x0, y0, x1+1, y1+1)
50 | }
51 |
52 | // SetArea assigns a new rect area to Buffer b.
53 | func (b *Buffer) SetArea(r image.Rectangle) {
54 | b.Area.Max = r.Max
55 | b.Area.Min = r.Min
56 | }
57 |
58 | // Sync sets drawing area to the buffer's bound
59 | func (b *Buffer) Sync() {
60 | b.SetArea(b.Bounds())
61 | }
62 |
63 | // NewCell returns a new cell
64 | func NewCell(ch rune, fg, bg Attribute) Cell {
65 | return Cell{ch, fg, bg}
66 | }
67 |
68 | // Merge merges bs Buffers onto b
69 | func (b *Buffer) Merge(bs ...Buffer) {
70 | for _, buf := range bs {
71 | for p, v := range buf.CellMap {
72 | b.Set(p.X, p.Y, v)
73 | }
74 | b.SetArea(b.Area.Union(buf.Area))
75 | }
76 | }
77 |
78 | // NewBuffer returns a new Buffer
79 | func NewBuffer() Buffer {
80 | return Buffer{
81 | CellMap: make(map[image.Point]Cell),
82 | Area: image.Rectangle{}}
83 | }
84 |
85 | // Fill fills the Buffer b with ch,fg and bg.
86 | func (b Buffer) Fill(ch rune, fg, bg Attribute) {
87 | for x := b.Area.Min.X; x < b.Area.Max.X; x++ {
88 | for y := b.Area.Min.Y; y < b.Area.Max.Y; y++ {
89 | b.Set(x, y, Cell{ch, fg, bg})
90 | }
91 | }
92 | }
93 |
94 | // NewFilledBuffer returns a new Buffer filled with ch, fb and bg.
95 | func NewFilledBuffer(x0, y0, x1, y1 int, ch rune, fg, bg Attribute) Buffer {
96 | buf := NewBuffer()
97 | buf.Area.Min = image.Pt(x0, y0)
98 | buf.Area.Max = image.Pt(x1, y1)
99 |
100 | for x := buf.Area.Min.X; x < buf.Area.Max.X; x++ {
101 | for y := buf.Area.Min.Y; y < buf.Area.Max.Y; y++ {
102 | buf.Set(x, y, Cell{ch, fg, bg})
103 | }
104 | }
105 | return buf
106 | }
107 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/canvas.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | package termui
6 |
7 | /*
8 | dots:
9 | ,___,
10 | |1 4|
11 | |2 5|
12 | |3 6|
13 | |7 8|
14 | `````
15 | */
16 |
17 | var brailleBase = '\u2800'
18 |
19 | var brailleOftMap = [4][2]rune{
20 | {'\u0001', '\u0008'},
21 | {'\u0002', '\u0010'},
22 | {'\u0004', '\u0020'},
23 | {'\u0040', '\u0080'}}
24 |
25 | // Canvas contains drawing map: i,j -> rune
26 | type Canvas map[[2]int]rune
27 |
28 | // NewCanvas returns an empty Canvas
29 | func NewCanvas() Canvas {
30 | return make(map[[2]int]rune)
31 | }
32 |
33 | func chOft(x, y int) rune {
34 | return brailleOftMap[y%4][x%2]
35 | }
36 |
37 | func (c Canvas) rawCh(x, y int) rune {
38 | if ch, ok := c[[2]int{x, y}]; ok {
39 | return ch
40 | }
41 | return '\u0000' //brailleOffset
42 | }
43 |
44 | // return coordinate in terminal
45 | func chPos(x, y int) (int, int) {
46 | return y / 4, x / 2
47 | }
48 |
49 | // Set sets a point (x,y) in the virtual coordinate
50 | func (c Canvas) Set(x, y int) {
51 | i, j := chPos(x, y)
52 | ch := c.rawCh(i, j)
53 | ch |= chOft(x, y)
54 | c[[2]int{i, j}] = ch
55 | }
56 |
57 | // Unset removes point (x,y)
58 | func (c Canvas) Unset(x, y int) {
59 | i, j := chPos(x, y)
60 | ch := c.rawCh(i, j)
61 | ch &= ^chOft(x, y)
62 | c[[2]int{i, j}] = ch
63 | }
64 |
65 | // Buffer returns un-styled points
66 | func (c Canvas) Buffer() Buffer {
67 | buf := NewBuffer()
68 | for k, v := range c {
69 | buf.Set(k[0], k[1], Cell{Ch: v + brailleBase})
70 | }
71 | return buf
72 | }
73 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | /*
6 | Package termui is a library for creating terminal user interfaces (TUIs) using widgets.
7 | */
8 | package termui
9 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/events.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | package termui
6 |
7 | import (
8 | "strconv"
9 |
10 | tb "github.com/nsf/termbox-go"
11 | )
12 |
13 | /*
14 | List of events:
15 | mouse events:
16 |
17 |
18 | keyboard events:
19 | any uppercase or lowercase letter or a set of two letters like j or jj or J or JJ
20 | etc
21 | etc
22 |
23 |
24 |
25 | > etc
26 | terminal events:
27 |
28 | */
29 |
30 | type EventType int
31 |
32 | const (
33 | KeyboardEvent EventType = iota
34 | MouseEvent
35 | ResizeEvent
36 | )
37 |
38 | type Event struct {
39 | Type EventType
40 | ID string
41 | Payload interface{}
42 | }
43 |
44 | // Mouse payload.
45 | type Mouse struct {
46 | Drag bool
47 | X int
48 | Y int
49 | }
50 |
51 | // Resize payload.
52 | type Resize struct {
53 | Width int
54 | Height int
55 | }
56 |
57 | // PollEvents gets events from termbox, converts them, then sends them to each of its channels.
58 | func PollEvents() <-chan Event {
59 | ch := make(chan Event)
60 | go func() {
61 | for {
62 | ch <- convertTermboxEvent(tb.PollEvent())
63 | }
64 | }()
65 | return ch
66 | }
67 |
68 | // convertTermboxKeyboardEvent converts a termbox keyboard event to a more friendly string format.
69 | // Combines modifiers into the string instead of having them as additional fields in an event.
70 | func convertTermboxKeyboardEvent(e tb.Event) Event {
71 | k := string(e.Ch)
72 | pre := ""
73 | mod := ""
74 |
75 | if e.Mod == tb.ModAlt {
76 | mod = " 0xFFFF-12 {
80 | k = ""
81 | } else if e.Key > 0xFFFF-25 {
82 | ks := []string{"", "", "", "", "", "", "", "", "", ""}
83 | k = ks[0xFFFF-int(e.Key)-12]
84 | }
85 |
86 | if e.Key <= 0x7F {
87 | pre = ""},
91 | tb.KeyBackspace: {"", ""},
92 | tb.KeyTab: {"", ""},
93 | tb.KeyEnter: {"", ""},
94 | tb.KeyEsc: {"", ""},
95 | tb.KeyCtrlBackslash: {"C-", "\\"},
96 | tb.KeyCtrlSlash: {"C-", "/"},
97 | tb.KeySpace: {"", ""},
98 | tb.KeyCtrl8: {"C-", "8"},
99 | }
100 | if sk, ok := kmap[e.Key]; ok {
101 | pre = sk[0]
102 | k = sk[1]
103 | }
104 | }
105 | }
106 |
107 | if pre != "" {
108 | k += ">"
109 | }
110 |
111 | id := pre + mod + k
112 |
113 | return Event{
114 | Type: KeyboardEvent,
115 | ID: id,
116 | }
117 | }
118 |
119 | func convertTermboxMouseEvent(e tb.Event) Event {
120 | mouseButtonMap := map[tb.Key]string{
121 | tb.MouseLeft: "",
122 | tb.MouseMiddle: "",
123 | tb.MouseRight: "",
124 | tb.MouseRelease: "",
125 | tb.MouseWheelUp: "",
126 | tb.MouseWheelDown: "",
127 | }
128 |
129 | converted, ok := mouseButtonMap[e.Key]
130 | if !ok {
131 | converted = "Unknown_Mouse_Button"
132 | }
133 |
134 | Drag := false
135 | if e.Mod == tb.ModMotion {
136 | Drag = true
137 | }
138 |
139 | return Event{
140 | Type: MouseEvent,
141 | ID: converted,
142 | Payload: Mouse{
143 | X: e.MouseX,
144 | Y: e.MouseY,
145 | Drag: Drag,
146 | },
147 | }
148 | }
149 |
150 | // convertTermboxEvent turns a termbox event into a termui event.
151 | func convertTermboxEvent(e tb.Event) Event {
152 | if e.Type == tb.EventError {
153 | panic(e.Err)
154 | }
155 |
156 | var event Event
157 |
158 | switch e.Type {
159 | case tb.EventKey:
160 | event = convertTermboxKeyboardEvent(e)
161 | case tb.EventMouse:
162 | event = convertTermboxMouseEvent(e)
163 | case tb.EventResize:
164 | event = Event{
165 | Type: ResizeEvent,
166 | ID: "",
167 | Payload: Resize{
168 | Width: e.Width,
169 | Height: e.Height,
170 | },
171 | }
172 | }
173 |
174 | return event
175 | }
176 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/gauge.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | package termui
6 |
7 | import (
8 | "strconv"
9 | "strings"
10 | )
11 |
12 | // Gauge is a progress bar like widget.
13 | // A simple example:
14 | /*
15 | g := termui.NewGauge()
16 | g.Percent = 40
17 | g.Width = 50
18 | g.Height = 3
19 | g.BorderLabel = "Slim Gauge"
20 | g.BarColor = termui.ColorRed
21 | g.PercentColor = termui.ColorBlue
22 | */
23 |
24 | const ColorUndef Attribute = Attribute(^uint16(0))
25 |
26 | type Gauge struct {
27 | Block
28 | Percent int
29 | BarColor Attribute
30 | PercentColor Attribute
31 | PercentColorHighlighted Attribute
32 | Label string
33 | LabelAlign Align
34 | }
35 |
36 | // NewGauge return a new gauge with current theme.
37 | func NewGauge() *Gauge {
38 | g := &Gauge{
39 | Block: *NewBlock(),
40 | PercentColor: ThemeAttr("gauge.percent.fg"),
41 | BarColor: ThemeAttr("gauge.bar.bg"),
42 | Label: "{{percent}}%",
43 | LabelAlign: AlignCenter,
44 | PercentColorHighlighted: ColorUndef,
45 | }
46 | g.Width = 12
47 | g.Height = 5
48 | return g
49 | }
50 |
51 | // Buffer implements Bufferer interface.
52 | func (g *Gauge) Buffer() Buffer {
53 | buf := g.Block.Buffer()
54 |
55 | // plot bar
56 | w := g.Percent * g.innerArea.Dx() / 100
57 | for i := 0; i < g.innerArea.Dy(); i++ {
58 | for j := 0; j < w; j++ {
59 | c := Cell{}
60 | c.Ch = ' '
61 | c.Bg = g.BarColor
62 | if c.Bg == ColorDefault {
63 | c.Bg |= AttrReverse
64 | }
65 | buf.Set(g.innerArea.Min.X+j, g.innerArea.Min.Y+i, c)
66 | }
67 | }
68 |
69 | // plot percentage
70 | s := strings.Replace(g.Label, "{{percent}}", strconv.Itoa(g.Percent), -1)
71 | pry := g.innerArea.Min.Y + g.innerArea.Dy()/2
72 | rs := str2runes(s)
73 | var pos int
74 | switch g.LabelAlign {
75 | case AlignLeft:
76 | pos = 0
77 |
78 | case AlignCenter:
79 | pos = (g.innerArea.Dx() - strWidth(s)) / 2
80 |
81 | case AlignRight:
82 | pos = g.innerArea.Dx() - strWidth(s) - 1
83 | }
84 | pos += g.innerArea.Min.X
85 |
86 | for i, v := range rs {
87 | c := Cell{
88 | Ch: v,
89 | Fg: g.PercentColor,
90 | }
91 |
92 | if w+g.innerArea.Min.X > pos+i {
93 | c.Bg = g.BarColor
94 | if c.Bg == ColorDefault {
95 | c.Bg |= AttrReverse
96 | }
97 |
98 | if g.PercentColorHighlighted != ColorUndef {
99 | c.Fg = g.PercentColorHighlighted
100 | }
101 | } else {
102 | c.Bg = g.Block.Bg
103 | }
104 |
105 | buf.Set(1+pos+i, pry, c)
106 | }
107 | return buf
108 | }
109 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/gizak/termui
2 |
3 | require (
4 | github.com/davecgh/go-spew v1.1.0
5 | github.com/mattn/go-runewidth v0.0.2
6 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7
7 | github.com/nsf/termbox-go v0.0.0-20180613055208-5c94acc5e6eb
8 | github.com/pmezard/go-difflib v1.0.0 // indirect
9 | github.com/stretchr/testify v1.2.2
10 | golang.org/x/net v0.0.0-20180801234040-f4c29de78a2a
11 | )
12 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3 | github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o=
4 | github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
5 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
6 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
7 | github.com/nsf/termbox-go v0.0.0-20180613055208-5c94acc5e6eb h1:YahEjAGkJtCrkqgVHhX6n8ZX+CZ3hDRL9fjLYugLfSs=
8 | github.com/nsf/termbox-go v0.0.0-20180613055208-5c94acc5e6eb/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
9 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
10 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
11 | github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
12 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
13 | golang.org/x/net v0.0.0-20180801234040-f4c29de78a2a h1:8fCF9zjAir2SP3N+axz9xs+0r4V8dqPzqsWO10t8zoo=
14 | golang.org/x/net v0.0.0-20180801234040-f4c29de78a2a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
15 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/grid.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | package termui
6 |
7 | // GridBufferer introduces a Bufferer that can be manipulated by Grid.
8 | type GridBufferer interface {
9 | Bufferer
10 | GetHeight() int
11 | SetWidth(int)
12 | SetX(int)
13 | SetY(int)
14 | }
15 |
16 | // Row builds a layout tree
17 | type Row struct {
18 | Cols []*Row //children
19 | Widget GridBufferer // root
20 | X int
21 | Y int
22 | Width int
23 | Height int
24 | Span int
25 | Offset int
26 | }
27 |
28 | // calculate and set the underlying layout tree's x, y, height and width.
29 | func (r *Row) calcLayout() {
30 | r.assignWidth(r.Width)
31 | r.Height = r.solveHeight()
32 | r.assignX(r.X)
33 | r.assignY(r.Y)
34 | }
35 |
36 | // tell if the node is leaf in the tree.
37 | func (r *Row) isLeaf() bool {
38 | return r.Cols == nil || len(r.Cols) == 0
39 | }
40 |
41 | func (r *Row) isRenderableLeaf() bool {
42 | return r.isLeaf() && r.Widget != nil
43 | }
44 |
45 | // assign widgets' (and their parent rows') width recursively.
46 | func (r *Row) assignWidth(w int) {
47 | r.SetWidth(w)
48 |
49 | accW := 0 // acc span and offset
50 | calcW := make([]int, len(r.Cols)) // calculated width
51 | calcOftX := make([]int, len(r.Cols)) // computed start position of x
52 |
53 | for i, c := range r.Cols {
54 | accW += c.Span + c.Offset
55 | cw := int(float64(c.Span*r.Width) / 12.0)
56 |
57 | if i >= 1 {
58 | calcOftX[i] = calcOftX[i-1] +
59 | calcW[i-1] +
60 | int(float64(r.Cols[i-1].Offset*r.Width)/12.0)
61 | }
62 |
63 | // use up the space if it is the last col
64 | if i == len(r.Cols)-1 && accW == 12 {
65 | cw = r.Width - calcOftX[i]
66 | }
67 | calcW[i] = cw
68 | r.Cols[i].assignWidth(cw)
69 | }
70 | }
71 |
72 | // bottom up calc and set rows' (and their widgets') height,
73 | // return r's total height.
74 | func (r *Row) solveHeight() int {
75 | if r.isRenderableLeaf() {
76 | r.Height = r.Widget.GetHeight()
77 | return r.Widget.GetHeight()
78 | }
79 |
80 | maxh := 0
81 | if !r.isLeaf() {
82 | for _, c := range r.Cols {
83 | nh := c.solveHeight()
84 | // when embed rows in Cols, row widgets stack up
85 | if r.Widget != nil {
86 | nh += r.Widget.GetHeight()
87 | }
88 | if nh > maxh {
89 | maxh = nh
90 | }
91 | }
92 | }
93 |
94 | r.Height = maxh
95 | return maxh
96 | }
97 |
98 | // recursively assign x position for r tree.
99 | func (r *Row) assignX(x int) {
100 | r.SetX(x)
101 |
102 | if !r.isLeaf() {
103 | acc := 0
104 | for i, c := range r.Cols {
105 | if c.Offset != 0 {
106 | acc += int(float64(c.Offset*r.Width) / 12.0)
107 | }
108 | r.Cols[i].assignX(x + acc)
109 | acc += c.Width
110 | }
111 | }
112 | }
113 |
114 | // recursively assign y position to r.
115 | func (r *Row) assignY(y int) {
116 | r.SetY(y)
117 |
118 | if r.isLeaf() {
119 | return
120 | }
121 |
122 | for i := range r.Cols {
123 | acc := 0
124 | if r.Widget != nil {
125 | acc = r.Widget.GetHeight()
126 | }
127 | r.Cols[i].assignY(y + acc)
128 | }
129 |
130 | }
131 |
132 | // GetHeight implements GridBufferer interface.
133 | func (r Row) GetHeight() int {
134 | return r.Height
135 | }
136 |
137 | // SetX implements GridBufferer interface.
138 | func (r *Row) SetX(x int) {
139 | r.X = x
140 | if r.Widget != nil {
141 | r.Widget.SetX(x)
142 | }
143 | }
144 |
145 | // SetY implements GridBufferer interface.
146 | func (r *Row) SetY(y int) {
147 | r.Y = y
148 | if r.Widget != nil {
149 | r.Widget.SetY(y)
150 | }
151 | }
152 |
153 | // SetWidth implements GridBufferer interface.
154 | func (r *Row) SetWidth(w int) {
155 | r.Width = w
156 | if r.Widget != nil {
157 | r.Widget.SetWidth(w)
158 | }
159 | }
160 |
161 | // Buffer implements Bufferer interface,
162 | // recursively merge all widgets buffer
163 | func (r *Row) Buffer() Buffer {
164 | merged := NewBuffer()
165 |
166 | if r.isRenderableLeaf() {
167 | return r.Widget.Buffer()
168 | }
169 |
170 | // for those are not leaves but have a renderable widget
171 | if r.Widget != nil {
172 | merged.Merge(r.Widget.Buffer())
173 | }
174 |
175 | // collect buffer from children
176 | if !r.isLeaf() {
177 | for _, c := range r.Cols {
178 | merged.Merge(c.Buffer())
179 | }
180 | }
181 |
182 | return merged
183 | }
184 |
185 | // Grid implements 12 columns system.
186 | // A simple example:
187 | /*
188 | import ui "github.com/gizak/termui"
189 | // init and create widgets...
190 |
191 | // build
192 | ui.Body.AddRows(
193 | ui.NewRow(
194 | ui.NewCol(6, 0, widget0),
195 | ui.NewCol(6, 0, widget1)),
196 | ui.NewRow(
197 | ui.NewCol(3, 0, widget2),
198 | ui.NewCol(3, 0, widget30, widget31, widget32),
199 | ui.NewCol(6, 0, widget4)))
200 |
201 | // calculate layout
202 | ui.Body.Align()
203 |
204 | ui.Render(ui.Body)
205 | */
206 | type Grid struct {
207 | Rows []*Row
208 | Width int
209 | X int
210 | Y int
211 | BgColor Attribute
212 | }
213 |
214 | // NewGrid returns *Grid with given rows.
215 | func NewGrid(rows ...*Row) *Grid {
216 | return &Grid{Rows: rows}
217 | }
218 |
219 | // AddRows appends given rows to Grid.
220 | func (g *Grid) AddRows(rs ...*Row) {
221 | g.Rows = append(g.Rows, rs...)
222 | }
223 |
224 | // NewRow creates a new row out of given columns.
225 | func NewRow(cols ...*Row) *Row {
226 | rs := &Row{Span: 12, Cols: cols}
227 | return rs
228 | }
229 |
230 | // NewCol accepts: widgets are LayoutBufferer or widgets is A NewRow.
231 | // Note that if multiple widgets are provided, they will stack up in the col.
232 | func NewCol(span, offset int, widgets ...GridBufferer) *Row {
233 | r := &Row{Span: span, Offset: offset}
234 |
235 | if widgets != nil && len(widgets) == 1 {
236 | wgt := widgets[0]
237 | nw, isRow := wgt.(*Row)
238 | if isRow {
239 | r.Cols = nw.Cols
240 | } else {
241 | r.Widget = wgt
242 | }
243 | return r
244 | }
245 |
246 | r.Cols = []*Row{}
247 | ir := r
248 | for _, w := range widgets {
249 | nr := &Row{Span: 12, Widget: w}
250 | ir.Cols = []*Row{nr}
251 | ir = nr
252 | }
253 |
254 | return r
255 | }
256 |
257 | // Align calculate each rows' layout.
258 | func (g *Grid) Align() {
259 | h := 0
260 | for _, r := range g.Rows {
261 | r.SetWidth(g.Width)
262 | r.SetX(g.X)
263 | r.SetY(g.Y + h)
264 | r.calcLayout()
265 | h += r.GetHeight()
266 | }
267 | }
268 |
269 | // Buffer implements Bufferer interface.
270 | func (g Grid) Buffer() Buffer {
271 | buf := NewBuffer()
272 |
273 | for _, r := range g.Rows {
274 | buf.Merge(r.Buffer())
275 | }
276 | return buf
277 | }
278 |
279 | var Body *Grid
280 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/linechart_others.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | // +build !windows
6 |
7 | package termui
8 |
9 | const VDASH = '┊'
10 | const HDASH = '┈'
11 | const ORIGIN = '└'
12 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/linechart_windows.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | // +build windows
6 |
7 | package termui
8 |
9 | const VDASH = '|'
10 | const HDASH = '-'
11 | const ORIGIN = '+'
12 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/list.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | package termui
6 |
7 | import "strings"
8 |
9 | // List displays []string as its items,
10 | // it has a Overflow option (default is "hidden"), when set to "hidden",
11 | // the item exceeding List's width is truncated, but when set to "wrap",
12 | // the overflowed text breaks into next line.
13 | /*
14 | strs := []string{
15 | "[0] github.com/gizak/termui",
16 | "[1] editbox.go",
17 | "[2] interrupt.go",
18 | "[3] keyboard.go",
19 | "[4] output.go",
20 | "[5] random_out.go",
21 | "[6] dashboard.go",
22 | "[7] nsf/termbox-go"}
23 |
24 | ls := termui.NewList()
25 | ls.Items = strs
26 | ls.ItemFgColor = termui.ColorYellow
27 | ls.BorderLabel = "List"
28 | ls.Height = 7
29 | ls.Width = 25
30 | ls.Y = 0
31 | */
32 | type List struct {
33 | Block
34 | Items []string
35 | Overflow string
36 | ItemFgColor Attribute
37 | ItemBgColor Attribute
38 | }
39 |
40 | // NewList returns a new *List with current theme.
41 | func NewList() *List {
42 | return &List{
43 | Block: *NewBlock(),
44 | Overflow: "hidden",
45 | ItemFgColor: ThemeAttr("list.item.fg"),
46 | ItemBgColor: ThemeAttr("list.item.bg"),
47 | }
48 | }
49 |
50 | // Buffer implements Bufferer interface.
51 | func (l *List) Buffer() Buffer {
52 | buf := l.Block.Buffer()
53 |
54 | switch l.Overflow {
55 | case "wrap":
56 | cs := DefaultTxBuilder.Build(strings.Join(l.Items, "\n"), l.ItemFgColor, l.ItemBgColor)
57 | i, j, k := 0, 0, 0
58 | for i < l.innerArea.Dy() && k < len(cs) {
59 | w := cs[k].Width()
60 | if cs[k].Ch == '\n' || j+w > l.innerArea.Dx() {
61 | i++
62 | j = 0
63 | if cs[k].Ch == '\n' {
64 | k++
65 | }
66 | continue
67 | }
68 | buf.Set(l.innerArea.Min.X+j, l.innerArea.Min.Y+i, cs[k])
69 |
70 | k++
71 | j++
72 | }
73 |
74 | case "hidden":
75 | trimItems := l.Items
76 | if len(trimItems) > l.innerArea.Dy() {
77 | trimItems = trimItems[:l.innerArea.Dy()]
78 | }
79 | for i, v := range trimItems {
80 | cs := DTrimTxCls(DefaultTxBuilder.Build(v, l.ItemFgColor, l.ItemBgColor), l.innerArea.Dx())
81 | j := 0
82 | for _, vv := range cs {
83 | w := vv.Width()
84 | buf.Set(l.innerArea.Min.X+j, l.innerArea.Min.Y+i, vv)
85 | j += w
86 | }
87 | }
88 | }
89 |
90 | return buf
91 | }
92 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/paragraph.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | package termui
6 |
7 | // Paragraph displays a paragraph.
8 | /*
9 | par := termui.NewParagraph("Simple Text")
10 | par.Height = 3
11 | par.Width = 17
12 | par.BorderLabel = "Label"
13 | */
14 | type Paragraph struct {
15 | Block
16 | Text string
17 | TextFgColor Attribute
18 | TextBgColor Attribute
19 | WrapLength int // words wrap limit. Note it may not work properly with multi-width char
20 | }
21 |
22 | // NewParagraph returns a new *Paragraph with given text as its content.
23 | func NewParagraph(s string) *Paragraph {
24 | return &Paragraph{
25 | Block: *NewBlock(),
26 | Text: s,
27 | TextFgColor: ThemeAttr("par.text.fg"),
28 | TextBgColor: ThemeAttr("par.text.bg"),
29 | WrapLength: 0,
30 | }
31 | }
32 |
33 | // Buffer implements Bufferer interface.
34 | func (p *Paragraph) Buffer() Buffer {
35 | buf := p.Block.Buffer()
36 |
37 | fg, bg := p.TextFgColor, p.TextBgColor
38 | cs := DefaultTxBuilder.Build(p.Text, fg, bg)
39 |
40 | // wrap if WrapLength set
41 | if p.WrapLength < 0 {
42 | cs = wrapTx(cs, p.Width-2)
43 | } else if p.WrapLength > 0 {
44 | cs = wrapTx(cs, p.WrapLength)
45 | }
46 |
47 | y, x, n := 0, 0, 0
48 | for y < p.innerArea.Dy() && n < len(cs) {
49 | w := cs[n].Width()
50 | if cs[n].Ch == '\n' || x+w > p.innerArea.Dx() {
51 | y++
52 | x = 0 // set x = 0
53 | if cs[n].Ch == '\n' {
54 | n++
55 | }
56 |
57 | if y >= p.innerArea.Dy() {
58 | buf.Set(p.innerArea.Min.X+p.innerArea.Dx()-1,
59 | p.innerArea.Min.Y+p.innerArea.Dy()-1,
60 | Cell{Ch: '…', Fg: p.TextFgColor, Bg: p.TextBgColor})
61 | break
62 | }
63 | continue
64 | }
65 |
66 | buf.Set(p.innerArea.Min.X+x, p.innerArea.Min.Y+y, cs[n])
67 |
68 | n++
69 | x += w
70 | }
71 |
72 | return buf
73 | }
74 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/position.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | package termui
6 |
7 | import "image"
8 |
9 | // Align is the position of the gauge's label.
10 | type Align uint
11 |
12 | // All supported positions.
13 | const (
14 | AlignNone Align = 0
15 | AlignLeft Align = 1 << iota
16 | AlignRight
17 | AlignBottom
18 | AlignTop
19 | AlignCenterVertical
20 | AlignCenterHorizontal
21 | AlignCenter = AlignCenterVertical | AlignCenterHorizontal
22 | )
23 |
24 | func AlignArea(parent, child image.Rectangle, a Align) image.Rectangle {
25 | w, h := child.Dx(), child.Dy()
26 |
27 | // parent center
28 | pcx, pcy := parent.Min.X+parent.Dx()/2, parent.Min.Y+parent.Dy()/2
29 | // child center
30 | ccx, ccy := child.Min.X+child.Dx()/2, child.Min.Y+child.Dy()/2
31 |
32 | if a&AlignLeft == AlignLeft {
33 | child.Min.X = parent.Min.X
34 | child.Max.X = child.Min.X + w
35 | }
36 |
37 | if a&AlignRight == AlignRight {
38 | child.Max.X = parent.Max.X
39 | child.Min.X = child.Max.X - w
40 | }
41 |
42 | if a&AlignBottom == AlignBottom {
43 | child.Max.Y = parent.Max.Y
44 | child.Min.Y = child.Max.Y - h
45 | }
46 |
47 | if a&AlignTop == AlignRight {
48 | child.Min.Y = parent.Min.Y
49 | child.Max.Y = child.Min.Y + h
50 | }
51 |
52 | if a&AlignCenterHorizontal == AlignCenterHorizontal {
53 | child.Min.X += pcx - ccx
54 | child.Max.X = child.Min.X + w
55 | }
56 |
57 | if a&AlignCenterVertical == AlignCenterVertical {
58 | child.Min.Y += pcy - ccy
59 | child.Max.Y = child.Min.Y + h
60 | }
61 |
62 | return child
63 | }
64 |
65 | func MoveArea(a image.Rectangle, dx, dy int) image.Rectangle {
66 | a.Min.X += dx
67 | a.Max.X += dx
68 | a.Min.Y += dy
69 | a.Max.Y += dy
70 | return a
71 | }
72 |
73 | var termWidth int
74 | var termHeight int
75 |
76 | func TermRect() image.Rectangle {
77 | return image.Rect(0, 0, termWidth, termHeight)
78 | }
79 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/render.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | package termui
6 |
7 | import (
8 | "image"
9 | "runtime/debug"
10 | "sync"
11 |
12 | tb "github.com/nsf/termbox-go"
13 | )
14 |
15 | // Bufferer should be implemented by all renderable components.
16 | type Bufferer interface {
17 | Buffer() Buffer
18 | }
19 |
20 | // Init initializes termui library. This function should be called before any others.
21 | // After initialization, the library must be finalized by 'Close' function.
22 | func Init() error {
23 | if err := tb.Init(); err != nil {
24 | return err
25 | }
26 | tb.SetInputMode(tb.InputEsc | tb.InputMouse)
27 | // DefaultEvtStream = NewEvtStream()
28 |
29 | // sysEvtChs = make([]chan Event, 0)
30 | // go hookTermboxEvt()
31 |
32 | renderJobs = make(chan []Bufferer)
33 | //renderLock = new(sync.RWMutex)
34 |
35 | Body = NewGrid()
36 | Body.X = 0
37 | Body.Y = 0
38 | Body.BgColor = ThemeAttr("bg")
39 | Body.Width = TermWidth()
40 |
41 | // resizeCh := Handle("")
42 | // go func() {
43 | // for e := range resizeCh {
44 | // payload := e.Payload.(Resize)
45 | // Body.Width = payload.Width
46 | // }
47 | // }()
48 |
49 | // DefaultWgtMgr = NewWgtMgr()
50 | // EventHook(DefaultWgtMgr.WgtHandlersHook())
51 |
52 | go func() {
53 | for bs := range renderJobs {
54 | render(bs...)
55 | }
56 | }()
57 |
58 | return nil
59 | }
60 |
61 | // Close finalizes termui library,
62 | // should be called after successful initialization when termui's functionality isn't required anymore.
63 | func Close() {
64 | tb.Close()
65 | }
66 |
67 | var renderLock sync.Mutex
68 |
69 | func termSync() {
70 | renderLock.Lock()
71 | tb.Sync()
72 | termWidth, termHeight = tb.Size()
73 | renderLock.Unlock()
74 | }
75 |
76 | // TermWidth returns the current terminal's width.
77 | func TermWidth() int {
78 | termSync()
79 | return termWidth
80 | }
81 |
82 | // TermHeight returns the current terminal's height.
83 | func TermHeight() int {
84 | termSync()
85 | return termHeight
86 | }
87 |
88 | // Render renders all Bufferer in the given order from left to right,
89 | // right could overlap on left ones.
90 | func render(bs ...Bufferer) {
91 | defer func() {
92 | if e := recover(); e != nil {
93 | Close()
94 | panic(debug.Stack())
95 | }
96 | }()
97 | for _, b := range bs {
98 |
99 | buf := b.Buffer()
100 | // set cels in buf
101 | for p, c := range buf.CellMap {
102 | if p.In(buf.Area) {
103 |
104 | tb.SetCell(p.X, p.Y, c.Ch, toTmAttr(c.Fg), toTmAttr(c.Bg))
105 |
106 | }
107 | }
108 |
109 | }
110 |
111 | renderLock.Lock()
112 | // render
113 | tb.Flush()
114 | renderLock.Unlock()
115 | }
116 |
117 | func Clear() {
118 | tb.Clear(tb.ColorDefault, toTmAttr(ThemeAttr("bg")))
119 | }
120 |
121 | func clearArea(r image.Rectangle, bg Attribute) {
122 | for i := r.Min.X; i < r.Max.X; i++ {
123 | for j := r.Min.Y; j < r.Max.Y; j++ {
124 | tb.SetCell(i, j, ' ', tb.ColorDefault, toTmAttr(bg))
125 | }
126 | }
127 | }
128 |
129 | func ClearArea(r image.Rectangle, bg Attribute) {
130 | clearArea(r, bg)
131 | tb.Flush()
132 | }
133 |
134 | var renderJobs chan []Bufferer
135 |
136 | func Render(bs ...Bufferer) {
137 | //go func() { renderJobs <- bs }()
138 | renderJobs <- bs
139 | }
140 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/sparkline.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | package termui
6 |
7 | // Sparkline is like: ▅▆▂▂▅▇▂▂▃▆▆▆▅▃. The data points should be non-negative integers.
8 | /*
9 | data := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1}
10 | spl := termui.NewSparkline()
11 | spl.Data = data
12 | spl.Title = "Sparkline 0"
13 | spl.LineColor = termui.ColorGreen
14 | */
15 | type Sparkline struct {
16 | Data []int
17 | Height int
18 | Title string
19 | TitleColor Attribute
20 | LineColor Attribute
21 | displayHeight int
22 | scale float32
23 | max int
24 | }
25 |
26 | // Sparklines is a renderable widget which groups together the given sparklines.
27 | /*
28 | spls := termui.NewSparklines(spl0,spl1,spl2) //...
29 | spls.Height = 2
30 | spls.Width = 20
31 | */
32 | type Sparklines struct {
33 | Block
34 | Lines []Sparkline
35 | displayLines int
36 | displayWidth int
37 | }
38 |
39 | var sparks = []rune{'▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'}
40 |
41 | // Add appends a given Sparkline to s *Sparklines.
42 | func (s *Sparklines) Add(sl Sparkline) {
43 | s.Lines = append(s.Lines, sl)
44 | }
45 |
46 | // NewSparkline returns a unrenderable single sparkline that intended to be added into Sparklines.
47 | func NewSparkline() Sparkline {
48 | return Sparkline{
49 | Height: 1,
50 | TitleColor: ThemeAttr("sparkline.title.fg"),
51 | LineColor: ThemeAttr("sparkline.line.fg")}
52 | }
53 |
54 | // NewSparklines return a new *Sparklines with given Sparkline(s), you can always add a new Sparkline later.
55 | func NewSparklines(ss ...Sparkline) *Sparklines {
56 | return &Sparklines{Block: *NewBlock(), Lines: ss}
57 | }
58 |
59 | func (sl *Sparklines) update() {
60 | for i, v := range sl.Lines {
61 | if v.Title == "" {
62 | sl.Lines[i].displayHeight = v.Height
63 | } else {
64 | sl.Lines[i].displayHeight = v.Height + 1
65 | }
66 | }
67 | sl.displayWidth = sl.innerArea.Dx()
68 |
69 | // get how many lines gotta display
70 | h := 0
71 | sl.displayLines = 0
72 | for _, v := range sl.Lines {
73 | if h+v.displayHeight <= sl.innerArea.Dy() {
74 | sl.displayLines++
75 | } else {
76 | break
77 | }
78 | h += v.displayHeight
79 | }
80 |
81 | for i := 0; i < sl.displayLines; i++ {
82 | data := sl.Lines[i].Data
83 |
84 | max := 0
85 | for _, v := range data {
86 | if max < v {
87 | max = v
88 | }
89 | }
90 | sl.Lines[i].max = max
91 | if max != 0 {
92 | sl.Lines[i].scale = float32(8*sl.Lines[i].Height) / float32(max)
93 | } else { // when all negative
94 | sl.Lines[i].scale = 0
95 | }
96 | }
97 | }
98 |
99 | // Buffer implements Bufferer interface.
100 | func (sl *Sparklines) Buffer() Buffer {
101 | buf := sl.Block.Buffer()
102 | sl.update()
103 |
104 | oftY := 0
105 | for i := 0; i < sl.displayLines; i++ {
106 | l := sl.Lines[i]
107 | data := l.Data
108 |
109 | if len(data) > sl.innerArea.Dx() {
110 | data = data[len(data)-sl.innerArea.Dx():]
111 | }
112 |
113 | if l.Title != "" {
114 | rs := trimStr2Runes(l.Title, sl.innerArea.Dx())
115 | oftX := 0
116 | for _, v := range rs {
117 | w := charWidth(v)
118 | c := Cell{
119 | Ch: v,
120 | Fg: l.TitleColor,
121 | Bg: sl.Bg,
122 | }
123 | x := sl.innerArea.Min.X + oftX
124 | y := sl.innerArea.Min.Y + oftY
125 | buf.Set(x, y, c)
126 | oftX += w
127 | }
128 | }
129 |
130 | for j, v := range data {
131 | // display height of the data point, zero when data is negative
132 | h := int(float32(v)*l.scale + 0.5)
133 | if v < 0 {
134 | h = 0
135 | }
136 |
137 | barCnt := h / 8
138 | barMod := h % 8
139 | for jj := 0; jj < barCnt; jj++ {
140 | c := Cell{
141 | Ch: ' ', // => sparks[7]
142 | Bg: l.LineColor,
143 | }
144 | x := sl.innerArea.Min.X + j
145 | y := sl.innerArea.Min.Y + oftY + l.Height - jj
146 |
147 | //p.Bg = sl.BgColor
148 | buf.Set(x, y, c)
149 | }
150 | if barMod != 0 {
151 | c := Cell{
152 | Ch: sparks[barMod-1],
153 | Fg: l.LineColor,
154 | Bg: sl.Bg,
155 | }
156 | x := sl.innerArea.Min.X + j
157 | y := sl.innerArea.Min.Y + oftY + l.Height - barCnt
158 | buf.Set(x, y, c)
159 | }
160 | }
161 |
162 | oftY += l.displayHeight
163 | }
164 |
165 | return buf
166 | }
167 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/table.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | package termui
6 |
7 | import "strings"
8 |
9 | /* Table is like:
10 |
11 | ┌Awesome Table ────────────────────────────────────────────────┐
12 | │ Col0 | Col1 | Col2 | Col3 | Col4 | Col5 | Col6 |
13 | │──────────────────────────────────────────────────────────────│
14 | │ Some Item #1 | AAA | 123 | CCCCC | EEEEE | GGGGG | IIIII |
15 | │──────────────────────────────────────────────────────────────│
16 | │ Some Item #2 | BBB | 456 | DDDDD | FFFFF | HHHHH | JJJJJ |
17 | └──────────────────────────────────────────────────────────────┘
18 |
19 | Datapoints are a two dimensional array of strings: [][]string
20 |
21 | Example:
22 | data := [][]string{
23 | {"Col0", "Col1", "Col3", "Col4", "Col5", "Col6"},
24 | {"Some Item #1", "AAA", "123", "CCCCC", "EEEEE", "GGGGG", "IIIII"},
25 | {"Some Item #2", "BBB", "456", "DDDDD", "FFFFF", "HHHHH", "JJJJJ"},
26 | }
27 |
28 | table := termui.NewTable()
29 | table.Rows = data // type [][]string
30 | table.FgColor = termui.ColorWhite
31 | table.BgColor = termui.ColorDefault
32 | table.Height = 7
33 | table.Width = 62
34 | table.Y = 0
35 | table.X = 0
36 | table.Border = true
37 | */
38 |
39 | // Table tracks all the attributes of a Table instance
40 | type Table struct {
41 | Block
42 | Rows [][]string
43 | CellWidth []int
44 | FgColor Attribute
45 | BgColor Attribute
46 | FgColors []Attribute
47 | BgColors []Attribute
48 | Separator bool
49 | TextAlign Align
50 | }
51 |
52 | // NewTable returns a new Table instance
53 | func NewTable() *Table {
54 | return &Table{
55 | Block: *NewBlock(),
56 | FgColor: ColorWhite,
57 | BgColor: ColorDefault,
58 | Separator: true,
59 | }
60 | }
61 |
62 | // CellsWidth calculates the width of a cell array and returns an int
63 | func cellsWidth(cells []Cell) int {
64 | width := 0
65 | for _, c := range cells {
66 | width += c.Width()
67 | }
68 | return width
69 | }
70 |
71 | // Analysis generates and returns an array of []Cell that represent all columns in the Table
72 | func (table *Table) Analysis() [][]Cell {
73 | var rowCells [][]Cell
74 | length := len(table.Rows)
75 | if length < 1 {
76 | return rowCells
77 | }
78 |
79 | if len(table.FgColors) == 0 {
80 | table.FgColors = make([]Attribute, len(table.Rows))
81 | }
82 | if len(table.BgColors) == 0 {
83 | table.BgColors = make([]Attribute, len(table.Rows))
84 | }
85 |
86 | cellWidths := make([]int, len(table.Rows[0]))
87 |
88 | for y, row := range table.Rows {
89 | if table.FgColors[y] == 0 {
90 | table.FgColors[y] = table.FgColor
91 | }
92 | if table.BgColors[y] == 0 {
93 | table.BgColors[y] = table.BgColor
94 | }
95 | for x, str := range row {
96 | cells := DefaultTxBuilder.Build(str, table.FgColors[y], table.BgColors[y])
97 | cw := cellsWidth(cells)
98 | if cellWidths[x] < cw {
99 | cellWidths[x] = cw
100 | }
101 | rowCells = append(rowCells, cells)
102 | }
103 | }
104 | table.CellWidth = cellWidths
105 | return rowCells
106 | }
107 |
108 | // SetSize calculates the table size and sets the internal value
109 | func (table *Table) SetSize() {
110 | length := len(table.Rows)
111 | if table.Separator {
112 | table.Height = length*2 + 1
113 | } else {
114 | table.Height = length + 2
115 | }
116 | table.Width = 2
117 | if length != 0 {
118 | for _, cellWidth := range table.CellWidth {
119 | table.Width += cellWidth + 3
120 | }
121 | }
122 | }
123 |
124 | // CalculatePosition ...
125 | func (table *Table) CalculatePosition(x int, y int, coordinateX *int, coordinateY *int, cellStart *int) {
126 | if table.Separator {
127 | *coordinateY = table.innerArea.Min.Y + y*2
128 | } else {
129 | *coordinateY = table.innerArea.Min.Y + y
130 | }
131 | if x == 0 {
132 | *cellStart = table.innerArea.Min.X
133 | } else {
134 | *cellStart += table.CellWidth[x-1] + 3
135 | }
136 |
137 | switch table.TextAlign {
138 | case AlignRight:
139 | *coordinateX = *cellStart + (table.CellWidth[x] - len(table.Rows[y][x])) + 2
140 | case AlignCenter:
141 | *coordinateX = *cellStart + (table.CellWidth[x]-len(table.Rows[y][x]))/2 + 2
142 | default:
143 | *coordinateX = *cellStart + 2
144 | }
145 | }
146 |
147 | // Buffer ...
148 | func (table *Table) Buffer() Buffer {
149 | buffer := table.Block.Buffer()
150 | rowCells := table.Analysis()
151 | pointerX := table.innerArea.Min.X + 2
152 | pointerY := table.innerArea.Min.Y
153 | borderPointerX := table.innerArea.Min.X
154 | for y, row := range table.Rows {
155 | for x := range row {
156 | table.CalculatePosition(x, y, &pointerX, &pointerY, &borderPointerX)
157 | background := DefaultTxBuilder.Build(strings.Repeat(" ", table.CellWidth[x]+3), table.BgColors[y], table.BgColors[y])
158 | cells := rowCells[y*len(row)+x]
159 | for i, back := range background {
160 | buffer.Set(borderPointerX+i, pointerY, back)
161 | }
162 |
163 | coordinateX := pointerX
164 | for _, printer := range cells {
165 | buffer.Set(coordinateX, pointerY, printer)
166 | coordinateX += printer.Width()
167 | }
168 |
169 | if x != 0 {
170 | dividors := DefaultTxBuilder.Build("|", table.FgColors[y], table.BgColors[y])
171 | for _, dividor := range dividors {
172 | buffer.Set(borderPointerX, pointerY, dividor)
173 | }
174 | }
175 | }
176 |
177 | if table.Separator {
178 | border := DefaultTxBuilder.Build(strings.Repeat("─", table.Width-2), table.FgColor, table.BgColor)
179 | for i, cell := range border {
180 | buffer.Set(table.innerArea.Min.X+i, pointerY+1, cell)
181 | }
182 | }
183 | }
184 |
185 | return buffer
186 | }
187 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/tabpane.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | package termui
6 |
7 | import "unicode/utf8"
8 |
9 | type Tab struct {
10 | Label string
11 | RuneLen int
12 | Blocks []Bufferer
13 | }
14 |
15 | func NewTab(label string) *Tab {
16 | return &Tab{
17 | Label: label,
18 | RuneLen: utf8.RuneCount([]byte(label))}
19 | }
20 |
21 | func (tab *Tab) AddBlocks(rs ...Bufferer) {
22 | for _, r := range rs {
23 | tab.Blocks = append(tab.Blocks, r)
24 | }
25 | }
26 |
27 | func (tab *Tab) Buffer() Buffer {
28 | buf := NewBuffer()
29 | for blockNum := 0; blockNum < len(tab.Blocks); blockNum++ {
30 | b := tab.Blocks[blockNum]
31 | buf.Merge(b.Buffer())
32 | }
33 | return buf
34 | }
35 |
36 | type TabPane struct {
37 | Block
38 | Tabs []Tab
39 | activeTabIndex int
40 | ActiveTabBg Attribute
41 | posTabText []int
42 | offTabText int
43 | }
44 |
45 | func NewTabPane() *TabPane {
46 | tp := TabPane{
47 | Block: *NewBlock(),
48 | activeTabIndex: 0,
49 | offTabText: 0,
50 | ActiveTabBg: ThemeAttr("bg.tab.active")}
51 | return &tp
52 | }
53 |
54 | func (tp *TabPane) SetTabs(tabs ...Tab) {
55 | tp.Tabs = make([]Tab, len(tabs))
56 | tp.posTabText = make([]int, len(tabs)+1)
57 | off := 0
58 | for i := 0; i < len(tp.Tabs); i++ {
59 | tp.Tabs[i] = tabs[i]
60 | tp.posTabText[i] = off
61 | off += tp.Tabs[i].RuneLen + 1 //+1 for space between tabs
62 | }
63 | tp.posTabText[len(tabs)] = off - 1 //total length of Tab's text
64 | }
65 |
66 | func (tp *TabPane) SetActiveLeft() {
67 | if tp.activeTabIndex == 0 {
68 | return
69 | }
70 | tp.activeTabIndex -= 1
71 | if tp.posTabText[tp.activeTabIndex] < tp.offTabText {
72 | tp.offTabText = tp.posTabText[tp.activeTabIndex]
73 | }
74 | }
75 |
76 | func (tp *TabPane) SetActiveRight() {
77 | if tp.activeTabIndex == len(tp.Tabs)-1 {
78 | return
79 | }
80 | tp.activeTabIndex += 1
81 | endOffset := tp.posTabText[tp.activeTabIndex] + tp.Tabs[tp.activeTabIndex].RuneLen
82 | if endOffset+tp.offTabText > tp.InnerWidth() {
83 | tp.offTabText = endOffset - tp.InnerWidth()
84 | }
85 | }
86 |
87 | // Checks if left and right tabs are fully visible
88 | // if only left tabs are not visible return -1
89 | // if only right tabs are not visible return 1
90 | // if both return 0
91 | // use only if fitsWidth() returns false
92 | func (tp *TabPane) checkAlignment() int {
93 | ret := 0
94 | if tp.offTabText > 0 {
95 | ret = -1
96 | }
97 | if tp.offTabText+tp.InnerWidth() < tp.posTabText[len(tp.Tabs)] {
98 | ret += 1
99 | }
100 | return ret
101 | }
102 |
103 | // Checks if all tabs fits innerWidth of TabPane
104 | func (tp *TabPane) fitsWidth() bool {
105 | return tp.InnerWidth() >= tp.posTabText[len(tp.Tabs)]
106 | }
107 |
108 | func (tp *TabPane) align() {
109 | if !tp.fitsWidth() && !tp.Border {
110 | tp.PaddingLeft += 1
111 | tp.PaddingRight += 1
112 | tp.Block.Align()
113 | }
114 | }
115 |
116 | // bridge the old Point stuct
117 | type point struct {
118 | X int
119 | Y int
120 | Ch rune
121 | Fg Attribute
122 | Bg Attribute
123 | }
124 |
125 | func buf2pt(b Buffer) []point {
126 | ps := make([]point, 0, len(b.CellMap))
127 | for k, c := range b.CellMap {
128 | ps = append(ps, point{X: k.X, Y: k.Y, Ch: c.Ch, Fg: c.Fg, Bg: c.Bg})
129 | }
130 |
131 | return ps
132 | }
133 |
134 | // Adds the point only if it is visible in TabPane.
135 | // Point can be invisible if concatenation of Tab's texts is widther then
136 | // innerWidth of TabPane
137 | func (tp *TabPane) addPoint(ptab []point, charOffset *int, oftX *int, points ...point) []point {
138 | if *charOffset < tp.offTabText || tp.offTabText+tp.InnerWidth() < *charOffset {
139 | *charOffset++
140 | return ptab
141 | }
142 | for _, p := range points {
143 | p.X = *oftX
144 | ptab = append(ptab, p)
145 | }
146 | *oftX++
147 | *charOffset++
148 | return ptab
149 | }
150 |
151 | // Draws the point and redraws upper and lower border points (if it has one)
152 | func (tp *TabPane) drawPointWithBorder(p point, ch rune, chbord rune, chdown rune, chup rune) []point {
153 | var addp []point
154 | p.Ch = ch
155 | if tp.Border {
156 | p.Ch = chdown
157 | p.Y = tp.InnerY() - 1
158 | addp = append(addp, p)
159 | p.Ch = chup
160 | p.Y = tp.InnerY() + 1
161 | addp = append(addp, p)
162 | p.Ch = chbord
163 | }
164 | p.Y = tp.InnerY()
165 | return append(addp, p)
166 | }
167 |
168 | func (tp *TabPane) Buffer() Buffer {
169 | if tp.Border {
170 | tp.Height = 3
171 | } else {
172 | tp.Height = 1
173 | }
174 | if tp.Width > tp.posTabText[len(tp.Tabs)]+2 {
175 | tp.Width = tp.posTabText[len(tp.Tabs)] + 2
176 | }
177 | buf := tp.Block.Buffer()
178 | ps := []point{}
179 |
180 | tp.align()
181 | if tp.InnerHeight() <= 0 || tp.InnerWidth() <= 0 {
182 | return NewBuffer()
183 | }
184 | oftX := tp.InnerX()
185 | charOffset := 0
186 | pt := point{Bg: tp.BorderBg, Fg: tp.BorderFg}
187 | for i, tab := range tp.Tabs {
188 |
189 | if i != 0 {
190 | pt.X = oftX
191 | pt.Y = tp.InnerY()
192 | addp := tp.drawPointWithBorder(pt, ' ', VERTICAL_LINE, HORIZONTAL_DOWN, HORIZONTAL_UP)
193 | ps = tp.addPoint(ps, &charOffset, &oftX, addp...)
194 | }
195 |
196 | if i == tp.activeTabIndex {
197 | pt.Bg = tp.ActiveTabBg
198 | }
199 | rs := []rune(tab.Label)
200 | for k := 0; k < len(rs); k++ {
201 |
202 | addp := make([]point, 0, 2)
203 | if i == tp.activeTabIndex && tp.Border {
204 | pt.Ch = ' '
205 | pt.Y = tp.InnerY() + 1
206 | pt.Bg = tp.BorderBg
207 | addp = append(addp, pt)
208 | pt.Bg = tp.ActiveTabBg
209 | }
210 |
211 | pt.Y = tp.InnerY()
212 | pt.Ch = rs[k]
213 |
214 | addp = append(addp, pt)
215 | ps = tp.addPoint(ps, &charOffset, &oftX, addp...)
216 | }
217 | pt.Bg = tp.BorderBg
218 |
219 | if !tp.fitsWidth() {
220 | all := tp.checkAlignment()
221 | pt.X = tp.InnerX() - 1
222 |
223 | pt.Ch = '*'
224 | if tp.Border {
225 | pt.Ch = VERTICAL_LINE
226 | }
227 | ps = append(ps, pt)
228 |
229 | if all <= 0 {
230 | addp := tp.drawPointWithBorder(pt, '<', QUOTA_LEFT, HORIZONTAL_LINE, HORIZONTAL_LINE)
231 | ps = append(ps, addp...)
232 | }
233 |
234 | pt.X = tp.InnerX() + tp.InnerWidth()
235 | pt.Ch = '*'
236 | if tp.Border {
237 | pt.Ch = VERTICAL_LINE
238 | }
239 | ps = append(ps, pt)
240 | if all >= 0 {
241 | addp := tp.drawPointWithBorder(pt, '>', QUOTA_RIGHT, HORIZONTAL_LINE, HORIZONTAL_LINE)
242 | ps = append(ps, addp...)
243 | }
244 | }
245 |
246 | //draw tab content below the TabPane
247 | if i == tp.activeTabIndex {
248 | blockPoints := buf2pt(tab.Buffer())
249 | for i := 0; i < len(blockPoints); i++ {
250 | blockPoints[i].Y += tp.Height + tp.Y
251 | }
252 | ps = append(ps, blockPoints...)
253 | }
254 | }
255 |
256 | for _, v := range ps {
257 | buf.Set(v.X, v.Y, NewCell(v.Ch, v.Fg, v.Bg))
258 | }
259 | buf.Sync()
260 | return buf
261 | }
262 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/theme.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | package termui
6 |
7 | import "strings"
8 |
9 | /*
10 | // A ColorScheme represents the current look-and-feel of the dashboard.
11 | type ColorScheme struct {
12 | BodyBg Attribute
13 | BlockBg Attribute
14 | HasBorder bool
15 | BorderFg Attribute
16 | BorderBg Attribute
17 | BorderLabelTextFg Attribute
18 | BorderLabelTextBg Attribute
19 | ParTextFg Attribute
20 | ParTextBg Attribute
21 | SparklineLine Attribute
22 | SparklineTitle Attribute
23 | GaugeBar Attribute
24 | GaugePercent Attribute
25 | LineChartLine Attribute
26 | LineChartAxes Attribute
27 | ListItemFg Attribute
28 | ListItemBg Attribute
29 | BarChartBar Attribute
30 | BarChartText Attribute
31 | BarChartNum Attribute
32 | MBarChartBar Attribute
33 | MBarChartText Attribute
34 | MBarChartNum Attribute
35 | TabActiveBg Attribute
36 | }
37 |
38 | // default color scheme depends on the user's terminal setting.
39 | var themeDefault = ColorScheme{HasBorder: true}
40 |
41 | var themeHelloWorld = ColorScheme{
42 | BodyBg: ColorBlack,
43 | BlockBg: ColorBlack,
44 | HasBorder: true,
45 | BorderFg: ColorWhite,
46 | BorderBg: ColorBlack,
47 | BorderLabelTextBg: ColorBlack,
48 | BorderLabelTextFg: ColorGreen,
49 | ParTextBg: ColorBlack,
50 | ParTextFg: ColorWhite,
51 | SparklineLine: ColorMagenta,
52 | SparklineTitle: ColorWhite,
53 | GaugeBar: ColorRed,
54 | GaugePercent: ColorWhite,
55 | LineChartLine: ColorYellow | AttrBold,
56 | LineChartAxes: ColorWhite,
57 | ListItemBg: ColorBlack,
58 | ListItemFg: ColorYellow,
59 | BarChartBar: ColorRed,
60 | BarChartNum: ColorWhite,
61 | BarChartText: ColorCyan,
62 | MBarChartBar: ColorRed,
63 | MBarChartNum: ColorWhite,
64 | MBarChartText: ColorCyan,
65 | TabActiveBg: ColorMagenta,
66 | }
67 |
68 | var theme = themeDefault // global dep
69 |
70 | // Theme returns the currently used theme.
71 | func Theme() ColorScheme {
72 | return theme
73 | }
74 |
75 | // SetTheme sets a new, custom theme.
76 | func SetTheme(newTheme ColorScheme) {
77 | theme = newTheme
78 | }
79 |
80 | // UseTheme sets a predefined scheme. Currently available: "hello-world" and
81 | // "black-and-white".
82 | func UseTheme(th string) {
83 | switch th {
84 | case "helloworld":
85 | theme = themeHelloWorld
86 | default:
87 | theme = themeDefault
88 | }
89 | }
90 | */
91 |
92 | var ColorMap = map[string]Attribute{
93 | "fg": ColorWhite,
94 | "bg": ColorDefault,
95 | "border.fg": ColorWhite,
96 | "label.fg": ColorGreen,
97 | "par.fg": ColorYellow,
98 | "par.label.bg": ColorWhite,
99 | }
100 |
101 | func ThemeAttr(name string) Attribute {
102 | return lookUpAttr(ColorMap, name)
103 | }
104 |
105 | func lookUpAttr(clrmap map[string]Attribute, name string) Attribute {
106 | a, ok := clrmap[name]
107 | if ok {
108 | return a
109 | }
110 |
111 | ns := strings.Split(name, ".")
112 | for i := range ns {
113 | nn := strings.Join(ns[i:len(ns)], ".")
114 | a, ok = ColorMap[nn]
115 | if ok {
116 | break
117 | }
118 | }
119 |
120 | return a
121 | }
122 |
123 | // 0<=r,g,b <= 5
124 | func ColorRGB(r, g, b int) Attribute {
125 | within := func(n int) int {
126 | if n < 0 {
127 | return 0
128 | }
129 |
130 | if n > 5 {
131 | return 5
132 | }
133 |
134 | return n
135 | }
136 |
137 | r, b, g = within(r), within(b), within(g)
138 | return Attribute(0x0f + 36*r + 6*g + b)
139 | }
140 |
141 | // Convert from familiar 24 bit colors into 6 bit terminal colors
142 | func ColorRGB24(r, g, b int) Attribute {
143 | return ColorRGB(r/51, g/51, b/51)
144 | }
145 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/utils.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | package termui
6 |
7 | import (
8 | "regexp"
9 | "strings"
10 |
11 | rw "github.com/mattn/go-runewidth"
12 | tb "github.com/nsf/termbox-go"
13 | )
14 |
15 | /* ---------------Port from termbox-go --------------------- */
16 |
17 | // Attribute is printable cell's color and style.
18 | type Attribute uint16
19 |
20 | // 8 basic clolrs
21 | const (
22 | ColorDefault Attribute = iota
23 | ColorBlack
24 | ColorRed
25 | ColorGreen
26 | ColorYellow
27 | ColorBlue
28 | ColorMagenta
29 | ColorCyan
30 | ColorWhite
31 | )
32 |
33 | // Have a constant that defines number of colors
34 | const NumberofColors = 8
35 |
36 | // Text style
37 | const (
38 | AttrBold Attribute = 1 << (iota + 9)
39 | AttrUnderline
40 | AttrReverse
41 | )
42 |
43 | var (
44 | dot = "…"
45 | dotw = rw.StringWidth(dot)
46 | )
47 |
48 | // termbox passthrough
49 | type OutputMode int
50 |
51 | // termbox passthrough
52 | const (
53 | OutputCurrent OutputMode = iota
54 | OutputNormal
55 | Output256
56 | Output216
57 | OutputGrayscale
58 | )
59 |
60 | /* ----------------------- End ----------------------------- */
61 |
62 | func toTmAttr(x Attribute) tb.Attribute {
63 | return tb.Attribute(x)
64 | }
65 |
66 | func str2runes(s string) []rune {
67 | return []rune(s)
68 | }
69 |
70 | // Here for backwards-compatibility.
71 | func trimStr2Runes(s string, w int) []rune {
72 | return TrimStr2Runes(s, w)
73 | }
74 |
75 | // TrimStr2Runes trims string to w[-1 rune], appends …, and returns the runes
76 | // of that string if string is grather then n. If string is small then w,
77 | // return the runes.
78 | func TrimStr2Runes(s string, w int) []rune {
79 | if w <= 0 {
80 | return []rune{}
81 | }
82 |
83 | sw := rw.StringWidth(s)
84 | if sw > w {
85 | return []rune(rw.Truncate(s, w, dot))
86 | }
87 | return str2runes(s)
88 | }
89 |
90 | // TrimStrIfAppropriate trim string to "s[:-1] + …"
91 | // if string > width otherwise return string
92 | func TrimStrIfAppropriate(s string, w int) string {
93 | if w <= 0 {
94 | return ""
95 | }
96 |
97 | sw := rw.StringWidth(s)
98 | if sw > w {
99 | return rw.Truncate(s, w, dot)
100 | }
101 |
102 | return s
103 | }
104 |
105 | func strWidth(s string) int {
106 | return rw.StringWidth(s)
107 | }
108 |
109 | func charWidth(ch rune) int {
110 | return rw.RuneWidth(ch)
111 | }
112 |
113 | var whiteSpaceRegex = regexp.MustCompile(`\s`)
114 |
115 | // StringToAttribute converts text to a termui attribute. You may specify more
116 | // then one attribute like that: "BLACK, BOLD, ...". All whitespaces
117 | // are ignored.
118 | func StringToAttribute(text string) Attribute {
119 | text = whiteSpaceRegex.ReplaceAllString(strings.ToLower(text), "")
120 | attributes := strings.Split(text, ",")
121 | result := Attribute(0)
122 |
123 | for _, theAttribute := range attributes {
124 | var match Attribute
125 | switch theAttribute {
126 | case "reset", "default":
127 | match = ColorDefault
128 |
129 | case "black":
130 | match = ColorBlack
131 |
132 | case "red":
133 | match = ColorRed
134 |
135 | case "green":
136 | match = ColorGreen
137 |
138 | case "yellow":
139 | match = ColorYellow
140 |
141 | case "blue":
142 | match = ColorBlue
143 |
144 | case "magenta":
145 | match = ColorMagenta
146 |
147 | case "cyan":
148 | match = ColorCyan
149 |
150 | case "white":
151 | match = ColorWhite
152 |
153 | case "bold":
154 | match = AttrBold
155 |
156 | case "underline":
157 | match = AttrUnderline
158 |
159 | case "reverse":
160 | match = AttrReverse
161 | }
162 |
163 | result |= match
164 | }
165 |
166 | return result
167 | }
168 |
169 | // TextCells returns a coloured text cells []Cell
170 | func TextCells(s string, fg, bg Attribute) []Cell {
171 | cs := make([]Cell, 0, len(s))
172 |
173 | // sequence := MarkdownTextRendererFactory{}.TextRenderer(s).Render(fg, bg)
174 | // runes := []rune(sequence.NormalizedText)
175 | runes := str2runes(s)
176 |
177 | for n := range runes {
178 | // point, _ := sequence.PointAt(n, 0, 0)
179 | // cs = append(cs, Cell{point.Ch, point.Fg, point.Bg})
180 | cs = append(cs, Cell{runes[n], fg, bg})
181 | }
182 | return cs
183 | }
184 |
185 | // Width returns the actual screen space the cell takes (usually 1 or 2).
186 | func (c Cell) Width() int {
187 | return charWidth(c.Ch)
188 | }
189 |
190 | // Copy return a copy of c
191 | func (c Cell) Copy() Cell {
192 | return c
193 | }
194 |
195 | // TrimTxCells trims the overflowed text cells sequence.
196 | func TrimTxCells(cs []Cell, w int) []Cell {
197 | if len(cs) <= w {
198 | return cs
199 | }
200 | return cs[:w]
201 | }
202 |
203 | // DTrimTxCls trims the overflowed text cells sequence and append dots at the end.
204 | func DTrimTxCls(cs []Cell, w int) []Cell {
205 | l := len(cs)
206 | if l <= 0 {
207 | return []Cell{}
208 | }
209 |
210 | rt := make([]Cell, 0, w)
211 | csw := 0
212 | for i := 0; i < l && csw <= w; i++ {
213 | c := cs[i]
214 | cw := c.Width()
215 |
216 | if cw+csw < w {
217 | rt = append(rt, c)
218 | csw += cw
219 | } else {
220 | rt = append(rt, Cell{'…', c.Fg, c.Bg})
221 | break
222 | }
223 | }
224 |
225 | return rt
226 | }
227 |
228 | func CellsToStr(cs []Cell) string {
229 | str := ""
230 | for _, c := range cs {
231 | str += string(c.Ch)
232 | }
233 | return str
234 | }
235 |
236 | // Passthrough to termbox using termbox constants above
237 | func SetOutputMode(mode OutputMode) {
238 | switch mode {
239 | case OutputCurrent:
240 | tb.SetOutputMode(tb.OutputCurrent)
241 | case OutputNormal:
242 | tb.SetOutputMode(tb.OutputNormal)
243 | case Output256:
244 | tb.SetOutputMode(tb.Output256)
245 | case Output216:
246 | tb.SetOutputMode(tb.Output216)
247 | case OutputGrayscale:
248 | tb.SetOutputMode(tb.OutputGrayscale)
249 | }
250 | }
251 |
--------------------------------------------------------------------------------
/vendor/github.com/gizak/termui/widget.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Zack Guo . All rights reserved.
2 | // Use of this source code is governed by a MIT license that can
3 | // be found in the LICENSE file.
4 |
5 | package termui
6 |
7 | import (
8 | "fmt"
9 | "sync"
10 | )
11 |
12 | // event mixins
13 | type WgtMgr map[string]WgtInfo
14 |
15 | type WgtInfo struct {
16 | Handlers map[string]func(Event)
17 | WgtRef Widget
18 | Id string
19 | }
20 |
21 | type Widget interface {
22 | Id() string
23 | }
24 |
25 | func NewWgtInfo(wgt Widget) WgtInfo {
26 | return WgtInfo{
27 | Handlers: make(map[string]func(Event)),
28 | WgtRef: wgt,
29 | Id: wgt.Id(),
30 | }
31 | }
32 |
33 | func NewWgtMgr() WgtMgr {
34 | wm := WgtMgr(make(map[string]WgtInfo))
35 | return wm
36 | }
37 |
38 | func (wm WgtMgr) AddWgt(wgt Widget) {
39 | wm[wgt.Id()] = NewWgtInfo(wgt)
40 | }
41 |
42 | func (wm WgtMgr) RmWgt(wgt Widget) {
43 | wm.RmWgtById(wgt.Id())
44 | }
45 |
46 | func (wm WgtMgr) RmWgtById(id string) {
47 | delete(wm, id)
48 | }
49 |
50 | func (wm WgtMgr) AddWgtHandler(id, path string, h func(Event)) {
51 | if w, ok := wm[id]; ok {
52 | w.Handlers[path] = h
53 | }
54 | }
55 |
56 | func (wm WgtMgr) RmWgtHandler(id, path string) {
57 | if w, ok := wm[id]; ok {
58 | delete(w.Handlers, path)
59 | }
60 | }
61 |
62 | var counter struct {
63 | sync.RWMutex
64 | count int
65 | }
66 |
67 | func GenId() string {
68 | counter.Lock()
69 | defer counter.Unlock()
70 |
71 | counter.count += 1
72 | return fmt.Sprintf("%d", counter.count)
73 | }
74 |
75 | func (wm WgtMgr) WgtHandlersHook() func(Event) {
76 | return func(e Event) {
77 | for _, v := range wm {
78 | if val, ok := v.Handlers[e.ID]; ok {
79 | val(e)
80 | }
81 | }
82 | }
83 | }
84 |
85 | var DefaultWgtMgr WgtMgr
86 |
87 | func (b *Block) Handle(path string, handler func(Event)) {
88 | if _, ok := DefaultWgtMgr[b.Id()]; !ok {
89 | DefaultWgtMgr.AddWgt(b)
90 | }
91 |
92 | DefaultWgtMgr.AddWgtHandler(b.Id(), path, handler)
93 | }
94 |
--------------------------------------------------------------------------------
/vendor/github.com/mattn/go-runewidth/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 | go:
3 | - tip
4 | before_install:
5 | - go get github.com/mattn/goveralls
6 | - go get golang.org/x/tools/cmd/cover
7 | script:
8 | - $HOME/gopath/bin/goveralls -repotoken lAKAWPzcGsD3A8yBX3BGGtRUdJ6CaGERL
9 |
--------------------------------------------------------------------------------
/vendor/github.com/mattn/go-runewidth/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Yasuhiro Matsumoto
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/vendor/github.com/mattn/go-runewidth/README.mkd:
--------------------------------------------------------------------------------
1 | go-runewidth
2 | ============
3 |
4 | [](https://travis-ci.org/mattn/go-runewidth)
5 | [](https://coveralls.io/r/mattn/go-runewidth?branch=HEAD)
6 | [](http://godoc.org/github.com/mattn/go-runewidth)
7 | [](https://goreportcard.com/report/github.com/mattn/go-runewidth)
8 |
9 | Provides functions to get fixed width of the character or string.
10 |
11 | Usage
12 | -----
13 |
14 | ```go
15 | runewidth.StringWidth("つのだ☆HIRO") == 12
16 | ```
17 |
18 |
19 | Author
20 | ------
21 |
22 | Yasuhiro Matsumoto
23 |
24 | License
25 | -------
26 |
27 | under the MIT License: http://mattn.mit-license.org/2013
28 |
--------------------------------------------------------------------------------
/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go:
--------------------------------------------------------------------------------
1 | // +build appengine
2 |
3 | package runewidth
4 |
5 | // IsEastAsian return true if the current locale is CJK
6 | func IsEastAsian() bool {
7 | return false
8 | }
9 |
--------------------------------------------------------------------------------
/vendor/github.com/mattn/go-runewidth/runewidth_js.go:
--------------------------------------------------------------------------------
1 | // +build js
2 | // +build !appengine
3 |
4 | package runewidth
5 |
6 | func IsEastAsian() bool {
7 | // TODO: Implement this for the web. Detect east asian in a compatible way, and return true.
8 | return false
9 | }
10 |
--------------------------------------------------------------------------------
/vendor/github.com/mattn/go-runewidth/runewidth_posix.go:
--------------------------------------------------------------------------------
1 | // +build !windows
2 | // +build !js
3 | // +build !appengine
4 |
5 | package runewidth
6 |
7 | import (
8 | "os"
9 | "regexp"
10 | "strings"
11 | )
12 |
13 | var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`)
14 |
15 | var mblenTable = map[string]int{
16 | "utf-8": 6,
17 | "utf8": 6,
18 | "jis": 8,
19 | "eucjp": 3,
20 | "euckr": 2,
21 | "euccn": 2,
22 | "sjis": 2,
23 | "cp932": 2,
24 | "cp51932": 2,
25 | "cp936": 2,
26 | "cp949": 2,
27 | "cp950": 2,
28 | "big5": 2,
29 | "gbk": 2,
30 | "gb2312": 2,
31 | }
32 |
33 | func isEastAsian(locale string) bool {
34 | charset := strings.ToLower(locale)
35 | r := reLoc.FindStringSubmatch(locale)
36 | if len(r) == 2 {
37 | charset = strings.ToLower(r[1])
38 | }
39 |
40 | if strings.HasSuffix(charset, "@cjk_narrow") {
41 | return false
42 | }
43 |
44 | for pos, b := range []byte(charset) {
45 | if b == '@' {
46 | charset = charset[:pos]
47 | break
48 | }
49 | }
50 | max := 1
51 | if m, ok := mblenTable[charset]; ok {
52 | max = m
53 | }
54 | if max > 1 && (charset[0] != 'u' ||
55 | strings.HasPrefix(locale, "ja") ||
56 | strings.HasPrefix(locale, "ko") ||
57 | strings.HasPrefix(locale, "zh")) {
58 | return true
59 | }
60 | return false
61 | }
62 |
63 | // IsEastAsian return true if the current locale is CJK
64 | func IsEastAsian() bool {
65 | locale := os.Getenv("LC_CTYPE")
66 | if locale == "" {
67 | locale = os.Getenv("LANG")
68 | }
69 |
70 | // ignore C locale
71 | if locale == "POSIX" || locale == "C" {
72 | return false
73 | }
74 | if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') {
75 | return false
76 | }
77 |
78 | return isEastAsian(locale)
79 | }
80 |
--------------------------------------------------------------------------------
/vendor/github.com/mattn/go-runewidth/runewidth_windows.go:
--------------------------------------------------------------------------------
1 | // +build windows
2 | // +build !appengine
3 |
4 | package runewidth
5 |
6 | import (
7 | "syscall"
8 | )
9 |
10 | var (
11 | kernel32 = syscall.NewLazyDLL("kernel32")
12 | procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP")
13 | )
14 |
15 | // IsEastAsian return true if the current locale is CJK
16 | func IsEastAsian() bool {
17 | r1, _, _ := procGetConsoleOutputCP.Call()
18 | if r1 == 0 {
19 | return false
20 | }
21 |
22 | switch int(r1) {
23 | case 932, 51932, 936, 949, 950:
24 | return true
25 | }
26 |
27 | return false
28 | }
29 |
--------------------------------------------------------------------------------
/vendor/github.com/mitchellh/go-wordwrap/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Mitchell Hashimoto
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/vendor/github.com/mitchellh/go-wordwrap/README.md:
--------------------------------------------------------------------------------
1 | # go-wordwrap
2 |
3 | `go-wordwrap` (Golang package: `wordwrap`) is a package for Go that
4 | automatically wraps words into multiple lines. The primary use case for this
5 | is in formatting CLI output, but of course word wrapping is a generally useful
6 | thing to do.
7 |
8 | ## Installation and Usage
9 |
10 | Install using `go get github.com/mitchellh/go-wordwrap`.
11 |
12 | Full documentation is available at
13 | http://godoc.org/github.com/mitchellh/go-wordwrap
14 |
15 | Below is an example of its usage ignoring errors:
16 |
17 | ```go
18 | wrapped := wordwrap.WrapString("foo bar baz", 3)
19 | fmt.Println(wrapped)
20 | ```
21 |
22 | Would output:
23 |
24 | ```
25 | foo
26 | bar
27 | baz
28 | ```
29 |
30 | ## Word Wrap Algorithm
31 |
32 | This library doesn't use any clever algorithm for word wrapping. The wrapping
33 | is actually very naive: whenever there is whitespace or an explicit linebreak.
34 | The goal of this library is for word wrapping CLI output, so the input is
35 | typically pretty well controlled human language. Because of this, the naive
36 | approach typically works just fine.
37 |
38 | In the future, we'd like to make the algorithm more advanced. We would do
39 | so without breaking the API.
40 |
--------------------------------------------------------------------------------
/vendor/github.com/mitchellh/go-wordwrap/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/mitchellh/go-wordwrap
2 |
--------------------------------------------------------------------------------
/vendor/github.com/mitchellh/go-wordwrap/wordwrap.go:
--------------------------------------------------------------------------------
1 | package wordwrap
2 |
3 | import (
4 | "bytes"
5 | "unicode"
6 | )
7 |
8 | // WrapString wraps the given string within lim width in characters.
9 | //
10 | // Wrapping is currently naive and only happens at white-space. A future
11 | // version of the library will implement smarter wrapping. This means that
12 | // pathological cases can dramatically reach past the limit, such as a very
13 | // long word.
14 | func WrapString(s string, lim uint) string {
15 | // Initialize a buffer with a slightly larger size to account for breaks
16 | init := make([]byte, 0, len(s))
17 | buf := bytes.NewBuffer(init)
18 |
19 | var current uint
20 | var wordBuf, spaceBuf bytes.Buffer
21 |
22 | for _, char := range s {
23 | if char == '\n' {
24 | if wordBuf.Len() == 0 {
25 | if current+uint(spaceBuf.Len()) > lim {
26 | current = 0
27 | } else {
28 | current += uint(spaceBuf.Len())
29 | spaceBuf.WriteTo(buf)
30 | }
31 | spaceBuf.Reset()
32 | } else {
33 | current += uint(spaceBuf.Len() + wordBuf.Len())
34 | spaceBuf.WriteTo(buf)
35 | spaceBuf.Reset()
36 | wordBuf.WriteTo(buf)
37 | wordBuf.Reset()
38 | }
39 | buf.WriteRune(char)
40 | current = 0
41 | } else if unicode.IsSpace(char) {
42 | if spaceBuf.Len() == 0 || wordBuf.Len() > 0 {
43 | current += uint(spaceBuf.Len() + wordBuf.Len())
44 | spaceBuf.WriteTo(buf)
45 | spaceBuf.Reset()
46 | wordBuf.WriteTo(buf)
47 | wordBuf.Reset()
48 | }
49 |
50 | spaceBuf.WriteRune(char)
51 | } else {
52 |
53 | wordBuf.WriteRune(char)
54 |
55 | if current+uint(spaceBuf.Len()+wordBuf.Len()) > lim && uint(wordBuf.Len()) < lim {
56 | buf.WriteRune('\n')
57 | current = 0
58 | spaceBuf.Reset()
59 | }
60 | }
61 | }
62 |
63 | if wordBuf.Len() == 0 {
64 | if current+uint(spaceBuf.Len()) <= lim {
65 | spaceBuf.WriteTo(buf)
66 | }
67 | } else {
68 | spaceBuf.WriteTo(buf)
69 | wordBuf.WriteTo(buf)
70 | }
71 |
72 | return buf.String()
73 | }
74 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/AUTHORS:
--------------------------------------------------------------------------------
1 | # Please keep this file sorted.
2 |
3 | Georg Reinke
4 | nsf
5 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (C) 2012 termbox-go authors
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/README.md:
--------------------------------------------------------------------------------
1 | [](http://godoc.org/github.com/nsf/termbox-go)
2 |
3 | ## Termbox
4 | Termbox is a library that provides a minimalistic API which allows the programmer to write text-based user interfaces. The library is crossplatform and has both terminal-based implementations on *nix operating systems and a winapi console based implementation for windows operating systems. The basic idea is an abstraction of the greatest common subset of features available on all major terminals and other terminal-like APIs in a minimalistic fashion. Small API means it is easy to implement, test, maintain and learn it, that's what makes the termbox a distinct library in its area.
5 |
6 | ### Installation
7 | Install and update this go package with `go get -u github.com/nsf/termbox-go`
8 |
9 | ### Examples
10 | For examples of what can be done take a look at demos in the _demos directory. You can try them with go run: `go run _demos/keyboard.go`
11 |
12 | There are also some interesting projects using termbox-go:
13 | - [godit](https://github.com/nsf/godit) is an emacsish lightweight text editor written using termbox.
14 | - [gotetris](https://github.com/jjinux/gotetris) is an implementation of Tetris.
15 | - [sokoban-go](https://github.com/rn2dy/sokoban-go) is an implementation of sokoban game.
16 | - [hecate](https://github.com/evanmiller/hecate) is a hex editor designed by Satan.
17 | - [httopd](https://github.com/verdverm/httopd) is top for httpd logs.
18 | - [mop](https://github.com/mop-tracker/mop) is stock market tracker for hackers.
19 | - [termui](https://github.com/gizak/termui) is a terminal dashboard.
20 | - [termloop](https://github.com/JoelOtter/termloop) is a terminal game engine.
21 | - [xterm-color-chart](https://github.com/kutuluk/xterm-color-chart) is a XTerm 256 color chart.
22 | - [gocui](https://github.com/jroimartin/gocui) is a minimalist Go library aimed at creating console user interfaces.
23 | - [dry](https://github.com/moncho/dry) is an interactive cli to manage Docker containers.
24 | - [pxl](https://github.com/ichinaski/pxl) displays images in the terminal.
25 | - [snake-game](https://github.com/DyegoCosta/snake-game) is an implementation of the Snake game.
26 | - [gone](https://github.com/guillaumebreton/gone) is a CLI pomodoro® timer.
27 | - [Spoof.go](https://github.com/sabey/spoofgo) controllable movement spoofing from the cli
28 | - [lf](https://github.com/gokcehan/lf) is a terminal file manager
29 | - [rat](https://github.com/ericfreese/rat) lets you compose shell commands to build terminal applications.
30 | - [httplab](https://github.com/gchaincl/httplab) An interactive web server.
31 | - [tetris](https://github.com/MichaelS11/tetris) Go Tetris with AI option
32 | - [wot](https://github.com/kyu-suke/wot) Wait time during command is completed.
33 | - [2048-go](https://github.com/1984weed/2048-go) is 2048 in Go
34 | - [jv](https://github.com/maxzender/jv) helps you view JSON on the command-line.
35 | - [pinger](https://github.com/hirose31/pinger) helps you to monitor numerous hosts using ICMP ECHO_REQUEST.
36 | - [vixl44](https://github.com/sebashwa/vixl44) lets you create pixel art inside your terminal using vim movements
37 | - [zterm](https://github.com/varunrau/zterm) is a typing game inspired by http://zty.pe/
38 | - [gotypist](https://github.com/pb-/gotypist) is a fun touch-typing tutor following Steve Yegge's method.
39 | - [cointop](https://github.com/miguelmota/cointop) is an interactive terminal based UI application for tracking cryptocurrencies.
40 | - [pexpo](https://github.com/nnao45/pexpo) is a terminal sending ping tool written in Go.
41 |
42 | ### API reference
43 | [godoc.org/github.com/nsf/termbox-go](http://godoc.org/github.com/nsf/termbox-go)
44 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/api_common.go:
--------------------------------------------------------------------------------
1 | // termbox is a library for creating cross-platform text-based interfaces
2 | package termbox
3 |
4 | // public API, common OS agnostic part
5 |
6 | type (
7 | InputMode int
8 | OutputMode int
9 | EventType uint8
10 | Modifier uint8
11 | Key uint16
12 | Attribute uint16
13 | )
14 |
15 | // This type represents a termbox event. The 'Mod', 'Key' and 'Ch' fields are
16 | // valid if 'Type' is EventKey. The 'Width' and 'Height' fields are valid if
17 | // 'Type' is EventResize. The 'Err' field is valid if 'Type' is EventError.
18 | type Event struct {
19 | Type EventType // one of Event* constants
20 | Mod Modifier // one of Mod* constants or 0
21 | Key Key // one of Key* constants, invalid if 'Ch' is not 0
22 | Ch rune // a unicode character
23 | Width int // width of the screen
24 | Height int // height of the screen
25 | Err error // error in case if input failed
26 | MouseX int // x coord of mouse
27 | MouseY int // y coord of mouse
28 | N int // number of bytes written when getting a raw event
29 | }
30 |
31 | // A cell, single conceptual entity on the screen. The screen is basically a 2d
32 | // array of cells. 'Ch' is a unicode character, 'Fg' and 'Bg' are foreground
33 | // and background attributes respectively.
34 | type Cell struct {
35 | Ch rune
36 | Fg Attribute
37 | Bg Attribute
38 | }
39 |
40 | // To know if termbox has been initialized or not
41 | var (
42 | IsInit bool = false
43 | )
44 |
45 | // Key constants, see Event.Key field.
46 | const (
47 | KeyF1 Key = 0xFFFF - iota
48 | KeyF2
49 | KeyF3
50 | KeyF4
51 | KeyF5
52 | KeyF6
53 | KeyF7
54 | KeyF8
55 | KeyF9
56 | KeyF10
57 | KeyF11
58 | KeyF12
59 | KeyInsert
60 | KeyDelete
61 | KeyHome
62 | KeyEnd
63 | KeyPgup
64 | KeyPgdn
65 | KeyArrowUp
66 | KeyArrowDown
67 | KeyArrowLeft
68 | KeyArrowRight
69 | key_min // see terminfo
70 | MouseLeft
71 | MouseMiddle
72 | MouseRight
73 | MouseRelease
74 | MouseWheelUp
75 | MouseWheelDown
76 | )
77 |
78 | const (
79 | KeyCtrlTilde Key = 0x00
80 | KeyCtrl2 Key = 0x00
81 | KeyCtrlSpace Key = 0x00
82 | KeyCtrlA Key = 0x01
83 | KeyCtrlB Key = 0x02
84 | KeyCtrlC Key = 0x03
85 | KeyCtrlD Key = 0x04
86 | KeyCtrlE Key = 0x05
87 | KeyCtrlF Key = 0x06
88 | KeyCtrlG Key = 0x07
89 | KeyBackspace Key = 0x08
90 | KeyCtrlH Key = 0x08
91 | KeyTab Key = 0x09
92 | KeyCtrlI Key = 0x09
93 | KeyCtrlJ Key = 0x0A
94 | KeyCtrlK Key = 0x0B
95 | KeyCtrlL Key = 0x0C
96 | KeyEnter Key = 0x0D
97 | KeyCtrlM Key = 0x0D
98 | KeyCtrlN Key = 0x0E
99 | KeyCtrlO Key = 0x0F
100 | KeyCtrlP Key = 0x10
101 | KeyCtrlQ Key = 0x11
102 | KeyCtrlR Key = 0x12
103 | KeyCtrlS Key = 0x13
104 | KeyCtrlT Key = 0x14
105 | KeyCtrlU Key = 0x15
106 | KeyCtrlV Key = 0x16
107 | KeyCtrlW Key = 0x17
108 | KeyCtrlX Key = 0x18
109 | KeyCtrlY Key = 0x19
110 | KeyCtrlZ Key = 0x1A
111 | KeyEsc Key = 0x1B
112 | KeyCtrlLsqBracket Key = 0x1B
113 | KeyCtrl3 Key = 0x1B
114 | KeyCtrl4 Key = 0x1C
115 | KeyCtrlBackslash Key = 0x1C
116 | KeyCtrl5 Key = 0x1D
117 | KeyCtrlRsqBracket Key = 0x1D
118 | KeyCtrl6 Key = 0x1E
119 | KeyCtrl7 Key = 0x1F
120 | KeyCtrlSlash Key = 0x1F
121 | KeyCtrlUnderscore Key = 0x1F
122 | KeySpace Key = 0x20
123 | KeyBackspace2 Key = 0x7F
124 | KeyCtrl8 Key = 0x7F
125 | )
126 |
127 | // Alt modifier constant, see Event.Mod field and SetInputMode function.
128 | const (
129 | ModAlt Modifier = 1 << iota
130 | ModMotion
131 | )
132 |
133 | // Cell colors, you can combine a color with multiple attributes using bitwise
134 | // OR ('|').
135 | const (
136 | ColorDefault Attribute = iota
137 | ColorBlack
138 | ColorRed
139 | ColorGreen
140 | ColorYellow
141 | ColorBlue
142 | ColorMagenta
143 | ColorCyan
144 | ColorWhite
145 | )
146 |
147 | // Cell attributes, it is possible to use multiple attributes by combining them
148 | // using bitwise OR ('|'). Although, colors cannot be combined. But you can
149 | // combine attributes and a single color.
150 | //
151 | // It's worth mentioning that some platforms don't support certain attributes.
152 | // For example windows console doesn't support AttrUnderline. And on some
153 | // terminals applying AttrBold to background may result in blinking text. Use
154 | // them with caution and test your code on various terminals.
155 | const (
156 | AttrBold Attribute = 1 << (iota + 9)
157 | AttrUnderline
158 | AttrReverse
159 | )
160 |
161 | // Input mode. See SetInputMode function.
162 | const (
163 | InputEsc InputMode = 1 << iota
164 | InputAlt
165 | InputMouse
166 | InputCurrent InputMode = 0
167 | )
168 |
169 | // Output mode. See SetOutputMode function.
170 | const (
171 | OutputCurrent OutputMode = iota
172 | OutputNormal
173 | Output256
174 | Output216
175 | OutputGrayscale
176 | )
177 |
178 | // Event type. See Event.Type field.
179 | const (
180 | EventKey EventType = iota
181 | EventResize
182 | EventMouse
183 | EventError
184 | EventInterrupt
185 | EventRaw
186 | EventNone
187 | )
188 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/api_windows.go:
--------------------------------------------------------------------------------
1 | package termbox
2 |
3 | import (
4 | "syscall"
5 | )
6 |
7 | // public API
8 |
9 | // Initializes termbox library. This function should be called before any other functions.
10 | // After successful initialization, the library must be finalized using 'Close' function.
11 | //
12 | // Example usage:
13 | // err := termbox.Init()
14 | // if err != nil {
15 | // panic(err)
16 | // }
17 | // defer termbox.Close()
18 | func Init() error {
19 | var err error
20 |
21 | interrupt, err = create_event()
22 | if err != nil {
23 | return err
24 | }
25 |
26 | in, err = syscall.Open("CONIN$", syscall.O_RDWR, 0)
27 | if err != nil {
28 | return err
29 | }
30 | out, err = syscall.Open("CONOUT$", syscall.O_RDWR, 0)
31 | if err != nil {
32 | return err
33 | }
34 |
35 | err = get_console_mode(in, &orig_mode)
36 | if err != nil {
37 | return err
38 | }
39 |
40 | err = set_console_mode(in, enable_window_input)
41 | if err != nil {
42 | return err
43 | }
44 |
45 | orig_size = get_term_size(out)
46 | win_size := get_win_size(out)
47 |
48 | err = set_console_screen_buffer_size(out, win_size)
49 | if err != nil {
50 | return err
51 | }
52 |
53 | err = get_console_cursor_info(out, &orig_cursor_info)
54 | if err != nil {
55 | return err
56 | }
57 |
58 | show_cursor(false)
59 | term_size = get_term_size(out)
60 | back_buffer.init(int(term_size.x), int(term_size.y))
61 | front_buffer.init(int(term_size.x), int(term_size.y))
62 | back_buffer.clear()
63 | front_buffer.clear()
64 | clear()
65 |
66 | diffbuf = make([]diff_msg, 0, 32)
67 |
68 | go input_event_producer()
69 | IsInit = true
70 | return nil
71 | }
72 |
73 | // Finalizes termbox library, should be called after successful initialization
74 | // when termbox's functionality isn't required anymore.
75 | func Close() {
76 | // we ignore errors here, because we can't really do anything about them
77 | Clear(0, 0)
78 | Flush()
79 |
80 | // stop event producer
81 | cancel_comm <- true
82 | set_event(interrupt)
83 | select {
84 | case <-input_comm:
85 | default:
86 | }
87 | <-cancel_done_comm
88 |
89 | set_console_cursor_info(out, &orig_cursor_info)
90 | set_console_cursor_position(out, coord{})
91 | set_console_screen_buffer_size(out, orig_size)
92 | set_console_mode(in, orig_mode)
93 | syscall.Close(in)
94 | syscall.Close(out)
95 | syscall.Close(interrupt)
96 | IsInit = false
97 | }
98 |
99 | // Interrupt an in-progress call to PollEvent by causing it to return
100 | // EventInterrupt. Note that this function will block until the PollEvent
101 | // function has successfully been interrupted.
102 | func Interrupt() {
103 | interrupt_comm <- struct{}{}
104 | }
105 |
106 | // Synchronizes the internal back buffer with the terminal.
107 | func Flush() error {
108 | update_size_maybe()
109 | prepare_diff_messages()
110 | for _, diff := range diffbuf {
111 | r := small_rect{
112 | left: 0,
113 | top: diff.pos,
114 | right: term_size.x - 1,
115 | bottom: diff.pos + diff.lines - 1,
116 | }
117 | write_console_output(out, diff.chars, r)
118 | }
119 | if !is_cursor_hidden(cursor_x, cursor_y) {
120 | move_cursor(cursor_x, cursor_y)
121 | }
122 | return nil
123 | }
124 |
125 | // Sets the position of the cursor. See also HideCursor().
126 | func SetCursor(x, y int) {
127 | if is_cursor_hidden(cursor_x, cursor_y) && !is_cursor_hidden(x, y) {
128 | show_cursor(true)
129 | }
130 |
131 | if !is_cursor_hidden(cursor_x, cursor_y) && is_cursor_hidden(x, y) {
132 | show_cursor(false)
133 | }
134 |
135 | cursor_x, cursor_y = x, y
136 | if !is_cursor_hidden(cursor_x, cursor_y) {
137 | move_cursor(cursor_x, cursor_y)
138 | }
139 | }
140 |
141 | // The shortcut for SetCursor(-1, -1).
142 | func HideCursor() {
143 | SetCursor(cursor_hidden, cursor_hidden)
144 | }
145 |
146 | // Changes cell's parameters in the internal back buffer at the specified
147 | // position.
148 | func SetCell(x, y int, ch rune, fg, bg Attribute) {
149 | if x < 0 || x >= back_buffer.width {
150 | return
151 | }
152 | if y < 0 || y >= back_buffer.height {
153 | return
154 | }
155 |
156 | back_buffer.cells[y*back_buffer.width+x] = Cell{ch, fg, bg}
157 | }
158 |
159 | // Returns a slice into the termbox's back buffer. You can get its dimensions
160 | // using 'Size' function. The slice remains valid as long as no 'Clear' or
161 | // 'Flush' function calls were made after call to this function.
162 | func CellBuffer() []Cell {
163 | return back_buffer.cells
164 | }
165 |
166 | // Wait for an event and return it. This is a blocking function call.
167 | func PollEvent() Event {
168 | select {
169 | case ev := <-input_comm:
170 | return ev
171 | case <-interrupt_comm:
172 | return Event{Type: EventInterrupt}
173 | }
174 | }
175 |
176 | // Returns the size of the internal back buffer (which is mostly the same as
177 | // console's window size in characters). But it doesn't always match the size
178 | // of the console window, after the console size has changed, the internal back
179 | // buffer will get in sync only after Clear or Flush function calls.
180 | func Size() (int, int) {
181 | return int(term_size.x), int(term_size.y)
182 | }
183 |
184 | // Clears the internal back buffer.
185 | func Clear(fg, bg Attribute) error {
186 | foreground, background = fg, bg
187 | update_size_maybe()
188 | back_buffer.clear()
189 | return nil
190 | }
191 |
192 | // Sets termbox input mode. Termbox has two input modes:
193 | //
194 | // 1. Esc input mode. When ESC sequence is in the buffer and it doesn't match
195 | // any known sequence. ESC means KeyEsc. This is the default input mode.
196 | //
197 | // 2. Alt input mode. When ESC sequence is in the buffer and it doesn't match
198 | // any known sequence. ESC enables ModAlt modifier for the next keyboard event.
199 | //
200 | // Both input modes can be OR'ed with Mouse mode. Setting Mouse mode bit up will
201 | // enable mouse button press/release and drag events.
202 | //
203 | // If 'mode' is InputCurrent, returns the current input mode. See also Input*
204 | // constants.
205 | func SetInputMode(mode InputMode) InputMode {
206 | if mode == InputCurrent {
207 | return input_mode
208 | }
209 | if mode&InputMouse != 0 {
210 | err := set_console_mode(in, enable_window_input|enable_mouse_input|enable_extended_flags)
211 | if err != nil {
212 | panic(err)
213 | }
214 | } else {
215 | err := set_console_mode(in, enable_window_input)
216 | if err != nil {
217 | panic(err)
218 | }
219 | }
220 |
221 | input_mode = mode
222 | return input_mode
223 | }
224 |
225 | // Sets the termbox output mode.
226 | //
227 | // Windows console does not support extra colour modes,
228 | // so this will always set and return OutputNormal.
229 | func SetOutputMode(mode OutputMode) OutputMode {
230 | return OutputNormal
231 | }
232 |
233 | // Sync comes handy when something causes desync between termbox's understanding
234 | // of a terminal buffer and the reality. Such as a third party process. Sync
235 | // forces a complete resync between the termbox and a terminal, it may not be
236 | // visually pretty though. At the moment on Windows it does nothing.
237 | func Sync() error {
238 | return nil
239 | }
240 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/collect_terminfo.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import sys, os, subprocess
4 |
5 | def escaped(s):
6 | return repr(s)[1:-1]
7 |
8 | def tput(term, name):
9 | try:
10 | return subprocess.check_output(['tput', '-T%s' % term, name]).decode()
11 | except subprocess.CalledProcessError as e:
12 | return e.output.decode()
13 |
14 |
15 | def w(s):
16 | if s == None:
17 | return
18 | sys.stdout.write(s)
19 |
20 | terminals = {
21 | 'xterm' : 'xterm',
22 | 'rxvt-256color' : 'rxvt_256color',
23 | 'rxvt-unicode' : 'rxvt_unicode',
24 | 'linux' : 'linux',
25 | 'Eterm' : 'eterm',
26 | 'screen' : 'screen'
27 | }
28 |
29 | keys = [
30 | "F1", "kf1",
31 | "F2", "kf2",
32 | "F3", "kf3",
33 | "F4", "kf4",
34 | "F5", "kf5",
35 | "F6", "kf6",
36 | "F7", "kf7",
37 | "F8", "kf8",
38 | "F9", "kf9",
39 | "F10", "kf10",
40 | "F11", "kf11",
41 | "F12", "kf12",
42 | "INSERT", "kich1",
43 | "DELETE", "kdch1",
44 | "HOME", "khome",
45 | "END", "kend",
46 | "PGUP", "kpp",
47 | "PGDN", "knp",
48 | "KEY_UP", "kcuu1",
49 | "KEY_DOWN", "kcud1",
50 | "KEY_LEFT", "kcub1",
51 | "KEY_RIGHT", "kcuf1"
52 | ]
53 |
54 | funcs = [
55 | "T_ENTER_CA", "smcup",
56 | "T_EXIT_CA", "rmcup",
57 | "T_SHOW_CURSOR", "cnorm",
58 | "T_HIDE_CURSOR", "civis",
59 | "T_CLEAR_SCREEN", "clear",
60 | "T_SGR0", "sgr0",
61 | "T_UNDERLINE", "smul",
62 | "T_BOLD", "bold",
63 | "T_BLINK", "blink",
64 | "T_REVERSE", "rev",
65 | "T_ENTER_KEYPAD", "smkx",
66 | "T_EXIT_KEYPAD", "rmkx"
67 | ]
68 |
69 | def iter_pairs(iterable):
70 | iterable = iter(iterable)
71 | while True:
72 | yield (next(iterable), next(iterable))
73 |
74 | def do_term(term, nick):
75 | w("// %s\n" % term)
76 | w("var %s_keys = []string{\n\t" % nick)
77 | for k, v in iter_pairs(keys):
78 | w('"')
79 | w(escaped(tput(term, v)))
80 | w('",')
81 | w("\n}\n")
82 | w("var %s_funcs = []string{\n\t" % nick)
83 | for k,v in iter_pairs(funcs):
84 | w('"')
85 | if v == "sgr":
86 | w("\\033[3%d;4%dm")
87 | elif v == "cup":
88 | w("\\033[%d;%dH")
89 | else:
90 | w(escaped(tput(term, v)))
91 | w('", ')
92 | w("\n}\n\n")
93 |
94 | def do_terms(d):
95 | w("var terms = []struct {\n")
96 | w("\tname string\n")
97 | w("\tkeys []string\n")
98 | w("\tfuncs []string\n")
99 | w("}{\n")
100 | for k, v in d.items():
101 | w('\t{"%s", %s_keys, %s_funcs},\n' % (k, v, v))
102 | w("}\n\n")
103 |
104 | w("// +build !windows\n\npackage termbox\n\n")
105 |
106 | for k,v in terminals.items():
107 | do_term(k, v)
108 |
109 | do_terms(terminals)
110 |
111 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/escwait.go:
--------------------------------------------------------------------------------
1 | // +build !darwin
2 |
3 | package termbox
4 |
5 | // On all systems other than macOS, disable behavior which will wait before
6 | // deciding that the escape key was pressed, to account for partially send
7 | // escape sequences, especially with regard to lengthy mouse sequences.
8 | // See https://github.com/nsf/termbox-go/issues/132
9 | func enable_wait_for_escape_sequence() bool {
10 | return false
11 | }
12 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/escwait_darwin.go:
--------------------------------------------------------------------------------
1 | package termbox
2 |
3 | // On macOS, enable behavior which will wait before deciding that the escape
4 | // key was pressed, to account for partially send escape sequences, especially
5 | // with regard to lengthy mouse sequences.
6 | // See https://github.com/nsf/termbox-go/issues/132
7 | func enable_wait_for_escape_sequence() bool {
8 | return true
9 | }
10 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/syscalls.go:
--------------------------------------------------------------------------------
1 | // +build ignore
2 |
3 | package termbox
4 |
5 | /*
6 | #include
7 | #include
8 | */
9 | import "C"
10 |
11 | type syscall_Termios C.struct_termios
12 |
13 | const (
14 | syscall_IGNBRK = C.IGNBRK
15 | syscall_BRKINT = C.BRKINT
16 | syscall_PARMRK = C.PARMRK
17 | syscall_ISTRIP = C.ISTRIP
18 | syscall_INLCR = C.INLCR
19 | syscall_IGNCR = C.IGNCR
20 | syscall_ICRNL = C.ICRNL
21 | syscall_IXON = C.IXON
22 | syscall_OPOST = C.OPOST
23 | syscall_ECHO = C.ECHO
24 | syscall_ECHONL = C.ECHONL
25 | syscall_ICANON = C.ICANON
26 | syscall_ISIG = C.ISIG
27 | syscall_IEXTEN = C.IEXTEN
28 | syscall_CSIZE = C.CSIZE
29 | syscall_PARENB = C.PARENB
30 | syscall_CS8 = C.CS8
31 | syscall_VMIN = C.VMIN
32 | syscall_VTIME = C.VTIME
33 |
34 | // on darwin change these to (on *bsd too?):
35 | // C.TIOCGETA
36 | // C.TIOCSETA
37 | syscall_TCGETS = C.TCGETS
38 | syscall_TCSETS = C.TCSETS
39 | )
40 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/syscalls_darwin.go:
--------------------------------------------------------------------------------
1 | // Created by cgo -godefs - DO NOT EDIT
2 | // cgo -godefs syscalls.go
3 |
4 | // +build !amd64
5 |
6 | package termbox
7 |
8 | type syscall_Termios struct {
9 | Iflag uint32
10 | Oflag uint32
11 | Cflag uint32
12 | Lflag uint32
13 | Cc [20]uint8
14 | Ispeed uint32
15 | Ospeed uint32
16 | }
17 |
18 | const (
19 | syscall_IGNBRK = 0x1
20 | syscall_BRKINT = 0x2
21 | syscall_PARMRK = 0x8
22 | syscall_ISTRIP = 0x20
23 | syscall_INLCR = 0x40
24 | syscall_IGNCR = 0x80
25 | syscall_ICRNL = 0x100
26 | syscall_IXON = 0x200
27 | syscall_OPOST = 0x1
28 | syscall_ECHO = 0x8
29 | syscall_ECHONL = 0x10
30 | syscall_ICANON = 0x100
31 | syscall_ISIG = 0x80
32 | syscall_IEXTEN = 0x400
33 | syscall_CSIZE = 0x300
34 | syscall_PARENB = 0x1000
35 | syscall_CS8 = 0x300
36 | syscall_VMIN = 0x10
37 | syscall_VTIME = 0x11
38 |
39 | syscall_TCGETS = 0x402c7413
40 | syscall_TCSETS = 0x802c7414
41 | )
42 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/syscalls_darwin_amd64.go:
--------------------------------------------------------------------------------
1 | // Created by cgo -godefs - DO NOT EDIT
2 | // cgo -godefs syscalls.go
3 |
4 | package termbox
5 |
6 | type syscall_Termios struct {
7 | Iflag uint64
8 | Oflag uint64
9 | Cflag uint64
10 | Lflag uint64
11 | Cc [20]uint8
12 | Pad_cgo_0 [4]byte
13 | Ispeed uint64
14 | Ospeed uint64
15 | }
16 |
17 | const (
18 | syscall_IGNBRK = 0x1
19 | syscall_BRKINT = 0x2
20 | syscall_PARMRK = 0x8
21 | syscall_ISTRIP = 0x20
22 | syscall_INLCR = 0x40
23 | syscall_IGNCR = 0x80
24 | syscall_ICRNL = 0x100
25 | syscall_IXON = 0x200
26 | syscall_OPOST = 0x1
27 | syscall_ECHO = 0x8
28 | syscall_ECHONL = 0x10
29 | syscall_ICANON = 0x100
30 | syscall_ISIG = 0x80
31 | syscall_IEXTEN = 0x400
32 | syscall_CSIZE = 0x300
33 | syscall_PARENB = 0x1000
34 | syscall_CS8 = 0x300
35 | syscall_VMIN = 0x10
36 | syscall_VTIME = 0x11
37 |
38 | syscall_TCGETS = 0x40487413
39 | syscall_TCSETS = 0x80487414
40 | )
41 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/syscalls_dragonfly.go:
--------------------------------------------------------------------------------
1 | // Created by cgo -godefs - DO NOT EDIT
2 | // cgo -godefs syscalls.go
3 |
4 | package termbox
5 |
6 | type syscall_Termios struct {
7 | Iflag uint32
8 | Oflag uint32
9 | Cflag uint32
10 | Lflag uint32
11 | Cc [20]uint8
12 | Ispeed uint32
13 | Ospeed uint32
14 | }
15 |
16 | const (
17 | syscall_IGNBRK = 0x1
18 | syscall_BRKINT = 0x2
19 | syscall_PARMRK = 0x8
20 | syscall_ISTRIP = 0x20
21 | syscall_INLCR = 0x40
22 | syscall_IGNCR = 0x80
23 | syscall_ICRNL = 0x100
24 | syscall_IXON = 0x200
25 | syscall_OPOST = 0x1
26 | syscall_ECHO = 0x8
27 | syscall_ECHONL = 0x10
28 | syscall_ICANON = 0x100
29 | syscall_ISIG = 0x80
30 | syscall_IEXTEN = 0x400
31 | syscall_CSIZE = 0x300
32 | syscall_PARENB = 0x1000
33 | syscall_CS8 = 0x300
34 | syscall_VMIN = 0x10
35 | syscall_VTIME = 0x11
36 |
37 | syscall_TCGETS = 0x402c7413
38 | syscall_TCSETS = 0x802c7414
39 | )
40 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/syscalls_freebsd.go:
--------------------------------------------------------------------------------
1 | // Created by cgo -godefs - DO NOT EDIT
2 | // cgo -godefs syscalls.go
3 |
4 | package termbox
5 |
6 | type syscall_Termios struct {
7 | Iflag uint32
8 | Oflag uint32
9 | Cflag uint32
10 | Lflag uint32
11 | Cc [20]uint8
12 | Ispeed uint32
13 | Ospeed uint32
14 | }
15 |
16 | const (
17 | syscall_IGNBRK = 0x1
18 | syscall_BRKINT = 0x2
19 | syscall_PARMRK = 0x8
20 | syscall_ISTRIP = 0x20
21 | syscall_INLCR = 0x40
22 | syscall_IGNCR = 0x80
23 | syscall_ICRNL = 0x100
24 | syscall_IXON = 0x200
25 | syscall_OPOST = 0x1
26 | syscall_ECHO = 0x8
27 | syscall_ECHONL = 0x10
28 | syscall_ICANON = 0x100
29 | syscall_ISIG = 0x80
30 | syscall_IEXTEN = 0x400
31 | syscall_CSIZE = 0x300
32 | syscall_PARENB = 0x1000
33 | syscall_CS8 = 0x300
34 | syscall_VMIN = 0x10
35 | syscall_VTIME = 0x11
36 |
37 | syscall_TCGETS = 0x402c7413
38 | syscall_TCSETS = 0x802c7414
39 | )
40 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/syscalls_linux.go:
--------------------------------------------------------------------------------
1 | // Created by cgo -godefs - DO NOT EDIT
2 | // cgo -godefs syscalls.go
3 |
4 | package termbox
5 |
6 | import "syscall"
7 |
8 | type syscall_Termios syscall.Termios
9 |
10 | const (
11 | syscall_IGNBRK = syscall.IGNBRK
12 | syscall_BRKINT = syscall.BRKINT
13 | syscall_PARMRK = syscall.PARMRK
14 | syscall_ISTRIP = syscall.ISTRIP
15 | syscall_INLCR = syscall.INLCR
16 | syscall_IGNCR = syscall.IGNCR
17 | syscall_ICRNL = syscall.ICRNL
18 | syscall_IXON = syscall.IXON
19 | syscall_OPOST = syscall.OPOST
20 | syscall_ECHO = syscall.ECHO
21 | syscall_ECHONL = syscall.ECHONL
22 | syscall_ICANON = syscall.ICANON
23 | syscall_ISIG = syscall.ISIG
24 | syscall_IEXTEN = syscall.IEXTEN
25 | syscall_CSIZE = syscall.CSIZE
26 | syscall_PARENB = syscall.PARENB
27 | syscall_CS8 = syscall.CS8
28 | syscall_VMIN = syscall.VMIN
29 | syscall_VTIME = syscall.VTIME
30 |
31 | syscall_TCGETS = syscall.TCGETS
32 | syscall_TCSETS = syscall.TCSETS
33 | )
34 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/syscalls_netbsd.go:
--------------------------------------------------------------------------------
1 | // Created by cgo -godefs - DO NOT EDIT
2 | // cgo -godefs syscalls.go
3 |
4 | package termbox
5 |
6 | type syscall_Termios struct {
7 | Iflag uint32
8 | Oflag uint32
9 | Cflag uint32
10 | Lflag uint32
11 | Cc [20]uint8
12 | Ispeed int32
13 | Ospeed int32
14 | }
15 |
16 | const (
17 | syscall_IGNBRK = 0x1
18 | syscall_BRKINT = 0x2
19 | syscall_PARMRK = 0x8
20 | syscall_ISTRIP = 0x20
21 | syscall_INLCR = 0x40
22 | syscall_IGNCR = 0x80
23 | syscall_ICRNL = 0x100
24 | syscall_IXON = 0x200
25 | syscall_OPOST = 0x1
26 | syscall_ECHO = 0x8
27 | syscall_ECHONL = 0x10
28 | syscall_ICANON = 0x100
29 | syscall_ISIG = 0x80
30 | syscall_IEXTEN = 0x400
31 | syscall_CSIZE = 0x300
32 | syscall_PARENB = 0x1000
33 | syscall_CS8 = 0x300
34 | syscall_VMIN = 0x10
35 | syscall_VTIME = 0x11
36 |
37 | syscall_TCGETS = 0x402c7413
38 | syscall_TCSETS = 0x802c7414
39 | )
40 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/syscalls_openbsd.go:
--------------------------------------------------------------------------------
1 | // Created by cgo -godefs - DO NOT EDIT
2 | // cgo -godefs syscalls.go
3 |
4 | package termbox
5 |
6 | type syscall_Termios struct {
7 | Iflag uint32
8 | Oflag uint32
9 | Cflag uint32
10 | Lflag uint32
11 | Cc [20]uint8
12 | Ispeed int32
13 | Ospeed int32
14 | }
15 |
16 | const (
17 | syscall_IGNBRK = 0x1
18 | syscall_BRKINT = 0x2
19 | syscall_PARMRK = 0x8
20 | syscall_ISTRIP = 0x20
21 | syscall_INLCR = 0x40
22 | syscall_IGNCR = 0x80
23 | syscall_ICRNL = 0x100
24 | syscall_IXON = 0x200
25 | syscall_OPOST = 0x1
26 | syscall_ECHO = 0x8
27 | syscall_ECHONL = 0x10
28 | syscall_ICANON = 0x100
29 | syscall_ISIG = 0x80
30 | syscall_IEXTEN = 0x400
31 | syscall_CSIZE = 0x300
32 | syscall_PARENB = 0x1000
33 | syscall_CS8 = 0x300
34 | syscall_VMIN = 0x10
35 | syscall_VTIME = 0x11
36 |
37 | syscall_TCGETS = 0x402c7413
38 | syscall_TCSETS = 0x802c7414
39 | )
40 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/syscalls_windows.go:
--------------------------------------------------------------------------------
1 | // Created by cgo -godefs - DO NOT EDIT
2 | // cgo -godefs -- -DUNICODE syscalls.go
3 |
4 | package termbox
5 |
6 | const (
7 | foreground_blue = 0x1
8 | foreground_green = 0x2
9 | foreground_red = 0x4
10 | foreground_intensity = 0x8
11 | background_blue = 0x10
12 | background_green = 0x20
13 | background_red = 0x40
14 | background_intensity = 0x80
15 | std_input_handle = -0xa
16 | std_output_handle = -0xb
17 | key_event = 0x1
18 | mouse_event = 0x2
19 | window_buffer_size_event = 0x4
20 | enable_window_input = 0x8
21 | enable_mouse_input = 0x10
22 | enable_extended_flags = 0x80
23 |
24 | vk_f1 = 0x70
25 | vk_f2 = 0x71
26 | vk_f3 = 0x72
27 | vk_f4 = 0x73
28 | vk_f5 = 0x74
29 | vk_f6 = 0x75
30 | vk_f7 = 0x76
31 | vk_f8 = 0x77
32 | vk_f9 = 0x78
33 | vk_f10 = 0x79
34 | vk_f11 = 0x7a
35 | vk_f12 = 0x7b
36 | vk_insert = 0x2d
37 | vk_delete = 0x2e
38 | vk_home = 0x24
39 | vk_end = 0x23
40 | vk_pgup = 0x21
41 | vk_pgdn = 0x22
42 | vk_arrow_up = 0x26
43 | vk_arrow_down = 0x28
44 | vk_arrow_left = 0x25
45 | vk_arrow_right = 0x27
46 | vk_backspace = 0x8
47 | vk_tab = 0x9
48 | vk_enter = 0xd
49 | vk_esc = 0x1b
50 | vk_space = 0x20
51 |
52 | left_alt_pressed = 0x2
53 | left_ctrl_pressed = 0x8
54 | right_alt_pressed = 0x1
55 | right_ctrl_pressed = 0x4
56 | shift_pressed = 0x10
57 |
58 | generic_read = 0x80000000
59 | generic_write = 0x40000000
60 | console_textmode_buffer = 0x1
61 | )
62 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/termbox_common.go:
--------------------------------------------------------------------------------
1 | package termbox
2 |
3 | // private API, common OS agnostic part
4 |
5 | type cellbuf struct {
6 | width int
7 | height int
8 | cells []Cell
9 | }
10 |
11 | func (this *cellbuf) init(width, height int) {
12 | this.width = width
13 | this.height = height
14 | this.cells = make([]Cell, width*height)
15 | }
16 |
17 | func (this *cellbuf) resize(width, height int) {
18 | if this.width == width && this.height == height {
19 | return
20 | }
21 |
22 | oldw := this.width
23 | oldh := this.height
24 | oldcells := this.cells
25 |
26 | this.init(width, height)
27 | this.clear()
28 |
29 | minw, minh := oldw, oldh
30 |
31 | if width < minw {
32 | minw = width
33 | }
34 | if height < minh {
35 | minh = height
36 | }
37 |
38 | for i := 0; i < minh; i++ {
39 | srco, dsto := i*oldw, i*width
40 | src := oldcells[srco : srco+minw]
41 | dst := this.cells[dsto : dsto+minw]
42 | copy(dst, src)
43 | }
44 | }
45 |
46 | func (this *cellbuf) clear() {
47 | for i := range this.cells {
48 | c := &this.cells[i]
49 | c.Ch = ' '
50 | c.Fg = foreground
51 | c.Bg = background
52 | }
53 | }
54 |
55 | const cursor_hidden = -1
56 |
57 | func is_cursor_hidden(x, y int) bool {
58 | return x == cursor_hidden || y == cursor_hidden
59 | }
60 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/terminfo.go:
--------------------------------------------------------------------------------
1 | // +build !windows
2 | // This file contains a simple and incomplete implementation of the terminfo
3 | // database. Information was taken from the ncurses manpages term(5) and
4 | // terminfo(5). Currently, only the string capabilities for special keys and for
5 | // functions without parameters are actually used. Colors are still done with
6 | // ANSI escape sequences. Other special features that are not (yet?) supported
7 | // are reading from ~/.terminfo, the TERMINFO_DIRS variable, Berkeley database
8 | // format and extended capabilities.
9 |
10 | package termbox
11 |
12 | import (
13 | "bytes"
14 | "encoding/binary"
15 | "encoding/hex"
16 | "errors"
17 | "fmt"
18 | "io/ioutil"
19 | "os"
20 | "strings"
21 | )
22 |
23 | const (
24 | ti_magic = 0432
25 | ti_header_length = 12
26 | ti_mouse_enter = "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h"
27 | ti_mouse_leave = "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l"
28 | )
29 |
30 | func load_terminfo() ([]byte, error) {
31 | var data []byte
32 | var err error
33 |
34 | term := os.Getenv("TERM")
35 | if term == "" {
36 | return nil, fmt.Errorf("termbox: TERM not set")
37 | }
38 |
39 | // The following behaviour follows the one described in terminfo(5) as
40 | // distributed by ncurses.
41 |
42 | terminfo := os.Getenv("TERMINFO")
43 | if terminfo != "" {
44 | // if TERMINFO is set, no other directory should be searched
45 | return ti_try_path(terminfo)
46 | }
47 |
48 | // next, consider ~/.terminfo
49 | home := os.Getenv("HOME")
50 | if home != "" {
51 | data, err = ti_try_path(home + "/.terminfo")
52 | if err == nil {
53 | return data, nil
54 | }
55 | }
56 |
57 | // next, TERMINFO_DIRS
58 | dirs := os.Getenv("TERMINFO_DIRS")
59 | if dirs != "" {
60 | for _, dir := range strings.Split(dirs, ":") {
61 | if dir == "" {
62 | // "" -> "/usr/share/terminfo"
63 | dir = "/usr/share/terminfo"
64 | }
65 | data, err = ti_try_path(dir)
66 | if err == nil {
67 | return data, nil
68 | }
69 | }
70 | }
71 |
72 | // next, /lib/terminfo
73 | data, err = ti_try_path("/lib/terminfo")
74 | if err == nil {
75 | return data, nil
76 | }
77 |
78 | // fall back to /usr/share/terminfo
79 | return ti_try_path("/usr/share/terminfo")
80 | }
81 |
82 | func ti_try_path(path string) (data []byte, err error) {
83 | // load_terminfo already made sure it is set
84 | term := os.Getenv("TERM")
85 |
86 | // first try, the typical *nix path
87 | terminfo := path + "/" + term[0:1] + "/" + term
88 | data, err = ioutil.ReadFile(terminfo)
89 | if err == nil {
90 | return
91 | }
92 |
93 | // fallback to darwin specific dirs structure
94 | terminfo = path + "/" + hex.EncodeToString([]byte(term[:1])) + "/" + term
95 | data, err = ioutil.ReadFile(terminfo)
96 | return
97 | }
98 |
99 | func setup_term_builtin() error {
100 | name := os.Getenv("TERM")
101 | if name == "" {
102 | return errors.New("termbox: TERM environment variable not set")
103 | }
104 |
105 | for _, t := range terms {
106 | if t.name == name {
107 | keys = t.keys
108 | funcs = t.funcs
109 | return nil
110 | }
111 | }
112 |
113 | compat_table := []struct {
114 | partial string
115 | keys []string
116 | funcs []string
117 | }{
118 | {"xterm", xterm_keys, xterm_funcs},
119 | {"rxvt", rxvt_unicode_keys, rxvt_unicode_funcs},
120 | {"linux", linux_keys, linux_funcs},
121 | {"Eterm", eterm_keys, eterm_funcs},
122 | {"screen", screen_keys, screen_funcs},
123 | // let's assume that 'cygwin' is xterm compatible
124 | {"cygwin", xterm_keys, xterm_funcs},
125 | {"st", xterm_keys, xterm_funcs},
126 | }
127 |
128 | // try compatibility variants
129 | for _, it := range compat_table {
130 | if strings.Contains(name, it.partial) {
131 | keys = it.keys
132 | funcs = it.funcs
133 | return nil
134 | }
135 | }
136 |
137 | return errors.New("termbox: unsupported terminal")
138 | }
139 |
140 | func setup_term() (err error) {
141 | var data []byte
142 | var header [6]int16
143 | var str_offset, table_offset int16
144 |
145 | data, err = load_terminfo()
146 | if err != nil {
147 | return setup_term_builtin()
148 | }
149 |
150 | rd := bytes.NewReader(data)
151 | // 0: magic number, 1: size of names section, 2: size of boolean section, 3:
152 | // size of numbers section (in integers), 4: size of the strings section (in
153 | // integers), 5: size of the string table
154 |
155 | err = binary.Read(rd, binary.LittleEndian, header[:])
156 | if err != nil {
157 | return
158 | }
159 |
160 | number_sec_len := int16(2)
161 | if header[0] == 542 { // doc says it should be octal 0542, but what I see it terminfo files is 542, learn to program please... thank you..
162 | number_sec_len = 4
163 | }
164 |
165 | if (header[1]+header[2])%2 != 0 {
166 | // old quirk to align everything on word boundaries
167 | header[2] += 1
168 | }
169 | str_offset = ti_header_length + header[1] + header[2] + number_sec_len*header[3]
170 | table_offset = str_offset + 2*header[4]
171 |
172 | keys = make([]string, 0xFFFF-key_min)
173 | for i, _ := range keys {
174 | keys[i], err = ti_read_string(rd, str_offset+2*ti_keys[i], table_offset)
175 | if err != nil {
176 | return
177 | }
178 | }
179 | funcs = make([]string, t_max_funcs)
180 | // the last two entries are reserved for mouse. because the table offset is
181 | // not there, the two entries have to fill in manually
182 | for i, _ := range funcs[:len(funcs)-2] {
183 | funcs[i], err = ti_read_string(rd, str_offset+2*ti_funcs[i], table_offset)
184 | if err != nil {
185 | return
186 | }
187 | }
188 | funcs[t_max_funcs-2] = ti_mouse_enter
189 | funcs[t_max_funcs-1] = ti_mouse_leave
190 | return nil
191 | }
192 |
193 | func ti_read_string(rd *bytes.Reader, str_off, table int16) (string, error) {
194 | var off int16
195 |
196 | _, err := rd.Seek(int64(str_off), 0)
197 | if err != nil {
198 | return "", err
199 | }
200 | err = binary.Read(rd, binary.LittleEndian, &off)
201 | if err != nil {
202 | return "", err
203 | }
204 | _, err = rd.Seek(int64(table+off), 0)
205 | if err != nil {
206 | return "", err
207 | }
208 | var bs []byte
209 | for {
210 | b, err := rd.ReadByte()
211 | if err != nil {
212 | return "", err
213 | }
214 | if b == byte(0x00) {
215 | break
216 | }
217 | bs = append(bs, b)
218 | }
219 | return string(bs), nil
220 | }
221 |
222 | // "Maps" the function constants from termbox.go to the number of the respective
223 | // string capability in the terminfo file. Taken from (ncurses) term.h.
224 | var ti_funcs = []int16{
225 | 28, 40, 16, 13, 5, 39, 36, 27, 26, 34, 89, 88,
226 | }
227 |
228 | // Same as above for the special keys.
229 | var ti_keys = []int16{
230 | 66, 68 /* apparently not a typo; 67 is F10 for whatever reason */, 69, 70,
231 | 71, 72, 73, 74, 75, 67, 216, 217, 77, 59, 76, 164, 82, 81, 87, 61, 79, 83,
232 | }
233 |
--------------------------------------------------------------------------------
/vendor/github.com/nsf/termbox-go/terminfo_builtin.go:
--------------------------------------------------------------------------------
1 | // +build !windows
2 |
3 | package termbox
4 |
5 | // Eterm
6 | var eterm_keys = []string{
7 | "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C",
8 | }
9 | var eterm_funcs = []string{
10 | "\x1b7\x1b[?47h", "\x1b[2J\x1b[?47l\x1b8", "\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b[m\x0f", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "", "", "", "",
11 | }
12 |
13 | // screen
14 | var screen_keys = []string{
15 | "\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~", "\x1b[4~", "\x1b[5~", "\x1b[6~", "\x1bOA", "\x1bOB", "\x1bOD", "\x1bOC",
16 | }
17 | var screen_funcs = []string{
18 | "\x1b[?1049h", "\x1b[?1049l", "\x1b[34h\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[J", "\x1b[m\x0f", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b[?1h\x1b=", "\x1b[?1l\x1b>", ti_mouse_enter, ti_mouse_leave,
19 | }
20 |
21 | // xterm
22 | var xterm_keys = []string{
23 | "\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1bOH", "\x1bOF", "\x1b[5~", "\x1b[6~", "\x1bOA", "\x1bOB", "\x1bOD", "\x1bOC",
24 | }
25 | var xterm_funcs = []string{
26 | "\x1b[?1049h", "\x1b[?1049l", "\x1b[?12l\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b(B\x1b[m", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b[?1h\x1b=", "\x1b[?1l\x1b>", ti_mouse_enter, ti_mouse_leave,
27 | }
28 |
29 | // rxvt-unicode
30 | var rxvt_unicode_keys = []string{
31 | "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C",
32 | }
33 | var rxvt_unicode_funcs = []string{
34 | "\x1b[?1049h", "\x1b[r\x1b[?1049l", "\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b[m\x1b(B", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b=", "\x1b>", ti_mouse_enter, ti_mouse_leave,
35 | }
36 |
37 | // linux
38 | var linux_keys = []string{
39 | "\x1b[[A", "\x1b[[B", "\x1b[[C", "\x1b[[D", "\x1b[[E", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~", "\x1b[4~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C",
40 | }
41 | var linux_funcs = []string{
42 | "", "", "\x1b[?25h\x1b[?0c", "\x1b[?25l\x1b[?1c", "\x1b[H\x1b[J", "\x1b[0;10m", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "", "", "", "",
43 | }
44 |
45 | // rxvt-256color
46 | var rxvt_256color_keys = []string{
47 | "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C",
48 | }
49 | var rxvt_256color_funcs = []string{
50 | "\x1b7\x1b[?47h", "\x1b[2J\x1b[?47l\x1b8", "\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b[m\x0f", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b=", "\x1b>", ti_mouse_enter, ti_mouse_leave,
51 | }
52 |
53 | var terms = []struct {
54 | name string
55 | keys []string
56 | funcs []string
57 | }{
58 | {"Eterm", eterm_keys, eterm_funcs},
59 | {"screen", screen_keys, screen_funcs},
60 | {"xterm", xterm_keys, xterm_funcs},
61 | {"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs},
62 | {"linux", linux_keys, linux_funcs},
63 | {"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs},
64 | }
65 |
--------------------------------------------------------------------------------
/vendor/github.com/pyk/byten/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Bayu Aldi Yansyah
2 |
3 | MIT License
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | "Software"), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/vendor/github.com/pyk/byten/README.md:
--------------------------------------------------------------------------------
1 | byten
2 | =====
3 | Bit the size of file and turn it into human readable format.
4 |
5 | This is a Go Package that convert size of file into human readable format.
6 |
7 | Another weekend project by [pyk](http://google.com/+bayualdiyansyah).
8 | ### Usage
9 | First thing first, get the remote package
10 | ```
11 | $ go get github.com/pyk/byten
12 | ```
13 | and import package into your project
14 | ```
15 | import "github.com/pyk/byten"
16 | ```
17 | then bite the bytes!
18 | ```
19 | byten.Size(1024) # => 1.0KB
20 | byten.Size(206848) # => 202KB
21 | byten.Size(10239999998976) # => 9.3TB
22 | byten.Size(6314666666666665984) # => 5.5EB
23 | ```
24 | easy huh? =))
25 |
26 | ### Docs
27 |
28 | Nothing fancy, but you can see [here](https://godoc.org/github.com/pyk/byten)
29 |
30 | ### License
31 | [MIT License](https://github.com/pyk/byten/blob/master/LICENSE)
32 |
--------------------------------------------------------------------------------
/vendor/github.com/pyk/byten/size.go:
--------------------------------------------------------------------------------
1 | package byten
2 |
3 | import (
4 | "fmt"
5 | "math"
6 | )
7 |
8 | func index(s int64) float64 {
9 | x := math.Log(float64(s)) / math.Log(1024)
10 | return math.Floor(x)
11 | }
12 |
13 | func countSize(s int64, i float64) float64 {
14 | return float64(s) / math.Pow(1024, math.Floor(i))
15 | }
16 |
17 | // Size return a formated string from file size
18 | func Size(s int64) string {
19 |
20 | symbols := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"}
21 | i := index(s)
22 | if s < 10 {
23 | return fmt.Sprintf("%dB", s)
24 | }
25 | size := countSize(s, i)
26 | format := "%.0f"
27 | if size < 10 {
28 | format = "%.1f"
29 | }
30 |
31 | return fmt.Sprintf(format+"%s", size, symbols[int(i)])
32 | }
33 |
--------------------------------------------------------------------------------
/vendor/modules.txt:
--------------------------------------------------------------------------------
1 | # github.com/antonholmquist/jason v1.0.0
2 | github.com/antonholmquist/jason
3 | # github.com/bsiegert/ranges v0.0.0-20111221115336-19303dc7aa63
4 | github.com/bsiegert/ranges
5 | # github.com/gizak/termui v0.0.0-20181228210747-b136f68f55f1
6 | github.com/gizak/termui
7 | # github.com/mattn/go-runewidth v0.0.4
8 | github.com/mattn/go-runewidth
9 | # github.com/mitchellh/go-wordwrap v1.0.0
10 | github.com/mitchellh/go-wordwrap
11 | # github.com/nsf/termbox-go v0.0.0-20180613055208-5c94acc5e6eb
12 | github.com/nsf/termbox-go
13 | # github.com/pyk/byten v0.0.0-20140925233358-f847a130bf6d
14 | github.com/pyk/byten
15 |
--------------------------------------------------------------------------------