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:
Павел Мешалкин 2026-03-25 19:34:13 +03:00
parent 79bde7f9a9
commit 8ffdf7d7ee
20 changed files with 987 additions and 227 deletions

View file

@ -274,6 +274,55 @@ for copying to clipboard.
and you cannot have mappings with and without conditions applying to multi-keys
with the same first key.
Non-Latin keyboard layout support
--------------------------------------
When using a non-Latin keyboard layout (e.g. Russian, Arabic, Greek), letter-key
shortcuts like :kbd:`Ctrl+C` stop working because the key produces a non-Latin
character. kitty solves this with the ``--allow-fallback`` option on the ``map``
directive, which controls how shortcuts fall back to the physical key position.
The ``--allow-fallback`` option accepts a comma-separated list of fallback types:
``shifted``
Fall back to the *shifted key* — the character produced when Shift is held
with the key. This is the default for all mappings and preserves the existing
behavior.
``ascii``
Fall back to the *alternate key* — the character that the physical key would
produce in a standard US layout. This only triggers when the key produces a
non-ASCII character, so it has no effect on Latin-based layouts like Dvorak
or Colemak.
``none``
Disable all fallback matching. The mapping will only match the exact key
specified, ignoring both shifted and alternate key positions.
For example::
# Enable both shifted and ASCII fallback (used by default kitty shortcuts)
map --allow-fallback=shifted,ascii kitty_mod+c copy_to_clipboard
# Only ASCII fallback, no shifted key fallback
map --allow-fallback=ascii ctrl+s save_something
# Disable all fallback (neither shifted nor alternate key matching)
map --allow-fallback=none ctrl+x some_action
All default kitty shortcuts use ``--allow-fallback=shifted,ascii``, so they work
out of the box with non-Latin layouts. Custom mappings without an explicit
``--allow-fallback`` get the default value of ``shifted``, which preserves
backward compatibility.
.. note::
The ``ascii`` fallback uses a non-ASCII guard: it only activates when
the key produces a character with a Unicode code point above 127. This means
alternative Latin layouts (Dvorak, Colemak, etc.) are never affected by the
``ascii`` fallback — only non-Latin layouts trigger it.
Sending arbitrary text or keys to the program running in kitty
--------------------------------------------------------------------------------