diff --git a/kitty/conf/definition.py b/kitty/conf/definition.py index c9a67dcd2..7477b93a4 100644 --- a/kitty/conf/definition.py +++ b/kitty/conf/definition.py @@ -33,15 +33,26 @@ class Option: self.add_to_docs = add_to_docs +class Shortcut: + + __slots__ = 'name', 'group', 'key', 'action_def', 'short_text', 'long_text', 'add_to_default', 'add_to_docs' + + def __init__(self, name, group, key, action_def, short_text, long_text, add_to_default, add_to_docs): + self.name, self.group, self.key, self.action_def = name, group, key, action_def + self.short_text, self.long_text = short_text, long_text + self.add_to_default = add_to_default + self.add_to_docs = add_to_docs + + def option( - all_options, - group, - name, - defval, - long_text='', - option_type=to_string, - add_to_default=True, - add_to_docs=True + all_options, + group, + name, + defval, + long_text='', + option_type=to_string, + add_to_default=True, + add_to_docs=True ): is_multiple = name.startswith('+') if is_multiple: @@ -66,6 +77,23 @@ def option( return ans +def shortcut( + all_options, + group, + action_name, + key, + action_def, + short_text, + long_text='', + add_to_default=True, + add_to_docs=True +): + ans = Shortcut(action_name, group[0], key, action_def, short_text, long_text, add_to_default, add_to_docs) + key = 'sc-' + action_name + all_options.setdefault(key, []).append(ans) + return ans + + def option_func(all_options, all_groups): all_groups = {k: Group(k, *v) for k, v in all_groups.items()} group = [None] @@ -73,13 +101,15 @@ def option_func(all_options, all_groups): def change_group(name): group[0] = all_groups[name] - return partial(option, all_options, group), change_group, all_groups + return partial(option, all_options, group), partial(shortcut, all_options, group), change_group, all_groups def merged_opts(all_options, opt, i): yield opt for k in range(i + 1, len(all_options)): q = all_options[k] + if not isinstance(q, Option): + break if not q.long_text and q.add_to_docs: yield q else: @@ -96,32 +126,54 @@ def render_block(text): return '\n'.join('#: ' + line for line in lines) -def render_group(a, group): - if '.' not in group.name: - a('# ' + group.short_text + ' {{''{') - a('') - if group.start_text: - a(render_block(group.start_text)) - a('') - - def as_conf_file(all_options): ans = ['# vim:fileencoding=utf-8:ft=conf:foldmethod=marker', ''] a = ans.append current_group = None + num_open_folds = 0 all_options = list(all_options) - for i, opt in enumerate(all_options): - if not opt.long_text or not opt.add_to_docs: - continue - if opt.group is not current_group: - if current_group: - if current_group.end_text: - a(''), a(current_group.end_text) - if '.' not in opt.group.name: - a('# }}''}'), a('') - current_group = opt.group - render_group(a, current_group) + def render_group(group, is_shortcut): + nonlocal num_open_folds + if is_shortcut or '.' not in group.name: + a('# ' + group.short_text + ' {{''{') + num_open_folds += 1 + a('') + if group.start_text: + a(render_block(group.start_text)) + a('') + + def handle_group_end(group, new_group_name='', new_group_is_shortcut=False): + nonlocal num_open_folds + if group.end_text: + a(''), a(group.end_text) + is_subgroup = new_group_name.startswith(group.name + '.') + if not is_subgroup and num_open_folds > 0: + a('# }}''}'), a('') + num_open_folds -= 1 + + def handle_group(new_group, is_shortcut=False): + nonlocal current_group + if new_group is not current_group: + if current_group: + handle_group_end(current_group, new_group.name, is_shortcut) + current_group = new_group + render_group(current_group, is_shortcut) + + def handle_shortcut(shortcuts): + handle_group(shortcuts[0].group, True) + for sc in shortcuts: + a('map {} {}'.format(sc.key, sc.action_def)) + if sc.long_text: + a('') + for line in sc.long_text.strip().splitlines(): + a('#: ' + line) + a('') + + def handle_option(opt): + if not opt.long_text or not opt.add_to_docs: + return + handle_group(opt.group) mopts = list(merged_opts(all_options, opt, i)) sz = max(len(x.name) for x in mopts) for mo in mopts: @@ -130,8 +182,16 @@ def as_conf_file(all_options): a('') a(render_block(opt.long_text)) a('') + + for i, opt in enumerate(all_options): + if isinstance(opt, Option): + handle_option(opt) + else: + handle_shortcut(opt) + if current_group: - if current_group.end_text: - a(''), a(current_group.end_text) - a('# }}''}') + handle_group_end(current_group) + while num_open_folds > 0: + a('# }}''}') + num_open_folds -= 1 return ans diff --git a/kitty/config_data.py b/kitty/config_data.py index a28aca90e..1bce43666 100644 --- a/kitty/config_data.py +++ b/kitty/config_data.py @@ -115,7 +115,7 @@ def to_layout_names(raw): all_options = {} -o, g, all_groups = option_func(all_options, { +o, k, g, all_groups = option_func(all_options, { 'fonts': [ _('Fonts'), _('kitty has very powerful font management. You can configure individual\n' @@ -168,6 +168,7 @@ For example:: map ctrl+f>2 set_font_size 20 ''') ], + 'shortcuts.clipboard': [_('Clipboard')], }) # }}} @@ -506,8 +507,7 @@ o('color15', '#ffffff', option_type=to_color) dfctl = defines.default_color_table() for i in range(16, 256): - k = 'color{}'.format(i) - o(k, color_as_sharp(color_from_int(dfctl[i])), option_type=to_color, add_to_docs=False) + o('color{}'.format(i), color_as_sharp(color_from_int(dfctl[i])), option_type=to_color, add_to_docs=False) # }}} @@ -608,6 +608,21 @@ shortcuts.''')) o('clear_all_shortcuts', False, long_text=_(''' You can have kitty remove all shortcut definition seen up to this point. Useful, for instance, to remove the default shortcuts.''')) + +g('shortcuts.clipboard') # {{{ +k('copy_to_clipboard', 'kitty_mod+c', 'copy_to_clipboard', _('Copy to clipboard')) +k('paste_from_clipboard', 'kitty_mod+v', 'paste_from_clipboard', _('Paste from clipboard')) +k('paste_from_selection', 'kitty_mod+s', 'paste_from_selection', _('Paste from selection')) +k('paste_from_selection', 'shift+insert', 'paste_from_selection', _('Paste from selection')) +k('pass_selection_to_program', 'kitty_mod+o', 'pass_selection_to_program', _('Pass selection to program'), long_text=_(''' +You can also pass the contents of the current selection to any program using +pass_selection_to_program. By default, the system's open program is used, but +you can specify your own, for example:: + + map kitty_mod+o pass_selection_to_program firefox +''')) + +# }}} # }}} -type_map = {o.name: o.option_type for o in all_options.values()} +type_map = {o.name: o.option_type for o in all_options.values() if hasattr(o, 'option_type')}