Parse labels in [[wiki links | label]] and support escaped characters

This commit is contained in:
Mickaël Menu 2021-02-28 11:21:33 +01:00
parent a5b7639bcd
commit 6175a73c1c
No known key found for this signature in database
GPG Key ID: 53D73664CD359895
4 changed files with 118 additions and 44 deletions

View File

@ -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
}

View File

@ -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",

View File

@ -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)
}

2
run
View File

@ -1,2 +0,0 @@
#!/bin/bash
./go run main.go $@