From bee862563d8f1f02ef121ea257768abae7917940 Mon Sep 17 00:00:00 2001 From: bouncepaw Date: Mon, 4 Jan 2021 02:10:33 +0500 Subject: [PATCH] Auto-detect links --- hyphae/hypha.go | 21 +++++++++++++++++---- markup/paragraph.go | 26 +++++++++++++++++++++----- templates/asset.qtpl.go | 2 +- templates/default.css | 2 +- 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/hyphae/hypha.go b/hyphae/hypha.go index be82302..e129355 100644 --- a/hyphae/hypha.go +++ b/hyphae/hypha.go @@ -1,8 +1,6 @@ package hyphae import ( - "errors" - "github.com/hashicorp/go-memdb" ) @@ -15,8 +13,23 @@ type Hypha struct { BackLinks []string } -func AddHypha(h Hypha) error { - return errors.New("Not implemented") +// AddHypha adds a hypha named `name` with such `textPath` and `binaryPath`. Both paths can be empty. Does //not// check for hypha's existence beforehand. Count is handled. +func AddHypha(name, textPath, binaryPath string) { + txn := db.Txn(true) + txn.Insert("hyphae", + &Hypha{ + Name: name, + TextPath: textPath, + BinaryPath: binaryPath, + OutLinks: make([]string, 0), + BackLinks: make([]string, 0), + }) + txn.Commit() + IncrementCount() +} + +// DeleteHypha clears both paths and all out-links from the named hypha and marks it as non-existent. It does not actually delete it from the memdb. Count is handled. +func DeleteHypha(name string) { } // Create the DB schema diff --git a/markup/paragraph.go b/markup/paragraph.go index 88d5e43..9148501 100644 --- a/markup/paragraph.go +++ b/markup/paragraph.go @@ -5,6 +5,7 @@ import ( "fmt" "html" "strings" + "unicode" ) type spanTokenType int @@ -34,8 +35,10 @@ func tagFromState(stt spanTokenType, tagState map[spanTokenType]bool, tagName, o } } -func getLinkNode(input *bytes.Buffer, hyphaName string) string { - input.Next(2) +func getLinkNode(input *bytes.Buffer, hyphaName string, isBracketedLink bool) string { + if isBracketedLink { + input.Next(2) // drop those [[ + } var ( escaping = false addrBuf = bytes.Buffer{} @@ -47,11 +50,13 @@ func getLinkNode(input *bytes.Buffer, hyphaName string) string { if escaping { currBuf.WriteByte(b) escaping = false - } else if b == '|' && currBuf == &addrBuf { + } else if isBracketedLink && b == '|' && currBuf == &addrBuf { currBuf = &displayBuf - } else if b == ']' && bytes.HasPrefix(input.Bytes(), []byte{']'}) { + } else if isBracketedLink && b == ']' && bytes.HasPrefix(input.Bytes(), []byte{']'}) { input.Next(1) break + } else if !isBracketedLink && unicode.IsSpace(rune(b)) { + break } else { currBuf.WriteByte(b) } @@ -65,6 +70,12 @@ func getTextNode(input *bytes.Buffer) string { var ( textNodeBuffer = bytes.Buffer{} escaping = false + startsWith = func(t string) bool { + return bytes.HasPrefix(input.Bytes(), []byte(t)) + } + couldBeLinkStart = func() bool { + return startsWith("https://") || startsWith("http://") || startsWith("gemini://") || startsWith("gopher://") || startsWith("ftp://") + } ) // Always read the first byte in advance to avoid endless loops that kill computers (sad experience) if input.Len() != 0 { @@ -82,6 +93,9 @@ func getTextNode(input *bytes.Buffer) string { } else if strings.IndexByte("/*`^,![~", b) >= 0 { input.UnreadByte() break + } else if couldBeLinkStart() { + textNodeBuffer.WriteByte(b) + break } else { textNodeBuffer.WriteByte(b) } @@ -132,7 +146,9 @@ func ParagraphToHtml(hyphaName, input string) string { ret.WriteString(tagFromState(spanMark, tagState, "s", "~~")) p.Next(2) case startsWith("[["): - ret.WriteString(getLinkNode(p, hyphaName)) + ret.WriteString(getLinkNode(p, hyphaName, true)) + case startsWith("https://"), startsWith("http://"), startsWith("gemini://"), startsWith("gopher://"), startsWith("ftp://"): + ret.WriteString(getLinkNode(p, hyphaName, false)) default: ret.WriteString(html.EscapeString(getTextNode(p))) } diff --git a/templates/asset.qtpl.go b/templates/asset.qtpl.go index b0416a1..a6cf70f 100644 --- a/templates/asset.qtpl.go +++ b/templates/asset.qtpl.go @@ -45,7 +45,7 @@ main h1:not(.navi-title) {font-size:1.7rem;} blockquote {border-left: 4px black solid; margin-left: 0; padding-left: 1rem;} .wikilink_new {color:#a55858;} .wikilink_new:visited {color:#a55858;} -.wikilink__destination-type {display: inline; margin-left: .5rem; vertical-align: sub; } +.wikilink__destination-type {display: inline; margin: 0 .25rem; vertical-align: sub; } article code {background-color:#eee; padding: .1rem .3rem; border-radius: .25rem; font-size: 90%; } article pre.codeblock {background-color:#eee; padding:.5rem; white-space: pre-wrap; border-radius: .25rem;} diff --git a/templates/default.css b/templates/default.css index 1c041f9..0d3ef7b 100644 --- a/templates/default.css +++ b/templates/default.css @@ -20,7 +20,7 @@ main h1:not(.navi-title) {font-size:1.7rem;} blockquote {border-left: 4px black solid; margin-left: 0; padding-left: 1rem;} .wikilink_new {color:#a55858;} .wikilink_new:visited {color:#a55858;} -.wikilink__destination-type {display: inline; margin-left: .5rem; vertical-align: sub; } +.wikilink__destination-type {display: inline; margin: 0 .25rem; vertical-align: sub; } article code {background-color:#eee; padding: .1rem .3rem; border-radius: .25rem; font-size: 90%; } article pre.codeblock {background-color:#eee; padding:.5rem; white-space: pre-wrap; border-radius: .25rem;}