fix sync bug with signature

This commit is contained in:
Levi Neely 2025-11-26 12:17:33 +01:00
parent 2eb9e3511c
commit 77a1db71e3
4 changed files with 48 additions and 26 deletions

View File

@ -116,6 +116,28 @@ You may change this by changing `ftype` in main.go.
This program supports encrypted notes by integrating with [acme-crypt](https://github.com/lneely/acme-crypt). Set up the [plumbing rules](./PLUMBING.md) to read encrypted notes, and use `CryptPut` to write them.
## Signature Support
Denote supports an optional signature component in filenames: `ID==SIGNATURE--TITLE__TAGS.ext`. Signatures are useful for sequential numbering, context markers, or priorities.
**Plumbing Configuration:**
To plumb files with signatures, you must add `=` to the file pattern character classes in your plumbing rules. Update these three patterns:
**Original patterns:**
```
data matches '([.a-zA-Z<>-\uffff0-9_/\-@]*[a-zA-Z<>-\uffff0-9_/\-])':$twocolonaddr,$twocolonaddr
data matches '([.a-zA-Z<>-\uffff0-9_/\-@]*[a-zA-Z<>-\uffff0-9_/\-])':$twocolonaddr
data matches '([.a-zA-Z<>-\uffff0-9_/\-@]*[a-zA-Z<>-\uffff0-9_/\-])('$addr')?'
```
**Modified patterns (add `=` to character classes):**
```
data matches '([.a-zA-Z<>-\uffff0-9_/\-@=]*[a-zA-Z<>-\uffff0-9_/\-=])':$twocolonaddr,$twocolonaddr
data matches '([.a-zA-Z<>-\uffff0-9_/\-@=]*[a-zA-Z<>-\uffff0-9_/\-=])':$twocolonaddr
data matches '([.a-zA-Z<>-\uffff0-9_/\-@=]*[a-zA-Z<>-\uffff0-9_/\-=])('$addr')?'
```
## Extensions
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 are intentionally *not* exact replicas.

View File

@ -113,8 +113,8 @@ func HandleRenameEvent(f *client.Fsys, identifier, denoteDir string) error {
return nil
}
// Find old file on disk.
pattern := filepath.Join(denoteDir, identifier+"--*")
// Find old file on disk (matches with or without signature).
pattern := filepath.Join(denoteDir, identifier+"*")
matches, err := filepath.Glob(pattern)
if err != nil {
return fmt.Errorf("failed to find file: %w", err)
@ -165,7 +165,7 @@ func HandleDeleteEvent(f *client.Fsys, identifier, denoteDir string) error {
dir = denoteDir
}
pattern := filepath.Join(dir, identifier+"--*")
pattern := filepath.Join(dir, identifier+"*")
matches, err := filepath.Glob(pattern)
if err != nil {
return fmt.Errorf("failed to find file: %w", err)

View File

@ -222,7 +222,7 @@ func SyncAll() error {
// Path in metadata doesn't exist - search for actual file on disk
// Use the directory from the metadata path (handles subdirs like journal/)
dir := filepath.Dir(path)
pattern := filepath.Join(dir, identifier+"--*")
pattern := filepath.Join(dir, identifier+"*")
matches, err := filepath.Glob(pattern)
if err != nil {
log.Printf("sync: failed to glob for %s: %v", identifier, err)

View File

@ -88,16 +88,16 @@ func ParseFrontMatter(content string, ext string) (*FrontMatter, error) {
switch ext {
case ".org":
fm.FileType = "org"
if m := regexp.MustCompile(`(?m)^#\+title:\s*(.+)$`).FindStringSubmatch(text); m != nil {
if m := regexp.MustCompile(`(?m)^#\+title:[ \t]*(.+)$`).FindStringSubmatch(text); m != nil {
fm.Title = strings.TrimSpace(m[1])
}
if m := regexp.MustCompile(`(?m)^#\+filetags:\s*:(.+):$`).FindStringSubmatch(text); m != nil {
if m := regexp.MustCompile(`(?m)^#\+filetags:[ \t]*:(.+):$`).FindStringSubmatch(text); m != nil {
fm.Tags = strings.Split(m[1], ":")
}
if m := regexp.MustCompile(`(?m)^#\+identifier:\s*(.+)$`).FindStringSubmatch(text); m != nil {
if m := regexp.MustCompile(`(?m)^#\+identifier:[ \t]*(.+)$`).FindStringSubmatch(text); m != nil {
fm.Identifier = strings.TrimSpace(m[1])
}
if m := regexp.MustCompile(`(?m)^#\+signature:\s*(.+)$`).FindStringSubmatch(text); m != nil {
if m := regexp.MustCompile(`(?m)^#\+signature:[ \t]*(.*)$`).FindStringSubmatch(text); m != nil {
fm.Signature = strings.TrimSpace(m[1])
}
@ -107,20 +107,20 @@ func ParseFrontMatter(content string, ext string) (*FrontMatter, error) {
if m := yamlRe.FindStringSubmatch(text); m != nil {
fm.FileType = "md-yaml"
yamlContent := m[1]
if m := regexp.MustCompile(`(?m)^title:\s*["']?(.+?)["']?$`).FindStringSubmatch(yamlContent); m != nil {
if m := regexp.MustCompile(`(?m)^title:[ \t]*["']?(.+?)["']?$`).FindStringSubmatch(yamlContent); m != nil {
fm.Title = strings.TrimSpace(m[1])
}
if m := regexp.MustCompile(`(?m)^tags:\s*\[(.+?)\]$`).FindStringSubmatch(yamlContent); m != nil {
if m := regexp.MustCompile(`(?m)^tags:[ \t]*\[(.+?)\]$`).FindStringSubmatch(yamlContent); m != nil {
tags := strings.Split(m[1], ",")
for i, t := range tags {
tags[i] = strings.TrimSpace(t)
}
fm.Tags = tags
}
if m := regexp.MustCompile(`(?m)^identifier:\s*["']?(.+?)["']?$`).FindStringSubmatch(yamlContent); m != nil {
if m := regexp.MustCompile(`(?m)^identifier:[ \t]*["']?(.+?)["']?$`).FindStringSubmatch(yamlContent); m != nil {
fm.Identifier = strings.TrimSpace(m[1])
}
if m := regexp.MustCompile(`(?m)^signature:\s*["']?(.+?)["']?$`).FindStringSubmatch(yamlContent); m != nil {
if m := regexp.MustCompile(`(?m)^signature:[ \t]*["']?(.*)["']?$`).FindStringSubmatch(yamlContent); m != nil {
fm.Signature = strings.TrimSpace(m[1])
}
} else {
@ -129,20 +129,20 @@ func ParseFrontMatter(content string, ext string) (*FrontMatter, error) {
if m := tomlRe.FindStringSubmatch(text); m != nil {
fm.FileType = "md-toml"
tomlContent := m[1]
if m := regexp.MustCompile(`(?m)^title\s*=\s*["']?(.+?)["']?$`).FindStringSubmatch(tomlContent); m != nil {
if m := regexp.MustCompile(`(?m)^title[ \t]*=[ \t]*["']?(.+?)["']?$`).FindStringSubmatch(tomlContent); m != nil {
fm.Title = strings.TrimSpace(m[1])
}
if m := regexp.MustCompile(`(?m)^tags\s*=\s*\[(.+?)\]$`).FindStringSubmatch(tomlContent); m != nil {
if m := regexp.MustCompile(`(?m)^tags[ \t]*=[ \t]*\[(.+?)\]$`).FindStringSubmatch(tomlContent); m != nil {
tags := strings.Split(m[1], ",")
for i, t := range tags {
tags[i] = strings.TrimSpace(t)
}
fm.Tags = tags
}
if m := regexp.MustCompile(`(?m)^identifier\s*=\s*["']?(.+?)["']?$`).FindStringSubmatch(tomlContent); m != nil {
if m := regexp.MustCompile(`(?m)^identifier[ \t]*=[ \t]*["']?(.+?)["']?$`).FindStringSubmatch(tomlContent); m != nil {
fm.Identifier = strings.TrimSpace(m[1])
}
if m := regexp.MustCompile(`(?m)^signature\s*=\s*["']?(.+?)["']?$`).FindStringSubmatch(tomlContent); m != nil {
if m := regexp.MustCompile(`(?m)^signature[ \t]*=[ \t]*["']?(.*)["']?$`).FindStringSubmatch(tomlContent); m != nil {
fm.Signature = strings.TrimSpace(m[1])
}
}
@ -150,16 +150,16 @@ func ParseFrontMatter(content string, ext string) (*FrontMatter, error) {
case ".txt":
fm.FileType = "txt"
if m := regexp.MustCompile(`(?m)^title:\s*(.+)$`).FindStringSubmatch(text); m != nil {
if m := regexp.MustCompile(`(?m)^title:[ \t]*(.+)$`).FindStringSubmatch(text); m != nil {
fm.Title = strings.TrimSpace(m[1])
}
if m := regexp.MustCompile(`(?m)^tags:\s*(.+)$`).FindStringSubmatch(text); m != nil {
if m := regexp.MustCompile(`(?m)^tags:[ \t]*(.+)$`).FindStringSubmatch(text); m != nil {
fm.Tags = strings.Fields(m[1])
}
if m := regexp.MustCompile(`(?m)^identifier:\s*(.+)$`).FindStringSubmatch(text); m != nil {
if m := regexp.MustCompile(`(?m)^identifier:[ \t]*(.+)$`).FindStringSubmatch(text); m != nil {
fm.Identifier = strings.TrimSpace(m[1])
}
if m := regexp.MustCompile(`(?m)^signature:\s*(.+)$`).FindStringSubmatch(text); m != nil {
if m := regexp.MustCompile(`(?m)^signature:[ \t]*(.*)$`).FindStringSubmatch(text); m != nil {
fm.Signature = strings.TrimSpace(m[1])
}
}
@ -206,8 +206,8 @@ func UpdateFrontMatter(originalContent string, fm *FrontMatter) (string, error)
}
case "md-yaml":
// Replace YAML front matter
re := regexp.MustCompile(`(?s)^---\n.*?\n---\n`)
// Replace YAML front matter (match trailing blank lines to avoid duplication)
re := regexp.MustCompile(`(?s)^---\n.*?\n---\n\n*`)
if re.MatchString(text) {
newText = re.ReplaceAllString(text, newFrontMatter)
} else {
@ -215,8 +215,8 @@ func UpdateFrontMatter(originalContent string, fm *FrontMatter) (string, error)
}
case "md-toml":
// Replace TOML front matter
re := regexp.MustCompile(`(?s)^\+\+\+\n.*?\n\+\+\+\n`)
// Replace TOML front matter (match trailing blank lines to avoid duplication)
re := regexp.MustCompile(`(?s)^\+\+\+\n.*?\n\+\+\+\n\n*`)
if re.MatchString(text) {
newText = re.ReplaceAllString(text, newFrontMatter)
} else {
@ -224,8 +224,8 @@ func UpdateFrontMatter(originalContent string, fm *FrontMatter) (string, error)
}
case "txt":
// Replace text front matter
re := regexp.MustCompile(`(?s)^title:.*?\n-+\n`)
// Replace text front matter (match trailing blank lines to avoid duplication)
re := regexp.MustCompile(`(?s)^title:.*?\n-+\n\n*`)
if re.MatchString(text) {
newText = re.ReplaceAllString(text, newFrontMatter)
} else {