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 }