Skip to content

Commit

Permalink
Add docs for performable
Browse files Browse the repository at this point in the history
  • Loading branch information
mitchellh committed Jan 2, 2025
1 parent f38d158 commit 95b73f1
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 5 deletions.
14 changes: 11 additions & 3 deletions src/Surface.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1941,16 +1941,24 @@ fn maybeHandleBinding(
return .closed;
}

// If we have the performable flag and the
// action was not performed do nothing at all
// If we have the performable flag and the action was not performed,
// then we act as though a binding didn't exist.
if (leaf.flags.performable and !performed) {
// If we're in a sequence, we treat this as if we pressed a key
// that doesn't exist in the sequence. Reset our sequence and flush
// any queued events.
if (self.keyboard.bindings != null) {
self.keyboard.bindings = null;
self.endKeySequence(.flush, .retain);
}

return null;
}

// If we consume this event, then we are done. If we don't consume
// it, we processed the action but we still want to process our
// encodings, too.
if (consumed) {
if (performed and consumed) {
// If we had queued events, we deinit them since we consumed
self.endKeySequence(.drop, .retain);

Expand Down
9 changes: 9 additions & 0 deletions src/config/Config.zig
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,15 @@ class: ?[:0]const u8 = null,
/// Since they are not associated with a specific terminal surface,
/// they're never encoded.
///
/// * `performable:` - Only consume the input if the action is able to be
/// performed. For example, the `copy_to_clipboard` action will only
/// consume the input if there is a selection to copy. If there is no
/// selection, Ghostty behaves as if the keybind was not set. This has
/// no effect with `global:` or `all:`-prefixed keybinds. For key
/// sequences, this will reset the sequence if the action is not
/// performable (acting identically to not having a keybind set at
/// all).
///
/// Keybind triggers are not unique per prefix combination. For example,
/// `ctrl+a` and `global:ctrl+a` are not two separate keybinds. The keybind
/// set later will overwrite the keybind set earlier. In this case, the
Expand Down
15 changes: 13 additions & 2 deletions src/input/Binding.zig
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ pub const Flags = packed struct {
/// See the keybind config documentation for more information.
global: bool = false,

/// True if this binding can be performed then the action is
/// triggered otherwise it acts as if it doesn't exist.
/// True if this binding should only be triggered if the action can be
/// performed. If the action can't be performed then the binding acts as
/// if it doesn't exist.
performable: bool = false,
};

Expand Down Expand Up @@ -1654,6 +1655,16 @@ test "parse: triggers" {
.flags = .{ .consumed = false },
}, try parseSingle("unconsumed:physical:a+shift=ignore"));

// performable keys
try testing.expectEqual(Binding{
.trigger = .{
.mods = .{ .shift = true },
.key = .{ .translated = .a },
},
.action = .{ .ignore = {} },
.flags = .{ .performable = true },
}, try parseSingle("performable:shift+a=ignore"));

// invalid key
try testing.expectError(Error.InvalidFormat, parseSingle("foo=ignore"));

Expand Down

0 comments on commit 95b73f1

Please sign in to comment.