package door import ( "bytes" "log" "strings" "unicode" ) /* door.Line - Display a line of text Example: var basicLine door.Line = {Text: "Welcome", DefaultColor: door.Color("BRIGHT YELLOW"), } d.Write(basicLine.Output() + door.Reset + door.CRNL) This outputs Welcome in Bright/Bold Yellow. Example Render: var renderAlphaDigit ColorRender = func(text string) string { var r door.Render{Text: text} var alpha string = door.ColorText("BOLD YELLOW") var digit string = door.ColorText("BOLD GREEN") var other string = door.Reset for _, letter := range text { if unicode.IsAlpha(letter) { r.Append(alpha, 1) } else { if unicode.IsDigit(letter) { r.Append(digit, 1) } else { r.Append(other, 1) } } } return r.Result } var renderLine door.Line = {Text: "Render - 12345", RenderF: renderAlphaDigit, } d.Write(renderLine.Output() + door.Reset + door.CRNL) This outputs "Render" in Yellow, "12345" in Green, and " - " in White. Example Update: var updateTime() string { var now time.Time = time.Now() var result string = now.Format("3:04:05 PM") return result } var timeLine door.Line = {Text: updateTime(), UpdateF: updateTime, } d.Write(timeLine.Output() + door.CRNL) time.Sleep(time.Second) // Check timeLine for update if timeLine.Update() { // Yes, there's an update to the time, output it. d.Write(timeLine.Output() + door.CRNL) } if timeLine.Update() { // This isn't called. There were no changes. d.Write(timeLine.Output() + door.CRNL) } This outputs the Current time in 12 hour format. It pauses for a second, and outputs the new time. */ /* Line of text to display. */ type Line struct { Text *bytes.Buffer // Text to be displayed update *bytes.Buffer // Buffer for updates DefaultColor []byte // Default Color to use RenderF ColorRender // Render function (displays string with colors) render *bytes.Buffer // Buffer for rendering UpdateF Updater // Update function updates the text Width int // Line length } func NewLine(text string) *Line { return &Line{Text: bytes.NewBuffer([]byte(text))} } /* Line Update - This calls the UpdateF if present. Returns true if the line has been updated, and there's an Update function. */ func (l *Line) Update() bool { if l.Text == nil { l.Text = &bytes.Buffer{} } if l.UpdateF == nil { return false } if l.update == nil { l.update = &bytes.Buffer{} } l.update.Reset() l.UpdateF(l.update) l.LineLength(l.update) // Has the line changed (update)? if bytes.Compare(l.update.Bytes(), l.Text.Bytes()) != 0 { // Yes, copy into Text. l.Text.Reset() l.Text.Write(l.update.Bytes()) return true } return false } // If a line Width has been set, make sure we match it. func (l *Line) LineLength(text *bytes.Buffer) { if l.Width == 0 { return } var length int if Unicode { if l.render == nil { l.render = &bytes.Buffer{} } l.render.Reset() l.render.Write(text.Bytes()) /* var ubuff *bytes.Buffer = bytes.NewBuffer(text.Bytes()) */ var e error var r rune for { r, _, e = l.render.ReadRune() if e != nil { break } length += UnicodeWidth(r) } l.render.Reset() } else { length = text.Len() } if length > l.Width { log.Printf("ERROR: Line Width %d: Have %d\n", l.Width, length) } else { for length < l.Width { text.WriteByte(' ') length++ } // *text += strings.Repeat(" ", l.Width-length) } } /* Line Output - returns a string with ANSI Color codes. If there is no RenderF, we use the DefaultColor. Otherwise we pass the text to RenderF and return what it returns. */ func (l *Line) Output() []byte { if l.UpdateF == nil { l.LineLength(l.Text) } if l.render == nil { l.render = &bytes.Buffer{} /* // No profiling changes here. if l.RenderF == nil { var cap int if l.DefaultColor != "" { cap += len(l.DefaultColor) } cap += l.Text.Len() l.render.Grow(cap) } */ } if l.RenderF == nil { l.render.Reset() if len(l.DefaultColor) != 0 { l.render.Write(l.DefaultColor) } l.render.Write(l.Text.Bytes()) return l.render.Bytes() // return l.DefaultColor + l.Text } else { l.RenderF(l.render, l.Text.Bytes()) return l.render.Bytes() } } // Make Uppercase RenderF func RenderUppercase(Upper string, NonUpper string) ColorRender { var UpperColor, NonUpperColor []byte if strings.HasPrefix(Upper, "\x1b") { UpperColor = []byte(Upper) } else { UpperColor = []byte(ColorText(Upper)) } if strings.HasPrefix(NonUpper, "\x1b") { NonUpperColor = []byte(NonUpper) } else { NonUpperColor = []byte(ColorText(NonUpper)) } if Unicode { var runeBuffer *bytes.Buffer = &bytes.Buffer{} return func(output *bytes.Buffer, text []byte) { var lastColor *[]byte output.Reset() runeBuffer.Reset() runeBuffer.Write(text) for r, _, err := runeBuffer.ReadRune(); err != nil; r, _, err = runeBuffer.ReadRune() { // for _, letter := range text { if unicode.IsUpper(r) { if lastColor != &UpperColor { output.Write(UpperColor) lastColor = &UpperColor } } else { if lastColor != &NonUpperColor { output.Write(NonUpperColor) lastColor = &UpperColor } } output.WriteRune(r) } } } else { return func(output *bytes.Buffer, text []byte) { var lastColor *[]byte output.Reset() for _, letter := range text { if unicode.IsUpper(rune(letter)) { if lastColor != &UpperColor { output.Write(UpperColor) lastColor = &UpperColor } } else { if lastColor != &NonUpperColor { output.Write(NonUpperColor) lastColor = &UpperColor } } output.WriteByte(letter) } } } } func RenderBlueYellow(output *bytes.Buffer, text []byte) { output.Reset() // var output = RenderPool.Get().(*strings.Builder) // output.Reset() var blue []byte = ColorText("BOLD BLUE") var yellow []byte = ColorText("BOLD YELLOW") var last *[]byte for _, letter := range text { if unicode.IsUpper(rune(letter)) { if last != &blue { output.Write(blue) last = &blue } } else { if last != &yellow { output.Write(yellow) last = &yellow } } output.WriteByte(letter) } // var result = output.String() // RenderPool.Put(output) // return result }