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

Issue listing is misaligned when labels contain emoji #1454

Closed
jennybc opened this issue Jul 29, 2020 · 2 comments · Fixed by #1677
Closed

Issue listing is misaligned when labels contain emoji #1454

jennybc opened this issue Jul 29, 2020 · 2 comments · Fixed by #1677
Labels
bug Something isn't working help wanted Contributions welcome

Comments

@jennybc
Copy link

jennybc commented Jul 29, 2020

gh version 0.11.1 (2020-07-28)
https://github.com/cli/cli/releases/tag/v0.11.1

The columnar alignment / spacing of gh issue list is off when the labels contain emoji.

Screen Shot 2020-07-29 at 4 24 22 PM

@jennybc jennybc added the bug Something isn't working label Jul 29, 2020
@mislav
Copy link
Contributor

mislav commented Jul 30, 2020

Thank you for reporting!

I think this is because our text.DisplayWidth() helper only recognizes Unicode characters for written languages that take a double-width space

// DisplayWidth calculates what the rendered width of a string may be
func DisplayWidth(s string) int {
w := 0
for _, r := range s {
w += runeDisplayWidth(r)
}
return w
}

but does not recognize that all emoji (including ones that are a multibyte sequence) are always presented as a single-space character. We would need to teach the helper to recognize emoji characters.

@mislav mislav added the help wanted Contributions welcome label Jul 30, 2020
@bpsagar
Copy link

bpsagar commented Aug 2, 2020

This issue is a bit tricky, I did some digging. It is caused mostly by the East Asian Ambiguous characters and a few characters that are made up of multiple runes.

Example code (using the same DisplayWidth from the codebase):

package main

import (
    "fmt"
    "golang.org/x/text/width"
)

func DisplayWidth(s string) int {
	w := 0
	for _, r := range s {
		w += runeDisplayWidth(r)
	}
	return w
}

func runeDisplayWidth(r rune) int {
	switch width.LookupRune(r).Kind() {
        case width.EastAsianWide, width.EastAsianFullwidth, width.EastAsianAmbiguous:
            return 2
        default:
            return 1
	}
}

func main() {
    string1 := "♂️"
    string2 := "“"

    fmt.Printf("%2v (%v): %v\n", string1, []rune(string1), DisplayWidth(string1))
    fmt.Printf("%2v (%v): %v\n", string2, []rune(string2), DisplayWidth(string2))
}

Output:

♂️ ([9794 65039]): 4
 “ ([8220]): 2

While returning 1 for EastAsianAmbiguous case fixes a few cases (like string2 in the sample code) in the runeDisplayWidth function, it doesn't solve all the cases (like string1 which is made up of multiple runes)

cli/pkg/text/truncate.go

Lines 56 to 63 in 88eaa28

func runeDisplayWidth(r rune) int {
switch width.LookupRune(r).Kind() {
case width.EastAsianWide, width.EastAsianAmbiguous, width.EastAsianFullwidth:
return 2
default:
return 1
}
}

I searched around on the web and there doesn't seem to be any fool proof solution for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Contributions welcome
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants