Merge branch 'action_aliases_command_palette' of https://github.com/dmgerman/kitty

This commit is contained in:
Kovid Goyal
2026-04-07 19:40:55 +05:30
3 changed files with 406 additions and 144 deletions

View File

@@ -17,8 +17,8 @@ class TestCommandPalette(BaseTest):
default_mode = data['modes']['']
# Should have at least some categories
self.assertTrue(len(default_mode) > 0, 'Should have at least one category')
# All category names should be from the known groups
known_titles = set(groups.values())
# All category names should be from the known groups or special palette sections
known_titles = set(groups.values()) | {'Action aliases', 'Kitten aliases', 'Combined actions'}
for cat_name in default_mode:
self.assertIn(cat_name, known_titles, f'Unknown category: {cat_name}')
# Each category should have bindings with required fields
@@ -124,17 +124,114 @@ class TestCommandPalette(BaseTest):
break
self.assertTrue(found_unmapped, 'Expected at least one unmapped action to always be present')
def test_unmapped_actions_sorted_order(self):
def test_alias_resolution(self):
from kittens.command_palette.main import collect_keys_data
from kitty.options.utils import ActionAlias, AliasMap, parse_map
opts = self.set_options()
# Set up action aliases: launch_tab (bound) and launch_bg (unbound)
alias_map = AliasMap()
alias_map.append('launch_tab', ActionAlias('launch_tab', 'launch --type=tab --cwd=current'))
alias_map.append('launch_bg', ActionAlias('launch_bg', 'launch --type=background'))
opts.alias_map = alias_map
# Add a keybinding that uses the launch_tab alias
for kd in parse_map('f1 launch_tab vim'):
kd = kd.resolve_and_copy(opts.kitty_mod)
default_mode = opts.keyboard_modes['']
default_mode.keymap.setdefault(kd.trigger, []).append(kd)
data = collect_keys_data(opts)
# In each category, mapped bindings (non-empty key) should come before unmapped ones
for cat_name, bindings in data['modes'].get('', {}).items():
seen_unmapped = False
# Aliases should have their own section
self.assertIn('Action aliases', data['modes'][''],
'Aliases should have a dedicated section')
alias_section = data['modes']['']['Action aliases']
# Bound alias should appear in the alias section with its key
bound = [b for b in alias_section if b.get('alias') == 'launch_tab']
self.ae(len(bound), 1, 'Bound alias should appear exactly once')
self.ae(bound[0]['action'], 'launch')
self.assertTrue(bound[0]['key'] != '', 'Bound alias should have its key')
# Unbound alias should also appear in the alias section
unbound = [b for b in alias_section if b['action_display'] == 'launch_bg']
self.ae(len(unbound), 1, 'Unbound alias should appear exactly once')
self.ae(unbound[0]['action'], 'launch')
self.ae(unbound[0]['key'], '')
self.assertTrue(unbound[0]['help'].startswith('Alias for:'))
# Bound alias should NOT appear in any other category
for cat_name, bindings in data['modes'][''].items():
if cat_name == 'Action aliases':
continue
for b in bindings:
if b['key'] == '':
seen_unmapped = True
elif seen_unmapped:
self.fail(
f'In category {cat_name!r}, mapped binding {b!r} follows an unmapped one'
)
self.assertNotEqual(b.get('alias'), 'launch_tab',
f'Alias binding should not appear in {cat_name!r}')
def test_kitten_alias_section(self):
from kittens.command_palette.main import collect_keys_data
from kitty.options.utils import ActionAlias, AliasMap
opts = self.set_options()
# Set up a kitten alias: kitten hints -> kitten hints --hints-offset=0
alias_map = AliasMap()
alias_map.append('kitten', ActionAlias('hints', 'hints --hints-offset=0', replace_second_arg=True))
opts.alias_map = alias_map
data = collect_keys_data(opts)
self.assertIn('Kitten aliases', data['modes'][''],
'Kitten aliases should have a dedicated section')
kitten_section = data['modes']['']['Kitten aliases']
found = [b for b in kitten_section if b['action_display'] == 'kitten hints']
self.ae(len(found), 1, 'Kitten alias should appear exactly once')
self.ae(found[0]['definition'], 'kitten hints --hints-offset=0')
self.assertTrue(found[0]['help'].startswith('Alias for:'))
def test_combine_actions_section(self):
from kittens.command_palette.main import collect_keys_data
from kitty.options.utils import parse_map
opts = self.set_options()
# Add a combine keybinding
for kd in parse_map('f2 combine : new_tab : launch vim'):
kd = kd.resolve_and_copy(opts.kitty_mod)
default_mode = opts.keyboard_modes['']
default_mode.keymap.setdefault(kd.trigger, []).append(kd)
data = collect_keys_data(opts)
# Combine bindings should have their own section
self.assertIn('Combined actions', data['modes'][''],
'Combine bindings should have a dedicated section')
combine_section = data['modes']['']['Combined actions']
found = [b for b in combine_section if b['action'] == 'combine']
self.assertTrue(len(found) > 0, 'Combine binding should be in the section')
self.ae(found[0]['key'], 'f2')
def test_no_duplicate_alias_entries(self):
from kittens.command_palette.main import collect_keys_data
from kitty.options.utils import ActionAlias, AliasMap, parse_map
opts = self.set_options()
# Set up aliases, some bound to keys and some not
alias_map = AliasMap()
alias_map.append('launch_tab', ActionAlias('launch_tab', 'launch --type=tab --cwd=current'))
alias_map.append('launch_bg', ActionAlias('launch_bg', 'launch --type=background'))
opts.alias_map = alias_map
for kd in parse_map('f1 launch_tab vim'):
kd = kd.resolve_and_copy(opts.kitty_mod)
default_mode = opts.keyboard_modes['']
default_mode.keymap.setdefault(kd.trigger, []).append(kd)
data = collect_keys_data(opts)
# Collect all entries that have an alias field across all categories
alias_entries: list[tuple[str, str]] = []
for cat_name, bindings in data['modes'][''].items():
for b in bindings:
if 'alias' in b:
alias_entries.append((cat_name, b['alias']))
# Each alias should appear exactly once
seen: set[str] = set()
for cat_name, alias_name in alias_entries:
self.assertNotIn(alias_name, seen,
f'Alias {alias_name!r} appears in multiple places')
seen.add(alias_name)
def test_unmapped_actions_sorted_order(self):
# Covered by test_collect_keys_bindings_sorted
pass