github.com/evanw/esbuild@v0.21.4/internal/logger/logger_windows.go (about) 1 //go:build windows 2 // +build windows 3 4 package logger 5 6 import ( 7 "os" 8 "strings" 9 "syscall" 10 "unsafe" 11 ) 12 13 const SupportsColorEscapes = true 14 15 var kernel32 = syscall.NewLazyDLL("kernel32.dll") 16 var getConsoleMode = kernel32.NewProc("GetConsoleMode") 17 var setConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") 18 var getConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") 19 20 type consoleScreenBufferInfo struct { 21 dwSizeX int16 22 dwSizeY int16 23 dwCursorPositionX int16 24 dwCursorPositionY int16 25 wAttributes uint16 26 srWindowLeft int16 27 srWindowTop int16 28 srWindowRight int16 29 srWindowBottom int16 30 dwMaximumWindowSizeX int16 31 dwMaximumWindowSizeY int16 32 } 33 34 func GetTerminalInfo(file *os.File) TerminalInfo { 35 fd := file.Fd() 36 37 // Is this file descriptor a terminal? 38 var unused uint32 39 isTTY, _, _ := syscall.Syscall(getConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&unused)), 0) 40 41 // Get the width of the window 42 var info consoleScreenBufferInfo 43 syscall.Syscall(getConsoleScreenBufferInfo.Addr(), 2, fd, uintptr(unsafe.Pointer(&info)), 0) 44 45 return TerminalInfo{ 46 IsTTY: isTTY != 0, 47 Width: int(info.dwSizeX) - 1, 48 Height: int(info.dwSizeY) - 1, 49 UseColorEscapes: !hasNoColorEnvironmentVariable(), 50 } 51 } 52 53 const ( 54 FOREGROUND_BLUE uint8 = 1 << iota 55 FOREGROUND_GREEN 56 FOREGROUND_RED 57 FOREGROUND_INTENSITY 58 BACKGROUND_BLUE 59 BACKGROUND_GREEN 60 BACKGROUND_RED 61 BACKGROUND_INTENSITY 62 ) 63 64 var windowsEscapeSequenceMap = map[string]uint8{ 65 TerminalColors.Reset: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, 66 TerminalColors.Dim: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, 67 TerminalColors.Bold: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, 68 69 // Apparently underlines only work with the CJK locale on Windows :( 70 TerminalColors.Underline: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, 71 72 TerminalColors.Red: FOREGROUND_RED, 73 TerminalColors.Green: FOREGROUND_GREEN, 74 TerminalColors.Blue: FOREGROUND_BLUE, 75 76 TerminalColors.Cyan: FOREGROUND_GREEN | FOREGROUND_BLUE, 77 TerminalColors.Magenta: FOREGROUND_RED | FOREGROUND_BLUE, 78 TerminalColors.Yellow: FOREGROUND_RED | FOREGROUND_GREEN, 79 80 TerminalColors.RedBgRed: FOREGROUND_RED | BACKGROUND_RED, 81 TerminalColors.RedBgWhite: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | BACKGROUND_RED, 82 TerminalColors.GreenBgGreen: FOREGROUND_GREEN | BACKGROUND_GREEN, 83 TerminalColors.GreenBgWhite: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | BACKGROUND_GREEN, 84 TerminalColors.BlueBgBlue: FOREGROUND_BLUE | BACKGROUND_BLUE, 85 TerminalColors.BlueBgWhite: FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | BACKGROUND_BLUE, 86 87 TerminalColors.CyanBgCyan: FOREGROUND_GREEN | FOREGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_BLUE, 88 TerminalColors.CyanBgBlack: BACKGROUND_GREEN | BACKGROUND_BLUE, 89 TerminalColors.MagentaBgMagenta: FOREGROUND_RED | FOREGROUND_BLUE | BACKGROUND_RED | BACKGROUND_BLUE, 90 TerminalColors.MagentaBgBlack: BACKGROUND_RED | BACKGROUND_BLUE, 91 TerminalColors.YellowBgYellow: FOREGROUND_RED | FOREGROUND_GREEN | BACKGROUND_RED | BACKGROUND_GREEN, 92 TerminalColors.YellowBgBlack: BACKGROUND_RED | BACKGROUND_GREEN, 93 } 94 95 func writeStringWithColor(file *os.File, text string) { 96 fd := file.Fd() 97 i := 0 98 99 for i < len(text) { 100 // Find the escape 101 if text[i] != 033 { 102 i++ 103 continue 104 } 105 106 // Find the 'm' 107 window := text[i:] 108 if len(window) > 8 { 109 window = window[:8] 110 } 111 m := strings.IndexByte(window, 'm') 112 if m == -1 { 113 i++ 114 continue 115 } 116 m += i + 1 117 118 // Find the escape sequence 119 attributes, ok := windowsEscapeSequenceMap[text[i:m]] 120 if !ok { 121 i++ 122 continue 123 } 124 125 // Write out the text before the escape sequence 126 file.WriteString(text[:i]) 127 128 // Apply the escape sequence 129 text = text[m:] 130 i = 0 131 setConsoleTextAttribute.Call(fd, uintptr(attributes)) 132 } 133 134 // Write out the remaining text 135 file.WriteString(text) 136 }