-
Notifications
You must be signed in to change notification settings - Fork 0
/
codearea_render.go
118 lines (103 loc) · 2.85 KB
/
codearea_render.go
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package tk
import (
"github.com/markusbkk/elvish/pkg/cli/term"
"github.com/markusbkk/elvish/pkg/ui"
"github.com/markusbkk/elvish/pkg/wcwidth"
)
// View model, calculated from State and used for rendering.
type view struct {
prompt ui.Text
rprompt ui.Text
code ui.Text
dot int
errors []error
}
var stylingForPending = ui.Underlined
func getView(w *codeArea) *view {
s := w.CopyState()
code, pFrom, pTo := patchPending(s.Buffer, s.Pending)
styledCode, errors := w.Highlighter(code.Content)
if pFrom < pTo {
// Apply stylingForPending to [pFrom, pTo)
parts := styledCode.Partition(pFrom, pTo)
pending := ui.StyleText(parts[1], stylingForPending)
styledCode = ui.Concat(parts[0], pending, parts[2])
}
var rprompt ui.Text
if !s.HideRPrompt {
rprompt = w.RPrompt()
}
return &view{w.Prompt(), rprompt, styledCode, code.Dot, errors}
}
func patchPending(c CodeBuffer, p PendingCode) (CodeBuffer, int, int) {
if p.From > p.To || p.From < 0 || p.To > len(c.Content) {
// Invalid Pending.
return c, 0, 0
}
if p.From == p.To && p.Content == "" {
return c, 0, 0
}
newContent := c.Content[:p.From] + p.Content + c.Content[p.To:]
newDot := 0
switch {
case c.Dot < p.From:
// Dot is before the replaced region. Keep it.
newDot = c.Dot
case c.Dot >= p.From && c.Dot < p.To:
// Dot is within the replaced region. Place the dot at the end.
newDot = p.From + len(p.Content)
case c.Dot >= p.To:
// Dot is after the replaced region. Maintain the relative position of
// the dot.
newDot = c.Dot - (p.To - p.From) + len(p.Content)
}
return CodeBuffer{Content: newContent, Dot: newDot}, p.From, p.From + len(p.Content)
}
func renderView(v *view, buf *term.BufferBuilder) {
buf.EagerWrap = true
buf.WriteStyled(v.prompt)
if len(buf.Lines) == 1 && buf.Col*2 < buf.Width {
buf.Indent = buf.Col
}
parts := v.code.Partition(v.dot)
buf.
WriteStyled(parts[0]).
SetDotHere().
WriteStyled(parts[1])
buf.EagerWrap = false
buf.Indent = 0
// Handle rprompts with newlines.
if rpromptWidth := styledWcswidth(v.rprompt); rpromptWidth > 0 {
padding := buf.Width - buf.Col - rpromptWidth
if padding >= 1 {
buf.WriteSpaces(padding)
buf.WriteStyled(v.rprompt)
}
}
if len(v.errors) > 0 {
for _, err := range v.errors {
buf.Newline()
buf.Write(err.Error())
}
}
}
func truncateToHeight(b *term.Buffer, maxHeight int) {
switch {
case len(b.Lines) <= maxHeight:
// We can show all line; do nothing.
case b.Dot.Line < maxHeight:
// We can show all lines before the cursor, and as many lines after the
// cursor as we can, adding up to maxHeight.
b.TrimToLines(0, maxHeight)
default:
// We can show maxHeight lines before and including the cursor line.
b.TrimToLines(b.Dot.Line-maxHeight+1, b.Dot.Line+1)
}
}
func styledWcswidth(t ui.Text) int {
w := 0
for _, seg := range t {
w += wcwidth.Of(seg.Text)
}
return w
}