From 104ee66f5930f4a37ac84538c29a291bf1d08f4f Mon Sep 17 00:00:00 2001 From: Guangxiong Lin Date: Tue, 24 Jan 2023 20:27:22 +0800 Subject: Use cobra as command selector --- pkg/doc.go | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pkg/map.go | 8 ++++ pkg/set.go | 16 +++++++ 3 files changed, 178 insertions(+) create mode 100644 pkg/doc.go create mode 100644 pkg/map.go create mode 100644 pkg/set.go (limited to 'pkg') diff --git a/pkg/doc.go b/pkg/doc.go new file mode 100644 index 0000000..9f85b3a --- /dev/null +++ b/pkg/doc.go @@ -0,0 +1,154 @@ +package pkg + +import ( + "bytes" + "io/fs" + "net/url" + "os" + "path/filepath" + "strings" + + "github.com/yuin/goldmark" + "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/extension" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/text" +) + +var md = goldmark.New( + goldmark.WithExtensions(extension.TaskList), +) + +type Doc struct { + path string + content []byte + links Set[string] + Backlinks Set[string] +} + +func (doc *Doc) UpdateLinks(source, target string) { + dirname := filepath.Dir(doc.path) + relpath, err := filepath.Rel(dirname, target) + if err != nil { + return + } + + for link := range doc.links { + if filepath.Join(dirname, link) != source { + continue + } + + doc.content = bytes.Replace(doc.content, []byte(link), []byte(relpath), -1) + // TODO: Update doc.links + } + + doc.Overwrite() +} + +func (doc *Doc) Overwrite() { + os.WriteFile(doc.path, doc.content, 0644) +} + +func NewDoc(filename string) *Doc { + // NOTE: so far, only markdown is supported + if !strings.HasSuffix(filename, ".md") { + return nil + } + + content, err := os.ReadFile(filename) + if err != nil { + return nil + } + + root := md.Parser().Parse( + text.NewReader(content), + parser.WithContext(parser.NewContext()), + ) + + links := Set[string]{} + err = ast.Walk(root, func(n ast.Node, entering bool) (ast.WalkStatus, error) { + if entering { + switch link := n.(type) { + case *ast.Link: + href, err := url.PathUnescape(string(link.Destination)) + if err != nil { + return ast.WalkStop, err + } + + links.Insert(href) + } + } + return ast.WalkContinue, nil + }) + + if err != nil { + return nil + } + + return &Doc{ + path: filename, + content: content, + links: links, + Backlinks: Set[string]{}, + } +} + +type Docs Map[string, *Doc] + +func (docs Docs) GetOrNew(filename string) *Doc { + if doc, ok := docs[filename]; ok { + return doc + } + + doc := NewDoc(filename) + docs[filename] = doc + return doc +} + +func NewDocs(dirname string) Docs { + docs := Docs{} + + dirname, err := filepath.Abs(dirname) + if err != nil { + return nil + } + + if err := filepath.Walk(dirname, func(path string, info fs.FileInfo, err error) error { + if err != nil { + return err + } + + // TODO: Add skip dir config + if info.IsDir() && (info.Name() == ".git" || info.Name() == "vendor") { + return filepath.SkipDir + } + + if doc := NewDoc(path); doc != nil { + docs[path] = doc + } + + return nil + }); err != nil { + return nil + } + + for filename, doc := range docs { + for link := range doc.links { + abslink := filepath.Join(filepath.Dir(filename), link) + if _, ok := docs[abslink]; !ok { + continue + } + + docs[abslink].Backlinks.Insert(filename) + } + } + + return docs +} + +func (docs Docs) Contain(filename string) bool { + _, ok := docs[filename] + return ok +} + +var DocCollection Docs diff --git a/pkg/map.go b/pkg/map.go new file mode 100644 index 0000000..a9f8ccb --- /dev/null +++ b/pkg/map.go @@ -0,0 +1,8 @@ +package pkg + +type Map[K comparable, V interface{}] map[K]V + +func (m Map[K, V]) Contain(key K) bool { + _, ok := m[key] + return ok +} diff --git a/pkg/set.go b/pkg/set.go new file mode 100644 index 0000000..634bb59 --- /dev/null +++ b/pkg/set.go @@ -0,0 +1,16 @@ +package pkg + +type Set[T comparable] map[T]bool + +func (s Set[T]) Contain(val T) bool { + _, ok := s[val] + return ok +} + +func (s Set[T]) Insert(val T) { + s[val] = true +} + +func (s Set[T]) Erase(val T) { + delete(s, val) +} -- cgit v1.2.3