Displaying at most N colored chars in a terminal

Hi there!

I'm currently making a program where I have to display an information with println() BUT I must also ensure it does not overflow the terminal's current line width.

So I used a crate to get the terminal's width in characters (let's call it N), and only kepts the N first characters of the string.

Problem, some of these characters are non-printable: the color escape sequences are considered as characters. Which results in the string being stripped too early.

How could I achieve the stripping so that I display at most N visible characters while keeping all the color escape sequences inside it?

Thanks in advance for your help!

Ah yes, this is an annoying problem.
The solution depends heavily on how you are constructing the "colored" strings.

  • If you are dealing with static strings, you can iterate through them and find the actual length. Inefficient but simple.
  • You can construct a ColoredString struct, which wraps around a String which has escape sequences. You can then keep track of the number of escape sequences included, and subtract them from the string length.
  • In order to truncate efficiently, you need to keep track of more information. If N is fixed, then you can find it out once and cache the result. If N is dynamic (which might be the case if you are handling terminal resizing), then there are many ways to go about it, where you need to tradeoff between space and time.
    The most time efficient way would be to store the string index for each visible character in a map. This will take a lot of space for long strings, so you might want to store every 5th or 10th character's index.
    Another way would be store the indices of the escape sequences and use that to find out how many extra characters you need to include in the final substring.
2 Likes

Welcome to the hell that is unicode. It's a difficult problem, since "difficult" is 9 USVs and yet "difficult" is only 7.

For another example, what do you want to happen for '𒀰'? (And yes, that's one "character".)

I suspect that the only reliable answer is another API from the whatever terminal crate you're using to ask it how long something is, and work based on that.

You might also be able to write using terminal APIs that offer rendering truncation, rather than going through println!.

4 Likes

Indeed, there are characters that are printed with a width higher than some other characters, and I'd like to also take care of that.

The string is entirely dynamic and can be made of multiple ColoredString put together for all I know. I just get an &str to display.

Your best bet might be to just iterate through the string. Since terminal widths are fairly small, time shouldn't be a bottle neck.

1 Like

It depends on your terminal and font and maybe the locale too. I don't think there's a sane and completely correct way (the less sane way involves paint-query-adjust across different terminal capabilities). I'm not saying don't try (in fact thank you for doing so), but just noting it's a best-effort situation.

1 Like

Playground seems to assume constant widths, the cursor is displayed in a wrong position when you edit behind it.

I don't think so. These double-width characters move the cursor correctly. The cuneiform sigil, however, seems to have a Unicode width of 1. This doesn't make much sense to me, as that's supposed to be the display width.