console-rs / indicatif

A command line progress reporting library for Rust
MIT License
4.46k stars 243 forks source link

Width-filling progress bars wrap around on >1 character wide emojis #611

Open MarijnS95 opened 11 months ago

MarijnS95 commented 11 months ago

afbeelding

afbeelding

Consider the example in report #144, modified to use 🖼️ emoji which takes up 2 characters in Windows terminals, and modified to use wide_bar to fill the entire screen:

use std::cmp::min;
use std::thread;
use std::time::Duration;

use indicatif::{ProgressBar, ProgressStyle};

fn main() {
    let mut downloaded = 0;
    let total_size = 231231231;

    let pb = ProgressBar::new(total_size);
    pb.set_style(
        ProgressStyle::default_bar()
            .template("{prefix} {msg:.bold} {wide_bar:.blue} ({pos}/{len}, {elapsed}, ETA {eta})")
            .unwrap()
            .progress_chars("█▇▆▅▄▃▂▁  "),
    );

    pb.set_message("🖼️ Downloading textures");
    while downloaded < total_size {
        let new = min(downloaded + 223211, total_size);
        downloaded = new;
        pb.set_position(new);
        thread::sleep(Duration::from_millis(1));
    }

    pb.set_style(
        ProgressStyle::default_bar()
            .template("{msg:>12.green.bold} downloading {total_bytes:.green} in {elapsed:.green}")
            .unwrap(),
    );
    pb.finish_with_message("Finished");
}

As can be seen in the screenshots the ) in Microsoft Terminal and the s) in VScode wrap around to the next line, spamming progress bars.

Note that in the VSCode terminal there appears to be no space between 🖼️ and Downloading, which is similar to what I observe on Linux (alacritty terminal): the emoji always takes up a single character even if it is rendered in two characters. Not having a space at all (e.g. 🖼️Downloading) gets the D rendered inside the picture frame emoji:

afbeelding

(I'm not familiar with the right ways to solve this in a platform-agnostic manner)


Looking at the organization structure here, perhaps this issue should be transferred to:

https://github.com/console-rs/console


Note that the leading space in the screenshot is simply caused by the unused {prefix} that I forgot to remove while transferring our progress bars to a minimal example.

MarijnS95 commented 11 months ago

Note that in both the example and our own code ansi-parsing and unicode-width are turned on on the console crate.

EDIT: Neither is this solved by enabling improved_unicode.

djc commented 11 months ago

I'm open to reviewing a fix for this, but probably won't be able to do much other work on this. I suppose the style::measure() function might be implicated? Not sure which part is going wrong here, I think (particularly with those Cargo features) we should have code for handling this kind of thing.

MarijnS95 commented 11 months ago

Yeah:

#[test]
fn measure_emojis() {
    assert_eq!(measure("🖼️"), 2);
    assert_eq!(measure("🧼"), 2);
}

Fails with measure() returning 1. I'm not sure if this is also something that changes with fonts and the renderer, as shown with Windows Terminal padding the emoji to two characters while VSCode does not. I.e. printing:

println!("abcdef");
println!("🖼️  🧼"); // Only 2 spaces here

Gives in VSCode terminal: afbeelding But in Microsoft Terminal: afbeelding

Even Windows Console Host adds a third space, despite not being able to render the emoji:

afbeelding