2021-08-31 17:27:52 +00:00
|
|
|
package hyphae
|
|
|
|
|
|
|
|
import (
|
2021-09-12 08:58:03 +00:00
|
|
|
"github.com/bouncepaw/mycomarkup/v2/tools"
|
2021-08-31 17:27:52 +00:00
|
|
|
"os"
|
|
|
|
|
|
|
|
"github.com/bouncepaw/mycorrhiza/util"
|
|
|
|
|
2021-09-12 08:58:03 +00:00
|
|
|
"github.com/bouncepaw/mycomarkup/v2"
|
|
|
|
"github.com/bouncepaw/mycomarkup/v2/links"
|
|
|
|
"github.com/bouncepaw/mycomarkup/v2/mycocontext"
|
2021-08-31 17:27:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Using set here seems like the most appropriate solution
|
|
|
|
type linkSet map[string]struct{}
|
|
|
|
|
2021-08-31 19:28:12 +00:00
|
|
|
func toLinkSet(xs []string) linkSet {
|
|
|
|
result := make(linkSet)
|
|
|
|
for _, x := range xs {
|
|
|
|
result[x] = struct{}{}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func fetchText(h *Hypha) string {
|
|
|
|
if h.TextPath == "" {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
text, err := os.ReadFile(h.TextPath)
|
|
|
|
if err == nil {
|
|
|
|
return string(text)
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2021-09-05 19:28:20 +00:00
|
|
|
// BacklinkIndexOperation is an operation for the backlink index. This operation is executed async-safe.
|
|
|
|
type BacklinkIndexOperation interface {
|
2021-09-01 06:28:21 +00:00
|
|
|
Apply()
|
|
|
|
}
|
|
|
|
|
2021-10-01 17:12:16 +00:00
|
|
|
// BacklinkIndexEdit contains data for backlink index update after a hypha edit
|
2021-09-05 19:28:20 +00:00
|
|
|
type BacklinkIndexEdit struct {
|
2021-09-01 06:28:21 +00:00
|
|
|
Name string
|
|
|
|
OldLinks []string
|
|
|
|
NewLinks []string
|
|
|
|
}
|
|
|
|
|
2021-10-01 17:12:16 +00:00
|
|
|
// Apply changes backlink index respective to the operation data
|
2021-09-05 19:28:20 +00:00
|
|
|
func (op BacklinkIndexEdit) Apply() {
|
2021-09-01 06:28:21 +00:00
|
|
|
oldLinks := toLinkSet(op.OldLinks)
|
|
|
|
newLinks := toLinkSet(op.NewLinks)
|
|
|
|
for link := range oldLinks {
|
|
|
|
if _, exists := newLinks[link]; !exists {
|
|
|
|
delete(backlinkIndex[link], op.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for link := range newLinks {
|
|
|
|
if _, exists := oldLinks[link]; !exists {
|
|
|
|
if _, exists := backlinkIndex[link]; !exists {
|
|
|
|
backlinkIndex[link] = make(linkSet)
|
|
|
|
}
|
|
|
|
backlinkIndex[link][op.Name] = struct{}{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-01 17:12:16 +00:00
|
|
|
// BacklinkIndexDeletion contains data for backlink index update after a hypha deletion
|
2021-09-05 19:28:20 +00:00
|
|
|
type BacklinkIndexDeletion struct {
|
2021-09-01 06:28:21 +00:00
|
|
|
Name string
|
|
|
|
Links []string
|
|
|
|
}
|
|
|
|
|
2021-10-01 17:12:16 +00:00
|
|
|
// Apply changes backlink index respective to the operation data
|
2021-09-05 19:28:20 +00:00
|
|
|
func (op BacklinkIndexDeletion) Apply() {
|
2021-09-01 06:28:21 +00:00
|
|
|
for _, link := range op.Links {
|
|
|
|
if lSet, exists := backlinkIndex[link]; exists {
|
|
|
|
delete(lSet, op.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-01 17:12:16 +00:00
|
|
|
// BacklinkIndexRenaming contains data for backlink index update after a hypha renaming
|
2021-09-05 19:28:20 +00:00
|
|
|
type BacklinkIndexRenaming struct {
|
2021-09-01 06:28:21 +00:00
|
|
|
OldName string
|
|
|
|
NewName string
|
|
|
|
Links []string
|
|
|
|
}
|
|
|
|
|
2021-10-01 17:12:16 +00:00
|
|
|
// Apply changes backlink index respective to the operation data
|
2021-09-05 19:28:20 +00:00
|
|
|
func (op BacklinkIndexRenaming) Apply() {
|
2021-09-01 06:28:21 +00:00
|
|
|
for _, link := range op.Links {
|
|
|
|
if lSet, exists := backlinkIndex[link]; exists {
|
|
|
|
delete(lSet, op.OldName)
|
|
|
|
backlinkIndex[link][op.NewName] = struct{}{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-31 17:27:52 +00:00
|
|
|
var backlinkIndex = make(map[string]linkSet)
|
2021-09-05 19:28:20 +00:00
|
|
|
var backlinkConveyor = make(chan BacklinkIndexOperation, 64)
|
|
|
|
|
2021-09-01 06:28:21 +00:00
|
|
|
// I hope, the buffer size is enough -- chekoopa
|
2021-09-05 19:28:20 +00:00
|
|
|
// Do we really need the buffer though? Dunno -- bouncepaw
|
2021-08-31 17:27:52 +00:00
|
|
|
|
|
|
|
// IndexBacklinks traverses all text hyphae, extracts links from them and forms an initial index
|
|
|
|
func IndexBacklinks() {
|
|
|
|
// It is safe to ignore the mutex, because there is only one worker.
|
|
|
|
src := FilterTextHyphae(YieldExistingHyphae())
|
|
|
|
for h := range src {
|
2021-09-05 19:28:20 +00:00
|
|
|
foundLinks := extractHyphaLinksFromContent(h.Name, fetchText(h))
|
|
|
|
for _, link := range foundLinks {
|
2021-08-31 19:28:12 +00:00
|
|
|
if _, exists := backlinkIndex[link]; !exists {
|
|
|
|
backlinkIndex[link] = make(linkSet)
|
|
|
|
}
|
|
|
|
backlinkIndex[link][h.Name] = struct{}{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-01 06:28:21 +00:00
|
|
|
// RunBacklinksConveyor runs an index operation processing loop
|
|
|
|
func RunBacklinksConveyor() {
|
|
|
|
// It is supposed to run as a goroutine for all the time. So, don't blame the infinite loop.
|
|
|
|
defer close(backlinkConveyor)
|
|
|
|
for {
|
|
|
|
(<-backlinkConveyor).Apply()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-05 19:28:20 +00:00
|
|
|
// BacklinksCount returns the amount of backlinks to the hypha.
|
2021-08-31 19:39:33 +00:00
|
|
|
func BacklinksCount(h *Hypha) int {
|
|
|
|
if _, exists := backlinkIndex[h.Name]; exists {
|
|
|
|
return len(backlinkIndex[h.Name])
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2021-09-01 06:28:21 +00:00
|
|
|
// BacklinksOnEdit is a creation/editing hook for backlinks index
|
2021-08-31 19:28:12 +00:00
|
|
|
func BacklinksOnEdit(h *Hypha, oldText string) {
|
2021-09-05 19:28:20 +00:00
|
|
|
oldLinks := extractHyphaLinksFromContent(h.Name, oldText)
|
|
|
|
newLinks := extractHyphaLinks(h)
|
|
|
|
backlinkConveyor <- BacklinkIndexEdit{h.Name, oldLinks, newLinks}
|
2021-08-31 19:28:12 +00:00
|
|
|
}
|
|
|
|
|
2021-09-01 06:28:21 +00:00
|
|
|
// BacklinksOnDelete is a deletion hook for backlinks index
|
2021-08-31 19:28:12 +00:00
|
|
|
func BacklinksOnDelete(h *Hypha, oldText string) {
|
2021-09-05 19:28:20 +00:00
|
|
|
oldLinks := extractHyphaLinksFromContent(h.Name, oldText)
|
|
|
|
backlinkConveyor <- BacklinkIndexDeletion{h.Name, oldLinks}
|
2021-08-31 19:28:12 +00:00
|
|
|
}
|
|
|
|
|
2021-09-01 06:28:21 +00:00
|
|
|
// BacklinksOnRename is a renaming hook for backlinks index
|
2021-08-31 19:28:12 +00:00
|
|
|
func BacklinksOnRename(h *Hypha, oldName string) {
|
2021-09-05 19:28:20 +00:00
|
|
|
actualLinks := extractHyphaLinks(h)
|
|
|
|
backlinkConveyor <- BacklinkIndexRenaming{oldName, h.Name, actualLinks}
|
2021-08-31 17:27:52 +00:00
|
|
|
}
|
|
|
|
|
2021-08-31 19:28:12 +00:00
|
|
|
// YieldHyphaBacklinks gets backlinks for a desired hypha, sorts and iterates over them
|
2021-08-31 17:27:52 +00:00
|
|
|
func YieldHyphaBacklinks(query string) <-chan string {
|
|
|
|
hyphaName := util.CanonicalName(query)
|
|
|
|
out := make(chan string)
|
|
|
|
sorted := PathographicSort(out)
|
|
|
|
go func() {
|
2021-09-05 19:28:20 +00:00
|
|
|
backlinks, exists := backlinkIndex[hyphaName]
|
2021-08-31 19:28:12 +00:00
|
|
|
if exists {
|
2021-09-05 19:28:20 +00:00
|
|
|
for link := range backlinks {
|
2021-08-31 19:28:12 +00:00
|
|
|
out <- link
|
|
|
|
}
|
2021-08-31 17:27:52 +00:00
|
|
|
}
|
|
|
|
close(out)
|
|
|
|
}()
|
|
|
|
return sorted
|
|
|
|
}
|
|
|
|
|
2021-09-05 19:28:20 +00:00
|
|
|
// extractHyphaLinks extracts hypha links from a desired hypha
|
|
|
|
func extractHyphaLinks(h *Hypha) []string {
|
|
|
|
return extractHyphaLinksFromContent(h.Name, fetchText(h))
|
2021-08-31 17:27:52 +00:00
|
|
|
}
|
|
|
|
|
2021-09-05 19:28:20 +00:00
|
|
|
// extractHyphaLinksFromContent extracts local hypha links from the provided text.
|
|
|
|
func extractHyphaLinksFromContent(hyphaName string, contents string) []string {
|
2021-08-31 17:27:52 +00:00
|
|
|
ctx, _ := mycocontext.ContextFromStringInput(hyphaName, contents)
|
2021-09-12 08:58:03 +00:00
|
|
|
linkVisitor, getLinks := tools.LinkVisitor(ctx)
|
2021-09-05 19:28:20 +00:00
|
|
|
// Ignore the result of BlockTree because we call it for linkVisitor.
|
|
|
|
_ = mycomarkup.BlockTree(ctx, linkVisitor)
|
2021-08-31 17:27:52 +00:00
|
|
|
foundLinks := getLinks()
|
|
|
|
var result []string
|
|
|
|
for _, link := range foundLinks {
|
|
|
|
if link.OfKind(links.LinkLocalHypha) {
|
|
|
|
result = append(result, link.TargetHypha())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|