-
Notifications
You must be signed in to change notification settings - Fork 348
Remove ANSI formatting before measuring string width #462
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for proposing to fix this!
terminal/runereader.go
Outdated
func StringWidth(str string) int { | ||
w := 0 | ||
rs := []rune(str) | ||
cleanedStr := stripansi.Strip(str) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's an overkill to pull in a dependency just to get access to a single regular expression. However, since we're looping to measure the printable string width here, I would be more in favor of an approach like this one: https://github.com/muesli/reflow/blob/v0.3.0/ansi/buffer.go#L20-L40
I'm also not suggesting to import github.com/muesli/reflow/ansi
here, but we could inline the approach of ignoring ANSI escape codes while iterating over a string. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I similarly agree that a whole import for this one line might be too much.. I also really like the iterative approach as it's slightly more readable than the regex too! I'll make these changes in the next day or two!
|
||
// isAnsiTerminator returns if a rune denotes the end of an ANSI sequence | ||
func isAnsiTerminator(r rune) bool { | ||
return (r >= 0x40 && r <= 0x5a) || (r == 0x5e) || (r >= 0x60 && r <= 0x7e) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These terminators were collected from various lists and other examples to match the XTerm Control Sequences.
Thank you! |
@mislav Some wonderful improvements and bug fixes have landed since September's v2.3.6 release. When you have a moment in the coming months, we'd greatly appreciate a fresh release 🙏🏻 |
Summary
Hello! This PR hopes to address an issue in the select prompt where formatted lines may delete the past output on terminals of certain sizes.
The problem
When ANSI escape codes are used in the options of a prompt, these lines are measured wider than what is visibly printed to the terminal. This is caused by the
StringWidth
counting these escaped characters as a part of the actual string, and results in previous output being deleted (if the measured line count is greater than the actual line count of an option).The following can demonstrate this on terminals that are only slightly wider than the option's visible length:
See this in action:
before.mov
Proposed solution
The
github.com/acarl005/stripansi
library offers a nice regex pattern to remove ANSI codes, and when used in theStringWidth
method can remove these codes before the string's width is measured.Additionally, a check for non-printable runes was added to the
runeWidth
method so that only printable characters are measured.unicode.IsPrint
was chosen overunicode.IsGraphic
to conform with the definitions of Go, but I could see a reason for preferringIsGraphic
!With the changes of this PR applied, the following result is achieved:
after.mov