From debbc76ebc4f2ee23909d4f972764e607f8fcfae Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Wed, 6 Aug 2025 15:18:36 +0200 Subject: [PATCH] Refactor commands database. --- ircplom/tui_base.py | 81 ++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 45 deletions(-) diff --git a/ircplom/tui_base.py b/ircplom/tui_base.py index 25152ca..204008c 100644 --- a/ircplom/tui_base.py +++ b/ircplom/tui_base.py @@ -381,22 +381,6 @@ class BaseTui(QueueMixin): window.set_geometry() self.redraw_affected() - def _cmd_name_to_cmd(self, cmd_name: str) -> Optional[Callable]: - 'Map cmd_name to executable TUI element method.' - cmd_name = CMD_SHORTCUTS.get(cmd_name, cmd_name) - ancestors = [self] - steps = cmd_name.split('.') - method_name = f'cmd__{steps[-1]}' - for step in steps[:-1]: - parent = ancestors[-1] - if not hasattr(parent, step): - break - ancestors += [getattr(parent, step)] - for ancestor in reversed(ancestors): - if hasattr(ancestor, method_name): - return getattr(ancestor, method_name) - return None - @property def window(self) -> Window: 'Currently selected Window.' @@ -406,6 +390,31 @@ class BaseTui(QueueMixin): self._window_idx = idx self.window.draw() + @property + def _commands(self) -> dict[str, tuple[Callable[..., None], + int, tuple[str, ...]]]: + cmds = {} + method_name_prefix = 'cmd__' + base = 'self' + for path in (base, f'{base}.window', f'{base}.window.prompt', + f'{base}.window.log'): + for cmd_method_name in [name for name in dir(eval(path)) + if name.startswith(method_name_prefix)]: + path_prefix = f'{path}.' + cmd_name = (path_prefix[len(base)+1:] + + cmd_method_name[len(method_name_prefix):]) + method = eval(f'{path_prefix}{cmd_method_name}') + n_args_min = 0 + arg_names = [] + for arg_name, param in signature(method).parameters.items(): + arg_names += [arg_name] + n_args_min += int(param.default == inspect_empty) + cmds[cmd_name] = (method, n_args_min, tuple(arg_names)) + for key, target in CMD_SHORTCUTS.items(): + if target in cmds: + cmds[key] = cmds[target] + return cmds + def handle_keyboard_event(self, typed_in: str) -> None: 'Translate keyboard input into appropriate actions.' if typed_in[0] == _CHAR_RESIZE: @@ -413,9 +422,7 @@ class BaseTui(QueueMixin): return if typed_in in _KEYBINDINGS: cmd_data = _KEYBINDINGS[typed_in] - cmd = self._cmd_name_to_cmd(cmd_data[0]) - if cmd: - cmd(*cmd_data[1:]) + self._commands[cmd_data[0]][0](*cmd_data[1:]) elif typed_in.startswith(_B64_PREFIX): encoded = typed_in[len(_B64_PREFIX):] to_paste = '' @@ -444,19 +451,17 @@ class BaseTui(QueueMixin): if to_parse[0] == '/': toks = to_parse.split(maxsplit=1) cmd_name = toks.pop(0) - cmd = self._cmd_name_to_cmd(cmd_name[1:]) + cmd, n_args_min, arg_names = self._commands.get(cmd_name[1:], + (None, 0, ())) if not cmd: alert = f'{cmd_name} unknown' elif cmd.__name__ == stack()[0].function: alert = f'{cmd_name} would loop into ourselves' else: - params = signature(cmd).parameters - n_args_max = len(params) + n_args_max = len(arg_names) if toks and not n_args_max: alert = f'{cmd_name} given argument(s) while none expected' else: - n_args_min = len([p for p in params.values() - if p.default == inspect_empty]) args: list[str] = [] while toks and len(args) < n_args_max: toks = toks[0].split(maxsplit=1) @@ -474,28 +479,14 @@ class BaseTui(QueueMixin): def cmd__help(self) -> None: 'Print available commands.' self._log.add('commands available in this window:') - cmds = {} - cmd_prefix = 'cmd__' - base = 'self' - for path in (base, f'{base}.window', f'{base}.window.prompt'): - for cmd_method_name in [attr_name for attr_name in dir(eval(path)) - if attr_name.startswith(cmd_prefix)]: - path_prefix = f'{path}.' - cmd_name = (path_prefix[len(base)+1:] - + cmd_method_name[len(cmd_prefix):]) - method = eval(f'{path_prefix}{cmd_method_name}') - args = [] - for key, param in signature(method).parameters.items(): - arg = key.upper() - if param.default != inspect_empty: - arg = f'[{arg}]' - args += [arg] - cmds[cmd_name] = args to_log = [] - for cmd_name, args in cmds.items(): - for key, target in CMD_SHORTCUTS.items(): - if target == cmd_name: - cmd_name = key + for cmd_name, cmd_data in self._commands.items(): + args = [] + for idx, arg in enumerate(cmd_data[2]): + arg = arg.upper() + if idx >= cmd_data[1]: + arg = f'[{arg}]' + args += [arg] to_log += [f'{cmd_name} {" ".join(args)}'] for item in sorted(to_log): self._log.add(f' /{item}') -- 2.30.2