Fix fullwidth rune handling.
This commit is contained in:
parent
f3b451c887
commit
84a4bbd440
24
src/utils/runes.go
Normal file
24
src/utils/runes.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
rw "github.com/mattn/go-runewidth"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TruncateFront(s string, w int, prefix string) string {
|
||||||
|
if rw.StringWidth(s) <= w {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
r := []rune(s)
|
||||||
|
pw := rw.StringWidth(prefix)
|
||||||
|
w -= pw
|
||||||
|
width := 0
|
||||||
|
i := len(r) - 1
|
||||||
|
for ; i >= 0; i-- {
|
||||||
|
cw := rw.RuneWidth(r[i])
|
||||||
|
width += cw
|
||||||
|
if width > w {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prefix + string(r[i+1:len(r)])
|
||||||
|
}
|
50
src/utils/runes_test.go
Normal file
50
src/utils/runes_test.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
const (
|
||||||
|
ELLIPSIS = "…"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTruncateFront(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
s string
|
||||||
|
w int
|
||||||
|
prefix string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"", 0, ELLIPSIS, ""},
|
||||||
|
{"", 1, ELLIPSIS, ""},
|
||||||
|
{"", 10, ELLIPSIS, ""},
|
||||||
|
|
||||||
|
{"abcdef", 0, ELLIPSIS, ELLIPSIS},
|
||||||
|
{"abcdef", 1, ELLIPSIS, ELLIPSIS},
|
||||||
|
{"abcdef", 2, ELLIPSIS, ELLIPSIS + "f"},
|
||||||
|
{"abcdef", 5, ELLIPSIS, ELLIPSIS + "cdef"},
|
||||||
|
{"abcdef", 6, ELLIPSIS, "abcdef"},
|
||||||
|
{"abcdef", 10, ELLIPSIS, "abcdef"},
|
||||||
|
|
||||||
|
{"abcdef", 0, "...", "..."},
|
||||||
|
{"abcdef", 1, "...", "..."},
|
||||||
|
{"abcdef", 3, "...", "..."},
|
||||||
|
{"abcdef", 4, "...", "...f"},
|
||||||
|
{"abcdef", 5, "...", "...ef"},
|
||||||
|
{"abcdef", 6, "...", "abcdef"},
|
||||||
|
{"abcdef", 10, "...", "abcdef"},
|
||||||
|
|
||||||
|
{"⦅full~width⦆", 15, ".", "⦅full~width⦆"},
|
||||||
|
{"⦅full~width⦆", 14, ".", ".full~width⦆"},
|
||||||
|
{"⦅full~width⦆", 13, ".", ".ull~width⦆"},
|
||||||
|
{"⦅full~width⦆", 10, ".", ".~width⦆"},
|
||||||
|
{"⦅full~width⦆", 9, ".", ".width⦆"},
|
||||||
|
{"⦅full~width⦆", 8, ".", ".width⦆"},
|
||||||
|
{"⦅full~width⦆", 3, ".", ".⦆"},
|
||||||
|
{"⦅full~width⦆", 2, ".", "."},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
if got := TruncateFront(test.s, test.w, test.prefix); got != test.want {
|
||||||
|
t.Errorf("TruncateFront(%q, %d, %q) = %q; want %q", test.s, test.w, test.prefix, got, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ import (
|
||||||
ui "github.com/cjbassi/gotop/src/termui"
|
ui "github.com/cjbassi/gotop/src/termui"
|
||||||
"github.com/cjbassi/gotop/src/utils"
|
"github.com/cjbassi/gotop/src/utils"
|
||||||
tui "github.com/gizak/termui/v3"
|
tui "github.com/gizak/termui/v3"
|
||||||
|
rw "github.com/mattn/go-runewidth"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -123,7 +124,8 @@ func (self *ProcWidget) HandleEvent(e tui.Event) bool {
|
||||||
self.SetEditingFilter(false)
|
self.SetEditingFilter(false)
|
||||||
case "<Backspace>":
|
case "<Backspace>":
|
||||||
if self.filter != "" {
|
if self.filter != "" {
|
||||||
self.filter = self.filter[:len(self.filter)-1]
|
r := []rune(self.filter)
|
||||||
|
self.filter = string(r[:len(r)-1])
|
||||||
self.update()
|
self.update()
|
||||||
}
|
}
|
||||||
case "<Space>":
|
case "<Space>":
|
||||||
|
@ -143,6 +145,8 @@ func (self *ProcWidget) Draw(buf *tui.Buffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ProcWidget) drawFilter(buf *tui.Buffer) {
|
func (self *ProcWidget) drawFilter(buf *tui.Buffer) {
|
||||||
|
padding := 2
|
||||||
|
|
||||||
style := self.TitleStyle
|
style := self.TitleStyle
|
||||||
label := " Filter: "
|
label := " Filter: "
|
||||||
if self.editingFilter {
|
if self.editingFilter {
|
||||||
|
@ -151,24 +155,34 @@ func (self *ProcWidget) drawFilter(buf *tui.Buffer) {
|
||||||
}
|
}
|
||||||
cursorStyle := tui.NewStyle(style.Bg, style.Fg, tui.ModifierClear)
|
cursorStyle := tui.NewStyle(style.Bg, style.Fg, tui.ModifierClear)
|
||||||
|
|
||||||
p := image.Pt(self.Min.X+2, self.Max.Y-1)
|
p := image.Pt(self.Min.X+padding, self.Max.Y-1)
|
||||||
buf.SetString(label, style, p)
|
buf.SetString(label, style, p)
|
||||||
p.X += utf8.RuneCountInString(label)
|
p.X += rw.StringWidth(label)
|
||||||
|
|
||||||
maxLen := self.Max.X - p.X - 5
|
tail := " "
|
||||||
filter := self.filter
|
if self.editingFilter {
|
||||||
if l := utf8.RuneCountInString(filter); l > maxLen {
|
tail = "] "
|
||||||
filter = ELLIPSIS + filter[l-maxLen+1:]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maxLen := self.Max.X - p.X - padding - rw.StringWidth(tail)
|
||||||
|
if self.editingFilter {
|
||||||
|
maxLen -= 1 // for cursor
|
||||||
|
}
|
||||||
|
|
||||||
|
filter := utils.TruncateFront(self.filter, maxLen, ELLIPSIS)
|
||||||
buf.SetString(filter, self.TitleStyle, p)
|
buf.SetString(filter, self.TitleStyle, p)
|
||||||
p.X += utf8.RuneCountInString(filter)
|
p.X += rw.StringWidth(filter)
|
||||||
|
|
||||||
if self.editingFilter {
|
if self.editingFilter {
|
||||||
buf.SetString(CURSOR, cursorStyle, p)
|
buf.SetString(CURSOR, cursorStyle, p)
|
||||||
p.X += 1
|
p.X += rw.StringWidth(CURSOR)
|
||||||
remaining := self.Max.X - 2 - p.X
|
|
||||||
buf.SetString(fmt.Sprintf("%*s", remaining, "] "), style, p)
|
if remaining := maxLen - rw.StringWidth(filter); remaining > 0 {
|
||||||
|
buf.SetString(strings.Repeat(" ", remaining), self.TitleStyle, p)
|
||||||
|
p.X += remaining
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
buf.SetString(tail, style, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ProcWidget) filterProcs(procs []Proc) []Proc {
|
func (self *ProcWidget) filterProcs(procs []Proc) []Proc {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user