From 9ef50717fa0ef5e57b71aae89846eb3d14cb2b16 Mon Sep 17 00:00:00 2001 From: Adam Strzelecki Date: Tue, 13 Oct 2015 12:32:24 +0200 Subject: [PATCH 1/4] terminal: Remove superflous hStdout = conHandle Use just hStdout on Windows to keep handle to process stdout. --- lib/pure/terminal.nim | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index b6d99e4298..2c3bdb8172 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -79,25 +79,25 @@ when defined(windows): stdcall, dynlib: "kernel32", importc: "SetConsoleTextAttribute".} var - conHandle: Handle + hStdout: Handle # = createFile("CONOUT$", GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0) block: var hTemp = getStdHandle(STD_OUTPUT_HANDLE) if duplicateHandle(getCurrentProcess(), hTemp, getCurrentProcess(), - addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0: + addr(hStdout), 0, 1, DUPLICATE_SAME_ACCESS) == 0: raiseOSError(osLastError()) proc getCursorPos(): tuple [x,y: int] = var c: CONSOLESCREENBUFFERINFO - if getConsoleScreenBufferInfo(conHandle, addr(c)) == 0: + if getConsoleScreenBufferInfo(hStdout, addr(c)) == 0: raiseOSError(osLastError()) return (int(c.dwCursorPosition.X), int(c.dwCursorPosition.Y)) proc getAttributes(): int16 = var c: CONSOLESCREENBUFFERINFO # workaround Windows bugs: try several times - if getConsoleScreenBufferInfo(conHandle, addr(c)) != 0: + if getConsoleScreenBufferInfo(hStdout, addr(c)) != 0: return c.wAttributes return 0x70'i16 # ERROR: return white background, black text @@ -126,7 +126,7 @@ proc setCursorPos*(x, y: int) = var c: COORD c.X = int16(x) c.Y = int16(y) - if setConsoleCursorPosition(conHandle, c) == 0: raiseOSError(osLastError()) + if setConsoleCursorPosition(hStdout, c) == 0: raiseOSError(osLastError()) else: stdout.write("\e[" & $y & ';' & $x & 'f') @@ -135,12 +135,11 @@ proc setCursorXPos*(x: int) = ## not changed. when defined(windows): var scrbuf: CONSOLESCREENBUFFERINFO - var hStdout = conHandle if getConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError()) var origin = scrbuf.dwCursorPosition origin.X = int16(x) - if setConsoleCursorPosition(conHandle, origin) == 0: + if setConsoleCursorPosition(hStdout, origin) == 0: raiseOSError(osLastError()) else: stdout.write("\e[" & $x & 'G') @@ -151,12 +150,11 @@ when defined(windows): ## not changed. **Warning**: This is not supported on UNIX! when defined(windows): var scrbuf: CONSOLESCREENBUFFERINFO - var hStdout = conHandle if getConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError()) var origin = scrbuf.dwCursorPosition origin.Y = int16(y) - if setConsoleCursorPosition(conHandle, origin) == 0: + if setConsoleCursorPosition(hStdout, origin) == 0: raiseOSError(osLastError()) else: discard @@ -233,12 +231,11 @@ proc eraseLine* = when defined(windows): var scrbuf: CONSOLESCREENBUFFERINFO var numwrote: DWORD - var hStdout = conHandle if getConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError()) var origin = scrbuf.dwCursorPosition origin.X = 0'i16 - if setConsoleCursorPosition(conHandle, origin) == 0: + if setConsoleCursorPosition(hStdout, origin) == 0: raiseOSError(osLastError()) var ht = scrbuf.dwSize.Y - origin.Y var wt = scrbuf.dwSize.X - origin.X @@ -258,7 +255,6 @@ proc eraseScreen* = var scrbuf: CONSOLESCREENBUFFERINFO var numwrote: DWORD var origin: COORD # is inititalized to 0, 0 - var hStdout = conHandle if getConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: raiseOSError(osLastError()) @@ -278,7 +274,7 @@ proc resetAttributes* {.noconv.} = ## resets all attributes; it is advisable to register this as a quit proc ## with ``system.addQuitProc(resetAttributes)``. when defined(windows): - discard setConsoleTextAttribute(conHandle, oldAttr) + discard setConsoleTextAttribute(hStdout, oldAttr) else: stdout.write("\e[0m") @@ -308,7 +304,7 @@ proc setStyle*(style: set[Style]) = if styleBlink in style: a = a or int16(BACKGROUND_INTENSITY) if styleReverse in style: a = a or 0x4000'i16 # COMMON_LVB_REVERSE_VIDEO if styleUnderscore in style: a = a or 0x8000'i16 # COMMON_LVB_UNDERSCORE - discard setConsoleTextAttribute(conHandle, a) + discard setConsoleTextAttribute(hStdout, a) else: for s in items(style): stdout.write("\e[" & $ord(s) & 'm') @@ -319,7 +315,7 @@ proc writeStyled*(txt: string, style: set[Style] = {styleBright}) = var old = getAttributes() setStyle(style) stdout.write(txt) - discard setConsoleTextAttribute(conHandle, old) + discard setConsoleTextAttribute(hStdout, old) else: setStyle(style) stdout.write(txt) @@ -368,7 +364,7 @@ proc setForegroundColor*(fg: ForegroundColor, bright=false) = (FOREGROUND_RED or FOREGROUND_BLUE), (FOREGROUND_BLUE or FOREGROUND_GREEN), (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED)] - discard setConsoleTextAttribute(conHandle, toU16(old or lookup[fg])) + discard setConsoleTextAttribute(hStdout, toU16(old or lookup[fg])) else: gFG = ord(fg) if bright: inc(gFG, 60) @@ -389,7 +385,7 @@ proc setBackgroundColor*(bg: BackgroundColor, bright=false) = (BACKGROUND_RED or BACKGROUND_BLUE), (BACKGROUND_BLUE or BACKGROUND_GREEN), (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED)] - discard setConsoleTextAttribute(conHandle, toU16(old or lookup[bg])) + discard setConsoleTextAttribute(hStdout, toU16(old or lookup[bg])) else: gBG = ord(bg) if bright: inc(gBG, 60) From 2bc6acc808f18de3910b7db12014c394dd400b39 Mon Sep 17 00:00:00 2001 From: Adam Strzelecki Date: Tue, 13 Oct 2015 13:43:57 +0200 Subject: [PATCH 2/4] terminal: Support both styled stdout and stderr This is important if we want to write styled diagnostics to stderr, eg. some tool outputting results to stdout, but writing styled error messages to stderr. Previously this module was assuming we are writing only to stdout. Now all module procs take file handle as first argument. Wrappers assuming stdout are provided for backwards compatibility. The new terminal.styledWriteLine(f, args) is provided and documented as counterpart for unstyled plain writeLine(f, args). --- lib/pure/terminal.nim | 305 ++++++++++++++++++++++++++---------------- 1 file changed, 188 insertions(+), 117 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 2c3bdb8172..caa7881368 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -79,33 +79,49 @@ when defined(windows): stdcall, dynlib: "kernel32", importc: "SetConsoleTextAttribute".} var - hStdout: Handle - # = createFile("CONOUT$", GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0) + hStdout: Handle # = createFile("CONOUT$", GENERIC_WRITE, 0, nil, + # OPEN_ALWAYS, 0, 0) + hStderr: Handle block: - var hTemp = getStdHandle(STD_OUTPUT_HANDLE) - if duplicateHandle(getCurrentProcess(), hTemp, getCurrentProcess(), + var hStdoutTemp = getStdHandle(STD_OUTPUT_HANDLE) + if duplicateHandle(getCurrentProcess(), hStdoutTemp, getCurrentProcess(), addr(hStdout), 0, 1, DUPLICATE_SAME_ACCESS) == 0: raiseOSError(osLastError()) + var hStderrTemp = getStdHandle(STD_ERROR_HANDLE) + if duplicateHandle(getCurrentProcess(), hStderrTemp, getCurrentProcess(), + addr(hStderr), 0, 1, DUPLICATE_SAME_ACCESS) == 0: + raiseOSError(osLastError()) - proc getCursorPos(): tuple [x,y: int] = + proc getCursorPos(h: Handle): tuple [x,y: int] = var c: CONSOLESCREENBUFFERINFO - if getConsoleScreenBufferInfo(hStdout, addr(c)) == 0: + if getConsoleScreenBufferInfo(h, addr(c)) == 0: raiseOSError(osLastError()) return (int(c.dwCursorPosition.X), int(c.dwCursorPosition.Y)) - proc getAttributes(): int16 = + proc setCursorPos(h: Handle, x, y: int) = + var c: COORD + c.X = int16(x) + c.Y = int16(y) + if setConsoleCursorPosition(h, c) == 0: + raiseOSError(osLastError()) + + proc getAttributes(h: Handle): int16 = var c: CONSOLESCREENBUFFERINFO # workaround Windows bugs: try several times - if getConsoleScreenBufferInfo(hStdout, addr(c)) != 0: + if getConsoleScreenBufferInfo(h, addr(c)) != 0: return c.wAttributes return 0x70'i16 # ERROR: return white background, black text var - oldAttr = getAttributes() + oldStdoutAttr = getAttributes(hStdout) + oldStderrAttr = getAttributes(hStderr) + + template conHandle(f: File): Handle = + if f == stderr: hStderr else: hStdout else: - import termios, unsigned + import termios proc setRaw(fd: FileHandle, time: cint = TCSAFLUSH) = var mode: Termios @@ -119,164 +135,173 @@ else: mode.c_cc[VTIME] = 0.cuchar discard fd.tcsetattr(time, addr mode) -proc setCursorPos*(x, y: int) = - ## sets the terminal's cursor to the (x,y) position. (0,0) is the - ## upper left of the screen. +proc setCursorPos*(f: File, x, y: int) = + ## Sets the terminal's cursor to the (x,y) position. + ## (0,0) is the upper left of the screen. when defined(windows): - var c: COORD - c.X = int16(x) - c.Y = int16(y) - if setConsoleCursorPosition(hStdout, c) == 0: raiseOSError(osLastError()) + let h = conHandle(f) + setCursorPos(h, x, y) else: - stdout.write("\e[" & $y & ';' & $x & 'f') + f.write("\e[" & $y & ';' & $x & 'f') -proc setCursorXPos*(x: int) = - ## sets the terminal's cursor to the x position. The y position is - ## not changed. +proc setCursorXPos*(f: File, x: int) = + ## Sets the terminal's cursor to the x position. + ## The y position is not changed. when defined(windows): + let h = conHandle(f) var scrbuf: CONSOLESCREENBUFFERINFO - if getConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: + if getConsoleScreenBufferInfo(h, addr(scrbuf)) == 0: raiseOSError(osLastError()) var origin = scrbuf.dwCursorPosition origin.X = int16(x) - if setConsoleCursorPosition(hStdout, origin) == 0: + if setConsoleCursorPosition(h, origin) == 0: raiseOSError(osLastError()) else: - stdout.write("\e[" & $x & 'G') + f.write("\e[" & $x & 'G') when defined(windows): - proc setCursorYPos*(y: int) = - ## sets the terminal's cursor to the y position. The x position is - ## not changed. **Warning**: This is not supported on UNIX! + proc setCursorYPos*(f: File, y: int) = + ## Sets the terminal's cursor to the y position. + ## The x position is not changed. + ## **Warning**: This is not supported on UNIX! when defined(windows): + let h = conHandle(f) var scrbuf: CONSOLESCREENBUFFERINFO - if getConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: + if getConsoleScreenBufferInfo(h, addr(scrbuf)) == 0: raiseOSError(osLastError()) var origin = scrbuf.dwCursorPosition origin.Y = int16(y) - if setConsoleCursorPosition(hStdout, origin) == 0: + if setConsoleCursorPosition(h, origin) == 0: raiseOSError(osLastError()) else: discard -proc cursorUp*(count=1) = +proc cursorUp*(f: File, count=1) = ## Moves the cursor up by `count` rows. when defined(windows): - var p = getCursorPos() + let h = conHandle(f) + var p = getCursorPos(h) dec(p.y, count) - setCursorPos(p.x, p.y) + setCursorPos(h, p.x, p.y) else: - stdout.write("\e[" & $count & 'A') + f.write("\e[" & $count & 'A') -proc cursorDown*(count=1) = +proc cursorDown*(f: File, count=1) = ## Moves the cursor down by `count` rows. when defined(windows): - var p = getCursorPos() + let h = conHandle(f) + var p = getCursorPos(h) inc(p.y, count) - setCursorPos(p.x, p.y) + setCursorPos(h, p.x, p.y) else: - stdout.write("\e[" & $count & 'B') + f.write("\e[" & $count & 'B') -proc cursorForward*(count=1) = +proc cursorForward*(f: File, count=1) = ## Moves the cursor forward by `count` columns. when defined(windows): - var p = getCursorPos() + let h = conHandle(f) + var p = getCursorPos(h) inc(p.x, count) - setCursorPos(p.x, p.y) + setCursorPos(h, p.x, p.y) else: - stdout.write("\e[" & $count & 'C') + f.write("\e[" & $count & 'C') -proc cursorBackward*(count=1) = +proc cursorBackward*(f: File, count=1) = ## Moves the cursor backward by `count` columns. when defined(windows): - var p = getCursorPos() + let h = conHandle(f) + var p = getCursorPos(h) dec(p.x, count) - setCursorPos(p.x, p.y) + setCursorPos(h, p.x, p.y) else: - stdout.write("\e[" & $count & 'D') + f.write("\e[" & $count & 'D') when true: discard else: - proc eraseLineEnd* = + proc eraseLineEnd*(f: File) = ## Erases from the current cursor position to the end of the current line. when defined(windows): discard else: - stdout.write("\e[K") + f.write("\e[K") - proc eraseLineStart* = + proc eraseLineStart*(f: File) = ## Erases from the current cursor position to the start of the current line. when defined(windows): discard else: - stdout.write("\e[1K") + f.write("\e[1K") - proc eraseDown* = + proc eraseDown*(f: File) = ## Erases the screen from the current line down to the bottom of the screen. when defined(windows): discard else: - stdout.write("\e[J") + f.write("\e[J") - proc eraseUp* = + proc eraseUp*(f: File) = ## Erases the screen from the current line up to the top of the screen. when defined(windows): discard else: - stdout.write("\e[1J") + f.write("\e[1J") -proc eraseLine* = +proc eraseLine*(f: File) = ## Erases the entire current line. when defined(windows): + let h = conHandle(f) var scrbuf: CONSOLESCREENBUFFERINFO var numwrote: DWORD - if getConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: + if getConsoleScreenBufferInfo(h, addr(scrbuf)) == 0: raiseOSError(osLastError()) var origin = scrbuf.dwCursorPosition origin.X = 0'i16 - if setConsoleCursorPosition(hStdout, origin) == 0: + if setConsoleCursorPosition(h, origin) == 0: raiseOSError(osLastError()) var ht = scrbuf.dwSize.Y - origin.Y var wt = scrbuf.dwSize.X - origin.X - if fillConsoleOutputCharacter(hStdout,' ', ht*wt, + if fillConsoleOutputCharacter(h, ' ', ht*wt, origin, addr(numwrote)) == 0: raiseOSError(osLastError()) - if fillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt, + if fillConsoleOutputAttribute(h, scrbuf.wAttributes, ht * wt, scrbuf.dwCursorPosition, addr(numwrote)) == 0: raiseOSError(osLastError()) else: - stdout.write("\e[2K") - setCursorXPos(0) + f.write("\e[2K") + setCursorXPos(f, 0) -proc eraseScreen* = +proc eraseScreen*(f: File) = ## Erases the screen with the background colour and moves the cursor to home. when defined(windows): + let h = conHandle(f) var scrbuf: CONSOLESCREENBUFFERINFO var numwrote: DWORD var origin: COORD # is inititalized to 0, 0 - if getConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: + if getConsoleScreenBufferInfo(h, addr(scrbuf)) == 0: raiseOSError(osLastError()) let numChars = int32(scrbuf.dwSize.X)*int32(scrbuf.dwSize.Y) - if fillConsoleOutputCharacter(hStdout, ' ', numChars, + if fillConsoleOutputCharacter(h, ' ', numChars, origin, addr(numwrote)) == 0: raiseOSError(osLastError()) - if fillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, numChars, + if fillConsoleOutputAttribute(h, scrbuf.wAttributes, numChars, origin, addr(numwrote)) == 0: raiseOSError(osLastError()) - setCursorXPos(0) + setCursorXPos(f, 0) else: - stdout.write("\e[2J") + f.write("\e[2J") -proc resetAttributes* {.noconv.} = - ## resets all attributes; it is advisable to register this as a quit proc - ## with ``system.addQuitProc(resetAttributes)``. +proc resetAttributes*(f: File) = + ## Resets all attributes. when defined(windows): - discard setConsoleTextAttribute(hStdout, oldAttr) + if f == stderr: + discard setConsoleTextAttribute(hStderr, oldStderrAttr) + else: + discard setConsoleTextAttribute(hStdout, oldStdoutAttr) else: - stdout.write("\e[0m") + f.write("\e[0m") type Style* = enum ## different styles for text output @@ -296,30 +321,31 @@ when not defined(windows): gFG = 0 gBG = 0 -proc setStyle*(style: set[Style]) = - ## sets the terminal style +proc setStyle*(f: File, style: set[Style]) = + ## Sets the terminal style. when defined(windows): + let h = conHandle(f) var a = 0'i16 if styleBright in style: a = a or int16(FOREGROUND_INTENSITY) if styleBlink in style: a = a or int16(BACKGROUND_INTENSITY) if styleReverse in style: a = a or 0x4000'i16 # COMMON_LVB_REVERSE_VIDEO if styleUnderscore in style: a = a or 0x8000'i16 # COMMON_LVB_UNDERSCORE - discard setConsoleTextAttribute(hStdout, a) + discard setConsoleTextAttribute(h, a) else: for s in items(style): - stdout.write("\e[" & $ord(s) & 'm') + f.write("\e[" & $ord(s) & 'm') proc writeStyled*(txt: string, style: set[Style] = {styleBright}) = - ## writes the text `txt` in a given `style`. + ## Writes the text `txt` in a given `style` to stdout. when defined(windows): - var old = getAttributes() - setStyle(style) + var old = getAttributes(hStdout) + stdout.setStyle(style) stdout.write(txt) discard setConsoleTextAttribute(hStdout, old) else: - setStyle(style) + stdout.setStyle(style) stdout.write(txt) - resetAttributes() + stdout.resetAttributes() if gFG != 0: stdout.write("\e[" & $ord(gFG) & 'm') if gBG != 0: @@ -349,10 +375,11 @@ type {.deprecated: [TForegroundColor: ForegroundColor, TBackgroundColor: BackgroundColor].} -proc setForegroundColor*(fg: ForegroundColor, bright=false) = - ## sets the terminal's foreground color +proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) = + ## Sets the terminal's foreground color. when defined(windows): - var old = getAttributes() and not 0x0007 + let h = conHandle(f) + var old = getAttributes(h) and not 0x0007 if bright: old = old or FOREGROUND_INTENSITY const lookup: array [ForegroundColor, int] = [ @@ -364,16 +391,17 @@ proc setForegroundColor*(fg: ForegroundColor, bright=false) = (FOREGROUND_RED or FOREGROUND_BLUE), (FOREGROUND_BLUE or FOREGROUND_GREEN), (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED)] - discard setConsoleTextAttribute(hStdout, toU16(old or lookup[fg])) + discard setConsoleTextAttribute(h, toU16(old or lookup[fg])) else: gFG = ord(fg) if bright: inc(gFG, 60) - stdout.write("\e[" & $gFG & 'm') + f.write("\e[" & $gFG & 'm') -proc setBackgroundColor*(bg: BackgroundColor, bright=false) = - ## sets the terminal's background color +proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) = + ## Sets the terminal's background color. when defined(windows): - var old = getAttributes() and not 0x0070 + let h = conHandle(f) + var old = getAttributes(h) and not 0x0070 if bright: old = old or BACKGROUND_INTENSITY const lookup: array [BackgroundColor, int] = [ @@ -385,14 +413,14 @@ proc setBackgroundColor*(bg: BackgroundColor, bright=false) = (BACKGROUND_RED or BACKGROUND_BLUE), (BACKGROUND_BLUE or BACKGROUND_GREEN), (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED)] - discard setConsoleTextAttribute(hStdout, toU16(old or lookup[bg])) + discard setConsoleTextAttribute(h, toU16(old or lookup[bg])) else: gBG = ord(bg) if bright: inc(gBG, 60) - stdout.write("\e[" & $gBG & 'm') + f.write("\e[" & $gBG & 'm') proc isatty*(f: File): bool = - ## returns true if `f` is associated with a terminal device. + ## Returns true if `f` is associated with a terminal device. when defined(posix): proc isatty(fildes: FileHandle): cint {. importc: "isatty", header: "".} @@ -406,39 +434,62 @@ type TerminalCmd* = enum ## commands that can be expressed as arguments resetStyle ## reset attributes -template styledEchoProcessArg(s: string) = write stdout, s -template styledEchoProcessArg(style: Style) = setStyle({style}) -template styledEchoProcessArg(style: set[Style]) = setStyle style -template styledEchoProcessArg(color: ForegroundColor) = setForegroundColor color -template styledEchoProcessArg(color: BackgroundColor) = setBackgroundColor color -template styledEchoProcessArg(cmd: TerminalCmd) = +template styledEchoProcessArg(f: File, s: string) = write f, s +template styledEchoProcessArg(f: File, style: Style) = setStyle(f, {style}) +template styledEchoProcessArg(f: File, style: set[Style]) = setStyle f, style +template styledEchoProcessArg(f: File, color: ForegroundColor) = + setForegroundColor f, color +template styledEchoProcessArg(f: File, color: BackgroundColor) = + setBackgroundColor f, color +template styledEchoProcessArg(f: File, cmd: TerminalCmd) = when cmd == resetStyle: - resetAttributes() + resetAttributes(f) -macro styledEcho*(m: varargs[expr]): stmt = - ## to be documented. +macro styledWriteLine*(f: File, m: varargs[expr]): stmt = + ## Similar to ``writeLine``, but treating terminal style arguments specially. + ## When some argument is ``Style``, ``set[Style]``, ``ForegroundColor``, + ## ``BackgroundColor`` or ``TerminalCmd`` then it is not sent directly to + ## ``f``, but instead corresponding terminal style proc is called. + ## + ## Example: + ## + ## .. code-block:: nim + ## + ## proc error(msg: string) = + ## styleWriteLine(stderr, fgRed, "Error: ", resetStyle, msg) + ## let m = callsite() var reset = false result = newNimNode(nnkStmtList) - for i in countup(1, m.len - 1): + for i in countup(2, m.len - 1): let item = m[i] case item.kind of nnkStrLit..nnkTripleStrLit: if i == m.len - 1: # optimize if string literal is last, just call writeLine - result.add(newCall(bindSym"writeLine", bindSym"stdout", item)) - if reset: result.add(newCall(bindSym"resetAttributes")) + result.add(newCall(bindSym"writeLine", f, item)) + if reset: result.add(newCall(bindSym"resetAttributes", f)) return else: # if it is string literal just call write, do not enable reset - result.add(newCall(bindSym"write", bindSym"stdout", item)) + result.add(newCall(bindSym"write", f, item)) else: - result.add(newCall(bindSym"styledEchoProcessArg", item)) + result.add(newCall(bindSym"styledEchoProcessArg", f, item)) reset = true - result.add(newCall(bindSym"write", bindSym"stdout", newStrLitNode("\n"))) - if reset: result.add(newCall(bindSym"resetAttributes")) + result.add(newCall(bindSym"write", f, newStrLitNode("\n"))) + if reset: result.add(newCall(bindSym"resetAttributes", f)) + +macro callStyledEcho(args: varargs[expr]): stmt = + result = newCall(bindSym"styledWriteLine") + result.add(bindSym"stdout") + for arg in children(args[0][1]): + result.add(arg) + +template styledEcho*(args: varargs[expr]): expr = + ## Echoes styles arguments to stdout using ``styledWriteLine``. + callStyledEcho(args) when defined(nimdoc): proc getch*(): char = @@ -458,15 +509,35 @@ elif not defined(windows): result = stdin.readChar() discard fd.tcsetattr(TCSADRAIN, addr oldMode) +# Wrappers assuming output to stdout: +template setCursorPos*(x, y: int) = setCursorPos(stdout, x, y) +template setCursorXPos*(x: int) = setCursorXPos(stdout, x) +when defined(windows): + template setCursorYPos(x: int) = setCursorYPos(stdout, x) +template cursorUp*(count=1) = cursorUp(stdout, f) +template cursorDown*(count=1) = cursorDown(stdout, f) +template cursorForward*(count=1) = cursorForward(stdout, f) +template cursorBackward*(count=1) = cursorBackward(stdout, f) +template eraseLine*() = eraseLine(stdout) +template eraseScreen*() = eraseScreen(stdout) +template setStyle*(style: set[Style]) = + setStyle(stdout, style) +template setForegroundColor*(fg: ForegroundColor, bright=false) = + setForegroundColor(stdout, fg, bright) +template setBackgroundColor*(bg: BackgroundColor, bright=false) = + setBackgroundColor(stdout, bg, bright) +proc resetAttributes*() {.noconv.} = + ## Resets all attributes on stdout. + ## It is advisable to register this as a quit proc with + ## ``system.addQuitProc(resetAttributes)``. + resetAttributes(stdout) + when not defined(testing) and isMainModule: - system.addQuitProc(resetAttributes) + #system.addQuitProc(resetAttributes) write(stdout, "never mind") - eraseLine() - #setCursorPos(2, 2) - writeStyled("styled text ", {styleBright, styleBlink, styleUnderscore}) - setBackGroundColor(bgCyan, true) - setForeGroundColor(fgBlue) - writeLine(stdout, "ordinary text") - - styledEcho("styled text ", {styleBright, styleBlink, styleUnderscore}) - + stdout.eraseLine() + stdout.styledWriteLine("styled text ", {styleBright, styleBlink, styleUnderscore}) + stdout.setBackGroundColor(bgCyan, true) + stdout.setForeGroundColor(fgBlue) + stdout.writeLine("ordinary text") + stdout.resetAttributes() From abb82554b7e9fce4073e6c072174b0ecb8a92d2f Mon Sep 17 00:00:00 2001 From: Adam Strzelecki Date: Tue, 13 Oct 2015 13:49:34 +0200 Subject: [PATCH 3/4] compiler/msgs: Default to stderr for diagnostics Previously we were defaulting to stdout for diagnostics, which could interfere with scripts or `nim c -r' programs outputting their results to stdout, possibly mixing their output with compiler messages. This change makes now Nim to be inline with other compilers emitting diagnostics to stderr. Also now --stdout option has proper meaning making all diagnostics to be sent to stdout instead. NOTE: Tester now calls compiler with --stdout. --- compiler/msgs.nim | 38 +++++++++++++++++++++----------------- tests/testament/tester.nim | 3 ++- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 4dd1341777..3fabf6bbf2 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -734,20 +734,22 @@ proc outWriteln*(s: string) = flushFile(stdout) proc msgWriteln*(s: string) = - ## Writes to stdout. If --stdout option is given, writes to stderr instead. + ## Writes to stderr. If --stdout option is given, writes to stdout instead. #if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return if not isNil(writelnHook): writelnHook(s) elif optStdout in gGlobalOptions: - if eStdErr in errorOutputs: - writeLine(stderr, s) - flushFile(stderr) - else: if eStdOut in errorOutputs: writeLine(stdout, s) flushFile(stdout) + else: + if eStdErr in errorOutputs: + writeLine(stderr, s) + # On Windows stderr is fully-buffered when piped, regardless of C std. + when defined(windows): + flushFile(stderr) macro callIgnoringStyle(theProc: typed, first: typed, args: varargs[expr]): stmt = @@ -767,8 +769,9 @@ macro callIgnoringStyle(theProc: typed, first: typed, typ != typTerminalCmd: result.add(arg) -macro callStyledEcho(args: varargs[expr]): stmt = - result = newCall(bindSym"styledEcho") +macro callStyledWriteLineStderr(args: varargs[expr]): stmt = + result = newCall(bindSym"styledWriteLine") + result.add(bindSym"stderr") for arg in children(args[0][1]): result.add(arg) @@ -782,16 +785,18 @@ template styledMsgWriteln*(args: varargs[expr]) = if not isNil(writelnHook): callIgnoringStyle(callWritelnHook, nil, args) elif optStdout in gGlobalOptions: - if eStdErr in errorOutputs: - callIgnoringStyle(writeLine, stderr, args) - flushFile(stderr) - else: if eStdOut in errorOutputs: + callIgnoringStyle(writeLine, stdout, args) + flushFile(stdout) + else: + if eStdErr in errorOutputs: if optUseColors in gGlobalOptions: - callStyledEcho(args) + callStyledWriteLineStderr(args) else: - callIgnoringStyle(writeLine, stdout, args) - flushFile stdout + callIgnoringStyle(writeLine, stderr, args) + # On Windows stderr is fully-buffered when piped, regardless of C std. + when defined(windows): + flushFile(stderr) proc coordToStr(coord: int): string = if coord == -1: result = "???" @@ -885,8 +890,7 @@ proc rawMessage*(msg: TMsgKind, arg: string) = proc resetAttributes* = if {optUseColors, optStdout} * gGlobalOptions == {optUseColors}: - terminal.resetAttributes() - stdout.flushFile() + terminal.resetAttributes(stderr) proc writeSurroundingSrc(info: TLineInfo) = const indent = " " @@ -1032,5 +1036,5 @@ proc listHints*() = ]) # enable colors by default on terminals -if terminal.isatty(stdout): +if terminal.isatty(stderr): incl(gGlobalOptions, optUseColors) diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 451bee1d18..b1e8ac0992 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -292,7 +292,8 @@ proc testSpec(r: var TResults, test: TTest) = case expected.action of actionCompile: var given = callCompiler(expected.cmd, test.name, - test.options & " --hint[Path]:off --hint[Processing]:off", test.target) + test.options & " --stdout --hint[Path]:off --hint[Processing]:off", + test.target) compilerOutputTests(test, given, expected, r) of actionRun, actionRunNoSpec: # In this branch of code "early return" pattern is clearer than deep From 78568859c5ae4441f9292cfa381e4e3e6e2e5ffe Mon Sep 17 00:00:00 2001 From: Adam Strzelecki Date: Fri, 16 Oct 2015 20:49:49 +0200 Subject: [PATCH 4/4] compiler/vm: Use stdout too in VM time echo Now VM time echo outputs to stdout too, same as compile time echo, rather using same handle as compiler diagnostics (stderr default). --- compiler/msgs.nim | 9 +++++++++ compiler/vm.nim | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 3fabf6bbf2..442d9efc23 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -751,6 +751,15 @@ proc msgWriteln*(s: string) = when defined(windows): flushFile(stderr) +proc stdoutWriteln*(s: string) = + ## Writes to stdout. + ## Should be used only for VM time equivalents to procs outputting to stdout. + if not isNil(writelnHook): + writelnHook(s) + else: + writeLine(stdout, s) + flushFile(stdout) + macro callIgnoringStyle(theProc: typed, first: typed, args: varargs[expr]): stmt = let typForegroundColor = bindSym"ForegroundColor".getType diff --git a/compiler/vm.nim b/compiler/vm.nim index 7ba4aaeb6b..6d0c63f146 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -811,13 +811,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcEcho: let rb = instr.regB if rb == 1: - msgWriteln(regs[ra].node.strVal) + stdoutWriteln(regs[ra].node.strVal) else: var outp = "" for i in ra..ra+rb-1: #if regs[i].kind != rkNode: debug regs[i] outp.add(regs[i].node.strVal) - msgWriteln(outp) + stdoutWriteln(outp) of opcContainsSet: decodeBC(rkInt) regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode))