1269 lines
37 KiB
Bash
Executable File
1269 lines
37 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
set -eu
|
|
# TODO: Make sensitive to failures. I don't want to miss appointments!
|
|
# TODO Ensure safe use of delimiters
|
|
|
|
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 " --today Show today's appointments"
|
|
echo " --yesterday Show yesterday's appointments"
|
|
echo " --tomorrow Show tomorrow's appointments"
|
|
echo " --goto Interactively enter date to jump to"
|
|
echo " --new [date/date-time] Create new entry (today)"
|
|
echo " --day [date] Show appointments of specified day (today)"
|
|
echo " --week [date] Show week of specified date (today)"
|
|
echo " --import file Import iCalendar file"
|
|
echo " --import-ni file Import iCalendar file non-interactively"
|
|
echo " --git cmd Run git command cmd relative to calendar root"
|
|
echo " --git-init Enable the use of git"
|
|
echo ""
|
|
echo "You may also start this program with setting locale and timezone"
|
|
echo "information. For instance, to see and modify all of your calendar"
|
|
echo "entries from the perspective of Saigon, run"
|
|
echo "TZ='Asia/Saigon' $0"
|
|
echo "Likewise, you may specify the usage of Greek with"
|
|
echo "LC_TIME=el_GR.UTF-8 $0"
|
|
exit
|
|
fi
|
|
|
|
###
|
|
### Helper functions
|
|
### err
|
|
###
|
|
|
|
# err()
|
|
# This is a helper function to print errors.
|
|
#
|
|
# @input $1: Error message
|
|
err() {
|
|
echo "❌ $1" >/dev/tty
|
|
}
|
|
|
|
###
|
|
### preview helper functions
|
|
### month_previous
|
|
### month_next
|
|
### datetime_str
|
|
###
|
|
|
|
# month_previous()
|
|
# Print previous month of specified input month as <month> <year>.
|
|
#
|
|
# @input $1: Month
|
|
# @input $2: Year
|
|
month_previous() {
|
|
month=$(echo "$1" | sed 's/^0//')
|
|
year=$(echo "$2" | sed 's/^0//')
|
|
if [ "$month" -eq 1 ]; then
|
|
month=12
|
|
year=$((year - 1))
|
|
else
|
|
month=$((month - 1))
|
|
fi
|
|
echo "$month $year"
|
|
}
|
|
|
|
# month_next()
|
|
# Print next month of specified input month as <month> <year>.
|
|
#
|
|
# @input $1: Month
|
|
# @input $2: Year
|
|
month_next() {
|
|
month=$(echo "$1" | sed 's/^0//')
|
|
year=$(echo "$2" | sed 's/^0//')
|
|
if [ "$month" -eq 12 ]; then
|
|
month=1
|
|
year=$((year + 1))
|
|
else
|
|
month=$((month + 1))
|
|
fi
|
|
echo "$month $year"
|
|
}
|
|
|
|
# datetime_str()
|
|
# Print date or datetime in a human readable form.
|
|
#
|
|
# @input $1: Seconds since epoch
|
|
# @input $2.. (optoinal): Prepend date format
|
|
datetime_str() {
|
|
s="$1"
|
|
shift
|
|
t=$(date -d "@$s" +"%R")
|
|
dfmt="$*%e %b %Y"
|
|
if [ "$t" != "00:00" ]; then
|
|
dfmt="$dfmt %R %Z"
|
|
fi
|
|
date -d "@$s" +"$dfmt"
|
|
}
|
|
|
|
###
|
|
### Preview command-line options
|
|
### --preview-event
|
|
### --preview_week
|
|
###
|
|
|
|
# --preview-event
|
|
# Print preview of event and exit.
|
|
#
|
|
# @input $1: Line from day view containing an event
|
|
# @req $ROOT: Path that contains the collections (see configuration)
|
|
# @req $AWK_GET: Awk script to extract fields from iCalendar file
|
|
# @req $CAT: Program to print
|
|
# @req colors
|
|
if [ "${1:-}" = "--preview-event" ]; then
|
|
hour=$(echo "$2" | cut -d '|' -f 2)
|
|
start=$(echo "$2" | cut -d '|' -f 3)
|
|
end=$(echo "$2" | cut -d '|' -f 4)
|
|
fpath=$(echo "$2" | cut -d '|' -f 5 | sed "s/ /|/g")
|
|
if [ -n "$hour" ] && [ -n "$fpath" ]; then
|
|
fpath="$ROOT/$fpath"
|
|
start=$(datetime_str "$start" "%a ")
|
|
end=$(datetime_str "$end" "%a ")
|
|
location=$(awk -v field="LOCATION" "$AWK_GET" "$fpath")
|
|
status=$(awk -v field="STATUS" "$AWK_GET" "$fpath")
|
|
if [ "$status" = "TENTATIVE" ]; then
|
|
symb="🟡"
|
|
elif [ "$status" = "CANCELLED" ]; then
|
|
symb="❌"
|
|
fi
|
|
echo "📅${symb:-} ${CYAN}$start${OFF} → ${CYAN}$end${OFF}"
|
|
if [ -n "${location:-}" ]; then
|
|
echo "📍 ${CYAN}$location${OFF}"
|
|
fi
|
|
echo ""
|
|
awk -v field="DESCRIPTION" "$AWK_GET" "$fpath" | $CAT
|
|
fi
|
|
exit
|
|
fi
|
|
|
|
# --preview-week
|
|
# Print preview of week.
|
|
#
|
|
# @input $2: Line from week view
|
|
# @req $AWK_CALSHIFT: Awk script to make `cal` output to start on Mondays
|
|
# @req $AWK_CALANNOT: Awk script to annotate calendar
|
|
if [ "${1:-}" = "--preview-week" ]; then
|
|
sign=$(echo "$2" | cut -d '|' -f 1)
|
|
if [ "$sign" = "+" ]; then
|
|
startdate=$(echo "$2" | cut -d '|' -f 2)
|
|
set -- $(date -d "$startdate" +"%Y %m %d")
|
|
year=$1
|
|
month=$2
|
|
day=$3
|
|
set -- $(date -d "today" +"%Y %m %d")
|
|
year_cur=$1
|
|
month_cur=$2
|
|
day_cur=$3
|
|
# Previous months
|
|
set -- $(month_previous "$month" "$year")
|
|
month_pre="$1"
|
|
year_pre="$2"
|
|
set -- $(month_previous "$month_pre" "$year_pre")
|
|
month_pre2="$1"
|
|
year_pre2="$2"
|
|
# Next months
|
|
set -- $(month_next "$month" "$year")
|
|
month_nex="$1"
|
|
year_nex="$2"
|
|
set -- $(month_next "$month_nex" "$year_nex")
|
|
month_nex2="$1"
|
|
year_nex2="$2"
|
|
set -- $(month_next "$month_nex2" "$year_nex2")
|
|
month_nex3="$1"
|
|
year_nex3="$2"
|
|
# Highlight today
|
|
if [ "$month_pre2" -eq "$month_cur" ] && [ "$year_pre2" -eq "$year_cur" ]; then
|
|
var_pre2=$day_cur
|
|
fi
|
|
if [ "$month_pre" -eq "$month_cur" ] && [ "$year_pre" -eq "$year_cur" ]; then
|
|
var_pre=$day_cur
|
|
fi
|
|
if [ "$month" -eq "$month_cur" ] && [ "$year" -eq "$year_cur" ]; then
|
|
var=$day_cur
|
|
fi
|
|
if [ "$month_nex" -eq "$month_cur" ] && [ "$year_nex" -eq "$year_cur" ]; then
|
|
var_nex=$day_cur
|
|
fi
|
|
if [ "$month_nex2" -eq "$month_cur" ] && [ "$year_nex2" -eq "$year_cur" ]; then
|
|
var_nex2=$day_cur
|
|
fi
|
|
if [ "$month_nex3" -eq "$month_cur" ] && [ "$year_nex3" -eq "$year_cur" ]; then
|
|
var_nex3=$day_cur
|
|
fi
|
|
# show
|
|
(
|
|
cal "$month_pre2" "$year_pre2" | awk "$AWK_CALSHIFT" | awk -v cur="${var_pre2:-}" "$AWK_CALANNOT"
|
|
cal "$month_pre" "$year_pre" | awk "$AWK_CALSHIFT" | awk -v cur="${var_pre:-}" "$AWK_CALANNOT"
|
|
cal "$month" "$year" | awk "$AWK_CALSHIFT" | awk -v cur="${var:-}" -v day="$day" "$AWK_CALANNOT"
|
|
cal "$month_nex" "$year_nex" | awk "$AWK_CALSHIFT" | awk -v cur="${var_nex:-}" "$AWK_CALANNOT"
|
|
cal "$month_nex2" "$year_nex2" | awk "$AWK_CALSHIFT" | awk -v cur="${var_nex2:-}" "$AWK_CALANNOT"
|
|
cal "$month_nex3" "$year_nex3" | awk "$AWK_CALSHIFT" | awk -v cur="${var_nex3:-}" "$AWK_CALANNOT"
|
|
) | awk '{ l[NR%8] = l[NR%8] " " $0 } END {for (i in l) if (i>0) print l[i] }'
|
|
fi
|
|
exit
|
|
fi
|
|
|
|
###
|
|
### View Functions
|
|
### __view_day
|
|
### __view_week
|
|
### __view_all
|
|
###
|
|
|
|
# __view_day()
|
|
# This function prints the view for the day specified in `$DISPLAY_DATE`.
|
|
#
|
|
# @req $DISPLAY_DATE: Specification of the day to show
|
|
# @req $WEEKLY_DATA_FILE: Filename of weekly data (see `__refresh_data` and `__load_weeks`)
|
|
# @req $ROOT: Path that contains the collections (see configuration)
|
|
# @req $COLLECTION_LABELS: Mapping between collections and lables (see configuration)
|
|
# @req $AWK_PARSE: Parse awk script
|
|
# @req $DAY_START: Start time of the day (see configuration)
|
|
# @req $DAY_END: Start time of the day (see configuration)
|
|
# @req $AWK_DAYVIEW: Day-view awk script
|
|
__view_day() {
|
|
weeknr=$(date -d "$DISPLAY_DATE" +"%G.%V")
|
|
files=$(grep "^$weeknr\ " "$WEEKLY_DATA_FILE" | cut -d " " -f 2-)
|
|
# Find relevant files in list of week files
|
|
sef=$({
|
|
set -- $files
|
|
for file in "$@"; do
|
|
file="$ROOT/$file"
|
|
awk \
|
|
-v collection_labels="$COLLECTION_LABELS" \
|
|
"$AWK_PARSE" "$file"
|
|
done
|
|
})
|
|
today=$(date -d "$DISPLAY_DATE" +"%D")
|
|
if [ -n "$sef" ]; then
|
|
sef=$(echo "$sef" | while IFS= read -r line; do
|
|
set -- $line
|
|
starttime="$1"
|
|
shift
|
|
endtime="$1"
|
|
shift
|
|
fpath="$(echo "$1" | sed 's/|/ /g')" # we will use | as delimiter (need to convert back!)
|
|
shift
|
|
collection="$1"
|
|
shift
|
|
status="$1"
|
|
shift
|
|
description="$(echo "$*" | sed 's/|/:/g')" # we will use | as delimiter
|
|
#
|
|
daystart=$(date -d "$today 00:00:00" +"%s")
|
|
dayend=$(date -d "$today 23:59:59" +"%s")
|
|
line=""
|
|
if [ "$starttime" -gt "$daystart" ] && [ "$starttime" -lt "$dayend" ]; then
|
|
s=$(date -d "@$starttime" +"%R")
|
|
elif [ "$starttime" -le "$daystart" ] && [ "$endtime" -gt "$daystart" ]; then
|
|
s="00:00"
|
|
else
|
|
continue
|
|
fi
|
|
if [ "$endtime" -gt "$daystart" ] && [ "$endtime" -lt "$dayend" ]; then
|
|
e=$(date -d "@$endtime" +"%R")
|
|
elif [ "$endtime" -ge "$dayend" ] && [ "$starttime" -lt "$dayend" ]; then
|
|
e="00:00"
|
|
else
|
|
continue
|
|
fi
|
|
echo "$s|$e|$starttime|$endtime|$fpath|$collection|$description|$status"
|
|
done)
|
|
fi
|
|
echo "$sef" | sort -n | awk -v today="$today" -v daystart="$DAY_START" -v dayend="$DAY_END" "$AWK_DAYVIEW"
|
|
}
|
|
|
|
# __view_week()
|
|
# This function prints the view for the week that contains the day specified in `$DISPLAY_DATE`.
|
|
#
|
|
# @req $DISPLAY_DATE: Specification of the day to show
|
|
# @req $WEEKLY_DATA_FILE: Filename of weekly data (see `__refresh_data` and `__load_weeks`)
|
|
# @req $ROOT: Path that contains the collections (see configuration)
|
|
# @req $COLLECTION_LABELS: Mapping between collections and lables (see configuration)
|
|
# @req $AWK_WEEKVIEW: Week-view awk script
|
|
# @req colors
|
|
__view_week() {
|
|
weeknr=$(date -d "$DISPLAY_DATE" +"%G.%V")
|
|
files=$(grep "^$weeknr\ " "$WEEKLY_DATA_FILE" | cut -d " " -f 2-)
|
|
dayofweek=$(date -d "$DISPLAY_DATE" +"%u")
|
|
delta=$((1 - dayofweek))
|
|
startofweek=$(date -d "$DISPLAY_DATE -$delta days" +"%D")
|
|
# loop over files
|
|
sef=$({
|
|
set -- $files
|
|
for file in "$@"; do
|
|
file="$ROOT/$file"
|
|
awk \
|
|
-v collection_labels="$COLLECTION_LABELS" \
|
|
"$AWK_PARSE" "$file"
|
|
done
|
|
})
|
|
if [ -n "$sef" ]; then
|
|
sef=$(echo "$sef" | while IFS= read -r line; do
|
|
set -- $line
|
|
starttime="$1"
|
|
shift
|
|
endtime="$1"
|
|
shift
|
|
#fpath="$1"
|
|
shift
|
|
collection="$1"
|
|
shift
|
|
status="$1"
|
|
shift
|
|
if [ "$status" = "TENTATIVE" ]; then
|
|
symb="$FAINT$CYAN"
|
|
elif [ "$status" = "CANCELLED" ]; then
|
|
symb="$STRIKE"
|
|
fi
|
|
description="${symb:-}$*$OFF"
|
|
for i in $(seq 0 7); do
|
|
daystart=$(date -d "$startofweek +$i days 00:00:00" +"%s")
|
|
dayend=$(date -d "$startofweek +$i days 23:59:59" +"%s")
|
|
if [ "$starttime" -gt "$daystart" ] && [ "$starttime" -lt "$dayend" ]; then
|
|
s=$(date -d "@$starttime" +"%H:%M")
|
|
s="$s -"
|
|
elif [ "$starttime" -le "$daystart" ] && [ "$endtime" -gt "$daystart" ]; then
|
|
s="00:00 -"
|
|
else
|
|
continue
|
|
fi
|
|
if [ "$endtime" -gt "$daystart" ] && [ "$endtime" -lt "$dayend" ]; then
|
|
e=$(date -d "@$endtime" +"%H:%M")
|
|
e="- $e"
|
|
elif [ "$endtime" -ge "$dayend" ] && [ "$starttime" -lt "$dayend" ]; then
|
|
e="- 00:00"
|
|
else
|
|
continue
|
|
fi
|
|
echo "$i $s$e >$description"
|
|
done
|
|
done)
|
|
fi
|
|
sef=$({
|
|
echo "$sef"
|
|
seq 0 7
|
|
} | sort -n)
|
|
echo "$sef" | awk -v startofweek="$startofweek" "$AWK_WEEKVIEW"
|
|
}
|
|
|
|
# __view_all()
|
|
# This function prints all entries.
|
|
#
|
|
# @req $APPROX_DATA_FILE: Filename of approximate data (see `__refresh_data` and `__load_approx_data`)
|
|
__view_all() {
|
|
cat "$APPROX_DATA_FILE"
|
|
}
|
|
|
|
###
|
|
### Command-line Arguments for reloading views
|
|
### --reload-day
|
|
### --reload-week
|
|
### --reload-all
|
|
###
|
|
|
|
# --reload-day
|
|
# Reload view of specified day.
|
|
#
|
|
# @input $2.. (optional): Specification of day, defaults to `today`
|
|
if [ "${1:-}" = "--reload-day" ]; then
|
|
shift
|
|
DISPLAY_DATE=${*:-today}
|
|
__view_day
|
|
exit
|
|
fi
|
|
|
|
# --reload-week
|
|
# Reload view of the week containing the specified date.
|
|
#
|
|
# @input $2.. (optional): Specification of day, defaults to `today`
|
|
if [ "${1:-}" = "--reload-week" ]; then
|
|
shift
|
|
DISPLAY_DATE=${*:-today}
|
|
DISPLAY_POS=$((8 - $(date -d "$DISPLAY_DATE" +"%u")))
|
|
__view_week
|
|
exit
|
|
fi
|
|
|
|
# --reload-all
|
|
# Reload view of all entries.
|
|
if [ "${1:-}" = "--reload-all" ]; then
|
|
__view_all
|
|
exit
|
|
fi
|
|
|
|
###
|
|
### Load Configuration
|
|
### ROOT: Directory containing the collections
|
|
### COLLECTION_LABELS: Mappings between collections and labels
|
|
### SYNC_CMD (optional): Synchronization command
|
|
### DAY_START (optional): Hour of start of the day (defaults to 8)
|
|
### DAY_END (optional): Hour of end of the day (defaults to 18)
|
|
### EDITOR (optional): Your favorite editor, is usually already exported
|
|
### TZ (optional): Your favorite timezone, usually system's choice
|
|
### LC_TIME (optional): Your favorite locale for date and time
|
|
### ZI_DIR (optional): Location of tzdata, defaults to /usr/share/zoneinfo
|
|
###
|
|
|
|
CONFIGFILE="$HOME/.config/fzf-vcal/config"
|
|
if [ ! -f "$CONFIGFILE" ]; then
|
|
err "Configuration '$CONFIGFILE' not found."
|
|
exit 1
|
|
fi
|
|
# shellcheck source=/dev/null
|
|
. "$CONFIGFILE"
|
|
if [ -z "${ROOT:-}" ] || [ -z "${COLLECTION_LABELS:-}" ]; then
|
|
err "Configuration is incomplete."
|
|
exit 1
|
|
fi
|
|
SYNC_CMD=${SYNC_CMD:-echo 'Synchronization disabled'}
|
|
DAY_START=${DAY_START:-8}
|
|
DAY_END=${DAY_END:-18}
|
|
ZI_DIR=${ZI_DIR:-/usr/share/zoneinfo/posix}
|
|
if [ ! -d "$ZI_DIR" ]; then
|
|
err "Could not determine time-zone information"
|
|
exit 1
|
|
fi
|
|
|
|
###
|
|
### Check and load required tools
|
|
### FZF: Fuzzy finder `fzf``
|
|
### UUIDGEN: Tool `uuidgen` to generate random uids
|
|
### CAT: `bat` or `batcat` or `cat`
|
|
### GIT: `git` if it exists
|
|
###
|
|
### The presence of POSIX tools is not checked.
|
|
###
|
|
|
|
if command -v "fzf" >/dev/null; then
|
|
FZF="fzf --black"
|
|
else
|
|
err "Did not find the command-line fuzzy finder fzf."
|
|
exit 1
|
|
fi
|
|
|
|
if command -v "uuidgen" >/dev/null; then
|
|
UUIDGEN="uuidgen"
|
|
else
|
|
err "Did not find the uuidgen command."
|
|
exit 1
|
|
fi
|
|
|
|
if command -v "bat" >/dev/null; then
|
|
CAT="bat"
|
|
elif command -v "batcat" >/dev/null; then
|
|
CAT="batcat"
|
|
fi
|
|
CAT=${CAT:+$CAT --color=always --style=numbers --language=md}
|
|
CAT=${CAT:-cat}
|
|
|
|
if command -v "git" >/dev/null && [ -d "$ROOT/.git" ]; then
|
|
GIT="git -C $ROOT"
|
|
fi
|
|
|
|
###
|
|
### AWK scripts
|
|
### AWK_APPROX: Generate approximate data of all files
|
|
### AWK_CALSHIFT: Shift calendar to start weeks on Mondays
|
|
### AWK_CALANNOT: Annotate calendar
|
|
### AWK_DAYVIEW: Generate view of the day
|
|
### AWK_GET: Print field of iCalendar file
|
|
### AWK_MERGE: Generate list of weeks with associated iCalendar files
|
|
### AWK_NEW: Make new iCalendar file
|
|
### AWK_PARSE: Timezone aware parsing of iCalendar file for day view
|
|
### AWK_SET: Set value of specific field in iCalendar file
|
|
### AWK_UPDATE: Update iCalendar file
|
|
### AWK_WEEKVIEW: Generate view of the week
|
|
###
|
|
|
|
# TODO: Complete documentation
|
|
AWK_APPROX=$(
|
|
cat <<'EOF'
|
|
@@include src/awk/approx.awk
|
|
EOF
|
|
)
|
|
|
|
AWK_MERGE=$(
|
|
cat <<'EOF'
|
|
@@include src/awk/merge.awk
|
|
EOF
|
|
)
|
|
|
|
AWK_PARSE=$(
|
|
cat <<'EOF'
|
|
@@include src/awk/parse.awk
|
|
EOF
|
|
)
|
|
|
|
AWK_WEEKVIEW=$(
|
|
cat <<'EOF'
|
|
@@include src/awk/weekview.awk
|
|
EOF
|
|
)
|
|
|
|
AWK_DAYVIEW=$(
|
|
cat <<'EOF'
|
|
@@include src/awk/dayview.awk
|
|
EOF
|
|
)
|
|
|
|
AWK_GET=$(
|
|
cat <<'EOF'
|
|
@@include src/awk/get.awk
|
|
EOF
|
|
)
|
|
|
|
AWK_UPDATE=$(
|
|
cat <<'EOF'
|
|
@@include src/awk/update.awk
|
|
EOF
|
|
)
|
|
|
|
AWK_NEW=$(
|
|
cat <<'EOF'
|
|
@@include src/awk/new.awk
|
|
EOF
|
|
)
|
|
|
|
AWK_CALSHIFT=$(
|
|
cat <<'EOF'
|
|
@@include src/awk/calshift.awk
|
|
EOF
|
|
)
|
|
|
|
AWK_CALANNOT=$(
|
|
cat <<'EOF'
|
|
@@include src/awk/calannot.awk
|
|
EOF
|
|
)
|
|
|
|
AWK_SET=$(
|
|
cat <<'EOF'
|
|
@@include src/awk/set.awk
|
|
EOF
|
|
)
|
|
|
|
###
|
|
### Colors
|
|
###
|
|
#GREEN="\033[1;32m"
|
|
#RED="\033[1;31m"
|
|
WHITE="\033[1;97m"
|
|
CYAN="\033[1;36m"
|
|
STRIKE="\033[9m"
|
|
ITALIC="\033[3m"
|
|
FAINT="\033[2m"
|
|
OFF="\033[m"
|
|
|
|
###
|
|
### Loading functions
|
|
### __load_approx_data
|
|
### __load_weeks
|
|
### __refresh_data
|
|
###
|
|
|
|
# __load_approx_data()
|
|
# Print approximate data from iCalendar files in `$ROOT`
|
|
# TODO: Make safe and POSIX compliant
|
|
#
|
|
# @req $ROOT: Path that contains the collections (see configuration)
|
|
# @req $COLLECTION_LABELS: Mapping between collections and lables (see configuration)
|
|
# @req $AWK_APPROX: Awk script for approximation
|
|
__load_approx_data() {
|
|
find "$ROOT" -type f -name '*.ics' -print0 |
|
|
xargs -0 -P0 \
|
|
awk \
|
|
-v collection_labels="$COLLECTION_LABELS" \
|
|
"$AWK_APPROX"
|
|
}
|
|
|
|
# __load_weeks()
|
|
# For every relevant week, print associated iCalendar files
|
|
#
|
|
# @req $APPROX_DATA_FILE: Filename of approximate data (see `__refresh_data` and `__load_approx_data`)
|
|
# @req $AWK_MERGE: Merge awk script
|
|
__load_weeks() {
|
|
dates=$(awk -F'|' '{ print $2; print $3 }' "$APPROX_DATA_FILE")
|
|
file_dates=$(mktemp)
|
|
echo "$dates" | date --file="/dev/stdin" +"%G|%V" >"$file_dates"
|
|
awk "$AWK_MERGE" "$file_dates" "$APPROX_DATA_FILE"
|
|
rm "$file_dates"
|
|
}
|
|
|
|
# __refresh_data()
|
|
# Refresh approximate data and per-week data.
|
|
#
|
|
# This functions stores the output of `__load_approx_data` in the temporary
|
|
# file `$APPROX_DATA_FILE` and the output of `__load_weeks` in the temporary
|
|
# file `@WEEKLY_DATA_FILE`.
|
|
__refresh_data() {
|
|
if [ -n "${APPROX_DATA_FILE:-}" ]; then
|
|
rm -f "$APPROX_DATA_FILE"
|
|
fi
|
|
if [ -n "${WEEKLY_DATA_FILE:-}" ]; then
|
|
rm -f "$WEEKLY_DATA_FILE"
|
|
fi
|
|
APPROX_DATA_FILE=$(mktemp)
|
|
__load_approx_data >"$APPROX_DATA_FILE"
|
|
WEEKLY_DATA_FILE=$(mktemp)
|
|
__load_weeks >"$WEEKLY_DATA_FILE"
|
|
trap 'rm -f "$APPROX_DATA_FILE" "$WEEKLY_DATA_FILE"' EXIT INT
|
|
}
|
|
|
|
###
|
|
### Helper functions
|
|
### __datetime_human_machine
|
|
### __summary_for_commit
|
|
###
|
|
|
|
# __datetime_human_machine()
|
|
# Print date or datetime in a human and machine readable form.
|
|
#
|
|
# @input $1: Seconds since epoch
|
|
__datetime_human_machine() {
|
|
s="$1"
|
|
t=$(date -d "@$s" +"%R")
|
|
dfmt="%F"
|
|
if [ "$t" != "00:00" ]; then
|
|
dfmt="$dfmt %R"
|
|
fi
|
|
date -d "@$s" +"$dfmt"
|
|
}
|
|
|
|
# __summary_for_commit()
|
|
# Get summary string that can be used in for git-commit messages.
|
|
#
|
|
# @input $1: iCalendar file path
|
|
# @req $AWK_GET: Awk script to extract fields from iCalendar file
|
|
__summary_for_commit() {
|
|
awk -v field="SUMMARY" "$AWK_GET" "$1" | tr -c -d "[:alnum:][:blank:]" | head -c 15
|
|
}
|
|
|
|
###
|
|
### iCalendar modification wrapper
|
|
###
|
|
### __edit
|
|
### __new
|
|
### __delete
|
|
### __import_to_collection
|
|
### __cancel_toggle
|
|
### __tentative_toggle
|
|
|
|
# __edit()
|
|
# Edit iCalendar file.
|
|
#
|
|
# @input $1: Start date/date-time
|
|
# @input $2: End date/date-time
|
|
# @input $3: Path to iCalendar file (relative to `$ROOT`)
|
|
# @req $AWK_GET: Awk script to extract fields from iCalendar file
|
|
# @req $AWK_UPDATE: Awk script to update iCalendar file
|
|
# @req $EDITOR: Environment variable of your favorite editor
|
|
__edit() {
|
|
start=$(__datetime_human_machine "$1")
|
|
end=$(__datetime_human_machine "$2")
|
|
fpath="$ROOT/$3"
|
|
location=$(awk -v field="LOCATION" "$AWK_GET" "$fpath")
|
|
summary=$(awk -v field="SUMMARY" "$AWK_GET" "$fpath")
|
|
description=$(awk -v field="DESCRIPTION" "$AWK_GET" "$fpath")
|
|
filetmp=$(mktemp --suffix='.md')
|
|
(
|
|
echo "::: |> $start"
|
|
echo "::: <| $end"
|
|
) >"$filetmp"
|
|
if [ -n "$location" ]; then
|
|
echo "@ $location" >>"$filetmp"
|
|
fi
|
|
(
|
|
echo "# $summary"
|
|
echo ""
|
|
echo "$description"
|
|
) >>"$filetmp"
|
|
checksum=$(cksum "$filetmp")
|
|
$EDITOR "$filetmp" >/dev/tty
|
|
|
|
# Update only if changes are detected
|
|
if [ "$checksum" != "$(cksum "$filetmp")" ]; then
|
|
filenew="$filetmp.ics"
|
|
if awk "$AWK_UPDATE" "$filetmp" "$fpath" >"$filenew"; then
|
|
mv "$filenew" "$fpath"
|
|
if [ -n "${GIT:-}" ]; then
|
|
$GIT add "$fpath"
|
|
$GIT commit -m "Modified event '$(__summary_for_commit "$fpath") ...'" -- "$fpath"
|
|
fi
|
|
__refresh_data
|
|
else
|
|
rm -f "$filenew"
|
|
err "Failed to edit entry. Press <enter> to continue."
|
|
read -r tmp
|
|
fi
|
|
fi
|
|
rm "$filetmp"
|
|
}
|
|
|
|
# __new()
|
|
# Generate new iCalendar file
|
|
#
|
|
# This function also sets the `$start` variable to the start of the new entry.
|
|
# On failure, start will be empty.
|
|
#
|
|
# If some start has been specified and the nanoseconds are not 0, we assume
|
|
# that the user entered "tomorrow" or something like that, and did not
|
|
# specify the time. So, we will use the `$DAY_START` time of that date.
|
|
# If the user specified a malformed date/date-time, we fail.
|
|
#
|
|
# @input $1 (optional): Date or datetime, defaults to today.
|
|
# @req $COLLECTION_LABELS: Mapping between collections and lables (see configuration)
|
|
# @req $UUIDGEN: `uuidgen` command
|
|
# @req $ROOT: Path that contains the collections (see configuration)
|
|
# @req $EDITOR: Environment variable of your favorite editor
|
|
# @req $AWK_GET: Awk script to extract fields from iCalendar file
|
|
# @req $AWK_new: Awk script to generate iCalendar file
|
|
__new() {
|
|
collection=$(echo "$COLLECTION_LABELS" | tr ';' '\n' | awk '/./ {print}' | $FZF --margin="30%" --no-info --delimiter='=' --with-nth=2 --accept-nth=1)
|
|
fpath=""
|
|
while [ -f "$fpath" ] || [ -z "$fpath" ]; do
|
|
uuid=$($UUIDGEN)
|
|
fpath="$ROOT/$collection/$uuid.ics"
|
|
done
|
|
d="today $DAY_START"
|
|
if [ -n "${1:-}" ]; then
|
|
d="$1"
|
|
if [ "$(date -d "$1" +"%N")" -ne 0 ]; then
|
|
d="$d $DAY_START:00"
|
|
fi
|
|
fi
|
|
startsec=$(date -d "$d" +"%s")
|
|
endsec=$((startsec + 3600))
|
|
start=$(__datetime_human_machine "$startsec")
|
|
end=$(__datetime_human_machine "$endsec")
|
|
filetmp=$(mktemp --suffix='.md')
|
|
(
|
|
echo "::: |> $start"
|
|
echo "::: <| $end"
|
|
echo "@ <!-- write location here, optional line -->"
|
|
echo "# <!-- write summary here -->"
|
|
echo ""
|
|
) >"$filetmp"
|
|
checksum=$(cksum "$filetmp")
|
|
$EDITOR "$filetmp" >/dev/tty
|
|
|
|
# Update only if changes are detected
|
|
if [ "$checksum" != "$(cksum "$filetmp")" ]; then
|
|
filenew="$filetmp.ics"
|
|
if awk -v uid="$uuid" "$AWK_NEW" "$filetmp" >"$filenew"; then
|
|
mv "$filenew" "$fpath"
|
|
if [ -n "${GIT:-}" ]; then
|
|
$GIT add "$fpath"
|
|
$GIT commit -m "Added event '$(__summary_for_commit "$fpath") ...'" -- "$fpath"
|
|
fi
|
|
start=$(awk -v field="DTSTART" "$AWK_GET" "$fpath" | grep -o '[0-9]\{8\}')
|
|
else
|
|
rm -f "$filenew"
|
|
start=""
|
|
err "Failed to create new entry. Press <enter> to continue."
|
|
read -r tmp
|
|
fi
|
|
fi
|
|
rm "$filetmp"
|
|
}
|
|
|
|
# __delete()
|
|
# Delete iCalendar file
|
|
#
|
|
# @input $1: Path to iCalendar file, relative to `$ROOT`
|
|
# @req $ROOT: Path that contains the collections (see configuration)
|
|
# @req $AWK_GET: Awk script to extract fields from iCalendar file
|
|
__delete() {
|
|
fpath="$ROOT/$1"
|
|
summary=$(awk -v field="SUMMARY" "$AWK_GET" "$fpath")
|
|
while true; do
|
|
printf "Do you want to delete the entry with the title \"%s\"? (yes/no): " "$summary" >/dev/tty
|
|
read -r yn
|
|
case $yn in
|
|
"yes")
|
|
rm -v "$fpath"
|
|
if [ -n "${GIT:-}" ]; then
|
|
$GIT add "$fpath"
|
|
$GIT commit -m "Deleted event '$(__summary_for_commit "$fpath") ...'" -- "$fpath"
|
|
fi
|
|
break
|
|
;;
|
|
"no")
|
|
break
|
|
;;
|
|
*)
|
|
echo "Please answer \"yes\" or \"no\"." >/dev/tty
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# __import_to_collection()
|
|
# Import iCalendar file to specified collection. The only modification made to
|
|
# the file is setting the UID.
|
|
#
|
|
# @input $1: path to iCalendar file
|
|
# @input $2: collection name
|
|
# @req $ROOT: Path that contains the collections (see configuration)
|
|
# @req $UUIDGEN: `uuidgen` command
|
|
# @req $AWK_SET: Awk script to set field value
|
|
__import_to_collection() {
|
|
file="$1"
|
|
collection="$2"
|
|
fpath=""
|
|
while [ -f "$fpath" ] || [ -z "$fpath" ]; do
|
|
uuid=$($UUIDGEN)
|
|
fpath="$ROOT/$collection/$uuid.ics"
|
|
done
|
|
filetmp=$(mktemp)
|
|
awk -v field="UID" -v value="$uuid" "$AWK_SET" "$file" >"$filetmp"
|
|
mv "$filetmp" "$fpath"
|
|
if [ -n "${GIT:-}" ]; then
|
|
$GIT add "$fpath"
|
|
$GIT commit -m "Imported event '$(__summary_for_commit "$fpath") ...'" -- "$fpath"
|
|
fi
|
|
}
|
|
|
|
# __cancel_toggle()
|
|
# Set status of appointment to CANCELLED or CONFIRMED (toggle)
|
|
#
|
|
# @input $1: path to iCalendar file
|
|
# @req $ROOT: Path that contains the collections (see configuration)
|
|
# @req $AWK_SET: Awk script to set field value
|
|
# @req $AWK_GET: Awk script to extract fields from iCalendar file
|
|
__cancel_toggle() {
|
|
fpath="$ROOT/$1"
|
|
status=$(awk -v field="STATUS" "$AWK_GET" "$fpath")
|
|
newstatus="CANCELLED"
|
|
if [ "${status:-}" = "$newstatus" ]; then
|
|
newstatus="CONFIRMED"
|
|
fi
|
|
filetmp=$(mktemp)
|
|
awk -v field="STATUS" -v value="$newstatus" "$AWK_SET" "$fpath" >"$filetmp"
|
|
mv "$filetmp" "$fpath"
|
|
if [ -n "${GIT:-}" ]; then
|
|
$GIT add "$fpath"
|
|
$GIT commit -m "Event '$(__summary_for_commit "$fpath") ...' has now status $status" -- "$fpath"
|
|
fi
|
|
}
|
|
|
|
# __tentative_toggle
|
|
# Toggle status flag: CONFIRMED <-> TENTATIVE
|
|
# @input $1: path to iCalendar file
|
|
# @req $ROOT: Path that contains the collections (see configuration)
|
|
# @req $AWK_SET: Awk script to set field value
|
|
# @req $AWK_GET: Awk script to extract fields from iCalendar file
|
|
__tentative_toggle() {
|
|
fpath="$ROOT/$1"
|
|
status=$(awk -v field="STATUS" "$AWK_GET" "$fpath")
|
|
newstatus="TENTATIVE"
|
|
if [ "${status:-}" = "$newstatus" ]; then
|
|
newstatus="CONFIRMED"
|
|
fi
|
|
filetmp=$(mktemp)
|
|
awk -v field="STATUS" -v value="$newstatus" "$AWK_SET" "$fpath" >"$filetmp"
|
|
mv "$filetmp" "$fpath"
|
|
if [ -n "${GIT:-}" ]; then
|
|
$GIT add "$fpath"
|
|
$GIT commit -m "Event '$(__summary_for_commit "$fpath") ...' has now status $status" -- "$fpath"
|
|
fi
|
|
}
|
|
|
|
###
|
|
### Extra command-line options
|
|
### --import-ni
|
|
### --import
|
|
### --git
|
|
### --git-init
|
|
###
|
|
|
|
# --import-ni
|
|
# Import iCalendar file noninteractively
|
|
#
|
|
# @input $2: Absolute path to iCalendar file
|
|
# @input $3: Collection
|
|
# @req $COLLECTION_LABELS: Mapping between collections and labels (see configuration)
|
|
# @req $ROOT: Path that contains the collections (see configuration)
|
|
# @return: On success, returns 0, otherwise 1
|
|
if [ "${1:-}" = "--import-ni" ]; then
|
|
shift
|
|
file="${1:-}"
|
|
collection="${2:-}"
|
|
if [ ! -f "$file" ]; then
|
|
err "File \"$file\" does not exist"
|
|
exit 1
|
|
fi
|
|
for c in $(echo "$COLLECTION_LABELS" | sed "s|=[^;]*;| |g"); do
|
|
if [ "$collection" = "$c" ]; then
|
|
cexists="yes"
|
|
break
|
|
fi
|
|
done
|
|
if [ -n "${cexists:-}" ] && [ -d "$ROOT/$collection" ]; then
|
|
__import_to_collection "$file" "$collection"
|
|
else
|
|
err "Collection \"$collection\" does not exist"
|
|
exit 1
|
|
fi
|
|
exit
|
|
fi
|
|
|
|
# --import
|
|
# Import iCalendar file.
|
|
#
|
|
# @input $2: Absolute path to iCalendar file
|
|
# @req $COLLECTION_LABELS: Mapping between collections and lables (see configuration)
|
|
# @req $AWK_PARSE: Parse awk script
|
|
# @req $AWK_GET: Awk script to extract fields from iCalendar file
|
|
# @req $FZF: `fzf` command
|
|
# @req $CAT: Program to print
|
|
# @return: On success, returns 0, otherwise 1
|
|
if [ "${1:-}" = "--import" ]; then
|
|
shift
|
|
file="${1:-}"
|
|
if [ ! -f "$file" ]; then
|
|
err "File \"$file\" does not exist"
|
|
return 1
|
|
fi
|
|
line=$(awk \
|
|
-v collection_labels="$COLLECTION_LABELS" \
|
|
"$AWK_PARSE" "$file")
|
|
set -- $line
|
|
startsec="${1:-}"
|
|
endsec="${2:-}"
|
|
if [ -z "$line" ] || [ -z "$startsec" ] || [ -z "$endsec" ]; then
|
|
err "File \"$file\" does not look like an iCalendar file containing an event"
|
|
return 1
|
|
fi
|
|
start=$(__datetime_human_machine "$startsec")
|
|
end=$(__datetime_human_machine "$endsec")
|
|
location=$(awk -v field="LOCATION" "$AWK_GET" "$file")
|
|
summary=$(awk -v field="SUMMARY" "$AWK_GET" "$file")
|
|
description=$(awk -v field="DESCRIPTION" "$AWK_GET" "$file")
|
|
filetmp=$(mktemp --suffix='.md')
|
|
(
|
|
echo "::: |> $start"
|
|
echo "::: <| $end"
|
|
) >"$filetmp"
|
|
if [ -n "$location" ]; then
|
|
echo "@ $location" >>"$filetmp"
|
|
fi
|
|
(
|
|
echo "# $summary"
|
|
echo ""
|
|
echo "$description"
|
|
) >>"$filetmp"
|
|
$CAT "$filetmp" >/dev/tty
|
|
while true; do
|
|
printf "Do you want to import this entry? (yes/no): " >/dev/tty
|
|
read -r yn
|
|
case $yn in
|
|
"yes")
|
|
collection=$(echo "$COLLECTION_LABELS" | tr ';' '\n' | awk '/./ {print}' | $FZF --margin="30%" --no-info --delimiter='=' --with-nth=2 --accept-nth=1)
|
|
if [ -z "$collection" ]; then
|
|
exit
|
|
fi
|
|
__import_to_collection "$file" "$collection"
|
|
break
|
|
;;
|
|
"no")
|
|
break
|
|
;;
|
|
*)
|
|
echo "Please answer \"yes\" or \"no\"." >/dev/tty
|
|
;;
|
|
esac
|
|
done
|
|
rm -f "$filetmp"
|
|
exit
|
|
fi
|
|
|
|
# --git
|
|
# Run git command
|
|
#
|
|
# @input $2..: Git command
|
|
# @req $GIT: git command with `-C` flag set
|
|
# @return: On success, returns 0, otherwise 1
|
|
if [ "${1:-}" = "--git" ]; then
|
|
if [ -z "${GIT:-}" ]; then
|
|
err "Git not supported, run \`$0 --git-init\` first"
|
|
return 1
|
|
fi
|
|
shift
|
|
$GIT "$@"
|
|
exit
|
|
fi
|
|
#
|
|
# --git-init
|
|
# Enable the ues of git
|
|
#
|
|
# @return: On success, returns 0, otherwise 1
|
|
if [ "${1:-}" = "--git-init" ]; then
|
|
if [ -n "${GIT:-}" ]; then
|
|
err "Git already enabled"
|
|
return 1
|
|
fi
|
|
if ! command -v "git" >/dev/null; then
|
|
err "Git command not found"
|
|
return 1
|
|
fi
|
|
git -C "$ROOT" init
|
|
git -C "$ROOT" add -A
|
|
git -C "$ROOT" commit -m 'Initial commit: Start git tracking'
|
|
exit
|
|
fi
|
|
|
|
### Start
|
|
__refresh_data
|
|
|
|
### Exports
|
|
# The preview calls run in subprocesses. These require the following variables:
|
|
export ROOT CAT AWK_GET AWK_CALSHIFT AWK_CALANNOT CYAN STRIKE FAINT WHITE ITALIC OFF
|
|
# The reload commands also run in subprocesses, and use in addition
|
|
export COLLECTION_LABELS DAY_START DAY_END AWK_DAYVIEW AWK_WEEKVIEW AWK_PARSE
|
|
# as well as the following variables that will be dynamically specified. So, we
|
|
# export them in the main loop using the following function.
|
|
|
|
# __export()
|
|
# Re-export dynamical variables to subshells.
|
|
__export() {
|
|
export DISPLAY_DATE WEEKLY_DATA_FILE APPROX_DATA_FILE
|
|
if [ -n "${TZ:-}" ]; then
|
|
export TZ
|
|
fi
|
|
}
|
|
|
|
###
|
|
### Main loop with the command-line argument
|
|
### --today
|
|
### --yesterday
|
|
### --tomorrow
|
|
### --goto
|
|
### --new <optional date/date-time argument>
|
|
### --day <optional date/date-time argument>
|
|
### --week <optional date/date-time argument>
|
|
### --set-tz
|
|
###
|
|
### The command-line argument defaults to "--week today".
|
|
|
|
while true; do
|
|
export DISPLAY_DATE WEEKLY_DATA_FILE APPROX_DATA_FILE
|
|
|
|
case "${1:-}" in
|
|
--today | --yesterday | --tomorrow | --goto | --new | --day | --week | --set-tz) ;;
|
|
*)
|
|
DISPLAY_DATE="today"
|
|
set -- "--week" "$DISPLAY_DATE"
|
|
;;
|
|
esac
|
|
|
|
if [ "$1" = "--today" ]; then
|
|
DISPLAY_DATE="today"
|
|
set -- "--day" "$DISPLAY_DATE"
|
|
fi
|
|
|
|
if [ "$1" = "--yesterday" ]; then
|
|
DISPLAY_DATE="yesterday"
|
|
set -- "--day" "$DISPLAY_DATE"
|
|
fi
|
|
|
|
if [ "$1" = "--tomorrow" ]; then
|
|
DISPLAY_DATE="tomorrow"
|
|
set -- "--day" "$DISPLAY_DATE"
|
|
fi
|
|
|
|
if [ "$1" = "--goto" ]; then
|
|
DISPLAY_DATE=""
|
|
while [ -z "${DISPLAY_DATE:-}" ]; do
|
|
printf "Enter date you want to jump to, e.g., today + 1 month or 2024-1-14: " >/dev/tty
|
|
read -r tmp
|
|
if date -d "$tmp" >/dev/null; then
|
|
DISPLAY_DATE="$(date -d "$tmp" +"%D")"
|
|
fi
|
|
done
|
|
set -- "--day" "$DISPLAY_DATE"
|
|
fi
|
|
|
|
if [ "$1" = "--set-tz" ]; then
|
|
new_tz=$(find "$ZI_DIR" -type f | sed "s|^$ZI_DIR/*||" | $FZF)
|
|
if [ -n "$new_tz" ]; then
|
|
TZ="$new_tz"
|
|
__refresh_data
|
|
__export
|
|
fi
|
|
shift
|
|
fi
|
|
|
|
if [ "${1:-}" = "--new" ]; then
|
|
__new "${2:-}"
|
|
if [ -n "$start" ]; then
|
|
DISPLAY_DATE="$start"
|
|
else
|
|
DISPLAY_DATE="${2:-}"
|
|
fi
|
|
__refresh_data
|
|
__export
|
|
set -- "--day" "$DISPLAY_DATE"
|
|
fi
|
|
|
|
if [ "$1" = "--day" ]; then
|
|
DISPLAY_DATE="${2:-today}"
|
|
__export
|
|
selection=$(
|
|
__view_day |
|
|
$FZF \
|
|
--reverse \
|
|
--ansi \
|
|
--no-sort \
|
|
--no-input \
|
|
--margin='20%,5%' \
|
|
--border='double' \
|
|
--color=label:bold:green \
|
|
--border-label-pos=3 \
|
|
--list-border="top" \
|
|
--list-label-pos=3 \
|
|
--cycle \
|
|
--delimiter='|' \
|
|
--with-nth='{6}' \
|
|
--accept-nth='1,2,3,4,5' \
|
|
--preview="$0 --preview-event {}" \
|
|
--expect="ctrl-n,ctrl-t,ctrl-g,ctrl-alt-d,esc,backspace,q,alt-v,x,c" \
|
|
--bind="load:pos(1)+transform(
|
|
echo change-border-label:🗓️ \$(date -d {1} +\"%A %e %B %Y\")
|
|
)+transform(
|
|
[ -n \"\${TZ:-}\" ] && echo \"change-list-label:\$WHITE\$ITALIC(\$TZ)\$OFF\"
|
|
)+transform(
|
|
[ -n \"\$(echo {} | cut -d '|' -f 5)\" ] && echo show-preview
|
|
)" \
|
|
--bind="start:hide-preview" \
|
|
--bind="j:down+hide-preview+transform([ -n \"\$(echo {} | cut -d '|' -f 5)\" ] && echo show-preview)" \
|
|
--bind="k:up+hide-preview+transform([ -n \"\$(echo {} | cut -d '|' -f 5)\" ] && echo show-preview)" \
|
|
--bind="ctrl-j:down+hide-preview+transform([ -n \"\$(echo {} | cut -d '|' -f 5)\" ] && echo show-preview)" \
|
|
--bind="ctrl-k:up+hide-preview+transform([ -n \"\$(echo {} | cut -d '|' -f 5)\" ] && echo show-preview)" \
|
|
--bind="down:down+hide-preview+transform([ -n \"\$(echo {} | cut -d '|' -f 5)\" ] && echo show-preview)" \
|
|
--bind="up:up+hide-preview+transform([ -n \"\$(echo {} | cut -d '|' -f 5)\" ] && echo show-preview)" \
|
|
--bind="l:hide-preview+reload:$0 --reload-day {1} '+1 day'" \
|
|
--bind="h:hide-preview+reload:$0 --reload-day {1} '-1 day'" \
|
|
--bind="right:hide-preview+reload:$0 --reload-day {1} '+1 day'" \
|
|
--bind="left:hide-preview+reload:$0 --reload-day {1} '-1 day'" \
|
|
--bind="ctrl-l:hide-preview+reload:$0 --reload-day {1} '+1 week'" \
|
|
--bind="ctrl-h:hide-preview+reload:$0 --reload-day {1} '-1 week'" \
|
|
--bind="alt-l:hide-preview+reload:$0 --reload-day {1} '+1 month'" \
|
|
--bind="alt-h:hide-preview+reload:$0 --reload-day {1} '-1 month'" \
|
|
--bind="ctrl-r:hide-preview+reload:$0 --reload-day today" \
|
|
--bind="ctrl-s:execute($SYNC_CMD ; printf 'Press <enter> to continue.'; read -r tmp)" \
|
|
--bind="w:toggle-preview-wrap" \
|
|
--bind="ctrl-d:preview-down" \
|
|
--bind="ctrl-u:preview-up"
|
|
)
|
|
key=$(echo "$selection" | head -1)
|
|
line=$(echo "$selection" | tail -1)
|
|
if [ "$line" = "$key" ]; then
|
|
line=""
|
|
fi
|
|
DISPLAY_DATE=$(echo "$line" | cut -d '|' -f 1)
|
|
hour=$(echo "$line" | cut -d '|' -f 2)
|
|
start=$(echo "$line" | cut -d '|' -f 3)
|
|
end=$(echo "$line" | cut -d '|' -f 4)
|
|
fpath=$(echo "$line" | cut -d '|' -f 5 | sed "s/ /|/g")
|
|
if [ "$key" = "ctrl-n" ]; then
|
|
if echo "$hour" | grep ":"; then
|
|
hour="$DAY_START"
|
|
fi
|
|
set -- "--new" "$DISPLAY_DATE $hour:00"
|
|
elif [ "$key" = "ctrl-alt-d" ] && [ -n "$fpath" ]; then
|
|
__delete "$fpath"
|
|
__refresh_data
|
|
set -- "--day" "$DISPLAY_DATE"
|
|
elif [ "$key" = "ctrl-g" ]; then
|
|
set -- "--goto"
|
|
elif [ "$key" = "ctrl-t" ]; then
|
|
set -- "--set-tz" "--day" "$DISPLAY_DATE"
|
|
elif [ "$key" = "esc" ] || [ "$key" = "backspace" ] || [ "$key" = "q" ]; then
|
|
set -- "--week" "$DISPLAY_DATE"
|
|
elif [ "$key" = "alt-v" ] && [ -f "$ROOT/$fpath" ]; then
|
|
$EDITOR "$ROOT/$fpath"
|
|
elif [ "$key" = "x" ] && [ -f "$ROOT/$fpath" ]; then
|
|
__cancel_toggle "$fpath"
|
|
elif [ "$key" = "c" ] && [ -f "$ROOT/$fpath" ]; then
|
|
__tentative_toggle "$fpath"
|
|
elif [ -z "$key" ] && [ -n "$fpath" ]; then
|
|
__edit "$start" "$end" "$fpath"
|
|
set -- "--day" "$DISPLAY_DATE"
|
|
fi
|
|
__export
|
|
fi
|
|
|
|
if [ "${1:-}" = "--week" ]; then
|
|
DISPLAY_DATE="${2:-today}"
|
|
DISPLAY_POS=$((8 - $(date -d "$DISPLAY_DATE" +"%u")))
|
|
__export
|
|
selection=$(
|
|
__view_week |
|
|
$FZF \
|
|
--tac \
|
|
--no-sort \
|
|
--no-hscroll \
|
|
--ellipsis="" \
|
|
--delimiter="|" \
|
|
--with-nth="{4}" \
|
|
--accept-nth=1,2 \
|
|
--ansi \
|
|
--gap 1 \
|
|
--no-scrollbar \
|
|
--no-input \
|
|
--info=right \
|
|
--info-command="printf \"$(date +"%R %Z")\"; [ -n \"\${TZ:-}\" ] && printf \" (\$TZ)\"" \
|
|
--preview-window=up,7,border-bottom \
|
|
--preview="$0 --preview-week {}" \
|
|
--bind="load:pos($DISPLAY_POS)" \
|
|
--expect="ctrl-n,ctrl-g,ctrl-t" \
|
|
--bind="q:abort" \
|
|
--bind="j:down" \
|
|
--bind="k:up" \
|
|
--bind="l:unbind(load)+reload:$0 --reload-week {2} '+1 week'" \
|
|
--bind="h:unbind(load)+reload:$0 --reload-week {2} '-1 week'" \
|
|
--bind="right:unbind(load)+reload:$0 --reload-week {2} '+1 week'" \
|
|
--bind="left:unbind(load)+reload:$0 --reload-week {2} '-1 week'" \
|
|
--bind="ctrl-l:unbind(load)+reload:$0 --reload-week {2} '+1 month'" \
|
|
--bind="ctrl-h:unbind(load)+reload:$0 --reload-week {2} '-1 month'" \
|
|
--bind="alt-l:unbind(load)+reload:$0 --reload-week {2} '+1 year'" \
|
|
--bind="alt-h:unbind(load)+reload:$0 --reload-week {2} '-1 year'" \
|
|
--bind="ctrl-r:rebind(load)+reload($0 --reload-week today)+show-preview" \
|
|
--bind="ctrl-s:execute($SYNC_CMD ; printf 'Press <enter> to continue.'; read -r tmp)" \
|
|
--bind="/:show-input+unbind(q)+unbind(j)+unbind(k)+unbind(l)+unbind(h)+unbind(ctrl-l)+unbind(ctrl-h)+unbind(alt-l)+unbind(alt-h)+unbind(load)+hide-preview+reload:$0 --reload-all" \
|
|
--bind="backward-eof:hide-input+rebind(q)+rebind(j)+rebind(k)+rebind(l)+rebind(h)+rebind(ctrl-l)+rebind(ctrl-h)+rebind(alt-l)+rebind(alt-h)+rebind(load)+show-preview+reload:$0 --reload-week today" \
|
|
--bind="esc:clear-query+hide-input+rebind(q)+rebind(j)+rebind(k)+rebind(l)+rebind(h)+rebind(ctrl-l)+rebind(ctrl-h)+rebind(alt-l)+rebind(alt-h)+rebind(load)+show-preview+reload:$0 --reload-week today"
|
|
)
|
|
|
|
key=$(echo "$selection" | head -1)
|
|
line=$(echo "$selection" | tail -1)
|
|
if [ "$line" = "$key" ]; then
|
|
line=""
|
|
fi
|
|
sign=$(echo "$line" | cut -d '|' -f 1)
|
|
DISPLAY_DATE=$(echo "$line" | cut -d '|' -f 2)
|
|
if [ "$key" = "ctrl-n" ]; then
|
|
if [ "$sign" = "~" ]; then
|
|
DISPLAY_DATE=""
|
|
fi
|
|
set -- "--new" "${DISPLAY_DATE:-today} $DAY_START:00"
|
|
elif [ "$key" = "ctrl-g" ]; then
|
|
set -- "--goto"
|
|
elif [ "$key" = "ctrl-t" ]; then
|
|
set -- "--set-tz" "$*"
|
|
else
|
|
if [ "$sign" = "~" ]; then
|
|
set -- "--week" "$DISPLAY_DATE"
|
|
else
|
|
set -- "--day" "$DISPLAY_DATE"
|
|
fi
|
|
fi
|
|
__export
|
|
fi
|
|
done
|