mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2025-06-04 03:54:06 +00:00
Delete the markup module, depend on the library instead
The old module had been moved to the library, but I changed it a little, so now both projects are broken. I sure do love programming.
This commit is contained in:
parent
b4dd2dcfad
commit
da6ea25bb6
@ -20,8 +20,9 @@ import (
|
|||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
"github.com/bouncepaw/mycorrhiza/markup"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycomarkup/legacy"
|
||||||
)
|
)
|
||||||
|
|
||||||
func geminiHomeHypha(w *gemini.ResponseWriter, rq *gemini.Request) {
|
func geminiHomeHypha(w *gemini.ResponseWriter, rq *gemini.Request) {
|
||||||
|
2
go.mod
2
go.mod
@ -5,7 +5,7 @@ go 1.14
|
|||||||
require (
|
require (
|
||||||
git.sr.ht/~adnano/go-gemini v0.1.13
|
git.sr.ht/~adnano/go-gemini v0.1.13
|
||||||
github.com/adrg/xdg v0.2.2
|
github.com/adrg/xdg v0.2.2
|
||||||
github.com/bouncepaw/mycomarkup v0.0.0-20210511092446-956f169b1499
|
github.com/bouncepaw/mycomarkup v0.0.0-20210512131946-becfae76473f
|
||||||
github.com/go-ini/ini v1.62.0
|
github.com/go-ini/ini v1.62.0
|
||||||
github.com/gorilla/feeds v1.1.1
|
github.com/gorilla/feeds v1.1.1
|
||||||
github.com/kr/pretty v0.2.1 // indirect
|
github.com/kr/pretty v0.2.1 // indirect
|
||||||
|
3
go.sum
3
go.sum
@ -7,6 +7,8 @@ github.com/bouncepaw/mycomarkup v0.0.0-20210502065108-4ddae294864d h1:H20wX93QMe
|
|||||||
github.com/bouncepaw/mycomarkup v0.0.0-20210502065108-4ddae294864d/go.mod h1:fx0FBKaCaV1fofgCQOu2lNIVB/5EoDg/83i8BgBdgpc=
|
github.com/bouncepaw/mycomarkup v0.0.0-20210502065108-4ddae294864d/go.mod h1:fx0FBKaCaV1fofgCQOu2lNIVB/5EoDg/83i8BgBdgpc=
|
||||||
github.com/bouncepaw/mycomarkup v0.0.0-20210511092446-956f169b1499 h1:RLKVj992QuayqjpIJ7M2csRjfhAnu9EO2r1Bm4D/WlU=
|
github.com/bouncepaw/mycomarkup v0.0.0-20210511092446-956f169b1499 h1:RLKVj992QuayqjpIJ7M2csRjfhAnu9EO2r1Bm4D/WlU=
|
||||||
github.com/bouncepaw/mycomarkup v0.0.0-20210511092446-956f169b1499/go.mod h1:fx0FBKaCaV1fofgCQOu2lNIVB/5EoDg/83i8BgBdgpc=
|
github.com/bouncepaw/mycomarkup v0.0.0-20210511092446-956f169b1499/go.mod h1:fx0FBKaCaV1fofgCQOu2lNIVB/5EoDg/83i8BgBdgpc=
|
||||||
|
github.com/bouncepaw/mycomarkup v0.0.0-20210512131946-becfae76473f h1:PgXG/AqO96jahMGQA81CWclIg90gsGt5kK4o7y0eZy8=
|
||||||
|
github.com/bouncepaw/mycomarkup v0.0.0-20210512131946-becfae76473f/go.mod h1:4Fz80hsrXi3lRVZuVfIk5+2xocp50CFAbJfGeJWb5aM=
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/go-ini/ini v1.62.0 h1:7VJT/ZXjzqSrvtraFp4ONq80hTcRQth1c9ZnQ3uNQvU=
|
github.com/go-ini/ini v1.62.0 h1:7VJT/ZXjzqSrvtraFp4ONq80hTcRQth1c9ZnQ3uNQvU=
|
||||||
@ -62,6 +64,7 @@ golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
34
markup/hr.go
34
markup/hr.go
@ -1,34 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"unicode"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MatchesHorizontalLine checks if the string can be interpreted as suitable for rendering as <hr/>.
|
|
||||||
//
|
|
||||||
// The rule is: if there are more than 4 characters "-" in the string, then make it a horizontal line.
|
|
||||||
// Otherwise it is a paragraph (<p>).
|
|
||||||
func MatchesHorizontalLine(line string) bool {
|
|
||||||
counter := 0
|
|
||||||
|
|
||||||
// Check initially that the symbol is "-". If it is not a "-", it is most likely a space or another character.
|
|
||||||
// With unicode.IsLetter() we can separate spaces and characters.
|
|
||||||
for _, ch := range line {
|
|
||||||
if ch == '-' {
|
|
||||||
counter++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// If we bump into any other character (letter) in the line, it is immediately an incorrect horizontal line.
|
|
||||||
// There is no point in counting further, we end the loop.
|
|
||||||
if unicode.IsLetter(ch) {
|
|
||||||
counter = 0
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if counter >= 4 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
203
markup/img.go
203
markup/img.go
@ -1,203 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycomarkup/links"
|
|
||||||
)
|
|
||||||
|
|
||||||
var imgRe = regexp.MustCompile(`^img\s+{`)
|
|
||||||
|
|
||||||
func MatchesImg(line string) bool {
|
|
||||||
return imgRe.MatchString(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
type imgState int
|
|
||||||
|
|
||||||
const (
|
|
||||||
inRoot imgState = iota
|
|
||||||
inName
|
|
||||||
inDimensionsW
|
|
||||||
inDimensionsH
|
|
||||||
inDescription
|
|
||||||
)
|
|
||||||
|
|
||||||
type Img struct {
|
|
||||||
entries []imgEntry
|
|
||||||
currEntry imgEntry
|
|
||||||
hyphaName string
|
|
||||||
state imgState
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) pushEntry() {
|
|
||||||
if strings.TrimSpace(img.currEntry.path.String()) != "" {
|
|
||||||
img.currEntry.srclink = links.From(img.currEntry.path.String(), "", img.hyphaName)
|
|
||||||
// img.currEntry.srclink.DoubtExistence()
|
|
||||||
img.entries = append(img.entries, img.currEntry)
|
|
||||||
img.currEntry = imgEntry{}
|
|
||||||
img.currEntry.path.Reset()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) Process(line string) (shouldGoBackToNormal bool) {
|
|
||||||
stateToProcessor := map[imgState]func(rune) bool{
|
|
||||||
inRoot: img.processInRoot,
|
|
||||||
inName: img.processInName,
|
|
||||||
inDimensionsW: img.processInDimensionsW,
|
|
||||||
inDimensionsH: img.processInDimensionsH,
|
|
||||||
inDescription: img.processInDescription,
|
|
||||||
}
|
|
||||||
for _, r := range line {
|
|
||||||
if shouldReturnTrue := stateToProcessor[img.state](r); shouldReturnTrue {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) processInDescription(r rune) (shouldReturnTrue bool) {
|
|
||||||
switch r {
|
|
||||||
case '}':
|
|
||||||
img.state = inName
|
|
||||||
default:
|
|
||||||
img.currEntry.desc.WriteRune(r)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) processInRoot(r rune) (shouldReturnTrue bool) {
|
|
||||||
switch r {
|
|
||||||
case '}':
|
|
||||||
img.pushEntry()
|
|
||||||
return true
|
|
||||||
case '\n', '\r':
|
|
||||||
img.pushEntry()
|
|
||||||
case ' ', '\t':
|
|
||||||
default:
|
|
||||||
img.state = inName
|
|
||||||
img.currEntry = imgEntry{}
|
|
||||||
img.currEntry.path.Reset()
|
|
||||||
img.currEntry.path.WriteRune(r)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) processInName(r rune) (shouldReturnTrue bool) {
|
|
||||||
switch r {
|
|
||||||
case '}':
|
|
||||||
img.pushEntry()
|
|
||||||
return true
|
|
||||||
case '|':
|
|
||||||
img.state = inDimensionsW
|
|
||||||
case '{':
|
|
||||||
img.state = inDescription
|
|
||||||
case '\n', '\r':
|
|
||||||
img.pushEntry()
|
|
||||||
img.state = inRoot
|
|
||||||
default:
|
|
||||||
img.currEntry.path.WriteRune(r)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) processInDimensionsW(r rune) (shouldReturnTrue bool) {
|
|
||||||
switch r {
|
|
||||||
case '}':
|
|
||||||
img.pushEntry()
|
|
||||||
return true
|
|
||||||
case '*':
|
|
||||||
img.state = inDimensionsH
|
|
||||||
case ' ', '\t', '\n':
|
|
||||||
case '{':
|
|
||||||
img.state = inDescription
|
|
||||||
default:
|
|
||||||
img.currEntry.sizeW.WriteRune(r)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) processInDimensionsH(r rune) (shouldGoBackToNormal bool) {
|
|
||||||
switch r {
|
|
||||||
case '}':
|
|
||||||
img.pushEntry()
|
|
||||||
return true
|
|
||||||
case ' ', '\t', '\n':
|
|
||||||
case '{':
|
|
||||||
img.state = inDescription
|
|
||||||
default:
|
|
||||||
img.currEntry.sizeH.WriteRune(r)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func ImgFromFirstLine(line, hyphaName string) (img *Img, shouldGoBackToNormal bool) {
|
|
||||||
img = &Img{
|
|
||||||
hyphaName: hyphaName,
|
|
||||||
entries: make([]imgEntry, 0),
|
|
||||||
}
|
|
||||||
line = line[strings.IndexRune(line, '{')+1:]
|
|
||||||
return img, img.Process(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) pagePathFor(path string) string {
|
|
||||||
path = strings.TrimSpace(path)
|
|
||||||
if strings.IndexRune(path, ':') != -1 || strings.IndexRune(path, '/') == 0 {
|
|
||||||
return path
|
|
||||||
} else {
|
|
||||||
return "/page/" + xclCanonicalName(img.hyphaName, path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseDimensions(dimensions string) (sizeW, sizeH string) {
|
|
||||||
xIndex := strings.IndexRune(dimensions, '*')
|
|
||||||
if xIndex == -1 { // If no x in dimensions
|
|
||||||
sizeW = strings.TrimSpace(dimensions)
|
|
||||||
} else {
|
|
||||||
sizeW = strings.TrimSpace(dimensions[:xIndex])
|
|
||||||
sizeH = strings.TrimSpace(strings.TrimPrefix(dimensions, dimensions[:xIndex+1]))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) markExistenceOfSrcLinks() {
|
|
||||||
HyphaIterate(func(hn string) {
|
|
||||||
for _, entry := range img.entries {
|
|
||||||
if hn == entry.srclink.Address() {
|
|
||||||
entry.srclink.DestinationUnknown = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (img *Img) ToHtml() (html string) {
|
|
||||||
img.markExistenceOfSrcLinks()
|
|
||||||
isOneImageOnly := len(img.entries) == 1 && img.entries[0].desc.Len() == 0
|
|
||||||
if isOneImageOnly {
|
|
||||||
html += `<section class="img-gallery img-gallery_one-image">`
|
|
||||||
} else {
|
|
||||||
html += `<section class="img-gallery img-gallery_many-images">`
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, entry := range img.entries {
|
|
||||||
html += `<figure>`
|
|
||||||
if entry.srclink.DestinationUnknown {
|
|
||||||
html += fmt.Sprintf(
|
|
||||||
`<a class="%s" href="%s">Hypha <i>%s</i> does not exist</a>`,
|
|
||||||
entry.srclink.Classes(),
|
|
||||||
entry.srclink.Href(),
|
|
||||||
entry.srclink.Address)
|
|
||||||
} else {
|
|
||||||
html += fmt.Sprintf(
|
|
||||||
`<a href="%s"><img src="%s" %s %s></a>`,
|
|
||||||
entry.srclink.Href(),
|
|
||||||
entry.srclink.ImgSrc(),
|
|
||||||
entry.sizeWAsAttr(),
|
|
||||||
entry.sizeHAsAttr())
|
|
||||||
}
|
|
||||||
html += entry.descriptionAsHtml(img.hyphaName)
|
|
||||||
html += `</figure>`
|
|
||||||
}
|
|
||||||
return html + `</section>`
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycomarkup/links"
|
|
||||||
)
|
|
||||||
|
|
||||||
type imgEntry struct {
|
|
||||||
srclink *links.Link
|
|
||||||
path strings.Builder
|
|
||||||
sizeW strings.Builder
|
|
||||||
sizeH strings.Builder
|
|
||||||
desc strings.Builder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *imgEntry) descriptionAsHtml(hyphaName string) (html string) {
|
|
||||||
if entry.desc.Len() == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
lines := strings.Split(entry.desc.String(), "\n")
|
|
||||||
for _, line := range lines {
|
|
||||||
if line = strings.TrimSpace(line); line != "" {
|
|
||||||
if html != "" {
|
|
||||||
html += `<br>`
|
|
||||||
}
|
|
||||||
html += ParagraphToHtml(hyphaName, line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return `<figcaption>` + html + `</figcaption>`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *imgEntry) sizeWAsAttr() string {
|
|
||||||
if entry.sizeW.Len() == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return ` width="` + entry.sizeW.String() + `"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *imgEntry) sizeHAsAttr() string {
|
|
||||||
if entry.sizeH.Len() == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return ` height="` + entry.sizeH.String() + `"`
|
|
||||||
}
|
|
229
markup/lexer.go
229
markup/lexer.go
@ -1,229 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"html"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HyphaExists holds function that checks that a hypha is present.
|
|
||||||
var HyphaExists func(string) bool
|
|
||||||
|
|
||||||
//
|
|
||||||
var HyphaImageForOG func(string) string
|
|
||||||
|
|
||||||
// HyphaAccess holds function that accesses a hypha by its name.
|
|
||||||
var HyphaAccess func(string) (rawText, binaryHtml string, err error)
|
|
||||||
|
|
||||||
// HyphaIterate is a function that iterates all hypha names existing.
|
|
||||||
var HyphaIterate func(func(string))
|
|
||||||
|
|
||||||
// GemLexerState is used by markup parser to remember what is going on.
|
|
||||||
type GemLexerState struct {
|
|
||||||
// Name of hypha being parsed
|
|
||||||
name string
|
|
||||||
where string // "", "list", "pre"
|
|
||||||
// Line id
|
|
||||||
id int
|
|
||||||
buf string
|
|
||||||
// Temporaries
|
|
||||||
img *Img
|
|
||||||
table *Table
|
|
||||||
list *List
|
|
||||||
}
|
|
||||||
|
|
||||||
type Line struct {
|
|
||||||
id int
|
|
||||||
// interface{} may be bad. TODO: a proper type
|
|
||||||
contents interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MycoDoc) lex() (ast []Line) {
|
|
||||||
var state = GemLexerState{name: md.hyphaName}
|
|
||||||
|
|
||||||
for _, line := range append(strings.Split(md.contents, "\n"), "") {
|
|
||||||
lineToAST(line, &state, &ast)
|
|
||||||
}
|
|
||||||
return ast
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lex `line` in markup and save it to `ast` using `state`.
|
|
||||||
func lineToAST(line string, state *GemLexerState, ast *[]Line) {
|
|
||||||
addLine := func(text interface{}) {
|
|
||||||
*ast = append(*ast, Line{id: state.id, contents: text})
|
|
||||||
}
|
|
||||||
addParagraphIfNeeded := func() {
|
|
||||||
if state.where == "p" {
|
|
||||||
state.where = ""
|
|
||||||
addLine(fmt.Sprintf("<p id='%d'>%s</p>", state.id, strings.ReplaceAll(ParagraphToHtml(state.name, state.buf), "\n", "<br>")))
|
|
||||||
state.buf = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process empty lines depending on the current state
|
|
||||||
if "" == strings.TrimSpace(line) {
|
|
||||||
switch state.where {
|
|
||||||
case "pre":
|
|
||||||
state.buf += "\n"
|
|
||||||
case "launchpad":
|
|
||||||
state.where = ""
|
|
||||||
addLine(state.buf + "</ul>")
|
|
||||||
case "p":
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
startsWith := func(token string) bool {
|
|
||||||
return strings.HasPrefix(line, token)
|
|
||||||
}
|
|
||||||
addHeading := func(i int) {
|
|
||||||
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.
|
|
||||||
switch state.where {
|
|
||||||
case "img":
|
|
||||||
goto imgState
|
|
||||||
case "table":
|
|
||||||
goto tableState
|
|
||||||
case "list":
|
|
||||||
goto listState
|
|
||||||
case "pre":
|
|
||||||
goto preformattedState
|
|
||||||
case "launchpad":
|
|
||||||
goto launchpadState
|
|
||||||
default: // "p" or ""
|
|
||||||
goto normalState
|
|
||||||
}
|
|
||||||
|
|
||||||
imgState:
|
|
||||||
if shouldGoBackToNormal := state.img.Process(line); shouldGoBackToNormal {
|
|
||||||
state.where = ""
|
|
||||||
addLine(*state.img)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
tableState:
|
|
||||||
if shouldGoBackToNormal := state.table.Process(line); shouldGoBackToNormal {
|
|
||||||
state.where = ""
|
|
||||||
addLine(*state.table)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
listState:
|
|
||||||
if done := state.list.Parse(line); done {
|
|
||||||
state.list.Finalize()
|
|
||||||
state.where = ""
|
|
||||||
goto normalState
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
preformattedState:
|
|
||||||
switch {
|
|
||||||
case startsWith("```"):
|
|
||||||
state.where = ""
|
|
||||||
state.buf = strings.TrimSuffix(state.buf, "\n")
|
|
||||||
addLine(state.buf + "</code></pre>")
|
|
||||||
state.buf = ""
|
|
||||||
default:
|
|
||||||
state.buf += html.EscapeString(line) + "\n"
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
launchpadState:
|
|
||||||
switch {
|
|
||||||
case startsWith("=>"):
|
|
||||||
href, text, class := Rocketlink(line, state.name)
|
|
||||||
state.buf += fmt.Sprintf(` <li class="launchpad__entry"><a href="%s" class="rocketlink %s">%s</a></li>`, href, class, text)
|
|
||||||
case startsWith("```"):
|
|
||||||
state.where = "pre"
|
|
||||||
addLine(state.buf + "</ul>")
|
|
||||||
state.id++
|
|
||||||
state.buf = fmt.Sprintf("<pre id='%d' alt='%s' class='codeblock'><code>", state.id, strings.TrimPrefix(line, "```"))
|
|
||||||
default:
|
|
||||||
state.where = ""
|
|
||||||
addLine(state.buf + "</ul>")
|
|
||||||
goto normalState
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
normalState:
|
|
||||||
state.id++
|
|
||||||
switch {
|
|
||||||
|
|
||||||
case startsWith("```"):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
state.where = "pre"
|
|
||||||
state.buf = fmt.Sprintf("<pre id='%d' alt='%s' class='codeblock'><code>", state.id, strings.TrimPrefix(line, "```"))
|
|
||||||
|
|
||||||
case startsWith("###### "):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
addHeading(6)
|
|
||||||
case startsWith("##### "):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
addHeading(5)
|
|
||||||
case startsWith("#### "):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
addHeading(4)
|
|
||||||
case startsWith("### "):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
addHeading(3)
|
|
||||||
case startsWith("## "):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
addHeading(2)
|
|
||||||
case startsWith("# "):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
addHeading(1)
|
|
||||||
|
|
||||||
case startsWith(">"):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
addLine(
|
|
||||||
fmt.Sprintf(
|
|
||||||
"<blockquote id='%d'>%s</blockquote>",
|
|
||||||
state.id,
|
|
||||||
ParagraphToHtml(state.name, remover(">")(line)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
case startsWith("=>"):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
state.where = "launchpad"
|
|
||||||
state.buf = fmt.Sprintf("<ul class='launchpad' id='%d'>\n", state.id)
|
|
||||||
goto launchpadState
|
|
||||||
|
|
||||||
case startsWith("<="):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
addLine(parseTransclusion(line, state.name))
|
|
||||||
case MatchesHorizontalLine(line):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
*ast = append(*ast, Line{id: -1, contents: "<hr/>"})
|
|
||||||
case MatchesList(line):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
list, _ := NewList(line, state.name)
|
|
||||||
state.where = "list"
|
|
||||||
state.list = list
|
|
||||||
addLine(state.list)
|
|
||||||
case MatchesImg(line):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
img, shouldGoBackToNormal := ImgFromFirstLine(line, state.name)
|
|
||||||
if shouldGoBackToNormal {
|
|
||||||
addLine(*img)
|
|
||||||
} else {
|
|
||||||
state.where = "img"
|
|
||||||
state.img = img
|
|
||||||
}
|
|
||||||
case MatchesTable(line):
|
|
||||||
addParagraphIfNeeded()
|
|
||||||
state.where = "table"
|
|
||||||
state.table = TableFromFirstLine(line, state.name)
|
|
||||||
|
|
||||||
case state.where == "p":
|
|
||||||
state.buf += "\n" + line
|
|
||||||
default:
|
|
||||||
state.where = "p"
|
|
||||||
state.buf = line
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycomarkup/links"
|
|
||||||
)
|
|
||||||
|
|
||||||
// LinkParts determines what href, text and class should resulting <a> have based on mycomarkup's addr, display and hypha name.
|
|
||||||
//
|
|
||||||
// => addr display
|
|
||||||
// [[addr|display]]
|
|
||||||
// TODO: deprecate
|
|
||||||
func LinkParts(addr, display, hyphaName string) (href, text, class string) {
|
|
||||||
l := links.From(addr, display, hyphaName)
|
|
||||||
if l.OfKind(links.LinkLocalHypha) && !HyphaExists(l.Address()) {
|
|
||||||
l.DestinationUnknown = true
|
|
||||||
}
|
|
||||||
return l.Href(), l.Display(), l.Classes()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse markup line starting with "=>" according to wikilink rules.
|
|
||||||
// See http://localhost:1737/page/wikilink
|
|
||||||
func Rocketlink(src, hyphaName string) (href, text, class string) {
|
|
||||||
src = strings.TrimSpace(src[2:]) // Drop =>
|
|
||||||
if src == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Href is text after => till first whitespace
|
|
||||||
addr := strings.Fields(src)[0]
|
|
||||||
display := strings.TrimPrefix(src, addr)
|
|
||||||
return LinkParts(addr, display, hyphaName)
|
|
||||||
}
|
|
171
markup/list.go
171
markup/list.go
@ -1,171 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func parseListItem(line string) (level int, offset int, ordered bool, err error) {
|
|
||||||
for line[level] == '*' {
|
|
||||||
level++
|
|
||||||
}
|
|
||||||
|
|
||||||
if line[level] == '.' {
|
|
||||||
ordered = true
|
|
||||||
offset = level + 2
|
|
||||||
} else {
|
|
||||||
ordered = false
|
|
||||||
offset = level + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if line[offset-1] != ' ' || len(line) < offset+2 || level < 1 || level > 6 {
|
|
||||||
err = errors.New("ill-formatted list item")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func MatchesList(line string) bool {
|
|
||||||
level, _, _, err := parseListItem(line)
|
|
||||||
return err == nil && level == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
type listItem struct {
|
|
||||||
content string
|
|
||||||
parent *listItem
|
|
||||||
children []*listItem
|
|
||||||
depth int
|
|
||||||
}
|
|
||||||
|
|
||||||
func newListItem(parent *listItem) *listItem {
|
|
||||||
depth := 0
|
|
||||||
if parent != nil {
|
|
||||||
depth = parent.depth + 1
|
|
||||||
}
|
|
||||||
return &listItem{
|
|
||||||
parent: parent,
|
|
||||||
children: make([]*listItem, 0),
|
|
||||||
depth: depth,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (item *listItem) renderAsHtmlTo(b *strings.Builder, hyphaName string, ordered bool) {
|
|
||||||
if len(item.content) > 0 {
|
|
||||||
b.WriteString("<li>")
|
|
||||||
b.WriteString(ParagraphToHtml(hyphaName, item.content))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(item.children) > 0 {
|
|
||||||
if ordered {
|
|
||||||
b.WriteString("<ol>")
|
|
||||||
} else {
|
|
||||||
b.WriteString("<ul>")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, child := range item.children {
|
|
||||||
child.renderAsHtmlTo(b, hyphaName, ordered)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ordered {
|
|
||||||
b.WriteString("</ol>")
|
|
||||||
} else {
|
|
||||||
b.WriteString("</ul>")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(item.content) > 0 {
|
|
||||||
b.WriteString("</li>")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A structure representing ordered and unordered lists in the AST.
|
|
||||||
type List struct {
|
|
||||||
curr *listItem
|
|
||||||
hyphaName string
|
|
||||||
ordered bool
|
|
||||||
finalized bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewList(line, hyphaName string) (*List, bool) {
|
|
||||||
list := &List{
|
|
||||||
hyphaName: hyphaName,
|
|
||||||
curr: newListItem(nil),
|
|
||||||
}
|
|
||||||
return list, list.Parse(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (list *List) pushItem() {
|
|
||||||
item := newListItem(list.curr)
|
|
||||||
list.curr.children = append(list.curr.children, item)
|
|
||||||
list.curr = item
|
|
||||||
}
|
|
||||||
|
|
||||||
func (list *List) popItem() {
|
|
||||||
if list.curr == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
list.curr = list.curr.parent
|
|
||||||
}
|
|
||||||
|
|
||||||
func (list *List) balance(level int) {
|
|
||||||
for level > list.curr.depth {
|
|
||||||
list.pushItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
for level < list.curr.depth {
|
|
||||||
list.popItem()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (list *List) Parse(line string) (done bool) {
|
|
||||||
level, offset, ordered, err := parseListItem(line)
|
|
||||||
if err != nil {
|
|
||||||
list.Finalize()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// update ordered flag if the current node is the root one
|
|
||||||
// (i.e. no parsing has been done yet)
|
|
||||||
if list.curr.parent == nil {
|
|
||||||
list.ordered = ordered
|
|
||||||
}
|
|
||||||
|
|
||||||
// if list type has suddenly changed (ill-formatted list), quit
|
|
||||||
if ordered != list.ordered {
|
|
||||||
list.Finalize()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
list.balance(level)
|
|
||||||
|
|
||||||
// if the current node already has content, create a new one
|
|
||||||
// to prevent overwriting existing content (effectively creating
|
|
||||||
// a new sibling node)
|
|
||||||
if len(list.curr.content) > 0 {
|
|
||||||
list.popItem()
|
|
||||||
list.pushItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
list.curr.content = line[offset:]
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (list *List) Finalize() {
|
|
||||||
if !list.finalized {
|
|
||||||
// close all opened nodes, effectively going up to the root node
|
|
||||||
list.balance(0)
|
|
||||||
list.finalized = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (list *List) RenderAsHtml() (html string) {
|
|
||||||
// for a good measure
|
|
||||||
list.Finalize()
|
|
||||||
|
|
||||||
b := &strings.Builder{}
|
|
||||||
|
|
||||||
// fire up recursive render process
|
|
||||||
list.curr.renderAsHtmlTo(b, list.hyphaName, list.ordered)
|
|
||||||
|
|
||||||
return b.String()
|
|
||||||
}
|
|
@ -1,98 +0,0 @@
|
|||||||
// This is not done yet
|
|
||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycomarkup/links"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Mycomarkup-formatted document
|
|
||||||
type MycoDoc struct {
|
|
||||||
// data
|
|
||||||
hyphaName string
|
|
||||||
contents string
|
|
||||||
// indicators
|
|
||||||
parsedAlready bool
|
|
||||||
// results
|
|
||||||
ast []Line
|
|
||||||
html string
|
|
||||||
firstImageURL string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructor
|
|
||||||
func Doc(hyphaName, contents string) *MycoDoc {
|
|
||||||
md := &MycoDoc{
|
|
||||||
hyphaName: hyphaName,
|
|
||||||
contents: contents,
|
|
||||||
}
|
|
||||||
return md
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MycoDoc) Lex(recursionLevel int) *MycoDoc {
|
|
||||||
if !md.parsedAlready {
|
|
||||||
md.ast = md.lex()
|
|
||||||
}
|
|
||||||
md.parsedAlready = true
|
|
||||||
return md
|
|
||||||
}
|
|
||||||
|
|
||||||
// AsHTML returns an html representation of the document
|
|
||||||
func (md *MycoDoc) AsHTML() string {
|
|
||||||
md.html = Parse(md.Lex(0).ast, 0, 0, 0)
|
|
||||||
return md.html
|
|
||||||
}
|
|
||||||
|
|
||||||
// AsGemtext returns a gemtext representation of the document. Currently really limited, just returns source text
|
|
||||||
func (md *MycoDoc) AsGemtext() string {
|
|
||||||
return md.contents
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used to clear opengraph description from html tags. This method is usually bad because of dangers of malformed HTML, but I'm going to use it only for Mycorrhiza-generated HTML, so it's okay. The question mark is required; without it the whole string is eaten away.
|
|
||||||
var htmlTagRe = regexp.MustCompile(`<.*?>`)
|
|
||||||
|
|
||||||
// OpenGraphHTML returns an html representation of og: meta tags.
|
|
||||||
func (md *MycoDoc) OpenGraphHTML() string {
|
|
||||||
md.ogFillVars()
|
|
||||||
return strings.Join([]string{
|
|
||||||
ogTag("title", md.hyphaName),
|
|
||||||
ogTag("type", "article"),
|
|
||||||
ogTag("image", md.firstImageURL),
|
|
||||||
ogTag("url", cfg.URL+"/hypha/"+md.hyphaName),
|
|
||||||
ogTag("determiner", ""),
|
|
||||||
ogTag("description", htmlTagRe.ReplaceAllString(md.description, "")),
|
|
||||||
}, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MycoDoc) ogFillVars() *MycoDoc {
|
|
||||||
md.firstImageURL = cfg.URL + "/favicon.ico"
|
|
||||||
foundDesc := false
|
|
||||||
foundImg := false
|
|
||||||
for _, line := range md.ast {
|
|
||||||
switch v := line.contents.(type) {
|
|
||||||
case string:
|
|
||||||
if !foundDesc {
|
|
||||||
md.description = v
|
|
||||||
foundDesc = true
|
|
||||||
}
|
|
||||||
case Img:
|
|
||||||
if !foundImg && len(v.entries) > 0 {
|
|
||||||
md.firstImageURL = v.entries[0].srclink.ImgSrc()
|
|
||||||
if !v.entries[0].srclink.OfKind(links.LinkExternal) {
|
|
||||||
md.firstImageURL = cfg.URL + md.firstImageURL
|
|
||||||
}
|
|
||||||
foundImg = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return md
|
|
||||||
}
|
|
||||||
|
|
||||||
func ogTag(property, content string) string {
|
|
||||||
return fmt.Sprintf(`<meta property="og:%s" content="%s"/>`, property, content)
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycomarkup/links"
|
|
||||||
)
|
|
||||||
|
|
||||||
// OutLinks returns a channel of names of hyphae this mycodocument links.
|
|
||||||
// Links include:
|
|
||||||
// * Regular links
|
|
||||||
// * Rocketlinks
|
|
||||||
// * Transclusion
|
|
||||||
// * Image galleries
|
|
||||||
// Not needed anymore, I guess.
|
|
||||||
func (md *MycoDoc) OutLinks() chan string {
|
|
||||||
ch := make(chan string)
|
|
||||||
if !md.parsedAlready {
|
|
||||||
md.Lex(0)
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
for _, line := range md.ast {
|
|
||||||
switch v := line.contents.(type) {
|
|
||||||
case string:
|
|
||||||
if strings.HasPrefix(v, "<p") || strings.HasPrefix(v, "<ul class='launchpad'") {
|
|
||||||
extractLinks(v, ch)
|
|
||||||
}
|
|
||||||
case Transclusion:
|
|
||||||
ch <- v.name
|
|
||||||
case Img:
|
|
||||||
extractImageLinks(v, ch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close(ch)
|
|
||||||
}()
|
|
||||||
return ch
|
|
||||||
}
|
|
||||||
|
|
||||||
var reLinks = regexp.MustCompile(`<a href="/hypha/([^"]*)".*?</a>`)
|
|
||||||
|
|
||||||
func extractLinks(html string, ch chan string) {
|
|
||||||
if results := reLinks.FindAllStringSubmatch(html, -1); results != nil {
|
|
||||||
for _, result := range results {
|
|
||||||
// result[0] is always present at this point and is not needed, because it is the whole matched substring (which we don't need)
|
|
||||||
ch <- result[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractImageLinks(img Img, ch chan string) {
|
|
||||||
for _, entry := range img.entries {
|
|
||||||
if entry.srclink.OfKind(links.LinkLocalHypha) {
|
|
||||||
ch <- entry.srclink.Address()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,185 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"html"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
)
|
|
||||||
|
|
||||||
type spanTokenType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
spanTextNode = iota
|
|
||||||
spanItalic
|
|
||||||
spanBold
|
|
||||||
spanMono
|
|
||||||
spanSuper
|
|
||||||
spanSub
|
|
||||||
spanMark
|
|
||||||
spanStrike
|
|
||||||
spanLink
|
|
||||||
)
|
|
||||||
|
|
||||||
func tagFromState(stt spanTokenType, tagState map[spanTokenType]bool, tagName, originalForm string) string {
|
|
||||||
if tagState[spanMono] && (stt != spanMono) {
|
|
||||||
return originalForm
|
|
||||||
}
|
|
||||||
if tagState[stt] {
|
|
||||||
tagState[stt] = false
|
|
||||||
return fmt.Sprintf("</%s>", tagName)
|
|
||||||
} else {
|
|
||||||
tagState[stt] = true
|
|
||||||
return fmt.Sprintf("<%s>", tagName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLinkNode(input *bytes.Buffer, hyphaName string, isBracketedLink bool) string {
|
|
||||||
if isBracketedLink {
|
|
||||||
input.Next(2) // drop those [[
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
escaping = false
|
|
||||||
addrBuf = bytes.Buffer{}
|
|
||||||
displayBuf = bytes.Buffer{}
|
|
||||||
currBuf = &addrBuf
|
|
||||||
)
|
|
||||||
for input.Len() != 0 {
|
|
||||||
b, _ := input.ReadByte()
|
|
||||||
if escaping {
|
|
||||||
currBuf.WriteByte(b)
|
|
||||||
escaping = false
|
|
||||||
} else if isBracketedLink && b == '|' && currBuf == &addrBuf {
|
|
||||||
currBuf = &displayBuf
|
|
||||||
} else if isBracketedLink && b == ']' && bytes.HasPrefix(input.Bytes(), []byte{']'}) {
|
|
||||||
input.Next(1)
|
|
||||||
break
|
|
||||||
} else if !isBracketedLink && (unicode.IsSpace(rune(b)) || strings.ContainsRune("<>{}|\\^[]`,()", rune(b))) {
|
|
||||||
input.UnreadByte()
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
currBuf.WriteByte(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
href, text, class := LinkParts(addrBuf.String(), displayBuf.String(), hyphaName)
|
|
||||||
return fmt.Sprintf(`<a href="%s" class="%s">%s</a>`, href, class, html.EscapeString(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
// getTextNode splits the `input` into two parts `textNode` and `rest` by the first encountered rune that resembles a span tag. If there is none, `textNode = input`, `rest = ""`. It handles escaping with backslash.
|
|
||||||
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 {
|
|
||||||
b, _ := input.ReadByte()
|
|
||||||
textNodeBuffer.WriteByte(b)
|
|
||||||
}
|
|
||||||
for input.Len() != 0 {
|
|
||||||
// Assume no error is possible because we check for length
|
|
||||||
b, _ := input.ReadByte()
|
|
||||||
if escaping {
|
|
||||||
textNodeBuffer.WriteByte(b)
|
|
||||||
escaping = false
|
|
||||||
} else if b == '\\' {
|
|
||||||
escaping = true
|
|
||||||
} else if strings.IndexByte("/*`^,![~", b) >= 0 {
|
|
||||||
input.UnreadByte()
|
|
||||||
break
|
|
||||||
} else if couldBeLinkStart() {
|
|
||||||
textNodeBuffer.WriteByte(b)
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
textNodeBuffer.WriteByte(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return textNodeBuffer.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParagraphToHtml(hyphaName, input string) string {
|
|
||||||
var (
|
|
||||||
p = bytes.NewBufferString(input)
|
|
||||||
ret strings.Builder
|
|
||||||
// true = tag is opened, false = tag is not opened
|
|
||||||
tagState = map[spanTokenType]bool{
|
|
||||||
spanItalic: false,
|
|
||||||
spanBold: false,
|
|
||||||
spanMono: false,
|
|
||||||
spanSuper: false,
|
|
||||||
spanSub: false,
|
|
||||||
spanMark: false,
|
|
||||||
spanLink: false,
|
|
||||||
}
|
|
||||||
startsWith = func(t string) bool {
|
|
||||||
return bytes.HasPrefix(p.Bytes(), []byte(t))
|
|
||||||
}
|
|
||||||
noTagsActive = func() bool {
|
|
||||||
return !(tagState[spanItalic] || tagState[spanBold] || tagState[spanMono] || tagState[spanSuper] || tagState[spanSub] || tagState[spanMark] || tagState[spanLink])
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
for p.Len() != 0 {
|
|
||||||
switch {
|
|
||||||
case startsWith("//"):
|
|
||||||
ret.WriteString(tagFromState(spanItalic, tagState, "em", "//"))
|
|
||||||
p.Next(2)
|
|
||||||
case startsWith("**"):
|
|
||||||
ret.WriteString(tagFromState(spanBold, tagState, "strong", "**"))
|
|
||||||
p.Next(2)
|
|
||||||
case startsWith("`"):
|
|
||||||
ret.WriteString(tagFromState(spanMono, tagState, "code", "`"))
|
|
||||||
p.Next(1)
|
|
||||||
case startsWith("^"):
|
|
||||||
ret.WriteString(tagFromState(spanSuper, tagState, "sup", "^"))
|
|
||||||
p.Next(1)
|
|
||||||
case startsWith(",,"):
|
|
||||||
ret.WriteString(tagFromState(spanSub, tagState, "sub", ",,"))
|
|
||||||
p.Next(2)
|
|
||||||
case startsWith("!!"):
|
|
||||||
ret.WriteString(tagFromState(spanMark, tagState, "mark", "!!"))
|
|
||||||
p.Next(2)
|
|
||||||
case startsWith("~~"):
|
|
||||||
ret.WriteString(tagFromState(spanMark, tagState, "s", "~~"))
|
|
||||||
p.Next(2)
|
|
||||||
case startsWith("[["):
|
|
||||||
ret.WriteString(getLinkNode(p, hyphaName, true))
|
|
||||||
case (startsWith("https://") || startsWith("http://") || startsWith("gemini://") || startsWith("gopher://") || startsWith("ftp://")) && noTagsActive():
|
|
||||||
ret.WriteString(getLinkNode(p, hyphaName, false))
|
|
||||||
default:
|
|
||||||
ret.WriteString(html.EscapeString(getTextNode(p)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for stt, open := range tagState {
|
|
||||||
if open {
|
|
||||||
switch stt {
|
|
||||||
case spanItalic:
|
|
||||||
ret.WriteString(tagFromState(spanItalic, tagState, "em", "//"))
|
|
||||||
case spanBold:
|
|
||||||
ret.WriteString(tagFromState(spanBold, tagState, "strong", "**"))
|
|
||||||
case spanMono:
|
|
||||||
ret.WriteString(tagFromState(spanMono, tagState, "code", "`"))
|
|
||||||
case spanSuper:
|
|
||||||
ret.WriteString(tagFromState(spanSuper, tagState, "sup", "^"))
|
|
||||||
case spanSub:
|
|
||||||
ret.WriteString(tagFromState(spanSub, tagState, "sub", ",,"))
|
|
||||||
case spanMark:
|
|
||||||
ret.WriteString(tagFromState(spanMark, tagState, "mark", "!!"))
|
|
||||||
case spanStrike:
|
|
||||||
ret.WriteString(tagFromState(spanMark, tagState, "s", "~~"))
|
|
||||||
case spanLink:
|
|
||||||
ret.WriteString(tagFromState(spanLink, tagState, "a", "[["))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret.String()
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
const maxRecursionLevel = 3
|
|
||||||
|
|
||||||
func Parse(ast []Line, from, to int, recursionLevel int) (html string) {
|
|
||||||
if recursionLevel > maxRecursionLevel {
|
|
||||||
return "Transclusion depth limit"
|
|
||||||
}
|
|
||||||
for _, line := range ast {
|
|
||||||
if line.id >= from && (line.id <= to || to == 0) || line.id == -1 {
|
|
||||||
switch v := line.contents.(type) {
|
|
||||||
case Transclusion:
|
|
||||||
html += Transclude(v, recursionLevel)
|
|
||||||
case Img:
|
|
||||||
html += v.ToHtml()
|
|
||||||
case Table:
|
|
||||||
html += v.asHtml()
|
|
||||||
case *List:
|
|
||||||
html += v.RenderAsHtml()
|
|
||||||
case string:
|
|
||||||
html += v
|
|
||||||
default:
|
|
||||||
html += "<b class='error'>Unknown element.</b>"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return html
|
|
||||||
}
|
|
230
markup/table.go
230
markup/table.go
@ -1,230 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
)
|
|
||||||
|
|
||||||
var tableRe = regexp.MustCompile(`^table\s+{`)
|
|
||||||
|
|
||||||
func MatchesTable(line string) bool {
|
|
||||||
return tableRe.MatchString(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TableFromFirstLine(line, hyphaName string) *Table {
|
|
||||||
return &Table{
|
|
||||||
hyphaName: hyphaName,
|
|
||||||
caption: line[strings.IndexRune(line, '{')+1:],
|
|
||||||
rows: make([]*tableRow, 0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Table) Process(line string) (shouldGoBackToNormal bool) {
|
|
||||||
if strings.TrimSpace(line) == "}" && !t.inMultiline {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if !t.inMultiline {
|
|
||||||
t.pushRow()
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
inLink bool
|
|
||||||
skipNext bool
|
|
||||||
escaping bool
|
|
||||||
lookingForNonSpace = !t.inMultiline
|
|
||||||
countingColspan bool
|
|
||||||
)
|
|
||||||
for i, r := range line {
|
|
||||||
switch {
|
|
||||||
case skipNext:
|
|
||||||
skipNext = false
|
|
||||||
continue
|
|
||||||
|
|
||||||
case lookingForNonSpace && unicode.IsSpace(r):
|
|
||||||
case lookingForNonSpace && (r == '!' || r == '|'):
|
|
||||||
t.currCellMarker = r
|
|
||||||
t.currColspan = 1
|
|
||||||
lookingForNonSpace = false
|
|
||||||
countingColspan = true
|
|
||||||
case lookingForNonSpace:
|
|
||||||
t.currCellMarker = '^' // ^ represents implicit |, not part of syntax
|
|
||||||
t.currColspan = 1
|
|
||||||
lookingForNonSpace = false
|
|
||||||
t.currCellBuilder.WriteRune(r)
|
|
||||||
|
|
||||||
case escaping:
|
|
||||||
t.currCellBuilder.WriteRune(r)
|
|
||||||
case inLink && r == ']' && len(line)-1 > i && line[i+1] == ']':
|
|
||||||
t.currCellBuilder.WriteString("]]")
|
|
||||||
inLink = false
|
|
||||||
skipNext = true
|
|
||||||
case inLink:
|
|
||||||
t.currCellBuilder.WriteRune(r)
|
|
||||||
|
|
||||||
case t.inMultiline && r == '}':
|
|
||||||
t.inMultiline = false
|
|
||||||
case t.inMultiline && i == len(line)-1:
|
|
||||||
t.currCellBuilder.WriteRune('\n')
|
|
||||||
case t.inMultiline:
|
|
||||||
t.currCellBuilder.WriteRune(r)
|
|
||||||
|
|
||||||
// Not in multiline:
|
|
||||||
case (r == '|' || r == '!') && !countingColspan:
|
|
||||||
t.pushCell()
|
|
||||||
t.currCellMarker = r
|
|
||||||
t.currColspan = 1
|
|
||||||
countingColspan = true
|
|
||||||
case r == t.currCellMarker && (r == '|' || r == '!') && countingColspan:
|
|
||||||
t.currColspan++
|
|
||||||
case r == '{':
|
|
||||||
t.inMultiline = true
|
|
||||||
countingColspan = false
|
|
||||||
case r == '[' && len(line)-1 > i && line[i+1] == '[':
|
|
||||||
t.currCellBuilder.WriteString("[[")
|
|
||||||
inLink = true
|
|
||||||
skipNext = true
|
|
||||||
case i == len(line)-1:
|
|
||||||
t.pushCell()
|
|
||||||
default:
|
|
||||||
t.currCellBuilder.WriteRune(r)
|
|
||||||
countingColspan = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type Table struct {
|
|
||||||
// data
|
|
||||||
hyphaName string
|
|
||||||
caption string
|
|
||||||
rows []*tableRow
|
|
||||||
// state
|
|
||||||
inMultiline bool
|
|
||||||
// tmp
|
|
||||||
currCellMarker rune
|
|
||||||
currColspan uint
|
|
||||||
currCellBuilder strings.Builder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Table) pushRow() {
|
|
||||||
t.rows = append(t.rows, &tableRow{
|
|
||||||
cells: make([]*tableCell, 0),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Table) pushCell() {
|
|
||||||
tc := &tableCell{
|
|
||||||
content: t.currCellBuilder.String(),
|
|
||||||
colspan: t.currColspan,
|
|
||||||
}
|
|
||||||
switch t.currCellMarker {
|
|
||||||
case '|', '^':
|
|
||||||
tc.kind = tableCellDatum
|
|
||||||
case '!':
|
|
||||||
tc.kind = tableCellHeader
|
|
||||||
}
|
|
||||||
// We expect the table to have at least one row ready, so no nil-checking
|
|
||||||
tr := t.rows[len(t.rows)-1]
|
|
||||||
tr.cells = append(tr.cells, tc)
|
|
||||||
t.currCellBuilder = strings.Builder{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Table) asHtml() (html string) {
|
|
||||||
if t.caption != "" {
|
|
||||||
html += fmt.Sprintf("<caption>%s</caption>", t.caption)
|
|
||||||
}
|
|
||||||
if len(t.rows) > 0 && t.rows[0].looksLikeThead() {
|
|
||||||
html += fmt.Sprintf("<thead>%s</thead>", t.rows[0].asHtml(t.hyphaName))
|
|
||||||
t.rows = t.rows[1:]
|
|
||||||
}
|
|
||||||
html += "\n<tbody>\n"
|
|
||||||
for _, tr := range t.rows {
|
|
||||||
html += tr.asHtml(t.hyphaName)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(`<table>%s</tbody></table>`, html)
|
|
||||||
}
|
|
||||||
|
|
||||||
type tableRow struct {
|
|
||||||
cells []*tableCell
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *tableRow) asHtml(hyphaName string) (html string) {
|
|
||||||
for _, tc := range tr.cells {
|
|
||||||
html += tc.asHtml(hyphaName)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("<tr>%s</tr>\n", html)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Most likely, rows with more than two header cells are theads. I allow one extra datum cell for tables like this:
|
|
||||||
// | ! a ! b
|
|
||||||
// ! c | d | e
|
|
||||||
// ! f | g | h
|
|
||||||
func (tr *tableRow) looksLikeThead() bool {
|
|
||||||
var (
|
|
||||||
headerAmount = 0
|
|
||||||
datumAmount = 0
|
|
||||||
)
|
|
||||||
for _, tc := range tr.cells {
|
|
||||||
switch tc.kind {
|
|
||||||
case tableCellHeader:
|
|
||||||
headerAmount++
|
|
||||||
case tableCellDatum:
|
|
||||||
datumAmount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return headerAmount >= 2 && datumAmount <= 1
|
|
||||||
}
|
|
||||||
|
|
||||||
type tableCell struct {
|
|
||||||
kind tableCellKind
|
|
||||||
colspan uint
|
|
||||||
content string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tc *tableCell) asHtml(hyphaName string) string {
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"<%[1]s %[2]s>%[3]s</%[1]s>\n",
|
|
||||||
tc.kind.tagName(),
|
|
||||||
tc.colspanAttribute(),
|
|
||||||
tc.contentAsHtml(hyphaName),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tc *tableCell) colspanAttribute() string {
|
|
||||||
if tc.colspan <= 1 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(`colspan="%d"`, tc.colspan)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tc *tableCell) contentAsHtml(hyphaName string) (html string) {
|
|
||||||
for _, line := range strings.Split(tc.content, "\n") {
|
|
||||||
if line = strings.TrimSpace(line); line != "" {
|
|
||||||
if html != "" {
|
|
||||||
html += `<br>`
|
|
||||||
}
|
|
||||||
html += ParagraphToHtml(hyphaName, line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return html
|
|
||||||
}
|
|
||||||
|
|
||||||
type tableCellKind int
|
|
||||||
|
|
||||||
const (
|
|
||||||
tableCellUnknown tableCellKind = iota
|
|
||||||
tableCellHeader
|
|
||||||
tableCellDatum
|
|
||||||
)
|
|
||||||
|
|
||||||
func (tck tableCellKind) tagName() string {
|
|
||||||
switch tck {
|
|
||||||
case tableCellHeader:
|
|
||||||
return "th"
|
|
||||||
case tableCellDatum:
|
|
||||||
return "td"
|
|
||||||
default:
|
|
||||||
return "p"
|
|
||||||
}
|
|
||||||
}
|
|
38
markup/testdata/test.myco
vendored
38
markup/testdata/test.myco
vendored
@ -1,38 +0,0 @@
|
|||||||
# 1
|
|
||||||
## 2
|
|
||||||
### 3
|
|
||||||
> quote
|
|
||||||
|
|
||||||
* li 1
|
|
||||||
* li 2
|
|
||||||
text
|
|
||||||
more text
|
|
||||||
=> Pear some link
|
|
||||||
|
|
||||||
* li\n"+
|
|
||||||
```alt text goes here
|
|
||||||
=> preformatted text
|
|
||||||
where markup is not lexed
|
|
||||||
```it ends here"
|
|
||||||
=>linking
|
|
||||||
|
|
||||||
text
|
|
||||||
```
|
|
||||||
()
|
|
||||||
/\
|
|
||||||
```
|
|
||||||
<= Apple : 1..3
|
|
||||||
|
|
||||||
img {
|
|
||||||
hypha1
|
|
||||||
hypha2|
|
|
||||||
hypha3| 60
|
|
||||||
hypha4| { line1
|
|
||||||
line2
|
|
||||||
} this is ignored
|
|
||||||
|
|
||||||
hypha5| {
|
|
||||||
state of minnesota
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Function that returns a function that can strip `prefix` and trim whitespace when called.
|
|
||||||
func remover(prefix string) func(string) string {
|
|
||||||
return func(l string) string {
|
|
||||||
return strings.TrimSpace(strings.TrimPrefix(l, prefix))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,108 +0,0 @@
|
|||||||
package markup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
|
||||||
"path"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const xclError = -9
|
|
||||||
|
|
||||||
// Transclusion is used by markup parser to remember what hyphae shall be transcluded.
|
|
||||||
type Transclusion struct {
|
|
||||||
name string
|
|
||||||
from int // inclusive
|
|
||||||
to int // inclusive
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transclude transcludes `xcl` and returns html representation.
|
|
||||||
func Transclude(xcl Transclusion, recursionLevel int) (html string) {
|
|
||||||
recursionLevel++
|
|
||||||
tmptOk := `<section class="transclusion transclusion_ok">
|
|
||||||
<a class="transclusion__link" href="/page/%s">%s</a>
|
|
||||||
<div class="transclusion__content">%s</div>
|
|
||||||
</section>`
|
|
||||||
tmptFailed := `<section class="transclusion transclusion_failed">
|
|
||||||
<p class="error">Hypha <a class="wikilink_new" href="/page/%s">%s</a> does not exist</p>
|
|
||||||
</section>`
|
|
||||||
if xcl.from == xclError || xcl.to == xclError || xcl.from > xcl.to {
|
|
||||||
return fmt.Sprintf(tmptFailed, xcl.name, xcl.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
rawText, binaryHtml, err := HyphaAccess(xcl.name)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Sprintf(tmptFailed, xcl.name, xcl.name)
|
|
||||||
}
|
|
||||||
md := Doc(xcl.name, rawText)
|
|
||||||
xclText := Parse(md.lex(), xcl.from, xcl.to, recursionLevel)
|
|
||||||
return fmt.Sprintf(tmptOk, xcl.name, xcl.name, binaryHtml+xclText)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Grammar from hypha ‘transclusion’:
|
|
||||||
transclusion_line ::= transclusion_token hypha_name LWS* [":" LWS* range LWS*]
|
|
||||||
transclusion_token ::= "<=" LWS+
|
|
||||||
hypha_name ::= canonical_name | noncanonical_name
|
|
||||||
range ::= id | (from_id two_dots to_id) | (from_id two_dots) | (two_dots to_id)
|
|
||||||
two_dots ::= ".."
|
|
||||||
*/
|
|
||||||
|
|
||||||
func parseTransclusion(line, hyphaName string) (xclusion Transclusion) {
|
|
||||||
line = strings.TrimSpace(remover("<=")(line))
|
|
||||||
if line == "" {
|
|
||||||
return Transclusion{"", xclError, xclError}
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.ContainsRune(line, ':') {
|
|
||||||
parts := strings.SplitN(line, ":", 2)
|
|
||||||
xclusion.name = xclCanonicalName(hyphaName, strings.TrimSpace(parts[0]))
|
|
||||||
selector := strings.TrimSpace(parts[1])
|
|
||||||
xclusion.from, xclusion.to = parseSelector(selector)
|
|
||||||
} else {
|
|
||||||
xclusion.name = xclCanonicalName(hyphaName, strings.TrimSpace(line))
|
|
||||||
}
|
|
||||||
return xclusion
|
|
||||||
}
|
|
||||||
|
|
||||||
func xclCanonicalName(hyphaName, xclName string) string {
|
|
||||||
switch {
|
|
||||||
case strings.HasPrefix(xclName, "./"):
|
|
||||||
return util.CanonicalName(path.Join(hyphaName, strings.TrimPrefix(xclName, "./")))
|
|
||||||
case strings.HasPrefix(xclName, "../"):
|
|
||||||
return util.CanonicalName(path.Join(path.Dir(hyphaName), strings.TrimPrefix(xclName, "../")))
|
|
||||||
default:
|
|
||||||
return util.CanonicalName(xclName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point:
|
|
||||||
// selector ::= id
|
|
||||||
// | from ".."
|
|
||||||
// | from ".." to
|
|
||||||
// | ".." to
|
|
||||||
// If it is not, return (xclError, xclError).
|
|
||||||
func parseSelector(selector string) (from, to int) {
|
|
||||||
if selector == "" {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
if strings.Contains(selector, "..") {
|
|
||||||
parts := strings.Split(selector, "..")
|
|
||||||
|
|
||||||
var (
|
|
||||||
fromStr = strings.TrimSpace(parts[0])
|
|
||||||
from, fromErr = strconv.Atoi(fromStr)
|
|
||||||
toStr = strings.TrimSpace(parts[1])
|
|
||||||
to, toErr = strconv.Atoi(toStr)
|
|
||||||
)
|
|
||||||
if fromStr == "" && toStr == "" {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
if fromErr == nil || toErr == nil {
|
|
||||||
return from, to
|
|
||||||
}
|
|
||||||
} else if id, err := strconv.Atoi(selector); err == nil {
|
|
||||||
return id, id
|
|
||||||
}
|
|
||||||
return xclError, xclError
|
|
||||||
}
|
|
@ -2,11 +2,11 @@ package shroom
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
"github.com/bouncepaw/mycorrhiza/markup"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/views"
|
"github.com/bouncepaw/mycorrhiza/views"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycomarkup/legacy"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -29,10 +29,4 @@ func init() {
|
|||||||
λ(h.Name)
|
λ(h.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
markup.HyphaImageForOG = func(hyphaName string) string {
|
|
||||||
if h := hyphae.ByName(hyphaName); h.Exists && h.BinaryPath != "" {
|
|
||||||
return cfg.URL + "/binary/" + hyphaName
|
|
||||||
}
|
|
||||||
return cfg.URL + "/favicon.ico"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
package shroom
|
package shroom
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
"github.com/bouncepaw/mycorrhiza/markup"
|
|
||||||
|
"github.com/bouncepaw/mycomarkup/blocks"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FetchTextPart tries to read text file of the given hypha. If there is no file, empty string is returned.
|
// FetchTextPart tries to read text file of the given hypha. If there is no file, empty string is returned.
|
||||||
@ -32,7 +33,7 @@ func SetHeaderLinks() {
|
|||||||
cfg.SetDefaultHeaderLinks()
|
cfg.SetDefaultHeaderLinks()
|
||||||
} else {
|
} else {
|
||||||
text := string(contents)
|
text := string(contents)
|
||||||
cfg.ParseHeaderLinks(text, markup.Rocketlink)
|
cfg.ParseHeaderLinks(text, blocks.Rocketlink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,12 @@ import (
|
|||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/history"
|
"github.com/bouncepaw/mycorrhiza/history"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
"github.com/bouncepaw/mycorrhiza/markup"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/shroom"
|
"github.com/bouncepaw/mycorrhiza/shroom"
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
"github.com/bouncepaw/mycorrhiza/user"
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
"github.com/bouncepaw/mycorrhiza/views"
|
"github.com/bouncepaw/mycorrhiza/views"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycomarkup/legacy"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initMutators() {
|
func initMutators() {
|
||||||
|
@ -11,11 +11,12 @@ import (
|
|||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/history"
|
"github.com/bouncepaw/mycorrhiza/history"
|
||||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||||
"github.com/bouncepaw/mycorrhiza/markup"
|
|
||||||
"github.com/bouncepaw/mycorrhiza/mimetype"
|
"github.com/bouncepaw/mycorrhiza/mimetype"
|
||||||
"github.com/bouncepaw/mycorrhiza/user"
|
"github.com/bouncepaw/mycorrhiza/user"
|
||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
"github.com/bouncepaw/mycorrhiza/views"
|
"github.com/bouncepaw/mycorrhiza/views"
|
||||||
|
|
||||||
|
"github.com/bouncepaw/mycomarkup/legacy"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initReaders() {
|
func initReaders() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user