sup 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sup might be problematic. Click here for more details.

Files changed (48) hide show
  1. data/History.txt +9 -0
  2. data/Manifest.txt +5 -1
  3. data/Rakefile +2 -1
  4. data/bin/sup +27 -10
  5. data/bin/sup-add +2 -1
  6. data/bin/sup-sync-back +51 -23
  7. data/doc/FAQ.txt +29 -37
  8. data/doc/Hooks.txt +38 -0
  9. data/doc/{UserGuide.txt → NewUserGuide.txt} +27 -21
  10. data/doc/TODO +91 -57
  11. data/lib/sup.rb +17 -1
  12. data/lib/sup/buffer.rb +80 -16
  13. data/lib/sup/colormap.rb +0 -2
  14. data/lib/sup/contact.rb +3 -2
  15. data/lib/sup/crypto.rb +110 -0
  16. data/lib/sup/draft.rb +2 -6
  17. data/lib/sup/hook.rb +131 -0
  18. data/lib/sup/imap.rb +27 -16
  19. data/lib/sup/index.rb +38 -14
  20. data/lib/sup/keymap.rb +0 -2
  21. data/lib/sup/label.rb +30 -9
  22. data/lib/sup/logger.rb +12 -1
  23. data/lib/sup/maildir.rb +48 -3
  24. data/lib/sup/mbox.rb +1 -1
  25. data/lib/sup/mbox/loader.rb +22 -12
  26. data/lib/sup/mbox/ssh-loader.rb +1 -1
  27. data/lib/sup/message-chunks.rb +198 -0
  28. data/lib/sup/message.rb +154 -115
  29. data/lib/sup/modes/compose-mode.rb +18 -0
  30. data/lib/sup/modes/contact-list-mode.rb +1 -1
  31. data/lib/sup/modes/edit-message-mode.rb +112 -31
  32. data/lib/sup/modes/file-browser-mode.rb +1 -1
  33. data/lib/sup/modes/inbox-mode.rb +1 -1
  34. data/lib/sup/modes/label-list-mode.rb +8 -6
  35. data/lib/sup/modes/label-search-results-mode.rb +4 -1
  36. data/lib/sup/modes/log-mode.rb +1 -1
  37. data/lib/sup/modes/reply-mode.rb +18 -16
  38. data/lib/sup/modes/search-results-mode.rb +1 -1
  39. data/lib/sup/modes/thread-index-mode.rb +61 -33
  40. data/lib/sup/modes/thread-view-mode.rb +111 -102
  41. data/lib/sup/person.rb +5 -1
  42. data/lib/sup/poll.rb +36 -7
  43. data/lib/sup/sent.rb +1 -0
  44. data/lib/sup/source.rb +7 -3
  45. data/lib/sup/textfield.rb +48 -34
  46. data/lib/sup/thread.rb +9 -5
  47. data/lib/sup/util.rb +16 -22
  48. metadata +7 -3
@@ -21,6 +21,7 @@ class SentManager
21
21
  f.puts "From #{from_email} #{date}"
22
22
  yield f
23
23
  end
24
+
24
25
  @source.each do |offset, labels|
25
26
  m = Message.new :source => @source, :source_info => offset, :labels => @source.labels
26
27
  Index.sync_message m
@@ -31,7 +31,7 @@ class Source
31
31
  ## - load_header offset
32
32
  ## - load_message offset
33
33
  ## - raw_header offset
34
- ## - raw_full_message offset
34
+ ## - raw_message offset
35
35
  ## - check
36
36
  ## - next (or each, if you prefer): should return a message and an
37
37
  ## array of labels.
@@ -77,8 +77,8 @@ class Source
77
77
  def seek_to! o; self.cur_offset = o; end
78
78
  def reset!; seek_to! start_offset; end
79
79
  def == o; o.uri == uri; end
80
- def done?; (self.cur_offset ||= start_offset) >= end_offset; end
81
- def is_source_for? uri; URI(self.uri) == URI(uri); end
80
+ def done?; start_offset.nil? || (self.cur_offset ||= start_offset) >= end_offset; end
81
+ def is_source_for? uri; uri == @uri; end
82
82
 
83
83
  ## check should throw a FatalSourceError or an OutOfSyncSourcError
84
84
  ## if it can detect a problem. it is called when the sup starts up
@@ -96,6 +96,10 @@ class Source
96
96
 
97
97
  protected
98
98
 
99
+ def Source.expand_filesystem_uri uri
100
+ uri.gsub "~", File.expand_path("~")
101
+ end
102
+
99
103
  def cur_offset= o
100
104
  @cur_offset = o
101
105
  @dirty = true
