summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Mattern <rephorm@rephorm.com>2019-06-04 14:21:13 -0700
committerBrian Mattern <rephorm@rephorm.com>2019-06-04 14:25:35 -0700
commit84a4bbd440f665a42d8192a8da21427c672032ec (patch)
treeb63098f0d9c6fb506c773a03fc94a2e0ebee44f3
parentf3b451c887ed8c9d50c750e6c4cc603cd87a0ccd (diff)
Fix fullwidth rune handling.
-rw-r--r--src/utils/runes.go24
-rw-r--r--src/utils/runes_test.go50
-rw-r--r--src/widgets/proc.go36
3 files changed, 99 insertions, 11 deletions
diff --git a/src/utils/runes.go b/src/utils/runes.go
new file mode 100644
index 0000000..76cb5e3
--- /dev/null
+++ b/src/utils/runes.go
@@ -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)])
+}
diff --git a/src/utils/runes_test.go b/src/utils/runes_test.go
new file mode 100644
index 0000000..67ceefc
--- /dev/null
+++ b/src/utils/runes_test.go
@@ -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)
+ }
+ }
+}
diff --git a/src/widgets/proc.go b/src/widgets/proc.go
index 3a1c61e..a598498 100644
--- a/src/widgets/proc.go
+++ b/src/widgets/proc.go
@@ -16,6 +16,7 @@ import (
ui "github.com/cjbassi/gotop/src/termui"
"github.com/cjbassi/gotop/src/utils"
tui "github.com/gizak/termui/v3"
+ rw "github.com/mattn/go-runewidth"
)
const (
@@ -123,7 +124,8 @@ func (self *ProcWidget) HandleEvent(e tui.Event) bool {
self.SetEditingFilter(false)
case "<Backspace>":
if self.filter != "" {
- self.filter = self.filter[:len(self.filter)-1]
+ r := []rune(self.filter)
+ self.filter = string(r[:len(r)-1])
self.update()
}
case "<Space>":
@@ -143,6 +145,8 @@ func (self *ProcWidget) Draw(buf *tui.Buffer) {
}
func (self *ProcWidget) drawFilter(buf *tui.Buffer) {
+ padding := 2
+
style := self.TitleStyle
label := " Filter: "
if self.editingFilter {
@@ -151,24 +155,34 @@ func (self *ProcWidget) drawFilter(buf *tui.Buffer) {
}
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)
- p.X += utf8.RuneCountInString(label)
+ p.X += rw.StringWidth(label)
+
+ tail := " "
+ if self.editingFilter {
+ tail = "] "
+ }
- maxLen := self.Max.X - p.X - 5
- filter := self.filter
- if l := utf8.RuneCountInString(filter); l > maxLen {
- 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)
- p.X += utf8.RuneCountInString(filter)
+ p.X += rw.StringWidth(filter)
if self.editingFilter {
buf.SetString(CURSOR, cursorStyle, p)
- p.X += 1
- remaining := self.Max.X - 2 - p.X
- buf.SetString(fmt.Sprintf("%*s", remaining, "] "), style, p)
+ p.X += rw.StringWidth(CURSOR)
+
+ 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 {