diff --git a/go.mod b/go.mod index 128907f..d45f788 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/kr/pretty v0.3.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.14 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect diff --git a/go.sum b/go.sum index dd35ad8..e33800e 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,8 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/state_model.go b/state_model.go index 3b963ff..e346bb9 100644 --- a/state_model.go +++ b/state_model.go @@ -2,6 +2,7 @@ package main import ( "github.com/gdamore/tcell/v2" + "github.com/mattn/go-runewidth" "github.com/viktomas/godu/commands" "github.com/viktomas/godu/interactive" ) @@ -48,8 +49,17 @@ func (vs visualState) GetCell(x, y int) (rune, tcell.Style, []rune, int) { if line.IsMarked { style = style.Foreground(tcell.ColorGreen) } - if x < len(vs.folders[shiftedIndex].Text) { - return line.Text[x], style, nil, 1 + + visualIndex := 0 + for _, r := range line.Text { + width := runewidth.RuneWidth(r) + if visualIndex == x { + return r, style, nil, width + } + visualIndex += width + if visualIndex > x { + break + } } return ' ', style, nil, 1 } diff --git a/state_model_test.go b/state_model_test.go new file mode 100644 index 0000000..3d307bd --- /dev/null +++ b/state_model_test.go @@ -0,0 +1,47 @@ +package main + +import ( + "testing" + + "github.com/gdamore/tcell/v2" + "github.com/viktomas/godu/interactive" +) + +func TestGetCell(t *testing.T) { + vs := visualState{ + folders: []interactive.Line{ + {Text: []rune("Hello"), IsMarked: false}, + {Text: []rune("你好"), IsMarked: true}, + }, + selected: 1, + screenHeight: 5, + } + + tests := []struct { + x, y int + expectedRune rune + expectedStyle tcell.Style + expectedWidth int + }{ + {0, 0, 'H', tcell.StyleDefault, 1}, + {1, 0, 'e', tcell.StyleDefault, 1}, + {0, 1, '你', tcell.StyleDefault.Reverse(true).Foreground(tcell.ColorGreen), 2}, + {1, 1, ' ', tcell.StyleDefault.Reverse(true).Foreground(tcell.ColorGreen), 1}, + {2, 1, '好', tcell.StyleDefault.Reverse(true).Foreground(tcell.ColorGreen), 2}, + {0, 2, ' ', tcell.StyleDefault, 1}, // out of bounds y + {6, 0, ' ', tcell.StyleDefault, 1}, // out of bounds x + } + + for _, tt := range tests { + r, style, _, width := vs.GetCell(tt.x, tt.y) + if r != tt.expectedRune { + t.Errorf("GetCell(%d, %d) = %c; want %c", tt.x, tt.y, r, tt.expectedRune) + } + if style != tt.expectedStyle { + t.Errorf("GetCell(%d, %d) style = %+v; want %+v", tt.x, tt.y, style, tt.expectedStyle) + } + if width != tt.expectedWidth { + t.Errorf("GetCell(%d, %d) width = %d; want %d", tt.x, tt.y, width, tt.expectedWidth) + } + } +}