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
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
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
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
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
_ 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
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"
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"
require 'thread'
require 'ncurses'
+if defined? Ncurses
module Ncurses
def rows
lame, lamer = [], []
KEY_CANCEL = 7 # ctrl-g
KEY_TAB = 9
end
+end
module Redwood
--- /dev/null
+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
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)})
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
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
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})"
unless entry
num += 1
numi += 1 if m.labels.include? :inbox
+ from_and_subj << [m.from.longname, m.subj]
end
m
end
@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
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
“Every other client we've tried is intolerable.”
</blockquote>
+ <blockquote>
+ “Sup is almost to the point where I could jump ship from mutt.”
+ </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