sup 0.0.4 → 0.0.5

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.

@@ -15,7 +15,7 @@ class ContactListMode < LineCursorMode
15
15
  def initialize mode = :regular
16
16
  @mode = mode
17
17
  @tags = Tagger.new self
18
- reload
18
+ @num = 0
19
19
  super()
20
20
  end
21
21
 
@@ -37,10 +37,10 @@ class ContactListMode < LineCursorMode
37
37
  def apply_to_tagged; @tags.apply_to_tagged; end
38
38
 
39
39
  def load; regen_text; end
40
- def load_more
41
- @num += LOAD_MORE_CONTACTS_NUM
40
+ def load_more num=LOAD_MORE_CONTACTS_NUM
41
+ @num += num
42
42
  regen_text
43
- BufferManager.flash "Loaded #{LOAD_MORE_CONTACTS_NUM} more contacts."
43
+ BufferManager.flash "Added #{num} contacts."
44
44
  end
45
45
 
46
46
  def multi_select people
@@ -60,7 +60,7 @@ class ContactListMode < LineCursorMode
60
60
  def multi_search people
61
61
  mode = PersonSearchResultsMode.new people
62
62
  BufferManager.spawn "personal search results", mode
63
- mode.load_more_threads :num => mode.buffer.content_height
63
+ mode.load_threads :num => mode.buffer.content_height
64
64
  end
65
65
 
66
66
  def search
@@ -70,7 +70,6 @@ class ContactListMode < LineCursorMode
70
70
 
71
71
  def reload
72
72
  @tags.drop_all_tags
73
- @num = LOAD_MORE_CONTACTS_NUM
74
73
  load
75
74
  end
76
75
 
@@ -101,8 +100,7 @@ protected
101
100
 
102
101
  def regen_text
103
102
  @user_contacts = ContactManager.contacts.invert
104
- recent = Index.load_contacts AccountManager.user_emails,
105
- :num => @num
103
+ recent = Index.load_contacts AccountManager.user_emails, :num => [@num - @user_contacts.length, 0].max
106
104
 
107
105
  @contacts = (@user_contacts.keys + recent.select { |p| !@user_contacts[p] }).sort_by { |p| p.sort_by_me + (p.name || "") + p.email }.remove_successive_dupes
108
106
 
@@ -6,8 +6,6 @@ class InboxMode < ThreadIndexMode
6
6
  register_keymap do |k|
7
7
  ## overwrite toggle_archived with archive
8
8
  k.add :archive, "Archive thread (remove from inbox)", 'a'
9
- k.add :load_more_threads, "Load #{LOAD_MORE_THREAD_NUM} more threads", 'M'
10
- k.add :reload, "Discard threads and reload", 'D'
11
9
  end
12
10
 
13
11
  def initialize
@@ -28,7 +26,7 @@ class InboxMode < ThreadIndexMode
28
26
 
29
27
  def is_relevant? m; m.has_label? :inbox; end
30
28
 
31
- def load_more_threads opts={}
29
+ def load_threads opts={}
32
30
  n = opts[:num] || ThreadIndexMode::LOAD_MORE_THREAD_NUM
33
31
  load_n_threads_background n, :label => :inbox,
34
32
  :load_killed => false,
@@ -38,12 +36,6 @@ class InboxMode < ThreadIndexMode
38
36
  BufferManager.flash "Added #{num} threads."
39
37
  end)
40
38
  end
41
-
42
- def reload
43
- drop_all_threads
44
- BufferManager.draw_screen
45
- load_more_threads :num => buffer.content_height
46
- end
47
39
  end
48
40
 
49
41
  end
@@ -81,7 +81,7 @@ protected
81
81
  b = BufferManager.spawn_unless_exists(label) do
82
82
  mode = LabelSearchResultsMode.new [label]
83
83
  end
84
- b.mode.load_more_threads :num => b.content_height
84
+ b.mode.load_threads :num => b.content_height
85
85
  end
86
86
  end
87
87
  end
@@ -1,10 +1,6 @@
1
1
  module Redwood
2
2
 
