]> git.cworth.org Git - sup/blobdiff - lib/sup/thread.rb
Merge branch 'master' into next
[sup] / lib / sup / thread.rb
index 6519757c467295095c47cdcd45b3fd5332f6e5f2..83f0db71aa4a08adfda53bd469d234ba684ac338 100644 (file)
@@ -47,8 +47,8 @@ class Thread
 
   ## unused
   def dump f=$stdout
-    f.puts "=== start thread #{self} with #{@containers.length} trees ==="
-    @containers.each { |c| c.dump_recursive f }
+    f.puts "=== start thread with #{@containers.length} trees ==="
+    @containers.each { |c| c.dump_recursive f; f.puts }
     f.puts "=== end thread ==="
   end
 
@@ -222,9 +222,9 @@ class Container
       f.print " " * indent
       f.print "+->"
     end
-    line = #"[#{useful? ? 'U' : ' '}] " +
+    line = "[#{thread.nil? ? ' ' : '*'}] " + #"[#{useful? ? 'U' : ' '}] " +
       if @message
-        "[#{thread}] #{@message.subj} " ##{@message.refs.inspect} / #{@message.replytos.inspect}"
+        message.subj ##{@message.refs.inspect} / #{@message.replytos.inspect}"
       else
         "<no message>"
       end
@@ -235,13 +235,16 @@ class Container
   end
 end
 
-## A set of threads (so a forest). Builds thread structures by reading
-## messages from an index.
+## A set of threads, so a forest. Is integrated with the index and
+## builds thread structures by reading messages from it.
 ##
 ## If 'thread_by_subj' is true, puts messages with the same subject in
 ## one thread, even if they don't reference each other. This is
 ## helpful for crappy MUAs that don't set In-reply-to: or References:
 ## headers, but means that messages may be threaded unnecessarily.
+##
+## The following invariants are maintained: every Thread has at least one
+## Container tree, and every Container tree has at least one Message.
 class ThreadSet
   attr_reader :num_messages
   bool_reader :thread_by_subj
@@ -261,16 +264,10 @@ class ThreadSet
   def thread_for m; thread_for_id m.id end
   def contains? m; contains_id? m.id end
 
-  def delete_cruft
-    @threads.each { |k, v| @threads.delete(k) if v.empty? }
-  end
-  private :delete_cruft
-
-  def threads; delete_cruft; @threads.values; end
-  def size; delete_cruft; @threads.size; end
+  def threads; @threads.values end
+  def size; @threads.size end
 
-  ## unused
-  def dump f
+  def dump f=$stdout
     @threads.each do |s, t|
       f.puts "**********************"
       f.puts "** for subject #{s} **"
@@ -279,32 +276,49 @@ class ThreadSet
     end
   end
 
+  ## link two containers
   def link p, c, overwrite=false
     if p == c || p.descendant_of?(c) || c.descendant_of?(p) # would create a loop
-#      puts "*** linking parent #{p} and child #{c} would create a loop"
+      #puts "*** linking parent #{p.id} and child #{c.id} would create a loop"
       return
     end
 
-    if c.parent.nil? || overwrite
-      c.parent.children.delete c if overwrite && c.parent
-      if c.thread
-        c.thread.drop c 
-        c.thread = nil
-      end
-      p.children << c
-      c.parent = p
-    end
+    #puts "in link for #{p.id} to #{c.id}, perform? #{c.parent.nil?} || #{overwrite}"
+
+    return unless c.parent.nil? || overwrite
+    remove_container c
+    p.children << c
+    c.parent = p
+
+    ## if the child was previously a top-level container, it now ain't,
+    ## so ditch our thread and kill it if necessary
+    prune_thread_of c
   end
   private :link
 
+  def remove_container c
+    c.parent.children.delete c if c.parent # remove from tree
+  end
+  private :remove_container
+
+  def prune_thread_of c
+    return unless c.thread
+    c.thread.drop c
+    @threads.delete_if { |k, v| v == c.thread } if c.thread.empty?
+    c.thread = nil
+  end
+  private :prune_thread_of
+
   def remove_id mid
     return unless(c = @messages[mid])
+    remove_container c
+    prune_thread_of c
+  end
 
-    c.parent.children.delete c if c.parent
-    if c.thread
-      c.thread.drop c
-      c.thread = nil
-    end
+  def remove_thread_containing_id mid
+    c = @messages[mid] or return
+    t = c.root.thread
+    @threads.delete_if { |key, thread| t == thread }
   end
 
   ## load in (at most) num number of threads from the index
@@ -344,17 +358,17 @@ class ThreadSet
     el = @messages[message.id]
     return if el.message # we've seen it before
 
+    #puts "adding: #{message.id}, refs #{message.refs.inspect}"
+
     el.message = message
     oldroot = el.root
 
     ## link via references:
-    prev = nil
-    message.refs.each do |ref_id|
+    (message.refs + [el.id]).inject(nil) do |prev, ref_id|
       ref = @messages[ref_id]
       link prev, ref if prev
-      prev = ref
+      ref
     end
-    link prev, el, true if prev
 
     ## link via in-reply-to:
     message.replytos.each do |ref_id|
@@ -364,13 +378,6 @@ class ThreadSet
     end
 
     root = el.root
-
-    ## new root. need to drop old one and put this one in its place
-    if root != oldroot && oldroot.thread
-      oldroot.thread.drop oldroot
-      oldroot.thread = nil
-    end
-
     key =
       if thread_by_subj?
         Message.normalize_subj root.subj