commit e8db677a1d4c33cf705df38c684a00fb8c58b835
parent 3132978548aaca3a65f3e418f0acbdc5c733397f
Author: wmorgan <wmorgan@5c8cc53c-5e98-4d25-b20a-d8db53a31250>
Date: Tue, 6 Nov 2007 22:36:13 +0000
add buffer search with '/' and 'n', and change index search to '\'
git-svn-id: svn://rubyforge.org/var/svn/sup/trunk@669 5c8cc53c-5e98-4d25-b20a-d8db53a31250
Diffstat:
7 files changed, 140 insertions(+), 27 deletions(-)
diff --git a/bin/sup b/bin/sup
@@ -40,10 +40,11 @@ global_keymap = Keymap.new do |k|
k.add :list_buffers, "List all buffers", 'B'
k.add :list_contacts, "List contacts", 'C'
k.add :redraw, "Redraw screen", :ctrl_l
- k.add :search, "Search messages", '/'
+ k.add :search, "Search all messages", '\\'
k.add :list_labels, "List labels", 'L'
k.add :poll, "Poll for new messages", 'P'
k.add :compose, "Compose new message", 'm'
+ k.add :nothing, "Do nothing", :ctrl_g
k.add :recall_draft, "Edit most recent draft message", 'R'
end
@@ -157,6 +158,7 @@ begin
c.add :reply_mode_selected_color, Ncurses::COLOR_YELLOW, Ncurses::COLOR_BLACK, Ncurses::A_BOLD
c.add :reply_mode_unselected_color, Ncurses::COLOR_CYAN, Ncurses::COLOR_BLACK
c.add :reply_mode_label_color, Ncurses::COLOR_CYAN, Ncurses::COLOR_BLACK
+ c.add :search_highlight_color, Ncurses::COLOR_BLACK, Ncurses::COLOR_YELLOW, Ncurses::A_BOLD, :highlight => :search_highlight_color
end
log "initializing buffer manager"
@@ -274,7 +276,7 @@ begin
when :redraw
bm.completely_redraw_screen
else
- bm.flash "Unknown key press '#{c.to_character}' for #{bm.focus_buf.mode.name}."
+ bm.flash "Unknown keypress '#{c.to_character}' for #{bm.focus_buf.mode.name}."
end
end
diff --git a/lib/sup/buffer.rb b/lib/sup/buffer.rb
@@ -133,6 +133,11 @@ class BufferManager
attr_reader :focus_buf
+ ## we have to define the key used to continue in-buffer search here, because
+ ## it has special semantics that BufferManager deals with---current searches
+ ## are canceled by any keypress except this one.
+ CONTINUE_IN_BUFFER_SEARCH_KEY = "n"
+
def initialize
@name_map = {}
@buffers = []
@@ -190,7 +195,13 @@ class BufferManager
end
def handle_input c
- @focus_buf && @focus_buf.mode.handle_input(c)
+ if @focus_buf
+ if @focus_buf.mode.in_search? && c != CONTINUE_IN_BUFFER_SEARCH_KEY[0]
+ @focus_buf.mode.cancel_search!
+ @focus_buf.mark_dirty
+ end
+ @focus_buf.mode.handle_input c
+ end
end
def exists? n; @name_map.member? n; end
diff --git a/lib/sup/colormap.rb b/lib/sup/colormap.rb
@@ -22,14 +22,14 @@ class Colormap
[]) + [nil]
end
- def add sym, fg, bg, *attrs
- raise ArgumentError, "color for #{sym} already defined" if
- @entries.member? sym
+ def add sym, fg, bg, attr=nil, opts={}
+ raise ArgumentError, "color for #{sym} already defined" if @entries.member? sym
raise ArgumentError, "color '#{fg}' unknown" unless CURSES_COLORS.include? fg
raise ArgumentError, "color '#{bg}' unknown" unless CURSES_COLORS.include? bg
+ attrs = [attr].flatten.compact
@entries[sym] = [fg, bg, attrs, nil]
- @entries[highlight_sym(sym)] = highlight_for(fg, bg, attrs) + [nil]
+ @entries[highlight_sym(sym)] = opts[:highlight] ? @entries[opts[:highlight]] : highlight_for(fg, bg, attrs) + [nil]
end
def highlight_sym sym
diff --git a/lib/sup/mode.rb b/lib/sup/mode.rb
@@ -27,6 +27,8 @@ class Mode
def draw; end
def focus; end
def blur; end
+ def cancel_search!; end
+ def in_search?; false end
def status; ""; end
def resize rows, cols; end
def cleanup
diff --git a/lib/sup/modes/line-cursor-mode.rb b/lib/sup/modes/line-cursor-mode.rb
@@ -55,6 +55,19 @@ protected
buffer.mark_dirty
end
+ ## override search behavior to be cursor-based
+ def search_goto_line line
+ while line > botline
+ page_down
+ end
+ while line < topline
+ page_up
+ end
+ set_cursor_pos line
+ end
+
+ def search_start_line; @curpos end
+
def line_down # overwrite scrollmode
super
call_load_more_callbacks([topline + buffer.content_height - lines, 10].max) if topline + buffer.content_height > lines
diff --git a/lib/sup/modes/scroll-mode.rb b/lib/sup/modes/scroll-mode.rb
@@ -24,6 +24,8 @@ class ScrollMode < Mode
k.add :jump_to_start, "Jump to top", :home, '^', '1'
k.add :jump_to_end, "Jump to bottom", :end, '$', '0'
k.add :jump_to_left, "Jump to the left", '['
+ k.add :search_in_buffer, "Search in current buffer", '/'
+ k.add :continue_search_in_buffer, "Jump to next search occurrence in buffer", BufferManager::CONTINUE_IN_BUFFER_SEARCH_KEY
end
def initialize opts={}
@@ -31,6 +33,8 @@ class ScrollMode < Mode
@slip_rows = opts[:slip_rows] || 0 # when we pgup/pgdown,
# how many lines do we keep?
@twiddles = opts.member?(:twiddles) ? opts[:twiddles] : true
+ @search_query = nil
+ @search_line = nil
super()
end
@@ -49,6 +53,35 @@ class ScrollMode < Mode
@status = "lines #{@topline + 1}:#{@botline}/#{lines}"
end
+ def in_search?; @search_line end
+
+ def cancel_search!; @search_line = nil end
+
+ def continue_search_in_buffer
+ unless @search_query
+ BufferManager.flash "No current search!"
+ return
+ end
+
+ if(line = find_text(@search_query, @search_line || search_start_line))
+ @search_line = line + 1
+ search_goto_line line
+ buffer.mark_dirty
+ else
+ BufferManager.flash "Not found!"
+ end
+ end
+
+ def search_in_buffer
+ query = BufferManager.ask(:search, "query: ") or return
+ @search_query = Regexp.escape query
+ continue_search_in_buffer
+ end
+
+ ## subclasses can override these two!
+ def search_goto_line line; jump_to_line line end
+ def search_start_line; @topline end
+
def col_left
return unless @leftcol > 0
@leftcol -= COL_JUMP
@@ -98,40 +131,91 @@ class ScrollMode < Mode
protected
+ def find_text query, start_line
+ regex = /#{query}/i
+ (start_line ... lines).each do |i|
+ case(s = self[i])
+ when String
+ return i if s =~ regex
+ when Array
+ return i if s.any? { |color, string| string =~ regex }
+ end
+ end
+ nil
+ end
+
def draw_line ln, opts={}
+ regex = /(#{@search_query})/i
case(s = self[ln])
when String
- buffer.write ln - @topline, 0, s[@leftcol .. -1],
- :highlight => opts[:highlight]
+ if in_search?
+ draw_line_from_array ln, matching_text_array(s, regex), opts
+ else
+ draw_line_from_string ln, s, opts
+ end
when Array
- xpos = 0
+ if in_search?
+ ## seems like there ought to be a better way of doing this
+ array = []
+ s.each do |color, text|
+ if text =~ regex
+ array += matching_text_array text, regex, color
+ else
+ array << [color, text]
+ end
+ end
+ draw_line_from_array ln, array, opts
+ else
+ draw_line_from_array ln, s, opts
+ end
+ else
+ raise "unknown drawable object: #{s.inspect}" # good for debugging
+ end
## speed test
# str = s.map { |color, text| text }.join
# buffer.write ln - @topline, 0, str, :color => :none, :highlight => opts[:highlight]
# return
+ end
- s.each do |color, text|
- raise "nil text for color '#{color}'" if text.nil? # good for debugging
- if xpos + text.length < @leftcol
- buffer.write ln - @topline, 0, "", :color => color,
- :highlight => opts[:highlight]
- xpos += text.length
- elsif xpos < @leftcol
- ## partial
- buffer.write ln - @topline, 0, text[(@leftcol - xpos) .. -1],
- :color => color,
- :highlight => opts[:highlight]
- xpos += text.length
- else
- buffer.write ln - @topline, xpos - @leftcol, text,
- :color => color, :highlight => opts[:highlight]
- xpos += text.length
- end
+ def matching_text_array s, regex, oldcolor=:none
+ s.split(regex).map do |text|
+ next if text.empty?
+ if text =~ regex
+ [:search_highlight_color, text]
+ else
+ [oldcolor, text]
+ end
+ end.compact + [[oldcolor, ""]]
+ end
+ def draw_line_from_array ln, a, opts
+ xpos = 0
+ a.each do |color, text|
+ raise "nil text for color '#{color}'" if text.nil? # good for debugging
+
+ if xpos + text.length < @leftcol
+ buffer.write ln - @topline, 0, "", :color => color,
+ :highlight => opts[:highlight]
+ xpos += text.length
+ elsif xpos < @leftcol
+ ## partial
+ buffer.write ln - @topline, 0, text[(@leftcol - xpos) .. -1],
+ :color => color,
+ :highlight => opts[:highlight]
+ xpos += text.length
+ else
+ buffer.write ln - @topline, xpos - @leftcol, text,
+ :color => color, :highlight => opts[:highlight]
+ xpos += text.length
end
end
end
+
+ def draw_line_from_string ln, s, opts
+ buffer.write ln - @topline, 0, s[@leftcol .. -1], :highlight => opts[:highlight]
+ end
end
end
+
diff --git a/lib/sup/modes/thread-view-mode.rb b/lib/sup/modes/thread-view-mode.rb
@@ -236,6 +236,7 @@ class ThreadViewMode < LineCursorMode
end
def jump_to_next_open
+ return continue_search_in_buffer if in_search? # hack: allow 'n' to apply to both operations
m = @message_lines[curpos] or return
while nextm = @layout[m].next
break if @layout[nextm].state != :closed