1 Commits

Author SHA1 Message Date
12ba1d3326 feat!: add builtin templates
WIP: document, check for breaking changes, decide on version, etc.
2024-06-07 11:57:15 +02:00
7 changed files with 228 additions and 42 deletions

View File

@@ -12,4 +12,11 @@ Initial release.
- Edit, list todo notes by any date format understood by `date(1)`
- Write custom template programs to generate content for newly created notes
## v0.2.0
### Added
- Builtin templates for the markdown and xit formats (`.md` and `.xit`
extensions); the templates carry over open tasks from previous agenda notes
[changelog.md]: https://changelog.md/

View File

@@ -6,15 +6,19 @@ PREFIX := /usr/local
.PHONY: install
install: ./agenda.1.gz
install -m 755 -D ./agenda $(PREFIX)/bin/agenda
mkdir -p $(PREFIX)/share/agenda/templates
install -m 755 ./templates/* $(PREFIX)/share/agenda/templates
install -m 644 -D ./agenda.1.gz $(PREFIX)/share/man/man1/agenda.1.gz
install -m 644 -D ./completion.bash $(PREFIX)/share/bash-completion/completions/agenda
.PHONY: uninstall
uninstall:
rm -f $(PREFIX)/bin/agenda
rm -f $(PREFIX)/share/man/man1/agenda.1.gz
rm -f $(PREFIX)/share/bash-completion/completions/agenda
$(RM) $(PREFIX)/bin/agenda
$(RM) $(PREFIX)/share/agenda/templates/*
$(RM) $(PREFIX)/share/man/man1/agenda.1.gz
$(RM) $(PREFIX)/share/bash-completion/completions/agenda
rmdir $(PREFIX)/share/agenda/templates $(PREFIX)/share/agenda
.PHONY: clean
clean:
rm -f ./agenda.1.gz
$(RM) ./agenda.1.gz

59
agenda
View File

@@ -17,6 +17,9 @@ AGENDA_EDITOR=${AGENDA_EDITOR:-${VISUAL:-${EDITOR:-vi}}}
#
AGENDA_DIR=${AGENDA_DIR:-}
# Location of additional files of an agenda installation.
AGENDA_DATA_DIRS=${AGENDA_DATA_DIRS:-}
# The default `-e` option.
AGENDA_EXTENSION=${AGENDA_EXTENSION:-"md"}
@@ -24,7 +27,10 @@ AGENDA_EXTENSION=${AGENDA_EXTENSION:-"md"}
AGENDA_DEFAULT=${AGENDA_DEFAULT:-"agenda"}
# Command that provides the initial contents of new agenda files.
AGENDA_TEMPLATE=${AGENDA_TEMPLATE:-"__agenda_default_template"}
#
# If this is null AGENDA_PATH is searched for an executable in the template
# subdirectory with the same name as AGENDA_EXTENSION.
AGENDA_TEMPLATE=${AGENDA_TEMPLATE:-}
__usage() {
fold -s <<-USAGE
@@ -56,7 +62,15 @@ __main() {
"--version") __version && return ;;
esac
if [ -z "$AGENDA_DATA_DIRS" ]; then
# FIXME: As per spec only absolute paths should be accepted!
AGENDA_DATA_DIRS="${XDG_CONFIG_HOME:-"$HOME/.config"}/agenda"
AGENDA_DATA_DIRS="$AGENDA_DATA_DIRS:/usr/local/share/agenda"
AGENDA_DATA_DIRS="$AGENDA_DATA_DIRS:/usr/share/agenda"
fi
if [ -z "$AGENDA_DIR" ]; then
# FIXME: As per spec only absolute paths should be accepted!
AGENDA_DIR="${XDG_DATA_HOME:-"$HOME/.local/share"}/agenda"
fi
@@ -291,6 +305,7 @@ __list() {
fi
done < <(
# Sort the paths by basename.
# TODO: Inline the prefix functions and explain this.
find "$dir" -type f \
| __agenda_sort_prefix \
| sort \
@@ -312,7 +327,18 @@ __agenda_create() {
tmp_err=$(mktemp)
if [ "$backlog" = "no" ]; then
# Capture exit code of the template command.
# Search for a template program if the AGENDA_TEMPLATE variable is empty.
if [ -z "$AGENDA_TEMPLATE" ]; then
AGENDA_TEMPLATE=$(
__agenda_get_template_path "$AGENDA_EXTENSION" ||
__agenda_get_template_path "default" ||
true
)
fi
# If a template program is set, attempt to use it to create the new agenda
# note file.
if [ -n "$AGENDA_TEMPLATE" ]; then
local code
set +e
"$AGENDA_TEMPLATE" > "$tmp" 2> "$tmp_err"
@@ -334,25 +360,15 @@ __agenda_create() {
echo "${0##*/}: cannot create '$file': template error" >&2
return $code
fi
else
touch "$tmp"
fi
fi
mkdir -p "${file%/*}"
mv "$tmp" "$file"
}
__agenda_default_template() {
case $AGENDA_EXTENSION in
"md")
printf "# %s %s\n\n- [ ] " "$AGENDA_NAME" "$AGENDA_DATE"
;;
"txt"|"xit"|"")
printf "%s %s\n[ ] " "$AGENDA_NAME" "$AGENDA_DATE"
;;
*)
;;
esac
}
__agenda_last() {
local file=$1 name=$2
@@ -375,6 +391,19 @@ __agenda_last() {
echo "${last:-}"
}
__agenda_get_template_path() {
local template=$1
local IFS=":" path
for path in $AGENDA_DATA_DIRS; do
if [ -x "$path/templates/$template" ]; then
AGENDA_TEMPLATE="$path/templates/$template"
return
fi
done
return 1
}
# https://stackoverflow.com/a/49035906
__slugify() {
echo "$1" \

View File

@@ -1,4 +1,4 @@
.TH AGENDA 1 "2024-06-07" "agenda 0.1.0"
.TH AGENDA 1 "2024-06-07" "agenda 0.2.0"
\#=============================================================================
.SH NAME
agenda \- Manage daily tasks and notes in any plain text format.
@@ -107,7 +107,26 @@ The editor process has access to certain environment variables.
\fBAGENDA_DIR\fP
.RS 4
The home of all agenda note files read and written by agenda. If this is empty
`${XDG_DATA_HOME:-"$HOME/.local/share"}/agenda` will be used.
\fBXDG_DATA_HOME\fP\fI/agenda\fP will be used.
.PP
.RE
\#
\fBAGENDA_DATA_DIRS\fP
.RS 4
Colon delimited Paths to additional files that come with an agenda installation
and custom overrides. Currently only used for template programs (located in the
\fItemplate/\fP sub-directory). If a file is searched in these paths the first file
that is found is used.
.nr PI 2n
If this variable is not set or empty it is set to include (in order)
.IP \[bu]
\fBXDG_CONFIG_HOME\fP\fI/agenda\fP
.IP \[bu]
\fI/usr/local/share/agenda\fP
.IP \[bu]
\fI/usr/share/agenda\fP
.PP
.RE
\#
@@ -127,10 +146,14 @@ The default agenda name if the \fI-t\fP option is omitted. This falls back to
\#
\fBAGENDA_TEMPLATE\fP
.RS 4
If this is not empty the value is interpreted as a command and executed when
creating a new agenda note with the \fI-c\fP flag. Any std output of the
command is written to the new file. The template process has access to certain
environment variables.
If this variable is not empty the value is interpreted as a command and
executed when creating a new agenda note with the \fI-c\fP flag. Any std output
of the command is written to the new file. The template process has access to
certain environment variables.
.PP
If this variable is unset or empty \fBAGENDA_DATA_DIRS\fP is searched for an
executable in the \fItemplates\fP sub-directory that is named either like the
corresponding extension or "default".
.sp 1
The following example will copy the last agenda note to
the new agenda note file.
@@ -153,6 +176,10 @@ Note however that this example is specific to \fBbash(1)\fP, since only bash
can export functions this way. In general you should write your template as a
script, save it, and set it as executable.
.PP
To set/override a template for a certain extension (e.g. markdown), save the
program to one of the \fBAGENDA_DATA_DIRS\fP, e.g.
\fI\fBXDG_CONFIG_HOME\fP/agenda/templates/md\fP.
.PP
\#-----------------------------------------------------------------------------
.SS "Templates and editor"
The template command and editor processes have access to certain environment
@@ -188,6 +215,21 @@ The file path of the agenda note.
The last agenda file. Can be empty if no previous agenda note exists.
.PP
.RE
.SS "Other"
\#
\fBXDG_CONFIG_HOME\fP
.RS 4
The default user configuration path. If this is not set the default of
\fBHOME\fP\fI/.config\fP is assumed.
.PP
.RE
\#
\fBXDG_DATA_HOME\fP
.RS 4
The default user data path. If this is not set the default of
\fBHOME\fP\fI/.local/share\fP is assumed.
.PP
.RE
\#=============================================================================
.SH "SEE ALSO"
\fBdate(1)\fP

6
templates/default Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/sh
set -eu
type agenda >/dev/null
# Capitalize the title.
echo "$AGENDA_NAME $AGENDA_DATE" | awk '{print toupper(substr($0,0,1))tolower(substr($0,2))}'

42
templates/md Executable file
View File

@@ -0,0 +1,42 @@
#!/bin/sh
set -eu
type agenda >/dev/null
if [ "${AGENDA_EXTENSION:-}" != "md" ]; then
exit
fi
# Capitalize the title.
echo "$AGENDA_NAME $AGENDA_DATE" | awk '{print "# "toupper(substr($0,0,1))tolower(substr($0,2))}'
echo
if [ -n "${AGENDA_IS_BACKLOG:-}" ]; then
exit
fi
# Carry over open tasks from the last agenda markdown file.
if [ -e "$AGENDA_LAST" ] && [ "${AGENDA_LAST#*.}" = "md" ]; then
while IFS= read -r REPLY; do
case ${state:-} in
"open_task")
case $REPLY in
"- [ ] "*|" "*)
# Append indented lines to open tasks.
echo "$REPLY"
;;
*)
unset state
;;
esac
;;
*)
case $REPLY in
"[ ] "*|"[@] "*|"[?] "*)
state="open_task"
echo "$REPLY"
;;
esac
;;
esac
done < "$AGENDA_LAST"
fi

