sup

A curses threads-with-tags style email client

sup.git

git clone https://supmua.dev/git/sup/
commit 8220732408d9c0d77f1d6cb9c9986362c3bfa7f9
parent f8c0cdfa14c06f885d40c1865efc288736d2c686
Author: wmorgan <wmorgan@5c8cc53c-5e98-4d25-b20a-d8db53a31250>
Date:   Thu, 28 Dec 2006 23:10:58 +0000

- created a PersonManager that keeps track of the names of all email addresses
  ever seen. this will probably need to be intelligently trimmed, but will be
  used for fast loading of messages from slow sources.

- moved initialization and shutdown to Redwood::start and ::finish in sup.rb.

- poll#initialize no longer automatically starts a thread. you must call
  #start_thread. (because sup-import now calls Redwood::start which initializes
  all managers, including pollmanager, and it certainly doesn't make sense to
  have anything threaded during import)

- misc. comment improvements



git-svn-id: svn://rubyforge.org/var/svn/sup/trunk@113 5c8cc53c-5e98-4d25-b20a-d8db53a31250

Diffstat:
M bin/sup | 14 ++++----------
M bin/sup-import | 3 +++
M lib/sup.rb | 20 +++++++++++++++++++-
M lib/sup/label.rb | 2 +-
M lib/sup/person.rb | 84 ++++++++++++++++++++++++++++++++++++++++++-------------------------------------
M lib/sup/poll.rb | 16 +++++++++-------
M lib/sup/util.rb | 3 ++-
7 files changed, 83 insertions(+), 59 deletions(-)
diff --git a/bin/sup b/bin/sup
@@ -52,13 +52,7 @@ def stop_cursing
 end
 module_function :start_cursing, :stop_cursing
 
-Redwood::SentManager.new Redwood::SENT_FN
-Redwood::ContactManager.new Redwood::CONTACT_FN
-Redwood::LabelManager.new Redwood::LABEL_FN
-Redwood::AccountManager.new $config[:accounts]
-Redwood::DraftManager.new Redwood::DRAFT_DIR
-Redwood::UpdateManager.new
-Redwood::PollManager.new
+Redwood::start
 
 Index.new.load
 log "loaded #{Index.size} messages from index"
@@ -74,7 +68,7 @@ if(s = Index.source_for SentManager.source_name)
 else
   Index.add_source SentManager.new_source
 end
-  
+
 begin
   log "starting curses"
   start_cursing
@@ -123,6 +117,7 @@ begin
   imode.load_more_threads ibuf.content_height
 
   reporting_thread { sleep 3; PollManager.poll }
+  PollManager.start_thread
 
   until $exception
     bm.draw_screen
@@ -185,11 +180,10 @@ begin
     end
   end
   bm.kill_all_buffers
-  Redwood::LabelManager.save
-  Redwood::ContactManager.save
 rescue Exception => e
   $exception ||= e
 ensure
+  Redwood::finish
   stop_cursing
 end
 
diff --git a/bin/sup-import b/bin/sup-import
@@ -81,6 +81,8 @@ if(o = ARGV.find { |x| x =~ /^--/ })
   educate_user
 end
 
+Redwood::start
+
 puts "loading index..."
 index = Redwood::Index.new
 index.load
@@ -171,6 +173,7 @@ begin
   end
 ensure
   index.save
+  Redwood::finish
 end
 
 if rebuild || force_rebuild
diff --git a/lib/sup.rb b/lib/sup.rb
@@ -19,6 +19,7 @@ module Redwood
   CONFIG_FN  = File.join(BASE_DIR, "config.yaml")
   SOURCE_FN  = File.join(BASE_DIR, "sources.yaml")
   LABEL_FN   = File.join(BASE_DIR, "labels.txt")
+  PERSON_FN   = File.join(BASE_DIR, "people.txt")
   CONTACT_FN = File.join(BASE_DIR, "contacts.txt")
   DRAFT_DIR  = File.join(BASE_DIR, "drafts")
   SENT_FN    = File.join(BASE_DIR, "sent.mbox")
@@ -59,7 +60,24 @@ module Redwood
     end
   end
 
-  module_function :register_yaml, :save_yaml_obj, :load_yaml_obj
+  def start
+    Redwood::PersonManager.new Redwood::PERSON_FN
+    Redwood::SentManager.new Redwood::SENT_FN
+    Redwood::ContactManager.new Redwood::CONTACT_FN
+    Redwood::LabelManager.new Redwood::LABEL_FN
+    Redwood::AccountManager.new $config[:accounts]
+    Redwood::DraftManager.new Redwood::DRAFT_DIR
+    Redwood::UpdateManager.new
+    Redwood::PollManager.new
+  end
+
+  def finish
+    Redwood::LabelManager.save
+    Redwood::ContactManager.save
+    Redwood::PersonManager.save
+  end
+
+  module_function :register_yaml, :save_yaml_obj, :load_yaml_obj, :start, :finish
 end
 
 ## set up default configuration file
diff --git a/lib/sup/label.rb b/lib/sup/label.rb
@@ -3,7 +3,7 @@ module Redwood
 class LabelManager
   include Singleton
 
-  ## all labels that have special meaning. user will be unable to
+  ## labels that have special semantics. user will be unable to
   ## add/remove these via normal label mechanisms.
   RESERVED_LABELS = [ :starred, :spam, :draft, :unread, :killed, :sent ]
 
