line.go (1839B)
1 package line 2 3 import ( 4 "strings" 5 "unicode/utf8" 6 7 "code.dwrz.net/src/pkg/editor/buffer/glyph" 8 ) 9 10 type Line struct { 11 data string 12 } 13 14 func New(s string) *Line { 15 return &Line{data: s} 16 } 17 18 func (l *Line) DecodeRune(index int) (r rune, size int) { 19 return utf8.DecodeRuneInString(l.data[index:]) 20 } 21 22 // GlyphIndex attempts to find the preceding index for a target glyph. 23 func (l *Line) FindGlyphIndex(target int) (index, g int) { 24 var li, lg int 25 for i, r := range l.data { 26 // We've reached the target. 27 if g == target { 28 29 return i, g 30 } 31 // We've gone too far. 32 // Return the preceding index and glyph. 33 if g > target { 34 return li, lg 35 } 36 37 // Otherwise, we haven't reached the target. 38 // Save this index and glyph. 39 li = i 40 lg = g 41 42 // Then increment the glyph. 43 g += glyph.Width(r) 44 } 45 // Target falls in-between the last glyph and the end of the line. 46 // Use the last glyph. 47 if target >= lg && target < g { 48 return li, lg 49 } 50 51 // We weren't able to find the glyph. 52 // Return the last possible index and glyph. 53 return len(l.data), g 54 } 55 56 func (l *Line) Length() int { 57 return len(l.data) 58 } 59 60 func (l *Line) Render(offset, width int) string { 61 var text = strings.ReplaceAll(l.data, "\t", " ") 62 if offset < 0 || offset > len(text) { 63 return "" 64 } 65 66 var str strings.Builder 67 for _, r := range text { 68 rw := glyph.Width(r) 69 if offset > 0 { 70 offset -= rw 71 continue 72 } 73 if r == utf8.RuneError { 74 continue 75 } 76 77 // Exhausted column zero. 78 if width-rw < -1 { 79 break 80 } 81 82 str.WriteRune(r) 83 width -= rw 84 } 85 86 return str.String() 87 } 88 89 func (l *Line) Runes() []rune { 90 return []rune(l.data) 91 } 92 93 func (l *Line) String() string { 94 return l.data 95 } 96 97 func (l *Line) Width() (count int) { 98 for _, r := range l.data { 99 rw := glyph.Width(r) 100 if r == utf8.RuneError { 101 continue 102 } 103 count += rw 104 } 105 106 return count 107 }