sup 0.3 → 0.4
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.
- data/HACKING +31 -9
- data/History.txt +7 -0
- data/Manifest.txt +2 -0
- data/Rakefile +9 -5
- data/bin/sup +81 -57
- data/bin/sup-config +1 -1
- data/bin/sup-sync +3 -0
- data/bin/sup-tweak-labels +127 -0
- data/doc/TODO +23 -12
- data/lib/sup.rb +13 -11
- data/lib/sup/account.rb +25 -12
- data/lib/sup/buffer.rb +61 -41
- data/lib/sup/colormap.rb +2 -0
- data/lib/sup/contact.rb +28 -18
- data/lib/sup/crypto.rb +86 -31
- data/lib/sup/draft.rb +12 -6
- data/lib/sup/horizontal-selector.rb +47 -0
- data/lib/sup/imap.rb +50 -37
- data/lib/sup/index.rb +76 -13
- data/lib/sup/keymap.rb +27 -8
- data/lib/sup/maildir.rb +1 -1
- data/lib/sup/mbox/loader.rb +1 -1
- data/lib/sup/message-chunks.rb +43 -15
- data/lib/sup/message.rb +67 -31
- data/lib/sup/mode.rb +40 -9
- data/lib/sup/modes/completion-mode.rb +1 -1
- data/lib/sup/modes/compose-mode.rb +3 -3
- data/lib/sup/modes/contact-list-mode.rb +12 -8
- data/lib/sup/modes/edit-message-mode.rb +100 -36
- data/lib/sup/modes/file-browser-mode.rb +1 -0
- data/lib/sup/modes/forward-mode.rb +43 -8
- data/lib/sup/modes/inbox-mode.rb +8 -5
- data/lib/sup/modes/label-search-results-mode.rb +12 -1
- data/lib/sup/modes/line-cursor-mode.rb +4 -7
- data/lib/sup/modes/reply-mode.rb +59 -54
- data/lib/sup/modes/resume-mode.rb +6 -6
- data/lib/sup/modes/scroll-mode.rb +4 -3
- data/lib/sup/modes/search-results-mode.rb +8 -5
- data/lib/sup/modes/text-mode.rb +19 -2
- data/lib/sup/modes/thread-index-mode.rb +109 -40
- data/lib/sup/modes/thread-view-mode.rb +180 -49
- data/lib/sup/person.rb +3 -3
- data/lib/sup/poll.rb +9 -8
- data/lib/sup/rfc2047.rb +7 -1
- data/lib/sup/sent.rb +1 -1
- data/lib/sup/tagger.rb +10 -4
- data/lib/sup/textfield.rb +7 -7
- data/lib/sup/thread.rb +86 -49
- data/lib/sup/update.rb +11 -0
- data/lib/sup/util.rb +74 -34
- data/test/test_message.rb +441 -0
- metadata +136 -117
data/HACKING
CHANGED
@@ -1,20 +1,42 @@
|
|
1
|
-
Running Sup
|
2
|
-
|
1
|
+
Running Sup from your git checkout
|
2
|
+
----------------------------------
|
3
|
+
|
3
4
|
Invoke it like this:
|
4
5
|
|
5
|
-
ruby -I lib -w bin/sup
|
6
|
+
ruby -I lib -w bin/sup
|
6
7
|
|
7
8
|
You'll have to install all gems mentioned in the Rakefile (look for the line
|
8
9
|
setting p.extra_deps). If you're on a Debian or Debian-based system (e.g.
|
9
10
|
Ubuntu), you'll have to make sure you have a complete Ruby installation,
|
10
|
-
especially libssl-ruby.
|
11
|
+
especially libssl-ruby. You will need libruby-devel, gcc, and make installed
|
12
|
+
to build certain gems like Ferret. Gem install does not do a good job of
|
13
|
+
detecting when these things are missing and the build fails.
|
14
|
+
|
15
|
+
Rubygems also is particularly aggressive about picking up libraries from
|
16
|
+
installed gems. If you do have Sup installed as a gem, please examine
|
17
|
+
backtraces to make sure you're loading files from the repository and NOT from
|
18
|
+
the installed gem before submitting any bug reports.
|
11
19
|
|
12
20
|
Coding standards
|
13
21
|
----------------
|
14
22
|
|
15
|
-
- Don't wrap code unless it really benefits from it.
|
16
|
-
|
17
|
-
|
18
|
-
|
23
|
+
- Don't wrap code unless it really benefits from it.
|
24
|
+
- Do wrap comments at 72 characters.
|
25
|
+
- Old lisp-style comment differentiations:
|
26
|
+
# one for comments on the same line as a line of code
|
27
|
+
## two for comments on their own line, except:
|
28
|
+
### three for comments that demarcate large sections of code (rare)
|
19
29
|
- Use {} for one-liner blocks and do/end for multi-line blocks.
|
20
|
-
|
30
|
+
- I like poetry mode. Don't use parentheses unless you must.
|
31
|
+
- The one exception to poetry mode is if-statements that have an assignment in
|
32
|
+
the condition. To make it clear this is not a comparison, surround the
|
33
|
+
condition by parentheses. E.g.:
|
34
|
+
if a == b if(a = some.computation)
|
35
|
+
... BUT ... something with a
|
36
|
+
end end
|
37
|
+
- and/or versus ||/&&. In Ruby, "and" and "or" bind very loosely---even
|
38
|
+
more loosely than function application. This makes them ideal for
|
39
|
+
end-of-line short-circuit control in poetry mode. So, use || and &&
|
40
|
+
for ordinary logical comparisons, and "and" and "or" for end-of-line
|
41
|
+
flow control. E.g.:
|
42
|
+
x = a || b or raise "neither is true"
|
data/History.txt
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
== 0.4 / 2008-01-23
|
2
|
+
* GPG support for signing and encrypting outgoing mail
|
3
|
+
* New hooks: mime attachment, attribution line
|
4
|
+
* Improved local charset detection using gettext library
|
5
|
+
* Better quoted region detection
|
6
|
+
* Many bugfixes and UI improvements
|
7
|
+
|
1
8
|
== 0.3 / 2007-10-29
|
2
9
|
* In-buffer search (finally!)
|
3
10
|
* Subscribe to/unsubscribe from mailing list commands.
|
data/Manifest.txt
CHANGED
@@ -11,6 +11,7 @@ bin/sup-dump
|
|
11
11
|
bin/sup-recover-sources
|
12
12
|
bin/sup-sync
|
13
13
|
bin/sup-sync-back
|
14
|
+
bin/sup-tweak-labels
|
14
15
|
doc/FAQ.txt
|
15
16
|
doc/Hooks.txt
|
16
17
|
doc/NewUserGuide.txt
|
@@ -24,6 +25,7 @@ lib/sup/contact.rb
|
|
24
25
|
lib/sup/crypto.rb
|
25
26
|
lib/sup/draft.rb
|
26
27
|
lib/sup/hook.rb
|
28
|
+
lib/sup/horizontal-selector.rb
|
27
29
|
lib/sup/imap.rb
|
28
30
|
lib/sup/index.rb
|
29
31
|
lib/sup/keymap.rb
|
data/Rakefile
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# -*- ruby -*-
|
2
|
-
|
3
1
|
require 'rubygems'
|
4
2
|
require 'hoe'
|
5
3
|
$:.unshift 'lib' # force loading from ./lib/ if it exists
|
@@ -9,7 +7,12 @@ class Hoe
|
|
9
7
|
def extra_deps; @extra_deps.reject { |x| Array(x).first == "hoe" } end
|
10
8
|
end # thanks to "Mike H"
|
11
9
|
|
12
|
-
|
10
|
+
## allow people who use development versions by running "rake gem"
|
11
|
+
## and installing the resulting gem it to be able to do this. (gem
|
12
|
+
## versions must be in dotted-digit notation only).
|
13
|
+
version = Redwood::VERSION == "git" ? "999" : Redwood::VERSION
|
14
|
+
|
15
|
+
Hoe.new('sup', version) do |p|
|
13
16
|
p.rubyforge_name = 'sup'
|
14
17
|
p.author = "William Morgan"
|
15
18
|
p.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.'
|
@@ -17,7 +20,7 @@ Hoe.new('sup', Redwood::VERSION) do |p|
|
|
17
20
|
p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[2].gsub(/^\s+/, "")
|
18
21
|
p.changes = p.paragraphs_of('History.txt', 0..0).join("\n\n")
|
19
22
|
p.email = "wmorgan-sup@masanjin.net"
|
20
|
-
p.extra_deps = [['ferret', '>= 0.10.13'], ['ncurses', '>= 0.9.1'], ['rmail', '>= 0.17'], 'highline', 'net-ssh', ['trollop', '>= 1.7'], 'lockfile', 'mime-types']
|
23
|
+
p.extra_deps = [['ferret', '>= 0.10.13'], ['ncurses', '>= 0.9.1'], ['rmail', '>= 0.17'], 'highline', 'net-ssh', ['trollop', '>= 1.7'], 'lockfile', 'mime-types', 'gettext']
|
21
24
|
end
|
22
25
|
|
23
26
|
rule 'ss?.png' => 'ss?-small.png' do |t|
|
@@ -45,4 +48,5 @@ task :upload_webpage_images => (SCREENSHOTS + SCREENSHOTS_SMALL) do |t|
|
|
45
48
|
sh "scp -C #{t.prerequisites * ' '} wmorgan@rubyforge.org:/var/www/gforge-projects/sup/"
|
46
49
|
end
|
47
50
|
|
48
|
-
# vim: syntax=
|
51
|
+
# vim: syntax=ruby
|
52
|
+
# -*- ruby -*-
|
data/bin/sup
CHANGED
@@ -7,6 +7,20 @@ require 'fileutils'
|
|
7
7
|
require 'trollop'
|
8
8
|
require "sup"
|
9
9
|
|
10
|
+
BIN_VERSION = "0.4"
|
11
|
+
|
12
|
+
unless Redwood::VERSION == BIN_VERSION
|
13
|
+
$stderr.puts <<EOS
|
14
|
+
|
15
|
+
Error: version mismatch!
|
16
|
+
The sup executable is at version #{BIN_VERSION.inspect}.
|
17
|
+
The sup libraries are at version #{Redwood::VERSION.inspect}.
|
18
|
+
|
19
|
+
Is your development environment conflicting with rubygems?
|
20
|
+
EOS
|
21
|
+
exit(-1)
|
22
|
+
end
|
23
|
+
|
10
24
|
$exceptions = []
|
11
25
|
$opts = Trollop::options do
|
12
26
|
version "sup v#{Redwood::VERSION}"
|
@@ -20,6 +34,7 @@ Options are:
|
|
20
34
|
EOS
|
21
35
|
opt :list_hooks, "List all hooks and descriptions thereof, and quit."
|
22
36
|
opt :no_threads, "Turn of threading. Helps with debugging. (Necessarily disables background polling for new messages.)"
|
37
|
+
opt :no_initial_poll, "Don't poll for new messages when starting."
|
23
38
|
opt :search, "Search for threads ", :type => String
|
24
39
|
end
|
25
40
|
|
@@ -154,9 +169,8 @@ begin
|
|
154
169
|
Ncurses::A_BOLD
|
155
170
|
c.add :completion_character_color, Ncurses::COLOR_WHITE,
|
156
171
|
Ncurses::COLOR_BLACK, Ncurses::A_BOLD
|
157
|
-
c.add :
|
158
|
-
c.add :
|
159
|
-
c.add :reply_mode_label_color, Ncurses::COLOR_CYAN, Ncurses::COLOR_BLACK
|
172
|
+
c.add :horizontal_selector_selected_color, Ncurses::COLOR_YELLOW, Ncurses::COLOR_BLACK, Ncurses::A_BOLD
|
173
|
+
c.add :horizontal_selector_unselected_color, Ncurses::COLOR_CYAN, Ncurses::COLOR_BLACK
|
160
174
|
c.add :search_highlight_color, Ncurses::COLOR_BLACK, Ncurses::COLOR_YELLOW, Ncurses::A_BOLD, :highlight => :search_highlight_color
|
161
175
|
end
|
162
176
|
|
@@ -180,9 +194,9 @@ begin
|
|
180
194
|
Redwood::log "fatal error loading from #{s}: #{e.message}"
|
181
195
|
end
|
182
196
|
end
|
183
|
-
end
|
197
|
+
end unless $opts[:no_initial_poll]
|
184
198
|
|
185
|
-
imode.load_threads :num => ibuf.content_height, :when_done => lambda { reporting_thread("poll after loading inbox") { sleep 1; PollManager.poll } unless $opts[:no_threads] }
|
199
|
+
imode.load_threads :num => ibuf.content_height, :when_done => lambda { reporting_thread("poll after loading inbox") { sleep 1; PollManager.poll } unless $opts[:no_threads] || $opts[:no_initial_poll] }
|
186
200
|
|
187
201
|
unless $opts[:no_threads]
|
188
202
|
PollManager.start
|
@@ -199,67 +213,77 @@ begin
|
|
199
213
|
next unless c
|
200
214
|
bm.erase_flash
|
201
215
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
curmode = bm.focus_buf.mode
|
209
|
-
bm.spawn_unless_exists("<help for #{curmode.name}>") { HelpMode.new curmode, global_keymap }
|
210
|
-
when :roll_buffers
|
211
|
-
bm.roll_buffers
|
212
|
-
when :roll_buffers_backwards
|
213
|
-
bm.roll_buffers_backwards
|
214
|
-
when :kill_buffer
|
215
|
-
bm.kill_buffer_safely bm.focus_buf
|
216
|
-
when :list_buffers
|
217
|
-
bm.spawn_unless_exists("Buffer List") { BufferListMode.new }
|
218
|
-
when :list_contacts
|
219
|
-
b, new = bm.spawn_unless_exists("Contact List") { ContactListMode.new }
|
220
|
-
b.mode.load_in_background if new
|
221
|
-
when :search
|
222
|
-
query = BufferManager.ask :search, "search all messages: "
|
223
|
-
next unless query && query !~ /^\s*$/
|
224
|
-
SearchResultsMode.spawn_from_query query
|
225
|
-
when :list_labels
|
226
|
-
labels = LabelManager.listable_labels.map { |l| LabelManager.string_for l }
|
227
|
-
user_label = bm.ask_with_completions :label, "Show threads with label (enter for listing): ", labels
|
228
|
-
unless user_label.nil?
|
229
|
-
if user_label.empty?
|
230
|
-
bm.spawn_unless_exists("Label list") { LabelListMode.new } if user_label && user_label.empty?
|
231
|
-
else
|
232
|
-
LabelSearchResultsMode.spawn_nicely user_label
|
233
|
-
end
|
216
|
+
action =
|
217
|
+
begin
|
218
|
+
if bm.handle_input c
|
219
|
+
:nothing
|
220
|
+
else
|
221
|
+
bm.resolve_input_with_keymap c, global_keymap
|
234
222
|
end
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
223
|
+
rescue InputSequenceAborted
|
224
|
+
:nothing
|
225
|
+
end
|
226
|
+
|
227
|
+
case action
|
228
|
+
when :quit
|
229
|
+
break if bm.kill_all_buffers_safely
|
230
|
+
when :help
|
231
|
+
curmode = bm.focus_buf.mode
|
232
|
+
bm.spawn_unless_exists("<help for #{curmode.name}>") { HelpMode.new curmode, global_keymap }
|
233
|
+
when :roll_buffers
|
234
|
+
bm.roll_buffers
|
235
|
+
when :roll_buffers_backwards
|
236
|
+
bm.roll_buffers_backwards
|
237
|
+
when :kill_buffer
|
238
|
+
bm.kill_buffer_safely bm.focus_buf
|
239
|
+
when :list_buffers
|
240
|
+
bm.spawn_unless_exists("Buffer List") { BufferListMode.new }
|
241
|
+
when :list_contacts
|
242
|
+
b, new = bm.spawn_unless_exists("Contact List") { ContactListMode.new }
|
243
|
+
b.mode.load_in_background if new
|
244
|
+
when :search
|
245
|
+
query = BufferManager.ask :search, "search all messages: "
|
246
|
+
next unless query && query !~ /^\s*$/
|
247
|
+
SearchResultsMode.spawn_from_query query
|
248
|
+
when :list_labels
|
249
|
+
labels = LabelManager.listable_labels.map { |l| LabelManager.string_for l }
|
250
|
+
user_label = bm.ask_with_completions :label, "Show threads with label (enter for listing): ", labels
|
251
|
+
unless user_label.nil?
|
252
|
+
if user_label.empty?
|
253
|
+
bm.spawn_unless_exists("Label list") { LabelListMode.new } if user_label && user_label.empty?
|
249
254
|
else
|
250
|
-
|
251
|
-
b.mode.load_threads :num => b.content_height if new
|
255
|
+
LabelSearchResultsMode.spawn_nicely user_label
|
252
256
|
end
|
253
|
-
|
254
|
-
|
255
|
-
|
257
|
+
end
|
258
|
+
when :compose
|
259
|
+
ComposeMode.spawn_nicely
|
260
|
+
when :poll
|
261
|
+
reporting_thread("user-invoked poll") { PollManager.poll }
|
262
|
+
when :recall_draft
|
263
|
+
case Index.num_results_for :label => :draft
|
264
|
+
when 0
|
265
|
+
bm.flash "No draft messages."
|
266
|
+
when 1
|
267
|
+
m = nil
|
268
|
+
Index.each_id_by_date(:label => :draft) { |mid, builder| m = builder.call }
|
269
|
+
r = ResumeMode.new(m)
|
270
|
+
BufferManager.spawn "Edit message", r
|
271
|
+
r.edit_message
|
256
272
|
else
|
257
|
-
|
273
|
+
b, new = BufferManager.spawn_unless_exists("All drafts") { LabelSearchResultsMode.new [:draft] }
|
274
|
+
b.mode.load_threads :num => b.content_height if new
|
258
275
|
end
|
276
|
+
when :nothing, InputSequenceAborted
|
277
|
+
when :redraw
|
278
|
+
bm.completely_redraw_screen
|
279
|
+
else
|
280
|
+
bm.flash "Unknown keypress '#{c.to_character}' for #{bm.focus_buf.mode.name}."
|
259
281
|
end
|
260
282
|
|
261
283
|
bm.draw_screen
|
262
284
|
end
|
285
|
+
|
286
|
+
bm.kill_all_buffers if SuicideManager.die?
|
263
287
|
rescue Exception => e
|
264
288
|
$exceptions << [e, "main"]
|
265
289
|
ensure
|
data/bin/sup-config
CHANGED
data/bin/sup-sync
CHANGED
@@ -0,0 +1,127 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'trollop'
|
5
|
+
require "sup"
|
6
|
+
|
7
|
+
class Float
|
8
|
+
def to_s; sprintf '%.2f', self; end
|
9
|
+
def to_time_s
|
10
|
+
infinite? ? "unknown" : super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Numeric
|
15
|
+
def to_time_s
|
16
|
+
i = to_i
|
17
|
+
sprintf "%d:%02d:%02d", i / 3600, (i / 60) % 60, i % 60
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def time
|
22
|
+
startt = Time.now
|
23
|
+
yield
|
24
|
+
Time.now - startt
|
25
|
+
end
|
26
|
+
|
27
|
+
opts = Trollop::options do
|
28
|
+
version "sup-tweak-labels (sup #{Redwood::VERSION})"
|
29
|
+
banner <<EOS
|
30
|
+
Batch modification of message state for messages already in the index.
|
31
|
+
|
32
|
+
Usage:
|
33
|
+
sup-tweak-labels [options] <source>*
|
34
|
+
|
35
|
+
where <source>* is zero or more source URIs. Supported source URI schemes can
|
36
|
+
be seen by running "sup-add --help".
|
37
|
+
|
38
|
+
Options:
|
39
|
+
EOS
|
40
|
+
opt :add, "One or more labels (comma-separated) to add to every message from the specified sources", :type => String
|
41
|
+
opt :remove, "One or more labels (comma-separated) to remove from every message from the specified sources, if those labels are present", :type => String
|
42
|
+
|
43
|
+
text <<EOS
|
44
|
+
|
45
|
+
Other options:
|
46
|
+
EOS
|
47
|
+
opt :verbose, "Print message ids as they're processed."
|
48
|
+
opt :all_sources, "Scan over all sources.", :short => :none
|
49
|
+
opt :dry_run, "Don't actually modify the index. Probably only useful with --verbose.", :short => "-n"
|
50
|
+
opt :version, "Show version information", :short => :none
|
51
|
+
end
|
52
|
+
|
53
|
+
add_labels = (opts[:add] || "").split(",").map { |l| l.intern }.uniq
|
54
|
+
remove_labels = (opts[:remove] || "").split(",").map { |l| l.intern }.uniq
|
55
|
+
|
56
|
+
Trollop::die "nothing to do: no labels to add or remove" if add_labels.empty? && remove_labels.empty?
|
57
|
+
|
58
|
+
Redwood::start
|
59
|
+
begin
|
60
|
+
index = Redwood::Index.new
|
61
|
+
index.load
|
62
|
+
|
63
|
+
source_ids =
|
64
|
+
if opts[:all_sources]
|
65
|
+
index.sources
|
66
|
+
else
|
67
|
+
ARGV.map do |uri|
|
68
|
+
index.source_for uri or Trollop::die "Unknown source: #{uri}. Did you add it with sup-add first?"
|
69
|
+
end
|
70
|
+
end.map { |s| s.id }
|
71
|
+
Trollop::die "nothing to do: no sources" if source_ids.empty?
|
72
|
+
|
73
|
+
query = "+(" + source_ids.map { |id| "source_id:#{id}" }.join(" ") + ")"
|
74
|
+
if add_labels.empty?
|
75
|
+
## if all we're doing is removing labels, we can further restrict the
|
76
|
+
## query to only messages with those labels
|
77
|
+
query += " +(" + remove_labels.map { |l| "label:#{l}" }.join(" ") + ")"
|
78
|
+
end
|
79
|
+
|
80
|
+
results = index.ferret.search query, :limit => :all
|
81
|
+
num_total = results.total_hits
|
82
|
+
|
83
|
+
$stderr.puts "Found #{num_total} documents across #{source_ids.length} sources. Scanning..."
|
84
|
+
|
85
|
+
num_changed = num_scanned = 0
|
86
|
+
last_info_time = start_time = Time.now
|
87
|
+
results.hits.each do |hit|
|
88
|
+
num_scanned += 1
|
89
|
+
id = hit.doc
|
90
|
+
|
91
|
+
m = index.build_message id
|
92
|
+
old_labels = m.labels.clone
|
93
|
+
|
94
|
+
m.labels += add_labels
|
95
|
+
m.labels -= remove_labels
|
96
|
+
m.labels = m.labels.uniq
|
97
|
+
|
98
|
+
unless m.labels.sort_by { |s| s.to_s } == old_labels.sort_by { |s| s.to_s }
|
99
|
+
num_changed += 1
|
100
|
+
puts "#{m.id}: {#{old_labels.join ','}} => {#{m.labels.join ','}}" if opts[:verbose]
|
101
|
+
index.sync_message m unless opts[:dry_run]
|
102
|
+
end
|
103
|
+
|
104
|
+
if Time.now - last_info_time > 60
|
105
|
+
last_info_time = Time.now
|
106
|
+
elapsed = last_info_time - start_time
|
107
|
+
pctdone = 100.0 * num_scanned.to_f / num_total.to_f
|
108
|
+
remaining = (100.0 - pctdone) * (elapsed.to_f / pctdone)
|
109
|
+
$stderr.puts "## #{num_scanned} (#{pctdone}%) read; #{elapsed.to_time_s} elapsed; #{remaining.to_time_s} remaining"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
$stderr.puts "Scanned #{num_scanned} / #{num_total} messages and changed #{num_changed}."
|
113
|
+
|
114
|
+
unless num_changed == 0
|
115
|
+
$stderr.puts "Optimizing index..."
|
116
|
+
index.ferret.optimize unless opts[:dry_run]
|
117
|
+
end
|
118
|
+
|
119
|
+
rescue Exception => e
|
120
|
+
File.open("sup-exception-log.txt", "w") { |f| f.puts e.backtrace }
|
121
|
+
raise
|
122
|
+
ensure
|
123
|
+
index.save
|
124
|
+
Redwood::finish
|
125
|
+
index.unlock
|
126
|
+
end
|
127
|
+
|
data/doc/TODO
CHANGED
@@ -1,20 +1,10 @@
|
|
1
|
-
for 0.
|
1
|
+
for 0.5
|
2
2
|
-------
|
3
3
|
_ mark thread as unread should remember the unread messages and mark
|
4
4
|
only them as unread, just like gmail
|
5
|
-
_ mark thread as unread should have a version within thread-view-mode
|
6
|
-
which then also closes the buffer
|
7
5
|
_ bugfix: time zone parsing broken?
|
8
|
-
_ forwards optionally include attachments
|
9
|
-
_ attach messages
|
10
|
-
_ flesh out gpg integration: sign & encrypt outgoing
|
11
|
-
_ mbox: don't keep filehandles open, and protect all reads with
|
12
|
-
dotlockfile
|
13
|
-
_ imap: share connection to the same server.
|
14
|
-
_ bugfix: screwing with the headers when editing causes a crash
|
15
6
|
_ need a better way to force an address to a particular name,
|
16
7
|
for things like evite addresses
|
17
|
-
_ pressing A in thread-view-mode should jump to next message
|
18
8
|
_ imap "add all folders on this server" option in sup-add
|
19
9
|
_ for new message flashes, add new message counts until keypress
|
20
10
|
_ bugfix: missing sources should be handled better
|
@@ -22,13 +12,29 @@ _ search results: highlight relevant snippets and open to relevant
|
|
22
12
|
portion of thread
|
23
13
|
_ have "notes" (treated as emails to oneself, never sent) as
|
24
14
|
first-class objects.
|
15
|
+
_ make use of the username in URIs, and move account passwords to
|
16
|
+
a different file from sources.yaml. heck, allow encryption of that
|
17
|
+
file.
|
18
|
+
|
19
|
+
for 0.4
|
20
|
+
-------
|
21
|
+
_ bugfix in drafts: the entire thread is currently discarded, rather
|
22
|
+
than just the one message. (need to distinguish per-message and
|
23
|
+
per-thread deletion in the update messages.)
|
24
|
+
_ bugfix in completion: capitalization for contact names
|
25
|
+
_ imap: cache headers
|
26
|
+
_ imap: share connection to the same server.
|
27
|
+
_ dispatch-and-kill version of mark thread as unread in thread-view-mode
|
28
|
+
_ forward for one or more tagged messages should attach them
|
29
|
+
_ mbox: don't keep filehandles open, and protect all reads with dotlockfile
|
30
|
+
_ bugfix: screwing with the headers when editing causes a crash
|
25
31
|
|
26
32
|
future
|
27
33
|
------
|
28
34
|
_ ldbd support
|
29
35
|
_ don't use a people.txt; store email addresses directly in the index. too many
|
30
36
|
problems with email addresses that occur with multiple names.
|
31
|
-
_ infix match instead of prefix match for tab completion
|
37
|
+
_ infix match instead of prefix match for tab completion
|
32
38
|
_ fix killed threads contributing to unread message count problem (prob. need
|
33
39
|
to maintain all killed message ids and our own unread message count for
|
34
40
|
inbox).
|
@@ -77,6 +83,11 @@ x gmail support: obsoleted by imap
|
|
77
83
|
|
78
84
|
done
|
79
85
|
----
|
86
|
+
x bugfix: threading broken
|
87
|
+
x bugfix: thread ordering in thread-index-mode sometimes jumps around with 'M'
|
88
|
+
x forwards optionally include attachments
|
89
|
+
x flesh out gpg integration: sign & encrypt outgoing
|
90
|
+
x pressing A in thread-view-mode should jump to next message
|
80
91
|
x multi-thread dump upon crash
|
81
92
|
x hook manager caches values of any proc "variables"
|
82
93
|
x bugfix: remove delay on startup if a usual imap source exists
|