]> git.cworth.org Git - sup/commitdiff
hook system
authorwmorgan <wmorgan@5c8cc53c-5e98-4d25-b20a-d8db53a31250>
Sun, 19 Aug 2007 00:45:50 +0000 (00:45 +0000)
committerwmorgan <wmorgan@5c8cc53c-5e98-4d25-b20a-d8db53a31250>
Sun, 19 Aug 2007 00:45:50 +0000 (00:45 +0000)
git-svn-id: svn://rubyforge.org/var/svn/sup/trunk@528 5c8cc53c-5e98-4d25-b20a-d8db53a31250

Manifest.txt
bin/sup
doc/TODO
lib/sup.rb
lib/sup/buffer.rb
lib/sup/hook.rb [new file with mode: 0644]
lib/sup/modes/thread-index-mode.rb
lib/sup/poll.rb
lib/sup/util.rb
www/index.html

index c499da0197a3da5f4c6adca7487df02d34ebc5c9..02628fc36df0a4a7a620f892a62b29b0e4c0999c 100644 (file)
@@ -21,6 +21,7 @@ lib/sup/buffer.rb
 lib/sup/colormap.rb
 lib/sup/contact.rb
 lib/sup/draft.rb
+lib/sup/hook.rb
 lib/sup/imap.rb
 lib/sup/index.rb
 lib/sup/keymap.rb
diff --git a/bin/sup b/bin/sup
index 2b412aad17f571c1a97bbd1e45ba987a89b836c3..918ec47636a368eb0daf8646123bab35dfbca789 100644 (file)
--- a/bin/sup
+++ b/bin/sup
@@ -16,9 +16,15 @@ Usage:
 
 Options are:
 EOS
+  opt :list_hooks, "List all hooks and descriptions thereof, and quit."
   opt :no_threads, "Turn of threading. Helps with debugging. (Necessarily disables background polling for new messages.)"
 end
 
+if $opts[:list_hooks]
+  Redwood::HookManager.print_hooks
+  exit
+end
+
 Thread.abort_on_exception = true # make debugging possible
 
 module Redwood
@@ -241,7 +247,6 @@ begin
         bm.spawn "New Message", mode
         mode.edit_message
       when :poll
-        #          bm.raise_to_front PollManager.buffer
         reporting_thread { PollManager.poll }
       when :recall_draft
         case Index.num_results_for :label => :draft
index 7522377da547fb633de76135875f2acfd8cf3b59..bf4be637f27d5a173bd2223f56faf2af43e6a2c2 100644 (file)
--- a/doc/TODO
+++ b/doc/TODO
@@ -1,15 +1,14 @@
 for next release
 ----------------
+_ imap "add all folders on this server" option in sup-add
 _ mailing list subscribe/unsubscribe
 _ BufferManager#ask_for_labels opens up label-list-mode if empty
 _ tab completion for mid-text cursors
+_ ncurses text entry horizontal scrolling
 _ forward attachments
 _ messages as attachments
-_ individual labeling in thread-view-mode
-_ tab completion for to: and cc: in compose-mode
 _ use trac or something. this file is getting a little silly.
 _ gpg integration
-_ user-defined hooks
 _ saved searches
 _ bugfix: missing sources should be handled better
 _ bugfix: screwing with the headers when editing causes a crash
@@ -20,10 +19,10 @@ _ bugfix: need a better way to force an address to a particular name,
    for things like evite addresses
 _ bugfix: ferret flakiness: just added message but can't find it (? still relevant ?)
 _ for new message flashes, add new message counts until keypress
-_ bugfix: deadlock (on rubyforge)
+_ bugfix: deadlock (on rubyforge) (? still valid ?)
 _ bugfix: ferret corrupt index problem at index.c:901. see http://ferret.davebalmain.com/trac/ticket/279
 _ bugfix: read before thread-index has finished loading then hides the
-   thread?!? wtf. (on jamie)
+   thread?!? wtf. (on jamie) (? still valid ?)
 _ bugfix: width in index-mode needs to be determined per-character
    rather than per-byte
 _ search results: highlight relevant snippets and open to relevant
@@ -33,6 +32,9 @@ _ undo
 _ gmail support
 _ warnings: top-posting, missing attachment, ruby-talk:XXXX detection
 _ Net::SMTP support
