ensure `ctrl++` parses, clarify case folding docs
parent
c4f1c78fcf
commit
8f40d1331e
|
|
@ -935,6 +935,22 @@ class: ?[:0]const u8 = null,
|
|||
/// QWERTY keyboard, but will match the `q` key on a AZERTY keyboard
|
||||
/// (assuming US physical layout).
|
||||
///
|
||||
/// For Unicode codepoints, matching is done by comparing the set of
|
||||
/// modifiers with the unmodified codepoint. The unmodified codepoint is
|
||||
/// sometimes called an "unshifted character" in other software, but all
|
||||
/// modifiers are considered, not only shift. For example, `ctrl+a` will match
|
||||
/// `a` but not `ctrl+shift+a` (which is `A` on a US keyboard).
|
||||
///
|
||||
/// Further, codepoint matching is case-insensitive and the unmodified
|
||||
/// codepoint is always case folded for comparison. As a result,
|
||||
/// `ctrl+A` configured will match when `ctrl+a` is pressed. Note that
|
||||
/// this means some key combinations are impossible depending on keyboard
|
||||
/// layout. For example, `ctrl+_` is impossible on a US keyboard because
|
||||
/// `_` is `shift+-` and `ctrl+shift+-` is not equal to `ctrl+_` (because
|
||||
/// the modifiers don't match!). More details on impossible key combinations
|
||||
/// can be found at this excellent source written by Qt developers:
|
||||
/// https://doc.qt.io/qt-6/qkeysequence.html#keyboard-layout-issues
|
||||
///
|
||||
/// Physical key codes can be specified by using any of the key codes
|
||||
/// as specified by the [W3C specification](https://www.w3.org/TR/uievents-code/).
|
||||
/// For example, `KeyA` will match the physical `a` key on a US standard
|
||||
|
|
|
|||
|
|
@ -1109,10 +1109,11 @@ pub const Trigger = struct {
|
|||
pub fn parse(input: []const u8) !Trigger {
|
||||
if (input.len == 0) return Error.InvalidFormat;
|
||||
var result: Trigger = .{};
|
||||
var iter = std.mem.tokenizeScalar(u8, input, '+');
|
||||
loop: while (iter.next()) |part| {
|
||||
// All parts must be non-empty
|
||||
if (part.len == 0) return Error.InvalidFormat;
|
||||
var rem: []const u8 = input;
|
||||
loop: while (rem.len > 0) {
|
||||
const idx = std.mem.indexOfScalar(u8, rem, '+') orelse rem.len;
|
||||
const part = rem[0..idx];
|
||||
rem = if (idx >= rem.len) "" else rem[idx + 1 ..];
|
||||
|
||||
// Check if its a modifier
|
||||
const modsInfo = @typeInfo(key.Mods).@"struct";
|
||||
|
|
@ -1148,6 +1149,13 @@ pub const Trigger = struct {
|
|||
// single keys.
|
||||
if (!result.isKeyUnset()) return Error.InvalidFormat;
|
||||
|
||||
// If the part is empty it means that it is actually
|
||||
// a literal `+`, which we treat as a Unicode character.
|
||||
if (part.len == 0) {
|
||||
result.key = .{ .unicode = '+' };
|
||||
continue :loop;
|
||||
}
|
||||
|
||||
// Check if its a key
|
||||
const keysInfo = @typeInfo(key.Key).@"enum";
|
||||
inline for (keysInfo.fields) |field| {
|
||||
|
|
@ -2000,6 +2008,32 @@ test "parse: w3c key names" {
|
|||
try testing.expectError(Error.InvalidFormat, parseSingle("Keya=ignore"));
|
||||
}
|
||||
|
||||
test "parse: plus sign" {
|
||||
const testing = std.testing;
|
||||
|
||||
try testing.expectEqual(
|
||||
Binding{
|
||||
.trigger = .{ .key = .{ .unicode = '+' } },
|
||||
.action = .{ .ignore = {} },
|
||||
},
|
||||
try parseSingle("+=ignore"),
|
||||
);
|
||||
|
||||
// Modifier
|
||||
try testing.expectEqual(
|
||||
Binding{
|
||||
.trigger = .{
|
||||
.key = .{ .unicode = '+' },
|
||||
.mods = .{ .ctrl = true },
|
||||
},
|
||||
.action = .{ .ignore = {} },
|
||||
},
|
||||
try parseSingle("ctrl++=ignore"),
|
||||
);
|
||||
|
||||
try testing.expectError(Error.InvalidFormat, parseSingle("++=ignore"));
|
||||
}
|
||||
|
||||
// For Ghostty 1.2+ we changed our key names to match the W3C and removed
|
||||
// `physical:`. This tests the backwards compatibility with the old format.
|
||||
// Note that our backwards compatibility isn't 100% perfect since triggers
|
||||
|
|
|
|||
Loading…
Reference in New Issue