From 8f84f6e57c31085dcf5cc5a987d44debc11860d5 Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Fri, 8 Oct 2010 02:23:43 +0200 Subject: vim_mode: Add :map to allow custom mappings. --- vim-mode/vim_mode.pl | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 2 deletions(-) (limited to 'vim-mode/vim_mode.pl') diff --git a/vim-mode/vim_mode.pl b/vim-mode/vim_mode.pl index 042fc34..e941d07 100644 --- a/vim-mode/vim_mode.pl +++ b/vim-mode/vim_mode.pl @@ -69,6 +69,20 @@ # * Display windows: :ls :buffers # * Display registers: :reg[isters] :di[splay] {args} # * Display undolist: :undol[ist] (mostly used for debugging) +# * Mappings: :map - display custom mappings +# :map {lhs} {rhs} - add mapping +# +# Mappings: +# +# {lhs} is the key combination to be mapped, {rhs} the target. The <> notation +# is used (e.g. is Ctrl-F), case is ignored. Supported <> keys: +# -, , , , . Mapping ex-mode command is +# supported. Only default mappings can be used in {rhs}. +# Examples: +# :map gb :bnext # to map gb to call :bnext +# :map gB :bprev +# :map w W # to remap w to work like W +# # # The following irssi settings are available: # @@ -330,6 +344,7 @@ my $commands_ex ls => { char => 'ls', func => \&ex_buffers, type => C_EX }, undolist => { char => 'undolist', func => \&ex_undolist, type => C_EX }, undol => { char => 'undol', func => \&ex_undolist, type => C_EX }, + map => { char => 'map', func => \&ex_map, type => C_EX }, }; # MAPPINGS @@ -1526,9 +1541,113 @@ sub ex_undolist { _print_undo_buffer(); } +sub ex_map { + my ($arg_str) = @_; + + # :map {lhs} {rhs} + if ($arg_str =~ /^map (\S+) (\S+)$/) { + my $lhs = _parse_mapping($1); + my $rhs = $2; + + if (not defined $lhs) { + return _warn_ex('map', 'invalid {lhs}'); + } + + # Add new mapping. + my $command; + if (index($rhs, ':') == 0) { + $rhs = substr $rhs, 1; + if (not exists $commands_ex->{$rhs}) { + return _warn_ex('map', "$2 not found"); + } else { + $command = $commands_ex->{$rhs}; + } + } else { + $rhs = _parse_mapping($2); + if (not defined $rhs) { + return _warn_ex('map', 'invalid {rhs}'); + } elsif (not exists $commands->{$rhs}) { + return _warn_ex('map', "$2 not found"); + } else { + $command = $commands->{$rhs}; + } + } + add_map($lhs, $command); + + # :map + } elsif ($arg_str eq 'map') { + my $active_window = Irssi::active_win(); + foreach my $key (sort keys %$maps) { + my $map = $maps->{$key}; + my $cmd = $map->{cmd}; + if (defined $map->{char}) { + my $char = _parse_mapping_reverse($map->{char}); + next if $char eq $cmd->{char}; # skip default mappings + + my $cmdc = _parse_mapping_reverse($cmd->{char}); + if ($cmd->{type} == C_EX) { + $cmdc = ":$cmdc"; + } + $active_window->print(sprintf "%-15s %s", $char, $cmdc); + } + } + } else { + _warn_ex('map'); + } +} +sub _parse_mapping { + my ($string) = @_; + + $string =~ s/<([^>]+)>/_parse_mapping_bracket($1)/ge; + if (index($string, '') != -1) { + return undef; + } + return $string; +} +sub _parse_mapping_bracket { + my ($string) = @_; + + $string = lc $string; + + # , get corresponding CTRL char. + if ($string =~ /^c-([a-z])$/i) { + $string = chr(ord($1) - 96); + # and + } elsif ($string =~ /^c-[6^]$/i) { + $string = chr(30); + # + } elsif ($string eq 'space') { + $string = ' '; + # + } elsif ($string eq 'cr') { + $string = "\n"; + # Invalid char, return special string to recognize the error. + } else { + $string = ''; + } + return $string; +} +sub _parse_mapping_reverse { + my ($string) = @_; + + # Convert char to . + $string =~ s/ //g; + $string =~ s/\n//g; + # Convert Ctrl-X to . + $string =~ s/([\x01-\x1A])/""/ge; + # Convert Ctrl-6 and Ctrl-^ to . + $string =~ s/\x1E//g; + + return $string; +} + sub _warn_ex { - my ($command) = @_; - _warn("Error in ex-mode command $command"); + my ($command, $description) = @_; + my $message = "Error in ex-mode command $command"; + if (defined $description) { + $message .= ": $description"; + } + _warn($message); } sub _matching_windows { -- cgit v1.2.3