From 809b89d689ab6ade1d52fcb6efac9c396baa9209 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Wed, 3 Apr 2024 21:13:59 +0200 Subject: [PATCH] Implemented image links --- README.md | 8 +++++--- Render.go | 32 ++++++++++++++++++++++++------ Render_test.go | 53 +++++++++++++++++++++++++++++--------------------- 3 files changed, 62 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 4c65a88..c19ffd8 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ A markdown renderer that supports only a subset of the CommonMark spec in order - Italic - Links - Lists +- Images - Headers - Paragraphs - Quotes @@ -38,6 +39,7 @@ PASS: TestItalic PASS: TestBold PASS: TestStrike PASS: TestLink +PASS: TestImage PASS: TestList PASS: TestTables PASS: TestCode @@ -51,9 +53,9 @@ coverage: 100.0% of statements ## Benchmarks ``` -BenchmarkSmall-12 5836533 205.3 ns/op 32 B/op 1 allocs/op -BenchmarkMedium-12 967740 1103 ns/op 512 B/op 1 allocs/op -BenchmarkLarge-12 277908 4099 ns/op 2560 B/op 2 allocs/op +BenchmarkSmall-12 5884641 201.5 ns/op 32 B/op 1 allocs/op +BenchmarkMedium-12 938371 1124 ns/op 512 B/op 1 allocs/op +BenchmarkLarge-12 277065 4115 ns/op 2560 B/op 2 allocs/op ``` ## License diff --git a/Render.go b/Render.go index eeae870..15aad80 100644 --- a/Render.go +++ b/Render.go @@ -279,6 +279,7 @@ func (r *renderer) writeText(markdown string) { searchStart = 0 linkTextStart = -1 linkTextEnd = -1 + linkIsImage = false emStart = -1 strongStart = -1 strikeStart = -1 @@ -286,7 +287,7 @@ func (r *renderer) writeText(markdown string) { begin: for { - i := strings.IndexAny(markdown[searchStart:], "[]()`*_~") + i := strings.IndexAny(markdown[searchStart:], "[]()`*_~!") if i == -1 { r.WriteString(html.EscapeString(markdown[tokenStart:])) @@ -332,11 +333,19 @@ begin: linkText := markdown[linkTextStart+1 : linkTextEnd] linkURL := markdown[i+1 : urlEnd] - r.WriteString("") - r.WriteString(html.EscapeString(linkText)) - r.WriteString("") + if linkIsImage { + r.WriteString("\"")") + } else { + r.WriteString("") + r.WriteString(html.EscapeString(linkText)) + r.WriteString("") + } linkTextStart = -1 linkTextEnd = -1 @@ -402,6 +411,17 @@ begin: tokenStart = i strikeStart = i + 2 } + + case '!': + if i+1 >= len(markdown) || markdown[i+1] != '[' { + continue + } + + r.WriteString(html.EscapeString(markdown[tokenStart:i])) + tokenStart = i + linkTextStart = i + 1 + searchStart++ + linkIsImage = true } } } diff --git a/Render_test.go b/Render_test.go index db81fd4..c71d402 100644 --- a/Render_test.go +++ b/Render_test.go @@ -45,13 +45,22 @@ func TestStrike(t *testing.T) { } func TestLink(t *testing.T) { - assert.Equal(t, markdown.Render("[text](https://example.com/)"), "

text

") - assert.Equal(t, markdown.Render("[text](https://example.com/"), "

[text](https://example.com/

") - assert.Equal(t, markdown.Render("[text]https://example.com/)"), "

[text]https://example.com/)

") - assert.Equal(t, markdown.Render("[text(https://example.com/)"), "

[text(https://example.com/)

") - assert.Equal(t, markdown.Render("text](https://example.com/)"), "

text](https://example.com/)

") - assert.Equal(t, markdown.Render("[text](https://example.com/_test_)"), "

text

") - assert.Equal(t, markdown.Render("Prefix [text](https://example.com/) suffix."), "

Prefix text suffix.

") + assert.Equal(t, markdown.Render("[text](https://example.com/)"), `

text

`) + assert.Equal(t, markdown.Render("[text](https://example.com/"), `

[text](https://example.com/

`) + assert.Equal(t, markdown.Render("[text]https://example.com/)"), `

[text]https://example.com/)

`) + assert.Equal(t, markdown.Render("[text(https://example.com/)"), `

[text(https://example.com/)

`) + assert.Equal(t, markdown.Render("text](https://example.com/)"), `

text](https://example.com/)

`) + assert.Equal(t, markdown.Render("[text](https://example.com/_test_)"), `

text

`) + assert.Equal(t, markdown.Render("Prefix [text](https://example.com/) suffix."), `

Prefix text suffix.

`) +} + +func TestImage(t *testing.T) { + assert.Equal(t, markdown.Render("!"), `

!

`) + assert.Equal(t, markdown.Render("!["), `

![

`) + assert.Equal(t, markdown.Render("![]"), `

![]

`) + assert.Equal(t, markdown.Render("![]("), `

![](

`) + assert.Equal(t, markdown.Render("![]()"), `

`) + assert.Equal(t, markdown.Render("![title](https://example.com/image.png)"), `

title

`) } func TestList(t *testing.T) { @@ -97,22 +106,22 @@ func TestSeparator(t *testing.T) { } func TestCombined(t *testing.T) { - assert.Equal(t, markdown.Render("# Header\n\nLine 1."), "

Header

Line 1.

") - 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

") - assert.Equal(t, markdown.Render("> **bold** and *italic* text."), "

bold and italic text.

") - assert.Equal(t, markdown.Render("> __bold__ and _italic_ text."), "

bold and italic text.

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

Header

Line 1.

`) + 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

`) + assert.Equal(t, markdown.Render("> **bold** and *italic* text."), `

bold and italic text.

`) + assert.Equal(t, markdown.Render("> __bold__ and _italic_ text."), `

bold and italic text.

`) } func TestSecurity(t *testing.T) { - assert.Equal(t, markdown.Render("[text](javascript:alert(\"xss\"))"), "

text

") - assert.Equal(t, markdown.Render("[text](javAscRipt:alert(\"xss\"))"), "

text

") - assert.Equal(t, markdown.Render("[text]( javascript:alert(\"xss\"))"), "

text

") - assert.Equal(t, markdown.Render("[text]('javAscRipt:alert(\"xss\")')"), "

text

") - assert.Equal(t, markdown.Render("[text](\">)"), "

text

") - assert.Equal(t, markdown.Render("[]()"), "

<script>alert(123)</script>

") + assert.Equal(t, markdown.Render(`[text](javascript:alert("xss"))`), `

text

`) + assert.Equal(t, markdown.Render(`[text](javAscRipt:alert("xss"))`), `

text

`) + assert.Equal(t, markdown.Render(`[text]( javascript:alert("xss"))`), `

text

`) + assert.Equal(t, markdown.Render(`[text]('javAscRipt:alert("xss")')`), `

text

`) + assert.Equal(t, markdown.Render(`[text](">)`), `

text

`) + assert.Equal(t, markdown.Render(`[]()`), `

<script>alert(123)</script>

`) }