Skip to content

Commit

Permalink
gh-111201: Allow bracketed paste to work (GH-118700)
Browse files Browse the repository at this point in the history
  • Loading branch information
pablogsal authored May 7, 2024
1 parent ad3d877 commit 7d90b8a
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 0 deletions.
10 changes: 10 additions & 0 deletions Lib/_pyrepl/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
2 changes: 2 additions & 0 deletions Lib/_pyrepl/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ def make_default_commands() -> dict[CommandName, type[Command]]:
(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()]
Expand Down
9 changes: 9 additions & 0 deletions Lib/_pyrepl/unix_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,10 +336,13 @@ def prepare(self):
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)
Expand Down Expand Up @@ -525,6 +528,12 @@ def clear(self):
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.
Expand Down
40 changes: 40 additions & 0 deletions Lib/test/test_pyrepl.py
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,46 @@ def test_paste_not_in_paste_mode(self):
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):
Expand Down

0 comments on commit 7d90b8a

Please sign in to comment.