Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ var template string
var mermaidJS string

func main() {
// Warnings go to stderr without a timestamp, like the messages from check()
log.SetFlags(0)
log.SetPrefix("mdview: ")

var outfilePtr = flag.String("o", "", "Output filename. (Optional)")
var versionPtr = flag.Bool("version", false, "Prints mdview version.")
var helpPtr = flag.Bool("help", false, "Prints mdview help message.")
Expand Down Expand Up @@ -118,10 +122,10 @@ func main() {

// Parse markdown to AST
doc := md.Parser().Parse(text.NewReader(dat))

// Extract title from AST
title := getTitleFromAST(doc, dat)

// Render to HTML
var buf bytes.Buffer
check(md.Renderer().Render(&buf, dat, doc))
Expand Down Expand Up @@ -239,7 +243,7 @@ func getTempDir() string {
}

if isSnap() {
var tmpdir = os.Getenv("HOME") + "/mdview-temp"
var tmpdir = filepath.Join(os.Getenv("HOME"), "mdview-temp")
check(os.MkdirAll(tmpdir, 0700))
return tmpdir
}
Expand Down Expand Up @@ -594,10 +598,10 @@ func hasMermaidBlock(doc ast.Node) bool {
// embedMermaidScript adds the embedded Mermaid.js library to the HTML content
// Since we set NoScript: true on the Mermaid extender, we need to manually add the script
func embedMermaidScript(htmlContent string) string {
// Escape any </script> tags (with closing >) inside the mermaid.js code
// Escape any </script> tags (with closing >) inside the mermaid.js code
// to prevent premature script closure. The standard way is to escape the forward slash.
escapedMermaidJS := strings.ReplaceAll(mermaidJS, "</script>", "<\\/script>")

// Add the embedded Mermaid.js and initialization at the end of the content
// Initialize mermaid with theme detection
initScript := `
Expand All @@ -614,6 +618,6 @@ func embedMermaidScript(htmlContent string) string {
});
`
inlineScript := fmt.Sprintf("<script>%s</script><script>%s</script>", escapedMermaidJS, initScript)

return htmlContent + inlineScript
}
68 changes: 68 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,3 +388,71 @@ func TestRemoveStaleTempFiles(t *testing.T) {
}
}
}

func TestGetTitleFromAST(t *testing.T) {
tests := []struct {
name string
markdown string
want string
}{
{"simple", "# Hello\n", "Hello"},
{"first H1 wins", "# First\n\n# Second\n", "First"},
{"skips other levels", "## Sub\n\n# Main\n", "Main"},
{"no H1", "## Sub\n\ntext\n", ""},
{"empty document", "", ""},
{"inline markup", "# Hello *big* **bold** `code` [link](http://x)\n", "Hello big bold code link"},
{"setext heading", "Title\n=====\n", "Title"},
{"surrounding space", "# Spaced \n", "Spaced"},
{"H1 below a paragraph", "text\n\n# Later\n", "Later"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
source := []byte(tt.markdown)
doc := goldmark.New().Parser().Parse(text.NewReader(source))
if got := getTitleFromAST(doc, source); got != tt.want {
t.Fatalf("getTitleFromAST = %q, want %q", got, tt.want)
}
})
}
}

func TestIsRelativePath(t *testing.T) {
tests := map[string]bool{
"image.png": true,
"./image.png": true,
"../image.png": true,
"dir/image.png": true,
"my image.png": true,
"": false,
"/etc/image.png": false,
"//cdn.example.com/a.png": false,
"http://example.com/a.png": false,
"https://example.com/a": false,
"data:image/png;base64,AA": false,
"file:///tmp/a.png": false,
"ftp://example.com/a.png": false,
"blob:abc": false,
"mailto:a@b.c": false,
}
for path, want := range tests {
if got := isRelativePath(path); got != want {
t.Errorf("isRelativePath(%q) = %v, want %v", path, got, want)
}
}
}

func TestTempFileName(t *testing.T) {
dir := t.TempDir()
first := tempFileName(dir, "mdview", ".html")
second := tempFileName(dir, "mdview", ".html")

if first == second {
t.Fatal("two temp file names were identical")
}
if filepath.Dir(first) != dir {
t.Fatalf("temp file %q is not in %q", first, dir)
}
if !tempFileRegex.MatchString(filepath.Base(first)) {
t.Fatalf("temp file name %q does not match the cleanup pattern", filepath.Base(first))
}
}
Loading