termplot 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +31 -33
- data/Rakefile +4 -2
- data/lib/termplot/cli.rb +4 -51
- data/lib/termplot/colors.rb +29 -29
- data/lib/termplot/consumer.rb +27 -23
- data/lib/termplot/{cursors/control_chars.rb → control_chars.rb} +0 -0
- data/lib/termplot/cursors.rb +7 -0
- data/lib/termplot/cursors/buffered_console_cursor.rb +51 -52
- data/lib/termplot/cursors/console_cursor.rb +42 -41
- data/lib/termplot/cursors/virtual_cursor.rb +60 -58
- data/lib/termplot/options.rb +131 -0
- data/lib/termplot/producers.rb +7 -0
- data/lib/termplot/producers/base_producer.rb +35 -0
- data/lib/termplot/producers/command_producer.rb +20 -0
- data/lib/termplot/producers/stdin_producer.rb +14 -0
- data/lib/termplot/version.rb +1 -1
- data/lib/termplot/window.rb +4 -4
- metadata +10 -6
- data/doc/demo.gif +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e44e30011841535366e60fe122b1a1975614d2907db4b99fc5e78538ffc1cc8d
|
4
|
+
data.tar.gz: b63812a1a1823e0126eec29b14072ac6a7ee4b95f52a02977ea5e35432e2a905
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a15e0921c87254909bfdb11f35c0c3ac5bc85b8bd06c023234ac6c735a73cde92a7bcc2423ad40e5c37ac2718338a502e92e9b85ff6865760bca56e8671cea1
|
7
|
+
data.tar.gz: f4ba5aaea3ce9ef9870becafbfb7c58b79bebb76ab9d96161533f13c824a2e905964f39e99c7b4b1352df5d65b154e2d6b593099cc36ea43aafd25dc2b2d0366
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@ Termplot is a simple terminal plotting tool for visualising streaming data.
|
|
4
4
|
|
5
5
|
See the demo on Asciinema:
|
6
6
|
|
7
|
-
[](https://asciinema.org/a/3rzeUSXp2fRjnErX0p3SptP5e)
|
8
8
|
|
9
9
|
## Overview
|
10
10
|
|
@@ -27,26 +27,34 @@ The basic usage is simple:
|
|
27
27
|
```
|
28
28
|
|
29
29
|
`{command}` is any command that will periodically output numbers to standard out
|
30
|
-
delimited by a newline.
|
31
|
-
|
30
|
+
delimited by a newline. You can also instead specify a command to run at a given
|
31
|
+
interval to produce the data.
|
32
32
|
|
33
|
-
Options
|
33
|
+
Options and examples are given below. All command line options are
|
34
|
+
optional.
|
34
35
|
|
35
36
|
```
|
36
37
|
Usage: termplot [OPTIONS]
|
37
|
-
-r, --rows ROWS
|
38
|
-
-c, --cols COLS Number of cols in the chart window (default: 80)
|
39
|
-
-t, --title TITLE Title of the series (default: Series)
|
38
|
+
-r, --rows ROWS Number of rows in the chart window (default: 19)
|
40
39
|
|
41
|
-
|
42
|
-
line [default], heavy-line, dot, star, x
|
40
|
+
-c, --cols COLS Number of cols in the chart window (default: 80)
|
43
41
|
|
44
|
-
|
45
|
-
(i.e. black, red [default], green, yellow, blue,
|
46
|
-
magenta, cyan, white). Light versions are specified
|
47
|
-
as light_{color}
|
42
|
+
-t, --title TITLE Title of the series (default: 'Series')
|
48
43
|
|
49
|
-
|
44
|
+
--line-style STYLE Line style. Options are: line (default), heavy-line, dot, star, x
|
45
|
+
|
46
|
+
--color COLOR Series color, specified as ansi 16-bit color name:
|
47
|
+
(i.e. black, light_black, red (default), light_red, green,
|
48
|
+
light_green, yellow, light_yellow, blue, light_blue, magenta,
|
49
|
+
light_magenta, cyan, light_cyan, white, light_white, default)
|
50
|
+
|
51
|
+
--command COMMAND Enables command mode, where input is received by executing
|
52
|
+
the specified command in intervals rather than from stdin
|
53
|
+
|
54
|
+
--interval INTERVAL The interval at which to run the specified command in
|
55
|
+
command mode in milliseconds (default: 1000)
|
56
|
+
|
57
|
+
-h, --help Display this help message
|
50
58
|
```
|
51
59
|
|
52
60
|
## Examples
|
@@ -59,25 +67,21 @@ for i in $(seq 500); do \
|
|
59
67
|
sleep 0.5; \
|
60
68
|
done | termplot -t "Sin(x)"
|
61
69
|
```
|
62
|
-

|
63
71
|
|
64
72
|
Total % memory usage:
|
65
73
|
```
|
66
|
-
|
67
|
-
|
68
|
-
sleep 0.5; \
|
69
|
-
done | termplot -t "Memory (% used)" --color light_magenta --line-style heavy-line
|
74
|
+
termplot --command "free | awk 'NR==2 { print ($3/$2) * 100 }'" \
|
75
|
+
-t "Memory (% used)" --color light_magenta --line-style heavy-line
|
70
76
|
```
|
71
|
-

|
72
78
|
|
73
79
|
% CPU usage of a "puma" process:
|
74
80
|
```
|
75
|
-
|
76
|
-
|
77
|
-
sleep 0.5; \
|
78
|
-
done | termplot -t "Ruby CPU(%)" --color yellow --line-style dot -r10 -c 120
|
81
|
+
termplot --command "ps au | grep puma | awk 'NR==1{ print $3 }'" \
|
82
|
+
-t "Ruby CPU(%)" --color yellow --line-style dot -r10 -c 120
|
79
83
|
```
|
80
|
-

|
81
85
|
|
82
86
|
|
83
87
|
## Notes
|
@@ -87,10 +91,6 @@ while true; do \
|
|
87
91
|
- The samples received are plotted in sequence order, and there is no notion of
|
88
92
|
temporal spacing. So even if the time between samples is inconsistent, they
|
89
93
|
will be plotted with the same amount of space between them.
|
90
|
-
- The `while true; do {...}; sleep INTERVAL` is pretty typical, I would like to
|
91
|
-
sometime soon implement a `--command "{...}" --interval INTERVAL` which would
|
92
|
-
reduce some of the boilerplate of calling some command at an interval and make
|
93
|
-
it easy to watch and plot.
|
94
94
|
|
95
95
|
## Background
|
96
96
|
|
@@ -107,12 +107,10 @@ ruby.
|
|
107
107
|
Now with termplot, it's as easy as:
|
108
108
|
|
109
109
|
```
|
110
|
-
|
111
|
-
ss -s | head -n1 | cut -d ' ' -f2; sleep 1; \
|
112
|
-
done | termplot -t "TCP Connections"
|
110
|
+
termplot --command "ss -s | head -n1 | cut -d' ' -f2" --interval 500 -t "TCP Connections"
|
113
111
|
```
|
114
112
|
|
115
|
-

|
116
114
|
|
117
115
|
## Development
|
118
116
|
|
data/Rakefile
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
|
3
3
|
task :bin do
|
4
|
-
|
5
|
-
exec "bin/termplot", *ARGV[2..-1]
|
4
|
+
exec "ruby", "-Ilib", "bin/termplot", *ARGV[2..-1]
|
6
5
|
end
|
7
6
|
|
8
7
|
task :sin_test do
|
@@ -16,3 +15,6 @@ task :sin_test do
|
|
16
15
|
exec cmd
|
17
16
|
end
|
18
17
|
|
18
|
+
task :command_test do
|
19
|
+
exec "ruby -Ilib bin/termplot --command 'echo $RANDOM' --interval 900"
|
20
|
+
end
|
data/lib/termplot/cli.rb
CHANGED
@@ -1,59 +1,12 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "termplot/options"
|
2
3
|
require "termplot/consumer"
|
3
4
|
|
4
5
|
module Termplot
|
5
6
|
class CLI
|
6
7
|
def self.run
|
7
|
-
opts =
|
8
|
-
Consumer.new(
|
9
|
-
end
|
10
|
-
|
11
|
-
private
|
12
|
-
def self.parse_options
|
13
|
-
options = {
|
14
|
-
rows: 19,
|
15
|
-
cols: 80,
|
16
|
-
title: "Series",
|
17
|
-
line_style: "line",
|
18
|
-
color: "red",
|
19
|
-
debug: false
|
20
|
-
}
|
21
|
-
OptionParser.new do |opts|
|
22
|
-
opts.banner = "Usage: termplot [OPTIONS]"
|
23
|
-
|
24
|
-
opts.on("-rROWS", "--rows ROWS", "Number of rows in the chart window (default: 19)") do |v|
|
25
|
-
options[:rows] = v.to_i
|
26
|
-
end
|
27
|
-
|
28
|
-
opts.on("-cCOLS", "--cols COLS", "Number of cols in the chart window (default: 80)") do |v|
|
29
|
-
options[:cols] = v.to_i
|
30
|
-
end
|
31
|
-
|
32
|
-
opts.on("-tTITLE", "--title TITLE", "Title of the series (default: Series)") do |v|
|
33
|
-
options[:title] = v
|
34
|
-
end
|
35
|
-
|
36
|
-
opts.on("--line-style STYLE", "Line style. Options are: line [default], heavy-line, dot, star, x") do |v|
|
37
|
-
options[:line_style] = v.downcase
|
38
|
-
end
|
39
|
-
|
40
|
-
opts.on("--color COLOR", "Series color, specified as ansi 16-bit color name",
|
41
|
-
"(i.e. black, red [default], green, yellow, blue, magenta, cyan, white)",
|
42
|
-
"with light versions specified as light_{color}") do |v|
|
43
|
-
options[:color] = v.downcase
|
44
|
-
end
|
45
|
-
|
46
|
-
opts.on("-d", "--debug", "Enable debug mode, Logs window data to stdout instead of rendering") do |v|
|
47
|
-
options[:debug] = v
|
48
|
-
end
|
49
|
-
|
50
|
-
opts.on("-h", "--help", "Display this help message") do
|
51
|
-
puts opts
|
52
|
-
exit(0)
|
53
|
-
end
|
54
|
-
|
55
|
-
end.parse!
|
56
|
-
options
|
8
|
+
opts = Options.new.parse_options!
|
9
|
+
Consumer.new(opts).run
|
57
10
|
end
|
58
11
|
end
|
59
12
|
end
|
data/lib/termplot/colors.rb
CHANGED
@@ -1,36 +1,36 @@
|
|
1
1
|
module Termplot
|
2
2
|
class Colors
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
}
|
3
|
+
COLORS = {
|
4
|
+
black: 0,
|
5
|
+
light_black: 60,
|
6
|
+
red: 1,
|
7
|
+
light_red: 61,
|
8
|
+
green: 2,
|
9
|
+
light_green: 62,
|
10
|
+
yellow: 3,
|
11
|
+
light_yellow: 63,
|
12
|
+
blue: 4,
|
13
|
+
light_blue: 64,
|
14
|
+
magenta: 5,
|
15
|
+
light_magenta: 65,
|
16
|
+
cyan: 6,
|
17
|
+
light_cyan: 66,
|
18
|
+
white: 7,
|
19
|
+
light_white: 67,
|
20
|
+
default: 9
|
21
|
+
}
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
23
|
+
MODES = {
|
24
|
+
default: 0,
|
25
|
+
bold: 1,
|
26
|
+
italic: 3,
|
27
|
+
underline: 4,
|
28
|
+
blink: 5,
|
29
|
+
swap: 7,
|
30
|
+
hide: 8
|
31
|
+
}
|
33
32
|
|
33
|
+
class << self
|
34
34
|
COLORS.each do |(color, code)|
|
35
35
|
define_method(color) do |str|
|
36
36
|
escape_color(color) + str + escape_mode(:default)
|
data/lib/termplot/consumer.rb
CHANGED
@@ -1,22 +1,24 @@
|
|
1
1
|
require "termplot/series"
|
2
2
|
require "termplot/renderer"
|
3
3
|
require "termplot/shell"
|
4
|
+
require "termplot/producers"
|
4
5
|
|
5
6
|
module Termplot
|
6
7
|
class Consumer
|
7
|
-
attr_reader :series, :renderer
|
8
|
+
attr_reader :options, :series, :renderer
|
8
9
|
|
9
|
-
def initialize(
|
10
|
+
def initialize(options)
|
11
|
+
@options = options
|
10
12
|
@renderer = Renderer.new(
|
11
|
-
cols: cols,
|
12
|
-
rows: rows,
|
13
|
-
debug: debug
|
13
|
+
cols: options.cols,
|
14
|
+
rows: options.rows,
|
15
|
+
debug: options.debug
|
14
16
|
)
|
15
17
|
@series = Series.new(
|
16
|
-
title: title,
|
18
|
+
title: options.title,
|
17
19
|
max_data_points: renderer.inner_width,
|
18
|
-
line_style: line_style,
|
19
|
-
color: color,
|
20
|
+
line_style: options.line_style,
|
21
|
+
color: options.color,
|
20
22
|
)
|
21
23
|
end
|
22
24
|
|
@@ -27,7 +29,7 @@ module Termplot
|
|
27
29
|
# Consumer thread will process and render any available input in the
|
28
30
|
# queue. If samples are available faster than it can render, multiple
|
29
31
|
# samples will be shifted from the queue so they can be rendered at once.
|
30
|
-
# If no samples are available but
|
32
|
+
# If no samples are available but the queue is open, it will sleep until
|
31
33
|
# woken to render new input.
|
32
34
|
consumer = Thread.new do
|
33
35
|
while !queue.closed?
|
@@ -45,27 +47,29 @@ module Termplot
|
|
45
47
|
end
|
46
48
|
end
|
47
49
|
|
48
|
-
#
|
49
|
-
#
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
50
|
+
# Producer will run in the main thread and will block while producing
|
51
|
+
# samples from some source (which depends on the type of producer).
|
52
|
+
# Samples will be added to the queue as they are available, and the
|
53
|
+
# consumer will be woken to check the queue
|
54
|
+
producer = build_producer(queue)
|
55
|
+
producer.register_consumer(consumer)
|
56
|
+
producer.run
|
56
57
|
|
57
|
-
#
|
58
|
-
# to finish rendering
|
59
|
-
queue.close
|
58
|
+
# As soon as producer continues, and we first give the consumer a chance
|
59
|
+
# to finish rendering the queue, then close the queue.
|
60
60
|
consumer.run
|
61
|
+
producer.close
|
61
62
|
consumer.join
|
62
63
|
end
|
63
64
|
|
64
65
|
private
|
65
66
|
|
66
|
-
|
67
|
-
|
68
|
-
|
67
|
+
def build_producer(queue)
|
68
|
+
producer_class = {
|
69
|
+
command: "Termplot::Producers::CommandProducer",
|
70
|
+
stdin: "Termplot::Producers::StdinProducer"
|
71
|
+
}.fetch(options.mode)
|
72
|
+
Object.const_get(producer_class).new(queue, options)
|
69
73
|
end
|
70
74
|
end
|
71
75
|
end
|
File without changes
|
@@ -1,70 +1,69 @@
|
|
1
|
-
require "termplot/cursors/virtual_cursor"
|
2
|
-
require "termplot/cursors/control_chars"
|
3
|
-
|
4
1
|
module Termplot
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
module Cursors
|
3
|
+
class BufferedConsoleCursor < VirtualCursor
|
4
|
+
include Termplot::ControlChars
|
5
|
+
attr_reader :buffer
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
def initialize(window, buffer)
|
8
|
+
super(window)
|
9
|
+
@buffer = buffer
|
10
|
+
end
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
def write(char)
|
13
|
+
if writeable?
|
14
|
+
buffer << char
|
15
|
+
super(char)
|
16
|
+
end
|
18
17
|
end
|
19
|
-
end
|
20
18
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
def forward(n = 1)
|
20
|
+
moved = super(n)
|
21
|
+
moved.times { buffer << FORWARD }
|
22
|
+
end
|
25
23
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
24
|
+
def back(n = 1)
|
25
|
+
moved = super(n)
|
26
|
+
moved.times { buffer << BACK }
|
27
|
+
end
|
30
28
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
29
|
+
def up(n=1)
|
30
|
+
moved = super(n)
|
31
|
+
moved.times { buffer << UP }
|
32
|
+
end
|
35
33
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
34
|
+
def down(n=1)
|
35
|
+
moved = super(n)
|
36
|
+
moved.times { buffer << DOWN }
|
37
|
+
end
|
40
38
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
39
|
+
def beginning_of_line
|
40
|
+
super
|
41
|
+
buffer << CR
|
42
|
+
end
|
45
43
|
|
46
|
-
|
47
|
-
|
48
|
-
|
44
|
+
def new_line
|
45
|
+
buffer << NEWLINE
|
46
|
+
end
|
49
47
|
|
50
|
-
|
51
|
-
|
52
|
-
|
48
|
+
def clear_buffer
|
49
|
+
buffer.clear
|
50
|
+
end
|
53
51
|
|
54
|
-
|
55
|
-
|
56
|
-
|
52
|
+
def flush
|
53
|
+
print buffer.join
|
54
|
+
end
|
57
55
|
|
58
|
-
|
59
|
-
|
60
|
-
|
56
|
+
def position=()
|
57
|
+
raise "Cannot set cursor position directly"
|
58
|
+
end
|
61
59
|
|
62
|
-
|
63
|
-
|
64
|
-
|
60
|
+
def row=()
|
61
|
+
raise "Cannot set cursor position directly"
|
62
|
+
end
|
65
63
|
|
66
|
-
|
67
|
-
|
64
|
+
def col=()
|
65
|
+
raise "Cannot set cursor position directly"
|
66
|
+
end
|
68
67
|
end
|
69
68
|
end
|
70
69
|
end
|
@@ -1,56 +1,57 @@
|
|
1
|
-
require "termplot/
|
2
|
-
require "termplot/cursors/control_chars"
|
1
|
+
require "termplot/control_chars"
|
3
2
|
|
4
3
|
module Termplot
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
module Cursors
|
5
|
+
class ConsoleCursor < VirtualCursor
|
6
|
+
include Termplot::ControlChars
|
7
|
+
|
8
|
+
def write(char)
|
9
|
+
if writeable?
|
10
|
+
print(char)
|
11
|
+
super(char)
|
12
|
+
end
|
12
13
|
end
|
13
|
-
end
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
def forward(n = 1)
|
16
|
+
moved = super(n)
|
17
|
+
moved.times { print FORWARD }
|
18
|
+
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
def back(n = 1)
|
21
|
+
moved = super(n)
|
22
|
+
moved.times { print BACK }
|
23
|
+
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
def up(n=1)
|
26
|
+
moved = super(n)
|
27
|
+
moved.times { print UP }
|
28
|
+
end
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
def down(n=1)
|
31
|
+
moved = super(n)
|
32
|
+
moved.times { print DOWN }
|
33
|
+
end
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
def beginning_of_line
|
36
|
+
super
|
37
|
+
print CR
|
38
|
+
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
def new_line
|
41
|
+
print NEWLINE
|
42
|
+
end
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
def position=()
|
45
|
+
raise "Cannot set cursor position directly"
|
46
|
+
end
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
48
|
+
def row=()
|
49
|
+
raise "Cannot set cursor position directly"
|
50
|
+
end
|
51
51
|
|
52
|
-
|
53
|
-
|
52
|
+
def col=()
|
53
|
+
raise "Cannot set cursor position directly"
|
54
|
+
end
|
54
55
|
end
|
55
56
|
end
|
56
57
|
end
|
@@ -1,76 +1,78 @@
|
|
1
1
|
module Termplot
|
2
|
-
|
3
|
-
|
2
|
+
module Cursors
|
3
|
+
class VirtualCursor
|
4
|
+
attr_reader :position, :window
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
def initialize(window)
|
7
|
+
@window = window
|
8
|
+
@position = 0
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
def write(char)
|
12
|
+
@position += 1 if writeable?
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
def writeable?
|
16
|
+
position < window.buffer.size
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
def forward(n = 1)
|
20
|
+
movable_chars = window.buffer.size - position
|
21
|
+
chars_to_move = [movable_chars, n].min
|
22
|
+
@position += chars_to_move
|
23
|
+
chars_to_move
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
def back(n = 1)
|
27
|
+
chars_to_move = [position, n].min
|
28
|
+
@position -= chars_to_move
|
29
|
+
chars_to_move
|
30
|
+
end
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
32
|
+
def up(n=1)
|
33
|
+
return unless row > 0
|
34
|
+
rows_to_move = [n, row].min
|
35
|
+
@position -= rows_to_move * window.cols
|
36
|
+
rows_to_move
|
37
|
+
end
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
39
|
+
def row
|
40
|
+
(position / window.cols).floor
|
41
|
+
end
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
def col
|
44
|
+
position % window.cols
|
45
|
+
end
|
45
46
|
|
46
|
-
|
47
|
-
|
48
|
-
|
47
|
+
def row=(y)
|
48
|
+
@position = y * window.cols + col
|
49
|
+
end
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
51
|
+
def col=(x)
|
52
|
+
beginning_of_line
|
53
|
+
forward(x)
|
54
|
+
end
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
56
|
+
def down(n=1)
|
57
|
+
return 0 unless row < (window.rows - 1)
|
58
|
+
rows_to_move = [n, window.rows - 1 - row].min
|
59
|
+
@position += window.cols * rows_to_move
|
60
|
+
rows_to_move
|
61
|
+
end
|
61
62
|
|
62
|
-
|
63
|
-
|
64
|
-
|
63
|
+
def beginning_of_line
|
64
|
+
@position = position - (position % window.cols)
|
65
|
+
end
|
65
66
|
|
66
|
-
|
67
|
-
|
68
|
-
|
67
|
+
def position=(n)
|
68
|
+
@position = n
|
69
|
+
end
|
69
70
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
71
|
+
def reset_position
|
72
|
+
return if position == 0
|
73
|
+
up(row) # Go up by row num times
|
74
|
+
beginning_of_line
|
75
|
+
end
|
74
76
|
end
|
75
77
|
end
|
76
78
|
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "optparse"
|
4
|
+
require "termplot/character_map"
|
5
|
+
require "termplot/colors"
|
6
|
+
|
7
|
+
module Termplot
|
8
|
+
class Options
|
9
|
+
attr_reader :rows,
|
10
|
+
:cols,
|
11
|
+
:title,
|
12
|
+
:line_style,
|
13
|
+
:color,
|
14
|
+
:debug,
|
15
|
+
:command,
|
16
|
+
:interval
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@rows = 19
|
20
|
+
@cols = 80
|
21
|
+
@title = "Series"
|
22
|
+
@line_style = "line"
|
23
|
+
@color = "red"
|
24
|
+
@debug = false
|
25
|
+
@command = nil
|
26
|
+
@interval = 1000
|
27
|
+
end
|
28
|
+
|
29
|
+
def mode
|
30
|
+
@command.nil? ? :stdin : :command
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse_options!
|
34
|
+
# Debug option is parsed manually to prevent it from showing up in the
|
35
|
+
# options help
|
36
|
+
parse_debug
|
37
|
+
|
38
|
+
OptionParser.new do |opts|
|
39
|
+
opts.banner = "Usage: termplot [OPTIONS]"
|
40
|
+
|
41
|
+
parse_rows(opts)
|
42
|
+
parse_cols(opts)
|
43
|
+
parse_title(opts)
|
44
|
+
parse_line_style(opts)
|
45
|
+
parse_color(opts)
|
46
|
+
parse_command(opts)
|
47
|
+
parse_interval(opts)
|
48
|
+
|
49
|
+
opts.on("-h", "--help", "Display this help message") do
|
50
|
+
puts opts
|
51
|
+
exit(0)
|
52
|
+
end
|
53
|
+
|
54
|
+
end.parse!
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def parse_rows(opts)
|
61
|
+
opts.on("-r ROWS", "--rows ROWS",
|
62
|
+
"Number of rows in the chart window (default: #{@rows})") do |v|
|
63
|
+
@rows = v.to_i
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse_cols(opts)
|
68
|
+
opts.on("-c COLS", "--cols COLS",
|
69
|
+
"Number of cols in the chart window (default: #{@cols})") do |v|
|
70
|
+
@cols = v.to_i
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def parse_title(opts)
|
75
|
+
opts.on("-tTITLE", "--title TITLE",
|
76
|
+
"Title of the series (default: '#{@title}')") do |v|
|
77
|
+
@title = v
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def parse_line_style(opts)
|
82
|
+
line_style_opts = with_default(Termplot::CharacterMap::LINE_STYLES.keys, @line_style)
|
83
|
+
opts.on("--line-style STYLE",
|
84
|
+
"Line style. Options are: #{line_style_opts.join(", ")}") do |v|
|
85
|
+
@line_style = v.downcase
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def parse_color(opts)
|
90
|
+
color_opts = Termplot::Colors::COLORS.keys.map(&:to_s).reject do |c|
|
91
|
+
c == :default
|
92
|
+
end
|
93
|
+
color_opts = with_default(color_opts, @color)
|
94
|
+
opts.on("--color COLOR",
|
95
|
+
"Series color, specified as ansi 16-bit color name:",
|
96
|
+
"(i.e. #{color_opts.join(", ")})") do |v|
|
97
|
+
@color = v.downcase
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def parse_command(opts)
|
102
|
+
opts.on("--command COMMAND",
|
103
|
+
"Enables command mode, where input is received by executing",
|
104
|
+
"the specified command in intervals rather than from stdin") do |v|
|
105
|
+
@command = v
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def parse_interval(opts)
|
110
|
+
opts.on("--interval INTERVAL",
|
111
|
+
"The interval at which to run the specified command in",
|
112
|
+
"command mode in milliseconds (default: #{@interval})") do |v|
|
113
|
+
@interval = v.to_i
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def parse_debug
|
118
|
+
if ARGV.delete("--debug") || ARGV.delete("-d")
|
119
|
+
@debug = true
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def with_default(opt_arr, default)
|
124
|
+
opt_arr.map do |opt|
|
125
|
+
opt == default ?
|
126
|
+
opt + " (default)" :
|
127
|
+
opt
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Termplot
|
2
|
+
module Producers
|
3
|
+
class BaseProducer
|
4
|
+
def initialize(queue, options)
|
5
|
+
@options = options
|
6
|
+
@queue = queue
|
7
|
+
@consumer = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def register_consumer(consumer)
|
11
|
+
@consumer = consumer
|
12
|
+
end
|
13
|
+
|
14
|
+
def shift
|
15
|
+
queue.shift
|
16
|
+
end
|
17
|
+
|
18
|
+
def closed?
|
19
|
+
queue.closed?
|
20
|
+
end
|
21
|
+
|
22
|
+
def close
|
23
|
+
queue.close
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
attr_reader :queue, :consumer, :options
|
28
|
+
|
29
|
+
FLOAT_REGEXP = /^[-+]?[0-9]*\.?[0-9]+$/
|
30
|
+
def numeric?(n)
|
31
|
+
n =~ FLOAT_REGEXP
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Termplot
|
2
|
+
module Producers
|
3
|
+
class CommandProducer < BaseProducer
|
4
|
+
def run
|
5
|
+
loop do
|
6
|
+
n = `/bin/bash -c '#{options.command}'`.chomp
|
7
|
+
# TODO: Error handling...
|
8
|
+
|
9
|
+
if numeric?(n)
|
10
|
+
queue << n.to_f
|
11
|
+
consumer&.run
|
12
|
+
end
|
13
|
+
|
14
|
+
# Interval is in ms
|
15
|
+
sleep(options.interval / 1000.0)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/termplot/version.rb
CHANGED
data/lib/termplot/window.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require "termplot/
|
2
|
-
require "termplot/cursors
|
1
|
+
require "termplot/control_chars"
|
2
|
+
require "termplot/cursors"
|
3
3
|
|
4
4
|
module Termplot
|
5
5
|
class Window
|
@@ -11,14 +11,14 @@ module Termplot
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def cursor
|
14
|
-
@cursor ||= VirtualCursor.new(self)
|
14
|
+
@cursor ||= Termplot::Cursors::VirtualCursor.new(self)
|
15
15
|
end
|
16
16
|
|
17
17
|
def console_cursor
|
18
18
|
# Console buffer has an extra rows - 1 to account for new line characters
|
19
19
|
# between rows
|
20
20
|
@console_cursor ||=
|
21
|
-
BufferedConsoleCursor.new(self, Array.new(cols * rows + rows - 1))
|
21
|
+
Termplot::Cursors::BufferedConsoleCursor.new(self, Array.new(cols * rows + rows - 1))
|
22
22
|
end
|
23
23
|
|
24
24
|
def size
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: termplot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Nyaga
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-11-
|
11
|
+
date: 2020-11-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-termios
|
@@ -42,7 +42,6 @@ files:
|
|
42
42
|
- bin/termplot
|
43
43
|
- doc/cpu.png
|
44
44
|
- doc/demo.cast
|
45
|
-
- doc/demo.gif
|
46
45
|
- doc/memory.png
|
47
46
|
- doc/sin.png
|
48
47
|
- doc/tcp.png
|
@@ -51,10 +50,16 @@ files:
|
|
51
50
|
- lib/termplot/cli.rb
|
52
51
|
- lib/termplot/colors.rb
|
53
52
|
- lib/termplot/consumer.rb
|
53
|
+
- lib/termplot/control_chars.rb
|
54
|
+
- lib/termplot/cursors.rb
|
54
55
|
- lib/termplot/cursors/buffered_console_cursor.rb
|
55
56
|
- lib/termplot/cursors/console_cursor.rb
|
56
|
-
- lib/termplot/cursors/control_chars.rb
|
57
57
|
- lib/termplot/cursors/virtual_cursor.rb
|
58
|
+
- lib/termplot/options.rb
|
59
|
+
- lib/termplot/producers.rb
|
60
|
+
- lib/termplot/producers/base_producer.rb
|
61
|
+
- lib/termplot/producers/command_producer.rb
|
62
|
+
- lib/termplot/producers/stdin_producer.rb
|
58
63
|
- lib/termplot/renderer.rb
|
59
64
|
- lib/termplot/series.rb
|
60
65
|
- lib/termplot/shell.rb
|
@@ -83,8 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
88
|
- !ruby/object:Gem::Version
|
84
89
|
version: '0'
|
85
90
|
requirements: []
|
86
|
-
|
87
|
-
rubygems_version: 2.7.3
|
91
|
+
rubygems_version: 3.1.4
|
88
92
|
signing_key:
|
89
93
|
specification_version: 4
|
90
94
|
summary: Plot time series charts in your terminal
|
data/doc/demo.gif
DELETED
Binary file
|