Compare commits
	
		
			7 Commits
		
	
	
		
			4079a53c2b
			...
			dev
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 2a3c188f02 | |||
| 6f268c05cd | |||
| 5144465792 | |||
| 83061bd54b | |||
| b613c25f98 | |||
| 374d7e08c8 | |||
| 7df51a6980 | 
@@ -1,4 +1,7 @@
 | 
			
		||||
A [fzf](https://github.com/junegunn/fzf)-based **journaling, notes, and tasks** application with CalDav support.
 | 
			
		||||
If you are interested in this, then you may also be interested in the
 | 
			
		||||
corresponding calendar application
 | 
			
		||||
[fzf-vcal](https://github.com/baumea/fzf-vcal).
 | 
			
		||||
 | 
			
		||||
Description and Use Case
 | 
			
		||||
------------------------
 | 
			
		||||
@@ -15,7 +18,8 @@ copy `fzf-vjour` to your preferred location, e.g., `~/.local/bin`, and make it e
 | 
			
		||||
 | 
			
		||||
### Requirements
 | 
			
		||||
This is a POSIX script with inline `awk` elements.
 | 
			
		||||
Make sure you have [fzf](https://github.com/junegunn/fzf) and [batcat](https://github.com/sharkdp/bat).
 | 
			
		||||
Make sure you have [fzf](https://github.com/junegunn/fzf) installed.
 | 
			
		||||
I also suggest to install [batcat](https://github.com/sharkdp/bat) for colorful previews.
 | 
			
		||||
 | 
			
		||||
Configuration
 | 
			
		||||
--------------
 | 
			
		||||
 
 | 
			
		||||
@@ -130,6 +130,21 @@ ENDFILE {
 | 
			
		||||
  if (!type) {
 | 
			
		||||
    exit
 | 
			
		||||
  }
 | 
			
		||||
  # Construct path, and check for validity
 | 
			
		||||
  depth = split(FILENAME, path, "/");
 | 
			
		||||
  fpath = path[depth-1] "/" path[depth]
 | 
			
		||||
  if (index(fpath, " "))
 | 
			
		||||
  {
 | 
			
		||||
    print 10,
 | 
			
		||||
          "-",
 | 
			
		||||
          "-",
 | 
			
		||||
          RED "ERROR: file '" fpath "' contains whitespaces!" OFF
 | 
			
		||||
    exit
 | 
			
		||||
  }
 | 
			
		||||
  # Collection name
 | 
			
		||||
  collection = path[depth-1]
 | 
			
		||||
  collection = collection in collection2label ? collection2label[collection] : collection;
 | 
			
		||||
 | 
			
		||||
  # Process content lines
 | 
			
		||||
  storetext_line(content_line, c, "CATEGORIES"   );
 | 
			
		||||
  storetext_line(content_line, c, "DESCRIPTION"  );
 | 
			
		||||
@@ -160,11 +175,6 @@ ENDFILE {
 | 
			
		||||
  #                format
 | 
			
		||||
  mod = c["LAST-MODIFIED"] ? c["LAST-MODIFIED"] : c["DTSTAMP"];
 | 
			
		||||
 | 
			
		||||
  # Collection name
 | 
			
		||||
  depth = split(FILENAME, path, "/");
 | 
			
		||||
  collection = depth > 1 ? path[depth-1] : "";
 | 
			
		||||
  collection = collection in collection2label ? collection2label[collection] : collection;
 | 
			
		||||
 | 
			
		||||
  # Date field. For VTODO entries, we show the due date, for journal entries,
 | 
			
		||||
  # the associated date.
 | 
			
		||||
  datecolor = CYAN;
 | 
			
		||||
@@ -206,10 +216,10 @@ ENDFILE {
 | 
			
		||||
 | 
			
		||||
  print prio,
 | 
			
		||||
        mod,
 | 
			
		||||
        fpath,
 | 
			
		||||
        collection,
 | 
			
		||||
        datecolor d OFF,
 | 
			
		||||
        flag,
 | 
			
		||||
        priotext summarycolor summary OFF,
 | 
			
		||||
        WHITE categories OFF,
 | 
			
		||||
        "                                                                                                                                                                    " FAINT FILENAME OFF;
 | 
			
		||||
        WHITE categories OFF;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										156
									
								
								src/main.sh
									
									
									
									
									
								
							
							
						
						
									
										156
									
								
								src/main.sh
									
									
									
									
									
								
							@@ -2,51 +2,99 @@
 | 
			
		||||
 | 
			
		||||
set -eu
 | 
			
		||||
 | 
			
		||||
# Read configuration
 | 
			
		||||
# shellcheck source=/dev/null
 | 
			
		||||
. "$HOME/.config/fzf-vjour/config"
 | 
			
		||||
if [ -z "$ROOT" ] || [ -z "$SYNC_CMD" ] || [ -z "$COLLECTION_LABELS" ]; then
 | 
			
		||||
  echo "Failed to get configuration." >/dev/tty
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
err() {
 | 
			
		||||
  echo "❌ $1" >/dev/tty
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
### AWK SCRIPTS
 | 
			
		||||
AWK_ALTERTODO=$(
 | 
			
		||||
if [ -z "${FZF_VJOUR_USE_EXPORTED:-}" ]; then
 | 
			
		||||
  # Read configuration
 | 
			
		||||
  CONFIGFILE="$HOME/.config/fzf-vjour/config"
 | 
			
		||||
  if [ ! -f "$CONFIGFILE" ]; then
 | 
			
		||||
    err "Configuration '$CONFIGFILE' not found."
 | 
			
		||||
    exit 1
 | 
			
		||||
  fi
 | 
			
		||||
  # shellcheck source=/dev/null
 | 
			
		||||
  . "$CONFIGFILE"
 | 
			
		||||
  if [ -z "${ROOT:-}" ] || [ -z "${SYNC_CMD:-}" ] || [ -z "${COLLECTION_LABELS:-}" ]; then
 | 
			
		||||
    err "Configuration is incomplete."
 | 
			
		||||
    exit 1
 | 
			
		||||
  fi
 | 
			
		||||
  export ROOT
 | 
			
		||||
  export SYNC_CMD
 | 
			
		||||
  export COLLECTION_LABELS
 | 
			
		||||
 | 
			
		||||
  # Tools
 | 
			
		||||
  if command -v "fzf" >/dev/null; then
 | 
			
		||||
    FZF="fzf"
 | 
			
		||||
  else
 | 
			
		||||
    err "Did not find the command-line fuzzy finder fzf."
 | 
			
		||||
    exit 1
 | 
			
		||||
  fi
 | 
			
		||||
  export FZF
 | 
			
		||||
 | 
			
		||||
  if command -v "uuidgen" >/dev/null; then
 | 
			
		||||
    UUIDGEN="uuidgen"
 | 
			
		||||
  else
 | 
			
		||||
    err "Did not find the uuidgen command."
 | 
			
		||||
    exit 1
 | 
			
		||||
  fi
 | 
			
		||||
  export UUIDGEN
 | 
			
		||||
 | 
			
		||||
  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}
 | 
			
		||||
  export CAT
 | 
			
		||||
 | 
			
		||||
  ### AWK SCRIPTS
 | 
			
		||||
  AWK_ALTERTODO=$(
 | 
			
		||||
    cat <<'EOF'
 | 
			
		||||
@@include src/awk/altertodo.awk
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
  )
 | 
			
		||||
  export AWK_ALTERTODO
 | 
			
		||||
 | 
			
		||||
AWK_EXPORT=$(
 | 
			
		||||
  AWK_EXPORT=$(
 | 
			
		||||
    cat <<'EOF'
 | 
			
		||||
@@include src/awk/export.awk
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
  )
 | 
			
		||||
  export AWK_EXPORT
 | 
			
		||||
 | 
			
		||||
AWK_GET=$(
 | 
			
		||||
  AWK_GET=$(
 | 
			
		||||
    cat <<'EOF'
 | 
			
		||||
@@include src/awk/get.awk
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
  )
 | 
			
		||||
  export AWK_GET
 | 
			
		||||
 | 
			
		||||
AWK_LIST=$(
 | 
			
		||||
  AWK_LIST=$(
 | 
			
		||||
    cat <<'EOF'
 | 
			
		||||
@@include src/awk/list.awk
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
  )
 | 
			
		||||
  export AWK_LIST
 | 
			
		||||
 | 
			
		||||
AWK_NEW=$(
 | 
			
		||||
  AWK_NEW=$(
 | 
			
		||||
    cat <<'EOF'
 | 
			
		||||
@@include src/awk/new.awk
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
  )
 | 
			
		||||
  export AWK_NEW
 | 
			
		||||
 | 
			
		||||
AWK_UPDATE=$(
 | 
			
		||||
  AWK_UPDATE=$(
 | 
			
		||||
    cat <<'EOF'
 | 
			
		||||
@@include src/awk/update.awk
 | 
			
		||||
EOF
 | 
			
		||||
)
 | 
			
		||||
### END OF AWK SCRIPTS
 | 
			
		||||
  )
 | 
			
		||||
  export AWK_UPDATE
 | 
			
		||||
  ### END OF AWK SCRIPTS
 | 
			
		||||
  FZF_VJOUR_USE_EXPORTED="yes"
 | 
			
		||||
  export FZF_VJOUR_USE_EXPORTED
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
__lines() {
 | 
			
		||||
  find "$ROOT" -type f -name '*.ics' -print0 | xargs -0 -P 0 \
 | 
			
		||||
@@ -57,12 +105,7 @@ __lines() {
 | 
			
		||||
    -v flag_journal="📘" \
 | 
			
		||||
    -v flag_note="🗒️" \
 | 
			
		||||
    "$AWK_LIST" |
 | 
			
		||||
    sort -g -r |
 | 
			
		||||
    cut -d ' ' -f 3-
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__filepath_from_selection() {
 | 
			
		||||
  echo "$1" | grep -o ' \{50\}.*$' | xargs
 | 
			
		||||
    sort -g -r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Program starts here
 | 
			
		||||
@@ -94,17 +137,19 @@ fi
 | 
			
		||||
# Command line arguments to be self-contained
 | 
			
		||||
# Generate preview of file from selection
 | 
			
		||||
if [ "${1:-}" = "--preview" ]; then
 | 
			
		||||
  file=$(__filepath_from_selection "$2")
 | 
			
		||||
  name=$(echo "$2" | cut -d ' ' -f 3)
 | 
			
		||||
  file="$ROOT/$name"
 | 
			
		||||
  awk -v field="DESCRIPTION" "$AWK_GET" "$file" |
 | 
			
		||||
    batcat --color=always --style=numbers --language=md
 | 
			
		||||
    $CAT
 | 
			
		||||
  exit
 | 
			
		||||
fi
 | 
			
		||||
# Delete file from selection
 | 
			
		||||
if [ "${1:-}" = "--delete" ]; then
 | 
			
		||||
  file=$(__filepath_from_selection "$2")
 | 
			
		||||
  name=$(echo "$2" | cut -d ' ' -f 3)
 | 
			
		||||
  file="$ROOT/$name"
 | 
			
		||||
  summary=$(awk -v field="SUMMARY" "$AWK_GET" "$file")
 | 
			
		||||
  while true; do
 | 
			
		||||
    printf "Do you want to delete the entry with the title \"%s\"? " "$summary" >/dev/tty
 | 
			
		||||
    printf "Do you want to delete the entry with the title \"%s\"? (yes/no): " "$summary" >/dev/tty
 | 
			
		||||
    read -r yn
 | 
			
		||||
    case $yn in
 | 
			
		||||
    "yes")
 | 
			
		||||
@@ -122,21 +167,13 @@ if [ "${1:-}" = "--delete" ]; then
 | 
			
		||||
fi
 | 
			
		||||
# Generate new entry
 | 
			
		||||
if [ "${1:-}" = "--new" ]; then
 | 
			
		||||
  label=$(printf "%s" "$COLLECTION_LABELS" |
 | 
			
		||||
    awk 'BEGIN { FS="="; RS=";"; } {print $2}' |
 | 
			
		||||
    fzf \
 | 
			
		||||
      --margin 20% \
 | 
			
		||||
      --prompt="Select collection> ")
 | 
			
		||||
 | 
			
		||||
  collection=$(printf "%s" "$COLLECTION_LABELS" |
 | 
			
		||||
    awk -v label="$label" 'BEGIN { FS="="; RS=";"; } $2 == label {print $1}')
 | 
			
		||||
  collection=$(echo "$COLLECTION_LABELS" | tr ';' '\n' | $FZF --delimiter='=' --with-nth=2 --accept-nth=1)
 | 
			
		||||
  file=""
 | 
			
		||||
  while [ -f "$file" ] || [ -z "$file" ]; do
 | 
			
		||||
    uuid=$(uuidgen)
 | 
			
		||||
    uuid=$($UUIDGEN)
 | 
			
		||||
    file="$ROOT/$collection/$uuid.ics"
 | 
			
		||||
  done
 | 
			
		||||
  tmpmd=$(mktemp --suffix='.md')
 | 
			
		||||
  tmpsha="$tmpmd.sha"
 | 
			
		||||
  {
 | 
			
		||||
    echo "::: |> <!-- keep this line to associate the entry to _today_ -->"
 | 
			
		||||
    echo "::: <| <!-- specify the due date for to-dos, can be empty, a date string, or even \"next Sunday\" -->"
 | 
			
		||||
@@ -144,36 +181,39 @@ if [ "${1:-}" = "--new" ]; then
 | 
			
		||||
    echo "> <!-- comma-separated list of categories -->"
 | 
			
		||||
    echo ""
 | 
			
		||||
  } >"$tmpmd"
 | 
			
		||||
  sha1sum "$tmpmd" >"$tmpsha"
 | 
			
		||||
  checksum=$(cksum "$tmpmd")
 | 
			
		||||
 | 
			
		||||
  # Open in editor
 | 
			
		||||
  $EDITOR "$tmpmd" >/dev/tty
 | 
			
		||||
 | 
			
		||||
  # Update if changes are detected
 | 
			
		||||
  if ! sha1sum -c "$tmpsha" >/dev/null 2>&1; then
 | 
			
		||||
  if [ "$checksum" != "$(cksum "$tmpmd")" ]; then
 | 
			
		||||
    tmpfile="$tmpmd.ics"
 | 
			
		||||
    awk -v uid="$uuid" "$AWK_NEW" "$tmpmd" >"$tmpfile"
 | 
			
		||||
    mv "$tmpfile" "$file"
 | 
			
		||||
  fi
 | 
			
		||||
  rm "$tmpmd" "$tmpsha"
 | 
			
		||||
  rm "$tmpmd"
 | 
			
		||||
fi
 | 
			
		||||
# Toggle completed flag
 | 
			
		||||
if [ "${1:-}" = "--toggle-completed" ]; then
 | 
			
		||||
  file=$(__filepath_from_selection "$2")
 | 
			
		||||
  name=$(echo "$2" | cut -d ' ' -f 3)
 | 
			
		||||
  file="$ROOT/$name"
 | 
			
		||||
  tmpfile=$(mktemp)
 | 
			
		||||
  awk "$AWK_ALTERTODO" "$file" >"$tmpfile"
 | 
			
		||||
  mv "$tmpfile" "$file"
 | 
			
		||||
fi
 | 
			
		||||
# Increase priority
 | 
			
		||||
if [ "${1:-}" = "--increase-priority" ]; then
 | 
			
		||||
  file=$(__filepath_from_selection "$2")
 | 
			
		||||
  name=$(echo "$2" | cut -d ' ' -f 3)
 | 
			
		||||
  file="$ROOT/$name"
 | 
			
		||||
  tmpfile=$(mktemp)
 | 
			
		||||
  awk -v delta="1" "$AWK_ALTERTODO" "$file" >"$tmpfile"
 | 
			
		||||
  mv "$tmpfile" "$file"
 | 
			
		||||
fi
 | 
			
		||||
# Decrease priority
 | 
			
		||||
if [ "${1:-}" = "--decrease-priority" ]; then
 | 
			
		||||
  file=$(__filepath_from_selection "$2")
 | 
			
		||||
  name=$(echo "$2" | cut -d ' ' -f 3)
 | 
			
		||||
  file="$ROOT/$name"
 | 
			
		||||
  tmpfile=$(mktemp)
 | 
			
		||||
  awk -v delta="-1" "$AWK_ALTERTODO" "$file" >"$tmpfile"
 | 
			
		||||
  mv "$tmpfile" "$file"
 | 
			
		||||
@@ -208,17 +248,17 @@ fi
 | 
			
		||||
if [ "${1:-}" = "--no-journal" ]; then
 | 
			
		||||
  query="!📘"
 | 
			
		||||
fi
 | 
			
		||||
if [ -z "$query" ]; then
 | 
			
		||||
  query="!✅"
 | 
			
		||||
fi
 | 
			
		||||
query=$(echo "$query" | sed 's/ *$//g')
 | 
			
		||||
query=${query:-!✅}
 | 
			
		||||
query=$(echo "$query" | xargs)
 | 
			
		||||
 | 
			
		||||
selection=$(
 | 
			
		||||
  __lines | fzf --ansi \
 | 
			
		||||
  __lines | $FZF --ansi \
 | 
			
		||||
    --query="$query " \
 | 
			
		||||
    --no-sort \
 | 
			
		||||
    --no-hscroll \
 | 
			
		||||
    --ellipsis='' \
 | 
			
		||||
    --with-nth=4.. \
 | 
			
		||||
    --accept-nth=3 \
 | 
			
		||||
    --preview="$0 --preview {}" \
 | 
			
		||||
    --bind="ctrl-r:reload-sync($0 --reload)" \
 | 
			
		||||
    --bind="ctrl-alt-d:become($0 --delete {})" \
 | 
			
		||||
@@ -230,13 +270,13 @@ selection=$(
 | 
			
		||||
    --bind="alt-1:change-query(📘)" \
 | 
			
		||||
    --bind="alt-2:change-query(🗒️)" \
 | 
			
		||||
    --bind="alt-3:change-query(✅ | 🔲)" \
 | 
			
		||||
    --bind="ctrl-s:execute($SYNC_CMD)"
 | 
			
		||||
    --bind="ctrl-s:execute($SYNC_CMD ; printf 'Press <enter> to continue.'; read -r tmp)"
 | 
			
		||||
)
 | 
			
		||||
if [ -z "$selection" ]; then
 | 
			
		||||
  return 0
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
file=$(__filepath_from_selection "$selection")
 | 
			
		||||
file="$ROOT/$selection"
 | 
			
		||||
 | 
			
		||||
if [ ! -f "$file" ]; then
 | 
			
		||||
  echo "ERROR: File '$file' does not exist!" >/dev/tty
 | 
			
		||||
@@ -245,20 +285,18 @@ fi
 | 
			
		||||
 | 
			
		||||
# Prepare file to be edited
 | 
			
		||||
filetmp=$(mktemp --suffix='.md')
 | 
			
		||||
filesha="$filetmp.sha"
 | 
			
		||||
awk "$AWK_EXPORT" "$file" >"$filetmp"
 | 
			
		||||
sha1sum "$filetmp" >"$filesha"
 | 
			
		||||
checksum=$(cksum "$filetmp")
 | 
			
		||||
 | 
			
		||||
# Open in editor
 | 
			
		||||
$EDITOR "$filetmp" >/dev/tty
 | 
			
		||||
 | 
			
		||||
# Update only if changes are detected
 | 
			
		||||
if ! sha1sum -c "$filesha" >/dev/null 2>&1; then
 | 
			
		||||
  echo "Uh... chages detected!" >/dev/tty
 | 
			
		||||
if [ "$checksum" != "$(cksum "$filetmp")" ]; then
 | 
			
		||||
  file_new="$filetmp.ics"
 | 
			
		||||
  awk "$AWK_UPDATE" "$filetmp" "$file" >"$file_new"
 | 
			
		||||
  mv "$file_new" "$file"
 | 
			
		||||
fi
 | 
			
		||||
rm "$filetmp" "$filesha"
 | 
			
		||||
rm "$filetmp"
 | 
			
		||||
 | 
			
		||||
exec "$0"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user