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  }