sup 0.0.1 → 0.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.

@@ -16,8 +16,8 @@ class PollMode < LogMode
16
16
  def poll
17
17
  puts unless @new
18
18
  @new = false
19
- puts "poll started at #{Time.now}"
20
- PollManager.poll { |s| puts s }
19
+ puts "Poll started at #{Time.now}"
20
+ PollManager.do_poll { |s| puts s }
21
21
  end
22
22
  end
23
23
 
@@ -40,7 +40,6 @@ class ReplyMode < EditMessageMode
40
40
 
41
41
  @headers[:user] = {
42
42
  "From" => "#{from.name} <#{from_email}>",
43
- "To" => "",
44
43
  }
45
44
 
46
45
  @headers[:all] = {
@@ -57,12 +56,15 @@ class ReplyMode < EditMessageMode
57
56
  refs = gen_references
58
57
  mid = gen_message_id
59
58
  @headers.each do |k, v|
60
- @headers[k] = v.merge({
59
+ @headers[k] = {
60
+ "To" => "",
61
+ "Cc" => "",
62
+ "Bcc" => "",
61
63
  "In-Reply-To" => "<#{@m.id}>",
62
64
  "Subject" => Message.reify_subj(@m.subj),
63
65
  "Message-Id" => mid,
64
66
  "References" => refs,
65
- })
67
+ }.merge v
66
68
  end
67
69
 
68
70
  @type_labels = REPLY_TYPES.select { |t| @headers.member?(t) }
@@ -107,6 +109,7 @@ protected
107
109
 
108
110
  if new_header.size != header.size ||
109
111
  header.any? { |k, v| new_header[k] != v }
112
+ #raise "nhs: #{new_header.size} hs: #{header.size} new: #{new_header.inspect} old: #{header.inspect}"
110
113
  @selected_type = :user
111
114
  @headers[:user] = new_header
112
115
  end
@@ -8,9 +8,34 @@ class ResumeMode < ComposeMode
8
8
  @header.delete "Date"
9
9
  @header["Message-Id"] = gen_message_id # generate a new'n
10
10
  regen_text
11
+ @safe = false
12
+ end
13
+
14
+ def killable?
15
+ unless @safe
16
+ case BufferManager.ask_yes_or_no "Discard draft?"
17
+ when true
18
+ DraftManager.discard @id
19
+ BufferManager.flash "Draft discarded."
20
+ true
21
+ when false
22
+ BufferManager.flash "Draft saved."
23
+ true
24
+ else
25
+ false
26
+ end
27
+ end
11
28
  end
12
29
 
13
30
  def send_message
31
+ if super
32
+ DraftManager.discard @id
33
+ @safe = true
34
+ end
35
+ end
36
+
37
+ def save_as_draft
38
+ @safe = true
14
39
  DraftManager.discard @id if super
15
40
  end
16
41
  end
@@ -14,6 +14,7 @@ class ScrollMode < Mode
14
14
  k.add :page_up, "Up one page", :page_up, 'p', :backspace
15
15
  k.add :jump_to_home, "Jump to top", :home, '^', '1'
16
16
  k.add :jump_to_end, "Jump to bottom", :end, '$', '0'
17
+ k.add :jump_to_left, "Jump to the left", '['
17
18
  end
18
19
 
19
20
  def initialize opts={}
@@ -48,6 +49,11 @@ class ScrollMode < Mode
48
49
  buffer.mark_dirty
49
50
  end
50
51
 
52
+ def jump_to_left
53
+ buffer.mark_dirty unless @leftcol == 0
54
+ @leftcol = 0
55
+ end
56
+
51
57
  ## set top line to l
52
58
  def jump_to_line l
53
59
  l = l.clamp 0, lines - 1
@@ -64,6 +70,7 @@ class ScrollMode < Mode
64
70
  def jump_to_home; jump_to_line 0; end
65
71
  def jump_to_end; jump_to_line lines - buffer.content_height; end
66
72
 
73
+
67
74
  def ensure_mode_validity
68
75
  @topline = @topline.clamp 0, lines - 1
69
76
  @topline = 0 if @topline < 0 # empty
@@ -180,8 +180,10 @@ class ThreadIndexMode < LineCursorMode
180
180
  threads = @threads + @hidden_threads.keys
181
181
  mbid = BufferManager.say "Saving threads..."
182
182
  threads.each_with_index do |t, i|
183
- BufferManager.say "Saving thread #{i + 1} of #{threads.length}...",
184
- mbid
183
+ if i % 5 == 0
184
+ BufferManager.say "Saving thread #{i + 1} of #{threads.length}...",
185
+ mbid
186
+ end
185
187
  t.save Index
186
188
  end
187
189
  BufferManager.clear mbid
@@ -263,14 +265,9 @@ class ThreadIndexMode < LineCursorMode
263
265
 
264
266
  def load_n_threads_background n=LOAD_MORE_THREAD_NUM, opts={}
265
267
  return if @load_thread
266
- @load_thread = ::Thread.new do
267
- begin
268
- num = load_n_threads n, opts
269
- opts[:when_done].call(num) if opts[:when_done]
270
- rescue Exception => e
271
- $exception ||= e
272
- raise
273
- end
268
+ @load_thread = Redwood::reporting_thread do
269
+ num = load_n_threads n, opts
270
+ opts[:when_done].call(num) if opts[:when_done]
274
271
  @load_thread = nil
275
272
  end
276
273
  end
@@ -359,7 +356,7 @@ protected
359
356
  [
360
357
  [:tagged_color, @tags.tagged?(t) ? ">" : " "],
361
358
  [:none, sprintf("%#{@date_width}s ", date)],
362
- [base_color, sprintf("%-#{@from_width}s ", from)],
359
+ [base_color, sprintf("%-#{@from_width}s", from)],
363
360
  [:starred_color, starred ? "*" : " "],
364
361
  [:none, t.size == 1 ? " " * (@size_width + 2) : sprintf("(%#{@size_width}d)", t.size)],
365
362
  [:to_me_color, dp ? " >" : (p ? ' -' : " ")],
@@ -16,6 +16,7 @@ class ThreadViewMode < LineCursorMode
16
16
  k.add :collapse_non_new_messages, "Collapse all but new messages", 'N'
17
17
  k.add :reply, "Reply to a message", 'r'
18
18
  k.add :forward, "Forward a message", 'f'
19
+ k.add :save_to_disk, "Save message/attachment to disk", 's'
19
20
  end
20
21
 
21
22
  def initialize thread, hidden_labels=[]
@@ -62,7 +63,7 @@ class ThreadViewMode < LineCursorMode
62
63
  def show_header
63
64
  return unless(m = @message_lines[curpos])
64
65
  BufferManager.spawn_unless_exists("Full header") do
65
- TextMode.new m.content #m.header_text
66
+ TextMode.new m.raw_header
66
67
  end
67
68
  end
68
69
 
@@ -109,6 +110,32 @@ class ThreadViewMode < LineCursorMode
109
110
  update
110
111
  end
111
112
 
113
+ def save fn
114
+ if File.exists? fn
115
+ return unless BufferManager.ask_yes_or_no "File exists. Overwrite?"
116
+ end
117
+ begin
118
+ File.open(fn, "w") { |f| yield f }
119
+ BufferManager.flash "Successfully wrote #{fn}."
120
+ rescue SystemCallError => e
121
+ BufferManager.flash "Error writing to file: #{e.message}"
122
+ end
123
+ end
124
+ private :save
125
+
126
+ def save_to_disk
127
+ return unless(chunk = @chunk_lines[curpos])
128
+ case chunk
129
+ when Message::Attachment
130
+ fn = BufferManager.ask :filename, "save attachment to file: ", chunk.filename
131
+ save(fn) { |f| f.print chunk } if fn
132
+ else
133
+ m = @message_lines[curpos]
134
+ fn = BufferManager.ask :filename, "save message to file: "
135
+ save(fn) { |f| f.print m.raw_full_message } if fn
136
+ end
137
+ end
138
+
112
139
  def edit_message
113
140
  return unless(m = @message_lines[curpos])
114
141
  if m.is_draft?
@@ -178,7 +205,7 @@ class ThreadViewMode < LineCursorMode
178
205
  ## not sure if this is really necessary but we might as well...
179
206
  def cleanup
180
207
  @thread.each do |m, d, p|
181
- if m.has_label? :unread
208
+ if m && m.has_label?(:unread)
182
209
  m.remove_label :unread
183
210
  UpdateManager.relay :read, m
184
211
  end
@@ -265,7 +292,7 @@ private
265
292
  x = [[prefix_widget, widget, imp_widget, [:message_patina_color, "From: #{m.from ? m.from.longname : '?'}"]]] +
266
293
  ((m.to.empty? ? [] : break_into_lines(" To: ", m.to.map { |x| x.longname })) +
267
294
  (m.cc.empty? ? [] : break_into_lines(" Cc: ", m.cc.map { |x| x.longname })) +
268
- (m.bcc.empty? ? [] : break_into_lines(" Bcc: ", m.bcc.map { |x| x.longname })) +
295
+ (m.bcc.empty? ? [] : break_into_lines(" Bcc: ", m.bcc.map { |x| x.longname })) +
269
296
  [" Date: #{m.date.strftime DATE_FORMAT} (#{m.date.to_nice_distance_s})"] +
270
297
  [" Subject: #{m.subj}"] +
271
298
  [(parent ? " In reply to: #{parent.from.mediumname}'s message of #{parent.date.strftime DATE_FORMAT}" : nil)] +
@@ -331,6 +358,7 @@ private
331
358
  BufferManager.flash "viewing #{a.content_type} attachment..."
332
359
  a.view!
333
360
  BufferManager.erase_flash
361
+ BufferManager.completely_redraw_screen
334
362
  end
335
363
 
336
364
  end
data/lib/sup/poll.rb CHANGED
@@ -13,25 +13,38 @@ class PollManager
13
13
 
14
14
  self.class.i_am_the_instance self
15
15
 
16
- ::Thread.new do
16
+ Redwood::reporting_thread do
17
17
  while true
18
18
  sleep DELAY / 2
19
- if @last_poll.nil? || (Time.now - @last_poll) >= DELAY
20
- mbid = BufferManager.say "Polling for new messages..."
21
- num, numi = poll { |s| BufferManager.say s, mbid }
22
- BufferManager.clear mbid
23
- BufferManager.flash "Loaded #{num} new messages, #{numi} to inbox." if num > 0
24
- end
19
+ poll if @last_poll.nil? || (Time.now - @last_poll) >= DELAY
25
20
  end
26
21
  end
27
22
  end
28
23
 
24
+ def buffer
25
+ BufferManager.spawn_unless_exists("<poll for new messages>", :hidden => true) do
26
+ PollMode.new
27
+ end
28
+ end
29
+
29
30
  def poll
31
+ BufferManager.flash "Polling for new messages..."
32
+ num, numi = buffer.mode.poll
33
+ if num > 0
34
+ BufferManager.flash "Loaded #{num} new messages, #{numi} to inbox."
35
+ else
36
+ BufferManager.flash "No new messages."
37
+ end
38
+ [num, numi]
39
+ end
40
+
41
+ def do_poll
30
42
  return [0, 0] if @polling
31
43
  @polling = true
32
44
  found = {}
33
45
  total_num = 0
34
46
  total_numi = 0
47
+
35
48
  Index.usual_sources.each do |source|
36
49
  next if source.done?
37
50
  yield "Loading from #{source}... "
@@ -41,9 +54,10 @@ class PollManager
41
54
  num_inbox = 0
42
55
  source.each do |offset, labels|
43
56
  start_offset ||= offset
44
-
57
+ yield " Found message at #{offset} with labels #{labels * ', '}"
45
58
  begin
46
- m = Redwood::Message.new source, offset, labels
59
+ m = Redwood::Message.new :source => source, :source_info => offset,
60
+ :labels => labels
47
61
  if found[m.id]
48
62
  yield "Skipping duplicate message #{m.id}"
49
63
  next
@@ -64,7 +78,7 @@ class PollManager
64
78
  if num % 1000 == 0 && num > 0
65
79
  elapsed = Time.now - start
66
80
  pctdone = (offset.to_f - start_offset) / (source.total.to_f - start_offset)
67
- remaining = (source.total.to_f - offset.to_f) * (elapsed.to_f / (offset.to_f - start_offset))
81
+ remaining = (source.end_offset.to_f - offset.to_f) * (elapsed.to_f / (offset.to_f - start_offset))
68
82
  yield "## #{num} (#{(pctdone * 100.0)}% done) read; #{elapsed.to_time_s} elapsed; est. #{remaining.to_time_s} remaining"
69
83
  end
70
84
  end
data/lib/sup/sent.rb CHANGED
@@ -10,9 +10,9 @@ class SentManager
10
10
  self.class.i_am_the_instance self
11
11
  end
12
12
 
13
- def self.source_name; "sent"; end
13
+ def self.source_name; "sent://"; end
14
14
  def self.source_id; 9998; end
15
- def new_source; @source = SentLoader.new @fn; end
15
+ def new_source; @source = SentLoader.new; end
16
16
 
17
17
  def write_sent_message date, from_email
18
18
  need_blank = File.exists?(@fn) && !File.zero?(@fn)
@@ -22,7 +22,7 @@ class SentManager
22
22
  yield f
23
23
  end
24
24
  @source.each do |offset, labels|
25
- m = Message.new @source, offset, labels
25
+ m = Message.new :source => @source, :source_info => offset, :labels => labels
26
26
  Index.add_message m
27
27
  UpdateManager.relay :add, m
28
28
  end
@@ -30,17 +30,17 @@ class SentManager
30
30
  end
31
31
 
32
32
  class SentLoader < MBox::Loader
33
- def initialize filename, end_offset=0
33
+ def initialize cur_offset=0
34
+ filename = Redwood::SENT_FN
34
35
  File.open(filename, "w") { } unless File.exists? filename
35
- super filename, end_offset, true, true
36
+ super "mbox://" + filename, cur_offset, true, true
36
37
  end
37
38
 
38
- def id; SentManager.source_id; end
39
39
  def to_s; SentManager.source_name; end
40
-
40
+ def id; SentManager.source_id; end
41
41
  def labels; [:sent, :inbox]; end
42
42
  end
43
43
 
44
- Redwood::register_yaml(SentLoader, %w(filename end_offset))
44
+ Redwood::register_yaml(SentLoader, %w(cur_offset))
45
45
 
46
46
  end
data/lib/sup/source.rb ADDED
@@ -0,0 +1,64 @@
1
+ module Redwood
2
+
3
+ class SourceError < StandardError; end
4
+
5
+ class Source
6
+ ## dirty? described whether cur_offset has changed, which means the
7
+ ## source needs to be re-saved to disk.
8
+ ##
9
+ ## broken? means no message can be loaded (e.g. IMAP server is
10
+ ## down), so don't even bother.
11
+ bool_reader :usual, :archived, :dirty
12
+ attr_reader :cur_offset, :broken_msg
13
+ attr_accessor :id
14
+
15
+ ## You should implement:
16
+ ##
17
+ ## start_offset
18
+ ## end_offset
19
+ ## load_header(offset)
20
+ ## load_message(offset)
21
+ ## raw_header(offset)
22
+ ## raw_full_message(offset)
23
+ ## next
24
+
25
+ def initialize uri, initial_offset=nil, usual=true, archived=false, id=nil
26
+ @uri = uri
27
+ @cur_offset = initial_offset || start_offset
28
+ @usual = usual
29
+ @archived = archived
30
+ @id = id
31
+ @dirty = false
32
+ @broken_msg = nil
33
+ end
34
+
35
+ def broken?; !@broken_msg.nil?; end
36
+ def to_s; @uri; end
37
+ def seek_to! o; self.cur_offset = o; end
38
+ def reset!; seek_to! start_offset; end
39
+ def == o; o.to_s == to_s; end
40
+ def done?; cur_offset >= end_offset; end
41
+ def is_source_for? s; to_s == s; end
42
+
43
+ def each
44
+ until done?
45
+ n, labels = self.next
46
+ raise "no message" unless n
47
+ labels += [:inbox] unless archived?
48
+ yield n, labels
49
+ end
50
+ end
51
+
52
+ protected
53
+
54
+ def cur_offset= o
55
+ @cur_offset = o
56
+ @dirty = true
57
+ end
58
+
59
+ attr_writer :broken_msg
60
+ end
61
+
62
+ Redwood::register_yaml(Source, %w(uri cur_offset usual archived id))
63
+
64
+ end
data/lib/sup/thread.rb CHANGED
@@ -7,6 +7,7 @@ class Thread
7
7
 
8
8
  attr_reader :containers
9
9
  def initialize
10
+ raise "wrong thread, buddy!" if block_given?
10
11
  @containers = []
11
12
  end
12
13
 
@@ -321,6 +322,7 @@ class ThreadSet
321
322
  else
322
323
  ## to disable subject grouping, use the next line instead
323
324
  ## (and the same for below)
325
+ #Redwood::log "[1] normalized subject for #{id} is #{Message.normalize_subj(root.subj)}"
324
326
  thread = (@subj_thread[Message.normalize_subj(root.subj)] ||= Thread.new)
325
327
  #thread = (@subj_thread[root.id] ||= Thread.new)
326
328
 
@@ -341,6 +343,7 @@ class ThreadSet
341
343
  else
342
344
  ## to disable subject grouping, use the next line instead
343
345
  ## (and the same above)
346
+ #Redwood::log "[2] normalized subject for #{id} is #{Message.normalize_subj(root.subj)}"
344
347
  thread = (@subj_thread[Message.normalize_subj(root.subj)] ||= Thread.new)
345
348
  #thread = (@subj_thread[root.id] ||= Thread.new)
346
349
 
data/lib/sup/util.rb CHANGED
@@ -216,10 +216,10 @@ class Time
216
216
  end
217
217
  end
218
218
 
219
- TO_NICE_S_MAX_LEN = 11 # e.g. "Jul 31 2005"
219
+ TO_NICE_S_MAX_LEN = 9 # e.g. "Yest.10am"
220
220
  def to_nice_s from=Time.now
221
221
  if year != from.year
222
- strftime "%b %e %Y"
222
+ strftime "%b %Y"
223
223
  elsif month != from.month
224
224
  strftime "%b %e"
225
225
  else
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: sup
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.1
7
- date: 2006-11-28 00:00:00 -08:00
6
+ version: 0.0.2
7
+ date: 2006-12-10 00:00:00 -08: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
@@ -79,6 +79,8 @@ files:
79
79
  - lib/sup/sent.rb
80
80
  - lib/sup/tagger.rb
81
81
  - lib/sup/colormap.rb
82
+ - lib/sup/source.rb
83
+ - lib/sup/imap.rb
82
84
  - lib/sup.rb
83
85
  test_files: []
84
86