diff options
| author | historia <gravel.justness270@slmail.me> | 2024-07-31 03:15:30 -0400 |
|---|---|---|
| committer | historia <gravel.justness270@slmail.me> | 2024-07-31 03:15:30 -0400 |
| commit | c4261ee98ef5fd75a208e8e981486ecb4dfcdafc (patch) | |
| tree | 365841f4fff686a02a8be281157341bb4dc31969 | |
| parent | f60ceeaa79af1e2438b8829c8a32f9187ffb6aa7 (diff) | |
| download | soon-c4261ee98ef5fd75a208e8e981486ecb4dfcdafc.tar.gz | |
Implemented most ranges (missing Julian/Dategroups)
| -rw-r--r-- | README.md | 12 | ||||
| -rw-r--r-- | src/then.nim | 103 |
2 files changed, 12 insertions, 103 deletions
@@ -159,9 +159,9 @@ Julian date modulus is definitely weird, but it's a concise way to express multi Then was created to adapt existing CLI calendars to my personal workflow. Then is not only for the niche of people who use their terminal for scheduling, but for a sub-niche who prefer to edit a file over using a TUI which is why concise, legible syntax is so important. This is a very small group of people who virtually exclusively use [Remind](https://dianne.skoll.ca/projects/remind/). If you're happy with a web or phone calendar, Then probably won't change your mind. -## Then has concise syntax compared to similar programs +Then has a few benefits over similar text file calendars: -It minimizes special characters and simplifies ranges. +1. **Then has the most concise syntax**. It minimizes special characters and simplifies ranges. Remind is arguably nicer for complex recurring cases and supports more types of conditions, but I rarely need these: **Then**: `1, Water bill`\ **When**: `* * 1, Water bill`\ @@ -171,19 +171,15 @@ It minimizes special characters and simplifies ranges. **When**: `m=Jul & d>=1 & d<=7 & y=2025, Vacation!`\ **Remind**: `REM Jul 1 2025 THROUGH Jul 7 2025 MSG Vacation!` -Remind is wordier but nicer for recurring cases cases that require Julian dates, although I rarely do anything this complex. Remind is inarguably more powerful when it comes to conditions and recurrence: - **Then**: `Jul 10-19 2025 J%3-2`\ **When**: `m=jul & d>=10 & d<=19 & y=2024 & !(j%3-2)`\ **Remind**: `REM Jul 10 2025 *3 THROUGH Jul 19 2025` -## Then *optionally* can archive calendar events. +2. Then *optionally* can use an archive file to treat events like todos Take the event "Change air filter", scheduled every 6 months. I might see that upcoming and archive this instance a week early, which removes it from my calendar. Or I might let it slide until next weekend, in which case it sticks around until it's done. -I use many calendar events (bills, etc.) as a quasi-todo list, so I need to know when I've actually completed them, not just be reminded on a specific day. - -## It *optionally* shows a to-do list and upcoming deadlines. +3. It *optionally* shows a todo list and upcoming deadlines. # Alternatives diff --git a/src/then.nim b/src/then.nim index a74a863..5395ca4 100644 --- a/src/then.nim +++ b/src/then.nim @@ -1,14 +1,13 @@ -import std/[times, os, strutils, strbasics, strscans, sets, re, tables], parseopt +import std/[times, os, strutils, strbasics, strscans, sets, tables], parseopt +import then/[conditions,typechecker] const VERSION = "0.1" -const MONTHS = DefaultLocale.MMM.toHashSet + DefaultLocale.MMMM.toHashSet -const WEEKDAYS = DefaultLocale.ddd.toHashSet + DefaultLocale.dddd.toHashSet const configDir = expandTilde("~/.config/then/") const defaultConfigFile = configDir & "then.toml" const defaultCalendarFile = configDir & "calendar.then" -# Forward declarations -proc checkCond(cond: string, date: DateTime): bool +const MONTHS = DefaultLocale.MMM.toHashSet + DefaultLocale.MMMM.toHashSet +const WEEKDAYS = DefaultLocale.ddd.toHashSet + DefaultLocale.dddd.toHashSet # Creates a default configuration file if it doesn't exist proc createDefaultConfigFile() = @@ -53,104 +52,19 @@ proc printVersion() = echo "then " & VERSION quit() -# Check an individual condition (Jan) and return the condition type (month) -proc checkType(c: string): string = - var cond = c - if find(cond, re"^\d{4}$") > -1: return "year" - if find(cond, re"^\d{1,2}$") > -1: return "day" - if find(cond, re"^\dw$") > -1: return "firstweek" - if find(cond, re"^\dl$") > -1: return "lastweek" - if find(cond, re"^\([A-Za-z0-9 ]+\)$") > -1: return "dategroup" - if find(cond, re"^\d{4}-\d{4}$") > -1: return "rangeyear" - if find(cond, re"^\d{1,2}-\d{1,2}$") > -1: return "rangeday" - if find(cond, re"^\d{1,2}:\d{2}(-\d{1,2}:\d{2})?$") > -1: return "time" - if find(cond, re"^\(?\s*[Jj]\s*%\s*\d+\s*([-+]\d+)?\s*\)?$") > -1: return "julian" - cond = cond.toLower.capitalizeAscii - if cond in MONTHS: return "month" - if cond in WEEKDAYS: return "dayofweek" - - # Split c on - and check for ranges - let cs = cond.split("-", maxsplit = 1) - if cs.len != 2: return "unknown" - let startRange = cs[0].toLower.capitalizeAscii - let endRange = cs[1].toLower.capitalizeAscii - if (startRange in MONTHS) and (endRange in MONTHS): return "rangemonth" - if (startRange in WEEKDAYS) and (endRange in WEEKDAYS): return "rangedayofweek" - if find(startRange, re"^\([A-Za-z0-9 ]+\)$") > -1 and find(endRange, re"^\([A-Za-z0-9 ]+\)$") > -1: - return "rangedategroup" - return "unknown" - -proc getWeekFromEnd(date: DateTime): int = - let daysInMonth = getDaysInMonth(date.month, date.year) - return int((daysInMonth - date.monthday) / 7) + 1 - -proc getWeek(date: DateTime): int = - return int((date.monthDay - 1) / 7) + 1 - -# Check each condition in a date group like (Jan 1 2025) -proc checkDateGroup(s: string, date: DateTime): bool = - var conditions = s - let cs = conditions[1..conditions.len-2].splitWhitespace - for c in cs: - if checkCond(c, date) == false: - return false - return true - -proc checkRangeYear(s: string, date: DateTime): bool = - var years = s.split('-') - return date.year >= years[0] and date.year <= years[1] - -proc checkCond(cond: string, date: DateTime): bool = - var condition = cond - var invert = false - var found = false - if condition[0] == '!': - condition.delete(0..0) - invert = true - case checkType(condition): - of "time": - return true - of "year": - if parseInt(condition) == date.year: - found = true - of "day": - if condition == date.format("d") or condition == date.format("dd"): - found = true - of "month": - if condition == date.format("MMM") or condition == date.format("MMMM"): - found = true - of "dayofweek": - if condition == date.format("ddd") or condition == date.format("dddd"): - found = true - of "firstweek": - if parseInt(condition[0..1]) == getWeek(date): - found = true - of "lastweek": - if parseInt(condition[0..1]) == getWeekFromEnd(date): - found = true - of "dategroup": - found = checkDateGroup(condition, date) - of "rangeyear": - found = checkRangeYear(condition, date) - of "unknown": - echo "ERROR: Unknown condition: ", condition - if invert == true: - return not found - return found - # Splits a string on whitespace, but maintains whitespace in date groups proc splitWhitespaceExceptParens(str:string): seq[string] = - var result: seq[string] = newSeq[string]() + var output: seq[string] = newSeq[string]() var inParens = false for token in str.splitWhitespace: if inParens: - result.add(result.pop & ' ' & token) + output.add(output.pop & ' ' & token) else: - result.add(token) + output.add(token) for c in token: if c == '(': inParens = true if c == ')': inParens = false - return result + return output # Returns Table[conditionType, seq of conditions] proc parseDateToTable(date:string): Table[string,seq[string]] = @@ -177,7 +91,6 @@ proc getEvents(cal: seq[string], date: DateTime): seq[string] = for k,cs in conditions: var keyMatched = false for c in cs: - #var cond = c.toLower.capitalizeAscii if checkCond(c, date) == true: keyMatched = true break |
