aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--vim-mode/vim_mode.pl241
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);
+}