sup 0.15.1 → 0.15.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CONTRIBUTORS +13 -11
- data/History.txt +7 -0
- data/ReleaseNotes +4 -0
- data/bin/sup +7 -5
- data/lib/sup/account.rb +42 -10
- data/lib/sup/buffer.rb +21 -71
- data/lib/sup/index.rb +8 -8
- data/lib/sup/keymap.rb +4 -2
- data/lib/sup/message_chunks.rb +1 -0
- data/lib/sup/modes/thread_index_mode.rb +1 -1
- data/lib/sup/tagger.rb +3 -1
- data/lib/sup/textfield.rb +48 -29
- data/lib/sup/util/ncurses.rb +274 -0
- data/lib/sup/version.rb +1 -1
- data/lib/sup.rb +1 -0
- metadata +8 -7
data/CONTRIBUTORS
CHANGED
@@ -6,6 +6,7 @@ Hamish Downer <dmishd at the gmail dot coms>
|
|
6
6
|
Damien Leone <damien.leone at the fensalir dot frs>
|
7
7
|
Sascha Silbe <sascha-pgp at the silbe dot orgs>
|
8
8
|
Eric Weikl <eric.weikl at the gmx dot nets>
|
9
|
+
Paweł Wilk <siefca at the gnu dot orgs>
|
9
10
|
Ismo Puustinen <ismo at the iki dot fis>
|
10
11
|
Nicolas Pouillard <nicolas.pouillard at the gmail dot coms>
|
11
12
|
Michael Stapelberg <michael at the stapelberg dot des>
|
@@ -29,43 +30,44 @@ Marc Hartstein <marc.hartstein at the alum.vassar dot edus>
|
|
29
30
|
Israel Herraiz <israel.herraiz at the gmail dot coms>
|
30
31
|
Bo Borgerson <gigabo at the gmail dot coms>
|
31
32
|
Michael Hamann <michael at the content-space dot des>
|
32
|
-
William Erik Baxter <web at the superscript dot coms>
|
33
33
|
Jonathan Lassoff <jof at the thejof dot coms>
|
34
|
+
William Erik Baxter <web at the superscript dot coms>
|
34
35
|
Grant Hollingworth <grant at the antiflux dot orgs>
|
35
36
|
Adeodato Simó <dato at the net.com.org dot ess>
|
36
|
-
Ico Doornekamp <ico at the pruts dot nls>
|
37
37
|
Markus Klinik <markus.klinik at the gmx dot des>
|
38
|
+
Ico Doornekamp <ico at the pruts dot nls>
|
38
39
|
Daniel Schoepe <daniel.schoepe at the googlemail dot coms>
|
39
40
|
James Taylor <james at the jamestaylor dot orgs>
|
40
41
|
Jason Petsod <jason at the petsod dot orgs>
|
41
|
-
Steve Goldman <sgoldman at the tower-research dot coms>
|
42
42
|
Robin Burchell <viroteck at the viroteck dot nets>
|
43
|
+
Steve Goldman <sgoldman at the tower-research dot coms>
|
43
44
|
Peter Harkins <ph at the malaprop dot orgs>
|
44
45
|
Decklin Foster <decklin at the red-bean dot coms>
|
45
46
|
Cameron Matheson <cam+sup at the cammunism dot orgs>
|
46
|
-
Carl Worth <cworth at the cworth dot orgs>
|
47
47
|
Alex Vandiver <alex at the chmrr dot nets>
|
48
|
+
Carl Worth <cworth at the cworth dot orgs>
|
48
49
|
Andrew Pimlott <andrew at the pimlott dot nets>
|
49
50
|
Jeff Balogh <its.jeff.balogh at the gmail dot coms>
|
50
51
|
Matías Aguirre <matiasaguirre at the gmail dot coms>
|
51
52
|
Kornilios Kourtis <kkourt at the cslab.ece.ntua dot grs>
|
52
|
-
Giorgio Lando <patroclo7 at the gmail dot coms>
|
53
53
|
Kevin Riggle <kevinr at the free-dissociation dot coms>
|
54
|
+
Giorgio Lando <patroclo7 at the gmail dot coms>
|
54
55
|
Benoît PIERRE <benoit.pierre at the gmail dot coms>
|
55
|
-
Alvaro Herrera <alvherre at the alvh.no-ip dot orgs>
|
56
56
|
Steven Lawrance <stl at the koffein dot nets>
|
57
|
+
Alvaro Herrera <alvherre at the alvh.no-ip dot orgs>
|
57
58
|
Jonah <Jonah at the GoodCoffee dot cas>
|
58
59
|
ian <itaylor at the uark dot edus>
|
59
|
-
Adam Lloyd <adam at the alloy-d dot nets>
|
60
60
|
Gregor Hoffleit <gregor at the sam.mediasupervision dot des>
|
61
61
|
0xACE <0xACE at the users.noreply.github dot coms>
|
62
|
-
|
63
|
-
MichaelRevell <mikearevell at the gmail dot coms>
|
62
|
+
Adam Lloyd <adam at the alloy-d dot nets>
|
64
63
|
Todd Eisenberger <teisenbe at the andrew.cmu dot edus>
|
64
|
+
MichaelRevell <mikearevell at the gmail dot coms>
|
65
|
+
Per Andersson <avtobiff at the gmail dot coms>
|
65
66
|
Steven Walter <swalter at the monarch.(none)>
|
66
|
-
Matthias Vallentin <vallentin at the icir dot orgs>
|
67
|
-
akojo <atte.kojo at the gmail dot coms>
|
68
67
|
Jon M. Dugan <jdugan at the es dot nets>
|
68
|
+
Matthias Vallentin <vallentin at the icir dot orgs>
|
69
69
|
Horacio Sanson <horacio at the skillupjapan.co dot jps>
|
70
70
|
Stefan Lundström <lundst at the snabb.(none)>
|
71
|
+
akojo <atte.kojo at the gmail dot coms>
|
72
|
+
Johannes Larsen <johs.a.larsen at the gmail dot coms>
|
71
73
|
Kirill Smelkov <kirr at the landau.phys.spbu dot rus>
|
data/History.txt
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
== 0.15.2 / 2013-12-20
|
2
|
+
|
3
|
+
* Use the form_driver_w routine for inputing multibyte chars when
|
4
|
+
available.
|
5
|
+
* Add hidden_alternates configuration option: hidden aliases for the
|
6
|
+
account.
|
7
|
+
|
1
8
|
== 0.15.1 / 2013-12-04
|
2
9
|
|
3
10
|
* Thread children are sorted last-activity latest (bottom).
|
data/ReleaseNotes
CHANGED
data/bin/sup
CHANGED
@@ -4,9 +4,10 @@
|
|
4
4
|
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
5
5
|
|
6
6
|
require 'rubygems'
|
7
|
-
|
8
7
|
require 'ncursesw'
|
9
8
|
|
9
|
+
require 'sup/util/ncurses'
|
10
|
+
|
10
11
|
no_gpgme = false
|
11
12
|
begin
|
12
13
|
require 'gpgme'
|
@@ -136,6 +137,7 @@ def start_cursing
|
|
136
137
|
Ncurses.use_default_colors
|
137
138
|
Ncurses.curs_set 0
|
138
139
|
Ncurses.start_color
|
140
|
+
Ncurses.prepare_form_driver
|
139
141
|
$cursing = true
|
140
142
|
end
|
141
143
|
|
@@ -236,14 +238,14 @@ begin
|
|
236
238
|
|
237
239
|
until Redwood::exceptions.nonempty? || $die
|
238
240
|
c = begin
|
239
|
-
Ncurses.
|
241
|
+
Ncurses::CharCode.get false
|
240
242
|
rescue Interrupt
|
241
243
|
raise if BufferManager.ask_yes_or_no "Die ungracefully now?"
|
242
244
|
BufferManager.draw_screen
|
243
|
-
|
245
|
+
Ncurses::CharCode.empty
|
244
246
|
end
|
245
247
|
|
246
|
-
if c.
|
248
|
+
if c.empty?
|
247
249
|
if BufferManager.sigwinch_happened?
|
248
250
|
debug "redrawing screen on sigwinch"
|
249
251
|
BufferManager.completely_redraw_screen
|
@@ -253,7 +255,7 @@ begin
|
|
253
255
|
|
254
256
|
IdleManager.ping
|
255
257
|
|
256
|
-
if c
|
258
|
+
if c.is_keycode? 410
|
257
259
|
## this is ncurses's way of telling us it's detected a refresh.
|
258
260
|
## since we have our own sigwinch handler, we don't do anything.
|
259
261
|
next
|
data/lib/sup/account.rb
CHANGED
@@ -31,6 +31,8 @@ class AccountManager
|
|
31
31
|
|
32
32
|
def initialize accounts
|
33
33
|
@email_map = {}
|
34
|
+
@hidden_email_map = {}
|
35
|
+
@email_map_dirty = false
|
34
36
|
@accounts = {}
|
35
37
|
@regexen = {}
|
36
38
|
@default_account = nil
|
@@ -40,7 +42,7 @@ class AccountManager
|
|
40
42
|
end
|
41
43
|
|
42
44
|
def user_accounts; @accounts.keys; end
|
43
|
-
def user_emails;
|
45
|
+
def user_emails(type = :all); email_map(type).keys.select { |e| String === e }; end
|
44
46
|
|
45
47
|
## must be called first with the default account. fills in missing
|
46
48
|
## values from the default account.
|
@@ -50,7 +52,9 @@ class AccountManager
|
|
50
52
|
[:name, :sendmail, :signature, :gpgkey].each { |k| hash[k] ||= @default_account.send(k) }
|
51
53
|
end
|
52
54
|
hash[:alternates] ||= []
|
55
|
+
hash[:hidden_alternates] ||= []
|
53
56
|
fail "alternative emails are not an array: #{hash[:alternates]}" unless hash[:alternates].kind_of? Array
|
57
|
+
fail "hidden alternative emails are not an array: #{hash[:hidden_alternates]}" unless hash[:hidden_alternates].kind_of? Array
|
54
58
|
|
55
59
|
[:name, :signature].each { |x| hash[x] ? hash[x].fix_encoding! : nil }
|
56
60
|
|
@@ -63,8 +67,11 @@ class AccountManager
|
|
63
67
|
end
|
64
68
|
|
65
69
|
([hash[:email]] + hash[:alternates]).each do |email|
|
66
|
-
|
67
|
-
|
70
|
+
add_email_to_map(:shown, email, a)
|
71
|
+
end
|
72
|
+
|
73
|
+
hash[:hidden_alternates].each do |email|
|
74
|
+
add_email_to_map(:hidden, email, a)
|
68
75
|
end
|
69
76
|
|
70
77
|
hash[:regexen].each do |re|
|
@@ -72,19 +79,44 @@ class AccountManager
|
|
72
79
|
end if hash[:regexen]
|
73
80
|
end
|
74
81
|
|
75
|
-
def is_account? p;
|
82
|
+
def is_account? p; is_account_email? p.email end
|
76
83
|
def is_account_email? email; !account_for(email).nil? end
|
84
|
+
|
77
85
|
def account_for email
|
78
|
-
|
79
|
-
|
80
|
-
else
|
81
|
-
@regexen.argfind { |re, a| re =~ email && a }
|
82
|
-
end
|
86
|
+
a = email_map[email]
|
87
|
+
a.nil? ? @regexen.argfind { |re, a| re =~ email && a } : a
|
83
88
|
end
|
89
|
+
|
84
90
|
def full_address_for email
|
85
91
|
a = account_for email
|
86
92
|
Person.full_address a.name, email
|
87
93
|
end
|
88
|
-
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def add_email_to_map(type, email, acc)
|
98
|
+
type = :shown if type != :hidden
|
99
|
+
m = email_map(type)
|
100
|
+
unless m.member? email
|
101
|
+
m[email] = acc
|
102
|
+
@email_map_dirty = true
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def email_map(type = nil)
|
107
|
+
case type
|
108
|
+
when :shown, :public then @email_map
|
109
|
+
when :hidden then @hidden_email_map
|
110
|
+
else
|
111
|
+
if @email_map_dirty
|
112
|
+
@email_map_all = @hidden_email_map.merge(@email_map)
|
113
|
+
if @email_map_all.count != @email_map.count + @hidden_email_map.count
|
114
|
+
@hidden_email_map.reject! { |m| @email_map.member? m }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
@email_map_all ||= {}
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end # class AccountManager
|
89
121
|
|
90
122
|
end
|
data/lib/sup/buffer.rb
CHANGED
@@ -2,67 +2,9 @@
|
|
2
2
|
|
3
3
|
require 'etc'
|
4
4
|
require 'thread'
|
5
|
-
|
6
5
|
require 'ncursesw'
|
7
6
|
|
8
|
-
|
9
|
-
module Ncurses
|
10
|
-
def rows
|
11
|
-
lame, lamer = [], []
|
12
|
-
stdscr.getmaxyx lame, lamer
|
13
|
-
lame.first
|
14
|
-
end
|
15
|
-
|
16
|
-
def cols
|
17
|
-
lame, lamer = [], []
|
18
|
-
stdscr.getmaxyx lame, lamer
|
19
|
-
lamer.first
|
20
|
-
end
|
21
|
-
|
22
|
-
def curx
|
23
|
-
lame, lamer = [], []
|
24
|
-
stdscr.getyx lame, lamer
|
25
|
-
lamer.first
|
26
|
-
end
|
27
|
-
|
28
|
-
def mutex; @mutex ||= Mutex.new; end
|
29
|
-
def sync &b; mutex.synchronize(&b); end
|
30
|
-
|
31
|
-
## magically, this stuff seems to work now. i could swear it didn't
|
32
|
-
## before. hm.
|
33
|
-
def nonblocking_getch
|
34
|
-
## INSANTIY
|
35
|
-
## it is NECESSARY to wrap Ncurses.getch in a select() otherwise all
|
36
|
-
## background threads will be BLOCKED. (except in very modern versions
|
37
|
-
## of libncurses-ruby. the current one on ubuntu seems to work well.)
|
38
|
-
if IO.select([$stdin], nil, nil, 0.5)
|
39
|
-
if Redwood::BufferManager.shelled?
|
40
|
-
# If we get input while we're shelled, we'll ignore it for the
|
41
|
-
# moment and use Ncurses.sync to wait until the shell_out is done.
|
42
|
-
Ncurses.sync { nil }
|
43
|
-
else
|
44
|
-
Ncurses.getch
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
## pretends ctrl-c's are ctrl-g's
|
50
|
-
def safe_nonblocking_getch
|
51
|
-
nonblocking_getch
|
52
|
-
rescue Interrupt
|
53
|
-
KEY_CANCEL
|
54
|
-
end
|
55
|
-
|
56
|
-
module_function :rows, :cols, :curx, :nonblocking_getch, :safe_nonblocking_getch, :mutex, :sync
|
57
|
-
|
58
|
-
remove_const :KEY_ENTER
|
59
|
-
remove_const :KEY_CANCEL
|
60
|
-
|
61
|
-
KEY_ENTER = 10
|
62
|
-
KEY_CANCEL = 7 # ctrl-g
|
63
|
-
KEY_TAB = 9
|
64
|
-
end
|
65
|
-
end
|
7
|
+
require 'sup/util/ncurses'
|
66
8
|
|
67
9
|
module Redwood
|
68
10
|
|
@@ -214,7 +156,14 @@ EOS
|
|
214
156
|
@sigwinch_mutex = Mutex.new
|
215
157
|
end
|
216
158
|
|
217
|
-
def sigwinch_happened
|
159
|
+
def sigwinch_happened!
|
160
|
+
@sigwinch_mutex.synchronize do
|
161
|
+
return if @sigwinch_happened
|
162
|
+
@sigwinch_happened = true
|
163
|
+
Ncurses.ungetch ?\C-l.ord
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
218
167
|
def sigwinch_happened?; @sigwinch_mutex.synchronize { @sigwinch_happened } end
|
219
168
|
|
220
169
|
def buffers; @name_map.to_a; end
|
@@ -266,7 +215,7 @@ EOS
|
|
266
215
|
|
267
216
|
def handle_input c
|
268
217
|
if @focus_buf
|
269
|
-
if @focus_buf.mode.in_search? && c != CONTINUE_IN_BUFFER_SEARCH_KEY
|
218
|
+
if @focus_buf.mode.in_search? && c != CONTINUE_IN_BUFFER_SEARCH_KEY
|
270
219
|
@focus_buf.mode.cancel_search!
|
271
220
|
@focus_buf.mark_dirty
|
272
221
|
end
|
@@ -398,9 +347,9 @@ EOS
|
|
398
347
|
draw_screen
|
399
348
|
|
400
349
|
until mode.done?
|
401
|
-
c = Ncurses.
|
402
|
-
next unless c # getch timeout
|
403
|
-
break if c
|
350
|
+
c = Ncurses::CharCode.get
|
351
|
+
next unless c.present? # getch timeout
|
352
|
+
break if c.is_keycode? Ncurses::KEY_CANCEL
|
404
353
|
begin
|
405
354
|
mode.handle_input c
|
406
355
|
rescue InputSequenceAborted # do nothing
|
@@ -590,8 +539,8 @@ EOS
|
|
590
539
|
end
|
591
540
|
|
592
541
|
while true
|
593
|
-
c = Ncurses.
|
594
|
-
next unless c # getch timeout
|
542
|
+
c = Ncurses::CharCode.get
|
543
|
+
next unless c.present? # getch timeout
|
595
544
|
break unless tf.handle_input c # process keystroke
|
596
545
|
|
597
546
|
if tf.new_completions?
|
@@ -643,10 +592,11 @@ EOS
|
|
643
592
|
ret = nil
|
644
593
|
done = false
|
645
594
|
until done
|
646
|
-
key = Ncurses.
|
647
|
-
if key
|
595
|
+
key = Ncurses::CharCode.get
|
596
|
+
next if key.empty?
|
597
|
+
if key.is_keycode? Ncurses::KEY_CANCEL
|
648
598
|
done = true
|
649
|
-
elsif accept.nil? || accept.empty? || accept.member?(key)
|
599
|
+
elsif accept.nil? || accept.empty? || accept.member?(key.code)
|
650
600
|
ret = key
|
651
601
|
done = true
|
652
602
|
end
|
@@ -664,7 +614,7 @@ EOS
|
|
664
614
|
## returns true (y), false (n), or nil (ctrl-g / cancel)
|
665
615
|
def ask_yes_or_no question
|
666
616
|
case(r = ask_getch question, "ynYN")
|
667
|
-
when ?y
|
617
|
+
when ?y, ?Y
|
668
618
|
true
|
669
619
|
when nil
|
670
620
|
nil
|
@@ -683,7 +633,7 @@ EOS
|
|
683
633
|
action, text = keymap.action_for c
|
684
634
|
while action.is_a? Keymap # multi-key commands, prompt
|
685
635
|
key = BufferManager.ask_getch text
|
686
|
-
unless key # user canceled, abort
|
636
|
+
unless key.empty? # user canceled, abort
|
687
637
|
erase_flash
|
688
638
|
raise InputSequenceAborted
|
689
639
|
end
|
data/lib/sup/index.rb
CHANGED
@@ -134,7 +134,7 @@ EOS
|
|
134
134
|
|
135
135
|
def add_message m; sync_message m, true end
|
136
136
|
def update_message m; sync_message m, true end
|
137
|
-
def update_message_state m; sync_message m, false end
|
137
|
+
def update_message_state m; sync_message m[0], false, m[1] end
|
138
138
|
|
139
139
|
def save_index
|
140
140
|
info "Flushing Xapian updates to disk. This may take a while..."
|
@@ -530,18 +530,18 @@ EOS
|
|
530
530
|
query
|
531
531
|
end
|
532
532
|
|
533
|
-
def save_message m
|
533
|
+
def save_message m, sync_back = true
|
534
534
|
if @sync_worker
|
535
|
-
@sync_queue << m
|
535
|
+
@sync_queue << [m, sync_back]
|
536
536
|
else
|
537
|
-
update_message_state m
|
537
|
+
update_message_state [m, sync_back]
|
538
538
|
end
|
539
539
|
m.clear_dirty
|
540
540
|
end
|
541
541
|
|
542
|
-
def save_thread t
|
542
|
+
def save_thread t, sync_back = true
|
543
543
|
t.each_dirty_message do |m|
|
544
|
-
save_message m
|
544
|
+
save_message m, sync_back
|
545
545
|
end
|
546
546
|
end
|
547
547
|
|
@@ -676,10 +676,10 @@ EOS
|
|
676
676
|
end
|
677
677
|
end
|
678
678
|
|
679
|
-
def sync_message m, overwrite
|
679
|
+
def sync_message m, overwrite, sync_back = true
|
680
680
|
## TODO: we should not save the message if the sync_back failed
|
681
681
|
## since it would overwrite the location field
|
682
|
-
m.sync_back
|
682
|
+
m.sync_back if sync_back
|
683
683
|
|
684
684
|
doc = synchronize { find_doc(m.id) }
|
685
685
|
existed = doc != nil
|
data/lib/sup/keymap.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'sup/util/ncurses'
|
2
|
+
|
1
3
|
module Redwood
|
2
4
|
|
3
5
|
class Keymap
|
@@ -96,11 +98,11 @@ EOS
|
|
96
98
|
end
|
97
99
|
|
98
100
|
def action_for kc
|
99
|
-
action, help, keys = @map[kc]
|
101
|
+
action, help, keys = @map[kc.code]
|
100
102
|
[action, help]
|
101
103
|
end
|
102
104
|
|
103
|
-
def has_key? k; @map[k] end
|
105
|
+
def has_key? k; @map[k.code] end
|
104
106
|
|
105
107
|
def keysyms; @map.values.map { |action, help, keys| keys }.flatten; end
|
106
108
|
|
data/lib/sup/message_chunks.rb
CHANGED
data/lib/sup/tagger.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'sup/util/ncurses'
|
2
|
+
|
1
3
|
module Redwood
|
2
4
|
|
3
5
|
class Tagger
|
@@ -27,7 +29,7 @@ class Tagger
|
|
27
29
|
|
28
30
|
unless action
|
29
31
|
c = BufferManager.ask_getch "apply to #{num_tagged} tagged #{noun}:"
|
30
|
-
return if c.
|
32
|
+
return if c.empty? # user cancelled
|
31
33
|
action = @mode.resolve_input c
|
32
34
|
end
|
33
35
|
|
data/lib/sup/textfield.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'sup/util/ncurses'
|
2
|
+
|
1
3
|
module Redwood
|
2
4
|
|
3
5
|
## a fully-functional text field supporting completions, expansions,
|
@@ -16,6 +18,8 @@ module Redwood
|
|
16
18
|
## in sup, completion support is implemented through BufferManager#ask
|
17
19
|
## and CompletionMode.
|
18
20
|
class TextField
|
21
|
+
include Ncurses::Form::DriverHelpers
|
22
|
+
|
19
23
|
def initialize
|
20
24
|
@i = nil
|
21
25
|
@history = []
|
@@ -48,8 +52,8 @@ class TextField
|
|
48
52
|
@w.attrset Colormap.color_for(:none)
|
49
53
|
@w.mvaddstr @y, 0, @question
|
50
54
|
Ncurses.curs_set 1
|
51
|
-
|
52
|
-
|
55
|
+
form_driver_key Ncurses::Form::REQ_END_FIELD
|
56
|
+
form_driver_key Ncurses::Form::REQ_NEXT_CHAR if @value && @value =~ / $/ # fucking RETARDED
|
53
57
|
end
|
54
58
|
|
55
59
|
def deactivate
|
@@ -63,7 +67,7 @@ class TextField
|
|
63
67
|
|
64
68
|
def handle_input c
|
65
69
|
## short-circuit exit paths
|
66
|
-
case c
|
70
|
+
case c.code
|
67
71
|
when Ncurses::KEY_ENTER # submit!
|
68
72
|
@value = get_cursed_value
|
69
73
|
@history.push @value unless @value =~ /^\s*$/
|
@@ -97,39 +101,27 @@ class TextField
|
|
97
101
|
reset_completion_state
|
98
102
|
@value = nil
|
99
103
|
|
100
|
-
|
101
|
-
|
104
|
+
# ctrl_c: control char
|
105
|
+
ctrl_c =
|
106
|
+
case c.keycode # only test for keycodes
|
102
107
|
when Ncurses::KEY_LEFT
|
103
108
|
Ncurses::Form::REQ_PREV_CHAR
|
104
109
|
when Ncurses::KEY_RIGHT
|
105
110
|
Ncurses::Form::REQ_NEXT_CHAR
|
106
111
|
when Ncurses::KEY_DC
|
107
112
|
Ncurses::Form::REQ_DEL_CHAR
|
108
|
-
when Ncurses::KEY_BACKSPACE
|
113
|
+
when Ncurses::KEY_BACKSPACE
|
109
114
|
Ncurses::Form::REQ_DEL_PREV
|
110
|
-
when
|
115
|
+
when Ncurses::KEY_HOME
|
111
116
|
nop
|
112
117
|
Ncurses::Form::REQ_BEG_FIELD
|
113
|
-
when
|
118
|
+
when Ncurses::KEY_END
|
114
119
|
Ncurses::Form::REQ_END_FIELD
|
115
|
-
when ?\C-k.ord
|
116
|
-
Ncurses::Form::REQ_CLR_EOF
|
117
|
-
when ?\C-u.ord
|
118
|
-
set_cursed_value cursed_value_after_point
|
119
|
-
Ncurses::Form.form_driver @form, Ncurses::Form::REQ_END_FIELD
|
120
|
-
nop
|
121
|
-
Ncurses::Form::REQ_BEG_FIELD
|
122
|
-
when ?\C-w.ord
|
123
|
-
while action = remove_extra_space
|
124
|
-
Ncurses::Form.form_driver @form, action
|
125
|
-
end
|
126
|
-
Ncurses::Form.form_driver @form, Ncurses::Form::REQ_PREV_CHAR
|
127
|
-
Ncurses::Form.form_driver @form, Ncurses::Form::REQ_DEL_WORD
|
128
120
|
when Ncurses::KEY_UP, Ncurses::KEY_DOWN
|
129
121
|
unless !@i || @history.empty?
|
130
122
|
value = get_cursed_value
|
131
123
|
#debug "history before #{@history.inspect}"
|
132
|
-
@i = @i + (c
|
124
|
+
@i = @i + (c.is_keycode?(Ncurses::KEY_UP) ? -1 : 1)
|
133
125
|
@i = 0 if @i < 0
|
134
126
|
@i = @history.size if @i > @history.size
|
135
127
|
@value = @history[@i] || ''
|
@@ -138,10 +130,37 @@ class TextField
|
|
138
130
|
Ncurses::Form::REQ_END_FIELD
|
139
131
|
end
|
140
132
|
else
|
141
|
-
|
133
|
+
# return other keycode or nil if it's not a keycode
|
134
|
+
c.dumb? ? nil : c.keycode
|
142
135
|
end
|
143
136
|
|
144
|
-
|
137
|
+
# handle keysyms
|
138
|
+
# ctrl_c: control char
|
139
|
+
ctrl_c = case c
|
140
|
+
when ?\177 # backspace (octal)
|
141
|
+
Ncurses::Form::REQ_DEL_PREV
|
142
|
+
when ?\C-a # home
|
143
|
+
nop
|
144
|
+
Ncurses::Form::REQ_BEG_FIELD
|
145
|
+
when ?\C-e # end keysym
|
146
|
+
Ncurses::Form::REQ_END_FIELD
|
147
|
+
when ?\C-k
|
148
|
+
Ncurses::Form::REQ_CLR_EOF
|
149
|
+
when ?\C-u
|
150
|
+
set_cursed_value cursed_value_after_point
|
151
|
+
form_driver_key Ncurses::Form::REQ_END_FIELD
|
152
|
+
nop
|
153
|
+
Ncurses::Form::REQ_BEG_FIELD
|
154
|
+
when ?\C-w
|
155
|
+
while action = remove_extra_space
|
156
|
+
form_driver_key action
|
157
|
+
end
|
158
|
+
form_driver_key Ncurses::Form::REQ_PREV_CHAR
|
159
|
+
form_driver_key Ncurses::Form::REQ_DEL_WORD
|
160
|
+
end if ctrl_c.nil?
|
161
|
+
|
162
|
+
c.replace(ctrl_c).keycode! if ctrl_c # no effect for dumb CharCode
|
163
|
+
form_driver c if c.present?
|
145
164
|
true
|
146
165
|
end
|
147
166
|
|
@@ -159,7 +178,7 @@ private
|
|
159
178
|
return nil unless @field
|
160
179
|
|
161
180
|
x = Ncurses.curx
|
162
|
-
|
181
|
+
form_driver_key Ncurses::Form::REQ_VALIDATION
|
163
182
|
v = @field.field_buffer(0).gsub(/^\s+|\s+$/, "")
|
164
183
|
|
165
184
|
## cursor <= end of text
|
@@ -175,7 +194,7 @@ private
|
|
175
194
|
# system locale and also hopefully the terminal/input encoding. an
|
176
195
|
# incorrectly configured terminal encoding (not matching the system
|
177
196
|
# encoding) will produce erronous results, but will also do that for
|
178
|
-
# a
|
197
|
+
# a lot of other programs since it is impossible to detect which is
|
179
198
|
# which and what encoding the inputted byte chars are supposed to have.
|
180
199
|
v.force_encoding($encoding).fix_encoding!
|
181
200
|
end
|
@@ -183,7 +202,7 @@ private
|
|
183
202
|
def remove_extra_space
|
184
203
|
return nil unless @field
|
185
204
|
|
186
|
-
|
205
|
+
form_driver_key Ncurses::Form::REQ_VALIDATION
|
187
206
|
x = Ncurses.curx
|
188
207
|
v = @field.field_buffer(0).gsub(/^\s+|\s+$/, "")
|
189
208
|
v_index = x - @question.length
|
@@ -226,8 +245,8 @@ private
|
|
226
245
|
## this is almost certainly unnecessary, but it's the only way
|
227
246
|
## i could get ncurses to remember my form's value
|
228
247
|
def nop
|
229
|
-
|
230
|
-
|
248
|
+
form_driver_char " "
|
249
|
+
form_driver_key Ncurses::Form::REQ_DEL_PREV
|
231
250
|
end
|
232
251
|
end
|
233
252
|
end
|
@@ -0,0 +1,274 @@
|
|
1
|
+
require 'ncursesw'
|
2
|
+
require 'sup/util'
|
3
|
+
|
4
|
+
if defined? Ncurses
|
5
|
+
module Ncurses
|
6
|
+
|
7
|
+
## Helper class for storing keycodes
|
8
|
+
## and multibyte characters.
|
9
|
+
class CharCode < String
|
10
|
+
## Status code allows us to detect
|
11
|
+
## printable characters and control codes.
|
12
|
+
attr_reader :status
|
13
|
+
|
14
|
+
## Reads character from user input.
|
15
|
+
def self.nonblocking_getwch
|
16
|
+
# If we get input while we're shelled, we'll ignore it for the
|
17
|
+
# moment and use Ncurses.sync to wait until the shell_out is done.
|
18
|
+
begin
|
19
|
+
s, c = Redwood::BufferManager.shelled? ? Ncurses.sync { nil } : Ncurses.get_wch
|
20
|
+
break if s != Ncurses::ERR
|
21
|
+
end until IO.select([$stdin], nil, nil, 2)
|
22
|
+
[s, c]
|
23
|
+
end
|
24
|
+
|
25
|
+
## Returns empty singleton.
|
26
|
+
def self.empty
|
27
|
+
Empty.instance
|
28
|
+
end
|
29
|
+
|
30
|
+
## Creates new instance of CharCode
|
31
|
+
## that keeps a given keycode.
|
32
|
+
def self.keycode(c)
|
33
|
+
generate c, Ncurses::KEY_CODE_YES
|
34
|
+
end
|
35
|
+
|
36
|
+
## Creates new instance of CharCode
|
37
|
+
## that keeps a printable character.
|
38
|
+
def self.character(c)
|
39
|
+
generate c, Ncurses::OK
|
40
|
+
end
|
41
|
+
|
42
|
+
## Generates new object like new
|
43
|
+
## but for empty or erroneous objects
|
44
|
+
## it returns empty singleton.
|
45
|
+
def self.generate(c = nil, status = Ncurses::OK)
|
46
|
+
if status == Ncurses::ERR || c.nil? || c === Ncurses::ERR
|
47
|
+
empty
|
48
|
+
else
|
49
|
+
new(c, status)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
## Gets character from input.
|
54
|
+
## Pretends ctrl-c's are ctrl-g's.
|
55
|
+
def self.get handle_interrupt=true
|
56
|
+
begin
|
57
|
+
status, code = nonblocking_getwch
|
58
|
+
generate code, status
|
59
|
+
rescue Interrupt => e
|
60
|
+
raise e unless handle_interrupt
|
61
|
+
keycode Ncurses::KEY_CANCEL
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
## Enables dumb mode for any new instance.
|
66
|
+
def self.dumb!
|
67
|
+
@dumb = true
|
68
|
+
end
|
69
|
+
|
70
|
+
## Asks if dumb mode was set
|
71
|
+
def self.dumb?
|
72
|
+
defined?(@dumb) && @dumb
|
73
|
+
end
|
74
|
+
|
75
|
+
def initialize(c = "", status = Ncurses::OK)
|
76
|
+
@status = status
|
77
|
+
c = "" if c.nil?
|
78
|
+
return super("") if status == Ncurses::ERR
|
79
|
+
c = enc_char(c) if c.is_a?(Fixnum)
|
80
|
+
super c.length > 1 ? c[0,1] : c
|
81
|
+
end
|
82
|
+
|
83
|
+
## Proxy method for String's replace
|
84
|
+
def replace(c)
|
85
|
+
return self if c.object_id == object_id
|
86
|
+
if c.is_a?(self.class)
|
87
|
+
@status = c.status
|
88
|
+
super(c)
|
89
|
+
else
|
90
|
+
@status = Ncurses::OK
|
91
|
+
c = "" if c.nil?
|
92
|
+
c = enc_char(c) if c.is_a?(Fixnum)
|
93
|
+
super c.length > 1 ? c[0,1] : c
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_character ; character? ? self : "<#{code}>" end ## Returns character or code as a string
|
98
|
+
def to_keycode ; keycode? ? code : Ncurses::ERR end ## Returns keycode or ERR if it's not a keycode
|
99
|
+
def to_sequence ; bytes.to_a end ## Returns unpacked sequence of bytes for a character
|
100
|
+
def code ; ord end ## Returns decimal representation of a character
|
101
|
+
def is_keycode?(c) ; keycode? && code == c end ## Tests if keycode matches
|
102
|
+
def is_character?(c); character? && self == c end ## Tests if character matches
|
103
|
+
def try_keycode ; keycode? ? code : nil end ## Returns dec. code if keycode, nil otherwise
|
104
|
+
def try_character ; character? ? self : nil end ## Returns character if character, nil otherwise
|
105
|
+
def keycode ; try_keycode end ## Alias for try_keycode
|
106
|
+
def character ; try_character end ## Alias for try_character
|
107
|
+
def character? ; dumb? || @status == Ncurses::OK end ## Returns true if character
|
108
|
+
def character! ; @status = Ncurses::OK ; self end ## Sets character flag
|
109
|
+
def keycode? ; dumb? || @status == Ncurses::KEY_CODE_YES end ## Returns true if keycode
|
110
|
+
def keycode! ; @status = Ncurses::KEY_CODE_YES ; self end ## Sets keycode flag
|
111
|
+
def keycode=(c) ; replace(c); keycode! ; self end ## Sets keycode
|
112
|
+
def present? ; not empty? end ## Proxy method
|
113
|
+
def printable? ; character? end ## Alias for character?
|
114
|
+
def dumb? ; self.class.dumb? end ## True if we cannot distinguish keycodes from characters
|
115
|
+
|
116
|
+
# Empty singleton that
|
117
|
+
# keeps GC from going crazy.
|
118
|
+
class Empty < CharCode
|
119
|
+
include Singleton
|
120
|
+
|
121
|
+
## Wrap methods that may change us
|
122
|
+
## and generate new object instead.
|
123
|
+
[ :"[]=", :"<<", :replace, :insert, :prepend, :append, :concat, :force_encoding, :setbyte ].
|
124
|
+
select{ |m| public_method_defined?(m) }.
|
125
|
+
concat(public_instance_methods.grep(/!\z/)).
|
126
|
+
each do |m|
|
127
|
+
class_eval <<-EVAL
|
128
|
+
def #{m}(*args)
|
129
|
+
CharCode.new.#{m}(*args)
|
130
|
+
end
|
131
|
+
EVAL
|
132
|
+
end
|
133
|
+
|
134
|
+
## proxy with class-level instance variable delegation
|
135
|
+
def self.dumb?
|
136
|
+
superclass.dumb? or !!@dumb
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.empty
|
140
|
+
instance
|
141
|
+
end
|
142
|
+
|
143
|
+
def initialize
|
144
|
+
super("", Ncurses::ERR)
|
145
|
+
end
|
146
|
+
|
147
|
+
def empty? ; true end ## always true
|
148
|
+
def present? ; false end ## always false
|
149
|
+
def clear ; self end ## always self
|
150
|
+
|
151
|
+
self
|
152
|
+
end.init # CharCode::Empty
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
## Tries to make external character right.
|
157
|
+
def enc_char(c)
|
158
|
+
begin
|
159
|
+
character = c.chr($encoding)
|
160
|
+
rescue RangeError, ArgumentError
|
161
|
+
begin
|
162
|
+
character = [c].pack('U')
|
163
|
+
rescue RangeError
|
164
|
+
begin
|
165
|
+
character = c.chr
|
166
|
+
rescue
|
167
|
+
begin
|
168
|
+
character = [c].pack('C')
|
169
|
+
rescue
|
170
|
+
character = ""
|
171
|
+
@status = Ncurses::ERR
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
character.fix_encoding!
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end # class CharCode
|
179
|
+
|
180
|
+
def rows
|
181
|
+
lame, lamer = [], []
|
182
|
+
stdscr.getmaxyx lame, lamer
|
183
|
+
lame.first
|
184
|
+
end
|
185
|
+
|
186
|
+
def cols
|
187
|
+
lame, lamer = [], []
|
188
|
+
stdscr.getmaxyx lame, lamer
|
189
|
+
lamer.first
|
190
|
+
end
|
191
|
+
|
192
|
+
def curx
|
193
|
+
lame, lamer = [], []
|
194
|
+
stdscr.getyx lame, lamer
|
195
|
+
lamer.first
|
196
|
+
end
|
197
|
+
|
198
|
+
## Create replacement wrapper for form_driver_w (), which is not (yet) a standard
|
199
|
+
## function in ncurses. Some systems (Mac OS X) does not have a working
|
200
|
+
## form_driver that accepts wide chars. We are just falling back to form_driver, expect problems.
|
201
|
+
def prepare_form_driver
|
202
|
+
if not defined? Form.form_driver_w
|
203
|
+
warn "Your Ncursesw does not have a form_driver_w function (wide char aware), " \
|
204
|
+
"non-ASCII chars may not work on your system."
|
205
|
+
Form.module_eval <<-FRM_DRV, __FILE__, __LINE__ + 1
|
206
|
+
def form_driver_w form, status, c
|
207
|
+
form_driver form, c
|
208
|
+
end
|
209
|
+
module_function :form_driver_w
|
210
|
+
module DriverHelpers
|
211
|
+
def form_driver c
|
212
|
+
if !c.dumb? && c.printable?
|
213
|
+
c.each_byte do |code|
|
214
|
+
Ncurses::Form.form_driver @form, code
|
215
|
+
end
|
216
|
+
else
|
217
|
+
Ncurses::Form.form_driver @form, c.code
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
FRM_DRV
|
222
|
+
end # if not defined? Form.form_driver_w
|
223
|
+
if not defined? Ncurses.get_wch
|
224
|
+
warn "Your Ncursesw does not have a get_wch function (wide char aware), " \
|
225
|
+
"non-ASCII chars may not work on your system."
|
226
|
+
Ncurses.module_eval <<-GET_WCH, __FILE__, __LINE__ + 1
|
227
|
+
def get_wch
|
228
|
+
c = getch
|
229
|
+
c == Ncurses::ERR ? [c, 0] : [Ncurses::OK, c]
|
230
|
+
end
|
231
|
+
module_function :get_wch
|
232
|
+
GET_WCH
|
233
|
+
CharCode.dumb!
|
234
|
+
end # if not defined? Ncurses.get_wch
|
235
|
+
end
|
236
|
+
|
237
|
+
def mutex; @mutex ||= Mutex.new; end
|
238
|
+
def sync &b; mutex.synchronize(&b); end
|
239
|
+
|
240
|
+
module_function :rows, :cols, :curx, :mutex, :sync, :prepare_form_driver
|
241
|
+
|
242
|
+
remove_const :KEY_ENTER
|
243
|
+
remove_const :KEY_CANCEL
|
244
|
+
|
245
|
+
KEY_ENTER = 10
|
246
|
+
KEY_CANCEL = 7 # ctrl-g
|
247
|
+
KEY_TAB = 9
|
248
|
+
|
249
|
+
module Form
|
250
|
+
## This module contains helpers that ease
|
251
|
+
## using form_driver_ methods when @form is present.
|
252
|
+
module DriverHelpers
|
253
|
+
private
|
254
|
+
|
255
|
+
## Ncurses::Form.form_driver_w wrapper for keycodes and control characters.
|
256
|
+
def form_driver_key c
|
257
|
+
form_driver CharCode.keycode(c)
|
258
|
+
end
|
259
|
+
|
260
|
+
## Ncurses::Form.form_driver_w wrapper for printable characters.
|
261
|
+
def form_driver_char c
|
262
|
+
form_driver CharCode.character(c)
|
263
|
+
#c.is_a?(Fixnum) ? c : c.ord
|
264
|
+
end
|
265
|
+
|
266
|
+
## Ncurses::Form.form_driver_w wrapper for charcodes.
|
267
|
+
def form_driver c
|
268
|
+
Ncurses::Form.form_driver_w @form, c.status, c.code
|
269
|
+
end
|
270
|
+
end # module DriverHelpers
|
271
|
+
end # module Form
|
272
|
+
|
273
|
+
end # module Ncurses
|
274
|
+
end # if defined? Ncurses
|
data/lib/sup/version.rb
CHANGED
data/lib/sup.rb
CHANGED
@@ -357,6 +357,7 @@ EOM
|
|
357
357
|
:name => name.dup.fix_encoding!,
|
358
358
|
:email => email.dup.fix_encoding!,
|
359
359
|
:alternates => [],
|
360
|
+
:hidden_alternates => [],
|
360
361
|
:sendmail => "/usr/sbin/sendmail -oem -ti",
|
361
362
|
:signature => File.join(ENV["HOME"], ".signature"),
|
362
363
|
:gpgkey => ""
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.15.
|
4
|
+
version: 0.15.2
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 15
|
9
|
-
-
|
10
|
-
hash: -
|
9
|
+
- 2
|
10
|
+
hash: -2694534377760147359
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- William Morgan
|
@@ -17,7 +17,7 @@ authors:
|
|
17
17
|
autorequire:
|
18
18
|
bindir: bin
|
19
19
|
cert_chain: []
|
20
|
-
date: 2013-12-
|
20
|
+
date: 2013-12-20 00:00:00.000000000 Z
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
23
23
|
name: xapian-ruby
|
@@ -36,13 +36,13 @@ dependencies:
|
|
36
36
|
- !ruby/object:Gem::Version
|
37
37
|
version: 1.2.15
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
|
-
name: ncursesw
|
39
|
+
name: ncursesw
|
40
40
|
requirement: !ruby/object:Gem::Requirement
|
41
41
|
none: false
|
42
42
|
requirements:
|
43
43
|
- - ~>
|
44
44
|
- !ruby/object:Gem::Version
|
45
|
-
version: 1.
|
45
|
+
version: 1.4.0
|
46
46
|
type: :runtime
|
47
47
|
prerelease: false
|
48
48
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -50,7 +50,7 @@ dependencies:
|
|
50
50
|
requirements:
|
51
51
|
- - ~>
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: 1.
|
53
|
+
version: 1.4.0
|
54
54
|
- !ruby/object:Gem::Dependency
|
55
55
|
name: rmail-sup
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
@@ -301,6 +301,7 @@ files:
|
|
301
301
|
- lib/sup/util/query.rb
|
302
302
|
- lib/sup/util/uri.rb
|
303
303
|
- lib/sup/util/path.rb
|
304
|
+
- lib/sup/util/ncurses.rb
|
304
305
|
- lib/sup/horizontal_selector.rb
|
305
306
|
- lib/sup/modes/line_cursor_mode.rb
|
306
307
|
- lib/sup/modes/label_list_mode.rb
|