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

@@ -2,7 +2,6 @@ package fzf
import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"os"
@@ -15,6 +14,9 @@ import (
"syscall"
"time"
"github.com/mattn/go-runewidth"
"github.com/rivo/uniseg"
"github.com/junegunn/fzf/src/tui"
"github.com/junegunn/fzf/src/util"
)
@@ -673,11 +675,8 @@ func (t *Terminal) sortSelected() []selectedItem {
}
func (t *Terminal) displayWidth(runes []rune) int {
l := 0
for _, r := range runes {
l += util.RuneWidth(r, l, t.tabstop)
}
return l
width, _ := util.RunesWidth(runes, 0, t.tabstop, 0)
return width
}
const (
@@ -1141,28 +1140,18 @@ func (t *Terminal) printItem(result Result, line int, i int, current bool) {
t.prevLines[i] = newLine
}
func (t *Terminal) trimRight(runes []rune, width int) ([]rune, int) {
func (t *Terminal) trimRight(runes []rune, width int) ([]rune, bool) {
// We start from the beginning to handle tab characters
l := 0
for idx, r := range runes {
l += util.RuneWidth(r, l, t.tabstop)
if l > width {
return runes[:idx], len(runes) - idx
}
width, overflowIdx := util.RunesWidth(runes, 0, t.tabstop, width)
if overflowIdx >= 0 {
return runes[:overflowIdx], true
}
return runes, 0
return runes, false
}
func (t *Terminal) displayWidthWithLimit(runes []rune, prefixWidth int, limit int) int {
l := 0
for _, r := range runes {
l += util.RuneWidth(r, l+prefixWidth, t.tabstop)
if l > limit {
// Early exit
return l
}
}
return l
width, _ := util.RunesWidth(runes, prefixWidth, t.tabstop, limit)
return width
}
func (t *Terminal) trimLeft(runes []rune, width int) ([]rune, int32) {
@@ -1362,9 +1351,9 @@ func (t *Terminal) renderPreviewText(height int, lines []string, lineNo int, unc
prefixWidth := 0
_, _, ansi = extractColor(line, ansi, func(str string, ansi *ansiState) bool {
trimmed := []rune(str)
trimmedLen := 0
isTrimmed := false
if !t.previewOpts.wrap {
trimmed, trimmedLen = t.trimRight(trimmed, maxWidth-t.pwindow.X())
trimmed, isTrimmed = t.trimRight(trimmed, maxWidth-t.pwindow.X())
}
str, width := t.processTabs(trimmed, prefixWidth)
prefixWidth += width
@@ -1374,7 +1363,7 @@ func (t *Terminal) renderPreviewText(height int, lines []string, lineNo int, unc
} else {
fillRet = t.pwindow.CFill(tui.ColPreview.Fg(), tui.ColPreview.Bg(), tui.AttrRegular, str)
}
return trimmedLen == 0 &&
return !isTrimmed &&
(fillRet == tui.FillContinue || t.previewOpts.wrap && fillRet == tui.FillNextLine)
})
t.previewer.scrollable = t.previewer.scrollable || t.pwindow.Y() == height-1 && t.pwindow.X() == t.pwindow.Width()
@@ -1430,16 +1419,21 @@ func (t *Terminal) printPreviewDelayed() {
}
func (t *Terminal) processTabs(runes []rune, prefixWidth int) (string, int) {
var strbuf bytes.Buffer
var strbuf strings.Builder
l := prefixWidth
for _, r := range runes {
w := util.RuneWidth(r, l, t.tabstop)
l += w
if r == '\t' {
gr := uniseg.NewGraphemes(string(runes))
for gr.Next() {
rs := gr.Runes()
str := string(rs)
var w int
if len(rs) == 1 && rs[0] == '\t' {
w = t.tabstop - l%t.tabstop
strbuf.WriteString(strings.Repeat(" ", w))
} else {
strbuf.WriteRune(r)
w = runewidth.StringWidth(str)
strbuf.WriteString(str)
}
l += w
}
return strbuf.String(), l
}