readline: activating copies of both emacs-standard and emacs-meta keymaps

39 Views Asked by At

My aim is to:

  1. Copy emacs-standard and emacs-meta keymaps,
  2. Activate the copies
  3. Make changes to the copies
  4. Restore the emacs-standard and emacs-meta keymaps

I have read that emacs-meta is assigned to the Esc key, \e.

My (failing) attempt is in perl, but working code in any language is welcome.

use v5.30;
use Term::ReadLine;
BEGIN {
    import Term::ReadLine::Gnu qw(:keymap_type);
}
my (%seq, %seq_meta, %func, %func_meta);
my $term = Term::ReadLine->new('keymap and custom dispatch test');

# add my own dispatch routine
my $func_name = 'hotkey_dispatch';
my $coderef = sub {};
$term->add_defun($func_name, $coderef);

my $emacs = $term->get_keymap;
say "initial state:";
dump_keyseq("\e"); # Esc
dump_keyseq("\e[A"); # Up arrow
$term->bind_keyseq("\e", $func_name);
$term->bind_keyseq("\e[A", $func_name);
my $emacs_meta = $term->get_keymap_by_name('emacs-meta');
my $nama = $term->copy_keymap($emacs);
my $nama_meta = $term->copy_keymap($emacs_meta);

$term->set_keymap_name('nama-meta', $nama_meta);
$term->set_keymap_name('nama',$nama);
$term->set_keymap($nama);
$term->generic_bind(ISKMAP,"\e",$nama_meta, $nama);

$term->bind_keyseq("\e", $func_name);
$term->bind_keyseq("\e[A", $func_name);

say "after copying, activating and binding:";
dump_keyseq("\e");
say "this should be 'nama-meta'";
dump_keyseq("\e[A");
say "this should be 'hotkey_dispatch'";



$term->set_keymap($emacs_meta);
$term->set_keymap($emacs);
$term->generic_bind(ISKMAP,"\e",$emacs_meta, $emacs);
say "after attempting restore:";
dump_keyseq("\e");
say "this should be emacs-meta, but only after nama-meta";
dump_keyseq("\e[A");
say "this should be previous-history";

sub dump_keyseq {
    my $seq = shift;
    my ($data, $type) = $term->function_of_keyseq($seq);
    if ($type == ISFUNC) {
        say "function: ", $term->get_function_name($data);
    } elsif ($type == ISKMAP) {
        say "keymap: ", $term->get_keymap_name($data);
    } elsif ($type == ISMACR) {
        say "macro: $data";
    } else {
        say "unknown type: $type";
    }
}
initial state:
keymap: emacs-meta
function: previous-history
after copying, activating and binding:
keymap: emacs-meta
this should be 'nama-meta'
function: hotkey_dispatch
this should be 'hotkey_dispatch'
after attempting restore:
keymap: emacs-meta
this should be emacs-meta, but only after nama-meta
function: hotkey_dispatch
this should be previous-history


So as you can see, I attempted to copy, activate, modify and restore the default keymaps, however failed being able to correctly activate or restore keymaps. I will appreciate any advice.

1

There are 1 best solutions below

0
Håkon Hægland On

after copying, activating and binding:
keymap: emacs-meta
this should be 'nama-meta'

It seems like it is not possible to change the keymap for the escape key (or any other key that is already of type ISKMAP, see source code https://git.savannah.gnu.org/cgit/readline.git/tree/bind.c#n472). Also it seems bind_keyseq() does not have effect if generic_bind() has been called first on the same key in the same keymap. So for these two lines:

$term->generic_bind(ISKMAP,"\e",$nama_meta, $nama);
$term->bind_keyseq("\e", $func_name);

the second will not have any effect since both happens in the keymap $nama. But the first line also does not have an effect since the key is escape which cannot be modifed. If you change it to another key like CTRL+M it works fine:

$term->generic_bind(ISKMAP,"\cm",$nama_meta, $nama);
$term->bind_keyseq("\cm", $func_name);

Now the first line has an effect, and the second line does not have any effect.