@@ -1,15 +1,17 @@
1
- require 'curses'
2
-
3
1
  module Redwood
4
2
 
5
3
  ## a fully-functional text field supporting completions, expansions,
6
4
  ## history--everything!
5
+ ##
6
+ ## writing this fucking sucked. if you thought ncurses was some 1970s
7
+ ## before-people-knew-how-to-program bullshit, wait till you see
8
+ ## ncurses forms.
7
9
  ##
8
- ## completion is done emacs-style, and mostly depends on outside
9
- ## support, as we merely signal the existence of a new set of
10
- ## completions to show (#new_completions?) or that the current list
11
- ## of completions should be rolled if they're too large to fill the
12
- ## screen (#roll_completions?).
10
+ ## completion comments: completion is done emacs-style, and mostly
11
+ ## depends on outside support, as we merely signal the existence of a
12
+ ## new set of completions to show (#new_completions?) or that the
13
+ ## current list of completions should be rolled if they're too large
14
+ ## to fill the screen (#roll_completions?).
13
15
  ##
14
16
  ## in sup, completion support is implemented through BufferManager#ask
15
17
  ## and CompletionMode.
@@ -27,22 +29,17 @@ class TextField
27
29
  bool_reader :new_completions, :roll_completions
28
30
  attr_reader :completions
29
31
 
30
- ## when the user presses enter, we store the value in @value and
31
- ## clean up all the ncurses cruft. before @value is set, we can
32
- ## get the current value from ncurses.
33
- def value; @field ? get_cur_value : @value end
32
+ def value; @value || get_cursed_value end
34
33
 
35
34
  def activate question, default=nil, &block
36
35
  @question = question
37
- @value = nil
38
36
  @completion_block = block
39
37
  @field = Ncurses::Form.new_field 1, @width - question.length,
40
38
  @y, @x + question.length, 0, 0
41
39
  @form = Ncurses::Form.new_form [@field]
42
-
43
- @history[@i = @history.size] = default || ""
40
+ @value = default
44
41
  Ncurses::Form.post_form @form
45
- set_cur_value @history[@i]
42
+ set_cursed_value default if default
46
43
  end
47
44
 
48
45
  def position_cursor
@@ -50,7 +47,7 @@ class TextField
50
47
  @w.mvaddstr @y, 0, @question
51
48
  Ncurses.curs_set 1
52
49
  Ncurses::Form.form_driver @form, Ncurses::Form::REQ_END_FIELD
53
- Ncurses::Form.form_driver @form, Ncurses::Form::REQ_NEXT_CHAR if @history[@i] =~ / $/ # fucking RETARDED!!!!
50
+ Ncurses::Form.form_driver @form, Ncurses::Form::REQ_NEXT_CHAR if @value && @value =~ / $/ # fucking RETARDED!!!!
54
51
  end
55
52
 
56
53
  def deactivate
@@ -66,20 +63,21 @@ class TextField
66
63
  ## short-circuit exit paths
67
64
  case c
68
65
  when Ncurses::KEY_ENTER # submit!
69
- @value = @history[@i] = get_cur_value
66
+ @value = get_cursed_value
67
+ @history.push @value
70
68
  return false
71
69
  when Ncurses::KEY_CANCEL # cancel
72
- @history.delete_at @i
73
- @i = @history.empty? ? nil : (@i - 1) % @history.size
74
70
  @value = nil
75
71
  return false
76
72
  when Ncurses::KEY_TAB # completion
77
73
  return true unless @completion_block
78
74
  if @completions.empty?
79
- v = get_cur_value
75
+ v = get_cursed_value
80
76
  c = @completion_block.call v
81
77
  if c.size > 0
82
- set_cur_value c.map { |full, short| full }.shared_prefix
78
+ @value = c.map { |full, short| full }.shared_prefix(true)
79
+ set_cursed_value @value
80
+ position_cursor
83
81
  end
84
82
  if c.size > 1
85
83
  @completions = c
@@ -94,6 +92,7 @@ class TextField
94
92
  end
95
93
 
96
94
  reset_completion_state
95
+ @value = nil
97
96
 
98
97
  d =
99
98
  case c
@@ -103,18 +102,24 @@ class TextField
103
102
  Ncurses::Form::REQ_NEXT_CHAR
104
103
  when Ncurses::KEY_BACKSPACE
105
104
  Ncurses::Form::REQ_DEL_PREV
106
- when ?\001
105
+ when 1 #ctrl-a
107
106
  Ncurses::Form::REQ_BEG_FIELD
108
- when ?\005
107
+ when 5 #ctrl-e
109
108
  Ncurses::Form::REQ_END_FIELD
