tui-rs
tui-rs copied to clipboard
Display ANSI color-codes in `Text::raw`
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

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;
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.
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.
@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?
It depends on what is your use case:
- 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). - 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.
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.
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>
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.
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 :)
The ansiiparser crate isn't fully complete at the moment, for example there is no support for true color atm.
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).
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.
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.
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?