Skip to content

Commit

Permalink
extras: Implement delete extension
Browse files Browse the repository at this point in the history
With Goldmark v1.7.1 and earlier, the Goldmark "strikethrough" extension was
triggered by wrapping text within a pair of double-tilde characters. With
Goldmark v1.7.2 and later, to provide full GFM compatibility, the Goldmark
"strikethrough" extension is triggered by wrapping text within a pair of
single- or double-tilde characters.

This change created a conflict with the Hugo Goldmark Extras "subscript"
extension.

When enabling the Hugo Goldmark Extras "subscript" extension, if you
want to render subscript and strikethrough text concurrently, you must:

1. Disable the Goldmark "strikethrough" extension
2. Enable the Hugo Goldmark Extras "delete" extension
  • Loading branch information
bowman2001 authored Jun 17, 2024
1 parent 246e082 commit 4d4fcd0
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 103 deletions.
18 changes: 18 additions & 0 deletions extras/_test/delete.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
1
//- - - - - - - - -//
~~Hi~~ Hello, world!
//- - - - - - - - -//
<p><del>Hi</del> Hello, world!</p>
//= = = = = = = = = = = = = = = = = = = = = = = =//



2
//- - - - - - - - -//
This ~~has a

new paragraph~~.
//- - - - - - - - -//
<p>This ~~has a</p>
<p>new paragraph~~.</p>
//= = = = = = = = = = = = = = = = = = = = = = = =//
35 changes: 7 additions & 28 deletions extras/_test/insert.txt
Original file line number Diff line number Diff line change
@@ -1,43 +1,22 @@
1
//- - - - - - - - -//
++Hi++ Hello, world!
Hello ++,++ world!
//- - - - - - - - -//
<p><ins>Hi</ins> Hello, world!</p>
<p>Hello <ins>,</ins> world!</p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

2
//- - - - - - - - -//
This ++has a
This ++expression is stretched over a

new paragraph++.
paragraph++.
//- - - - - - - - -//
<p>This ++has a</p>
<p>new paragraph++.</p>
<p>This ++expression is stretched over a</p>
<p>paragraph++.</p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

3
//- - - - - - - - -//
x ++++foo++ bar++
//- - - - - - - - -//
<p>x <ins><ins>foo</ins> bar</ins></p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

4
//- - - - - - - - -//
x ++foo ++bar++++
//- - - - - - - - -//
<p>x <ins>foo <ins>bar</ins></ins></p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

5
//- - - - - - - - -//
x ++++foo++++
//- - - - - - - - -//
<p>x <ins><ins>foo</ins></ins></p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

6
//- - - - - - - - -//
**++test**++

++**test++**
Expand All @@ -46,7 +25,7 @@ x ++++foo++++
<p><ins>**test</ins>**</p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

7
4
//- - - - - - - - -//
[++link]()++

Expand Down
23 changes: 1 addition & 22 deletions extras/_test/mark.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,6 @@ new paragraph==.

3
//- - - - - - - - -//
x ====foo== bar==
//- - - - - - - - -//
<p>x <mark><mark>foo</mark> bar</mark></p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

4
//- - - - - - - - -//
x ==foo ==bar====
//- - - - - - - - -//
<p>x <mark>foo <mark>bar</mark></mark></p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

5
//- - - - - - - - -//
x ====foo====
//- - - - - - - - -//
<p>x <mark><mark>foo</mark></mark></p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

6
//- - - - - - - - -//
**==test**==

==**test==**
Expand All @@ -46,7 +25,7 @@ x ====foo====
<p><mark>**test</mark>**</p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

7
4
//- - - - - - - - -//
[==link]()==

Expand Down
25 changes: 16 additions & 9 deletions extras/_test/subscript.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,30 @@ x~i~ + x~j~
<p><sub>foo bar</sub></p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

7: Mixed subscript and strikethrough
//- - - - - - - - -//
~~x~foobar~~~
//- - - - - - - - -//
<p><del>x<sub>foobar</sub></del></p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

8: Tildes in the middle and text before
7: Tildes in the middle and text before
//- - - - - - - - -//
text H~2~O
//- - - - - - - - -//
<p>text H<sub>2</sub>O</p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

9: Tildes in the middle and text after
8: Tildes in the middle and text after
//- - - - - - - - -//
H~2~O text
//- - - - - - - - -//
<p>H<sub>2</sub>O text</p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

