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 23use strict; 24use Irssi 20070804; 25use Irssi::TextUI; 26 27use vars qw($VERSION %IRSSI); 28$VERSION = '2.1'; 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); 37 38# is the searching enabled? 39my $enabled = 0; 40# the typed text (the query) last time a key was pressed 41my $prev_typed; 42# the position in the input of where the typed text started. 43# everything before it is not typed by the user but added by this script as part of the result 44my $prev_startpos; 45# the current list of matches 46my @matches; 47# at what place are we in @matches? 48my $current_match_index; 49 50Irssi::command_bind('history_search', sub { 51 my ($data, $server, $item) = @_; 52 if ($data !~ /^ *(-forward)? *$/) { 53 Irssi::print("history_search: Unknown arguments: $data"); 54 return; 55 } 56 my $forward = $1 eq '-forward'; 57 58 if (! $enabled) { 59 $enabled = 1; 60 $prev_typed = ''; 61 $prev_startpos = 0; 62 @matches = (); 63 $current_match_index = -1; 64 } else { 65 if ($forward) { 66 if ($current_match_index + 1 < scalar(@matches)) { 67 $current_match_index++; 68 } 69 } else { # backwards 70 if ($current_match_index > 0) { 71 $current_match_index--; 72 } 73 } 74 } 75}); 76 77Irssi::signal_add_last 'gui key pressed' => sub { 78 my ($key) = @_; 79 80 if ($key == 10 || $key == 13 || $key == 27) { # enter or escape 81 $enabled = 0; 82 } 83 84 return unless $enabled; 85 86 # get the content of the input line 87 my $prompt = Irssi::parse_special('$L'); 88 my $pos = Irssi::gui_input_get_pos(); 89 90 # stop if the cursor is before the position where the typing started (e.g. if user pressed backspace more than he typed characters) 91 if ($pos < $prev_startpos) { 92 $enabled = 0; 93 return; 94 } 95 96 # get the part of the input line that the user typed (strip the part before and after which this script added) 97 my $typed = substr($prompt, $prev_startpos, ($pos-$prev_startpos)); 98 99 if ($typed ne $prev_typed) { # something changed 100 # find matches 101 find_matches($typed); 102 103 # start searching from the end again 104 $current_match_index = scalar(@matches) - 1; 105 } 106 107 # if nothing was found, just show what the user typed 108 # else, show the current match 109 my $result = ($current_match_index == -1) ? $typed : $matches[$current_match_index]; 110 111 # update the input line 112 my $startpos = index(lc($result), lc($typed)); 113 Irssi::gui_input_set($result); 114 Irssi::gui_input_set_pos($startpos + length($typed)); 115 116 # remember for next time 117 $prev_typed = $typed; 118 $prev_startpos = $startpos; 119}; 120 121# find matches for the given user-typed text, and put it in @matches 122sub find_matches($) { 123 my ($typed) = @_; 124 if (Irssi::version() > 20090117) { 125 $typed = lc($typed); 126 my @history; 127 if ($prev_typed ne '' && index($typed, lc($prev_typed)) != -1) { # previous typed plus more 128 @history = @matches; # only search in previous results 129 } else { 130 @history = Irssi::active_win->get_history_lines(); 131 } 132 @matches = (); 133 for my $history_line (@history) { 134 my $startpos = index(lc($history_line), $typed); 135 if ($startpos != -1) { 136 push @matches, $history_line; 137 } 138 } 139 } else { # older irssi version, can only get the last match 140 @matches = (); 141 my $last_match = Irssi::parse_special('$!' . $typed . '!'); 142 if ($last_match ne '') { 143 push @matches, $last_match; 144 } 145 } 146} 147