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); +} | 
