Skip to content

Commit

Permalink
Change mattn/go-runewidth dependency to rivo/uniseg for accurate results
Browse files Browse the repository at this point in the history
Related #3588 #3588 #3567
  • Loading branch information
junegunn committed Jan 20, 2024
1 parent 6654620 commit 16f6473
Show file tree
Hide file tree
Showing 11 changed files with 59 additions and 51 deletions.
4 changes: 2 additions & 2 deletions BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ make release
Third-party libraries used
--------------------------

- [mattn/go-runewidth](https://github.com/mattn/go-runewidth)
- Licensed under [MIT](http://mattn.mit-license.org)
- [rivo/uniseg](https://github.com/rivo/uniseg)
- Licensed under [MIT](https://raw.githubusercontent.com/rivo/uniseg/master/LICENSE.txt)
- [mattn/go-shellwords](https://github.com/mattn/go-shellwords)
- Licensed under [MIT](http://mattn.mit-license.org)
- [mattn/go-isatty](https://github.com/mattn/go-isatty)
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ CHANGELOG
' --preview 'seq {} 10000' --preview-window up
```
- And we're phasing out `{fzf:prompt}` and `{fzf:action}`
- Changed [mattn/go-runewidth](https://github.com/mattn/go-runewidth) dependency to [rivo/uniseg](https://github.com/rivo/uniseg) for accurate results
- Set `--ambidouble` if your terminal displays characters with East Asian Width Class Ambiguous (e.g. box-drawing characters for borders) as 2 columns
- Bug fixes
0.45.0
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ module github.com/junegunn/fzf

require (
github.com/gdamore/tcell/v2 v2.5.4
github.com/junegunn/go-runewidth v0.0.15-0.20240119074001-7d2ea235ec54
github.com/junegunn/uniseg v0.0.0-20240120174029-b504da4f6ed2
github.com/mattn/go-isatty v0.0.17
github.com/mattn/go-shellwords v1.0.12
github.com/rivo/uniseg v0.4.4
github.com/saracen/walker v0.1.3
golang.org/x/sys v0.16.0
golang.org/x/term v0.16.0
Expand All @@ -15,6 +14,7 @@ require (
github.com/gdamore/encoding v1.0.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/text v0.5.0 // indirect
)
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdk
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell/v2 v2.5.4 h1:TGU4tSjD3sCL788vFNeJnTdzpNKIw1H5dgLnJRQVv/k=
github.com/gdamore/tcell/v2 v2.5.4/go.mod h1:dZgRy5v4iMobMEcWNYBtREnDZAT9DYmfqIkrgEMxLyw=
github.com/junegunn/go-runewidth v0.0.15-0.20240119074001-7d2ea235ec54 h1:eBbXiL4VPihi5C8DhESYtax8HYfgCDUkzVYigADhkzU=
github.com/junegunn/go-runewidth v0.0.15-0.20240119074001-7d2ea235ec54/go.mod h1:Mq6NazeZhIIQPMFoInCi35AktcN/MuW2elHsDK5N52w=
github.com/junegunn/uniseg v0.0.0-20240120174029-b504da4f6ed2 h1:oEwPBh29BPu1MaTsz2dM9bDrkOgKBoYFC0u6uY2izWo=
github.com/junegunn/uniseg v0.0.0-20240120174029-b504da4f6ed2/go.mod h1:ywqF55XaSE3/uS2tkJqVFKiE0oIYAXRvU2N7DU4y3XQ=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
Expand Down
10 changes: 7 additions & 3 deletions man/man1/fzf.1
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,8 @@ Draw border around the finder
.br

If you use a terminal emulator where each box-drawing character takes
2 columns, try setting \fBRUNEWIDTH_EASTASIAN\fR environment variable to
\fB0\fR or \fB1\fR. If the border is still not properly rendered, set
\fB--no-unicode\fR.
2 columns, try setting \fB--ambidouble\fR. If the border is still not properly
rendered, set \fB--no-unicode\fR.

.TP
.BI "--border-label" [=LABEL]
Expand Down Expand Up @@ -313,6 +312,11 @@ the label. Label is printed on the top border line by default, add
Use ASCII characters instead of Unicode drawing characters to draw borders,
the spinner and the horizontal separator.

.TP
.B "--ambidouble"
Set this option if your terminal displays characters with East Asian Width
Class Ambiguous (e.g. box-drawing characters for borders) as 2 columns.

.TP
.BI "--margin=" MARGIN
Comma-separated expression for margins around the finder.
Expand Down
42 changes: 21 additions & 21 deletions src/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
"github.com/junegunn/fzf/src/algo"
"github.com/junegunn/fzf/src/tui"
"github.com/junegunn/fzf/src/util"
"github.com/junegunn/uniseg"

"github.com/junegunn/go-runewidth"
"github.com/mattn/go-shellwords"
)

Expand Down Expand Up @@ -337,6 +337,7 @@ type Options struct {
BorderLabel labelOpts
PreviewLabel labelOpts
Unicode bool
Ambidouble bool
Tabstop int
ListenAddr *listenAddress
Unsafe bool
Expand Down Expand Up @@ -406,6 +407,7 @@ func defaultOptions() *Options {
Margin: defaultMargin(),
Padding: defaultMargin(),
Unicode: true,
Ambidouble: os.Getenv("RUNEWIDTH_EASTASIAN") == "1",
Tabstop: 8,
BorderLabel: labelOpts{},
PreviewLabel: labelOpts{},
Expand Down Expand Up @@ -1593,8 +1595,6 @@ func parseOptions(opts *Options, allArgs []string) {
}
}
validateJumpLabels := false
validatePointer := false
validateMarker := false
for i := 0; i < len(allArgs); i++ {
arg := allArgs[i]
switch arg {
Expand Down Expand Up @@ -1774,10 +1774,8 @@ func parseOptions(opts *Options, allArgs []string) {
opts.Prompt = nextString(allArgs, &i, "prompt string required")
case "--pointer":
opts.Pointer = firstLine(nextString(allArgs, &i, "pointer sign string required"))
validatePointer = true
case "--marker":
opts.Marker = firstLine(nextString(allArgs, &i, "selected sign string required"))
validateMarker = true
case "--sync":
opts.Sync = true
case "--no-sync":
Expand Down Expand Up @@ -1845,6 +1843,10 @@ func parseOptions(opts *Options, allArgs []string) {
opts.Unicode = false
case "--unicode":
opts.Unicode = true
case "--ambidouble":
opts.Ambidouble = true
case "--no-ambidouble":
opts.Ambidouble = false
case "--margin":
opts.Margin = parseMargin(
"margin",
Expand Down Expand Up @@ -1903,10 +1905,8 @@ func parseOptions(opts *Options, allArgs []string) {
opts.Prompt = value
} else if match, value := optString(arg, "--pointer="); match {
opts.Pointer = firstLine(value)
validatePointer = true
} else if match, value := optString(arg, "--marker="); match {
opts.Marker = firstLine(value)
validateMarker = true
} else if match, value := optString(arg, "-n", "--nth="); match {
opts.Nth = splitNth(value)
} else if match, value := optString(arg, "--with-nth="); match {
Expand Down Expand Up @@ -2013,31 +2013,31 @@ func parseOptions(opts *Options, allArgs []string) {
}
}
}

if validatePointer {
if err := validateSign(opts.Pointer, "pointer"); err != nil {
errorExit(err.Error())
}
}

if validateMarker {
if err := validateSign(opts.Marker, "marker"); err != nil {
errorExit(err.Error())
}
}
}

func validateSign(sign string, signOptName string) error {
if sign == "" {
return fmt.Errorf("%v cannot be empty", signOptName)
}
if runewidth.StringWidth(sign) > 2 {
if uniseg.StringWidth(sign) > 2 {
return fmt.Errorf("%v display width should be up to 2", signOptName)
}
return nil
}

func postProcessOptions(opts *Options) {
if opts.Ambidouble {
uniseg.EastAsianAmbiguousWidth = 2
}

if err := validateSign(opts.Pointer, "pointer"); err != nil {
errorExit(err.Error())
}

if err := validateSign(opts.Marker, "marker"); err != nil {
errorExit(err.Error())
}

if !opts.Version && !tui.IsLightRendererSupported() && opts.Height.size > 0 {
errorExit("--height option is currently not supported on this platform")
}
Expand All @@ -2048,7 +2048,7 @@ func postProcessOptions(opts *Options) {
errorExit("--scrollbar should be given one or two characters")
}
for _, r := range runes {
if runewidth.RuneWidth(r) != 1 {
if uniseg.StringWidth(string(r)) != 1 {
errorExit("scrollbar display width should be 1")
}
}
Expand Down
5 changes: 2 additions & 3 deletions src/terminal.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ import (
"syscall"
"time"

"github.com/junegunn/go-runewidth"
"github.com/rivo/uniseg"
"github.com/junegunn/uniseg"

"github.com/junegunn/fzf/src/tui"
"github.com/junegunn/fzf/src/util"
Expand Down Expand Up @@ -798,7 +797,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
t.separator, t.separatorLen = t.ansiLabelPrinter(bar, &tui.ColSeparator, true)
}
if t.unicode {
t.borderWidth = runewidth.RuneWidth('│')
t.borderWidth = uniseg.StringWidth("│")
}
if opts.Scrollbar == nil {
if t.unicode && t.borderWidth == 1 {
Expand Down
15 changes: 7 additions & 8 deletions src/tui/light.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import (
"time"
"unicode/utf8"

"github.com/junegunn/go-runewidth"
"github.com/rivo/uniseg"
"github.com/junegunn/uniseg"

"golang.org/x/term"
)
Expand Down Expand Up @@ -804,7 +803,7 @@ func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
if w.preview {
color = ColPreviewBorder
}
hw := runewidth.RuneWidth(w.border.top)
hw := runeWidth(w.border.top)
if top {
w.Move(0, 0)
w.CPrint(color, repeat(w.border.top, w.width/hw))
Expand Down Expand Up @@ -842,13 +841,13 @@ func (w *LightWindow) drawBorderAround(onlyHorizontal bool) {
if w.preview {
color = ColPreviewBorder
}
hw := runewidth.RuneWidth(w.border.top)
tcw := runewidth.RuneWidth(w.border.topLeft) + runewidth.RuneWidth(w.border.topRight)
bcw := runewidth.RuneWidth(w.border.bottomLeft) + runewidth.RuneWidth(w.border.bottomRight)
hw := runeWidth(w.border.top)
tcw := runeWidth(w.border.topLeft) + runeWidth(w.border.topRight)
bcw := runeWidth(w.border.bottomLeft) + runeWidth(w.border.bottomRight)
rem := (w.width - tcw) % hw
w.CPrint(color, string(w.border.topLeft)+repeat(w.border.top, (w.width-tcw)/hw)+repeat(' ', rem)+string(w.border.topRight))
if !onlyHorizontal {
vw := runewidth.RuneWidth(w.border.left)
vw := runeWidth(w.border.left)
for y := 1; y < w.height-1; y++ {
w.Move(y, 0)
w.CPrint(color, string(w.border.left))
Expand Down Expand Up @@ -1020,7 +1019,7 @@ func wrapLine(input string, prefixLength int, max int, tabstop int) []wrappedLin
} else if rs[0] == '\r' {
w++
} else {
w = runewidth.StringWidth(str)
w = uniseg.StringWidth(str)
}
width += w

Expand Down
11 changes: 5 additions & 6 deletions src/tui/tcell.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import (
"github.com/gdamore/tcell/v2/encoding"
"github.com/junegunn/fzf/src/util"

"github.com/junegunn/go-runewidth"
"github.com/rivo/uniseg"
"github.com/junegunn/uniseg"
)

func HasFullscreenRenderer() bool {
Expand Down Expand Up @@ -738,7 +737,7 @@ func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
style = w.normal.style()
}

hw := runewidth.RuneWidth(w.borderStyle.top)
hw := runeWidth(w.borderStyle.top)
switch shape {
case BorderRounded, BorderSharp, BorderBold, BorderBlock, BorderThinBlock, BorderDouble, BorderHorizontal, BorderTop:
max := right - 2*hw
Expand Down Expand Up @@ -773,7 +772,7 @@ func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
}
switch shape {
case BorderRounded, BorderSharp, BorderBold, BorderBlock, BorderThinBlock, BorderDouble, BorderVertical, BorderRight:
vw := runewidth.RuneWidth(w.borderStyle.right)
vw := runeWidth(w.borderStyle.right)
for y := top; y < bot; y++ {
_screen.SetContent(right-vw, y, w.borderStyle.right, nil, style)
}
Expand All @@ -782,8 +781,8 @@ func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
switch shape {
case BorderRounded, BorderSharp, BorderBold, BorderBlock, BorderThinBlock, BorderDouble:
_screen.SetContent(left, top, w.borderStyle.topLeft, nil, style)
_screen.SetContent(right-runewidth.RuneWidth(w.borderStyle.topRight), top, w.borderStyle.topRight, nil, style)
_screen.SetContent(right-runeWidth(w.borderStyle.topRight), top, w.borderStyle.topRight, nil, style)
_screen.SetContent(left, bot-1, w.borderStyle.bottomLeft, nil, style)
_screen.SetContent(right-runewidth.RuneWidth(w.borderStyle.bottomRight), bot-1, w.borderStyle.bottomRight, nil, style)
_screen.SetContent(right-runeWidth(w.borderStyle.bottomRight), bot-1, w.borderStyle.bottomRight, nil, style)
}
}
6 changes: 6 additions & 0 deletions src/tui/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"os"
"strconv"
"time"

"github.com/junegunn/uniseg"
)

// Types of user action
Expand Down Expand Up @@ -812,3 +814,7 @@ func initPalette(theme *ColorTheme) {
ColPreviewScrollbar = pair(theme.PreviewScrollbar, theme.PreviewBg)
ColPreviewSpinner = pair(theme.Spinner, theme.PreviewBg)
}

func runeWidth(r rune) int {
return uniseg.StringWidth(string(r))
}
7 changes: 3 additions & 4 deletions src/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ import (
"strings"
"time"

"github.com/junegunn/go-runewidth"
"github.com/junegunn/uniseg"
"github.com/mattn/go-isatty"
"github.com/rivo/uniseg"
)

// StringWidth returns string width where each CR/LF character takes 1 column
func StringWidth(s string) int {
return runewidth.StringWidth(s) + strings.Count(s, "\n") + strings.Count(s, "\r")
return uniseg.StringWidth(s) + strings.Count(s, "\n") + strings.Count(s, "\r")
}

// RunesWidth returns runes width
Expand Down Expand Up @@ -165,7 +164,7 @@ func RepeatToFill(str string, length int, limit int) string {
output := strings.Repeat(str, times)
if rest > 0 {
for _, r := range str {
rest -= runewidth.RuneWidth(r)
rest -= uniseg.StringWidth(string(r))
if rest < 0 {
break
}
Expand Down

0 comments on commit 16f6473

Please sign in to comment.