[GH-ISSUE #57] Text and ESC seq #45

Open
opened 2026-03-03 16:21:52 +03:00 by kerem · 19 comments
Owner

Originally created by @enverbisevac on GitHub (Jun 27, 2018).
Original GitHub issue: https://github.com/mum4k/termdash/issues/57

Originally assigned to: @cayter on GitHub.

Is it possible to use with Text widget esc seq for foreground color?
Example ESC[39;49m

Originally created by @enverbisevac on GitHub (Jun 27, 2018). Original GitHub issue: https://github.com/mum4k/termdash/issues/57 Originally assigned to: @cayter on GitHub. Is it possible to use with Text widget esc seq for foreground color? Example ESC[39;49m
Author
Owner

@mum4k commented on GitHub (Jun 27, 2018):

That is a good idea. I was thinking about adding a wrapper that would convert unstructured annotated text (with colors, other attributes, etc) into the cell options the API accepts currently.

Do you have a specific requirement for this to use ESC sequences? Or could it be say in something resembling the Go's text template syntax (https://godoc.org/text/template). A rough example:

"Text in the default color.{{FgColor ColorGreen}}This text is green{{FgColor ColorBlack}}This text is black."

What do you think?

<!-- gh-comment-id:400806877 --> @mum4k commented on GitHub (Jun 27, 2018): That is a good idea. I was thinking about adding a wrapper that would convert unstructured annotated text (with colors, other attributes, etc) into the cell options the API accepts currently. Do you have a specific requirement for this to use ESC sequences? Or could it be say in something resembling the Go's text template syntax (https://godoc.org/text/template). A rough example: "Text in the default color.{{FgColor ColorGreen}}This text is green{{FgColor ColorBlack}}This text is black." What do you think?
Author
Owner

@enverbisevac commented on GitHub (Jun 27, 2018):

I think you shoud not involve any templating for example if someone want to load external file with esc seq. I resolve this issue in a way removing special characters like rexp \x1b\[[0-9;]*m without colors, but I think this feature is very nice to have.

<!-- gh-comment-id:400809037 --> @enverbisevac commented on GitHub (Jun 27, 2018): I think you shoud not involve any templating for example if someone want to load external file with esc seq. I resolve this issue in a way removing special characters like rexp `\x1b\[[0-9;]*m` without colors, but I think this feature is very nice to have.
Author
Owner

@mum4k commented on GitHub (Jun 27, 2018):

Thanks for explaining the use case. Sounds like we specifically want to support colors in escape sequences then. I would still lean towards writing this as a separate wrapper in order to not complicate the implementation of the text widget itself.

How does a package with exported function similar to this one sound?

// WriteEscapedText reads data from the reader and writes the content to the text widget.
// Recognizes ANSI escape sequences and changes cell colors accordingly.
func WriteEscapedText(t *text.Text, io.Reader) error
<!-- gh-comment-id:400812043 --> @mum4k commented on GitHub (Jun 27, 2018): Thanks for explaining the use case. Sounds like we specifically want to support colors in escape sequences then. I would still lean towards writing this as a separate wrapper in order to not complicate the implementation of the text widget itself. How does a package with exported function similar to this one sound? ```go // WriteEscapedText reads data from the reader and writes the content to the text widget. // Recognizes ANSI escape sequences and changes cell colors accordingly. func WriteEscapedText(t *text.Text, io.Reader) error ```
Author
Owner

@enverbisevac commented on GitHub (Jun 27, 2018):

@mum4k at the moment this is good approach, later it should be in text widget I think

<!-- gh-comment-id:400838535 --> @enverbisevac commented on GitHub (Jun 27, 2018): @mum4k at the moment this is good approach, later it should be in text widget I think
Author
Owner

@mum4k commented on GitHub (Jun 27, 2018):

Great, I think we can start with this and then iterate.

Is this something you would be able to help with, or would you prefer if I take it on? The main priority is to reach the first milestone now (feature parity with termui) so I can slot it after that.

<!-- gh-comment-id:400839411 --> @mum4k commented on GitHub (Jun 27, 2018): Great, I think we can start with this and then iterate. Is this something you would be able to help with, or would you prefer if I take it on? The main priority is to reach the first milestone now (feature parity with termui) so I can slot it after that.
Author
Owner

@cayter commented on GitHub (Mar 14, 2020):

@mum4k Thanks for the great work. Wanted to know if this is still being planned? Would you mind helping to explain why we had to forbid the escape characters as I'm pretty new to this terminal building thing? In any case, where should we implement WriteEscapedText() so that we can get this started? Thanks.

<!-- gh-comment-id:599028470 --> @cayter commented on GitHub (Mar 14, 2020): @mum4k Thanks for the great work. Wanted to know if this is still being planned? Would you mind helping to explain why we had to forbid the escape characters as I'm pretty new to this terminal building thing? In any case, where should we implement `WriteEscapedText()` so that we can get this started? Thanks.
Author
Owner

@mum4k commented on GitHub (Mar 15, 2020):

Hello @cayter and thank you for reaching out. While I don't plan to work on this in the immediate future, we certainly can work together to add this feature if needed.

First to answer your question about why we had to forbid them. Terminal escape sequences are "metadata", or hidden characters on the terminal. They don't occupy terminal cells, instead they describe how terminal cells behave (their color, are they blinking, etc.). Termdash is a layer above the physical terminal, it provides abstractions for drawing on a canvas. Canvas is essentially a 2D matrix of cells where each cell has its own options (the metadata). By moving the metadata into an object we reached a point where we only work with visible characters within termdash and describe their colors in the options. Termdash uses termbox-go or tcell to translate this canvas onto the physical terminal. This is the layer where options get translated into these invisible terminal sequences, e.g. termbox-go does it here.

Since escape sequences cannot be passed through the termdash and terminal layers directly as explained above, we will have to translate them. I agree with @enverbisevac that this should be in some shape or form available on the text widget itself. I can imagine the following solution:

  1. We can add a new private package internal/escape with a function that translates an escape sequence to the corresponding termdash cell options, its API could be like this:
// ToCellOpts translates a known terminal escape sequence to the corresponding
// cell options. Returns an error if the escape sequence isn't recognized.
func ToCellOpts(esc string) (*cell.Options, error) { ... }
  1. We can add a new private package widgets/text/escaped that given a text will parse the text for known escape sequences and return chunks of texts with corresponding cell options. This package will implement a text parser to find all escape sequences and will leverage (1) above to perform escape sequence translations. One thing to keep in mind here is that termdash aims to support unicode, so as any text parser, this one will have to account for wide characters too. There are plenty of examples of this in termdash already and I will be happy to provide more detail if need be. Its API could look like this:
// TextChunk is a part of text that shares the same cell options.
type TextChunk struct {
  text  string
  cellOpts *cell.Options
}

// ParseColors parses the provided text for terminal escape sequences that specify
// text colors and returns individual chunks of text. The parsed escape sequences
// are removed from the text. Returns an error if an unsupported escape sequence
// is encountered.
func ParseColors(text string) ([]*TextChunk, error) { ... }
  1. Lastly we can go back to the text widget itself and implement a new method that would display text with escape sequences. This method would first use (2) to parse the text into chunks. Known escape sequences that set colors will be removed and replaced by the corresponding cell options. This method would then use the existing method write to write all the text chunks with their individual cell options. Its API could look like this:
// WriteEscapedText reads data from the reader and writes the content to the text
// widget. Recognizes ANSI escape sequences that set colors and changes cell
// colors accordingly.
func (t *Text) WriteEscapedText(text string) error { ... }

Note - this assumes we will only support escape sequences that set background and foreground colors. If we need to support more, we need to rethink this a bit.

Please feel free to let me know if you have any questions or would like to discuss this further. Is this something you would be interested in picking up and implementing?

<!-- gh-comment-id:599225773 --> @mum4k commented on GitHub (Mar 15, 2020): Hello @cayter and thank you for reaching out. While I don't plan to work on this in the immediate future, we certainly can work together to add this feature if needed. First to answer your question about why we had to forbid them. Terminal escape sequences are "metadata", or hidden characters on the terminal. They don't occupy terminal cells, instead they describe how terminal cells behave (their color, are they blinking, etc.). Termdash is a layer above the physical terminal, it provides abstractions for drawing on a [canvas](https://github.com/mum4k/termdash/blob/e6af456c61cd50c650ae80857a74763d1ab4e158/internal/canvas/canvas.go#L29-L38). Canvas is essentially a 2D matrix of [cells](https://github.com/mum4k/termdash/blob/5430f4d89ee524dcbabc500c83cf978025977cf8/internal/canvas/buffer/buffer.go#L36-L43) where each cell has its own [options](https://github.com/mum4k/termdash/blob/5430f4d89ee524dcbabc500c83cf978025977cf8/cell/cell.go#L24-L28) (the metadata). By moving the metadata into an object we reached a point where we only work with visible characters within termdash and describe their colors in the options. Termdash uses [termbox-go](https://github.com/nsf/termbox-go) or [tcell](https://github.com/gdamore/tcell) to translate this canvas onto the physical terminal. This is the layer where options get translated into these invisible terminal sequences, e.g. [termbox-go](https://github.com/nsf/termbox-go/blob/4d2b513ad8bee47a9a5a65b0dee0182049a31916/termbox.go#L99-L110) does it here. Since escape sequences cannot be passed through the termdash and terminal layers directly as explained above, we will have to translate them. I agree with @enverbisevac that this should be in some shape or form available on the text widget itself. I can imagine the following solution: 1. We can add a new private package `internal/escape` with a function that translates an escape sequence to the corresponding termdash cell options, its API could be like this: ```go // ToCellOpts translates a known terminal escape sequence to the corresponding // cell options. Returns an error if the escape sequence isn't recognized. func ToCellOpts(esc string) (*cell.Options, error) { ... } ``` 2. We can add a new private package `widgets/text/escaped` that given a text will parse the text for known escape sequences and return chunks of texts with corresponding cell options. This package will implement a text parser to find all escape sequences and will leverage (1) above to perform escape sequence translations. One thing to keep in mind here is that termdash aims to support unicode, so as any text parser, this one will have to account for wide characters too. There are plenty of examples of this in termdash already and I will be happy to provide more detail if need be. Its API could look like this: ```go // TextChunk is a part of text that shares the same cell options. type TextChunk struct { text string cellOpts *cell.Options } // ParseColors parses the provided text for terminal escape sequences that specify // text colors and returns individual chunks of text. The parsed escape sequences // are removed from the text. Returns an error if an unsupported escape sequence // is encountered. func ParseColors(text string) ([]*TextChunk, error) { ... } ``` 3. Lastly we can go back to the text widget itself and implement a new method that would display text with escape sequences. This method would first use (2) to parse the text into chunks. Known escape sequences that set colors will be removed and replaced by the corresponding cell options. This method would then use the existing method [write](https://github.com/mum4k/termdash/blob/68bf0566e6ec8effaf6e83400ef0046347a5f5f1/widgets/text/text.go#L99) to write all the text chunks with their individual cell options. Its API could look like this: ```go // WriteEscapedText reads data from the reader and writes the content to the text // widget. Recognizes ANSI escape sequences that set colors and changes cell // colors accordingly. func (t *Text) WriteEscapedText(text string) error { ... } ``` Note - this assumes we will only support escape sequences that set background and foreground colors. If we need to support more, we need to rethink this a bit. Please feel free to let me know if you have any questions or would like to discuss this further. Is this something you would be interested in picking up and implementing?
Author
Owner

@cayter commented on GitHub (Mar 16, 2020):

@mum4k Thanks for the detailed explanation. I'm still reading through the source code and only doing this when I have free time, will try to submit a PR within the next 1 month.

<!-- gh-comment-id:599334837 --> @cayter commented on GitHub (Mar 16, 2020): @mum4k Thanks for the detailed explanation. I'm still reading through the source code and only doing this when I have free time, will try to submit a PR within the next 1 month.
Author
Owner

@mum4k commented on GitHub (Mar 16, 2020):

Of course, please take your time @cayter, there is no time limit here. Feel free to let me know if you have any other questions or ideas.

<!-- gh-comment-id:599773287 --> @mum4k commented on GitHub (Mar 16, 2020): Of course, please take your time @cayter, there is no time limit here. Feel free to let me know if you have any other questions or ideas.
Author
Owner

@cayter commented on GitHub (Mar 20, 2020):

Okay, did some quick experiments within my own codebase, here's the result and the code implementation is over here. Will find some time to port the implementation back to this repository.

Any idea when we would start supporting go modules? I find it a bit challenging to setup local dev for go repositories without go modules while my environment is already setup with v1.14.x go modules. Thanks.

<!-- gh-comment-id:601649747 --> @cayter commented on GitHub (Mar 20, 2020): Okay, did some quick experiments within my own codebase, here's the [result](https://asciinema.org/a/v6R4F6l3FA4NFoetzLdShoLwm) and the code implementation is over [here](https://github.com/appist/appy/blob/master/cmd_start.go#L483-L614). Will find some time to port the implementation back to this repository. Any idea when we would start supporting go modules? I find it a bit challenging to setup local dev for go repositories without go modules while my environment is already setup with v1.14.x go modules. Thanks.
Author
Owner

@mum4k commented on GitHub (Mar 20, 2020):

That looks great @cayter, thanks a lot for working on this. One thing I forgot to note, if you can, please fork off and pull back to the devel branch. I have now documented it here:
https://github.com/mum4k/termdash/blob/devel/CONTRIBUTING.md

I will pull it to master at release time. Apart from that please check out the other portions of the CONTRIBUTING.md doc, there is also a requirement to sign Google's CLA. The PR auto scripts will let you know and give you instructions.

What do you think - should we add an option for the text widget to explicitly enable this new behavior (similar to text.WrapAtRunes and the other options)? Or do we want to leave it enabled by default?

You have a very good point about go modules, I think it is about time we introduce them. I will add go modules later today. Please make sure you sync and fork off the devel branch after I add them. Ideally the go modules will go into the same release as your PR. Sorry about any trouble the lack of go modules caused you and thanks again!

<!-- gh-comment-id:601791663 --> @mum4k commented on GitHub (Mar 20, 2020): That looks great @cayter, thanks a lot for working on this. One thing I forgot to note, if you can, please fork off and pull back to the devel branch. I have now documented it here: https://github.com/mum4k/termdash/blob/devel/CONTRIBUTING.md I will pull it to master at release time. Apart from that please check out the other portions of the CONTRIBUTING.md doc, there is also a requirement to sign Google's CLA. The PR auto scripts will let you know and give you instructions. What do you think - should we add an option for the text widget to explicitly enable this new behavior (similar to text.WrapAtRunes and the other options)? Or do we want to leave it enabled by default? You have a very good point about go modules, I think it is about time we introduce them. I will add go modules later today. Please make sure you sync and fork off the devel branch after I add them. Ideally the go modules will go into the same release as your PR. Sorry about any trouble the lack of go modules caused you and thanks again!
Author
Owner

@mum4k commented on GitHub (Mar 20, 2020):

(fyi) #228 tracks migration to Go modules.

<!-- gh-comment-id:601792445 --> @mum4k commented on GitHub (Mar 20, 2020): (fyi) #228 tracks migration to Go modules.
Author
Owner

@mum4k commented on GitHub (Mar 20, 2020):

The devel branch is now migrated to Go modules. Please let me know if you encounter any issues while forking or developing off it.

<!-- gh-comment-id:601846318 --> @mum4k commented on GitHub (Mar 20, 2020): The devel branch is now migrated to Go modules. Please let me know if you encounter any issues while forking or developing off it.
Author
Owner

@mum4k commented on GitHub (Mar 20, 2020):

Please ignore my question about whether we should add an option to enable this behavior. I remembered that we are adding a separate method so we are all good.

<!-- gh-comment-id:601870888 --> @mum4k commented on GitHub (Mar 20, 2020): Please ignore my question about whether we should add an option to enable this behavior. I remembered that we are adding a separate method so we are all good.
Author
Owner

@cayter commented on GitHub (Mar 21, 2020):

Apart from that please check out the other portions of the CONTRIBUTING.md doc, there is also a requirement to sign Google's CLA.

Just did.

Please ignore my question about whether we should add an option to enable this behavior. I remembered that we are adding a separate method so we are all good.

Yep. Although right now my implementation can be ported over to Write() but I guess we need to ensure its backward compatibility so that whoever is using this function doesn't get surprised by this behaviour change. If needed, it'd be better to accomodate the whole change in the next major version bump.

The devel branch is now migrated to Go modules. Please let me know if you encounter any issues while forking or developing off it.

Thanks for the quick response! Will try to set that up later.

<!-- gh-comment-id:601995463 --> @cayter commented on GitHub (Mar 21, 2020): > Apart from that please check out the other portions of the CONTRIBUTING.md doc, there is also a requirement to sign Google's CLA. Just did. > Please ignore my question about whether we should add an option to enable this behavior. I remembered that we are adding a separate method so we are all good. Yep. Although right now my implementation can be ported over to `Write()` but I guess we need to ensure its backward compatibility so that whoever is using this function doesn't get surprised by this behaviour change. If needed, it'd be better to accomodate the whole change in the next major version bump. > The devel branch is now migrated to Go modules. Please let me know if you encounter any issues while forking or developing off it. Thanks for the quick response! Will try to set that up later.
Author
Owner

@yyewolf commented on GitHub (Apr 24, 2020):

I really would love to see that feature coming ! I would also really need it for my project.

<!-- gh-comment-id:619107996 --> @yyewolf commented on GitHub (Apr 24, 2020): I really would love to see that feature coming ! I would also really need it for my project.
Author
Owner

@mum4k commented on GitHub (Apr 24, 2020):

Thanks for reaching out @yyewolf.

@cayter are you still planning to pull the implementation back to this repository?

<!-- gh-comment-id:619143325 --> @mum4k commented on GitHub (Apr 24, 2020): Thanks for reaching out @yyewolf. @cayter are you still planning to pull the implementation back to this repository?
Author
Owner

@cayter commented on GitHub (Apr 25, 2020):

@mum4k Yes, sorry for the late reply. My full-time job has been crazy in the last 3 weeks due to Covid19 pandemic. Will try to squeeze some time in the next 1 month to move that over. In case anyone got spare time to do it first, you can have a look into the implementation here. The implementation is being used by our internal projects for few weeks and so far seems fine. Again, sorry and thanks.

<!-- gh-comment-id:619330203 --> @cayter commented on GitHub (Apr 25, 2020): @mum4k Yes, sorry for the late reply. My full-time job has been crazy in the last 3 weeks due to Covid19 pandemic. Will try to squeeze some time in the next 1 month to move that over. In case anyone got spare time to do it first, you can have a look into the implementation [here](https://github.com/appist/appy/blob/master/cmd/start.go#L518-L654). The implementation is being used by our internal projects for few weeks and so far seems fine. Again, sorry and thanks.
Author
Owner

@mum4k commented on GitHub (Apr 26, 2020):

There's no rush @cayter, please take your time. I merely wanted to know if this is still on your radar and glad to hear it is.

Thanks for the help!

<!-- gh-comment-id:619556700 --> @mum4k commented on GitHub (Apr 26, 2020): There's no rush @cayter, please take your time. I merely wanted to know if this is still on your radar and glad to hear it is. Thanks for the help!
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/termdash#45
No description provided.