Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a terminal progress bar abstraction. #32

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions nimutils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
## same license. I also migrated the crypto to openssl.

import nimutils/[box, random, unicodeid, pubsub, sinks, misc, texttable],
nimutils/[file, process, filetable, encodings, advisory_lock]
import nimutils/[sha, aes, prp, hexdump, markdown, htmlparse, net]
import nimutils/[colortable, rope_base, rope_styles, rope_construct,
rope_prerender, rope_ansirender, switchboard, subproc]
nimutils/[file, process, filetable, encodings, advisory_lock, progress],
nimutils/[sha, aes, prp, hexdump, markdown, htmlparse, net],
nimutils/[colortable, rope_base, rope_styles, rope_construct],
nimutils/[rope_prerender, rope_ansirender, switchboard, subproc]
export box, random, unicodeid, pubsub, sinks, misc, random, texttable,
file, process, filetable, encodings, advisory_lock, sha, aes, prp,
hexdump, markdown, htmlparse, net
export colortable, rope_base, rope_styles, rope_construct, rope_prerender,
rope_ansirender, switchboard, subproc
file, process, filetable, encodings, advisory_lock, progress, sha,
aes, prp, hexdump, markdown, htmlparse, net, colortable, rope_base,
rope_styles, rope_construct, rope_prerender, rope_ansirender,
switchboard, subproc

