listpager 1.0.2 → 1.0.3
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 +8 -17
- data/lib/listpager/client_terminal.rb +87 -26
- data/lib/listpager/list.rb +5 -14
- data/lib/listpager/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1cb09a07756c07b56bc85e800cc44c160c164c0e
|
4
|
+
data.tar.gz: ccfa85c999c3b235699461e3ef78dd9e428c9a16
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d83d8ee3ed4476a2e19e1b11a75a5233d8bd215994481a559a455bc055903355b1baa6b9f83d2d8cfe2d82d48a5e21b0e435b016d50cb7e3148da704ba3acc2
|
7
|
+
data.tar.gz: 218bbe3375248ba86926f6a9244e2457acbd93e77c747c2bcf2498ee513a11dbcd686381aa95d006e0eff617dad24876c6096351b04a5d54848d60708123bb65
|
data/README.md
CHANGED
@@ -28,9 +28,7 @@ listpager.sync = true
|
|
28
28
|
listpager.puts "Item #{i}"
|
29
29
|
end
|
30
30
|
|
31
|
-
|
32
|
-
listpager.puts "%%"
|
33
|
-
listpager.puts "select 35"
|
31
|
+
listpager.puts "%set-selected 35"
|
34
32
|
```
|
35
33
|
|
36
34
|
If you want to play with the protocol, it's easiest to use two terminals and
|
@@ -48,28 +46,20 @@ And a "client", like:
|
|
48
46
|
socat TCP:localhost:4500 -
|
49
47
|
```
|
50
48
|
|
51
|
-
Add some items on your keyboard, then enter
|
52
|
-
|
53
|
-
escape it with `\%%`. If you need a literal `\\%%`, you're out of luck, because
|
54
|
-
complete escaping isn't available yet.
|
49
|
+
Add some items on your keyboard, then enter some commands, prefixed by `%`. If
|
50
|
+
you need an item that starts with a liter `%`, start it with `%%`.
|
55
51
|
|
56
52
|
There are a lot of obvious things the protocol could do, that it doesn't
|
57
|
-
currently. It's way low-hanging fruit for any contributors
|
58
|
-
`move`, etc.)
|
53
|
+
currently. It's way low-hanging fruit for any contributors.
|
59
54
|
|
60
55
|
## Protocol
|
61
56
|
listpager reads each item from stdin, and it becomes a list item. As the user
|
62
57
|
arrows through the list, it outputs messages like:
|
63
58
|
|
64
|
-
`
|
65
|
-
caption. Any other keys pressed on an item are written out like
|
59
|
+
`is-selected 21 apples` where `21` is the index into the list, and `apples` is
|
60
|
+
the caption. Any other keys pressed on an item are written out like
|
66
61
|
`keypress enter apples`.
|
67
62
|
|
68
|
-
listpager stops considering input bulk list items once it sees: `%%`, where it
|
69
|
-
enters command mode. Currently, command mode does nothing, but in the future,
|
70
|
-
it will allow the calling program to instruct listpager to select certain items,
|
71
|
-
ask for statuses, manipulate the list, add badges, change captions, etc.
|
72
|
-
|
73
63
|
|
74
64
|
## Dependencies and Installation
|
75
65
|
Install listpager with `gem install listpager`. It has few dependencies:
|
@@ -83,7 +73,7 @@ sudo apt install libncursesw5-dev
|
|
83
73
|
|
84
74
|
## Implementation Notes
|
85
75
|
curses is terrible but portable. 'curses' doesn't expose enough to be useful,
|
86
|
-
'
|
76
|
+
'ncursesw' is about as good as you'll do in Ruby.
|
87
77
|
|
88
78
|
|
89
79
|
## Upcoming Features
|
@@ -96,6 +86,7 @@ be more functional, I'd like to add a few features:
|
|
96
86
|
* Mouse support, with scroll wheels.
|
97
87
|
* Checkboxes
|
98
88
|
* Extend command mode
|
89
|
+
* `Listpager::Client`
|
99
90
|
|
100
91
|
|
101
92
|
## Contributing
|
@@ -11,11 +11,13 @@ module Listpager
|
|
11
11
|
attr_reader :tty
|
12
12
|
attr_reader :self_pipe
|
13
13
|
attr_reader :list
|
14
|
-
|
14
|
+
|
15
|
+
attr_reader :locked
|
15
16
|
|
16
17
|
def initialize
|
17
18
|
@tty = File.open('/dev/tty', 'r+')
|
18
19
|
@self_pipe = IO.pipe
|
20
|
+
@locked = false
|
19
21
|
|
20
22
|
[@tty, *self_pipe].each do |io|
|
21
23
|
io.sync = true
|
@@ -24,8 +26,35 @@ module Listpager
|
|
24
26
|
initialize_curses
|
25
27
|
|
26
28
|
@list = List.new(Ncurses.stdscr)
|
27
|
-
|
29
|
+
connect_list
|
30
|
+
|
28
31
|
@buffer = ''
|
32
|
+
@locked_buffer = []
|
33
|
+
end
|
34
|
+
|
35
|
+
def key_name(v)
|
36
|
+
@m ||= {
|
37
|
+
27 => 'esc',
|
38
|
+
10 => 'enter',
|
39
|
+
260 => 'left',
|
40
|
+
261 => 'right',
|
41
|
+
127 => 'backspace',
|
42
|
+
330 => 'delete',
|
43
|
+
' ' => 'space',
|
44
|
+
}
|
45
|
+
@m[v] || (v < 255 && v.chr.match(/[[:print:]]/) ? v.chr : "\##{v}")
|
46
|
+
end
|
47
|
+
|
48
|
+
def connect_list
|
49
|
+
cterm = self
|
50
|
+
list.define_singleton_method :on_select_change do
|
51
|
+
cterm.cmd!('is-selected', selected, selected_value, observe_lock: true)
|
52
|
+
end
|
53
|
+
|
54
|
+
list.define_singleton_method :on_key_press do |k|
|
55
|
+
cterm.cmd!('key-pressed', cterm.key_name(k),
|
56
|
+
selected, selected_value, observe_lock: true)
|
57
|
+
end
|
29
58
|
end
|
30
59
|
|
31
60
|
def initialize_curses
|
@@ -50,45 +79,77 @@ module Listpager
|
|
50
79
|
@tty.close
|
51
80
|
end
|
52
81
|
|
53
|
-
def
|
54
|
-
|
82
|
+
def line!(line, observe_lock: false)
|
83
|
+
if observe_lock && @locked
|
84
|
+
@locked_buffer.push(line)
|
85
|
+
else
|
86
|
+
$stdout.puts line
|
87
|
+
$stdout.flush
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def cmd!(*args, observe_lock: false)
|
92
|
+
line!('%' + Shellwords.join(args.map(&:to_s)),
|
93
|
+
observe_lock: observe_lock)
|
55
94
|
end
|
56
95
|
|
57
96
|
def process_command(argv)
|
58
97
|
cmd, *args = argv
|
59
98
|
case cmd
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
99
|
+
# TODO: This to be refactored into CommandProcessor
|
100
|
+
when 'quit'
|
101
|
+
raise Interrupt
|
102
|
+
|
64
103
|
when 'clear'
|
65
|
-
|
66
|
-
|
104
|
+
list.values = []
|
105
|
+
list.selected = 0
|
106
|
+
list.dirty!
|
107
|
+
|
108
|
+
when 'append'
|
109
|
+
list.values.push(args.fetch(0))
|
67
110
|
list.dirty!
|
111
|
+
|
112
|
+
when 'lock'
|
113
|
+
@locked = true
|
114
|
+
cmd! 'lock'
|
115
|
+
|
116
|
+
when 'unlock'
|
117
|
+
@locked = false
|
118
|
+
cmd! 'unlock'
|
119
|
+
@locked_buffer.each do |line|
|
120
|
+
line!(line)
|
121
|
+
end
|
122
|
+
@locked_buffer = []
|
123
|
+
|
124
|
+
when 'get-title'
|
125
|
+
cmd! 'title-is', @list.title
|
126
|
+
when 'set-title'
|
127
|
+
@list.title = args[0]
|
128
|
+
cmd! 'title-is', @list.title
|
129
|
+
|
68
130
|
when 'get-selected'
|
69
|
-
list.
|
70
|
-
when '
|
131
|
+
cmd! 'selected-is', list.selected, list.selected_value
|
132
|
+
when 'set-selected'
|
71
133
|
list.selected = args.fetch(0).to_i
|
134
|
+
cmd! 'seleted-is', list.selected, list.selected_value
|
135
|
+
|
72
136
|
when 'get-item'
|
73
|
-
|
74
|
-
when '
|
75
|
-
|
137
|
+
cmd! 'item-is', args.fetch(0), list.values[args.fetch(0).to_i]
|
138
|
+
when 'set-item'
|
139
|
+
cmd! 'item-is'
|
76
140
|
end
|
77
141
|
end
|
78
142
|
|
79
143
|
def process_line(line)
|
80
|
-
if
|
81
|
-
|
82
|
-
|
83
|
-
elsif line[0] == '%'
|
84
|
-
cmd = Shellwords.split(line[1..-1])
|
85
|
-
process_command(cmd)
|
86
|
-
else
|
87
|
-
list.values.push(line)
|
88
|
-
list.dirty!
|
89
|
-
end
|
144
|
+
if line[0] == '%' && line[1] != '%'
|
145
|
+
cmd = Shellwords.split(line[1..-1])
|
146
|
+
process_command(cmd)
|
90
147
|
else
|
91
|
-
|
148
|
+
if line[0] == '%'
|
149
|
+
line = line[1..-1]
|
150
|
+
end
|
151
|
+
list.values.push(line)
|
152
|
+
list.dirty!
|
92
153
|
end
|
93
154
|
end
|
94
155
|
|
data/lib/listpager/list.rb
CHANGED
@@ -14,11 +14,9 @@ module Listpager
|
|
14
14
|
BLANK_SPACE = ' '
|
15
15
|
|
16
16
|
def on_select_change
|
17
|
-
puts "select #{selected} #{values[selected]}"
|
18
17
|
end
|
19
18
|
|
20
19
|
def on_key_press(k)
|
21
|
-
puts "keypress #{key_name(k)} #{selected} #{values[selected]}"
|
22
20
|
end
|
23
21
|
|
24
22
|
attr_reader :window
|
@@ -72,25 +70,18 @@ module Listpager
|
|
72
70
|
|
73
71
|
if v != @selected
|
74
72
|
dirty!
|
73
|
+
@selected = v
|
75
74
|
on_select_change
|
76
75
|
end
|
77
76
|
|
78
|
-
return
|
77
|
+
return @selected
|
79
78
|
end
|
80
79
|
|
81
|
-
def
|
82
|
-
|
83
|
-
27 => 'esc',
|
84
|
-
10 => 'enter',
|
85
|
-
260 => 'left',
|
86
|
-
261 => 'right',
|
87
|
-
127 => 'backspace',
|
88
|
-
330 => 'delete',
|
89
|
-
' ' => 'space',
|
90
|
-
}
|
91
|
-
@m[v] || (v < 255 && v.chr.match(/[[:print:]]/) ? v.chr : "\##{v}")
|
80
|
+
def selected_value
|
81
|
+
values[selected]
|
92
82
|
end
|
93
83
|
|
84
|
+
|
94
85
|
def key_input(value)
|
95
86
|
maxx, maxy = getmaxxy
|
96
87
|
|
data/lib/listpager/version.rb
CHANGED