tui-rs icon indicating copy to clipboard operation
tui-rs copied to clipboard

Display ANSI color-codes in `Text::raw`

Open Ma27 opened this issue 5 years ago • 12 comments

Describe the bug

When I have a string with ANSI color-codes I want to pass to Text::raw, those won't be displayed properly.

A use-case where this is needed is e.g. when you want to render the output from e.g. std::process in the paragraph-widget.

To Reproduce

/* loop, main func, etc */
let x = "\x1b[0;31mTest string\x1b[0m";
terminal.draw(|mut f| {
    let size = f.size();
    let text = [Text::raw(x)];
    let paragraph = Paragraph::new(text.iter());
    f.render_widget(paragraph, size);
})?;

Expected behavior

I'd expect to have a red Test string rendered on my terminal. What happens instead is demonstrated as screenshot below.

Screenshots 2020-05-27-114226_screenshot

Desktop (please complete the following information):

  • OS: NixOS 20.03 (Linux 5.6.14)
  • Terminal Emulator: alacritty 0.4.2
  • Crate version 0.9.5 (also tested with master on 4fe647df0aabfffd4e8612f503bab3664e2b64eb)
  • Backend termion

Additional context

This may be related to #98 and #88

This issue is caused by the render-function: the .width()-function returns 0 for the unicode-segment \x1b, so x isn't incremented. Therefore the \x1b-symbol in the buffer will be replaced by the [-symbol as both of them should land in the cell 0:0.

A local fix for this may look like this, but I didn't search for further implications which is why I didn't open a PR yet:

diff --git a/src/widgets/paragraph.rs b/src/widgets/paragraph.rs
index b5a57ce..46a9f2a 100644
--- a/src/widgets/paragraph.rs
+++ b/src/widgets/paragraph.rs
@@ -146,7 +146,7 @@ where
                     buf.get_mut(text_area.left() + x, text_area.top() + y - self.scroll)
                         .set_symbol(symbol)
                         .set_style(*style);
-                    x += symbol.width() as u16;
+                    x += std::cmp::max(symbol.width() as u16, 1);
                 }
             }
             y += 1;

Ma27 avatar May 27 '20 10:05 Ma27

There is no plan at the moment to support ansi codes directly in Text::raw. The main reason is that it has portability issues and tui has already its own concept of styling. The idea is that you would have to convert those ansi code to tui primitives. However, I well aware that the current primitives are not enough to support rich styling and I'm working on a solution that would allow us to have good support in most widgets.

fdehau avatar Jun 03 '20 20:06 fdehau

Would it be an option to add another Text::ANSIRaw variant that does implement ANSI sequences? I'm working on an application that needs to print out the stdout of arbitrary shell commands, so having support for ansi sequences would be nice.

elkowar avatar Jun 27 '20 13:06 elkowar

@fdehau now that we have an improved Text-API, would it be okay to implement some kind of ANSIRawSpan for this purpose as suggested by @elkowar?

Ma27 avatar Jul 11 '20 14:07 Ma27

It depends on what is your use case:

  1. You need this because you are given a string with ANSI escapes sequences and you want to display it as is in tui. For me it is something specific to your application and thus has low priority (I would accept a PR though).
  2. You need this because you are not able to get the styling you want using the new API. Then I'm interested to hear your use case and how we may improve the state of things.

fdehau avatar Jul 11 '20 16:07 fdehau

It's about the first use-case: I want to display the output of another process (including its ANSI color-codes) in a tui-rs powered application.

For me it is something specific to your application and thus has low priority (I would accept a PR though).

I understand that and I didn't mean to push more work to you. I just wanted to make sure that you'd be okay with having such a feature in the codebase. As soon as I have time to, I can file a PR.

Ma27 avatar Jul 11 '20 17:07 Ma27

As soon as I have time to, I can file a PR

Great ! You can also open a dedicated issue if you want to discuss the design of such addition. My first thoughts on this are that we probably want this in its own feature-gated module and an AnsiParser with a method like fn parse<'a, S>(&self, input: S) -> Text<'a> where S: AsRef<str>

fdehau avatar Jul 11 '20 17:07 fdehau

While this is somewhat use-case dependent, I do feel like this might not be the weirdest use-case out there. I'm helping with a project that is currently using termion and there was some brief discussion about moving to tui, as the scope of the project has expanded. I made a little test project to see how tui handles ANSI colour codes, as we also work on input containing them, and it turns out... it doesn't. ANSI escape sequences are the standard way of colouring terminal output on any POSIX compatible terminal. I feel like a parser for strings containing them would make sense to have.

Xandaros avatar Jul 17 '20 01:07 Xandaros

Yeah, we agreed upon that. I started implementing such a feature, but this isn't a high-priority thing as this is for a side-project, so you may need to be a bit patient :)

@fdehau sounds reasonable. Internally, I'll use https://docs.rs/ansi-parser/0.6.5/ansi_parser/ to parse the raw input and transform this into Style objects. Will ping you as soon as I have a bit reviewable code :)

Ma27 avatar Jul 17 '20 14:07 Ma27

The ansiiparser crate isn't fully complete at the moment, for example there is no support for true color atm.

elkowar avatar Jul 18 '20 15:07 elkowar

I wrote a (pretty naive) crate to handle the conversion. I use it in my notes program for translating Command output. It uses termwiz for ansi parsing, but I've only tested it on Ubuntu. I mention it here in case it's helpful for any potential future implementation of ANSI support in TUI, or for other people who found this issue while searching for an answer (like me).

ansi4tui: crates.io

bmhenry avatar Nov 06 '20 19:11 bmhenry

I also wrote a crate to translate ansi color codes to tui::text::Text. ~~It doesn't have any dependency other that tui~~ It uses nom parser to do the parsing. It also supports true color and utf-8. I've tested it only on linux. Should also support windows / mac.

I tried @bmhenry 's crate but it only supports linux so I made this.

ansi-to-tui

example

uttarayan21 avatar Apr 26 '21 20:04 uttarayan21

Ah, this is a bit of a problem for me too as I am intercepting output from other logging utilities and writing into the buffer.

thorlucas avatar Dec 10 '21 11:12 thorlucas

I would like to show the content of a log file (which has an ANSI styled progress bar) inside of my tui, guess this is just not possible with tui-rs right now?

kabouzeid avatar Jan 28 '23 21:01 kabouzeid