2021-04-18 14:37:54 +00:00
|
|
|
package core
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net/url"
|
|
|
|
"strings"
|
|
|
|
|
2024-01-10 21:47:22 +00:00
|
|
|
"github.com/zk-org/zk/internal/util/errors"
|
|
|
|
"github.com/zk-org/zk/internal/util/paths"
|
2021-04-18 14:37:54 +00:00
|
|
|
)
|
|
|
|
|
2021-09-25 17:28:29 +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"`
|
2021-09-25 17:28:29 +00:00
|
|
|
// Absolute file path to the note.
|
2022-01-10 13:28:14 +00:00
|
|
|
AbsPath string `json:"absPath" handlebars:"abs-path"`
|
2021-09-25 17:28:29 +00:00
|
|
|
// 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"`
|
2021-09-25 17:28:29 +00:00
|
|
|
// Title of the note.
|
2022-01-10 13:28:14 +00:00
|
|
|
Title string `json:"title"`
|
2021-09-25 17:28:29 +00:00
|
|
|
// Metadata extracted from the YAML frontmatter.
|
2022-01-10 13:28:14 +00:00
|
|
|
Metadata map[string]interface{} `json:"metadata"`
|
2021-09-25 17:28:29 +00:00
|
|
|
}
|
|
|
|
|
2022-01-10 13:28:14 +00:00
|
|
|
func NewLinkFormatterContext(path NotebookPath, title string, metadata map[string]interface{}) (LinkFormatterContext, error) {
|
|
|
|
relPath, err := path.PathRelToWorkingDir()
|
2021-09-25 17:28:29 +00:00
|
|
|
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(),
|
2021-09-25 17:28:29 +00:00
|
|
|
RelPath: relPath,
|
2022-01-10 13:28:14 +00:00
|
|
|
Title: title,
|
|
|
|
Metadata: metadata,
|
2021-09-25 17:28:29 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-04-18 14:37:54 +00:00
|
|
|
// LinkFormatter formats internal links according to user configuration.
|
2021-09-25 17:28:29 +00:00
|
|
|
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", "":
|
2021-05-23 13:26:04 +00:00
|
|
|
return NewMarkdownLinkFormatter(config, false)
|
2021-04-18 14:37:54 +00:00
|
|
|
case "wiki":
|
2021-05-23 13:26:04 +00:00
|
|
|
return NewWikiLinkFormatter(config)
|
2021-04-18 14:37:54 +00:00
|
|
|
default:
|
2021-05-23 13:26:04 +00:00
|
|
|
return NewCustomLinkFormatter(config, templateLoader)
|
2021-04-18 14:37:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-23 13:26:04 +00:00
|
|
|
func NewMarkdownLinkFormatter(config MarkdownConfig, onlyHref bool) (LinkFormatter, error) {
|
2021-09-25 17:28:29 +00:00
|
|
|
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, `)`, `\)`)
|
|
|
|
}
|
2021-05-23 13:26:04 +00:00
|
|
|
if onlyHref {
|
|
|
|
return fmt.Sprintf("(%s)", path), nil
|
|
|
|
} else {
|
2021-09-25 17:28:29 +00:00
|
|
|
title := context.Title
|
2021-05-23 13:26:04 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-05-23 13:26:04 +00:00
|
|
|
func NewWikiLinkFormatter(config MarkdownConfig) (LinkFormatter, error) {
|
2021-09-25 17:28:29 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-05-23 13:26:04 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2021-09-25 17:28:29 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-05-23 13:26:04 +00:00
|
|
|
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
|
|
|
|
}
|