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
This commit is contained in:
Levi Neely 2025-11-07 18:19:50 +01:00
parent 313914d49e
commit c2c809cdbe
3 changed files with 112 additions and 5 deletions

64
PLUMBING.md Normal file
View File

@ -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:<identifier>` 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)

View File

@ -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 <identifier>\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 <identifier> 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

View File

@ -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()))