use strict; use warnings; use Irssi; use Irssi::TextUI; # for sbar_items_redraw # /statusbar window add vim_mode to get the status. use vars qw($VERSION %IRSSI); $VERSION = "1.0.1"; %IRSSI = ( authors => "shabble", contact => 'shabble+irssi@metavore.org, shabble@#irssi/Freenode', name => "vim_mode", description => "Give Irssi Vim-like commands for editing the inputline", license => "Public Domain", changed => "20/9/2010" ); #sub DEBUG () { 1 } sub DEBUG () { 0 } # circular buffer to keep track of the last N keystrokes. my @key_buf; my $mode = 0; # 0 is insert, 1 is command. no Ex for now. my $commands = { 'i' => { command => 'insert at cur', func => \&cmd_insert, params => {pos => sub { _input_pos() }}, }, 'I' => { command => 'insert at end', func => \&cmd_insert, params => {pos => sub { _input_len() }}, }, 'A' => { command => 'insert at start', func => \&cmd_insert, params => { pos => sub { 0 } }, }, 'h' => { command => 'move left', func => \&cmd_move, params => { 'dir' => 'left' }, }, 'l' => { command => 'move right', func => \&cmd_move, params => { 'dir' => 'right' }, }, }; sub cmd_jump_word { my ($params) = @_; } sub cmd_insert { my ($params) = @_; my $pos = $params->{pos}->(); _input_pos($pos); $mode = 0; _update_mode(); _stop(); } sub cmd_move { my ($params) = @_; my $dir = $params->{dir}; my $current_pos = _input_pos(); if ($dir eq 'left') { _input_pos($current_pos -1) if $current_pos; } elsif ($dir eq 'right') { my $current_len = _input_len(); _input_pos($current_pos +1) unless $current_pos == $current_len; } else { print "Unknown direction: $dir"; } _stop(); } sub vim_mode_cb { my ($sb_item, $get_size_only) = @_; my $mode_str = ''; if ($mode == 0) { $mode_str = 'Insert'; } else { $mode_str = '%_Command%_'; } $sb_item->default_handler($get_size_only, "{sb $mode_str}", '', 0); } sub got_key { my ($key) = @_; _key_buf_add($key); if ($mode == 0) { # we're in insert mode. if ($key == 27) { # esc $mode = 1; _update_mode(); Irssi::signal_stop(); return; } return; } else { # command mode handle_command($key); } print "Keys: ", join ', ', @key_buf; } sub handle_command { my ($key) = @_; my $char = chr($key); if (exists $commands->{$char}) { my $cmd = $commands->{$char}; print "Going to execute command: ", $cmd->{command}; $cmd->{func}->( $cmd->{params} ); } else { # some error handling. } } Irssi::signal_add_first 'gui key pressed' => \&got_key; Irssi::statusbar_item_register ('vim_mode', 0, 'vim_mode_cb'); sub _key_buf_add { my ($key) = @_; push @key_buf, $key; if (@key_buf > 5) { shift @key_buf; } } sub _input { my ($data) = @_; if (defined $data) { Irssi::gui_input_set($data); } else { $data = Irssi::parse_special('$L', 0, 0) } return $data; } sub _input_len { return length (Irssi::parse_special('$L', 0, 0)); } sub _input_pos { my ($pos) = @_; if (defined $pos) { Irssi::gui_input_set_pos($pos); } else { $pos = Irssi::gui_input_get_pos(); } return $pos; } sub _stop() { Irssi::signal_stop(); } sub _update_mode() { Irssi::statusbar_items_redraw("vim_mode"); }