3
3
  class LabelSearchResultsMode < ThreadIndexMode
4
- register_keymap do |k|
5
- k.add :load_more_threads, "Load #{LOAD_MORE_THREAD_NUM} more threads", 'M'
6
- end
7
-
8
4
  def initialize labels
9
5
  @labels = labels
10
6
  super
@@ -12,7 +8,7 @@ class LabelSearchResultsMode < ThreadIndexMode
12
8
 
13
9
  def is_relevant? m; @labels.all? { |l| m.has_label? l }; end
14
10
 
15
- def load_more_threads opts={}
11
+ def load_threads opts={}
16
12
  n = opts[:num] || ThreadIndexMode::LOAD_MORE_THREAD_NUM
17
13
  load_n_threads_background n, :labels => @labels,
18
14
  :load_killed => true,
@@ -124,7 +124,8 @@ protected
124
124
  private
125
125
 
126
126
  def set_status
127
- @status = "line #{@curpos + 1} of #{lines}"
127
+ l = lines
128
+ @status = l > 0 ? "line #{@curpos + 1} of #{l}" : ""
128
129
  end
129
130
 
130
131
  end
@@ -1,10 +1,6 @@
1
1
  module Redwood
2
2
 
3
3
  class PersonSearchResultsMode < ThreadIndexMode
4
- register_keymap do |k|
5
- k.add :load_more_threads, "Load #{LOAD_MORE_THREAD_NUM} more threads", 'M'
6
- end
7
-
8
4
  def initialize people
9
5
  @people = people
10
6
  super
@@ -12,7 +8,7 @@ class PersonSearchResultsMode < ThreadIndexMode
12
8
 
13
9
  def is_relevant? m; @people.any? { |p| m.from == p }; end
14
10
 
15
- def load_more_threads opts={}
11
+ def load_threads opts={}
16
12
  n = opts[:num] || ThreadIndexMode::LOAD_MORE_THREAD_NUM
17
13
  load_n_threads_background n, :participants => @people,
18
14
  :load_killed => true,
@@ -18,11 +18,16 @@ class ReplyMode < EditMessageMode
18
18
  super 2, :twiddles => false
19
19
  @m = message
20
20
 
21
+ ## it's important to put this early because it forces a read of
22
+ ## the full headers (most importantly the list-post header, if
23
+ ## any)
24
+ @body = reply_body_lines(message)
25
+
21
26
  from =
22
27
  if @m.recipient_email
23
28
  AccountManager.account_for(@m.recipient_email)
24
29
  else
25
- (@m.to + @m.cc).argfind { |p| AccountManager.is_account? p }
30
+ (@m.to + @m.cc).find { |p| AccountManager.is_account? p }
26
31
  end || AccountManager.default_account
27
32
 
28
33
  from_email = @m.recipient_email || from.email
@@ -70,7 +75,7 @@ class ReplyMode < EditMessageMode
70
75
  @type_labels = REPLY_TYPES.select { |t| @headers.member?(t) }
71
76
  @selected_type = @m.is_list_message? ? :list : :sender
72
77
 
73
- @body = reply_body_lines(message) + sig_lines
78
+ @body += sig_lines
74
79
  regen_text
75
80
  end
76
81
 
@@ -49,11 +49,13 @@ class ScrollMode < Mode
49
49
  buffer.mark_dirty
50
50
  end
51
51
 
52
- def jump_to_left
53
- buffer.mark_dirty unless @leftcol == 0
54
- @leftcol = 0
52
+ def jump_to_col col
53
+ buffer.mark_dirty unless @leftcol == col
54
+ @leftcol = col
55
55
  end
56
56
 
57
+ def jump_to_left; jump_to_col 0; end
58
+
57
59
  ## set top line to l
58
60
  def jump_to_line l
59
61
  l = l.clamp 0, lines - 1
@@ -1,10 +1,6 @@
1
1
  module Redwood
2
2
 
3
3
  class SearchResultsMode < ThreadIndexMode