56
templates/xit Executable file
View File

@@ -0,0 +1,56 @@
#!/bin/sh
set -eu
type agenda >/dev/null
if [ "${AGENDA_EXTENSION:-}" != "xit" ]; then
exit
fi
# Capitalize the title.
echo "$AGENDA_NAME $AGENDA_DATE" | awk '{print toupper(substr($0,0,1))tolower(substr($0,2))}'
if [ -n "${AGENDA_IS_BACKLOG:-}" ]; then
exit
fi
# Carry over open tasks from the last agenda xit file.
if [ -e "$AGENDA_LAST" ] && [ "${AGENDA_LAST#*.}" = "xit" ]; then
line=0
while IFS= read -r REPLY; do
line=$(( line + 1 ))
case ${state:-} in
"open_task")
case $REPLY in
"[ ] "*|"[@] "*|"[?] "*|" "*)
# Append indented lines to open tasks.
echo "$REPLY"
;;
*)
unset state
;;
esac
;;
*)
case $REPLY in
"[ ] "*|"[@] "*|"[?] "*)
# Carry over group heading with open tasks.
if [ -n "${header:-}" ]; then
echo
echo "$header"
unset header
fi
state="open_task"
echo "$REPLY"
;;
[a-z]*|[A-Z]*)
# Skip the first heading (assumed to be the generated
# title of the agenda note).
if [ $line -gt 1 ]; then
header=$REPLY
fi
;;
esac
;;
esac
done < "$AGENDA_LAST"
fi