Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tabs mess up bar width calculation #150

Closed
jeffparsons opened this issue Feb 5, 2020 · 3 comments
Closed

Tabs mess up bar width calculation #150

jeffparsons opened this issue Feb 5, 2020 · 3 comments

Comments

@jeffparsons
Copy link

Indicatif appears to not understand how many columns a tab will take up in a terminal. The result is that "wide messages", for example, will wrap to a new line before they are truncated, and bars will re-render down the screen instead of in-place.

I discovered this while building a program that runs arbitrary external commands and displays their most recent line of stdout or stderr on a progress bar. I was scratching my head for a while until I realized that some of those external commands are printing tabs to stderr!

Observed on Mac, using both iTerm2 and the inbuilt terminal. Not sure if it affects other platforms.

My workaround is to simply .replace("\t", " ") before setting the bar message. I'm guessing there's some way to interrogate a terminal and discover how wide a tab will be, and that could be used in width calculations. But I don't know enough about terminals to know if that's safe enough to rely on across all platforms, or whether replacing tabs with spaces would be a more pragmatic solution. Thoughts?

Minimal-ish reproduction:

use std::thread;
use std::time::Duration;

use indicatif::{ProgressBar, ProgressStyle};

const LOREM_IPSUM: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ac facilisis est. Nulla commodo sit amet nulla non maximus. Ut in magna quis nibh ullamcorper volutpat at et purus. Praesent non libero in magna pellentesque pellentesque a nec nisl. Nulla lorem quam, maximus suscipit dapibus vitae, mattis a felis. In volutpat tellus elit, sed laoreet felis tempus vitae. Aenean aliquam, risus vitae sagittis lobortis, nunc tellus iaculis sapien, vel consectetur neque tellus pharetra tortor. Quisque at semper leo. Praesent tellus lectus, bibendum vitae nisl in, posuere placerat nunc. Aliquam dapibus, lorem dignissim suscipit suscipit, mi urna malesuada nunc, condimentum sollicitudin orci massa et nisi. Nam purus purus, sodales commodo augue eget, suscipit iaculis nulla.";
const LOREM_IPSUM_WITH_TABS: &str = "Lorem ipsum\tdolor sit\tamet, consectetur adipiscing elit. Duis ac facilisis est. Nulla commodo sit amet nulla non maximus. Ut in magna quis nibh ullamcorper volutpat at et purus. Praesent non libero in magna pellentesque pellentesque a nec nisl. Nulla lorem quam, maximus suscipit dapibus vitae, mattis a felis. In volutpat tellus elit, sed laoreet felis tempus vitae. Aenean aliquam, risus vitae sagittis lobortis, nunc tellus iaculis sapien, vel consectetur neque tellus pharetra tortor. Quisque at semper leo. Praesent tellus lectus, bibendum vitae nisl in, posuere placerat nunc. Aliquam dapibus, lorem dignissim suscipit suscipit, mi urna malesuada nunc, condimentum sollicitudin orci massa et nisi. Nam purus purus, sodales commodo augue eget, suscipit iaculis nulla.";

fn main() {
    let pb = ProgressBar::new(1024);
    let style = ProgressStyle::default_bar().template("{pos} {wide_msg}");
    pb.set_style(style);
    for i in 0..1024 {
        pb.inc(1);
        if i < 500 {
            pb.set_message(LOREM_IPSUM);
        } else {
            pb.set_message(LOREM_IPSUM_WITH_TABS);
        }
        thread::sleep(Duration::from_millis(5));
    }
    pb.finish_with_message("done");
}

If you run this, as soon as i >= 500 the bar will start "smearing" down the screen, presumably because the accidental line wrapping makes Indicatif's math for how many lines it should jump up to re-render the bar incorrect.

If I replace the tabs with emojis, the same thing happens in reverse; indicatif appears to expect the emojis to take up more columns than they do in practice, and so the line is truncated early instead.

@mibac138
Copy link
Contributor

This is because of how unicode-width works. You can find a related issue here: unicode-rs/unicode-width#6.

@jeffparsons
Copy link
Author

@mibac138 Thanks; I'll follow along on that issue. :)

@mibac138
Copy link
Contributor

mibac138 commented May 4, 2020

FWIW indicatif isn't the only one with this issue, see: codespan (issue), annotate-snippets-rs (issue), rustc (code)

chris-laplante added a commit to chris-laplante/indicatif that referenced this issue Mar 31, 2022
Tab width in terminals is not standardized (though 4 or 8 spaces seems common)
but this provides an escape hatch.

This should fix console-rs#150.
chris-laplante added a commit to chris-laplante/indicatif that referenced this issue Jun 17, 2022
chris-laplante added a commit to chris-laplante/indicatif that referenced this issue Jun 17, 2022
chris-laplante added a commit to chris-laplante/indicatif that referenced this issue Jun 23, 2022
chris-laplante added a commit to chris-laplante/indicatif that referenced this issue Jul 5, 2022
chris-laplante added a commit to chris-laplante/indicatif that referenced this issue Jul 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants