Tons of changes

This commit is contained in:
2025-05-23 14:04:18 +02:00
parent 5bbde62142
commit 9a5aaea387

629
fzf-vjour
View File

@@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
set -eu set -u
# Read configuration # Read configuration
CONFIGFILE="$HOME/.config/fzf-vjour/config.yaml" CONFIGFILE="$HOME/.config/fzf-vjour/config.yaml"
@@ -24,71 +24,52 @@ SED_COLLECTIONLABELS_TO_NAMES=$(
yq '.collections[] | "s|\ *" + .label + "\ *|/" + .name + "/|"' <"$CONFIGFILE" | yq '.collections[] | "s|\ *" + .label + "\ *|/" + .name + "/|"' <"$CONFIGFILE" |
xargs printf "-e \"%s\" " xargs printf "-e \"%s\" "
) )
COLLECTION_LABEL_MAX_LEN=$(yq '[.collections[].label | length] | max' <"$CONFIGFILE") COLLECTION_NAME_MAX_LEN=$(yq '[.collections[].name | length] | max' <"$CONFIGFILE")
LABLES=$(yq '.collections[].label' <"$CONFIGFILE") LABLES=$(yq '.collections[].label' <"$CONFIGFILE")
SYNC_CMD=$(yq '.sync_cmd' <"$CONFIGFILE") SYNC_CMD=$(yq '.sync_cmd' <"$CONFIGFILE")
__vjournalnew() { __vtodopriority() {
python3 -c '
import sys
from datetime import date, datetime
from icalendar.cal import Calendar, Journal
if not len(sys.argv) == 2:
print("Pass uid as first argument!", file=sys.stderr)
sys.exit(1)
UID = sys.argv[1]
ical = Calendar()
j = Journal()
isnote = True
line = sys.stdin.readline().strip()
if line[:4] == "::: ":
isnote = False
line = sys.stdin.readline().strip()
if not line[:2] == "# ":
print("Error: Summary line is corrupt!", file=sys.stderr)
sys.exit(1)
summary = line[2:]
line = sys.stdin.readline().strip()
if not line[:2] == "> " and not line == ">":
print("Error: Categories line is corrupt!", file=sys.stderr)
sys.exit(1)
categories = line[2:].split(",")
line = sys.stdin.readline().strip()
if not line == "":
print("Error: Missing separating line!", file=sys.stderr)
sys.exit(1)
description = sys.stdin.read()
# Create ical
j["SUMMARY"] = summary
j.categories = categories
j["DESCRIPTION"] = description
j.LAST_MODIFIED = datetime.utcnow()
j.DTSTAMP = datetime.utcnow()
j["UID"] = UID
j["SEQUENCE"] = 1
j["STATUS"] = "FINAL"
if not isnote:
j.DTSTART = date.today()
ical.add_component(j)
ical["VERSION"] = "2.0"
ical["PRODID"] = "vjournew/basic"
# Print
print(ical.to_ical().decode().replace("\r\n", "\n"))' "$@"
}
__vjournalupdate() {
python3 -c ' python3 -c '
import sys import sys
from datetime import datetime from datetime import datetime
from icalendar.cal import Journal from icalendar.cal import Todo
if not len(sys.argv) == 3:
print("Pass ical file as first argument!", file=sys.stderr)
sys.exit(1)
increase = 1 if sys.argv[2] == "1" else -1
with open(sys.argv[1], "r") as f:
try:
ical = Todo.from_ical(f.read())
except Exception as e:
print(f"Failed to read vjournal file: {e}", file=sys.stderr)
sys.exit(1)
tlist = [component for component in ical.walk("VTODO")]
if len(tlist) == 0:
print("ical file is not a VTODO", file=sys.stderr)
sys.exit(1)
t = tlist[0]
# Update ical
priority = t.pop("PRIORITY")
priority = (int(priority) if priority else 0) + increase
priority = 0 if priority < 0 else 9 if priority > 9 else priority
t["PRIORITY"] = priority
# Print
print(ical.to_ical().decode().replace("\r\n", "\n"))
' "$@"
}
__vtodotogglecompleted() {
python3 -c '
import sys
from datetime import datetime
from icalendar.cal import Todo
from icalendar.prop import vDDDTypes
if not len(sys.argv) == 2: if not len(sys.argv) == 2:
print("Pass ical file as first argument!", file=sys.stderr) print("Pass ical file as first argument!", file=sys.stderr)
@@ -96,18 +77,64 @@ if not len(sys.argv) == 2:
with open(sys.argv[1], "r") as f: with open(sys.argv[1], "r") as f:
try: try:
ical = Journal.from_ical(f.read()) ical = Todo.from_ical(f.read())
except Exception as e: except Exception as e:
print(f"Failed to read vjournal file: {e}", file=sys.stderr) print(f"Failed to read vjournal file: {e}", file=sys.stderr)
sys.exit(1) sys.exit(1)
jlist = [component for component in ical.walk("VJOURNAL")] tlist = [component for component in ical.walk("VTODO")]
if len(jlist) == 0: if len(tlist) == 0:
print("ical file is not a VJOURNAL", file=sys.stderr) print("ical file is not a VTODO", file=sys.stderr)
sys.exit(1) sys.exit(1)
j = jlist[0] t = tlist[0]
# Update ical
if t.has_key("STATUS") and t["STATUS"] == "COMPLETED":
# Mark as not completed
t["STATUS"] = "NEEDS-ACTION"
t["PERCENT-COMPLETE"] = 0
if t.has_key("COMPLETED"): t.pop("COMPLETED")
else:
t["STATUS"] = "COMPLETED"
t["PERCENT-COMPLETE"] = 100
t["COMPLETED"] = vDDDTypes(datetime.utcnow())
# Print
print(ical.to_ical().decode().replace("\r\n", "\n"))
' "$@"
}
__vicalnew() {
python3 -c '
import sys
from datetime import date, datetime
from icalendar.cal import Calendar, Journal, Todo
from icalendar.prop import vDDDTypes
if not len(sys.argv) == 2:
print("Pass UID as first argument!", file=sys.stderr)
sys.exit(1)
UID = sys.argv[1]
start = None
due = None
line = sys.stdin.readline().strip() line = sys.stdin.readline().strip()
if line[:6] == "::: |>":
start = datetime.utcnow().date()
line = sys.stdin.readline().strip()
if line[:6] == "::: <|":
lst = line.split(" ")
due = True
if len(lst) >= 3:
try:
duedate = datetime.strptime(lst[2], "%Y-%m-%d").date()
due = duedate
except Exception as e:
pass
line = sys.stdin.readline().strip()
if not line[:2] == "# ": if not line[:2] == "# ":
print("Error: Summary line is corrupt!", file=sys.stderr) print("Error: Summary line is corrupt!", file=sys.stderr)
sys.exit(1) sys.exit(1)
@@ -115,11 +142,142 @@ summary = line[2:]
line = sys.stdin.readline().strip() line = sys.stdin.readline().strip()
if not line[:2] == "> " and not line == ">": if not line[:2] == "> " and not line == ">":
print("Error: Categories line is corrupt!", file=sys.stderr) categories = []
sys.exit(1) else:
categories = line[2:].split(",") categories = line[2:].split(",")
line = sys.stdin.readline().strip()
if not line == "":
print("Error: Missing separating line!", file=sys.stderr)
sys.exit(1)
description = sys.stdin.read()
# Create ical
now = datetime.utcnow()
ical = Calendar()
if due:
o = Todo()
# The following are REQUIRED, but MUST NOT occur more than once.
# dtstamp / uid /
o.DTSTAMP = now
o["UID"] = UID
# The following are OPTIONAL, but MUST NOT occur more than once.
# class / completed / created / description / dtstart / geo / last-mod /
# location / organizer / percent / priority / recurid / seq / status /
# summary / url /
o["CLASS"] = "PRIVATE"
o["CREATED"] = vDDDTypes(now)
o["DESCRIPTION"] = description.strip()
o.LAST_MODIFIED = now
o["PRIORITY"] = 0
o["SEQUENCE"] = 0
o["STATUS"] = "NEEDS-ACTION"
o["SUMMARY"] = summary
# The following is OPTIONAL, but SHOULD NOT occur more than once.
# rrule /
# Either "due" or "duration" MAY appear in a "todoprop", but "due" and
# "duration" MUST NOT occur in the same "todoprop". If "duration" appear in
# a "todoprop", then "dtstart" MUST also appear in the same "todoprop".
# due / duration /
if isinstance(due, date):
o.DUE = due
# The following are OPTIONAL, and MAY occur more than once.
# attach / attendee / categories / comment / contact / exdate / rstatus /
# related / resources / rdate / x-prop / iana-prop
o.categories = categories
else:
o = Journal()
# The following are REQUIRED, but MUST NOT occur more than once.
# dtstamp / uid /
o.DTSTAMP = now
o["UID"] = UID
# The following are OPTIONAL, but MUST NOT occur more than once.
# class / created / dtstart / last-mod / organizer / recurid / seq / status
# / summary / url /
o["CLASS"] = "PRIVATE"
o["CREATED"] = vDDDTypes(now)
o.DTSTART = start
o.LAST_MODIFIED = now
o["SEQUENCE"] = 0
o["STATUS"] = "FINAL"
o["SUMMARY"] = summary
# The following is OPTIONAL, but SHOULD NOT occur more than once.
# rrule /
# The following are OPTIONAL, and MAY occur more than once.
# attach / attendee / categories / comment / contact / description / exdate
# / related / rdate / rstatus / x-prop / iana-prop
o.categories = categories
o["DESCRIPTION"] = description.strip()
ical.add_component(o)
ical["PRODID"] = "fzf-vjour/basic"
ical["VERSION"] = "2.0"
# Print
print(ical.to_ical().decode().replace("\r\n", "\n"))' "$@"
}
__icalupdate() {
python3 -c '
import sys
from datetime import date, datetime
from icalendar.cal import Calendar, Todo
if not len(sys.argv) == 2:
print("Pass ical file as first argument!", file=sys.stderr)
sys.exit(1)
with open(sys.argv[1], "r") as f:
try:
ical = Calendar.from_ical(f.read())
except Exception as e:
print(f"Failed to read ical file: {e}", file=sys.stderr)
sys.exit(1)
olist = [component for component in ical.walk(select=lambda c:c.name in ["VJOURNAL", "VTODO"])]
if len(olist) == 0:
sys.exit(0)
o = olist[0]
line = sys.stdin.readline().strip() line = sys.stdin.readline().strip()
due = None
if isinstance(o, Todo):
if not line[:6] == "::: <|":
print("Error: Due date line is corrupt!", file=sys.stderr)
sys.exit(1)
lst = line.split(" ")
due = True
if len(lst) >= 3:
try:
duedate = datetime.strptime(lst[2], "%Y-%m-%d").date()
due = duedate
except Exception as e:
pass
line = sys.stdin.readline().strip()
if not line[:2] == "# ":
print("Error: Summary line is corrupt!", file=sys.stderr)
sys.exit(1)
summary = line[2:]
line = sys.stdin.readline().strip()
if not line[:2] == "> " and not line == ">":
categories = []
else:
categories = line[2:].split(",")
line = sys.stdin.readline().strip()
if not line == "": if not line == "":
print("Error: Missing separating line!", file=sys.stderr) print("Error: Missing separating line!", file=sys.stderr)
sys.exit(1) sys.exit(1)
@@ -127,100 +285,77 @@ if not line == "":
description = sys.stdin.read() description = sys.stdin.read()
# Update ical # Update ical
j["SUMMARY"] = summary if due:
j.categories = categories if isinstance(due, date):
j["DESCRIPTION"] = description o.DUE = due
j.LAST_MODIFIED = datetime.utcnow() elif "DUE" in o.keys():
o.pop("DUE")
o["SUMMARY"] = summary
o.categories = categories
o["DESCRIPTION"] = description.strip()
o.LAST_MODIFIED = datetime.utcnow()
seq = o["SEQUENCE"] if "SEQUENCE" in o.keys() else 0
o["SEQUENCE"] = seq + 1
# Print # Print
print(ical.to_ical().decode().replace("\r\n", "\n")) print(ical.to_ical().decode().replace("\r\n", "\n"))
' "$@" ' "$@"
} }
__vjournal2json() { __ical2json() {
python3 -c ' python3 -c '
import sys import sys
import json import json
from datetime import datetime from datetime import datetime
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
from icalendar.cal import Journal from icalendar.cal import Calendar, Todo
input_data = sys.stdin.read() input_data = sys.stdin.read()
try: try:
ical = Journal.from_ical(input_data) ical = Calendar.from_ical(input_data)
except Exception as e: except Exception as e:
print(f"Failed to read vjournal file: {e}", file=sys.stderr) print(f"Failed to read ical file: {e}", file=sys.stderr)
sys.exit(1) sys.exit(1)
jlist = [component for component in ical.walk("VJOURNAL")] olist = [component for component in ical.walk(select=lambda c:c.name in ["VJOURNAL", "VTODO"])]
if len(jlist) == 0: if len(olist) == 0:
sys.exit(0) sys.exit(0)
j = jlist[0] o = olist[0]
local_tz = ZoneInfo("localtime") local_tz = ZoneInfo("localtime")
data = { data = {
"summary": j.get("SUMMARY"), "summary": o.get("SUMMARY"),
"description": j.get("DESCRIPTION"), "description": o.get("DESCRIPTION") if "DESCRIPTION" in o.keys() else "",
"categories": j.categories, "categories": o.categories,
"class": j.get("CLASS"), "class": o.get("CLASS"),
"created": str(j.DTSTAMP.astimezone(local_tz)), "created": str(o.DTSTAMP.astimezone(local_tz)) if o.DTSTAMP else "",
"last_modified": str(j.LAST_MODIFIED.astimezone(local_tz)), "last_modified": str(o.LAST_MODIFIED.astimezone(local_tz)) if o.LAST_MODIFIED else "",
"start": str( "start": str(
j.DTSTART.astimezone(local_tz) o.DTSTART.astimezone(local_tz)
if isinstance(j.DTSTART, datetime) if isinstance(o.DTSTART, datetime)
else j.DTSTART or "" else o.DTSTART or ""
), ),
} }
if isinstance(o, Todo):
data["due"] = str(o.DUE) if o.DUE else ""
print(json.dumps(data))' print(json.dumps(data))'
} }
# Process each file
# This function takes two arguments:
#
# @param string: Path to ics file
# @param string: Maximum length of filenames (for padding purposes)
__filepath_to_searchline() {
filepath="$1"
maxlen="$2"
totallen=$((maxlen + ${#ROOT}))
filepathpad=$(printf "%-${totallen}s" "$filepath")
# Color support __date_not_in_future() {
GREEN=$(printf '\033[1;32m') date_target=$(date -d "$1" +%s)
WHITE=$(printf '\033[1;97m') date_today=$(date -d "00:00" +%s)
FAINT=$(printf '\033[2m') date_delta=$((date_target - date_today))
OFF=$(printf '\033[m') if [ "$date_delta" -le 0 ]; then
echo 1
# Parse file
summary=""
categories=""
dtstamp=""
dtstart=""
while IFS= read -r line; do
case "$line" in
SUMMARY:*)
summary="${line#SUMMARY:}"
;;
CATEGORIES:*)
categories="${line#CATEGORIES:}"
;;
DTSTAMP:*)
dtstamp="${line#DTSTAMP:}"
;;
DTSTART*:[0-9]*)
dtstart=$(echo "$line" | grep -oE '[0-9]{8}')
;;
esac
if [ -n "$summary" ] && [ -n "$categories" ] && [ -n "$dtstamp" ] && [ -n "$dtstart" ]; then
break
fi fi
done <"$filepath" }
# Parse date __date_to_expression() {
if [ -n "$dtstart" ]; then date_target=$(date -d "$1" +%s)
date_target=$(date -d "$dtstart" +%s) date_today=$(date -d "00:00" +%s)
date_today=$(date +%s)
date_delta=$(((date_target - date_today) / 86400)) date_delta=$(((date_target - date_today) / 86400))
date_expr=$date_delta date_expr=$date_delta
if [ "$date_delta" -eq 0 ]; then if [ "$date_delta" -eq 0 ]; then
@@ -230,21 +365,87 @@ __filepath_to_searchline() {
elif [ "$date_delta" -eq 1 ]; then elif [ "$date_delta" -eq 1 ]; then
date_expr="tomorrow" date_expr="tomorrow"
elif [ "$date_delta" -lt -1 ] && [ "$date_delta" -ge -7 ]; then elif [ "$date_delta" -lt -1 ] && [ "$date_delta" -ge -7 ]; then
date_expr="last $(date -d "$dtstart" +%A)" date_expr="last $(date -d "$1" +%A)"
elif [ "$date_delta" -gt 1 ] && [ "$date_delta" -le 7 ]; then elif [ "$date_delta" -gt 1 ] && [ "$date_delta" -le 7 ]; then
date_expr="next $(date -d "$dtstart" +%A)" date_expr="next $(date -d "$1" +%A)"
else else
date_expr=$(date -d "$dtstart" +%x) date_expr=$(date -d "$1" +%x)
fi fi
date_emoji="📘" echo "$date_expr"
}
# Process each file
# This function takes two arguments:
#
# @param string: Path to ics file
# @param string: Maximum length of filenames (for padding purposes)
__filepath_to_searchline() {
filepath="$1"
maxlen="$2"
totallen=$((maxlen + ${#ROOT} + COLLECTION_NAME_MAX_LEN + 2))
#filepathpad=$(printf "%-${totallen}s" "$filepath")
filepathpad=$(dirname "$filepath")/$(printf "%-${maxlen}s" "$(basename "$filepath")")
# Color support
GREEN=$(printf '\033[1;32m')
RED=$(printf '\033[1;31m')
WHITE=$(printf '\033[1;97m')
FAINT=$(printf '\033[2m')
OFF=$(printf '\033[m')
# Parse file
summary=$(grep '^SUMMARY:' "$filepath" | cut -d ':' -f 2)
categories=$(grep '^CATEGORIES:' "$filepath" | cut -d ':' -f 2)
dtstamp=$(grep '^DTSTAMP:' "$filepath" | cut -d ':' -f 2)
dtstart=$(grep '^DTSTART' "$filepath" | grep -oE '[0-9]{8}')
due=$(grep '^DUE' "$filepath" | grep -oE '[0-9]{8}')
priority=$(grep '^PRIORITY:' "$filepath" | cut -d ':' -f 2)
task=$(grep '^BEGIN:VTODO' "$filepath")
completed=$(grep '^STATUS:COMPLETED' "$filepath")
# Parse date
if [ -n "$dtstart" ]; then
emoji="📘"
date_expr=$(__date_to_expression "$dtstart")
else else
date_emoji="🗒️" emoji="🗒️"
date_expr="" date_expr=""
fi fi
# Check if this is a task
if [ -n "$task" ]; then
emoji="🔲"
if [ -n "$completed" ]; then
emoji="✅"
fi
fi
# Check Priority
if [ -n "$priority" ] && [ "$priority" -gt 0 ]; then
prioritymsg="❗($priority) "
priority=$((10 - priority))
else
prioritymsg=""
priority=0
fi
# Check due date
if [ -n "$due" ]; then
date_expr=$(__date_to_expression "$due")
fi
date_expr=$(printf "%12s" "$date_expr") date_expr=$(printf "%12s" "$date_expr")
# Color date
notinfuture=$(__date_not_in_future "$due")
if [ -n "$notinfuture" ] && [ -n "$due" ]; then
date_expr="$RED$date_expr$OFF"
summary_color="$RED"
else
date_expr="$WHITE$date_expr$OFF"
summary_color="$GREEN"
fi
# Print line # Print line
echo "$dtstamp $filepath $WHITE$date_expr$OFF $date_emoji $GREEN$summary$OFF $FAINT$categories$OFF" echo "$priority $dtstamp $filepathpad $date_expr $emoji $prioritymsg$summary_color$summary$OFF $FAINT$categories$OFF"
} }
__lines() { __lines() {
@@ -280,59 +481,127 @@ __filepath_from_selection() {
# Program starts here # Program starts here
if [ "${1:-}" = "--help" ]; then
echo "Usage: $0 [OPTION]"
echo ""
echo "You may specify at most one option."
echo " --help Show this help and exit"
echo " --tasks Show tasks only"
echo " --no-tasks Ignore tasks"
echo " --completed Show completed tasks only"
echo " --no-completed Ignore completed tasks"
echo " --new Create new entry"
echo ""
echo "The following options are for internal use."
echo " --reload Reload list"
echo " --preview <selection> Generate preview"
echo " --delete <selection> Delete selected entry"
echo " --decrease-priority <selection> Decrease priority of selected task"
echo " --increase-priority <selection> Increase priority of selected task"
echo " --toggle-completed <selection> Toggle completion flag of task"
exit
fi
# Command line arguments to be self-contained # Command line arguments to be self-contained
# Generate preview of file from selection # Generate preview of file from selection
if [ "${1:-}" = "--preview" ]; then if [ "${1:-}" = "--preview" ]; then
vjfile=$(__filepath_from_selection "$2") vjfile=$(__filepath_from_selection "$2")
__vjournal2json <"$vjfile" | jq -r ".description" | batcat --color=always --style=numbers --language=md __ical2json <"$vjfile" | jq -r ".description" | batcat --color=always --style=numbers --language=md
exit exit
fi fi
# Delete file from selection # Delete file from selection
if [ "${1:-}" = "--delete" ]; then if [ "${1:-}" = "--delete" ]; then
vjfile=$(__filepath_from_selection "$2") vjfile=$(__filepath_from_selection "$2")
rm -v "$vjfile" rm -i "$vjfile"
fi fi
# Generate new entry # Generate new entry
if [ "${1:-}" = "--new" ]; then if [ "${1:-}" = "--new" ]; then
label_new=$(echo "$LABLES" | fzf \ collection=$(echo "$LABLES" | fzf \
--margin 20% \ --margin 20% \
--prompt="Select collection> ") --prompt="Select collection> ")
uuid_new=$(uuidgen) file=""
vjfile_new=$(__filepath_from_selection "$label_new $uuid_new.ics") while [ -f "$file" ] || [ -z "$file" ]; do
if [ -f "$vjfile_new" ]; then uuid=$(uuidgen)
echo "Bad luck..." file=$(__filepath_from_selection "$collection $uuid.ics")
return 1 done
fi tmpmd=$(mktemp --suffix='.md')
# tmpsha="$tmpmd.sha"
TMPFILE=$(mktemp --suffix='.md')
SHAFILE="$TMPFILE.sha"
{ {
echo "::: Keep this line if you want to add a **JOURNAL** entry (associated to today), else we will add a **NOTE**" echo "::: |> <!-- keep this line to associate the entry to _today_ -->"
echo "# <write summary here>" echo "::: <| <!-- specify the due date for to-dos, can be empty -->"
echo "> <comma-separated list of categories>" echo "# <!-- write summary here -->"
echo "> <!-- comma-separated list of categories -->"
echo "" echo ""
} >"$TMPFILE" } >"$tmpmd"
sha1sum "$TMPFILE" >"$SHAFILE" sha1sum "$tmpmd" >"$tmpsha"
# Open in editor # Open in editor
$EDITOR "$TMPFILE" >/dev/tty $EDITOR "$tmpmd" >/dev/tty
# Update if changes are detected # Update if changes are detected
if ! sha1sum -c "$SHAFILE" >/dev/null 2>&1; then if ! sha1sum -c "$tmpsha" >/dev/null 2>&1; then
vjfile_tmp="$TMPFILE.ics" tmpfile="$tmpmd.ics"
__vjournalnew "$uuid_new" <"$TMPFILE" >"$vjfile_tmp" && mv "$vjfile_tmp" "$vjfile_new" || rm "$vjfile_tmp" tmpferr="$tmpmd.err"
if __vicalnew "$uuid" <"$tmpmd" >"$tmpfile" 2>"$tmpferr"; then
mv "$tmpfile" "$file"
else
rm "$tmpfile"
less "$tmpferr"
fi fi
rm "$TMPFILE" "$SHAFILE" rm "$tmpferr"
fi
rm "$tmpmd" "$tmpsha"
fi
# Toggle completed flag
if [ "${1:-}" = "--toggle-completed" ]; then
vtfile=$(__filepath_from_selection "$2")
vtfile_tmp=$(mktemp)
__vtodotogglecompleted "$vtfile" >"$vtfile_tmp" && mv "$vtfile_tmp" "$vtfile" || rm "$vtfile_tmp"
fi
# Increase priority
if [ "${1:-}" = "--increase-priority" ]; then
vtfile=$(__filepath_from_selection "$2")
vtfile_tmp=$(mktemp)
__vtodopriority "$vtfile" "1" >"$vtfile_tmp" && mv "$vtfile_tmp" "$vtfile" || rm "$vtfile_tmp"
fi
# Decrease priority
if [ "${1:-}" = "--decrease-priority" ]; then
vtfile=$(__filepath_from_selection "$2")
vtfile_tmp=$(mktemp)
__vtodopriority "$vtfile" "-1" >"$vtfile_tmp" && mv "$vtfile_tmp" "$vtfile" || rm "$vtfile_tmp"
fi fi
if [ "${1:-}" = "--reload" ]; then if [ "${1:-}" = "--reload" ]; then
__lines __lines
exit exit
fi fi
query="${FZF_QUERY:-}"
if [ "${1:-}" = "--no-completed" ]; then
query="!✅"
fi
if [ "${1:-}" = "--completed" ]; then
query="✅"
fi
if [ "${1:-}" = "--tasks" ]; then
query="✅ | 🔲"
fi
if [ "${1:-}" = "--no-tasks" ]; then
query="!✅ !🔲"
fi
if [ -z "$query" ]; then
query="!✅"
fi
query=$(echo "$query" | sed 's/ *$//g')
selection=$( selection=$(
__lines | fzf --ansi \ __lines | fzf --ansi \
--query="$query " \
--no-sort \
--no-hscroll \
--preview="$0 --preview {}" \ --preview="$0 --preview {}" \
--bind="ctrl-d:become($0 --delete {})" \ --bind="ctrl-d:become($0 --delete {})" \
--bind="ctrl-x:become($0 --toggle-completed {})" \
--bind="alt-up:become($0 --increase-priority {})" \
--bind="alt-down:become($0 --decrease-priority {})" \
--bind="ctrl-n:become($0 --new)" \ --bind="ctrl-n:become($0 --new)" \
--bind="ctrl-s:execute($SYNC_CMD)" \ --bind="ctrl-s:execute($SYNC_CMD)" \
--bind="ctrl-r:reload-sync($0 --reload)" --bind="ctrl-r:reload-sync($0 --reload)"
@@ -341,40 +610,44 @@ if [ -z "$selection" ]; then
return 0 return 0
fi fi
VJ_FILE=$(__filepath_from_selection "$selection") file=$(__filepath_from_selection "$selection")
if [ ! -f "$VJ_FILE" ]; then if [ ! -f "$file" ]; then
echo "ERROR: File '$VJ_FILE' does not exist!" echo "ERROR: File '$file' does not exist!"
return 1 return 1
fi fi
# Parse vjournal file and save as json # Parse vjournal file and save as json
TMPJSON=$(mktemp) filejson=$(mktemp)
__vjournal2json <"$VJ_FILE" >"$TMPJSON" __ical2json <"$file" >"$filejson"
# Prepare file to be edited # Prepare file to be edited
TMPFILE=$(mktemp --suffix='.md') filetmp=$(mktemp --suffix='.md')
SHAFILE="$TMPFILE.sha" filesha="$filetmp.sha"
SUMMARY=$(jq -r '.summary' "$TMPJSON") if jq -e '.due' "$filejson"; then
CATEGORIES=$(jq -r '.categories | join(",")' "$TMPJSON") due=$(jq -r '.due' "$filejson")
echo "::: <| $due" >"$filetmp"
fi
summary=$(jq -r '.summary' "$filejson")
categories=$(jq -r '.categories | join(",")' "$filejson")
{ {
echo "# $SUMMARY" echo "# $summary"
echo "> $CATEGORIES" echo "> $categories"
echo "" echo ""
jq -r '.description' "$TMPJSON" jq -r '.description' "$filejson"
} >"$TMPFILE" } >>"$filetmp"
rm "$TMPJSON" rm "$filejson"
sha1sum "$TMPFILE" >"$SHAFILE" sha1sum "$filetmp" >"$filesha"
# Open in editor # Open in editor
$EDITOR "$TMPFILE" $EDITOR "$filetmp"
# Update only if changes are detected # Update only if changes are detected
if ! sha1sum -c "$SHAFILE" >/dev/null 2>&1; then if ! sha1sum -c "$filesha" >/dev/null 2>&1; then
echo "Uh... chages detected!" echo "Uh... chages detected!"
vj_file_new="$TMPFILE.ics" vj_file_new="$filetmp.ics"
__vjournalupdate "$VJ_FILE" <"$TMPFILE" >"$vj_file_new" && mv "$vj_file_new" "$VJ_FILE" || rm "$vj_file_new" __icalupdate "$file" <"$filetmp" >"$vj_file_new" && mv "$vj_file_new" "$file" || rm "$vj_file_new"
fi fi
rm "$TMPFILE" "$SHAFILE" rm "$filetmp" "$filesha"
exec "$0" exec "$0"