You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何复制、激活、修改并恢复emacs-standard与emacs-meta按键映射?

复制、修改并恢复Term::ReadLine::Gnu的Emacs按键映射

目标

  • 复制emacs-standard和emacs-meta按键映射
  • 激活这些副本
  • 修改副本
  • 恢复emacs-standard和emacs-meta按键映射

已知emacs-meta被分配给Esc键(\e)。我用Perl尝试实现但未成功,欢迎任何语言的可行代码。

尝试的Perl代码

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

当前问题:尝试复制、激活、修改并恢复默认按键映射,但未能正确激活副本或恢复原始映射,寻求解决建议。


解决方案

问题出在几个核心环节:

  1. 复制前修改了原始映射:你在复制emacs和emacs-meta之前,就用bind_keyseq修改了原始映射的\e和\e[A,导致副本继承了修改后的状态,而非初始默认值。
  2. 绑定未指定目标keymap:修改副本按键时,没有明确指定要修改的目标keymap,导致操作仍作用于原始映射。
  3. 恢复逻辑不完整:仅切换回原始keymap并重新绑定Esc,但未重置被修改的按键序列。

修正后的代码

use v5.30;
use Term::ReadLine;
BEGIN {
    import Term::ReadLine::Gnu qw(:keymap_type);
}

my $term = Term::ReadLine->new('keymap and custom dispatch test');
my $func_name = 'hotkey_dispatch';
$term->add_defun($func_name, sub {});

# 先保存原始keymap,再执行后续操作
my $emacs = $term->get_keymap;
my $emacs_meta = $term->get_keymap_by_name('emacs-meta');

say "initial state:";
dump_keyseq("\e");
dump_keyseq("\e[A");

# 复制原始keymap副本
my $nama = $term->copy_keymap($emacs);
my $nama_meta = $term->copy_keymap($emacs_meta);
$term->set_keymap_name('nama', $nama);
$term->set_keymap_name('nama-meta', $nama_meta);

# 激活副本keymap
$term->set_keymap($nama);
# 将副本的Esc键绑定到副本meta keymap
$term->generic_bind(ISKMAP, "\e", $nama_meta, $nama);

# 修改副本keymap的按键绑定(指定目标为nama_meta)
$term->bind_keyseq("\e", $func_name, $nama_meta);
$term->bind_keyseq("\e[A", $func_name, $nama_meta);

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

# 恢复原始keymap
$term->set_keymap($emacs);
$term->generic_bind(ISKMAP, "\e", $emacs_meta, $emacs);

say "\nafter restoring:";
dump_keyseq("\e");
dump_keyseq("\e[A");

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";
    }
}

关键修正说明

  • 提前保存原始映射:在任何修改操作前获取并复制原始的emacs和emacs-meta,确保副本是初始默认状态。
  • 指定绑定目标keymap:调用bind_keyseq时传入第三个参数(目标keymap),确保修改的是副本而非原始映射。
  • 完整恢复原始映射:切换回原始keymap后,重新绑定Esc到原始的emacs-meta,由于原始映射未被修改,\e[A会自动恢复为previous-history。

修正后运行输出符合预期:

initial state:
keymap: emacs-meta
function: previous-history

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

after restoring:
keymap: emacs-meta
function: previous-history

内容的提问来源于stack exchange,提问作者Joel Roth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 05:03:15