diff options
Diffstat (limited to 'lib/pure')
-rw-r--r-- | lib/pure/terminal.nim | 158 |
1 files changed, 73 insertions, 85 deletions
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index f2be5cf18..904274ade 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -19,22 +19,30 @@ import macros import strformat from strutils import toLowerAscii -import colors +import colors, tables -const - hasThreadSupport = compileOption("threads") +when defined(windows): + import winlean -when not hasThreadSupport: - import tables - var - colorsFGCache = initTable[Color, string]() - colorsBGCache = initTable[Color, string]() - styleCache = initTable[int, string]() +type + PTerminal = ref object + trueColorIsSupported: bool + trueColorIsEnabled: bool + fgSetColor: bool + when defined(windows): + hStdout: Handle + hStderr: Handle + oldStdoutAttr: int16 + oldStderrAttr: int16 -var - trueColorIsSupported: bool - trueColorIsEnabled: bool - fgSetColor: bool +var gTerm {.threadvar.}: PTerminal + +proc newTerminal(): PTerminal + +proc getTerminal(): PTerminal {.inline.} = + if isNil(gTerm): + gTerm = newTerminal() + result = gTerm const fgPrefix = "\x1b[38;2;" @@ -156,23 +164,6 @@ when defined(windows): proc setConsoleMode(hConsoleHandle: Handle, dwMode: DWORD): WINBOOL{. stdcall, dynlib: "kernel32", importc: "SetConsoleMode".} - var - hStdout: Handle # = createFile("CONOUT$", GENERIC_WRITE, 0, nil, - # OPEN_ALWAYS, 0, 0) - hStderr: Handle - - block: - var hStdoutTemp = getStdHandle(STD_OUTPUT_HANDLE) - if duplicateHandle(getCurrentProcess(), hStdoutTemp, getCurrentProcess(), - addr(hStdout), 0, 1, DUPLICATE_SAME_ACCESS) == 0: - when defined(consoleapp): - raiseOSError(osLastError()) - var hStderrTemp = getStdHandle(STD_ERROR_HANDLE) - if duplicateHandle(getCurrentProcess(), hStderrTemp, getCurrentProcess(), - addr(hStderr), 0, 1, DUPLICATE_SAME_ACCESS) == 0: - when defined(consoleapp): - raiseOSError(osLastError()) - proc getCursorPos(h: Handle): tuple [x,y: int] = var c: CONSOLESCREENBUFFERINFO if getConsoleScreenBufferInfo(h, addr(c)) == 0: @@ -193,12 +184,23 @@ when defined(windows): return c.wAttributes return 0x70'i16 # ERROR: return white background, black text - var - oldStdoutAttr = getAttributes(hStdout) - oldStderrAttr = getAttributes(hStderr) + proc initTerminal(term: PTerminal) = + var hStdoutTemp = getStdHandle(STD_OUTPUT_HANDLE) + if duplicateHandle(getCurrentProcess(), hStdoutTemp, getCurrentProcess(), + addr(term.hStdout), 0, 1, DUPLICATE_SAME_ACCESS) == 0: + when defined(consoleapp): + raiseOSError(osLastError()) + var hStderrTemp = getStdHandle(STD_ERROR_HANDLE) + if duplicateHandle(getCurrentProcess(), hStderrTemp, getCurrentProcess(), + addr(term.hStderr), 0, 1, DUPLICATE_SAME_ACCESS) == 0: + when defined(consoleapp): + raiseOSError(osLastError()) + term.oldStdoutAttr = getAttributes(term.hStdout) + term.oldStderrAttr = getAttributes(term.hStderr) template conHandle(f: File): Handle = - if f == stderr: hStderr else: hStdout + let term = getTerminal() + if f == stderr: term.hStderr else: term.hStdout else: import termios, posix, os, parseutils @@ -459,10 +461,11 @@ proc eraseScreen*(f: File) = proc resetAttributes*(f: File) = ## Resets all attributes. when defined(windows): + let term = getTerminal() if f == stderr: - discard setConsoleTextAttribute(hStderr, oldStderrAttr) + discard setConsoleTextAttribute(term.hStderr, term.oldStderrAttr) else: - discard setConsoleTextAttribute(hStdout, oldStdoutAttr) + discard setConsoleTextAttribute(term.hStdout, term.oldStdoutAttr) else: f.write(ansiResetCode) @@ -487,14 +490,7 @@ when not defined(windows): gBG {.threadvar.}: int proc ansiStyleCode*(style: int): string = - when hasThreadSupport: - result = fmt"{stylePrefix}{style}m" - else: - if styleCache.hasKey(style): - result = styleCache[style] - else: - result = fmt"{stylePrefix}{style}m" - styleCache[style] = result + result = fmt"{stylePrefix}{style}m" template ansiStyleCode*(style: Style): string = ansiStyleCode(style.int) @@ -521,10 +517,11 @@ proc setStyle*(f: File, style: set[Style]) = proc writeStyled*(txt: string, style: set[Style] = {styleBright}) = ## Writes the text `txt` in a given `style` to stdout. when defined(windows): - var old = getAttributes(hStdout) + let term = getTerminal() + var old = getAttributes(term.hStdout) stdout.setStyle(style) stdout.write(txt) - discard setConsoleTextAttribute(hStdout, old) + discard setConsoleTextAttribute(term.hStdout, old) else: stdout.setStyle(style) stdout.write(txt) @@ -633,32 +630,16 @@ template ansiForegroundColorCode*(fg: static[ForegroundColor], ansiStyleCode(fg.int + bright.int * 60) proc ansiForegroundColorCode*(color: Color): string = - when hasThreadSupport: - let rgb = extractRGB(color) - result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m" - else: - if colorsFGCache.hasKey(color): - result = colorsFGCache[color] - else: - let rgb = extractRGB(color) - result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m" - colorsFGCache[color] = result + let rgb = extractRGB(color) + result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m" template ansiForegroundColorCode*(color: static[Color]): string = const rgb = extractRGB(color) (static(fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m")) proc ansiBackgroundColorCode*(color: Color): string = - when hasThreadSupport: - let rgb = extractRGB(color) - result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m" - else: - if colorsBGCache.hasKey(color): - result = colorsBGCache[color] - else: - let rgb = extractRGB(color) - result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m" - colorsFGCache[color] = result + let rgb = extractRGB(color) + result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m" template ansiBackgroundColorCode*(color: static[Color]): string = const rgb = extractRGB(color) @@ -666,16 +647,17 @@ template ansiBackgroundColorCode*(color: static[Color]): string = proc setForegroundColor*(f: File, color: Color) = ## Sets the terminal's foreground true color. - if trueColorIsEnabled: + if getTerminal().trueColorIsEnabled: f.write(ansiForegroundColorCode(color)) proc setBackgroundColor*(f: File, color: Color) = ## Sets the terminal's background true color. - if trueColorIsEnabled: + if getTerminal().trueColorIsEnabled: f.write(ansiBackgroundColorCode(color)) proc setTrueColor(f: File, color: Color) = - if fgSetColor: + let term = getTerminal() + if term.fgSetColor: setForegroundColor(f, color) else: setBackgroundColor(f, color) @@ -727,11 +709,10 @@ macro styledWrite*(f: File, m: varargs[typed]): untyped = ## stdout.styledWrite(fgRed, "red text ") ## stdout.styledWrite(fgGreen, "green text") ## - let m = callsite() var reset = false result = newNimNode(nnkStmtList) - for i in countup(2, m.len - 1): + for i in countup(0, m.len - 1): let item = m[i] case item.kind of nnkStrLit..nnkTripleStrLit: @@ -872,54 +853,61 @@ proc resetAttributes*() {.noconv.} = proc isTrueColorSupported*(): bool = ## Returns true if a terminal supports true color. - return trueColorIsSupported + return getTerminal().trueColorIsSupported when defined(windows): import os proc enableTrueColors*() = ## Enable true color. + var term = getTerminal() when defined(windows): var ver: OSVERSIONINFO ver.dwOSVersionInfoSize = sizeof(ver).DWORD let res = getVersionExW(addr ver) if res == 0: - trueColorIsSupported = false + term.trueColorIsSupported = false else: - trueColorIsSupported = ver.dwMajorVersion > 10 or + term.trueColorIsSupported = ver.dwMajorVersion > 10 or (ver.dwMajorVersion == 10 and (ver.dwMinorVersion > 0 or (ver.dwMinorVersion == 0 and ver.dwBuildNumber >= 10586))) - if not trueColorIsSupported: - trueColorIsSupported = getEnv("ANSICON_DEF").len > 0 + if not term.trueColorIsSupported: + term.trueColorIsSupported = getEnv("ANSICON_DEF").len > 0 - if trueColorIsSupported: + if term.trueColorIsSupported: if getEnv("ANSICON_DEF").len == 0: var mode: DWORD = 0 if getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), addr(mode)) != 0: mode = mode or ENABLE_VIRTUAL_TERMINAL_PROCESSING if setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), mode) != 0: - trueColorIsEnabled = true + term.trueColorIsEnabled = true else: - trueColorIsEnabled = false + term.trueColorIsEnabled = false else: - trueColorIsEnabled = true + term.trueColorIsEnabled = true else: - trueColorIsSupported = string(getEnv("COLORTERM")).toLowerAscii() in ["truecolor", "24bit"] - trueColorIsEnabled = trueColorIsSupported + term.trueColorIsSupported = string(getEnv("COLORTERM")).toLowerAscii() in ["truecolor", "24bit"] + term.trueColorIsEnabled = term.trueColorIsSupported proc disableTrueColors*() = ## Disable true color. + var term = getTerminal() when defined(windows): - if trueColorIsSupported: + if term.trueColorIsSupported: if getEnv("ANSICON_DEF").len == 0: var mode: DWORD = 0 if getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), addr(mode)) != 0: mode = mode and not ENABLE_VIRTUAL_TERMINAL_PROCESSING discard setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), mode) - trueColorIsEnabled = false + term.trueColorIsEnabled = false else: - trueColorIsEnabled = false + term.trueColorIsEnabled = false + +proc newTerminal(): PTerminal = + new result + when defined(windows): + initTerminal(result) when not defined(testing) and isMainModule: assert ansiStyleCode(styleBright) == "\e[1m" |