|
|
@ -3,6 +3,7 @@ package extensions
|
|
|
|
import (
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/mickael-menu/zk/core/note"
|
|
|
|
"github.com/yuin/goldmark"
|
|
|
|
"github.com/yuin/goldmark"
|
|
|
|
"github.com/yuin/goldmark/ast"
|
|
|
|
"github.com/yuin/goldmark/ast"
|
|
|
|
"github.com/yuin/goldmark/parser"
|
|
|
|
"github.com/yuin/goldmark/parser"
|
|
|
@ -10,7 +11,7 @@ import (
|
|
|
|
"github.com/yuin/goldmark/util"
|
|
|
|
"github.com/yuin/goldmark/util"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// WikiLink is an extension parsing wiki links and neuron's Folgezettel.
|
|
|
|
// WikiLink is an extension parsing wiki links and Neuron's Folgezettel.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// For example, [[wiki link]], [[[legacy downlink]]], #[[uplink]], [[downlink]]#.
|
|
|
|
// For example, [[wiki link]], [[[legacy downlink]]], #[[uplink]], [[downlink]]#.
|
|
|
|
var WikiLink = &wikiLink{}
|
|
|
|
var WikiLink = &wikiLink{}
|
|
|
@ -34,32 +35,46 @@ func (p *wlParser) Trigger() []byte {
|
|
|
|
func (p *wlParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
|
|
|
|
func (p *wlParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
|
|
|
|
line, _ := block.PeekLine()
|
|
|
|
line, _ := block.PeekLine()
|
|
|
|
|
|
|
|
|
|
|
|
openerCharCount := 0
|
|
|
|
var (
|
|
|
|
opened := false
|
|
|
|
href string
|
|
|
|
closed := false
|
|
|
|
label string
|
|
|
|
content := []byte{}
|
|
|
|
rel note.LinkRelation
|
|
|
|
closerCharCount := 0
|
|
|
|
)
|
|
|
|
endPos := 0
|
|
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
|
|
|
opened = false // Found at least [[
|
|
|
|
|
|
|
|
closed = false // Found at least ]]
|
|
|
|
|
|
|
|
escaping = false // Found a backslash, next character will be literal
|
|
|
|
|
|
|
|
parsingLabel = false // Found a | in a Wikilink, now we parse the link's label
|
|
|
|
|
|
|
|
openerCharCount = 0 // Number of [ encountered
|
|
|
|
|
|
|
|
closerCharCount = 0 // Number of ] encountered
|
|
|
|
|
|
|
|
endPos = 0 // Last position of the link in the line
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// Folgezettel direction: -1 down, 0 unknown, 1 up
|
|
|
|
appendChar := func(c byte) {
|
|
|
|
direction := 0
|
|
|
|
if parsingLabel {
|
|
|
|
|
|
|
|
label += string(c)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
href += string(c)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for i, char := range line {
|
|
|
|
for i, char := range line {
|
|
|
|
endPos = i
|
|
|
|
endPos = i
|
|
|
|
|
|
|
|
|
|
|
|
if closed {
|
|
|
|
if closed {
|
|
|
|
// Supports trailing hash syntax for neuron's Folgezettel, e.g. [[id]]#
|
|
|
|
// Supports trailing hash syntax for Neuron's Folgezettel, e.g. [[id]]#
|
|
|
|
if char == '#' {
|
|
|
|
if char == '#' {
|
|
|
|
direction = -1
|
|
|
|
rel = note.LinkRelationDown
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if !opened {
|
|
|
|
if !opened {
|
|
|
|
switch char {
|
|
|
|
switch char {
|
|
|
|
// Supports leading hash syntax for neuron's Folgezettel, e.g. #[[id]]
|
|
|
|
// Supports leading hash syntax for Neuron's Folgezettel, e.g. #[[id]]
|
|
|
|
case '#':
|
|
|
|
case '#':
|
|
|
|
direction = 1
|
|
|
|
rel = note.LinkRelationUp
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
case '[':
|
|
|
|
case '[':
|
|
|
|
openerCharCount += 1
|
|
|
|
openerCharCount += 1
|
|
|
@ -72,40 +87,59 @@ func (p *wlParser) Parse(parent ast.Node, block text.Reader, pc parser.Context)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
opened = true
|
|
|
|
opened = true
|
|
|
|
|
|
|
|
|
|
|
|
if char == ']' {
|
|
|
|
if !escaping {
|
|
|
|
closerCharCount += 1
|
|
|
|
switch char {
|
|
|
|
if closerCharCount == openerCharCount {
|
|
|
|
|
|
|
|
closed = true
|
|
|
|
case '|': // [[href | label]]
|
|
|
|
// neuron's legacy [[[Folgezettel]]].
|
|
|
|
parsingLabel = true
|
|
|
|
if closerCharCount == 3 {
|
|
|
|
continue
|
|
|
|
direction = -1
|
|
|
|
|
|
|
|
|
|
|
|
case '\\':
|
|
|
|
|
|
|
|
escaping = true
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case ']':
|
|
|
|
|
|
|
|
closerCharCount += 1
|
|
|
|
|
|
|
|
if closerCharCount == openerCharCount {
|
|
|
|
|
|
|
|
closed = true
|
|
|
|
|
|
|
|
// Neuron's legacy [[[Folgezettel]]].
|
|
|
|
|
|
|
|
if closerCharCount == 3 {
|
|
|
|
|
|
|
|
rel = note.LinkRelationDown
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
}
|
|
|
|
if closerCharCount > 0 {
|
|
|
|
escaping = false
|
|
|
|
content = append(content, strings.Repeat("]", closerCharCount)...)
|
|
|
|
|
|
|
|
closerCharCount = 0
|
|
|
|
// Found incomplete number of closing brackets to close the link.
|
|
|
|
|
|
|
|
// We add them to the HREF and reset the count.
|
|
|
|
|
|
|
|
if closerCharCount > 0 {
|
|
|
|
|
|
|
|
for i := 0; i < closerCharCount; i++ {
|
|
|
|
|
|
|
|
appendChar(']')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
content = append(content, char)
|
|
|
|
closerCharCount = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
appendChar(char)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if !closed || len(content) == 0 {
|
|
|
|
if !closed || len(href) == 0 {
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
block.Advance(endPos)
|
|
|
|
block.Advance(endPos)
|
|
|
|
|
|
|
|
|
|
|
|
link := ast.NewLink()
|
|
|
|
href = strings.TrimSpace(href)
|
|
|
|
link.Destination = content
|
|
|
|
label = strings.TrimSpace(label)
|
|
|
|
|
|
|
|
if len(label) == 0 {
|
|
|
|
// Title will be parsed as the link's rels.
|
|
|
|
label = href
|
|
|
|
switch direction {
|
|
|
|
|
|
|
|
case -1:
|
|
|
|
|
|
|
|
link.Title = []byte("down")
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
|
|
link.Title = []byte("up")
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
link := ast.NewLink()
|
|
|
|
|
|
|
|
link.Destination = []byte(href)
|
|
|
|
|
|
|
|
// Title will be parsed as the link's rel by the Markdown parser.
|
|
|
|
|
|
|
|
link.Title = []byte(rel)
|
|
|
|
|
|
|
|
link.AppendChild(link, ast.NewString([]byte(label)))
|
|
|
|
|
|
|
|
|
|
|
|
return link
|
|
|
|
return link
|
|
|
|
}
|
|
|
|
}
|
|
|
|