-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathutf8.go
More file actions
103 lines (90 loc) · 2.59 KB
/
Copy pathutf8.go
File metadata and controls
103 lines (90 loc) · 2.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package vt
import (
"unicode/utf8"
uv "github.com/charmbracelet/ultraviolet"
"github.com/charmbracelet/x/ansi"
)
// handlePrint handles printable characters.
func (e *Emulator) handlePrint(r rune) {
if r >= ansi.SP && r < ansi.DEL {
if len(e.grapheme) > 0 {
// If we have a grapheme buffer, flush it before handling the ASCII character.
e.flushGrapheme()
}
e.handleGrapheme(string(r), 1)
} else {
e.grapheme = append(e.grapheme, r)
}
}
// flushGrapheme flushes the current grapheme buffer, if any, and handles the
// grapheme as a single unit.
func (e *Emulator) flushGrapheme() {
if len(e.grapheme) == 0 {
return
}
// XXX: We always use [ansi.GraphemeWidth] here to report accurate widths
// and it's up to the caller to decide how to handle Unicode vs non-Unicode
// modes.
method := ansi.GraphemeWidth
graphemes := string(e.grapheme)
for len(graphemes) > 0 {
cluster, width := ansi.FirstGraphemeCluster(graphemes, method)
e.handleGrapheme(cluster, width)
graphemes = graphemes[len(cluster):]
}
e.grapheme = e.grapheme[:0] // Reset the grapheme buffer.
}
// handleGrapheme handles UTF-8 graphemes.
func (e *Emulator) handleGrapheme(content string, width int) {
awm := e.isModeSet(ansi.ModeAutoWrap)
cell := uv.Cell{
Content: content,
Width: width,
Style: e.scr.cursorPen(),
Link: e.scr.cursorLink(),
}
x, y := e.scr.CursorPosition()
if e.atPhantom && awm {
// moves cursor down similar to [Terminal.linefeed] except it doesn't
// respects [ansi.LNM] mode.
// This will reset the phantom state i.e. pending wrap state.
e.index()
_, y = e.scr.CursorPosition()
x = 0
}
// Handle character set mappings
if len(content) == 1 { //nolint:nestif
var charset CharSet
c := content[0]
if e.gsingle > 1 && e.gsingle < 4 {
charset = e.charsets[e.gsingle]
e.gsingle = 0
} else if c < 128 {
charset = e.charsets[e.gl]
} else {
charset = e.charsets[e.gr]
}
if charset != nil {
if r, ok := charset[c]; ok {
cell.Content = r
cell.Width = 1
}
}
}
if cell.Width == 1 && len(content) == 1 {
e.lastChar, _ = utf8.DecodeRuneInString(content)
}
// IRM (Insert/Replace Mode): when set, insert blank cells before writing
// to shift existing content right. Without this, characters always overwrite.
if e.isModeSet(ansi.ModeInsertReplace) {
e.scr.InsertCell(cell.Width)
}
e.scr.SetCell(x, y, &cell)
// Handle phantom state at the end of the line
e.atPhantom = awm && x >= e.scr.Width()-1
if !e.atPhantom {
x += cell.Width
}
// NOTE: We don't reset the phantom state here, we handle it up above.
e.scr.setCursor(x, y, false)
}