charmbracelet/lipgloss

"nonspacing mark" unicode causes container's width to shift

OnFireByte opened this issue · 2 comments

Describe the bug
Nonspacing mark unicodes are a type of character that doesn't consume space; instead, they "attach" themselves to the previous character. For example, "ี" or "ุ" are nonspacing marks commonly attached to another character like "ด," resulting in "ดี" or "ดุ."

When there are nonspacing mark unicodes inside lipgloss's container, the layout will shift on that line because lipgloss counts these characters as normal unicode that occupy 1 space.

Setup
Please complete the following information along with version numbers, if applicable.

  • macOS
  • zsh
  • iterm2
  • th_TH.UTF-8

To Reproduce
Steps to reproduce the behavior:

  1. Create a style with a fixed width, such as 16 characters.
  2. Render it with a string that has nonspacing mark unicodes, for example, "สวัสดี." ("ั" and "ี" are nonspacing marks)
  3. See the layout shift.

Source Code

package main

import (
	"fmt"

	"github.com/charmbracelet/lipgloss"
)

var box = lipgloss.NewStyle().Width(8).Height(8).Border(lipgloss.RoundedBorder())

func main() {
	fmt.Println(box.Render("สวัสดี"))
}

Screenshots
image

This is a known issue that is pending these pull requests:

Without these being merged, the problem cannot be resolved. For context, this is due to incorrect rune width calculations not handling grapheme clusters (which obviously understand ZWJ).

Confirmed this is fixed with the pull requests I have listed above:
CleanShot 2024-02-16 at 16 23 20