aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Feist <shabble@metavore.org>2011-05-05 16:02:47 +0000
committerTom Feist <shabble@metavore.org>2011-05-05 16:02:47 +0000
commitce421c4c8a04215e127e1a7c777f62ed1f381442 (patch)
treeee6510e08dab823344e7d652d36480a32b84fd52
parentvim-mode/vim_mode: fix some warnings/errors on script unload, allow cmd_seq to (diff)
downloadirssi-scripts-ce421c4c8a04215e127e1a7c777f62ed1f381442.tar.gz
irssi-scripts-ce421c4c8a04215e127e1a7c777f62ed1f381442.zip
vim-mode/vim_mode: quite heavily broken. Needs a whole new rethink about the way
it's going to be buffered and processed.
-rw-r--r--vim-mode/vim_mode.pl924
1 files changed, 490 insertions, 434 deletions
diff --git a/vim-mode/vim_mode.pl b/vim-mode/vim_mode.pl
index 0aa4578..cf75d7d 100644
--- a/vim-mode/vim_mode.pl
+++ b/vim-mode/vim_mode.pl
@@ -3475,6 +3475,70 @@ sub ex_history_show {
}
}
+################################################################
+# INPUT BUFFER MODEL #
+################################################################
+
+sub _append_to_mode_buffer {
+ my ($keycode) = @_;
+ my $char = chr $keycode;
+ if ($mode == M_CMD) {
+ _debug("Appending %s to CMD buf", $char);
+ push @cmd_mode_buf, $keycode;
+ } elsif ($mode == M_EX) {
+ _debug("Appending %s to EX buf", $char);
+
+ push @ex_mode_buf, $keycode;
+ } else {
+ _debug("Appending %s to INS buf", $char);
+
+ push @insert_mode_buf, $keycode;
+ }
+}
+
+sub _get_mode_buffer {
+ if ($mode == M_CMD) {
+ return @cmd_mode_buf;
+ } elsif ($mode == M_EX) {
+ return @ex_mode_buf;
+ } else {
+ return @insert_mode_buf;
+ }
+}
+
+sub _reset_mode_buffer {
+ my ($override_mode) = @_;
+
+ if (not defined $override_mode) {
+ $override_mode = $mode;
+ }
+
+ if ($override_mode == M_CMD) {
+ _debug("Resetting CMD buffer");
+ @cmd_mode_buf = ();
+ } elsif ($override_mode == M_EX) {
+ _debug("Resetting EX buffer");
+ @ex_mode_buf = ();
+ } elsif ($override_mode == M_INS) {
+ _debug("Resetting INS buffer");
+ @insert_mode_buf = ();
+ } else {
+ _warn("Reset mode buffer: Unknown mode: $override_mode");
+ }
+}
+
+sub _buf_to_str {
+ return join '', map { chr } @_;
+}
+
+sub _buf_to_hex_str {
+ my @list;
+ foreach my $key (@_) {
+ push @list, sprintf('0x%02x', $key);
+ }
+ return '[' . join(' ', @list) . ']';
+}
+
################################################################
# INPUT SIGNAL HANDLER #
@@ -3499,6 +3563,9 @@ sub sig_gui_keypress {
# INPUT MODEL #
################################################################
+# buffer the input, and set up timeout handlers for both
+# keycode processor and command mapping processor.
+
sub process_input_key {
my ($key, $char) = @_;
@@ -3512,7 +3579,14 @@ sub process_input_key {
= ($settings->{timeoutlen}->{value},
$settings->{ttimeoutlen}->{value});
- _append_to_mode_buffer($key);
+ if ($mode != M_INS
+ and $key != KEY_BS
+ and $key != KEY_DEL
+ and $key != KEY_RET)
+ {
+ _append_to_mode_buffer($key);
+ }
+
if ((not $timeout) and (not $ttimeout)) {
# wait forever
@@ -3520,37 +3594,51 @@ sub process_input_key {
return if $keycode_timeouts_active or $mapping_timeouts_active;
- process_keys_timeout(TIMEOUT_MODE_NONE); # make these constants.
+ process_keys_timeout($key, $char, TIMEOUT_MODE_NONE);
+
} elsif ($timeout) {
_debug("process_keys_timeout both");
# timeout on both mappings and keycodes
- process_keys_timeout(TIMEOUT_MODE_BOTH, $timeout_len, $ttimeout_len);
+ process_keys_timeout($key, $char, TIMEOUT_MODE_BOTH,
+ $timeout_len, $ttimeout_len);
+
} elsif ((not $timeout) and $ttimeout) {
# timeout on codes (but not mappings?
_debug("process_keys_timeout code");
return if $keycode_timeouts_active;
- process_keys_timeout(TIMEOUT_MODE_CODE, $timeout_len, $ttimeout_len);
+ process_keys_timeout($key, $char, TIMEOUT_MODE_CODE,
+ $timeout_len, $ttimeout_len);
+
} else {
_debug("What the buggery, shouldn't happen: T: $timeout, TT: $ttimeout");
}
- # TODO: may need to stop regardless and reemit keystrokes due to
- # timeouts meaning we don't know at this point whether to stop or not.
- return SIG_STOP; # $mode == M_INS ? SIG_CONT : SIG_STOP;
+ # stop signal propagation regardless. If we have to, we'll re-emit
+ # any buffer contents with _emulate_keystrokes() later on.
+ return SIG_STOP;
}
+
# TODO: try process_keycode and /then/ mapping in some fashion
# one should be able to suppress the other from acting.
+
+# set up the timeout callbacks based on the value of the various
+# timeout and ttimeout[_len] settings.
+
sub process_keys_timeout {
- my ($timeout_mode, $timeout_len, $ttimeout_len) = @_;
+ my ($key, $char, $timeout_mode, $timeout_len, $ttimeout_len) = @_;
+
+ my $ret;
if ($timeout_mode eq TIMEOUT_MODE_NONE) {
- _try_process_keycode();
- _try_process_mapping();
+ $ret = _try_processing_keycode($key, $char, 0);
+ if (not $ret) {
+ _try_processing_maps($key, $char, 0);
+ }
} elsif ($timeout_mode eq TIMEOUT_MODE_BOTH) {
@@ -3558,7 +3646,7 @@ sub process_keys_timeout {
if ($ttimeout_len == 0) {
return if $keycode_timeouts_active;
- _try_process_keycode();
+ _try_processing_keycode($key, $char, 0);
} elsif ($ttimeout_len > 0 and $ttimeout_len < 10) {
$mapping_delay = 10; # minimum irssi timeout.
@@ -3573,7 +3661,7 @@ sub process_keys_timeout {
_debug("Setting mapping delay to %d", $mapping_delay);
$escape_buf_ttimeout
= Irssi::timeout_add_once($mapping_delay,
- \&_try_process_mapping, 1);
+ \&_try_processing_maps, [$key, $char, 1]);
$mapping_timeouts_active = 1;
} else {
@@ -3585,7 +3673,7 @@ sub process_keys_timeout {
_debug("Setting keycode delay to %d", $keycode_delay);
$escape_buf_timeout
= Irssi::timeout_add_once($keycode_delay,
- \&_try_process_keycode, 1);
+ \&_try_processing_keycode, [$key, $char, 1]);
$keycode_timeouts_active = 1;
} else {
@@ -3596,155 +3684,442 @@ sub process_keys_timeout {
$keycode_timeouts_active = 1;
- _try_process_mapping();
+ _try_processing_maps($key, $char, 0);
$escape_buf_timeout
= Irssi::timeout_add_once($timeout_len,
- \&_try_process_keycode, 1);
+ \&_try_processing_keycode, [$key, $char, 1]);
}
}
-sub _try_process_mapping {
- my ($from_timer) = @_;
+sub _try_processing_maps {
+ my $args = shift;
+ my ($key, $char, $from_timer) = @$args;
- my $cmd;
- my @buf = _get_mode_buffer();
- my $str = _buf_to_str(@buf);
- _debug("process mapping: '%s'", $str);
+ # TODO: Temp disabled.
+ $mapping_timeouts_active = 0 if defined $from_timer;
+ return 0;
- $cmd = search_user_mode_maps($str);
- if (defined $cmd) {
- _execute_cmd($cmd);
- } else {
- if ($mode == M_CMD) {
- _debug('checking existence of $commands->{%s}', $str);
- if (exists $commands->{$str}) {
- $cmd = $commands->{$str};
- _execute_cmd($cmd);
- }
- }
- }
+ # my $cmd;
+ # my @buf = _get_mode_buffer();
+ # my $str = _buf_to_str(@buf);
+ # _debug("process mapping: '%s'", $str);
+
+ # $cmd = search_user_mode_maps($str);
+ # if (defined $cmd) {
+ # _execute_cmd($cmd);
+ # } else {
+ # if ($mode == M_CMD) {
+ # _debug('checking existence of $commands->{%s}', $str);
+ # if (exists $commands->{$str}) {
+ # $cmd = $commands->{$str};
+ # _execute_cmd($cmd);
+ # }
+ # }
+ # }
- $mapping_timeouts_active = 0 if $from_timer;
+ # $mapping_timeouts_active = 0 if defined $from_timer;
}
-sub _execute_cmd {
- my ($cmd) = @_;
- _debug('Executing command: %s', Dumper($cmd));
+sub _try_processing_keycode {
+ my $args = shift;
+ my ($key, $char, $from_timer) = @$args;
+
+ my $ret = 0;
+
+
+ _debug("keycode mapping: '%s'", $char);
+
+ if ($mode == M_INS) {
+ $ret = process_keycode_insert($key, $char);
+ } elsif ($mode == M_CMD) {
+ $ret = process_keycode_command($key, $char);
+ } elsif ($mode == M_EX) {
+ $ret = process_keycode_ex($key, $char);
+ }
+
+ $keycode_timeouts_active = 0 if defined $from_timer;
+
+ # avoids warnings about _reset_mode_buffer being symbol-table murdered
+ # during script destruction.
+
+ return $ret if $unloading;
+
+ # TODO: only reset necessary buffers.
+ # _reset_mode_buffer($_) for (M_CMD, M_INS, M_EX);
+
+ return $ret;
}
-sub _try_process_keycode {
- my ($from_timer) = @_;
+sub process_keycode_ex {
+ my ($key, $char);
- my @buf = _get_mode_buffer();
- _debug("keycode mapping: '%s'", _buf_to_str(@buf));
+ _debug('process_keycode_ex %s', $char);
+ # BS key (8) or DEL key (127) - remove last character.
+ if ($key == KEY_BS or $key == KEY_DEL) {
+
+ _debug("Ex: Delete");
+
+ if (@ex_mode_buf > 0) {
+ pop @ex_mode_buf;
+ _set_prompt(':' . _buf_to_str(@ex_mode_buf));
+ # Backspacing over : exits ex-mode.
+ } else {
+ _update_mode(M_CMD);
+ }
- #TODO: switch on M_TYPE first.
- # This function only handles changing modes, numeric prefixes, and
- #
+ # Return key - execute command
+ } elsif ($key == KEY_RET) {
+
+ _debug("Run ex-mode command");
+ cmd_ex_command();
+ _update_mode(M_CMD);
+ _reset_mode_buffer(M_EX) unless $unloading;
+
+ } elsif ($key > 0 and $key < 32) {
+ # TODO: use them later, e.g. completion
+ _debug("Ex: Control key: 0x%02x", $key);
+ } else {
- if (!$movement and !$pending_map and
- ($char =~ m/[1-9]/ or ($numeric_prefix && $char =~ m/[0-9]/))) {
- print "Processing numeric prefix: $char" if DEBUG;
- handle_numeric_prefix($char);
- return 1; # call _stop()
+ _append_to_mode_buffer($key);
+ _set_prompt(':' . _buf_to_str(@ex_mode_buf));
}
+ Irssi::statusbar_items_redraw("vim_windows");
+
+ _stop();
+}
+
+sub process_keycode_insert {
+ my ($key, $char) = @_;
+
+ my $ret = 0;
+ _debug('process_keycode_insert');
+ my @buf = ($key);
if (_is_cmd_entry_sequence(@buf)) {
_debug ("Buf contains ESC!");
_update_mode(M_CMD);
-
+ $ret = 1;
} elsif (@buf == 1 and $buf[0] == KEY_RET) {
_commit_line();
_debug("Pressed enter");
_emulate_keystrokes(KEY_RET);
+ _reset_mode_buffer(M_INS) unless $unloading;
+ $ret = 1;
} else {
_debug("buf contains: '%s'", _buf_to_hex_str(@buf));
_emulate_keystrokes(@buf);
+ $ret = 0;
}
+ return $ret;
+}
+
+sub process_keycode_command {
+ my ($key, $char) = @_;
+
+ _debug('process_keycode_command %s', $char);
- return if $unloading;
- _reset_mode_buffer($_) for (M_CMD, M_INS, M_EX);
- $keycode_timeouts_active = 0 if $from_timer;
}
-sub _is_cmd_entry_sequence {
- my (@data) = @_;
- return 1 if (@data == 1 and $data[0] == KEY_ESC);
- return 1 if (@data == 1 and $data[0] == KEY_C_C);
+# sub blahblbhal {
- my $str = _buf_to_str(@data);
- my $cmd_seq = _setting_get_cached('cmd_seq');
+# my $pending_map_flushed = 0;
- return 1 if (@data == 2 and length($cmd_seq) and $str eq $cmd_seq);
+# if (not defined $key) {
+# $char = $pending_map;
+# $key = 0;
+# $pending_map_flushed = 1;
+# }
- return 0;
-}
-################################################################
-# INPUT BUFFER MODEL #
-################################################################
+# # Counts
-sub _append_to_mode_buffer {
- my ($keycode) = @_;
- my $char = chr $keycode;
- if ($mode == M_CMD) {
- _debug("Appending %s to CMD buf", $char);
- push @cmd_mode_buf, $keycode;
- } elsif ($mode == M_EX) {
- _debug("Appending %s to EX buf", $char);
+# if (defined $pending_map and not $pending_map_flushed) {
+# $pending_map = $pending_map . $char;
+# $char = $pending_map;
+# }
- push @ex_mode_buf, $keycode;
- } else {
- _debug("Appending %s to INS buf", $char);
+# my $map;
+# if ($movement) {
+# $map = { char => $movement->{char},
+# cmd => $movement,
+# maps => {},
+# };
- push @insert_mode_buf, $keycode;
- }
-}
+# } elsif (exists $cmaps->{$char}) {
+# $map = $cmaps->{$char};
-sub _get_mode_buffer {
- if ($mode == M_CMD) {
- return @cmd_mode_buf;
- } elsif ($mode == M_EX) {
- return @ex_mode_buf;
- } else {
- return @insert_mode_buf;
- }
-}
+# # We have multiple mappings starting with this key sequence.
+# if (!$pending_map_flushed and scalar keys %{$map->{maps}} > 0) {
+# if (not defined $pending_map) {
+# $pending_map = $char;
+# }
-sub _reset_mode_buffer {
- my ($override_mode) = @_;
+# # The current key sequence has a command mapped to it, run if
+# # after a timeout.
+# if (defined $map->{cmd}) {
+# Irssi::timeout_add_once(1000, \&flush_pending_map,
+# $pending_map);
+# }
+# return 1; # call _stop()
+# }
- if (not defined $override_mode) {
- $override_mode = $mode;
- }
+# } else {
+# print "No mapping found for $char" if DEBUG;
+# $pending_map = undef;
+# $numeric_prefix = undef;
+# return 1; # call _stop()
+# }
- if ($override_mode == M_CMD) {
- _debug("Resetting CMD buffer");
- @cmd_mode_buf = ();
- } elsif ($override_mode == M_EX) {
- _debug("Resetting EX buffer");
- @ex_mode_buf = ();
- } elsif ($override_mode == M_INS) {
- _debug("Resetting INS buffer");
- @insert_mode_buf = ();
- } else {
- _warn("Reset mode buffer: Unknown mode: $override_mode");
- }
-}
+# $pending_map = undef;
-sub _buf_to_str {
- return join '', map { chr } @_;
-}
+# my $cmd = $map->{cmd};
-sub _buf_to_hex_str {
- my @list;
- foreach my $key (@_) {
- push @list, sprintf('0x%02x', $key);
- }
- return '[' . join(' ', @list) . ']';
+# # Make sure we have a valid $cmd.
+# if (not defined $cmd) {
+# print "Bug in pending_map_flushed() $map->{char}" if DEBUG;
+# return 1; # call _stop()
+# }
+
+# # Ex-mode commands can also be bound in command mode.
+# if ($cmd->{type} == C_EX) {
+# print "Processing ex-command: $map->{char} ($cmd->{char})" if DEBUG;
+
+# $cmd->{func}->(substr($cmd->{char}, 1), $numeric_prefix);
+# $numeric_prefix = undef;
+
+# return 1; # call _stop()
+# # As can irssi commands.
+# } elsif ($cmd->{type} == C_IRSSI) {
+# print "Processing irssi-command: $map->{char} ($cmd->{char})" if DEBUG;
+
+# _command_with_context($cmd->{func});
+
+# $numeric_prefix = undef;
+# return 1; # call _stop();
+# # <Nop> does nothing.
+# } elsif ($cmd->{type} == C_NOP) {
+# print "Processing <Nop>: $map->{char}" if DEBUG;
+
+# $numeric_prefix = undef;
+# return 1; # call _stop();
+# }
+
+# # text-objects (i a) are simulated with $movement
+# if (!$movement and ($cmd->{type} == C_NEEDSKEY or
+# ($operator and ($char eq 'i' or $char eq 'a')))) {
+# print "Processing movement: $map->{char} ($cmd->{char})" if DEBUG;
+# if ($char eq 'i') {
+# $movement = $commands->{_i};
+# } elsif ($char eq 'a') {
+# $movement = $commands->{_a};
+# } else {
+# $movement = $cmd;
+# }
+
+# } elsif (!$movement and $cmd->{type} == C_OPERATOR) {
+# print "Processing operator: $map->{char} ($cmd->{char})" if DEBUG;
+# # Abort operator if we already have one pending.
+# if ($operator) {
+# # But allow cc/dd/yy.
+# if ($operator == $cmd) {
+# print "Processing line operator: ",
+# $map->{char}, " (",
+# $cmd->{char} ,")"
+# if DEBUG;
+
+# my $pos = _input_pos();
+# $cmd->{func}->(0, _input_len(), undef, 0);
+# # Restore position for yy.
+# if ($cmd == $commands->{y}) {
+# _input_pos($pos);
+# # And save undo for other operators.
+# } else {
+# _add_undo_entry(_input(), _input_pos());
+# }
+# if ($register ne '"') {
+# print 'Changing register to "' if DEBUG;
+# $register = '"';
+# }
+# }
+# $numeric_prefix = undef;
+# $operator = undef;
+# $movement = undef;
+# # Set new operator.
+# } else {
+# $operator = $cmd;
+# }
+
+# # Start Ex mode.
+# } elsif ($cmd == $commands->{':'}) {
+
+# if (not script_is_loaded('uberprompt')) {
+# _warn("Warning: Ex mode requires the 'uberprompt' script. " .
+# "Please load it and try again.");
+# } else {
+# _update_mode(M_EX);
+# _set_prompt(':');
+# }
+
+# # Enter key sends the current input line in command mode as well.
+# } elsif ($key == 10) {
+# _commit_line();
+# return 0; # don't call _stop()
+
+# } else {
+# print "Processing command: $map->{char} ($cmd->{char})" if DEBUG;
+
+# my $skip = 0;
+# my $repeat = 0;
+
+# if (!$movement) {
+# # . repeats the last command.
+# if ($cmd == $commands->{'.'} and defined $last->{cmd}) {
+# $cmd = $last->{cmd};
+# $char = $last->{char};
+# # If . is given a count then it replaces original count.
+# if (not defined $numeric_prefix) {
+# $numeric_prefix = $last->{numeric_prefix};
+# }
+# $operator = $last->{operator};
+# $movement = $last->{movement};
+# $register = $last->{register};
+# $repeat = 1;
+# } elsif ($cmd == $commands->{'.'}) {
+# print '. pressed but $last->{char} not set' if DEBUG;
+# $skip = 1;
+# }
+# }
+
+# # Ignore invalid operator/command combinations.
+# if ($operator and $cmd->{no_operator}) {
+# print "Invalid operator/command: $operator->{char} $cmd->{char}"
+# if DEBUG;
+# $skip = 1;
+# }
+
+# if ($skip) {
+# print "Skipping movement and operator." if DEBUG;
+# } else {
+# # Make sure count is at least 1 except for functions which need to
+# # know if no count was used.
+# if (not $numeric_prefix and not $cmd->{needs_count}) {
+# $numeric_prefix = 1;
+# }
+
+# my $cur_pos = _input_pos();
+
+# # If defined $cur_pos will be changed to this.
+# my $old_pos;
+# # Position after the move.
+# my $new_pos;
+# # Execute the movement (multiple times).
+# if (not $movement) {
+# ($old_pos, $new_pos)
+# = $cmd->{func}->($numeric_prefix, $cur_pos, $repeat);
+# } else {
+# ($old_pos, $new_pos)
+# = $cmd->{func}->($numeric_prefix, $cur_pos, $repeat,
+# $char);
+# }
+# if (defined $old_pos) {
+# print "Changing \$cur_pos from $cur_pos to $old_pos" if DEBUG;
+# $cur_pos = $old_pos;
+# }
+# if (defined $new_pos) {
+# _input_pos($new_pos);
+# } else {
+# $new_pos = _input_pos();
+# }
+
+# # Update input position of last undo entry so that undo/redo
+# # restores correct position.
+# if (@undo_buffer and _input() eq $undo_buffer[0]->[0] and
+# ((defined $operator and $operator == $commands->{d}) or
+# $cmd->{repeatable})) {
+# print "Updating history position: $undo_buffer[0]->[0]"
+# if DEBUG;
+# $undo_buffer[0]->[1] = $cur_pos;
+# }
+
+# # If we have an operator pending then run it on the handled text.
+# # But only if the movement changed the position (this prevents
+# # problems with e.g. f when the search string doesn't exist).
+# if ($operator and $cur_pos != $new_pos) {
+# print "Processing operator: ", $operator->{char} if DEBUG;
+# $operator->{func}->($cur_pos, $new_pos, $cmd, $repeat);
+# }
+
+# # Save an undo checkpoint here for operators, all repeatable
+# # movements, operators and repetition.
+# if ((defined $operator and $operator == $commands->{d}) or
+# $cmd->{repeatable}) {
+# # TODO: why do history entries still show up in undo
+# # buffer? Is avoiding the commands here insufficient?
+
+# _add_undo_entry(_input(), _input_pos());
+# }
+
+# # Store command, necessary for .
+# if ($operator or $cmd->{repeatable}) {
+# $last->{cmd} = $cmd;
+# $last->{char} = $char;
+# $last->{numeric_prefix} = $numeric_prefix;
+# $last->{operator} = $operator;
+# $last->{movement} = $movement;
+# $last->{register} = $register;
+# }
+# }
+
+# # Reset the count unless we go into insert mode, _update_mode() needs
+# # to know it when leaving insert mode to support insert with counts
+# # (like 3i).
+# if ($repeat or $cmd->{type} != C_INSERT) {
+# $numeric_prefix = undef;
+# }
+# $operator = undef;
+# $movement = undef;
+
+# if ($cmd != $commands->{'"'} and $register ne '"') {
+# print 'Changing register to "' if DEBUG;
+# $register = '"';
+# }
+
+# }
+# _stop();
+# }
+
+
+ # } elsif ($key == 9) { # TAB
+ # print "Tab pressed" if DEBUG;
+ # print "Ex buf contains: " . join('', @ex_mode_buf) if DEBUG;
+ # @tab_candidates = _tab_complete(join('', @ex_mode_buf), [keys %$commands_ex]);
+ # _debug("Candidates: " . join(", ", @tab_candidates));
+ # if (@tab_candidates == 1) {
+ # @ex_mode_buf = ( split('', $tab_candidates[0]), ' ');
+ # _set_prompt(':' . join '', @ex_mode_buf);
+ # }
+ # # Ignore control characters for now.
+
+
+
+
+# determines whether the buffer contents contains keystroke(s) that
+# would enter command mode.
+sub _is_cmd_entry_sequence {
+ my (@data) = @_;
+ return 1 if (@data == 1 and $data[0] == KEY_ESC);
+ return 1 if (@data == 1 and $data[0] == KEY_C_C);
+
+ my $str = _buf_to_str(@data);
+ my $cmd_seq = _setting_get_cached('cmd_seq');
+
+ return 1 if (@data == 2 and length($cmd_seq) and $str eq $cmd_seq);
+
+ return 0;
}
+
# sub attempt_escape_buffer_parse {
# my ($key, $char) = @_;
@@ -3946,325 +4321,6 @@ sub handle_numeric_prefix {
}
}
-sub handle_command_cmd {
- my ($key) = @_;
-
- my $pending_map_flushed = 0;
-
- my $char;
- if (defined $key) {
- $char = chr($key);
- # We were called from flush_pending_map().
- } else {
- $char = $pending_map;
- $key = 0;
- $pending_map_flushed = 1;
- }
-
- # Counts
-
- if (defined $pending_map and not $pending_map_flushed) {
- $pending_map = $pending_map . $char;
- $char = $pending_map;
- }
-
- my $map;
- if ($movement) {
- $map = { char => $movement->{char},
- cmd => $movement,
- maps => {},
- };
-
- } elsif (exists $cmaps->{$char}) {
- $map = $cmaps->{$char};
-
- # We have multiple mappings starting with this key sequence.
- if (!$pending_map_flushed and scalar keys %{$map->{maps}} > 0) {
- if (not defined $pending_map) {
- $pending_map = $char;
- }
-
- # The current key sequence has a command mapped to it, run if
- # after a timeout.
- if (defined $map->{cmd}) {
- Irssi::timeout_add_once(1000, \&flush_pending_map,
- $pending_map);
- }
- return 1; # call _stop()
- }
-
- } else {
- print "No mapping found for $char" if DEBUG;
- $pending_map = undef;
- $numeric_prefix = undef;
- return 1; # call _stop()
- }
-
- $pending_map = undef;
-
- my $cmd = $map->{cmd};
-
- # Make sure we have a valid $cmd.
- if (not defined $cmd) {
- print "Bug in pending_map_flushed() $map->{char}" if DEBUG;
- return 1; # call _stop()
- }
-
- # Ex-mode commands can also be bound in command mode.
- if ($cmd->{type} == C_EX) {
- print "Processing ex-command: $map->{char} ($cmd->{char})" if DEBUG;
-
- $cmd->{func}->(substr($cmd->{char}, 1), $numeric_prefix);
- $numeric_prefix = undef;
-
- return 1; # call _stop()
- # As can irssi commands.
- } elsif ($cmd->{type} == C_IRSSI) {
- print "Processing irssi-command: $map->{char} ($cmd->{char})" if DEBUG;
-
- _command_with_context($cmd->{func});
-
- $numeric_prefix = undef;
- return 1; # call _stop();
- # <Nop> does nothing.
- } elsif ($cmd->{type} == C_NOP) {
- print "Processing <Nop>: $map->{char}" if DEBUG;
-
- $numeric_prefix = undef;
- return 1; # call _stop();
- }
-
- # text-objects (i a) are simulated with $movement
- if (!$movement and ($cmd->{type} == C_NEEDSKEY or
- ($operator and ($char eq 'i' or $char eq 'a')))) {
- print "Processing movement: $map->{char} ($cmd->{char})" if DEBUG;
- if ($char eq 'i') {
- $movement = $commands->{_i};
- } elsif ($char eq 'a') {
- $movement = $commands->{_a};
- } else {
- $movement = $cmd;
- }
-
- } elsif (!$movement and $cmd->{type} == C_OPERATOR) {
- print "Processing operator: $map->{char} ($cmd->{char})" if DEBUG;
- # Abort operator if we already have one pending.
- if ($operator) {
- # But allow cc/dd/yy.
- if ($operator == $cmd) {
- print "Processing line operator: ",
- $map->{char}, " (",
- $cmd->{char} ,")"
- if DEBUG;
-
- my $pos = _input_pos();
- $cmd->{func}->(0, _input_len(), undef, 0);
- # Restore position for yy.
- if ($cmd == $commands->{y}) {
- _input_pos($pos);
- # And save undo for other operators.
- } else {
- _add_undo_entry(_input(), _input_pos());
- }
- if ($register ne '"') {
- print 'Changing register to "' if DEBUG;
- $register = '"';
- }
- }
- $numeric_prefix = undef;
- $operator = undef;
- $movement = undef;
- # Set new operator.
- } else {
- $operator = $cmd;
- }
-
- # Start Ex mode.
- } elsif ($cmd == $commands->{':'}) {
-
- if (not script_is_loaded('uberprompt')) {
- _warn("Warning: Ex mode requires the 'uberprompt' script. " .
- "Please load it and try again.");
- } else {
- _update_mode(M_EX);
- _set_prompt(':');
- }
-
- # Enter key sends the current input line in command mode as well.
- } elsif ($key == 10) {
- _commit_line();
- return 0; # don't call _stop()
-
- } else {
- print "Processing command: $map->{char} ($cmd->{char})" if DEBUG;
-
- my $skip = 0;
- my $repeat = 0;
-
- if (!$movement) {
- # . repeats the last command.
- if ($cmd == $commands->{'.'} and defined $last->{cmd}) {
- $cmd = $last->{cmd};
- $char = $last->{char};
- # If . is given a count then it replaces original count.
- if (not defined $numeric_prefix) {
- $numeric_prefix = $last->{numeric_prefix};
- }
- $operator = $last->{operator};
- $movement = $last->{movement};
- $register = $last->{register};
- $repeat = 1;
- } elsif ($cmd == $commands->{'.'}) {
- print '. pressed but $last->{char} not set' if DEBUG;
- $skip = 1;
- }
- }
-
- # Ignore invalid operator/command combinations.
- if ($operator and $cmd->{no_operator}) {
- print "Invalid operator/command: $operator->{char} $cmd->{char}"
- if DEBUG;
- $skip = 1;
- }
-
- if ($skip) {
- print "Skipping movement and operator." if DEBUG;
- } else {
- # Make sure count is at least 1 except for functions which need to
- # know if no count was used.
- if (not $numeric_prefix and not $cmd->{needs_count}) {
- $numeric_prefix = 1;
- }
-
- my $cur_pos = _input_pos();
-
- # If defined $cur_pos will be changed to this.
- my $old_pos;
- # Position after the move.
- my $new_pos;
- # Execute the movement (multiple times).
- if (not $movement) {
- ($old_pos, $new_pos)
- = $cmd->{func}->($numeric_prefix, $cur_pos, $repeat);
- } else {
- ($old_pos, $new_pos)
- = $cmd->{func}->($numeric_prefix, $cur_pos, $repeat,
- $char);
- }
- if (defined $old_pos) {
- print "Changing \$cur_pos from $cur_pos to $old_pos" if DEBUG;
- $cur_pos = $old_pos;
- }
- if (defined $new_pos) {
- _input_pos($new_pos);
- } else {
- $new_pos = _input_pos();
- }
-
- # Update input position of last undo entry so that undo/redo
- # restores correct position.
- if (@undo_buffer and _input() eq $undo_buffer[0]->[0] and
- ((defined $operator and $operator == $commands->{d}) or
- $cmd->{repeatable})) {
- print "Updating history position: $undo_buffer[0]->[0]"
- if DEBUG;
- $undo_buffer[0]->[1] = $cur_pos;
- }
-
- # If we have an operator pending then run it on the handled text.
- # But only if the movement changed the position (this prevents
- # problems with e.g. f when the search string doesn't exist).
- if ($operator and $cur_pos != $new_pos) {
- print "Processing operator: ", $operator->{char} if DEBUG;
- $operator->{func}->($cur_pos, $new_pos, $cmd, $repeat);
- }
-
- # Save an undo checkpoint here for operators, all repeatable
- # movements, operators and repetition.
- if ((defined $operator and $operator == $commands->{d}) or
- $cmd->{repeatable}) {
- # TODO: why do history entries still show up in undo
- # buffer? Is avoiding the commands here insufficient?
-
- _add_undo_entry(_input(), _input_pos());
- }
-
- # Store command, necessary for .
- if ($operator or $cmd->{repeatable}) {
- $last->{cmd} = $cmd;
- $last->{char} = $char;
- $last->{numeric_prefix} = $numeric_prefix;
- $last->{operator} = $operator;
- $last->{movement} = $movement;
- $last->{register} = $register;
- }
- }
-
- # Reset the count unless we go into insert mode, _update_mode() needs
- # to know it when leaving insert mode to support insert with counts
- # (like 3i).
- if ($repeat or $cmd->{type} != C_INSERT) {
- $numeric_prefix = undef;
- }
- $operator = undef;
- $movement = undef;
-
- if ($cmd != $commands->{'"'} and $register ne '"') {
- print 'Changing register to "' if DEBUG;
- $register = '"';
- }
-
- }
-
- return 1; # call _stop()
-}
-
-sub handle_command_ex {
- my ($key) = @_;
-
- # BS key (8) or DEL key (127) - remove last character.
- if ($key == 8 || $key == 127) {
- print "Delete" if DEBUG;
- if (@ex_mode_buf > 0) {
- pop @ex_mode_buf;
- _set_prompt(':' . join '', @ex_mode_buf);
- # Backspacing over : exits ex-mode.
- } else {
- _update_mode(M_CMD);
- }
-
- # Return key - execute command
- } elsif ($key == 10) {
- print "Run ex-mode command" if DEBUG;
- cmd_ex_command();
- _update_mode(M_CMD);
-
- } elsif ($key == 9) { # TAB
- print "Tab pressed" if DEBUG;
- print "Ex buf contains: " . join('', @ex_mode_buf) if DEBUG;
- @tab_candidates = _tab_complete(join('', @ex_mode_buf), [keys %$commands_ex]);
- _debug("Candidates: " . join(", ", @tab_candidates));
- if (@tab_candidates == 1) {
- @ex_mode_buf = ( split('', $tab_candidates[0]), ' ');
- _set_prompt(':' . join '', @ex_mode_buf);
- }
- # Ignore control characters for now.
- } elsif ($key > 0 && $key < 32) {
- # TODO: use them later, e.g. completion
-
- # Append entered key
- } else {
- if ($key != -1) {
- # check we're not called from an ex_history_* function
- push @ex_mode_buf, chr $key;
- }
- _set_prompt(':' . join '', @ex_mode_buf);
- }
-
- Irssi::statusbar_items_redraw("vim_windows");
-
- _stop();
-}
################################################################
# STARTUP #