m/fzf
1
0
mirror of https://github.com/junegunn/fzf.git synced 2025-11-18 16:45:38 -05:00

Replace RuneWidth to StringWidth to handle grapheme clusters

Fix #2482
This commit is contained in:
Junegunn Choi
2021-05-14 11:43:32 +09:00
parent 4cd621e877
commit 3f75a8369f
6 changed files with 110 additions and 108 deletions

View File

@@ -10,7 +10,8 @@ import (
"time"
"unicode/utf8"
"github.com/junegunn/fzf/src/util"
"github.com/mattn/go-runewidth"
"github.com/rivo/uniseg"
"golang.org/x/term"
)
@@ -50,7 +51,7 @@ func (r *LightRenderer) stderrInternal(str string, allowNLCR bool) {
}
bytes = bytes[sz:]
}
r.queued += string(runes)
r.queued.WriteString(string(runes))
}
func (r *LightRenderer) csi(code string) {
@@ -58,9 +59,9 @@ func (r *LightRenderer) csi(code string) {
}
func (r *LightRenderer) flush() {
if len(r.queued) > 0 {
fmt.Fprint(os.Stderr, r.queued)
r.queued = ""
if r.queued.Len() > 0 {
fmt.Fprint(os.Stderr, r.queued.String())
r.queued.Reset()
}
}
@@ -82,7 +83,7 @@ type LightRenderer struct {
escDelay int
fullscreen bool
upOneLine bool
queued string
queued strings.Builder
y int
x int
maxHeightFunc func(int) int
@@ -889,20 +890,26 @@ func wrapLine(input string, prefixLength int, max int, tabstop int) []wrappedLin
lines := []wrappedLine{}
width := 0
line := ""
for _, r := range input {
w := util.RuneWidth(r, prefixLength+width, 8)
width += w
str := string(r)
if r == '\t' {
gr := uniseg.NewGraphemes(input)
for gr.Next() {
rs := gr.Runes()
str := string(rs)
var w int
if len(rs) == 1 && rs[0] == '\t' {
w = tabstop - (prefixLength+width)%tabstop
str = repeat(' ', w)
} else {
w = runewidth.StringWidth(str)
}
width += w
if prefixLength+width <= max {
line += str
} else {
lines = append(lines, wrappedLine{string(line), width - w})
line = str
prefixLength = 0
width = util.RuneWidth(r, prefixLength, 8)
width = w
}
}
lines = append(lines, wrappedLine{string(line), width})

View File

@@ -5,7 +5,6 @@ package tui
import (
"os"
"time"
"unicode/utf8"
"runtime"
@@ -13,6 +12,7 @@ import (
"github.com/gdamore/tcell/encoding"
"github.com/mattn/go-runewidth"
"github.com/rivo/uniseg"
)
func HasFullscreenRenderer() bool {
@@ -482,7 +482,6 @@ func (w *TcellWindow) Print(text string) {
}
func (w *TcellWindow) printString(text string, pair ColorPair) {
t := text
lx := 0
a := pair.Attr()
@@ -496,33 +495,28 @@ func (w *TcellWindow) printString(text string, pair ColorPair) {
Dim(a&Attr(tcell.AttrDim) != 0)
}
for {
if len(t) == 0 {
break
}
r, size := utf8.DecodeRuneInString(t)
t = t[size:]
gr := uniseg.NewGraphemes(text)
for gr.Next() {
rs := gr.Runes()
if r < rune(' ') { // ignore control characters
continue
}
if r == '\n' {
w.lastY++
lx = 0
} else {
if r == '\u000D' { // skip carriage return
if len(rs) == 1 {
r := rs[0]
if r < rune(' ') { // ignore control characters
continue
} else if r == '\n' {
w.lastY++
lx = 0
continue
} else if r == '\u000D' { // skip carriage return
continue
}
var xPos = w.left + w.lastX + lx
var yPos = w.top + w.lastY
if xPos < (w.left+w.width) && yPos < (w.top+w.height) {
_screen.SetContent(xPos, yPos, r, nil, style)
}
lx += runewidth.RuneWidth(r)
}
var xPos = w.left + w.lastX + lx
var yPos = w.top + w.lastY
if xPos < (w.left+w.width) && yPos < (w.top+w.height) {
_screen.SetContent(xPos, yPos, rs[0], rs[1:], style)
}
lx += runewidth.StringWidth(string(rs))
}
w.lastX += lx
}
@@ -549,30 +543,32 @@ func (w *TcellWindow) fillString(text string, pair ColorPair) FillReturn {
Underline(a&Attr(tcell.AttrUnderline) != 0).
Italic(a&Attr(tcell.AttrItalic) != 0)
for _, r := range text {
if r == '\n' {
gr := uniseg.NewGraphemes(text)
for gr.Next() {
rs := gr.Runes()
if len(rs) == 1 && rs[0] == '\n' {
w.lastY++
w.lastX = 0
lx = 0
} else {
var xPos = w.left + w.lastX + lx
// word wrap:
if xPos >= (w.left + w.width) {
w.lastY++
w.lastX = 0
lx = 0
xPos = w.left
}
var yPos = w.top + w.lastY
if yPos >= (w.top + w.height) {
return FillSuspend
}
_screen.SetContent(xPos, yPos, r, nil, style)
lx += runewidth.RuneWidth(r)
continue
}
// word wrap:
xPos := w.left + w.lastX + lx
if xPos >= (w.left + w.width) {
w.lastY++
w.lastX = 0
lx = 0
xPos = w.left
}
yPos := w.top + w.lastY
if yPos >= (w.top + w.height) {
return FillSuspend
}
_screen.SetContent(xPos, yPos, rs[0], rs[1:], style)
lx += runewidth.StringWidth(string(rs))
}
w.lastX += lx
if w.lastX == w.width {