1#!/usr/local/bin/python3.8
2# vim:fileencoding=utf-8
3# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
4
5import inspect
6from typing import Dict, List, NamedTuple
7
8from .boss import Boss
9from .tabs import Tab
10from .types import run_once, ActionGroup, ActionSpec
11from .window import Window
12
13
14class Action(NamedTuple):
15    name: str
16    group: str
17    short_help: str
18    long_help: str
19
20
21groups: Dict[ActionGroup, str] = {
22    'cp': 'Copy/paste',
23    'sc': 'Scrolling',
24    'win': 'Window management',
25    'tab': 'Tab management',
26    'mouse': 'Mouse actions',
27    'mk': 'Marks',
28    'lay': 'Layouts',
29    'misc': 'Miscellaneous',
30}
31group_title = groups.__getitem__
32
33
34@run_once
35def get_all_actions() -> Dict[str, List[Action]]:
36    ' test docstring '
37
38    ans: Dict[str, List[Action]] = {}
39
40    def is_action(x: object) -> bool:
41        return isinstance(getattr(x, 'action_spec', None), ActionSpec)
42
43    def as_action(x: object) -> Action:
44        spec: ActionSpec = getattr(x, 'action_spec')
45        doc = inspect.cleandoc(spec.doc)
46        lines = doc.splitlines()
47        first = lines.pop(0)
48        short_help = first
49        long_help = '\n'.join(lines).strip()
50        return Action(getattr(x, '__name__'), spec.group, short_help, long_help)
51
52    seen = set()
53    for cls in (Window, Tab, Boss):
54        for (name, func) in inspect.getmembers(cls, is_action):
55            ac = as_action(func)
56            if ac.name not in seen:
57                ans.setdefault(ac.group, []).append(ac)
58                seen.add(ac.name)
59    for i, which in enumerate('first second third fourth fifth sixth seventh eighth ninth tenth'.split()):
60        name = f'{which}_window'
61        if name not in seen:
62            seen.add(name)
63            ans['win'].append(Action(name, 'win', f'Focus the {which} window', ''))
64
65    return ans
66
67
68def dump() -> None:
69    from pprint import pprint
70    pprint(get_all_actions())
71
72
73def as_rst() -> str:
74    from .options.definition import definition
75    from .conf.types import Mapping
76    allg = get_all_actions()
77    lines: List[str] = []
78    a = lines.append
79    maps: Dict[str, List[Mapping]] = {}
80    for m in definition.iter_all_maps():
81        if m.documented:
82            func = m.action_def.split()[0]
83            maps.setdefault(func, []).append(m)
84
85    for group in sorted(allg, key=lambda x: group_title(x).lower()):
86        title = group_title(group)
87        a('')
88        a(f'.. _action-group-{group}:')
89        a('')
90        a(title)
91        a('-' * len(title))
92        a('')
93
94        for action in allg[group]:
95            a('')
96            a(f'.. _action-{action.name}:')
97            a('')
98            a(action.name)
99            a('+' * len(action.name))
100            a('')
101            a(action.short_help)
102            a('')
103            if action.long_help:
104                a(action.long_help)
105            if action.name in maps:
106                a('')
107                a('Default shortcuts using this action:')
108                scs = {f':sc:`kitty.{m.name}`' for m in maps[action.name]}
109                a(', '.join(sorted(scs)))
110    return '\n'.join(lines)
111