9: Check for a direct conflict with strikethrough
//- - - - - - - - -//
~~foobar~~
//- - - - - - - - -//
<p><del>foobar</del></p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

10: Mixed subscript and strikethrough will not work as expected!
//- - - - - - - - -//
~~x~foobar~~~
//- - - - - - - - -//
<p>~~x~foobar~~~</p>
//= = = = = = = = = = = = = = = = = = = = = = = =//
21 changes: 7 additions & 14 deletions extras/_test/superscript.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
1: Surrounded by cares
1: Surrounded by carets
//- - - - - - - - -//
^foo^
//- - - - - - - - -//
Expand All @@ -19,49 +19,42 @@ x^2^ + x^5^
<p>x<sup>2</sup> + x<sup>5</sup></p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

4: Escaped care
4: Escaped caret
//- - - - - - - - -//
^foo\^
//- - - - - - - - -//
<p>^foo^</p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

5: : Cares surround text with a non-breaking space entity
5: Carets surround text with a non-breaking space entity
//- - - - - - - - -//
^foo&nbsp;bar^
//- - - - - - - - -//
<p><sup>foo bar</sup></p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

6: Cares surround text Surround with a non-breaking space (UTF-8)
6: Carets surround text Surround with a non-breaking space (UTF-8)
//- - - - - - - - -//
^foo bar^
//- - - - - - - - -//
<p><sup>foo bar</sup></p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

7: Cares in the middle and text before
7: Carets in the middle and text before
//- - - - - - - - -//
text C^foo^C
//- - - - - - - - -//
<p>text C<sup>foo</sup>C</p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

8: Cares in the middle and text after
8: Carets in the middle and text after
//- - - - - - - - -//
C^foo^C text
//- - - - - - - - -//
<p>C<sup>foo</sup>C text</p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

9: Wrong markers in LaTeX style notation should be left untouched
//- - - - - - - - -//
x^2 + x^3
//- - - - - - - - -//
<p>x^2 + x^3</p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

10: Footnote markers should be left untouched
9: Footnote markers should be left untouched
//- - - - - - - - -//
text[^1] text[^2]
//- - - - - - - - -//
Expand Down
11 changes: 11 additions & 0 deletions extras/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ var markTag = inlineTag{
RenderPriority: 550,
}

var deleteTag = inlineTag{
TagKind: kindDelete,
Char: '~',
Number: 2,
Html: "del",
WhitespaceAllowed: false,
ParsePriority: 400,
RenderPriority: 400,
}

