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:
parent
f45758cb0e
commit
fbf94975aa
@ -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; }
|
||||||
`)
|
`)
|
||||||
|
@ -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; }
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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.
|
||||||
|
@ -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")
|
||||||
|
19
util/util.go
19
util/util.go
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user