+x user-defined hooks
+x tab completion for to: and cc: in compose-mode
+x individual labeling in thread-view-mode
 x translate aliases in queries on to: and from: fields
 x tab completion on labeling
 
index 311b401f142b8fdfaef9f4a306863808b6c68916..7de719a1602722fe7b315a7870dce8644816f607 100644 (file)
@@ -43,6 +43,7 @@ module Redwood
   SENT_FN    = File.join(BASE_DIR, "sent.mbox")
   LOCK_FN    = File.join(BASE_DIR, "lock")
   SUICIDE_FN = File.join(BASE_DIR, "please-kill-yourself")
+  HOOK_DIR   = File.join(BASE_DIR, "hooks")
 
   YAML_DOMAIN = "masanjin.net"
   YAML_DATE = "2006-10-01"
@@ -205,6 +206,13 @@ else
 end
 
 require "sup/util"
+require "sup/hook"
+
+## we have to initialize this guy first, because other classes must
+## reference it in order to register hooks, and they do that at parse
+## time.
+Redwood::HookManager.new Redwood::HOOK_DIR
+
 require "sup/update"
 require "sup/suicide"
 require "sup/message"
index 55a53ff5ebdd040c656885448362291f7bc0c422..467675a92de35f8cd278c5953901d6681dc945ee 100644 (file)
@@ -2,6 +2,7 @@ require 'etc'
 require 'thread'
 require 'ncurses'
 
+if defined? Ncurses
 module Ncurses
   def rows
     lame, lamer = [], []
@@ -43,6 +44,7 @@ module Ncurses
   KEY_CANCEL = 7 # ctrl-g
   KEY_TAB = 9
 end
+end
 
 module Redwood
 
diff --git a/lib/sup/hook.rb b/lib/sup/hook.rb
new file mode 100644 (file)
index 0000000..11c6323
--- /dev/null
@@ -0,0 +1,111 @@
+module Redwood
+
+class HookManager
+
+  ## there's probably a better way to do this, but to evaluate a hook
+  ## with a bunch of pre-set "local variables" i define a function
+  ## per variable and then instance_evaluate the code.
+  ##
+  ## i don't bother providing setters, since i'm pretty sure the
+  ## charade will fall apart pretty quickly with respect to scoping.
+  ## this is basically fail-fast.
+  class HookContext
+    def initialize name, hash
+      @__name = name
+      hash.each do |k, v|
+        self.class.instance_eval { define_method(k) { v } }
+      end
+    end
+
+    def say s
+      @__say_id = BufferManager.say s, @__say_id
+    end
+
+    def log s
+      Redwood::log "hook[#@__name]: #{s}"
+    end
+
+    def __binding 
+      binding
+    end
+
+    def __cleanup
+      BufferManager.clear @__say_id if @__say_id
+    end
+  end
+
+  include Singleton
+
+  def initialize dir
+    @dir = dir
+    @hooks = {}
+    @descs = {}
+    Dir.mkdir dir unless File.exists? dir
+
+    self.class.i_am_the_instance self
+  end
+
+  def run name, locals={}
+    hook = hook_for(name) or return
+    context = HookContext.new name, locals
+
+    begin
+      result = eval @hooks[name], context.__binding, fn_for(name)
+      if result.is_a? String
+        log "got return value: #{result.inspect}"
+        BufferManager.flash result 
+      end
+    rescue Exception => e
+      log "error running hook: #{e.message}"
+      BufferManager.flash "Error running hook: #{e.message}"
+    end
+    context.__cleanup
+  end
+
+  def register name, desc
+    @descs[name] = desc
+  end
+
+  def print_hooks f=$stdout
+puts <<EOS
+Have #{@descs.size} registered hooks:
+
+EOS
+
+    @descs.sort.each do |name, desc|
+      f.puts <<EOS
+#{name}
+#{"-" * name.length}
+File: #{fn_for name}
+#{desc}
+EOS
+    end
+  end
+
+private
+
+  def hook_for name
+    unless @hooks.member? name
+      @hooks[name] =
+        begin
+          returning IO.readlines(fn_for(name)).join do
+            log "read '#{name}' from #{fn_for(name)}"
+          end
+        rescue SystemCallError => e
+          nil
+        end
+    end
+
+    @hooks[name]
+  end
+
+  def fn_for name
+    File.join @dir, "#{name}.rb"
+  end
+
+  def log m
+    Redwood::log("hook: " + m)
+  end
+end
+
+end
index fc86be7bbc388d41ff9cf263dea0f8fa15e50449..952dd229ad5551552555d741fbd1499428237a31 100644 (file)
@@ -382,9 +382,9 @@ class ThreadIndexMode < LineCursorMode
     myopts = @load_thread_opts.merge({ :when_done => (lambda do |num|
       opts[:when_done].call(num) if opts[:when_done]
       if num > 0
-        BufferManager.flash "Found #{num} threads"
+        BufferManager.flash "Found #{num} threads."
       else
-        BufferManager.flash "No matches"
+        BufferManager.flash "No matches."
       end
     end)})
 
