diff --git a/README.md b/README.md index ea5cc1b..b0d3057 100644 --- a/README.md +++ b/README.md @@ -4,23 +4,20 @@ A markdown renderer that supports only a subset of the CommonMark spec in order ## Features -- Bold -- Code -- Italic +- Code blocks +- Formatting (bold, italic, monospace) - Links - Lists -- Images - Headers - Paragraphs - Quotes - Separators -- Strikethrough - Tables ## Installation ```shell -go get git.urbach.dev/go/markdown +go get git.akyoto.dev/go/markdown ``` ## Usage @@ -37,9 +34,7 @@ PASS: TestParagraph PASS: TestHeader PASS: TestItalic PASS: TestBold -PASS: TestStrike PASS: TestLink -PASS: TestImage PASS: TestList PASS: TestTables PASS: TestCode @@ -53,14 +48,14 @@ coverage: 100.0% of statements ## Benchmarks ``` -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 +BenchmarkSmall-12 5986922 187.5 ns/op 32 B/op 1 allocs/op +BenchmarkMedium-12 1000000 1077 ns/op 512 B/op 1 allocs/op +BenchmarkLarge-12 255178 4055 ns/op 2560 B/op 2 allocs/op ``` ## License -Please see the [license documentation](https://urbach.dev/license). +Please see the [license documentation](https://akyoto.dev/license). ## Copyright diff --git a/Render.go b/Render.go index d9facae..3bc3df4 100644 --- a/Render.go +++ b/Render.go @@ -17,7 +17,6 @@ type renderer struct { paragraphLevel int quoteLevel int listLevel int - olistLevel int tableLevel int codeLines int tableHeaderWritten bool @@ -201,26 +200,6 @@ func (r *renderer) processLine(line string) { } } - pos := 0 - - for pos < len(line) && line[pos] >= '0' && line[pos] <= '9' { - pos++ - - if pos < len(line) && (line[pos] == '.' || line[pos] == ')') { - line = strings.TrimSpace(line[pos+1:]) - - if r.olistLevel == 0 { - r.WriteString("
") r.paragraphLevel++ @@ -254,12 +233,7 @@ func (r *renderer) closeLists() { r.WriteString("") } - for range r.olistLevel { - r.WriteString("
")
+ r.WriteString(html.EscapeString(markdown[codeStart:i]))
+ r.WriteString("
")
+ codeStart = -1
+ tokenStart = i + 1
+ } else {
+ r.WriteString(html.EscapeString(markdown[tokenStart:i]))
+ tokenStart = i
+ codeStart = i + 1
}
- r.WriteString(html.EscapeString(markdown[tokenStart:i]))
- r.WriteString("")
- r.WriteString(html.EscapeString(markdown[searchStart : searchStart+end]))
- r.WriteString("
")
-
- searchStart += end + 1
- tokenStart = searchStart
-
case '*', '_':
if i == emStart {
strongStart = i + 1
@@ -394,34 +343,6 @@ begin:
tokenStart = i
emStart = i + 1
}
-
- case '~':
- if i+1 >= len(markdown) || markdown[i+1] != '~' {
- continue
- }
-
- if strikeStart != -1 {
- r.WriteString("bold
") } -func TestStrike(t *testing.T) { - assert.Equal(t, markdown.Render("~normal text~"), "~normal text~
") - assert.Equal(t, markdown.Render("~~deleted text~~"), "deleted text
[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_)"), ``) - 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("![]()"), `[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("Prefix [text](https://example.com/) suffix."), "Prefix text suffix.
") } func TestList(t *testing.T) { - assert.Equal(t, markdown.Render("-"), "-
") - assert.Equal(t, markdown.Render("- "), "1
") - assert.Equal(t, markdown.Render("1."), "-
") } func TestTables(t *testing.T) { @@ -88,7 +64,6 @@ func TestCode(t *testing.T) { assert.Equal(t, markdown.Render("```\nText\n```"), "Text
")
assert.Equal(t, markdown.Render("```go\ntype A struct {\n\t\n}\n```"), "type A struct {\n\t\n}
")
assert.Equal(t, markdown.Render("`monospace`"), "monospace
`unfinished
") assert.Equal(t, markdown.Render("Inline `monospace` text."), "Inline monospace
text.
Line 1.
`) - assert.Equal(t, markdown.Render("# Header\nLine 1.\nLine 2.\nLine 3."), `Line 1. Line 2. Line 3.
`) - assert.Equal(t, markdown.Render("# Header 1\nLine 1.\n# Header 2\nLine 2."), `Line 1.
Line 2.
`) - assert.Equal(t, markdown.Render("# [Header Link](https://example.com/)"), `Text.
`) - assert.Equal(t, markdown.Render("- Entry\n# Header"), ``) - assert.Equal(t, markdown.Render("> **bold** and *italic* text."), `
- Entry
Header
`) - assert.Equal(t, markdown.Render("> __bold__ and _italic_ text."), `bold and italic text.
`) + assert.Equal(t, markdown.Render("# Header\n\nLine 1."), "bold and italic text.
Line 1.
") + assert.Equal(t, markdown.Render("# Header\nLine 1.\nLine 2.\nLine 3."), "Line 1. Line 2. Line 3.
") + assert.Equal(t, markdown.Render("# Header 1\nLine 1.\n# Header 2\nLine 2."), "Line 1.
Line 2.
") + assert.Equal(t, markdown.Render("# [Header Link](https://example.com/)"), "Text.
") + assert.Equal(t, markdown.Render("- Entry\n# Header"), "") + assert.Equal(t, markdown.Render("> **bold** and *italic* text."), "
- Entry
Header
") + 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"))`), ``) - assert.Equal(t, markdown.Render(`[text](javAscRipt:alert("xss"))`), ``) - assert.Equal(t, markdown.Render(`[text]( javascript:alert("xss"))`), ``) - assert.Equal(t, markdown.Render(`[text]('javAscRipt:alert("xss")')`), ``) - assert.Equal(t, markdown.Render(`[text](">)`), ``) - assert.Equal(t, markdown.Render(`[]()`), ``) -} \ No newline at end of file + assert.Equal(t, markdown.Render("[text](javascript:alert(\"xss\"))"), "") + assert.Equal(t, markdown.Render("[text](javAscRipt:alert(\"xss\"))"), "") + assert.Equal(t, markdown.Render("[text]( javascript:alert(\"xss\"))"), "") + assert.Equal(t, markdown.Render("[text]('javAscRipt:alert(\"xss\")')"), "") + assert.Equal(t, markdown.Render("[text](\">)"), "") + assert.Equal(t, markdown.Render("[]()"), "") +} diff --git a/benchmarks_test.go b/benchmarks_test.go index 50501be..c9231e9 100644 --- a/benchmarks_test.go +++ b/benchmarks_test.go @@ -4,8 +4,8 @@ import ( "os" "testing" - "git.urbach.dev/go/assert" - "git.urbach.dev/go/markdown" + "git.akyoto.dev/go/assert" + "git.akyoto.dev/go/markdown" ) func BenchmarkSmall(b *testing.B) { @@ -13,7 +13,7 @@ func BenchmarkSmall(b *testing.B) { assert.Nil(b, err) input := string(small) - for b.Loop() { + for range b.N { markdown.Render(input) } } @@ -23,7 +23,7 @@ func BenchmarkMedium(b *testing.B) { assert.Nil(b, err) input := string(medium) - for b.Loop() { + for range b.N { markdown.Render(input) } } @@ -33,7 +33,7 @@ func BenchmarkLarge(b *testing.B) { assert.Nil(b, err) input := string(large) - for b.Loop() { + for range b.N { markdown.Render(input) } -} \ No newline at end of file +} diff --git a/go.mod b/go.mod index 229a4be..f178769 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ -module git.urbach.dev/go/markdown +module git.akyoto.dev/go/markdown -go 1.24 +go 1.22.1 -require git.urbach.dev/go/assert v0.0.0-20250606150337-559d3d3afcda +require git.akyoto.dev/go/assert v0.1.3 diff --git a/go.sum b/go.sum index 7684754..9fc2547 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,2 @@ -git.urbach.dev/go/assert v0.0.0-20250606150337-559d3d3afcda h1:VN6ZQwtwLOm2xTms+v8IIeeNjvs55qyEBNArv3dPq9g= -git.urbach.dev/go/assert v0.0.0-20250606150337-559d3d3afcda/go.mod h1:PNI/NSBOqvoeU58/7eBsIR09Yoq2S/qtSRiTrctkiq0= +git.akyoto.dev/go/assert v0.1.3 h1:QwCUbmG4aZYsNk/OuRBz1zWVKmGlDUHhOnnDBfn8Qw8= +git.akyoto.dev/go/assert v0.1.3/go.mod h1:0GzMaM0eURuDwtGkJJkCsI7r2aUKr+5GmWNTFPgDocM=bold and italic text.