all repos — pls @ ce30a6292f36cae6710577326cd939f636a94245

A polite but determined task runner.

Removed editing capabilities
h3rald h3rald@h3rald.com
Mon, 04 Oct 2021 12:03:14 +0200
commit

ce30a6292f36cae6710577326cd939f636a94245

parent

4c72497792873631a2309415b395cbdc9abf6d01

5 files changed, 23 insertions(+), 578 deletions(-)

jump to
M src/pls.nimsrc/pls.nim

@@ -5,8 +5,7 @@ parseopt,

logging, algorithm, strutils, - sequtils, - pegs + sequtils import plspkg/plslogger

@@ -16,17 +15,7 @@ setLogFilter(lvlInfo)

import plspkg/config, - plspkg/project, - plspkg/messaging - -let pegTaskDef = peg""" - def <- (id)('+' id)* - id <- [$a-z0-9_][a-zA-Z0-9_-]+ -""" - -let pegPropName = peg""" - [a-z0-9_][a-zA-Z0-9_-]+ -""" + plspkg/project let usage* = """ $1 v$2 - $3 (c) 2021 $4

@@ -38,85 +27,14 @@ => For more information on available tasks, run: pls help

Options: --help, -h Displays this message. - --force, -f Do not ask for confirmation when executing the specified task. --log, -l Specifies the log level (debug|info|notice|warn|error|fatal). Default: info --version, -h Displays the version of the application. """ % [pkgTitle, pkgVersion, pkgDescription, pkgAuthor] -var force = false # Helper Methods -proc addProperty(parentObj: JsonNode, name = ""): tuple[key: string, value: JsonNode] = - var done = false - while (not done): - if name == "": - var ok = false - while not ok: - result.key = editValue("Name") - if not result.key.match(pegPropName): - warn "Property name contains invalid characters." - else: - ok = true - elif name == "name": - warn "Property identifier 'name' cannot be modified." - else: - printValue(" Name", name) - result.key = name - var ok = false - while (not ok): - var value = "" - if parentObj.hasKey(result.key): - value = $parentObj[result.key] - try: - result.value = editValue("Value", value).parseJson - if (result.value == newJNull()): - ok = confirm("Remove property '$1'?" % result.key) - done = true - else: - ok = true - except: - warn("Please enter a valid JSON value.") - done = done or confirm("OK?") - -proc addProperties(obj: var JsonNode) = - var done = false - while (not done): - let prop = addProperty(obj) - obj[prop.key] = prop.value - done = not confirm("Do you want to add/remove more properties?") - -proc addTaskDefinition(parentObj: JsonNode, name = ""): tuple[key: string, value: JsonNode] = - # TODO: validate name of task definition! (not $syntax or $description, etc.) - if name == "": - var ok = false - while not ok: - result.key = editValue("Task Definition") - if not result.key.match(pegTaskDef): - warn "Task definition contains invalid characters." - else: - ok = true - else: - printValue(" Task Definition", name) - result.key = name - result.value = newJObject() - result.value["cmd"] = addProperty(parentObj[name], "cmd").value - -proc addTaskDefinitions(obj: var JsonNode) = - var done = false - while (not done): - let prop = addTaskDefinition(obj) - obj[prop.key] = prop.value - done = not confirm("Do you want to add/remove more task definitions?") - -proc changeValue(oldv: tuple[label: string, value: JsonNode], newv: tuple[label: string, value: JsonNode]): bool = - if oldv.value != newJNull(): - printDeleted(oldv.label, $oldv.value) - if newv.value != newJNull(): - printAdded(newv.label, $newv.value) - return confirm("Confirm change?") - proc update(PROJECT: var PlsProject, sysProject: JsonNode): bool {.discardable.} = result = false let sysTasks = sysProject["tasks"]

@@ -129,22 +47,13 @@ let sysProp = sysTask[prop]

var prjProp = newJNull() if prjTask.hasKey(prop): prjProp = prjTask[prop] - if prjProp != newJNull(): - if prjProp != sysProp: - let sysVal = (label: k & "." & prop, value: sysProp) - let prjVal = (label: k & "." & prop, value: prjProp) - if changeValue(prjVal, sysVal): - prjTask[prop] = sysProp - result = true - else: + if prjProp == newJNull(): result = true # Adding new property - printAdded("$1.$2" % [k, prop], $sysProp) prjTask[prop] = sysProp else: result = true # Adding new task - printAdded(k, $sysTasks[k]) PROJECT.tasks[k] = sysTasks[k] ### MAIN ###

