terminal
terminal copied to clipboard
proposed enhancement: ESC sequence to get actual string length
One of the major challenges in building console interfaces is the unpredictability of the actual length of a string (in screen cells) when using Unicode characters. The width of the same characters can be displayed differently across various terminals.
Of course, it is possible to determine this width by displaying the characters and checking how much the cursor has moved (sample in python is below), but this approach is slow and clutters the console log.
It would be great if you could propose an additional terminal extension, such as an ESC sequence, that allows querying the terminal to determine how many screen cells a given string of Unicode characters will occupy.
Thank you!
import os
import sys
import tty
import termios
def get_cursor_position():
"""Gets current cursor position in terminal"""
sys.stdout.write('\033[6n')
sys.stdout.flush()
buf = ""
while True:
ch = sys.stdin.read(1)
buf += ch
if ch == "R":
break
# Answer sample: '\033[12;40R'
# Extracts position from string
try:
rows, cols = map(int, buf.lstrip('\033[').rstrip('R').split(';'))
except ValueError:
rows, cols = -1, -1 # In case of an error
return rows, cols
def hide_cursor():
"""Hides cursor"""
sys.stdout.write('\033[?25l')
sys.stdout.flush()
def show_cursor():
"""Shows cursor"""
sys.stdout.write('\033[?25h')
sys.stdout.flush()
def print_teststring():
"""Measures screen cells number"""
teststring = 'a1🙂❤️'
# Save terminal settings
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
# Switching terminal to non blocking mode
tty.setcbreak(sys.stdin.fileno())
# Saving cursor position
sys.stdout.write('\033[s')
# Hiding cursor
hide_cursor()
# Moving cursor outside the visible area
sys.stdout.write('\033[1000A')
# Getting position before string output
_, start_col = get_cursor_position()
# Printing string
sys.stdout.write(teststring)
sys.stdout.flush()
# Getting position after string output
_, end_col = get_cursor_position()
# Moving cursor back to saved position
sys.stdout.write('\033[u')
# Showing cursor
show_cursor()
# Counting screen cells used
cells_used = end_col - start_col
# Printing result
print("String: \"", teststring, f"\", screen cells count: {cells_used}")
finally:
# Restoring terminal settings
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
# Showing cursor in case of an error
show_cursor()
if __name__ == "__main__":
print_teststring()
Of course, it is possible to determine this width by displaying the characters and checking how much the cursor has moved (sample in python is below), but this approach is slow and clutters the console log.
Honestly this is what I would recommend. You can avoid it cluttering the log by running the tests on a background page, or on terminals that don't support pages you could fallback to using hidden attributes, and also erase the text immediately afterwards.
I can't imagine a custom length testing sequence is going to be much faster than this, and you're still going to have to fallback to the cursor movement tests on the 99% of terminals that likely won't support it. It just doesn't seem worth the effort in my opinion.
You know what, we do have a pretty related thread so I'll just move discussion over there: /dup #218
Thanks!
Hi! We've identified this issue as a duplicate of another one that already exists on this Issue Tracker. This specific instance is being closed in favor of tracking the concern over on the referenced thread. Thanks for your report!