commit 0f103b2e4058d1c3c368c13d4c80242caae04588
parent 568b7eec9483358ae0e10ee03ffa41c3d542bd47
Author: William Morgan <wmorgan-sup@masanjin.net>
Date: Wed, 20 Jan 2010 11:21:08 -0500
Merge branch 'insta-save'
Conflicts:
lib/sup/modes/thread-view-mode.rb
Diffstat:
7 files changed, 90 insertions(+), 36 deletions(-)
diff --git a/bin/sup b/bin/sup
@@ -167,6 +167,7 @@ end
begin
Redwood::start
Index.load
+ Index.start_sync_worker unless $opts[:no_threads]
$die = false
trap("TERM") { |x| $die = true }
@@ -359,6 +360,7 @@ ensure
HookManager.run "shutdown"
+ Index.stop_sync_worker
Redwood::finish
stop_cursing
Redwood::Logger.remove_all_sinks!
diff --git a/lib/sup/index.rb b/lib/sup/index.rb
@@ -30,6 +30,8 @@ class BaseIndex
def initialize dir=BASE_DIR
@dir = dir
@lock = Lockfile.new lockfile, :retries => 0, :max_age => nil
+ @sync_worker = nil
+ @sync_queue = Queue.new
end
def lockfile; File.join @dir, "lock" end
@@ -174,6 +176,37 @@ class BaseIndex
def parse_query s
unimplemented
end
+
+ def save_thread t
+ t.each_dirty_message do |m|
+ if @sync_worker
+ @sync_queue << m
+ else
+ update_message_state m
+ end
+ m.clear_dirty
+ end
+ end
+
+ def start_sync_worker
+ @sync_worker = Redwood::reporting_thread('index sync') { run_sync_worker }
+ end
+
+ def stop_sync_worker
+ return unless worker = @sync_worker
+ @sync_worker = nil
+ @sync_queue << :die
+ worker.join
+ end
+
+ def run_sync_worker
+ while m = @sync_queue.deq
+ return if m == :die
+ update_message_state m
+ # Necessary to keep Xapian calls from lagging the UI too much.
+ sleep 0.03
+ end
+ end
end
## just to make the backtraces even more insane, here we engage in yet more
diff --git a/lib/sup/message.rb b/lib/sup/message.rb
@@ -189,11 +189,8 @@ class Message
## don't tempt me.
def sanitize_message_id mid; mid.gsub(/(\s|[^\000-\177])+/, "")[0..254] end
- def save_state index
- return unless @dirty
- index.update_message_state self
+ def clear_dirty
@dirty = false
- true
end
def has_label? t; @labels.member? t; end
diff --git a/lib/sup/modes/inbox-mode.rb b/lib/sup/modes/inbox-mode.rb
@@ -37,11 +37,13 @@ class InboxMode < ThreadIndexMode
UndoManager.register "archiving thread" do
thread.apply_label :inbox
add_or_unhide thread.first
+ Index.save_thread thread
end
cursor_thread.remove_label :inbox
hide_thread cursor_thread
regen_text
+ Index.save_thread thread
end
def multi_archive threads
@@ -49,6 +51,7 @@ class InboxMode < ThreadIndexMode
threads.map do |t|
t.apply_label :inbox
add_or_unhide t.first
+ Index.save_thread t
end
regen_text
end
@@ -58,6 +61,7 @@ class InboxMode < ThreadIndexMode
hide_thread t
end
regen_text
+ threads.each { |t| Index.save_thread t }
end
def read_and_archive
@@ -69,12 +73,14 @@ class InboxMode < ThreadIndexMode
thread.apply_label :inbox
thread.apply_label :unread if was_unread
add_or_unhide thread.first
+ Index.save_thread thread
end
cursor_thread.remove_label :unread
cursor_thread.remove_label :inbox
hide_thread cursor_thread
regen_text
+ Index.save_thread thread
end
def multi_read_and_archive threads
@@ -91,10 +97,12 @@ class InboxMode < ThreadIndexMode
threads.zip(old_labels).each do |t, l|
t.labels = l
add_or_unhide t.first
+ Index.save_thread t
end
regen_text
end
+ threads.each { |t| Index.save_thread t }
end
def handle_unarchived_update sender, m
diff --git a/lib/sup/modes/thread-index-mode.rb b/lib/sup/modes/thread-index-mode.rb
@@ -37,7 +37,7 @@ EOS
k.add :toggle_spam, "Mark/unmark thread as spam", 'S'
k.add :toggle_deleted, "Delete/undelete thread", 'd'
k.add :kill, "Kill thread (never to be seen in inbox again)", '&'
- k.add :save, "Save changes now", '$'
+ k.add :flush_index, "Flush all changes now", '$'
k.add :jump_to_next_new, "Jump to next new thread", :tab
k.add :reply, "Reply to latest message in a thread", 'r'
k.add :reply_all, "Reply to all participants of the latest message in a thread", 'G'
@@ -266,15 +266,18 @@ EOS
def toggle_starred
t = cursor_thread or return
undo = actually_toggle_starred t
- UndoManager.register "toggling thread starred status", undo
+ UndoManager.register "toggling thread starred status", undo, lambda { Index.save_thread t }
update_text_for_line curpos
cursor_down
+ Index.save_thread t
end
def multi_toggle_starred threads
UndoManager.register "toggling #{threads.size.pluralize 'thread'} starred status",
- threads.map { |t| actually_toggle_starred t }
+ threads.map { |t| actually_toggle_starred t },
+ lambda { threads.each { |t| Index.save_thread t } }
regen_text
+ threads.each { |t| Index.save_thread t }
end
## returns an undo lambda
@@ -350,14 +353,18 @@ EOS
def toggle_archived
t = cursor_thread or return
undo = actually_toggle_archived t
- UndoManager.register "deleting/undeleting thread #{t.first.id}", undo, lambda { update_text_for_line curpos }
+ UndoManager.register "deleting/undeleting thread #{t.first.id}", undo, lambda { update_text_for_line curpos },
+ lambda { Index.save_thread t }
update_text_for_line curpos
+ Index.save_thread t
end
def multi_toggle_archived threads
undos = threads.map { |t| actually_toggle_archived t }
- UndoManager.register "deleting/undeleting #{threads.size.pluralize 'thread'}", undos, lambda { regen_text }
+ UndoManager.register "deleting/undeleting #{threads.size.pluralize 'thread'}", undos, lambda { regen_text },
+ lambda { threads.each { |t| Index.save_thread t } }
regen_text
+ threads.each { |t| Index.save_thread t }
end
def toggle_new
@@ -365,11 +372,13 @@ EOS
t.toggle_label :unread
update_text_for_line curpos
cursor_down
+ Index.save_thread t
end
def multi_toggle_new threads
threads.each { |t| t.toggle_label :unread }
regen_text
+ threads.each { |t| Index.save_thread t }
end
def multi_toggle_tagged threads
@@ -385,6 +394,7 @@ EOS
def multi_join_threads threads
@ts.join_threads threads or return
+ threads.each { |t| Index.save_thread t }
@tags.drop_all_tags # otherwise we have tag pointers to invalid threads!
update
end
@@ -419,8 +429,9 @@ EOS
undos = threads.map { |t| actually_toggle_spammed t }
threads.each { |t| HookManager.run("mark-as-spam", :thread => t) }
UndoManager.register "marking/unmarking #{threads.size.pluralize 'thread'} as spam",
- undos, lambda { regen_text }
+ undos, lambda { regen_text }, lambda { threads.each { |t| Index.save_thread t } }
regen_text
+ threads.each { |t| Index.save_thread t }
end
def toggle_deleted
@@ -432,8 +443,9 @@ EOS
def multi_toggle_deleted threads
undos = threads.map { |t| actually_toggle_deleted t }
UndoManager.register "deleting/undeleting #{threads.size.pluralize 'thread'}",
- undos, lambda { regen_text }
+ undos, lambda { regen_text }, lambda { threads.each { |t| Index.save_thread t } }
regen_text
+ threads.each { |t| Index.save_thread t }
end
def kill
@@ -441,12 +453,19 @@ EOS
multi_kill [t]
end
+ def flush_index
+ @flush_id = BufferManager.say "Flushing index..."
+ Index.save_index
+ BufferManager.clear @flush_id
+ end
+
## m-m-m-m-MULTI-KILL
def multi_kill threads
UndoManager.register "killing #{threads.size.pluralize 'thread'}" do
threads.each do |t|
t.remove_label :killed
add_or_unhide t.first
+ Index.save_thread t
end
regen_text
end
@@ -458,29 +477,7 @@ EOS
regen_text
BufferManager.flash "#{threads.size.pluralize 'thread'} killed."
- end
-
- def save background=true
- if background
- Redwood::reporting_thread("saving thread") { actually_save }
- else
- actually_save
- end
- end
-
- def actually_save
- @save_thread_mutex.synchronize do
- BufferManager.say("Saving contacts...") { ContactManager.instance.save }
- dirty_threads = @mutex.synchronize { (@threads + @hidden_threads.keys).select { |t| t.dirty? } }
- next if dirty_threads.empty?
-
- BufferManager.say("Saving threads...") do |say_id|
- dirty_threads.each_with_index do |t, i|
- BufferManager.say "Saving modified thread #{i + 1} of #{dirty_threads.length}...", say_id
- t.save_state Index
- end
- end
- end
+ threads.each { |t| Index.save_thread t }
end
def cleanup
@@ -492,7 +489,8 @@ EOS
sleep 0.1 # TODO: necessary?
BufferManager.erase_flash
end
- save false
+ dirty_threads = @mutex.synchronize { (@threads + @hidden_threads.keys).select { |t| t.dirty? } }
+ fail "dirty threads remain" unless dirty_threads.empty?
super
end
@@ -543,9 +541,11 @@ EOS
thread.labels = old_labels
update_text_for_line pos
UpdateManager.relay self, :labeled, thread.first
+ Index.save_thread thread
end
UpdateManager.relay self, :labeled, thread.first
+ Index.save_thread thread
end
def multi_edit_labels threads
@@ -579,9 +579,12 @@ EOS
threads.zip(old_labels).map do |t, old_labels|
t.labels = old_labels
UpdateManager.relay self, :labeled, t.first
+ Index.save_thread t
end
regen_text
end
+
+ threads.each { |t| Index.save_thread t }
end
def reply type_arg=nil
diff --git a/lib/sup/modes/thread-view-mode.rb b/lib/sup/modes/thread-view-mode.rb
@@ -135,6 +135,7 @@ EOS
@layout[earliest].state = :detailed if earliest.has_label?(:unread) || @thread.size == 1
@thread.remove_label :unread
+ Index.save_thread @thread
end
def toggle_wrap
@@ -275,8 +276,10 @@ EOS
new_labels.each { |l| LabelManager << l }
update
UpdateManager.relay self, :labeled, @thread.first
+ Index.save_thread @thread
UndoManager.register "labeling thread" do
@thread.labels = old_labels
+ Index.save_thread @thread
UpdateManager.relay self, :labeled, @thread.first
end
end
@@ -301,6 +304,7 @@ EOS
## star to the display
update
UpdateManager.relay self, :single_message_labeled, m
+ Index.save_thread @thread
end
## called when someone presses enter when the cursor is highlighting
@@ -521,8 +525,10 @@ EOS
dispatch op do
@thread.remove_label :inbox
UpdateManager.relay self, :archived, @thread.first
+ Index.save_thread @thread
UndoManager.register "archiving 1 thread" do
@thread.apply_label :inbox
+ Index.save_thread @thread
UpdateManager.relay self, :unarchived, @thread.first
end
end
@@ -532,8 +538,10 @@ EOS
dispatch op do
@thread.apply_label :spam
UpdateManager.relay self, :spammed, @thread.first
+ Index.save_thread @thread
UndoManager.register "marking 1 thread as spam" do
@thread.remove_label :spam
+ Index.save_thread @thread
UpdateManager.relay self, :unspammed, @thread.first
end
end
@@ -543,8 +551,10 @@ EOS
dispatch op do
@thread.apply_label :deleted
UpdateManager.relay self, :deleted, @thread.first
+ Index.save_thread @thread
UndoManager.register "deleting 1 thread" do
@thread.remove_label :deleted
+ Index.save_thread @thread
UpdateManager.relay self, :undeleted, @thread.first
end
end
@@ -554,6 +564,7 @@ EOS
dispatch op do
@thread.apply_label :unread
UpdateManager.relay self, :unread, @thread.first
+ Index.save_thread @thread
end
end
diff --git a/lib/sup/thread.rb b/lib/sup/thread.rb
@@ -112,7 +112,7 @@ class Thread
def set_labels l; each { |m, *o| m && m.labels = l }; end
def has_label? t; any? { |m, *o| m && m.has_label?(t) }; end
- def save_state index; each { |m, *o| m && m.save_state(index) }; end
+ def each_dirty_message; each { |m, *o| m && m.dirty? && yield(m) }; end
def direct_participants
map { |m, *o| [m.from] + m.to if m }.flatten.compact.uniq