File: logger_windows.go

package info (click to toggle)
golang-github-evanw-esbuild 0.25.10-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 10,184 kB
  • sloc: javascript: 28,602; makefile: 856; sh: 17
file content (136 lines) | stat: -rw-r--r-- 4,015 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//go:build windows
// +build windows

package logger

import (
	"os"
	"strings"
	"syscall"
	"unsafe"
)

const SupportsColorEscapes = true

var kernel32 = syscall.NewLazyDLL("kernel32.dll")
var getConsoleMode = kernel32.NewProc("GetConsoleMode")
var setConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
var getConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")

type consoleScreenBufferInfo struct {
	dwSizeX              int16
	dwSizeY              int16
	dwCursorPositionX    int16
	dwCursorPositionY    int16
	wAttributes          uint16
	srWindowLeft         int16
	srWindowTop          int16
	srWindowRight        int16
	srWindowBottom       int16
	dwMaximumWindowSizeX int16
	dwMaximumWindowSizeY int16
}

func GetTerminalInfo(file *os.File) TerminalInfo {
	fd := file.Fd()

	// Is this file descriptor a terminal?
	var unused uint32
	isTTY, _, _ := syscall.Syscall(getConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&unused)), 0)

	// Get the width of the window
	var info consoleScreenBufferInfo
	syscall.Syscall(getConsoleScreenBufferInfo.Addr(), 2, fd, uintptr(unsafe.Pointer(&info)), 0)

	return TerminalInfo{
		IsTTY:           isTTY != 0,
		Width:           int(info.dwSizeX) - 1,
		Height:          int(info.dwSizeY) - 1,
		UseColorEscapes: !hasNoColorEnvironmentVariable(),
	}
}

const (
	FOREGROUND_BLUE uint8 = 1 << iota
	FOREGROUND_GREEN
	FOREGROUND_RED
	FOREGROUND_INTENSITY
	BACKGROUND_BLUE
	BACKGROUND_GREEN
	BACKGROUND_RED
	BACKGROUND_INTENSITY
)

var windowsEscapeSequenceMap = map[string]uint8{
	TerminalColors.Reset: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
	TerminalColors.Dim:   FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
	TerminalColors.Bold:  FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY,

	// Apparently underlines only work with the CJK locale on Windows :(
	TerminalColors.Underline: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,

	TerminalColors.Red:   FOREGROUND_RED,
	TerminalColors.Green: FOREGROUND_GREEN,
	TerminalColors.Blue:  FOREGROUND_BLUE,

	TerminalColors.Cyan:    FOREGROUND_GREEN | FOREGROUND_BLUE,
	TerminalColors.Magenta: FOREGROUND_RED | FOREGROUND_BLUE,
	TerminalColors.Yellow:  FOREGROUND_RED | FOREGROUND_GREEN,

	TerminalColors.RedBgRed:     FOREGROUND_RED | BACKGROUND_RED,
	TerminalColors.RedBgWhite:   FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | BACKGROUND_RED,
	TerminalColors.GreenBgGreen: FOREGROUND_GREEN | BACKGROUND_GREEN,
	TerminalColors.GreenBgWhite: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | BACKGROUND_GREEN,
	TerminalColors.BlueBgBlue:   FOREGROUND_BLUE | BACKGROUND_BLUE,
	TerminalColors.BlueBgWhite:  FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | BACKGROUND_BLUE,

	TerminalColors.CyanBgCyan:       FOREGROUND_GREEN | FOREGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_BLUE,
	TerminalColors.CyanBgBlack:      BACKGROUND_GREEN | BACKGROUND_BLUE,
	TerminalColors.MagentaBgMagenta: FOREGROUND_RED | FOREGROUND_BLUE | BACKGROUND_RED | BACKGROUND_BLUE,
	TerminalColors.MagentaBgBlack:   BACKGROUND_RED | BACKGROUND_BLUE,
	TerminalColors.YellowBgYellow:   FOREGROUND_RED | FOREGROUND_GREEN | BACKGROUND_RED | BACKGROUND_GREEN,
	TerminalColors.YellowBgBlack:    BACKGROUND_RED | BACKGROUND_GREEN,
}

func writeStringWithColor(file *os.File, text string) {
	fd := file.Fd()
	i := 0

	for i < len(text) {
		// Find the escape
		if text[i] != 033 {
			i++
			continue
		}

		// Find the 'm'
		window := text[i:]
		if len(window) > 8 {
			window = window[:8]
		}
		m := strings.IndexByte(window, 'm')
		if m == -1 {
			i++
			continue
		}
		m += i + 1

		// Find the escape sequence
		attributes, ok := windowsEscapeSequenceMap[text[i:m]]
		if !ok {
			i++
			continue
		}

		// Write out the text before the escape sequence
		file.WriteString(text[:i])

		// Apply the escape sequence
		text = text[m:]
		i = 0
		setConsoleTextAttribute.Call(fd, uintptr(attributes))
	}

	// Write out the remaining text
	file.WriteString(text)
}