From 8e009702c2805f5e99de63c36909e7d81b58088e Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Mon, 1 Apr 2024 20:04:20 +0200 Subject: [PATCH] Implemented lists --- README.md | 3 ++- Render.go | 42 +++++++++++++++++++++++++++++++++++++----- Render_test.go | 9 +++++++++ 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b59879e..c5f42be 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Markdown renderer. ## Features - Links +- Lists - Headers - Paragraphs - Quotes @@ -37,7 +38,7 @@ coverage: 100.0% of statements ## Benchmarks ``` -BenchmarkSmall-12 2425053 492.8 ns/op 248 B/op 5 allocs/op +BenchmarkSmall-12 2585194 462.4 ns/op 248 B/op 5 allocs/op ``` ## License diff --git a/Render.go b/Render.go index 1447a87..d13efe2 100644 --- a/Render.go +++ b/Render.go @@ -14,6 +14,7 @@ type renderer struct { out strings.Builder paragraphLevel int quoteLevel int + listLevel int } // Render creates HTML from the supplied markdown text. @@ -26,7 +27,7 @@ func Render(markdown string) string { for { if i > len(markdown) { - r.closeParagraphs() + r.closeAll() for range r.quoteLevel { r.out.WriteString("") @@ -72,8 +73,14 @@ func (r *renderer) processLine(line string) { r.quoteLevel = newQuoteLevel - if strings.HasPrefix(line, "#") { - r.closeParagraphs() + if len(line) == 0 { + r.closeAll() + return + } + + switch line[0] { + case '#': + r.closeAll() space := strings.IndexByte(line, ' ') if space > 0 && space <= 6 { @@ -83,10 +90,19 @@ func (r *renderer) processLine(line string) { } return - } - if len(line) == 0 { + case '-', '*': r.closeParagraphs() + line = strings.TrimSpace(line[1:]) + + if r.listLevel == 0 { + r.out.WriteString("") + } + + r.listLevel = 0 +} + // writeText converts inline markdown to HTML. func (r *renderer) writeText(markdown string) { var ( @@ -170,6 +201,7 @@ func (r *renderer) writeText(markdown string) { } } +// sanitizeURL makes a URL safe to use as the value for a `href` attribute. func sanitizeURL(linkURL string) string { linkURL = strings.TrimSpace(linkURL) diff --git a/Render_test.go b/Render_test.go index 4ccecaa..95be167 100644 --- a/Render_test.go +++ b/Render_test.go @@ -38,6 +38,12 @@ func TestLink(t *testing.T) { assert.Equal(t, markdown.Render("Prefix [text](https://example.com/) suffix."), "

Prefix text suffix.

") } +func TestList(t *testing.T) { + assert.Equal(t, markdown.Render("- Entry"), "") + assert.Equal(t, markdown.Render("- Entry 1\n- Entry 2"), "") + assert.Equal(t, markdown.Render("- Entry 1\n- Entry 2\n- Entry 3"), "") +} + func TestQuote(t *testing.T) { assert.Equal(t, markdown.Render("> Line"), "

Line

") assert.Equal(t, markdown.Render("> Line 1\n> Line 2"), "

Line 1 Line 2

") @@ -51,6 +57,9 @@ func TestCombined(t *testing.T) { assert.Equal(t, markdown.Render("# Header\nLine 1.\nLine 2.\nLine 3."), "

Header

Line 1. Line 2. Line 3.

") assert.Equal(t, markdown.Render("# Header 1\nLine 1.\n# Header 2\nLine 2."), "

Header 1

Line 1.

Header 2

Line 2.

") assert.Equal(t, markdown.Render("# [Header Link](https://example.com/)"), "

Header Link

") + assert.Equal(t, markdown.Render("# Title\n\n- Entry 1\n- Entry 2\n\nText."), "

Title

Text.

") + assert.Equal(t, markdown.Render("- Entry\n# Header"), "

Header

") + assert.Equal(t, markdown.Render("> - Entry\n> # Header"), "

Header

") } func TestSecurity(t *testing.T) {