mirror of
https://github.com/mickael-menu/zk
synced 2024-11-07 15:20:21 +00:00
Parse labels in [[wiki links | label]] and support escaped characters
This commit is contained in:
parent
a5b7639bcd
commit
6175a73c1c
@ -3,6 +3,7 @@ package extensions
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/mickael-menu/zk/core/note"
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
@ -10,7 +11,7 @@ import (
|
||||
"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]]#.
|
||||
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 {
|
||||
line, _ := block.PeekLine()
|
||||
|
||||
openerCharCount := 0
|
||||
opened := false
|
||||
closed := false
|
||||
content := []byte{}
|
||||
closerCharCount := 0
|
||||
endPos := 0
|
||||
var (
|
||||
href string
|
||||
label string
|
||||
rel note.LinkRelation
|
||||
)
|
||||
|
||||
// Folgezettel direction: -1 down, 0 unknown, 1 up
|
||||
direction := 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
|
||||
)
|
||||
|
||||
appendChar := func(c byte) {
|
||||
if parsingLabel {
|
||||
label += string(c)
|
||||
} else {
|
||||
href += string(c)
|
||||
}
|
||||
}
|
||||
|
||||
for i, char := range line {
|
||||
endPos = i
|
||||
|
||||
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 == '#' {
|
||||
direction = -1
|
||||
rel = note.LinkRelationDown
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if !opened {
|
||||
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 '#':
|
||||
direction = 1
|
||||
rel = note.LinkRelationUp
|
||||
continue
|
||||
case '[':
|
||||
openerCharCount += 1
|
||||
@ -72,40 +87,59 @@ func (p *wlParser) Parse(parent ast.Node, block text.Reader, pc parser.Context)
|
||||
}
|
||||
opened = true
|
||||
|
||||
if char == ']' {
|
||||
closerCharCount += 1
|
||||
if closerCharCount == openerCharCount {
|
||||
closed = true
|
||||
// neuron's legacy [[[Folgezettel]]].
|
||||
if closerCharCount == 3 {
|
||||
direction = -1
|
||||
if !escaping {
|
||||
switch char {
|
||||
|
||||
case '|': // [[href | label]]
|
||||
parsingLabel = true
|
||||
continue
|
||||
|
||||
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 {
|
||||
content = append(content, strings.Repeat("]", closerCharCount)...)
|
||||
closerCharCount = 0
|
||||
}
|
||||
content = append(content, char)
|
||||
}
|
||||
escaping = false
|
||||
|
||||
// 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(']')
|
||||
}
|
||||
closerCharCount = 0
|
||||
}
|
||||
appendChar(char)
|
||||
}
|
||||
|
||||
if !closed || len(content) == 0 {
|
||||
if !closed || len(href) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
block.Advance(endPos)
|
||||
|
||||
link := ast.NewLink()
|
||||
link.Destination = content
|
||||
|
||||
// Title will be parsed as the link's rels.
|
||||
switch direction {
|
||||
case -1:
|
||||
link.Title = []byte("down")
|
||||
case 1:
|
||||
link.Title = []byte("up")
|
||||
href = strings.TrimSpace(href)
|
||||
label = strings.TrimSpace(label)
|
||||
if len(label) == 0 {
|
||||
label = href
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -170,7 +170,9 @@ A link can have [one relation](one "rel-1") or [several relations](several "rel-
|
||||
|
||||
An https://inline-link.com and http://another-inline-link.com.
|
||||
|
||||
A [[Wiki link]] is surrounded by two brackets.
|
||||
A [[Wiki link]] is surrounded by [[2-brackets | two brackets]].
|
||||
|
||||
It can contain [[esca]\]ped \[chara\\cters]].
|
||||
|
||||
A [[[Folgezettel link]]] is surrounded by three brackets.
|
||||
|
||||
@ -178,6 +180,8 @@ Neuron also supports a [[trailing hash]]# for Folgezettel links.
|
||||
|
||||
A #[[leading hash]] is used for #uplinks.
|
||||
|
||||
Neuron links with titles: [[trailing|Trailing link]]# #[[leading | Leading link]]
|
||||
|
||||
[External links](http://example.com) are marked [as such](ftp://domain).
|
||||
`, []note.Link{
|
||||
{
|
||||
@ -234,33 +238,61 @@ A link can have [one relation](one "rel-1") or [several relations](several "rel-
|
||||
Snippet: "An https://inline-link.com and http://another-inline-link.com.",
|
||||
},
|
||||
{
|
||||
Title: "",
|
||||
Title: "Wiki link",
|
||||
Href: "Wiki link",
|
||||
External: false,
|
||||
Rels: []string{},
|
||||
Snippet: "A [[Wiki link]] is surrounded by two brackets.",
|
||||
Snippet: "A [[Wiki link]] is surrounded by [[2-brackets | two brackets]].",
|
||||
},
|
||||
{
|
||||
Title: "",
|
||||
Title: "two brackets",
|
||||
Href: "2-brackets",
|
||||
External: false,
|
||||
Rels: []string{},
|
||||
Snippet: "A [[Wiki link]] is surrounded by [[2-brackets | two brackets]].",
|
||||
},
|
||||
{
|
||||
Title: `esca]]ped [chara\cters`,
|
||||
Href: `esca]]ped [chara\cters`,
|
||||
External: false,
|
||||
Rels: []string{},
|
||||
Snippet: `It can contain [[esca]\]ped \[chara\\cters]].`,
|
||||
},
|
||||
{
|
||||
Title: "Folgezettel link",
|
||||
Href: "Folgezettel link",
|
||||
External: false,
|
||||
Rels: []string{"down"},
|
||||
Snippet: "A [[[Folgezettel link]]] is surrounded by three brackets.",
|
||||
},
|
||||
{
|
||||
Title: "",
|
||||
Title: "trailing hash",
|
||||
Href: "trailing hash",
|
||||
External: false,
|
||||
Rels: []string{"down"},
|
||||
Snippet: "Neuron also supports a [[trailing hash]]# for Folgezettel links.",
|
||||
},
|
||||
{
|
||||
Title: "",
|
||||
Title: "leading hash",
|
||||
Href: "leading hash",
|
||||
External: false,
|
||||
Rels: []string{"up"},
|
||||
Snippet: "A #[[leading hash]] is used for #uplinks.",
|
||||
},
|
||||
{
|
||||
Title: "Trailing link",
|
||||
Href: "trailing",
|
||||
External: false,
|
||||
Rels: []string{"down"},
|
||||
Snippet: "Neuron links with titles: [[trailing|Trailing link]]# #[[leading | Leading link]]",
|
||||
},
|
||||
{
|
||||
Title: "Leading link",
|
||||
Href: "leading",
|
||||
External: false,
|
||||
Rels: []string{"up"},
|
||||
Snippet: "Neuron links with titles: [[trailing|Trailing link]]# #[[leading | Leading link]]",
|
||||
},
|
||||
{
|
||||
Title: "External links",
|
||||
Href: "http://example.com",
|
||||
|
@ -23,6 +23,16 @@ type Link struct {
|
||||
Snippet string
|
||||
}
|
||||
|
||||
// LinkRelation defines the relationship between a link's source and target.
|
||||
type LinkRelation string
|
||||
|
||||
const (
|
||||
// LinkRelationDown defines the target note as a child of the source.
|
||||
LinkRelationDown LinkRelation = "down"
|
||||
// LinkRelationDown defines the target note as a parent of the source.
|
||||
LinkRelationUp LinkRelation = "up"
|
||||
)
|
||||
|
||||
type Parser interface {
|
||||
Parse(source string) (*Content, error)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user