@@ -157,8 +66,6 @@ of cmdArgument:

args.add key of cmdLongOption, cmdShortOption: case key: - of "force", "f": - force = true of "log", "l": var val = val setLogLevel(val)

@@ -198,79 +105,12 @@ if args.len == 0:

echo usage quit(0) case args[0]: - of "def": - if args.len < 3: - fatal "No alias specified." - quit(3) - let kind = args[1] - let alias = args[2] - var props = newJObject() - if not ["target", "task"].contains(kind): - fatal "Unknown definition type $1" % kind - quit(6) - if kind == "target": - if PROJECT.targets.hasKey(alias): - notice "Redefining existing target: " & alias - warn "Specify properties for target '$1':" % alias - props = PROJECT.targets[alias] - for k, v in props.mpairs: - if k == "name": - continue - let prop = addProperty(props, k) - props[prop.key] = prop.value - if confirm "Do you want to add/remove more properties?": - addProperties(props) - else: - notice "Definining new target: " & alias - warn "Specify properties for target '$1':" % alias - addProperties(props) - PROJECT.defTarget(alias, props) - else: # task - if PROJECT.tasks.hasKey(alias): - notice "Redefining existing task: " & alias - warn "Specify properties for task '$1':" % alias - props = PROJECT.tasks[alias] - for k, v in props.mpairs: - if ["$syntax", "$description"].contains(k): - let prop = addProperty(props, k) - props[prop.key] = prop.value - else: - let prop = addTaskDefinition(props, k) - props[prop.key] = prop.value - if confirm "Do you want to add/remove more task definitions?": - addTaskDefinitions(props) - else: - props["$syntax"] = addProperty(props, "$syntax").value - props["$description"] = addProperty(props, "$description").value - addTaskDefinitions(props) - PROJECT.defTask(alias, props) - of "undef": - if args.len < 3: - fatal "No alias specified." - quit(3) - let kind = args[1] - let alias = args[2] - if not ["target", "task"].contains(kind): - fatal "Unknown definition type $1" % kind - quit(6) - if kind == "target": - if not PROJECT.targets.hasKey(alias): - fatal "Target '$1' not defined." % [alias] - quit(4) - if force or confirm("Remove definition for target '$1'?" % alias): - PROJECT.undefTarget(alias) - else: # task - if not PROJECT.tasks.hasKey(alias): - fatal "Task '$1' not defined." % [alias] - quit(4) - if force or confirm("Remove definition for task '$1'?" % alias): - PROJECT.undefTask(alias) of "info": if args.len < 2: for t, props in PROJECT.targets.pairs: - echo "$1:" % [t] + echo "\n $1:" % [t] for k, v in props.pairs: - echo " - $1:\t$2" % [k, $v] + echo " - $1:\t$2" % [k, $v] else: let alias = args[1] if not PROJECT.targets.hasKey(alias):

@@ -278,22 +118,22 @@ fatal "Target '$1' not defined." % [alias]

quit(4) let data = PROJECT.targets[alias] for k, v in data.pairs: - echo "$1:\t$2" % [k, $v] + echo "\n $1:\t$2" % [k, $v] of "help": echo "" if args.len < 2: var sortedKeys = toSeq(PROJECT.help.keys) sortedKeys.sort(cmp[string]) for k in sortedKeys: - printGreen " pls $1" % PROJECT.help[k]["$syntax"].getStr - echo "\n $1\n" % PROJECT.help[k]["$description"].getStr + echo " pls $1" % PROJECT.help[k]["$syntax"].getStr + echo " $1\n" % PROJECT.help[k]["$description"].getStr else: let cmd = args[1] let help = PROJECT.help[cmd] if not PROJECT.help.hasKey(cmd): fatal "Task '$1' is not defined." % cmd quit(5) - printGreen " pls " & help["$syntax"].getStr + echo " pls " & help["$syntax"].getStr echo "\n $1\n" % help["$description"].getStr else: if args.len < 1:
M src/plspkg/help.jsonsrc/plspkg/help.json

@@ -6,13 +6,5 @@ },

"info": { "$syntax": "info [<target>]", "$description": "Displays information on <target> (or all targets)." - }, - "def": { - "$syntax": "def (task|target) <id>", - "$description": "Configures a new or existing task or target <id>." - }, - "undef": { - "$syntax": "undef (task|target) <id>", - "$description": "Unmaps the previously-mapped task or target <id>." } }
D src/plspkg/messaging.nim

