From c2c809cdbe11bf3f5ac0fab2d05430b06773c893 Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:19:50 +0100 Subject: [PATCH] Add plumbing integration and improve output format - Add PLUMBING.md documentation for denote plumber integration - Update DisplayNotes to use clickable format: denote:ID | title | tags - Simplify plumbing rules to single pattern matching rule --- PLUMBING.md | 64 +++++++++++++++++++++++++++++++++++++++ cmd/Denote/main.go | 46 ++++++++++++++++++++++++++++ internal/denote/denote.go | 7 ++--- 3 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 PLUMBING.md diff --git a/PLUMBING.md b/PLUMBING.md new file mode 100644 index 0000000..2826502 --- /dev/null +++ b/PLUMBING.md @@ -0,0 +1,64 @@ +# Plumbing Integration for Denote + +This document describes how to integrate the Denote command with the Plan 9 plumber to enable right-click opening of denote notes. + +## Overview + +The plumbing rules allow you to right-click on `denote:` text in acme and have it automatically open the corresponding note. + +## Setup + +Add these rules to your `$HOME/lib/plumbing` file: + +``` +# declarations of ports without rules +plumb to denote + +# Denote identifiers - open note by identifier +type is text +data matches 'denote:([0-9]+T[0-9]+)' +plumb to denote +plumb start Denote open $1 +``` + +### Rule Placement + +- The port declaration (`plumb to denote`) should go with other port declarations near the top +- The pattern matching rule should be placed **before** generic file matching rules to ensure it fires first + +## Reload Rules + +After editing `$HOME/lib/plumbing`, reload the rules: + +```rc +cat $HOME/lib/plumbing | 9p write plumb/rules +``` + +## Usage + +Once configured, you can right-click (button 3) on any text matching the pattern: + +``` +denote:20251103T165653 +``` + +This will execute `Denote open 20251103T165653` and open the note in acme. + +## Pattern Details + +The regex pattern `denote:([0-9]+T[0-9]+)` matches: +- The literal text `denote:` +- One or more digits +- The letter `T` +- One or more digits + +This matches the standard Denote identifier format: `YYYYMMddTHHmmss` + +## Troubleshooting + +If right-clicking doesn't work: + +1. Verify rules are loaded: `9p read plumb/rules | grep denote` +2. Check the command exists: `which Denote` +3. Test manually: `plumb "denote:20251103T165653"` +4. Ensure the pattern uses `+` not `{n}` quantifiers (Plan 9 regex limitation) diff --git a/cmd/Denote/main.go b/cmd/Denote/main.go index a4d4bc8..06832bc 100644 --- a/cmd/Denote/main.go +++ b/cmd/Denote/main.go @@ -4,6 +4,7 @@ import ( "flag" "fmt" "os" + "os/exec" "path/filepath" "regexp" "strings" @@ -272,6 +273,44 @@ func handleRenameReturnNote(args []string) *denote.Note { return renamedNote } +func handleOpen(dir string, args []string) { + if len(args) == 0 { + fmt.Fprintf(os.Stderr, "Usage: open \n\nOpen a note by its identifier.\n\nExample:\n open 20251016T091314\n") + os.Exit(0) + } + + identifier := args[0] + + // Search for note with this identifier + identFilter, err := denote.ParseFilter(fmt.Sprintf("date:%s", identifier)) + if err != nil { + fmt.Fprintf(os.Stderr, "error: invalid identifier: %v\n", err) + os.Exit(1) + } + + notes, err := denote.FindNotes(dir, []*denote.Filter{identFilter}) + if err != nil { + fmt.Fprintf(os.Stderr, "error searching for note: %v\n", err) + os.Exit(1) + } + + if len(notes) == 0 { + fmt.Fprintf(os.Stderr, "error: no note found with identifier %s\n", identifier) + os.Exit(1) + } + + if len(notes) > 1 { + fmt.Fprintf(os.Stderr, "warning: multiple notes found with identifier %s, opening first\n", identifier) + } + + // Open the file in acme using plumb + cmd := exec.Command("plumb", notes[0].Path) + if err := cmd.Run(); err != nil { + fmt.Fprintf(os.Stderr, "error opening file: %v\n", err) + os.Exit(1) + } +} + func main() { dir := flag.String("dir", denote.DefaultDir(), "denote directory") flag.Parse() @@ -354,6 +393,7 @@ Usage: Denote + 'Title' [tags...] Alias for new Denote rename /path ['Title'] [tags...] Rename a note Denote r /path ['Title'] [tags...] Alias for rename + Denote open Open note by identifier Denote ? Show this help Filters: @@ -390,6 +430,12 @@ For 'rename' command help: Denote rename return } + // Check if this is an "open" command + if len(filterArgs) > 0 && filterArgs[0] == "open" { + handleOpen(*dir, filterArgs[1:]) + return + } + var filters []*denote.Filter sortType := denote.SortByID // Default sort diff --git a/internal/denote/denote.go b/internal/denote/denote.go index 0404bd2..3f37608 100644 --- a/internal/denote/denote.go +++ b/internal/denote/denote.go @@ -233,11 +233,8 @@ func DisplayNotes(notes []*Note) error { title = "(untitled)" } - buf.WriteString(title) - if len(n.Keywords) > 0 { - fmt.Fprintf(&buf, " (%s)", strings.Join(n.Keywords, ", ")) - } - fmt.Fprintf(&buf, "\n%s\n\n", n.Path) + tags := strings.Join(n.Keywords, ", ") + fmt.Fprintf(&buf, "denote:%s | %s | %s\n", n.Identifier, title, tags) } w.Write("body", []byte(buf.String()))