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.'
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:
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 = ''
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)
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}')