index 822521d4da0bfe85febedf12f246449563d954e7..4d6c5c0ebfa36085e894bf0fbfde11b163f63418 100644 (file)
@@ -5,6 +5,20 @@ module Redwood
 class PollManager
   include Singleton
 
+  HookManager.register "before-poll", <<EOS
+Executes immediately before a poll for new messages commences.
+No variables.
+EOS
+
+  HookManager.register "after-poll", <<EOS
+Executes immediately before a poll for new messages commences.
+Variables:
+            num: the total number of new messages
+      num_inbox: the number of new messages appearing in the inbox (i.e. not
+                 auto-archived).
+  from_and_subj: an array of (from email address, subject) pairs
+EOS
+
   DELAY = 300
 
   def initialize
@@ -20,13 +34,18 @@ class PollManager
   end
 
   def poll
+    HookManager.run "before-poll"
+
     BufferManager.flash "Polling for new messages..."
-    num, numi = buffer.mode.poll
+    num, numi, from_and_subj = buffer.mode.poll
     if num > 0
       BufferManager.flash "Loaded #{num} new messages, #{numi} to inbox." 
     else
       BufferManager.flash "No new messages." 
     end
+
+    HookManager.run "after-poll", :num => num, :num_inbox => numi, :from_and_subj => from_and_subj
+
     [num, numi]
   end
 
@@ -46,6 +65,8 @@ class PollManager
 
   def do_poll
     total_num = total_numi = 0
+    from_and_subj = []
+
     @mutex.synchronize do
       Index.usual_sources.each do |source|
 #        yield "source #{source} is done? #{source.done?} (cur_offset #{source.cur_offset} >= #{source.end_offset})"
@@ -66,6 +87,7 @@ class PollManager
           unless entry
             num += 1
             numi += 1 if m.labels.include? :inbox
+            from_and_subj << [m.from.longname, m.subj]
           end
           m
         end
@@ -78,7 +100,7 @@ class PollManager
       @last_poll = Time.now
       @polling = false
     end
-    [total_num, total_numi]
+    [total_num, total_numi, from_and_subj]
   end
 
   ## this is the main mechanism for adding new messages to the
index b9563e12a55cec07e334a40efa3fc9b143cb59ac..520a648e1344fa04886a31ab71ff53490c0c8936 100644 (file)
@@ -123,21 +123,7 @@ class Object
     ret
   end
 
-  ## takes a value which it yields and then returns, so that code
-  ## like:
-  ##
-  ## x = expensive_operation
-  ## log "got #{x}"
-  ## x
-  ##
-  ## now becomes:
-  ##
-  ## with(expensive_operation) { |x| log "got #{x}" }
-  ##
-  ## i'm sure there's pithy comment i could make here about the
-  ## superiority of lisp, but fuck lisp.
-  ##
-  ## addendum: apparently this is a "k combinator". whoda thunk it?
+  ## "k combinator"
   def returning x; yield x; x; end
 
   ## clone of java-style whole-method synchronization
index 697e1373c784831ac716ac7749759ab00d72b7aa..95a20543d1ea34481658c6e79f454956e0af8127 100644 (file)
                &ldquo;Every other client we've tried is intolerable.&rdquo;
                </blockquote>
 
+               <blockquote>
+               &ldquo;Sup is almost to the point where I could jump ship from mutt.&rdquo;
+               </blockquote>
+
                <p>
    Sup is a console-based email client for people with a lot of email.
    It supports tagging, very fast full-text search, automatic