From 70e5335bd8abd335c128e8de3c8a25728c3b2e8f Mon Sep 17 00:00:00 2001 From: Lily Mara Date: Tue, 11 Jul 2023 22:14:54 -0700 Subject: [PATCH] Implement basic relative wikilink support Closes #35. This allows hugo-obsidian to intuit absolute paths from unanchored wikilink paths. Previously, a note at `directory/note.md` would need to be linked using `[[directory/note]]` in order for the link to be recognized by this tool. This change allows the shorter `[[note]]` link to be used. This is accomplished by first walking the directory tree and storing absolute paths for every path basename, and using this to substitute link names before rendering them. There's no test infrastructure for this project, but IWOMM --- main.go | 12 +++------ parse.go | 10 +++++--- walk.go | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 80 insertions(+), 18 deletions(-) diff --git a/main.go b/main.go index d5dfd44..d134eba 100644 --- a/main.go +++ b/main.go @@ -2,22 +2,16 @@ package main import ( "flag" - "github.com/BurntSushi/toml" - wikilink "github.com/abhinav/goldmark-wikilink" - "github.com/yuin/goldmark" "io/ioutil" "path/filepath" "time" + + "github.com/BurntSushi/toml" + "github.com/yuin/goldmark" ) var md goldmark.Markdown -func init() { - md = goldmark.New( - goldmark.WithExtensions(&wikilink.Extender{}), - ) -} - type Link struct { Source string `json:"source"` Target string `json:"target"` diff --git a/parse.go b/parse.go index a36c71d..cf0e65b 100644 --- a/parse.go +++ b/parse.go @@ -3,13 +3,15 @@ package main import ( "bytes" "fmt" - "github.com/PuerkitoBio/goquery" "io/ioutil" "strings" + + "github.com/PuerkitoBio/goquery" + "github.com/yuin/goldmark" ) // parse single file for links -func parse(dir, pathPrefix string) []Link { +func parse(md goldmark.Markdown, fileIndex fileIndex, dir, pathPrefix string) []Link { // read file source, err := ioutil.ReadFile(dir) if err != nil { @@ -37,8 +39,10 @@ func parse(dir, pathPrefix string) []Link { target = processTarget(target) source := processSource(trim(dir, pathPrefix, ".md")) + target = fileIndex.resolve(target) + // fmt.Printf(" '%s' => %s\n", source, target) - if !strings.HasPrefix(text, "^"){ + if !strings.HasPrefix(text, "^") { links = append(links, Link{ Source: source, Target: target, diff --git a/walk.go b/walk.go index 34e3795..978d130 100644 --- a/walk.go +++ b/walk.go @@ -9,7 +9,9 @@ import ( "strings" "time" + wikilink "github.com/abhinav/goldmark-wikilink" "github.com/adrg/frontmatter" + "github.com/yuin/goldmark" "gopkg.in/yaml.v2" ) @@ -19,6 +21,60 @@ type Front struct { Tags []string `yaml:"tags"` } +type fileIndex struct { + index map[string]string +} + +// resolve takes a link path and attempts to canonicalize it according to this +// fileIndex. If a matching canonical link cannot be found, the link is returned +// untouched. +func (i *fileIndex) resolve(path string) string { + if !isInternal(path) { + return path + } + + trimmedPath := strings.TrimLeft(path, "/") + + // If the path has any degree of nesting built into it, treat it as an absolute path + if strings.Contains(trimmedPath, "/") { + return path + } + + resolved, ok := i.index[trimmedPath] + if ok { + target := processTarget(resolved) + return target + } + + return path +} + +func buildFileIndex(root, ext string, ignorePaths map[string]struct{}) (fileIndex, error) { + index := map[string]string{} + err := filepath.WalkDir(root, func(fp string, d fs.DirEntry, e error) error { + if e != nil { + return e + } + + // path normalize fp + s := filepath.ToSlash(fp) + s = strings.ReplaceAll(s, " ", "-") + if _, ignored := ignorePaths[s]; ignored { + return nil + } else if filepath.Ext(d.Name()) == ext { + base := filepath.Base(strings.TrimSuffix(s, ".md")) + index[base] = strings.TrimPrefix(s, root) + } + + return nil + }) + if err != nil { + return fileIndex{}, err + } + + return fileIndex{index: index}, nil +} + // recursively walk directory and return all files with given extension func walk(root, ext string, index bool, ignorePaths map[string]struct{}) (res []Link, i ContentIndex) { fmt.Printf("Scraping %s\n", root) @@ -32,7 +88,15 @@ func walk(root, ext string, index bool, ignorePaths map[string]struct{}) (res [] start := time.Now() - err := filepath.WalkDir(root, func(fp string, d fs.DirEntry, e error) error { + md := goldmark.New( + goldmark.WithExtensions(&wikilink.Extender{})) + + fileIndex, err := buildFileIndex(root, ext, ignorePaths) + if err != nil { + panic(err) + } + + err = filepath.WalkDir(root, func(fp string, d fs.DirEntry, e error) error { if e != nil { return e } @@ -69,10 +133,10 @@ func walk(root, ext string, index bool, ignorePaths map[string]struct{}) (res [] title = strings.TrimSuffix(filepath.Base(fileName), filepath.Ext(fileName)) } - // default tags - if matter.Tags == nil { - matter.Tags = []string{} - } + // default tags + if matter.Tags == nil { + matter.Tags = []string{} + } // add to content and link index i[source] = Content{ @@ -81,7 +145,7 @@ func walk(root, ext string, index bool, ignorePaths map[string]struct{}) (res [] Content: body, Tags: matter.Tags, } - res = append(res, parse(s, root)...) + res = append(res, parse(md, fileIndex, s, root)...) } else { fmt.Printf("[Ignored] %s\n", d.Name()) nPrivate++