test(dired): teach describe.key about mode scope; drop one overclaim

`describe_key_identifies_every_default_binding` iterated every binding
in the stack and asserted `pmacs.describe.key` resolves it context-free.
That held only because no builtin had ever bound a mode-scoped key:
dired is #129's first non-detection consumer, so its `n` / `p` / `g`
correctly resolved to nothing and the test went red on the feature
rather than on a defect.

It now sets the effective context per binding -- the mode for a
mode-scoped default, and explicitly NO mode for a global one, because a
leaked mode legitimately shadows a global chord of the same name
(dired's `RET` shadows `edit.newline-and-indent`, which is the point of
the mode) and would make the assertion compare the wrong pair. A floor
assertion keeps the new arm from going vacuous if the last mode-scoped
default is ever removed.

Also corrects a doc comment rather than leaving it to be believed:
acceptance 3c does not pin the descent ROUTING. Dired holds focus in its
own panel, so a raw `switch_buffer` lands in the same window and the
mutation is vacuous against that test; dedication is what distinguishes
the two paths, so the discriminating pin is the dedicated-panel test
next to it. Verified by mutation, not assumed.
This commit is contained in:
Levi Neuwirth 2026-07-25 15:03:19 -04:00
parent f71055a206
commit e7fa9e9720
2 changed files with 55 additions and 7 deletions

View File

@ -5659,17 +5659,25 @@ mod tests {
// ---- T M2.11 acceptance --------------------------------------------------
/// Every chord in the default global keymap must round-trip through
/// Every chord in the default keymap must round-trip through
/// `pmacs.describe.key`: returning a non-nil table whose `command`
/// matches the binding the keymap stack stores.
///
/// `describe.key` resolves against the **effective context**
/// (buffer-local → mode → global), so a mode-scoped default is
/// asserted with a buffer that carries that mode rather than
/// context-free. Dired is the first builtin to bind mode-scoped keys
/// (#129's first non-detection consumer), and without the mode in
/// place its `n` / `p` / `g` correctly resolve to nothing.
#[test]
fn describe_key_identifies_every_default_binding() {
use crate::keymap_stack::Scope;
let s = EditorState::new();
let kms = s.lua_host.keymaps().borrow();
let bindings: Vec<(String, String)> = kms
let bindings: Vec<(Scope, String, String)> = kms
.iter_all()
.into_iter()
.map(|(_, seq, b)| (crate::key::display_sequence(&seq), b.command))
.map(|(scope, seq, b)| (scope, crate::key::display_sequence(&seq), b.command))
.collect();
drop(kms);
// Sanity floor: the default keymap binds at least the M1 surface.
@ -5678,18 +5686,50 @@ mod tests {
"default keymap unexpectedly small: {} bindings",
bindings.len()
);
let modes: usize = bindings
.iter()
.filter(|(scope, _, _)| matches!(scope, Scope::Mode(_)))
.count();
assert!(
modes >= 1,
"a mode-scoped default is expected since dired Stage 1; \
found none, so the mode arm below asserts nothing"
);
for (seq, expected_command) in &bindings {
for (scope, seq, expected_command) in &bindings {
let mode = match scope {
Scope::Mode(name) => Some(name.clone()),
// No buffer-scoped defaults exist; a future one would
// need its own buffer context here.
Scope::Buffer(_) => continue,
Scope::Global => None,
};
// Set the context explicitly on EVERY iteration, including
// the global one: a mode left over from a previous iteration
// legitimately shadows a global binding of the same chord
// (dired's mode-scoped `RET` shadows
// `edit.newline-and-indent`, which is the point of the
// mode), so a leaked mode would make this assert the wrong
// thing.
let context = match &mode {
Some(name) => {
format!("pmacs.buffer.set_major_mode(pmacs.window.buffer(), {name:?}); ")
}
None => "pmacs.buffer.set_major_mode(pmacs.window.buffer(), nil); ".to_owned(),
};
let script = format!(
"local r = pmacs.describe.key({seq:?}); \
"{context}local r = pmacs.describe.key({seq:?}); \
if r == nil then return 'nil' else return r.command end"
);
let got: String = s.lua_host.lua().load(&script).eval().unwrap_or_else(|e| {
panic!("describe.key({seq}) raised: {e}");
});
assert_eq!(
&got, expected_command,
"describe.key for {seq:?} returned {got:?}, expected {expected_command:?}"
&got,
expected_command,
"describe.key for {seq:?} (scope {}) returned {got:?}, \
expected {expected_command:?}",
scope.render()
);
}
}

View File

@ -700,6 +700,14 @@ fn dired_canonicalization_is_the_cores_own_normalizer() {
/// side window (Q#DR10): the next directory is the same kind of thing as
/// the current one and belongs in the same slot. Neither replaced by a
/// document window nor duplicated.
///
/// **This test does not pin the routing itself, and says so rather than
/// implying otherwise:** dired holds the focus in its own panel here, so
/// a raw `switch_buffer` lands in that same window and the assertions
/// below hold either way (verified — the mutation is VACUOUS against
/// this test). What distinguishes `display { side = … }` from the raw
/// switch is dedication, so the discriminating pin is
/// `dired_descent_from_a_dedicated_panel_leaves_the_pin_alone` below.
#[test]
fn dired_directory_descent_stays_in_its_side_window() {
let td = fixture_dir();