Skip to content

Commit f4eab6e

Browse files
committed
Add more style options
1 parent 5025ab6 commit f4eab6e

File tree

4 files changed

+230
-36
lines changed

4 files changed

+230
-36
lines changed

examples/colorful.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
{
9191
"call": "styled",
9292
"foreground": "#FF00FF",
93+
"bold": true,
9394
"content": {
9495
"call": "text",
9596
"content": " » "

internal/builtins/renderables.go

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,46 @@ func styledFunction(_ *eval.Evaluator, args map[string]eval.Value) (result eval.
123123
style.Background = optional.FromValue(color)
124124
}
125125

126+
if boldValue, ok := args["bold"]; ok {
127+
var value eval.Bool
128+
if value, err = eval.AsBool(boldValue); err != nil {
129+
return
130+
}
131+
style.Bold = optional.FromValue(value.Value)
132+
}
133+
134+
if dimValue, ok := args["dim"]; ok {
135+
var value eval.Bool
136+
if value, err = eval.AsBool(dimValue); err != nil {
137+
return
138+
}
139+
style.Dim = optional.FromValue(value.Value)
140+
}
141+
142+
if italicValue, ok := args["italic"]; ok {
143+
var value eval.Bool
144+
if value, err = eval.AsBool(italicValue); err != nil {
145+
return
146+
}
147+
style.Italic = optional.FromValue(value.Value)
148+
}
149+
150+
if underlineValue, ok := args["underline"]; ok {
151+
var value eval.Bool
152+
if value, err = eval.AsBool(underlineValue); err != nil {
153+
return
154+
}
155+
style.Underline = optional.FromValue(value.Value)
156+
}
157+
158+
if blinkValue, ok := args["blink"]; ok {
159+
var value eval.Bool
160+
if value, err = eval.AsBool(blinkValue); err != nil {
161+
return
162+
}
163+
style.Blink = optional.FromValue(value.Value)
164+
}
165+
126166
var contentsRenderable eval.Renderable
127167
if contentsRenderable, err = eval.AsRenderable(contentValue); err != nil {
128168
return
@@ -133,12 +173,18 @@ func styledFunction(_ *eval.Evaluator, args map[string]eval.Value) (result eval.
133173
}
134174

135175
var Styled = Builtin{
136-
Name: "styled",
176+
Name: "styled",
177+
Description: "Applies the specified style to the content.",
137178
Function: eval.BuiltinFunction{
138179
Parameters: []eval.ParameterSpec{
139180
{Name: "content"},
140181
{Name: "foreground", Optional: true},
141182
{Name: "background", Optional: true},
183+
{Name: "bold", Optional: true},
184+
{Name: "dim", Optional: true},
185+
{Name: "italic", Optional: true},
186+
{Name: "underline", Optional: true},
187+
{Name: "blink", Optional: true},
142188
},
143189
Function: styledFunction,
144190
},

internal/render/style.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,9 @@ import (
77
type Style struct {
88
Foreground optional.Optional[Color]
99
Background optional.Optional[Color]
10+
Bold optional.Optional[bool]
11+
Dim optional.Optional[bool]
12+
Italic optional.Optional[bool]
13+
Underline optional.Optional[bool]
14+
Blink optional.Optional[bool]
1015
}

internal/term/render.go

Lines changed: 177 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,26 @@ func mergeStyles(parent render.Style, child render.Style) render.Style {
5959
merged.Background = child.Background
6060
}
6161

62+
if child.Bold.Valid {
63+
merged.Bold = child.Bold
64+
}
65+
66+
if child.Dim.Valid {
67+
merged.Dim = child.Dim
68+
}
69+
70+
if child.Italic.Valid {
71+
merged.Italic = child.Italic
72+
}
73+
74+
if child.Underline.Valid {
75+
merged.Underline = child.Underline
76+
}
77+
78+
if child.Blink.Valid {
79+
merged.Blink = child.Blink
80+
}
81+
6282
return merged
6383
}
6484

@@ -69,6 +89,15 @@ const (
6989
styleOpResetForeground
7090
styleOpSetBackground
7191
styleOpResetBackground
92+
styleOpSetBold
93+
styleOpSetDim
94+
styleOpResetIntensity
95+
styleOpSetItalic
96+
styleOpResetItalic
97+
styleOpSetUnderline
98+
styleOpResetUnderline
99+
styleOpSetBlink
100+
styleOpResetBlink
72101
)
73102

74103
type styleOp struct {
@@ -95,6 +124,57 @@ func calculateStyleDiff(old render.Style, new render.Style) []styleOp {
95124
}
96125
}
97126

127+
// Bold and dim share a single ANSI reset code, so they must be diffed
128+
// together. If either attribute needs to be turned off, emit one
129+
// styleOpResetIntensity and then re-enable whichever attribute remains active
130+
// in the new style even if its value did not change, because the shared reset
131+
// will have cleared it.
132+
oldBold := old.Bold.Valid && old.Bold.Value
133+
newBold := new.Bold.Valid && new.Bold.Value
134+
oldDim := old.Dim.Valid && old.Dim.Value
135+
newDim := new.Dim.Valid && new.Dim.Value
136+
needsIntensityReset := (oldBold && !newBold) || (oldDim && !newDim)
137+
if needsIntensityReset {
138+
ops = append(ops, styleOp{Kind: styleOpResetIntensity})
139+
if newBold {
140+
ops = append(ops, styleOp{Kind: styleOpSetBold})
141+
}
142+
if newDim {
143+
ops = append(ops, styleOp{Kind: styleOpSetDim})
144+
}
145+
} else {
146+
if !oldBold && newBold {
147+
ops = append(ops, styleOp{Kind: styleOpSetBold})
148+
}
149+
if !oldDim && newDim {
150+
ops = append(ops, styleOp{Kind: styleOpSetDim})
151+
}
152+
}
153+
154+
if old.Italic != new.Italic {
155+
if new.Italic.Valid && new.Italic.Value {
156+
ops = append(ops, styleOp{Kind: styleOpSetItalic})
157+
} else {
158+
ops = append(ops, styleOp{Kind: styleOpResetItalic})
159+
}
160+
}
161+
162+
if old.Underline != new.Underline {
163+
if new.Underline.Valid && new.Underline.Value {
164+
ops = append(ops, styleOp{Kind: styleOpSetUnderline})
165+
} else {
166+
ops = append(ops, styleOp{Kind: styleOpResetUnderline})
167+
}
168+
}
169+
170+
if old.Blink != new.Blink {
171+
if new.Blink.Valid && new.Blink.Value {
172+
ops = append(ops, styleOp{Kind: styleOpSetBlink})
173+
} else {
174+
ops = append(ops, styleOp{Kind: styleOpResetBlink})
175+
}
176+
}
177+
98178
return ops
99179
}
100180

@@ -112,48 +192,110 @@ func xterm16ColorPaletteIndexToEscapeCode(index int, isForeground bool) int {
112192
}
113193
}
114194

115-
func (r *Renderer) applyStyleOp(op styleOp) {
116-
if r.options.promptEscapesRequired() {
117-
fmt.Fprint(&r.buffer, r.options.Capabilities.PromptEscapes.Value.Start)
195+
func (r *Renderer) setForeground(color render.Color) {
196+
switch r.options.Capabilities.ColorSupport {
197+
case ColorSupportNone:
198+
case ColorSupportBasic:
199+
colorIndex := r.quantizer.Quantize(color)
200+
fmt.Fprintf(&r.buffer, "\x1b[%dm", xterm16ColorPaletteIndexToEscapeCode(colorIndex, true))
201+
case ColorSupport256:
202+
colorIndex := r.quantizer.Quantize(color)
203+
fmt.Fprintf(&r.buffer, "\x1b[38;5;%dm", colorIndex)
204+
case ColorSupportTrueColor:
205+
fmt.Fprintf(&r.buffer, "\x1b[38;2;%d;%d;%dm", color.R, color.G, color.B)
118206
}
207+
}
119208

209+
func (r *Renderer) resetForeground() {
210+
fmt.Fprint(&r.buffer, "\x1b[39m")
211+
}
212+
213+
func (r *Renderer) setBackground(color render.Color) {
120214
switch r.options.Capabilities.ColorSupport {
121215
case ColorSupportNone:
122216
case ColorSupportBasic:
123-
color := r.quantizer.Quantize(op.Color)
124-
switch op.Kind {
125-
case styleOpSetForeground:
126-
fmt.Fprintf(&r.buffer, "\x1b[%dm", xterm16ColorPaletteIndexToEscapeCode(color, true))
127-
case styleOpResetForeground:
128-
fmt.Fprint(&r.buffer, "\x1b[39m")
129-
case styleOpSetBackground:
130-
fmt.Fprintf(&r.buffer, "\x1b[%dm", xterm16ColorPaletteIndexToEscapeCode(color, false))
131-
case styleOpResetBackground:
132-
fmt.Fprint(&r.buffer, "\x1b[49m")
133-
}
217+
colorIndex := r.quantizer.Quantize(color)
218+
fmt.Fprintf(&r.buffer, "\x1b[%dm", xterm16ColorPaletteIndexToEscapeCode(colorIndex, false))
134219
case ColorSupport256:
135-
color := r.quantizer.Quantize(op.Color)
136-
switch op.Kind {
137-
case styleOpSetForeground:
138-
fmt.Fprintf(&r.buffer, "\x1b[38;5;%dm", color)
139-
case styleOpResetForeground:
140-
fmt.Fprint(&r.buffer, "\x1b[39m")
141-
case styleOpSetBackground:
142-
fmt.Fprintf(&r.buffer, "\x1b[48;5;%dm", color)
143-
case styleOpResetBackground:
144-
fmt.Fprint(&r.buffer, "\x1b[49m")
145-
}
220+
colorIndex := r.quantizer.Quantize(color)
221+
fmt.Fprintf(&r.buffer, "\x1b[48;5;%dm", colorIndex)
146222
case ColorSupportTrueColor:
147-
switch op.Kind {
148-
case styleOpSetForeground:
149-
fmt.Fprintf(&r.buffer, "\x1b[38;2;%d;%d;%dm", op.Color.R, op.Color.G, op.Color.B)
150-
case styleOpResetForeground:
151-
fmt.Fprint(&r.buffer, "\x1b[39m")
152-
case styleOpSetBackground:
153-
fmt.Fprintf(&r.buffer, "\x1b[48;2;%d;%d;%dm", op.Color.R, op.Color.G, op.Color.B)
154-
case styleOpResetBackground:
155-
fmt.Fprint(&r.buffer, "\x1b[49m")
156-
}
223+
fmt.Fprintf(&r.buffer, "\x1b[48;2;%d;%d;%dm", color.R, color.G, color.B)
224+
}
225+
}
226+
227+
func (r *Renderer) resetBackground() {
228+
fmt.Fprint(&r.buffer, "\x1b[49m")
229+
}
230+
231+
func (r *Renderer) setBold() {
232+
fmt.Fprint(&r.buffer, "\x1b[1m")
233+
}
234+
235+
func (r *Renderer) setDim() {
236+
fmt.Fprint(&r.buffer, "\x1b[2m")
237+
}
238+
239+
func (r *Renderer) resetIntensity() {
240+
fmt.Fprint(&r.buffer, "\x1b[22m")
241+
}
242+
243+
func (r *Renderer) setItalic() {
244+
fmt.Fprint(&r.buffer, "\x1b[3m")
245+
}
246+
247+
func (r *Renderer) resetItalic() {
248+
fmt.Fprint(&r.buffer, "\x1b[23m")
249+
}
250+
251+
func (r *Renderer) setUnderline() {
252+
fmt.Fprint(&r.buffer, "\x1b[4m")
253+
}
254+
255+
func (r *Renderer) resetUnderline() {
256+
fmt.Fprint(&r.buffer, "\x1b[24m")
257+
}
258+
259+
func (r *Renderer) setBlink() {
260+
fmt.Fprint(&r.buffer, "\x1b[5m")
261+
}
262+
263+
func (r *Renderer) resetBlink() {
264+
fmt.Fprint(&r.buffer, "\x1b[25m")
265+
}
266+
267+
func (r *Renderer) applyStyleOp(op styleOp) {
268+
if r.options.promptEscapesRequired() {
269+
fmt.Fprint(&r.buffer, r.options.Capabilities.PromptEscapes.Value.Start)
270+
}
271+
272+
switch op.Kind {
273+
case styleOpSetForeground:
274+
r.setForeground(op.Color)
275+
case styleOpResetForeground:
276+
r.resetForeground()
277+
case styleOpSetBackground:
278+
r.setBackground(op.Color)
279+
case styleOpResetBackground:
280+
r.resetBackground()
281+
case styleOpSetBold:
282+
r.setBold()
283+
case styleOpSetDim:
284+
r.setDim()
285+
case styleOpResetIntensity:
286+
r.resetIntensity()
287+
case styleOpSetItalic:
288+
r.setItalic()
289+
case styleOpResetItalic:
290+
r.resetItalic()
291+
case styleOpSetUnderline:
292+
r.setUnderline()
293+
case styleOpResetUnderline:
294+
r.resetUnderline()
295+
case styleOpSetBlink:
296+
r.setBlink()
297+
case styleOpResetBlink:
298+
r.resetBlink()
157299
}
158300

159301
if r.options.promptEscapesRequired() {

0 commit comments

Comments
 (0)