Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add code to ignore OSC commands #2326

Merged
merged 3 commits into from Aug 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 24 additions & 0 deletions mu/interface/panes.py
Expand Up @@ -182,6 +182,9 @@ def __init__(self, connection, theme="day", parent=None):
self.vt100_regex = re.compile(
r"\x1B\[(?P<count>[\d]*)(;?[\d]*)*(?P<action>[A-Za-z])"
)
self.osc_regex = re.compile(
r"\x1B\](?P<command>[\d]*);(?P<string>[^\x1B]*)\x1B\\"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this regex not start with r"\x1B\[?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No wait, I was looking at the wrong command, nevermind, this is the one to match: https://terminalguide.namepad.de/seq/osc-0/

)

def insertFromMimeData(self, source):
"""
Expand Down Expand Up @@ -431,6 +434,27 @@ def process_tty_data(self, data):
# incomplete input
self.unprocessed_input = data[i:]
break
elif len(data) > i + 1 and data[i + 1] == "]":
# OSC cursor detected: <Esc>]
match = self.osc_regex.search(data[i:])
if match:
# move to (almost) after control seq
# (will ++ at end of loop)
i += match.end() - 1
command = match.group("command")
string = match.group("string")
if command == "0": # Set window title and icon name
logger.warning("dropped title {}".format(string))
else:
# Unknown action, log warning and ignore
command = match.group(0).replace("\x1B", "<Esc>")
msg = "Received unsupported VT100 command: {}"
logger.warning(msg.format(command))
else:
# Cursor detected, but no match, must be
# incomplete input
self.unprocessed_input = data[i:]
break
elif len(data) == i + 1:
# Escape received as end of transmission. Perhaps
# the transmission is incomplete, wait until next
Expand Down
67 changes: 67 additions & 0 deletions tests/interface/test_panes.py
Expand Up @@ -898,6 +898,73 @@ def test_MicroPythonREPLPane_process_tty_data_unsupported_vt100_command():
assert rp.device_cursor_position == 5


def test_MicroPythonREPLPane_process_tty_data_partial_osc_reception():
"""
Ensure that when partially received multibyte OSC commands are
received they are handled properly
"""
mock_repl_connection = mock.MagicMock()
rp = mu.interface.panes.MicroPythonREPLPane(mock_repl_connection)
rp.setPlainText("Hello world!")
# Move cursor to after first 'o'
rp.device_cursor_position = 5
rp.set_qtcursor_to_devicecursor()
# Receive: \x1B
bs = b"\x1B]"
rp.process_tty_data(bs)
assert rp.unprocessed_input == "\x1B]"
assert rp.toPlainText() == "Hello world!"
assert rp.textCursor().position() == 5
assert rp.device_cursor_position == 5
# Receive the rest of the title
bs = b";hello\x1B\\"
rp.process_tty_data(bs)
assert rp.unprocessed_input == ""
assert rp.toPlainText() == "Hello world!"
assert rp.textCursor().position() == 5
assert rp.device_cursor_position == 5


def test_MicroPythonREPLPane_process_tty_data_osc_title_command():
"""
Ensure nothing is done, when receiving an OSC title command
"""
mock_repl_connection = mock.MagicMock()
rp = mu.interface.panes.MicroPythonREPLPane(mock_repl_connection)
rp.setPlainText("Hello world!")
# Move cursor to after first 'o'
rp.device_cursor_position = 5
rp.set_qtcursor_to_devicecursor()
# Receive: \x1B[4X - unknown command X
bs = b"\x1B]0;hello\x1B\\"
rp.process_tty_data(bs)
# Do nothing
assert rp.unprocessed_input == b""
assert rp.toPlainText() == "Hello world!"
assert rp.textCursor().position() == 5
assert rp.device_cursor_position == 5


def test_MicroPythonREPLPane_process_tty_data_unsupported_osc_command():
"""
Ensure nothing is done, when receiving an unsupported OSC command
"""
mock_repl_connection = mock.MagicMock()
rp = mu.interface.panes.MicroPythonREPLPane(mock_repl_connection)
rp.setPlainText("Hello world!")
# Move cursor to after first 'o'
rp.device_cursor_position = 5
rp.set_qtcursor_to_devicecursor()
# Receive: \x1B[4X - unknown command X
bs = b"\x1B]100;hello\x1B\\"
rp.process_tty_data(bs)
# Do nothing
assert rp.unprocessed_input == b""
assert rp.toPlainText() == "Hello world!"
assert rp.textCursor().position() == 5
assert rp.device_cursor_position == 5


def test_MicroPythonREPLPane_clear():
"""
Ensure setText is called with an empty string.
Expand Down