github.com/richardwilkes/toolbox@v1.121.0/xio/term/ansi.go (about) 1 // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved. 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, version 2.0. If a copy of the MPL was not distributed with 5 // this file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 // 7 // This Source Code Form is "Incompatible With Secondary Licenses", as 8 // defined by the Mozilla Public License, version 2.0. 9 10 package term 11 12 import ( 13 "fmt" 14 "io" 15 ) 16 17 // ANSI color constants 18 const ( 19 Black Color = iota 20 Red 21 Green 22 Yellow 23 Blue 24 Magenta 25 Cyan 26 White 27 ) 28 29 // ANSI style constants. Multiple styles may be or'd together. 30 const ( 31 Bold Style = 1 << iota 32 Underline 33 Blink 34 Normal = 0 35 ) 36 37 // Color represents an ANSI terminal color 38 type Color int 39 40 // Style represents an ANSI terminal style. Multiple styles may be or'd together. 41 type Style int 42 43 // ANSI provides support for ANSI terminal escape sequences. 44 type ANSI struct { 45 out io.Writer 46 ok bool 47 } 48 49 // NewANSI creates a new ANSI terminal and attaches it to 'out'. 50 func NewANSI(out io.Writer) *ANSI { 51 return &ANSI{out: out, ok: IsTerminal(out)} 52 } 53 54 // Bell causes the bell to sound. 55 func (a *ANSI) Bell() { 56 fmt.Fprint(a, "\007") 57 } 58 59 // Reset colors and styles. 60 func (a *ANSI) Reset() { 61 if a.ok { 62 fmt.Fprint(a, "\033[m") 63 } 64 } 65 66 // Up moves the cursor up 'count' rows. If this would put it beyond the top edge of the screen, it will instead go to 67 // the top edge of the screen. 68 func (a *ANSI) Up(count int) { 69 if a.ok { 70 fmt.Fprintf(a, "\033[%dA", count) 71 } 72 } 73 74 // Down moves the cursor down 'count' rows. If this would put it beyond the bottom edge of the screen, it will instead 75 // go to the bottom edge of the screen. 76 func (a *ANSI) Down(count int) { 77 if a.ok { 78 fmt.Fprintf(a, "\033[%dB", count) 79 } 80 } 81 82 // Left moves the cursor left 'count' columns. If this would put it beyond the left edge of the screen, it will instead 83 // go to the left edge of the screen. 84 func (a *ANSI) Left(count int) { 85 if a.ok { 86 fmt.Fprintf(a, "\033[%dD", count) 87 } 88 } 89 90 // Right moves the cursor right 'count' columns. If this would put it beyond the right edge of the screen, it will 91 // instead go to the right edge of the screen. 92 func (a *ANSI) Right(count int) { 93 if a.ok { 94 fmt.Fprintf(a, "\033[%dC", count) 95 } 96 } 97 98 // Position the cursor at 'row' and 'column'. Both values are 1-based. 99 func (a *ANSI) Position(row, column int) { 100 if a.ok { 101 fmt.Fprintf(a, "\033[%d;%dH", row, column) 102 } 103 } 104 105 // Clear the screen and position the cursor at row 1, column 1. 106 func (a *ANSI) Clear() { 107 if a.ok { 108 fmt.Fprint(a, "\033[2J") 109 } 110 } 111 112 // ClearToStart clears the screen from the cursor to the beginning of the screen. 113 func (a *ANSI) ClearToStart() { 114 if a.ok { 115 fmt.Fprint(a, "\033[1J") 116 } 117 } 118 119 // ClearToEnd clears the screen from the cursor to the end of the screen. 120 func (a *ANSI) ClearToEnd() { 121 if a.ok { 122 fmt.Fprint(a, "\033[J") 123 } 124 } 125 126 // EraseLine clears the current row. 127 func (a *ANSI) EraseLine() { 128 if a.ok { 129 fmt.Fprint(a, "\033[2K") 130 } 131 } 132 133 // EraseLineToStart clears from the cursor position to the start of the current row. 134 func (a *ANSI) EraseLineToStart() { 135 if a.ok { 136 fmt.Fprint(a, "\033[1K") 137 } 138 } 139 140 // EraseLineToEnd clears from the cursor position to the end of the current row. 141 func (a *ANSI) EraseLineToEnd() { 142 if a.ok { 143 fmt.Fprint(a, "\033[K") 144 } 145 } 146 147 // SavePosition saves the current cursor position. 148 func (a *ANSI) SavePosition() { 149 if a.ok { 150 fmt.Fprint(a, "\033[s") 151 } 152 } 153 154 // RestorePosition restores the previously saved cursor position. 155 func (a *ANSI) RestorePosition() { 156 if a.ok { 157 fmt.Fprint(a, "\033[u") 158 } 159 } 160 161 // HideCursor makes the cursor invisible. 162 func (a *ANSI) HideCursor() { 163 if a.ok { 164 fmt.Fprint(a, "\033[?25l") 165 } 166 } 167 168 // ShowCursor makes the cursor visible. 169 func (a *ANSI) ShowCursor() { 170 if a.ok { 171 fmt.Fprint(a, "\033[?25h") 172 } 173 } 174 175 // Foreground sets the foreground color and style for subsequent output. 176 func (a *ANSI) Foreground(color Color, style Style) { 177 if a.ok { 178 fmt.Fprint(a, "\033[0;") 179 if style&Bold == Bold { 180 fmt.Fprint(a, "1;") 181 } 182 if style&Underline == Underline { 183 fmt.Fprint(a, "4;") 184 } 185 if style&Blink == Blink { 186 fmt.Fprint(a, "5;") 187 } 188 fmt.Fprintf(a, "%dm", 30+color) 189 } 190 } 191 192 // Background sets the background color for subsequent output. 193 func (a *ANSI) Background(color Color) { 194 if a.ok { 195 fmt.Fprintf(a, "\033[%dm", 40+color) 196 } 197 } 198 199 // Write implements the io.Writer interface. 200 func (a *ANSI) Write(p []byte) (n int, err error) { 201 return a.out.Write(p) 202 }