sup 0.15.1 → 0.15.2

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.
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
- Per Andersson <avtobiff at the gmail dot coms>
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
@@ -1,3 +1,7 @@
1
+ Release 0.15.2:
2
+
3
+ Use form_driver_w when available. New hidden_alternates option.
4
+
1
5
  Release 0.15.1:
2
6
 
3
7
  Sort threads last-activity-first and bug fix.
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.nonblocking_getch
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
- nil
245
+ Ncurses::CharCode.empty
244
246
  end
245
247
 
246
- if c.nil?
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 == 410
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; @email_map.keys.select { |e| String === e }; end
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
- next if @email_map.member? email
67
- @email_map[email] = a
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; is_account_email? p.email end
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
- if(a = @email_map[email])
79
- a
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
- end
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
- if defined? Ncurses
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!; @sigwinch_mutex.synchronize { @sigwinch_happened = true } end
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.ord
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.safe_nonblocking_getch
402
- next unless c # getch timeout
403
- break if c == Ncurses::KEY_CANCEL
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.safe_nonblocking_getch
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.safe_nonblocking_getch or next
647
- if key == Ncurses::KEY_CANCEL
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.ord, ?Y.ord
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
 
@@ -205,6 +205,7 @@ EOS
205
205
  begin
206
206
  file = Tempfile.new(["sup", Shellwords.escape(@filename.gsub("/", "_")) || "sup-attachment"])
207
207
  file.print @raw_content
208
+ file.flush
208
209
  yield file if block_given?
209
210
  return file.path
210
211
  ensure
@@ -207,7 +207,7 @@ EOS
207
207
  @ts.delete_message m
208
208
  @ts.add_message m
209
209
  end
210
- Index.save_thread t
210
+ Index.save_thread t, sync_back = false
211
211
  update_text_for_line l
212
212
  end
213
213
 
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.nil? # user cancelled
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
- Ncurses::Form.form_driver @form, Ncurses::Form::REQ_END_FIELD
52
- Ncurses::Form.form_driver @form, Ncurses::Form::REQ_NEXT_CHAR if @value && @value =~ / $/ # fucking RETARDED
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
- d =
101
- case c
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, 127 # 127 is also a backspace keysym
113
+ when Ncurses::KEY_BACKSPACE
109
114
  Ncurses::Form::REQ_DEL_PREV
110
- when ?\C-a.ord, Ncurses::KEY_HOME
115
+ when Ncurses::KEY_HOME
111
116
  nop
112
117
  Ncurses::Form::REQ_BEG_FIELD
113
- when ?\C-e.ord, Ncurses::KEY_END
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 == Ncurses::KEY_UP ? -1 : 1)
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
- c
133
+ # return other keycode or nil if it's not a keycode
134
+ c.dumb? ? nil : c.keycode
142
135
  end
143
136
 
144
- Ncurses::Form.form_driver @form, d if d
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
- Ncurses::Form.form_driver @form, Ncurses::Form::REQ_VALIDATION
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 log of other programs since it is impossible to detect which is
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
- Ncurses::Form.form_driver @form, Ncurses::Form::REQ_VALIDATION
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
- Ncurses::Form.form_driver @form, " ".ord
230
- Ncurses::Form.form_driver @form, Ncurses::Form::REQ_DEL_PREV
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
@@ -1,3 +1,3 @@
1
1
  module Redwood
2
- VERSION = "0.15.1"
2
+ VERSION = "0.15.2"
3
3
  end
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.1
4
+ version: 0.15.2
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 15
9
- - 1
10
- hash: -3773962691868465541
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-04 00:00:00.000000000 Z
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-sup
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.3.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.3.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