From d34c003c3af4855811c97567eaca3335eaf0bcb8 Mon Sep 17 00:00:00 2001 From: Robin Jadoul Date: Thu, 19 Jan 2023 16:41:47 +0100 Subject: [PATCH 1/2] Improve handling of remote input and output when enabled. This makes the handling of the prompt and the display of external inputs and outputs less messy, ensuring it looks more like something that was run in the actual console (with a "Remote" prefix), while not having the local input prompt all throughout the text. --- jupyter_console/ptshell.py | 60 +++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/jupyter_console/ptshell.py b/jupyter_console/ptshell.py index 1dae25b..2bcd207 100644 --- a/jupyter_console/ptshell.py +++ b/jupyter_console/ptshell.py @@ -554,12 +554,13 @@ def _(event): if not PTK3: use_asyncio_event_loop() + self.lexer = get_pygments_lexer(lexer) self.pt_cli = PromptSession( message=(lambda: PygmentsTokens(self.get_prompt_tokens())), multiline=True, complete_style=self.pt_complete_style, editing_mode=editing_mode, - lexer=PygmentsLexer(get_pygments_lexer(lexer)), + lexer=PygmentsLexer(self.lexer), prompt_continuation=( lambda width, lineno, is_soft_wrap: PygmentsTokens( self.get_continuation_tokens(width) @@ -868,13 +869,15 @@ def handle_iopub(self, msg_id=''): if self._pending_clearoutput: print("\r", end="") self._pending_clearoutput = False - print(sub_msg["content"]["text"], end="") + print_formatted_text(sub_msg["content"]["text"], end="") sys.stdout.flush() elif sub_msg["content"]["name"] == "stderr": if self._pending_clearoutput: print("\r", file=sys.stderr, end="") self._pending_clearoutput = False - print(sub_msg["content"]["text"], file=sys.stderr, end="") + print_formatted_text( + sub_msg["content"]["text"], file=sys.stderr, end="" + ) sys.stderr.flush() elif msg_type == 'execute_result': @@ -883,7 +886,11 @@ def handle_iopub(self, msg_id=''): self._pending_clearoutput = False self.execution_count = int(sub_msg["content"]["execution_count"]) if not self.from_here(sub_msg): - sys.stdout.write(self.other_output_prefix) + print_formatted_text( + PygmentsTokens(self.get_remote_prompt_tokens()), + style=self.pt_cli.app.style, + end="", + ) format_dict = sub_msg["content"]["data"] self.handle_rich_data(format_dict) @@ -898,39 +905,50 @@ def handle_iopub(self, msg_id=''): text_repr = format_dict['text/plain'] if '\n' in text_repr: # For multi-line results, start a new line after prompt - print() - print(text_repr) + print_formatted_text() + print_formatted_text(text_repr) - # Remote: add new prompt if not self.from_here(sub_msg): - sys.stdout.write('\n') + # Because this occurs after the execute_input, we need to make sure our next prompt looks correct again + self.execution_count += 1 sys.stdout.flush() - self.print_remote_prompt() elif msg_type == 'display_data': data = sub_msg["content"]["data"] handled = self.handle_rich_data(data) if not handled: if not self.from_here(sub_msg): - sys.stdout.write(self.other_output_prefix) + print_formatted_text( + PygmentsTokens(self.get_other_prompt_tokens()), + style=self.pt_cli.app.style, + end="", + ) # if it was an image, we handled it by now - if 'text/plain' in data: - print(data['text/plain']) + if "text/plain" in data: + print_formatted_text(data["text/plain"]) # If execute input: print it elif msg_type == 'execute_input': content = sub_msg['content'] ec = content.get('execution_count', self.execution_count - 1) - # New line - sys.stdout.write('\n') - sys.stdout.flush() - - # With `Remote In [3]: ` - self.print_remote_prompt(ec=ec) - # And the code - sys.stdout.write(content['code'] + '\n') + first_prompt = ( + self.get_remote_prompt_tokens() + self.get_prompt_tokens(ec=ec) + ) + remote_prompt_len = len("".join(t for _, t in first_prompt)) + continuation = self.get_continuation_tokens(remote_prompt_len) + + tokens = first_prompt[:] + lexer = self.lexer() + for _, t, v in lexer.get_tokens_unprocessed(content["code"]): + tokens.append((t, v)) + if v == "\n": + tokens += continuation + print_formatted_text( + PygmentsTokens(tokens), style=self.pt_cli.app.style + ) + sys.stdout.flush() elif msg_type == 'clear_output': if sub_msg["content"]["wait"]: @@ -941,6 +959,8 @@ def handle_iopub(self, msg_id=''): elif msg_type == 'error': for frame in sub_msg["content"]["traceback"]: print(frame, file=sys.stderr) + # Trigger prompt redraw + print_formatted_text("", end="") _imagemime = { 'image/png': 'png', From 3860fc81cb45d65c4dfbf0a761f9da590d423736 Mon Sep 17 00:00:00 2001 From: Robin Jadoul Date: Thu, 19 Jan 2023 23:09:07 +0100 Subject: [PATCH 2/2] Match extra blank lines better for external output --- jupyter_console/ptshell.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jupyter_console/ptshell.py b/jupyter_console/ptshell.py index 2bcd207..bf5674b 100644 --- a/jupyter_console/ptshell.py +++ b/jupyter_console/ptshell.py @@ -870,6 +870,8 @@ def handle_iopub(self, msg_id=''): print("\r", end="") self._pending_clearoutput = False print_formatted_text(sub_msg["content"]["text"], end="") + if not self.from_here(sub_msg): + print_formatted_text() sys.stdout.flush() elif sub_msg["content"]["name"] == "stderr": if self._pending_clearoutput: @@ -911,6 +913,7 @@ def handle_iopub(self, msg_id=''): if not self.from_here(sub_msg): # Because this occurs after the execute_input, we need to make sure our next prompt looks correct again self.execution_count += 1 + print_formatted_text() sys.stdout.flush() elif msg_type == 'display_data':