1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2024-12-12 05:20:26 +00:00

Add anchor links

This commit is contained in:
bouncepaw 2021-03-02 21:36:57 +05:00
parent f45758cb0e
commit fbf94975aa
7 changed files with 72 additions and 15 deletions

View File

@ -134,6 +134,9 @@ blockquote { margin-left: 0; padding-left: 1rem; }
article { overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; line-height: 150%; } article { overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; line-height: 150%; }
main h1, main h2, main h3, main h4, main h5, main h6 { margin: 1.5rem 0 0 0; } main h1, main h2, main h3, main h4, main h5, main h6 { margin: 1.5rem 0 0 0; }
.heading__link { text-decoration: none; display: inline-block; }
.heading__link::after { width: 1rem; content: "§"; color: transparent; }
.heading__link:hover::after, .heading__link:active::after { color: #999; }
article p { margin: .5rem 0; } article p { margin: .5rem 0; }
article ul, ol { padding-left: 1.5rem; margin: .5rem 0; } article ul, ol { padding-left: 1.5rem; margin: .5rem 0; }
article code { padding: .1rem .3rem; border-radius: .25rem; font-size: 90%; } article code { padding: .1rem .3rem; border-radius: .25rem; font-size: 90%; }
@ -291,12 +294,7 @@ mark { background: rgba(130, 80, 30, 5); color: inherit; }
@media screen and (max-width: 800px) { @media screen and (max-width: 800px) {
.hypha-tabs { background-color: #232323; } .hypha-tabs { background-color: #232323; }
} }
@media screen and (min-width: 801px) {
/* .hypha-tabs__tab { border: 1px #ddd solid; } */
/* .hypha-tabs__tab_active { border-bottom: 1px white solid; } */
} }
}
.backlinks { display: none; } .backlinks { display: none; }
`) `)

View File

@ -109,6 +109,9 @@ blockquote { margin-left: 0; padding-left: 1rem; }
article { overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; line-height: 150%; } article { overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; line-height: 150%; }
main h1, main h2, main h3, main h4, main h5, main h6 { margin: 1.5rem 0 0 0; } main h1, main h2, main h3, main h4, main h5, main h6 { margin: 1.5rem 0 0 0; }
.heading__link { text-decoration: none; display: inline-block; }
.heading__link::after { width: 1rem; content: "§"; color: transparent; }
.heading__link:hover::after, .heading__link:active::after { color: #999; }
article p { margin: .5rem 0; } article p { margin: .5rem 0; }
article ul, ol { padding-left: 1.5rem; margin: .5rem 0; } article ul, ol { padding-left: 1.5rem; margin: .5rem 0; }
article code { padding: .1rem .3rem; border-radius: .25rem; font-size: 90%; } article code { padding: .1rem .3rem; border-radius: .25rem; font-size: 90%; }
@ -266,11 +269,6 @@ mark { background: rgba(130, 80, 30, 5); color: inherit; }
@media screen and (max-width: 800px) { @media screen and (max-width: 800px) {
.hypha-tabs { background-color: #232323; } .hypha-tabs { background-color: #232323; }
} }
@media screen and (min-width: 801px) {
/* .hypha-tabs__tab { border: 1px #ddd solid; } */
/* .hypha-tabs__tab_active { border-bottom: 1px white solid; } */
} }
}
.backlinks { display: none; } .backlinks { display: none; }

View File

