POSIX compliant
Also, it's smoother than the ZSH variant
This commit is contained in:
260
fzf-vjour
260
fzf-vjour
@@ -1,27 +1,31 @@
|
||||
#!/usr/bin/env zsh
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
# Read configuration
|
||||
CONFIGFILE="$HOME/.config/fzf-vjour/config.yaml"
|
||||
if [[ ! -e "$CONFIGFILE" ]]
|
||||
then
|
||||
if [ ! -e "$CONFIGFILE" ]; then
|
||||
echo "Config file '$CONFIGFILE' not found"
|
||||
exit 1
|
||||
fi
|
||||
ROOT=$(yq '.datadir' < "$CONFIGFILE")
|
||||
if [[ ! -d "$ROOT" ]]
|
||||
then
|
||||
ROOT=$(yq '.datadir' <"$CONFIGFILE")
|
||||
if [ ! -d "$ROOT" ]; then
|
||||
echo "Root directory not set or wrongly set"
|
||||
exit 1
|
||||
fi
|
||||
COLLECTION_NAMES=($(yq '.collections [].name' "$CONFIGFILE"))
|
||||
COLLECTION_LABELS=($(yq '.collections [].label' "$CONFIGFILE"))
|
||||
COLLECTION_LABEL_MAX_LEN=0
|
||||
for label in "${COLLECTION_LABELS[@]}"; do
|
||||
[[ ${#label} -gt $COLLECTION_LABEL_MAX_LEN ]] && COLLECTION_LABEL_MAX_LEN=${#label}
|
||||
done
|
||||
SYNC_CMD=$(yq '.sync_cmd' < "$CONFIGFILE")
|
||||
SED_COLLECTIONNAMES_TO_LABELS=$(
|
||||
printf "sed "
|
||||
yq '.collections[] | "s|/*" + .name + "/*|:" + .label + "\ |"' <"$CONFIGFILE" |
|
||||
xargs printf "-e \"%s\" "
|
||||
)
|
||||
SED_COLLECTIONLABELS_TO_NAMES=$(
|
||||
printf "sed "
|
||||
yq '.collections[] | "s|\ *" + .label + "\ *|/" + .name + "/|"' <"$CONFIGFILE" |
|
||||
xargs printf "-e \"%s\" "
|
||||
)
|
||||
COLLECTION_LABEL_MAX_LEN=$(yq '[.collections[].label | length] | max' <"$CONFIGFILE")
|
||||
LABLES=$(yq '.collections[].label' <"$CONFIGFILE")
|
||||
SYNC_CMD=$(yq '.sync_cmd' <"$CONFIGFILE")
|
||||
|
||||
__vjournalnew() {
|
||||
python3 -c '
|
||||
@@ -176,63 +180,60 @@ print(json.dumps(data))'
|
||||
# @param string: Path to ics file
|
||||
# @param string: Maximum length of filenames (for padding purposes)
|
||||
__filepath_to_searchline() {
|
||||
local filepath="$1"
|
||||
local maxlen="$2"
|
||||
local totallen=$((maxlen + ${#ROOT}))
|
||||
local filepathpad=$(printf "%-${totallen}s" "$filepath")
|
||||
|
||||
filepath="$1"
|
||||
maxlen="$2"
|
||||
totallen=$((maxlen + ${#ROOT}))
|
||||
filepathpad=$(printf "%-${totallen}s" "$filepath")
|
||||
|
||||
# Color support
|
||||
local GREEN=$'\033[1;32m'
|
||||
local WHITE=$'\033[1;97m'
|
||||
local FAINT=$'\033[2m'
|
||||
local OFF=$'\033[m';
|
||||
GREEN=$(printf '\033[1;32m')
|
||||
WHITE=$(printf '\033[1;97m')
|
||||
FAINT=$(printf '\033[2m')
|
||||
OFF=$(printf '\033[m')
|
||||
|
||||
# Parse file
|
||||
local summary categories dtstamp dtstart
|
||||
|
||||
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}')
|
||||
;;
|
||||
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
|
||||
if [ -n "$summary" ] && [ -n "$categories" ] && [ -n "$dtstamp" ] && [ -n "$dtstart" ]; then
|
||||
break
|
||||
fi
|
||||
done < "$filepath"
|
||||
|
||||
# Parse date
|
||||
if [[ -n $dtstart ]]; then
|
||||
local date_target=$(date -d "$dtstart" +%s)
|
||||
local date_today=$(date +%s)
|
||||
local date_delta=$(( (date_target - date_today) / 86400 ))
|
||||
local date_expr=$date_delta
|
||||
if [[ $date_delta -eq 0 ]]
|
||||
then
|
||||
done <"$filepath"
|
||||
|
||||
# Parse date
|
||||
if [ -n "$dtstart" ]; then
|
||||
date_target=$(date -d "$dtstart" +%s)
|
||||
date_today=$(date +%s)
|
||||
date_delta=$(((date_target - date_today) / 86400))
|
||||
date_expr=$date_delta
|
||||
if [ "$date_delta" -eq 0 ]; then
|
||||
date_expr="today"
|
||||
elif [[ $date_delta -eq -1 ]]
|
||||
then
|
||||
elif [ "$date_delta" -eq -1 ]; then
|
||||
date_expr="yesterday"
|
||||
elif [[ $date_delta -eq 1 ]]
|
||||
then
|
||||
elif [ "$date_delta" -eq 1 ]; then
|
||||
date_expr="tomorrow"
|
||||
elif [[ $date_delta -lt -1 && $date_delta -ge -7 ]]
|
||||
then
|
||||
date_expr="last $(date -d $dtstart +%A)"
|
||||
elif [[ $date_delta -gt 1 && $date_delta -le 7 ]]
|
||||
then
|
||||
date_expr="next $(date -d $dtstart +%A)"
|
||||
elif [ "$date_delta" -lt -1 ] && [ "$date_delta" -ge -7 ]; then
|
||||
date_expr="last $(date -d "$dtstart" +%A)"
|
||||
elif [ "$date_delta" -gt 1 ] && [ "$date_delta" -le 7 ]; then
|
||||
date_expr="next $(date -d "$dtstart" +%A)"
|
||||
else
|
||||
date_expr=$(date -d $dtstart +%x)
|
||||
date_expr=$(date -d "$dtstart" +%x)
|
||||
fi
|
||||
date_emoji="📘"
|
||||
else
|
||||
@@ -242,142 +243,137 @@ __filepath_to_searchline() {
|
||||
date_expr=$(printf "%12s" "$date_expr")
|
||||
|
||||
# Print line
|
||||
echo -e "$dtstamp $filepath $WHITE$date_expr$OFF $date_emoji $GREEN$summary$OFF $FAINT$categories$OFF"
|
||||
echo "$dtstamp $filepath $WHITE$date_expr$OFF $date_emoji $GREEN$summary$OFF $FAINT$categories$OFF"
|
||||
}
|
||||
|
||||
__lines() {
|
||||
# Collect all files
|
||||
local files=()
|
||||
while IFS= read -r -d '' file; do
|
||||
files+=("$file")
|
||||
done < <(find "$ROOT" -type f -name '*.ics' -print0)
|
||||
FILES_TMP=$(mktemp)
|
||||
trap 'rm -f "$FILES_TMP"' EXIT
|
||||
find "$ROOT" -type f -name '*.ics' >"$FILES_TMP"
|
||||
|
||||
# Compute max length of basenames
|
||||
local maxlen=0
|
||||
for file in "${files[@]}"; do
|
||||
local name=$(basename "$file")
|
||||
[[ ${#name} -gt $maxlen ]] && maxlen=${#name}
|
||||
done
|
||||
maxlen=0
|
||||
while IFS= read -r file; do
|
||||
name=$(basename "$file")
|
||||
[ ${#name} -gt "$maxlen" ] && maxlen=${#name}
|
||||
done <"$FILES_TMP"
|
||||
|
||||
local lines=$(for file in "${files[@]}"; do
|
||||
__filepath_to_searchline "$file" "$maxlen"
|
||||
done)
|
||||
lines=$(while IFS= read -r file; do
|
||||
__filepath_to_searchline "$file" "$maxlen"
|
||||
done <"$FILES_TMP")
|
||||
|
||||
# Decorate
|
||||
for ((i = 1; i <= ${#COLLECTION_NAMES[@]}; i++)); do
|
||||
#local label=$(printf "%${COLLECTION_LABEL_MAX_LEN}s" "${COLLECTION_LABELS[$i]}")
|
||||
local label="${COLLECTION_LABELS[$i]}"
|
||||
lines=$(echo "$lines" | sed "s|/*${COLLECTION_NAMES[$i]}/*|:$label |")
|
||||
done
|
||||
lines=$(echo "$lines" | eval "$SED_COLLECTIONNAMES_TO_LABELS")
|
||||
|
||||
# Sort and cut off irreleant part
|
||||
lines=$(echo "$lines" | sort -g -r | cut -d ':' -f 2-)
|
||||
|
||||
echo -e "$lines"
|
||||
echo "$lines"
|
||||
}
|
||||
|
||||
__filepath_from_selection() {
|
||||
local selection=$(echo "$1" | cut -d " " -f 1,2)
|
||||
for ((i = 1; i <= ${#COLLECTION_NAMES[@]}; i++)); do
|
||||
selection=$(echo "$selection" | sed "s|^.*${COLLECTION_LABELS[$i]} *|$ROOT/${COLLECTION_NAMES[$i]}/|")
|
||||
done
|
||||
echo -e "$selection"
|
||||
selection=$(echo "$1" | cut -d " " -f 1,2 | eval "$SED_COLLECTIONLABELS_TO_NAMES" | sed "s|^|$ROOT/|")
|
||||
echo "$selection"
|
||||
}
|
||||
|
||||
# Program starts here
|
||||
|
||||
# Command line arguments to be self-contained
|
||||
# Generate preview of file from selection
|
||||
if [[ "${1:-}" == "--preview" ]]; then
|
||||
local vjfile=$(__filepath_from_selection "$2")
|
||||
cat "$vjfile" | __vjournal2json | jq -r ".description" | batcat --color=always --style=numbers --language=md
|
||||
if [ "${1:-}" = "--preview" ]; then
|
||||
vjfile=$(__filepath_from_selection "$2")
|
||||
__vjournal2json <"$vjfile" | jq -r ".description" | batcat --color=always --style=numbers --language=md
|
||||
exit
|
||||
fi
|
||||
# Delete file from selection
|
||||
if [[ "${1:-}" == "--delete" ]]; then
|
||||
local vjfile=$(__filepath_from_selection "$2")
|
||||
if [ "${1:-}" = "--delete" ]; then
|
||||
vjfile=$(__filepath_from_selection "$2")
|
||||
rm -v "$vjfile"
|
||||
exit
|
||||
fi
|
||||
# Generate new entry
|
||||
if [[ "${1:-}" == "--new" ]]; then
|
||||
local label_new=$(for label in "${COLLECTION_LABELS[@]}"; do
|
||||
echo "$label"
|
||||
done | fzf --prompt="Select collection> ")
|
||||
local uuid_new=$(uuidgen)
|
||||
local vjfile_new=$(__filepath_from_selection "$label_new $uuid_new.ics")
|
||||
if [[ -f "$vjfile_new" ]]
|
||||
then
|
||||
if [ "${1:-}" = "--new" ]; then
|
||||
label_new=$(echo "$LABLES" | fzf --prompt="Select collection> ")
|
||||
uuid_new=$(uuidgen)
|
||||
vjfile_new=$(__filepath_from_selection "$label_new $uuid_new.ics")
|
||||
if [ -f "$vjfile_new" ]; then
|
||||
echo "Bad luck..."
|
||||
return 1
|
||||
fi
|
||||
#
|
||||
local TMPFILE="$(mktemp).md"
|
||||
local SHAFILE="$TMPFILE.sha"
|
||||
trap "rm -f $TMPFILE $SHAFILE" EXIT
|
||||
echo "::: Keep this line if you want to add a **JOURNAL** entry (associated to today), else we will add a **NOTE**" > "$TMPFILE"
|
||||
echo "# <write summary here>" >> "$TMPFILE"
|
||||
echo "> <comma-separated list of categories>" >> "$TMPFILE"
|
||||
echo >> "$TMPFILE"
|
||||
sha1sum "$TMPFILE" > "$SHAFILE"
|
||||
TMPFILE="$(mktemp).md"
|
||||
SHAFILE="$TMPFILE.sha"
|
||||
trap 'rm -f "$TMPFILE" "$SHAFILE"' EXIT
|
||||
{
|
||||
echo "::: Keep this line if you want to add a **JOURNAL** entry (associated to today), else we will add a **NOTE**"
|
||||
echo "# <write summary here>"
|
||||
echo "> <comma-separated list of categories>"
|
||||
echo ""
|
||||
} >"$TMPFILE"
|
||||
sha1sum "$TMPFILE" >"$SHAFILE"
|
||||
|
||||
# Open in editor
|
||||
$EDITOR "$TMPFILE"
|
||||
$EDITOR "$TMPFILE" >/dev/tty
|
||||
|
||||
# Update if changes are detected
|
||||
if ! sha1sum -c "$SHAFILE" > /dev/null 2>&1
|
||||
then
|
||||
local vjfile_tmp="$TMPFILE.ics"
|
||||
trap "rm -f $vjfile_tmp" EXIT
|
||||
__vjournalnew "$uuid_new" < "$TMPFILE" > "$vjfile_tmp" && mv "$vjfile_tmp" "$vjfile_new"
|
||||
if ! sha1sum -c "$SHAFILE" >/dev/null 2>&1; then
|
||||
vjfile_tmp="$TMPFILE.ics"
|
||||
trap 'rm -f "$vjfile_tmp"' EXIT
|
||||
__vjournalnew "$uuid_new" <"$TMPFILE" >"$vjfile_tmp" && mv "$vjfile_tmp" "$vjfile_new"
|
||||
fi
|
||||
fi
|
||||
if [ "${1:-}" = "--reload" ]; then
|
||||
__lines
|
||||
exit
|
||||
fi
|
||||
|
||||
selection=$(__lines | fzf --ansi \
|
||||
--preview="$0 --preview {}" \
|
||||
--bind="ctrl-d:execute($0 --delete {})" \
|
||||
--bind="ctrl-n:execute($0 --new)" \
|
||||
--bind="ctrl-s:execute($SYNC_CMD)" \
|
||||
selection=$(
|
||||
__lines | fzf --ansi \
|
||||
--preview="$0 --preview {}" \
|
||||
--bind="ctrl-d:become($0 --delete {})" \
|
||||
--bind="ctrl-n:become($0 --new)" \
|
||||
--bind="ctrl-s:execute($SYNC_CMD)" \
|
||||
--bind="ctrl-r:reload-sync($0 --reload)"
|
||||
)
|
||||
if [[ -z "$selection" ]]; then
|
||||
return 0;
|
||||
if [ -z "$selection" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
VJ_FILE=$(__filepath_from_selection "$selection")
|
||||
|
||||
if [[ ! -f "$VJ_FILE" ]]; then
|
||||
if [ ! -f "$VJ_FILE" ]; then
|
||||
echo "ERROR: File '$VJ_FILE' does not exist!"
|
||||
return 1;
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Parse vjournal file and save as json
|
||||
TMPJSON="$(mktemp).json"
|
||||
trap "rm -f $TMPJSON" EXIT
|
||||
cat "$VJ_FILE" | __vjournal2json > "$TMPJSON"
|
||||
trap 'rm -f "$TMPJSON"' EXIT
|
||||
__vjournal2json <"$VJ_FILE" >"$TMPJSON"
|
||||
|
||||
# Prepare file to be edited
|
||||
TMPFILE="$(mktemp).md"
|
||||
SHAFILE="$TMPFILE.sha"
|
||||
trap "rm -f $TMPFILE $SHAFILE" EXIT
|
||||
trap 'rm -f "$TMPFILE" "$SHAFILE"' EXIT
|
||||
SUMMARY=$(jq -r '.summary' "$TMPJSON")
|
||||
CATEGORIES=$(jq -r '.categories | join(",")' "$TMPJSON")
|
||||
echo "# $SUMMARY" > "$TMPFILE"
|
||||
echo "> $CATEGORIES" >> "$TMPFILE"
|
||||
echo >> "$TMPFILE"
|
||||
jq -r '.description' "$TMPJSON" >> "$TMPFILE"
|
||||
sha1sum "$TMPFILE" > "$SHAFILE"
|
||||
{
|
||||
echo "# $SUMMARY"
|
||||
echo "> $CATEGORIES"
|
||||
echo ""
|
||||
jq -r '.description' "$TMPJSON"
|
||||
} >"$TMPFILE"
|
||||
sha1sum "$TMPFILE" >"$SHAFILE"
|
||||
|
||||
# Open in editor
|
||||
$EDITOR "$TMPFILE"
|
||||
|
||||
# Update only if changes are detected
|
||||
if ! sha1sum -c "$SHAFILE" > /dev/null 2>&1
|
||||
then
|
||||
if ! sha1sum -c "$SHAFILE" >/dev/null 2>&1; then
|
||||
echo "Uh... chages detected!"
|
||||
local vj_file_new="$TMPFILE.ics"
|
||||
trap "rm -f $vj_file_new" EXIT
|
||||
__vjournalupdate "$VJ_FILE" < "$TMPFILE" > "$vj_file_new" && mv "$vj_file_new" "$VJ_FILE"
|
||||
vj_file_new="$TMPFILE.ics"
|
||||
trap 'rm -f "$vj_file_new"' EXIT
|
||||
__vjournalupdate "$VJ_FILE" <"$TMPFILE" >"$vj_file_new" && mv "$vj_file_new" "$VJ_FILE"
|
||||
fi
|
||||
|
||||
exec "$0"
|
||||
|
||||
Reference in New Issue
Block a user