mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-19 06:54:58 +02:00
feat: add per-mapping --allow-fallback for layout-independent shortcuts
Add --allow-fallback option to the map command that controls shifted and ascii (alternate_key) fallback for individual key mappings. For non-Latin keyboard layouts, when the current layout key is non-ascii (codepoint > 127 and < 0xE000), the alternate_key from the base layout is used for matching if the mapping opts in via --allow-fallback=shifted,ascii. Default kitty bindings use --allow-fallback=shifted,ascii so they work out of the box with non-Latin layouts. User custom mappings default to --allow-fallback=shifted (preserving existing shifted_key behavior without ascii fallback). --allow-fallback=none disables all fallback for a mapping. Python side: parse_options_for_map() in options/utils.py handles flag parsing, ShortcutMapping uses it in __init__. get_shortcut() filters candidates by per-mapping allow_fallback. Go side: ParseMap() handles --allow-fallback, KeyAction stores AllowFallback, ShortcutTracker.Match passes it to matching. MatchesParsedShortcut defaults to shifted,ascii for hardcoded shortcuts. Migrated kittens (themes, command_palette, diff, choose_files) to use ShortcutTracker with configurable map entries. Tests added for Python (5 test methods) and Go (ParseMap + key matching).
This commit is contained in:
@@ -28,6 +28,140 @@ func TestStringLiteralParsing(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMap(t *testing.T) {
|
||||
// Test without --allow-fallback (default "shifted")
|
||||
ka, err := ParseMap("ctrl+c copy_to_clipboard")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ka.AllowFallback != "shifted" {
|
||||
t.Fatalf("Expected AllowFallback 'shifted', got %#v", ka.AllowFallback)
|
||||
}
|
||||
if ka.Name != "copy_to_clipboard" {
|
||||
t.Fatalf("Expected Name 'copy_to_clipboard', got %#v", ka.Name)
|
||||
}
|
||||
if diff := cmp.Diff([]string{"ctrl+c"}, ka.Normalized_keys); diff != "" {
|
||||
t.Fatalf("Keys mismatch:\n%s", diff)
|
||||
}
|
||||
|
||||
// Test with --allow-fallback=ascii
|
||||
ka, err = ParseMap("--allow-fallback=ascii ctrl+c copy_to_clipboard")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ka.AllowFallback != "ascii" {
|
||||
t.Fatalf("Expected AllowFallback 'ascii', got %#v", ka.AllowFallback)
|
||||
}
|
||||
if ka.Name != "copy_to_clipboard" {
|
||||
t.Fatalf("Expected Name 'copy_to_clipboard', got %#v", ka.Name)
|
||||
}
|
||||
|
||||
// Test with --allow-fallback=shifted,ascii
|
||||
ka, err = ParseMap("--allow-fallback=shifted,ascii cmd+c copy_to_clipboard")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ka.AllowFallback != "shifted,ascii" {
|
||||
t.Fatalf("Expected AllowFallback 'shifted,ascii', got %#v", ka.AllowFallback)
|
||||
}
|
||||
if ka.Name != "copy_to_clipboard" {
|
||||
t.Fatalf("Expected Name 'copy_to_clipboard', got %#v", ka.Name)
|
||||
}
|
||||
if diff := cmp.Diff([]string{"super+c"}, ka.Normalized_keys); diff != "" {
|
||||
t.Fatalf("Keys mismatch:\n%s", diff)
|
||||
}
|
||||
|
||||
// Test with --allow-fallback and action args
|
||||
ka, err = ParseMap("--allow-fallback=shifted,ascii ctrl+shift+f launch --type=tab grep")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ka.AllowFallback != "shifted,ascii" {
|
||||
t.Fatalf("Expected AllowFallback 'shifted,ascii', got %#v", ka.AllowFallback)
|
||||
}
|
||||
if ka.Name != "launch" {
|
||||
t.Fatalf("Expected Name 'launch', got %#v", ka.Name)
|
||||
}
|
||||
if ka.Args != "--type=tab grep" {
|
||||
t.Fatalf("Expected Args '--type=tab grep', got %#v", ka.Args)
|
||||
}
|
||||
|
||||
// Test space form: --allow-fallback ascii (without =)
|
||||
ka, err = ParseMap("--allow-fallback ascii ctrl+c copy_to_clipboard")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ka.AllowFallback != "ascii" {
|
||||
t.Fatalf("Expected AllowFallback 'ascii', got %#v", ka.AllowFallback)
|
||||
}
|
||||
if ka.Name != "copy_to_clipboard" {
|
||||
t.Fatalf("Expected Name 'copy_to_clipboard', got %#v", ka.Name)
|
||||
}
|
||||
if diff := cmp.Diff([]string{"ctrl+c"}, ka.Normalized_keys); diff != "" {
|
||||
t.Fatalf("Keys mismatch:\n%s", diff)
|
||||
}
|
||||
|
||||
// Test space form: --allow-fallback shifted,ascii
|
||||
ka, err = ParseMap("--allow-fallback shifted,ascii cmd+c copy_to_clipboard")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ka.AllowFallback != "shifted,ascii" {
|
||||
t.Fatalf("Expected AllowFallback 'shifted,ascii', got %#v", ka.AllowFallback)
|
||||
}
|
||||
if ka.Name != "copy_to_clipboard" {
|
||||
t.Fatalf("Expected Name 'copy_to_clipboard', got %#v", ka.Name)
|
||||
}
|
||||
|
||||
// Test --allow-fallback=none (equals form)
|
||||
ka, err = ParseMap("--allow-fallback=none ctrl+c copy_to_clipboard")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ka.AllowFallback != "" {
|
||||
t.Fatalf("Expected AllowFallback '', got %#v", ka.AllowFallback)
|
||||
}
|
||||
if ka.Name != "copy_to_clipboard" {
|
||||
t.Fatalf("Expected Name 'copy_to_clipboard', got %#v", ka.Name)
|
||||
}
|
||||
|
||||
// Test --allow-fallback none (space form)
|
||||
ka, err = ParseMap("--allow-fallback none ctrl+c copy_to_clipboard")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ka.AllowFallback != "" {
|
||||
t.Fatalf("Expected AllowFallback '', got %#v", ka.AllowFallback)
|
||||
}
|
||||
if ka.Name != "copy_to_clipboard" {
|
||||
t.Fatalf("Expected Name 'copy_to_clipboard', got %#v", ka.Name)
|
||||
}
|
||||
|
||||
// Test error: unknown flag
|
||||
_, err = ParseMap("--allow-fallbak=ascii ctrl+c copy")
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for unknown flag --allow-fallbak")
|
||||
}
|
||||
|
||||
// Test error: unknown flag without =
|
||||
_, err = ParseMap("--unknown ctrl+c copy")
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for unknown flag --unknown")
|
||||
}
|
||||
|
||||
// Test error: invalid allow-fallback value
|
||||
_, err = ParseMap("--allow-fallback=typo ctrl+c copy")
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for invalid allow-fallback value 'typo'")
|
||||
}
|
||||
|
||||
// Test error: invalid allow-fallback value in space form
|
||||
_, err = ParseMap("--allow-fallback typo ctrl+c copy")
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for invalid allow-fallback value 'typo' in space form")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeShortcuts(t *testing.T) {
|
||||
for q, expected_ := range map[string]string{
|
||||
`a`: `a`,
|
||||
|
||||
Reference in New Issue
Block a user