2020# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2121
2222import os
23+ import re
2324import time
2425
2526# Categories of actions:
@@ -494,10 +495,45 @@ def do(self) -> None:
494495 self .reader .invalidate_full ()
495496
496497
498+ _REPL_PASTE_HINT = "press F3 (paste mode) to strip >>> and ... prompts"
499+
500+
497501class paste_mode (Command ):
498502 def do (self ) -> None :
499- self .reader .paste_mode = not self .reader .paste_mode
500- self .reader .invalidate_prompt ()
503+ r = self .reader
504+ r .paste_mode = not r .paste_mode
505+ if r .paste_mode and getattr (r , "can_strip_repl_prompts" , False ):
506+ r .buffer [:] = _strip_repl_prompts (r .get_unicode ())
507+ r .pos = len (r .buffer )
508+ r .invalidate_buffer (0 )
509+ r .invalidate_prompt ()
510+
511+
512+ def _is_repl_session (data : str ) -> bool :
513+ return data [:3 ] in {">>>" , "..." } and data [3 :4 ] in {"" , " " , "\r " , "\n " }
514+
515+
516+ def _strip_repl_prompts (data : str ) -> str :
517+ lines : list [str ] = []
518+ statement_start = None
519+ for line in re .findall (r"[^\r\n]*(?:\r\n?|\n|$)" , data ):
520+ if _is_repl_session (line ):
521+ if line .startswith (">>>" ) or statement_start is None :
522+ statement_start = len (lines )
523+ lines .append (line [3 :].removeprefix (" " ))
524+ elif statement_start is not None :
525+ if line .rstrip ("\r \n " ) == "KeyboardInterrupt" :
526+ # Ctrl-C leaves the abandoned input on screen with its prompts.
527+ del lines [statement_start :]
528+ statement_start = None
529+ elif (previous := lines [- 1 ].rstrip ("\r \n " )).endswith ("\\ " ):
530+ # PyREPL wraps input with a backslash and no prompt on the
531+ # next screen row. Rejoin it without the display backslash.
532+ lines [- 1 ] = previous [:- 1 ] + line
533+ else :
534+ # Output (including a traceback) ends the input statement.
535+ statement_start = None
536+ return "" .join (lines )
501537
502538
503539class perform_bracketed_paste (Command ):
@@ -513,4 +549,13 @@ def do(self) -> None:
513549 l = len (data ),
514550 s = time .time () - start ,
515551 )
516- self .reader .insert (data .replace (done , "" ))
552+ data = data .replace (done , "" )
553+ if (
554+ getattr (self .reader , "paste_repl_sessions" , False )
555+ and _is_repl_session (data )
556+ ):
557+ if self .reader .paste_mode :
558+ data = _strip_repl_prompts (data )
559+ else :
560+ self .reader .error (_REPL_PASTE_HINT )
561+ self .reader .insert (data )
0 commit comments