@@ -1,103 +0,0 @@

-import - terminal, - strutils, - sequtils - -import - minimline - -type - TreeNode* = object - label: string - nodes: seq[TreeNode] - -proc foreground(str: string, color: ForegroundColor) = - stdout.setForegroundColor(color) - stdout.write(str) - resetAttributes() - -proc printGreen*(str: string) = - foreground(str, fgGreen) - -proc printRed*(str: string) = - foreground(str, fgRed) - -proc printYellow*(str: string) = - foreground(str, fgYellow) - -proc printBlue*(str: string) = - foreground(str, fgBlue) - -proc confirm*(q: string): bool = - printYellow("(!) " & q & " [y/n]: ") - var ed = initEditor() - let answer = ed.readLine().toLowerAscii[0] - if answer == 'y': - return true - return false - -proc printValue*(key, value: string) = - printBlue(" -> $1: " % key) - printGreen(value) - resetAttributes() - stdout.write("\n") - -proc editValue*(key: string, value = ""): string = - printBlue(" -> $1: " % key) - var ed = initEditor() - result = ed.edit(value) - -proc printDeleted*(label, value: string) = - printRed("--- ") - echo label & ": " & value - -proc printAdded*(label, value: string) = - printGreen("+++ ") - echo label & ": " & value - -when defined(windows): - proc ch(s: string): string = - case s: - of "└": - return $(192.chr) - of "├": - return $(195.chr) - of "─": - return $(196.chr) - of "┬": - return $(194.chr) - of "│": - return $(179.chr) -else: - proc ch(s: string): string = - return s - -proc newTreeNode*(label: string): TreeNode = - result.label = label - result.nodes = newSeq[TreeNode]() - -proc add*(x: var TreeNode, node: TreeNode) = - x.nodes.add(node) - -proc tree*(node: TreeNode, prefix = ""): string = - let splitterPart = if node.nodes.len > 0: ch("│") else: "" - let splitter = "\n" & prefix & splitterPart & "" - return prefix & [node.label].join(splitter) & "\n" & node.nodes.map(proc(x: TreeNode): string = - let ix = node.nodes.find(x) - let last = node.nodes.len-1 == ix - let more = x.nodes.len > 0 - let prefixPart = if last: " " else: ch("│") - let newPrefix = prefix & prefixPart & " " - let lastPart = if last: ch("└") else: ch("├") - let morePart = if more: ch("┬") else: ch("─") - let rec = tree(x, newPrefix) - var offset = if ch("└").len > 1: 3 else: 1 - var endSpace = "" - if lastPart == ch("└"): - offset = 2 - endSpace = " " - return prefix & lastPart & ch("─") & morePart & endSpace & rec[prefix.len+offset .. rec.len-1] - ).join("") - - -
D src/plspkg/minimline.nim

@@ -1,297 +0,0 @@

