rich icon indicating copy to clipboard operation
rich copied to clipboard

[BUG] Backspacing deletes the prompt in `console.input` using `readline`

Open JoseKilo opened this issue 3 years ago • 8 comments

Describe the bug

Very similar to a previous issue https://github.com/Textualize/rich/issues/299 . Not exactly a regression, since that one depended on having a custom theme. But the steps to reproduce are the same:

  • Enter some text at the prompt
  • Delete it with backspace
  • The prompt gets deleted too

It also happens by using Ctrl-u to delete the whole line.

It doesn't happen if readline isn't used.

I'm using version 12.4.4, but I've tried some older versions and I think it started happening between v11.2.0 and v12.0.0.

import readline

from rich.console import Console


def main():
    console = Console()
    command = console.input('> ')
    print('C1', command)


if __name__ == '__main__':
    main()

Platform

Click to expand
> python -m rich.diagnose
╭───────────────────────── <class 'rich.console.Console'> ─────────────────────────╮
│ A high level console interface.                                                  │
│                                                                                  │
│ ╭──────────────────────────────────────────────────────────────────────────────╮ │
│ │ <console width=191 ColorSystem.TRUECOLOR>                                    │ │
│ ╰──────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                  │
│     color_system = 'truecolor'                                                   │
│         encoding = 'utf-8'                                                       │
│             file = <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'> │
│           height = 53                                                            │
│    is_alt_screen = False                                                         │
│ is_dumb_terminal = False                                                         │
│   is_interactive = True                                                          │
│       is_jupyter = False                                                         │
│      is_terminal = True                                                          │
│   legacy_windows = False                                                         │
│         no_color = False                                                         │
│          options = ConsoleOptions(                                               │
│                        size=ConsoleDimensions(width=191, height=53),             │
│                        legacy_windows=False,                                     │
│                        min_width=1,                                              │
│                        max_width=191,                                            │
│                        is_terminal=True,                                         │
│                        encoding='utf-8',                                         │
│                        max_height=53,                                            │
│                        justify=None,                                             │
│                        overflow=None,                                            │
│                        no_wrap=False,                                            │
│                        highlight=None,                                           │
│                        markup=None,                                              │
│                        height=None                                               │
│                    )                                                             │
│            quiet = False                                                         │
│           record = False                                                         │
│         safe_box = True                                                          │
│             size = ConsoleDimensions(width=191, height=53)                       │
│        soft_wrap = False                                                         │
│           stderr = False                                                         │
│            style = None                                                          │
│         tab_size = 8                                                             │
│            width = 191                                                           │
╰──────────────────────────────────────────────────────────────────────────────────╯
╭─── <class 'rich._windows.WindowsConsoleFeatures'> ────╮
│ Windows features available.                           │
│                                                       │
│ ╭───────────────────────────────────────────────────╮ │
│ │ WindowsConsoleFeatures(vt=False, truecolor=False) │ │
│ ╰───────────────────────────────────────────────────╯ │
│                                                       │
│ truecolor = False                                     │
│        vt = False                                     │
╰───────────────────────────────────────────────────────╯
╭─────────────────────────────────────────────────────────────────────────────────── Environment Variables ───────────────────────────────────────────────────────────────────────────────────╮
│ {                                                                                                                                                                                           │
│     'TERM': 'xterm',                                                                                                                                                                        │
│     'COLORTERM': 'truecolor',                                                                                                                                                               │
│     'CLICOLOR': None,                                                                                                                                                                       │
│     'NO_COLOR': None,                                                                                                                                                                       │
│     'TERM_PROGRAM': None,                                                                                                                                                                   │
│     'COLUMNS': None,                                                                                                                                                                        │
│     'LINES': None,                                                                                                                                                                          │
│     'JPY_PARENT_PID': None,                                                                                                                                                                 │
│     'VSCODE_VERBOSE_LOGGING': None                                                                                                                                                          │
│ }                                                                                                                                                                                           │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
platform="Linux"
> pip freeze | grep rich
rich==12.4.4

JoseKilo avatar May 24 '22 19:05 JoseKilo

I did some digging and reverting to code prior to 568b951 seems to correct the issue. I'm guessing that the readline library needs to know how long the prompt is in order to properly maintain this, and to do that, you need to pass in the prompt to input(), and that's why the old code was rendering the prompt within a capture() context. Reverting to that old approach seems like the solution. (Although I'm not sure why it was removed --and this fix would possibly make #1757 worse....)

tewhalen avatar Sep 17 '22 16:09 tewhalen

hi. i'm having the same problem here. is there a fix?

divers0 avatar Oct 21 '22 12:10 divers0

The same issue in Click: https://github.com/pallets/click/issues/665 was resolved with a bit dirty hack: https://github.com/pallets/click/pull/1836

    def prompt_func(text: str) -> str:
        f = hidden_prompt_func if hide_input else visible_prompt_func
        try:
            # Write the prompt separately so that we get nice
            # coloring through colorama on Windows
            echo(text.rstrip(" "), nl=False, err=err)
            # Echo a space to stdout to work around an issue where
            # readline causes backspace to clear the whole line.
            return f(" ")
        except (KeyboardInterrupt, EOFError):
            # getpass doesn't print a newline if the user aborts input with ^C.
            # Allegedly this behavior is inherited from getpass(3).
            # A doc bug has been filed at https://bugs.python.org/issue24711
            if hide_input:
                echo(None, err=err)
            raise Abort() from None

which is definitely not perfect, because it implies other issues like https://github.com/pallets/click/issues/2018.

jaklan avatar Dec 27 '22 02:12 jaklan

One solution which works for me is to modify the result = input() line in console.py

https://github.com/Textualize/rich/blob/6d30ad0f30028210124c149811cbbe2b183711f9/rich/console.py#L2123

to result = input("\u00ad")

I tried a few other zero-width characters but only this one seems to work without adding any visible space.

mannjani avatar Apr 30 '23 08:04 mannjani

Modifying rich/console.py as per https://github.com/Textualize/rich/issues/2293#issuecomment-1528969444 results in the soft-hyphen character being rendered as a literal hyphen for me. Too bad there's no invisible solution.

synrg avatar Jun 02 '23 16:06 synrg

There is no break space "\u00A0"

def prompt():
    console.print("\n[bold]Question:[/]", end="")
    return input("\u00A0")

rudolfowski avatar Jan 10 '24 22:01 rudolfowski

No break space emits a space. I think @mannjani was looking for a character with zero width so the print statement alone provides all of the visible text in the prompt. For my use case, that doesn't matter - an ordinary space is fine.

synrg avatar Jan 12 '24 12:01 synrg

Incidentally, the workaround does not work on all terminal types. It is fine in Windows Terminal, but not OK when ssh'd to my linux server, nor directly logged in via console, nor on X (neither in xterm nor in gnome-terminal). So far, I have found no workaround that works on linux (Debian), but only Windows.

synrg avatar Jan 12 '24 12:01 synrg