Merge pull request #3441 from nanoant/patch/msg-to-stderr-by-default

Extend terminal module, use stderr for Nim compiler diagnostics by default
This commit is contained in:
Andreas Rumpf 2015-10-16 21:51:40 +02:00
commit ae7bba7456
4 changed files with 224 additions and 143 deletions

View file

@ -734,18 +734,29 @@ proc outWriteln*(s: string) =
flushFile(stdout) flushFile(stdout)
proc msgWriteln*(s: string) = 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 gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
if not isNil(writelnHook): if not isNil(writelnHook):
writelnHook(s) writelnHook(s)
elif optStdout in gGlobalOptions: elif optStdout in gGlobalOptions:
if eStdOut in errorOutputs:
writeLine(stdout, s)
flushFile(stdout)
else:
if eStdErr in errorOutputs: if eStdErr in errorOutputs:
writeLine(stderr, s) writeLine(stderr, s)
# On Windows stderr is fully-buffered when piped, regardless of C std.
when defined(windows):
flushFile(stderr) 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: else:
if eStdOut in errorOutputs:
writeLine(stdout, s) writeLine(stdout, s)
flushFile(stdout) flushFile(stdout)
@ -767,8 +778,9 @@ macro callIgnoringStyle(theProc: typed, first: typed,
typ != typTerminalCmd: typ != typTerminalCmd:
result.add(arg) result.add(arg)
macro callStyledEcho(args: varargs[expr]): stmt = macro callStyledWriteLineStderr(args: varargs[expr]): stmt =
result = newCall(bindSym"styledEcho") result = newCall(bindSym"styledWriteLine")
result.add(bindSym"stderr")
for arg in children(args[0][1]): for arg in children(args[0][1]):
result.add(arg) result.add(arg)
@ -782,16 +794,18 @@ template styledMsgWriteln*(args: varargs[expr]) =
if not isNil(writelnHook): if not isNil(writelnHook):
callIgnoringStyle(callWritelnHook, nil, args) callIgnoringStyle(callWritelnHook, nil, args)
elif optStdout in gGlobalOptions: elif optStdout in gGlobalOptions:
if eStdErr in errorOutputs:
callIgnoringStyle(writeLine, stderr, args)
flushFile(stderr)
else:
if eStdOut in errorOutputs: if eStdOut in errorOutputs:
if optUseColors in gGlobalOptions:
callStyledEcho(args)
else:
callIgnoringStyle(writeLine, stdout, args) callIgnoringStyle(writeLine, stdout, args)
flushFile stdout flushFile(stdout)
else:
if eStdErr in errorOutputs:
if optUseColors in gGlobalOptions:
callStyledWriteLineStderr(args)
else:
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 = proc coordToStr(coord: int): string =
if coord == -1: result = "???" if coord == -1: result = "???"
@ -885,8 +899,7 @@ proc rawMessage*(msg: TMsgKind, arg: string) =
proc resetAttributes* = proc resetAttributes* =
if {optUseColors, optStdout} * gGlobalOptions == {optUseColors}: if {optUseColors, optStdout} * gGlobalOptions == {optUseColors}:
terminal.resetAttributes() terminal.resetAttributes(stderr)
stdout.flushFile()
proc writeSurroundingSrc(info: TLineInfo) = proc writeSurroundingSrc(info: TLineInfo) =
const indent = " " const indent = " "
@ -1032,5 +1045,5 @@ proc listHints*() =
]) ])
# enable colors by default on terminals # enable colors by default on terminals
if terminal.isatty(stdout): if terminal.isatty(stderr):
incl(gGlobalOptions, optUseColors) incl(gGlobalOptions, optUseColors)

View file

@ -811,13 +811,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcEcho: of opcEcho:
let rb = instr.regB let rb = instr.regB
if rb == 1: if rb == 1:
msgWriteln(regs[ra].node.strVal) stdoutWriteln(regs[ra].node.strVal)
else: else:
var outp = "" var outp = ""
for i in ra..ra+rb-1: for i in ra..ra+rb-1:
#if regs[i].kind != rkNode: debug regs[i] #if regs[i].kind != rkNode: debug regs[i]
outp.add(regs[i].node.strVal) outp.add(regs[i].node.strVal)
msgWriteln(outp) stdoutWriteln(outp)
of opcContainsSet: of opcContainsSet:
decodeBC(rkInt) decodeBC(rkInt)
regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode)) regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode))

