diff options
-rw-r--r-- | auto-server/auto_server.pl | 208 | ||||
-rw-r--r-- | feature-tests/easy_exec.pl | 35 | ||||
-rw-r--r-- | feature-tests/template.pl | 8 | ||||
-rw-r--r-- | history-search/rl_history_search.pl | 13 | ||||
-rw-r--r-- | ido-mode/ido_switcher.pl | 838 | ||||
-rw-r--r-- | prompt_info/uberprompt.pl | 13 | ||||
-rw-r--r-- | sb-position/sb_position.pl | 112 |
7 files changed, 1218 insertions, 9 deletions
diff --git a/auto-server/auto_server.pl b/auto-server/auto_server.pl new file mode 100644 index 0000000..7054664 --- /dev/null +++ b/auto-server/auto_server.pl @@ -0,0 +1,208 @@ +# This script was initially written by shabble, this fork (and molestation) is +# built upon his original work. +# +# USAGE: +# +# the primary command used is /join+ #channelname +# +# Mappings for channels to servers is accomplished with the +# joinplus_server_maps setting. +# +# Within this setting, space separated pairs denote channel, server pairs. +# Spaces also separate individual pairs, for example: +# +# /set joinplus_server_maps #foo Freenode #bar irc.somewhere.tld #meep DALNet +# +# Then use /join+ #foo, and if you are not already connected to freenode, it +# will connect you, and then join that channel. + +# TODO: +# Autocompletion for channel names +# address conflict resolution +# fix that disgusting race condition + +use strict; +use warnings; + + +use Irssi; +use Irssi::Irc; +use Irssi::TextUI; + +use Data::Dumper; + + +my $DEBUG_ENABLED = 0; +sub DEBUG () { $DEBUG_ENABLED } + +sub _debug_print { + return unless DEBUG; + return unless scalar (grep { defined && length } @_) == @_; + my $win = Irssi::active_win; + my $str = join('', @_); + $win->print($str, Irssi::MSGLEVEL_CLIENTCRAP); +} + +our $VERSION = '0.1'; + +our %IRSSI = ( + authors => 'shabble, richo', + contact => 'richo@psych0tik.net', + name => 'auto-join-ng', + description => 'connects to a specified server in order to connect' . + ' to a channel there, without having first to' . + ' connect to the server', + license => 'Public Domain', + ); + +my $channel_map; +my @hack_channels; +my $pending_joins; + +sub auto_server_init { + Irssi::command_bind('join+', \&join_plus); + Irssi::settings_add_str('join_plus', 'joinplus_server_maps', ''); + Irssi::signal_add_last('setup changed', \&setup_changed); + Irssi::settings_add_bool('join_plus', 'join_plus_debug', 0); + + setup_changed(); + $pending_joins = {}; + +} + +sub setup_changed { + $DEBUG_ENABLED = Irssi::settings_get_bool('join_plus_debug'); + parse_channel_map(); +} + +# This is a tremendous kludge. +# If anyone knows a better way to get this listing, I'd like to hear it. +# This has so many race condition bugs I just don't even know where to start. +sub retrieve_channels { + @hack_channels = (); + Irssi::signal_add_first('print text', 'haxy_print_hook'); + Irssi::command("CHANNEL LIST"); + Irssi::signal_remove('print text', 'haxy_print_hook'); + return join(" ", @hack_channels); +} + + +# The idea for how to do this courtesy of http://wouter.coekaerts.be/site/irssi/aliases +sub haxy_print_hook { + Irssi::signal_remove('print text', 'haxy_print_hook'); + Irssi::signal_stop(); + my $data = $_[1]; + # Hose control characters + $data =~ s/\x04.//g; + if ($data =~ m/^#/) { + my @items = split /\s+/, $data; + push(@hack_channels, $items[0]); + push(@hack_channels, $items[1]); + } + Irssi::signal_add_first('print text', 'haxy_print_hook'); +} + +sub parse_channel_map { + #my $data = Irssi::settings_get_str('joinplus_server_maps'); + my $data = retrieve_channels(); + my @items = split /\s+/, $data; + if (@items % 2 == 0) { + $channel_map = { @items }; # risky? + } else { + Irssi::active_win->print("Could not process channel => server mappings"); + $channel_map = {}; + } + _debug_print Dumper($channel_map); +} + +sub join_plus { + my ($args, $cmd_server, $witem) = @_; + #print Dumper($cmd, "moo", $win); + + # parse out channel name from args: + my $channel; + if ($args =~ m/^(#?[#a-zA-Z0-9]+)/) { + $channel = $1; + _debug_print ("Channel is: $channel"); + } + + unless ($channel) { + Irssi::active_win()->print("Channel $args not recognised"); + return; + } + + # lookup server + my $server_id = $channel_map->{$channel}; + _debug_print($server_id); + + unless ($server_id) { + Irssi::active_win()->print("Channel $channel does not have an" + . " appropriate server mapping"); + return; + } + # TODO: search values() and give a 'did you mean' for closest channel + + # check if we're connected to that server + my $server = Irssi::server_find_tag($server_id); + + if (not defined $server) { + $server = Irssi::server_find_chatnet($server_id); + } + + if (not defined $server) { + # still no server, walk the server list looking for address matches. + my @servers = Irssi::servers(); + foreach my $srv (@servers) { + if (($srv->{address} eq $server_id) or + ($srv->{real_address} eq $server_id)) { + $server = $srv; + last; + } + } + } + + if (defined $server) { + + _debug_print ("Already connected to server: " . $server->{tag} ); + + # check if we're already on the required channel + my $on_channel = $server->channel_find($channel); + + if (defined $channel && ref($channel) eq 'Irssi::Irc::Channel') { + Irssi::active_win()->print("You are already connected to " + . " $channel on " . $server->{tag}); + return; + } else { + _debug_print ("joining channel: $channel"); + $server->command("JOIN $channel"); + } + } else { + # not connected to server. + _debug_print ("connecting to server: $server_id"); + + Irssi::command("CONNECT $server_id"); + _debug_print ("awaiting connection for join"); + + $pending_joins->{$server_id} = $channel; + Irssi::signal_add_last("event 376", 'do_channel_join'); + } +} + +sub do_channel_join { + my ($serv) = @_; + #_debug_print("server is " . Dumper($serv)); + _debug_print(sprintf("server is %s (%s)", $serv->{address}, $serv->{tag})); + + my $channel = $pending_joins->{$serv->{address}}; + $channel = $pending_joins->{$serv->{tag}} unless $channel; + + _debug_print ("attempting to join $channel"); + + Irssi::server_find_tag($serv->{tag})->command("JOIN $channel"); + + delete $pending_joins->{$serv->{address}}; + delete $pending_joins->{$serv->{tag}}; + +} + +auto_server_init(); diff --git a/feature-tests/easy_exec.pl b/feature-tests/easy_exec.pl new file mode 100644 index 0000000..dbcd101 --- /dev/null +++ b/feature-tests/easy_exec.pl @@ -0,0 +1,35 @@ +use strict; +use warnings; + +# export everything. +use Irssi (@Irssi::EXPORT_OK); +use Irssi::Irc; +use Irssi::TextUI; + +use Data::Dumper; + +our $VERSION = '0.1'; +our %IRSSI = ( + authors => 'shabble', + contact => 'shabble+irssi@metavore.org', + name => 'easy_exec', + description => 'drop-in replacement for /script exec which imports' + . ' all of the Irssi:: namespace for easier testing', + license => 'Public Domain', + ); + +Irssi::signal_add_first 'command script exec', \&better_exec; + +sub better_exec { + my ($args, $serv, $witem) = @_; + eval $args; + Irssi::signal_stop(); +} + +sub Dump { + print Dumper(\@_); +} + +sub test() { + print "This is a test"; +} diff --git a/feature-tests/template.pl b/feature-tests/template.pl index 2c75044..f3ae68a 100644 --- a/feature-tests/template.pl +++ b/feature-tests/template.pl @@ -3,12 +3,16 @@ use warnings; use Irssi; +use Irssi::Irc; +use Irssi::TextUI; + +use Data::Dumper; our $VERSION = '0.1'; our %IRSSI = ( - authors => '', - contact => '', + authors => 'shabble', + contact => 'shabble+irssi@metavore.org', name => '', description => '', license => 'Public Domain', diff --git a/history-search/rl_history_search.pl b/history-search/rl_history_search.pl index fcdde8b..ca45fe5 100644 --- a/history-search/rl_history_search.pl +++ b/history-search/rl_history_search.pl @@ -90,22 +90,29 @@ sub script_is_loaded { return $retval; } -unless (script_is_loaded('uberprompt')) { +if (not script_is_loaded('uberprompt')) { + print "This script requires 'uberprompt.pl' in order to work. " . "Attempting to load it now..."; - Irssi::signal_add('script error', \&load_uberprompt_failed); + + Irssi::signal_add('script error', 'load_uberprompt_failed'); Irssi::command("script load uberprompt.pl"); + unless(script_is_loaded('uberprompt')) { load_uberprompt_failed("File does not exist"); } history_init(); +} else { + history_init(); } sub load_uberprompt_failed { - Irssi::signal_remove('script error', \&load_prompt_failed); + Irssi::signal_remove('script error', 'load_prompt_failed'); + print "Script could not be loaded. Script cannot continue. " . "Check you have uberprompt.pl installed in your path and " . "try again."; + die "Script Load Failed: " . join(" ", @_); } diff --git a/ido-mode/ido_switcher.pl b/ido-mode/ido_switcher.pl new file mode 100644 index 0000000..5d6be5a --- /dev/null +++ b/ido-mode/ido_switcher.pl @@ -0,0 +1,838 @@ +# Search and select windows similar to ido-mode for emacs +# +# INSTALL: +# +# This script requires that you have first installed and loaded 'uberprompt.pl' +# Uberprompt can be downloaded from: +# +# http://github.com/shabble/irssi-scripts/raw/master/prompt_info/uberprompt.pl +# +# and follow the instructions at the top of that file for installation. +# +# SETUP: +# +# * Setup: /bind ^G /ido_switch_start +# +# * Then type ctrl-G and type what you're searching for +# +# USAGE: +# +# C-g (or whatever you've set the above bind to), enters window switching mode. +# +# NB: When entering window switching mode, the contents of your input line will +# be saved and cleared, to avoid visual clutter whilst using the switching +# interface. It will be restored once you exit the mode using either C-g, Esc, +# or RET. + +# The following key-bindings are available only once the mode has been +# activated: +# +# * C-g - cancel out of the mode without changing windows. +# * Esc - cancel out, as above. +# * C-s - rotate the list of window candidates forward by 1 +# * C-r - rotate the list of window candidates backward by 1 +# * C-e - Toggle 'Active windows only' filter +# * C-f - Switch between 'Flex' and 'Exact' matching. +# * C-d - Select a network or server to filter candidates by +# * C-u - Clear the current search string +# * C-q - Cycle between showing only queries, channels, or all. +# * C-SPC - Filter candidates by current search string, and then reset +# the search string +# * RET - Select the current head of the candidate list (the green one) +# * SPC - Select the current head of the list, without exiting the +# switching mode. The head is then moved one place to the right, +# allowing one to cycle through channels by repeatedly pressing space. +# * TAB - [currently in development] displays all possible completions +# at the bottom of the current window. +# * All other keys (a-z, A-Z, etc) - Add that character to the current search +# string. +# +# USAGE NOTES: +# +# * Using C-e (show actives), followed by repeatedly pressing space will cycle +# through all your currently active windows. +# +# * If you enter a search string fragment, and realise that more than one candidate +# is still presented, rather than delete the whole string and modify it, you can +# use C-SPC to 'lock' the current matching candidates, but allow you to search +# through those matches alone. +# +# Based in part on window_switcher.pl script Copyright 2007 Wouter Coekaerts +# <coekie@irssi.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +use strict; +use Irssi; +use Irssi::TextUI; +use Data::Dumper; + +use vars qw($VERSION %IRSSI); +$VERSION = '2.0'; +%IRSSI = + ( + authors => 'Tom Feist, Wouter Coekaerts', + contact => 'shabble+irssi@metavore.org, shabble@#irssi/freenode', + name => 'ido_switcher', + description => 'Select window[-items] using an ido-mode like search interface', + license => 'GPLv2 or later', + url => 'http://github.com/shabble/irssi-scripts/tree/master/ido-mode/', + changed => '24/7/2010' + ); + + +# TODO: +# DONE C-g - cancel +# DONE C-spc - narrow +# DONE flex matching (on by default, but optional) +# TODO server/network narrowing +# DONE colourised output (via uberprompt) +# DONE C-r / C-s rotate matches +# DONE toggle queries/channels +# DONE remove inputline content, restore it afterwards. +# TODO tab - display all possibilities in window (clean up afterwards) +# how exactly will this work? +# DONE sort by recent activity/recently used windows (separate commands?) +# TODO need to be able to switch ordering of active ones (numerical, or most recently +# active, priority to PMs/hilights, etc?) +# DONE should space auto-move forward to next window for easy stepping through +# sequential/active windows? + +my $input_copy = ''; +my $input_pos_copy = 0; + +my $ido_switch_active = 0; # for intercepting keystrokes + +my @window_cache = (); +my @search_matches = (); + +my $match_index = 0; +my $search_str = ''; +my $active_only = 0; + +my $mode_type = 'ALL'; +my @mode_cache; + +my $need_clear = 0; + +my $sort_ordering = "start-asc"; + +# /set configurable settings +my $ido_show_count; +my $ido_use_flex; + +my $DEBUG_ENABLED = 0; +sub DEBUG () { $DEBUG_ENABLED } + + +sub MODE_WIN () { 0 } # windows +sub MODE_NET () { 1 } # chatnets +#sub MODE_C () { 2 } # channels +#sub MODE_S () { 3 } # select server +#sub MODE_W () { 4 } # select window + +my $MODE = MODE_WIN; + +# check we have uberprompt loaded. + +sub _print { + my $win = Irssi::active_win; + my $str = join('', @_); + $need_clear = 1; + $win->print($str, Irssi::MSGLEVEL_NEVER); +} + +sub _debug_print { + return unless DEBUG; + my $win = Irssi::active_win; + my $str = join('', @_); + $win->print($str, Irssi::MSGLEVEL_CLIENTCRAP); +} + +sub _print_clear { + return unless $need_clear; + my $win = Irssi::active_win(); + $win->command('/scrollback levelclear -level NEVER'); +} + +sub print_all_matches { + my $msg = join(", ", map { $_->{name} } @search_matches); + my $message_header = "Windows:"; + my $win = Irssi::active_win(); + my $win_width = $win->{width} || 80; + + # TODO: needs to prefix ambig things with chatnet, or maybe order in groups + # by chatnet with newlines. + + # Also, colourise the channel list. + + my $col_width; + + for (@search_matches) { + my $len = length($_->{name}); + $col_width = $len if $len > $col_width; + } + + my $cols = int($win_width / $col_width); + + my @lines; + my $i = 0; + my @line; + + for my $item (@search_matches) { + my $name = $item->{name}; + push @line, sprintf('%.*s', $col_width, $name); + if ($i == $cols) { + push @lines, join ' ', @line; + @line = (); + $i = 0; + } + } + # flush rest out. + push @lines, join ' ', @line; + + _print($message_header); + _print($_) for (@lines); + #_print("Longtest name: $longest_name"); +} + + sub script_is_loaded { + my $name = shift; + _debug_print "Checking if $name is loaded"; + no strict 'refs'; + my $retval = defined %{ "Irssi::Script::${name}::" }; + use strict 'refs'; + + return $retval; + } + +unless (script_is_loaded('uberprompt')) { + + _print "This script requires '\%_uberprompt.pl\%_' in order to work. " + . "Attempting to load it now..."; + + Irssi::signal_add('script error', 'load_uberprompt_failed'); + Irssi::command("script load uberprompt.pl"); + + unless(script_is_loaded('uberprompt')) { + load_uberprompt_failed("File does not exist"); + } + ido_switch_init(); +} + +sub load_uberprompt_failed { + Irssi::signal_remove('script error', 'load_uberprompt_failed'); + _print "Script could not be loaded. Script cannot continue. " + . "Check you have uberprompt.pl installed in your path and " + . "try again."; + die "Script Load Failed: " . join(" ", @_); +} + +sub ido_switch_init { + Irssi::settings_add_bool('ido_switch', 'ido_switch_debug', 0); + Irssi::settings_add_bool('ido_switch', 'ido_use_flex', 1); + Irssi::settings_add_int ('ido_switch', 'ido_show_count', 5); + + Irssi::command_bind('ido_switch_start', \&ido_switch_start); + + Irssi::signal_add ('setup changed' => \&setup_changed); + Irssi::signal_add_first('gui key pressed' => \&handle_keypress); + + setup_changed(); +} + +sub setup_changed { + $DEBUG_ENABLED = Irssi::settings_get_bool('ido_switch_debug'); + $ido_show_count = Irssi::settings_get_int ('ido_show_count'); + $ido_use_flex = Irssi::settings_get_bool('ido_use_flex'); +} + + +sub ido_switch_start { + # store copy of input line to restore later. + $input_copy = Irssi::parse_special('$L'); + $input_pos_copy = Irssi::gui_input_get_pos(); + + Irssi::gui_input_set(''); + + # set startup flags + $ido_switch_active = 1; + $search_str = ''; + $match_index = 0; + $mode_type = 'ALL'; + + # refresh in case we toggled it last time. + $ido_use_flex = Irssi::settings_get_bool('ido_use_flex'); + $active_only = 0; + + _debug_print "Win cache: " . join(", ", map { $_->{name} } @window_cache); + + _update_cache(); + + update_matches(); + update_window_select_prompt(); +} + +sub _update_cache { + @window_cache = get_all_windows(); +} + +sub _build_win_obj { + my ($win, $win_item) = @_; + + my @base = ( + b_pos => -1, + e_pos => -1, + hilight_field => 'name', + active => $win->{data_level} > 0, + num => $win->{refnum}, + server => $win->{active_server}, + + ); + + if (defined($win_item)) { + return ( + @base, + name => $win_item->{visible_name}, + type => $win_item->{type}, + itemname => $win_item->{name}, + active => $win_item->{data_level} > 0, + + ) + } else { + return ( + @base, + name => $win->{name}, + type => 'WIN', + ); + } +} + +sub get_all_windows { + my @ret; + + foreach my $win (Irssi::windows()) { + my @items = $win->items(); + + if ($win->{name} ne '') { + _debug_print "Adding window: " . $win->{name}; + push @ret, { _build_win_obj($win, undef) }; + } + if (scalar @items) { + foreach my $item (@items) { + _debug_print "Adding windowitem: " . $item->{visible_name}; + push @ret, { _build_win_obj($win, $item) }; + } + } else { + _debug_print "Error occurred reading info from window: $win"; + #_debug_print Dumper($win); + } + } + @ret = _sort_windows(\@ret); + + return @ret; + +} + + sub _sort_windows { + my $list_ref = shift; + my @ret = @$list_ref; + + @ret = sort { $a->{num} <=> $b->{num} } @ret; + + return @ret; + } + + sub ido_switch_select { + my ($selected) = @_; + + _debug_print "Selecting window: " . $selected->{name}; + + Irssi::command("WINDOW GOTO " . $selected->{name}); + + if ($selected->{type} ne 'WIN') { + _debug_print "Selecting window item: " . $selected->{itemname}; + Irssi::command("WINDOW ITEM GOTO " . $selected->{itemname}); + } + + update_matches(); + } + + sub ido_switch_exit { + $ido_switch_active = 0; + + _print_clear(); + + Irssi::gui_input_set($input_copy); + Irssi::gui_input_set_pos($input_pos_copy); + Irssi::signal_emit('change prompt', '', 'UP_INNER'); + } + + sub _order_matches { + return @_[$match_index .. $#_, + 0 .. $match_index - 1] + } + + sub update_window_select_prompt { + + # take the top $ido_show_count entries and display them. + my $match_count = scalar @search_matches; + my $show_count = $ido_show_count; + my $match_string = '[No match'; + + $show_count = $match_count if $match_count < $show_count; + + if ($show_count > 0) { # otherwise, default message above. + _debug_print "Showing: $show_count matches"; + + my @ordered_matches = _order_matches(@search_matches); + + my @display = @ordered_matches[0..$show_count - 1]; + + # determine which items are non-unique, if any. + + my %uniq; + + foreach my $res (@display) { + my $name = $res->{name}; + + if (exists $uniq{$name}) { + push @{$uniq{$name}}, $res; + } else { + $uniq{$name} = []; + push @{$uniq{$name}}, $res; + } + } + + # and set a flag to ensure they have their network tag applied + # to them when drawn. + foreach my $name (keys %uniq) { + my @values = @{$uniq{$name}}; + if (@values > 1) { + $_->{display_net} = 1 for @values; + } + } + + # show the first entry in green + + my $first = shift @display; + my $formatted_first = _format_display_entry($first, '%g'); + unshift @display, $formatted_first; + + # and array-slice-map the rest to be red. + # or yellow, if they have unviewed activity + + @display[1..$#display] + = map + { + _format_display_entry($_, $_->{active}?'%y':'%r') + + } @display[1..$#display]; + + # join em all up + $match_string = join ', ', @display; + } + + my @indicators; + + # indicator if flex mode is being used (C-f to toggle) + push @indicators, $ido_use_flex ? 'Flex' : 'Exact'; + push @indicators, 'Active' if $active_only; + push @indicators, ucfirst(lc($mode_type)); + + my $flex = sprintf(' %%k[%%n%s%%k]%%n ', join ',', @indicators); + + my $search = ''; + $search = (sprintf '`%s\': ', $search_str) if length $search_str; + + Irssi::signal_emit('change prompt', $flex . $search . $match_string, + 'UP_INNER'); + } + + + + sub _format_display_entry { + my ($obj, $colour) = @_; + + my $field = $obj->{hilight_field}; + my $hilighted = { name => $obj->{name}, num => $obj->{num} }; + my $show_tag = $obj->{display_net} || 0; + + if ($obj->{b_pos} >= 0 && $obj->{e_pos} > $obj->{b_pos}) { + substr($hilighted->{$field}, $obj->{e_pos}, 0) = '%_'; + substr($hilighted->{$field}, $obj->{b_pos}, 0) = '%_'; + _debug_print "Showing $field as: " . $hilighted->{$field} + } + + return sprintf('%s%s:%s%s%%n', + $colour, + $hilighted->{num}, + $show_tag ? _format_display_tag($obj) : '', + $hilighted->{name}); + } + + sub _format_display_tag { + my $obj = shift; + if (defined $obj->{server}) { + my $server = $obj->{server}; + my $tag = $server->{tag}; + return $tag . '/' if length $tag; + } + return ''; + } + + sub _check_active { + my ($obj) = @_; + return 1 unless $active_only; + return $obj->{active}; + } + + sub update_matches { + + _update_cache() unless $search_str; + + if ($mode_type ne 'ALL') { + @mode_cache = @window_cache; + @window_cache = grep { print "Type: " . $_->{type}; $_->{type} eq $mode_type } @window_cache; + } else { + @window_cache = @mode_cache if @mode_cache; + } + + if ($search_str =~ m/^\d+$/) { + + @search_matches = + grep { + _check_active($_) and regex_match($_, 'num') + } @window_cache; + + } elsif ($ido_use_flex) { + + @search_matches = + grep { + _check_active($_) and flex_match($_) >= 0 + } @window_cache; + + } else { + + @search_matches = + grep { + _check_active($_) and regex_match($_, 'name') + } @window_cache; + } + + } + + sub regex_match { + my ($obj, $field) = @_; + if ($obj->{$field} =~ m/^(.*?)\Q$search_str\E(.*?)$/i) { + $obj->{hilight_field} = $field; + $obj->{b_pos} = length $1; + $obj->{e_pos} = $obj->{b_pos} + length($search_str); + return 1; + } + return 0; + } + + sub flex_match { + my ($obj) = @_; + + my $pattern = $search_str; + my $source = $obj->{name}; + + _debug_print "Flex match: $pattern / $source"; + + # default to matching everything if we don't have a pattern to compare + # against. + + return 0 unless $pattern; + + my @chars = split '', lc($pattern); + my $ret = -1; + my $first = 0; + + my $lc_source = lc($source); + + $obj->{hilight_field} = 'name'; + + foreach my $char (@chars) { + my $pos = index($lc_source, $char, $ret); + if ($pos > -1) { + + # store the beginning of the match + $obj->{b_pos} = $pos unless $first; + $first = 1; + + _debug_print("matched: $char at $pos in $source"); + $ret = $pos + 1; + + } else { + + $obj->{b_pos} = $obj->{e_pos} = -1; + _debug_print "Flex returning: -1"; + + return -1; + } + } + + _debug_print "Flex returning: $ret"; + + #store the end of the match. + $obj->{e_pos} = $ret; + + return $ret; + } + + sub prev_match { + + $match_index++; + if ($match_index > $#search_matches) { + $match_index = 0; + } + + _debug_print "index now: $match_index"; + } + + sub next_match { + + $match_index--; + if ($match_index < 0) { + $match_index = $#search_matches; + } + _debug_print "index now: $match_index"; + } + + sub get_window_match { + return $search_matches[$match_index]; + } + + sub handle_keypress { + my ($key) = @_; + + return unless $ido_switch_active; + + if ($key == 0) { # C-SPC? + _debug_print "\%_Ctrl-space\%_"; + + $search_str = ''; + @window_cache = @search_matches; + update_window_select_prompt(); + + Irssi::signal_stop(); + return; + } + + if ($key == 3) { # C-c + _print_clear(); + Irssi::signal_stop(); + return; + } + if ($key == 4) { # C-d + update_network_select_prompt(); + Irssi::signal_stop(); + return; + } + + if ($key == 5) { # C-e + $active_only = not $active_only; + Irssi::signal_stop(); + update_matches(); + update_window_select_prompt(); + return; + } + + if ($key == 6) { # C-f + + $ido_use_flex = not $ido_use_flex; + _update_cache(); + + update_matches(); + update_window_select_prompt(); + + Irssi::signal_stop(); + return; + } + if ($key == 9) { # TAB + _debug_print "Tab complete"; + print_all_matches(); + Irssi::signal_stop(); + } + + if ($key == 10) { # enter + _debug_print "selecting history and quitting"; + my $selected_win = get_window_match(); + ido_switch_select($selected_win); + + ido_switch_exit(); + Irssi::signal_stop(); + return; + } + + if ($key == 18) { # Ctrl-R + _debug_print "skipping to prev match"; + #update_matches(); + next_match(); + + update_window_select_prompt(); + Irssi::signal_stop(); # prevent the bind from being re-triggered. + return; + } + + if ($key == 17) { # Ctrl-q + if ($mode_type eq 'CHANNEL') { + $mode_type = 'QUERY'; + } elsif ($mode_type eq 'QUERY') { + $mode_type = 'ALL'; + } else { # ALL + $mode_type = 'CHANNEL'; + } + update_matches(); + update_window_select_prompt(); + Irssi::signal_stop(); + } + + if ($key == 19) { # Ctrl-s + _debug_print "skipping to next match"; + prev_match(); + + #update_matches(); + update_window_select_prompt(); + + Irssi::signal_stop(); + return; + } + + if ($key == 7) { # Ctrl-g + _debug_print "aborting search"; + ido_switch_exit(); + Irssi::signal_stop(); + return; + } + + if ($key == 21) { # Ctrl-u + $search_str = ''; + update_matches(); + update_window_select_prompt(); + + Irssi::signal_stop(); + return; + + } + + if ($key == 127) { # DEL + + if (length $search_str) { + $search_str = substr($search_str, 0, -1); + _debug_print "Deleting char, now: $search_str"; + } + + update_matches(); + update_window_select_prompt(); + + Irssi::signal_stop(); + return; + } + + # TODO: handle esc- sequences and arrow-keys? + + if ($key == 27) { # Esc + ido_switch_exit(); + return; + } + + if ($key == 32) { # space + my $selected_win = get_window_match(); + ido_switch_select($selected_win); + + prev_match(); + update_window_select_prompt(); + + Irssi::signal_stop(); + + return; + } + + if ($key > 32) { # printable + $search_str .= chr($key); + + update_matches(); + update_window_select_prompt(); + + Irssi::signal_stop(); + return; + } + + # ignore all other keys. + Irssi::signal_stop(); + } + + ido_switch_init(); + + sub update_network_select_prompt { + + my @servers = map + { + { + name => $_->{tag}, + type => 'SERVER', + active => 0, + e_pos => -1, + b_pos => -1, + hilight_field => 'name', + } + } Irssi::servers(); + + my $match_count = scalar @servers; + my $show_count = $ido_show_count; + my $match_string = '(no matches) '; + + $show_count = $match_count if $match_count < $show_count; + + if ($show_count > 0) { + _debug_print "Showing: $show_count matches"; + + my @ordered_matches = _order_matches(@servers); + my @display = @ordered_matches[0..$show_count - 1]; + + # show the first entry in green + + unshift(@display, _format_display_entry(shift(@display), '%g')); + + # and array-slice-map the rest to be red (or yellow for active) + @display[1..$#display] + = map + { + _format_display_entry($_, $_->{active}?'%y':'%r') + + } @display[1..$#display]; + + # join em all up + $match_string = join ', ', @display; + } + + my @indicators; + + # indicator if flex mode is being used (C-f to toggle) + push @indicators, $ido_use_flex ? 'Flex' : 'Exact'; + push @indicators, 'Active' if $active_only; + + my $flex = sprintf(' %%k[%%n%s%%k]%%n ', join ',', @indicators); + + my $search = ''; + $search = (sprintf '`%s\': ', $search_str) if length $search_str; + + Irssi::signal_emit('change prompt', $flex . $search . $match_string, + 'UP_INNER'); + + } diff --git a/prompt_info/uberprompt.pl b/prompt_info/uberprompt.pl index 41803d8..c1c4a55 100644 --- a/prompt_info/uberprompt.pl +++ b/prompt_info/uberprompt.pl @@ -117,6 +117,8 @@ use Irssi; use Irssi::TextUI; # for sbar_items_redraw use Data::Dumper; +{ package Irssi::Nick } + our $VERSION = "0.2"; our %IRSSI = ( @@ -235,10 +237,6 @@ sub init { Irssi::signal_register({'prompt length request' => []}); Irssi::signal_add('prompt length request', \&length_request_handler); - - if (DEBUG) { - Irssi::signal_add 'prompt changed', \&debug_prompt_changed; - } } sub refresh_if_me { @@ -277,6 +275,13 @@ sub reload_settings { $DEBUG_ENABLED = Irssi::settings_get_bool('uberprompt_debug'); + if (DEBUG) { + Irssi::signal_add 'prompt changed', 'debug_prompt_changed'; + } else { + Irssi::signal_remove 'prompt changed', 'debug_prompt_changed'; + } + + my $new = Irssi::settings_get_str('uberprompt_format'); if ($prompt_format ne $new) { diff --git a/sb-position/sb_position.pl b/sb-position/sb_position.pl new file mode 100644 index 0000000..3c60205 --- /dev/null +++ b/sb-position/sb_position.pl @@ -0,0 +1,112 @@ + +# Display current position in scrollback in a statusbar item named 'position'. + +# Copyright (C) 2010 Simon Ruderich & Tom Feist +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +use strict; +use warnings; + +use Irssi; +use Irssi::TextUI; +use POSIX qw(ceil); + +{ package Irssi::Nick } + +our $VERSION = '0.1'; +our %IRSSI = ( + authors => 'Simon Ruderich, Tom Feist', + contact => 'simon@ruderich.org, shabble+irssi@metavore.org', + name => 'sb_position', + description => 'Displays current position in scrollback.', + license => 'GPLv3 or later', + changed => '2010-12-02' +); + +my ($buf, $size, $pos, $height, $empty); +my ($pages, $cur_page, $percent); + + +init(); + +sub init { + + # (re-)register it so we can access the WIN_REC object directly. + Irssi::signal_register({'gui page scrolled' => [qw/Irssi::UI::Window/]}); + # primary update signal. + Irssi::signal_add('gui page scrolled', \&update_position); + Irssi::statusbar_item_register('position', 0, 'position_statusbar'); + + Irssi::signal_add("window changed", \&update_position); + Irssi::signal_add_last("command clear", \&update_cmd_shim); + Irssi::signal_add_last("command scrollback", \&update_cmd_shim); + # Irssi::signal_add_last("gui print text finished", sig_statusbar_more_updated); + + update_position(Irssi::active_win()); +} + +sub update_cmd_shim { + my ($cmd, $server, $witem) = @_; + + my $win = $witem ? $witem->window : Irssi::active_win; + + update_position($win); +} + +sub update_position { + + my $win = shift; + return unless $win; + + my $view = $win->view; + + $pos = $view->{ypos}; + $buf = $view->{buffer}; + $height = $view->{height}; + $size = $buf->{lines_count}; + + $empty = $view->{empty_linecount}; + $empty = 0 unless $empty; + + + $pages = ceil($size / $height); + $pages = 1 unless $pages; + + my $buf_pos_cache = $size - $pos + ($height - $empty) - 1; + + if ($pos == -1 or $size < $height) { + $cur_page = $pages; + $percent = 100; + } elsif ($pos > ($size - $height)) { + $cur_page = 1; + $percent = 0; + } else { + $cur_page = ceil($buf_pos_cache / $height); + $percent = ceil($buf_pos_cache / $size * 100); + } + + Irssi::statusbar_items_redraw('position'); +} + +sub position_statusbar { + my ($statusbar_item, $get_size_only) = @_; + + # Alternate view. + #my $sb = "p=$pos, s=$size, h=$height, pp:$cur_page/$pages $percent%%"; + my $sb = "Page: $cur_page/$pages $percent%%"; + + $statusbar_item->default_handler($get_size_only, "{sb $sb}", 0, 1); +} |