4
- register_keymap do |k|
5
- k.add :load_more_threads, "Load #{LOAD_MORE_THREAD_NUM} more threads", 'M'
6
- end
7
-
8
4
  def initialize qobj
9
5
  @qobj = qobj
10
6
  super
@@ -13,7 +9,7 @@ class SearchResultsMode < ThreadIndexMode
13
9
  ## TODO: think about this
14
10
  def is_relevant? m; super; end
15
11
 
16
- def load_more_threads opts={}
12
+ def load_threads opts={}
17
13
  n = opts[:num] || ThreadIndexMode::LOAD_MORE_THREAD_NUM
18
14
  load_n_threads_background n, :qobj => @qobj,
19
15
  :load_killed => true,
@@ -1,18 +1,22 @@
1
1
  module Redwood
2
2
 
3
+ ## subclasses should implement load_threads
4
+
3
5
  class ThreadIndexMode < LineCursorMode
4
6
  DATE_WIDTH = Time::TO_NICE_S_MAX_LEN
5
7
  FROM_WIDTH = 15
6
8
  LOAD_MORE_THREAD_NUM = 20
7
9
 
8
10
  register_keymap do |k|
11
+ k.add :load_threads, "Load #{LOAD_MORE_THREAD_NUM} more threads", 'M'
12
+ k.add :reload, "Discard threads and reload", 'D'
9
13
  k.add :toggle_archived, "Toggle archived status", 'a'
10
14
  k.add :toggle_starred, "Star or unstar all messages in thread", '*'
11
15
  k.add :toggle_new, "Toggle new/read status of all messages in thread", 'N'
12
16
  k.add :edit_labels, "Edit or add labels for a thread", 'l'
13
17
  k.add :edit_message, "Edit message (drafts only)", 'e'
14
18
  k.add :mark_as_spam, "Mark thread as spam", 'S'
15
- k.add :kill, "Kill thread (never to be seen in inbox again)", 'K'
19
+ k.add :kill, "Kill thread (never to be seen in inbox again)", '&'
16
20
  k.add :save, "Save changes now", '$'
17
21
  k.add :jump_to_next_new, "Jump to next new thread", :tab
18
22
  k.add :reply, "Reply to a thread", 'r'
@@ -41,10 +45,15 @@ class ThreadIndexMode < LineCursorMode
41
45
  def lines; @text.length; end
42
46
  def [] i; @text[i]; end
43
47
 
48
+ def reload
49
+ drop_all_threads
50
+ BufferManager.draw_screen
51
+ load_threads :num => buffer.content_height
52
+ end
53
+
44
54
  ## open up a thread view window
45
- def select
46
- this_curpos = curpos
47
- t = @threads[this_curpos]
55
+ def select t=nil
56
+ t ||= @threads[curpos]
48
57
 
49
58
  ## TODO: don't regen text completely
50
59
  Redwood::reporting_thread do
@@ -53,6 +62,10 @@ class ThreadIndexMode < LineCursorMode
53
62
  BufferManager.draw_screen
54
63
  end
55
64
  end
65
+
66
+ def multi_select threads
67
+ threads.each { |t| select t }
68
+ end
56
69
 
57
70
  def handle_starred_update m
58
71
  return unless(t = @ts.thread_for m)
@@ -161,7 +174,7 @@ class ThreadIndexMode < LineCursorMode
161
174
 
162
175
  def multi_mark_as_spam threads
163
176
  threads.each do |t|
164
- t.apply_label :spam
177
+ t.toggle_label :spam
165
178
  hide_thread t
166
179
  end
167
180
  regen_text
@@ -182,15 +195,14 @@ class ThreadIndexMode < LineCursorMode
182
195
 
183
196
  def save
184
197
  threads = @threads + @hidden_threads.keys
185
- mbid = BufferManager.say "Saving threads..."
186
- threads.each_with_index do |t, i|
187
- if i % 5 == 0
188
- BufferManager.say "Saving thread #{i + 1} of #{threads.length}...",
189
- mbid
198
+
199
+ BufferManager.say("Saving threads...") do |say_id|
200
+ threads.each_with_index do |t, i|
201
+ next unless t.dirty?
202
+ BufferManager.say "Saving thread #{i +1} of #{threads.length}...", say_id
203
+ t.save Index
190
204
  end
