summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Galindo Salgado <Pablogsal@gmail.com>2024-05-07 12:54:56 (GMT)
committerGitHub <noreply@github.com>2024-05-07 12:54:56 (GMT)
commit7d90b8aadbd6993ee50a73b7536f769334718423 (patch)
treec52e6f2f5f03a3e2a6b4f57a5b2a0e321c72000a
parentad3d877a126bc892d1c598cf1357a2c39fd466c7 (diff)
downloadcpython-7d90b8aadbd6993ee50a73b7536f769334718423.zip
cpython-7d90b8aadbd6993ee50a73b7536f769334718423.tar.gz
cpython-7d90b8aadbd6993ee50a73b7536f769334718423.tar.bz2
gh-111201: Allow bracketed paste to work (GH-118700)
-rw-r--r--Lib/_pyrepl/commands.py10
-rw-r--r--Lib/_pyrepl/reader.py2
-rw-r--r--Lib/_pyrepl/unix_console.py9
-rw-r--r--Lib/test/test_pyrepl.py40
4 files changed, 61 insertions, 0 deletions
diff --git a/Lib/_pyrepl/commands.py b/Lib/_pyrepl/commands.py
index 60ceb30..bb6bebac 100644
--- a/Lib/_pyrepl/commands.py
+++ b/Lib/_pyrepl/commands.py
@@ -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")
diff --git a/Lib/_pyrepl/reader.py b/Lib/_pyrepl/reader.py
index 071dfe5..e36f65c 100644
--- a/Lib/_pyrepl/reader.py
+++ b/Lib/_pyrepl/reader.py
@@ -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()]
diff --git a/Lib/_pyrepl/unix_console.py b/Lib/_pyrepl/unix_console.py
index c22b1d5..605318c 100644
--- a/Lib/_pyrepl/unix_console.py
+++ b/Lib/_pyrepl/unix_console.py
@@ -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.
diff --git a/Lib/test/test_pyrepl.py b/Lib/test/test_pyrepl.py
index 3df76e0..b7ae91b 100644
--- a/Lib/test/test_pyrepl.py
+++ b/Lib/test/test_pyrepl.py
@@ -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):