109
+ when 11 # ctrl-k
110
+ Ncurses::Form::REQ_CLR_EOF
110
111
  when Ncurses::KEY_UP
111
- @history[@i] = @field.field_buffer 0
112
+ @i ||= @history.size
113
+ @history[@i] = get_cursed_value
112
114
  @i = (@i - 1) % @history.size
113
- set_cur_value @history[@i]
115
+ @value = @history[@i]
116
+ set_cursed_value @value
114
117
  when Ncurses::KEY_DOWN
115
- @history[@i] = @field.field_buffer 0
118
+ @i ||= @history.size
119
+ @history[@i] = get_cursed_value
116
120
  @i = (@i + 1) % @history.size
117
- set_cur_value @history[@i]
121
+ @value = @history[@i]
122
+ set_cursed_value @value
118
123
  else
119
124
  c
120
125
  end
@@ -131,16 +136,25 @@ private
131
136
  end
132
137
 
133
138
  ## ncurses inanity wrapper
134
- def get_cur_value
139
+ ##
140
+ ## DO NOT READ THIS CODE. YOU WILL GO MAD.
141
+ def get_cursed_value
142
+ return nil unless @field
143
+
144
+ x = Ncurses.curx
135
145
  Ncurses::Form.form_driver @form, Ncurses::Form::REQ_VALIDATION
136
- @field.field_buffer(0).gsub(/^\s+|\s+$/, "").gsub(/\s+/, " ")
146
+ v = @field.field_buffer(0).gsub(/^\s+|\s+$/, "")
147
+
148
+ ## cursor <= end of text
149
+ if x - @question.length - v.length <= 0
150
+ v
151
+ else # trailing spaces
152
+ v + (" " * (x - @question.length - v.length))
153
+ end
137
154
  end
138
-
139
- ## ncurses inanity wrapper
140
- def set_cur_value v
155
+
156
+ def set_cursed_value v
141
157
  @field.set_field_buffer 0, v
142
- Ncurses::Form.form_driver @form, Ncurses::Form::REQ_END_FIELD
143
158
  end
144
-
145
159
  end
146
160
  end
@@ -54,7 +54,7 @@ class Thread
54
54
 
55
55
  ## yields each message, its depth, and its parent. the message yield
56
56
  ## parameter can be a Message object, or :fake_root, or nil (no
57
- ## message found but the presence of one induced from other
57
+ ## message found but the presence of one deduced from other
58
58
  ## messages).
59
59
  def each fake_root=false
60
60
  adj = 0
@@ -85,7 +85,10 @@ class Thread
85
85
  def first; each { |m, *o| return m if m }; nil; end
86
86
  def dirty?; any? { |m, *o| m && m.dirty? }; end
87
87
  def date; map { |m, *o| m.date if m }.compact.max; end
88
- def snippet; argfind { |m, *o| m && m.snippet }; end
88
+ def snippet
89
+ last_m, last_stuff = select { |m, *o| m && m.snippet && !m.snippet.empty? }.sort_by { |m, *o| m.date }.last
90
+ last_m ? last_m.snippet : ""
91
+ end
89
92
  def authors; map { |m, *o| m.from if m }.compact.uniq; end
90
93
 
91
94
  def apply_label t; each { |m, *o| m && m.add_label(t) }; end
@@ -303,18 +306,19 @@ class ThreadSet
303
306
  next if contains_id? mid
304
307
 
305
308
  m = builder.call
306
- add_message m
307
- load_thread_for_message m, :load_killed => opts[:load_killed]
309
+ load_thread_for_message m, :skip_killed => opts[:skip_killed], :load_deleted => opts[:load_deleted], :load_spam => opts[:load_spam]
308
310
  yield size if block_given?
309
311
  end
310
312
  end
311
313
 
312
314
  ## loads in all messages needed to thread m
315
+ ## may do nothing if m's thread is killed
313
316
  def load_thread_for_message m, opts={}
314
- @index.each_message_in_thread_for m, opts.merge({:limit => 100}) do |mid, builder|
317
+ good = @index.each_message_in_thread_for m, opts do |mid, builder|
315
318
  next if contains_id? mid
316
319
  add_message builder.call
317
320
  end
321
+ add_message m if good
318
322
  end
319
323
 
320
324
  ## merges in a pre-loaded thread
@@ -63,13 +63,13 @@ module RMail
63
63
  class EncodingUnsupportedError < StandardError; end
64
64
 
65
65
  class Message
66
- def add_attachment fn
66
+ def add_file_attachment fn
67
67
  bfn = File.basename fn
68
68
  a = Message.new
