mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2024-12-13 05:50:27 +00:00
Implement backlinks updating
This commit is contained in:
parent
820de6a0aa
commit
7c41403eee
@ -15,6 +15,25 @@ import (
|
||||
// Using set here seems like the most appropriate solution
|
||||
type linkSet map[string]struct{}
|
||||
|
||||
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 ""
|
||||
}
|
||||
|
||||
var backlinkIndex = make(map[string]linkSet)
|
||||
var backlinkIndexMutex = sync.Mutex{}
|
||||
|
||||
@ -23,41 +42,85 @@ func IndexBacklinks() {
|
||||
// It is safe to ignore the mutex, because there is only one worker.
|
||||
src := FilterTextHyphae(YieldExistingHyphae())
|
||||
for h := range src {
|
||||
fileContentsT, errT := os.ReadFile(h.TextPath)
|
||||
if errT == nil {
|
||||
links := ExtractHyphaLinksFromContent(h.Name, string(fileContentsT))
|
||||
for _, link := range links {
|
||||
if _, exists := backlinkIndex[link]; !exists {
|
||||
backlinkIndex[link] = make(linkSet)
|
||||
}
|
||||
backlinkIndex[link][h.Name] = struct{}{}
|
||||
links := ExtractHyphaLinksFromContent(h.Name, fetchText(h))
|
||||
for _, link := range links {
|
||||
if _, exists := backlinkIndex[link]; !exists {
|
||||
backlinkIndex[link] = make(linkSet)
|
||||
}
|
||||
backlinkIndex[link][h.Name] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BacklinksOnEdit(h *Hypha, oldText string) {
|
||||
backlinkIndexMutex.Lock()
|
||||
newLinks := toLinkSet(ExtractHyphaLinks(h))
|
||||
oldLinks := toLinkSet(ExtractHyphaLinksFromContent(h.Name, oldText))
|
||||
for link := range oldLinks {
|
||||
if _, exists := newLinks[link]; !exists {
|
||||
delete(backlinkIndex[link], h.Name)
|
||||
}
|
||||
}
|
||||
for link := range newLinks {
|
||||
if _, exists := oldLinks[link]; !exists {
|
||||
if _, exists := backlinkIndex[link]; !exists {
|
||||
backlinkIndex[link] = make(linkSet)
|
||||
}
|
||||
backlinkIndex[link][h.Name] = struct{}{}
|
||||
}
|
||||
}
|
||||
backlinkIndexMutex.Unlock()
|
||||
}
|
||||
|
||||
func BacklinksOnDelete(h *Hypha, oldText string) {
|
||||
backlinkIndexMutex.Lock()
|
||||
oldLinks := ExtractHyphaLinksFromContent(h.Name, oldText)
|
||||
for _, link := range oldLinks {
|
||||
if lSet, exists := backlinkIndex[link]; exists {
|
||||
delete(lSet, h.Name)
|
||||
}
|
||||
}
|
||||
backlinkIndexMutex.Unlock()
|
||||
}
|
||||
|
||||
func BacklinksOnRename(h *Hypha, oldName string) {
|
||||
backlinkIndexMutex.Lock()
|
||||
actualLinks := ExtractHyphaLinks(h)
|
||||
for _, link := range actualLinks {
|
||||
if lSet, exists := backlinkIndex[link]; exists {
|
||||
delete(lSet, oldName)
|
||||
backlinkIndex[link][h.Name] = struct{}{}
|
||||
}
|
||||
}
|
||||
backlinkIndexMutex.Unlock()
|
||||
}
|
||||
|
||||
// YieldHyphaBacklinks gets backlinks for a desired hypha, sorts and iterates over them
|
||||
func YieldHyphaBacklinks(query string) <-chan string {
|
||||
hyphaName := util.CanonicalName(query)
|
||||
out := make(chan string)
|
||||
sorted := PathographicSort(out)
|
||||
go func() {
|
||||
links := backlinkIndex[hyphaName]
|
||||
for link := range links {
|
||||
out <- link
|
||||
links, exists := backlinkIndex[hyphaName]
|
||||
if exists {
|
||||
for link := range links {
|
||||
out <- link
|
||||
}
|
||||
}
|
||||
close(out)
|
||||
}()
|
||||
return sorted
|
||||
}
|
||||
|
||||
// YieldHyphaLinks extracts hypha links from a desired hypha and iterates over them
|
||||
// YieldHyphaLinks extracts hypha links from a desired hypha, sorts and iterates over them
|
||||
func YieldHyphaLinks(query string) <-chan string {
|
||||
// That is merely a debug function, but it could be useful.
|
||||
// Should we extract them into link-specific subfile? -- chekoopa
|
||||
hyphaName := util.CanonicalName(query)
|
||||
out := make(chan string)
|
||||
go func() {
|
||||
links := ExtractHyphaLinks(hyphaName)
|
||||
var h = ByName(hyphaName)
|
||||
links := ExtractHyphaLinks(h)
|
||||
for _, link := range links {
|
||||
out <- link
|
||||
}
|
||||
@ -67,15 +130,8 @@ func YieldHyphaLinks(query string) <-chan string {
|
||||
}
|
||||
|
||||
// ExtractHyphaLinks extracts hypha links from a desired hypha
|
||||
func ExtractHyphaLinks(hyphaName string) []string {
|
||||
var h = ByName(hyphaName)
|
||||
if h.Exists {
|
||||
fileContentsT, errT := os.ReadFile(h.TextPath)
|
||||
if errT == nil {
|
||||
return ExtractHyphaLinksFromContent(hyphaName, string(fileContentsT))
|
||||
}
|
||||
}
|
||||
return make([]string, 0)
|
||||
func ExtractHyphaLinks(h *Hypha) []string {
|
||||
return ExtractHyphaLinksFromContent(h.Name, fetchText(h))
|
||||
}
|
||||
|
||||
// ExtractHyphaLinksFromContent extracts hypha links from a provided text
|
||||
|
@ -17,12 +17,14 @@ func DeleteHypha(u *user.User, h *hyphae.Hypha) (hop *history.HistoryOp, errtitl
|
||||
return hop, errtitle
|
||||
}
|
||||
|
||||
originalText, _ := FetchTextPart(h)
|
||||
hop.
|
||||
WithFilesRemoved(h.TextPath, h.BinaryPath).
|
||||
WithMsg(fmt.Sprintf("Delete ‘%s’", h.Name)).
|
||||
WithUser(u).
|
||||
Apply()
|
||||
if !hop.HasErrors() {
|
||||
hyphae.BacklinksOnDelete(h, originalText)
|
||||
h.Delete()
|
||||
}
|
||||
return hop, ""
|
||||
|
@ -67,11 +67,13 @@ func RenameHypha(h *hyphae.Hypha, newHypha *hyphae.Hypha, recursive bool, u *use
|
||||
Apply()
|
||||
if len(hop.Errs) == 0 {
|
||||
for _, h := range hyphaeToRename {
|
||||
oldName := h.Name
|
||||
h.RenameTo(replaceName(h.Name))
|
||||
h.Lock()
|
||||
h.TextPath = replaceName(h.TextPath)
|
||||
h.BinaryPath = replaceName(h.BinaryPath)
|
||||
h.Unlock()
|
||||
hyphae.BacklinksOnRename(h, oldName)
|
||||
}
|
||||
}
|
||||
return hop, ""
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
// YieldHyphaNamesContainingString picks hyphae with have a string in their title, sorts and iterates over them.
|
||||
func YieldHyphaNamesContainingString(query string) <-chan string {
|
||||
query = util.CanonicalName(query)
|
||||
out := make(chan string)
|
||||
|
@ -66,6 +66,7 @@ func uploadHelp(h *hyphae.Hypha, hop *history.HistoryOp, ext string, data []byte
|
||||
var (
|
||||
fullPath = filepath.Join(files.HyphaeDir(), h.Name+ext)
|
||||
originalFullPath = &h.TextPath
|
||||
originalText = "" // for backlink update
|
||||
)
|
||||
// Reject if the path is outside the hyphae dir
|
||||
if !strings.HasPrefix(fullPath, files.HyphaeDir()) {
|
||||
@ -80,6 +81,10 @@ func uploadHelp(h *hyphae.Hypha, hop *history.HistoryOp, ext string, data []byte
|
||||
return hop.WithErrAbort(err), err.Error()
|
||||
}
|
||||
|
||||
if hop.Type == history.TypeEditText {
|
||||
originalText, _ = FetchTextPart(h)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(fullPath, data, 0666); err != nil {
|
||||
return hop.WithErrAbort(err), err.Error()
|
||||
}
|
||||
@ -96,5 +101,8 @@ func uploadHelp(h *hyphae.Hypha, hop *history.HistoryOp, ext string, data []byte
|
||||
return hop.Abort(), "No changes"
|
||||
}
|
||||
*originalFullPath = fullPath
|
||||
if hop.Type == history.TypeEditText {
|
||||
hyphae.BacklinksOnEdit(h, originalText)
|
||||
}
|
||||
return hop.WithFiles(fullPath).WithUser(u).Apply(), ""
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user