From a607e7c48588be04fde23d3032d4d90f0d4c42cc Mon Sep 17 00:00:00 2001 From: wmorgan Date: Tue, 2 Jan 2007 19:27:15 +0000 Subject: [PATCH] yet more fixes to imap, some buffer debugging, and more comment cleanups git-svn-id: svn://rubyforge.org/var/svn/sup/trunk@136 5c8cc53c-5e98-4d25-b20a-d8db53a31250 --- bin/sup | 36 +------ bin/sup-import | 3 +- lib/sup.rb | 16 ++- lib/sup/buffer.rb | 160 ++++++++++++++++-------------- lib/sup/imap.rb | 66 ++++++------ lib/sup/index.rb | 9 -- lib/sup/mbox/ssh-file.rb | 15 +-- lib/sup/modes/line-cursor-mode.rb | 1 - lib/sup/modes/scroll-mode.rb | 6 ++ 9 files changed, 150 insertions(+), 162 deletions(-) diff --git a/bin/sup b/bin/sup index 3066d6d..36fd152 100644 --- a/bin/sup +++ b/bin/sup @@ -8,19 +8,6 @@ Thread.abort_on_exception = true # make debugging possible module Redwood -$exception = nil -def reporting_thread - ::Thread.new do - begin - yield - rescue Exception => e - $exception ||= e - raise - end - end -end -module_function :reporting_thread - global_keymap = Keymap.new do |k| k.add :quit, "Quit Redwood", 'q' k.add :help, "Show help", 'H', '?' @@ -115,8 +102,8 @@ begin bm.draw_screen imode.load_more_threads ibuf.content_height - # reporting_thread { sleep 3; PollManager.poll } - #PollManager.start_thread + reporting_thread { sleep 5; PollManager.poll } + PollManager.start_thread until $exception bm.draw_screen @@ -203,23 +190,7 @@ end Index.save unless $exception # TODO: think about this if $exception - case $exception - when IndexError - $stderr.puts < e + $exception ||= e + raise + end + end + end + module_function :reporting_thread + ## one-stop shop for yamliciousness def register_yaml klass, props vars = props.map { |p| "@#{p}" } diff --git a/lib/sup/buffer.rb b/lib/sup/buffer.rb index d672bfe..4000004 100644 --- a/lib/sup/buffer.rb +++ b/lib/sup/buffer.rb @@ -13,6 +13,9 @@ module Ncurses lamer.first end + def mutex; @mutex ||= Mutex.new; end + def sync &b; mutex.synchronize &b; end + ## aaahhh, user input. who would have though that such a simple ## idea would be SO FUCKING COMPLICATED?! because apparently ## Ncurses.getch (and Curses.getch), even in cbreak mode, BLOCKS @@ -37,23 +40,13 @@ module Ncurses end end - module_function :rows, :cols, :nonblocking_getch + module_function :rows, :cols, :nonblocking_getch, :mutex, :sync KEY_CANCEL = "\a"[0] # ctrl-g end module Redwood -## could be a bottleneck, but doesn't seem to significantly slow -## things down. - -class SafeNcurses - def self.method_missing meth, *a, &b - @mutex ||= Mutex.new - @mutex.synchronize { Ncurses.send meth, *a, &b } - end -end - class Buffer attr_reader :mode, :x, :y, :width, :height, :title bool_reader :dirty @@ -71,9 +64,10 @@ class Buffer def content_width; @width; end def resize rows, cols - return if rows == @width && cols == @height + return if cols == @width && rows == @height @width = cols @height = rows + @dirty = true mode.resize rows, cols end @@ -82,6 +76,7 @@ class Buffer draw_status commit end + def mark_dirty; @dirty = true; end def commit @@ -143,7 +138,7 @@ class BufferManager @minibuf_stack = [] @textfields = {} @flash = nil - @freeze = false + @shelled = false self.class.i_am_the_instance self end @@ -151,8 +146,7 @@ class BufferManager def buffers; @name_map.to_a; end def focus_on buf - raise ArgumentError, "buffer not on stack: #{buf.inspect}" unless - @buffers.member? buf + raise ArgumentError, "buffer not on stack: #{buf.inspect}" unless @buffers.member? buf return if buf == @focus_buf @focus_buf.blur if @focus_buf @focus_buf = buf @@ -160,8 +154,7 @@ class BufferManager end def raise_to_front buf - raise ArgumentError, "buffer not on stack: #{buf.inspect}" unless - @buffers.member? buf + raise ArgumentError, "buffer not on stack: #{buf.inspect}" unless @buffers.member? buf @buffers.delete buf @buffers.push buf focus_on buf @@ -189,40 +182,49 @@ class BufferManager end def completely_redraw_screen - return if @freeze - SafeNcurses.clear - @dirty = true - draw_screen + return if @shelled + + Ncurses.sync do + @dirty = true + Ncurses.clear + draw_screen :sync => false + end end def handle_resize - return if @freeze - rows, cols = SafeNcurses.rows, SafeNcurses.cols + return if @shelled + rows, cols = Ncurses.rows, Ncurses.cols @buffers.each { |b| b.resize rows - minibuf_lines, cols } completely_redraw_screen - flash "resized to #{rows}x#{cols}" + flash "Resized to #{rows}x#{cols}" end - def draw_screen skip_minibuf=false - return if @freeze + def draw_screen opts={} + return if @shelled + + Ncurses.mutex.lock unless opts[:sync] == false ## disabling this for the time being, to help with debugging ## (currently we only have one buffer visible at a time). ## TODO: reenable this if we allow multiple buffers false && @buffers.inject(@dirty) do |dirty, buf| - buf.resize SafeNcurses.rows - minibuf_lines, SafeNcurses.cols + buf.resize Ncurses.rows - minibuf_lines, Ncurses.cols @dirty ? buf.draw : buf.redraw end + ## quick hack if true buf = @buffers.last - buf.resize SafeNcurses.rows - minibuf_lines, SafeNcurses.cols + buf.resize Ncurses.rows - minibuf_lines, Ncurses.cols + File.open("asdf.txt", "a") { |f| f.puts "dirty #@dirty, (re)drawing #{buf.mode.name}" } @dirty ? buf.draw : buf.redraw end - draw_minibuf unless skip_minibuf + draw_minibuf :sync => false unless opts[:skip_minibuf] @dirty = false - SafeNcurses.doupdate + Ncurses.doupdate + Ncurses.refresh if opts[:refresh] + Ncurses.mutex.unlock unless opts[:sync] == false end ## gets the mode from the block, which is only called if the buffer @@ -247,9 +249,8 @@ class BufferManager num += 1 end - Redwood::log "spawning buffer \"#{realtitle}\"" - width = opts[:width] || SafeNcurses.cols - height = opts[:height] || SafeNcurses.rows - 1 + width = opts[:width] || Ncurses.cols + height = opts[:height] || Ncurses.rows - 1 ## since we are currently only doing multiple full-screen modes, ## use stdscr for each window. once we become more sophisticated, @@ -257,7 +258,7 @@ class BufferManager ## ## w = Ncurses::WINDOW.new(height, width, (opts[:top] || 0), ## (opts[:left] || 0)) - w = SafeNcurses.stdscr + w = Ncurses.stdscr b = Buffer.new w, mode, width, height, :title => realtitle mode.buffer = b @name_map[realtitle] = b @@ -277,7 +278,6 @@ class BufferManager def kill_buffer buf raise ArgumentError, "buffer not on stack: #{buf.inspect}" unless @buffers.member? buf - Redwood::log "killing buffer \"#{buf.title}\"" buf.mode.cleanup @buffers.delete buf @@ -292,8 +292,7 @@ class BufferManager end def ask domain, question, default=nil - @textfields[domain] ||= TextField.new SafeNcurses.stdscr, SafeNcurses.rows - 1, 0, - SafeNcurses.cols + @textfields[domain] ||= TextField.new Ncurses.stdscr, Ncurses.rows - 1, 0, Ncurses.cols tf = @textfields[domain] ## this goddamn ncurses form shit is a fucking 1970's @@ -301,19 +300,19 @@ class BufferManager ## that needs to happen in order to display a form and have the ## entire screen not disappear and have the cursor in the right ## place is TOO FUCKING COMPLICATED. - tf.activate question, default - @dirty = true - draw_screen true + Ncurses.sync do + tf.activate question, default + @dirty = true + draw_screen :skip_minibuf => true, :sync => false + end ret = nil - @freeze = true tf.position_cursor - SafeNcurses.refresh - while tf.handle_input(SafeNcurses.nonblocking_getch); end - @freeze = false + Ncurses.sync { Ncurses.refresh } + while tf.handle_input(Ncurses.nonblocking_getch); end ret = tf.value - tf.deactivate + Ncurses.sync { tf.deactivate } @dirty = true ret @@ -324,15 +323,17 @@ class BufferManager accept = accept.split(//).map { |x| x[0] } if accept flash question - SafeNcurses.curs_set 1 - SafeNcurses.move SafeNcurses.rows - 1, question.length + 1 - SafeNcurses.refresh + Ncurses.sync do + Ncurses.curs_set 1 + Ncurses.move Ncurses.rows - 1, question.length + 1 + Ncurses.refresh + end ret = nil done = false - @freeze = true + @shelled = true until done - key = SafeNcurses.nonblocking_getch + key = Ncurses.nonblocking_getch if key == Ncurses::KEY_CANCEL done = true elsif (accept && accept.member?(key)) || !accept @@ -340,11 +341,15 @@ class BufferManager done = true end end - @freeze = false - SafeNcurses.curs_set 0 - erase_flash - draw_screen - SafeNcurses.curs_set 0 + + @shelled = false + + Ncurses.sync do + SafeNcurses.curs_set 0 + erase_flash + draw_screen :sync => false + SafeNcurses.curs_set 0 + end ret end @@ -363,23 +368,30 @@ class BufferManager def minibuf_lines; [(@flash ? 1 : 0) + @minibuf_stack.compact.size, 1].max; end - def draw_minibuf - SafeNcurses.attrset Colormap.color_for(:none) + def draw_minibuf opts={} m = @minibuf_stack.compact m << @flash if @flash m << "" if m.empty? + + Ncurses.mutex.lock unless opts[:sync] == false + Ncurses.attrset Colormap.color_for(:none) m.each_with_index do |s, i| - SafeNcurses.mvaddstr SafeNcurses.rows - i - 1, 0, s + (" " * [SafeNcurses.cols - s.length, 0].max) + Ncurses.mvaddstr Ncurses.rows - i - 1, 0, s + (" " * [Ncurses.cols - s.length, 0].max) end + Ncurses.refresh if opts[:refresh] + Ncurses.mutex.unlock unless opts[:sync] == false end def say s, id=nil + new_id = id.nil? id ||= @minibuf_stack.length @minibuf_stack[id] = s - unless @freeze - draw_screen - SafeNcurses.refresh + if new_id + draw_screen :refresh => true + else + draw_minibuf :refresh => true end + if block_given? begin yield @@ -394,10 +406,7 @@ class BufferManager def flash s @flash = s - unless @freeze - draw_screen - SafeNcurses.refresh - end + draw_screen :refresh => true end ## a little tricky because we can't just delete_at id because ids @@ -406,24 +415,23 @@ class BufferManager @minibuf_stack[id] = nil if id == @minibuf_stack.length - 1 id.downto(0) do |i| - break unless @minibuf_stack[i].nil? + break if @minibuf_stack[i] @minibuf_stack.delete_at i end end - unless @freeze - draw_screen - SafeNcurses.refresh - end + draw_screen :refresh => true end def shell_out command - @freeze = true - SafeNcurses.endwin - system command - SafeNcurses.refresh - SafeNcurses.curs_set 0 - @freeze = false + @shelled = true + Ncurses.sync do + Ncurses.endwin + system command + Ncurses.refresh + Ncurses.curs_set 0 + end + @shelled = false end end end diff --git a/lib/sup/imap.rb b/lib/sup/imap.rb index 06ade31..296a5d3 100644 --- a/lib/sup/imap.rb +++ b/lib/sup/imap.rb @@ -52,6 +52,16 @@ class IMAP < Source @mutex = Mutex.new end + def say s + @say_id = BufferManager.say s, @say_id if BufferManager.instantiated? + Redwood::log s + end + def shutup + BufferManager.clear @say_id if BufferManager.instantiated? + @say_id = nil + end + private :say, :shutup + def connect return false if broken? return true if @imap @@ -69,28 +79,22 @@ class IMAP < Source ## ## FUCK!!!!!!!!! - Redwood::log "connecting to #{@parsed_uri.host} port #{ssl? ? 993 : 143}, ssl=#{ssl?} ..." - sid = BufferManager.say "Connecting to IMAP server #{host}..." if BufferManager.instantiated? + say "Connecting to IMAP server #{host}:#{port}..." Redwood::reporting_thread do begin #raise Net::IMAP::ByeResponseError, "simulated imap failure" - # @imap = Net::IMAP.new host, ssl? ? 993 : 143, ssl? - sleep 3 - BufferManager.say "Logging in...", sid if BufferManager.instantiated? - # @imap.authenticate 'LOGIN', @username, @password - sleep 3 - BufferManager.say "Sizing mailbox...", sid if BufferManager.instantiated? - # @imap.examine mailbox - # last_id = @imap.responses["EXISTS"][-1] - sleep 1 + @imap = Net::IMAP.new host, ssl? ? 993 : 143, ssl? + say "Logging in..." + @imap.authenticate 'LOGIN', @username, @password + say "Sizing mailbox..." + @imap.examine mailbox + last_id = @imap.responses["EXISTS"][-1] - BufferManager.say "Reading headers (because IMAP sucks)...", sid if BufferManager.instantiated? - # values = @imap.fetch(1 .. last_id, ['RFC822.SIZE', 'INTERNALDATE']) - sleep 3 + say "Reading headers (because IMAP sucks)..." + values = @imap.fetch(1 .. last_id, ['RFC822.SIZE', 'INTERNALDATE']) - raise Net::IMAP::ByeResponseError, "simulated imap failure" - Redwood::log "successfully connected to #{@parsed_uri}" + say "Successfully connected to #{@parsed_uri}" values.each do |v| id = make_id v @@ -101,24 +105,24 @@ class IMAP < Source self.broken_msg = e.message.chomp # fucking chomp! fuck!!! @imap = nil Redwood::log "error connecting to IMAP server: #{self.broken_msg}" - ensure - BufferManager.clear sid if BufferManager.instantiated? + ensure + shutup end end.join - @mutex.unlock !!@imap end private :connect def make_id imap_stuff msize, mdate = imap_stuff.attr['RFC822.SIZE'], Time.parse(imap_stuff.attr["INTERNALDATE"]) - sprintf("%d.%07d", mdate.to_i, msize).to_i + sprintf("%d%07d", mdate.to_i, msize).to_i end private :make_id def host; @parsed_uri.host; end - def mailbox; @parsed_uri.path[1..-1] end ##XXXX TODO handle nil + def port; @parsed_uri.port || (ssl? ? 993 : 143); end + def mailbox; @parsed_uri.path[1..-1] || 'INBOX'; end def ssl?; @parsed_uri.scheme == 'imaps' end def load_header id @@ -146,17 +150,16 @@ class IMAP < Source def get_imap_field id, field f = nil - @mutex.synchronize do - imap_id = @imap_ids[id] or raise SourceError, "Unknown message id #{id}. It is likely that messages have been deleted from this IMAP mailbox." - begin - f = @imap.fetch imap_id, [field, 'RFC822.SIZE', 'INTERNALDATE'] - got_id = make_id f - raise SourceError, "IMAP message mismatch: requested #{id}, got #{got_id}. It is likely the IMAP mailbox has been modified." unless got_id == id - rescue Net::IMAP::Error => e - raise SourceError, e.message - end - raise SourceError, "null IMAP field '#{field}' for message with id #{id} imap id #{imap_id}" if f.nil? + imap_id = @imap_ids[id] or raise SourceError, "Unknown message id #{id}. It is likely that messages have been deleted from this IMAP mailbox." + begin + f = @imap.fetch imap_id, [field, 'RFC822.SIZE', 'INTERNALDATE'] + got_id = make_id f[0] + raise SourceError, "IMAP message mismatch: requested #{id}, got #{got_id}. It is likely the IMAP mailbox has been modified." unless got_id == id + rescue Net::IMAP::Error => e + raise SourceError, e.message end + raise SourceError, "null IMAP field '#{field}' for message with id #{id} imap id #{imap_id}" if f.nil? + f[0].attr[field] end private :get_imap_field @@ -176,6 +179,7 @@ class IMAP < Source @mutex.synchronize { connect or raise SourceError, broken_msg } @ids.first end + def end_offset @mutex.synchronize { connect or raise SourceError, broken_msg } @ids.last diff --git a/lib/sup/index.rb b/lib/sup/index.rb index c407894..2e5ba49 100644 --- a/lib/sup/index.rb +++ b/lib/sup/index.rb @@ -7,15 +7,6 @@ require 'ferret' module Redwood -class IndexError < StandardError - attr_reader :source - - def initialize source, s - super s - @source = source - end -end - class Index include Singleton diff --git a/lib/sup/mbox/ssh-file.rb b/lib/sup/mbox/ssh-file.rb index 9e91d70..50a2172 100644 --- a/lib/sup/mbox/ssh-file.rb +++ b/lib/sup/mbox/ssh-file.rb @@ -78,7 +78,7 @@ end ## the file-like interface to a remote file class SSHFile MAX_BUF_SIZE = 1024 * 1024 # bytes - MAX_TRANSFER_SIZE = 1024 * 64 + MAX_TRANSFER_SIZE = 1024 * 128 REASONABLE_TRANSFER_SIZE = 1024 * 32 SIZE_CHECK_INTERVAL = 60 * 1 # seconds @@ -99,12 +99,11 @@ class SSHFile @say_id = BufferManager.say s, @say_id if BufferManager.instantiated? Redwood::log s end - private :say - def shutup BufferManager.clear @say_id if BufferManager.instantiated? @say_id = nil end + private :say, :shutup def connect return if @session @@ -114,14 +113,10 @@ class SSHFile begin #raise SSHFileError, "simulated SSH file error" - #@session = Net::SSH.start @host, @ssh_opts - sleep 3 + @session = Net::SSH.start @host, @ssh_opts say "Starting SSH shell..." - # @shell = @session.shell.sync - sleep 3 + @shell = @session.shell.sync say "Checking for #@fn..." - sleep 1 - raise Errno::ENOENT, @fn raise Errno::ENOENT, @fn unless @shell.test("-e #@fn").status == 0 ensure shutup @@ -161,7 +156,7 @@ private begin retries = 0 connect - MBox::debug "sending command: #{cmd.inspect}" + # MBox::debug "sending command: #{cmd.inspect}" begin result = @shell.send_command cmd raise SSHFileError, "Failure during remote command #{cmd.inspect}: #{result.stderr[0 .. 100]}" unless result.status == 0 diff --git a/lib/sup/modes/line-cursor-mode.rb b/lib/sup/modes/line-cursor-mode.rb index f8a46e9..373a5fc 100644 --- a/lib/sup/modes/line-cursor-mode.rb +++ b/lib/sup/modes/line-cursor-mode.rb @@ -79,7 +79,6 @@ protected if @curpos == topline page_up set_cursor_pos [botline - 2, topline].max -# raise "cursor position now #@curpos, topline #{topline} botline #{botline}" else @curpos -= 1 unless buffer.dirty? diff --git a/lib/sup/modes/scroll-mode.rb b/lib/sup/modes/scroll-mode.rb index 3085607..d5978b6 100644 --- a/lib/sup/modes/scroll-mode.rb +++ b/lib/sup/modes/scroll-mode.rb @@ -91,6 +91,12 @@ protected :highlight => opts[:highlight] when Array xpos = 0 + + ## speed test + # str = s.map { |color, text| text }.join + # buffer.write ln - @topline, 0, str, :color => :none, :highlight => opts[:highlight] + # return + s.each do |color, text| raise "nil text for color '#{color}'" if text.nil? # good for debugging if xpos + text.length < @leftcol -- 2.45.2