diff options
Diffstat (limited to 'vim-mode')
-rw-r--r-- | vim-mode/vim_mode.pl | 241 |
1 files changed, 204 insertions, 37 deletions
diff --git a/vim-mode/vim_mode.pl b/vim-mode/vim_mode.pl index 97105d1..6fd88cd 100644 --- a/vim-mode/vim_mode.pl +++ b/vim-mode/vim_mode.pl @@ -29,7 +29,7 @@ use strict; use warnings; use Irssi; -use Irssi::TextUI; # for sbar_items_redraw +use Irssi::TextUI; # for sbar_items_redraw use vars qw($VERSION %IRSSI); @@ -44,56 +44,102 @@ $VERSION = "1.0.1"; changed => "20/9/2010" ); -#sub DEBUG () { 1 } -sub DEBUG () { 0 } +sub DEBUG () { 1 } +#sub DEBUG () { 0 } -# circular buffer to keep track of the last N keystrokes. -my @key_buf; -my $buf_idx = 0; -my $key_buf_timer; -my $key_buf_enabled = 0; -my $should_ignore = 0; +sub A_NUM() { 0 } # expects a specific number of args +sub A_RET() { 1 } # expects a return to indicate end of args. +sub A_NON() { 2 } # expects zero args. +# TODO: do we need a A_FUN that calls a function to check if we've got all of them? sub M_CMD() { 1 } # command mode sub M_INS() { 0 } # insert mode +sub M_EX () { 2 } # extended mode (after a :?) + + +# buffer to keep track of the last N keystrokes following an Esc character. +my @esc_buf; +my $esc_buf_idx = 0; +my $esc_buf_timer; +my $esc_buf_enabled = 0; + +# flag to allow us to emulate keystrokes without re-intercepting them +my $should_ignore = 0; +my $pending_command; + +# argument handling. +my @args_buf; +my $collecting_args = 0; # if we're actively collecting them. +my $args_type = A_NON; # what type of args (constants above) +my $args_num = 0; # how many args we expect + +# for commands like 10x +my $numeric_prefix = undef; + +# what Vi mode we're in. We start in insert mode. my $mode = M_INS; +sub script_is_loaded { + my $name = shift; + print "Checking if $name is loaded" if DEBUG; + no strict 'refs'; + my $retval = defined %{ "Irssi::Script::${name}::" }; + use strict 'refs'; + + return $retval; +} + +unless (script_is_loaded('prompt_info')) { + die "This script requires 'prompt_info' in order to work. " + . "Please load it and try again"; +} else { + vim_mode_init(); +} + + my $commands = { 'i' => { command => 'insert at cur', func => \&cmd_insert, + args => { type => A_NON }, params => {pos => sub { _input_pos() }}, }, 'I' => { command => 'insert at end', func => \&cmd_insert, + args => { type => A_NON }, params => {pos => sub { _input_len() }}, }, 'A' => { command => 'insert at start', func => \&cmd_insert, + args => { type => A_NON }, params => { pos => sub { 0 } }, }, 'h' => { command => 'move left', func => \&cmd_move, + args => { type => A_NON }, params => { 'dir' => 'left' }, }, 'l' => { command => 'move right', func => \&cmd_move, + args => { type => A_NON }, params => { 'dir' => 'right' }, }, 'w' => { command => 'move forward word', func => \&cmd_jump_word, + args => { type => A_NON }, params => { 'dir' => 'fwd', 'pos' => sub { _input_pos() } }, }, 'b' => { command => 'move backward word', func => \&cmd_jump_word, + args => { type => A_NON }, params => { 'dir' => 'back', 'pos' => sub { _input_pos() } }, @@ -101,17 +147,34 @@ my $commands 'x' => { command => 'delete char forward', func => \&cmd_delete_char, + args => { type => A_NON }, params => { 'dir' => 'fwd', 'pos' => sub { _input_pos() } }, }, + ':' => { command => 'search/replace', + func => \&cmd_replace, + args => { type => A_RET }, + params => { 'dir' => 'fwd', + 'pos' => sub { _input_pos() } + }, + } }; +sub cmd_replace { + my ($params) = @_; +} + +sub cmd_ex_mode { + my ($params) = @_; + _set_prompt(":"); +} + sub cmd_delete_char { my ($params) = @_; my $pos = $params->{pos}->(); my $direction = $params->{dir}; - print "Sending keystrokes for delete-char"; + print "Sending keystrokes for delete-char" if DEBUG; _stop(); my @buf = (4); _emulate_keystrokes(@buf); @@ -177,19 +240,26 @@ sub got_key { return if ($should_ignore); if ($key == 27) { - print "Esc seen, starting buffer"; - $key_buf_enabled = 1; + print "Esc seen, starting buffer" if DEBUG; + $esc_buf_enabled = 1; # NOTE: this timeout might be too low on laggy systems, but # it comes at the cost of keystroke latency for things that # contain escape sequences (arrow keys, etc) - $key_buf_timer - = Irssi::timeout_add_once(10, \&handle_key_buffer, undef); + $esc_buf_timer + = Irssi::timeout_add_once(10, \&handle_esc_buffer, undef); + + } elsif ($key == 3 && $mode == M_INS) { + $mode = M_CMD; + _update_mode(); + _stop(); + return; } - if ($key_buf_enabled) { - $key_buf[$buf_idx++] = $key; + if ($esc_buf_enabled) { + $esc_buf[$esc_buf_idx++] = $key; _stop(); + return; } if ($mode == M_CMD) { @@ -198,16 +268,16 @@ sub got_key { } } -sub handle_key_buffer { +sub handle_esc_buffer { - Irssi::timeout_remove($key_buf_timer); - $key_buf_timer = undef; + Irssi::timeout_remove($esc_buf_timer); + $esc_buf_timer = undef; # see what we've collected. - print "Key buffer contains: ", join(", ", @key_buf); + print "Esc buffer contains: ", join(", ", @esc_buf) if DEBUG; - if (@key_buf == 1 && $key_buf[0] == 27) { + if (@esc_buf == 1 && $esc_buf[0] == 27) { - print "Command Mode"; + print "Enter Command Mode" if DEBUG; $mode = M_CMD; _update_mode(); @@ -216,36 +286,125 @@ sub handle_key_buffer { # or pass it off to the command handler. if ($mode == M_CMD) { # command - my $key_str = join '', map { chr } @key_buf; + my $key_str = join '', map { chr } @esc_buf; if ($key_str =~ m/^\e\[([ABCD])/) { - print "Arrow key: $1"; + print "Arrow key: $1" if DEBUG; } else { - print "Dunno what that is." + print "Dunno what that is." if DEBUG; } } else { - _emulate_keystrokes(@key_buf); + _emulate_keystrokes(@esc_buf); } } - @key_buf = (); - $buf_idx = 0; - $key_buf_enabled = 0; + @esc_buf = (); + $esc_buf_idx = 0; + $esc_buf_enabled = 0; +} + +sub collect_args { + my $key = shift; + + print "Collecting arguments" if DEBUG; + + if ($args_type == A_NUM) { + push @args_buf, $key; + + print "numbered args, expect: $args_num, got: " .scalar(@args_buf) + if DEBUG; + + if (scalar @args_buf == $args_num) { + dispatch_command($pending_command, @args_buf); + } + } elsif ($args_type == A_RET) { + print "ret terminated args. Key is $key" if DEBUG; + + if ($key == 10) { + dispatch_command($pending_command, @args_buf); + } else { + push @args_buf, $key; + } + } +} + +sub handle_numeric_prefix { + my ($char) = @_; + my $num = 0+$char; + + if (defined $numeric_prefix) { + $numeric_prefix *= 10; + $numeric_prefix += $num; + } else { + $numeric_prefix = $num; + } } 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} ); + + if ($collecting_args) { + + collect_args($key); + _stop(); + } else { - _stop(); # disable everything else + my $char = chr($key); + if ($char =~ m/[0-9]/) { + print "Processing numeric prefix: $char" if DEBUG; + handle_numeric_prefix($char); + _stop(); + } else { + print "Processing new command: $char" if DEBUG; + if (exists $commands->{$char}) { + + my $cmd = $commands->{$char}; + $args_type = $cmd->{args}->{type}; + + if ($args_type == A_NON) { + + # we can dispatch straight away + $pending_command = undef; + dispatch_command($cmd); + + } elsif ($args_type == A_NUM) { + + @args_buf = (); + $args_num = $cmd->{args}->{num}; + $collecting_args = 1; + $pending_command = $commands->{$char}; + _stop(); + } elsif ($args_type == A_RET) { + + $collecting_args = 1; + $pending_command = $commands->{$char}; + _stop(); + } + } else { + _stop(); # disable everything else + } + } + } +} + +sub dispatch_command { + my ($cmd, @args) = @_; + $collecting_args = 0; + + if (defined $numeric_prefix) { + $cmd->{params}->{prefix} = $numeric_prefix; + print "Commadn has numeric prefix: $numeric_prefix" if DEBUG; + $numeric_prefix = undef; } + + print "Dispatchign command with args: " . join(", ", @args) if DEBUG; + $cmd->{params}->{arg_buf} = \@args; + $cmd->{func}->($cmd->{params} ); } -Irssi::signal_add_first 'gui key pressed' => \&got_key; -Irssi::statusbar_item_register ('vim_mode', 0, 'vim_mode_cb'); +sub vim_mode_init { + Irssi::signal_add_first 'gui key pressed' => \&got_key; + Irssi::statusbar_item_register ('vim_mode', 0, 'vim_mode_cb'); +} sub _input { my ($data) = @_; @@ -282,6 +441,7 @@ sub _emulate_keystrokes { } $should_ignore = 0; } + sub _stop() { Irssi::signal_stop(); } @@ -289,3 +449,10 @@ sub _stop() { sub _update_mode() { Irssi::statusbar_items_redraw("vim_mode"); } + +sub _set_prompt { + my $msg = shift; + # add a leading space unless we're trying to clear it entirely. + $msg = ' ' . $msg if length $msg; + Irssi::signal_emit('change prompt', $msg); +} |