Move filter logic earlier in CLI processing, support state filtering.

This commit is contained in:
2025-11-19 17:10:14 -06:00
parent bc37640f2e
commit 759d00e2f8
2 changed files with 68 additions and 45 deletions

View File

@@ -31,6 +31,7 @@ proc parseExclPropertiesOption(propsOpt: string): TableRef[string, seq[string]]
if result.hasKey(pair[0]): result[pair[0]].add(val) if result.hasKey(pair[0]): result[pair[0]].add(val)
else: result[pair[0]] = @[val] else: result[pair[0]] = @[val]
proc reorder(ctx: CliContext, state: IssueState) = proc reorder(ctx: CliContext, state: IssueState) =
# load the issues to make sure the order file contains all issues in the state. # load the issues to make sure the order file contains all issues in the state.
@@ -87,6 +88,10 @@ when isMainModule:
var tagsOption = none(seq[string]) var tagsOption = none(seq[string])
var exclTagsOption = none(seq[string]) var exclTagsOption = none(seq[string])
let filter = initFilter()
var filterOption = none(IssueFilter)
if args["--properties"] or args["--context"]: if args["--properties"] or args["--context"]:
var props = var props =
@@ -116,6 +121,54 @@ when isMainModule:
if args["--excl-tags"]: exclTagsOption = if args["--excl-tags"]: exclTagsOption =
some(($args["--excl-tags"]).split(",").mapIt(it.strip)) some(($args["--excl-tags"]).split(",").mapIt(it.strip))
# Initialize filter with properties (if given)
if propertiesOption.isSome:
filter.properties = propertiesOption.get
filterOption = some(filter)
# Add property exclusions (if given)
if exclPropsOption.isSome:
filter.exclProperties = exclPropsOption.get
filterOption = some(filter)
# If they supplied text matches, add that to the filter.
if args["--match"]:
filter.summaryMatch = some(re("(?i)" & $args["--match"]))
filterOption = some(filter)
if args["--match-all"]:
filter.fullMatch = some(re("(?i)" & $args["--match-all"]))
filterOption = some(filter)
# If no "context" property is given, use the default (if we have one)
if ctx.defaultContext.isSome and not filter.properties.hasKey("context"):
stderr.writeLine("Limiting to default context: " & ctx.defaultContext.get)
filter.properties["context"] = ctx.defaultContext.get
filterOption = some(filter)
if tagsOption.isSome:
filter.hasTags = tagsOption.get
filterOption = some(filter)
if exclTagsOption.isSome:
filter.exclTags = exclTagsOption.get
filterOption = some(filter)
if args["--today"]:
filter.inclStates.add(@[Current, TodoToday, Pending])
filterOption = some(filter)
if args["--future"]:
filter.inclStates.add(@[Pending, Todo])
filterOption = some(filter)
# Finally, if the "context" is "all", don't filter on context
if filter.properties.hasKey("context") and
filter.properties["context"] == "all":
filter.properties.del("context")
filter.exclProperties.del("context")
## Actual command runners ## Actual command runners
if args["new"] or args["add"]: if args["new"] or args["add"]:
let state = let state =
@@ -224,7 +277,6 @@ when isMainModule:
updatedIssues.add(nextIssue) updatedIssues.add(nextIssue)
stdout.writeLine formatIssue(nextIssue) stdout.writeLine formatIssue(nextIssue)
issue.changeState(ctx.cfg.tasksDir, targetState) issue.changeState(ctx.cfg.tasksDir, targetState)
updatedIssues.add(issue) updatedIssues.add(issue)
@@ -276,49 +328,6 @@ when isMainModule:
elif args["list"]: elif args["list"]:
let filter = initFilter()
var filterOption = none(IssueFilter)
# Initialize filter with properties (if given)
if propertiesOption.isSome:
filter.properties = propertiesOption.get
filterOption = some(filter)
# Add property exclusions (if given)
if exclPropsOption.isSome:
filter.exclProperties = exclPropsOption.get
filterOption = some(filter)
# If they supplied text matches, add that to the filter.
if args["--match"]:
filter.summaryMatch = some(re("(?i)" & $args["--match"]))
filterOption = some(filter)
if args["--match-all"]:
filter.fullMatch = some(re("(?i)" & $args["--match-all"]))
filterOption = some(filter)
# If no "context" property is given, use the default (if we have one)
if ctx.defaultContext.isSome and not filter.properties.hasKey("context"):
stderr.writeLine("Limiting to default context: " & ctx.defaultContext.get)
filter.properties["context"] = ctx.defaultContext.get
filterOption = some(filter)
if tagsOption.isSome:
filter.hasTags = tagsOption.get
filterOption = some(filter)
if exclTagsOption.isSome:
filter.exclTags = exclTagsOption.get
filterOption = some(filter)
# Finally, if the "context" is "all", don't filter on context
if filter.properties.hasKey("context") and
filter.properties["context"] == "all":
filter.properties.del("context")
filter.exclProperties.del("context")
var listContexts = false var listContexts = false
var listTags = false var listTags = false
var statesOption = none(seq[IssueState]) var statesOption = none(seq[IssueState])