69
69
  t = MIME::Types.type_for(bfn).first || MIME::Types.type_for("exe").first
70
70
 
71
- a.header.add "Content-Disposition", "attachment; filename=#{bfn}"
72
- a.header.add "Content-Type", "#{t.content_type}; name=#{bfn}"
71
+ a.header.add "Content-Disposition", "attachment; filename=#{bfn.to_s.inspect}"
72
+ a.header.add "Content-Type", "#{t.content_type}; name=#{bfn.to_s.inspect}"
73
73
  a.header.add "Content-Transfer-Encoding", t.encoding
74
74
  a.body =
75
75
  case t.encoding
@@ -77,12 +77,20 @@ module RMail
77
77
  [IO.read(fn)].pack "m"
78
78
  when "quoted-printable"
79
79
  [IO.read(fn)].pack "M"
80
+ when "7bit", "8bit"
81
+ IO.read(fn)
80
82
  else
81
83
  raise EncodingUnsupportedError, t.encoding
82
84
  end
83
85
 
84
86
  add_part a
85
87
  end
88
+
89
+ def charset
90
+ if header.field?("content-type") && header.fetch("content-type") =~ /charset="?(.*?)"?(;|$)/
91
+ $1
92
+ end
93
+ end
86
94
  end
87
95
  end
88
96
 
@@ -123,21 +131,7 @@ class Object
123
131
  ret
124
132
  end
125
133
 
126
- ## takes a value which it yields and then returns, so that code
127
- ## like:
128
- ##
129
- ## x = expensive_operation
130
- ## log "got #{x}"
131
- ## x
132
- ##
133
- ## now becomes:
134
- ##
135
- ## with(expensive_operation) { |x| log "got #{x}" }
136
- ##
137
- ## i'm sure there's pithy comment i could make here about the
138
- ## superiority of lisp, but fuck lisp.
139
- ##
140
- ## addendum: apparently this is a "k combinator". whoda thunk it?
134
+ ## "k combinator"
141
135
  def returning x; yield x; x; end
142
136
 
143
137
  ## clone of java-style whole-method synchronization
@@ -277,14 +271,14 @@ module Enumerable
277
271
 
278
272
  ## returns the maximum shared prefix of an array of strings
279
273
  ## optinally excluding a prefix
280
- def shared_prefix exclude=""
274
+ def shared_prefix caseless=false, exclude=""
281
275
  return "" if empty?
282
276
  prefix = ""
283
277
  (0 ... first.length).each do |i|
284
- c = first[i]
285
- break unless all? { |s| s[i] == c }
278
+ c = (caseless ? first.downcase : first)[i]
279
+ break unless all? { |s| (caseless ? s.downcase : s)[i] == c }
286
280
  next if exclude[i] == c
287
- prefix += c.chr
281
+ prefix += first[i].chr
288
282
  end
289
283
  prefix
290
284
  end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: sup
5
5
  version: !ruby/object:Gem::Version
6
- version: "0.1"
7
- date: 2007-07-17 00:00:00 -07:00
6
+ version: "0.2"
7
+ date: 2007-10-29 00:00:00 -07:00
8
8
  summary: A console-based email client with the best features of GMail, mutt, and emacs. Features full text search, labels, tagged operations, multiple buffers, recent contacts, and more.
9
9
  require_paths:
10
10
  - lib
@@ -43,15 +43,18 @@ files:
43
43
  - bin/sup-sync
44
44
  - bin/sup-sync-back
45
45
  - doc/FAQ.txt
46
+ - doc/Hooks.txt
47
+ - doc/NewUserGuide.txt
46
48
  - doc/Philosophy.txt
47
49
  - doc/TODO
48
- - doc/UserGuide.txt
49
50
  - lib/sup.rb
50
51
  - lib/sup/account.rb
51
52
  - lib/sup/buffer.rb
52
53
  - lib/sup/colormap.rb
53
54
  - lib/sup/contact.rb
55
+ - lib/sup/crypto.rb
54
56
  - lib/sup/draft.rb
57
+ - lib/sup/hook.rb
55
58
  - lib/sup/imap.rb
56
59
  - lib/sup/index.rb
57
60
  - lib/sup/keymap.rb
@@ -62,6 +65,7 @@ files:
62
65
  - lib/sup/mbox/loader.rb
63
66
  - lib/sup/mbox/ssh-file.rb
64
67
  - lib/sup/mbox/ssh-loader.rb
68
+ - lib/sup/message-chunks.rb
65
69
  - lib/sup/message.rb
66
70
  - lib/sup/mode.rb
67
71
  - lib/sup/modes/buffer-list-mode.rb