html/history_search.pl


   1 # Search within your typed history as you type (like ctrl-R in bash)
   2 # Usage:
   3 # * First do: /bind ^R /history_search
   4 # * Then type ctrl-R and type what you're searching for
   5 # * Optionally, you can bind something to "/history_search -forward" to go forward in the results
   6 
   7 # Copyright 2007-2009  Wouter Coekaerts <coekie@irssi.org>
   8 #
   9 # This program is free software; you can redistribute it and/or modify
  10 # it under the terms of the GNU General Public License as published by
  11 # the Free Software Foundation; either version 2 of the License, or
  12 # (at your option) any later version.
  13 #
  14 # This program is distributed in the hope that it will be useful,
  15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 # GNU General Public License for more details.
  18 #
  19 # You should have received a copy of the GNU General Public License
  20 # along with this program; if not, write to the Free Software
  21 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  22 
  23 use strict;
  24 use Irssi 20070804;
  25 use Irssi::TextUI;
  26 
  27 use vars qw($VERSION %IRSSI);
  28 $VERSION = '2.0';
  29 %IRSSI = (
  30     authors     => 'Wouter Coekaerts',
  31     contact     => 'coekie@irssi.org',
  32     name        => 'history_search',
  33     description => 'Search within your typed history as you type (like ctrl-R in bash)',
  34     license     => 'GPLv2 or later',
  35     url         => 'http://wouter.coekaerts.be/irssi/',
  36     changed     => '17/01/09'
  37 );
  38 
  39 # is the searching enabled?
  40 my $enabled = 0;
  41 # the typed text (the query) last time a key was pressed
  42 my $prev_typed;
  43 # the position in the input of where the typed text started.
  44 # everything before it is not typed by the user but added by this script as part of the result
  45 my $prev_startpos;
  46 # the current list of matches
  47 my @matches;
  48 # at what place are we in @matches?
  49 my $current_match_index;
  50 
  51 Irssi::command_bind('history_search', sub {
  52 	my ($data, $server, $item) = @_;
  53 	if ($data !~ /^ *(-forward)? *$/) {
  54 		Irssi::print("history_search: Unknown arguments: $data");
  55 		return;
  56 	}
  57 	my $forward = $1 eq '-forward';
  58 	
  59 	if (! $enabled) {
  60 		$enabled = 1;
  61 		$prev_typed = '';
  62 		$prev_startpos = 0;
  63 		@matches = ();
  64 		$current_match_index = -1;
  65 	} else {
  66 		if ($forward) {
  67 			if ($current_match_index + 1 < scalar(@matches)) {
  68 				$current_match_index++;
  69 			}
  70 		} else { # backwards
  71 			if ($current_match_index > 0) {
  72 				$current_match_index--;
  73 			}
  74 		}
  75 	}
  76 });
  77 
  78 Irssi::signal_add_last 'gui key pressed' => sub {
  79 	my ($key) = @_;
  80 	
  81 	if ($key == 10 || $key == 27) { # enter or escape
  82 		$enabled = 0;
  83 	}
  84 
  85 	return unless $enabled;
  86 	
  87 	# get the content of the input line
  88 	my $prompt = Irssi::parse_special('$L');
  89 	my $pos = Irssi::gui_input_get_pos();
  90 	
  91 	# stop if the cursor is before the position where the typing started (e.g. if user pressed backspace more than he typed characters)
  92 	if ($pos < $prev_startpos) {
  93 		$enabled = 0;
  94 		return;
  95 	}
  96 	
  97 	# get the part of the input line that the user typed (strip the part before and after which this script added)
  98 	my $typed = substr($prompt, $prev_startpos, ($pos-$prev_startpos));
  99 	
 100 	if ($typed ne $prev_typed) { # something changed
 101 		# find matches
 102 		find_matches($typed);
 103 		
 104 		# start searching from the end again
 105 		$current_match_index = scalar(@matches) - 1;
 106 	}
 107 	
 108 	# if nothing was found, just show what the user typed
 109 	# else, show the current match
 110 	my $result = ($current_match_index == -1) ? $typed : $matches[$current_match_index];
 111 		
 112 	# update the input line
 113 	my $startpos = index(lc($result), lc($typed));
 114 	Irssi::gui_input_set($result);
 115 	Irssi::gui_input_set_pos($startpos + length($typed));
 116 
 117 	# remember for next time
 118 	$prev_typed = $typed;
 119 	$prev_startpos = $startpos;
 120 };
 121 
 122 # find matches for the given user-typed text, and put it in @matches
 123 sub find_matches($) {
 124 	my ($typed) = @_;
 125 	if (Irssi::version() > 20090117) {
 126 		$typed = lc($typed);
 127 		my @history;
 128 		if ($prev_typed ne '' && index($typed, lc($prev_typed)) != -1) { # previous typed plus more
 129 			@history = @matches; # only search in previous results
 130 		} else {
 131 			@history = Irssi::active_win->get_history_lines();
 132 		}
 133 		@matches = ();
 134 		for my $history_line (@history) {
 135 			my $startpos = index(lc($history_line), $typed);
 136 			if ($startpos != -1) {
 137 				push @matches, $history_line;
 138 			}
 139 		}
 140 	} else { # older irssi version, can only get the last match
 141 		@matches = ();
 142 		my $last_match = Irssi::parse_special('$!' . $typed . '!');
 143 		if ($last_match ne '') {
 144 			push @matches, $last_match;
 145 		}
 146 	}
 147 }