## Things we don't want to force people to consume need to be imported
## manually. Currently, that's:
Expand Down
8 changes: 4 additions & 4 deletions nimutils/hex.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ add_offset(char **optr, uint64_t start_offset, uint64_t offset_len,
uint8_t chr;
char *p = *optr;
uint64_t value = start_offset + (uint64_t)(line * cpl);
uint64_t ix = offset_len;
uint64_t ix = offset_len;
char buf[offset_len];


Expand All @@ -51,7 +51,7 @@ add_offset(char **optr, uint64_t start_offset, uint64_t offset_len,
value = value >> 4;
buf[--ix] = hex_map[chr];
}

for (ix = 0; ix < offset_len; ix++) {
*p++ = buf[ix];
}
Expand All @@ -72,7 +72,7 @@ add_offset(char **optr, uint64_t start_offset, uint64_t offset_len,
char *
chexl(void *ptr, unsigned int len, unsigned int start_offset,
unsigned int width, char *prefix) {

struct winsize ws;
uint64_t offset_len = calculate_size_prefix(len, start_offset);
uint64_t chars_per_line;
Expand Down Expand Up @@ -257,7 +257,7 @@ chex(void *ptr, unsigned int len, unsigned int start_offset,
char buf[1024] = {0, };

sprintf(buf, "Dump of %d bytes at: %p\n", len, ptr);

return chexl(ptr, len, start_offset, width, buf);
}

Expand Down
11 changes: 9 additions & 2 deletions nimutils/managedtmp.nim
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@ proc getNewTempDir*(tmpFilePrefix = defaultTmpPrefix,
result = createTempDir(tmpFilePrefix, tmpFileSuffix)
managedTmpDirs.add(result)

proc getNewTempFile*(prefix = defaultTmpPrefix, suffix = defaultTmpSuffix,
proc getNewTempFile*(prefix = defaultTmpPrefix,
suffix = defaultTmpSuffix,
autoClean = true): (FileStream, string) =
var (f, path) = createTempFile(prefix, suffix)
# in some cases such as docker, due to snap permissions
# it does not have access directly to files created in /tmp
# but it can access those files if they are nested in another
# nested dir
let dir = genTempPath(prefix, suffix)
createDir(dir)
var (f, path) = createTempFile(prefix, suffix, dir = dir)
if autoClean:
managedTmpFiles.add(path)

Expand Down
4 changes: 0 additions & 4 deletions nimutils/misc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@
## :Copyright: 2022-2023, Crash Override, Inc.

import macros, times, os, posix

# The name flatten conflicts with a method in the options module.
from options import get, Option, isSome, isNone
export get, Option, isSome, isNone

#{.warning[UnusedImport]: off.}


template unreachable*() =
let info = instantiationInfo()

Expand Down
135 changes: 135 additions & 0 deletions nimutils/progress.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import misc, sugar, rope_ansirender, terminal, strformat, unicode

type
ProgressWinchCb = () -> bool
ProgressBar* = object
totalItems*: int
curItems*: int
totalWidth*: int
lastWidth*: int
showPct*: bool
showBars*: bool
showTime*: bool
eraseAtEnd*: bool
color*: string
winchCb*: ProgressWinchCb
startTime*: uint64
progChar*: Rune
curChar*: Rune
timeColor*: string
progColor*: string
curColor*: string
pctColor*: string
barColor*: string
lastN*: int
lastT*: string
lastPct*: string


proc secToDuration*(duration: uint64): string =
var
sec = duration mod 60
mi = (duration div 60) mod 60
hr = duration div 3600

result = fmt"{mi:02}:{sec:02}"

if hr != 0:
result = $(hr) & ":" & result


template updateBar(txt: string) =
eraseLine()
stdout.write("\r")
stdout.write(txt)
stdout.flushFile()

proc update*(ctx: var ProgressBar, newCur: int): bool {.discardable.} =
var redraw = false

if newCur == ctx.curItems:
if ctx.winchCb == ProgressWinchCb(nil):
return
if not ctx.winchCb():
return
else:
redraw = true

ctx.curItems = newCur

var
(w, _) = terminalSize()
elapsedSec = (unixTimeInMs() - ctx.startTime) div 1000
curProgress: float = newCur / ctx.totalItems
intPct: int = int(curProgress * 100.0 )
pctStr: string = fmt"{intPct:3}% "
usedLen: int = 0
bar: string
timeStr: string

if ctx.showPct:
if pctStr != ctx.lastPct:
redraw = true
ctx.lastPct = pctStr
usedLen = len(pctStr)
pctStr = withColor(pctStr, ctx.pctColor)
if ctx.showBars:
usedLen += 2
bar = withColor("|", ctx.barColor)
if ctx.showTime:
timeStr = " " & elapsedSec.secToDuration() & " "
if timeStr != ctx.lastT:
redraw = true
ctx.lastT = timeStr

usedLen += len(timeStr)
timeStr = withColor(timestr, ctx.timeColor)

let availableLen = w - usedLen

if availableLen >= 4:
let
shownProgress = int(float(availableLen - 1) * curProgress)
nonProgress = (availableLen - 1) - shownProgress
curStr = withColor($(ctx.curChar), ctx.curColor)
nonPr = $(Rune(' ').repeat(nonProgress))
progRaw = $(ctx.progChar.repeat(shownProgress))
progStr = withColor(progRaw, ctx.progColor)

if shownProgress != ctx.lastN:
redraw = true

if redraw:
updateBar(pctStr & bar & progStr & curStr & nonPr & bar & timeStr)
else:
updateBar(pctStr & timeStr)

if newCur == ctx.totalItems:
return true
else:
showCursor()
return false

proc initProgress*(ctx: var ProgressBar, totalItems: int, showTime = true,
showPct = true, showBars = true, progChar = Rune('-'),
winchCb = ProgressWinchCb(nil), eraseAtEnd = false,
curChar = Rune('>'), timeColor = "atomiclime",
progColor = "jazzberry", curColor = "jazzberry",
pctColor = "atomiclime", barColor = "jazzberry") =
hideCursor()

ctx.totalItems = totalItems
ctx.showTime = showTime
ctx.showPct = showPct
ctx.showBars = showBars
ctx.curItems = -1
ctx.winchCb = winchCb
ctx.startTime = unixTimeInMs()
ctx.progChar = progChar
ctx.curChar = curChar
ctx.timeColor = timeColor
ctx.progColor = progColor
ctx.curColor = curColor
ctx.pctColor = pctColor
ctx.barColor = barColor
ctx.update(0)
Loading
Loading