1
0
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:
Mikhail Chekan 2021-09-01 03:28:12 +08:00 committed by Timur Ismagilov
parent 820de6a0aa
commit 7c41403eee
5 changed files with 91 additions and 22 deletions

View File

@ -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

View File

@ -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, ""

View File

@ -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, ""

View File

@ -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)

View File

@ -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(), ""
}