From: wmorgan Date: Tue, 6 Nov 2007 22:36:13 +0000 (+0000) Subject: add buffer search with '/' and 'n', and change index search to '\' X-Git-Url: https://git.cworth.org/git?a=commitdiff_plain;h=e8db677a1d4c33cf705df38c684a00fb8c58b835;p=sup 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 --- diff --git a/bin/sup b/bin/sup index b695306..e2cb945 100644 --- 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 index c96232f..4bc8788 100644 --- 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 index f60f84b..004df26 100644 --- 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 index 7d437e2..4f2c28c 100644 --- 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 index 602e721..7fd5555 100644 --- 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 index 513ecc9..b19db2c 100644 --- 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 index 2ea3c25..6bc7164 100644 --- 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