aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhistoria <[not public]>2025-02-28 01:01:01 -0500
committerhistoria <[not public]>2025-02-28 01:01:01 -0500
commita3e3aae87e8f159749863987dfaff336a9ef89fe (patch)
treeba1a9aeb29eb77c4b183b58679f03b1307be6d50 /src
parentbb3ef24f8db4e1a02649c06c6d0b5015562cd869 (diff)
downloadsoon-a3e3aae87e8f159749863987dfaff336a9ef89fe.tar.gz
feat: implemented various cli arguments
Diffstat (limited to 'src')
-rw-r--r--src/soon.nim51
-rw-r--r--src/soon/agenda.nim16
-rw-r--r--src/soon/columnize.nim30
-rw-r--r--src/soon/todos.nim34
4 files changed, 91 insertions, 40 deletions
diff --git a/src/soon.nim b/src/soon.nim
index 2c5f109..61ac16d 100644
--- a/src/soon.nim
+++ b/src/soon.nim
@@ -1,4 +1,4 @@
-import std/[os, strutils, strtabs, sequtils, parseopt]
+import std/[os, strutils, strtabs, sequtils, osproc, parseopt]
import soon/[config, agenda, todos]
const VERSION = "0.1"
@@ -10,14 +10,15 @@ proc printHelp(badCmd: string) =
Usage: soon [options]
Options:
--a print agenda (default behavior if no options)
+-a print agenda
-c print reference calendar
-t print todos
+-d print deadlines
-e open default calendar file in $EDITOR
-i open TUI in interactive mode to schedule/archive events
-w, -m, -y print agenda for upcoming week, month, or year
-j print the modified julian day
--d print today's date
+-i, --date print today's date
-p, --past=DAYS days in past to print agenda (default: 1)
-f, --future=DAYS days in future to print agenda (default: 14)
-h, --help show this help
@@ -44,9 +45,15 @@ proc getFileLines(file: string): seq[string] =
f.close()
proc readAllSoonFiles(path: string): seq[string] =
+ var soonFileExists = false
for file in os.walkDirRec(path):
if file.toLower().endsWith(".soon"):
+ soonFileExists = true
result = result.concat(getFileLines(file))
+ if not soonFileExists:
+ echo "No .soon files exist at " & path
+ echo "Check your paths in ~/.config/soon/soon.conf"
+ quit()
proc processCalendars(path: string, past: int, future: int) =
var calendar = readAllSoonFiles(path)
@@ -67,15 +74,51 @@ proc parseArgs(config: StringTableRef): seq[string] =
result.add(key)
of cmdArgument:
printHelp(key)
+ if result.len == 0:
+ result.add("a")
+ result.add("t")
+ result.add("d")
+ echo "I'm running soon -atd, but I should've checked the user config and run that!"
+
+proc printReference() =
+ discard execCmd("cal -3")
+
+proc sanitize(str: string): string =
+ result = ""
+ for ch in str:
+ if ch.isAlphaNumeric() or ch in {'/', '~', '.'}:
+ result.add(ch)
+
+proc openEditor(path: string, file: string) =
+ var editor = getEnv("EDITOR").sanitize
+ if editor.isEmptyOrWhitespace: editor = "vim"
+ let sanePath = path.sanitize
+ let saneFile = file.sanitize
+ discard execCmd("$1 $2/$3" % [editor, sanePath, saneFile])
proc validateConfig(config: StringTableRef) =
createDefaultCalendarFile(config["path"] / config["defaultFile"])
+proc processCommands(commands: seq[string], conf: StringTableRef) =
+ var calendar = readAllSoonFiles(conf["calendarPath"])
+ var past = 0 - parseInt(conf["past"])
+ var future = parseInt(conf["future"])
+ for command in commands:
+ case command
+ of "a": agenda.printAgenda(calendar, past, future)
+ of "w": agenda.printAgenda(calendar, 0, 7)
+ of "m": agenda.printAgenda(calendar, 0, 28)
+ of "y": agenda.printAgenda(calendar, 0, 365)
+ of "t": todos.printTodos(calendar)
+ of "d": todos.printDeadlineTodos(calendar)
+ of "c": printReference()
+ of "e": openEditor(conf["calendarPath"], conf["defaultFile"])
+
proc main() =
var conf = config.loadConfig()
let commands = parseArgs(conf)
validateConfig(conf)
- processCalendars(conf["calendarPath"], 0 - parseInt(conf["past"]), parseInt(conf["future"]))
+ processCommands(commands, conf)
when isMainModule:
main()
diff --git a/src/soon/agenda.nim b/src/soon/agenda.nim
index 29adf28..1c2ce11 100644
--- a/src/soon/agenda.nim
+++ b/src/soon/agenda.nim
@@ -1,4 +1,4 @@
-import std/[times, strutils, strbasics, strscans, sequtils, tables, algorithm, re]
+import std/[times, strutils, sequtils, tables, algorithm]
import conditionchecker, typechecker, parsetime, columnize
type Event = object
@@ -87,11 +87,11 @@ proc getCalendarForDate(cal: seq[string], date: DateTime): Calendar =
proc compare(a, b: string): int =
cmp(a.len, b.len)
-proc startWithDate(datePrinted: bool, date: DateTime, format: string): seq[string] =
+proc startWithDate(datePrinted: bool, date: DateTime, format: string): seq[Column] =
if datePrinted:
- result.add("")
+ result.add(Column(text: "", color: fgWhite))
else:
- result.add(date.format(format))
+ result.add(Column(text: date.format(format), color: fgYellow))
proc eventTime(event: Event): string =
if event.time.monthday != 1:
@@ -106,15 +106,15 @@ proc getDaysEvents(cal: seq[string], date: DateTime, format: string): seq[Line]
for event in events:
var columns = startWithDate(datePrinted, date, format)
datePrinted = true
- columns.add(eventTime(event))
- columns.add(event.name)
+ columns.add(Column(text: eventTime(event), color: fgBlue))
+ columns.add(Column(text: event.name, color: fgWhite))
result.add(Line(columns: columns, attachments: event.attachments))
proc printAgenda*(calendar: seq[string], past: int, future: int) =
let now = now()
let today = dateTime(now.year, now.month, now.monthday, 00, 00, 00, 00, local())
var agenda = newSeq[Line]()
- for i in past..future:
- let curDay = today + i.days
+ for day in past..future:
+ let curDay = today + day.days
agenda = agenda.concat(getDaysEvents(calendar, curDay, "ddd yyyy MMM dd"))
columnize.echo(agenda)
diff --git a/src/soon/columnize.nim b/src/soon/columnize.nim
index 0fba176..a9074f1 100644
--- a/src/soon/columnize.nim
+++ b/src/soon/columnize.nim
@@ -1,18 +1,22 @@
-import std/[strutils, enumerate, sequtils]
+import std/[strutils, enumerate, sequtils, terminal]
+
+type Column* = object
+ text*: string
+ color*: ForegroundColor
type Line* = object
- columns*: seq[string]
+ columns*: seq[Column]
attachments*: seq[string]
-proc prefixSpaceCount(s: seq[string]): int =
- return foldl(s[0..s.len-2], a + b.len, 0) + (2 * s.len-2)
+proc prefixSpaceCount(s: seq[Column]): int =
+ return foldl(s[0..s.len-2], a + b.text.len, 0) + (2 * s.len-2)
proc pad(table: seq[Line], max: seq[int]): seq[Line] =
for line in table:
- var resLine = newSeq[string]()
+ var resLine = newSeq[Column]()
var resAttach = newSeq[string]()
for i, field in enumerate(line.columns):
- resLine.add(field & repeat(' ', max[i] - field.len))
+ resLine.add(Column(text:field.text & repeat(' ', max[i] - field.text.len), color: field.color))
if line.attachments.len > 0:
let spaces = prefixSpaceCount(resLine)
for attachment in line.attachments:
@@ -23,13 +27,19 @@ proc columnize(table: seq[Line]): seq[Line] =
var max = newSeq[int](table[0].columns.len)
for line in table:
for i, field in enumerate(line.columns):
- if field.len > max[i]:
- max[i] = field.len
+ if field.text.len > max[i]:
+ max[i] = field.text.len
result = pad(table, max)
+proc writeColumns(line: Line) =
+ for column in line.columns[0 .. ^2]:
+ stdout.styledWrite(column.color, column.text & " ")
+ stdout.styledWrite(line.columns[^1].color, line.columns[^1].text)
+
proc echo*(table: seq[Line]) =
let lines = columnize(table)
for line in lines:
- echo line.columns.join(" ")
+ writeColumns(line)
+ stdout.writeLine("")
for attachment in line.attachments:
- echo attachment
+ stdout.styledWriteLine(fgMagenta, attachment)
diff --git a/src/soon/todos.nim b/src/soon/todos.nim
index 569bb8f..cac8c90 100644
--- a/src/soon/todos.nim
+++ b/src/soon/todos.nim
@@ -1,4 +1,4 @@
-import std/[times, strutils, tables, strtabs, re, options]
+import std/[times, strutils, re, options, terminal]
import conditionchecker, columnize
type
@@ -50,35 +50,33 @@ proc splitRegularAndDeadlineTodos(todos: seq[Todo]): (seq[Todo], seq[Todo]) =
else:
result[0].add(todo)
-proc printRegularTodos(todos: seq[Todo]) =
- if todos.len == 0: return
- echo "\nTodo List:"
- for todo in todos:
- echo todo.name
- for attachment in todo.attachments:
- echo attachment
-
proc toLine(todo: Todo): Line =
- result.columns = @[todo.deadline.format("ddd yyyy MMM dd")]
+ result.columns = @[Column(text: todo.deadline.format("ddd yyyy MMM dd"), color: fgYellow)]
let due = (todo.deadline - now()).inDays
if due > 0:
- result.columns.add("(Due in " & due.intToStr & " days)")
+ result.columns.add(Column(text: "(Due in " & due.intToStr & " days)", color: fgGreen))
else:
- result.columns.add("(OVERDUE)")
- result.columns.add(todo.name)
+ result.columns.add(Column(text: "(OVERDUE)", color: fgRed))
+ result.columns.add(Column(text: todo.name, color: fgWhite))
result.attachments = todo.attachments
# TODO: Sort on deadlines
-proc printDeadlineTodos(todos: seq[Todo]) =
- if todos.len == 0: return
+proc printDeadlineTodos*(calendar: seq[string]) =
+ let todos = getTodosFromCalendar(calendar)
+ var (regulars, deadlines) = splitRegularAndDeadlineTodos(todos)
+ if deadlines.len == 0: return
echo "\nUpcoming Deadlines:"
var lines = newSeq[Line]()
- for todo in todos:
+ for todo in deadlines:
lines.add(todo.toLine)
columnize.echo(lines)
proc printTodos*(calendar: seq[string]) =
let todos = getTodosFromCalendar(calendar)
var (regulars, deadlines) = splitRegularAndDeadlineTodos(todos)
- printRegularTodos(regulars)
- printDeadlineTodos(deadlines)
+ if todos.len == 0: return
+ echo "\nTodo List:"
+ for todo in todos:
+ echo todo.name
+ for attachment in todo.attachments:
+ stdout.styledWriteLine(fgMagenta, attachment)