-import - critbits, - terminal, - std/exitprocs - -exitprocs.addExitProc(resetAttributes) - -when defined(windows): - proc getchr*(): cint {.header: "<conio.h>", importc: "_getch".} - proc putchr*(c: cint): cint {.discardable, header: "<conio.h>", importc: "_putch".} -else: - proc putchr*(c: cint) = - stdout.write(c.chr) - - proc getchr*(): cint = - return getch().ord.cint - -# Types - -type - Key* = int - KeySeq* = seq[Key] - KeyCallback* = proc(ed: var LineEditor) - LineError* = ref Exception - LineEditorError* = ref Exception - Line = object - text: string - position: int - LineEditor* = object - line: Line - -# Internal Methods - -proc empty(line: Line): bool = - return line.text.len <= 0 - -proc full(line: Line): bool = - return line.position >= line.text.len - -proc first(line: Line): int = - if line.empty: - raise LineError(msg: "Line is empty!") - return 0 - -proc last(line: Line): int = - if line.empty: - raise LineError(msg: "Line is empty!") - return line.text.len-1 - -proc fromStart(line: Line): string = - if line.empty: - return "" - return line.text[line.first..line.position-1] - -proc toEnd(line: Line): string = - if line.empty: - return "" - return line.text[line.position..line.last] - -proc back*(ed: var LineEditor, n=1) = - if ed.line.position <= 0: - return - stdout.cursorBackward(n) - ed.line.position = ed.line.position - n - -proc forward*(ed: var LineEditor, n=1) = - if ed.line.full: - return - stdout.cursorForward(n) - ed.line.position += n - -# Public API - -proc deletePrevious*(ed: var LineEditor) = - if ed.line.position <= 0: - return - if not ed.line.empty: - if ed.line.full: - stdout.cursorBackward - putchr(32) - stdout.cursorBackward - ed.line.position.dec - ed.line.text = ed.line.text[0..ed.line.last-1] - else: - let rest = ed.line.toEnd & " " - ed.back - for i in rest: - putchr i.ord.cint - ed.line.text = ed.line.fromStart & ed.line.text[ed.line.position+1..ed.line.last] - stdout.cursorBackward(rest.len) - -proc deleteNext*(ed: var LineEditor) = - if not ed.line.empty: - if not ed.line.full: - let rest = ed.line.toEnd[1..^1] & " " - for c in rest: - putchr c.ord.cint - stdout.cursorBackward(rest.len) - ed.line.text = ed.line.fromStart & ed.line.toEnd[1..^1] - -proc printChar*(ed: var LineEditor, c: int) = - if ed.line.full: - putchr(c.cint) - ed.line.text &= c.chr - ed.line.position += 1 - else: - putchr(c.cint) - let rest = ed.line.toEnd - ed.line.text.insert($c.chr, ed.line.position) - ed.line.position += 1 - for j in rest: - putchr(j.ord.cint) - ed.line.position += 1 - ed.back(rest.len) - -proc print*(ed: var LineEditor, str: string) = - for c in str: - ed.printChar(c.ord) - -# Character sets -const - CTRL* = {0 .. 31} - DIGIT* = {48 .. 57} - LETTER* = {65 .. 122} - UPPERLETTER* = {65 .. 90} - LOWERLETTER* = {97 .. 122} - PRINTABLE* = {32 .. 126} -when defined(windows): - const - ESCAPES* = {0, 22, 224} -else: - const - ESCAPES* = {27} - - -# Key Mappings -var KEYMAP*: CritBitTree[KeyCallBack] - -KEYMAP["backspace"] = proc(ed: var LineEditor) = - ed.deletePrevious() -KEYMAP["delete"] = proc(ed: var LineEditor) = - ed.deleteNext() -KEYMAP["down"] = proc(ed: var LineEditor) = - discard -KEYMAP["up"] = proc(ed: var LineEditor) = - discard -KEYMAP["left"] = proc(ed: var LineEditor) = - ed.back() -KEYMAP["right"] = proc(ed: var LineEditor) = - ed.forward() -KEYMAP["ctrl+c"] = proc(ed: var LineEditor) = - quit(0) -KEYMAP["ctrl+d"] = proc(ed: var LineEditor) = - quit(0) - -# Key Names -var KEYNAMES*: array[0..31, string] -KEYNAMES[1] = "ctrl+a" -KEYNAMES[2] = "ctrl+b" -KEYNAMES[3] = "ctrl+c" -KEYNAMES[4] = "ctrl+d" -KEYNAMES[5] = "ctrl+e" -KEYNAMES[6] = "ctrl+f" -KEYNAMES[7] = "ctrl+g" -KEYNAMES[8] = "ctrl+h" -KEYNAMES[9] = "ctrl+i" -KEYNAMES[9] = "tab" -KEYNAMES[10] = "ctrl+j" -KEYNAMES[11] = "ctrl+k" -KEYNAMES[12] = "ctrl+l" -KEYNAMES[13] = "ctrl+m" -KEYNAMES[14] = "ctrl+n" -KEYNAMES[15] = "ctrl+o" -KEYNAMES[16] = "ctrl+p" -KEYNAMES[17] = "ctrl+q" -KEYNAMES[18] = "ctrl+r" -KEYNAMES[19] = "ctrl+s" -KEYNAMES[20] = "ctrl+t" -KEYNAMES[21] = "ctrl+u" -KEYNAMES[22] = "ctrl+v" -KEYNAMES[23] = "ctrl+w" -KEYNAMES[24] = "ctrl+x" -KEYNAMES[25] = "ctrl+y" -KEYNAMES[26] = "ctrl+z" - -# Key Sequences -var KEYSEQS*: CritBitTree[KeySeq] - -when defined(windows): - KEYSEQS["up"] = @[224, 72] - KEYSEQS["down"] = @[224, 80] - KEYSEQS["right"] = @[224, 77] - KEYSEQS["left"] = @[224, 75] - KEYSEQS["insert"] = @[224, 82] - KEYSEQS["delete"] = @[224, 83] -else: - KEYSEQS["up"] = @[27, 91, 65] - KEYSEQS["down"] = @[27, 91, 66] - KEYSEQS["right"] = @[27, 91, 67] - KEYSEQS["left"] = @[27, 91, 68] - KEYSEQS["home"] = @[27, 91, 72] - KEYSEQS["end"] = @[27, 91, 70] - KEYSEQS["insert"] = @[27, 91, 50, 126] - KEYSEQS["delete"] = @[27, 91, 51, 126] - -proc readLine*(ed: var LineEditor, prompt="", hidechars = false, reset = true): string = - stdout.write(prompt) - if reset: - ed.line = Line(text: "", position: 0) - var c = -1 # Used to manage completions - var esc = false - while true: - var c1: int - if c > 0: - c1 = c - c = -1 - else: - c1 = getchr() - if esc: - esc = false - continue - elif c1 in {10, 13}: - stdout.write("\n") - return ed.line.text - elif c1 in {8, 127}: - KEYMAP["backspace"](ed) - elif c1 in PRINTABLE: - if hidechars: - putchr('*'.ord.cint) - ed.line.text &= c1.chr - ed.line.position.inc - else: - ed.printChar(c1) - elif c1 in ESCAPES: - var s = newSeq[Key](0) - s.add(c1) - let c2 = getchr() - s.add(c2) - if s == KEYSEQS["left"]: - KEYMAP["left"](ed) - elif s == KEYSEQS["right"]: - KEYMAP["right"](ed) - elif s == KEYSEQS["up"]: - KEYMAP["up"](ed) - elif s == KEYSEQS["down"]: - KEYMAP["down"](ed) - elif s == KEYSEQS["home"]: - KEYMAP["home"](ed) - elif s == KEYSEQS["end"]: - KEYMAP["end"](ed) - elif s == KEYSEQS["delete"]: - KEYMAP["delete"](ed) - elif s == KEYSEQS["insert"]: - KEYMAP["insert"](ed) - elif c2 == 91: - let c3 = getchr() - s.add(c3) - if s == KEYSEQS["right"]: - KEYMAP["right"](ed) - elif s == KEYSEQS["left"]: - KEYMAP["left"](ed) - elif s == KEYSEQS["up"]: - KEYMAP["up"](ed) - elif s == KEYSEQS["down"]: - KEYMAP["down"](ed) - elif s == KEYSEQS["home"]: - KEYMAP["home"](ed) - elif s == KEYSEQS["end"]: - KEYMAP["end"](ed) - elif c3 in {50, 51}: - let c4 = getchr() - s.add(c4) - if c4 == 126 and c3 == 50: - KEYMAP["insert"](ed) - elif c4 == 126 and c3 == 51: - KEYMAP["delete"](ed) - elif KEYMAP.hasKey(KEYNAMES[c1]): - KEYMAP[KEYNAMES[c1]](ed) - else: - # Assuming unhandled two-values escape sequence; do nothing. - if esc: - esc = false - continue - else: - esc = true - continue - -proc password*(ed: var LineEditor, prompt=""): string = - return ed.readLine(prompt, true) - -proc initEditor*(): LineEditor = - result.line.text = "" - result.line.position = 0 - -proc edit*(ed: var LineEditor, value: string): string = - ed.print value - return ed.readLine("", false, false)
M src/plspkg/pls.jsonsrc/plspkg/pls.json

@@ -1,5 +1,5 @@

{ - "version": 1633338269, + "version": 1633340696, "tasks": { "go": { "$syntax": "go <target>",

@@ -12,6 +12,19 @@ "cmd": "cd \"{{folder}}\" && bash --login"

}, "$os:macos+folder": { "cmd": "cd \"{{folder}}\" && bash --login" + } + }, + "edit": { + "$syntax": "edit <target>", + "$description": "Opens <target> for editing.", + "$os:windows+file+value": { + "cmd": "notepad \"{{value}}\"" + }, + "$os:linux+file+value": { + "cmd": "vim \"{{value}}\"" + }, + "$os:macos+file+value": { + "cmd": "vim \"{{value}}\"" } } },