Compare commits

...

3 Commits
4.0.3 ... 4.0.6

2 changed files with 59 additions and 35 deletions

View File

@ -1,6 +1,6 @@
# Package # Package
version = "4.0.3" version = "4.0.6"
author = "Jonathan Bernard" author = "Jonathan Bernard"
description = "Personal issue tracker." description = "Personal issue tracker."
license = "MIT" license = "MIT"

View File

@ -68,9 +68,9 @@ proc getIssueContextDisplayName(ctx: CliContext, context: string): string =
else: return context.capitalize() else: return context.capitalize()
return ctx.contexts[context] return ctx.contexts[context]
proc writeIssue(ctx: CliContext, issue: Issue, state: IssueState, proc writeIssue(ctx: CliContext, issue: Issue, width: int, indent = "",
width: int, indent: string, topPadded: bool) = verbose = false, topPadded = false) =
var showDetails = not issue.details.isNilOrWhitespace var showDetails = not issue.details.isNilOrWhitespace and verbose
if showDetails and not topPadded: stdout.writeLine("") if showDetails and not topPadded: stdout.writeLine("")
@ -92,7 +92,7 @@ proc writeIssue(ctx: CliContext, issue: Issue, state: IssueState,
else: stdout.writeLine("") else: stdout.writeLine("")
stdout.resetAttributes stdout.resetAttributes
if state == Pending and issue.hasProp("pending"): if issue.hasProp("pending"):
let startIdx = "Pending: ".len let startIdx = "Pending: ".len
var pendingText = issue["pending"].wordWrap(width - startIdx - 2) var pendingText = issue["pending"].wordWrap(width - startIdx - 2)
.indent(startIdx) .indent(startIdx)
@ -103,7 +103,7 @@ proc writeIssue(ctx: CliContext, issue: Issue, state: IssueState,
proc writeSection(ctx: CliContext, issues: seq[Issue], state: IssueState, proc writeSection(ctx: CliContext, issues: seq[Issue], state: IssueState,
indent = "") = indent = "", verbose = false) =
let innerWidth = ctx.termWidth - (indent.len * 2) let innerWidth = ctx.termWidth - (indent.len * 2)
stdout.setForegroundColor(fgBlue, true) stdout.setForegroundColor(fgBlue, true)
@ -112,27 +112,28 @@ proc writeSection(ctx: CliContext, issues: seq[Issue], state: IssueState,
stdout.writeLine("") stdout.writeLine("")
stdout.resetAttributes stdout.resetAttributes
var topPadded = true
let issuesByContext = issues.groupBy("context") let issuesByContext = issues.groupBy("context")
var topPadded = true
if issues.len > 5 and issuesByContext.len > 1: if issues.len > 5 and issuesByContext.len > 1:
for context, ctxIssues in issuesByContext: for context, ctxIssues in issuesByContext:
topPadded = true
stdout.setForegroundColor(fgYellow, false) stdout.setForegroundColor(fgYellow, false)
stdout.writeLine(indent & ctx.getIssueContextDisplayName(context) & ":") stdout.writeLine(indent & ctx.getIssueContextDisplayName(context) & ":")
stdout.writeLine("") stdout.writeLine("")
stdout.resetAttributes stdout.resetAttributes
for i in ctxIssues: for i in ctxIssues:
ctx.writeIssue(i, state, innerWidth - 2, indent & " ", topPadded) ctx.writeIssue(i, innerWidth - 2, indent & " ", verbose, topPadded)
topPadded = not i.details.isNilOrWhitespace topPadded = not i.details.isNilOrWhitespace and verbose
if not topPadded: stdout.writeLine("") if not topPadded: stdout.writeLine("")
else: else:
for i in issues: for i in issues:
ctx.writeIssue(i, state, innerWidth, indent, topPadded) ctx.writeIssue(i, innerWidth, indent, verbose, topPadded)
topPadded = not i.details.isNilOrWhitespace topPadded = not i.details.isNilOrWhitespace and verbose
stdout.writeLine("") stdout.writeLine("")
@ -184,12 +185,12 @@ proc edit(issue: Issue) =
getCurrentExceptionMsg() getCurrentExceptionMsg()
issue.store() issue.store()
proc list(ctx: CliContext, filter: Option[IssueFilter], state: Option[IssueState], today, future: bool) = proc list(ctx: CliContext, filter: Option[IssueFilter], state: Option[IssueState], today, future, verbose: bool) =
if state.isSome: if state.isSome:
ctx.loadIssues(state.get) ctx.loadIssues(state.get)
if filter.isSome: ctx.filterIssues(filter.get) if filter.isSome: ctx.filterIssues(filter.get)
ctx.writeSection(ctx.issues[state.get], state.get) ctx.writeSection(ctx.issues[state.get], state.get, "", verbose)
return return
ctx.loadAllIssues() ctx.loadAllIssues()
@ -203,14 +204,14 @@ proc list(ctx: CliContext, filter: Option[IssueFilter], state: Option[IssueState
for s in [Current, TodoToday]: for s in [Current, TodoToday]:
if ctx.issues.hasKey(s) and ctx.issues[s].len > 0: if ctx.issues.hasKey(s) and ctx.issues[s].len > 0:
ctx.writeSection(ctx.issues[s], s, indent) ctx.writeSection(ctx.issues[s], s, indent, verbose)
if ctx.issues.hasKey(Done): if ctx.issues.hasKey(Done):
let doneIssues = ctx.issues[Done].filterIt( let doneIssues = ctx.issues[Done].filterIt(
it.hasProp("completed") and it.hasProp("completed") and
sameDay(getTime().local, it.getDateTime("completed"))) sameDay(getTime().local, it.getDateTime("completed")))
if doneIssues.len > 0: if doneIssues.len > 0:
ctx.writeSection(doneIssues, Done, indent) ctx.writeSection(doneIssues, Done, indent, verbose)
# Future items # Future items
if future: if future:
@ -218,7 +219,7 @@ proc list(ctx: CliContext, filter: Option[IssueFilter], state: Option[IssueState
for s in [Pending, Todo]: for s in [Pending, Todo]:
if ctx.issues.hasKey(s) and ctx.issues[s].len > 0: if ctx.issues.hasKey(s) and ctx.issues[s].len > 0:
ctx.writeSection(ctx.issues[s], s, indent) ctx.writeSection(ctx.issues[s], s, indent, verbose)
when isMainModule: when isMainModule:
@ -226,26 +227,30 @@ when isMainModule:
let doc = """ let doc = """
Usage: Usage:
pit ( new | add) <summary> [<state>] [options] pit ( new | add) <summary> [<state>] [options]
pit list [<state>] [options] pit list [<listable>] [options]
pit ( start | done | pending | do-today | todo | suspend ) <id>... pit ( start | done | pending | do-today | todo | suspend ) <id>...
pit edit <id> pit edit <id>
pit delete <id>... pit ( delete | rm ) <id>...
Options: Options:
-h, --help Print this usage information. -h, --help Print this usage information.
-t, --tags <tags> Specify tags for an issue.
-p, --properties <props> Specify properties. Formatted as "key:val;key:val" -p, --properties <props> Specify properties. Formatted as "key:val;key:val"
When used with the list command this option applies When used with the list command this option applies
a filter to the issues listed, only allowing those a filter to the issues listed, only allowing those
which have all of the given properties. which have all of the given properties.
-c, --context <ctxName> Shorthand for '-p context:<ctxName>'
-t, --tags <tags> Specify tags for an issue.
-T, --today Limit to today's issues. -T, --today Limit to today's issues.
-F, --future Limit to future issues. -F, --future Limit to future issues.
-v, --verbose Show issue details when listing issues.
-y, --yes Automatically answer "yes" to any prompts. -y, --yes Automatically answer "yes" to any prompts.
-C, --config <cfgFile> Location of the config file (defaults to $HOME/.pitrc) -C, --config <cfgFile> Location of the config file (defaults to $HOME/.pitrc)
@ -261,7 +266,7 @@ Options:
logging.addHandler(newConsoleLogger()) logging.addHandler(newConsoleLogger())
# Parse arguments # Parse arguments
let args = docopt(doc, version = "pit 4.0.3") let args = docopt(doc, version = "pit 4.0.6")
if args["--echo-args"]: stderr.writeLine($args) if args["--echo-args"]: stderr.writeLine($args)
@ -276,6 +281,17 @@ Options:
if not existsDir(ctx.tasksDir / $s): if not existsDir(ctx.tasksDir / $s):
(ctx.tasksDir / $s).createDir (ctx.tasksDir / $s).createDir
var propertiesOption = none(TableRef[string,string])
if args["--properties"] or args["--context"]:
var props =
if args["--properties"]: parsePropertiesOption($args["--properties"])
else: newTable[string,string]()
if args["--context"]: props["context"] = $args["--context"]
propertiesOption = some(props)
## Actual command runners ## Actual command runners
if args["new"] or args["add"]: if args["new"] or args["add"]:
let state = let state =
@ -285,9 +301,7 @@ Options:
var issue = Issue( var issue = Issue(
id: genUUID(), id: genUUID(),
summary: $args["<summary>"], summary: $args["<summary>"],
properties: properties: propertiesOption.get(newTable[string,string]()),
if args["--properties"]: parsePropertiesOption($args["--properties"])
else: newTable[string,string](),
tags: tags:
if args["--tags"]: ($args["tags"]).split(",").mapIt(it.strip) if args["--tags"]: ($args["tags"]).split(",").mapIt(it.strip)
else: newSeq[string]()) else: newSeq[string]())
@ -322,7 +336,7 @@ Options:
discard execShellCmd(cmd) discard execShellCmd(cmd)
elif targetState == Done: discard execShellCmd("ptk stop") elif targetState == Done: discard execShellCmd("ptk stop")
elif args["delete"]: elif args["delete"] or args["rm"]:
for id in @(args["<id>"]): for id in @(args["<id>"]):
let issue = ctx.tasksDir.loadIssueById(id) let issue = ctx.tasksDir.loadIssueById(id)
@ -338,21 +352,31 @@ Options:
let filter = initFilter() let filter = initFilter()
var filterOption = none(IssueFilter) var filterOption = none(IssueFilter)
if args["--properties"]: if propertiesOption.isSome:
filter.properties = parsePropertiesOption($args["--properties"]) filter.properties = propertiesOption.get
filterOption = some(filter) filterOption = some(filter)
let stateOption = var stateOption = none(IssueState)
if args["<state>"]: some(parseEnum[IssueState]($args["<state>"])) var issueIdOption = none(string)
else: none(IssueState) if args["<listable>"]:
try: stateOption = some(parseEnum[IssueState]($args["<listable>"]))
except: issueIdOption = some($args["<listable>"])
# List a specific issue
if issueIdOption.isSome:
let issue = ctx.tasksDir.loadIssueById(issueIdOption.get)
ctx.writeIssue(issue, ctx.termWidth, "", true, true)
# List all issues
else:
let showBoth = args["--today"] == args["--future"] let showBoth = args["--today"] == args["--future"]
ctx.list(filterOption, stateOption, showBoth or args["--today"], ctx.list(filterOption, stateOption, showBoth or args["--today"],
showBoth or args["--future"]) showBoth or args["--future"],
args["--verbose"])
if ctx.autoList and not args["list"]: if ctx.autoList and not args["list"]:
ctx.loadAllIssues() ctx.loadAllIssues()
ctx.list(none(IssueFilter), none(IssueState), true, true) ctx.list(none(IssueFilter), none(IssueState), true, true, false)
except: except:
fatal "pit: " & getCurrentExceptionMsg() fatal "pit: " & getCurrentExceptionMsg()