REALNAME = 'debug debugger'
TIMEOUT_CONNECT = 5
TIMEOUT_LOOP = 0.1
-INPUT_PROMPT = ':'
+INPUT_PROMPT = '> '
KEYBINDINGS = {
'KEY_BACKSPACE': ('prompt_backspace',),
return to_return + [self._wrapped[-1][1]]
+class TuiPrompt:
+ 'Keyboard-controlled command input field.'
+ start_y: int
+
+ def __init__(self, term: Terminal) -> None:
+ self._term = term
+ self._buffer = ''
+
+ def append(self, char: str) -> None:
+ 'Append char to current content.'
+ self._buffer += char
+ self.draw()
+
+ def backspace(self) -> None:
+ 'Truncate current content by one character, if possible.'
+ self._buffer = self._buffer[:-1]
+ self.draw()
+
+ def clear(self) -> None:
+ 'Empty current content.'
+ self._buffer = ''
+ self.draw()
+
+ def draw(self) -> None:
+ 'Print into screen..'
+ self._term.write_yx(YX(self.start_y, len(INPUT_PROMPT)),
+ f'{self._buffer}_')
+
+ def enter(self) -> str:
+ 'Return current content while also clearing and then redrawing.'
+ to_return = self._buffer[:]
+ self.clear()
+ self.draw()
+ return to_return
+
+
class TuiLoop(Loop):
'Loop for drawing/updating TUI.'
def __init__(self, term: Terminal, *args, **kwargs) -> None:
self._term = term
- self._prompt = ''
+ self._prompt = TuiPrompt(self._term)
self._logs = [LogBuffer(self._term.wrap) for i in range(2)]
self._log_selected = 0
self._upscroll = 0
elif event.type_ == 'KEYBINDING':
getattr(self, f'_cmd__{event.payload[0]}')(*event.payload[1:])
elif event.type_ == 'INPUT_CHAR':
- self._prompt += event.payload
- self._draw_prompt()
+ self._prompt.append(event.payload)
elif event.type_ == 'SIGWINCH':
self._calc_and_draw_all()
# elif event.type_ == 'DEBUG':
return self._logs[self._log_selected]
def _cmd__prompt_backspace(self) -> None:
- self._prompt = self._prompt[:-1]
- self._draw_prompt()
+ self._prompt.backspace()
def _cmd__prompt_enter(self) -> None:
- if self._prompt:
- alert: Optional[str] = None
- if len(self._prompt) > 1 and self._prompt[0] == '/':
- toks = self._prompt[1:].split(maxsplit=1)
- method_name = f'_cmd__{toks[0]}'
- alert = f'{toks[0]} unknown'
- if hasattr(self, method_name)\
- and method_name != stack()[0].function:
- method = getattr(self, method_name)
- params = signature(method).parameters
- n_args_max = len(params)
- n_args_min = len([p for p in params.values()
- if p.default == inspect_empty])
- alert = f'{toks[0]} needs {n_args_min} - {n_args_max} args'
- if len(toks) == 1 and not n_args_min:
- alert = method()
- elif len(toks) > 1 and params\
- and n_args_min <= len(toks[1].split()):
- args = []
- while len(toks) > 1 and n_args_max:
- toks = toks[1].split(maxsplit=1)
- args += [toks[0]]
- n_args_max -= 1
- alert = method(*args)
- else:
- alert = 'not prefixed by /'
- if alert:
- self.broadcast('ALERT', f'invalid prompt command: {alert}')
- self._prompt = ''
- self._draw_prompt()
+ to_parse = self._prompt.enter()
+ if not to_parse:
+ return
+ alert: Optional[str] = None
+ if to_parse[0:1] == '/':
+ toks = to_parse[1:].split(maxsplit=1)
+ method_name = f'_cmd__{toks[0]}'
+ alert = f'{toks[0]} unknown'
+ if hasattr(self, method_name)\
+ and method_name != stack()[0].function:
+ method = getattr(self, method_name)
+ params = signature(method).parameters
+ n_args_max = len(params)
+ n_args_min = len([p for p in params.values()
+ if p.default == inspect_empty])
+ alert = f'{toks[0]} needs {n_args_min} - {n_args_max} args'
+ if len(toks) == 1 and not n_args_min:
+ alert = method()
+ elif len(toks) > 1 and params\
+ and n_args_min <= len(toks[1].split()):
+ args = []
+ while len(toks) > 1 and n_args_max:
+ toks = toks[1].split(maxsplit=1)
+ args += [toks[0]]
+ n_args_max -= 1
+ alert = method(*args)
+ else:
+ alert = 'not prefixed by /'
+ if alert:
+ self.broadcast('ALERT', f'invalid prompt command: {alert}')
def _cmd__scroll(self, direction: str) -> None:
self._log.scroll(up=direction == 'up')
def _calc_and_draw_all(self) -> None:
self._term.clear()
self._term.calc_geometry()
- self._y_prompt = self._term.size.y - 1
+ self._prompt.start_y = self._term.size.y - 1
self._y_separator = self._term.size.y - 2
for log in self._logs:
log.apply_geometry(YX(self._y_separator, self._term.size.x))
self._draw_frame()
self._draw_log()
- self._draw_prompt()
+ self._prompt.draw()
def _draw_frame(self) -> None:
self._term.write_yx(YX(self._y_separator, 0), '=' * self._term.size.x)
- self._term.write_yx(YX(self._y_prompt, 0), INPUT_PROMPT)
+ self._term.write_yx(YX(self._prompt.start_y, 0), INPUT_PROMPT)
def _draw_log(self) -> None:
for i, line in enumerate(self._log.wrapped):
self._term.write_yx(YX(i, 0), line)
- def _draw_prompt(self) -> None:
- self._term.write_yx(YX(self._y_prompt, len(INPUT_PROMPT)),
- f'{self._prompt}_')
-
class SocketRecvLoop(Loop):
'Loop receiving and translating socket messages towards main loop.'