From: Christian Heller Date: Mon, 9 Jun 2025 23:42:36 +0000 (+0200) Subject: Allow prompt cursor to move horizontally. X-Git-Url: https://plomlompom.com/repos/%7B%7Bprefix%7D%7D/%22https:/validator.w3.org/foo.html?a=commitdiff_plain;p=ircplom Allow prompt cursor to move horizontally. --- diff --git a/ircplom.py b/ircplom.py index 67d8ae2..0e1381d 100755 --- a/ircplom.py +++ b/ircplom.py @@ -24,6 +24,8 @@ CONN_RECV_BUFSIZE = 1024 KEYBINDINGS = { 'KEY_BACKSPACE': ('window.prompt.backspace',), 'KEY_ENTER': ('prompt_enter',), + 'KEY_LEFT': ('window.prompt.move_cursor', 'left'), + 'KEY_RIGHT': ('window.prompt.move_cursor', 'right'), 'KEY_UP': ('window.prompt.scroll', 'up'), 'KEY_DOWN': ('window.prompt.scroll', 'down'), 'KEY_PGUP': ('window.log.scroll', 'up'), @@ -40,6 +42,10 @@ B64_PREFIX = 'b64:' OSC52_PREFIX = ']52;c;' PASTE_DELIMITER = '\007' +PROMPT_TEMPLATE = '> ' +PROMPT_ELL_IN = '<…' +PROMPT_ELL_OUT = '…>' + IRCSPEC_LINE_SEPARATOR = b'\r\n' IRCSPEC_TAG_ESCAPES = ((r'\:', ';'), (r'\s', ' '), @@ -537,32 +543,43 @@ class PromptWidget(ScrollableWidget): def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) + self._clear() self._input_buffer = '' - self._history_idx = 0 def set_geometry(self, measurements: YX) -> None: self._y, self._width = measurements def append(self, to_append: str) -> None: - self._input_buffer += to_append + self._cursor_x += len(to_append) + self._input_buffer = (self._input_buffer[:self._cursor_x - 2] + + to_append + + self._input_buffer[self._cursor_x - 2:]) self._history_idx = 0 self.draw() def draw(self) -> None: - cursor = '_' - prompt_template = '> ' - prompt = f'{prompt_template}' + prompt = PROMPT_TEMPLATE[:] + content = self._input_buffer[:] + if self._cursor_x > len(self._input_buffer): + content += ' ' + half_width = self._width // 2 + to_write = f'{prompt}{content}' offset = 0 - while True: - to_write = f'{prompt}{self._input_buffer[offset:]}{cursor}' - len_too_much = len(to_write) - self._width - if len_too_much <= 0: - break - offset += len_too_much - prompt = f'<{offset}|{prompt_template}…' - self._write(to_write[:-1], self._y, padding=False) - self._write(to_write[-1], attribute='reverse', padding=False) - self._write() + if len(to_write) > self._width and self._cursor_x > half_width: + prompt = f'{PROMPT_TEMPLATE}{PROMPT_ELL_IN}' + if self._cursor_x > len(content) - half_width: + offset = len(content) - self._width + len(prompt) + else: + offset = self._cursor_x - half_width + len(prompt) // 2 + cursor_x_to_write = len(prompt) - 1 + self._cursor_x - offset + to_write = f'{prompt}{content[offset:]}' + if len(to_write) > self._width: + to_write = (to_write[:self._width - len(PROMPT_ELL_OUT)] + + PROMPT_ELL_OUT) + self._write(to_write[:cursor_x_to_write], self._y, padding=False) + self._write(to_write[cursor_x_to_write], attribute='reverse', + padding=False) + self._write(to_write[cursor_x_to_write + 1:]) def _scroll(self, up: bool = True) -> None: if up and -(self._history_idx) < len(self._history): @@ -579,18 +596,30 @@ class PromptWidget(ScrollableWidget): else: return self._input_buffer = self._history[self._history_idx][:] - self.draw() def cmd__backspace(self) -> None: 'Truncate current content by one character, if possible.' - self._input_buffer = self._input_buffer[:-1] - self._history_idx = 0 + if self._cursor_x > 1: + self._cursor_x -= 1 + self._input_buffer = (self._input_buffer[:self._cursor_x - 1] + + self._input_buffer[self._cursor_x:]) + self._history_idx = 0 + self.draw() + + def cmd__move_cursor(self, direction: str) -> None: + 'Move cursor one space into direction ("left" or "right") if possible.' + if direction == 'left' and self._cursor_x > 1: + self._cursor_x -= 1 + elif direction == 'right'\ + and self._cursor_x <= len(self._input_buffer): + self._cursor_x += 1 + else: + return self.draw() def _clear(self) -> None: - 'Empty current content.' self._input_buffer = '' - self.draw() + self._cursor_x = len(self._input_buffer) + 1 def enter(self) -> str: 'Return current content while also clearing and then redrawing.' @@ -903,7 +932,7 @@ class KeyboardLoop(Loop): self.broadcast(EventType.PROMPT_ADD, yielded) else: self.broadcast(EventType.ALERT, - 'unknown keyboard input: {yielded}') + f'unknown keyboard input: {yielded}') def run() -> None: