config: unbind keybind triggers unbinds both translated and physical

Fixes #4703

This changes `unbind` so it always removes all keybinds with the given
trigger pattern regardless of if it is translated or physical.

The previous behavior was technically correct, but this implements the pattern
of least surprise. I can't think of a scenario where you really want to
be exact about what key you're unbinding. And if that scenario does
exist, you can always fix it by rebinding after unbind.
This commit is contained in:
Mitchell Hashimoto
2025-01-06 09:45:26 -08:00
parent 3461204741
commit 359c390218
2 changed files with 38 additions and 2 deletions

View File

@ -914,7 +914,9 @@ class: ?[:0]const u8 = null,
/// ///
/// * `unbind` - Remove the binding. This makes it so the previous action /// * `unbind` - Remove the binding. This makes it so the previous action
/// is removed, and the key will be sent through to the child command /// is removed, and the key will be sent through to the child command
/// if it is printable. /// if it is printable. Unbind will remove any matching trigger,
/// including `physical:`-prefixed triggers without specifying the
/// prefix.
/// ///
/// * `csi:text` - Send a CSI sequence. i.e. `csi:A` sends "cursor up". /// * `csi:text` - Send a CSI sequence. i.e. `csi:A` sends "cursor up".
/// ///

View File

@ -1528,6 +1528,22 @@ pub const Set = struct {
/// Remove a binding for a given trigger. /// Remove a binding for a given trigger.
pub fn remove(self: *Set, alloc: Allocator, t: Trigger) void { pub fn remove(self: *Set, alloc: Allocator, t: Trigger) void {
// Remove whatever this trigger is
self.removeExact(alloc, t);
// If we have a physical we remove translated and vice versa.
const alternate: Trigger.Key = switch (t.key) {
.unicode => return,
.translated => |k| .{ .physical = k },
.physical => |k| .{ .translated = k },
};
var alt_t: Trigger = t;
alt_t.key = alternate;
self.removeExact(alloc, alt_t);
}
fn removeExact(self: *Set, alloc: Allocator, t: Trigger) void {
const entry = self.bindings.get(t) orelse return; const entry = self.bindings.get(t) orelse return;
_ = self.bindings.remove(t); _ = self.bindings.remove(t);
@ -1559,7 +1575,7 @@ pub const Set = struct {
}, },
} }
} else { } else {
// No over trigger points to this action so we remove // No other trigger points to this action so we remove
// the reverse mapping completely. // the reverse mapping completely.
_ = self.reverse.remove(leaf.action); _ = self.reverse.remove(leaf.action);
} }
@ -2101,6 +2117,24 @@ test "set: parseAndPut removed binding" {
try testing.expect(s.getTrigger(.{ .new_window = {} }) == null); try testing.expect(s.getTrigger(.{ .new_window = {} }) == null);
} }
test "set: parseAndPut removed physical binding" {
const testing = std.testing;
const alloc = testing.allocator;
var s: Set = .{};
defer s.deinit(alloc);
try s.parseAndPut(alloc, "physical:a=new_window");
try s.parseAndPut(alloc, "a=unbind");
// Creates forward mapping
{
const trigger: Trigger = .{ .key = .{ .physical = .a } };
try testing.expect(s.get(trigger) == null);
}
try testing.expect(s.getTrigger(.{ .new_window = {} }) == null);
}
test "set: parseAndPut sequence" { test "set: parseAndPut sequence" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;