View File

@@ -18,13 +18,15 @@ type
Current = "current", Current = "current",
TodoToday = "todo-today", TodoToday = "todo-today",
Pending = "pending", Pending = "pending",
Done = "done",
Todo = "todo" Todo = "todo"
Dormant = "dormant" Dormant = "dormant"
Done = "done",
IssueFilter* = ref object IssueFilter* = ref object
completedRange*: Option[tuple[b, e: DateTime]] completedRange*: Option[tuple[b, e: DateTime]]
fullMatch*, summaryMatch*: Option[Regex] fullMatch*, summaryMatch*: Option[Regex]
inclStates*: seq[IssueState]
exclStates*: seq[IssueState]
hasTags*: seq[string] hasTags*: seq[string]
exclTags*: seq[string] exclTags*: seq[string]
properties*: TableRef[string, string] properties*: TableRef[string, string]
@@ -136,6 +138,8 @@ proc initFilter*(): IssueFilter =
completedRange: none(tuple[b, e: DateTime]), completedRange: none(tuple[b, e: DateTime]),
fullMatch: none(Regex), fullMatch: none(Regex),
summaryMatch: none(Regex), summaryMatch: none(Regex),
inclStates: @[],
exclStates: @[],
hasTags: @[], hasTags: @[],
exclTags: @[], exclTags: @[],
properties: newTable[string, string](), properties: newTable[string, string](),
@@ -165,6 +169,10 @@ proc hasTagsFilter*(tags: seq[string]): IssueFilter =
result = initFilter() result = initFilter()
result.hasTags = tags result.hasTags = tags
proc stateFilter*(states: seq[IssueState]): IssueFilter =
result = initFilter()
result.inclStates = states
proc groupBy*(issues: seq[Issue], propertyKey: string): TableRef[string, seq[Issue]] = proc groupBy*(issues: seq[Issue], propertyKey: string): TableRef[string, seq[Issue]] =
result = newTable[string, seq[Issue]]() result = newTable[string, seq[Issue]]()
for i in issues: for i in issues:
@@ -426,6 +434,12 @@ proc filter*(issues: seq[Issue], filter: IssueFilter): seq[Issue] =
let exclTag = exclTagLent let exclTag = exclTagLent
f = f --> filter(it.tags.find(exclTag) < 0) f = f --> filter(it.tags.find(exclTag) < 0)
if filter.inclStates.len > 0:
f = f --> filter(filter.inclStates.contains(it.state))
if filter.exclStates.len > 0:
f = f --> filter(not filter.exclStates.contains(it.state))
return f # not using result because zero_functional doesn't play nice with it return f # not using result because zero_functional doesn't play nice with it
proc find*( proc find*(