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