191
- t.save Index
192
205
  end
193
- BufferManager.clear mbid
194
206
  end
195
207
 
196
208
  def cleanup
@@ -296,7 +308,11 @@ class ThreadIndexMode < LineCursorMode
296
308
  end
297
309
 
298
310
  def status
299
- "line #{curpos + 1} of #{lines} #{dirty? ? '*modified*' : ''}"
311
+ if (l = lines) == 0
312
+ ""
313
+ else
314
+ "line #{curpos + 1} of #{l} #{dirty? ? '*modified*' : ''}"
315
+ end
300
316
  end
301
317
 
302
318
  protected
@@ -25,26 +25,20 @@ class ThreadViewMode < LineCursorMode
25
25
  @state = {}
26
26
  @hidden_labels = hidden_labels
27
27
 
28
- earliest = nil
29
- latest = nil
28
+ earliest, latest = nil, nil
30
29
  latest_date = nil
31
30
  @thread.each do |m, d, p|
32
31
  next unless m
33
32
  earliest ||= m
34
- @state[m] =
35
- if m.has_label?(:unread) && m == earliest
36
- :detailed
37
- elsif m.has_label?(:starred) || m.has_label?(:unread)
38
- :open
39
- else
40
- :closed
41
- end
33
+ @state[m] = initial_state_for m
42
34
  if latest_date.nil? || m.date > latest_date
43
35
  latest_date = m.date
44
36
  latest = m
45
37
  end
46
38
  end
39
+
47
40
  @state[latest] = :open if @state[latest] == :closed
41
+ @state[earliest] = :detailed if earliest.has_label?(:unread) || @thread.size == 1
48
42
 
49
43
  BufferManager.say "Loading message bodies..." do
50
44
  regen_chunks
@@ -106,6 +100,7 @@ class ThreadViewMode < LineCursorMode
106
100
  case chunk
107
101
  when Message, Message::Quote, Message::Signature
108
102
  @state[chunk] = (@state[chunk] != :closed ? :closed : :open)
103
+ cursor_down if @state[chunk] == :closed
109
104
  when Message::Attachment
110
105
  view_attachment chunk
111
106
  end
@@ -164,8 +159,8 @@ class ThreadViewMode < LineCursorMode
164
159
  ## otherwise, to the previous message
165
160
  top = @messages[m][0]
166
161
  if curpos == top
167
- while prevm = @messages[m][2]
168
- break if @state[prevm] == :open
162
+ while(prevm = @messages[m][2])
163
+ break if @state[prevm] != :closed
169
164
  m = prevm
170
165
  end
171
166
  jump_to_message prevm if prevm
@@ -175,9 +170,10 @@ class ThreadViewMode < LineCursorMode
175
170
  end
176
171
 
177
172
  def jump_to_message m
178
- top, bot, prevm, nextm = @messages[m]
173
+ top, bot, prevm, nextm, depth = @messages[m]
179
174
  jump_to_line top unless top >= topline &&
180
175
  top <= botline && bot >= topline && bot <= botline
176
+ jump_to_col depth * 2 # sorry!!!! TODO: make this a constant
181
177
  set_cursor_pos top
182
178
  end
183
179
 
@@ -199,18 +195,19 @@ class ThreadViewMode < LineCursorMode
199
195
  quotes = @chunks[m].select { |c| c.is_a?(Message::Quote) || c.is_a?(Message::Signature) }
200
196
  open, closed = quotes.partition { |c| @state[c] == :open }
201
197
  newstate = open.length > closed.length ? :closed : :open
202
- Redwood::log "#{open.length} opened, #{closed.length} closed, new state is thus #{newstate}"
203
198
  quotes.each { |c| @state[c] = newstate }
204
199
  update
205
200
  end
206
201
  end
207
202
 
208
- ## not sure if this is really necessary but we might as well...
203
+ ## kinda slow for large threads. TODO: fasterify
209
204
  def cleanup