@ -88,6 +88,10 @@ func (h *Hypha) Delete() {
DecrementCount() DecrementCount()
byNamesMutex.Unlock() byNamesMutex.Unlock()
h.Unlock() h.Unlock()
for _, outlinkHypha := range h.OutLinks {
outlinkHypha.DropBackLink(h)
}
} }
func (h *Hypha) RenameTo(newName string) { func (h *Hypha) RenameTo(newName string) {
@ -113,7 +117,16 @@ func (h *Hypha) MergeIn(oh *Hypha) {
} }
} }
// Link related stuff: // ## Link related stuff
// Notes in pseudocode and whatnot:
// * (Reader h) does not mutate h => safe
// * (Rename h) reuses the same hypha object => safe
// * (Unattach h) and (Attach h) do not change (Backlinks h) => safe
// * (Delete h) does not change (Backlinks h), but changes (Outlinks h), removing h from them => make it safe
// * (Unattach h) and (Attach h) => h may start or stop existing => may change (Outlinks h) => make it safe
// * (Edit h) => h may start existing => may change (Backlinks h) => make it safe
// * (Edit h) may add or remove h to or from (Outlinks h) => make it safe
func (h *Hypha) AddOutLink(oh *Hypha) (added bool) { func (h *Hypha) AddOutLink(oh *Hypha) (added bool) {
h.Lock() h.Lock()
@ -140,3 +153,23 @@ func (h *Hypha) AddBackLink(bh *Hypha) (added bool) {
h.BackLinks = append(h.BackLinks, bh) h.BackLinks = append(h.BackLinks, bh)
return true return true
} }
func (h *Hypha) DropBackLink(bh *Hypha) {
h.Lock()
defer h.Unlock()
if len(h.BackLinks) <= 1 {
h.BackLinks = make([]*Hypha, 0)
return
}
lastBackLinkIndex := len(h.BackLinks)
for i, backlink := range h.BackLinks {
if backlink == bh {
if i != lastBackLinkIndex {
h.BackLinks[i] = h.BackLinks[lastBackLinkIndex]
}
h.BackLinks = h.BackLinks[:lastBackLinkIndex]
return
}
}
}

View File

@ -32,6 +32,8 @@ type Link struct {
Kind LinkType Kind LinkType
DestinationUnknown bool DestinationUnknown bool
// #...
Anchor string
Protocol string Protocol string
// How the link address looked originally in source text. // How the link address looked originally in source text.
SrcAddress string SrcAddress string
@ -66,7 +68,7 @@ func (l *Link) Href() string {
case LinkExternal, LinkLocalRoot: case LinkExternal, LinkLocalRoot:
return l.Address return l.Address
default: default:
return "/hypha/" + l.Address return "/hypha/" + l.Address + l.Anchor
} }
} }
@ -117,6 +119,10 @@ func From(address, display, hyphaName string) *Link {
case strings.HasPrefix(address, "../"): case strings.HasPrefix(address, "../"):
link.Kind = LinkLocalHypha link.Kind = LinkLocalHypha
link.Address = util.CanonicalName(path.Join(path.Dir(hyphaName), address[3:])) link.Address = util.CanonicalName(path.Join(path.Dir(hyphaName), address[3:]))
case strings.HasPrefix(address, "#"):
link.Kind = LinkLocalHypha
link.Address = util.CanonicalName(hyphaName)
link.Anchor = address
default: default:
link.Kind = LinkLocalHypha link.Kind = LinkLocalHypha
link.Address = util.CanonicalName(address) link.Address = util.CanonicalName(address)

View File

@ -4,6 +4,8 @@ import (
"fmt" "fmt"
"html" "html"
"strings" "strings"
"github.com/bouncepaw/mycorrhiza/util"
) )
// HyphaExists holds function that checks that a hypha is present. // HyphaExists holds function that checks that a hypha is present.
@ -83,7 +85,8 @@ func lineToAST(line string, state *GemLexerState, ast *[]Line) {
return strings.HasPrefix(line, token) return strings.HasPrefix(line, token)
} }
addHeading := func(i int) { addHeading := func(i int) {
addLine(fmt.Sprintf("<h%d id='%d'>%s</h%d>", i, state.id, ParagraphToHtml(state.name, line[i+1:]), i)) id := util.LettersNumbersOnly(line[i+1:])
addLine(fmt.Sprintf(`<h%d id='%d'>%s<a href="#%s" id="%s" class="heading__link"></a></h%d>`, i, state.id, ParagraphToHtml(state.name, line[i+1:]), id, id, i))
} }
// Beware! Usage of goto. Some may say it is considered evil but in this case it helped to make a better-structured code. // Beware! Usage of goto. Some may say it is considered evil but in this case it helped to make a better-structured code.

View File

@ -89,7 +89,7 @@ func renamingPairs(hyphaeToRename []*hyphae.Hypha, replaceName func(string) stri
renameMap := make(map[string]string) renameMap := make(map[string]string)
newNames := make([]string, len(hyphaeToRename)) newNames := make([]string, len(hyphaeToRename))
for _, h := range hyphaeToRename { for _, h := range hyphaeToRename {
h.RLock() h.Lock()
newNames = append(newNames, replaceName(h.Name)) newNames = append(newNames, replaceName(h.Name))
if h.TextPath != "" { if h.TextPath != "" {
renameMap[h.TextPath] = replaceName(h.TextPath) renameMap[h.TextPath] = replaceName(h.TextPath)
@ -97,7 +97,7 @@ func renamingPairs(hyphaeToRename []*hyphae.Hypha, replaceName func(string) stri
if h.BinaryPath != "" { if h.BinaryPath != "" {
renameMap[h.BinaryPath] = replaceName(h.BinaryPath) renameMap[h.BinaryPath] = replaceName(h.BinaryPath)
} }
h.RUnlock() h.Unlock()
} }
if firstFailure, ok := hyphae.AreFreeNames(newNames...); !ok { if firstFailure, ok := hyphae.AreFreeNames(newNames...); !ok {
return nil, errors.New("Hypha " + firstFailure + " already exists") return nil, errors.New("Hypha " + firstFailure + " already exists")

View File

@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"regexp" "regexp"
"strings" "strings"
"unicode"
) )
var ( var (
@ -22,6 +23,24 @@ var (
GeminiCertPath string GeminiCertPath string
) )
// LettersNumbersOnly keeps letters and numbers only in the given string.
func LettersNumbersOnly(s string) string {
var (
ret strings.Builder
usedUnderscore bool
)
for _, r := range s {
if unicode.IsLetter(r) || unicode.IsNumber(r) {
ret.WriteRune(r)
usedUnderscore = false
} else if !usedUnderscore {
ret.WriteRune('_')
usedUnderscore = true
}
}
return strings.Trim(ret.String(), "_")
}
// ShorterPath is used by handlerList to display shorter path to the files. It simply strips WikiDir. // ShorterPath is used by handlerList to display shorter path to the files. It simply strips WikiDir.
func ShorterPath(path string) string { func ShorterPath(path string) string {
if strings.HasPrefix(path, WikiDir) { if strings.HasPrefix(path, WikiDir) {