end
Thread.abort_on_exception = true # make debugging possible
+Thread.current.priority = 1 # keep ui responsive
module Redwood
bm.erase_flash
- action = begin
- if bm.handle_input c
+ action =
+ begin
+ if bm.handle_input c
+ :nothing
+ else
+ bm.resolve_input_with_keymap c, global_keymap
+ end
+ rescue InputSequenceAborted
:nothing
- else
- bm.resolve_input_with_keymap c, global_keymap
end
- rescue InputSequenceAborted
- :nothing
- end
case action
when :quit_now
break if bm.kill_all_buffers_safely
def mutex; @mutex ||= Mutex.new; end
def sync &b; mutex.synchronize(&b); end
+ ## magically, this stuff seems to work now. i could swear it didn't
+ ## before. hm.
def nonblocking_getch
## INSANTIY
## it is NECESSARY to wrap Ncurses.getch in a select() otherwise all
def content_height; @height - 1; end
def content_width; @width; end
- def resize rows, cols
+ def resize rows, cols
return if cols == @width && rows == @height
@width = cols
@height = rows
query = {}
subs = HookManager.run("custom-search", :subs => s) || s
- subs = s.gsub(/\b(to|from):(\S+)\b/) do
+
+ subs = subs.gsub(/\b(to|from):(\S+)\b/) do
field, name = $1, $2
if(p = ContactManager.contact_for(name))
[field, p.email]
+require 'open3'
module Redwood
class Mode
end
def initialize
- super
+ super "console"
@console = Console.new self
@binding = @console.instance_eval { binding }
end
def initialize opts={}
@cursor_top = @curpos = opts.delete(:skip_top_rows) || 0
@load_more_callbacks = []
- @load_more_callbacks_m = Mutex.new
- @load_more_callbacks_active = false
+ @load_more_q = Queue.new
+ @load_more_thread = ::Thread.new do
+ while true
+ e = @load_more_q.pop
+ @load_more_callbacks.each { |c| c.call e }
+ end
+ end
+
super opts
end
+ def cleanup
+ @load_more_thread.kill
+ super
+ end
+
def draw
super
set_status
end
def cursor_down
- call_load_more_callbacks buffer.content_height if @curpos == lines - 1
+ call_load_more_callbacks buffer.content_height if @curpos >= lines - [buffer.content_height/2,1].max
return false unless @curpos < lines - 1
if @curpos >= botline - 1
end
def call_load_more_callbacks size
- go =
- @load_more_callbacks_m.synchronize do
- if @load_more_callbacks_active
- false
- else
- @load_more_callbacks_active = true
- end
- end
-
- return unless go
-
- @load_more_callbacks.each { |c| c.call size }
- @load_more_callbacks_active = false
- end
-
+ @load_more_q.push size
+ end
end
end
@last_load_more_size = nil
to_load_more do |size|
next if @last_load_more_size == 0
- load_threads :num => 1, :background => false
- load_threads :num => (size - 1),
+ load_threads :num => size,
:when_done => lambda { |num| @last_load_more_size = num }
end
end
mode = ThreadViewMode.new t, @hidden_labels, self
BufferManager.spawn t.subj, mode
BufferManager.draw_screen
- mode.jump_to_first_open true
+ mode.jump_to_first_open
BufferManager.draw_screen # lame TODO: make this unnecessary
## the first draw_screen is needed before topline and botline
## are set, and the second to show the cursor having moved
BufferManager.draw_screen
last_update = Time.now
end
+ ::Thread.pass
break if @interrupt_search
end
@ts.threads.each { |th| th.labels.each { |l| LabelManager << l } }
-require 'open3'
module Redwood
class ThreadViewMode < LineCursorMode
end
end
- def jump_to_first_open loose_alignment=false
+ def jump_to_first_open
m = @message_lines[0] or return
if @layout[m].state != :closed
- jump_to_message m, loose_alignment
+ jump_to_message m#, true
else
- jump_to_next_open loose_alignment
+ jump_to_next_open #true
end
end
- def jump_to_next_open loose_alignment=false
+ def jump_to_next_open force_alignment=nil
return continue_search_in_buffer if in_search? # hack: allow 'n' to apply to both operations
m = (curpos ... @message_lines.length).argfind { |i| @message_lines[i] }
return unless m
break if @layout[nextm].state != :closed
m = nextm
end
- jump_to_message nextm, loose_alignment if nextm
+ jump_to_message nextm, force_alignment if nextm
end
def align_current_message
m = @message_lines[curpos] or return
- jump_to_message m
+ jump_to_message m, true
end
- def jump_to_prev_open loose_alignment=false
+ def jump_to_prev_open
m = (0 .. curpos).to_a.reverse.argfind { |i| @message_lines[i] } # bah, .to_a
return unless m
## jump to the top of the current message if we're in the body;
break if @layout[prevm].state != :closed
m = prevm
end
- jump_to_message prevm, loose_alignment if prevm
+ jump_to_message prevm if prevm
else
- jump_to_message m, loose_alignment
+ jump_to_message m
end
end
- IDEAL_TOP_CONTEXT = 3 # try and give 3 rows of top context
- IDEAL_LEFT_CONTEXT = 4 # try and give 4 columns of left context
- def jump_to_message m, loose_alignment=false
+ def jump_to_message m, force_alignment=false
l = @layout[m]
- left = l.depth * INDENT_SPACES
- right = left + l.width
- ## jump to the top line
- if loose_alignment
- jump_to_line [l.top - IDEAL_TOP_CONTEXT, 0].max # give 3 lines of top context
- else
- jump_to_line l.top
- end
-
- ## jump to the left column
- ideal_left = left +
- if loose_alignment
- -IDEAL_LEFT_CONTEXT + (l.width - buffer.content_width + IDEAL_LEFT_CONTEXT + 1).clamp(0, IDEAL_LEFT_CONTEXT)
- else
- 0
- end
+ ## boundaries of the message
+ message_left = l.depth * INDENT_SPACES
+ message_right = message_left + l.width
- jump_to_col [ideal_left, 0].max
+ ## calculate leftmost colum
+ left = if force_alignment # force mode: align exactly
+ message_left
+ else # regular: minimize cursor movement
+ ## leftmost and rightmost are boundaries of all valid left-column
+ ## alignments.
+ leftmost = [message_left, message_right - buffer.content_width + 1].min
+ rightmost = message_left
+ leftcol.clamp(leftmost, rightmost)
+ end
- ## either way, move the cursor to the first line
- set_cursor_pos l.top
+ jump_to_line l.top # move vertically
+ jump_to_col left # move horizontally
+ set_cursor_pos l.top # set cursor pos
end
def expand_all_messages
query = {}
subs = HookManager.run("custom-search", :subs => s) || s
- subs = s.gsub(/\b(to|from):(\S+)\b/) do
+ subs = subs.gsub(/\b(to|from):(\S+)\b/) do
field, name = $1, $2
if(p = ContactManager.contact_for(name))
[field, p.email]