]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-111201: Allow bracketed paste to work (GH-118700)
authorPablo Galindo Salgado <Pablogsal@gmail.com>
Tue, 7 May 2024 12:54:56 +0000 (13:54 +0100)
committerGitHub <noreply@github.com>
Tue, 7 May 2024 12:54:56 +0000 (12:54 +0000)
Lib/_pyrepl/commands.py
Lib/_pyrepl/reader.py
Lib/_pyrepl/unix_console.py
Lib/test/test_pyrepl.py

index 60ceb30d2cd77dc80e0d62d5b75e54601f1d3ae6..bb6bebace30ec87d4d8d8e089eda53b11b1f30f6 100644 (file)
@@ -462,3 +462,13 @@ class paste_mode(Command):
     def do(self) -> None:
         self.reader.paste_mode = not self.reader.paste_mode
         self.reader.dirty = True
+
+
+class enable_bracketed_paste(Command):
+    def do(self) -> None:
+        self.reader.paste_mode = True
+
+class disable_bracketed_paste(Command):
+    def do(self) -> None:
+        self.reader.paste_mode = False
+        self.reader.insert("\n")
index 071dfe54aba8fb3ddb461a6cf5336bcf75b03b29..e36f65c176e81f2f251fe7edb546df5aa9c426e4 100644 (file)
@@ -127,6 +127,8 @@ default_keymap: tuple[tuple[KeySpec, CommandName], ...] = tuple(
         (r"\M-9", "digit-arg"),
         # (r'\M-\n', 'insert-nl'),
         ("\\\\", "self-insert"),
+        (r"\x1b[200~", "enable_bracketed_paste"),
+        (r"\x1b[201~", "disable_bracketed_paste"),
     ]
     + [(c, "self-insert") for c in map(chr, range(32, 127)) if c != "\\"]
     + [(c, "self-insert") for c in map(chr, range(128, 256)) if c.isalpha()]
index c22b1d5b5bc2904884f91345cd9a4c39cd9be6c3..605318c82ae2eae8df2e8f397c5d3cc128f95f0c 100644 (file)
@@ -336,10 +336,13 @@ class UnixConsole(Console):
         except ValueError:
             pass
 
+        self.__enable_bracketed_paste()
+
     def restore(self):
         """
         Restore the console to the default state
         """
+        self.__disable_bracketed_paste()
         self.__maybe_write_code(self._rmkx)
         self.flushoutput()
         tcsetattr(self.input_fd, termios.TCSADRAIN, self.__svtermstate)
@@ -525,6 +528,12 @@ class UnixConsole(Console):
         self.__posxy = 0, 0
         self.screen = []
 
+    def __enable_bracketed_paste(self) -> None:
+        os.write(self.output_fd, b"\x1b[?2004h")
+
+    def __disable_bracketed_paste(self) -> None:
+        os.write(self.output_fd, b"\x1b[?2004l")
+
     def __setup_movement(self):
         """
         Set up the movement functions based on the terminal capabilities.
index 3df76e02b231dc21cef71b52dead3fbcd719e257..b7ae91b919527a5b836c7bd895125e0e3d9f47f7 100644 (file)
@@ -817,6 +817,46 @@ class TestPasteEvent(TestCase):
         output = multiline_input(reader)
         self.assertEqual(output, output_code)
 
+    def test_bracketed_paste(self):
+        """Test that bracketed paste using \x1b[200~ and \x1b[201~ works."""
+        # fmt: off
+        input_code = (
+            'def a():\n'
+            '  for x in range(10):\n'
+            '\n'
+            '    if x%2:\n'
+            '      print(x)\n'
+            '\n'
+            '    else:\n'
+            '      pass\n'
+        )
+        # fmt: on
+
+        output_code = (
+            'def a():\n'
+            '  for x in range(10):\n'
+            '\n'
+            '    if x%2:\n'
+            '      print(x)\n'
+            '\n'
+            '    else:\n'
+            '      pass\n'
+            '\n'
+        )
+
+        paste_start = "\x1b[200~"
+        paste_end = "\x1b[201~"
+
+        events = itertools.chain(
+            code_to_events(paste_start),
+            code_to_events(input_code),
+            code_to_events(paste_end),
+            code_to_events("\n"),
+        )
+        reader = self.prepare_reader(events)
+        output = multiline_input(reader)
+        self.assertEqual(output, output_code)
+
 
 class TestReader(TestCase):
     def assert_screen_equals(self, reader, expected):