src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/ui/text_builder.go (about) 1 package ui 2 3 import "strings" 4 5 // Methods of [TextBuilder] are fully exercised by other functions Concat, so 6 // there are no dedicated tests for it. 7 8 // TextBuilder can be used to efficiently build a [Text]. The zero value is 9 // ready to use. Do not copy a non-zero TextBuilder. 10 type TextBuilder struct { 11 segs []*Segment 12 style Style 13 text strings.Builder 14 } 15 16 // WriteText appends t to the TextBuilder. 17 func (tb *TextBuilder) WriteText(t Text) { 18 if len(t) == 0 { 19 return 20 } 21 if tb.style == t[0].Style { 22 // Merge the first segment of t with the pending segment. 23 tb.text.WriteString(t[0].Text) 24 t = t[1:] 25 if len(t) == 0 { 26 return 27 } 28 } 29 // At this point, the first segment of t has a different style than the 30 // pending segment (assuming that t is normal). Add the pending segment if 31 // it's non-empty. 32 if tb.text.Len() > 0 { 33 tb.segs = append(tb.segs, &Segment{tb.style, tb.text.String()}) 34 tb.text.Reset() 35 } 36 // Add all segments from t except the last one. 37 tb.segs = append(tb.segs, t[:len(t)-1]...) 38 39 // Use the last segment of t as the pending segment. 40 tb.style = t[len(t)-1].Style 41 tb.text.WriteString(t[len(t)-1].Text) 42 } 43 44 // Text returns the [Text] that has been built. 45 func (tb *TextBuilder) Text() Text { 46 if tb.Empty() { 47 return nil 48 } 49 t := append(Text(nil), tb.segs...) 50 return append(t, &Segment{tb.style, tb.text.String()}) 51 } 52 53 // Empty returns nothing has been written to the TextBuilder yet. 54 func (tb *TextBuilder) Empty() bool { 55 return len(tb.segs) == 0 && tb.text.Len() == 0 56 } 57 58 // Reset resets the TextBuilder to be empty. 59 func (tb *TextBuilder) Reset() { 60 tb.segs = nil 61 tb.style = Style{} 62 tb.text.Reset() 63 }