├── .gitignore ├── LICENSE ├── README.md ├── color └── color.go ├── example └── hello.go └── terminal.go /.gitignore: -------------------------------------------------------------------------------- 1 | example/example 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Meng Zhang. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Terminal ## 2 | Terminal is a simple golang package that provides basic terminal handling. 3 | 4 | Terminal wraps color/format functions provided by [ANSI escape code](http://en.wikipedia.org/wiki/ANSI_escape_code) 5 | 6 | ## Usage ## 7 | ```go 8 | package main 9 | 10 | import ( 11 | "github.com/wsxiaoys/terminal" 12 | "github.com/wsxiaoys/terminal/color" 13 | ) 14 | 15 | func main() { 16 | terminal.Stdout.Color("y"). 17 | Print("Hello world").Nl(). 18 | Reset(). 19 | Colorf("@{kW}Hello world\n") 20 | 21 | color.Print("@rHello world") 22 | } 23 | ``` 24 | Check the [godoc result](https://godoc.org/github.com/wsxiaoys/terminal) for more details. 25 | -------------------------------------------------------------------------------- /color/color.go: -------------------------------------------------------------------------------- 1 | // The colors package provide a simple way to bring colorful characters to terminal interface. 2 | // 3 | // This example will output the text with a Blue foreground and a Black background 4 | // color.Println("@{bK}Example Text") 5 | // 6 | // This one will output the text with a red foreground 7 | // color.Println("@rExample Text") 8 | // 9 | // This one will escape the @ 10 | // color.Println("@@") 11 | // 12 | // Full color syntax code 13 | // @{rgbcmykwRGBCMYKW} foreground/background color 14 | // r/R: Red 15 | // g/G: Green 16 | // b/B: Blue 17 | // c/C: Cyan 18 | // m/M: Magenta 19 | // y/Y: Yellow 20 | // k/K: Black 21 | // w/W: White 22 | // @{|} Reset format style 23 | // @{!./_} Bold / Dim / Italic / Underline 24 | // @{^&} Blink / Fast blink 25 | // @{*} High intensity foreground color 26 | // @{?} Reverse the foreground and background color 27 | // @{-} Hide the text 28 | // Note some of the functions are not widely supported, like "Fast blink" and "Italic". 29 | package color 30 | 31 | import ( 32 | "bytes" 33 | "errors" 34 | "fmt" 35 | "io" 36 | "log" 37 | ) 38 | 39 | const ( 40 | EscapeChar = '@' // Escape character for color syntax 41 | ResetCode = "\033[0m" // Short for reset to default style 42 | ) 43 | 44 | // Mapping from character to concrete escape code. 45 | var codeMap = map[int]int{ 46 | '|': 0, 47 | '!': 1, 48 | '.': 2, 49 | '/': 3, 50 | '_': 4, 51 | '^': 5, 52 | '&': 6, 53 | '?': 7, 54 | '-': 8, 55 | '*': 60, 56 | 57 | 'k': 30, 58 | 'r': 31, 59 | 'g': 32, 60 | 'y': 33, 61 | 'b': 34, 62 | 'm': 35, 63 | 'c': 36, 64 | 'w': 37, 65 | 'd': 39, 66 | 67 | 'K': 40, 68 | 'R': 41, 69 | 'G': 42, 70 | 'Y': 43, 71 | 'B': 44, 72 | 'M': 45, 73 | 'C': 46, 74 | 'W': 47, 75 | 'D': 49, 76 | } 77 | 78 | // Compile color syntax string like "rG" to escape code. 79 | func Colorize(x string) string { 80 | attr := 0 81 | fg := 39 82 | bg := 49 83 | 84 | for _, key := range x { 85 | c, ok := codeMap[int(key)] 86 | switch { 87 | case !ok: 88 | log.Printf("Wrong color syntax: %c", key) 89 | case 0 <= c && c <= 8: 90 | attr = c 91 | case 30 <= c && c <= 37: 92 | fg = c 93 | case 40 <= c && c <= 47: 94 | bg = c 95 | case c == 60: 96 | fg += c 97 | } 98 | } 99 | return fmt.Sprintf("\033[%d;%d;%dm", attr, fg, bg) 100 | } 101 | 102 | // Handle state after meeting one '@' 103 | func compileColorSyntax(input, output *bytes.Buffer) { 104 | i, _, err := input.ReadRune() 105 | if err != nil { 106 | // EOF got 107 | log.Print("Parse failed on color syntax") 108 | return 109 | } 110 | 111 | switch i { 112 | default: 113 | output.WriteString(Colorize(string(i))) 114 | case '{': 115 | color := bytes.NewBufferString("") 116 | for { 117 | i, _, err := input.ReadRune() 118 | if err != nil { 119 | log.Print("Parse failed on color syntax") 120 | break 121 | } 122 | if i == '}' { 123 | break 124 | } 125 | color.WriteRune(i) 126 | } 127 | output.WriteString(Colorize(color.String())) 128 | case EscapeChar: 129 | output.WriteRune(EscapeChar) 130 | } 131 | } 132 | 133 | // Compile the string and replace color syntax with concrete escape code. 134 | func compile(x string) string { 135 | if x == "" { 136 | return "" 137 | } 138 | 139 | input := bytes.NewBufferString(x) 140 | output := bytes.NewBufferString("") 141 | 142 | for { 143 | i, _, err := input.ReadRune() 144 | if err != nil { 145 | break 146 | } 147 | switch i { 148 | default: 149 | output.WriteRune(i) 150 | case EscapeChar: 151 | compileColorSyntax(input, output) 152 | } 153 | } 154 | return output.String() 155 | } 156 | 157 | // Compile multiple values, only do compiling on string type. 158 | func compileValues(a *[]interface{}) { 159 | for i, x := range *a { 160 | if str, ok := x.(string); ok { 161 | (*a)[i] = compile(str) 162 | } 163 | } 164 | } 165 | 166 | // Similar to fmt.Print, will reset the color at the end. 167 | func Print(a ...interface{}) (int, error) { 168 | a = append(a, ResetCode) 169 | compileValues(&a) 170 | return fmt.Print(a...) 171 | } 172 | 173 | // Similar to fmt.Println, will reset the color at the end. 174 | func Println(a ...interface{}) (int, error) { 175 | a = append(a, ResetCode) 176 | compileValues(&a) 177 | return fmt.Println(a...) 178 | } 179 | 180 | // Similar to fmt.Printf, will reset the color at the end. 181 | func Printf(format string, a ...interface{}) (int, error) { 182 | format += ResetCode 183 | format = compile(format) 184 | return fmt.Printf(format, a...) 185 | } 186 | 187 | // Similar to fmt.Fprint, will reset the color at the end. 188 | func Fprint(w io.Writer, a ...interface{}) (int, error) { 189 | a = append(a, ResetCode) 190 | compileValues(&a) 191 | return fmt.Fprint(w, a...) 192 | } 193 | 194 | // Similar to fmt.Fprintln, will reset the color at the end. 195 | func Fprintln(w io.Writer, a ...interface{}) (int, error) { 196 | a = append(a, ResetCode) 197 | compileValues(&a) 198 | return fmt.Fprintln(w, a...) 199 | } 200 | 201 | // Similar to fmt.Fprintf, will reset the color at the end. 202 | func Fprintf(w io.Writer, format string, a ...interface{}) (int, error) { 203 | format += ResetCode 204 | format = compile(format) 205 | return fmt.Fprintf(w, format, a...) 206 | } 207 | 208 | // Similar to fmt.Sprint, will reset the color at the end. 209 | func Sprint(a ...interface{}) string { 210 | a = append(a, ResetCode) 211 | compileValues(&a) 212 | return fmt.Sprint(a...) 213 | } 214 | 215 | // Similar to fmt.Sprintf, will reset the color at the end. 216 | func Sprintf(format string, a ...interface{}) string { 217 | format += ResetCode 218 | format = compile(format) 219 | return fmt.Sprintf(format, a...) 220 | } 221 | 222 | // Similar to fmt.Errorf, will reset the color at the end. 223 | func Errorf(format string, a ...interface{}) error { 224 | return errors.New(Sprintf(format, a...)) 225 | } 226 | -------------------------------------------------------------------------------- /example/hello.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/wsxiaoys/terminal" 5 | "github.com/wsxiaoys/terminal/color" 6 | ) 7 | 8 | func main() { 9 | terminal.Stdout.Color("y"). 10 | Print("Hello world").Nl(). 11 | Reset(). 12 | Colorf("@{kW}Hello world\n") 13 | 14 | color.Print("@rHello world") 15 | } 16 | -------------------------------------------------------------------------------- /terminal.go: -------------------------------------------------------------------------------- 1 | package terminal 2 | 3 | import ( 4 | "fmt" 5 | "github.com/wsxiaoys/terminal/color" 6 | "io" 7 | "log" 8 | "os" 9 | ) 10 | 11 | type TerminalWriter struct { 12 | io.Writer 13 | } 14 | 15 | var ( 16 | Stdout = &TerminalWriter{os.Stdout} 17 | Stderr = &TerminalWriter{os.Stderr} 18 | ) 19 | 20 | func (w *TerminalWriter) checkOutput(s string) { 21 | if _, err := io.WriteString(w, s); err != nil { 22 | log.Fatal("Write to %v failed.", w) 23 | } 24 | } 25 | 26 | func (w *TerminalWriter) Color(syntax string) *TerminalWriter { 27 | escapeCode := color.Colorize(syntax) 28 | w.checkOutput(escapeCode) 29 | return w 30 | } 31 | 32 | func (w *TerminalWriter) Reset() *TerminalWriter { 33 | w.checkOutput(color.ResetCode) 34 | return w 35 | } 36 | 37 | func (w *TerminalWriter) Print(a ...interface{}) *TerminalWriter { 38 | fmt.Fprint(w, a...) 39 | return w 40 | } 41 | 42 | func (w *TerminalWriter) Nl(a ...interface{}) *TerminalWriter { 43 | length := 1 44 | if len(a) > 0 { 45 | length = a[0].(int) 46 | } 47 | for i := 0; i < length; i++ { 48 | w.checkOutput("\n") 49 | } 50 | return w 51 | } 52 | 53 | func (w *TerminalWriter) Colorf(format string, a ...interface{}) *TerminalWriter { 54 | w.checkOutput(color.Sprintf(format, a...)) 55 | return w 56 | } 57 | 58 | func (w *TerminalWriter) Clear() *TerminalWriter { 59 | w.checkOutput("\033[2J") 60 | return w 61 | } 62 | 63 | func (w *TerminalWriter) ClearLine() *TerminalWriter { 64 | w.checkOutput("\033[2K") 65 | return w 66 | } 67 | 68 | func (w *TerminalWriter) Move(x, y int) *TerminalWriter { 69 | w.checkOutput(fmt.Sprintf("\033[%d;%dH", x, y)) 70 | return w 71 | } 72 | 73 | func (w *TerminalWriter) Up(x int) *TerminalWriter { 74 | w.checkOutput(fmt.Sprintf("\033[%dA", x)) 75 | return w 76 | } 77 | 78 | func (w *TerminalWriter) Down(x int) *TerminalWriter { 79 | w.checkOutput(fmt.Sprintf("\033[%dB", x)) 80 | return w 81 | } 82 | 83 | func (w *TerminalWriter) Right(x int) *TerminalWriter { 84 | w.checkOutput(fmt.Sprintf("\033[%dC", x)) 85 | return w 86 | } 87 | 88 | func (w *TerminalWriter) Left(x int) *TerminalWriter { 89 | w.checkOutput(fmt.Sprintf("\033[%dD", x)) 90 | return w 91 | } 92 | --------------------------------------------------------------------------------