From 582870fc3bf17dcee7d4bc63f97b698d14e199ef Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:33:21 +0100 Subject: [PATCH] better feature parity in Dmerge, updated docs --- README.md | 110 +++++++++++++++++++++--- scripts/Dmerge | 229 +++++++++++++++++++++++++++++++++++++------------ 2 files changed, 271 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 8eab8f0..4ac0e3e 100644 --- a/README.md +++ b/README.md @@ -118,23 +118,17 @@ This program supports encrypted notes by integrating with [acme-crypt](https://g ## Extensions -Some extensions have also been ported. +Some extensions have also been ported. While these extensions, like the main program, try to stay as true as possible to the original program and be as feature-complete as possible, they is intentionally *not* exact replicas. ### Djournal -Concept from prot's [denote-journal](https://github.com/protesilaos/denote-journal). While `Djournal` tries to stay as true as possible to the original program and be as feature-complete as possible, it is intentionally *not* an exact replica. +Create or open journal entries with date-based titles. Supports daily, weekly, monthly, or yearly journals. Concept from prot's [denote-journal](https://github.com/protesilaos/denote-journal). -Create or open today's journal entry (or this week's, month's, or year's, depending on `interval` configuration): +**Configuration:** -``` -Djournal -``` +Customize journal behavior by editing variables in the `Djournal` script: -Supports: `d` (days), `h` (hours), `m` (minutes). You can use this to create multiple entries on the same day. To find an existing journal entry, use the search function in `Denote` described above. You can also pass the args in with the `2-1` chord. - -Configure `Djournal` by editing the configuration options in the `Djournal` script. - -``` +```rc # Journal interval: daily, weekly, monthly, yearly interval=daily @@ -150,5 +144,97 @@ titleformat=full signature='' ``` -### Merge +**Basic Usage:** +Open today's journal entry (creates if it doesn't exist): + +``` +Djournal +``` + +Journal entries are stored in `journal/` subdirectory with the `journal` tag. + +**Time Navigation:** + +Navigate to past or future dates using time offsets: + +``` +Djournal +2d # 2 days from now +Djournal -1d # yesterday +Djournal -3h # 3 hours ago +Djournal +30m # 30 minutes from now +``` + +Supported units: `d` (days), `h` (hours), `m` (minutes) + +### Dmerge + +Merge notes together or move regions of text between notes while maintaining referential integrity. Concept from prot's [denote-merge](https://github.com/protesilaos/denote-merge). + + +**Configuration:** + +Customize annotations by editing variables in the `Dmerge` script: + +```rc +# Annotation for merged files (set to '' to disable) +annotation='MERGED FILE:' + +# Annotation for merged regions (set to '' to disable) +regionannotation='MERGED REGION:' +``` + +**File Merge** + +Merge an entire note into another note: + +``` +Dmerge +``` + +This will: +- Strip frontmatter from source note +- Append source content to destination note with annotation +- Update all backlinks pointing to source to point to destination +- Delete the source note file + +Example: +``` +Dmerge 20251125T120000 20251125T130000 +``` + +**Region Merge** + +Move a selected region of text from one note to another. Select text in an Acme window, then: + +``` +Dmerge +``` + +This will: +- Move the selected text to the destination note +- Replace the selection with a link to the destination: `denote:` +- Leave the source note open (dirty) for you to `Put` + +**With Formatting:** + +You can wrap the merged region in various formats: + +``` +Dmerge +``` + +Available formats: +- `plain` - No formatting (default) +- `plain-indented` - Add 4-space indentation +- `org-src` - Org source block (`#+begin_src` / `#+end_src`) +- `org-quote` - Org quote block (`#+begin_quote` / `#+end_quote`) +- `org-example` - Org example block (`#+begin_example` / `#+end_example`) +- `md-quote` - Markdown blockquote (prefix with `> `) +- `md-fence` - Markdown fenced code block (` ``` `) + +Examples: +``` +Dmerge 20251125T130000 org-quote +Dmerge 20251125T130000 md-fence +``` diff --git a/scripts/Dmerge b/scripts/Dmerge index 6d1af73..2dbf2f1 100755 --- a/scripts/Dmerge +++ b/scripts/Dmerge @@ -1,66 +1,183 @@ #!/usr/local/plan9/bin/rc -# Merge - Merge source note into destination note -# Usage: Merge +# Merge - Merge notes or regions +# Usage: Dmerge (file merge) +# Dmerge [format] (region merge, in acme) +# Formats: plain, plain-indented, org-src, org-quote, org-example, md-quote, md-fence -if(~ $#* 2) { - src=$1 - dst=$2 -} -if not { - echo 'usage: Merge source-id dest-id' >[1=2] - exit 1 -} +# Annotation for merged files (set to '' to disable) +annotation='MERGED FILE:' -# Read paths and titles -srcpath=`{9p read denote/n/$src/path} -dstpath=`{9p read denote/n/$dst/path} -srctitle=`{9p read denote/n/$src/title} +# Annotation for merged regions (set to '' to disable) +regionannotation='MERGED REGION:' -if(~ $#srcpath 0 || ~ $#dstpath 0) { - echo 'error: note not found' >[1=2] - exit 1 -} - -# Extract content without frontmatter (everything after first blank line) -awk ' -BEGIN { found_blank = 0 } -found_blank { print } -!found_blank && /^$/ { found_blank = 1 } -' $srcpath > /tmp/content.$pid - -# Append source content to destination with annotation -{ - cat $dstpath - echo '' - echo '* * *' - echo '' - echo 'MERGED FILE:' $srctitle - echo '' - cat /tmp/content.$pid -} > /tmp/merge.$pid - -rm /tmp/content.$pid - -# Update all backlinks pointing to source -9p read denote/n/$src/backlinks | awk -F'|' '{print $1}' | while(read linkid) { - linkid=`{echo $linkid | awk '{gsub(/^ +| +$/, ""); print}'} - if(! ~ $#linkid 0) { - linkpath=`{9p read denote/n/$linkid/path} - if(! ~ $#linkpath 0) { - # Replace denote:src with denote:dst in the file - sed 's/denote:'$src'/denote:'$dst'/g' $linkpath > /tmp/relink.$pid - cp /tmp/relink.$pid $linkpath - rm /tmp/relink.$pid - } +# Check for selection if in acme +hasselection=0 +if(! ~ $winid '') { + selection=`{9p read acme/$winid/rdsel} + if(! ~ $#selection 0 && ! ~ $selection '') { + hasselection=1 } } -# Write merged content to destination -cp /tmp/merge.$pid $dstpath -rm /tmp/merge.$pid +# Parse arguments and determine mode +if(~ $#* 2) { + # Two args: region merge with format OR file merge + if(~ $hasselection 1) { + # Region merge with format + dst=$1 + format=$2 + mode=region -# Delete source note -echo 'd' | 9p write denote/n/$src/ctl + # Extract source note ID from window tag + tag=`{9p read acme/$winid/tag} + filename=`{echo $tag | awk '{for(i=1;i<=NF;i++) if($i ~ /[0-9]{8}T[0-9]{6}/) {print $i; exit}}'} + src=`{echo $filename | sed 's/--.*//'} + } + if not { + # File merge + src=$1 + dst=$2 + mode=file + } +} +if not if(~ $#* 1) { + # One arg: region merge (plain format) + if(~ $winid '') { + echo 'region merge requires running from acme window' >[1=2] + exit 1 + } + if(~ $hasselection 0) { + echo 'no text selected' >[1=2] + exit 1 + } -echo 'Merged' $src 'into' $dst + dst=$1 + format=plain + mode=region + + # Extract source note ID from window tag + tag=`{9p read acme/$winid/tag} + filename=`{echo $tag | awk '{for(i=1;i<=NF;i++) if($i ~ /[0-9]{8}T[0-9]{6}/) {print $i; exit}}'} + src=`{echo $filename | sed 's/--.*//'} +} +if not { + echo 'usage: Dmerge source-id dest-id OR Dmerge dest-id [format] (in acme)' >[1=2] + exit 1 +} + +# Branch based on mode +if(~ $mode file) { + # FILE MERGE MODE + + # Read paths and titles + srcpath=`{9p read denote/n/$src/path} + dstpath=`{9p read denote/n/$dst/path} + srctitle=`{9p read denote/n/$src/title} + + if(~ $#srcpath 0 || ~ $#dstpath 0) { + echo 'error: note not found' >[1=2] + exit 1 + } + + # Extract content without frontmatter (everything after first blank line) + awk ' + BEGIN { found_blank = 0 } + found_blank { print } + !found_blank && /^$/ { found_blank = 1 } + ' $srcpath > /tmp/content.$pid + + # Append source content to destination with optional annotation + { + cat $dstpath + if(! ~ $annotation '') { + echo '' + echo '* * *' + echo '' + echo $annotation $srctitle + echo '' + } + cat /tmp/content.$pid + } > /tmp/merge.$pid + + rm /tmp/content.$pid + + # Update all backlinks pointing to source + 9p read denote/n/$src/backlinks | awk -F'|' '{print $1}' | while(read linkid) { + linkid=`{echo $linkid | awk '{gsub(/^ +| +$/, ""); print}'} + if(! ~ $#linkid 0) { + linkpath=`{9p read denote/n/$linkid/path} + if(! ~ $#linkpath 0) { + # Replace denote:src with denote:dst in the file + sed 's/denote:'$src'/denote:'$dst'/g' $linkpath > /tmp/relink.$pid + cp /tmp/relink.$pid $linkpath + rm /tmp/relink.$pid + } + } + } + + # Write merged content to destination + cp /tmp/merge.$pid $dstpath + rm /tmp/merge.$pid + + # Delete source note + echo 'd' | 9p write denote/n/$src/ctl + + echo 'Merged' $src 'into' $dst +} + +if not if(~ $mode region) { + # REGION MERGE MODE + + # Read destination path + dstpath=`{9p read denote/n/$dst/path} + + if(~ $#dstpath 0) { + echo 'error: destination note not found' >[1=2] + exit 1 + } + + # Apply formatting to selection + switch($format) { + case plain + formatted=$selection + case plain-indented + formatted=`{echo $selection | sed 's/^/ /'} + case org-src + formatted=`{echo '#+begin_src'; echo $selection; echo '#+end_src'} + case org-quote + formatted=`{echo '#+begin_quote'; echo $selection; echo '#+end_quote'} + case org-example + formatted=`{echo '#+begin_example'; echo $selection; echo '#+end_example'} + case md-quote + formatted=`{echo $selection | sed 's/^/> /'} + case md-fence + formatted=`{echo '```'; echo $selection; echo '```'} + case * + echo 'unknown format:' $format >[1=2] + echo 'valid formats: plain, plain-indented, org-src, org-quote, org-example, md-quote, md-fence' >[1=2] + exit 1 + } + + # Append formatted selection to destination with optional annotation + { + cat $dstpath + if(! ~ $regionannotation '') { + echo '' + echo '* * *' + echo '' + echo $regionannotation + echo '' + } + echo "$formatted" + } > /tmp/merge.$pid + + # Write merged content to destination + cp /tmp/merge.$pid $dstpath + rm /tmp/merge.$pid + + # Replace selection with link to destination + echo -n 'denote:'$dst | 9p write acme/$winid/wrsel + + echo 'Region merged from' $src 'into' $dst '(format:' $format')' +}