mirror of
https://github.com/go-gitea/gitea.git
synced 2024-12-21 17:23:55 +08:00
Refactor markdown render (#32728)
Follow up recent render system refactoring PRs (split test code), and fine tune the math render (added some new cases)
This commit is contained in:
parent
f7f68e4cc0
commit
0f18046df4
56
modules/markup/markdown/markdown_attention_test.go
Normal file
56
modules/markup/markdown/markdown_attention_test.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package markdown_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/markup"
|
||||||
|
"code.gitea.io/gitea/modules/markup/markdown"
|
||||||
|
"code.gitea.io/gitea/modules/svg"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"golang.org/x/text/cases"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAttention(t *testing.T) {
|
||||||
|
defer svg.MockIcon("octicon-info")()
|
||||||
|
defer svg.MockIcon("octicon-light-bulb")()
|
||||||
|
defer svg.MockIcon("octicon-report")()
|
||||||
|
defer svg.MockIcon("octicon-alert")()
|
||||||
|
defer svg.MockIcon("octicon-stop")()
|
||||||
|
|
||||||
|
renderAttention := func(attention, icon string) string {
|
||||||
|
tmpl := `<blockquote class="attention-header attention-{attention}"><p><svg class="attention-icon attention-{attention} svg {icon}" width="16" height="16"></svg><strong class="attention-{attention}">{Attention}</strong></p>`
|
||||||
|
tmpl = strings.ReplaceAll(tmpl, "{attention}", attention)
|
||||||
|
tmpl = strings.ReplaceAll(tmpl, "{icon}", icon)
|
||||||
|
tmpl = strings.ReplaceAll(tmpl, "{Attention}", cases.Title(language.English).String(attention))
|
||||||
|
return tmpl
|
||||||
|
}
|
||||||
|
|
||||||
|
test := func(input, expected string) {
|
||||||
|
result, err := markdown.RenderString(markup.NewTestRenderContext(), input)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(result)))
|
||||||
|
}
|
||||||
|
|
||||||
|
test(`
|
||||||
|
> [!NOTE]
|
||||||
|
> text
|
||||||
|
`, renderAttention("note", "octicon-info")+"\n<p>text</p>\n</blockquote>")
|
||||||
|
|
||||||
|
test(`> [!note]`, renderAttention("note", "octicon-info")+"\n</blockquote>")
|
||||||
|
test(`> [!tip]`, renderAttention("tip", "octicon-light-bulb")+"\n</blockquote>")
|
||||||
|
test(`> [!important]`, renderAttention("important", "octicon-report")+"\n</blockquote>")
|
||||||
|
test(`> [!warning]`, renderAttention("warning", "octicon-alert")+"\n</blockquote>")
|
||||||
|
test(`> [!caution]`, renderAttention("caution", "octicon-stop")+"\n</blockquote>")
|
||||||
|
|
||||||
|
// escaped by mdformat
|
||||||
|
test(`> \[!NOTE\]`, renderAttention("note", "octicon-info")+"\n</blockquote>")
|
||||||
|
|
||||||
|
// legacy GitHub style
|
||||||
|
test(`> **warning**`, renderAttention("warning", "octicon-alert")+"\n</blockquote>")
|
||||||
|
}
|
25
modules/markup/markdown/markdown_benchmark_test.go
Normal file
25
modules/markup/markdown/markdown_benchmark_test.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package markdown_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/markup"
|
||||||
|
"code.gitea.io/gitea/modules/markup/markdown"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkSpecializedMarkdown(b *testing.B) {
|
||||||
|
// 240856 4719 ns/op
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
markdown.SpecializedMarkdown(&markup.RenderContext{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMarkdownRender(b *testing.B) {
|
||||||
|
// 23202 50840 ns/op
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, _ = markdown.RenderString(markup.NewTestRenderContext(), "https://example.com\n- a\n- b\n")
|
||||||
|
}
|
||||||
|
}
|
163
modules/markup/markdown/markdown_math_test.go
Normal file
163
modules/markup/markdown/markdown_math_test.go
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package markdown
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/markup"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMathRender(t *testing.T) {
|
||||||
|
const nl = "\n"
|
||||||
|
testcases := []struct {
|
||||||
|
testcase string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"$a$",
|
||||||
|
`<p><code class="language-math is-loading">a</code></p>` + nl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ a $",
|
||||||
|
`<p><code class="language-math is-loading">a</code></p>` + nl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$a$ $b$",
|
||||||
|
`<p><code class="language-math is-loading">a</code> <code class="language-math is-loading">b</code></p>` + nl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`\(a\) \(b\)`,
|
||||||
|
`<p><code class="language-math is-loading">a</code> <code class="language-math is-loading">b</code></p>` + nl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`$a$.`,
|
||||||
|
`<p><code class="language-math is-loading">a</code>.</p>` + nl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`.$a$`,
|
||||||
|
`<p>.$a$</p>` + nl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`$a a$b b$`,
|
||||||
|
`<p>$a a$b b$</p>` + nl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`a a$b b`,
|
||||||
|
`<p>a a$b b</p>` + nl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`a$b $a a$b b$`,
|
||||||
|
`<p>a$b $a a$b b$</p>` + nl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a$x$",
|
||||||
|
`<p>a$x$</p>` + nl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$x$a",
|
||||||
|
`<p>$x$a</p>` + nl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$a$ ($b$) [$c$] {$d$}",
|
||||||
|
`<p><code class="language-math is-loading">a</code> (<code class="language-math is-loading">b</code>) [$c$] {$d$}</p>` + nl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$a$$",
|
||||||
|
`<pre class="code-block is-loading"><code class="chroma language-math display">a</code></pre>` + nl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$a$$ test",
|
||||||
|
`<p><code class="language-math display is-loading">a</code> test</p>` + nl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"test $$a$$",
|
||||||
|
`<p>test <code class="language-math display is-loading">a</code></p>` + nl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"foo $x=\\$$ bar",
|
||||||
|
`<p>foo <code class="language-math is-loading">x=\$</code> bar</p>` + nl,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testcases {
|
||||||
|
t.Run(test.testcase, func(t *testing.T) {
|
||||||
|
res, err := RenderString(markup.NewTestRenderContext(), test.testcase)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, test.expected, string(res))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMathRenderBlockIndent(t *testing.T) {
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
testcase string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"indent-0",
|
||||||
|
`
|
||||||
|
\[
|
||||||
|
\alpha
|
||||||
|
\]
|
||||||
|
`,
|
||||||
|
`<pre class="code-block is-loading"><code class="chroma language-math display">
|
||||||
|
\alpha
|
||||||
|
</code></pre>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indent-1",
|
||||||
|
`
|
||||||
|
\[
|
||||||
|
\alpha
|
||||||
|
\]
|
||||||
|
`,
|
||||||
|
`<pre class="code-block is-loading"><code class="chroma language-math display">
|
||||||
|
\alpha
|
||||||
|
</code></pre>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indent-2",
|
||||||
|
`
|
||||||
|
\[
|
||||||
|
\alpha
|
||||||
|
\]
|
||||||
|
`,
|
||||||
|
`<pre class="code-block is-loading"><code class="chroma language-math display">
|
||||||
|
\alpha
|
||||||
|
</code></pre>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indent-0-oneline",
|
||||||
|
`$$ x $$
|
||||||
|
foo`,
|
||||||
|
`<pre class="code-block is-loading"><code class="chroma language-math display"> x </code></pre>
|
||||||
|
<p>foo</p>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indent-3-oneline",
|
||||||
|
` $$ x $$<SPACE>
|
||||||
|
foo`,
|
||||||
|
`<pre class="code-block is-loading"><code class="chroma language-math display"> x </code></pre>
|
||||||
|
<p>foo</p>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testcases {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
res, err := RenderString(markup.NewTestRenderContext(), strings.ReplaceAll(test.testcase, "<SPACE>", " "))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, test.expected, string(res), "unexpected result for test case:\n%s", test.testcase)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,13 +13,10 @@ import (
|
||||||
"code.gitea.io/gitea/modules/markup"
|
"code.gitea.io/gitea/modules/markup"
|
||||||
"code.gitea.io/gitea/modules/markup/markdown"
|
"code.gitea.io/gitea/modules/markup/markdown"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/svg"
|
|
||||||
"code.gitea.io/gitea/modules/test"
|
"code.gitea.io/gitea/modules/test"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"golang.org/x/text/cases"
|
|
||||||
"golang.org/x/text/language"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -386,81 +383,6 @@ func TestColorPreview(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMathBlock(t *testing.T) {
|
|
||||||
const nl = "\n"
|
|
||||||
testcases := []struct {
|
|
||||||
testcase string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"$a$",
|
|
||||||
`<p><code class="language-math is-loading">a</code></p>` + nl,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ a $",
|
|
||||||
`<p><code class="language-math is-loading">a</code></p>` + nl,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$a$ $b$",
|
|
||||||
`<p><code class="language-math is-loading">a</code> <code class="language-math is-loading">b</code></p>` + nl,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`\(a\) \(b\)`,
|
|
||||||
`<p><code class="language-math is-loading">a</code> <code class="language-math is-loading">b</code></p>` + nl,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`$a$.`,
|
|
||||||
`<p><code class="language-math is-loading">a</code>.</p>` + nl,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`.$a$`,
|
|
||||||
`<p>.$a$</p>` + nl,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`$a a$b b$`,
|
|
||||||
`<p>$a a$b b$</p>` + nl,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`a a$b b`,
|
|
||||||
`<p>a a$b b</p>` + nl,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`a$b $a a$b b$`,
|
|
||||||
`<p>a$b $a a$b b$</p>` + nl,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"a$x$",
|
|
||||||
`<p>a$x$</p>` + nl,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$x$a",
|
|
||||||
`<p>$x$a</p>` + nl,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$$a$$",
|
|
||||||
`<pre class="code-block is-loading"><code class="chroma language-math display">a</code></pre>` + nl,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$a$ ($b$) [$c$] {$d$}",
|
|
||||||
`<p><code class="language-math is-loading">a</code> (<code class="language-math is-loading">b</code>) [$c$] {$d$}</p>` + nl,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$$a$$ test",
|
|
||||||
`<p><code class="language-math display is-loading">a</code> test</p>` + nl,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"test $$a$$",
|
|
||||||
`<p>test <code class="language-math display is-loading">a</code></p>` + nl,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testcases {
|
|
||||||
res, err := markdown.RenderString(markup.NewTestRenderContext(), test.testcase)
|
|
||||||
assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
|
|
||||||
assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTaskList(t *testing.T) {
|
func TestTaskList(t *testing.T) {
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
testcase string
|
testcase string
|
||||||
|
@ -551,56 +473,3 @@ space</p>
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, expected, string(result))
|
assert.Equal(t, expected, string(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAttention(t *testing.T) {
|
|
||||||
defer svg.MockIcon("octicon-info")()
|
|
||||||
defer svg.MockIcon("octicon-light-bulb")()
|
|
||||||
defer svg.MockIcon("octicon-report")()
|
|
||||||
defer svg.MockIcon("octicon-alert")()
|
|
||||||
defer svg.MockIcon("octicon-stop")()
|
|
||||||
|
|
||||||
renderAttention := func(attention, icon string) string {
|
|
||||||
tmpl := `<blockquote class="attention-header attention-{attention}"><p><svg class="attention-icon attention-{attention} svg {icon}" width="16" height="16"></svg><strong class="attention-{attention}">{Attention}</strong></p>`
|
|
||||||
tmpl = strings.ReplaceAll(tmpl, "{attention}", attention)
|
|
||||||
tmpl = strings.ReplaceAll(tmpl, "{icon}", icon)
|
|
||||||
tmpl = strings.ReplaceAll(tmpl, "{Attention}", cases.Title(language.English).String(attention))
|
|
||||||
return tmpl
|
|
||||||
}
|
|
||||||
|
|
||||||
test := func(input, expected string) {
|
|
||||||
result, err := markdown.RenderString(markup.NewTestRenderContext(), input)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(result)))
|
|
||||||
}
|
|
||||||
|
|
||||||
test(`
|
|
||||||
> [!NOTE]
|
|
||||||
> text
|
|
||||||
`, renderAttention("note", "octicon-info")+"\n<p>text</p>\n</blockquote>")
|
|
||||||
|
|
||||||
test(`> [!note]`, renderAttention("note", "octicon-info")+"\n</blockquote>")
|
|
||||||
test(`> [!tip]`, renderAttention("tip", "octicon-light-bulb")+"\n</blockquote>")
|
|
||||||
test(`> [!important]`, renderAttention("important", "octicon-report")+"\n</blockquote>")
|
|
||||||
test(`> [!warning]`, renderAttention("warning", "octicon-alert")+"\n</blockquote>")
|
|
||||||
test(`> [!caution]`, renderAttention("caution", "octicon-stop")+"\n</blockquote>")
|
|
||||||
|
|
||||||
// escaped by mdformat
|
|
||||||
test(`> \[!NOTE\]`, renderAttention("note", "octicon-info")+"\n</blockquote>")
|
|
||||||
|
|
||||||
// legacy GitHub style
|
|
||||||
test(`> **warning**`, renderAttention("warning", "octicon-alert")+"\n</blockquote>")
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSpecializedMarkdown(b *testing.B) {
|
|
||||||
// 240856 4719 ns/op
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
markdown.SpecializedMarkdown(&markup.RenderContext{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkMarkdownRender(b *testing.B) {
|
|
||||||
// 23202 50840 ns/op
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, _ = markdown.RenderString(markup.NewTestRenderContext(), "https://example.com\n- a\n- b\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -54,21 +54,19 @@ func (b *blockParser) Open(parent ast.Node, reader text.Reader, pc parser.Contex
|
||||||
idx := bytes.Index(line[pos+2:], endBytes)
|
idx := bytes.Index(line[pos+2:], endBytes)
|
||||||
if idx >= 0 {
|
if idx >= 0 {
|
||||||
// for case $$ ... $$ any other text
|
// for case $$ ... $$ any other text
|
||||||
for i := pos + idx + 4; i < len(line); i++ {
|
for i := pos + 2 + idx + 2; i < len(line); i++ {
|
||||||
if line[i] != ' ' && line[i] != '\n' {
|
if line[i] != ' ' && line[i] != '\n' {
|
||||||
return nil, parser.NoChildren
|
return nil, parser.NoChildren
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
segment.Stop = segment.Start + idx + 2
|
segment.Start += pos + 2
|
||||||
reader.Advance(segment.Len() - 1)
|
segment.Stop = segment.Start + idx
|
||||||
segment.Start += 2
|
|
||||||
node.Lines().Append(segment)
|
node.Lines().Append(segment)
|
||||||
node.Closed = true
|
node.Closed = true
|
||||||
return node, parser.Close | parser.NoChildren
|
return node, parser.Close | parser.NoChildren
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.Advance(segment.Len() - 1)
|
segment.Start += pos + 2
|
||||||
segment.Start += 2
|
|
||||||
node.Lines().Append(segment)
|
node.Lines().Append(segment)
|
||||||
return node, parser.NoChildren
|
return node, parser.NoChildren
|
||||||
}
|
}
|
||||||
|
@ -103,7 +101,6 @@ func (b *blockParser) Continue(node ast.Node, reader text.Reader, pc parser.Cont
|
||||||
pos, padding := util.IndentPosition(line, 0, block.Indent)
|
pos, padding := util.IndentPosition(line, 0, block.Indent)
|
||||||
seg := text.NewSegmentPadding(segment.Start+pos, segment.Stop, padding)
|
seg := text.NewSegmentPadding(segment.Start+pos, segment.Stop, padding)
|
||||||
node.Lines().Append(seg)
|
node.Lines().Append(seg)
|
||||||
reader.AdvanceAndSetPadding(segment.Stop-segment.Start-pos-1, padding)
|
|
||||||
return parser.Continue | parser.NoChildren
|
return parser.Continue | parser.NoChildren
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ var defaultDualDollarParser = &inlineParser{
|
||||||
end: []byte{'$', '$'},
|
end: []byte{'$', '$'},
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInlineDollarParser returns a new inline parser
|
|
||||||
func NewInlineDollarParser() parser.InlineParser {
|
func NewInlineDollarParser() parser.InlineParser {
|
||||||
return defaultInlineDollarParser
|
return defaultInlineDollarParser
|
||||||
}
|
}
|
||||||
|
@ -40,7 +39,6 @@ var defaultInlineBracketParser = &inlineParser{
|
||||||
end: []byte{'\\', ')'},
|
end: []byte{'\\', ')'},
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInlineDollarParser returns a new inline parser
|
|
||||||
func NewInlineBracketParser() parser.InlineParser {
|
func NewInlineBracketParser() parser.InlineParser {
|
||||||
return defaultInlineBracketParser
|
return defaultInlineBracketParser
|
||||||
}
|
}
|
||||||
|
@ -81,35 +79,29 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.
|
||||||
opener := len(parser.start)
|
opener := len(parser.start)
|
||||||
|
|
||||||
// Now look for an ending line
|
// Now look for an ending line
|
||||||
ender := opener
|
ender := -1
|
||||||
for {
|
for i := opener; i < len(line); i++ {
|
||||||
pos := bytes.Index(line[ender:], parser.end)
|
if bytes.HasPrefix(line[i:], parser.end) {
|
||||||
if pos < 0 {
|
succeedingCharacter := byte(0)
|
||||||
return nil
|
if i+len(parser.end) < len(line) {
|
||||||
|
succeedingCharacter = line[i+len(parser.end)]
|
||||||
}
|
}
|
||||||
|
|
||||||
ender += pos
|
|
||||||
|
|
||||||
// Now we want to check the character at the end of our parser section
|
|
||||||
// that is ender + len(parser.end) and check if char before ender is '\'
|
|
||||||
pos = ender + len(parser.end)
|
|
||||||
if len(line) <= pos {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
suceedingCharacter := line[pos]
|
|
||||||
// check valid ending character
|
// check valid ending character
|
||||||
if !isPunctuation(suceedingCharacter) &&
|
isValidEndingChar := isPunctuation(succeedingCharacter) || isBracket(succeedingCharacter) ||
|
||||||
!(suceedingCharacter == ' ') &&
|
succeedingCharacter == ' ' || succeedingCharacter == '\n' || succeedingCharacter == 0
|
||||||
!(suceedingCharacter == '\n') &&
|
if !isValidEndingChar {
|
||||||
!isBracket(suceedingCharacter) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if line[ender-1] != '\\' {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
ender = i
|
||||||
// move the pointer onwards
|
break
|
||||||
ender += len(parser.end)
|
}
|
||||||
|
if line[i] == '\\' {
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ender == -1 {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
block.Advance(opener)
|
block.Advance(opener)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user