diff --git a/lib/sup/person.rb b/lib/sup/person.rb
@@ -1,5 +1,30 @@
 module Redwood
 
+class PersonManager
+  include Singleton
+
+  def initialize fn
+    @fn = fn
+    @names = {}
+    IO.readlines(fn).map { |l| l =~ /^(.*)?:\s+(.*)$/ && @names[$1] = $2 } if File.exists? fn
+    self.class.i_am_the_instance self
+  end
+
+  def name_for email; @names[email]; end
+  def register email, name
+    return unless name
+
+    name = name.gsub(/^\s+|\s+$/, "").gsub(/\s+/, " ")
+
+    ## all else being equal, prefer longer names, unless the prior name
+    ## doesn't contain any capitalization
+    oldname = @names[email]
+    @names[email] = name if oldname.nil? || oldname.length < name.length || (oldname !~ /[A-Z]/ && name =~ /[A-Z]/)
+  end
+
+  def save; File.open(@fn, "w") { |f| @names.each { |email, name| f.puts "#{email}: #{name}" } }; end
+end
+
 class Person
   @@email_map = {}
 
@@ -7,22 +32,14 @@ class Person
 
   def initialize name, email
     raise ArgumentError, "email can't be nil" unless email
-    @name = 
-      if name
-        name.gsub(/^\s+|\s+$/, "").gsub(/\s+/, " ")
-      else
-        nil
-      end
     @email = email.gsub(/^\s+|\s+$/, "").gsub(/\s+/, " ").downcase
-    @@email_map[@email] = self
+    PersonManager.register @email, name
+    @name = PersonManager.name_for @email
   end
 
   def == o; o && o.email == email; end
   alias :eql? :==
-
-  def hash
-    [name, email].hash
-  end
+  def hash; [name, email].hash; end
 
   def shortname
     case @name
@@ -31,9 +48,9 @@ class Person
     when /(\S+) \S+/
       $1
     when nil
-      @email #[0 ... 10]
+      @email
     else
-      @name #[0 ... 10]
+      @name
     end
   end
 
@@ -45,18 +62,12 @@ class Person
     end
   end
 
-  def mediumname
-    if @name
-      name
-    else
-      @email
-    end
-  end
+  def mediumname; @name || @email; end
 
   def full_address
     if @name && @email
       if @name =~ /"/
-        "#{@name.inspect} <#@email>"
+        "#{@name.inspect} <#@email>" # escape quotes
       else
         "#@name <#@email>"
       end
@@ -65,6 +76,7 @@ class Person
     end
   end
 
+  ## when sorting addresses, sort by this 
   def sort_by_me
     case @name
     when /^(\S+), \S+/
@@ -80,18 +92,10 @@ class Person
     end.downcase
   end
 
-  def self.for_several s
-    return [] if s.nil?
-
-    begin
-      s.split_on_commas.map { |ss| self.for ss }
-    rescue StandardError => e
-      raise "#{e.message}: for #{s.inspect}"
-    end
-  end
-
   def self.for s
     return nil if s.nil?
+
+    ## try and parse an email address and name
     name, email =
       case s
       when /["'](.*?)["'] <(.*?)>/, /([^,]+) <(.*?)>/
@@ -105,14 +109,16 @@ class Person
         [nil, s]
       end
 
-    if name && (p = @@email_map[email])
-      ## all else being equal, prefer longer names, unless the prior name
-      ## doesn't contain any capitalization
-      p.name = name if (p.name.nil? || p.name.length < name.length) unless
-        p.name =~ /[A-Z]/ || (AccountManager.instantiated? && AccountManager.is_account?(p))
-      p 
-    else
-      Person.new name, email
+    @@email_map[email] ||= Person.new name, email
+  end
+
+  def self.for_several s
+    return [] if s.nil?
+
+    begin
+      s.split_on_commas.map { |ss| self.for ss }
+    rescue StandardError => e
+      raise "#{e.message}: for #{s.inspect}"
     end
   end
 end
diff --git a/lib/sup/poll.rb b/lib/sup/poll.rb
@@ -12,13 +12,6 @@ class PollManager
     @last_poll = nil
     
     self.class.i_am_the_instance self
-
-    Redwood::reporting_thread do
-      while true
-        sleep DELAY / 2
-        poll if @last_poll.nil? || (Time.now - @last_poll) >= DELAY
-      end
-    end
   end
 
   def buffer
@@ -38,6 +31,15 @@ class PollManager
     [num, numi]
   end
 
+  def start_thread
+    Redwood::reporting_thread do
+      while true
+        sleep DELAY / 2
+        poll if @last_poll.nil? || (Time.now - @last_poll) >= DELAY
+      end
+    end
+  end
+
   def do_poll
     return [0, 0] if @polling
     @polling = true
diff --git a/lib/sup/util.rb b/lib/sup/util.rb
@@ -43,7 +43,8 @@ class String
     self[0 .. 0].upcase + self[1 .. -1]
   end
 
-  ## found on teh internets
+  ## a very complicated regex found on teh internets to split on
+  ## commas, unless they occurr within double quotes.
   def split_on_commas
     split(/,\s*(?=(?:[^"]*"[^"]*")*(?![^"]*"))/)
   end