gh-145375: Fix bracketed paste in PyREPL isearch mode#145398
Conversation
| data += ev.data | ||
| paste_content = data.replace(done, "") | ||
| r.isearch_term += paste_content | ||
| r.invalidate_prompt() |
There was a problem hiding this comment.
I think this should be r.invalidate_prompt(). dirty is not used here, so the prompt can keep showing the previous search term.
There was a problem hiding this comment.
Yes. I've updated in adapt code for #146584 (structured rendered screens).
| data += ev.data | ||
| paste_content = data.replace(done, "") | ||
| r.isearch_term += paste_content | ||
| r.invalidate_prompt() |
There was a problem hiding this comment.
r.dirty is not read anywhere in _pyrepl, so this is a no-op and the search prompt won't refresh after the paste. We probably want r.invalidate_prompt() here, like isearch_add_character does.
|
|
||
| isearch_keymap: tuple[tuple[KeySpec, CommandName], ...] = tuple( | ||
| [("\\%03o" % c, "isearch-end") for c in range(256) if chr(c) != "\\"] | ||
| [("\\%03o" % c, "isearch-end") for c in range(256) if chr(c) not in ("\\", "\x1b")] |
There was a problem hiding this comment.
Unless I am missing something, this breaks plain Esc: it now waits for the rest of a bracketed-paste sequence and does not end isearch. Can we keep the old Esc path?
There was a problem hiding this comment.
Thanks for the review! You're right that removing \x1b from isearch-end makes it look like bare Esc would stop working.
However, I just noticed that even without this change, a single ESC doesn't immediately end isearch, because EventQueue treats \x1b as a prefix for all escape sequences, it does not send the Esc event but instead waits for the next byte. That's a pre‑existing behavior (press Esc twice to exit isearch).
This should be another issue: Because a single Esc byte can be the start of a multi-byte escape sequence, it's ambiguous whether it's a standalone key press. I wonder if we should stop using Esc to quit isearch. Or, we could add a timeout mechanism (as other TUI apps like ncurses, Vim, Tmux) to tell them apart?
There was a problem hiding this comment.
I found a cleaner way that avoids touching isearch-end.
Instead of binding \x1b[200~ in the keymap, we can move detection to the event-queue layer, translating \x1b[200~ into a named event. If \x1b[200~ is consumed at the event-queue level, the isearch keymap never sees the leading \x1b during a paste. So \x1b stays bound to isearch-end, Esc is not impacted, although the Esc delay is already pre-existing, which is a separate issue.
If you think this named-event approach is OK: This change goes a bit beyond the original scope of this PR. Since \x1b[200~ is now translated at the event-queue level, the normal-mode keymap in reader.py never sees the raw sequence and needs to be updated to also bind the named event. If you'd prefer, I'm happy to split this into two separate PRs:
- isearch fix only (removing
\x1bfromisearch-end, bind raw\x1b[200~toisearch-bracketed-paste) - named-event refactor (restore old
Escpath toisearch-end, let both normal and isearch keymap bind the new event).
|
|
||
| isearch_keymap: tuple[tuple[KeySpec, CommandName], ...] = tuple( | ||
| [("\\%03o" % c, "isearch-end") for c in range(256) if chr(c) != "\\"] | ||
| [("\\%03o" % c, "isearch-end") for c in range(256) if chr(c) not in ("\\", "\x1b")] |
There was a problem hiding this comment.
Removing \x1b from isearch-end means a bare ESC no longer ends the search immediately, it now waits for the next key. Is that intentional? Pressing Escape to leave isearch won't take effect until another key arrives.
| r.pop_input_trans() | ||
| r.invalidate_prompt() | ||
|
|
||
| class isearch_bracketed_paste(commands.Command): |
There was a problem hiding this comment.
Nit: two blank lines between top-level classes here.
The event queue now turns the terminal's \x1b[200~ bracketed-paste start sequence into a synthetic "bracketed paste" named event, so readers can bind \<bracketed paste> instead of matching the raw escape sequence directly.
Bind \<bracketed paste> to isearch-bracketed-paste and keep \x1b bound to isearch-end. Because the paste start marker is now consumed by the event queue, a lone Esc still ends the search immediately (no disambiguation delay), while a paste is dispatched to the command.
Update the normal (non-isearch) keymap to use \<bracketed paste> so it stays consistent with the event queue translation.
- test_eventqueue: assert \x1b[200~ is translated to "bracketed paste". - test_pyrepl: feed the "bracketed paste" event and cover isearch mode.
This excludes
\x1bfrom isearch-end keymap and add newisearch_bracketed_pastefunction to handle bracketed paste in isearch mode.