if len(line) > 0 {
i := 0
for len(line) > 0 {
- r, size := utf8.DecodeRune(line)
+ r, _, size := util.DecodeCharacter(line)
line = line[size:]
i++
}
i := 0
for len(line) > 0 {
- r, size := utf8.DecodeRune(line)
+ r, _, size := util.DecodeCharacter(line)
line = line[size:]
if i == x {
"unicode/utf8"
"github.com/zyedidia/micro/v2/pkg/highlight"
+ "github.com/zyedidia/micro/v2/internal/util"
)
// Finds the byte index of the nth rune in a byte slice
count := 0
i := 0
for len(txt) > 0 {
- _, size := utf8.DecodeRune(txt)
+ _, _, size := util.DecodeCharacter(txt)
txt = txt[size:]
count += size
import (
"strconv"
- "unicode/utf8"
runewidth "github.com/mattn/go-runewidth"
"github.com/zyedidia/micro/v2/internal/buffer"
curStyle := config.DefStyle
var s *tcell.Style
for len(b) > 0 {
- r, size := utf8.DecodeRune(b)
+ r, _, size := util.DecodeCharacter(b)
- curStyle, found := w.getStyle(curStyle, bloc, r)
+ curStyle, found := w.getStyle(curStyle, bloc)
if found {
s = &curStyle
}
return bloc
}
- r, size := utf8.DecodeRune(line)
+ r, _, size := util.DecodeCharacter(line)
draw()
width := 0
// getStyle returns the highlight style for the given character position
// If there is no change to the current highlight style it just returns that
-func (w *BufWindow) getStyle(style tcell.Style, bloc buffer.Loc, r rune) (tcell.Style, bool) {
+func (w *BufWindow) getStyle(style tcell.Style, bloc buffer.Loc) (tcell.Style, bool) {
if group, ok := w.Buf.Match(bloc.Y)[bloc.X]; ok {
s := config.GetColor(group.String())
return s, true
}
bloc.X = bslice
- draw := func(r rune, style tcell.Style, showcursor bool) {
+ draw := func(r rune, combc []rune, style tcell.Style, showcursor bool) {
if nColsBeforeStart <= 0 {
for _, c := range cursors {
if c.HasSelection() &&
}
}
- screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, nil, style)
+ screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, combc, style)
if showcursor {
for _, c := range cursors {
totalwidth := w.StartCol - nColsBeforeStart
for len(line) > 0 {
- r, size := utf8.DecodeRune(line)
- curStyle, _ = w.getStyle(curStyle, bloc, r)
+ r, combc, size := util.DecodeCharacter(line)
- draw(r, curStyle, true)
+ curStyle, _ = w.getStyle(curStyle, bloc)
+
+ draw(r, combc, curStyle, true)
width := 0
// Draw any extra characters either spaces for tabs or @ for incomplete wide runes
if width > 1 {
for i := 1; i < width; i++ {
- draw(char, curStyle, false)
+ draw(char, nil, curStyle, false)
}
}
bloc.X++
}
if vloc.X != bufWidth {
- draw(' ', curStyle, true)
+ draw(' ', nil, curStyle, true)
}
bloc.X = w.StartCol
line, nColsBeforeStart, bslice := util.SliceVisualEnd(line, blocX, tabsize)
blocX = bslice
- draw := func(r rune, style tcell.Style) {
+ draw := func(r rune, combc []rune, style tcell.Style) {
if nColsBeforeStart <= 0 {
bloc := buffer.Loc{X: blocX, Y: 0}
if activeC.HasSelection() &&
c := r
if j > 0 {
c = ' '
+ combc = nil
}
- screen.SetContent(vlocX, i.Y, c, nil, style)
+ screen.SetContent(vlocX, i.Y, c, combc, style)
}
vlocX++
}
for len(line) > 0 {
curVX := vlocX
curBX := blocX
- r, size := utf8.DecodeRune(line)
+ r, combc, size := util.DecodeCharacter(line)
- draw(r, i.defStyle())
+ draw(r, combc, i.defStyle())
width := 0
// Draw any extra characters either spaces for tabs or @ for incomplete wide runes
if width > 1 {
for j := 1; j < width; j++ {
- draw(char, i.defStyle())
+ draw(char, nil, i.defStyle())
}
}
if activeC.X == curBX {
winX := s.win.X
for x := 0; x < s.win.Width; x++ {
if x < leftLen {
- r, size := utf8.DecodeRune(leftText)
+ r, combc, size := util.DecodeCharacter(leftText)
leftText = leftText[size:]
rw := runewidth.RuneWidth(r)
for j := 0; j < rw; j++ {
c := r
if j > 0 {
c = ' '
+ combc = nil
x++
}
- screen.SetContent(winX+x, y, c, nil, statusLineStyle)
+ screen.SetContent(winX+x, y, c, combc, statusLineStyle)
}
} else if x >= s.win.Width-rightLen && x < rightLen+s.win.Width-rightLen {
- r, size := utf8.DecodeRune(rightText)
+ r, combc, size := util.DecodeCharacter(rightText)
rightText = rightText[size:]
rw := runewidth.RuneWidth(r)
for j := 0; j < rw; j++ {
c := r
if j > 0 {
c = ' '
+ combc = nil
x++
}
- screen.SetContent(winX+x, y, c, nil, statusLineStyle)
+ screen.SetContent(winX+x, y, c, combc, statusLineStyle)
}
} else {
screen.SetContent(winX+x, y, ' ', nil, statusLineStyle)
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/shell"
+ "github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/tcell"
"github.com/zyedidia/terminal"
)
textLen := utf8.RuneCount(text)
for x := 0; x < w.Width; x++ {
if x < textLen {
- r, size := utf8.DecodeRune(text)
+ r, combc, size := util.DecodeCharacter(text)
text = text[size:]
- screen.SetContent(w.X+x, w.Y+w.Height, r, nil, statusLineStyle)
+ screen.SetContent(w.X+x, w.Y+w.Height, r, combc, statusLineStyle)
} else {
screen.SetContent(w.X+x, w.Y+w.Height, ' ', nil, statusLineStyle)
}
--- /dev/null
+package util
+
+import (
+ "unicode"
+ "unicode/utf8"
+)
+
+// combining character range table
+var combining = &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {0x0300, 0x036f, 1}, // combining diacritical marks
+ {0x1ab0, 0x1aff, 1}, // combining diacritical marks extended
+ {0x1dc0, 0x1dff, 1}, // combining diacritical marks supplement
+ {0x20d0, 0x20ff, 1}, // combining diacritical marks for symbols
+ {0xfe20, 0xfe2f, 1}, // combining half marks
+ },
+}
+
+
+// DecodeCharacter returns the next character from an array of bytes
+// A character is a rune along with any accompanying combining runes
+func DecodeCharacter(b []byte) (rune, []rune, int) {
+ r, size := utf8.DecodeRune(b)
+ b = b[size:]
+ c, s := utf8.DecodeRune(b)
+
+ var combc []rune
+ for unicode.In(c, combining) {
+ combc = append(combc, c)
+ size += s
+
+ b = b[s:]
+ c, s = utf8.DecodeRune(b)
+ }
+
+ return r, combc, size
+}
+
+// CharacterCount returns the number of characters in a byte array
+// Similar to utf8.RuneCount but for unicode characters
+func CharacterCount(b []byte) int {
+ s := 0
+
+ for len(b) > 0 {
+ r, size := utf8.DecodeRune(b)
+ if !unicode.In(r, combining) {
+ s++
+ }
+
+ b = b[size:]
+ }
+
+ return s
+}
+
+// CharacterCount returns the number of characters in a string
+// Similar to utf8.RuneCountInString but for unicode characters
+func CharacterCountInString(str string) int {
+ s := 0
+
+ for _, r := range str {
+ if !unicode.In(r, combining) {
+ s++
+ }
+ }
+
+ return s
+}
return slc[totalSize:]
}
- _, size := utf8.DecodeRune(slc[totalSize:])
+ _, _, size := DecodeCharacter(slc[totalSize:])
totalSize += size
i++
}
return slc[:totalSize]
}
- _, size := utf8.DecodeRune(slc[totalSize:])
+ _, _, size := DecodeCharacter(slc[totalSize:])
totalSize += size
i++
}
width := 0
i := 0
for len(b) > 0 {
- r, size := utf8.DecodeRune(b)
+ r, _, size := DecodeCharacter(b)
w := 0
switch r {
i := 0
width := 0
for len(b) > 0 {
- r, size := utf8.DecodeRune(b)
+ r, _, size := DecodeCharacter(b)
b = b[size:]
switch r {
// RunePos returns the rune index of a given byte index
// Make sure the byte index is not between code points
func RunePos(b []byte, i int) int {
- return utf8.RuneCount(b[:i])
+ return CharacterCount(b[:i])
}
// MakeRelative will attempt to make a relative path between path and base
func GetLeadingWhitespace(b []byte) []byte {
ws := []byte{}
for len(b) > 0 {
- r, size := utf8.DecodeRune(b)
+ r, _, size := DecodeCharacter(b)
if r == ' ' || r == '\t' {
ws = append(ws, byte(r))
} else {
i := 0 // char pos
width := 0 // string visual width
for len(b) > 0 {
- r, size := utf8.DecodeRune(b)
+ r, _, size := DecodeCharacter(b)
b = b[size:]
switch r {