]> git.cworth.org Git - sup/commitdiff
add buffer search with '/' and 'n', and change index search to '\'
authorwmorgan <wmorgan@5c8cc53c-5e98-4d25-b20a-d8db53a31250>
Tue, 6 Nov 2007 22:36:13 +0000 (22:36 +0000)
committerwmorgan <wmorgan@5c8cc53c-5e98-4d25-b20a-d8db53a31250>
Tue, 6 Nov 2007 22:36:13 +0000 (22:36 +0000)
git-svn-id: svn://rubyforge.org/var/svn/sup/trunk@669 5c8cc53c-5e98-4d25-b20a-d8db53a31250

bin/sup
lib/sup/buffer.rb
lib/sup/colormap.rb
lib/sup/mode.rb
lib/sup/modes/line-cursor-mode.rb
lib/sup/modes/scroll-mode.rb
lib/sup/modes/thread-view-mode.rb

diff --git a/bin/sup b/bin/sup
index b69530658f146b4e9eb4d8a88d0ce2d4112b6f49..e2cb94504b9aefb683a5bc2a895667cb9cd76479 100644 (file)
--- 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
 
index c96232f05520720e40f0d9d7f6d6647ccdd81b4a..4bc8788e3c15debf0ebcefdc62301e3605959853 100644 (file)
@@ -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
index f60f84b240f9a586a68f400fd129305bcf41f777..004df263197039c5ad3dbd8da71c80abacfbd600 100644 (file)
@@ -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
index 7d437e2237c19eeb3c85fcd2700fe119230bde1e..4f2c28c70d42d797c78dccfb40c40b3e7cac103a 100644 (file)
@@ -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
index 602e721515ab22331f60bf8951608565133917b0..7fd55557bbb3428f34463afcd331f33627cc0ff7 100644 (file)
@@ -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
index 513ecc90a030dc16f8a1dffb9633270f7a0e548d..b19db2cd9fccc859d62aaa123fe609959c9b3be8 100644 (file)
@@ -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
+
index 2ea3c25e7cb43717799811242e7a55963d412daf..6bc716493eabf32562209b8cc110cbdf65c79093 100644 (file)
@@ -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