From 3b5b3deecd8d49515dfa4836c452647c7ab7a506 Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Fri, 15 Jun 2018 11:26:09 -0400 Subject: [PATCH 1/2] Add styledWrite macro Also: - Move the tests block to the end of the file - Fix the older tests - Add tests for existing styledEcho - Add new tests for styledWrite Fixes https://github.com/nim-lang/Nim/issues/8046. --- lib/pure/terminal.nim | 86 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 12 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 8ee95957d6..3cdfe466de 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -692,6 +692,40 @@ template styledEchoProcessArg(f: File, cmd: TerminalCmd) = when cmd == bgColor: fgSetColor = false +macro styledWrite*(f: File, m: varargs[typed]): untyped = + ## Similar to ``write``, 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 + ## + ## 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): + let item = m[i] + case item.kind + of nnkStrLit..nnkTripleStrLit: + if i == m.len - 1: + # optimize if string literal is last, just call write + result.add(newCall(bindSym"write", 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", f, item)) + else: + result.add(newCall(bindSym"styledEchoProcessArg", f, item)) + reset = true + if reset: result.add(newCall(bindSym"resetAttributes", f)) + macro styledWriteLine*(f: File, m: varargs[typed]): untyped = ## Similar to ``writeLine``, but treating terminal style arguments specially. ## When some argument is ``Style``, ``set[Style]``, ``ForegroundColor``, @@ -781,7 +815,7 @@ when defined(windows): inc i, x password.string.setLen(max(password.len - x, 0)) of chr(0x0): - # modifier key - ignore - for details see + # modifier key - ignore - for details see # https://github.com/nim-lang/Nim/issues/7764 continue else: @@ -840,17 +874,6 @@ proc resetAttributes*() {.noconv.} = ## ``system.addQuitProc(resetAttributes)``. resetAttributes(stdout) -when not defined(testing) and isMainModule: - #system.addQuitProc(resetAttributes) - write(stdout, "never mind") - stdout.eraseLine() - stdout.styledWriteLine("styled text ", {styleBright, styleBlink, styleUnderscore}) - stdout.styledWriteLine("italic text ", {styleItalic}) - stdout.setBackGroundColor(bgCyan, true) - stdout.setForeGroundColor(fgBlue) - stdout.writeLine("ordinary text") - stdout.resetAttributes() - proc isTrueColorSupported*(): bool = ## Returns true if a terminal supports true color. return trueColorIsSupported @@ -901,3 +924,42 @@ proc disableTrueColors*() = trueColorIsEnabled = false else: trueColorIsEnabled = false + +when not defined(testing) and isMainModule: + #system.addQuitProc(resetAttributes) + write(stdout, "never mind") + stdout.eraseLine() + stdout.styledWriteLine({styleBright, styleBlink, styleUnderscore}, "styled text ") + stdout.styledWriteLine("italic text ", {styleItalic}) + stdout.setBackGroundColor(bgCyan, true) + stdout.setForeGroundColor(fgBlue) + stdout.write("blue text in cyan background") + stdout.resetAttributes() + echo "" + stdout.writeLine("ordinary text") + echo "more ordinary text" + styledEcho styleBright, fgGreen, "[PASS]", resetStyle, fgGreen, " Yay!" + echo "ordinary text again" + styledEcho styleBright, fgRed, "[FAIL]", resetStyle, fgRed, " Nay :(" + echo "ordinary text again" + setForeGroundColor(fgGreen) + echo "green text" + echo "more green text" + setForeGroundColor(fgBlue) + echo "blue text" + resetAttributes() + echo "ordinary text" + + stdout.styledWriteLine(fgRed, "red text ") + # Below, resetStyle is needed to prevent leaking the set bgRed to the next + # newline. + stdout.styledWriteLine(fgWhite, bgRed, "white text in red background", resetStyle) + stdout.styledWriteLine(" ordinary text ") + stdout.styledWriteLine(fgGreen, "green text") + + stdout.styledWrite(fgRed, "red text ") + stdout.styledWrite(fgWhite, bgRed, "white text in red background") + stdout.styledWrite(" ordinary text ") + stdout.styledWrite(fgGreen, "green text") + echo "" + echo "ordinary text" From abbf9ba9f74fa2415efcad4d1792d68527eba09d Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Fri, 15 Jun 2018 12:41:25 -0400 Subject: [PATCH 2/2] Convert styledWriteLine and styledEcho to templates This also fixes a bug in the styledWriteLine behavior where the background color leaked onto the next newline if that command did not end with resetStyle. Now it is not necessary to end styledWriteLine calls that set BackgroundColor to end in resetStyle. --- lib/pure/terminal.nim | 42 +++++++----------------------------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 3cdfe466de..7ad243150a 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -726,11 +726,8 @@ macro styledWrite*(f: File, m: varargs[typed]): untyped = reset = true if reset: result.add(newCall(bindSym"resetAttributes", f)) -macro styledWriteLine*(f: File, m: varargs[typed]): untyped = - ## 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. +template styledWriteLine*(f: File, args: varargs[untyped]) = + ## Calls ``styledWrite`` and appends a newline at the end. ## ## Example: ## @@ -739,35 +736,12 @@ macro styledWriteLine*(f: File, m: varargs[typed]): untyped = ## proc error(msg: string) = ## styledWriteLine(stderr, fgRed, "Error: ", resetStyle, msg) ## - let m = callsite() - var reset = false - result = newNimNode(nnkStmtList) + styledWrite(f, args) + write(f, "\n") - 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", 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", f, item)) - else: - result.add(newCall(bindSym"styledEchoProcessArg", f, item)) - reset = true - - result.add(newCall(bindSym"write", f, newStrLitNode("\n"))) - if reset: result.add(newCall(bindSym"resetAttributes", f)) - -macro styledEcho*(args: varargs[untyped]): untyped = +template styledEcho*(args: varargs[untyped]) = ## Echoes styles arguments to stdout using ``styledWriteLine``. - result = newCall(bindSym"styledWriteLine") - result.add(bindSym"stdout") - for arg in children(args): - result.add(arg) + stdout.styledWriteLine(args) proc getch*(): char = ## Read a single character from the terminal, blocking until it is entered. @@ -951,9 +925,7 @@ when not defined(testing) and isMainModule: echo "ordinary text" stdout.styledWriteLine(fgRed, "red text ") - # Below, resetStyle is needed to prevent leaking the set bgRed to the next - # newline. - stdout.styledWriteLine(fgWhite, bgRed, "white text in red background", resetStyle) + stdout.styledWriteLine(fgWhite, bgRed, "white text in red background") stdout.styledWriteLine(" ordinary text ") stdout.styledWriteLine(fgGreen, "green text")