View file

@ -79,33 +79,49 @@ when defined(windows):
stdcall, dynlib: "kernel32", importc: "SetConsoleTextAttribute".} stdcall, dynlib: "kernel32", importc: "SetConsoleTextAttribute".}
var var
conHandle: Handle hStdout: Handle # = createFile("CONOUT$", GENERIC_WRITE, 0, nil,
# = createFile("CONOUT$", GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0) # OPEN_ALWAYS, 0, 0)
hStderr: Handle
block: block:
var hTemp = getStdHandle(STD_OUTPUT_HANDLE) var hStdoutTemp = getStdHandle(STD_OUTPUT_HANDLE)
if duplicateHandle(getCurrentProcess(), hTemp, getCurrentProcess(), if duplicateHandle(getCurrentProcess(), hStdoutTemp, getCurrentProcess(),
addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0: 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()) raiseOSError(osLastError())
proc getCursorPos(): tuple [x,y: int] = proc getCursorPos(h: Handle): tuple [x,y: int] =
var c: CONSOLESCREENBUFFERINFO var c: CONSOLESCREENBUFFERINFO
if getConsoleScreenBufferInfo(conHandle, addr(c)) == 0: if getConsoleScreenBufferInfo(h, addr(c)) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
return (int(c.dwCursorPosition.X), int(c.dwCursorPosition.Y)) 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 var c: CONSOLESCREENBUFFERINFO
# workaround Windows bugs: try several times # workaround Windows bugs: try several times
if getConsoleScreenBufferInfo(conHandle, addr(c)) != 0: if getConsoleScreenBufferInfo(h, addr(c)) != 0:
return c.wAttributes return c.wAttributes
return 0x70'i16 # ERROR: return white background, black text return 0x70'i16 # ERROR: return white background, black text
var var
oldAttr = getAttributes() oldStdoutAttr = getAttributes(hStdout)
oldStderrAttr = getAttributes(hStderr)
template conHandle(f: File): Handle =
if f == stderr: hStderr else: hStdout
else: else:
import termios, unsigned import termios
proc setRaw(fd: FileHandle, time: cint = TCSAFLUSH) = proc setRaw(fd: FileHandle, time: cint = TCSAFLUSH) =
var mode: Termios var mode: Termios
@ -119,168 +135,173 @@ else:
mode.c_cc[VTIME] = 0.cuchar mode.c_cc[VTIME] = 0.cuchar
discard fd.tcsetattr(time, addr mode) discard fd.tcsetattr(time, addr mode)
proc setCursorPos*(x, y: int) = proc setCursorPos*(f: File, x, y: int) =
## sets the terminal's cursor to the (x,y) position. (0,0) is the ## Sets the terminal's cursor to the (x,y) position.
## upper left of the screen. ## (0,0) is the upper left of the screen.
when defined(windows): when defined(windows):
var c: COORD let h = conHandle(f)
c.X = int16(x) setCursorPos(h, x, y)
c.Y = int16(y)
if setConsoleCursorPosition(conHandle, c) == 0: raiseOSError(osLastError())
else: else:
stdout.write("\e[" & $y & ';' & $x & 'f') f.write("\e[" & $y & ';' & $x & 'f')
proc setCursorXPos*(x: int) = proc setCursorXPos*(f: File, x: int) =
## sets the terminal's cursor to the x position. The y position is ## Sets the terminal's cursor to the x position.
## not changed. ## The y position is not changed.
when defined(windows): when defined(windows):
let h = conHandle(f)
var scrbuf: CONSOLESCREENBUFFERINFO var scrbuf: CONSOLESCREENBUFFERINFO
var hStdout = conHandle if getConsoleScreenBufferInfo(h, addr(scrbuf)) == 0:
if getConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
var origin = scrbuf.dwCursorPosition var origin = scrbuf.dwCursorPosition
origin.X = int16(x) origin.X = int16(x)
if setConsoleCursorPosition(conHandle, origin) == 0: if setConsoleCursorPosition(h, origin) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
else: else:
stdout.write("\e[" & $x & 'G') f.write("\e[" & $x & 'G')
when defined(windows): when defined(windows):
proc setCursorYPos*(y: int) = proc setCursorYPos*(f: File, y: int) =
## sets the terminal's cursor to the y position. The x position is ## Sets the terminal's cursor to the y position.
## not changed. **Warning**: This is not supported on UNIX! ## The x position is not changed.
## **Warning**: This is not supported on UNIX!
when defined(windows): when defined(windows):
let h = conHandle(f)
var scrbuf: CONSOLESCREENBUFFERINFO var scrbuf: CONSOLESCREENBUFFERINFO
var hStdout = conHandle if getConsoleScreenBufferInfo(h, addr(scrbuf)) == 0:
if getConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
var origin = scrbuf.dwCursorPosition var origin = scrbuf.dwCursorPosition
origin.Y = int16(y) origin.Y = int16(y)
if setConsoleCursorPosition(conHandle, origin) == 0: if setConsoleCursorPosition(h, origin) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
else: else:
discard discard
proc cursorUp*(count=1) = proc cursorUp*(f: File, count=1) =
## Moves the cursor up by `count` rows. ## Moves the cursor up by `count` rows.
when defined(windows): when defined(windows):
var p = getCursorPos() let h = conHandle(f)
var p = getCursorPos(h)
dec(p.y, count) dec(p.y, count)
setCursorPos(p.x, p.y) setCursorPos(h, p.x, p.y)
else: 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. ## Moves the cursor down by `count` rows.
when defined(windows): when defined(windows):
var p = getCursorPos() let h = conHandle(f)
var p = getCursorPos(h)
inc(p.y, count) inc(p.y, count)
setCursorPos(p.x, p.y) setCursorPos(h, p.x, p.y)
else: 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. ## Moves the cursor forward by `count` columns.
when defined(windows): when defined(windows):
var p = getCursorPos() let h = conHandle(f)
var p = getCursorPos(h)
inc(p.x, count) inc(p.x, count)
setCursorPos(p.x, p.y) setCursorPos(h, p.x, p.y)
else: 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. ## Moves the cursor backward by `count` columns.
when defined(windows): when defined(windows):
var p = getCursorPos() let h = conHandle(f)
var p = getCursorPos(h)
dec(p.x, count) dec(p.x, count)
setCursorPos(p.x, p.y) setCursorPos(h, p.x, p.y)
else: else:
stdout.write("\e[" & $count & 'D') f.write("\e[" & $count & 'D')
when true: when true:
discard discard
else: else:
proc eraseLineEnd* = proc eraseLineEnd*(f: File) =
## Erases from the current cursor position to the end of the current line. ## Erases from the current cursor position to the end of the current line.
when defined(windows): when defined(windows):
discard discard
else: 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. ## Erases from the current cursor position to the start of the current line.
when defined(windows): when defined(windows):
discard discard
else: 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. ## Erases the screen from the current line down to the bottom of the screen.
when defined(windows): when defined(windows):
discard discard
else: 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. ## Erases the screen from the current line up to the top of the screen.
when defined(windows): when defined(windows):
discard discard
else: else:
stdout.write("\e[1J") f.write("\e[1J")
proc eraseLine* = proc eraseLine*(f: File) =
## Erases the entire current line. ## Erases the entire current line.
when defined(windows): when defined(windows):
let h = conHandle(f)
var scrbuf: CONSOLESCREENBUFFERINFO var scrbuf: CONSOLESCREENBUFFERINFO
var numwrote: DWORD var numwrote: DWORD
var hStdout = conHandle if getConsoleScreenBufferInfo(h, addr(scrbuf)) == 0:
if getConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
var origin = scrbuf.dwCursorPosition var origin = scrbuf.dwCursorPosition
origin.X = 0'i16 origin.X = 0'i16
if setConsoleCursorPosition(conHandle, origin) == 0: if setConsoleCursorPosition(h, origin) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
var ht = scrbuf.dwSize.Y - origin.Y var ht = scrbuf.dwSize.Y - origin.Y
var wt = scrbuf.dwSize.X - origin.X var wt = scrbuf.dwSize.X - origin.X
if fillConsoleOutputCharacter(hStdout,' ', ht*wt, if fillConsoleOutputCharacter(h, ' ', ht*wt,
origin, addr(numwrote)) == 0: origin, addr(numwrote)) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
if fillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt, if fillConsoleOutputAttribute(h, scrbuf.wAttributes, ht * wt,
scrbuf.dwCursorPosition, addr(numwrote)) == 0: scrbuf.dwCursorPosition, addr(numwrote)) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
else: else:
stdout.write("\e[2K") f.write("\e[2K")
setCursorXPos(0) setCursorXPos(f, 0)
proc eraseScreen* = proc eraseScreen*(f: File) =
## Erases the screen with the background colour and moves the cursor to home. ## Erases the screen with the background colour and moves the cursor to home.
when defined(windows): when defined(windows):
let h = conHandle(f)
var scrbuf: CONSOLESCREENBUFFERINFO var scrbuf: CONSOLESCREENBUFFERINFO
var numwrote: DWORD var numwrote: DWORD
var origin: COORD # is inititalized to 0, 0 var origin: COORD # is inititalized to 0, 0
var hStdout = conHandle
if getConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: if getConsoleScreenBufferInfo(h, addr(scrbuf)) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
let numChars = int32(scrbuf.dwSize.X)*int32(scrbuf.dwSize.Y) let numChars = int32(scrbuf.dwSize.X)*int32(scrbuf.dwSize.Y)
if fillConsoleOutputCharacter(hStdout, ' ', numChars, if fillConsoleOutputCharacter(h, ' ', numChars,
origin, addr(numwrote)) == 0: origin, addr(numwrote)) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
if fillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, numChars, if fillConsoleOutputAttribute(h, scrbuf.wAttributes, numChars,
origin, addr(numwrote)) == 0: origin, addr(numwrote)) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
setCursorXPos(0) setCursorXPos(f, 0)
else: else:
stdout.write("\e[2J") f.write("\e[2J")
proc resetAttributes* {.noconv.} = proc resetAttributes*(f: File) =
## resets all attributes; it is advisable to register this as a quit proc ## Resets all attributes.
## with ``system.addQuitProc(resetAttributes)``.
when defined(windows): when defined(windows):
discard setConsoleTextAttribute(conHandle, oldAttr) if f == stderr:
discard setConsoleTextAttribute(hStderr, oldStderrAttr)
else: else:
stdout.write("\e[0m") discard setConsoleTextAttribute(hStdout, oldStdoutAttr)
else:
f.write("\e[0m")
type type
Style* = enum ## different styles for text output Style* = enum ## different styles for text output
@ -300,30 +321,31 @@ when not defined(windows):
gFG = 0 gFG = 0
gBG = 0 gBG = 0
proc setStyle*(style: set[Style]) = proc setStyle*(f: File, style: set[Style]) =
## sets the terminal style ## Sets the terminal style.
when defined(windows): when defined(windows):
let h = conHandle(f)
var a = 0'i16 var a = 0'i16
if styleBright in style: a = a or int16(FOREGROUND_INTENSITY) if styleBright in style: a = a or int16(FOREGROUND_INTENSITY)
if styleBlink in style: a = a or int16(BACKGROUND_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 styleReverse in style: a = a or 0x4000'i16 # COMMON_LVB_REVERSE_VIDEO
if styleUnderscore in style: a = a or 0x8000'i16 # COMMON_LVB_UNDERSCORE if styleUnderscore in style: a = a or 0x8000'i16 # COMMON_LVB_UNDERSCORE
discard setConsoleTextAttribute(conHandle, a) discard setConsoleTextAttribute(h, a)
else: else:
for s in items(style): 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}) = 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): when defined(windows):
var old = getAttributes() var old = getAttributes(hStdout)
setStyle(style) stdout.setStyle(style)
stdout.write(txt) stdout.write(txt)
discard setConsoleTextAttribute(conHandle, old) discard setConsoleTextAttribute(hStdout, old)
else: else:
setStyle(style) stdout.setStyle(style)
stdout.write(txt) stdout.write(txt)
resetAttributes() stdout.resetAttributes()
if gFG != 0: if gFG != 0:
stdout.write("\e[" & $ord(gFG) & 'm') stdout.write("\e[" & $ord(gFG) & 'm')
if gBG != 0: if gBG != 0:
@ -353,10 +375,11 @@ type
{.deprecated: [TForegroundColor: ForegroundColor, {.deprecated: [TForegroundColor: ForegroundColor,
TBackgroundColor: BackgroundColor].} TBackgroundColor: BackgroundColor].}
proc setForegroundColor*(fg: ForegroundColor, bright=false) = proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) =
## sets the terminal's foreground color ## Sets the terminal's foreground color.
when defined(windows): when defined(windows):
var old = getAttributes() and not 0x0007 let h = conHandle(f)
var old = getAttributes(h) and not 0x0007
if bright: if bright:
old = old or FOREGROUND_INTENSITY old = old or FOREGROUND_INTENSITY
const lookup: array [ForegroundColor, int] = [ const lookup: array [ForegroundColor, int] = [
@ -368,16 +391,17 @@ proc setForegroundColor*(fg: ForegroundColor, bright=false) =
(FOREGROUND_RED or FOREGROUND_BLUE), (FOREGROUND_RED or FOREGROUND_BLUE),
(FOREGROUND_BLUE or FOREGROUND_GREEN), (FOREGROUND_BLUE or FOREGROUND_GREEN),
(FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED)] (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED)]
discard setConsoleTextAttribute(conHandle, toU16(old or lookup[fg])) discard setConsoleTextAttribute(h, toU16(old or lookup[fg]))
else: else:
gFG = ord(fg) gFG = ord(fg)
if bright: inc(gFG, 60) if bright: inc(gFG, 60)
stdout.write("\e[" & $gFG & 'm') f.write("\e[" & $gFG & 'm')
proc setBackgroundColor*(bg: BackgroundColor, bright=false) = proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) =
## sets the terminal's background color ## Sets the terminal's background color.
when defined(windows): when defined(windows):
var old = getAttributes() and not 0x0070 let h = conHandle(f)
var old = getAttributes(h) and not 0x0070
if bright: if bright:
old = old or BACKGROUND_INTENSITY old = old or BACKGROUND_INTENSITY
const lookup: array [BackgroundColor, int] = [ const lookup: array [BackgroundColor, int] = [
@ -389,14 +413,14 @@ proc setBackgroundColor*(bg: BackgroundColor, bright=false) =
(BACKGROUND_RED or BACKGROUND_BLUE), (BACKGROUND_RED or BACKGROUND_BLUE),
(BACKGROUND_BLUE or BACKGROUND_GREEN), (BACKGROUND_BLUE or BACKGROUND_GREEN),
(BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED)] (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED)]
discard setConsoleTextAttribute(conHandle, toU16(old or lookup[bg])) discard setConsoleTextAttribute(h, toU16(old or lookup[bg]))
else: else:
gBG = ord(bg) gBG = ord(bg)
if bright: inc(gBG, 60) if bright: inc(gBG, 60)
stdout.write("\e[" & $gBG & 'm') f.write("\e[" & $gBG & 'm')
proc isatty*(f: File): bool = 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): when defined(posix):
proc isatty(fildes: FileHandle): cint {. proc isatty(fildes: FileHandle): cint {.
importc: "isatty", header: "<unistd.h>".} importc: "isatty", header: "<unistd.h>".}
@ -410,39 +434,62 @@ type
TerminalCmd* = enum ## commands that can be expressed as arguments TerminalCmd* = enum ## commands that can be expressed as arguments
resetStyle ## reset attributes resetStyle ## reset attributes
template styledEchoProcessArg(s: string) = write stdout, s template styledEchoProcessArg(f: File, s: string) = write f, s
template styledEchoProcessArg(style: Style) = setStyle({style}) template styledEchoProcessArg(f: File, style: Style) = setStyle(f, {style})
template styledEchoProcessArg(style: set[Style]) = setStyle style template styledEchoProcessArg(f: File, style: set[Style]) = setStyle f, style
template styledEchoProcessArg(color: ForegroundColor) = setForegroundColor color template styledEchoProcessArg(f: File, color: ForegroundColor) =
template styledEchoProcessArg(color: BackgroundColor) = setBackgroundColor color setForegroundColor f, color
template styledEchoProcessArg(cmd: TerminalCmd) = template styledEchoProcessArg(f: File, color: BackgroundColor) =
setBackgroundColor f, color
template styledEchoProcessArg(f: File, cmd: TerminalCmd) =
when cmd == resetStyle: when cmd == resetStyle:
resetAttributes() resetAttributes(f)
macro styledEcho*(m: varargs[expr]): stmt = macro styledWriteLine*(f: File, m: varargs[expr]): stmt =
## to be documented. ## 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() let m = callsite()
var reset = false var reset = false
result = newNimNode(nnkStmtList) result = newNimNode(nnkStmtList)
for i in countup(1, m.len - 1): for i in countup(2, m.len - 1):
let item = m[i] let item = m[i]
case item.kind case item.kind
of nnkStrLit..nnkTripleStrLit: of nnkStrLit..nnkTripleStrLit:
if i == m.len - 1: if i == m.len - 1:
# optimize if string literal is last, just call writeLine # optimize if string literal is last, just call writeLine
result.add(newCall(bindSym"writeLine", bindSym"stdout", item)) result.add(newCall(bindSym"writeLine", f, item))
if reset: result.add(newCall(bindSym"resetAttributes")) if reset: result.add(newCall(bindSym"resetAttributes", f))
return return
else: else:
# if it is string literal just call write, do not enable reset # 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: else:
result.add(newCall(bindSym"styledEchoProcessArg", item)) result.add(newCall(bindSym"styledEchoProcessArg", f, item))
reset = true reset = true
result.add(newCall(bindSym"write", bindSym"stdout", newStrLitNode("\n"))) result.add(newCall(bindSym"write", f, newStrLitNode("\n")))
if reset: result.add(newCall(bindSym"resetAttributes")) 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): when defined(nimdoc):
proc getch*(): char = proc getch*(): char =
@ -462,15 +509,35 @@ elif not defined(windows):
result = stdin.readChar() result = stdin.readChar()
discard fd.tcsetattr(TCSADRAIN, addr oldMode) 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: when not defined(testing) and isMainModule:
system.addQuitProc(resetAttributes) #system.addQuitProc(resetAttributes)
write(stdout, "never mind") write(stdout, "never mind")
eraseLine() stdout.eraseLine()
#setCursorPos(2, 2) stdout.styledWriteLine("styled text ", {styleBright, styleBlink, styleUnderscore})
writeStyled("styled text ", {styleBright, styleBlink, styleUnderscore}) stdout.setBackGroundColor(bgCyan, true)
setBackGroundColor(bgCyan, true) stdout.setForeGroundColor(fgBlue)
setForeGroundColor(fgBlue) stdout.writeLine("ordinary text")
writeLine(stdout, "ordinary text") stdout.resetAttributes()
styledEcho("styled text ", {styleBright, styleBlink, styleUnderscore})

View file

@ -292,7 +292,8 @@ proc testSpec(r: var TResults, test: TTest) =
case expected.action case expected.action
of actionCompile: of actionCompile:
var given = callCompiler(expected.cmd, test.name, 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) compilerOutputTests(test, given, expected, r)
of actionRun, actionRunNoSpec: of actionRun, actionRunNoSpec:
# In this branch of code "early return" pattern is clearer than deep # In this branch of code "early return" pattern is clearer than deep