terminal icon indicating copy to clipboard operation
terminal copied to clipboard

proposed enhancement: ESC sequence to get actual string length

Open unxed opened this issue 1 year ago • 1 comments

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()

unxed avatar Aug 26 '24 22:08 unxed

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.

j4james avatar Aug 27 '24 00:08 j4james

You know what, we do have a pretty related thread so I'll just move discussion over there: /dup #218

Thanks!

zadjii-msft avatar Aug 28 '24 21:08 zadjii-msft

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!