zk/internal/core/link_format.go

113 lines
3.4 KiB
Go
Raw Normal View History

2021-04-18 14:37:54 +00:00
package core
import (
"fmt"
"net/url"
"strings"
"github.com/zk-org/zk/internal/util/errors"
"github.com/zk-org/zk/internal/util/paths"
2021-04-18 14:37:54 +00:00
)
// Metadata used to generate a link.
type LinkFormatterContext struct {
// Filename of the note
2022-01-10 13:28:14 +00:00
Filename string `json:"filename"`
// File path to the note, relative to the notebook root.link-encode-path
Path string `json:"path"`
// Absolute file path to the note.
2022-01-10 13:28:14 +00:00
AbsPath string `json:"absPath" handlebars:"abs-path"`
// File path to the note, relative to the current directory.
2022-01-10 13:28:14 +00:00
RelPath string `json:"relPath" handlebars:"rel-path"`
// Title of the note.
2022-01-10 13:28:14 +00:00
Title string `json:"title"`
// Metadata extracted from the YAML frontmatter.
2022-01-10 13:28:14 +00:00
Metadata map[string]interface{} `json:"metadata"`
}
2022-01-10 13:28:14 +00:00
func NewLinkFormatterContext(path NotebookPath, title string, metadata map[string]interface{}) (LinkFormatterContext, error) {
relPath, err := path.PathRelToWorkingDir()
if err != nil {
return LinkFormatterContext{}, err
}
return LinkFormatterContext{
2022-01-10 13:28:14 +00:00
Filename: path.Filename(),
Path: path.Path,
AbsPath: path.AbsPath(),
RelPath: relPath,
2022-01-10 13:28:14 +00:00
Title: title,
Metadata: metadata,
}, nil
}
2021-04-18 14:37:54 +00:00
// LinkFormatter formats internal links according to user configuration.
type LinkFormatter func(context LinkFormatterContext) (string, error)
2021-04-18 14:37:54 +00:00
// NewLinkFormatter generates a new LinkFormatter from the user Markdown
// configuration.
func NewLinkFormatter(config MarkdownConfig, templateLoader TemplateLoader) (LinkFormatter, error) {
switch config.LinkFormat {
case "markdown", "":
return NewMarkdownLinkFormatter(config, false)
2021-04-18 14:37:54 +00:00
case "wiki":
return NewWikiLinkFormatter(config)
2021-04-18 14:37:54 +00:00
default:
return NewCustomLinkFormatter(config, templateLoader)
2021-04-18 14:37:54 +00:00
}
}
func NewMarkdownLinkFormatter(config MarkdownConfig, onlyHref bool) (LinkFormatter, error) {
return func(context LinkFormatterContext) (string, error) {
path := formatPath(context.RelPath, config)
2021-04-18 14:37:54 +00:00
if !config.LinkEncodePath {
path = strings.ReplaceAll(path, `\`, `\\`)
path = strings.ReplaceAll(path, `)`, `\)`)
}
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
}
2021-04-18 14:37:54 +00:00
}, nil
}
func NewWikiLinkFormatter(config MarkdownConfig) (LinkFormatter, error) {
return func(context LinkFormatterContext) (string, error) {
path := formatPath(context.Path, config)
2021-04-18 14:37:54 +00:00
if !config.LinkEncodePath {
path = strings.ReplaceAll(path, `\`, `\\`)
path = strings.ReplaceAll(path, `]]`, `\]]`)
}
return "[[" + path + "]]", nil
}, nil
}
func NewCustomLinkFormatter(config MarkdownConfig, templateLoader TemplateLoader) (LinkFormatter, error) {
2021-04-18 14:37:54 +00:00
wrap := errors.Wrapperf("failed to render custom link with format: %s", config.LinkFormat)
template, err := templateLoader.LoadTemplate(config.LinkFormat)
if err != nil {
return nil, wrap(err)
}
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)
2021-04-18 14:37:54 +00:00
}, nil
}
func formatPath(path string, config MarkdownConfig) string {
if config.LinkDropExtension {
path = paths.DropExt(path)
}
if config.LinkEncodePath {
path = strings.ReplaceAll(url.PathEscape(path), "%2F", "/")
}
return path
}