瀏覽代碼

First import

mattn 11 年之前
當前提交
53471aefcd
共有 4 個文件被更改,包括 228 次插入0 次删除
  1. 34 0
      README.md
  2. 15 0
      _example/main.go
  3. 11 0
      colorable_others.go
  4. 168 0
      colorable_windows.go

+ 34 - 0
README.md

@@ -0,0 +1,34 @@
+# go-colorable
+
+Colorable writer for windows
+
+## Too Bad!
+
+
+## So Good!
+
+
+## Usage
+
+```go
+logrus.SetOutput(colorable.NewColorableWriter())
+
+logrus.Info("succeeded")
+logrus.Warn("not correct")
+logrus.Error("something error")
+logrus.Fatal("panic")
+```
+
+## Installation
+
+```
+$ go get github.com/mattn/go-colorable
+```
+
+# License
+
+MIT
+
+# Author
+
+Yasuhiro Matsumoto (a.k.a mattn)

+ 15 - 0
_example/main.go

@@ -0,0 +1,15 @@
+package main
+
+import (
+	"github.com/mattn/go-colorable"
+	"github.com/Sirupsen/logrus"
+)
+
+func main() {
+	logrus.SetOutput(colorable.NewColorableWriter())
+
+	logrus.Info("succeeded")
+	logrus.Warn("not correct")
+	logrus.Error("something error")
+	logrus.Fatal("panic")
+}

+ 11 - 0
colorable_others.go

@@ -0,0 +1,11 @@
+// +build !windows
+
+package colorable
+
+import (
+	"os"
+)
+
+func NewColorableWriter() *Writer {
+	return os.Stdout
+}

+ 168 - 0
colorable_windows.go

@@ -0,0 +1,168 @@
+package colorable
+
+import (
+	"bytes"
+	"fmt"
+	"os"
+	"strconv"
+	"strings"
+	"syscall"
+	"unsafe"
+)
+
+const (
+	foregroundBlue      = 0x1
+	foregroundGreen     = 0x2
+	foregroundRed       = 0x4
+	foregroundIntensity = 0x8
+	foregroundMask      = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity)
+	backgroundBlue      = 0x10
+	backgroundGreen     = 0x20
+	backgroundRed       = 0x40
+	backgroundIntensity = 0x80
+	backgroundMask      = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
+)
+
+type wchar uint16
+type short int16
+type dword uint32
+type word uint16
+
+type coord struct {
+	x short
+	y short
+}
+
+type smallRect struct {
+	left   short
+	top    short
+	right  short
+	bottom short
+}
+
+type consoleScreenBufferInfo struct {
+	size              coord
+	cursorPosition    coord
+	attributes        word
+	window            smallRect
+	maximumWindowSize coord
+}
+
+var (
+	kernel32                       = syscall.NewLazyDLL("kernel32.dll")
+	procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
+	procSetConsoleTextAttribute    = kernel32.NewProc("SetConsoleTextAttribute")
+)
+
+type Writer struct {
+	out     syscall.Handle
+	lastbuf bytes.Buffer
+}
+
+func NewColorableWriter() *Writer {
+	return &Writer{out: syscall.Handle(os.Stdout.Fd())}
+}
+
+func (w *Writer) Write(data []byte) (n int, err error) {
+	var csbi consoleScreenBufferInfo
+	procGetConsoleScreenBufferInfo.Call(uintptr(w.out), uintptr(unsafe.Pointer(&csbi)))
+	attr_old := csbi.attributes
+	defer func() {
+		procSetConsoleTextAttribute.Call(uintptr(w.out), uintptr(attr_old))
+	}()
+
+	er := bytes.NewBuffer(data)
+loop:
+	for {
+		r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.out), uintptr(unsafe.Pointer(&csbi)))
+		if r1 == 0 {
+			break loop
+		}
+
+		c1, _, err := er.ReadRune()
+		if err != nil {
+			break loop
+		}
+		if c1 != 0x1b {
+			fmt.Print(string(c1))
+			continue
+		}
+		c2, _, err := er.ReadRune()
+		if err != nil {
+			w.lastbuf.WriteRune(c1)
+			break loop
+		}
+		if c2 != 0x5b {
+			w.lastbuf.WriteRune(c1)
+			w.lastbuf.WriteRune(c2)
+			continue
+		}
+
+		var buf bytes.Buffer
+		var m rune
+		for {
+			c, _, err := er.ReadRune()
+			if err != nil {
+				w.lastbuf.WriteRune(c1)
+				w.lastbuf.WriteRune(c2)
+				w.lastbuf.Write(buf.Bytes())
+				break loop
+			}
+			if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
+				m = c
+				break
+			}
+			buf.Write([]byte(string(c)))
+		}
+
+		switch m {
+		case 'm':
+			attr := csbi.attributes
+			cs := buf.String()
+			if cs == "" {
+				procSetConsoleTextAttribute.Call(uintptr(w.out), uintptr(attr_old))
+				continue
+			}
+			for _, ns := range strings.Split(cs, ";") {
+				if n, err = strconv.Atoi(ns); err == nil {
+					switch {
+					case n == 0 || n == 100:
+						attr = attr_old
+					case 1 <= n && n <= 5:
+						attr |= foregroundIntensity
+					case n == 7:
+						attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
+					case 22 == n || n == 25 || n == 25:
+						attr |= foregroundIntensity
+					case n == 27:
+						attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
+					case 30 <= n && n <= 37:
+						attr = (attr & backgroundMask)
+						if (n-30)&1 != 0 {
+							attr |= foregroundRed
+						}
+						if (n-30)&2 != 0 {
+							attr |= foregroundGreen
+						}
+						if (n-30)&4 != 0 {
+							attr |= foregroundBlue
+						}
+					case 40 <= n && n <= 47:
+						attr = (attr & foregroundMask)
+						if (n-40)&1 != 0 {
+							attr |= backgroundRed
+						}
+						if (n-40)&2 != 0 {
+							attr |= backgroundGreen
+						}
+						if (n-40)&4 != 0 {
+							attr |= backgroundBlue
+						}
+					}
+					procSetConsoleTextAttribute.Call(uintptr(w.out), uintptr(attr))
+				}
+			}
+		}
+	}
+	return len(data) - w.lastbuf.Len(), nil
+}