Add the core.LinkFormatterContext to have additional info for the link formatter

pull/71/head
Mickaël Menu 3 years ago
parent f0e4dc1f80
commit d51ada787c
No known key found for this signature in database
GPG Key ID: 53D73664CD359895

@ -273,8 +273,8 @@ func testLoader(opts LoaderOpts) *Loader {
loader.RegisterHelper("style", helpers.NewStyleHelper(opts.Styler, &util.NullLogger))
loader.RegisterHelper("slug", helpers.NewSlugHelper("en", &util.NullLogger))
formatter := func(path, title string) (string, error) {
return path + " - " + title, nil
formatter := func(context core.LinkFormatterContext) (string, error) {
return context.Path + " - " + context.Title, nil
}
loader.RegisterHelper("format-link", helpers.NewLinkHelper(formatter, &util.NullLogger))

@ -8,13 +8,19 @@ import (
// NewLinkHelper creates a new template helper to generate an internal link
// using a LinkFormatter.
//
// {{link "path/to/note.md" "An interesting subject"}} -> (depends on the LinkFormatter)
// {{format-link "path/to/note.md" "An interesting subject"}} -> (depends on the LinkFormatter)
// [[path/to/note]]
// [An interesting subject](path/to/note)
func NewLinkHelper(formatter core.LinkFormatter, logger util.Logger) interface{} {
return func(path string, opt interface{}) string {
title, _ := opt.(string)
link, err := formatter(path, title)
link, err := formatter(core.LinkFormatterContext{
Path: path,
RelPath: path,
AbsPath: path,
Title: title,
Metadata: map[string]interface{}{},
})
if err != nil {
logger.Err(err)
return ""

@ -3,14 +3,47 @@ package core
import (
"fmt"
"net/url"
"path/filepath"
"strings"
"github.com/mickael-menu/zk/internal/util/errors"
"github.com/mickael-menu/zk/internal/util/paths"
)
// Metadata used to generate a link.
type LinkFormatterContext struct {
// Filename of the note
Filename string
// File path to the note, relative to the notebook root.
Path string
// Absolute file path to the note.
AbsPath string `handlebars:"abs-path"`
// File path to the note, relative to the current directory.
RelPath string `handlebars:"rel-path"`
// Title of the note.
Title string
// Metadata extracted from the YAML frontmatter.
Metadata map[string]interface{}
}
func NewLinkFormatterContext(note Note, notebookDir string, currentDir string) (LinkFormatterContext, error) {
absPath := filepath.Join(notebookDir, note.Path)
relPath, err := filepath.Rel(currentDir, absPath)
if err != nil {
return LinkFormatterContext{}, err
}
return LinkFormatterContext{
Filename: filepath.Base(note.Path),
Path: note.Path,
AbsPath: absPath,
RelPath: relPath,
Title: note.Title,
Metadata: note.Metadata,
}, nil
}
// LinkFormatter formats internal links according to user configuration.
type LinkFormatter func(path string, title string) (string, error)
type LinkFormatter func(context LinkFormatterContext) (string, error)
// NewLinkFormatter generates a new LinkFormatter from the user Markdown
// configuration.
@ -26,8 +59,8 @@ func NewLinkFormatter(config MarkdownConfig, templateLoader TemplateLoader) (Lin
}
func NewMarkdownLinkFormatter(config MarkdownConfig, onlyHref bool) (LinkFormatter, error) {
return func(path, title string) (string, error) {
path = formatPath(path, config)
return func(context LinkFormatterContext) (string, error) {
path := formatPath(context.RelPath, config)
if !config.LinkEncodePath {
path = strings.ReplaceAll(path, `\`, `\\`)
path = strings.ReplaceAll(path, `)`, `\)`)
@ -35,6 +68,7 @@ func NewMarkdownLinkFormatter(config MarkdownConfig, onlyHref bool) (LinkFormatt
if onlyHref {
return fmt.Sprintf("(%s)", path), nil
} else {
title := context.Title
title = strings.ReplaceAll(title, `\`, `\\`)
title = strings.ReplaceAll(title, `]`, `\]`)
return fmt.Sprintf("[%s](%s)", title, path), nil
@ -43,8 +77,8 @@ func NewMarkdownLinkFormatter(config MarkdownConfig, onlyHref bool) (LinkFormatt
}
func NewWikiLinkFormatter(config MarkdownConfig) (LinkFormatter, error) {
return func(path, title string) (string, error) {
path = formatPath(path, config)
return func(context LinkFormatterContext) (string, error) {
path := formatPath(context.Path, config)
if !config.LinkEncodePath {
path = strings.ReplaceAll(path, `\`, `\\`)
path = strings.ReplaceAll(path, `]]`, `\]]`)
@ -60,17 +94,15 @@ func NewCustomLinkFormatter(config MarkdownConfig, templateLoader TemplateLoader
return nil, wrap(err)
}
return func(path, title string) (string, error) {
path = formatPath(path, config)
return template.Render(customLinkRenderContext{Path: path, Title: title})
return func(context LinkFormatterContext) (string, error) {
context.Filename = formatPath(context.Filename, config)
context.Path = formatPath(context.Path, config)
context.RelPath = formatPath(context.RelPath, config)
context.AbsPath = formatPath(context.AbsPath, config)
return template.Render(context)
}, nil
}
type customLinkRenderContext struct {
Path string
Title string
}
func formatPath(path string, config MarkdownConfig) string {
if config.LinkDropExtension {
path = paths.DropExt(path)

@ -1,6 +1,7 @@
package core
import (
"path/filepath"
"testing"
"github.com/mickael-menu/zk/internal/util/test/assert"
@ -16,7 +17,13 @@ func TestMarkdownLinkFormatter(t *testing.T) {
assert.Nil(t, err)
return func(path, title, expected string) {
actual, err := formatter(path, title)
actual, err := formatter(LinkFormatterContext{
Filename: "filename",
Path: "path",
RelPath: path,
AbsPath: "abs-path",
Title: title,
})
assert.Nil(t, err)
assert.Equal(t, actual, expected)
}
@ -46,7 +53,13 @@ func TestMarkdownLinkFormatterOnlyHref(t *testing.T) {
assert.Nil(t, err)
return func(path, expected string) {
actual, err := formatter(path, "")
actual, err := formatter(LinkFormatterContext{
Filename: "filename",
Path: "path",
RelPath: path,
AbsPath: "abs-path",
Title: "title",
})
assert.Nil(t, err)
assert.Equal(t, actual, expected)
}
@ -76,7 +89,13 @@ func TestWikiLinkFormatter(t *testing.T) {
assert.Nil(t, err)
return func(path, title, expected string) {
actual, err := formatter(path, title)
actual, err := formatter(LinkFormatterContext{
Filename: "filename",
Path: path,
RelPath: "rel-path",
AbsPath: "abs-path",
Title: "title",
})
assert.Nil(t, err)
assert.Equal(t, actual, expected)
}
@ -98,8 +117,8 @@ func TestWikiLinkFormatter(t *testing.T) {
}
func TestCustomLinkFormatter(t *testing.T) {
newTester := func(encodePath, dropExtension bool) func(path, title string, expected customLinkRenderContext) {
return func(path, title string, expected customLinkRenderContext) {
newTester := func(encodePath, dropExtension bool) func(path, title string, expected LinkFormatterContext) {
return func(path, title string, expected LinkFormatterContext) {
loader := newTemplateLoaderMock()
template := loader.SpyString("custom")
@ -110,7 +129,13 @@ func TestCustomLinkFormatter(t *testing.T) {
}, loader)
assert.Nil(t, err)
actual, err := formatter(path, title)
actual, err := formatter(LinkFormatterContext{
Filename: filepath.Base(path),
Path: path,
AbsPath: "/" + path,
RelPath: "../" + path,
Title: title,
})
assert.Nil(t, err)
assert.Equal(t, actual, "custom")
assert.Equal(t, template.Contexts, []interface{}{expected})
@ -118,29 +143,54 @@ func TestCustomLinkFormatter(t *testing.T) {
}
test := newTester(false, false)
test("path/to note.md", "", customLinkRenderContext{Path: "path/to note.md"})
test("", "", customLinkRenderContext{})
test("path/to note.md", "An interesting subject", customLinkRenderContext{
Title: "An interesting subject",
Path: "path/to note.md",
test("path/to note.md", "", LinkFormatterContext{
Filename: "to note.md",
Path: "path/to note.md",
AbsPath: "/path/to note.md",
RelPath: "../path/to note.md",
})
test(`path/(no\te).md`, `An [interesting] \subject`, customLinkRenderContext{
Title: `An [interesting] \subject`,
Path: `path/(no\te).md`,
test("", "", LinkFormatterContext{
Filename: "",
Path: "",
AbsPath: "/",
RelPath: "../",
})
test("path/to note.md", "An interesting subject", LinkFormatterContext{
Filename: "to note.md",
Path: "path/to note.md",
AbsPath: "/path/to note.md",
RelPath: "../path/to note.md",
Title: "An interesting subject",
})
test(`path/(no\te).md`, `An [interesting] \subject`, LinkFormatterContext{
Filename: `(no\te).md`,
Path: `path/(no\te).md`,
AbsPath: `/path/(no\te).md`,
RelPath: `../path/(no\te).md`,
Title: `An [interesting] \subject`,
})
test = newTester(true, false)
test("path/to note.md", "An interesting subject", customLinkRenderContext{
Title: "An interesting subject",
Path: "path/to%20note.md",
test("path/to note.md", "An interesting subject", LinkFormatterContext{
Filename: "to%20note.md",
Path: "path/to%20note.md",
AbsPath: "/path/to%20note.md",
RelPath: "../path/to%20note.md",
Title: "An interesting subject",
})
test = newTester(false, true)
test("path/to note.md", "An interesting subject", customLinkRenderContext{
Title: "An interesting subject",
Path: "path/to note",
test("path/to note.md", "An interesting subject", LinkFormatterContext{
Filename: "to note",
Path: "path/to note",
AbsPath: "/path/to note",
RelPath: "../path/to note",
Title: "An interesting subject",
})
test = newTester(true, true)
test("path/to note.md", "An interesting subject", customLinkRenderContext{
Title: "An interesting subject",
Path: "path/to%20note",
test("path/to note.md", "An interesting subject", LinkFormatterContext{
Filename: "to%20note",
Path: "path/to%20note",
AbsPath: "/path/to%20note",
RelPath: "../path/to%20note",
Title: "An interesting subject",
})
}

@ -38,7 +38,13 @@ func newNoteFormatter(basePath string, template Template, linkFormatter LinkForm
AbsPath: absPath,
Title: note.Title,
Link: newLazyStringer(func() string {
link, _ := linkFormatter(path, note.Title)
link, _ := linkFormatter(LinkFormatterContext{
Path: note.Path,
RelPath: path,
AbsPath: absPath,
Title: note.Title,
Metadata: note.Metadata,
})
return link
}),
Lead: note.Lead,

Loading…
Cancel
Save