diff --git a/ui/buffer/rope.go b/ui/buffer/rope.go index dd873fe..7cdbeac 100755 --- a/ui/buffer/rope.go +++ b/ui/buffer/rope.go @@ -36,17 +36,12 @@ func (b *RopeBuffer) LineColToPos(line, col int) int { l.EachLeaf(func(n *ropes.Node) bool { data := n.Value() // Reference; not a copy. - var i int - for i < len(data) { - if col == 0 || data[i] == '\n' { + for _, r := range string(data) { + if col == 0 || r == '\n' { return true // Found the position of the column } pos++ col-- - - // Respect Utf-8 codepoint boundaries - _, size := utf8.DecodeRune(data[i:]) - i += size } return false // Have not gotten to the appropriate position, yet }) @@ -68,11 +63,11 @@ func (b *RopeBuffer) Line(line int) []byte { var isCRLF bool // true if the last byte was '\r' l.EachLeaf(func(n *ropes.Node) bool { data := n.Value() // Reference; not a copy. - var i int - for i < len(data) { - if data[i] == '\r' { + for i, r := range string(data) { + if r == '\r' { isCRLF = true - } else if data[i] == '\n' { + } else if r == '\n' { + bytes += i // Add bytes before i if isCRLF { bytes += 2 // Add the CRLF bytes } else { @@ -82,12 +77,8 @@ func (b *RopeBuffer) Line(line int) []byte { } else { isCRLF = false } - - // Respect Utf-8 codepoint boundaries - _, size := utf8.DecodeRune(data[i:]) - bytes += size - i += size } + bytes += len(data) return false // Have not read the whole line, yet }) @@ -198,26 +189,13 @@ func (b *RopeBuffer) RunesInLineWithDelim(line int) int { _, r := b.rope.SplitAt(linePos) l, _ := r.SplitAt(ropeLen - linePos) - var isCRLF bool l.EachLeaf(func(n *ropes.Node) bool { data := n.Value() // Reference; not a copy. - var i int - for i < len(data) { + for _, r := range string(data) { count++ // Before: we count the line delimiter - if data[i] == '\r' { - isCRLF = true - } else if data[i] == '\n' { + if r == '\n' { return true // Read (past-tense) the whole line - } else { - if isCRLF { - isCRLF = false - count++ // Add the '\r' we previously thought was part of the delim. - } } - - // Respect Utf-8 codepoint boundaries - _, size := utf8.DecodeRune(data[i:]) - i += size } return false // Have not read the whole line, yet }) @@ -244,11 +222,10 @@ func (b *RopeBuffer) RunesInLine(line int) int { var isCRLF bool l.EachLeaf(func(n *ropes.Node) bool { data := n.Value() // Reference; not a copy. - var i int - for i < len(data) { - if data[i] == '\r' { + for _, r := range string(data) { + if r == '\r' { isCRLF = true - } else if data[i] == '\n' { + } else if r == '\n' { return true // Read (past-tense) the whole line } else { if isCRLF { @@ -257,10 +234,6 @@ func (b *RopeBuffer) RunesInLine(line int) int { } } count++ - - // Respect Utf-8 codepoint boundaries - _, size := utf8.DecodeRune(data[i:]) - i += size } return false // Have not read the whole line, yet }) diff --git a/ui/drawfunctions.go b/ui/drawfunctions.go index 8b86e6b..42b927e 100644 --- a/ui/drawfunctions.go +++ b/ui/drawfunctions.go @@ -1,9 +1,8 @@ package ui import ( - "unicode/utf8" - "github.com/gdamore/tcell/v2" + "github.com/mattn/go-runewidth" ) // DrawRect renders a filled box at `x` and `y`, of size `width` and `height`. @@ -16,22 +15,20 @@ func DrawRect(s tcell.Screen, x, y, width, height int, char rune, style tcell.St } } -// DrawStr will render each character of a string at `x` and `y`. -func DrawStr(s tcell.Screen, x, y int, str string, style tcell.Style) { +// DrawStr will render each character of a string at `x` and `y`. Returned is +// the number of columns that were drawn to the screen. +func DrawStr(s tcell.Screen, x, y int, str string, style tcell.Style) int { var col int - bytes := []byte(str) - var i int - for i < len(bytes) { - r, size := utf8.DecodeRune(bytes[i:]) + for _, r := range str { if r == '\n' { col = 0 y++ } else { s.SetContent(x+col, y, r, nil, style) } - i += size - col++ + col += runewidth.RuneWidth(r) } + return col } // DrawQuickCharStr renders a string very similar to how DrawStr works, but stylizes the @@ -41,20 +38,17 @@ func DrawQuickCharStr(s tcell.Screen, x, y int, str string, quickCharIdx int, st var col int var runeIdx int - bytes := []byte(str) - for i := 0; i < len(bytes); runeIdx++ { // i is a byte index - r, size := utf8.DecodeRune(bytes[i:]) - + for _, r := range str { sty := style if runeIdx == quickCharIdx { sty = style.Underline(true) } s.SetContent(x+col, y, r, nil, sty) - i += size - col++ + runeIdx++ + col += runewidth.RuneWidth(r) } - return col // TODO: use mattn/runewidth + return col } // DrawRectOutline draws only the outline of a rectangle, using `ul`, `ur`, `bl`, and `br` diff --git a/ui/textedit.go b/ui/textedit.go index 653eb13..9d9adaf 100755 --- a/ui/textedit.go +++ b/ui/textedit.go @@ -476,7 +476,6 @@ func (t *TextEdit) Draw(s tcell.Screen) { col += runewidth.RuneWidth(r) - // Understanding the tab simulation is unnecessary; just know that it works. byteIdx += size runeIdx++ }