mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2025-01-07 02:10:26 +00:00
Backlinks: Make less things exported, better wording
This commit is contained in:
parent
1f36af66a5
commit
b453d71364
@ -2,16 +2,64 @@
|
|||||||
package backlinks
|
package backlinks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycomarkup/v3"
|
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
"github.com/bouncepaw/mycomarkup/v3/links"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
"github.com/bouncepaw/mycomarkup/v3/mycocontext"
|
|
||||||
"github.com/bouncepaw/mycomarkup/v3/tools"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// YieldHyphaBacklinks gets backlinks for the desired hypha, sorts and yields them one by one.
|
||||||
|
func YieldHyphaBacklinks(hyphaName string) <-chan string {
|
||||||
|
hyphaName = util.CanonicalName(hyphaName)
|
||||||
|
out := make(chan string)
|
||||||
|
sorted := hyphae.PathographicSort(out)
|
||||||
|
go func() {
|
||||||
|
backlinks, exists := backlinkIndex[hyphaName]
|
||||||
|
if exists {
|
||||||
|
for link := range backlinks {
|
||||||
|
out <- link
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(out)
|
||||||
|
}()
|
||||||
|
return sorted
|
||||||
|
}
|
||||||
|
|
||||||
|
var backlinkConveyor = make(chan backlinkIndexOperation) // No need to buffer because these operations are rare.
|
||||||
|
|
||||||
|
// RunBacklinksConveyor runs an index operation processing loop. Call it somewhere in main.
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var backlinkIndex = make(map[string]linkSet)
|
||||||
|
|
||||||
|
// IndexBacklinks traverses all text hyphae, extracts links from them and forms an initial index. Call it when indexing and reindexing hyphae.
|
||||||
|
func IndexBacklinks() {
|
||||||
|
// It is safe to ignore the mutex, because there is only one worker.
|
||||||
|
for h := range hyphae.FilterTextHyphae(hyphae.YieldExistingHyphae()) {
|
||||||
|
foundLinks := extractHyphaLinksFromContent(h.Name, fetchText(h))
|
||||||
|
for _, link := range foundLinks {
|
||||||
|
if _, exists := backlinkIndex[link]; !exists {
|
||||||
|
backlinkIndex[link] = make(linkSet)
|
||||||
|
}
|
||||||
|
backlinkIndex[link][h.Name] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BacklinksCount returns the amount of backlinks to the hypha.
|
||||||
|
func BacklinksCount(h *hyphae.Hypha) int {
|
||||||
|
if _, exists := backlinkIndex[h.Name]; exists {
|
||||||
|
return len(backlinkIndex[h.Name])
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
// 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{}
|
||||||
|
|
||||||
@ -41,18 +89,18 @@ type backlinkIndexOperation interface {
|
|||||||
|
|
||||||
// backlinkIndexEdit contains data for backlink index update after a hypha edit
|
// backlinkIndexEdit contains data for backlink index update after a hypha edit
|
||||||
type backlinkIndexEdit struct {
|
type backlinkIndexEdit struct {
|
||||||
Name string
|
name string
|
||||||
OldLinks []string
|
oldLinks []string
|
||||||
NewLinks []string
|
newLinks []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply changes backlink index respective to the operation data
|
// apply changes backlink index respective to the operation data
|
||||||
func (op backlinkIndexEdit) apply() {
|
func (op backlinkIndexEdit) apply() {
|
||||||
oldLinks := toLinkSet(op.OldLinks)
|
oldLinks := toLinkSet(op.oldLinks)
|
||||||
newLinks := toLinkSet(op.NewLinks)
|
newLinks := toLinkSet(op.newLinks)
|
||||||
for link := range oldLinks {
|
for link := range oldLinks {
|
||||||
if _, exists := newLinks[link]; !exists {
|
if _, exists := newLinks[link]; !exists {
|
||||||
delete(backlinkIndex[link], op.Name)
|
delete(backlinkIndex[link], op.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for link := range newLinks {
|
for link := range newLinks {
|
||||||
@ -60,134 +108,39 @@ func (op backlinkIndexEdit) apply() {
|
|||||||
if _, exists := backlinkIndex[link]; !exists {
|
if _, exists := backlinkIndex[link]; !exists {
|
||||||
backlinkIndex[link] = make(linkSet)
|
backlinkIndex[link] = make(linkSet)
|
||||||
}
|
}
|
||||||
backlinkIndex[link][op.Name] = struct{}{}
|
backlinkIndex[link][op.name] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// backlinkIndexDeletion contains data for backlink index update after a hypha deletion
|
// backlinkIndexDeletion contains data for backlink index update after a hypha deletion
|
||||||
type backlinkIndexDeletion struct {
|
type backlinkIndexDeletion struct {
|
||||||
Name string
|
name string
|
||||||
Links []string
|
links []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply changes backlink index respective to the operation data
|
// apply changes backlink index respective to the operation data
|
||||||
func (op backlinkIndexDeletion) apply() {
|
func (op backlinkIndexDeletion) apply() {
|
||||||
for _, link := range op.Links {
|
for _, link := range op.links {
|
||||||
if lSet, exists := backlinkIndex[link]; exists {
|
if lSet, exists := backlinkIndex[link]; exists {
|
||||||
delete(lSet, op.Name)
|
delete(lSet, op.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// backlinkIndexRenaming contains data for backlink index update after a hypha renaming
|
// backlinkIndexRenaming contains data for backlink index update after a hypha renaming
|
||||||
type backlinkIndexRenaming struct {
|
type backlinkIndexRenaming struct {
|
||||||
OldName string
|
oldName string
|
||||||
NewName string
|
newName string
|
||||||
Links []string
|
links []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply changes backlink index respective to the operation data
|
// Apply changes backlink index respective to the operation data
|
||||||
func (op backlinkIndexRenaming) apply() {
|
func (op backlinkIndexRenaming) apply() {
|
||||||
for _, link := range op.Links {
|
for _, link := range op.links {
|
||||||
if lSet, exists := backlinkIndex[link]; exists {
|
if lSet, exists := backlinkIndex[link]; exists {
|
||||||
delete(lSet, op.OldName)
|
delete(lSet, op.oldName)
|
||||||
backlinkIndex[link][op.NewName] = struct{}{}
|
backlinkIndex[link][op.newName] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var backlinkIndex = make(map[string]linkSet)
|
|
||||||
var backlinkConveyor = make(chan backlinkIndexOperation, 64)
|
|
||||||
|
|
||||||
// I hope, the buffer size is enough -- chekoopa
|
|
||||||
// Do we really need the buffer though? Dunno -- bouncepaw
|
|
||||||
|
|
||||||
// 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 := hyphae.FilterTextHyphae(hyphae.YieldExistingHyphae())
|
|
||||||
for h := range src {
|
|
||||||
foundLinks := extractHyphaLinksFromContent(h.Name, fetchText(h))
|
|
||||||
for _, link := range foundLinks {
|
|
||||||
if _, exists := backlinkIndex[link]; !exists {
|
|
||||||
backlinkIndex[link] = make(linkSet)
|
|
||||||
}
|
|
||||||
backlinkIndex[link][h.Name] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BacklinksCount returns the amount of backlinks to the hypha.
|
|
||||||
func BacklinksCount(h *hyphae.Hypha) int {
|
|
||||||
if _, exists := backlinkIndex[h.Name]; exists {
|
|
||||||
return len(backlinkIndex[h.Name])
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// BacklinksOnEdit is a creation/editing hook for backlinks index
|
|
||||||
func BacklinksOnEdit(h *hyphae.Hypha, oldText string) {
|
|
||||||
oldLinks := extractHyphaLinksFromContent(h.Name, oldText)
|
|
||||||
newLinks := extractHyphaLinks(h)
|
|
||||||
backlinkConveyor <- backlinkIndexEdit{h.Name, oldLinks, newLinks}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BacklinksOnDelete is a deletion hook for backlinks index
|
|
||||||
func BacklinksOnDelete(h *hyphae.Hypha, oldText string) {
|
|
||||||
oldLinks := extractHyphaLinksFromContent(h.Name, oldText)
|
|
||||||
backlinkConveyor <- backlinkIndexDeletion{h.Name, oldLinks}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BacklinksOnRename is a renaming hook for backlinks index
|
|
||||||
func BacklinksOnRename(h *hyphae.Hypha, oldName string) {
|
|
||||||
actualLinks := extractHyphaLinks(h)
|
|
||||||
backlinkConveyor <- backlinkIndexRenaming{oldName, h.Name, actualLinks}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 := hyphae.PathographicSort(out)
|
|
||||||
go func() {
|
|
||||||
backlinks, exists := backlinkIndex[hyphaName]
|
|
||||||
if exists {
|
|
||||||
for link := range backlinks {
|
|
||||||
out <- link
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close(out)
|
|
||||||
}()
|
|
||||||
return sorted
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractHyphaLinks extracts hypha links from a desired hypha
|
|
||||||
func extractHyphaLinks(h *hyphae.Hypha) []string {
|
|
||||||
return extractHyphaLinksFromContent(h.Name, fetchText(h))
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractHyphaLinksFromContent extracts local hypha links from the provided text.
|
|
||||||
func extractHyphaLinksFromContent(hyphaName string, contents string) []string {
|
|
||||||
ctx, _ := mycocontext.ContextFromStringInput(hyphaName, contents)
|
|
||||||
linkVisitor, getLinks := tools.LinkVisitor(ctx)
|
|
||||||
// Ignore the result of BlockTree because we call it for linkVisitor.
|
|
||||||
_ = mycomarkup.BlockTree(ctx, linkVisitor)
|
|
||||||
foundLinks := getLinks()
|
|
||||||
var result []string
|
|
||||||
for _, link := range foundLinks {
|
|
||||||
if link.OfKind(links.LinkLocalHypha) {
|
|
||||||
result = append(result, link.TargetHypha())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
49
hyphae/backlinks/hooks.go
Normal file
49
hyphae/backlinks/hooks.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package backlinks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/bouncepaw/mycomarkup/v3"
|
||||||
|
"github.com/bouncepaw/mycomarkup/v3/links"
|
||||||
|
"github.com/bouncepaw/mycomarkup/v3/mycocontext"
|
||||||
|
"github.com/bouncepaw/mycomarkup/v3/tools"
|
||||||
|
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UpdateBacklinksAfterEdit is a creation/editing hook for backlinks index
|
||||||
|
func UpdateBacklinksAfterEdit(h *hyphae.Hypha, oldText string) {
|
||||||
|
oldLinks := extractHyphaLinksFromContent(h.Name, oldText)
|
||||||
|
newLinks := extractHyphaLinks(h)
|
||||||
|
backlinkConveyor <- backlinkIndexEdit{h.Name, oldLinks, newLinks}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateBacklinksAfterDelete is a deletion hook for backlinks index
|
||||||
|
func UpdateBacklinksAfterDelete(h *hyphae.Hypha, oldText string) {
|
||||||
|
oldLinks := extractHyphaLinksFromContent(h.Name, oldText)
|
||||||
|
backlinkConveyor <- backlinkIndexDeletion{h.Name, oldLinks}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateBacklinksAfterRename is a renaming hook for backlinks index
|
||||||
|
func UpdateBacklinksAfterRename(h *hyphae.Hypha, oldName string) {
|
||||||
|
actualLinks := extractHyphaLinks(h)
|
||||||
|
backlinkConveyor <- backlinkIndexRenaming{oldName, h.Name, actualLinks}
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractHyphaLinks extracts hypha links from a desired hypha
|
||||||
|
func extractHyphaLinks(h *hyphae.Hypha) []string {
|
||||||
|
return extractHyphaLinksFromContent(h.Name, fetchText(h))
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractHyphaLinksFromContent extracts local hypha links from the provided text.
|
||||||
|
func extractHyphaLinksFromContent(hyphaName string, contents string) []string {
|
||||||
|
ctx, _ := mycocontext.ContextFromStringInput(hyphaName, contents)
|
||||||
|
linkVisitor, getLinks := tools.LinkVisitor(ctx)
|
||||||
|
// Ignore the result of BlockTree because we call it for linkVisitor.
|
||||||
|
_ = mycomarkup.BlockTree(ctx, linkVisitor)
|
||||||
|
foundLinks := getLinks()
|
||||||
|
var result []string
|
||||||
|
for _, link := range foundLinks {
|
||||||
|
if link.OfKind(links.LinkLocalHypha) {
|
||||||
|
result = append(result, link.TargetHypha())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
@ -26,7 +26,7 @@ func DeleteHypha(u *user.User, h *hyphae.Hypha, lc *l18n.Localizer) (hop *histor
|
|||||||
WithUser(u).
|
WithUser(u).
|
||||||
Apply()
|
Apply()
|
||||||
if !hop.HasErrors() {
|
if !hop.HasErrors() {
|
||||||
backlinks.BacklinksOnDelete(h, originalText)
|
backlinks.UpdateBacklinksAfterDelete(h, originalText)
|
||||||
h.Delete()
|
h.Delete()
|
||||||
}
|
}
|
||||||
return hop, ""
|
return hop, ""
|
||||||
|
@ -75,7 +75,7 @@ func RenameHypha(h *hyphae.Hypha, newHypha *hyphae.Hypha, recursive bool, u *use
|
|||||||
h.TextPath = replaceName(h.TextPath)
|
h.TextPath = replaceName(h.TextPath)
|
||||||
h.BinaryPath = replaceName(h.BinaryPath)
|
h.BinaryPath = replaceName(h.BinaryPath)
|
||||||
h.Unlock()
|
h.Unlock()
|
||||||
backlinks.BacklinksOnRename(h, oldName)
|
backlinks.UpdateBacklinksAfterRename(h, oldName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return hop, ""
|
return hop, ""
|
||||||
|
@ -106,7 +106,7 @@ func uploadHelp(h *hyphae.Hypha, hop *history.Op, ext string, data []byte, u *us
|
|||||||
}
|
}
|
||||||
*originalFullPath = fullPath
|
*originalFullPath = fullPath
|
||||||
if hop.Type == history.TypeEditText {
|
if hop.Type == history.TypeEditText {
|
||||||
backlinks.BacklinksOnEdit(h, originalText)
|
backlinks.UpdateBacklinksAfterEdit(h, originalText)
|
||||||
}
|
}
|
||||||
return hop.WithFiles(fullPath).WithUser(u).Apply(), ""
|
return hop.WithFiles(fullPath).WithUser(u).Apply(), ""
|
||||||
}
|
}
|
||||||
|
@ -122,17 +122,18 @@ It outputs a poorly formatted JSON, but it works and is valid.
|
|||||||
}
|
}
|
||||||
{% endfunc %}
|
{% endfunc %}
|
||||||
|
|
||||||
{% func BacklinksHTML(query string, generator func(string) <-chan string, lc *l18n.Localizer) %}
|
{% func BacklinksHTML(hyphaName string, generator func(string) <-chan string, lc *l18n.Localizer) %}
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<main class="main-width backlinks">
|
<main class="main-width backlinks">
|
||||||
<h1>{%s lc.Get("ui.backlinks_query", &l18n.Replacements{"query": query})%}</h1>
|
<h1>{%s lc.Get("ui.backlinks_query", &l18n.Replacements{"query": hyphaName})%}</h1>
|
||||||
<p>{%s lc.Get("ui.backlinks_desc")%}</p>
|
<p>{%s lc.Get("ui.backlinks_desc")%}</p>
|
||||||
<ul class="backlinks__list">
|
<ul class="backlinks__list">
|
||||||
{% for hyphaName := range generator(query) %}
|
{% for hyphaName := range generator(hyphaName) %}
|
||||||
<li class="backlinks__entry">
|
<li class="backlinks__entry">
|
||||||
<a class="backlinks__link wikilink" href="/hypha/{%s hyphaName %}">{%s util.BeautifulName(hyphaName) %}</a>
|
<a class="backlinks__link wikilink" href="/hypha/{%s hyphaName %}">{%s util.BeautifulName(hyphaName) %}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
{% endfunc %}
|
{% endfunc %}
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user