Fix issue in returning cursor after print_above()

This commit is contained in:
David A. van Leeuwen 2026-04-13 21:18:56 +02:00
parent 2bb2dfff02
commit bf69a4fd35

View file

@ -12,8 +12,10 @@ import requests
import serial.tools.list_ports import serial.tools.list_ports
from pathlib import Path from pathlib import Path
import traceback import traceback
from prompt_toolkit.application.current import get_app_or_none
from prompt_toolkit.shortcuts import PromptSession from prompt_toolkit.shortcuts import PromptSession
from prompt_toolkit.shortcuts import CompleteStyle from prompt_toolkit.shortcuts import CompleteStyle
from prompt_toolkit.shortcuts import print_formatted_text
from prompt_toolkit.completion import NestedCompleter, PathCompleter from prompt_toolkit.completion import NestedCompleter, PathCompleter
from prompt_toolkit.completion import CompleteEvent, Completer, Completion from prompt_toolkit.completion import CompleteEvent, Completer, Completion
from prompt_toolkit.history import FileHistory from prompt_toolkit.history import FileHistory
@ -98,26 +100,42 @@ def escape_ansi(line):
ansi_escape = re.compile(r'(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]') ansi_escape = re.compile(r'(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]')
return ansi_escape.sub('', line) return ansi_escape.sub('', line)
def print_one_line_above(str): def print_one_line_above(text):
""" prints a string above current line """ """Status line(s) above the prompt; delegates to print_above."""
width = os.get_terminal_size().columns print_above(text)
stringlen = len(escape_ansi(str))-1
lines = divmod(stringlen, width)[0] + 1
print("\u001B[s", end="") # Save current cursor position
print("\u001B[A", end="") # Move cursor up one line
print("\u001B[999D", end="") # Move cursor to beginning of line
for _ in range(lines):
print("\u001B[S", end="") # Scroll up/pan window down 1 line
print("\u001B[L", end="") # Insert new line
for _ in range(lines - 1):
print("\u001B[A", end="") # Move cursor up one line
print(str, end="") # Print output status msg
print("\u001B[u", end="", flush=True) # Jump back to saved cursor position
def print_above(str): def print_above(text):
lines = str.split('\n') """
for l in lines: Show text above the current input line.
print_one_line_above(l)
When prompt_toolkit has a running Application (interactive prompt), plain
print() and DEC save/restore fight the renderer use print_formatted_text,
which suspends the UI, writes via the Output layer (UTF-8), and redraws.
With no active app (scripts, pipes), use CSI save/move/clear/restore only.
"""
if text == "":
return
if get_app_or_none() is not None:
print_formatted_text(ANSI(text), end="\n")
return
lines = text.split("\n")
n = len(lines)
chunks = ["\033[s", f"\033[{n}A"]
for i, line in enumerate(lines):
chunks.append("\033[1G\033[2K")
chunks.append(line)
if i < n - 1:
chunks.append("\033[B")
chunks.append("\033[u")
combined = "".join(chunks)
enc = getattr(sys.stdout, "encoding", None) or "utf-8"
try:
sys.stdout.buffer.write(combined.encode(enc, errors="replace"))
sys.stdout.buffer.flush()
except (AttributeError, TypeError):
sys.stdout.write(combined)
sys.stdout.flush()
async def process_event_message(mc, ev, json_output, end="\n", above=False): async def process_event_message(mc, ev, json_output, end="\n", above=False):
""" display incoming message """ """ display incoming message """
@ -3373,7 +3391,7 @@ async def next_cmd(mc, cmds, json_output=False):
else: else:
if json_output: if json_output:
print(json.dumps(res.payload)) print(json.dumps(res.payload))
else : else:
path_len = res.payload['path_len'] path_len = res.payload['path_len']
if (path_len == 0) : if (path_len == 0) :
print("0 hop") print("0 hop")