210
- @thread.each do |m, d, p|
211
- if m && m.has_label?(:unread)
212
- m.remove_label :unread
213
- UpdateManager.relay :read, m
205
+ BufferManager.say "Marking messages as read..." do
206
+ @thread.each do |m, d, p|
207
+ if m && m.has_label?(:unread)
208
+ m.remove_label :unread
209
+ UpdateManager.relay :read, m
210
+ end
214
211
  end
215
212
  end
216
213
  @messages = @chunks = @text = nil
@@ -218,6 +215,14 @@ class ThreadViewMode < LineCursorMode
218
215
 
219
216
  private
220
217
 
218
+ def initial_state_for m
219
+ if m.has_label?(:starred) || m.has_label?(:unread)
220
+ :open
221
+ else
222
+ :closed
223
+ end
224
+ end
225
+
221
226
  def update
222
227
  regen_text
223
228
  buffer.mark_dirty if buffer
@@ -236,15 +241,27 @@ private
236
241
 
237
242
  prev_m = nil
238
243
  @thread.each do |m, depth, parent|
244
+ ## we're occasionally called on @threads that have had messages
245
+ ## added to them since initialization. luckily we regen_text on
246
+ ## the entire thread every time the user does anything besides
247
+ ## scrolling (basically), so we can just slap this on here.
248
+ ##
249
+ ## to pick nits, the niceness that i do in the constructor with
250
+ ## 'latest' might not be valid, but i don't see that as a huge
251
+ ## issue.
252
+ @state[m] ||= initial_state_for m if m
253
+
239
254
  text = chunk_to_lines m, @state[m], @text.length, depth, parent
240
255
  (0 ... text.length).each do |i|
241
256
  @chunk_lines[@text.length + i] = m
242
257
  @message_lines[@text.length + i] = m
243
258
  end
244
259
 
245
- @messages[m] = [@text.length, @text.length + text.length, prev_m, nil]
260
+ ## sorry i store all this shit in an array. very, very sorry.
261
+ ## also sorry about the * 2. very, very sorry.
262
+ @messages[m] = [@text.length, @text.length + text.length, prev_m, nil, depth]
246
263
  @messages[prev_m][3] = m if prev_m
247
- prev_m = m
264
+ prev_m = m if m.is_a? Message
248
265
 
249
266
  @text += text
250
267
  if @state[m] != :closed && @chunks.member?(m)
@@ -283,7 +300,6 @@ private
283
300
  [[prefix_widget, widget, imp_widget,
284
301
  [:message_patina_color,
285
302
  "#{m.from ? m.from.mediumname : '?'} to #{m.recipients.map { |l| l.shortname }.join(', ')} #{m.date.to_nice_s} (#{m.date.to_nice_distance_s})"]]]
286
- # (m.to.empty? ? [] : [[[:message_patina_color, prefix + " To: " + m.recipients.map { |x| x.mediumname }.join(", ")]]]) +
287
303
  when :closed
288
304
  [[prefix_widget, widget, imp_widget,
289
305
  [:message_patina_color,
@@ -323,13 +339,12 @@ private
323
339
  when Message
324
340
  message_patina_lines(chunk, state, parent, prefix) +
325
341
  (chunk.is_draft? ? [[[:draft_notification_color, prefix + " >>> This message is a draft. To edit, hit 'e'. <<<"]]] : [])
326
-
327
342
  when Message::Attachment
328
343
  [[[:mime_color, "#{prefix}+ MIME attachment #{chunk.content_type}#{chunk.desc ? ' (' + chunk.desc + ')': ''}"]]]
329
344
  when Message::Text
330
345
  t = chunk.lines
331
- if t.last =~ /^\s*$/
332
- t.pop while t[t.length - 2] =~ /^\s*$/
346
+ if t.last =~ /^\s*$/ && t.length > 1
347
+ t.pop while t[-2] =~ /^\s*$/ # pop until only one file empty line
333
348
  end
334
349
  t.map { |line| [[:none, "#{prefix}#{line}"]] }
335
350
  when Message::Quote