mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-12 11:39:33 +02:00
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).
179 lines
5.1 KiB
Go
179 lines
5.1 KiB
Go
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
|
|
|
|
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
)
|
|
|
|
var _ = fmt.Print
|
|
|
|
func TestStringLiteralParsing(t *testing.T) {
|
|
for q, expected := range map[string]string{
|
|
`abc`: `abc`,
|
|
`a\nb\M`: "a\nb\\M",
|
|
`a\x20\x1\u1234\123\12|`: "a \\x1\u1234\123\x0a|",
|
|
} {
|
|
actual, err := StringLiteral(q)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if expected != actual {
|
|
t.Fatalf("Failed with input: %#v\n%#v != %#v", q, expected, actual)
|
|
}
|
|
}
|
|
}
|
|
|
|
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`,
|
|
`+`: `plus`,
|
|
`cmd+b>opt+>`: `super+b alt+>`,
|
|
`cmd+>>opt+>`: `super+> alt+>`,
|
|
} {
|
|
expected := strings.Split(expected_, " ")
|
|
actual := NormalizeShortcuts(q)
|
|
if diff := cmp.Diff(expected, actual); diff != "" {
|
|
t.Fatalf("failed with input: %#v\n%s", q, diff)
|
|
}
|
|
}
|
|
}
|