type inlineTagNode struct {
ast.BaseInline

Expand All @@ -73,6 +83,7 @@ var (
kindSubscript = ast.NewNodeKind("Subscript")
kindInsert = ast.NewNodeKind("Insert")
kindMark = ast.NewNodeKind("Mark")
kindDelete = ast.NewNodeKind("Delete")
)

func (n *inlineTagNode) Kind() ast.NodeKind {
Expand Down
30 changes: 9 additions & 21 deletions extras/inline.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,35 +48,15 @@ func (s *inlineTagParser) Parse(_ ast.Node, block text.Reader, pc parser.Context
before := block.PrecendingCharacter()
line, segment := block.PeekLine()
node := parser.ScanDelimiter(line, before, s.Number, newInlineTagDelimiterProcessor(s.inlineTag))
if node == nil {
if node == nil || node.OriginalLength > 2 || before == rune(s.Char) {
return nil
}
if !s.WhitespaceAllowed && node.CanOpen && hasSpace(line) {
if !(node.CanClose && pc.LastDelimiter() != nil && pc.LastDelimiter().Char == node.Char) {
return nil
}
}
node.Segment = segment.WithStop(segment.Start + node.OriginalLength)
block.Advance(node.OriginalLength)
pc.PushDelimiter(node)
return node
}

// Check if there is an ordinary white space in the line before the next marker
func hasSpace(line []byte) bool {
marker := line[0]
for i := 1; i < len(line); i++ {
c := line[i]
if c == marker {
break
}
if util.IsSpace(c) {
return true
}
}
return false
}

type inlineTagHTMLRenderer struct {
htmlTag string
tagKind ast.NodeKind
Expand Down Expand Up @@ -133,6 +113,7 @@ type Config struct {
Subscript SubscriptConfig
Insert InsertConfig
Mark MarkConfig
Delete DeleteConfig
}

// SuperscriptConfig configures the superscript extension.
Expand All @@ -155,6 +136,10 @@ type MarkConfig struct {
Enable bool
}

type DeleteConfig struct {
Enable bool
}

// New returns a new inline tag extension.

func New(config Config) goldmark.Extender {
Expand Down Expand Up @@ -185,4 +170,7 @@ func (tag *inlineExtension) Extend(md goldmark.Markdown) {
if tag.conf.Mark.Enable {
addTag(markTag)
}
if tag.conf.Delete.Enable {
addTag(deleteTag)
}
}
58 changes: 49 additions & 9 deletions extras/inline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"testing"

"github.com/gohugoio/hugo-goldmark-extensions/extras"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/text"

"github.com/yuin/goldmark"
Expand All @@ -17,11 +16,17 @@ func buildGoldmarkWithInlineTag(conf extras.Config) goldmark.Markdown {
}

var (
markdown = goldmark.New()
markdownWithSuperscript = buildGoldmarkWithInlineTag(extras.Config{Superscript: extras.SuperscriptConfig{Enable: true}})
markdownWithSubscript = buildGoldmarkWithInlineTag(extras.Config{Subscript: extras.SubscriptConfig{Enable: true}})
markdownWithInsert = buildGoldmarkWithInlineTag(extras.Config{Insert: extras.InsertConfig{Enable: true}})
markdownWithMark = buildGoldmarkWithInlineTag(extras.Config{Mark: extras.MarkConfig{Enable: true}})
markdown = goldmark.New()
markdownWithSuperscript = buildGoldmarkWithInlineTag(extras.Config{Superscript: extras.SuperscriptConfig{Enable: true}})
markdownWithSubscript = buildGoldmarkWithInlineTag(extras.Config{Subscript: extras.SubscriptConfig{Enable: true}})
markdownWithInsert = buildGoldmarkWithInlineTag(extras.Config{Insert: extras.InsertConfig{Enable: true}})
markdownWithMark = buildGoldmarkWithInlineTag(extras.Config{Mark: extras.MarkConfig{Enable: true}})
markdownWithDelete = buildGoldmarkWithInlineTag(extras.Config{Delete: extras.DeleteConfig{Enable: true}})
markdownWithDeleteAndSubscript = goldmark.New(
goldmark.WithExtensions(
extras.New(extras.Config{Subscript: extras.SubscriptConfig{Enable: true}}),
extras.New(extras.Config{Delete: extras.DeleteConfig{Enable: true}}),
))
)

func TestSuperscript(t *testing.T) {
Expand Down Expand Up @@ -62,9 +67,7 @@ This formula contains one superscript: f(x) = x^2^ .`
}

func TestSubscript(t *testing.T) {
markdown := goldmark.New(
goldmark.WithExtensions(extras.New(extras.Config{Subscript: extras.SubscriptConfig{Enable: true}}), extension.Strikethrough))
testutil.DoTestCaseFile(markdown, "_test/subscript.txt", t, testutil.ParseCliCaseArg()...)
testutil.DoTestCaseFile(markdownWithDeleteAndSubscript, "_test/subscript.txt", t, testutil.ParseCliCaseArg()...)
}

func TestSubscriptDump(t *testing.T) {
Expand Down Expand Up @@ -175,3 +178,40 @@ Add some marked text: ==marked==. Amazing.`
}
})
}

func TestDelete(t *testing.T) {
testutil.DoTestCaseFile(markdownWithDelete, "_test/delete.txt", t, testutil.ParseCliCaseArg()...)
}

func TestDeleteDump(t *testing.T) {
input := "Delete some text: ~~deleted~~. Amazing."
root := markdownWithDelete.Parser().Parse(text.NewReader([]byte(input)))
root.Dump([]byte(input), 0)
}

func BenchmarkWithAndWithoutDelete(b *testing.B) {
const input = `
## Delete text
Delete some text: ~~deleted~~. Amazing.`

b.Run("without delete extension", func(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
var buf bytes.Buffer
if err := markdown.Convert([]byte(input), &buf); err != nil {
b.Fatal(err)
}
}
})

b.Run("with delete extension", func(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
var buf bytes.Buffer
if err := markdownWithMark.Convert([]byte(input), &buf); err != nil {
b.Fatal(err)
}
}
})
}

0 comments on commit 4d4fcd0

Please sign in to comment.