Parse Markdown links

pull/6/head
Mickaël Menu 3 years ago
parent 467acb3a08
commit 3d967746d1
No known key found for this signature in database
GPG Key ID: 53D73664CD359895

@ -31,9 +31,7 @@ func NewParser() *Parser {
}
// Parse implements note.Parse.
func (p *Parser) Parse(source string) (note.Content, error) {
out := note.Content{}
func (p *Parser) Parse(source string) (*note.Content, error) {
bytes := []byte(source)
context := parser.NewContext()
@ -42,21 +40,28 @@ func (p *Parser) Parse(source string) (note.Content, error) {
parser.WithContext(context),
)
links, err := parseLinks(root, bytes)
if err != nil {
return nil, err
}
frontmatter, err := parseFrontmatter(context, bytes)
if err != nil {
return out, err
return nil, err
}
title, bodyStart, err := parseTitle(frontmatter, root, bytes)
if err != nil {
return out, err
return nil, err
}
out.Title = title
out.Body = parseBody(bodyStart, bytes)
out.Lead = parseLead(out.Body)
return out, nil
body := parseBody(bodyStart, bytes)
return &note.Content{
Title: title,
Body: body,
Lead: parseLead(body),
Links: links,
}, nil
}
// parseTitle extracts the note title with its node.
@ -116,6 +121,26 @@ func parseLead(body opt.String) opt.String {
return opt.NewNotEmptyString(strings.TrimSpace(lead))
}
// parseLinks extracts outbound links from the note.
func parseLinks(root ast.Node, source []byte) ([]note.Link, error) {
links := make([]note.Link, 0)
err := ast.Walk(root, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
if link, ok := n.(*ast.Link); ok && entering {
target := string(link.Destination)
if target != "" {
links = append(links, note.Link{
Title: string(link.Text(source)),
Target: target,
Rels: strings.Fields(string(link.Title)),
})
}
}
return ast.WalkContinue, nil
})
return links, err
}
// frontmatter contains metadata parsed from a YAML frontmatter.
type frontmatter struct {
values map[string]interface{}

@ -153,8 +153,56 @@ Paragraph`,
)
}
func TestParseLinks(t *testing.T) {
test := func(source string, links []note.Link) {
content := parse(t, source)
assert.Equal(t, content.Links, links)
}
test("", []note.Link{})
test("No links around here", []note.Link{})
test(`
# Heading with a [link](heading)
Paragraph containing [multiple **links**](stripped-formatting) some [external like this one](http://example.com), and other [relative](../other).
A link can have [one relation](one "rel-1") or [several relations](several "rel-1 rel-2").
`, []note.Link{
{
Title: "link",
Target: "heading",
Rels: []string{},
},
{
Title: "multiple links",
Target: "stripped-formatting",
Rels: []string{},
},
{
Title: "external like this one",
Target: "http://example.com",
Rels: []string{},
},
{
Title: "relative",
Target: "../other",
Rels: []string{},
},
{
Title: "one relation",
Target: "one",
Rels: []string{"rel-1"},
},
{
Title: "several relations",
Target: "several",
Rels: []string{"rel-1", "rel-2"},
},
})
}
func parse(t *testing.T, source string) note.Content {
content, err := NewParser().Parse(source)
assert.Nil(t, err)
return content
return *content
}

@ -11,8 +11,16 @@ type Content struct {
Lead opt.String
// Body is the content of the note, including the Lead but without the Title.
Body opt.String
// Links is the list of outbound links found in the note.
Links []Link
}
type Link struct {
Title string
Target string
Rels []string
}
type Parser interface {
Parse(source string) (Content, error)
Parse(source string) (*Content, error)
}

Loading…
Cancel
Save